1
0
mirror of https://github.com/mifi/lossless-cut.git synced 2024-11-22 10:22:31 +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);
const { outSegFileNames, outSegError } = generateOutSegFileNames({ segments: segmentsToExport, template: outSegTemplateOrDefault });
if (outSegError != null) {
const { outSegFileNames, outSegProblems } = generateOutSegFileNames({ segments: segmentsToExport, template: outSegTemplateOrDefault });
if (outSegProblems.error != null) {
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 OutSegTemplateEditor = memo(({ outSegTemplate, setOutSegTemplate, generateOutSegFileNames, currentSegIndexSafe, getOutSegError }) => {
const OutSegTemplateEditor = memo(({ outSegTemplate, setOutSegTemplate, generateOutSegFileNames, currentSegIndexSafe }) => {
const { safeOutputFileName, toggleSafeOutputFileName, outputFileNameMinZeroPadding, setOutputFileNameMinZeroPadding } = useUserSettings();
const [text, setText] = useState(outSegTemplate);
const [debouncedText] = useDebounce(text, 500);
const [validText, setValidText] = useState();
const [error, setError] = useState();
const [outSegProblems, setOutSegProblems] = useState({ error: undefined, sameAsInputFileNameWarning: false });
const [outSegFileNames, setOutSegFileNames] = useState();
const [shown, setShown] = useState();
const inputRef = useRef();
@ -42,22 +42,16 @@ const OutSegTemplateEditor = memo(({ outSegTemplate, setOutSegTemplate, generate
if (debouncedText == null) return;
try {
const { outSegFileNames: newOutSegFileNames, outSegError } = generateOutSegFileNames({ template: debouncedText });
setOutSegFileNames(newOutSegFileNames);
if (outSegError) {
setError(outSegError);
setValidText();
return;
}
setValidText(debouncedText);
setError();
const outSegs = generateOutSegFileNames({ template: debouncedText });
setOutSegFileNames(outSegs.outSegFileNames);
setOutSegProblems(outSegs.outSegProblems);
setValidText(outSegs.outSegProblems.error == null ? debouncedText : undefined);
} catch (err) {
console.error(err);
setValidText();
setError(err.message);
setOutSegProblems({ error: err.message });
}
}, [debouncedText, generateOutSegFileNames, getOutSegError, t]);
}, [debouncedText, generateOutSegFileNames, t]);
// eslint-disable-next-line no-template-curly-in-string
const isMissingExtension = validText != null && !validText.endsWith(extVar);
@ -80,8 +74,8 @@ const OutSegTemplateEditor = memo(({ outSegTemplate, setOutSegTemplate, generate
}, [setOutSegTemplate]);
const onHideClick = useCallback(() => {
if (error == null) setShown(false);
}, [error]);
if (outSegProblems.error == null) setShown(false);
}, [outSegProblems.error]);
const onShowClick = useCallback(() => {
if (!shown) setShown(true);
@ -89,7 +83,7 @@ const OutSegTemplateEditor = memo(({ outSegTemplate, setOutSegTemplate, generate
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 input = inputRef.current;
@ -132,7 +126,18 @@ const OutSegTemplateEditor = memo(({ outSegTemplate, setOutSegTemplate, generate
))}
</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 && (
<div style={{ marginBottom: '1em' }}>

View File

@ -9,10 +9,11 @@ import { getSegmentTags, formatSegNum } from '../segments';
export const segNumVariable = 'SEG_NUM';
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 sameAsInputFileNameWarning = false;
// eslint-disable-next-line no-restricted-syntax
for (const fileName of fileNames) {
@ -49,6 +50,10 @@ function getOutSegError({ fileNames, filePath, outputDir, safeOutputFileName })
const shouldCheckPathLength = 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) {
error = i18n.t('At least one resulting file name has no length');
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 undefined;
return {
error,
sameAsInputFileNameWarning,
};
}
// 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 });
const outSegError = getOutSegError({ fileNames: outSegFileNames, filePath, outputDir, safeOutputFileName });
if (outSegError != null) {
const outSegProblems = getOutSegProblems({ fileNames: outSegFileNames, filePath, outputDir, safeOutputFileName });
if (outSegProblems.error != null) {
outSegFileNames = generate({ template: defaultOutSegTemplate, forceSafeOutputFileName: true });
}
return { outSegFileNames, outSegError };
return { outSegFileNames, outSegProblems };
}