allow merge with multi cut

should cover #98
pull/124/head
Mikael Finstad 8 years ago
parent a2be8b8e41
commit e16d235a38

@ -54,14 +54,9 @@ function handleProgress(process, cutDuration, onProgress) {
} }
async function cut({ async function cut({
customOutDir, filePath, format, cutFrom, cutTo, cutToApparent, videoDuration, rotation, filePath, format, cutFrom, cutTo, cutToApparent, videoDuration, rotation,
includeAllStreams, onProgress, stripAudio, keyframeCut, includeAllStreams, onProgress, stripAudio, keyframeCut, outPath,
}) { }) {
const ext = path.extname(filePath) || `.${format}`;
const cutSpecification = `${formatDuration(cutFrom, true)}-${formatDuration(cutToApparent, true)}`;
const outPath = getOutPath(customOutDir, filePath, `${cutSpecification}${ext}`);
console.log('Cutting from', cutFrom, 'to', cutToApparent); console.log('Cutting from', cutFrom, 'to', cutToApparent);
const cutDuration = cutToApparent - cutFrom; const cutDuration = cutToApparent - cutFrom;
@ -121,11 +116,19 @@ async function cutMultiple({
return onProgress((sum(Object.values(singleProgresses)) / segments.length)); return onProgress((sum(Object.values(singleProgresses)) / segments.length));
} }
const outFiles = [];
let i = 0; let i = 0;
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
for (const { cutFrom, cutTo, cutToApparent } of segments) { for (const { cutFrom, cutTo, cutToApparent } of segments) {
const ext = path.extname(filePath) || `.${format}`;
const cutSpecification = `${formatDuration(cutFrom, true)}-${formatDuration(cutToApparent, true)}`;
const outPath = getOutPath(customOutDir, filePath, `${cutSpecification}${ext}`);
// eslint-disable-next-line no-await-in-loop // eslint-disable-next-line no-await-in-loop
await cut({ await cut({
outPath,
customOutDir, customOutDir,
filePath, filePath,
format, format,
@ -140,8 +143,13 @@ async function cutMultiple({
// eslint-disable-next-line no-loop-func // eslint-disable-next-line no-loop-func
onProgress: progress => onSingleProgress(i, progress), onProgress: progress => onSingleProgress(i, progress),
}); });
outFiles.push(outPath);
i += 1; i += 1;
} }
return outFiles;
} }
async function html5ify(filePath, outPath, encodeVideo) { async function html5ify(filePath, outPath, encodeVideo) {
@ -166,10 +174,7 @@ async function html5ify(filePath, outPath, encodeVideo) {
await transferTimestamps(filePath, outPath); await transferTimestamps(filePath, outPath);
} }
async function mergeFiles(paths) { async function mergeFiles(paths, outPath) {
const firstPath = paths[0];
const ext = path.extname(firstPath);
const outPath = `${firstPath}-merged${ext}`;
console.log('Merging files', { paths }, 'to', outPath); console.log('Merging files', { paths }, 'to', outPath);
// https://blog.yo1.dog/fix-for-ffmpeg-protocol-not-on-whitelist-error-for-urls/ // https://blog.yo1.dog/fix-for-ffmpeg-protocol-not-on-whitelist-error-for-urls/
@ -196,6 +201,19 @@ async function mergeFiles(paths) {
console.log(result.stdout); console.log(result.stdout);
} }
async function mergeAnyFiles(paths) {
const firstPath = paths[0];
const ext = path.extname(firstPath);
const outPath = `${firstPath}-merged${ext}`;
return mergeFiles(paths, outPath);
}
async function autoMergeSegments({ customOutDir, sourceFile, segmentPaths }) {
const ext = path.extname(sourceFile);
const outPath = getOutPath(customOutDir, sourceFile, `cut-merged-${new Date().getTime()}${ext}`);
return mergeFiles(segmentPaths, outPath);
}
/** /**
* ffmpeg only supports encoding certain formats, and some of the detected input * ffmpeg only supports encoding certain formats, and some of the detected input
* formats are not the same as the names used for encoding. * formats are not the same as the names used for encoding.
@ -311,6 +329,7 @@ module.exports = {
cutMultiple, cutMultiple,
getFormat, getFormat,
html5ify, html5ify,
mergeFiles, mergeAnyFiles,
autoMergeSegments,
extractAllStreams, extractAllStreams,
}; };

@ -95,6 +95,7 @@ const globalState = {
captureFormat: 'jpeg', captureFormat: 'jpeg',
customOutDir: undefined, customOutDir: undefined,
keyframeCut: true, keyframeCut: true,
autoMerge: false,
}; };
class App extends React.Component { class App extends React.Component {
@ -169,7 +170,7 @@ class App extends React.Component {
// TODO customOutDir ? // TODO customOutDir ?
// console.log('merge', paths); // console.log('merge', paths);
await ffmpeg.mergeFiles(paths); await ffmpeg.mergeAnyFiles(paths);
} catch (err) { } catch (err) {
errorToast('Failed to merge files. Make sure they are all of the exact same format and codecs'); errorToast('Failed to merge files. Make sure they are all of the exact same format and codecs');
console.error('Failed to merge files', err); console.error('Failed to merge files', err);
@ -343,6 +344,8 @@ class App extends React.Component {
toggleKeyframeCut = () => this.setState(({ keyframeCut }) => ({ keyframeCut: !keyframeCut })); toggleKeyframeCut = () => this.setState(({ keyframeCut }) => ({ keyframeCut: !keyframeCut }));
toggleAutoMerge = () => this.setState(({ autoMerge }) => ({ autoMerge: !autoMerge }));
addCutSegment = () => { addCutSegment = () => {
const { cutSegments, currentTime, duration } = this.state; const { cutSegments, currentTime, duration } = this.state;
@ -424,7 +427,7 @@ class App extends React.Component {
cutClick = async () => { cutClick = async () => {
const { const {
filePath, customOutDir, fileFormat, duration, includeAllStreams, filePath, customOutDir, fileFormat, duration, includeAllStreams,
stripAudio, keyframeCut, working, cutSegments, stripAudio, keyframeCut, autoMerge, working, cutSegments,
} = this.state; } = this.state;
if (working) { if (working) {
@ -451,7 +454,7 @@ class App extends React.Component {
cutToApparent: this.getApparentCutEndTime(i), cutToApparent: this.getApparentCutEndTime(i),
})); }));
await ffmpeg.cutMultiple({ const outFiles = await ffmpeg.cutMultiple({
customOutDir, customOutDir,
filePath, filePath,
format: fileFormat, format: fileFormat,
@ -463,6 +466,16 @@ class App extends React.Component {
segments, segments,
onProgress: this.onCutProgress, onProgress: this.onCutProgress,
}); });
if (outFiles.length > 1 && autoMerge) {
this.onCutProgress(0); // TODO
await ffmpeg.autoMergeSegments({
customOutDir,
sourceFile: filePath,
segmentPaths: outFiles,
});
}
} catch (err) { } catch (err) {
console.error('stdout:', err.stdout); console.error('stdout:', err.stdout);
console.error('stderr:', err.stderr); console.error('stderr:', err.stderr);
@ -471,6 +484,7 @@ class App extends React.Component {
errorToast('Whoops! ffmpeg was unable to cut this video. It may be of an unknown format or codec combination'); errorToast('Whoops! ffmpeg was unable to cut this video. It may be of an unknown format or codec combination');
return; return;
} }
showFfmpegFail(err); showFfmpegFail(err);
} finally { } finally {
this.setState({ working: false }); this.setState({ working: false });
@ -565,7 +579,7 @@ class App extends React.Component {
const { const {
working, filePath, duration: durationRaw, cutProgress, currentTime, playing, working, filePath, duration: durationRaw, cutProgress, currentTime, playing,
fileFormat, playbackRate, keyframeCut, includeAllStreams, stripAudio, captureFormat, fileFormat, playbackRate, keyframeCut, includeAllStreams, stripAudio, captureFormat,
helpVisible, currentSeg, cutSegments, helpVisible, currentSeg, cutSegments, autoMerge,
} = this.state; } = this.state;
const duration = durationRaw || 1; const duration = durationRaw || 1;
@ -764,11 +778,19 @@ class App extends React.Component {
<button <button
type="button" type="button"
title={`Add cut segment ${currentSeg + 1}`} title="Add cut segment"
onClick={withBlur(() => this.addCutSegment())} onClick={withBlur(() => this.addCutSegment())}
> >
c+ c+
</button> </button>
<button
type="button"
title={`Auto merge segments to one file after export? ${autoMerge ? 'Auto merge enabled' : 'No merging'}`}
onClick={withBlur(this.toggleAutoMerge)}
>
{autoMerge ? 'am' : 'nm'}
</button>
</div> </div>
<div className="right-menu"> <div className="right-menu">

Loading…
Cancel
Save