refactor and improve timeline logic

pull/1452/head
Mikael Finstad 4 years ago
parent 76f1883aa9
commit a09b9aff52
No known key found for this signature in database
GPG Key ID: 25AB36E3E81CBC26

@ -325,7 +325,10 @@ const App = memo(() => {
const onTimelineWheel = useTimelineScroll({ wheelSensitivity, mouseWheelZoomModifierKey, invertTimelineScroll, zoomRel, seekRel });
const getCurrentTime = useCallback(() => (playing ? videoRef.current.currentTime : commandedTimeRef.current), [playing]);
// 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]);
// The reason why we also have a getter is because it can be used when we need to get the time, but don't want to re-render for every time update (which can be heavy!)
const getRelevantTime = useCallback(() => (playing ? videoRef.current.currentTime : commandedTimeRef.current) || 0, [playing]);
const maxLabelLength = safeOutputFileName ? 100 : 500;
@ -337,7 +340,7 @@ const App = memo(() => {
const {
cutSegments, cutSegmentsHistory, createSegmentsFromKeyframes, shuffleSegments, detectBlackScenes, detectSilentScenes, detectSceneChanges, removeCutSegment, invertAllSegments, fillSegmentsGaps, combineOverlappingSegments, shiftAllSegmentTimes, alignSegmentTimesToKeyframes, onViewSegmentTags, updateSegOrder, updateSegOrders, reorderSegsByStartTime, addSegment, setCutStart, setCutEnd, onLabelSegment, splitCurrentSegment, createNumSegments, createFixedDurationSegments, createRandomSegments, apparentCutSegments, haveInvalidSegs, currentSegIndexSafe, currentCutSeg, currentApparentCutSeg, inverseCutSegments, clearSegments, loadCutSegments, selectedSegmentsRaw, setCutTime, getSegApparentEnd, setCurrentSegIndex, onLabelSelectedSegments, deselectAllSegments, selectAllSegments, selectOnlyCurrentSegment, toggleCurrentSegmentSelected, removeSelectedSegments, setDeselectedSegmentIds, onSelectSegmentsByLabel, toggleSegmentSelected, selectOnlySegment,
} = useSegments({ filePath, workingRef, setWorking, setCutProgress, mainVideoStream, duration, getCurrentTime, maxLabelLength, checkFileOpened });
} = useSegments({ filePath, workingRef, setWorking, setCutProgress, mainVideoStream, duration, getRelevantTime, maxLabelLength, checkFileOpened });
const jumpSegStart = useCallback((index) => seekAbs(apparentCutSegments[index].start), [apparentCutSegments, seekAbs]);
const jumpSegEnd = useCallback((index) => seekAbs(apparentCutSegments[index].end), [apparentCutSegments, seekAbs]);
@ -1222,7 +1225,7 @@ const App = memo(() => {
if (!filePath) return;
try {
const currentTime = getCurrentTime();
const currentTime = getRelevantTime();
const video = videoRef.current;
const useFffmpeg = usingPreviewFile || captureFrameMethod === 'ffmpeg';
const outPath = useFffmpeg
@ -1234,7 +1237,7 @@ const App = memo(() => {
console.error(err);
errorToast(i18n.t('Failed to capture frame'));
}
}, [filePath, getCurrentTime, usingPreviewFile, captureFrameMethod, captureFrameFromFfmpeg, customOutDir, captureFormat, enableTransferTimestamps, captureFrameQuality, captureFrameFromTag, hideAllNotifications]);
}, [filePath, getRelevantTime, usingPreviewFile, captureFrameMethod, captureFrameFromFfmpeg, customOutDir, captureFormat, enableTransferTimestamps, captureFrameQuality, captureFrameFromTag, hideAllNotifications]);
const extractSegmentFramesAsImages = useCallback(async (index) => {
if (!filePath || detectedFps == null || workingRef.current) return;
@ -1407,10 +1410,10 @@ const App = memo(() => {
const jumpSeg = useCallback((val) => setCurrentSegIndex((old) => Math.max(Math.min(old + val, cutSegments.length - 1), 0)), [cutSegments.length, setCurrentSegIndex]);
const seekClosestKeyframe = useCallback((direction) => {
const time = findNearestKeyFrameTime({ time: getCurrentTime(), direction });
const time = findNearestKeyFrameTime({ time: getRelevantTime(), direction });
if (time == null) return;
seekAbs(time);
}, [findNearestKeyFrameTime, getCurrentTime, seekAbs]);
}, [findNearestKeyFrameTime, getRelevantTime, seekAbs]);
const seekAccelerationRef = useRef(1);
@ -1618,7 +1621,7 @@ const App = memo(() => {
const captureSnapshotAsCoverArt = useCallback(async () => {
if (!filePath) return;
try {
const currentTime = getCurrentTime();
const currentTime = getRelevantTime();
const path = await captureFrameFromFfmpeg({ customOutDir, filePath, fromTime: currentTime, captureFormat, enableTransferTimestamps, quality: captureFrameQuality });
if (!(await addFileAsCoverArt(path))) return;
if (!hideAllNotifications) toast.fire({ text: i18n.t('Current frame has been set as cover art') });
@ -1626,7 +1629,7 @@ const App = memo(() => {
console.error(err);
errorToast(i18n.t('Failed to capture frame'));
}
}, [addFileAsCoverArt, captureFormat, captureFrameFromFfmpeg, captureFrameQuality, customOutDir, enableTransferTimestamps, filePath, getCurrentTime, hideAllNotifications]);
}, [addFileAsCoverArt, captureFormat, captureFrameFromFfmpeg, captureFrameQuality, customOutDir, enableTransferTimestamps, filePath, getRelevantTime, hideAllNotifications]);
const batchLoadPaths = useCallback((newPaths, append) => {
setBatchFiles((existingFiles) => {
@ -2249,11 +2252,12 @@ const App = memo(() => {
thumbnailsEnabled={thumbnailsEnabled}
neighbouringKeyFrames={neighbouringKeyFrames}
thumbnails={thumbnailsSorted}
getCurrentTime={getCurrentTime}
commandedTimeRef={commandedTimeRef}
startTimeOffset={startTimeOffset}
playerTime={playerTime}
commandedTime={commandedTime}
relevantTime={relevantTime}
getRelevantTime={getRelevantTime}
commandedTimeRef={commandedTimeRef}
startTimeOffset={startTimeOffset}
zoom={zoom}
seekAbs={seekAbs}
durationSafe={durationSafe}

@ -55,7 +55,7 @@ const CommandedTime = memo(({ commandedTimePercent }) => {
});
const Timeline = memo(({
durationSafe, getCurrentTime, startTimeOffset, playerTime, commandedTime,
durationSafe, startTimeOffset, playerTime, commandedTime, relevantTime,
zoom, neighbouringKeyFrames, seekAbs, apparentCutSegments,
setCurrentSegIndex, currentSegIndexSafe, inverseCutSegments, formatTimecode,
waveforms, shouldShowWaveform, shouldShowKeyframes, timelineHeight = 36, thumbnails,
@ -73,8 +73,7 @@ const Timeline = memo(({
const [hoveringTime, setHoveringTime] = useState();
const currentTime = getCurrentTime() || 0;
const displayTime = (hoveringTime != null && isFileOpened && !playing ? hoveringTime : currentTime) + startTimeOffset;
const displayTime = (hoveringTime != null && isFileOpened && !playing ? hoveringTime : relevantTime) + startTimeOffset;
const displayTimePercent = useMemo(() => `${Math.round((displayTime / durationSafe) * 100)}%`, [displayTime, durationSafe]);
const isZoomed = zoom > 1;
@ -96,10 +95,10 @@ const Timeline = memo(({
const timeOfInterestPosPixels = useMemo(() => {
// https://github.com/mifi/lossless-cut/issues/676
const pos = calculateTimelinePos(playerTime);
const pos = calculateTimelinePos(relevantTime);
if (pos != null && timelineScrollerRef.current) return pos * zoom * timelineScrollerRef.current.offsetWidth;
return undefined;
}, [calculateTimelinePos, playerTime, zoom]);
}, [calculateTimelinePos, relevantTime, zoom]);
const calcZoomWindowStartTime = useCallback(() => (timelineScrollerRef.current
? (timelineScrollerRef.current.scrollLeft / (timelineScrollerRef.current.offsetWidth * zoom)) * durationSafe
@ -195,7 +194,7 @@ const Timeline = memo(({
useEffect(() => {
setHoveringTime();
}, [playerTime, commandedTime]);
}, [relevantTime]);
const onMouseDown = useCallback((e) => {
if (e.nativeEvent.buttons !== 1) return; // not primary button

@ -17,7 +17,7 @@ import { maxSegmentsAllowed } from '../util/constants';
export default ({
filePath, workingRef, setWorking, setCutProgress, mainVideoStream,
duration, getCurrentTime, maxLabelLength, checkFileOpened,
duration, getRelevantTime, maxLabelLength, checkFileOpened,
}) => {
// Segment related state
const segCounterRef = useRef(0);
@ -299,7 +299,7 @@ export default ({
// Cannot add if prev seg is not finished
if (currentCutSeg.start === undefined && currentCutSeg.end === undefined) return;
const suggestedStart = getCurrentTime();
const suggestedStart = getRelevantTime();
/* if (keyframeCut) {
const keyframeAlignedStart = getSafeCutTime(suggestedStart, true);
if (keyframeAlignedStart != null) suggestedStart = keyframeAlignedStart;
@ -317,20 +317,20 @@ export default ({
} catch (err) {
console.error(err);
}
}, [currentCutSeg.start, currentCutSeg.end, getCurrentTime, duration, cutSegments, createIndexedSegment, setCutSegments, setCurrentSegIndex]);
}, [currentCutSeg.start, currentCutSeg.end, getRelevantTime, duration, cutSegments, createIndexedSegment, setCutSegments, setCurrentSegIndex]);
const setCutStart = useCallback(() => {
if (!checkFileOpened()) return;
const currentTime = getCurrentTime();
const relevantTime = getRelevantTime();
// https://github.com/mifi/lossless-cut/issues/168
// If current time is after the end of the current segment in the timeline,
// add a new segment that starts at playerTime
if (currentCutSeg.end != null && currentTime >= currentCutSeg.end) {
if (currentCutSeg.end != null && relevantTime >= currentCutSeg.end) {
addSegment();
} else {
try {
const startTime = currentTime;
const startTime = relevantTime;
/* if (keyframeCut) {
const keyframeAlignedCutTo = getSafeCutTime(startTime, true);
if (keyframeAlignedCutTo != null) startTime = keyframeAlignedCutTo;
@ -340,13 +340,13 @@ export default ({
handleError(err);
}
}
}, [checkFileOpened, getCurrentTime, currentCutSeg.end, addSegment, setCutTime]);
}, [checkFileOpened, getRelevantTime, currentCutSeg.end, addSegment, setCutTime]);
const setCutEnd = useCallback(() => {
if (!checkFileOpened()) return;
try {
const endTime = getCurrentTime();
const endTime = getRelevantTime();
/* if (keyframeCut) {
const keyframeAlignedCutTo = getSafeCutTime(endTime, false);
@ -356,7 +356,7 @@ export default ({
} catch (err) {
handleError(err);
}
}, [checkFileOpened, getCurrentTime, setCutTime]);
}, [checkFileOpened, getRelevantTime, setCutTime]);
const onLabelSegment = useCallback(async (index) => {
const { name } = cutSegments[index];
@ -365,8 +365,8 @@ export default ({
}, [cutSegments, updateSegAtIndex, maxLabelLength]);
const splitCurrentSegment = useCallback(() => {
const currentTime = getCurrentTime();
const segmentsAtCursorIndexes = findSegmentsAtCursor(apparentCutSegments, currentTime);
const relevantTime = getRelevantTime();
const segmentsAtCursorIndexes = findSegmentsAtCursor(apparentCutSegments, relevantTime);
if (segmentsAtCursorIndexes.length === 0) {
errorToast(i18n.t('No segment to split. Please move cursor over the segment you want to split'));
@ -378,13 +378,13 @@ export default ({
const getNewName = (oldName, suffix) => oldName && `${segment.name} ${suffix}`;
const firstPart = createIndexedSegment({ segment: { name: getNewName(segment.name, '1'), start: segment.start, end: currentTime }, incrementCount: false });
const secondPart = createIndexedSegment({ segment: { name: getNewName(segment.name, '2'), start: currentTime, end: segment.end }, incrementCount: true });
const firstPart = createIndexedSegment({ segment: { name: getNewName(segment.name, '1'), start: segment.start, end: relevantTime }, incrementCount: false });
const secondPart = createIndexedSegment({ segment: { name: getNewName(segment.name, '2'), start: relevantTime, end: segment.end }, incrementCount: true });
const newSegments = [...cutSegments];
newSegments.splice(firstSegmentAtCursorIndex, 1, firstPart, secondPart);
setCutSegments(newSegments);
}, [apparentCutSegments, createIndexedSegment, cutSegments, getCurrentTime, setCutSegments]);
}, [apparentCutSegments, createIndexedSegment, cutSegments, getRelevantTime, setCutSegments]);
const createNumSegments = useCallback(async () => {
if (!checkFileOpened() || !isDurationValid(duration)) return;

Loading…
Cancel
Save