import React, { memo, useCallback, useMemo } from 'react'; import { FaYinYang, FaKeyboard } from 'react-icons/fa'; import { GlobeIcon, CleanIcon, CogIcon, Button, NumericalIcon, FolderCloseIcon, DocumentIcon, TimeIcon } from 'evergreen-ui'; import { useTranslation } from 'react-i18next'; import CaptureFormatButton from './CaptureFormatButton'; import AutoExportToggler from './AutoExportToggler'; import Switch from './Switch'; import useUserSettings from '../hooks/useUserSettings'; import { askForFfPath } from '../dialogs'; import { isMasBuild, isStoreBuild } from '../util'; import { langNames } from '../util/constants'; import styles from './Settings.module.css'; import Select from './Select'; import { getModifierKeyNames } from '../hooks/useTimelineScroll'; // eslint-disable-next-line react/jsx-props-no-spreading const Row = (props) => ; // eslint-disable-next-line react/jsx-props-no-spreading const KeyCell = (props) => ; const Header = ({ title }) => ( {title} ); const detailsStyle = { opacity: 0.75, fontSize: '.9em', marginTop: '.3em' }; const Settings = memo(({ onTunerRequested, onKeyboardShortcutsDialogRequested, askForCleanupChoices, toggleStoreProjectInWorkingDir, }) => { const { t } = useTranslation(); const { customOutDir, changeOutDir, keyframeCut, toggleKeyframeCut, timecodeFormat, setTimecodeFormat, invertCutSegments, setInvertCutSegments, askBeforeClose, setAskBeforeClose, enableAskForImportChapters, setEnableAskForImportChapters, enableAskForFileOpenAction, setEnableAskForFileOpenAction, autoSaveProjectFile, setAutoSaveProjectFile, invertTimelineScroll, setInvertTimelineScroll, language, setLanguage, ffmpegExperimental, setFfmpegExperimental, hideNotifications, setHideNotifications, autoLoadTimecode, setAutoLoadTimecode, enableAutoHtml5ify, setEnableAutoHtml5ify, customFfPath, setCustomFfPath, storeProjectInWorkingDir, enableOverwriteOutput, setEnableOverwriteOutput, mouseWheelZoomModifierKey, setMouseWheelZoomModifierKey, captureFrameMethod, setCaptureFrameMethod, captureFrameQuality, setCaptureFrameQuality, captureFrameFileNameFormat, setCaptureFrameFileNameFormat, enableNativeHevc, setEnableNativeHevc, enableUpdateCheck, setEnableUpdateCheck, allowMultipleInstances, setAllowMultipleInstances, preferStrongColors, setPreferStrongColors, treatInputFileModifiedTimeAsStart, setTreatInputFileModifiedTimeAsStart, treatOutputFileModifiedTimeAsStart, setTreatOutputFileModifiedTimeAsStart } = useUserSettings(); const onLangChange = useCallback((e) => { const { value } = e.target; const l = value !== '' ? value : undefined; setLanguage(l); }, [setLanguage]); const timecodeFormatOptions = useMemo(() => ({ frameCount: t('Frame counts'), timecodeWithDecimalFraction: t('Millisecond fractions'), timecodeWithFramesFraction: t('Frame fractions'), }), [t]); const onTimecodeFormatClick = useCallback(() => { const keys = Object.keys(timecodeFormatOptions); let index = keys.indexOf(timecodeFormat); if (index === -1 || index >= keys.length - 1) index = 0; else index += 1; setTimecodeFormat(keys[index]); }, [setTimecodeFormat, timecodeFormat, timecodeFormatOptions]); const changeCustomFfPath = useCallback(async () => { const newCustomFfPath = await askForFfPath(customFfPath); setCustomFfPath(newCustomFfPath); }, [customFfPath, setCustomFfPath]); return ( <>
{t('Hover mouse over buttons in the main interface to see which function they have')}
App language {t('Choose cutting mode: Remove or keep selected segments from video when exporting?')}
{t('Keep')}: {t('The video inside segments will be kept, while the video outside will be discarded.')}
{t('Remove')}: {t('The video inside segments will be discarded, while the video surrounding them will be kept.')}
{t('Working directory')}
{t('This is where working files and exported files are stored.')}
{t('Auto save project file?')}
{t('Store project file (.llc) in the working directory or next to loaded media file?')}
{t('Keyboard & mouse shortcuts')}
{t('Mouse wheel zoom modifier key')} {t('Timeline trackpad/wheel sensitivity')} {t('Timeline keyboard seek speed')} {t('Timeline keyboard seek acceleration')} {t('Invert timeline trackpad/wheel direction?')}
{t('Set file modification date/time of output files to:')}
{t('Treat source file modification date/time as:')} {t('Keyframe cut mode')}
{keyframeCut ? ( <> {t('Cut at the nearest keyframe (not accurate time.) Equiv to')}:
ffmpeg -ss -i ... ) : ( <> {t('Accurate time but could leave an empty portion at the beginning of the video. Equiv to')}:
ffmpeg -i -ss ... )}
{t('Overwrite files when exporting, if a file with the same name as the output file name exists?')} {t('Cleanup files after export?')}
{t('Snapshot capture format')}
{t('Snapshot capture method')}
{t('FFmpeg capture method might sometimes capture more correct colors, but the captured snapshot might be off by one or more frames, relative to the preview.')}
{t('Snapshot capture quality')} {t('File names of extracted video frames')} {t('In timecode show')}
{t('Show informational notifications')}
{t('Ask about what to do when opening a new file when another file is already already open?')} {t('Ask for confirmation when closing app or file?')} {t('Ask about importing chapters from opened file?')}
{t('Prefer strong colors')}
{!isMasBuild && ( {t('Custom FFmpeg directory (experimental)')}
{t('This allows you to specify custom FFmpeg and FFprobe binaries to use. Make sure the "ffmpeg" and "ffprobe" executables exist in the same directory, and then select the directory.')}
)} {!isStoreBuild && ( {t('Check for updates on startup?')} )} {t('Allow multiple instances of LosslessCut to run concurrently? (experimental)')} {t('Enable HEVC / H265 hardware decoding (you may need to turn this off if you have problems with HEVC files)')} {t('Enable experimental ffmpeg features flag?')} {t('Auto load timecode from file as an offset in the timeline?')} {t('Try to automatically convert to supported format when opening unsupported file?')} {t('Extract unprocessable tracks to separate files or discard them?')}
{t('(data tracks such as GoPro GPS, telemetry etc. are not copied over by default because ffmpeg cannot cut them, thus they will cause the media duration to stay the same after cutting video/audio)')}
{t('Settings')} {t('Current setting')}
{customOutDir}
toggleKeyframeCut()} /> setCaptureFrameQuality(Math.max(Math.min(1, parseInt(e.target.value, 10) / 1000)), 0)} />
{Math.round(captureFrameQuality * 100)}%
setHideNotifications(v ? undefined : 'all')} />
{customFfPath}
); }); export default Settings;