1
0
mirror of https://github.com/mifi/lossless-cut.git synced 2024-11-25 11:43:17 +01:00

allow opening directories recursively

This commit is contained in:
Mikael Finstad 2024-01-31 21:59:54 +07:00
parent fabab12bce
commit bc04a4f2a4
No known key found for this signature in database
GPG Key ID: 25AB36E3E81CBC26
2 changed files with 47 additions and 8 deletions

View File

@ -68,7 +68,7 @@ import {
getOutPath, getSuffixedOutPath, handleError, getOutDir,
isStoreBuild, dragPreventer,
havePermissionToReadFile, resolvePathIfNeeded, getPathReadAccessError, html5ifiedPrefix, html5dummySuffix, findExistingHtml5FriendlyFile,
deleteFiles, isOutOfSpaceError, isExecaFailure, readFileSize, readFileSizes, checkFileSizes, setDocumentTitle, getOutFileExtension, getSuffixedFileName, mustDisallowVob, readVideoTs, getImportProjectType,
deleteFiles, isOutOfSpaceError, isExecaFailure, readFileSize, readFileSizes, checkFileSizes, setDocumentTitle, getOutFileExtension, getSuffixedFileName, mustDisallowVob, readVideoTs, readDirRecursively, getImportProjectType,
calcShouldShowWaveform, calcShouldShowKeyframes,
} from './util';
import { toast, errorToast } from './swal';
@ -88,6 +88,7 @@ import isDev from './isDev';
const electron = window.require('electron');
const { exists } = window.require('fs-extra');
const { lstat } = window.require('fs/promises');
const filePathToUrl = window.require('file-url');
const { parse: parsePath, join: pathJoin, basename, dirname } = window.require('path');
@ -1798,12 +1799,28 @@ function App() {
[lastOpenedPathRef.current] = filePaths;
// https://en.wikibooks.org/wiki/Inside_DVD-Video/Directory_Structure
if (filePaths.length === 1 && /^VIDEO_TS$/i.test(basename(filePaths[0]))) {
if (mustDisallowVob()) return;
filePaths = await readVideoTs(filePaths[0]);
// first check if it is a single directory, and if so, read it recursively
if (filePaths.length === 1) {
const firstFilePath = filePaths[0];
const firstFileStat = await lstat(firstFilePath);
if (firstFileStat.isDirectory()) {
console.log('Reading directory...');
filePaths = await readDirRecursively(firstFilePath);
}
}
// Only allow opening regular files
// eslint-disable-next-line no-restricted-syntax
for (const path of filePaths) {
// eslint-disable-next-line no-await-in-loop
const fileStat = await lstat(path);
if (!fileStat.isFile()) {
errorToast(i18n.t('Cannot open anything else than regular files'));
console.warn('Not a file:', path);
return;
}
}
if (filePaths.length > 1) {
if (alwaysConcatMultipleFiles) {
@ -1818,6 +1835,12 @@ function App() {
// filePaths.length is now 1
const firstFilePath = filePaths[0];
// https://en.wikibooks.org/wiki/Inside_DVD-Video/Directory_Structure
if (/^VIDEO_TS$/i.test(basename(firstFilePath))) {
if (mustDisallowVob()) return;
filePaths = await readVideoTs(firstFilePath);
}
if (workingRef.current) return;
try {
setWorking({ text: i18n.t('Loading file') });
@ -1898,7 +1921,7 @@ function App() {
}, [alwaysConcatMultipleFiles, batchLoadPaths, setWorking, isFileOpened, batchFiles.length, userOpenSingleFile, checkFileOpened, loadEdlFile, enableAskForFileOpenAction, addStreamSourceFile, filePath]);
const openFilesDialog = useCallback(async () => {
const { canceled, filePaths } = await showOpenDialog({ properties: ['openFile', 'multiSelections'], defaultPath: lastOpenedPathRef.current });
const { canceled, filePaths } = await showOpenDialog({ properties: ['openFile', 'openDirectory', 'multiSelections'], defaultPath: lastOpenedPathRef.current });
if (canceled) return;
userOpenFiles(filePaths);
}, [userOpenFiles]);

View File

@ -11,7 +11,7 @@ import { ffmpegExtractWindow } from './util/constants';
const { dirname, parse: parsePath, join, extname, isAbsolute, resolve, basename } = window.require('path');
const fsExtra = window.require('fs-extra');
const { stat, readdir, utimes, unlink } = window.require('fs/promises');
const { stat, lstat, readdir, utimes, unlink } = window.require('fs/promises');
const os = window.require('os');
const { ipcRenderer } = window.require('electron');
const remote = window.require('@electron/remote');
@ -90,7 +90,7 @@ export async function getPathReadAccessError(pathIn) {
}
export async function dirExists(dirPath) {
return (await pathExists(dirPath)) && (await fsExtra.lstat(dirPath)).isDirectory();
return (await pathExists(dirPath)) && (await lstat(dirPath)).isDirectory();
}
// const testFailFsOperation = isDev;
@ -402,6 +402,22 @@ export async function readVideoTs(videoTsPath) {
return ret;
}
export async function readDirRecursively(dirPath) {
const files = await readdir(dirPath, { recursive: true });
const ret = (await pMap(files, async (path) => {
if (['.DS_Store'].includes(basename(path))) return [];
const absPath = join(dirPath, path);
const fileStat = await lstat(absPath); // readdir also returns directories...
if (!fileStat.isFile()) return [];
return [absPath];
}, { concurrency: 5 })).flat();
if (ret.length === 0) throw new Error('No files found in folder');
return ret;
}
export function getImportProjectType(filePath) {
if (filePath.endsWith('Summary.txt')) return 'dv-analyzer-summary-txt';
const edlFormatForExtension = { csv: 'csv', pbf: 'pbf', edl: 'mplayer', cue: 'cue', xml: 'xmeml', fcpxml: 'fcpxml' };