feat: publish av-tools 0.1.0
This commit is contained in:
174
tests/unit/media/stream-playback.test.ts
Normal file
174
tests/unit/media/stream-playback.test.ts
Normal file
@@ -0,0 +1,174 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import {
|
||||
assessBrowserPlayback,
|
||||
assessGeneratedResultPreview,
|
||||
buildPlaybackMimeCandidates,
|
||||
} from '../../../src/media/browser-playback';
|
||||
import type {
|
||||
MediaProbe,
|
||||
MediaStreamProbe,
|
||||
} from '../../../src/media/media.types';
|
||||
import {
|
||||
selectDefaultStreams,
|
||||
streamSelectionMapArgs,
|
||||
validateStreamSelection,
|
||||
} from '../../../src/media/stream-selection';
|
||||
|
||||
function stream(
|
||||
index: number,
|
||||
type: MediaStreamProbe['type'],
|
||||
overrides: Partial<MediaStreamProbe> = {}
|
||||
): MediaStreamProbe {
|
||||
return {
|
||||
index,
|
||||
type,
|
||||
disposition: {},
|
||||
tags: {},
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function probe(streams: MediaStreamProbe[]): MediaProbe {
|
||||
return {
|
||||
formatNames: ['mov', 'mp4'],
|
||||
tags: {},
|
||||
streams,
|
||||
chapters: [],
|
||||
warnings: [],
|
||||
};
|
||||
}
|
||||
|
||||
describe('stream selection', () => {
|
||||
const media = probe([
|
||||
stream(0, 'video', { codecName: 'h264' }),
|
||||
stream(1, 'audio', {
|
||||
language: 'eng',
|
||||
disposition: { default: true },
|
||||
}),
|
||||
stream(2, 'audio', {
|
||||
language: 'de-DE',
|
||||
}),
|
||||
stream(3, 'subtitle', { language: 'deu' }),
|
||||
stream(4, 'video', { disposition: { attached_pic: true } }),
|
||||
]);
|
||||
|
||||
it('chooses default streams, avoids cover art and can include subtitles', () => {
|
||||
expect(
|
||||
selectDefaultStreams(media, {
|
||||
preferredAudioLanguages: ['de'],
|
||||
preferredSubtitleLanguages: ['deu'],
|
||||
includeSubtitles: true,
|
||||
})
|
||||
).toEqual({
|
||||
videoStreamIndex: 0,
|
||||
audioStreamIndexes: [2],
|
||||
subtitleStreamIndexes: [3],
|
||||
});
|
||||
});
|
||||
|
||||
it('validates stream existence, type and duplicate mappings', () => {
|
||||
const result = validateStreamSelection(media, {
|
||||
videoStreamIndex: 1,
|
||||
audioStreamIndexes: [2, 2, 99],
|
||||
subtitleStreamIndexes: [],
|
||||
});
|
||||
expect(result.valid).toBe(false);
|
||||
expect(result.issues.map((issue) => issue.code)).toEqual([
|
||||
'wrong-stream-type',
|
||||
'duplicate-stream',
|
||||
'stream-not-found',
|
||||
]);
|
||||
});
|
||||
|
||||
it('generates structured map arguments in deterministic order', () => {
|
||||
expect(
|
||||
streamSelectionMapArgs({
|
||||
videoStreamIndex: 0,
|
||||
audioStreamIndexes: [2],
|
||||
subtitleStreamIndexes: [3],
|
||||
})
|
||||
).toEqual(['-map', '0:0', '-map', '0:2', '-map', '0:3']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('browser playback assessment', () => {
|
||||
const mp4 = probe([
|
||||
stream(0, 'video', { codecName: 'h264' }),
|
||||
stream(1, 'audio', { codecName: 'aac' }),
|
||||
]);
|
||||
|
||||
it('builds codec-qualified and container-only MIME candidates', () => {
|
||||
expect(buildPlaybackMimeCandidates(mp4)).toEqual([
|
||||
'video/mp4; codecs="avc1.42E01E, mp4a.40.2"',
|
||||
'video/mp4',
|
||||
]);
|
||||
});
|
||||
|
||||
it('keeps browser support separate from FFmpeg readability', () => {
|
||||
expect(
|
||||
assessBrowserPlayback(mp4, (mime) =>
|
||||
mime.includes('codecs') ? 'probably' : ''
|
||||
)
|
||||
).toMatchObject({
|
||||
support: 'probably',
|
||||
needsPreviewProxy: false,
|
||||
});
|
||||
expect(assessBrowserPlayback(mp4, () => '')).toMatchObject({
|
||||
support: 'unsupported',
|
||||
needsPreviewProxy: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('does not treat an audio/video MIME prefix as playable when the browser rejects it', () => {
|
||||
expect(
|
||||
assessGeneratedResultPreview(
|
||||
{
|
||||
fileName: 'generated.flac',
|
||||
mimeType: 'audio/flac',
|
||||
},
|
||||
() => ''
|
||||
)
|
||||
).toMatchObject({
|
||||
kind: 'audio',
|
||||
previewable: false,
|
||||
support: 'unsupported',
|
||||
});
|
||||
expect(
|
||||
assessGeneratedResultPreview(
|
||||
{
|
||||
fileName: 'generated.mp4',
|
||||
mimeType: 'video/mp4',
|
||||
probe: mp4,
|
||||
},
|
||||
(mime) => (mime.includes('codecs') ? 'probably' : '')
|
||||
)
|
||||
).toMatchObject({
|
||||
kind: 'video',
|
||||
previewable: true,
|
||||
support: 'probably',
|
||||
});
|
||||
expect(
|
||||
assessGeneratedResultPreview(
|
||||
{
|
||||
fileName: 'frame.png',
|
||||
mimeType: 'image/png',
|
||||
},
|
||||
() => ''
|
||||
)
|
||||
).toMatchObject({ kind: 'image', previewable: true });
|
||||
expect(
|
||||
assessGeneratedResultPreview(
|
||||
{
|
||||
fileName: 'frame.tiff',
|
||||
mimeType: 'image/tiff',
|
||||
},
|
||||
() => ''
|
||||
)
|
||||
).toMatchObject({
|
||||
kind: 'image',
|
||||
previewable: false,
|
||||
support: 'unsupported',
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user