kopia lustrzana https://github.com/mifi/lossless-cut
rodzic
0d423b5968
commit
44ca797526
18
src/App.jsx
18
src/App.jsx
|
@ -308,12 +308,13 @@ const App = memo(() => {
|
|||
}, [seekRel, zoomedDuration]);
|
||||
|
||||
const shortStep = useCallback((direction) => {
|
||||
if (!detectedFps) return;
|
||||
// If we don't know fps, just assume 30 (for example if audio file)
|
||||
const fps = detectedFps || 30;
|
||||
|
||||
// try to align with frame
|
||||
const currentTimeNearestFrameNumber = getFrameCountRaw(detectedFps, videoRef.current.currentTime);
|
||||
const currentTimeNearestFrameNumber = getFrameCountRaw(fps, videoRef.current.currentTime);
|
||||
const nextFrame = currentTimeNearestFrameNumber + direction;
|
||||
seekAbs(nextFrame / detectedFps);
|
||||
seekAbs(nextFrame / fps);
|
||||
}, [seekAbs, detectedFps]);
|
||||
|
||||
// 360 means we don't modify rotation
|
||||
|
@ -475,8 +476,6 @@ const App = memo(() => {
|
|||
|
||||
const getFrameCount = useCallback((sec) => getFrameCountRaw(detectedFps, sec), [detectedFps]);
|
||||
|
||||
const getTimeFromFrameNum = useCallback((frameNum) => getTimeFromFrameNumRaw(detectedFps, frameNum), [detectedFps]);
|
||||
|
||||
const formatTimecode = useCallback(({ seconds, shorten }) => {
|
||||
if (timecodeFormat === 'frameCount') {
|
||||
const frameCount = getFrameCount(seconds);
|
||||
|
@ -1566,14 +1565,13 @@ const App = memo(() => {
|
|||
const haveVideoStream = !!videoStream;
|
||||
const haveAudioStream = !!audioStream;
|
||||
|
||||
const detectedFpsNew = haveVideoStream ? getStreamFps(videoStream) : undefined;
|
||||
|
||||
const copyStreamIdsForPathNew = fromPairs(fileMeta.streams.map((stream) => [
|
||||
stream.index, shouldCopyStreamByDefault(stream),
|
||||
]));
|
||||
|
||||
if (timecode) setStartTimeOffset(timecode);
|
||||
if (detectedFpsNew != null) setDetectedFps(detectedFpsNew);
|
||||
|
||||
setDetectedFps(haveVideoStream ? getStreamFps(videoStream) : undefined);
|
||||
|
||||
if (isAudioDefinitelyNotSupported(fileMeta.streams)) {
|
||||
toast.fire({ icon: 'info', text: i18n.t('The audio track is not supported. You can convert to a supported format from the menu') });
|
||||
|
@ -2150,7 +2148,7 @@ const App = memo(() => {
|
|||
if (!checkFileOpened()) return;
|
||||
|
||||
try {
|
||||
const edl = await askForEdlImport({ type, getTimeFromFrameNum });
|
||||
const edl = await askForEdlImport({ type, fps: detectedFps });
|
||||
if (edl.length > 0) loadCutSegments(edl, true);
|
||||
} catch (err) {
|
||||
handleError(err);
|
||||
|
@ -2187,7 +2185,7 @@ const App = memo(() => {
|
|||
const entries = Object.entries(action);
|
||||
entries.forEach(([key, value]) => electron.ipcRenderer.on(key, value));
|
||||
return () => entries.forEach(([key, value]) => electron.ipcRenderer.removeListener(key, value));
|
||||
}, [apparentCutSegments, askSetStartTimeOffset, checkFileOpened, clearSegments, closeBatch, closeFileWithConfirm, concatCurrentBatch, createFixedDurationSegments, createNumSegments, customOutDir, cutSegments, extractAllStreams, fileFormat, filePath, fillSegmentsGaps, getFrameCount, getTimeFromFrameNum, invertAllSegments, loadCutSegments, loadMedia, openSendReportDialogWithState, reorderSegsByStartTime, setWorking, shiftAllSegmentTimes, shuffleSegments, toggleHelp, toggleSettings, tryFixInvalidDuration, userHtml5ifyCurrentFile, userOpenFiles]);
|
||||
}, [apparentCutSegments, askSetStartTimeOffset, checkFileOpened, clearSegments, closeBatch, closeFileWithConfirm, concatCurrentBatch, createFixedDurationSegments, createNumSegments, customOutDir, cutSegments, detectedFps, extractAllStreams, fileFormat, filePath, fillSegmentsGaps, getFrameCount, invertAllSegments, loadCutSegments, loadMedia, openSendReportDialogWithState, reorderSegsByStartTime, setWorking, shiftAllSegmentTimes, shuffleSegments, toggleHelp, toggleSettings, tryFixInvalidDuration, userHtml5ifyCurrentFile, userOpenFiles]);
|
||||
|
||||
const showAddStreamSourceDialog = useCallback(async () => {
|
||||
try {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import JSON5 from 'json5';
|
||||
import i18n from 'i18next';
|
||||
|
||||
import { parseCuesheet, parseXmeml, parseCsv, parsePbf, parseMplayerEdl, formatCsvHuman, formatTsv, formatCsvFrames, formatCsvSeconds } from './edlFormats';
|
||||
import { parseCuesheet, parseXmeml, parseCsv, parsePbf, parseMplayerEdl, formatCsvHuman, formatTsv, formatCsvFrames, formatCsvSeconds, getTimeFromFrameNum } from './edlFormats';
|
||||
import { askForYouTubeInput } from './dialogs';
|
||||
|
||||
const fs = window.require('fs-extra');
|
||||
|
@ -15,8 +15,9 @@ export async function loadCsvSeconds(path) {
|
|||
return parseCsv(await fs.readFile(path, 'utf-8'));
|
||||
}
|
||||
|
||||
export async function loadCsvFrames(path, getTimeFromFrameNum) {
|
||||
return parseCsv(await fs.readFile(path, 'utf-8'), (frameNum) => getTimeFromFrameNum(frameNum));
|
||||
export async function loadCsvFrames(path, fps) {
|
||||
if (!fps) throw new Error('The loaded file has an unknown framerate');
|
||||
return parseCsv(await fs.readFile(path, 'utf-8'), (frameNum) => getTimeFromFrameNum(fps, frameNum));
|
||||
}
|
||||
|
||||
export async function loadXmeml(path) {
|
||||
|
@ -64,9 +65,10 @@ export async function loadLlcProject(path) {
|
|||
return JSON5.parse(await fs.readFile(path));
|
||||
}
|
||||
|
||||
export async function readEdlFile({ type, path, getTimeFromFrameNum }) {
|
||||
|
||||
export async function readEdlFile({ type, path, fps }) {
|
||||
if (type === 'csv') return loadCsvSeconds(path);
|
||||
if (type === 'csv-frames') return loadCsvFrames(path, getTimeFromFrameNum);
|
||||
if (type === 'csv-frames') return loadCsvFrames(path, fps);
|
||||
if (type === 'xmeml') return loadXmeml(path);
|
||||
if (type === 'cue') return loadCue(path);
|
||||
if (type === 'pbf') return loadPbf(path);
|
||||
|
@ -78,7 +80,7 @@ export async function readEdlFile({ type, path, getTimeFromFrameNum }) {
|
|||
throw new Error('Invalid EDL type');
|
||||
}
|
||||
|
||||
export async function askForEdlImport({ type, getTimeFromFrameNum }) {
|
||||
export async function askForEdlImport({ type, fps }) {
|
||||
if (type === 'youtube') return askForYouTubeInput();
|
||||
|
||||
let filters;
|
||||
|
@ -91,7 +93,7 @@ export async function askForEdlImport({ type, getTimeFromFrameNum }) {
|
|||
|
||||
const { canceled, filePaths } = await dialog.showOpenDialog({ properties: ['openFile'], filters });
|
||||
if (canceled || filePaths.length < 1) return [];
|
||||
return readEdlFile({ type, path: filePaths[0], getTimeFromFrameNum });
|
||||
return readEdlFile({ type, path: filePaths[0], fps });
|
||||
}
|
||||
|
||||
export async function exportEdlFile({ type, cutSegments, filePath, getFrameCount }) {
|
||||
|
|
|
@ -391,6 +391,7 @@ function useFfmpegOperations({ filePath, enableTransferTimestamps }) {
|
|||
if (!needsSmartCut) return smartCutMainPartOutPath;
|
||||
|
||||
try {
|
||||
if (!detectedFps) throw new Error('Smart cut is not possible when FPS is unknown');
|
||||
const frameDuration = 1 / detectedFps;
|
||||
const encodeCutTo = Math.max(desiredCutFrom + frameDuration, smartCutFrom - frameDuration); // Subtract one frame so we don't end up with duplicates when concating, and make sure we don't create a 0 length segment
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue