mirror of
https://github.com/mifi/lossless-cut.git
synced 2024-11-21 18:02:35 +01:00
improve playback rate
instead always show it in the bottom bar closes #2109
This commit is contained in:
parent
343ab5a644
commit
016f4ce1b7
@ -116,7 +116,6 @@ function App() {
|
|||||||
// Per project state
|
// Per project state
|
||||||
const [commandedTime, setCommandedTime] = useState(0);
|
const [commandedTime, setCommandedTime] = useState(0);
|
||||||
const [ffmpegCommandLog, setFfmpegCommandLog] = useState<FfmpegCommandLog>([]);
|
const [ffmpegCommandLog, setFfmpegCommandLog] = useState<FfmpegCommandLog>([]);
|
||||||
|
|
||||||
const [previewFilePath, setPreviewFilePath] = useState<string>();
|
const [previewFilePath, setPreviewFilePath] = useState<string>();
|
||||||
const [working, setWorkingState] = useState<{ text: string, abortController?: AbortController | undefined }>();
|
const [working, setWorkingState] = useState<{ text: string, abortController?: AbortController | undefined }>();
|
||||||
const [usingDummyVideo, setUsingDummyVideo] = useState(false);
|
const [usingDummyVideo, setUsingDummyVideo] = useState(false);
|
||||||
@ -149,6 +148,7 @@ function App() {
|
|||||||
const [exportConfirmVisible, setExportConfirmVisible] = useState(false);
|
const [exportConfirmVisible, setExportConfirmVisible] = useState(false);
|
||||||
const [cacheBuster, setCacheBuster] = useState(0);
|
const [cacheBuster, setCacheBuster] = useState(0);
|
||||||
const [mergedOutFileName, setMergedOutFileName] = useState<string>();
|
const [mergedOutFileName, setMergedOutFileName] = useState<string>();
|
||||||
|
const [playbackRate, setPlaybackRateState] = useState(1);
|
||||||
const [outputPlaybackRate, setOutputPlaybackRateState] = useState(1);
|
const [outputPlaybackRate, setOutputPlaybackRateState] = useState(1);
|
||||||
|
|
||||||
const { fileFormat, setFileFormat, detectedFileFormat, setDetectedFileFormat, isCustomFormatSelected } = useFileFormatState();
|
const { fileFormat, setFileFormat, detectedFileFormat, setDetectedFileFormat, isCustomFormatSelected } = useFileFormatState();
|
||||||
@ -221,9 +221,14 @@ function App() {
|
|||||||
const videoRef = useRef<ChromiumHTMLVideoElement>(null);
|
const videoRef = useRef<ChromiumHTMLVideoElement>(null);
|
||||||
const videoContainerRef = useRef<HTMLDivElement>(null);
|
const videoContainerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const setOutputPlaybackRate = useCallback((v: number) => {
|
const setPlaybackRate = useCallback((rate: number) => {
|
||||||
setOutputPlaybackRateState(v);
|
if (videoRef.current) videoRef.current.playbackRate = rate;
|
||||||
if (videoRef.current) videoRef.current.playbackRate = v;
|
setPlaybackRateState(rate);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const setOutputPlaybackRate = useCallback((rate: number) => {
|
||||||
|
setOutputPlaybackRateState(rate);
|
||||||
|
if (videoRef.current) videoRef.current.playbackRate = rate;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const isFileOpened = !!filePath;
|
const isFileOpened = !!filePath;
|
||||||
@ -832,7 +837,7 @@ function App() {
|
|||||||
const video = videoRef.current;
|
const video = videoRef.current;
|
||||||
setCommandedTime(0);
|
setCommandedTime(0);
|
||||||
video!.currentTime = 0;
|
video!.currentTime = 0;
|
||||||
video!.playbackRate = 1;
|
setPlaybackRate(1);
|
||||||
|
|
||||||
// setWorking();
|
// setWorking();
|
||||||
setPreviewFilePath(undefined);
|
setPreviewFilePath(undefined);
|
||||||
@ -872,7 +877,7 @@ function App() {
|
|||||||
setOutputPlaybackRateState(1);
|
setOutputPlaybackRateState(1);
|
||||||
|
|
||||||
cancelRenderThumbnails();
|
cancelRenderThumbnails();
|
||||||
}, [cutSegmentsHistory, clearSegments, setFileFormat, setDetectedFileFormat, setDeselectedSegmentIds, resetMergedOutFileName, cancelRenderThumbnails]);
|
}, [setPlaybackRate, cutSegmentsHistory, clearSegments, setFileFormat, setDetectedFileFormat, setDeselectedSegmentIds, resetMergedOutFileName, cancelRenderThumbnails]);
|
||||||
|
|
||||||
|
|
||||||
const showUnsupportedFileMessage = useCallback(() => {
|
const showUnsupportedFileMessage = useCallback(() => {
|
||||||
@ -993,7 +998,7 @@ function App() {
|
|||||||
// This was added to re-sync time if file gets reloaded #1674 - but I had to remove this because it broke loop-selected-segments https://github.com/mifi/lossless-cut/discussions/1785#discussioncomment-7852134
|
// This was added to re-sync time if file gets reloaded #1674 - but I had to remove this because it broke loop-selected-segments https://github.com/mifi/lossless-cut/discussions/1785#discussioncomment-7852134
|
||||||
// if (Math.abs(commandedTimeRef.current - video.currentTime) > 1) video.currentTime = commandedTimeRef.current;
|
// if (Math.abs(commandedTimeRef.current - video.currentTime) > 1) video.currentTime = commandedTimeRef.current;
|
||||||
|
|
||||||
if (resetPlaybackRate) video!.playbackRate = outputPlaybackRate;
|
if (resetPlaybackRate) setPlaybackRate(outputPlaybackRate);
|
||||||
video?.play().catch((err) => {
|
video?.play().catch((err) => {
|
||||||
if (err instanceof Error && err.name === 'AbortError' && 'code' in err && err.code === 20) { // Probably "DOMException: The play() request was interrupted by a call to pause()."
|
if (err instanceof Error && err.name === 'AbortError' && 'code' in err && err.code === 20) { // Probably "DOMException: The play() request was interrupted by a call to pause()."
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@ -1001,7 +1006,7 @@ function App() {
|
|||||||
showPlaybackFailedMessage();
|
showPlaybackFailedMessage();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, [filePath, outputPlaybackRate]);
|
}, [filePath, outputPlaybackRate, setPlaybackRate]);
|
||||||
|
|
||||||
const togglePlay = useCallback(({ resetPlaybackRate, requestPlaybackMode }: { resetPlaybackRate?: boolean, requestPlaybackMode?: PlaybackMode } | undefined = {}) => {
|
const togglePlay = useCallback(({ resetPlaybackRate, requestPlaybackMode }: { resetPlaybackRate?: boolean, requestPlaybackMode?: PlaybackMode } | undefined = {}) => {
|
||||||
playbackModeRef.current = requestPlaybackMode;
|
playbackModeRef.current = requestPlaybackMode;
|
||||||
@ -1512,7 +1517,7 @@ function App() {
|
|||||||
const extractCurrentSegmentFramesAsImages = useCallback(() => extractSegmentFramesAsImages([currentCutSeg?.segId]), [currentCutSeg?.segId, extractSegmentFramesAsImages]);
|
const extractCurrentSegmentFramesAsImages = useCallback(() => extractSegmentFramesAsImages([currentCutSeg?.segId]), [currentCutSeg?.segId, extractSegmentFramesAsImages]);
|
||||||
const extractSelectedSegmentsFramesAsImages = useCallback(() => extractSegmentFramesAsImages(selectedSegments.map((seg) => seg.segId)), [extractSegmentFramesAsImages, selectedSegments]);
|
const extractSelectedSegmentsFramesAsImages = useCallback(() => extractSegmentFramesAsImages(selectedSegments.map((seg) => seg.segId)), [extractSegmentFramesAsImages, selectedSegments]);
|
||||||
|
|
||||||
const changePlaybackRate = useCallback((dir: number, rateMultiplier?: number) => {
|
const userChangePlaybackRate = useCallback((dir: number, rateMultiplier?: number) => {
|
||||||
if (compatPlayerEnabled) {
|
if (compatPlayerEnabled) {
|
||||||
toast.fire({ title: i18n.t('Unable to change playback rate right now'), timer: 1000 });
|
toast.fire({ title: i18n.t('Unable to change playback rate right now'), timer: 1000 });
|
||||||
return;
|
return;
|
||||||
@ -1523,10 +1528,9 @@ function App() {
|
|||||||
video!.play();
|
video!.play();
|
||||||
} else {
|
} else {
|
||||||
const newRate = adjustRate(video!.playbackRate, dir, rateMultiplier);
|
const newRate = adjustRate(video!.playbackRate, dir, rateMultiplier);
|
||||||
showNotification({ title: `${i18n.t('Playback rate:')} ${Math.round(newRate * 100)}%`, timer: 1000 });
|
setPlaybackRate(newRate);
|
||||||
video!.playbackRate = newRate;
|
|
||||||
}
|
}
|
||||||
}, [compatPlayerEnabled, showNotification]);
|
}, [compatPlayerEnabled, setPlaybackRate]);
|
||||||
|
|
||||||
const loadEdlFile = useCallback(async ({ path, type, append }: { path: string, type: EdlFileType, append?: boolean }) => {
|
const loadEdlFile = useCallback(async ({ path, type, append }: { path: string, type: EdlFileType, append?: boolean }) => {
|
||||||
console.log('Loading EDL file', type, path, append);
|
console.log('Loading EDL file', type, path, append);
|
||||||
@ -2226,10 +2230,10 @@ function App() {
|
|||||||
toggleLoopSelectedSegments,
|
toggleLoopSelectedSegments,
|
||||||
play: () => play(),
|
play: () => play(),
|
||||||
pause,
|
pause,
|
||||||
reducePlaybackRate: () => changePlaybackRate(-1),
|
reducePlaybackRate: () => userChangePlaybackRate(-1),
|
||||||
reducePlaybackRateMore: () => changePlaybackRate(-1, 2),
|
reducePlaybackRateMore: () => userChangePlaybackRate(-1, 2),
|
||||||
increasePlaybackRate: () => changePlaybackRate(1),
|
increasePlaybackRate: () => userChangePlaybackRate(1),
|
||||||
increasePlaybackRateMore: () => changePlaybackRate(1, 2),
|
increasePlaybackRateMore: () => userChangePlaybackRate(1, 2),
|
||||||
timelineToggleComfortZoom,
|
timelineToggleComfortZoom,
|
||||||
captureSnapshot,
|
captureSnapshot,
|
||||||
captureSnapshotAsCoverArt,
|
captureSnapshotAsCoverArt,
|
||||||
@ -2341,7 +2345,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, apparentCutSegments, seekRel, keyboardSeekAccFactor, togglePlay, play, changePlaybackRate, keyboardNormalSeekSpeed, keyboardSeekSpeed2, keyboardSeekSpeed3, seekRelPercent, seekClosestKeyframe, shortStep, jumpSeg, setCurrentSegIndex, cutSegments.length, 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, 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, apparentCutSegments, seekRel, keyboardSeekAccFactor, togglePlay, play, userChangePlaybackRate, keyboardNormalSeekSpeed, keyboardSeekSpeed2, keyboardSeekSpeed3, seekRelPercent, seekClosestKeyframe, shortStep, jumpSeg, setCurrentSegIndex, cutSegments.length, 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]);
|
||||||
|
|
||||||
@ -2890,6 +2894,7 @@ function App() {
|
|||||||
setOutputPlaybackRate={setOutputPlaybackRate}
|
setOutputPlaybackRate={setOutputPlaybackRate}
|
||||||
formatTimecode={formatTimecode}
|
formatTimecode={formatTimecode}
|
||||||
parseTimecode={parseTimecode}
|
parseTimecode={parseTimecode}
|
||||||
|
playbackRate={playbackRate}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -193,7 +193,7 @@ function BottomBar({
|
|||||||
darkMode, setDarkMode,
|
darkMode, setDarkMode,
|
||||||
toggleShowThumbnails, toggleWaveformMode, waveformMode, showThumbnails,
|
toggleShowThumbnails, toggleWaveformMode, waveformMode, showThumbnails,
|
||||||
outputPlaybackRate, setOutputPlaybackRate,
|
outputPlaybackRate, setOutputPlaybackRate,
|
||||||
formatTimecode, parseTimecode,
|
formatTimecode, parseTimecode, playbackRate,
|
||||||
}: {
|
}: {
|
||||||
zoom: number,
|
zoom: number,
|
||||||
setZoom: Dispatch<SetStateAction<number>>,
|
setZoom: Dispatch<SetStateAction<number>>,
|
||||||
@ -242,6 +242,7 @@ function BottomBar({
|
|||||||
setOutputPlaybackRate: (v: number) => void,
|
setOutputPlaybackRate: (v: number) => void,
|
||||||
formatTimecode: FormatTimecode,
|
formatTimecode: FormatTimecode,
|
||||||
parseTimecode: ParseTimecode,
|
parseTimecode: ParseTimecode,
|
||||||
|
playbackRate: number,
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { getSegColor } = useSegColors();
|
const { getSegColor } = useSegColors();
|
||||||
@ -468,6 +469,8 @@ function BottomBar({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<IoMdSpeedometer title={t('Change FPS')} style={{ padding: '0 .2em', fontSize: '1.3em' }} role="button" onClick={handleChangePlaybackRateClick} />
|
<IoMdSpeedometer title={t('Change FPS')} style={{ padding: '0 .2em', fontSize: '1.3em' }} role="button" onClick={handleChangePlaybackRateClick} />
|
||||||
|
|
||||||
|
<div title={t('Playback rate')} style={{ color: 'var(--gray11)', fontSize: '.7em', marginLeft: '.1em' }}>{playbackRate.toFixed(1)}</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user