mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-08-07 07:21:35 -07:00
fix(logging): surface subtitle processing debug/warn logs (#182)
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
type: fixed
|
||||||
|
area: logging
|
||||||
|
|
||||||
|
- Background startup now respects the configured logging level when `--log-level` is not explicitly provided.
|
||||||
+1
-1
@@ -149,7 +149,7 @@ Once Jellyfin is configured, the tray menu includes `Jellyfin Discovery` for sta
|
|||||||
|
|
||||||
- `--log-level` controls logger verbosity.
|
- `--log-level` controls logger verbosity.
|
||||||
- `--dev` and `--debug` are app/dev-mode switches; they are not log-level aliases.
|
- `--dev` and `--debug` are app/dev-mode switches; they are not log-level aliases.
|
||||||
- `--background` defaults to quieter logging (`warn`) unless `--log-level` is set.
|
- `--background` starts at the default quieter logging level (`warn`), then follows `logging.level` after config loads. An explicit `--log-level` remains the override.
|
||||||
- `--background` launched from a terminal detaches and returns the prompt; stop it with tray Quit or `SubMiner.AppImage --stop` (`SubMiner.exe --stop` on Windows).
|
- `--background` launched from a terminal detaches and returns the prompt; stop it with tray Quit or `SubMiner.AppImage --stop` (`SubMiner.exe --stop` on Windows).
|
||||||
- Linux desktop launcher starts SubMiner with `--background` by default (via electron-builder `linux.executableArgs`).
|
- Linux desktop launcher starts SubMiner with `--background` by default (via electron-builder `linux.executableArgs`).
|
||||||
- On Hyprland and other Wayland compositors, the tray icon appears only when your panel provides a StatusNotifier/AppIndicator tray host.
|
- On Hyprland and other Wayland compositors, the tray icon appears only when your panel provides a StatusNotifier/AppIndicator tray host.
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ test('runStartupBootstrapRuntime skips lifecycle when generate-config flow handl
|
|||||||
assert.deepEqual(calls, ['setLog:warn:cli', 'forceX11', 'enforceWayland']);
|
assert.deepEqual(calls, ['setLog:warn:cli', 'forceX11', 'enforceWayland']);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('runStartupBootstrapRuntime enables quiet background mode by default', () => {
|
test('runStartupBootstrapRuntime lets config govern background log level by default', () => {
|
||||||
const calls: string[] = [];
|
const calls: string[] = [];
|
||||||
const args = makeArgs({ background: true });
|
const args = makeArgs({ background: true });
|
||||||
|
|
||||||
@@ -222,7 +222,7 @@ test('runStartupBootstrapRuntime enables quiet background mode by default', () =
|
|||||||
});
|
});
|
||||||
|
|
||||||
assert.equal(result.backgroundMode, true);
|
assert.equal(result.backgroundMode, true);
|
||||||
assert.deepEqual(calls, ['setLog:warn:cli', 'forceX11', 'enforceWayland', 'startLifecycle']);
|
assert.deepEqual(calls, ['forceX11', 'enforceWayland', 'startLifecycle']);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('runStartupBootstrapRuntime enables quiet update mode by default', () => {
|
test('runStartupBootstrapRuntime enables quiet update mode by default', () => {
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export function runStartupBootstrapRuntime(
|
|||||||
|
|
||||||
if (initialArgs.logLevel) {
|
if (initialArgs.logLevel) {
|
||||||
deps.setLogLevel(initialArgs.logLevel, 'cli');
|
deps.setLogLevel(initialArgs.logLevel, 'cli');
|
||||||
} else if (initialArgs.background || initialArgs.update) {
|
} else if (initialArgs.update) {
|
||||||
deps.setLogLevel('warn', 'cli');
|
deps.setLogLevel('warn', 'cli');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1982,6 +1982,7 @@ const resolveActiveSubtitleSidebarSourceHandler = createResolveActiveSubtitleSid
|
|||||||
getFfmpegPath: () => configService.getConfig().subsync.ffmpeg_path.trim() || 'ffmpeg',
|
getFfmpegPath: () => configService.getConfig().subsync.ffmpeg_path.trim() || 'ffmpeg',
|
||||||
extractInternalSubtitleTrack: (ffmpegPath, videoPath, track) =>
|
extractInternalSubtitleTrack: (ffmpegPath, videoPath, track) =>
|
||||||
extractInternalSubtitleTrackToTempFile(ffmpegPath, videoPath, track),
|
extractInternalSubtitleTrackToTempFile(ffmpegPath, videoPath, track),
|
||||||
|
logDebug: (message) => logger.debug(message),
|
||||||
});
|
});
|
||||||
|
|
||||||
const refreshSubtitlePrefetchFromActiveTrackHandler =
|
const refreshSubtitlePrefetchFromActiveTrackHandler =
|
||||||
@@ -1991,6 +1992,8 @@ const refreshSubtitlePrefetchFromActiveTrackHandler =
|
|||||||
shouldKeepExistingCuesOnMissingSource: (videoPath) => isYoutubeMediaPath(videoPath),
|
shouldKeepExistingCuesOnMissingSource: (videoPath) => isYoutubeMediaPath(videoPath),
|
||||||
subtitlePrefetchInitController,
|
subtitlePrefetchInitController,
|
||||||
resolveActiveSubtitleSidebarSource: (input) => resolveActiveSubtitleSidebarSourceHandler(input),
|
resolveActiveSubtitleSidebarSource: (input) => resolveActiveSubtitleSidebarSourceHandler(input),
|
||||||
|
logDebug: (message) => logger.debug(message),
|
||||||
|
logWarn: (message) => logger.warn(message),
|
||||||
});
|
});
|
||||||
|
|
||||||
const subtitlePrefetchRuntime = {
|
const subtitlePrefetchRuntime = {
|
||||||
@@ -4366,6 +4369,7 @@ const {
|
|||||||
refreshDiscordPresence: () => {
|
refreshDiscordPresence: () => {
|
||||||
discordPresenceRuntime.publishDiscordPresence();
|
discordPresenceRuntime.publishDiscordPresence();
|
||||||
},
|
},
|
||||||
|
logSubtitleProcessingDebug: (message: string) => logger.debug(message),
|
||||||
ensureImmersionTrackerInitialized: () => {
|
ensureImmersionTrackerInitialized: () => {
|
||||||
ensureImmersionTrackerStarted();
|
ensureImmersionTrackerStarted();
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -70,6 +70,24 @@ test('subtitle change handler broadcasts cached annotated payload immediately wh
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('subtitle change handler logs debug when a cached payload is emitted immediately', () => {
|
||||||
|
const debugs: string[] = [];
|
||||||
|
const handler = createHandleMpvSubtitleChangeHandler({
|
||||||
|
setCurrentSubText: () => {},
|
||||||
|
getImmediateSubtitlePayload: (text) => (text ? { text, tokens: [] } : null),
|
||||||
|
broadcastSubtitle: () => {},
|
||||||
|
onSubtitleChange: () => {},
|
||||||
|
refreshDiscordPresence: () => {},
|
||||||
|
logDebug: (message) => debugs.push(message),
|
||||||
|
});
|
||||||
|
|
||||||
|
handler({ text: 'キャッシュ済みの行' });
|
||||||
|
handler({ text: '' });
|
||||||
|
|
||||||
|
assert.equal(debugs.length, 1);
|
||||||
|
assert.match(debugs[0]!, /cached subtitle/);
|
||||||
|
});
|
||||||
|
|
||||||
test('subtitle change handler emits cached annotation after forwarding the subtitle change', () => {
|
test('subtitle change handler emits cached annotation after forwarding the subtitle change', () => {
|
||||||
const calls: string[] = [];
|
const calls: string[] = [];
|
||||||
const handler = createHandleMpvSubtitleChangeHandler({
|
const handler = createHandleMpvSubtitleChangeHandler({
|
||||||
|
|||||||
@@ -20,11 +20,15 @@ export function createHandleMpvSubtitleChangeHandler(deps: {
|
|||||||
broadcastSubtitle: (payload: SubtitleData) => void;
|
broadcastSubtitle: (payload: SubtitleData) => void;
|
||||||
onSubtitleChange: (text: string) => void;
|
onSubtitleChange: (text: string) => void;
|
||||||
refreshDiscordPresence: () => void;
|
refreshDiscordPresence: () => void;
|
||||||
|
logDebug?: (message: string) => void;
|
||||||
}) {
|
}) {
|
||||||
return ({ text }: { text: string }): void => {
|
return ({ text }: { text: string }): void => {
|
||||||
deps.setCurrentSubText(text);
|
deps.setCurrentSubText(text);
|
||||||
const immediatePayload = deps.getImmediateSubtitlePayload?.(text) ?? null;
|
const immediatePayload = deps.getImmediateSubtitlePayload?.(text) ?? null;
|
||||||
if (immediatePayload) {
|
if (immediatePayload) {
|
||||||
|
deps.logDebug?.(
|
||||||
|
`[subtitle-processing] emitted cached subtitle immediately (${text.length} chars)`,
|
||||||
|
);
|
||||||
deps.onSubtitleChange(text);
|
deps.onSubtitleChange(text);
|
||||||
(deps.emitImmediateSubtitle ?? deps.broadcastSubtitle)(immediatePayload);
|
(deps.emitImmediateSubtitle ?? deps.broadcastSubtitle)(immediatePayload);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ export function createBindMpvMainEventHandlersHandler(deps: {
|
|||||||
emitImmediateSubtitle?: (payload: SubtitleData) => void;
|
emitImmediateSubtitle?: (payload: SubtitleData) => void;
|
||||||
broadcastSubtitle: (payload: SubtitleData) => void;
|
broadcastSubtitle: (payload: SubtitleData) => void;
|
||||||
onSubtitleChange: (text: string) => void;
|
onSubtitleChange: (text: string) => void;
|
||||||
|
logSubtitleProcessingDebug?: (message: string) => void;
|
||||||
refreshDiscordPresence: () => void;
|
refreshDiscordPresence: () => void;
|
||||||
|
|
||||||
setCurrentSubAssText: (text: string) => void;
|
setCurrentSubAssText: (text: string) => void;
|
||||||
@@ -123,6 +124,9 @@ export function createBindMpvMainEventHandlersHandler(deps: {
|
|||||||
: undefined,
|
: undefined,
|
||||||
broadcastSubtitle: (payload) => deps.broadcastSubtitle(payload),
|
broadcastSubtitle: (payload) => deps.broadcastSubtitle(payload),
|
||||||
onSubtitleChange: (text) => deps.onSubtitleChange(text),
|
onSubtitleChange: (text) => deps.onSubtitleChange(text),
|
||||||
|
logDebug: deps.logSubtitleProcessingDebug
|
||||||
|
? (message) => deps.logSubtitleProcessingDebug?.(message)
|
||||||
|
: undefined,
|
||||||
refreshDiscordPresence: () => deps.refreshDiscordPresence(),
|
refreshDiscordPresence: () => deps.refreshDiscordPresence(),
|
||||||
});
|
});
|
||||||
const handleMpvSubtitleAssChange = createHandleMpvSubtitleAssChangeHandler({
|
const handleMpvSubtitleAssChange = createHandleMpvSubtitleAssChangeHandler({
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ export function createBuildBindMpvMainEventHandlersMainDepsHandler(deps: {
|
|||||||
getImmediateSubtitlePayload?: (text: string) => SubtitleData | null;
|
getImmediateSubtitlePayload?: (text: string) => SubtitleData | null;
|
||||||
emitImmediateSubtitle?: (payload: SubtitleData) => void;
|
emitImmediateSubtitle?: (payload: SubtitleData) => void;
|
||||||
onSubtitleChange: (text: string) => void;
|
onSubtitleChange: (text: string) => void;
|
||||||
|
logSubtitleProcessingDebug?: (message: string) => void;
|
||||||
onSubtitleTrackChange?: (sid: number | null) => void;
|
onSubtitleTrackChange?: (sid: number | null) => void;
|
||||||
onSubtitleTrackListChange?: (trackList: unknown[] | null) => void;
|
onSubtitleTrackListChange?: (trackList: unknown[] | null) => void;
|
||||||
updateCurrentMediaPath: (path: string) => void;
|
updateCurrentMediaPath: (path: string) => void;
|
||||||
@@ -155,6 +156,9 @@ export function createBuildBindMpvMainEventHandlersMainDepsHandler(deps: {
|
|||||||
broadcastSubtitle: (payload: SubtitleData) =>
|
broadcastSubtitle: (payload: SubtitleData) =>
|
||||||
deps.broadcastToOverlayWindows('subtitle:set', payload),
|
deps.broadcastToOverlayWindows('subtitle:set', payload),
|
||||||
onSubtitleChange: (text: string) => deps.onSubtitleChange(text),
|
onSubtitleChange: (text: string) => deps.onSubtitleChange(text),
|
||||||
|
logSubtitleProcessingDebug: deps.logSubtitleProcessingDebug
|
||||||
|
? (message: string) => deps.logSubtitleProcessingDebug!(message)
|
||||||
|
: undefined,
|
||||||
onSubtitleTrackChange: deps.onSubtitleTrackChange
|
onSubtitleTrackChange: deps.onSubtitleTrackChange
|
||||||
? (sid: number | null) => deps.onSubtitleTrackChange!(sid)
|
? (sid: number | null) => deps.onSubtitleTrackChange!(sid)
|
||||||
: undefined,
|
: undefined,
|
||||||
|
|||||||
@@ -234,3 +234,26 @@ test('subtitle prefetch init clears parsed cues when initialization fails', asyn
|
|||||||
|
|
||||||
assert.deepEqual(cueUpdates, [null]);
|
assert.deepEqual(cueUpdates, [null]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('subtitle prefetch init logs a warning when the source parses to zero cues', async () => {
|
||||||
|
const warnings: string[] = [];
|
||||||
|
const controller = createSubtitlePrefetchInitController({
|
||||||
|
getCurrentService: () => null,
|
||||||
|
setCurrentService: () => {},
|
||||||
|
loadSubtitleSourceText: async () => 'not really subtitles',
|
||||||
|
parseSubtitleCues: (): SubtitleCue[] => [],
|
||||||
|
createSubtitlePrefetchService: () => {
|
||||||
|
throw new Error('should not create a service without cues');
|
||||||
|
},
|
||||||
|
tokenizeSubtitle: async () => null,
|
||||||
|
preCacheTokenization: () => {},
|
||||||
|
isCacheFull: () => false,
|
||||||
|
logInfo: () => {},
|
||||||
|
logWarn: (message) => warnings.push(message),
|
||||||
|
});
|
||||||
|
|
||||||
|
await controller.initSubtitlePrefetch('/tmp/broken.ass', 0);
|
||||||
|
|
||||||
|
assert.equal(warnings.length, 1);
|
||||||
|
assert.match(warnings[0]!, /\[subtitle-prefetch\].*0 cues.*\/tmp\/broken\.ass/);
|
||||||
|
});
|
||||||
|
|||||||
@@ -59,6 +59,9 @@ export function createSubtitlePrefetchInitController(
|
|||||||
const cues = deps.parseSubtitleCues(content, sourcePath);
|
const cues = deps.parseSubtitleCues(content, sourcePath);
|
||||||
if (revision !== initRevision || cues.length === 0) {
|
if (revision !== initRevision || cues.length === 0) {
|
||||||
if (revision === initRevision) {
|
if (revision === initRevision) {
|
||||||
|
deps.logWarn(
|
||||||
|
`[subtitle-prefetch] parsed 0 cues from ${sourcePath}; prefetch disabled for this source`,
|
||||||
|
);
|
||||||
deps.onParsedSubtitleCuesChanged?.(null, null);
|
deps.onParsedSubtitleCuesChanged?.(null, null);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -130,3 +130,121 @@ test('subtitle prefetch runtime does not extract internal subtitle tracks from r
|
|||||||
assert.equal(resolved, null);
|
assert.equal(resolved, null);
|
||||||
assert.equal(extracted, false);
|
assert.equal(extracted, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('subtitle prefetch refresh logs a warning when source resolution throws', async () => {
|
||||||
|
const warnings: string[] = [];
|
||||||
|
const refresh = createRefreshSubtitlePrefetchFromActiveTrackHandler({
|
||||||
|
getMpvClient: () => ({
|
||||||
|
connected: true,
|
||||||
|
requestProperty: async (name) => (name === 'path' ? '/media/video.mkv' : null),
|
||||||
|
}),
|
||||||
|
getLastObservedTimePos: () => 0,
|
||||||
|
subtitlePrefetchInitController: {
|
||||||
|
cancelPendingInit: () => {},
|
||||||
|
initSubtitlePrefetch: async () => {},
|
||||||
|
},
|
||||||
|
resolveActiveSubtitleSidebarSource: async () => {
|
||||||
|
throw new Error('ffmpeg ENOENT');
|
||||||
|
},
|
||||||
|
logWarn: (message) => warnings.push(message),
|
||||||
|
});
|
||||||
|
|
||||||
|
await refresh();
|
||||||
|
|
||||||
|
assert.equal(warnings.length, 1);
|
||||||
|
assert.match(warnings[0]!, /\[subtitle-prefetch\].*ffmpeg ENOENT/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('subtitle prefetch refresh logs debug when mpv client is not connected', async () => {
|
||||||
|
const debugs: string[] = [];
|
||||||
|
const refresh = createRefreshSubtitlePrefetchFromActiveTrackHandler({
|
||||||
|
getMpvClient: () => null,
|
||||||
|
getLastObservedTimePos: () => 0,
|
||||||
|
subtitlePrefetchInitController: {
|
||||||
|
cancelPendingInit: () => {},
|
||||||
|
initSubtitlePrefetch: async () => {},
|
||||||
|
},
|
||||||
|
resolveActiveSubtitleSidebarSource: async () => null,
|
||||||
|
logDebug: (message) => debugs.push(message),
|
||||||
|
});
|
||||||
|
|
||||||
|
await refresh();
|
||||||
|
|
||||||
|
assert.equal(debugs.length, 1);
|
||||||
|
assert.match(debugs[0]!, /\[subtitle-prefetch\].*not connected/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('subtitle prefetch refresh logs debug when no subtitle source resolves', async () => {
|
||||||
|
const debugs: string[] = [];
|
||||||
|
const cancels: number[] = [];
|
||||||
|
const refresh = createRefreshSubtitlePrefetchFromActiveTrackHandler({
|
||||||
|
getMpvClient: () => ({
|
||||||
|
connected: true,
|
||||||
|
requestProperty: async (name) => (name === 'path' ? '/media/video.mkv' : null),
|
||||||
|
}),
|
||||||
|
getLastObservedTimePos: () => 0,
|
||||||
|
subtitlePrefetchInitController: {
|
||||||
|
cancelPendingInit: () => {
|
||||||
|
cancels.push(1);
|
||||||
|
},
|
||||||
|
initSubtitlePrefetch: async () => {},
|
||||||
|
},
|
||||||
|
resolveActiveSubtitleSidebarSource: async () => null,
|
||||||
|
logDebug: (message) => debugs.push(message),
|
||||||
|
});
|
||||||
|
|
||||||
|
await refresh();
|
||||||
|
|
||||||
|
assert.deepEqual(cancels, [1]);
|
||||||
|
assert.equal(debugs.length, 1);
|
||||||
|
assert.match(debugs[0]!, /\[subtitle-prefetch\].*no active subtitle source/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('subtitle source resolver logs debug when internal track extraction is unavailable', async () => {
|
||||||
|
const debugs: string[] = [];
|
||||||
|
const resolveSource = createResolveActiveSubtitleSidebarSourceHandler({
|
||||||
|
getFfmpegPath: () => 'ffmpeg',
|
||||||
|
extractInternalSubtitleTrack: async () => null,
|
||||||
|
logDebug: (message) => debugs.push(message),
|
||||||
|
});
|
||||||
|
|
||||||
|
const resolved = await resolveSource({
|
||||||
|
currentExternalFilenameRaw: null,
|
||||||
|
currentTrackRaw: {
|
||||||
|
type: 'sub',
|
||||||
|
id: 3,
|
||||||
|
'ff-index': 7,
|
||||||
|
codec: 'hdmv_pgs_subtitle',
|
||||||
|
},
|
||||||
|
trackListRaw: [],
|
||||||
|
sidRaw: 3,
|
||||||
|
videoPath: '/media/video.mkv',
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(resolved, null);
|
||||||
|
assert.equal(debugs.length, 1);
|
||||||
|
assert.match(debugs[0]!, /\[subtitle-prefetch\].*extraction.*hdmv_pgs_subtitle/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('subtitle source resolver logs debug when no active subtitle track is selected', async () => {
|
||||||
|
const debugs: string[] = [];
|
||||||
|
const resolveSource = createResolveActiveSubtitleSidebarSourceHandler({
|
||||||
|
getFfmpegPath: () => 'ffmpeg',
|
||||||
|
extractInternalSubtitleTrack: async () => {
|
||||||
|
throw new Error('should not extract without a track');
|
||||||
|
},
|
||||||
|
logDebug: (message) => debugs.push(message),
|
||||||
|
});
|
||||||
|
|
||||||
|
const resolved = await resolveSource({
|
||||||
|
currentExternalFilenameRaw: null,
|
||||||
|
currentTrackRaw: null,
|
||||||
|
trackListRaw: [],
|
||||||
|
sidRaw: null,
|
||||||
|
videoPath: '/media/video.mkv',
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(resolved, null);
|
||||||
|
assert.equal(debugs.length, 1);
|
||||||
|
assert.match(debugs[0]!, /\[subtitle-prefetch\].*no active subtitle track/);
|
||||||
|
});
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ export function createResolveActiveSubtitleSidebarSourceHandler(deps: {
|
|||||||
videoPath: string,
|
videoPath: string,
|
||||||
track: MpvSubtitleTrackLike,
|
track: MpvSubtitleTrackLike,
|
||||||
) => Promise<{ path: string; cleanup: () => Promise<void> } | null>;
|
) => Promise<{ path: string; cleanup: () => Promise<void> } | null>;
|
||||||
|
logDebug?: (message: string) => void;
|
||||||
}) {
|
}) {
|
||||||
return async (input: {
|
return async (input: {
|
||||||
currentExternalFilenameRaw: unknown;
|
currentExternalFilenameRaw: unknown;
|
||||||
@@ -104,6 +105,7 @@ export function createResolveActiveSubtitleSidebarSourceHandler(deps: {
|
|||||||
|
|
||||||
const track = getActiveSubtitleTrack(input.currentTrackRaw, input.trackListRaw, input.sidRaw);
|
const track = getActiveSubtitleTrack(input.currentTrackRaw, input.trackListRaw, input.sidRaw);
|
||||||
if (!track) {
|
if (!track) {
|
||||||
|
deps.logDebug?.('[subtitle-prefetch] no active subtitle track selected yet');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,6 +116,7 @@ export function createResolveActiveSubtitleSidebarSourceHandler(deps: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isRemoteMediaPath(input.videoPath)) {
|
if (isRemoteMediaPath(input.videoPath)) {
|
||||||
|
deps.logDebug?.('[subtitle-prefetch] skipping internal subtitle extraction for remote media');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,6 +126,9 @@ export function createResolveActiveSubtitleSidebarSourceHandler(deps: {
|
|||||||
track,
|
track,
|
||||||
);
|
);
|
||||||
if (!extracted) {
|
if (!extracted) {
|
||||||
|
deps.logDebug?.(
|
||||||
|
`[subtitle-prefetch] internal subtitle extraction unavailable (codec=${String(track.codec ?? 'unknown')}, ff-index=${String(track['ff-index'] ?? 'unknown')})`,
|
||||||
|
);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,10 +150,13 @@ export function createRefreshSubtitlePrefetchFromActiveTrackHandler(deps: {
|
|||||||
resolveActiveSubtitleSidebarSource: (
|
resolveActiveSubtitleSidebarSource: (
|
||||||
input: Parameters<ReturnType<typeof createResolveActiveSubtitleSidebarSourceHandler>>[0],
|
input: Parameters<ReturnType<typeof createResolveActiveSubtitleSidebarSourceHandler>>[0],
|
||||||
) => Promise<ActiveSubtitleSidebarSource | null>;
|
) => Promise<ActiveSubtitleSidebarSource | null>;
|
||||||
|
logDebug?: (message: string) => void;
|
||||||
|
logWarn?: (message: string) => void;
|
||||||
}) {
|
}) {
|
||||||
return async (): Promise<void> => {
|
return async (): Promise<void> => {
|
||||||
const client = deps.getMpvClient();
|
const client = deps.getMpvClient();
|
||||||
if (!client?.connected) {
|
if (!client?.connected) {
|
||||||
|
deps.logDebug?.('[subtitle-prefetch] skipped refresh: mpv client not connected');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,6 +171,7 @@ export function createRefreshSubtitlePrefetchFromActiveTrackHandler(deps: {
|
|||||||
]);
|
]);
|
||||||
const videoPath = typeof videoPathRaw === 'string' ? videoPathRaw : '';
|
const videoPath = typeof videoPathRaw === 'string' ? videoPathRaw : '';
|
||||||
if (!videoPath) {
|
if (!videoPath) {
|
||||||
|
deps.logDebug?.('[subtitle-prefetch] skipped refresh: no media path');
|
||||||
deps.subtitlePrefetchInitController.cancelPendingInit();
|
deps.subtitlePrefetchInitController.cancelPendingInit();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -175,8 +185,14 @@ export function createRefreshSubtitlePrefetchFromActiveTrackHandler(deps: {
|
|||||||
});
|
});
|
||||||
if (!resolvedSource) {
|
if (!resolvedSource) {
|
||||||
if (deps.shouldKeepExistingCuesOnMissingSource?.(videoPath) === true) {
|
if (deps.shouldKeepExistingCuesOnMissingSource?.(videoPath) === true) {
|
||||||
|
deps.logDebug?.(
|
||||||
|
'[subtitle-prefetch] no active subtitle source resolved; keeping existing cues',
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
deps.logDebug?.(
|
||||||
|
'[subtitle-prefetch] no active subtitle source resolved; cancelling prefetch',
|
||||||
|
);
|
||||||
deps.subtitlePrefetchInitController.cancelPendingInit();
|
deps.subtitlePrefetchInitController.cancelPendingInit();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -190,8 +206,12 @@ export function createRefreshSubtitlePrefetchFromActiveTrackHandler(deps: {
|
|||||||
} finally {
|
} finally {
|
||||||
await resolvedSource.cleanup?.();
|
await resolvedSource.cleanup?.();
|
||||||
}
|
}
|
||||||
} catch {
|
} catch (error) {
|
||||||
// Skip refresh when the track query fails.
|
deps.logWarn?.(
|
||||||
|
`[subtitle-prefetch] failed to refresh from active track: ${
|
||||||
|
error instanceof Error ? error.message : String(error)
|
||||||
|
}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user