App v0.2.26 and gateway 0.1.20

Client: seek controls, Bazarr subtitle download and cast panel in the
player; MDBList ratings strip; episode and schedule detail pages; series
pace estimate; what's new panel; install-permission onboarding step;
synced per-profile preferences; Emby outage banner.

Gateway: rebuilt admin console (one fragment per page), preference
history and restore, merged Continue Watching, Emby health probe,
subtitle selection and Bazarr download, structured request logging with
per-request identity, and embedded build version.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
ponzischeme89
2026-08-06 22:33:56 +12:00
co-authored by Claude Opus 5
parent 2675e6d82b
commit 4a4df7a73c
257 changed files with 24868 additions and 3108 deletions
+25 -2
View File
@@ -25,7 +25,9 @@ type Config struct {
RedisURL string
// ClientName is reported to Emby in the X-Emby-Authorization header, so sessions
// show up as this in Emby's dashboard.
// show up as this in Emby's dashboard. It is deliberately not the app's own name:
// the header travels to whatever Emby does with its logs, and the product name has
// no business being the thing that identifies a client to a third party.
ClientName string
HomeTTL time.Duration
@@ -103,6 +105,20 @@ type Config struct {
// news, and each TV dedupes by alert id. Zero turns the banners off.
RadarrAlertWindow time.Duration
// Bazarr is optional and is what lets a viewer fetch a subtitle a title does not have.
// It writes the file beside the media, so Emby serves the result and the gateway needs
// no storage of its own. Unset means the player simply never offers the option.
BazarrURL string
BazarrAPIKey string
// BazarrTTL is how long the movie/series/episode listings are cached. They exist only
// to turn an Emby item into the *arr id Bazarr keys on, and a household's library does
// not change between one subtitle search and the next.
BazarrTTL time.Duration
// BazarrTimeout bounds a manual search, which queries live subtitle providers and is
// legitimately slow. It is separate from BazarrTTL because a short HTTP timeout here
// shows up as "no subtitles found" rather than as an error.
BazarrTimeout time.Duration
// Tracearr is an optional, read-only source of completion, session-length and
// direct-play signals for the per-user For You area. The public API key stays in
// the gateway and is never returned to a TV.
@@ -127,7 +143,7 @@ func Load() (Config, error) {
EmbyPublicURL: strings.TrimRight(os.Getenv("MEMBY_EMBY_PUBLIC_URL"), "/"),
DatabaseURL: os.Getenv("MEMBY_DATABASE_URL"),
RedisURL: env("MEMBY_REDIS_URL", "redis://localhost:6379/0"),
ClientName: env("MEMBY_CLIENT_NAME", "Memby"),
ClientName: env("MEMBY_CLIENT_NAME", "MbyATV"),
HomeTTL: duration("MEMBY_HOME_TTL", 60*time.Second),
ItemTTL: duration("MEMBY_ITEM_TTL", 10*time.Minute),
SearchTTL: duration("MEMBY_SEARCH_TTL", 5*time.Minute),
@@ -163,6 +179,10 @@ func Load() (Config, error) {
RadarrTTL: duration("MEMBY_RADARR_TTL", 5*time.Minute),
RadarrWebhookToken: strings.TrimSpace(os.Getenv("MEMBY_RADARR_WEBHOOK_TOKEN")),
RadarrAlertWindow: duration("MEMBY_RADARR_ALERT_WINDOW", 3*time.Hour),
BazarrURL: strings.TrimRight(strings.TrimSpace(os.Getenv("MEMBY_BAZARR_URL")), "/"),
BazarrAPIKey: strings.TrimSpace(os.Getenv("MEMBY_BAZARR_API_KEY")),
BazarrTTL: duration("MEMBY_BAZARR_TTL", 5*time.Minute),
BazarrTimeout: duration("MEMBY_BAZARR_TIMEOUT", 45*time.Second),
TracearrURL: strings.TrimRight(strings.TrimSpace(os.Getenv("MEMBY_TRACEARR_URL")), "/"),
TracearrAPIKey: strings.TrimSpace(os.Getenv("MEMBY_TRACEARR_API_KEY")),
TracearrServerID: strings.TrimSpace(os.Getenv("MEMBY_TRACEARR_SERVER_ID")),
@@ -191,6 +211,9 @@ func Load() (Config, error) {
if (c.RadarrURL == "") != (c.RadarrAPIKey == "") {
return c, fmt.Errorf("MEMBY_RADARR_URL and MEMBY_RADARR_API_KEY must be set together")
}
if (c.BazarrURL == "") != (c.BazarrAPIKey == "") {
return c, fmt.Errorf("MEMBY_BAZARR_URL and MEMBY_BAZARR_API_KEY must be set together")
}
if (c.TracearrURL == "") != (c.TracearrAPIKey == "") {
return c, fmt.Errorf("MEMBY_TRACEARR_URL and MEMBY_TRACEARR_API_KEY must be set together")
}