diff --git a/src/App.jsx b/src/App.jsx index 6a25a018..db598da5 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -378,6 +378,16 @@ const App = memo(() => { return formatDuration({ seconds, shorten, fileNameFriendly }); }, [detectedFps, timecodeFormat, getFrameCount]); + const formatTimeAndFrames = useCallback((seconds) => { + const frameCount = getFrameCount(seconds); + + const timeStr = timecodeFormat === 'timecodeWithFramesFraction' + ? formatDuration({ seconds, fps: detectedFps }) + : formatDuration({ seconds }); + + return `${timeStr} (${frameCount ?? '0'})`; + }, [detectedFps, timecodeFormat, getFrameCount]); + const { captureFrameFromTag, captureFrameFromFfmpeg, captureFramesRange } = useFrameCapture({ formatTimecode, treatOutputFileModifiedTimeAsStart }); // const getSafeCutTime = useCallback((cutTime, next) => ffmpeg.getSafeCutTime(neighbouringFrames, cutTime, next), [neighbouringFrames]); @@ -2365,6 +2375,7 @@ const App = memo(() => { currentSegIndexSafe={currentSegIndexSafe} inverseCutSegments={inverseCutSegments} formatTimecode={formatTimecode} + formatTimeAndFrames={formatTimeAndFrames} onZoomWindowStartTimeChange={setZoomWindowStartTime} playing={playing} isFileOpened={isFileOpened} diff --git a/src/Timeline.jsx b/src/Timeline.jsx index 9660127c..efaeac66 100644 --- a/src/Timeline.jsx +++ b/src/Timeline.jsx @@ -55,7 +55,7 @@ const CommandedTime = memo(({ commandedTimePercent }) => { const Timeline = memo(({ durationSafe, startTimeOffset, playerTime, commandedTime, relevantTime, zoom, neighbouringKeyFrames, seekAbs, apparentCutSegments, - setCurrentSegIndex, currentSegIndexSafe, inverseCutSegments, formatTimecode, + setCurrentSegIndex, currentSegIndexSafe, inverseCutSegments, formatTimecode, formatTimeAndFrames, waveforms, shouldShowWaveform, shouldShowKeyframes, thumbnails, onZoomWindowStartTimeChange, waveformEnabled, showThumbnails, playing, isFileOpened, onWheel, commandedTimeRef, goToTimecode, isSegmentSelected, @@ -330,7 +330,7 @@ const Timeline = memo(({
- {formatTimecode({ seconds: displayTime })}{isZoomed ? ` ${displayTimePercent}` : ''} + {formatTimeAndFrames(displayTime)}{isZoomed ? ` ${displayTimePercent}` : ''}
diff --git a/src/util/duration.js b/src/util/duration.js index 14ac9683..21a07ea0 100644 --- a/src/util/duration.js +++ b/src/util/duration.js @@ -31,7 +31,7 @@ export function formatDuration({ seconds: totalSecondsIn, fileNameFriendly, show let fraction = ''; if (showFraction && !(shorten && remainder === 0)) { const numDigits = fps != null ? 2 : 3; - fraction = `.${padStart(remainder, numDigits, '0')}`; + fraction = `.${padStart(Math.floor(remainder), numDigits, '0')}`; } return `${sign}${hoursPart}${minutesPadded}${delim}${secondsPadded}${fraction}`; diff --git a/src/util/duration.test.js b/src/util/duration.test.js index 664a6745..501f03ce 100644 --- a/src/util/duration.test.js +++ b/src/util/duration.test.js @@ -7,6 +7,7 @@ it('should format duration properly', () => { expect(formatDuration({ seconds: 1.5, fps: 30, shorten: true })).toBe('0:01.15'); expect(formatDuration({ seconds: 1.5, fps: 30, fileNameFriendly: true })).toBe('00.00.01.15'); expect(formatDuration({ seconds: -1.5, fps: 30 })).toBe('-00:00:01.15'); + expect(formatDuration({ seconds: 32.670476, fps: 23.976 })).toBe('00:00:32.15'); expect(formatDuration({ seconds: 101.5 })).toBe('00:01:41.500'); expect(formatDuration({ seconds: 101.5, shorten: true })).toBe('1:41.500'); expect(formatDuration({ seconds: 10000 })).toBe('02:46:40.000');