Commit Graph
58 Commits
Author SHA1 Message Date
edde746 369c6279d6 fix(i18n): translate the player, downloads and server-setup text left in English
A Portuguese user reported "Skip Intro" rendering in English on Android TV.
The locale files were not the problem - all 22 were structurally complete.
skip_marker_button.dart simply never imported strings.g.dart and assigned
'Skip Intro' / 'Skip Credits' / 'Next Episode' as plain literals. An audit of
lib/ found ~120 more sites in the same state, in four shapes that need
different fixes:

A literal in a file that never imported the i18n layer is the easy one -
skip_marker_button, performance_stats, track_label_builder and codec_utils all
render text with no `t` in the file at all. TrackLabelBuilder._compose now takes
a fallbackLabel builder instead of an English fallbackPrefix, so the caller
supplies t.audioTracks.track / t.videoControls.subtitleTrack and every unnamed
audio and subtitle row in the track menus is localized.

English reaching the user through an exception message is the widest one, and
it needs care: MediaServerException.message feeds both toString() - logs and
Sentry grouping - and verbatim UI display. Localizing it in place would make
bug-report logs follow the user's locale and split one Sentry issue into 22.
The MediaServer and Seerr families instead gain a nullable `display` alongside
the English `message`, and the six screens that print these errors read
`display ?? message`. PlaybackException keeps the opposite rule, because it
already carries a PlaybackFailureReason for logic and classifyPlaybackFailure
already builds it from t.messages: its stragglers are localized at the throw
site. That also removes the literal "Exception: " prefix Live TV users saw on
a tune failure, since PlaybackException.toString() returns the bare message.

Localized parts hand-concatenated with bare English are the shape no search for
Text('...') can find: '${t.common.pause} auto-scroll' on the home carousel,
'${day} at ${time}' on the Live TV schedule row, and an actor-screen count that
hand-rolled its plural as `n == 1 ? 'title' : 'titles'` - wrong for ru and pl
regardless of translation, now a real Slang plural.

Finally a literal assigned to provider state that a widget renders later:
DownloadProgress.errorMessage, and the four background_downloader notification
bodies, which sit inside a plugin config call where no widget-shaped search
reaches them.

Two things surfaced while converting. track_chapter_controls compared a track
label against 'Audio Track N' to swap in a localized version; once the builder
localized its own fallback that branch became unreachable, so it and the
orphaned _joinTrackLabel are gone. And discovery_view's PeerError fallback arm
looks like a leak but is not - its producers already localize, and a test says
so - so it stays as it is.

All 21 non-base locales are translated, including the 21 keys left empty by
earlier commits that were falling back to English. No locale has an empty value.

scripts/check_hardcoded_strings.py guards the three shapes a structural check
can see, and runs in ci_checks.sh after translation hygiene. Its first draft
passed its own tests while missing this very bug, because 'Skip Intro' is bound
to a local rather than handed to Text(); the name-bound rule that closes that
gap is restricted to phrase-shaped literals, or it cannot tell copy from the
identifiers this codebase binds constantly ('cast_row', 'auto', 'liveTv'). It
cannot see English inside a throw or assigned to a provider field - neither is
distinguishable from a log message without dataflow analysis - and the docstring
says so. label: and actionLabel: are deliberately unscanned: here they name a
diagnostic operation, and a check that is chronically red is a check that gets
switched off.

One commit rather than one per area: the keys, the 22 locale files and the
generated output are a single unit, and any partial split fails the repo's own
unused-key scan on the way through.

close #1856
2026-08-10 15:32:43 +02:00
edde746 7437b43207 fix(plex): validate a failover candidate before switching the live endpoint
A transient GET failure on a healthy endpoint could park the client on an
unreachable fallback (e.g. the server host's Docker bridge gateway, which
plex.tv advertises as a local connection) for a full connect timeout, failing
every request in flight during that window (log bbr90).

The cascade now probes each candidate with an unauthenticated /identity
request under the discovery-race budget and only switches when it answers as
the expected server, mirroring the Jellyfin trust gate. Unreachable-looking
private IPv4 candidates stay in the list — a client on the server host can
legitimately reach them, so reachability is probed, not inferred.
2026-08-08 12:18:32 +02:00
edde746 13179f08cd refactor: move the Plex switch-token parser in with the Plex models
user_switch_response.dart was left holding a single 13-line function after
UserSwitchResponse's decorative fields were dropped, so the filename no
longer described its contents and it sat at lib/models/ root while every
other Plex model lives in lib/models/plex/.

