Split timeline by byte size

closes #1219
pull/2350/head
Mikael Finstad 1 year ago
parent 250218bf5f
commit 250218e2d0
No known key found for this signature in database
GPG Key ID: 25AB36E3E81CBC26

@ -266,6 +266,12 @@ export default ({ app, mainWindow, newVersion, isStoreBuild }: {
mainWindow.webContents.send('createFixedDurationSegments'); mainWindow.webContents.send('createFixedDurationSegments');
}, },
}, },
{
label: esc(t('Create fixed byte sized segments')),
click() {
mainWindow.webContents.send('createFixedByteSizedSegments');
},
},
{ {
label: esc(t('Create random segments')), label: esc(t('Create random segments')),
click() { click() {

@ -335,8 +335,8 @@ function App() {
}, [isFileOpened]); }, [isFileOpened]);
const { const {
cutSegments, cutSegmentsHistory, createSegmentsFromKeyframes, shuffleSegments, detectBlackScenes, detectSilentScenes, detectSceneChanges, removeCutSegment, invertAllSegments, fillSegmentsGaps, combineOverlappingSegments, combineSelectedSegments, shiftAllSegmentTimes, alignSegmentTimesToKeyframes, updateSegOrder, updateSegOrders, reorderSegsByStartTime, addSegment, setCutStart, setCutEnd, onLabelSegment, splitCurrentSegment, focusSegmentAtCursor, createNumSegments, createFixedDurationSegments, createRandomSegments, haveInvalidSegs, currentSegIndexSafe, currentCutSeg, inverseCutSegments, clearSegments, loadCutSegments, isSegmentSelected, setCutTime, setCurrentSegIndex, onLabelSelectedSegments, deselectAllSegments, selectAllSegments, selectOnlyCurrentSegment, toggleCurrentSegmentSelected, invertSelectedSegments, removeSelectedSegments, setDeselectedSegmentIds, onSelectSegmentsByLabel, onSelectSegmentsByExpr, onMutateSegmentsByExpr, toggleSegmentSelected, selectOnlySegment, selectedSegments, selectedSegmentsOrInverse, nonFilteredSegmentsOrInverse, segmentsToExport, duplicateCurrentSegment, duplicateSegment, updateSegAtIndex, findSegmentsAtCursor, cutSegments, cutSegmentsHistory, createSegmentsFromKeyframes, shuffleSegments, detectBlackScenes, detectSilentScenes, detectSceneChanges, removeCutSegment, invertAllSegments, fillSegmentsGaps, combineOverlappingSegments, combineSelectedSegments, shiftAllSegmentTimes, alignSegmentTimesToKeyframes, updateSegOrder, updateSegOrders, reorderSegsByStartTime, addSegment, setCutStart, setCutEnd, onLabelSegment, splitCurrentSegment, focusSegmentAtCursor, createNumSegments, createFixedDurationSegments, createFixedByteSizedSegments, createRandomSegments, haveInvalidSegs, currentSegIndexSafe, currentCutSeg, inverseCutSegments, clearSegments, loadCutSegments, isSegmentSelected, setCutTime, setCurrentSegIndex, onLabelSelectedSegments, deselectAllSegments, selectAllSegments, selectOnlyCurrentSegment, toggleCurrentSegmentSelected, invertSelectedSegments, removeSelectedSegments, setDeselectedSegmentIds, onSelectSegmentsByLabel, onSelectSegmentsByExpr, onMutateSegmentsByExpr, toggleSegmentSelected, selectOnlySegment, selectedSegments, selectedSegmentsOrInverse, nonFilteredSegmentsOrInverse, segmentsToExport, duplicateCurrentSegment, duplicateSegment, updateSegAtIndex, findSegmentsAtCursor,
} = useSegments({ filePath, workingRef, setWorking, setProgress, videoStream: activeVideoStream, duration, getRelevantTime, maxLabelLength, checkFileOpened, invertCutSegments, segmentsToChaptersOnly, timecodePlaceholder, parseTimecode, appendFfmpegCommandLog, durationSafe }); } = useSegments({ filePath, workingRef, setWorking, setProgress, videoStream: activeVideoStream, duration, getRelevantTime, maxLabelLength, checkFileOpened, invertCutSegments, segmentsToChaptersOnly, timecodePlaceholder, parseTimecode, appendFfmpegCommandLog, durationSafe, mainFileMeta });
const { getEdlFilePath, projectFileSavePath, getProjectFileSavePath } = useSegmentsAutoSave({ autoSaveProjectFile, storeProjectInWorkingDir, filePath, customOutDir, cutSegments }); const { getEdlFilePath, projectFileSavePath, getProjectFileSavePath } = useSegmentsAutoSave({ autoSaveProjectFile, storeProjectInWorkingDir, filePath, customOutDir, cutSegments });
@ -1993,6 +1993,7 @@ function App() {
combineSelectedSegments, combineSelectedSegments,
createFixedDurationSegments, createFixedDurationSegments,
createNumSegments, createNumSegments,
createFixedByteSizedSegments,
createRandomSegments, createRandomSegments,
alignSegmentTimesToKeyframes, alignSegmentTimesToKeyframes,
shuffleSegments, shuffleSegments,
@ -2049,7 +2050,7 @@ function App() {
}; };
return ret; return ret;
}, [toggleLoopSelectedSegments, pause, timelineToggleComfortZoom, captureSnapshot, captureSnapshotAsCoverArt, setCutStart, setCutEnd, cleanupFilesDialog, splitCurrentSegment, focusSegmentAtCursor, increaseRotation, goToTimecode, jumpCutStart, jumpCutEnd, jumpTimelineStart, jumpTimelineEnd, batchOpenSelectedFile, closeBatch, addSegment, duplicateCurrentSegment, onExportPress, extractCurrentSegmentFramesAsImages, extractSelectedSegmentsFramesAsImages, reorderSegsByStartTime, invertAllSegments, fillSegmentsGaps, combineOverlappingSegments, combineSelectedSegments, createFixedDurationSegments, createNumSegments, createRandomSegments, alignSegmentTimesToKeyframes, shuffleSegments, clearSegments, toggleSegmentsList, toggleStreamsSelector, extractAllStreams, convertFormatBatch, concatBatch, toggleCaptureFormat, toggleStripAudio, toggleStripThumbnail, askStartTimeOffset, deselectAllSegments, selectAllSegments, selectOnlyCurrentSegment, editCurrentSegmentTags, toggleCurrentSegmentSelected, invertSelectedSegments, removeSelectedSegments, tryFixInvalidDuration, shiftAllSegmentTimes, toggleMuted, copySegmentsToClipboard, handleShowStreamsSelectorClick, openFilesDialog, openDirDialog, toggleSettings, createSegmentsFromKeyframes, toggleWaveformMode, toggleShowThumbnails, toggleShowKeyframes, showIncludeExternalStreamsDialog, toggleFullscreenVideo, checkFileOpened, cutSegments, seekRel, keyboardSeekAccFactor, togglePlay, play, userChangePlaybackRate, keyboardNormalSeekSpeed, keyboardSeekSpeed2, keyboardSeekSpeed3, seekRelPercent, seekClosestKeyframe, shortStep, jumpSeg, setCurrentSegIndex, zoomRel, batchFileJump, removeCutSegment, currentSegIndexSafe, cutSegmentsHistory, onLabelSegment, toggleLastCommands, userHtml5ifyCurrentFile, toggleKeyframeCut, setPlaybackVolume, closeFileWithConfirm, openSendReportDialogWithState, detectBlackScenes, detectSilentScenes, detectSceneChanges]); }, [toggleLoopSelectedSegments, pause, timelineToggleComfortZoom, captureSnapshot, captureSnapshotAsCoverArt, setCutStart, setCutEnd, cleanupFilesDialog, splitCurrentSegment, focusSegmentAtCursor, increaseRotation, goToTimecode, jumpCutStart, jumpCutEnd, jumpTimelineStart, jumpTimelineEnd, batchOpenSelectedFile, closeBatch, addSegment, duplicateCurrentSegment, onExportPress, extractCurrentSegmentFramesAsImages, extractSelectedSegmentsFramesAsImages, reorderSegsByStartTime, invertAllSegments, fillSegmentsGaps, combineOverlappingSegments, combineSelectedSegments, createFixedDurationSegments, createNumSegments, createFixedByteSizedSegments, createRandomSegments, alignSegmentTimesToKeyframes, shuffleSegments, clearSegments, toggleSegmentsList, toggleStreamsSelector, extractAllStreams, convertFormatBatch, concatBatch, toggleCaptureFormat, toggleStripAudio, toggleStripThumbnail, askStartTimeOffset, deselectAllSegments, selectAllSegments, selectOnlyCurrentSegment, editCurrentSegmentTags, toggleCurrentSegmentSelected, invertSelectedSegments, removeSelectedSegments, tryFixInvalidDuration, shiftAllSegmentTimes, toggleMuted, copySegmentsToClipboard, handleShowStreamsSelectorClick, openFilesDialog, openDirDialog, toggleSettings, createSegmentsFromKeyframes, toggleWaveformMode, toggleShowThumbnails, toggleShowKeyframes, showIncludeExternalStreamsDialog, toggleFullscreenVideo, checkFileOpened, cutSegments, seekRel, keyboardSeekAccFactor, togglePlay, play, userChangePlaybackRate, keyboardNormalSeekSpeed, keyboardSeekSpeed2, keyboardSeekSpeed3, seekRelPercent, seekClosestKeyframe, shortStep, jumpSeg, setCurrentSegIndex, zoomRel, batchFileJump, removeCutSegment, currentSegIndexSafe, cutSegmentsHistory, onLabelSegment, toggleLastCommands, userHtml5ifyCurrentFile, toggleKeyframeCut, setPlaybackVolume, closeFileWithConfirm, openSendReportDialogWithState, detectBlackScenes, detectSilentScenes, detectSceneChanges]);
const getKeyboardAction = useCallback((action: MainKeyboardAction) => mainActions[action], [mainActions]); const getKeyboardAction = useCallback((action: MainKeyboardAction) => mainActions[action], [mainActions]);

