Files
SubMiner/src/core/services/subtitle-generation-reference.test.ts
T

138 lines
4.1 KiB
TypeScript

import assert from 'node:assert/strict';
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import path from 'node:path';
import test from 'node:test';
import {
loadSubtitleGenerationReference,
readSubtitleGenerationReferences,
subtitleGenerationReferences,
} from './subtitle-generation-reference';
const embedded = { type: 'sub', codec: 'ass', 'ff-index': 2, lang: 'eng' };
test('references exclude signs, songs, forced, bitmap and generated tracks even when selected', () => {
const excluded = [
'Signs & Songs',
'SignsSongs',
'Signs/Songs',
'S&S',
'S+S',
'Forced',
'Karaoke',
'OP',
'ED',
'English lyrics',
'Generated Japanese',
];
assert.deepEqual(
subtitleGenerationReferences([
...excluded.map((title) => ({ ...embedded, title, selected: true })),
{ ...embedded, forced: true },
{ ...embedded, codec: 'hdmv_pgs_subtitle' },
{
...embedded,
title: 'English',
external: true,
'external-filename': '/subs/show.en.signs.ass',
},
{ ...embedded, title: 'English Full' },
]).map((reference) => reference.label),
['English Full'],
);
});
test('references rank English dialogue first and resolve loaded external files against mpv cwd', () => {
const refs = subtitleGenerationReferences(
[
{ ...embedded, title: 'French Full', lang: 'fra', selected: true },
{ ...embedded, title: 'English' },
{ type: 'sub', external: true, 'external-filename': 'subs/show.en.full.srt' },
{ type: 'sub', external: true, 'external-filename': 'https://example.com/en.srt' },
{ type: 'sub', 'ff-index': -1 },
null,
],
'/mpv',
);
assert.deepEqual(
refs.map((ref) => ref.label),
['show.en.full.srt', 'English', 'French Full'],
);
assert.deepEqual(refs[0]?.source, { kind: 'external', path: '/mpv/subs/show.en.full.srt' });
assert.deepEqual(
subtitleGenerationReferences([
{ type: 'sub', external: true, 'external-filename': 'relative.en.srt' },
]),
[],
);
});
test('reference discovery captures primary and secondary subtitle delays', async () => {
const properties: Record<string, unknown> = {
'working-directory': '/mpv',
sid: 1,
'secondary-sid': 2,
'sub-delay': 1.5,
'secondary-sub-delay': -2,
};
const refs = await readSubtitleGenerationReferences(
[
{ ...embedded, id: 1 },
{ ...embedded, id: 2 },
{ ...embedded, id: 3 },
],
async (name) => properties[name],
);
assert.deepEqual(
refs.map((ref) => ref.delaySeconds),
[1.5, -2, 0],
);
});
test('reference extraction retries unreadable tracks, restores audio offset and ignores marked lyrics', async () => {
const directory = await mkdtemp(path.join(tmpdir(), 'generation-reference-'));
try {
const ffmpegPath = path.join(directory, 'ffmpeg');
await writeFile(
ffmpegPath,
`#!${process.execPath}
const args = process.argv.slice(2);
if (args[args.indexOf('-map') + 1] === '0:2') process.exit(1);
require('node:fs').writeFileSync(args.at(-1), '1\\n00:00:10,000 --> 00:00:12,000\\nHello\\n\\n2\\n00:00:20,000 --> 00:00:22,000\\n♪ Song ♪\\n\\n3\\n00:00:30,000 --> 00:00:32,000\\nWorld\\n');
`,
{ mode: 0o755 },
);
const refs = subtitleGenerationReferences([{ ...embedded }, { ...embedded, 'ff-index': 3 }]);
const hints = await loadSubtitleGenerationReference({
references: refs,
mediaPath: '/video.mkv',
ffmpegPath,
directory,
audioOffset: 2.5,
});
assert.deepEqual(hints, [7.5, 27.5]);
assert.deepEqual(
await loadSubtitleGenerationReference({
references: refs.slice(0, 1),
mediaPath: '/video.mkv',
ffmpegPath,
directory,
audioOffset: 0,
}),
[],
);
await assert.rejects(
loadSubtitleGenerationReference({
references: refs,
mediaPath: '/video.mkv',
ffmpegPath,
directory,
audioOffset: 0,
signal: AbortSignal.abort(),
}),
);
} finally {
await rm(directory, { recursive: true, force: true });
}
});