mirror of
https://github.com/mifi/lossless-cut.git
synced 2024-11-22 18:32:34 +01:00
implement go to timecode #625
inputted value `g` key or right click timeline context menu
This commit is contained in:
parent
9e05ec3498
commit
87df1c5e5f
24
src/App.jsx
24
src/App.jsx
@ -1648,6 +1648,18 @@ const App = memo(() => {
|
||||
}
|
||||
}, [customOutDir, filePath, html5ifyAndLoad, hasVideo, hasAudio, rememberConvertToSupportedFormat, setWorking]);
|
||||
|
||||
const goToTimecode = useCallback(async () => {
|
||||
if (!filePath) return;
|
||||
const timeCode = await promptTimeOffset({
|
||||
initialValue: formatDuration({ seconds: commandedTimeRef.current }),
|
||||
title: i18n.t('Seek to timecode'),
|
||||
});
|
||||
|
||||
if (timeCode === undefined) return;
|
||||
|
||||
seekAbs(timeCode);
|
||||
}, [filePath, seekAbs]);
|
||||
|
||||
|
||||
// TODO split up?
|
||||
useEffect(() => {
|
||||
@ -1704,6 +1716,7 @@ const App = memo(() => {
|
||||
mousetrap.bind('d', () => cleanupFiles());
|
||||
mousetrap.bind('b', () => splitCurrentSegment());
|
||||
mousetrap.bind('r', () => increaseRotation());
|
||||
mousetrap.bind('g', () => goToTimecode());
|
||||
|
||||
mousetrap.bind('left', () => seekBackwards());
|
||||
mousetrap.bind('left', () => seekReset(), 'keyup');
|
||||
@ -1746,7 +1759,7 @@ const App = memo(() => {
|
||||
setCutEnd, setCutStart, seekRel, seekRelPercent, shortStep, cleanupFiles, jumpSeg,
|
||||
seekClosestKeyframe, zoomRel, toggleComfortZoom, splitCurrentSegment, exportConfirmVisible,
|
||||
increaseRotation, jumpCutStart, jumpCutEnd, cutSegmentsHistory, keyboardSeekAccFactor,
|
||||
keyboardNormalSeekSpeed, onLabelSegmentPress, currentSegIndexSafe, batchFileJump,
|
||||
keyboardNormalSeekSpeed, onLabelSegmentPress, currentSegIndexSafe, batchFileJump, goToTimecode,
|
||||
]);
|
||||
|
||||
const onVideoError = useCallback(async () => {
|
||||
@ -1797,9 +1810,11 @@ const App = memo(() => {
|
||||
}
|
||||
|
||||
async function setStartOffset() {
|
||||
const newStartTimeOffset = await promptTimeOffset(
|
||||
startTimeOffset !== undefined ? formatDuration({ seconds: startTimeOffset }) : undefined,
|
||||
);
|
||||
const newStartTimeOffset = await promptTimeOffset({
|
||||
initialValue: startTimeOffset !== undefined ? formatDuration({ seconds: startTimeOffset }) : undefined,
|
||||
title: i18n.t('Set custom start time offset'),
|
||||
text: i18n.t('Instead of video apparently starting at 0, you can offset by a specified value. This only applies to the preview inside LosslessCut and does not modify the file in any way. (Useful for viewing/cutting videos according to timecodes)'),
|
||||
});
|
||||
|
||||
if (newStartTimeOffset === undefined) return;
|
||||
|
||||
@ -2415,6 +2430,7 @@ const App = memo(() => {
|
||||
playing={playing}
|
||||
isFileOpened={isFileOpened}
|
||||
onWheel={onTimelineWheel}
|
||||
goToTimecode={goToTimecode}
|
||||
/>
|
||||
|
||||
<TimelineControls
|
||||
|
@ -60,6 +60,7 @@ const HelpSheet = memo(({ visible, onTogglePress, ffmpegCommandLog, currentCutSe
|
||||
<div><kbd>CTRL</kbd> / <kbd>CMD</kbd> + <kbd>→</kbd> {t('Seek forward 1% of timeline at current zoom')}</div>
|
||||
<div style={{ lineHeight: 1.7 }}><SegmentCutpointButton currentCutSeg={currentCutSeg} side="start" Icon={FaStepBackward} style={{ verticalAlign: 'middle' }} />, <kbd>SHIFT</kbd> + <kbd>←</kbd> {t('Jump to cut start')}</div>
|
||||
<div style={{ lineHeight: 1.7 }}><SegmentCutpointButton currentCutSeg={currentCutSeg} side="end" Icon={FaStepForward} style={{ verticalAlign: 'middle' }} />, <kbd>SHIFT</kbd> + <kbd>→</kbd> {t('Jump to cut end')}</div>
|
||||
<div><kbd>G</kbd> {t('Seek to timecode')}</div>
|
||||
|
||||
<h2>{t('Segments and cut points')}</h2>
|
||||
|
||||
|
@ -7,6 +7,7 @@ import { FaCaretDown, FaCaretUp } from 'react-icons/fa';
|
||||
|
||||
import TimelineSeg from './TimelineSeg';
|
||||
import BetweenSegments from './BetweenSegments';
|
||||
import useContextMenu from './hooks/useContextMenu';
|
||||
|
||||
|
||||
import { timelineBackground } from './colors';
|
||||
@ -41,14 +42,14 @@ const Waveform = memo(({ calculateTimelinePercent, durationSafe, waveform, zoom,
|
||||
|
||||
const CommandedTime = memo(({ commandedTimePercent }) => {
|
||||
const color = 'white';
|
||||
const commonStyle = { left: commandedTimePercent, position: 'absolute', zIndex: 4, pointerEvents: 'none' }
|
||||
const commonStyle = { left: commandedTimePercent, position: 'absolute', zIndex: 4, pointerEvents: 'none' };
|
||||
return (
|
||||
<>
|
||||
<FaCaretDown style={{ ...commonStyle, top: 0, color, fontSize: 14, marginLeft: -7, marginTop: -6 }} />
|
||||
<div style={{ ...commonStyle, bottom: 0, top: 0, backgroundColor: color, width: currentTimeWidth }} />
|
||||
<FaCaretUp style={{ ...commonStyle, bottom: 0, color, fontSize: 14, marginLeft: -7, marginBottom: -5 }} />
|
||||
</>
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
const Timeline = memo(({
|
||||
@ -57,7 +58,7 @@ const Timeline = memo(({
|
||||
setCurrentSegIndex, currentSegIndexSafe, invertCutSegments, inverseCutSegments, formatTimecode,
|
||||
waveform, shouldShowWaveform, shouldShowKeyframes, timelineHeight, thumbnails,
|
||||
onZoomWindowStartTimeChange, waveformEnabled, thumbnailsEnabled,
|
||||
playing, isFileOpened, onWheel, commandedTimeRef,
|
||||
playing, isFileOpened, onWheel, commandedTimeRef, goToTimecode,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@ -191,6 +192,10 @@ const Timeline = memo(({
|
||||
const onMouseMove = useCallback((e) => setHoveringTime(getMouseTimelinePos(e.nativeEvent)), [getMouseTimelinePos]);
|
||||
const onMouseOut = useCallback(() => setHoveringTime(), []);
|
||||
|
||||
useContextMenu(timelineScrollerRef, [
|
||||
{ label: t('Seek to timecode'), click: goToTimecode },
|
||||
]);
|
||||
|
||||
return (
|
||||
// eslint-disable-next-line jsx-a11y/mouse-events-have-key-events
|
||||
<Hammer
|
||||
|
@ -21,12 +21,12 @@ const { dialog, app } = electron.remote;
|
||||
|
||||
const ReactSwal = withReactContent(Swal);
|
||||
|
||||
export async function promptTimeOffset(inputValue) {
|
||||
export async function promptTimeOffset({ initialValue, title, text }) {
|
||||
const { value } = await Swal.fire({
|
||||
title: i18n.t('Set custom start time offset'),
|
||||
text: i18n.t('Instead of video apparently starting at 0, you can offset by a specified value. This only applies to the preview inside LosslessCut and does not modify the file in any way. (Useful for viewing/cutting videos according to timecodes)'),
|
||||
title,
|
||||
text,
|
||||
input: 'text',
|
||||
inputValue: inputValue || '',
|
||||
inputValue: initialValue || '',
|
||||
showCancelButton: true,
|
||||
inputPlaceholder: '00:00:00.000',
|
||||
});
|
||||
@ -37,7 +37,7 @@ export async function promptTimeOffset(inputValue) {
|
||||
|
||||
const duration = parseDuration(value);
|
||||
// Invalid, try again
|
||||
if (duration === undefined) return promptTimeOffset(value);
|
||||
if (duration === undefined) return promptTimeOffset({ initialValue: value, title, text });
|
||||
|
||||
return duration;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user