2026-07-05 02:32:00 +02:00
{
2026-07-05 03:29:20 +02:00
"navLabel" : "Plex" ,
2026-07-05 02:32:00 +02:00
"backToFeed" : "YouTube-Feed" ,
"backToLibrary" : "Zurück zur Bibliothek" ,
"searchPlaceholder" : "Plex durchsuchen…" ,
"sort" : {
"added" : "Kürzlich hinzugefügt" ,
feat(plex): expanded metadata filters, clickable info page, image disk cache
Backend (migration 0045_plex_filter_meta): plex_items gains rating (audienceRating
~IMDb), content_rating, studio, originally_available_at, and GIN-indexed genres /
directors / cast_names — all mirrored from the cheap section listing (no per-item API
calls; they also seed a future watch-habit recommender). /browse gains genre / content-
rating / year / rating / duration / added-within / director / actor / studio filters
(@> containment, GIN) + sort by year|rating|duration|release; new /facets endpoint
returns available genres+ratings (with counts) and the year/rating/duration bounds. A
thin on-disk image cache (.plex-img-cache) serves posters/art/cast photos from local
disk after first fetch (~7-14x faster repeat loads).
Frontend: PlexSidebar grows the full filter set (facet-driven genre/age chips, rating
steps, year range inputs, duration buckets, added-within, active people/studio chips,
clear-all); filters persist per-account as one JSON blob. PlexInfo metadata (year,
genre, director, cast, studio, IMDb score) is clickable → sets the matching filter and
returns to the filtered grid (page variant only; the in-player overlay stays read-only
so a stray click can't stop playback). i18n en/hu/de.
2026-07-05 23:45:55 +02:00
"title" : "Titel" ,
"year" : "Jahr" ,
"rating" : "IMDb-Wertung" ,
"duration" : "Länge" ,
"release" : "Erscheinungsdatum"
2026-07-05 02:32:00 +02:00
} ,
2026-07-05 03:29:20 +02:00
"filter" : {
"library" : "Bibliothek" ,
feat(plex): unify movies + shows into one cross-library browser
Collapse the separate Movie/Show library sections into ONE unified library with a
shared search + shared filter sidebar and a Movies/Shows/Both scope selector.
Backend: new GET /api/plex/library — a cross-library UNION of movies (plex_items) and
shows (plex_shows) as one mixed, paginated, sorted feed, scoped movie|show|both, with
the shared filters (extracted into _apply_meta_filters, DRY), FTS search, and per-user
watch-state (a show's state = the aggregate of its episodes). On a search that also
matches episodes, matching episodes come back in a separate 'episodes' list (the grouped
'Episodes' section — Proposal 3). /facets is now scope-aware (merged across the scope's
libraries). /item and /show now return their library section key (for the admin
collection editor, since there's no single library prop in the unified view).
Frontend: PlexSidebar's library picker -> a scope selector (Both/Movies/Shows); facets +
browse follow the scope (App's plexLib repurposed to a validated scope, default both).
PlexBrowse uses the unified endpoint, renders a mixed Titles grid + an Episodes section
on search. Poster cards are generalized: a hover Play/Resume overlay on every card, a
clickable title (not just the poster), and a movie/show type tag. The quick watched
toggle is now optimistic so it reliably flips BOTH ways (fixes the movie card that could
mark but not un-mark). Cast/crew members and the show hero's meta (year/rating/genre/
content-rating) are clickable filters; clicking a person widens the scope to Both so the
result is a mixed movie+show feed. i18n plex.filter.scope*/plex.unified.* (en/hu/de).
Still pending from the polish list (next pass): season-card quick toggles (watched +
add-to-playlist), per-episode watched toggle, and the full glassy art-bg + hide-cast
customize menu on the series pages.
2026-07-11 00:15:49 +02:00
"scope" : "Bibliothek" ,
"scopeOpt" : {
"both" : "Alle" ,
"movie" : "Filme" ,
"show" : "Serien"
} ,
2026-07-05 03:29:20 +02:00
"show" : "Anzeige" ,
"showOpt" : {
"all" : "Alle" ,
"unwatched" : "Ungesehen" ,
"in_progress" : "Läuft" ,
"watched" : "Gesehen"
} ,
feat(plex): expanded metadata filters, clickable info page, image disk cache
Backend (migration 0045_plex_filter_meta): plex_items gains rating (audienceRating
~IMDb), content_rating, studio, originally_available_at, and GIN-indexed genres /
directors / cast_names — all mirrored from the cheap section listing (no per-item API
calls; they also seed a future watch-habit recommender). /browse gains genre / content-
rating / year / rating / duration / added-within / director / actor / studio filters
(@> containment, GIN) + sort by year|rating|duration|release; new /facets endpoint
returns available genres+ratings (with counts) and the year/rating/duration bounds. A
thin on-disk image cache (.plex-img-cache) serves posters/art/cast photos from local
disk after first fetch (~7-14x faster repeat loads).
Frontend: PlexSidebar grows the full filter set (facet-driven genre/age chips, rating
steps, year range inputs, duration buckets, added-within, active people/studio chips,
clear-all); filters persist per-account as one JSON blob. PlexInfo metadata (year,
genre, director, cast, studio, IMDb score) is clickable → sets the matching filter and
returns to the filtered grid (page variant only; the in-player overlay stays read-only
so a stray click can't stop playback). i18n en/hu/de.
2026-07-05 23:45:55 +02:00
"sort" : "Sortierung" ,
"clearAll" : "Filter zurücksetzen" ,
"active" : "Aktiv" ,
"any" : "Alle" ,
"rating" : "IMDb-Wertung" ,
"genre" : "Genre" ,
"year" : "Jahr" ,
"duration" : "Länge" ,
2026-07-06 17:33:16 +02:00
"durationOpt" : {
"short" : "Unter 90 Min." ,
"mid" : "90– 120 Min." ,
"long" : "Über 2 Std."
} ,
feat(plex): expanded metadata filters, clickable info page, image disk cache
Backend (migration 0045_plex_filter_meta): plex_items gains rating (audienceRating
~IMDb), content_rating, studio, originally_available_at, and GIN-indexed genres /
directors / cast_names — all mirrored from the cheap section listing (no per-item API
calls; they also seed a future watch-habit recommender). /browse gains genre / content-
rating / year / rating / duration / added-within / director / actor / studio filters
(@> containment, GIN) + sort by year|rating|duration|release; new /facets endpoint
returns available genres+ratings (with counts) and the year/rating/duration bounds. A
thin on-disk image cache (.plex-img-cache) serves posters/art/cast photos from local
disk after first fetch (~7-14x faster repeat loads).
Frontend: PlexSidebar grows the full filter set (facet-driven genre/age chips, rating
steps, year range inputs, duration buckets, added-within, active people/studio chips,
clear-all); filters persist per-account as one JSON blob. PlexInfo metadata (year,
genre, director, cast, studio, IMDb score) is clickable → sets the matching filter and
returns to the filtered grid (page variant only; the in-player overlay stays read-only
so a stray click can't stop playback). i18n en/hu/de.
2026-07-05 23:45:55 +02:00
"added" : "Zu Plex hinzugefügt" ,
2026-07-06 17:33:16 +02:00
"addedOpt" : {
"24h" : "24 Std." ,
"1w" : "1 Woche" ,
"1m" : "1 Monat" ,
"6m" : "6 Monate" ,
"1y" : "1 Jahr"
} ,
2026-07-06 00:15:31 +02:00
"contentRating" : "Altersfreigabe" ,
2026-07-06 17:33:16 +02:00
"match" : {
"any" : "Beliebig" ,
"all" : "Alle"
} ,
"dir" : {
"desc" : "↓ Absteigend" ,
"asc" : "↑ Aufsteigend"
} ,
2026-07-06 02:25:48 +02:00
"collection" : "Sammlung" ,
"collectionSearch" : "Sammlungen suchen…" ,
"collectionEmpty" : "Keine Sammlungen"
2026-07-05 03:29:20 +02:00
} ,
2026-07-05 02:32:00 +02:00
"count" : "{{count}} Titel" ,
"searchCount" : "{{count}} Treffer" ,
2026-07-05 03:29:20 +02:00
"noMatches" : "Keine Treffer." ,
2026-07-08 23:19:23 +02:00
"noMatchesFiltered_one" : "Keine Treffer — ein Filter schränkt die Ergebnisse zusätzlich ein." ,
"noMatchesFiltered_other" : "Keine Treffer — {{count}} Filter schränken die Ergebnisse zusätzlich ein." ,
2026-07-05 02:32:00 +02:00
"loading" : "Wird geladen…" ,
"empty" : "Noch nichts hier. Starte eine Plex-Synchronisierung auf der Admin-Konfigurationsseite." ,
"loadMore" : "Mehr laden" ,
"watched" : "Angesehen" ,
2026-07-10 21:43:09 +02:00
"inProgress" : "Läuft" ,
2026-07-05 05:26:16 +02:00
"play" : "Abspielen" ,
"resume" : "Fortsetzen" ,
"openShow" : "Serie öffnen" ,
"markWatched" : "Als gesehen markieren" ,
"markUnwatched" : "Als ungesehen markieren" ,
2026-07-05 02:32:00 +02:00
"seasons" : "{{count}} Staffeln" ,
feat(plex): unify movies + shows into one cross-library browser
Collapse the separate Movie/Show library sections into ONE unified library with a
shared search + shared filter sidebar and a Movies/Shows/Both scope selector.
Backend: new GET /api/plex/library — a cross-library UNION of movies (plex_items) and
shows (plex_shows) as one mixed, paginated, sorted feed, scoped movie|show|both, with
the shared filters (extracted into _apply_meta_filters, DRY), FTS search, and per-user
watch-state (a show's state = the aggregate of its episodes). On a search that also
matches episodes, matching episodes come back in a separate 'episodes' list (the grouped
'Episodes' section — Proposal 3). /facets is now scope-aware (merged across the scope's
libraries). /item and /show now return their library section key (for the admin
collection editor, since there's no single library prop in the unified view).
Frontend: PlexSidebar's library picker -> a scope selector (Both/Movies/Shows); facets +
browse follow the scope (App's plexLib repurposed to a validated scope, default both).
PlexBrowse uses the unified endpoint, renders a mixed Titles grid + an Episodes section
on search. Poster cards are generalized: a hover Play/Resume overlay on every card, a
clickable title (not just the poster), and a movie/show type tag. The quick watched
toggle is now optimistic so it reliably flips BOTH ways (fixes the movie card that could
mark but not un-mark). Cast/crew members and the show hero's meta (year/rating/genre/
content-rating) are clickable filters; clicking a person widens the scope to Both so the
result is a mixed movie+show feed. i18n plex.filter.scope*/plex.unified.* (en/hu/de).
Still pending from the polish list (next pass): season-card quick toggles (watched +
add-to-playlist), per-episode watched toggle, and the full glassy art-bg + hide-cast
customize menu on the series pages.
2026-07-11 00:15:49 +02:00
"unified" : {
"titles" : "Titel" ,
"episodes" : "Folgen"
} ,
feat(plex): Plex-web-style 3-level series view (Phase 2 of series view)
Restructure TV browsing into show detail → seasons → season episodes → player, like
the Plex web app.
Backend: /show/{rk} now returns a rich show page — hero meta (rating/content_rating/
genres/studio + live IMDb), live Cast & Crew, Related shows (Plex 'related', mapped to
our mirrored shows so they're openable), and per-season cards with an aggregate
watch-state + on-deck episode, plus show-level resume (on-deck)/first(play-from-start)/
status rollup. New PlexClient.related(). New bulk-state endpoints POST /show/{rk}/state
and /season/{rk}/state mark every episode watched/unwatched for the user and mirror
each change to a linked Plex account in the background (best-effort, checked once).
Frontend: PlexShowView reworked into the show detail page (hero + Resume/Play-from-start/
Mark-show-watched/Add-to-playlist/[admin]Add-to-collection + season card grid + cast +
related strips); new PlexSeasonView season subpage (hero + Resume/Play/Mark-season/
Add-season-to-playlist + landscape episode grid). Both read the one cached ['plex-show']
payload (season page picks its season out of it — instant, no extra fetch). Player queue =
the whole show (from the show page) or the season (from the season page) so prev/next +
auto-advance follow order. New 'season' history subview; Backspace steps back one drill
level (grid←show←season, out of info/playlist) alongside browser/mouse Back. Season-level
'Add to collection' intentionally omitted (Plex collections hold whole shows, not seasons).
i18n plex.series.* (en/hu/de).
2026-07-10 22:08:04 +02:00
"series" : {
"seasons" : "Staffeln" ,
"backToShow" : "Zurück zur Serie" ,
"episodeCount" : "{{count}} Folgen" ,
"playFromStart" : "Von Anfang an" ,
"related" : "Ähnliche Serien" ,
"markShowWatched" : "Serie als gesehen" ,
"markShowUnwatched" : "Serie als ungesehen" ,
"markSeasonWatched" : "Staffel als gesehen" ,
"markSeasonUnwatched" : "Staffel als ungesehen" ,
"addShowCollection" : "Zur Sammlung"
} ,
2026-07-05 02:32:00 +02:00
"playerSoon" : "Player kommt bald — „{{title}}“" ,
2026-07-06 00:56:01 +02:00
"people" : {
"match" : "Personen" ,
"actor" : "Schauspieler" ,
"director" : "Regie" ,
"count" : "{{count}} Titel" ,
"added" : "hinzugefügt"
} ,
2026-07-06 17:33:16 +02:00
"collEditor" : {
"manage" : "Sammlungen" ,
"title" : "Sammlungen — {{title}}" ,
"newPlaceholder" : "Name der neuen Sammlung…" ,
"create" : "Erstellen" ,
"search" : "Sammlungen filtern…" ,
"none" : "Noch keine bearbeitbaren Sammlungen. Erstelle oben eine." ,
"count" : "{{count}} Titel" ,
"delete" : "Löschen" ,
"deleteConfirm" : "Sammlung „{{title}}“ aus Plex löschen? Betrifft alle Benutzer." ,
"note" : "Änderungen werden in Plex geschrieben und für alle Clients/Benutzer sichtbar." ,
"others" : "{{count}} vorhandene Sammlungen verwalten…" ,
"makeEditable" : "Verwalten"
} ,
feat(plex): P2 player — rich full-page HLS/direct player + watch-state
- backend: GET /api/plex/item/{rk} detail (metadata, cast + intro/credit markers
from Plex, episode prev/next, per-user resume+status); POST /item/{rk}/progress
(resume checkpoint, near-end→watched) + /state (new|watched|hidden).
- frontend PlexPlayer.tsx (lazy, pulls hls.js): full-page player over the Plex
module. Direct files stream raw <video>; remux via hls.js on the seek-restart
session — custom controls map the ABSOLUTE timeline over the session offset, and a
seek beyond the loaded region restarts the session at the target. Play/pause/resume
/restart, ±10s seek, prev/next episode (Shift+←/→), volume, windowed/fullscreen,
full keyboard, Skip intro/Skip credits from markers (shaded on the seekbar),
auto-advance + watched-on-end, resume from saved position, 10s progress checkpoints.
- PlexBrowse: card/episode click opens the player as a history subview (browser Back
returns to the grid/show). api client (plexItem/plexSession/plexProgress/plexSetState)
+ types; plex.player.* i18n en/hu/de. hls.js@^1.6.16.
- Verified over HTTP: detail w/ markers+cast+episode-nav, progress persist. Video
playback itself is pending browser UAT. Subtitle/audio track SELECTION deferred.
2026-07-05 04:28:00 +02:00
"player" : {
"loading" : "Wird geladen…" ,
"back" : "Zurück" ,
"skipIntro" : "Intro überspringen" ,
"skipCredits" : "Abspann überspringen" ,
"playPause" : "Wiedergabe / Pause (Leertaste)" ,
"restart" : "Von vorn beginnen" ,
"prev" : "Vorherige Folge" ,
"next" : "Nächste Folge" ,
"mute" : "Stumm (m)" ,
2026-07-05 05:26:16 +02:00
"fullscreen" : "Vollbild (f)" ,
2026-07-05 06:08:44 +02:00
"download" : "Originaldatei herunterladen" ,
"tracks" : "Audio & Untertitel" ,
"audio" : "Audio" ,
"subtitles" : "Untertitel" ,
2026-07-05 06:58:34 +02:00
"subOff" : "Aus" ,
"errNotFound" : "Diese Datei kann nicht abgespielt werden — die Medien sind auf dem Server nicht erreichbar (die Plex-Medieneinbindung fehlt oder die Pfadzuordnung ist falsch). Bitte den Administrator, die Plex-Konfiguration zu prüfen." ,
"errTranscode" : "Das Format dieser Datei erfordert eine Transkodierung, die noch nicht unterstützt wird." ,
2026-07-05 21:27:02 +02:00
"errGeneric" : "Die Wiedergabe konnte nicht gestartet werden. Bitte erneut versuchen." ,
"stop" : "Stopp" ,
"help" : "Tastenkürzel" ,
feat(plex): rich media info — info page, in-player info overlay, IMDb + cast photos
Backend (no migration): item_detail now also returns IMDb score + id/url (from the
Plex Rating/Guid arrays), content rating, genres, director(s), studio, tagline, and
cast as {name, role, photo}. New host-whitelisted /person-image proxy serves cast
photos from Plex's public metadata CDN (keeps third-party requests off the browser).
Frontend: reusable PlexInfo component in two forms — a full info page opened from a
card's 'i' button (history subview, art backdrop, Play/Resume + watch controls) and a
lean overlay in the player toggled with 'I' (video keeps playing). Two per-user view
prefs (faint backdrop, cast row) persisted in preferences. Mark-unwatched / clear-resume
cover the watched-reset gap. i18n en/hu/de.
2026-07-05 22:57:18 +02:00
"infoBtn" : "Medieninfo" ,
2026-07-05 21:27:02 +02:00
"keys" : {
"playPause" : "Wiedergabe / Pause" ,
"seek" : "10 Sekunden spulen" ,
"episode" : "Vorherige / nächste Folge" ,
"volume" : "Lautstärke lauter / leiser" ,
"mute" : "Stumm" ,
"fullscreen" : "Vollbild" ,
feat(plex): rich media info — info page, in-player info overlay, IMDb + cast photos
Backend (no migration): item_detail now also returns IMDb score + id/url (from the
Plex Rating/Guid arrays), content rating, genres, director(s), studio, tagline, and
cast as {name, role, photo}. New host-whitelisted /person-image proxy serves cast
photos from Plex's public metadata CDN (keeps third-party requests off the browser).
Frontend: reusable PlexInfo component in two forms — a full info page opened from a
card's 'i' button (history subview, art backdrop, Play/Resume + watch controls) and a
lean overlay in the player toggled with 'I' (video keeps playing). Two per-user view
prefs (faint backdrop, cast row) persisted in preferences. Mark-unwatched / clear-resume
cover the watched-reset gap. i18n en/hu/de.
2026-07-05 22:57:18 +02:00
"info" : "Medieninfo" ,
2026-07-05 21:27:02 +02:00
"back" : "Stopp & zurück zum Feed" ,
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.
2026-07-08 21:35:38 +02:00
"help" : "Diese Hilfe anzeigen" ,
"audio" : "Audiospur wechseln" ,
"subtitle" : "Untertitel wechseln" ,
"fineSeek" : "Feines Springen"
} ,
"settings" : "Einstellungen" ,
2026-07-09 01:32:04 +02:00
"sync" : "Offsets" ,
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.
2026-07-08 21:35:38 +02:00
"subOffset" : "Untertitel-Verzögerung" ,
"audioOffset" : "Audio-Verzögerung" ,
"playback" : "Wiedergabe" ,
"seekStep" : "Sprungweite" ,
"fineSeek" : "Feines Springen (Strg)" ,
"autoHide" : "Steuerung ausblenden" ,
2026-07-09 01:32:04 +02:00
"subStyle" : "Untertitel" ,
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.
2026-07-08 21:35:38 +02:00
"subSize" : "Schriftgröße" ,
"subPos" : "Position" ,
"subColor" : "Textfarbe" ,
"subBg" : "Hintergrund" ,
2026-07-08 22:38:47 +02:00
"subBgNone" : "Keiner" ,
"clock" : "Uhr" ,
"clockShow" : "Uhr anzeigen" ,
"clock24h" : "24-Stunden" ,
"clockSize" : "Größe" ,
"clockColor" : "Farbe" ,
"clockDate" : "Datum anzeigen" ,
"subShadow" : "Schatten" ,
"subShadowColor" : "Schattenfarbe" ,
"autoSkipIntro" : "Intro autom. überspringen" ,
"autoSkipCredits" : "Abspann autom. überspringen" ,
"autoSkipDelay" : "Auto-Skip-Verzögerung"
feat(plex): P2 player — rich full-page HLS/direct player + watch-state
- backend: GET /api/plex/item/{rk} detail (metadata, cast + intro/credit markers
from Plex, episode prev/next, per-user resume+status); POST /item/{rk}/progress
(resume checkpoint, near-end→watched) + /state (new|watched|hidden).
- frontend PlexPlayer.tsx (lazy, pulls hls.js): full-page player over the Plex
module. Direct files stream raw <video>; remux via hls.js on the seek-restart
session — custom controls map the ABSOLUTE timeline over the session offset, and a
seek beyond the loaded region restarts the session at the target. Play/pause/resume
/restart, ±10s seek, prev/next episode (Shift+←/→), volume, windowed/fullscreen,
full keyboard, Skip intro/Skip credits from markers (shaded on the seekbar),
auto-advance + watched-on-end, resume from saved position, 10s progress checkpoints.
- PlexBrowse: card/episode click opens the player as a history subview (browser Back
returns to the grid/show). api client (plexItem/plexSession/plexProgress/plexSetState)
+ types; plex.player.* i18n en/hu/de. hls.js@^1.6.16.
- Verified over HTTP: detail w/ markers+cast+episode-nav, progress persist. Video
playback itself is pending browser UAT. Subtitle/audio track SELECTION deferred.
2026-07-05 04:28:00 +02:00
} ,
feat(plex): rich media info — info page, in-player info overlay, IMDb + cast photos
Backend (no migration): item_detail now also returns IMDb score + id/url (from the
Plex Rating/Guid arrays), content rating, genres, director(s), studio, tagline, and
cast as {name, role, photo}. New host-whitelisted /person-image proxy serves cast
photos from Plex's public metadata CDN (keeps third-party requests off the browser).
Frontend: reusable PlexInfo component in two forms — a full info page opened from a
card's 'i' button (history subview, art backdrop, Play/Resume + watch controls) and a
lean overlay in the player toggled with 'I' (video keeps playing). Two per-user view
prefs (faint backdrop, cast row) persisted in preferences. Mark-unwatched / clear-resume
cover the watched-reset gap. i18n en/hu/de.
2026-07-05 22:57:18 +02:00
"info" : {
"title" : "Medieninfo" ,
"customize" : "Ansicht anpassen" ,
"prefArtBg" : "Dezenter Hintergrund" ,
"prefCast" : "Besetzung" ,
2026-07-06 06:49:43 +02:00
"stripSource" : {
"collection" : "Sammlungen" ,
"imdb" : "IMDb" ,
"tmdb" : "TMDb" ,
"tvdb" : "TVDb" ,
"trakt" : "Trakt" ,
"smart" : "Smart-Listen"
} ,
feat(plex): rich media info — info page, in-player info overlay, IMDb + cast photos
Backend (no migration): item_detail now also returns IMDb score + id/url (from the
Plex Rating/Guid arrays), content rating, genres, director(s), studio, tagline, and
cast as {name, role, photo}. New host-whitelisted /person-image proxy serves cast
photos from Plex's public metadata CDN (keeps third-party requests off the browser).
Frontend: reusable PlexInfo component in two forms — a full info page opened from a
card's 'i' button (history subview, art backdrop, Play/Resume + watch controls) and a
lean overlay in the player toggled with 'I' (video keeps playing). Two per-user view
prefs (faint backdrop, cast row) persisted in preferences. Mark-unwatched / clear-resume
cover the watched-reset gap. i18n en/hu/de.
2026-07-05 22:57:18 +02:00
"openImdb" : "Bei IMDb öffnen" ,
"director" : "Regie" ,
"cast" : "Besetzung & Crew" ,
"markWatched" : "Als gesehen markieren" ,
"markUnwatched" : "Gesehen zurücknehmen" ,
feat(plex): expanded metadata filters, clickable info page, image disk cache
Backend (migration 0045_plex_filter_meta): plex_items gains rating (audienceRating
~IMDb), content_rating, studio, originally_available_at, and GIN-indexed genres /
directors / cast_names — all mirrored from the cheap section listing (no per-item API
calls; they also seed a future watch-habit recommender). /browse gains genre / content-
rating / year / rating / duration / added-within / director / actor / studio filters
(@> containment, GIN) + sort by year|rating|duration|release; new /facets endpoint
returns available genres+ratings (with counts) and the year/rating/duration bounds. A
thin on-disk image cache (.plex-img-cache) serves posters/art/cast photos from local
disk after first fetch (~7-14x faster repeat loads).
Frontend: PlexSidebar grows the full filter set (facet-driven genre/age chips, rating
steps, year range inputs, duration buckets, added-within, active people/studio chips,
clear-all); filters persist per-account as one JSON blob. PlexInfo metadata (year,
genre, director, cast, studio, IMDb score) is clickable → sets the matching filter and
returns to the filtered grid (page variant only; the in-player overlay stays read-only
so a stray click can't stop playback). i18n en/hu/de.
2026-07-05 23:45:55 +02:00
"clearResume" : "Fortsetzungsposition löschen" ,
"filterYear" : "Titel aus {{year}}" ,
"filterRating" : "Titel mit {{n}}+" ,
2026-07-06 02:25:48 +02:00
"filterActor" : "Titel mit {{name}}" ,
"browseCollection" : "Sammlung durchsuchen →"
feat(plex): rich media info — info page, in-player info overlay, IMDb + cast photos
Backend (no migration): item_detail now also returns IMDb score + id/url (from the
Plex Rating/Guid arrays), content rating, genres, director(s), studio, tagline, and
cast as {name, role, photo}. New host-whitelisted /person-image proxy serves cast
photos from Plex's public metadata CDN (keeps third-party requests off the browser).
Frontend: reusable PlexInfo component in two forms — a full info page opened from a
card's 'i' button (history subview, art backdrop, Play/Resume + watch controls) and a
lean overlay in the player toggled with 'I' (video keeps playing). Two per-user view
prefs (faint backdrop, cast row) persisted in preferences. Mark-unwatched / clear-resume
cover the watched-reset gap. i18n en/hu/de.
2026-07-05 22:57:18 +02:00
} ,
2026-07-05 02:32:00 +02:00
"playable" : {
"direct" : "Spielt direkt im Browser" ,
"remux" : "Braucht ein leichtes Remux (keine Video-Neucodierung)" ,
"transcode" : "Braucht Transcoding (aufwendiger)"
feat(plex): Playlists Phase 1 — per-user ordered watch-lists (Siftlode-native)
Personal ordered lists of Plex items, kept in Siftlode's own DB (works for users
without a Plex account, like watch-state) — the "your own lists" counterpart to
shared collections. Plex-direction sync is a later phase (plex_rating_key
reserved).
Backend: migration 0048 (plex_playlists + plex_playlist_items with position),
PlexPlaylist/PlexPlaylistItem models, and per-user CRUD endpoints under
/api/plex/playlists (list [+?contains for the add dialog], create [seeded],
detail [ordered cards], rename, delete, add/remove item, reorder). _leaf_card
handles the movie/episode mix. Frontend: "Add to playlist" dialog from the movie
info page (all users), a Playlists section in PlexSidebar (list + create),
PlexPlaylistView (reorder up/down, remove, rename, delete, Play all), and
PlexPlayer gained an optional `queue` so play-through follows the list order
(prev/next + auto-advance). i18n en/hu/de. Verified end-to-end on localdev
(backend CRUD + the create→add→view→play-through UI flow).
2026-07-06 19:05:12 +02:00
} ,
"playlistAdd" : {
"manage" : "Zur Playlist" ,
"title" : "Zur Playlist — {{title}}" ,
"newPlaceholder" : "Name der neuen Playlist…" ,
"create" : "Erstellen" ,
"none" : "Noch keine Playlists. Erstelle oben eine." ,
2026-07-06 22:14:56 +02:00
"count" : "{{count}} Titel" ,
"groupProgress" : "{{in}} / {{size}}"
feat(plex): Playlists Phase 1 — per-user ordered watch-lists (Siftlode-native)
Personal ordered lists of Plex items, kept in Siftlode's own DB (works for users
without a Plex account, like watch-state) — the "your own lists" counterpart to
shared collections. Plex-direction sync is a later phase (plex_rating_key
reserved).
Backend: migration 0048 (plex_playlists + plex_playlist_items with position),
PlexPlaylist/PlexPlaylistItem models, and per-user CRUD endpoints under
/api/plex/playlists (list [+?contains for the add dialog], create [seeded],
detail [ordered cards], rename, delete, add/remove item, reorder). _leaf_card
handles the movie/episode mix. Frontend: "Add to playlist" dialog from the movie
info page (all users), a Playlists section in PlexSidebar (list + create),
PlexPlaylistView (reorder up/down, remove, rename, delete, Play all), and
PlexPlayer gained an optional `queue` so play-through follows the list order
(prev/next + auto-advance). i18n en/hu/de. Verified end-to-end on localdev
(backend CRUD + the create→add→view→play-through UI flow).
2026-07-06 19:05:12 +02:00
} ,
"playlist" : {
"section" : "Playlists" ,
"newPlaceholder" : "Neue Playlist…" ,
"create" : "Playlist erstellen" ,
"none" : "Noch keine Playlists." ,
"deleteConfirm" : "Playlist „{{title}}“ löschen?" ,
"rename" : "Umbenennen" ,
"playAll" : "Alle abspielen" ,
"delete" : "Playlist löschen" ,
"empty" : "Diese Playlist ist leer. Füge Titel über deren Infoseite hinzu." ,
"up" : "Nach oben" ,
"down" : "Nach unten" ,
2026-07-06 22:14:56 +02:00
"remove" : "Entfernen" ,
"layoutAccordion" : "Akkordeon-Ansicht" ,
"layoutTree" : "Baum-Ansicht" ,
"removeShow" : "Ganze Serie entfernen" ,
"removeSeason" : "Ganze Staffel entfernen" ,
"episodes" : "{{count}} Folgen" ,
"season" : "Staffel {{n}}" ,
"seasonUnknown" : "Folgen" ,
"addShow" : "Ganze Serie zu einer Playlist" ,
"addSeason" : "Staffel zu einer Playlist" ,
"addEpisode" : "Folge zu einer Playlist"
2026-07-05 02:32:00 +02:00
}
}