diff --git a/src/renderer/src/hooks/useUserSettingsRoot.ts b/src/renderer/src/hooks/useUserSettingsRoot.ts index b18beb6a..3bc26171 100644 --- a/src/renderer/src/hooks/useUserSettingsRoot.ts +++ b/src/renderer/src/hooks/useUserSettingsRoot.ts @@ -10,6 +10,7 @@ import { mySpring, emitter as animationsEmitter } from '../animations'; const { configStore } = window.require('@electron/remote').require('./index.js'); const { systemPreferences } = window.require('@electron/remote'); +const { lstat } = window.require('fs/promises'); const animationSettings = systemPreferences.getAnimationSettings(); @@ -56,6 +57,31 @@ export default function useUserSettingsRoot() { useEffect(() => safeSetConfig({ captureFormat }), [captureFormat]); const [customOutDir, setCustomOutDir] = useState(safeGetConfigInitial('customOutDir')); useEffect(() => safeSetConfig({ customOutDir }), [customOutDir]); + + // Validate customOutDir on mount - reset if it's not a valid directory + // This prevents issues where customOutDir was set to a file path instead of a directory + // https://github.com/mifi/lossless-cut/issues/XXXX + useEffect(() => { + async function validateCustomOutDir() { + if (customOutDir == null) return; + + try { + const stat = await lstat(customOutDir); + if (!stat.isDirectory()) { + console.warn('customOutDir is not a directory, resetting:', customOutDir); + setCustomOutDir(undefined); + } + } catch (err) { + // If we can't stat the path (doesn't exist, permission denied, etc.), reset it + console.warn('customOutDir is invalid, resetting:', customOutDir, err); + setCustomOutDir(undefined); + } + } + + validateCustomOutDir(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); // Only run on mount + const [keyframeCut, setKeyframeCut] = useState(safeGetConfigInitial('keyframeCut')); useEffect(() => safeSetConfig({ keyframeCut }), [keyframeCut]); const [preserveMetadata, setPreserveMetadata] = useState(safeGetConfigInitial('preserveMetadata'));