mirror of
https://github.com/mifi/lossless-cut.git
synced 2024-11-22 02:12:30 +01:00
suppor timecode running time #211
This commit is contained in:
parent
4d5d6aad86
commit
085b72591e
@ -55,7 +55,7 @@ const TimelineSeg = ({
|
||||
role="button"
|
||||
tabIndex="0"
|
||||
onClick={onThisSegClick}
|
||||
title={`${formatDuration(cutEndTime - cutStartTime)}`}
|
||||
title={`${formatDuration({ seconds: cutEndTime - cutStartTime })}`}
|
||||
/>
|
||||
)}
|
||||
{cutEndTime !== undefined && (
|
||||
|
@ -23,7 +23,7 @@ async function captureFrame(customOutDir, filePath, video, currentTime, captureF
|
||||
const buf = getFrameFromVideo(video, captureFormat);
|
||||
|
||||
const ext = mime.extension(buf.mimetype);
|
||||
const time = formatDuration(currentTime, true);
|
||||
const time = formatDuration({ seconds: currentTime, fileNameFriendly: true });
|
||||
|
||||
const outPath = getOutPath(customOutDir, filePath, `${time}.${ext}`);
|
||||
await fs.writeFileAsync(outPath, buf);
|
||||
|
@ -136,7 +136,7 @@ async function cutMultiple({
|
||||
// eslint-disable-next-line no-restricted-syntax,no-unused-vars
|
||||
for (const { cutFrom, cutTo, cutToApparent } of segments) {
|
||||
const ext = path.extname(filePath) || `.${format}`;
|
||||
const cutSpecification = `${formatDuration(cutFrom, true)}-${formatDuration(cutToApparent, true)}`;
|
||||
const cutSpecification = `${formatDuration({ seconds: cutFrom, fileNameFriendly: true })}-${formatDuration({ seconds: cutToApparent, fileNameFriendly: true })}`;
|
||||
|
||||
const outPath = getOutPath(customOutDir, filePath, `${cutSpecification}${ext}`);
|
||||
|
||||
|
@ -100,6 +100,7 @@ const App = memo(() => {
|
||||
const [rotationPreviewRequested, setRotationPreviewRequested] = useState(false);
|
||||
const [filePath, setFilePath] = useState('');
|
||||
const [playbackRate, setPlaybackRate] = useState(1);
|
||||
const [detectedFps, setDetectedFps] = useState();
|
||||
|
||||
// Global state
|
||||
const [stripAudio, setStripAudio] = useState(false);
|
||||
@ -109,6 +110,7 @@ const App = memo(() => {
|
||||
const [keyframeCut, setKeyframeCut] = useState(true);
|
||||
const [autoMerge, setAutoMerge] = useState(false);
|
||||
const [helpVisible, setHelpVisible] = useState(false);
|
||||
const [timecodeShowFrames, setTimecodeShowFrames] = useState(false);
|
||||
|
||||
const resetState = useCallback(() => {
|
||||
const video = getVideo();
|
||||
@ -134,6 +136,7 @@ const App = memo(() => {
|
||||
setRotationPreviewRequested(false);
|
||||
setFilePath(''); // Setting video src="" prevents memory leak in chromium
|
||||
setPlaybackRate(1);
|
||||
setDetectedFps();
|
||||
}, []);
|
||||
|
||||
useEffect(() => () => {
|
||||
@ -148,6 +151,10 @@ const App = memo(() => {
|
||||
setCutSegments(cloned);
|
||||
}, [currentSeg, cutSegments]);
|
||||
|
||||
function formatTimecode(sec) {
|
||||
return formatDuration({ seconds: sec, fps: timecodeShowFrames ? detectedFps : undefined });
|
||||
}
|
||||
|
||||
const setCutStart = useCallback(() => setCutTime('start', currentTime), [setCutTime, currentTime]);
|
||||
const setCutEnd = useCallback(() => setCutTime('end', currentTime), [setCutTime, currentTime]);
|
||||
|
||||
@ -462,7 +469,18 @@ const App = memo(() => {
|
||||
return;
|
||||
}
|
||||
|
||||
const { streams: newStreams } = await ffmpeg.getAllStreams(fp);
|
||||
const { streams } = await ffmpeg.getAllStreams(fp);
|
||||
// console.log('streams', streams);
|
||||
|
||||
streams.find((stream) => {
|
||||
const match = typeof stream.avg_frame_rate === 'string' && stream.avg_frame_rate.match(/^([0-9]+)\/([0-9]+)$/);
|
||||
if (stream.codec_type === 'video' && match) {
|
||||
const fps = parseInt(match[1], 10) / parseInt(match[2], 10);
|
||||
setDetectedFps(fps);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
setFileNameTitle(fp);
|
||||
setFilePath(fp);
|
||||
@ -473,7 +491,7 @@ const App = memo(() => {
|
||||
setHtml5FriendlyPath(html5FriendlyPathRequested);
|
||||
} else if (
|
||||
!(await checkExistingHtml5FriendlyFile(fp, 'slow') || await checkExistingHtml5FriendlyFile(fp, 'fast'))
|
||||
&& !doesPlayerSupportFile(newStreams)
|
||||
&& !doesPlayerSupportFile(streams)
|
||||
) {
|
||||
await createDummyVideo(fp);
|
||||
}
|
||||
@ -573,7 +591,7 @@ const App = memo(() => {
|
||||
|
||||
async function setStartOffset() {
|
||||
const newStartTimeOffset = await promptTimeOffset(
|
||||
startTimeOffset !== undefined ? formatDuration(startTimeOffset) : undefined,
|
||||
startTimeOffset !== undefined ? formatDuration({ seconds: startTimeOffset }) : undefined,
|
||||
);
|
||||
|
||||
if (newStartTimeOffset === undefined) return;
|
||||
@ -663,7 +681,7 @@ const App = memo(() => {
|
||||
onChange={e => handleCutTimeInput(e.target.value)}
|
||||
value={isCutTimeManualSet()
|
||||
? cutTimeManual
|
||||
: formatDuration(cutTime + startTimeOffset)}
|
||||
: formatDuration({ seconds: cutTime + startTimeOffset })}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -769,7 +787,7 @@ const App = memo(() => {
|
||||
/>
|
||||
))}
|
||||
|
||||
<div id="current-time-display">{formatDuration(offsetCurrentTime)}</div>
|
||||
<div id="current-time-display">{formatTimecode(offsetCurrentTime)}</div>
|
||||
</div>
|
||||
</Hammer>
|
||||
|
||||
@ -885,6 +903,14 @@ const App = memo(() => {
|
||||
{round(playbackRate, 1) || 1}
|
||||
</span>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
title={`Average FPS (${timecodeShowFrames ? 'FPS fraction' : 'millisecond fraction'})`}
|
||||
onClick={withBlur(() => setTimecodeShowFrames(v => !v))}
|
||||
>
|
||||
{detectedFps ? round(detectedFps, 1) || 1 : '-'}
|
||||
</button>
|
||||
|
||||
<button
|
||||
style={{ ...infoSpanStyle, background: segBgColor, color: 'white' }}
|
||||
disabled={cutSegments.length < 2}
|
||||
|
@ -6,7 +6,7 @@ const swal = require('sweetalert2');
|
||||
const randomColor = require('./random-color');
|
||||
|
||||
|
||||
function formatDuration(_seconds, fileNameFriendly) {
|
||||
function formatDuration({ seconds: _seconds, fileNameFriendly, fps }) {
|
||||
const seconds = _seconds || 0;
|
||||
const minutes = seconds / 60;
|
||||
const hours = minutes / 60;
|
||||
@ -14,7 +14,9 @@ function formatDuration(_seconds, fileNameFriendly) {
|
||||
const hoursPadded = _.padStart(Math.floor(hours), 2, '0');
|
||||
const minutesPadded = _.padStart(Math.floor(minutes % 60), 2, '0');
|
||||
const secondsPadded = _.padStart(Math.floor(seconds) % 60, 2, '0');
|
||||
const msPadded = _.padStart(Math.floor((seconds - Math.floor(seconds)) * 1000), 3, '0');
|
||||
const msPadded = fps != null
|
||||
? _.padStart(Math.floor((seconds * fps) % fps), 2, '0')
|
||||
: _.padStart(Math.floor((seconds - Math.floor(seconds)) * 1000), 3, '0');
|
||||
|
||||
// Be nice to filenames and use .
|
||||
const delim = fileNameFriendly ? '.' : ':';
|
||||
|
Loading…
Reference in New Issue
Block a user