don't fill gaps for matroska

it seems to support gaps
pull/2514/head
Mikael Finstad 1 year ago
parent 250808703f
commit 106897aa55
No known key found for this signature in database
GPG Key ID: 25AB36E3E81CBC26

@ -61,7 +61,7 @@ import {
mapRecommendedDefaultFormat,
getFfCommandLine,
} from './ffmpeg';
import { shouldCopyStreamByDefault, getAudioStreams, getRealVideoStreams, isAudioDefinitelyNotSupported, willPlayerProperlyHandleVideo, doesPlayerSupportHevcPlayback, getSubtitleStreams, enableVideoTrack, enableAudioTrack, canHtml5PlayerPlayStreams } from './util/streams';
import { shouldCopyStreamByDefault, getAudioStreams, getRealVideoStreams, isAudioDefinitelyNotSupported, willPlayerProperlyHandleVideo, doesPlayerSupportHevcPlayback, getSubtitleStreams, enableVideoTrack, enableAudioTrack, canHtml5PlayerPlayStreams, isMatroska } from './util/streams';
import { exportEdlFile, readEdlFile, loadLlcProject, askForEdlImport } from './edlStore';
import { formatYouTube, getFrameCountRaw, formatTsvHuman } from './edlFormats';
import {
@ -83,7 +83,7 @@ import { askForHtml5ifySpeed } from './dialogs/html5ify';
import { askForOutDir, askForImportChapters, promptTimecode, askForFileOpenAction, confirmExtractAllStreamsDialog, showCleanupFilesDialog, showDiskFull, showExportFailedDialog, showConcatFailedDialog, openYouTubeChaptersDialog, showRefuseToOverwrite, openDirToast, openExportFinishedToast, openConcatFinishedToast, showOpenDialog, showMuxNotSupported, promptDownloadMediaUrl, CleanupChoicesType, showOutputNotWritable } from './dialogs';
import { openSendReportDialog } from './reporting';
import { fallbackLng } from './i18n';
import { sortSegments, convertSegmentsToChapters, hasAnySegmentOverlap, isDurationValid, getPlaybackAction, getSegmentTags, filterNonMarkers } from './segments';
import { sortSegments, convertSegmentsToChaptersWithGaps, hasAnySegmentOverlap, isDurationValid, getPlaybackAction, getSegmentTags, filterNonMarkers } from './segments';
import { generateOutSegFileNames as generateOutSegFileNamesRaw, generateMergedFileNames as generateMergedFileNamesRaw, defaultOutSegTemplate, defaultCutMergedFileTemplate } from './util/outputNameTemplate';
import { rightBarWidth, leftBarWidth, ffmpegExtractWindow, zoomMax } from './util/constants';
import BigWaveform from './components/BigWaveform';
@ -443,7 +443,7 @@ function App() {
setRotation((r) => (r + 90) % 450);
setHideMediaSourcePlayer(false);
// Matroska is known not to work, so we warn user. See https://github.com/mifi/lossless-cut/discussions/661
const supportsRotation = !(fileFormat != null && ['matroska', 'webm'].includes(fileFormat));
const supportsRotation = !isMatroska(fileFormat);
if (!supportsRotation) showNotification({ text: i18n.t('Lossless rotation might not work with this file format. You may try changing to MP4') });
}, [fileFormat, showNotification]);
@ -1085,7 +1085,8 @@ function App() {
errorToast(i18n.t('Make sure you have no overlapping segments.'));
return;
}
chaptersToAdd = convertSegmentsToChapters(sortedSegments);
// matroska supports gaps, so we can use segments directly
chaptersToAdd = isMatroska(fileFormat) ? sortedSegments : convertSegmentsToChaptersWithGaps(sortedSegments);
}
console.log('outSegTemplateOrDefault', outSegTemplateOrDefault);

@ -1,9 +1,9 @@
import { test, it, expect, describe } from 'vitest';
import { convertSegmentsToChapters, partitionIntoOverlappingRanges, formatSegNum, combineOverlappingSegments, invertSegments } from './segments';
import { convertSegmentsToChaptersWithGaps, partitionIntoOverlappingRanges, formatSegNum, combineOverlappingSegments, invertSegments } from './segments';
it('converts segments to chapters with gaps', () => {
expect(convertSegmentsToChapters([
expect(convertSegmentsToChaptersWithGaps([
{
start: 104.612,
end: 189.053,
@ -27,8 +27,8 @@ it('converts segments to chapters with gaps', () => {
])).toMatchSnapshot();
});
it('converts segments to chapters with no gaps', () => {
expect(convertSegmentsToChapters([
it('converts segments to chapters with no gaps when adjacent', () => {
expect(convertSegmentsToChaptersWithGaps([
{
start: 0,
end: 2,
@ -43,7 +43,7 @@ it('converts segments to chapters with no gaps', () => {
});
it('converts segments to chapters with single long segment', () => {
expect(convertSegmentsToChapters([
expect(convertSegmentsToChaptersWithGaps([
{
start: 0,
end: 1,

@ -4,7 +4,7 @@ import minBy from 'lodash/minBy';
import maxBy from 'lodash/maxBy';
import invariant from 'tiny-invariant';
import { PlaybackMode, SegmentBase, SegmentTags, SegmentToExport, StateSegment } from './types';
import { DefiniteSegmentBase, PlaybackMode, SegmentBase, SegmentTags, SegmentToExport, StateSegment } from './types';
export const isDurationValid = (duration?: number): duration is number => duration != null && Number.isFinite(duration) && duration > 0;
@ -208,19 +208,16 @@ export function invertSegments(
return ret.filter(({ start, end }) => end == null || end > start);
}
// because chapters need to be contiguous, we need to insert gaps in-between
export function convertSegmentsToChapters(sortedSegments: { start: number, end: number, name?: string | undefined }[]) {
if (sortedSegments.length === 0) return [];
if (hasAnySegmentOverlap(sortedSegments)) throw new Error('Segments cannot overlap');
// because chapters need to be contiguous in formats like MP4, we insert "gap" segments in-between, so that end times will be correct
export function convertSegmentsToChaptersWithGaps(sortedSegments: (DefiniteSegmentBase & { name?: string | undefined })[]) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const invertedSegments = invertSegments(sortedSegments, true, false).map(({ end, name: _ignored, ...seg }) => {
const invertedSegmentsWithoutName = invertSegments(sortedSegments, true, false).map(({ end, name: _ignored, ...seg }) => {
invariant(end != null); // to please typescript
return { ...seg, end };
});
// inverted segments will be "gap" segments. Merge together with normal segments
return sortSegments([...sortedSegments, ...invertedSegments]);
return sortSegments([...sortedSegments, ...invertedSegmentsWithoutName]);
}
export function getPlaybackAction({ playbackMode, currentTime, playingSegment }: {

@ -101,6 +101,8 @@ export function getActiveDisposition(disposition: FFprobeStreamDisposition | und
export const isMov = (format: string | undefined) => format != null && ['ismv', 'ipod', 'mp4', 'mov'].includes(format);
export const isMatroska = (format: string | undefined) => format != null && ['matroska', 'webm'].includes(format);
type GetVideoArgsFn = (a: { streamIndex: number, outputIndex: number }) => string[] | undefined;
function getPerStreamFlags({ stream, outputIndex, outFormat, manuallyCopyDisposition = false, getVideoArgs = () => undefined, areWeCutting }: {

Loading…
Cancel
Save