fix broken loop selected segments

pull/2350/head
Mikael Finstad 2025-02-17 15:21:27 +08:00
rodzic 25021774bb
commit 250217fee6
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 25AB36E3E81CBC26
5 zmienionych plików z 41 dodań i 37 usunięć

Wyświetl plik

@ -335,7 +335,7 @@ function App() {
}, [isFileOpened]);
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, getCutSegmentById, selectedSegments, selectedSegmentsOrInverse, nonFilteredSegmentsOrInverse, segmentsToExport, duplicateCurrentSegment, duplicateSegment, updateSegAtIndex, getSegApparentEnd, getApparentCutSegments,
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, getSegApparentEnd, getApparentCutSegments,
} = useSegments({ filePath, workingRef, setWorking, setProgress, videoStream: activeVideoStream, duration, getRelevantTime, maxLabelLength, checkFileOpened, invertCutSegments, segmentsToChaptersOnly, timecodePlaceholder, parseTimecode, appendFfmpegCommandLog });
const { getEdlFilePath, getEdlFilePathOld, projectFileSavePath, getProjectFileSavePath } = useSegmentsAutoSave({ autoSaveProjectFile, storeProjectInWorkingDir, filePath, customOutDir, cutSegments });
@ -698,17 +698,20 @@ function App() {
return;
}
// If we are using a special playback mode, we might need to do more:
if (playbackModeRef.current != null) {
const selectedSegmentAtCursor = selectedSegments.find((selectedSegment) => selectedSegment.segId === segmentAtCursorRef.current?.segId);
const isSomeSegmentAtCursor = selectedSegmentAtCursor != null && commandedTimeRef.current != null && getSegApparentEnd(selectedSegmentAtCursor) - commandedTimeRef.current > 0.1;
if (!isSomeSegmentAtCursor) { // if a segment is already at cursor, don't do anything
// if no segment at cursor, and looping playback mode, continue looping
if (playbackModeRef.current === 'loop-selected-segments') {
const firstSelectedSegment = selectedSegments[0];
if (firstSelectedSegment == null) throw new Error();
invariant(firstSelectedSegment != null);
const index = cutSegments.indexOf(firstSelectedSegment);
if (index >= 0) setCurrentSegIndex(index);
seekAbs(firstSelectedSegment.start);
} else {
// for all other playback modes, seek to start of current segment
seekAbs(getSegApparentStart(currentCutSeg));
}
}
@ -722,31 +725,33 @@ function App() {
setPlayerTime(currentTime);
const playbackMode = playbackModeRef.current;
if (playbackMode != null && segmentAtCursorRef.current != null) { // todo and is currently playing?
const playingSegment = getCutSegmentById(segmentAtCursorRef.current.segId);
if (playingSegment != null) {
const nextAction = getPlaybackMode({ playbackMode, currentTime, playingSegment: { start: getSegApparentStart(playingSegment), end: getSegApparentEnd(playingSegment) } });
if (nextAction != null) {
console.log(nextAction);
if (nextAction.nextSegment) {
const index = selectedSegments.indexOf(playingSegment);
let newIndex = getNewJumpIndex(index >= 0 ? index : 0, 1);
if (newIndex > selectedSegments.length - 1) newIndex = 0; // have reached end of last segment, start over
const nextSelectedSegment = selectedSegments[newIndex];
if (nextSelectedSegment != null) seekAbs(nextSelectedSegment.start);
}
if (nextAction.seekTo != null) {
seekAbs(nextAction.seekTo);
}
if (nextAction.exit) {
playbackModeRef.current = undefined;
pause();
}
const segmentsAtCursorIndexes = findSegmentsAtCursor(cutSegments, commandedTimeRef.current);
const firstSegmentAtCursorIndex = segmentsAtCursorIndexes[0];
const playingSegment = firstSegmentAtCursorIndex != null ? cutSegments[firstSegmentAtCursorIndex] : undefined;
if (playbackMode != null && playingSegment) { // todo and is currently playing?
const nextAction = getPlaybackMode({ playbackMode, currentTime, playingSegment: { start: getSegApparentStart(playingSegment), end: getSegApparentEnd(playingSegment) } });
if (nextAction != null) {
console.log(nextAction);
if (nextAction.nextSegment) {
const index = selectedSegments.indexOf(playingSegment);
let newIndex = getNewJumpIndex(index >= 0 ? index : 0, 1);
if (newIndex > selectedSegments.length - 1) newIndex = 0; // have reached end of last segment, start over
const nextSelectedSegment = selectedSegments[newIndex];
if (nextSelectedSegment != null) seekAbs(nextSelectedSegment.start);
}
if (nextAction.seekTo != null) {
seekAbs(nextAction.seekTo);
}
if (nextAction.exit) {
playbackModeRef.current = undefined;
pause();
}
}
}
}, [getCutSegmentById, getSegApparentEnd, pause, playbackModeRef, playerTime, seekAbs, selectedSegments, setPlayerTime]);
}, [commandedTimeRef, cutSegments, getSegApparentEnd, pause, playbackModeRef, playerTime, seekAbs, selectedSegments, setPlayerTime]);
const closeFileWithConfirm = useCallback(() => {
if (!isFileOpened || workingRef.current) return;

Wyświetl plik

@ -330,7 +330,7 @@ function SegmentList({
if (cutSegments.length < 2) return;
const { value } = await Swal.fire({
title: `${t('Change order of segment')} ${index + 1}`,
text: t('Please enter a number from 1 to {{n}} to be the new order for the current segment', { n: cutSegments.length}),
text: t('Please enter a number from 1 to {{n}} to be the new order for the current segment', { n: cutSegments.length }),
input: 'text',
inputValue: index + 1,
showCancelButton: true,

Wyświetl plik

@ -148,8 +148,6 @@ function useSegments({ filePath, workingRef, setWorking, setProgress, videoStrea
end: getSegApparentEnd(cutSegment),
})), [getSegApparentEnd]);
const getCutSegmentById = useCallback((id: string) => cutSegments.find((s) => s.segId === id), [cutSegments]);
const haveInvalidSegs = useMemo(() => cutSegments.some((cutSegment) => getSegApparentStart(cutSegment) >= getSegApparentEnd(cutSegment)), [cutSegments, getSegApparentEnd]);
const currentSegIndexSafe = Math.min(currentSegIndex, cutSegments.length - 1);
@ -683,7 +681,6 @@ function useSegments({ filePath, workingRef, setWorking, setProgress, videoStrea
createNumSegments,
createFixedDurationSegments,
createRandomSegments,
getCutSegmentById,
getApparentCutSegments,
getSegApparentEnd,
haveInvalidSegs,

Wyświetl plik

@ -1,10 +1,10 @@
import { ReactEventHandler, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { ReactEventHandler, useCallback, useMemo, useRef, useState } from 'react';
import { ChromiumHTMLVideoElement, PlaybackMode } from '../types';
import { isDurationValid } from '../segments';
import { showPlaybackFailedMessage } from '../swal';
export default ({ filePath }: { filePath: string | undefined }) => {
const [commandedTime, setCommandedTime] = useState(0);
const [commandedTime, setCommandedTimeRaw] = useState(0);
const [compatPlayerEventId, setCompatPlayerEventId] = useState(0);
const [playbackRate, setPlaybackRateState] = useState(1);
const [outputPlaybackRate, setOutputPlaybackRateState] = useState(1);
@ -54,6 +54,13 @@ export default ({ filePath }: { filePath: string | undefined }) => {
}
}, []);
const commandedTimeRef = useRef(commandedTime);
const setCommandedTime = useCallback((t: number) => {
commandedTimeRef.current = t;
setCommandedTimeRaw(t);
}, []);
const seekAbs = useCallback((val: number | undefined) => {
const video = videoRef.current;
if (video == null || val == null || Number.isNaN(val)) return;
@ -64,12 +71,7 @@ export default ({ filePath }: { filePath: string | undefined }) => {
smoothSeek(outVal);
setCommandedTime(outVal);
setCompatPlayerEventId((id) => id + 1); // To make sure that we can seek even to the same commanded time that we are already add (e.g. loop current segment)
}, [smoothSeek]);
const commandedTimeRef = useRef(commandedTime);
useEffect(() => {
commandedTimeRef.current = commandedTime;
}, [commandedTime]);
}, [setCommandedTime, smoothSeek]);
// Relevant time is the player's playback position if we're currently playing - if not, it's the user's commanded time.
const relevantTime = useMemo(() => (playing ? playerTime : commandedTime) || 0, [commandedTime, playerTime, playing]);
@ -86,7 +88,7 @@ export default ({ filePath }: { filePath: string | undefined }) => {
if (!val) {
setCommandedTime(videoRef.current!.currentTime);
}
}, []);
}, [setCommandedTime]);
const onStopPlaying = useCallback(() => {
onPlayingChange(false);

Wyświetl plik

@ -53,7 +53,7 @@ export function findSegmentsAtCursor(segments: SegmentBase[], currentTime: numbe
segments.forEach((segment, index) => {
if ((segment.start == null || segment.start <= currentTime) && (segment.end == null || segment.end >= currentTime)) indexes.push(index);
});
return indexes;
return indexes.reverse(); // if we are on multiple, select the last. That way, auto play selected segments won't go on repeat on the same segment
}
// in the past we had non-string tags