implement abort

closes #524
pull/1255/head
Mikael Finstad 2023-01-06 23:28:46 +08:00
rodzic e49bfee750
commit 84535c8f8e
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 25AB36E3E81CBC26
4 zmienionych plików z 43 dodań i 14 usunięć

Wyświetl plik

@ -44,7 +44,7 @@ import SubtitleControl from './components/SubtitleControl';
import BatchFilesList from './components/BatchFilesList';
import ConcatDialog from './components/ConcatDialog';
import KeyboardShortcuts from './components/KeyboardShortcuts';
import Loading from './components/Loading';
import Working from './components/Working';
import OutputFormatSelect from './components/OutputFormatSelect';
import { loadMifiLink, runStartupCheck } from './mifi';
@ -55,7 +55,7 @@ import {
extractStreams, setCustomFfPath as ffmpegSetCustomFfPath,
isIphoneHevc, isProblematicAvc1, tryMapChaptersToEdl, blackDetect, silenceDetect, detectSceneChanges as ffmpegDetectSceneChanges,
getDuration, getTimecodeFromStreams, createChaptersFromSegments, extractSubtitleTrack,
RefuseOverwriteError, readFrames, mapTimesToSegments,
RefuseOverwriteError, readFrames, mapTimesToSegments, abortFfmpegs,
} from './ffmpeg';
import { shouldCopyStreamByDefault, getAudioStreams, getRealVideoStreams, isAudioDefinitelyNotSupported, willPlayerProperlyHandleVideo, doesPlayerSupportHevcPlayback } from './util/streams';
import { exportEdlFile, readEdlFile, saveLlcProject, loadLlcProject, askForEdlImport } from './edlStore';
@ -1371,6 +1371,10 @@ const App = memo(() => {
const revealPath = concatOutPath || outFiles[0];
if (!hideAllNotifications) openCutFinishedToast({ filePath: revealPath, warnings, notices });
} catch (err) {
if (err.killed === true) {
// assume execa killed (aborted by user)
return;
}
if (err instanceof RefuseOverwriteError) {
showRefuseToOverwrite();
return;
@ -2484,7 +2488,7 @@ const App = memo(() => {
)}
<AnimatePresence>
{working && <Loading text={working} cutProgress={cutProgress} />}
{working && <Working text={working} cutProgress={cutProgress} onAbortClick={abortFfmpegs} />}
</AnimatePresence>
{tunerVisible && <ValueTuners type={tunerVisible} onFinished={() => setTunerVisible()} />}

Wyświetl plik

@ -1,39 +1,45 @@
import React, { memo } from 'react';
import { motion } from 'framer-motion';
import Lottie from 'react-lottie-player';
import { Button } from 'evergreen-ui';
import { Trans } from 'react-i18next';
import { primaryColor } from '../colors';
import loadingLottie from '../7077-magic-flow.json';
const Loading = memo(({ text, cutProgress }) => (
const Working = memo(({ text, cutProgress, onAbortClick }) => (
<div style={{ position: 'absolute', zIndex: 1, bottom: 0, top: 0, left: 0, right: 0, display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
<motion.div
style={{ background: primaryColor, boxShadow: `${primaryColor} 0px 0px 20px 25px`, borderRadius: 20, paddingBottom: 15, color: 'white', textAlign: 'center', fontSize: 14 }}
style={{ background: primaryColor, boxShadow: `${primaryColor} 0px 0px 20px 25px`, borderRadius: 60, paddingBottom: 5, color: 'white', fontSize: 14, display: 'flex', flexDirection: 'column', alignItems: 'center' }}
initial={{ opacity: 0, scale: 0 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0 }}
>
<div style={{ width: 150, height: 150 }}>
<div style={{ width: 150, height: 80 }}>
<Lottie
loop
animationData={loadingLottie}
play
style={{ width: '170%', height: '130%', marginLeft: '-35%', marginTop: '-29%', pointerEvents: 'none' }}
style={{ width: '170%', height: '210%', marginLeft: '-40%', marginTop: '-35%', pointerEvents: 'none' }}
/>
</div>
<div style={{ marginTop: 10, width: 150 }}>
<div style={{ marginTop: 5 }}>
{text}...
</div>
{(cutProgress != null) && (
<div style={{ marginTop: 10 }}>
<div style={{ marginTop: 5 }}>
{`${(cutProgress * 100).toFixed(1)} %`}
</div>
)}
<div style={{ marginTop: 5 }}>
<Button intent="danger" onClick={onAbortClick} height={20}><Trans>Abort</Trans></Button>
</div>
</motion.div>
</div>
));
export default Loading;
export default Working;

Wyświetl plik

@ -30,7 +30,7 @@ export async function askExtractFramesAsImages({ segmentNumFrames, fps }) {
text: i18n.t('Capture the best image every nth second'),
icon: 'question',
input: 'text',
inputLabel: i18n.t('Enter a decimal number of seconds'),
inputLabel: i18n.t('Enter the number of seconds between each image (decimal)'),
inputValue: 5,
showCancelButton: true,
});
@ -49,7 +49,7 @@ export async function askExtractFramesAsImages({ segmentNumFrames, fps }) {
text: i18n.t('Capture exactly one image every nth frame'),
icon: 'question',
input: 'number',
inputLabel: i18n.t('Enter an integer number of frames'),
inputLabel: i18n.t('Enter the number of frames between each image (integer)'),
inputValue: 30,
showCancelButton: true,
});
@ -62,7 +62,7 @@ export async function askExtractFramesAsImages({ segmentNumFrames, fps }) {
text: i18n.t('Capture exactly one image every nth second'),
icon: 'question',
input: 'text',
inputLabel: i18n.t('Enter a decimal number of seconds'),
inputLabel: i18n.t('Enter the number of seconds between each image (decimal)'),
inputValue: 5,
showCancelButton: true,
});

Wyświetl plik

@ -18,6 +18,9 @@ const { pathExists } = window.require('fs-extra');
let customFfPath;
const runningFfmpegs = new Set();
// setInterval(() => console.log(runningFfmpegs.size), 1000);
export class RefuseOverwriteError extends Error {}
@ -60,7 +63,23 @@ export async function runFfprobe(args, { timeout = isDev ? 10000 : 30000 } = {})
export function runFfmpeg(args, execaOptions, { logCli = true } = {}) {
const ffmpegPath = getFfmpegPath();
if (logCli) console.log(getFfCommandLine('ffmpeg', args));
return execa(ffmpegPath, args, execaOptions);
const process = execa(ffmpegPath, args, execaOptions);
(async () => {
runningFfmpegs.add(process);
try {
await process;
} finally {
runningFfmpegs.delete(process);
}
})();
return process;
}
export function abortFfmpegs() {
runningFfmpegs.forEach((process) => {
process.kill('SIGTERM', { forceKillAfterTimeout: 10000 });
});
}
export function handleProgress(process, durationIn, onProgress, customMatcher = () => {}) {