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
+53
View File
@@ -0,0 +1,53 @@
package api
import "strings"
// The lifecycle word a schedule card wears: whether the show is still being made, whether
// the film has actually come out.
//
// It is *arr's own vocabulary rather than a Memby one, and deliberately so — the household
// operator reads the same word in Sonarr or Radarr, and a card that said something else
// would be a second thing to reconcile. The gateway sends both halves: the slug is what the
// television colours by, the label is what it prints, so a status this build has never
// heard of still reads correctly on a TV that predates it.
type lifecycleTag struct {
Status string
Label string
}
// seriesLifecycleTag maps Sonarr's series status. Anything unrecognised carries no tag at
// all rather than an invented one: a card with no lifecycle is the honest answer when the
// only thing we know is that Sonarr said a word we do not have.
func seriesLifecycleTag(status string) lifecycleTag {
switch strings.ToLower(strings.TrimSpace(status)) {
case "continuing":
return lifecycleTag{Status: "continuing", Label: "CONTINUING"}
case "upcoming":
return lifecycleTag{Status: "upcoming", Label: "UPCOMING"}
case "ended":
return lifecycleTag{Status: "ended", Label: "ENDED"}
case "deleted":
return lifecycleTag{Status: "deleted", Label: "REMOVED"}
default:
return lifecycleTag{}
}
}
// movieLifecycleTag maps Radarr's movie status. "inCinemas" loses its capital on the wire
// because the slug is a lookup key on the television, not prose.
func movieLifecycleTag(status string) lifecycleTag {
switch strings.ToLower(strings.TrimSpace(status)) {
case "tba":
return lifecycleTag{Status: "tba", Label: "TBA"}
case "announced":
return lifecycleTag{Status: "announced", Label: "ANNOUNCED"}
case "incinemas":
return lifecycleTag{Status: "incinemas", Label: "IN CINEMAS"}
case "released":
return lifecycleTag{Status: "released", Label: "RELEASED"}
case "deleted":
return lifecycleTag{Status: "deleted", Label: "REMOVED"}
default:
return lifecycleTag{}
}
}