Renamed to lib/models/plex/plex_switch_response.dart, with the test moved
alongside the other plex_*_test.dart files. The parser stays public so the
#1488 drift characterization tests keep exercising it directly.
2026-07-26 06:09:50 +02:00
edde746 4eaf4423a1 refactor: share focus chrome and simplify the TV picker and browse paths
Focus chrome was implemented twice, once in the focusable wrapper and once
in the focus builders; both now go through FocusChrome. TvColorPicker's
channel row was a copy of TvNumberSpinner and is now that widget in compact
density.

Also trims unused helpers and fields and simplifies the Jellyfin browse
paths.
2026-07-26 06:09:49 +02:00
edde746 352b88109b refactor: extract shared mixins and helpers, drop dead abstractions
Introduces shared seams for paginated views, D-pad reorder, media control
routing, async singletons and the device method channel, then points the
open-coded copies at them.

Also removes unused models and duplicated provider/server plumbing, folds
the twice-implemented artifact store in the server, and factors the
repeated Flutter toolchain prologue in CI into a composite action.
2026-07-26 06:09:48 +02:00
edde746 1bdd76d95a fix(plex): wait for shared server resource tokens 2026-07-14 00:15:46 +02:00
edde746 bc55aea701 refactor: remove superseded code paths 2026-07-12 08:42:19 +02:00
edde746 9c6fcbc841 fix(plex): tolerate scalar schema drift 2026-07-12 08:42:18 +02:00
edde746 d80a1ed15a feat: report real device identity to media servers
Resolve platform, hardware model, and friendly device name once via a
shared DeviceIdentityService and send them to both backends: Plex gets
a real X-Plex-Platform plus X-Plex-Device/X-Plex-Device-Name (shown as
Player in dashboards/Tautulli), Jellyfin gets the device name in the
MediaBrowser auth header. Transcode and live-TV decision requests keep
their pinned platform names, which Plex validates server-side.

close #1270
2026-07-05 01:22:20 +02:00
edde746 3599b0b0e1 fix(startup): no-servers flash on fresh login + faster connect splash
- start binder before navigating from auth, mark isBinding synchronously
- overlap plex.tv resource refresh with optimistic cached-metadata connect,
  reconcile tokens/membership in background once it lands
- cached endpoint probe gets a head start on the race instead of serially
  blocking it
