1
0
mirror of https://github.com/mifi/lossless-cut.git synced 2024-11-22 18:32:34 +01:00

reduce the risk of overwriting source files

This commit is contained in:
Mikael Finstad 2024-01-31 22:36:55 +07:00
parent bc04a4f2a4
commit 35e12d3a3d
No known key found for this signature in database
GPG Key ID: 25AB36E3E81CBC26
3 changed files with 42 additions and 29 deletions

View File

@ -1176,8 +1176,8 @@ function App() {
console.log('outSegTemplateOrDefault', outSegTemplateOrDefault); console.log('outSegTemplateOrDefault', outSegTemplateOrDefault);
const { outSegFileNames, outSegError } = generateOutSegFileNames({ segments: segmentsToExport, template: outSegTemplateOrDefault }); const { outSegFileNames, outSegProblems } = generateOutSegFileNames({ segments: segmentsToExport, template: outSegTemplateOrDefault });
if (outSegError != null) { if (outSegProblems.error != null) {
console.warn('Output segments file name invalid, using default instead', outSegFileNames); console.warn('Output segments file name invalid, using default instead', outSegFileNames);
} }

View File

@ -23,13 +23,13 @@ const formatVariable = (variable) => `\${${variable}}`;
const extVar = formatVariable('EXT'); const extVar = formatVariable('EXT');
const OutSegTemplateEditor = memo(({ outSegTemplate, setOutSegTemplate, generateOutSegFileNames, currentSegIndexSafe, getOutSegError }) => { const OutSegTemplateEditor = memo(({ outSegTemplate, setOutSegTemplate, generateOutSegFileNames, currentSegIndexSafe }) => {
const { safeOutputFileName, toggleSafeOutputFileName, outputFileNameMinZeroPadding, setOutputFileNameMinZeroPadding } = useUserSettings(); const { safeOutputFileName, toggleSafeOutputFileName, outputFileNameMinZeroPadding, setOutputFileNameMinZeroPadding } = useUserSettings();
const [text, setText] = useState(outSegTemplate); const [text, setText] = useState(outSegTemplate);
const [debouncedText] = useDebounce(text, 500); const [debouncedText] = useDebounce(text, 500);
const [validText, setValidText] = useState(); const [validText, setValidText] = useState();
const [error, setError] = useState(); const [outSegProblems, setOutSegProblems] = useState({ error: undefined, sameAsInputFileNameWarning: false });
const [outSegFileNames, setOutSegFileNames] = useState(); const [outSegFileNames, setOutSegFileNames] = useState();
const [shown, setShown] = useState(); const [shown, setShown] = useState();
const inputRef = useRef(); const inputRef = useRef();
@ -42,22 +42,16 @@ const OutSegTemplateEditor = memo(({ outSegTemplate, setOutSegTemplate, generate
if (debouncedText == null) return; if (debouncedText == null) return;
try { try {
const { outSegFileNames: newOutSegFileNames, outSegError } = generateOutSegFileNames({ template: debouncedText }); const outSegs = generateOutSegFileNames({ template: debouncedText });
setOutSegFileNames(newOutSegFileNames); setOutSegFileNames(outSegs.outSegFileNames);
if (outSegError) { setOutSegProblems(outSegs.outSegProblems);
setError(outSegError); setValidText(outSegs.outSegProblems.error == null ? debouncedText : undefined);
setValidText();
return;
}
setValidText(debouncedText);
setError();
} catch (err) { } catch (err) {
console.error(err); console.error(err);
setValidText(); setValidText();
setError(err.message); setOutSegProblems({ error: err.message });
} }
}, [debouncedText, generateOutSegFileNames, getOutSegError, t]); }, [debouncedText, generateOutSegFileNames, t]);
// eslint-disable-next-line no-template-curly-in-string // eslint-disable-next-line no-template-curly-in-string
const isMissingExtension = validText != null && !validText.endsWith(extVar); const isMissingExtension = validText != null && !validText.endsWith(extVar);
@ -80,8 +74,8 @@ const OutSegTemplateEditor = memo(({ outSegTemplate, setOutSegTemplate, generate
}, [setOutSegTemplate]); }, [setOutSegTemplate]);
const onHideClick = useCallback(() => { const onHideClick = useCallback(() => {
if (error == null) setShown(false); if (outSegProblems.error == null) setShown(false);
}, [error]); }, [outSegProblems.error]);
const onShowClick = useCallback(() => { const onShowClick = useCallback(() => {
if (!shown) setShown(true); if (!shown) setShown(true);
@ -89,7 +83,7 @@ const OutSegTemplateEditor = memo(({ outSegTemplate, setOutSegTemplate, generate
const onTextChange = useCallback((e) => setText(e.target.value), []); const onTextChange = useCallback((e) => setText(e.target.value), []);
const needToShow = shown || error != null; const needToShow = shown || outSegProblems.error != null || outSegProblems.sameAsInputFileNameWarning;
const onVariableClick = useCallback((variable) => { const onVariableClick = useCallback((variable) => {
const input = inputRef.current; const input = inputRef.current;
@ -132,7 +126,18 @@ const OutSegTemplateEditor = memo(({ outSegTemplate, setOutSegTemplate, generate
))} ))}
</div> </div>
{error != null && <div style={{ marginBottom: '1em' }}><ErrorIcon color="var(--red9)" size={14} verticalAlign="baseline" /> {error}</div>} {outSegProblems.error != null && (
<div style={{ marginBottom: '1em' }}>
<ErrorIcon color="var(--red9)" size={14} verticalAlign="baseline" /> {outSegProblems.error}
</div>
)}
{outSegProblems.error == null && outSegProblems.sameAsInputFileNameWarning && (
<div style={{ marginBottom: '1em' }}>
<WarningSignIcon verticalAlign="middle" color="var(--amber9)" />{' '}
{i18n.t('Output file name is the same as the source file name. This increases the risk of accidentally overwriting or deleting source files!')}
</div>
)}
{isMissingExtension && ( {isMissingExtension && (
<div style={{ marginBottom: '1em' }}> <div style={{ marginBottom: '1em' }}>

View File

@ -9,10 +9,11 @@ import { getSegmentTags, formatSegNum } from '../segments';
export const segNumVariable = 'SEG_NUM'; export const segNumVariable = 'SEG_NUM';
export const segSuffixVariable = 'SEG_SUFFIX'; export const segSuffixVariable = 'SEG_SUFFIX';
const { parse: parsePath, sep: pathSep, join: pathJoin, normalize: pathNormalize } = window.require('path'); const { parse: parsePath, sep: pathSep, join: pathJoin, normalize: pathNormalize, basename } = window.require('path');
function getOutSegError({ fileNames, filePath, outputDir, safeOutputFileName }) { function getOutSegProblems({ fileNames, filePath, outputDir, safeOutputFileName }) {
let error; let error;
let sameAsInputFileNameWarning = false;
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
for (const fileName of fileNames) { for (const fileName of fileNames) {
@ -49,6 +50,10 @@ function getOutSegError({ fileNames, filePath, outputDir, safeOutputFileName })
const shouldCheckPathLength = isWindows || isDev; const shouldCheckPathLength = isWindows || isDev;
const shouldCheckFileEnd = isWindows || isDev; const shouldCheckFileEnd = isWindows || isDev;
if (basename(filePath) === fileName) {
sameAsInputFileNameWarning = true; // just an extra warning in case sameAsInputPath doesn't work
}
if (fileName.length === 0) { if (fileName.length === 0) {
error = i18n.t('At least one resulting file name has no length'); error = i18n.t('At least one resulting file name has no length');
break; break;
@ -73,11 +78,14 @@ function getOutSegError({ fileNames, filePath, outputDir, safeOutputFileName })
} }
} }
if (error != null) return error; if (error == null && hasDuplicates(fileNames)) {
error = i18n.t('Output file name template results in duplicate file names (you are trying to export multiple files with the same name). You can fix this for example by adding the "{{segNumVariable}}" variable.', { segNumVariable });
}
if (hasDuplicates(fileNames)) return i18n.t('Output file name template results in duplicate file names (you are trying to export multiple files with the same name). You can fix this for example by adding the "{{segNumVariable}}" variable.', { segNumVariable }); return {
error,
return undefined; sameAsInputFileNameWarning,
};
} }
// This is used as a fallback and so it has to always generate unique file names // This is used as a fallback and so it has to always generate unique file names
@ -155,10 +163,10 @@ export function generateOutSegFileNames({ segments, template: desiredTemplate, f
let outSegFileNames = generate({ template: desiredTemplate, forceSafeOutputFileName: false }); let outSegFileNames = generate({ template: desiredTemplate, forceSafeOutputFileName: false });
const outSegError = getOutSegError({ fileNames: outSegFileNames, filePath, outputDir, safeOutputFileName }); const outSegProblems = getOutSegProblems({ fileNames: outSegFileNames, filePath, outputDir, safeOutputFileName });
if (outSegError != null) { if (outSegProblems.error != null) {
outSegFileNames = generate({ template: defaultOutSegTemplate, forceSafeOutputFileName: true }); outSegFileNames = generate({ template: defaultOutSegTemplate, forceSafeOutputFileName: true });
} }
return { outSegFileNames, outSegError }; return { outSegFileNames, outSegProblems };
} }