@ -18,6 +18,13 @@ const Row = (props) => <Table.Row height="auto" paddingY={12} {...props} />;
/ / e s l i n t - d i s a b l e - n e x t - l i n e r e a c t / j s x - p r o p s - n o - s p r e a d i n g
const KeyCell = ( props ) => < Table.TextCell textProps = { { whiteSpace : 'auto' } } { ...props } / > ;
const Header = ( { title } ) => (
< Row backgroundColor = "rgba(0,0,0,0.05)" >
< Table.TextCell > { title } < / Table.TextCell >
< Table.TextCell / >
< / Row >
) ;
const Settings = memo ( ( {
onTunerRequested ,
onKeyboardShortcutsDialogRequested ,
@ -64,9 +71,15 @@ const Settings = memo(({
< / Row >
< Row >
< KeyCell > { t ( 'Keyboard & mouse shortcuts' ) } < / KeyCell >
< KeyCell >
{ t ( 'Choose cutting mode: Remove or keep selected segments from video when exporting?' ) } < br / >
< b > { t ( 'Keep' ) } < / b > : { t ( 'The video inside segments will be kept, while the video outside will be discarded.' ) } < br / >
< b > { t ( 'Remove' ) } < / b > : { t ( 'The video inside segments will be discarded, while the video surrounding them will be kept.' ) }
< / KeyCell >
< Table.TextCell >
< Button iconBefore = { < FaKeyboard / > } onClick = { onKeyboardShortcutsDialogRequested } > { t ( 'Keyboard & mouse shortcuts' ) } < / Button >
< Button iconBefore = { FaYinYang } appearance = { invertCutSegments ? 'default' : 'primary' } intent = "success" onClick = { ( ) => setInvertCutSegments ( ( v ) => ! v ) } >
{ invertCutSegments ? t ( 'Remove' ) : t ( 'Keep' ) }
< / Button >
< / Table.TextCell >
< / Row >
@ -105,77 +118,95 @@ const Settings = memo(({
< / Table.TextCell >
< / Row >
{ ! isMasBuild && (
< Row >
< KeyCell >
{ t ( 'Custom FFmpeg directory (experimental)' ) } < br / >
{ 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.' ) }
< / KeyCell >
< Table.TextCell >
< Button iconBefore = { CogIcon } onClick = { changeCustomFfPath } >
{ customFfPath ? t ( 'Using external ffmpeg' ) : t ( 'Using built-in ffmpeg' ) }
< / Button >
< div > { customFfPath } < / div >
< / Table.TextCell >
< / Row >
) }
< Header title = { t ( 'Keyboard, mouse and input' ) } / >
< Row >
< KeyCell > { t ( ' Set file modification date/time of output files to: ') } < / KeyCell >
< KeyCell > { t ( 'Keyboard & mouse shortcuts' ) } < / KeyCell >
< Table.TextCell >
< Button iconBefore = { enableTransferTimestamps ? DocumentIcon : TimeIcon } onClick = { ( ) => setEnableTransferTimestamps ( ( v ) => ! v ) } >
{ enableTransferTimestamps ? t ( 'Source file\'s time' ) : t ( 'Current time' ) }
< / Button >
< Button iconBefore = { < FaKeyboard / > } onClick = { onKeyboardShortcutsDialogRequested } > { t ( 'Keyboard & mouse shortcuts' ) } < / Button >
< / Table.TextCell >
< / Row >
< Row >
< KeyCell >
{ t ( 'Keyframe cut mode' ) } < br / >
< b > { t ( 'Keyframe cut' ) } < / b > : { t ( 'Cut at the nearest keyframe (not accurate time.) Equiv to' ) } < i > ffmpeg - ss - i ... < / i > < br / >
< b > { t ( 'Normal cut' ) } < / b > : { t ( 'Accurate time but could leave an empty portion at the beginning of the video. Equiv to' ) } < i > ffmpeg - i - ss ... < / i > < br / >
< / KeyCell >
< KeyCell > { t ( 'Mouse wheel zoom modifier key' ) } < / KeyCell >
< Table.TextCell >
< Button iconBefore = { keyframeCut ? KeyIcon : undefined } onClick = { ( ) => toggleKeyframeCut ( ) } >
{ keyframeCut ? t ( 'Keyframe cut' ) : t ( 'Normal cut' ) }
< / Button >
< Select value = { mouseWheelZoomModifierKey } onChange = { ( e ) => setMouseWheelZoomModifierKey ( e . target . value ) } >
{ Object . keys ( keyMap ) . map ( ( key ) => (
< option key = { key } value = { key } > { key } < / option >
) ) }
< / Select >
< / Table.TextCell >
< / Row >
< Row >
< KeyCell >
{ t ( 'Choose cutting mode: Remove or keep selected segments from video when exporting?' ) } < br / >
< b > { t ( 'Keep' ) } < / b > : { t ( 'The video inside segments will be kept, while the video outside will be discarded.' ) } < br / >
< b > { t ( 'Remove' ) } < / b > : { t ( 'The video inside segments will be discarded, while the video surrounding them will be kept.' ) }
< / KeyCell >
< KeyCell > { t ( 'Timeline trackpad/wheel sensitivity' ) } < / KeyCell >
< Table.TextCell >
< Button iconBefore = { FaYinYang } appearance = { invertCutSegments ? 'default' : 'primary' } intent = "success" onClick = { ( ) => setInvertCutSegments ( ( v ) => ! v ) } >
{ invertCutSegments ? t ( 'Remove' ) : t ( 'Keep' ) }
< Button onClick = { ( ) => onTunerRequested ( 'wheelSensitivity' ) } > { t ( 'Change value' ) } < / Button >
< / Table.TextCell >
< / Row >
< Row >
< KeyCell > { t ( 'Timeline keyboard seek speed' ) } < / KeyCell >
< Table.TextCell >
< Button onClick = { ( ) => onTunerRequested ( 'keyboardNormalSeekSpeed' ) } > { t ( 'Change value' ) } < / Button >
< / Table.TextCell >
< / Row >
< Row >
< KeyCell > { t ( 'Timeline keyboard seek acceleration' ) } < / KeyCell >
< Table.TextCell >
< Button onClick = { ( ) => onTunerRequested ( 'keyboardSeekAccFactor' ) } > { t ( 'Change value' ) } < / Button >
< / Table.TextCell >
< / Row >
< Row >
< KeyCell > { t ( 'Invert timeline trackpad/wheel direction?' ) } < / KeyCell >
< Table.TextCell >
< Checkbox
label = { t ( 'Invert direction' ) }
checked = { invertTimelineScroll }
onChange = { e => setInvertTimelineScroll ( e . target . checked ) }
/ >
< / Table.TextCell >
< / Row >
< Header title = { t ( 'Options affecting exported files' ) } / >
< Row >
< KeyCell > { t ( 'Set file modification date/time of output files to:' ) } < / KeyCell >
< Table.TextCell >
< Button iconBefore = { enableTransferTimestamps ? DocumentIcon : TimeIcon } onClick = { ( ) => setEnableTransferTimestamps ( ( v ) => ! v ) } >
{ enableTransferTimestamps ? t ( 'Source file\'s time' ) : t ( 'Current time' ) }
< / Button >
< / Table.TextCell >
< / Row >
< Row >
< KeyCell >
{ t ( 'Extract unprocessable tracks to separate files or discard them?' ) } < br / >
{ 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 ( 'Keyframe cut mode' ) } < br / >
< b > { t ( 'Keyframe cut' ) } < / b > : { t ( 'Cut at the nearest keyframe (not accurate time.) Equiv to' ) } < i > ffmpeg - ss - i ... < / i > < br / >
< b > { t ( 'Normal cut' ) } < / b > : { t ( 'Accurate time but could leave an empty portion at the beginning of the video. Equiv to' ) } < i > ffmpeg - i - ss ... < / i > < br / >
< / KeyCell >
< Table.TextCell >
< AutoExportToggler / >
< Button iconBefore = { keyframeCut ? KeyIcon : undefined } onClick = { ( ) => toggleKeyframeCut ( ) } >
{ keyframeCut ? t ( 'Keyframe cut' ) : t ( 'Normal cut' ) }
< / Button >
< / Table.TextCell >
< / Row >
< Row >
< KeyCell > { t ( 'Enable experimental ffmpeg features flag?' ) } < / KeyCell >
< KeyCell > { t ( ' Overwrite files when exporting, if a file with the same name as the output file name exists ?') } < / KeyCell >
< Table.TextCell >
< Checkbox
label = { t ( 'Experimental flag' ) }
checked = { ffmpegExperimental }
onChange = { e => setFfmpegExperimental ( e . target . checked ) }
label = { t ( ' Overwrite existing files ') }
checked = { enableOverwriteOutput }
onChange = { e => set EnableOverwriteOutput ( e . target . checked ) }
/ >
< / Table.TextCell >
< / Row >
< Header title = { t ( 'Snapshots and frame extraction' ) } / >
< Row >
< KeyCell >
{ t ( 'Snapshot capture format' ) }
@ -203,7 +234,7 @@ const Settings = memo(({
< / Row >
< Row >
< KeyCell > { t ( ' Extract video frames file name ') } < / KeyCell >
< KeyCell > { t ( ' File names of extracted video frames ') } < / KeyCell >
< Table.TextCell >
< Button iconBefore = { captureFrameFileNameFormat === 'timestamp' ? TimeIcon : NumericalIcon } onClick = { ( ) => setCaptureFrameFileNameFormat ( ( existing ) => ( existing === 'timestamp' ? 'index' : 'timestamp' ) ) } >
{ captureFrameFileNameFormat === 'timestamp' ? t ( 'Frame timestamp' ) : t ( 'File number' ) }
@ -220,45 +251,26 @@ const Settings = memo(({
< / Table.TextCell >
< / Row >
< Row >
< KeyCell > { t ( 'Mouse wheel zoom modifier key' ) } < / KeyCell >
< Table.TextCell >
< Select value = { mouseWheelZoomModifierKey } onChange = { ( e ) => setMouseWheelZoomModifierKey ( e . target . value ) } >
{ Object . keys ( keyMap ) . map ( ( key ) => (
< option key = { key } value = { key } > { key } < / option >
) ) }
< / Select >
< / Table.TextCell >
< / Row >
< Row >
< KeyCell > { t ( 'Timeline trackpad/wheel sensitivity' ) } < / KeyCell >
< Table.TextCell >
< Button onClick = { ( ) => onTunerRequested ( 'wheelSensitivity' ) } > { t ( 'Change value' ) } < / Button >
< / Table.TextCell >
< / Row >
< Row >
< KeyCell > { t ( 'Timeline keyboard seek speed' ) } < / KeyCell >
< Table.TextCell >
< Button onClick = { ( ) => onTunerRequested ( 'keyboardNormalSeekSpeed' ) } > { t ( 'Change value' ) } < / Button >
< / Table.TextCell >
< / Row >
< Header title = { t ( 'Prompts and dialogs' ) } / >
< Row >
< KeyCell > { t ( ' Timeline keyboard seek acceleration ') } < / KeyCell >
< KeyCell > { t ( 'Hide informational notifications?' ) } < / KeyCell >
< Table.TextCell >
< Button onClick = { ( ) => onTunerRequested ( 'keyboardSeekAccFactor' ) } > { t ( 'Change value' ) } < / Button >
< Checkbox
label = { t ( 'Check to hide notifications' ) }
checked = { ! ! hideNotifications }
onChange = { e => setHideNotifications ( e . target . checked ? 'all' : undefined ) }
/ >
< / Table.TextCell >
< / Row >
< Row >
< KeyCell > { t ( ' Invert timeline trackpad/wheel directio n?') } < / KeyCell >
< KeyCell > { t ( 'Ask about what to do when opening a new file when another file is already already open?' ) } < / KeyCell >
< Table.TextCell >
< Checkbox
label = { t ( ' Invert directio n') }
checked = { invertTimelineScroll }
onChange = { e => set InvertTimelineScroll ( e . target . checked ) }
label = { t ( 'Ask on file open' ) }
checked = { enableAskForFileOpenAction }
onChange = { e => setEnableAskForFileOpenAction ( e . target . checked ) }
/ >
< / Table.TextCell >
< / Row >
@ -285,13 +297,30 @@ const Settings = memo(({
< / Table.TextCell >
< / Row >
< Header title = { t ( 'Advanced options' ) } / >
{ ! isMasBuild && (
< Row >
< KeyCell >
{ t ( 'Custom FFmpeg directory (experimental)' ) } < br / >
{ 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.' ) }
< / KeyCell >
< Table.TextCell >
< Button iconBefore = { CogIcon } onClick = { changeCustomFfPath } >
{ customFfPath ? t ( 'Using external ffmpeg' ) : t ( 'Using built-in ffmpeg' ) }
< / Button >
< div > { customFfPath } < / div >
< / Table.TextCell >
< / Row >
) }
< Row >
< KeyCell > { t ( 'Overwrite files when exporting, if a file with the same name as the output file name exists?' ) } < / KeyCell >
< KeyCell > { t ( ' Enable experimental ffmpeg features flag ?') } < / KeyCell >
< Table.TextCell >
< Checkbox
label = { t ( 'Overwrite existing files' ) }
checked = { enableOverwriteOutput }
onChange = { e => setEnableOverwriteOutput ( e . target . checked ) }
label = { t ( ' Experimental flag ') }
checked = { ffmpegExperimental }
onChange = { e => set FfmpegExperimental ( e . target . checked ) }
/ >
< / Table.TextCell >
< / Row >
@ -318,26 +347,13 @@ const Settings = memo(({
< / Table.TextCell >
< / Row >
< Row >
< KeyCell > { t ( 'Hide informational notifications?' ) } < / KeyCell >
< Table.TextCell >
< Checkbox
label = { t ( 'Check to hide notifications' ) }
checked = { ! ! hideNotifications }
onChange = { e => setHideNotifications ( e . target . checked ? 'all' : undefined ) }
/ >
< / Table.TextCell >
< / Row >
< Row >
< KeyCell > { t ( 'Ask about what to do when opening a new file when another file is already already open?' ) } < / KeyCell >
< KeyCell >
{ t ( 'Extract unprocessable tracks to separate files or discard them?' ) } < br / >
{ 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)' ) }
< / KeyCell >
< Table.TextCell >
< Checkbox
label = { t ( 'Ask on file open' ) }
checked = { enableAskForFileOpenAction }
onChange = { e => setEnableAskForFileOpenAction ( e . target . checked ) }
/ >
< AutoExportToggler / >
< / Table.TextCell >
< / Row >
< / >