feat(subtitles): use loaded subtitles to guide generation timing (#249)

This commit is contained in:
2026-09-18 00:24:42 -07:00
committed by GitHub
parent 186d4a0640
commit a8c16147ad
24 changed files with 965 additions and 75 deletions
+30 -10
View File
@@ -8,6 +8,10 @@ import {
resolveSubtitleGenerationTools,
} from '../../src/core/services/subtitle-generation.js';
import { requireSubtitleGenerationTools } from '../../src/core/services/subtitle-generation-tools.js';
import {
readSubtitleGenerationReferences,
type SubtitleGenerationReference,
} from '../../src/core/services/subtitle-generation-reference.js';
import {
resolveSubtitleGenerationConfig,
type SubtitleGenerationProgress,
@@ -69,12 +73,9 @@ async function readMpvMedia(socketPath: string, command: GenerationCommandDeps['
return localMediaPath(media, workingDirectory);
}
async function readMpvAudioStream(
socketPath: string,
command: GenerationCommandDeps['mpvCommand'],
) {
const tracks = await command(socketPath, ['get_property', 'track-list'], 1000);
for (const track of Array.isArray(tracks) ? tracks : []) {
function selectedMpvAudioStream(value: unknown) {
const tracks: unknown[] = Array.isArray(value) ? value : [];
for (const track of tracks) {
if (
typeof track === 'object' &&
track !== null &&
@@ -160,11 +161,29 @@ export async function runGenerateSubtitlesCommand(
const mediaPath = options.mediaPath ? localMediaPath(options.mediaPath) : currentMedia;
if (!mediaPath)
throw new Error('Pass a local video file or open one in mpv before running generate-subs.');
// Capture audio and subtitle timing together before model setup can yield to playback changes.
const matchesCurrentMedia = currentMedia !== null && sameFile(currentMedia, mediaPath);
const tracks = matchesCurrentMedia
? await deps
.mpvCommand(context.mpvSocketPath, ['get_property', 'track-list'], 1000)
.catch(() => null)
: null;
let references: SubtitleGenerationReference[] = [];
if (matchesCurrentMedia) {
const candidates = await readSubtitleGenerationReferences(tracks, (name) =>
deps.mpvCommand(context.mpvSocketPath, ['get_property', name], 1000),
);
const stillPlaying = await readMpvMedia(context.mpvSocketPath, deps.mpvCommand).catch(
() => null,
);
if (stillPlaying && sameFile(stillPlaying, mediaPath)) references = candidates;
else if (!options.mediaPath && options.audioStreamIndex === undefined)
throw new Error(
'The current mpv media changed or could not be verified while reading tracks. Run generate-subs again.',
);
}
const audioStreamIndex =
options.audioStreamIndex ??
(!options.mediaPath
? await readMpvAudioStream(context.mpvSocketPath, deps.mpvCommand)
: undefined);
options.audioStreamIndex ?? (!options.mediaPath ? selectedMpvAudioStream(tracks) : undefined);
const onProgress = createGenerationProgressReporter(write);
// Missing executables fail here, before any model download starts.
requireSubtitleGenerationTools(await deps.resolveTools(config));
@@ -183,6 +202,7 @@ export async function runGenerateSubtitlesCommand(
modelDirectory,
mediaPath,
audioStreamIndex,
references,
outputPath: options.outputPath
? path.resolve(resolvePathMaybe(options.outputPath))
: undefined,