fix(jellyfin): subtitle timing, resume progress, and overlay sync

- Add per-stream subtitle delay persistence and auto timeline-offset correction
- Strip server-selected subtitle stream from mpv load URL; suppress plugin subtitle rearm and auto-start during app-managed preload
- Fix resume position lost when mpv resets on stop; use last known position for final progress/stopped reports
- Keep Play vs Resume distinct to avoid early seek race on normal play
- Fix discovery resume when remote play sends StartPositionTicks=0 despite saved progress
- Deduplicate show/hide overlay commands using recorded visibility state
- Rewrite docs-site Jellyfin page around cast-to-device UX
This commit is contained in:
2026-05-24 02:49:47 -07:00
parent dc9d7b77bb
commit 127e1ea88e
42 changed files with 2113 additions and 298 deletions
+8 -1
View File
@@ -21,6 +21,7 @@ type SubtitleDelayShiftDeps = {
loadSubtitleSourceText: (source: string) => Promise<string>;
sendMpvCommand: (command: Array<string | number>) => void;
showMpvOsd: (text: string) => void;
onSubtitleDelayShifted?: (delaySeconds: number) => void;
};
function asTrackId(value: unknown): number | null {
@@ -175,10 +176,11 @@ export function createShiftSubtitleDelayToAdjacentCueHandler(deps: SubtitleDelay
throw new Error('MPV not connected.');
}
const [trackListRaw, sidRaw, subStartRaw] = await Promise.all([
const [trackListRaw, sidRaw, subStartRaw, subDelayRaw] = await Promise.all([
client.requestProperty('track-list'),
client.requestProperty('sid'),
client.requestProperty('sub-start'),
client.requestProperty('sub-delay'),
]);
const currentStart =
@@ -198,6 +200,11 @@ export function createShiftSubtitleDelayToAdjacentCueHandler(deps: SubtitleDelay
const targetStart = findAdjacentCueStart(cueStarts, currentStart, direction);
const delta = targetStart - currentStart;
deps.sendMpvCommand(['add', 'sub-delay', delta]);
const currentDelay =
typeof subDelayRaw === 'number' && Number.isFinite(subDelayRaw) ? subDelayRaw : 0;
try {
deps.onSubtitleDelayShifted?.(currentDelay + delta);
} catch {}
deps.showMpvOsd('Subtitle delay: ${sub-delay}');
};
}