diff --git a/src/StreamsSelector.jsx b/src/StreamsSelector.jsx
index 3a55d728..740d13aa 100644
--- a/src/StreamsSelector.jsx
+++ b/src/StreamsSelector.jsx
@@ -4,6 +4,7 @@ import { FaVideo, FaVideoSlash, FaFileExport, FaFileImport, FaVolumeUp, FaVolume
import { GoFileBinary } from 'react-icons/go';
import { MdSubtitles } from 'react-icons/md';
import Swal from 'sweetalert2';
+import { SegmentedControl } from 'evergreen-ui';
import withReactContent from 'sweetalert2-react-content';
@@ -12,10 +13,18 @@ const ReactSwal = withReactContent(Swal);
const { formatDuration } = require('./util');
const { getStreamFps } = require('./ffmpeg');
+function onInfoClick(s, title) {
+ ReactSwal.fire({
+ showCloseButton: true,
+ title,
+ html:
Click to select which tracks to keep when exporting:
@@ -94,23 +112,23 @@ const StreamsSelector = memo(({
+ {renderFileRow(mainFilePath, mainFileFormatData)}
+
{existingStreams.map((stream) => (
toggleCopyStreamId(mainFilePath, streamId)}
+ fileDuration={getFormatDuration(mainFileFormatData)}
/>
))}
- {Object.entries(externalFiles).map(([path, { streams }]) => (
+ {externalFilesEntries.map(([path, { streams, formatData }]) => (
-
- removeFile(path)} /> |
-
- {path}
- |
-
+ |
+
+ {renderFileRow(path, formatData, () => removeFile(path))}
{streams.map((stream) => (
toggleCopyStreamId(path, streamId)}
+ fileDuration={getFormatDuration(formatData)}
/>
))}
@@ -125,11 +144,27 @@ const StreamsSelector = memo(({
+ {externalFilesEntries.length > 0 && (
+
+
+ If the streams have different length, do you want to make the combined output file as long as the longest stream or the shortest stream?
+
+
setShortestFlag(value === 'shortest')}
+ />
+
+
+ )}
+
+ {exportExtraStreams &&
Unprocessable tracks will be extracted to separate files. This can be configured in settings.
}
+
- Include tracks from other file
+ Include more tracks from other file
- {Object.keys(externalFiles).length === 0 && (
+ {externalFilesEntries.length === 0 && (
Export each track as individual files
diff --git a/src/ffmpeg.js b/src/ffmpeg.js
index 303e3c62..0065d5d0 100644
--- a/src/ffmpeg.js
+++ b/src/ffmpeg.js
@@ -145,7 +145,7 @@ function getNextPrevKeyframe(frames, cutTime, nextMode) {
async function cut({
filePath, outFormat, cutFrom, cutTo, videoDuration, rotation,
- onProgress, copyStreamIds, keyframeCut, outPath, appendFfmpegCommandLog,
+ onProgress, copyStreamIds, keyframeCut, outPath, appendFfmpegCommandLog, shortestFlag,
}) {
console.log('Cutting from', cutFrom, 'to', cutTo);
@@ -178,6 +178,8 @@ async function cut({
'-c', 'copy',
+ ...(shortestFlag ? ['-shortest'] : []),
+
...flatMapDeep(copyStreamIdsFiltered, ({ streamIds }, fileIndex) => streamIds.map(streamId => ['-map', `${fileIndex}:${streamId}`])),
'-map_metadata', '0',
// https://video.stackexchange.com/questions/23741/how-to-prevent-ffmpeg-from-dropping-metadata
@@ -210,7 +212,7 @@ async function cut({
async function cutMultiple({
customOutDir, filePath, segments: segmentsUnsorted, videoDuration, rotation,
onProgress, keyframeCut, copyStreamIds, outFormat, isOutFormatUserSelected,
- appendFfmpegCommandLog,
+ appendFfmpegCommandLog, shortestFlag,
}) {
const segments = sortBy(segmentsUnsorted, 'cutFrom');
const singleProgresses = {};
@@ -241,6 +243,7 @@ async function cutMultiple({
keyframeCut,
cutFrom,
cutTo,
+ shortestFlag,
// eslint-disable-next-line no-loop-func
onProgress: progress => onSingleProgress(i, progress),
appendFfmpegCommandLog,
@@ -372,13 +375,17 @@ function determineOutputFormat(ffprobeFormats, ft) {
return ffprobeFormats[0] || undefined;
}
-async function getFormat(filePath) {
- console.log('getFormat', filePath);
+async function getFormatData(filePath) {
+ console.log('getFormatData', filePath);
const { stdout } = await runFfprobe([
'-of', 'json', '-show_format', '-i', filePath,
]);
- const formatsStr = JSON.parse(stdout).format.format_name;
+ return JSON.parse(stdout).format;
+}
+
+async function getDefaultOutFormat(filePath, formatData) {
+ const formatsStr = formatData.format_name;
console.log('formats', formatsStr);
const formats = (formatsStr || '').split(',');
@@ -504,7 +511,8 @@ function getStreamFps(stream) {
module.exports = {
cutMultiple,
- getFormat,
+ getFormatData,
+ getDefaultOutFormat,
html5ify,
html5ifyDummy,
mergeAnyFiles,
diff --git a/src/renderer.jsx b/src/renderer.jsx
index 488c443e..63e159c8 100644
--- a/src/renderer.jsx
+++ b/src/renderer.jsx
@@ -47,7 +47,10 @@ const ffmpeg = require('./ffmpeg');
const configStore = require('./store');
const edlStore = require('./edlStore');
-const { defaultProcessedCodecTypes, getStreamFps, isCuttingStart, isCuttingEnd } = ffmpeg;
+const {
+ defaultProcessedCodecTypes, getStreamFps, isCuttingStart, isCuttingEnd,
+ getDefaultOutFormat, getFormatData,
+} = ffmpeg;
const {
@@ -99,6 +102,7 @@ const App = memo(() => {
const [playerTime, setPlayerTime] = useState();
const [duration, setDuration] = useState();
const [fileFormat, setFileFormat] = useState();
+ const [fileFormatData, setFileFormatData] = useState();
const [detectedFileFormat, setDetectedFileFormat] = useState();
const [rotation, setRotation] = useState(360);
const [cutProgress, setCutProgress] = useState();
@@ -116,6 +120,7 @@ const App = memo(() => {
const [debouncedCommandedTime, setDebouncedCommandedTime] = useState(0);
const [ffmpegCommandLog, setFfmpegCommandLog] = useState([]);
const [neighbouringFrames, setNeighbouringFrames] = useState([]);
+ const [shortestFlag, setShortestFlag] = useState(false);
// Segment related state
const [currentSegIndex, setCurrentSegIndex] = useState(0);
@@ -229,6 +234,7 @@ const App = memo(() => {
setCutStartTimeManual();
setCutEndTimeManual();
setFileFormat();
+ setFileFormatData();
setDetectedFileFormat();
setRotation(360);
setCutProgress();
@@ -242,6 +248,7 @@ const App = memo(() => {
setStreamsSelectorShown(false);
setZoom(1);
setNeighbouringFrames([]);
+ setShortestFlag(false);
}, [cutSegmentsHistory, setCutSegments, cancelCommandedTimeDebounce]);
useEffect(() => () => {
@@ -758,6 +765,7 @@ const App = memo(() => {
segments: ffmpegSegments,
onProgress: setCutProgress,
appendFfmpegCommandLog,
+ shortestFlag,
});
if (outFiles.length > 1 && autoMerge) {
@@ -798,7 +806,7 @@ const App = memo(() => {
effectiveRotation, outSegments,
working, duration, filePath, keyframeCut, detectedFileFormat,
autoMerge, customOutDir, fileFormat, haveInvalidSegs, copyStreamIds, numStreamsToCopy,
- exportExtraStreams, nonCopiedExtraStreams, outputDir,
+ exportExtraStreams, nonCopiedExtraStreams, outputDir, shortestFlag,
]);
// TODO use ffmpeg to capture frame
@@ -871,7 +879,9 @@ const App = memo(() => {
setWorking(true);
try {
- const ff = await ffmpeg.getFormat(fp);
+ const fd = await getFormatData(fp);
+
+ const ff = await getDefaultOutFormat(fp, fd);
if (!ff) {
errorToast('Unsupported file');
return;
@@ -895,6 +905,7 @@ const App = memo(() => {
setFilePath(fp);
setFileFormat(ff);
setDetectedFileFormat(ff);
+ setFileFormatData(fd);
if (html5FriendlyPathRequested) {
setHtml5FriendlyPath(html5FriendlyPathRequested);
@@ -1003,8 +1014,9 @@ const App = memo(() => {
const addStreamSourceFile = useCallback(async (path) => {
if (externalStreamFiles[path]) return;
const { streams } = await ffmpeg.getAllStreams(path);
+ const formatData = await getFormatData(path);
// console.log('streams', streams);
- setExternalStreamFiles(old => ({ ...old, [path]: { streams } }));
+ setExternalStreamFiles(old => ({ ...old, [path]: { streams, formatData } }));
setCopyStreamIdsForPath(path, () => fromPairs(streams.map(({ index }) => [index, true])));
}, [externalStreamFiles]);
@@ -1546,6 +1558,7 @@ const App = memo(() => {
>
{
toggleCopyStreamId={toggleCopyStreamId}
setCopyStreamIdsForPath={setCopyStreamIdsForPath}
onExtractAllStreamsPress={onExtractAllStreamsPress}
+ shortestFlag={shortestFlag}
+ setShortestFlag={setShortestFlag}
+ exportExtraStreams={exportExtraStreams}
/>