mirror of
https://github.com/mifi/lossless-cut.git
synced 2024-11-25 19:52:44 +01:00
Allow for testing keyframe cut
This means a button for toggling whether -ss should be before -i or after (default) See discussion #13 and #79 Also some text/button enchancements
This commit is contained in:
parent
197c2f9418
commit
31c8b86e50
@ -57,7 +57,7 @@ function handleProgress(process, cutDuration, onProgress) {
|
|||||||
|
|
||||||
async function cut({
|
async function cut({
|
||||||
customOutDir, filePath, format, cutFrom, cutTo, cutToApparent, videoDuration, rotation,
|
customOutDir, filePath, format, cutFrom, cutTo, cutToApparent, videoDuration, rotation,
|
||||||
includeAllStreams, onProgress, stripAudio,
|
includeAllStreams, onProgress, stripAudio, keyframeCut,
|
||||||
}) {
|
}) {
|
||||||
const ext = path.extname(filePath) || `.${format}`;
|
const ext = path.extname(filePath) || `.${format}`;
|
||||||
const cutSpecification = `${util.formatDuration(cutFrom, true)}-${util.formatDuration(cutToApparent, true)}`;
|
const cutSpecification = `${util.formatDuration(cutFrom, true)}-${util.formatDuration(cutToApparent, true)}`;
|
||||||
@ -72,18 +72,32 @@ async function cut({
|
|||||||
const cutFromArgs = cutFrom === 0 ? [] : ['-ss', cutFrom];
|
const cutFromArgs = cutFrom === 0 ? [] : ['-ss', cutFrom];
|
||||||
const cutToArgs = cutTo === undefined || cutTo === videoDuration ? [] : ['-t', cutDuration];
|
const cutToArgs = cutTo === undefined || cutTo === videoDuration ? [] : ['-t', cutDuration];
|
||||||
|
|
||||||
|
const inputCutArgs = keyframeCut ? [
|
||||||
|
...cutFromArgs,
|
||||||
|
'-i', filePath,
|
||||||
|
...cutToArgs,
|
||||||
|
'-avoid_negative_ts', 'make_zero',
|
||||||
|
] : [
|
||||||
|
'-i', filePath,
|
||||||
|
...cutFromArgs,
|
||||||
|
...cutToArgs,
|
||||||
|
];
|
||||||
|
|
||||||
const rotationArgs = rotation !== undefined ? ['-metadata:s:v:0', `rotate=${rotation}`] : [];
|
const rotationArgs = rotation !== undefined ? ['-metadata:s:v:0', `rotate=${rotation}`] : [];
|
||||||
const ffmpegArgs = [
|
const ffmpegArgs = [
|
||||||
'-i', filePath, '-y',
|
...inputCutArgs,
|
||||||
|
|
||||||
...(stripAudio ? ['-an'] : ['-acodec', 'copy']),
|
...(stripAudio ? ['-an'] : ['-acodec', 'copy']),
|
||||||
|
|
||||||
'-vcodec', 'copy',
|
'-vcodec', 'copy',
|
||||||
'-scodec', 'copy',
|
'-scodec', 'copy',
|
||||||
...cutFromArgs, ...cutToArgs,
|
|
||||||
...(includeAllStreams ? ['-map', '0'] : []),
|
...(includeAllStreams ? ['-map', '0'] : []),
|
||||||
'-map_metadata', '0',
|
'-map_metadata', '0',
|
||||||
|
|
||||||
...rotationArgs,
|
...rotationArgs,
|
||||||
'-f', format,
|
|
||||||
outPath,
|
'-f', format, '-y', outPath,
|
||||||
];
|
];
|
||||||
|
|
||||||
console.log('ffmpeg', ffmpegArgs.join(' '));
|
console.log('ffmpeg', ffmpegArgs.join(' '));
|
||||||
@ -108,8 +122,7 @@ async function html5ify(filePath, outPath, encodeVideo) {
|
|||||||
|
|
||||||
const ffmpegArgs = [
|
const ffmpegArgs = [
|
||||||
'-i', filePath, ...videoArgs, '-an',
|
'-i', filePath, ...videoArgs, '-an',
|
||||||
'-y',
|
'-y', outPath,
|
||||||
outPath,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
console.log('ffmpeg', ffmpegArgs.join(' '));
|
console.log('ffmpeg', ffmpegArgs.join(' '));
|
||||||
|
@ -103,6 +103,7 @@ class App extends React.Component {
|
|||||||
includeAllStreams: false,
|
includeAllStreams: false,
|
||||||
captureFormat: 'jpeg',
|
captureFormat: 'jpeg',
|
||||||
customOutDir: undefined,
|
customOutDir: undefined,
|
||||||
|
keyframeCut: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
@ -316,15 +317,12 @@ class App extends React.Component {
|
|||||||
async cutClick() {
|
async cutClick() {
|
||||||
if (this.state.working) return alert('I\'m busy');
|
if (this.state.working) return alert('I\'m busy');
|
||||||
|
|
||||||
const cutStartTime = this.state.cutStartTime;
|
const {
|
||||||
const cutEndTime = this.state.cutEndTime;
|
cutStartTime, cutEndTime, filePath, customOutDir, fileFormat, duration, includeAllStreams,
|
||||||
const filePath = this.state.filePath;
|
stripAudio, keyframeCut,
|
||||||
const outputDir = this.state.customOutDir;
|
} = this.state;
|
||||||
const fileFormat = this.state.fileFormat;
|
|
||||||
const videoDuration = this.state.duration;
|
|
||||||
const rotation = this.isRotationSet() ? this.getRotation() : undefined;
|
const rotation = this.isRotationSet() ? this.getRotation() : undefined;
|
||||||
const includeAllStreams = this.state.includeAllStreams;
|
|
||||||
const stripAudio = this.state.stripAudio;
|
|
||||||
|
|
||||||
if (!this.isCutRangeValid()) {
|
if (!this.isCutRangeValid()) {
|
||||||
return alert('Start time must be before end time');
|
return alert('Start time must be before end time');
|
||||||
@ -333,16 +331,17 @@ class App extends React.Component {
|
|||||||
this.setState({ working: true });
|
this.setState({ working: true });
|
||||||
try {
|
try {
|
||||||
return await ffmpeg.cut({
|
return await ffmpeg.cut({
|
||||||
customOutDir: outputDir,
|
customOutDir,
|
||||||
filePath,
|
filePath,
|
||||||
format: fileFormat,
|
format: fileFormat,
|
||||||
cutFrom: cutStartTime,
|
cutFrom: cutStartTime,
|
||||||
cutTo: cutEndTime,
|
cutTo: cutEndTime,
|
||||||
cutToApparent: this.getApparentCutEndTime(),
|
cutToApparent: this.getApparentCutEndTime(),
|
||||||
videoDuration,
|
videoDuration: duration,
|
||||||
rotation,
|
rotation,
|
||||||
includeAllStreams,
|
includeAllStreams,
|
||||||
stripAudio,
|
stripAudio,
|
||||||
|
keyframeCut,
|
||||||
onProgress: progress => this.onCutProgress(progress),
|
onProgress: progress => this.onCutProgress(progress),
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -547,6 +546,13 @@ class App extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="right-menu">
|
<div className="right-menu">
|
||||||
|
<button
|
||||||
|
title={`Cut mode ${this.state.keyframeCut ? 'nearest keyframe cut' : 'normal cut'}`}
|
||||||
|
onClick={withBlur(() => this.setState({ keyframeCut: !this.state.keyframeCut }))}
|
||||||
|
>
|
||||||
|
{this.state.keyframeCut ? 'kc' : 'nc'}
|
||||||
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
title={`Set output streams. Current: ${this.state.includeAllStreams ? 'include (and cut) all streams' : 'include only primary streams'}`}
|
title={`Set output streams. Current: ${this.state.includeAllStreams ? 'include (and cut) all streams' : 'include only primary streams'}`}
|
||||||
onClick={withBlur(() => this.toggleIncludeAllStreams())}
|
onClick={withBlur(() => this.toggleIncludeAllStreams())}
|
||||||
@ -555,7 +561,7 @@ class App extends React.Component {
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
title={`Delete audio? Current: ${this.state.stripAudio ? 'delete audio tracks' : "don't delete audio tracks"}`}
|
title={`Delete audio? Current: ${this.state.stripAudio ? 'delete audio tracks' : 'keep audio tracks'}`}
|
||||||
onClick={withBlur(() => this.setState({ stripAudio: !this.state.stripAudio }))}
|
onClick={withBlur(() => this.setState({ stripAudio: !this.state.stripAudio }))}
|
||||||
>
|
>
|
||||||
{this.state.stripAudio ? 'da' : 'ka'}
|
{this.state.stripAudio ? 'da' : 'ka'}
|
||||||
@ -572,7 +578,7 @@ class App extends React.Component {
|
|||||||
title={`Custom output dir (cancel to restore default). Current: ${this.getOutputDir() || 'Not set (use input dir)'}`}
|
title={`Custom output dir (cancel to restore default). Current: ${this.getOutputDir() || 'Not set (use input dir)'}`}
|
||||||
onClick={withBlur(() => this.setOutputDir())}
|
onClick={withBlur(() => this.setOutputDir())}
|
||||||
>
|
>
|
||||||
{this.getOutputDir() ? `...${this.getOutputDir().substr(-10)}` : 'OUTDIR'}
|
{this.getOutputDir() ? 'cd' : 'id'}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<i
|
<i
|
||||||
|
Loading…
Reference in New Issue
Block a user