mirror of
https://github.com/mifi/lossless-cut.git
synced 2024-11-22 02:12:30 +01:00
parent
65f0ad7c96
commit
b326fbaeaf
33
src/App.jsx
33
src/App.jsx
@ -1276,19 +1276,34 @@ const App = memo(() => {
|
||||
}
|
||||
}, [filePath, getRelevantTime, usingPreviewFile, captureFrameMethod, captureFrameFromFfmpeg, customOutDir, captureFormat, captureFrameQuality, captureFrameFromTag, hideAllNotifications]);
|
||||
|
||||
const extractSegmentFramesAsImages = useCallback(async (index) => {
|
||||
const extractSegmentFramesAsImages = useCallback(async (segIds) => {
|
||||
if (!filePath || detectedFps == null || workingRef.current) return;
|
||||
const { start, end } = apparentCutSegments[index];
|
||||
const segmentNumFrames = getFrameCount(end - start);
|
||||
const captureFramesResponse = await askExtractFramesAsImages({ segmentNumFrames, fps: detectedFps });
|
||||
const segments = apparentCutSegments.filter((seg) => segIds.includes(seg.segId));
|
||||
const segmentsNumFrames = segments.reduce((acc, { start, end }) => acc + getFrameCount(end - start) ?? 0, 0);
|
||||
const captureFramesResponse = await askExtractFramesAsImages({ segmentsNumFrames, plural: segments.length > 1, fps: detectedFps });
|
||||
if (captureFramesResponse == null) return;
|
||||
|
||||
try {
|
||||
setWorking(i18n.t('Extracting frames'));
|
||||
console.log('Extracting frames as images', { segIds, captureFramesResponse });
|
||||
|
||||
setCutProgress(0);
|
||||
const outPath = await captureFramesRange({ customOutDir, filePath, fps: detectedFps, fromTime: start, toTime: end, estimatedMaxNumFiles: captureFramesResponse.estimatedMaxNumFiles, captureFormat, quality: captureFrameQuality, filter: captureFramesResponse.filter, outputTimestamps: captureFrameFileNameFormat === 'timestamp', onProgress: setCutProgress });
|
||||
if (!hideAllNotifications) openDirToast({ icon: 'success', filePath: outPath, text: i18n.t('Frames extracted to: {{path}}', { path: outputDir }) });
|
||||
|
||||
let lastOutPath;
|
||||
let totalProgress = 0;
|
||||
|
||||
const onProgress = (progress) => {
|
||||
totalProgress += progress;
|
||||
setCutProgress(totalProgress / segments.length);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const segment of segments) {
|
||||
const { start, end } = segment;
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
lastOutPath = await captureFramesRange({ customOutDir, filePath, fps: detectedFps, fromTime: start, toTime: end, estimatedMaxNumFiles: captureFramesResponse.estimatedMaxNumFiles, captureFormat, quality: captureFrameQuality, filter: captureFramesResponse.filter, outputTimestamps: captureFrameFileNameFormat === 'timestamp', onProgress });
|
||||
}
|
||||
if (!hideAllNotifications) openDirToast({ icon: 'success', filePath: lastOutPath, text: i18n.t('Frames extracted to: {{path}}', { path: outputDir }) });
|
||||
} catch (err) {
|
||||
handleError(err);
|
||||
} finally {
|
||||
@ -1297,7 +1312,8 @@ const App = memo(() => {
|
||||
}
|
||||
}, [apparentCutSegments, captureFormat, captureFrameFileNameFormat, captureFrameQuality, captureFramesRange, customOutDir, detectedFps, filePath, getFrameCount, hideAllNotifications, outputDir, setWorking]);
|
||||
|
||||
const extractCurrentSegmentFramesAsImages = useCallback(() => extractSegmentFramesAsImages(currentSegIndexSafe), [currentSegIndexSafe, extractSegmentFramesAsImages]);
|
||||
const extractCurrentSegmentFramesAsImages = useCallback(() => extractSegmentFramesAsImages([currentCutSeg?.segId]), [currentCutSeg?.segId, extractSegmentFramesAsImages]);
|
||||
const extractSelectedSegmentsFramesAsImages = useCallback(() => extractSegmentFramesAsImages(selectedSegments.map((seg) => seg.segId)), [extractSegmentFramesAsImages, selectedSegments]);
|
||||
|
||||
const changePlaybackRate = useCallback((dir, rateMultiplier) => {
|
||||
if (canvasPlayerEnabled) {
|
||||
@ -1920,6 +1936,7 @@ const App = memo(() => {
|
||||
toggleLastCommands: () => { toggleLastCommands(); return false; },
|
||||
export: onExportPress,
|
||||
extractCurrentSegmentFramesAsImages,
|
||||
extractSelectedSegmentsFramesAsImages,
|
||||
reorderSegsByStartTime,
|
||||
invertAllSegments,
|
||||
fillSegmentsGaps,
|
||||
@ -1995,7 +2012,7 @@ const App = memo(() => {
|
||||
if (match) return bubble;
|
||||
|
||||
return true; // bubble the event
|
||||
}, [addSegment, alignSegmentTimesToKeyframes, askSetStartTimeOffset, batchFileJump, batchOpenSelectedFile, captureSnapshot, captureSnapshotAsCoverArt, changePlaybackRate, cleanupFilesDialog, clearSegments, closeBatch, closeExportConfirm, combineOverlappingSegments, combineSelectedSegments, concatCurrentBatch, concatDialogVisible, convertFormatBatch, copySegmentsToClipboard, createFixedDurationSegments, createNumSegments, createRandomSegments, currentSegIndexSafe, cutSegmentsHistory, deselectAllSegments, duplicateCurrentSegment, exportConfirmVisible, extractAllStreams, extractCurrentSegmentFramesAsImages, fillSegmentsGaps, goToTimecode, increaseRotation, invertAllSegments, invertSelectedSegments, jumpCutEnd, jumpCutStart, jumpSeg, jumpTimelineEnd, jumpTimelineStart, keyboardNormalSeekSpeed, keyboardSeekAccFactor, keyboardShortcutsVisible, onExportConfirm, onExportPress, onLabelSegment, pause, play, removeCutSegment, removeSelectedSegments, reorderSegsByStartTime, seekClosestKeyframe, seekRel, seekRelPercent, selectAllSegments, selectOnlyCurrentSegment, setCutEnd, setCutStart, setPlaybackVolume, shiftAllSegmentTimes, shortStep, shuffleSegments, splitCurrentSegment, timelineToggleComfortZoom, toggleCaptureFormat, toggleCurrentSegmentSelected, toggleKeyboardShortcuts, toggleKeyframeCut, toggleLastCommands, toggleLoopSelectedSegments, togglePlay, toggleSegmentsList, toggleStreamsSelector, toggleStripAudio, tryFixInvalidDuration, userHtml5ifyCurrentFile, zoomRel]);
|
||||
}, [addSegment, alignSegmentTimesToKeyframes, askSetStartTimeOffset, batchFileJump, batchOpenSelectedFile, captureSnapshot, captureSnapshotAsCoverArt, changePlaybackRate, cleanupFilesDialog, clearSegments, closeBatch, closeExportConfirm, combineOverlappingSegments, combineSelectedSegments, concatCurrentBatch, concatDialogVisible, convertFormatBatch, copySegmentsToClipboard, createFixedDurationSegments, createNumSegments, createRandomSegments, currentSegIndexSafe, cutSegmentsHistory, deselectAllSegments, duplicateCurrentSegment, exportConfirmVisible, extractAllStreams, extractCurrentSegmentFramesAsImages, extractSelectedSegmentsFramesAsImages, fillSegmentsGaps, goToTimecode, increaseRotation, invertAllSegments, invertSelectedSegments, jumpCutEnd, jumpCutStart, jumpSeg, jumpTimelineEnd, jumpTimelineStart, keyboardNormalSeekSpeed, keyboardSeekAccFactor, keyboardShortcutsVisible, onExportConfirm, onExportPress, onLabelSegment, pause, play, removeCutSegment, removeSelectedSegments, reorderSegsByStartTime, seekClosestKeyframe, seekRel, seekRelPercent, selectAllSegments, selectOnlyCurrentSegment, setCutEnd, setCutStart, setPlaybackVolume, shiftAllSegmentTimes, shortStep, shuffleSegments, splitCurrentSegment, timelineToggleComfortZoom, toggleCaptureFormat, toggleCurrentSegmentSelected, toggleKeyboardShortcuts, toggleKeyframeCut, toggleLastCommands, toggleLoopSelectedSegments, togglePlay, toggleSegmentsList, toggleStreamsSelector, toggleStripAudio, tryFixInvalidDuration, userHtml5ifyCurrentFile, zoomRel]);
|
||||
|
||||
useKeyboard({ keyBindings, onKeyPress });
|
||||
|
||||
|
@ -63,7 +63,7 @@ const Segment = memo(({ darkMode, seg, index, currentSegIndex, formatTimecode, g
|
||||
{ type: 'separator' },
|
||||
|
||||
{ label: t('Segment tags'), click: () => onViewSegmentTags(index) },
|
||||
{ label: t('Extract frames as image files'), click: () => onExtractSegmentFramesAsImages(index) },
|
||||
{ label: t('Extract frames as image files'), click: () => onExtractSegmentFramesAsImages([seg.segId]) },
|
||||
];
|
||||
}, [invertCutSegments, t, jumpSegStart, jumpSegEnd, addSegment, onLabelPress, onRemovePress, onLabelSelectedSegments, onRemoveSelected, onReorderPress, onDuplicateSegmentClick, seg, onSelectSingleSegment, onSelectAllSegments, onDeselectAllSegments, onSelectSegmentsByLabel, onInvertSelectedSegments, updateOrder, onViewSegmentTags, index, onExtractSegmentFramesAsImages]);
|
||||
|
||||
|
@ -421,7 +421,11 @@ const KeyboardShortcuts = memo(({
|
||||
category: outputCategory,
|
||||
},
|
||||
extractCurrentSegmentFramesAsImages: {
|
||||
name: t('Extract frames from segment as image files'),
|
||||
name: t('Extract frames from current segment as image files'),
|
||||
category: outputCategory,
|
||||
},
|
||||
extractSelectedSegmentsFramesAsImages: {
|
||||
name: t('Extract frames from selected segments as image files'),
|
||||
category: outputCategory,
|
||||
},
|
||||
cleanupFilesDialog: {
|
||||
|
@ -4,9 +4,9 @@ import Swal from '../swal';
|
||||
|
||||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export async function askExtractFramesAsImages({ segmentNumFrames, fps }) {
|
||||
export async function askExtractFramesAsImages({ segmentsNumFrames, plural, fps }) {
|
||||
const { value: captureChoice } = await Swal.fire({
|
||||
text: i18n.t('Extract frames of the selected segment as images?'),
|
||||
text: i18n.t(plural ? 'Extract frames of the selected segments as images' : 'Extract frames of the current segment as images'),
|
||||
icon: 'question',
|
||||
input: 'radio',
|
||||
inputValue: 'thumbnailFilter',
|
||||
@ -24,7 +24,7 @@ export async function askExtractFramesAsImages({ segmentNumFrames, fps }) {
|
||||
if (!captureChoice) return undefined;
|
||||
|
||||
let filter;
|
||||
let estimatedMaxNumFiles = segmentNumFrames;
|
||||
let estimatedMaxNumFiles = segmentsNumFrames;
|
||||
|
||||
if (captureChoice === 'thumbnailFilter') {
|
||||
const { value } = await Swal.fire({
|
||||
@ -40,7 +40,7 @@ export async function askExtractFramesAsImages({ segmentNumFrames, fps }) {
|
||||
if (Number.isNaN(intervalFrames) || intervalFrames < 1 || intervalFrames > 1000) return undefined; // a too large value uses a lot of memory
|
||||
|
||||
filter = `thumbnail=${intervalFrames}`;
|
||||
estimatedMaxNumFiles = Math.round(segmentNumFrames / intervalFrames);
|
||||
estimatedMaxNumFiles = Math.round(segmentsNumFrames / intervalFrames);
|
||||
}
|
||||
|
||||
if (captureChoice === 'selectNthSec' || captureChoice === 'selectNthFrame') {
|
||||
@ -74,7 +74,7 @@ export async function askExtractFramesAsImages({ segmentNumFrames, fps }) {
|
||||
}
|
||||
|
||||
filter = `select=not(mod(n\\,${nthFrame}))`;
|
||||
estimatedMaxNumFiles = Math.round(segmentNumFrames / nthFrame);
|
||||
estimatedMaxNumFiles = Math.round(segmentsNumFrames / nthFrame);
|
||||
}
|
||||
if (captureChoice === 'selectScene') {
|
||||
const { value } = await Swal.fire({
|
||||
|
@ -51,7 +51,6 @@ export default ({ formatTimecode, treatOutputFileModifiedTimeAsStart }) => {
|
||||
const matches = files.map((fileName) => {
|
||||
const escapedRegexp = escapeRegExp(getSuffixedFileName(filePath, tmpSuffix));
|
||||
const regexp = `^${escapedRegexp}(\\d+)`;
|
||||
console.log(regexp);
|
||||
const match = fileName.match(new RegExp(regexp));
|
||||
if (!match) return undefined;
|
||||
const frameNum = parseInt(match[1], 10);
|
||||
|
Loading…
Reference in New Issue
Block a user