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.
205 lines
6.1 KiB
JSON
205 lines
6.1 KiB
JSON
{
|
||
"navLabel": "Plex",
|
||
"backToFeed": "YouTube feed",
|
||
"backToLibrary": "Back to library",
|
||
"searchPlaceholder": "Search Plex…",
|
||
"sort": {
|
||
"added": "Recently added",
|
||
"title": "Title",
|
||
"year": "Year",
|
||
"rating": "IMDb rating",
|
||
"duration": "Length",
|
||
"release": "Release date"
|
||
},
|
||
"filter": {
|
||
"library": "Library",
|
||
"show": "Show",
|
||
"showOpt": {
|
||
"all": "All",
|
||
"unwatched": "Unwatched",
|
||
"in_progress": "In progress",
|
||
"watched": "Watched"
|
||
},
|
||
"sort": "Sort",
|
||
"clearAll": "Clear filters",
|
||
"active": "Active",
|
||
"any": "Any",
|
||
"rating": "IMDb rating",
|
||
"genre": "Genre",
|
||
"year": "Year",
|
||
"duration": "Length",
|
||
"durationOpt": {
|
||
"short": "Under 90m",
|
||
"mid": "90–120m",
|
||
"long": "Over 2h"
|
||
},
|
||
"added": "Added to Plex",
|
||
"addedOpt": {
|
||
"24h": "24h",
|
||
"1w": "1 week",
|
||
"1m": "1 month",
|
||
"6m": "6 months",
|
||
"1y": "1 year"
|
||
},
|
||
"contentRating": "Age rating",
|
||
"match": {
|
||
"any": "Any",
|
||
"all": "All"
|
||
},
|
||
"dir": {
|
||
"desc": "↓ Descending",
|
||
"asc": "↑ Ascending"
|
||
},
|
||
"collection": "Collection",
|
||
"collectionSearch": "Search collections…",
|
||
"collectionEmpty": "No collections"
|
||
},
|
||
"count": "{{count}} titles",
|
||
"searchCount": "{{count}} matches",
|
||
"noMatches": "No matches.",
|
||
"loading": "Loading…",
|
||
"empty": "Nothing here yet. Run a Plex sync from the admin Config page.",
|
||
"loadMore": "Load more",
|
||
"watched": "Watched",
|
||
"play": "Play",
|
||
"resume": "Resume",
|
||
"openShow": "Open show",
|
||
"markWatched": "Mark watched",
|
||
"markUnwatched": "Mark unwatched",
|
||
"seasons": "{{count}} seasons",
|
||
"playerSoon": "Player coming soon — “{{title}}”",
|
||
"people": {
|
||
"match": "People",
|
||
"actor": "Actor",
|
||
"director": "Director",
|
||
"count": "{{count}} titles",
|
||
"added": "added"
|
||
},
|
||
"collEditor": {
|
||
"manage": "Collections",
|
||
"title": "Collections — {{title}}",
|
||
"newPlaceholder": "New collection name…",
|
||
"create": "Create",
|
||
"search": "Filter collections…",
|
||
"none": "No editable collections yet. Create one above.",
|
||
"count": "{{count}} items",
|
||
"delete": "Delete",
|
||
"deleteConfirm": "Delete the collection \"{{title}}\" from Plex? This affects all users.",
|
||
"note": "Changes are written to Plex and shared across all clients and users.",
|
||
"others": "Manage {{count}} existing collections…",
|
||
"makeEditable": "Manage"
|
||
},
|
||
"player": {
|
||
"loading": "Loading…",
|
||
"back": "Back",
|
||
"skipIntro": "Skip intro",
|
||
"skipCredits": "Skip credits",
|
||
"playPause": "Play / pause (space)",
|
||
"restart": "Restart from beginning",
|
||
"prev": "Previous episode",
|
||
"next": "Next episode",
|
||
"mute": "Mute (m)",
|
||
"fullscreen": "Fullscreen (f)",
|
||
"download": "Download original file",
|
||
"tracks": "Audio & subtitles",
|
||
"audio": "Audio",
|
||
"subtitles": "Subtitles",
|
||
"subOff": "Off",
|
||
"errNotFound": "This file can't be played — the media isn't reachable on the server (the Plex media mount is missing or the path map is wrong). Ask the admin to check the Plex configuration.",
|
||
"errTranscode": "This file's format needs transcoding, which isn't supported yet.",
|
||
"errGeneric": "Playback couldn't start. Please try again.",
|
||
"stop": "Stop",
|
||
"help": "Keyboard shortcuts",
|
||
"infoBtn": "Media info",
|
||
"keys": {
|
||
"playPause": "Play / pause",
|
||
"seek": "Seek 10 seconds",
|
||
"episode": "Previous / next episode",
|
||
"volume": "Volume up / down",
|
||
"mute": "Mute",
|
||
"fullscreen": "Fullscreen",
|
||
"info": "Media info",
|
||
"back": "Stop & back to feed",
|
||
"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",
|
||
"customize": "Customize view",
|
||
"prefArtBg": "Faint backdrop",
|
||
"prefCast": "Cast & crew",
|
||
"stripSource": {
|
||
"collection": "Collections",
|
||
"imdb": "IMDb",
|
||
"tmdb": "TMDb",
|
||
"tvdb": "TVDb",
|
||
"trakt": "Trakt",
|
||
"smart": "Smart lists"
|
||
},
|
||
"openImdb": "Open on IMDb",
|
||
"director": "Director",
|
||
"cast": "Cast & crew",
|
||
"markWatched": "Mark watched",
|
||
"markUnwatched": "Mark unwatched",
|
||
"clearResume": "Clear resume position",
|
||
"filterYear": "Show titles from {{year}}",
|
||
"filterRating": "Show titles rated {{n}}+",
|
||
"filterActor": "Show titles with {{name}}",
|
||
"browseCollection": "Browse collection →"
|
||
},
|
||
"playable": {
|
||
"direct": "Plays directly in the browser",
|
||
"remux": "Needs a light remux (no video re-encode)",
|
||
"transcode": "Needs transcoding (heavier)"
|
||
},
|
||
"playlistAdd": {
|
||
"manage": "Add to playlist",
|
||
"title": "Add to playlist — {{title}}",
|
||
"newPlaceholder": "New playlist name…",
|
||
"create": "Create",
|
||
"none": "No playlists yet. Create one above.",
|
||
"count": "{{count}} items",
|
||
"groupProgress": "{{in}} / {{size}}"
|
||
},
|
||
"playlist": {
|
||
"section": "Playlists",
|
||
"newPlaceholder": "New playlist…",
|
||
"create": "Create playlist",
|
||
"none": "No playlists yet.",
|
||
"deleteConfirm": "Delete the playlist \"{{title}}\"?",
|
||
"rename": "Rename",
|
||
"playAll": "Play all",
|
||
"delete": "Delete playlist",
|
||
"empty": "This playlist is empty. Add titles from their info page.",
|
||
"up": "Move up",
|
||
"down": "Move down",
|
||
"remove": "Remove",
|
||
"layoutAccordion": "Accordion view",
|
||
"layoutTree": "Tree view",
|
||
"removeShow": "Remove whole show",
|
||
"removeSeason": "Remove whole season",
|
||
"episodes": "{{count}} episodes",
|
||
"season": "Season {{n}}",
|
||
"seasonUnknown": "Episodes",
|
||
"addShow": "Add whole show to a playlist",
|
||
"addSeason": "Add season to a playlist",
|
||
"addEpisode": "Add episode to a playlist"
|
||
}
|
||
}
|