From 40694fe7382152a1590b299a6a73f122716f1115 Mon Sep 17 00:00:00 2001 From: Mikael Finstad Date: Sun, 22 Nov 2020 23:39:39 +0100 Subject: [PATCH] Improve duration logic #477 --- src/App.jsx | 31 +++++++++++++++++++------------ src/Timeline.jsx | 12 ++++++------ src/ffmpeg.js | 3 ++- src/util.js | 2 ++ 4 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 756266af..88b2b1c4 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -46,6 +46,7 @@ import { saveCsv, loadCsv, loadXmeml, loadCue } from './edlStore'; import { getOutPath, formatDuration, toast, errorToast, showFfmpegFail, setFileNameTitle, getOutDir, withBlur, checkDirWriteAccess, dirExists, openDirToast, isMasBuild, isStoreBuild, dragPreventer, doesPlayerSupportFile, + isDurationValid, } from './util'; import { askForOutDir, askForImportChapters, createNumSegments, createFixedDurationSegments, promptTimeOffset, askForHtml5ifySpeed, askForYouTubeInput, askForFileOpenAction } from './dialogs'; import { openSendReportDialog } from './reporting'; @@ -133,8 +134,8 @@ const App = memo(() => { const [debouncedCutSegments] = useDebounce(cutSegments, 500); - const durationSafe = duration || 1; - const zoomedDuration = duration != null ? duration / zoom : undefined; + const durationSafe = isDurationValid(duration) ? duration : 1; + const zoomedDuration = isDurationValid(duration) ? duration / zoom : undefined; const isCustomFormatSelected = fileFormat !== detectedFileFormat; @@ -275,6 +276,7 @@ const App = memo(() => { }, [seekAbs]); const seekRelPercent = useCallback((val) => { + if (!isDurationValid(zoomedDuration)) return; seekRel(val * zoomedDuration); }, [seekRel, zoomedDuration]); @@ -301,7 +303,7 @@ const App = memo(() => { if (canvasPlayerEnabled) setHideCanvasPreview(false); }, [canvasPlayerEnabled]); - const comfortZoom = duration ? Math.max(duration / 100, 1) : undefined; + const comfortZoom = isDurationValid(duration) ? Math.max(duration / 100, 1) : undefined; const toggleComfortZoom = useCallback(() => { if (!comfortZoom) return; @@ -314,7 +316,7 @@ const App = memo(() => { const getSegApparentEnd = useCallback((seg) => { const time = seg.end; if (time !== undefined) return time; - if (duration !== undefined) return duration; + if (isDurationValid(duration)) return duration; return 0; // Haven't gotten duration yet }, [duration]); @@ -348,7 +350,7 @@ const App = memo(() => { }); if (foundOverlap) return undefined; - if (duration == null) return undefined; + if (!isDurationValid(duration)) return undefined; const ret = []; @@ -379,6 +381,8 @@ const App = memo(() => { }, [duration, haveInvalidSegs, sortedCutSegments]); const setCutTime = useCallback((type, time) => { + if (!isDurationValid(duration)) return; + const currentSeg = currentCutSeg; if (type === 'start' && time >= getSegApparentEnd(currentSeg)) { throw new Error('Start time must precede end time'); @@ -545,7 +549,10 @@ const App = memo(() => { const onSartPlaying = useCallback(() => onPlayingChange(true), []); const onDurationChange = useCallback((e) => { // Some files report duration infinity first, then proper duration later - if (e.target.duration !== Infinity) setDuration(e.target.duration); + // Sometimes after seeking to end of file, duration might change + const { duration: durationNew } = e.target; + console.log('onDurationChange', durationNew); + if (isDurationValid(durationNew)) setDuration(durationNew); }, []); const onTimeUpdate = useCallback((e) => { @@ -688,8 +695,8 @@ const App = memo(() => { } } - if (duration) renderThumbnails(); - }, 500, [zoomedDuration, duration, filePath, zoomWindowStartTime, thumbnailsEnabled]); + if (isDurationValid(zoomedDuration)) renderThumbnails(); + }, 500, [zoomedDuration, filePath, zoomWindowStartTime, thumbnailsEnabled]); // Cleanup removed thumbnails useEffect(() => { @@ -1024,7 +1031,8 @@ const App = memo(() => { ]); const capture = useCallback(async () => { - if (!filePath) return; + if (!filePath || !isDurationValid(duration)) return; + try { const mustCaptureFfmpeg = html5FriendlyPath || dummyVideoPath; const currentTime = currentTimeRef.current; @@ -1609,13 +1617,13 @@ const App = memo(() => { } async function createNumSegments2() { - if (!checkFileOpened()) return; + if (!checkFileOpened() || !isDurationValid(duration)) return; const segments = await createNumSegments(duration); if (segments) loadCutSegments(segments); } async function createFixedDurationSegments2() { - if (!checkFileOpened()) return; + if (!checkFileOpened() || !isDurationValid(duration)) return; const segments = await createFixedDurationSegments(duration); if (segments) loadCutSegments(segments); } @@ -2011,7 +2019,6 @@ const App = memo(() => { seekAbs={seekAbs} seekRel={seekRel} zoomRel={zoomRel} - duration={duration} durationSafe={durationSafe} apparentCutSegments={apparentCutSegments} setCurrentSegIndex={setCurrentSegIndex} diff --git a/src/Timeline.jsx b/src/Timeline.jsx index 0cde2a46..834b0da8 100644 --- a/src/Timeline.jsx +++ b/src/Timeline.jsx @@ -40,7 +40,7 @@ const Waveform = memo(({ calculateTimelinePercent, durationSafe, waveform, zoom, const Timeline = memo(({ durationSafe, getCurrentTime, startTimeOffset, playerTime, commandedTime, - zoom, neighbouringFrames, seekAbs, seekRel, duration, apparentCutSegments, zoomRel, + zoom, neighbouringFrames, seekAbs, seekRel, apparentCutSegments, zoomRel, setCurrentSegIndex, currentSegIndexSafe, invertCutSegments, inverseCutSegments, formatTimecode, waveform, shouldShowWaveform, shouldShowKeyframes, timelineHeight, thumbnails, onZoomWindowStartTimeChange, waveformEnabled, thumbnailsEnabled, wheelSensitivity, @@ -76,8 +76,8 @@ const Timeline = memo(({ }, [calculateTimelinePos, playerTime, zoom]); const calcZoomWindowStartTime = useCallback(() => (timelineScrollerRef.current - ? (timelineScrollerRef.current.scrollLeft / (timelineScrollerRef.current.offsetWidth * zoom)) * duration - : 0), [duration, zoom]); + ? (timelineScrollerRef.current.scrollLeft / (timelineScrollerRef.current.offsetWidth * zoom)) * durationSafe + : 0), [durationSafe, zoom]); // const zoomWindowStartTime = calcZoomWindowStartTime(duration, zoom); @@ -149,8 +149,8 @@ const Timeline = memo(({ const target = timelineWrapperRef.current; const rect = target.getBoundingClientRect(); const relX = e.srcEvent.pageX - (rect.left + document.body.scrollLeft); - if (duration) seekAbs((relX / target.offsetWidth) * duration); - }, [duration, seekAbs]); + seekAbs((relX / target.offsetWidth) * durationSafe); + }, [durationSafe, seekAbs]); const onWheel = useCallback((e) => { const { pixelX, pixelY } = normalizeWheel(e); @@ -245,7 +245,7 @@ const Timeline = memo(({ ))} {shouldShowKeyframes && !areKeyframesTooClose && keyframes.map((f) => ( -
+
))}
diff --git a/src/ffmpeg.js b/src/ffmpeg.js index 9f76d6e3..ff3ebc13 100644 --- a/src/ffmpeg.js +++ b/src/ffmpeg.js @@ -6,7 +6,7 @@ import sortBy from 'lodash/sortBy'; import moment from 'moment'; import i18n from 'i18next'; -import { formatDuration, getOutPath, transferTimestamps, filenamify } from './util'; +import { formatDuration, getOutPath, transferTimestamps, filenamify, isDurationValid } from './util'; const execa = window.require('execa'); const { join, extname } = window.require('path'); @@ -101,6 +101,7 @@ export function isCuttingStart(cutFrom) { } export function isCuttingEnd(cutTo, duration) { + if (!isDurationValid(duration)) return true; return cutTo < duration; } diff --git a/src/util.js b/src/util.js index e3faac8f..99514da5 100644 --- a/src/util.js +++ b/src/util.js @@ -159,3 +159,5 @@ export function doesPlayerSupportFile(streams) { export const isMasBuild = window.process.mas; export const isStoreBuild = isMasBuild || window.process.windowsStore; + +export const isDurationValid = (duration) => Number.isFinite(duration) && duration > 0;