1
0
mirror of https://github.com/mifi/lossless-cut.git synced 2024-11-22 18:32:34 +01:00

show commanded time in addition to player time

This commit is contained in:
Mikael Finstad 2020-02-20 13:45:38 +08:00
parent fc4c8873ac
commit b9c99afc5c

View File

@ -87,7 +87,7 @@ const App = memo(() => {
const [working, setWorking] = useState(false); const [working, setWorking] = useState(false);
const [dummyVideoPath, setDummyVideoPath] = useState(false); const [dummyVideoPath, setDummyVideoPath] = useState(false);
const [playing, setPlaying] = useState(false); const [playing, setPlaying] = useState(false);
const [currentTime, setCurrentTime] = useState(); const [playerTime, setPlayerTime] = useState();
const [duration, setDuration] = useState(); const [duration, setDuration] = useState();
const [cutSegments, setCutSegments] = useState([createSegment()]); const [cutSegments, setCutSegments] = useState([createSegment()]);
const [currentSegIndex, setCurrentSegIndex] = useState(0); const [currentSegIndex, setCurrentSegIndex] = useState(0);
@ -106,6 +106,7 @@ const App = memo(() => {
const [copyStreamIdsByFile, setCopyStreamIdsByFile] = useState({}); const [copyStreamIdsByFile, setCopyStreamIdsByFile] = useState({});
const [streamsSelectorShown, setStreamsSelectorShown] = useState(false); const [streamsSelectorShown, setStreamsSelectorShown] = useState(false);
const [zoom, setZoom] = useState(1); const [zoom, setZoom] = useState(1);
const [commandedTime, setCommandedTime] = useState(0);
// Preferences // Preferences
const [captureFormat, setCaptureFormat] = useState(configStore.get('captureFormat')); const [captureFormat, setCaptureFormat] = useState(configStore.get('captureFormat'));
@ -163,6 +164,7 @@ const App = memo(() => {
if (outVal > video.duration) outVal = video.duration; if (outVal > video.duration) outVal = video.duration;
video.currentTime = outVal; video.currentTime = outVal;
setCommandedTime(outVal);
} }
const seekRel = useCallback((val) => { const seekRel = useCallback((val) => {
@ -175,6 +177,7 @@ const App = memo(() => {
const resetState = useCallback(() => { const resetState = useCallback(() => {
const video = videoRef.current; const video = videoRef.current;
setCommandedTime(0);
video.currentTime = 0; video.currentTime = 0;
video.playbackRate = 1; video.playbackRate = 1;
@ -301,19 +304,22 @@ const App = memo(() => {
return formatDuration({ seconds: sec, fps: timecodeShowFrames ? detectedFps : undefined }); return formatDuration({ seconds: sec, fps: timecodeShowFrames ? detectedFps : undefined });
} }
const getCurrentTime = useCallback(() => (
playing ? playerTime : commandedTime), [commandedTime, playerTime, playing]);
const addCutSegment = useCallback(() => { const addCutSegment = useCallback(() => {
const cutStartTime = currentCutSeg.start; const cutStartTime = currentCutSeg.start;
const cutEndTime = currentCutSeg.end; const cutEndTime = currentCutSeg.end;
if (cutStartTime === undefined && cutEndTime === undefined) return; if (cutStartTime === undefined && cutEndTime === undefined) return;
const suggestedStart = currentTime; const suggestedStart = getCurrentTime();
const suggestedEnd = suggestedStart + 10; const suggestedEnd = suggestedStart + 10;
const cutSegmentsNew = [ const cutSegmentsNew = [
...cutSegments, ...cutSegments,
createSegment({ createSegment({
start: currentTime, start: suggestedStart,
end: suggestedEnd <= duration ? suggestedEnd : undefined, end: suggestedEnd <= duration ? suggestedEnd : undefined,
}), }),
]; ];
@ -322,32 +328,32 @@ const App = memo(() => {
setCutSegments(cutSegmentsNew); setCutSegments(cutSegmentsNew);
setCurrentSegIndex(currentSegIndexNew); setCurrentSegIndex(currentSegIndexNew);
}, [ }, [
currentCutSeg, cutSegments, currentTime, duration, currentCutSeg, cutSegments, getCurrentTime, duration,
]); ]);
const setCutStart = useCallback(() => { const setCutStart = useCallback(() => {
// https://github.com/mifi/lossless-cut/issues/168 // https://github.com/mifi/lossless-cut/issues/168
// If we are after the end of the last segment in the timeline, // If we are after the end of the last segment in the timeline,
// add a new segment that starts at currentTime // add a new segment that starts at playerTime
if (currentCutSeg.end != null if (currentCutSeg.end != null
&& currentTime > currentCutSeg.end) { && getCurrentTime() > currentCutSeg.end) {
addCutSegment(); addCutSegment();
} else { } else {
try { try {
setCutTime('start', currentTime); setCutTime('start', getCurrentTime());
} catch (err) { } catch (err) {
errorToast(err.message); errorToast(err.message);
} }
} }
}, [setCutTime, currentTime, currentCutSeg, addCutSegment]); }, [setCutTime, getCurrentTime, currentCutSeg, addCutSegment]);
const setCutEnd = useCallback(() => { const setCutEnd = useCallback(() => {
try { try {
setCutTime('end', currentTime); setCutTime('end', getCurrentTime());
} catch (err) { } catch (err) {
errorToast(err.message); errorToast(err.message);
} }
}, [setCutTime, currentTime]); }, [setCutTime, getCurrentTime]);
async function setOutputDir() { async function setOutputDir() {
const { filePaths } = await dialog.showOpenDialog({ properties: ['openDirectory'] }); const { filePaths } = await dialog.showOpenDialog({ properties: ['openDirectory'] });
@ -375,10 +381,10 @@ const App = memo(() => {
queue.add(async () => { queue.add(async () => {
if (!frameRenderEnabled) return; if (!frameRenderEnabled) return;
if (currentTime == null || !filePath) return; if (playerTime == null || !filePath) return;
try { try {
const framePathNew = await ffmpeg.renderFrame(currentTime, filePath, effectiveRotation); const framePathNew = await ffmpeg.renderFrame(playerTime, filePath, effectiveRotation);
setFramePath(framePathNew); setFramePath(framePathNew);
} catch (err) { } catch (err) {
console.error(err); console.error(err);
@ -391,7 +397,7 @@ const App = memo(() => {
throttledRender(); throttledRender();
}, [ }, [
filePath, currentTime, frameRenderEnabled, effectiveRotation, filePath, playerTime, frameRenderEnabled, effectiveRotation,
]); ]);
// Cleanup old frames // Cleanup old frames
@ -399,14 +405,17 @@ const App = memo(() => {
function onPlayingChange(val) { function onPlayingChange(val) {
setPlaying(val); setPlaying(val);
if (!val) videoRef.current.playbackRate = 1; if (!val) {
videoRef.current.playbackRate = 1;
setCommandedTime(videoRef.current.currentTime);
}
} }
function onTimeUpdate(e) { function onTimeUpdate(e) {
const { currentTime: ct } = e.target; const { currentTime } = e.target;
if (currentTime === ct) return; if (playerTime === currentTime) return;
setRotationPreviewRequested(false); // Reset this setRotationPreviewRequested(false); // Reset this
setCurrentTime(ct); setPlayerTime(currentTime);
} }
function increaseRotation() { function increaseRotation() {
@ -414,7 +423,7 @@ const App = memo(() => {
setRotationPreviewRequested(true); setRotationPreviewRequested(true);
} }
const offsetCurrentTime = (currentTime || 0) + startTimeOffset; const offsetCurrentTime = (getCurrentTime() || 0) + startTimeOffset;
const mergeFiles = useCallback(async ({ paths, allStreams }) => { const mergeFiles = useCallback(async ({ paths, allStreams }) => {
try { try {
@ -499,15 +508,18 @@ const App = memo(() => {
const durationSafe = duration || 1; const durationSafe = duration || 1;
const currentTimeWidth = 1; const currentTimeWidth = 1;
// Prevent it from overflowing (and causing scroll) when end of timeline // Prevent it from overflowing (and causing scroll) when end of timeline
const currentTimePos = currentTime !== undefined && currentTime < durationSafe ? `${(currentTime / durationSafe) * 100}%` : undefined;
const calculateTimelinePos = (time) => (time !== undefined && time < durationSafe ? `${(time / durationSafe) * 100}%` : undefined);
const currentTimePos = calculateTimelinePos(playerTime);
const commandedTimePos = calculateTimelinePos(commandedTime);
const zoomed = zoom > 1; const zoomed = zoom > 1;
useEffect(() => { useEffect(() => {
const { currentTime: ct } = videoRef.current; const { currentTime } = videoRef.current;
timelineScrollerSkipEventRef.current = true; timelineScrollerSkipEventRef.current = true;
if (zoom > 1) { if (zoom > 1) {
timelineScrollerRef.current.scrollLeft = (ct / durationSafe) timelineScrollerRef.current.scrollLeft = (currentTime / durationSafe)
* (timelineWrapperRef.current.offsetWidth - timelineScrollerRef.current.offsetWidth); * (timelineWrapperRef.current.offsetWidth - timelineScrollerRef.current.offsetWidth);
} }
}, [zoom, durationSafe]); }, [zoom, durationSafe]);
@ -680,12 +692,12 @@ const App = memo(() => {
return; return;
} }
try { try {
await captureFrame(customOutDir, filePath, videoRef.current, currentTime, captureFormat); await captureFrame(customOutDir, filePath, videoRef.current, playerTime, captureFormat);
} catch (err) { } catch (err) {
console.error(err); console.error(err);
errorToast('Failed to capture frame'); errorToast('Failed to capture frame');
} }
}, [filePath, currentTime, captureFormat, customOutDir, html5FriendlyPath, dummyVideoPath]); }, [filePath, playerTime, captureFormat, customOutDir, html5FriendlyPath, dummyVideoPath]);
const changePlaybackRate = useCallback((dir) => { const changePlaybackRate = useCallback((dir) => {
const video = videoRef.current; const video = videoRef.current;
@ -1427,7 +1439,8 @@ const App = memo(() => {
style={{ height: 36, width: `${zoom * 100}%`, position: 'relative', backgroundColor: '#444' }} style={{ height: 36, width: `${zoom * 100}%`, position: 'relative', backgroundColor: '#444' }}
ref={timelineWrapperRef} ref={timelineWrapperRef}
> >
{currentTimePos !== undefined && <div style={{ position: 'absolute', bottom: 0, top: 0, left: currentTimePos, zIndex: 3, backgroundColor: 'rgba(255, 255, 255, 1)', width: currentTimeWidth, pointerEvents: 'none' }} />} {currentTimePos !== undefined && <motion.div transition={{ type: 'spring', damping: 70, stiffness: 800 }} animate={{ left: currentTimePos }} style={{ position: 'absolute', bottom: 0, top: 0, zIndex: 3, backgroundColor: 'black', width: currentTimeWidth, pointerEvents: 'none' }} />}
{commandedTimePos !== undefined && <div style={{ left: commandedTimePos, position: 'absolute', bottom: 0, top: 0, zIndex: 4, backgroundColor: 'white', width: currentTimeWidth, pointerEvents: 'none' }} />}
{apparentCutSegments.map((seg, i) => ( {apparentCutSegments.map((seg, i) => (
<TimelineSeg <TimelineSeg