diff --git a/src/App.jsx b/src/App.jsx index 010eac73..8eabe768 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -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); } diff --git a/src/components/OutSegTemplateEditor.jsx b/src/components/OutSegTemplateEditor.jsx index 224e1add..e55f0c2e 100644 --- a/src/components/OutSegTemplateEditor.jsx +++ b/src/components/OutSegTemplateEditor.jsx @@ -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 ))} - {error != null &&
{error}
} + {outSegProblems.error != null && ( +
+ {outSegProblems.error} +
+ )} + + {outSegProblems.error == null && outSegProblems.sameAsInputFileNameWarning && ( +
+ {' '} + {i18n.t('Output file name is the same as the source file name. This increases the risk of accidentally overwriting or deleting source files!')} +
+ )} {isMissingExtension && (
diff --git a/src/util/outputNameTemplate.js b/src/util/outputNameTemplate.js index ca12e9a5..cebcafa1 100644 --- a/src/util/outputNameTemplate.js +++ b/src/util/outputNameTemplate.js @@ -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 (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 }); + 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 }); + } - 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 }; }