@ -395,6 +395,10 @@ const KeyboardShortcuts = memo(({
name: t('Create num segments'), name: t('Create num segments'),
category: segmentsAndCutpointsCategory, category: segmentsAndCutpointsCategory,
}, },
createFixedByteSizedSegments: {
name: t('Create fixed byte sized segments'),
category: segmentsAndCutpointsCategory,
},
createRandomSegments: { createRandomSegments: {
name: t('Create random segments'), name: t('Create random segments'),
category: segmentsAndCutpointsCategory, category: segmentsAndCutpointsCategory,

@ -235,7 +235,7 @@ export async function createNumSegments(fileDuration: number) {
const exampleDuration = '00:00:05.123'; const exampleDuration = '00:00:05.123';
async function askForSegmentDuration({ fileDuration, inputPlaceholder, parseTimecode }: { export async function askForSegmentDuration({ fileDuration, inputPlaceholder, parseTimecode }: {
fileDuration: number, inputPlaceholder: string, parseTimecode: ParseTimecode, fileDuration: number, inputPlaceholder: string, parseTimecode: ParseTimecode,
}) { }) {
const { value } = await Swal.fire({ const { value } = await Swal.fire({
@ -467,19 +467,44 @@ export async function showCleanupFilesDialog(cleanupChoicesIn: CleanupChoicesTyp
return undefined; return undefined;
} }
export async function createFixedDurationSegments({ fileDuration, inputPlaceholder, parseTimecode }: { function parseBytesHuman(str: string) {
fileDuration: number, inputPlaceholder: string, parseTimecode: ParseTimecode, const match = str.replaceAll(/\s/g, '').match(/^(\d+)([gkmt]?)b$/i);
if (!match) return undefined;
const size = parseInt(match[1]!, 10);
const unit = match[2]!.toLowerCase();
if (unit === 't') return size * 1024 * 1024 * 1024 * 1024;
if (unit === 'g') return size * 1024 * 1024 * 1024;
if (unit === 'm') return size * 1024 * 1024;
if (unit === 'k') return size * 1024;
return size;
}
export async function createFixedByteSixedSegments({ fileDuration, fileSize }: {
fileDuration: number, fileSize: number,
}) { }) {
const segmentDuration = await askForSegmentDuration({ fileDuration, inputPlaceholder, parseTimecode }); const example = '100 MB';
if (segmentDuration == null) return undefined; const { value } = await Swal.fire({
const edl: { start: number, end: number }[] = []; input: 'text',
for (let start = 0; start < fileDuration; start += segmentDuration) { showCancelButton: true,
const end = start + segmentDuration; inputValue: example,
edl.push({ start, end: end >= fileDuration ? fileDuration : end }); inputPlaceholder: example,
} text: i18n.t('Divide timeline into a number of segments with an approximate byte size'),
return edl; inputValidator: (v) => {
const bytes = parseBytesHuman(v);
if (bytes != null) return undefined;
return i18n.t('Please input a valid size. Example: {{example}}', { example });
},
});
if (value == null) return undefined;
const parsed = parseBytesHuman(value);
invariant(parsed != null);
return fileDuration * (parsed / fileSize);
} }
export async function createRandomSegments(fileDuration: number) { export async function createRandomSegments(fileDuration: number) {
const response = await askForSegmentsRandomDurationRange(); const response = await askForSegmentsRandomDurationRange();
if (response == null) return undefined; if (response == null) return undefined;

@ -9,19 +9,19 @@ import { detectSceneChanges as ffmpegDetectSceneChanges, readFrames, mapTimesToS
import { handleError, shuffleArray } from '../util'; import { handleError, shuffleArray } from '../util';
import { errorToast } from '../swal'; import { errorToast } from '../swal';
import { showParametersDialog } from '../dialogs/parameters'; import { showParametersDialog } from '../dialogs/parameters';
import { createNumSegments as createNumSegmentsDialog, createFixedDurationSegments as createFixedDurationSegmentsDialog, createRandomSegments as createRandomSegmentsDialog, labelSegmentDialog, askForShiftSegments, askForAlignSegments, selectSegmentsByLabelDialog, selectSegmentsByExprDialog, mutateSegmentsByExprDialog } from '../dialogs'; import { createNumSegments as createNumSegmentsDialog, createFixedByteSixedSegments as createFixedByteSixedSegmentsDialog, createRandomSegments as createRandomSegmentsDialog, labelSegmentDialog, askForShiftSegments, askForAlignSegments, selectSegmentsByLabelDialog, selectSegmentsByExprDialog, mutateSegmentsByExprDialog, askForSegmentDuration } from '../dialogs';
import { createSegment, sortSegments, invertSegments, combineOverlappingSegments as combineOverlappingSegments2, combineSelectedSegments as combineSelectedSegments2, isDurationValid, addSegmentColorIndex, filterNonMarkers } from '../segments'; import { createSegment, sortSegments, invertSegments, combineOverlappingSegments as combineOverlappingSegments2, combineSelectedSegments as combineSelectedSegments2, isDurationValid, addSegmentColorIndex, filterNonMarkers, makeDurationSegments } from '../segments';
import { parameters as allFfmpegParameters, FfmpegDialog } from '../ffmpegParameters'; import { parameters as allFfmpegParameters, FfmpegDialog } from '../ffmpegParameters';
import { maxSegmentsAllowed } from '../util/constants'; import { maxSegmentsAllowed } from '../util/constants';
import { ParseTimecode, SegmentBase, segmentTagsSchema, SegmentToExport, StateSegment, UpdateSegAtIndex } from '../types'; import { ParseTimecode, SegmentBase, segmentTagsSchema, SegmentToExport, StateSegment, UpdateSegAtIndex } from '../types';
import safeishEval from '../worker/eval'; import safeishEval from '../worker/eval';
import { ScopeSegment } from '../../../../types'; import { ScopeSegment } from '../../../../types';
import { FFprobeStream } from '../../../../ffprobe'; import { FFprobeFormat, FFprobeStream } from '../../../../ffprobe';
const { ffmpeg: { blackDetect, silenceDetect } } = window.require('@electron/remote').require('./index.js'); const { ffmpeg: { blackDetect, silenceDetect } } = window.require('@electron/remote').require('./index.js');
function useSegments({ filePath, workingRef, setWorking, setProgress, videoStream, duration, getRelevantTime, maxLabelLength, checkFileOpened, invertCutSegments, segmentsToChaptersOnly, timecodePlaceholder, parseTimecode, appendFfmpegCommandLog, durationSafe }: { function useSegments({ filePath, workingRef, setWorking, setProgress, videoStream, duration, getRelevantTime, maxLabelLength, checkFileOpened, invertCutSegments, segmentsToChaptersOnly, timecodePlaceholder, parseTimecode, appendFfmpegCommandLog, durationSafe, mainFileMeta }: {
filePath?: string | undefined, filePath?: string | undefined,
workingRef: MutableRefObject<boolean>, workingRef: MutableRefObject<boolean>,
setWorking: (w: { text: string, abortController?: AbortController } | undefined) => void, setWorking: (w: { text: string, abortController?: AbortController } | undefined) => void,
@ -37,6 +37,7 @@ function useSegments({ filePath, workingRef, setWorking, setProgress, videoStrea
parseTimecode: ParseTimecode, parseTimecode: ParseTimecode,
appendFfmpegCommandLog: (args: string[]) => void, appendFfmpegCommandLog: (args: string[]) => void,
durationSafe: number, durationSafe: number,
mainFileMeta: { formatData: FFprobeFormat } | undefined,
}) { }) {
// Segment related state // Segment related state
const segCounterRef = useRef(0); const segCounterRef = useRef(0);
@ -528,10 +529,20 @@ function useSegments({ filePath, workingRef, setWorking, setProgress, videoStrea
const createFixedDurationSegments = useCallback(async () => { const createFixedDurationSegments = useCallback(async () => {
if (!checkFileOpened() || !isDurationValid(duration)) return; if (!checkFileOpened() || !isDurationValid(duration)) return;
const segments = await createFixedDurationSegmentsDialog({ fileDuration: duration, inputPlaceholder: timecodePlaceholder, parseTimecode }); const segmentDuration = await askForSegmentDuration({ fileDuration: duration, inputPlaceholder: timecodePlaceholder, parseTimecode });
if (segments) loadCutSegments(segments); if (segmentDuration == null) return;
loadCutSegments(makeDurationSegments(segmentDuration, duration));
}, [checkFileOpened, duration, loadCutSegments, parseTimecode, timecodePlaceholder]); }, [checkFileOpened, duration, loadCutSegments, parseTimecode, timecodePlaceholder]);
const createFixedByteSizedSegments = useCallback(async () => {
if (!checkFileOpened() || !isDurationValid(duration)) return;
const fileSize = mainFileMeta && parseInt(mainFileMeta.formatData.size, 10);
invariant(fileSize != null && !Number.isNaN(fileSize));
const segmentDuration = await createFixedByteSixedSegmentsDialog({ fileDuration: duration, fileSize });
if (segmentDuration == null) return;
loadCutSegments(makeDurationSegments(segmentDuration, duration));
}, [checkFileOpened, duration, loadCutSegments, mainFileMeta]);
const createRandomSegments = useCallback(async () => { const createRandomSegments = useCallback(async () => {
if (!checkFileOpened() || !isDurationValid(duration)) return; if (!checkFileOpened() || !isDurationValid(duration)) return;
const segments = await createRandomSegmentsDialog(duration); const segments = await createRandomSegmentsDialog(duration);
@ -707,6 +718,7 @@ function useSegments({ filePath, workingRef, setWorking, setProgress, videoStrea
focusSegmentAtCursor, focusSegmentAtCursor,
createNumSegments, createNumSegments,
createFixedDurationSegments, createFixedDurationSegments,
createFixedByteSizedSegments,
createRandomSegments, createRandomSegments,
haveInvalidSegs, haveInvalidSegs,
currentSegIndexSafe, currentSegIndexSafe,

@ -253,3 +253,12 @@ export const filterNonMarkers = <T extends { end?: number | undefined }>(segment
...rest, ...rest,
end, end,
}] : [])); }] : []));
export function makeDurationSegments(segmentDuration: number, fileDuration: number) {
const edl: { start: number, end: number }[] = [];
for (let start = 0; start < fileDuration; start += segmentDuration) {
const end = start + segmentDuration;
edl.push({ start, end: end >= fileDuration ? fileDuration : end });
}
return edl;
}

@ -1,4 +1,4 @@
export type KeyboardAction = 'addSegment' | 'togglePlayResetSpeed' | 'togglePlayNoResetSpeed' | 'reducePlaybackRate' | 'reducePlaybackRateMore' | 'increasePlaybackRate' | 'increasePlaybackRateMore' | 'timelineToggleComfortZoom' | 'seekPreviousFrame' | 'seekNextFrame' | 'captureSnapshot' | 'setCutStart' | 'setCutEnd' | 'removeCurrentSegment' | 'cleanupFilesDialog' | 'splitCurrentSegment' | 'focusSegmentAtCursor' | 'increaseRotation' | 'goToTimecode' | 'seekBackwards' | 'seekBackwards2' | 'seekBackwards3' | 'seekBackwardsPercent' | 'seekBackwardsPercent' | 'seekBackwardsKeyframe' | 'jumpCutStart' | 'seekForwards' | 'seekForwards2' | 'seekForwards3' | 'seekForwardsPercent' | 'seekForwardsPercent' | 'seekForwardsKeyframe' | 'jumpCutEnd' | 'jumpTimelineStart' | 'jumpTimelineEnd' | 'jumpFirstSegment' | 'jumpPrevSegment' | 'timelineZoomIn' | 'timelineZoomIn' | 'batchPreviousFile' | 'jumpLastSegment' | 'jumpNextSegment' | 'timelineZoomOut' | 'timelineZoomOut' | 'batchNextFile' | 'batchOpenSelectedFile' | 'batchOpenPreviousFile' | 'batchOpenNextFile' | 'undo' | 'undo' | 'redo' | 'redo' | 'copySegmentsToClipboard' | 'copySegmentsToClipboard' | 'toggleFullscreenVideo' | 'labelCurrentSegment' | 'export' | 'toggleKeyboardShortcuts' | 'closeActiveScreen' | 'increaseVolume' | 'decreaseVolume' | 'toggleMuted' | 'detectBlackScenes' | 'detectSilentScenes' | 'detectSceneChanges' | 'toggleLastCommands' | 'play' | 'pause' | 'reloadFile' | 'html5ify' | 'togglePlayOnlyCurrentSegment' | 'toggleLoopOnlyCurrentSegment' | 'toggleLoopStartEndOnlyCurrentSegment' | 'toggleLoopSelectedSegments' | 'editCurrentSegmentTags' | 'duplicateCurrentSegment' | 'reorderSegsByStartTime' | 'invertAllSegments' | 'fillSegmentsGaps' | 'shiftAllSegmentTimes' | 'alignSegmentTimesToKeyframes' | 'createSegmentsFromKeyframes' | 'createFixedDurationSegments' | 'createNumSegments' | 'createRandomSegments' | 'shuffleSegments' | 'combineOverlappingSegments' | 'combineSelectedSegments' | 'clearSegments' | 'toggleSegmentsList' | 'selectOnlyCurrentSegment' | 'deselectAllSegments' | 'selectAllSegments' | 'toggleCurrentSegmentSelected' | 'invertSelectedSegments' | 'removeSelectedSegments' | 'toggleStreamsSelector' | 'extractAllStreams' | 'showStreamsSelector' | 'showIncludeExternalStreamsDialog' | 'captureSnapshotAsCoverArt' | 'extractCurrentSegmentFramesAsImages' | 'extractSelectedSegmentsFramesAsImages' | 'convertFormatBatch' | 'convertFormatCurrentFile' | 'fixInvalidDuration' | 'closeBatch' | 'concatBatch' | 'toggleKeyframeCutMode' | 'toggleCaptureFormat' | 'toggleStripAudio' | 'toggleStripThumbnail' | 'setStartTimeOffset' | 'toggleWaveformMode' | 'toggleShowThumbnails' | 'toggleShowKeyframes' | 'toggleSettings' | 'openSendReportDialog' | 'openFilesDialog' | 'openDirDialog' | 'exportYouTube' | 'closeCurrentFile' | 'quit'; export type KeyboardAction = 'addSegment' | 'togglePlayResetSpeed' | 'togglePlayNoResetSpeed' | 'reducePlaybackRate' | 'reducePlaybackRateMore' | 'increasePlaybackRate' | 'increasePlaybackRateMore' | 'timelineToggleComfortZoom' | 'seekPreviousFrame' | 'seekNextFrame' | 'captureSnapshot' | 'setCutStart' | 'setCutEnd' | 'removeCurrentSegment' | 'cleanupFilesDialog' | 'splitCurrentSegment' | 'focusSegmentAtCursor' | 'increaseRotation' | 'goToTimecode' | 'seekBackwards' | 'seekBackwards2' | 'seekBackwards3' | 'seekBackwardsPercent' | 'seekBackwardsPercent' | 'seekBackwardsKeyframe' | 'jumpCutStart' | 'seekForwards' | 'seekForwards2' | 'seekForwards3' | 'seekForwardsPercent' | 'seekForwardsPercent' | 'seekForwardsKeyframe' | 'jumpCutEnd' | 'jumpTimelineStart' | 'jumpTimelineEnd' | 'jumpFirstSegment' | 'jumpPrevSegment' | 'timelineZoomIn' | 'timelineZoomIn' | 'batchPreviousFile' | 'jumpLastSegment' | 'jumpNextSegment' | 'timelineZoomOut' | 'timelineZoomOut' | 'batchNextFile' | 'batchOpenSelectedFile' | 'batchOpenPreviousFile' | 'batchOpenNextFile' | 'undo' | 'undo' | 'redo' | 'redo' | 'copySegmentsToClipboard' | 'copySegmentsToClipboard' | 'toggleFullscreenVideo' | 'labelCurrentSegment' | 'export' | 'toggleKeyboardShortcuts' | 'closeActiveScreen' | 'increaseVolume' | 'decreaseVolume' | 'toggleMuted' | 'detectBlackScenes' | 'detectSilentScenes' | 'detectSceneChanges' | 'toggleLastCommands' | 'play' | 'pause' | 'reloadFile' | 'html5ify' | 'togglePlayOnlyCurrentSegment' | 'toggleLoopOnlyCurrentSegment' | 'toggleLoopStartEndOnlyCurrentSegment' | 'toggleLoopSelectedSegments' | 'editCurrentSegmentTags' | 'duplicateCurrentSegment' | 'reorderSegsByStartTime' | 'invertAllSegments' | 'fillSegmentsGaps' | 'shiftAllSegmentTimes' | 'alignSegmentTimesToKeyframes' | 'createSegmentsFromKeyframes' | 'createFixedDurationSegments' | 'createNumSegments' | 'createFixedByteSizedSegments' | 'createRandomSegments' | 'shuffleSegments' | 'combineOverlappingSegments' | 'combineSelectedSegments' | 'clearSegments' | 'toggleSegmentsList' | 'selectOnlyCurrentSegment' | 'deselectAllSegments' | 'selectAllSegments' | 'toggleCurrentSegmentSelected' | 'invertSelectedSegments' | 'removeSelectedSegments' | 'toggleStreamsSelector' | 'extractAllStreams' | 'showStreamsSelector' | 'showIncludeExternalStreamsDialog' | 'captureSnapshotAsCoverArt' | 'extractCurrentSegmentFramesAsImages' | 'extractSelectedSegmentsFramesAsImages' | 'convertFormatBatch' | 'convertFormatCurrentFile' | 'fixInvalidDuration' | 'closeBatch' | 'concatBatch' | 'toggleKeyframeCutMode' | 'toggleCaptureFormat' | 'toggleStripAudio' | 'toggleStripThumbnail' | 'setStartTimeOffset' | 'toggleWaveformMode' | 'toggleShowThumbnails' | 'toggleShowKeyframes' | 'toggleSettings' | 'openSendReportDialog' | 'openFilesDialog' | 'openDirDialog' | 'exportYouTube' | 'closeCurrentFile' | 'quit';
export interface KeyBinding { export interface KeyBinding {
keys: string, keys: string,

Loading…
Cancel
Save