- defer phase-1 HTTPS upgrade off the splash critical path
- per-server connect progress stream for incremental splash checkmarks
- startup timing instrumentation (bind/fetch/race/connect elapsedMs)
2026-06-12 01:46:43 +02:00
edde746 74b8dc4561 refactor: type server identifiers 2026-06-01 11:06:03 +02:00
edde746 6642348a96 feat(jellyfin): support multiple server urls 2026-05-28 03:43:46 +02:00
edde746 cfd4aa307d fix(plex): tolerate transient resource failures 2026-05-16 22:54:53 +02:00
edde746 ec93062600 fix(app): harden playback and plex flows 2026-05-10 00:35:01 +02:00
edde746 fb084d3ea1 fix(plex): stabilize Linux endpoint failover 2026-05-06 00:51:00 +02:00
edde746 91c23786f2 refactor: reduce model and focus boilerplate 2026-05-06 00:26:44 +02:00
edde746 d9d66fe54e fix: add HTTP fallback for native Plex hostnames 2026-05-01 06:45:10 +02:00
edde746 31d2d9dc98 feat: jellyfin 2026-05-01 01:20:36 +02:00
edde746 feba5b6f74 lint: enforce unawaited_futures, prefer_final_locals, etc. 2026-04-25 12:27:28 +02:00
edde746 d0a93d57dd refactor: codebase review cleanup 2026-04-25 03:16:11 +02:00
edde746 c10cf55f2d feat: transcoding 2026-04-22 21:07:12 +02:00
edde746 fd701e5d1f fix: restrict failover to working endpoint network class
close #902
2026-04-21 19:20:57 +02:00
edde746 4665f2a5d8 fix: dispose services and subscriptions, persist client id 2026-04-21 12:04:48 +02:00
edde746 e1c9ce6f71 fix: gate http fallback for hostname https, throw on non-2xx in failover
close #900
2026-04-21 08:39:28 +02:00
edde746 3da51f9d64 chore: pre-commit ci hook, dart format 2026-04-18 12:40:35 +02:00
edde746 15b22f75a8 refactor: replace dio with package:http 2026-04-08 00:43:08 +02:00
edde746 7528dcec71 fix: connection selection failover and timeout budget 2026-04-07 06:49:51 +02:00
edde746 7150ea2987 fix: log reason for https upgrade failure 2026-04-07 05:48:44 +02:00
edde746 bc2febf480 fix: prefer https endpoints, catch h2 protocol errors 2026-04-07 05:48:44 +02:00
edde746 52b8944d71 feat: use HTTP/2 for all network requests 2026-04-06 22:53:13 +02:00
edde746 f3c735e3a1 fix: remove Docker bridge IP filter 2026-03-09 17:35:28 +01:00
edde746 a6119414ca Merge branch 'fix-plex-notifications' 2026-02-27 22:20:12 +01:00
edde746 00667fe8b8 fix: add const constructors and remove unused imports 2026-02-27 15:43:34 +01:00
Matt Vogel e9218e0a3d Fix duplicate Plex notifications and Flutter app name on login
Connection probe requests in testConnectionWithLatency were sent without
X-Plex-Client-Identifier, X-Plex-Product, or X-Plex-Device-Name headers.
Plex treated each anonymous probe as a new unknown device and fired a
"New Device" notification for every server tested (one per shared/owned
server), while displaying "Flutter" as the device name from the HTTP
user-agent.

Pass clientIdentifier through findBestWorkingConnection and all connection
test helpers so every probe request identifies itself as "Plezy" with the
persistent client UUID. This prevents spurious notifications and ensures
the device shows the correct app name in Plex's device list.

https://claude.ai/code/session_01V5VraujkNmk5GGPLyZ33fN
2026-02-26 08:21:06 -05:00
edde746 778ac50116 perf: fast offline startup
close #511
2026-02-23 14:09:13 +01:00
edde746 8aa7d15dca chore: upgrade dependencies and fix new lint warnings 2026-02-09 09:06:31 +01:00
edde746 3459c9f59d chore: format 2026-02-08 16:03:40 +01:00
edde746 0c44a35eb8 feat: log raw API connections 2026-02-08 03:54:22 +01:00
edde746 747fee0889 feat: log uploading 2026-02-07 20:09:30 +01:00
edde746 d3c7b55213 refactor: simplify server connection logic 2026-01-28 04:23:27 +01:00
edde746 3c039ad69c fix: failover storm during network loss 2026-01-28 04:03:43 +01:00
edde746 33e977872e fix: consume full connection stream and upgrade to HTTPS early 2026-01-27 17:45:10 +01:00
edde746 3c89d0eba8 refactor: update formatting 2025-12-15 02:29:03 +01:00
edde746 8f817815e6 refactor: simplify file structure 2025-12-04 08:36:07 +01:00
edde746 37b0cf65b2 fix: https required servers issue 2025-11-18 20:47:31 +01:00
edde746 f14ed7d762 chore: format 2025-11-15 04:08:57 +01:00
edde746 e6b699c976 fix: better connection selection 2025-11-15 00:42:45 +01:00
Doezer 3366ed02f5 Ran dart format using this pwsh command
$FILES = Get-ChildItem -Path lib, test -Recurse -Filter "*.dart" -File -ErrorAction SilentlyContinue | Where-Object { $_.Name -notmatch '\.(g|freezed)\.dart$' } | Select-Object -ExpandProperty FullName; if (-not $FILES) { Write-Host "No Dart files found to format"; exit 0 } else { dart format --output=write --set-exit-if-changed $FILES }
2025-11-13 22:53:31 +01:00
edde746 a607904a35 fix: better server selector errors 2025-11-09 21:24:44 +01:00
edde746 e4e7911573 fix: try http connections 2025-11-06 03:58:50 +01:00