feat(plex): rework player timing (copyts) + player settings & personalization

Streaming / subtitle sync (the core fix):
- stream.py: add -copyts so HLS segments carry the true absolute PTS, and measure
  the real keyframe start K from seg_0 (ffprobe) -> return it as the session start.
  Fixes the seconds-long subtitle lead caused by using the requested seek offset
  instead of the keyframe ffmpeg actually lands on with video stream-copy.
- Add -noaccurate_seek so the re-encoded audio starts at the same keyframe as the
  video (was starting (X-K)s later -> seconds of silence after each seek/audio switch).
- Compensate the fixed ~1.0s lag hls.js introduces for non-zero-start copyts streams,
  folded into the session start so the clock, seeking and the subtitle shift are all
  content-accurate.
- /subtitle gains an offset param; _shift_vtt shifts absolute cues onto the session's
  zero-based clock and DROPS fully-past cue blocks (collapsing them to 0->0 made every
  past cue active at currentTime 0 on resume -> a pile-up until playback advanced).

Audio:
- /session + stream.py gain an audio A/V-sync offset (-itsoffset, full +/-, second
  input only when non-zero).

Player settings & personalization (per-account, persisted -> survive F5):
- storage.ts: useAccountPersistedObject (per-account JSON prefs blob).
- PlexPlayer: volume/mute, audio+subtitle language (index-based match, fixes the F5
  audio-revert), sync offsets, seek steps, subtitle style, auto-hide, play intent.
- Hotkeys A (cycle audio) / S (cycle subtitle), mouse-wheel volume, Ctrl+arrow fine
  seek, per-user plain/fine seek-step + auto-hide toggle.
- Subtitle appearance: size / colour / vertical position / background via ::cue + line.
- UI: split into a Tracks quick-menu + a tabbed gear panel (Sync | Playback | Subtitle);
  both dismiss on outside-click; edge-aware control tooltips.
- i18n en/hu/de for all new strings.
This commit is contained in:
npeter83 2026-07-08 21:35:38 +02:00
parent 6232a66878
commit 0a0703b769
8 changed files with 707 additions and 114 deletions

View file

@ -120,8 +120,25 @@
"fullscreen": "Vollbild",
"info": "Medieninfo",
"back": "Stopp & zurück zum Feed",
"help": "Diese Hilfe anzeigen"
}
"help": "Diese Hilfe anzeigen",
"audio": "Audiospur wechseln",
"subtitle": "Untertitel wechseln",
"fineSeek": "Feines Springen"
},
"settings": "Einstellungen",
"sync": "Synchronität",
"subOffset": "Untertitel-Verzögerung",
"audioOffset": "Audio-Verzögerung",
"playback": "Wiedergabe",
"seekStep": "Sprungweite",
"fineSeek": "Feines Springen (Strg)",
"autoHide": "Steuerung ausblenden",
"subStyle": "Untertitel-Stil",
"subSize": "Schriftgröße",
"subPos": "Position",
"subColor": "Textfarbe",
"subBg": "Hintergrund",
"subBgNone": "Keiner"
},
"info": {
"title": "Medieninfo",

View file

@ -120,8 +120,25 @@
"fullscreen": "Fullscreen",
"info": "Media info",
"back": "Stop & back to feed",
"help": "Show this help"
}
"help": "Show this help",
"audio": "Cycle audio track",
"subtitle": "Cycle subtitle",
"fineSeek": "Fine seek"
},
"settings": "Settings",
"sync": "Sync offset",
"subOffset": "Subtitle delay",
"audioOffset": "Audio delay",
"playback": "Playback",
"seekStep": "Seek step",
"fineSeek": "Fine seek (Ctrl)",
"autoHide": "Auto-hide controls",
"subStyle": "Subtitle style",
"subSize": "Text size",
"subPos": "Position",
"subColor": "Text color",
"subBg": "Background",
"subBgNone": "None"
},
"info": {
"title": "Media info",

View file

@ -120,8 +120,25 @@
"fullscreen": "Teljes képernyő",
"info": "Média infó",
"back": "Leállítás és vissza a feedre",
"help": "Súgó megjelenítése"
}
"help": "Súgó megjelenítése",
"audio": "Hangsáv váltása",
"subtitle": "Felirat váltása",
"fineSeek": "Finom léptetés"
},
"settings": "Beállítások",
"sync": "Szinkron-eltolás",
"subOffset": "Felirat-eltolás",
"audioOffset": "Hang-eltolás",
"playback": "Lejátszás",
"seekStep": "Léptetés",
"fineSeek": "Finom léptetés (Ctrl)",
"autoHide": "Vezérlők elrejtése",
"subStyle": "Felirat stílusa",
"subSize": "Szövegméret",
"subPos": "Pozíció",
"subColor": "Szövegszín",
"subBg": "Háttér",
"subBgNone": "Nincs"
},
"info": {
"title": "Média infó",