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
+21 -5
View File
@@ -17,6 +17,8 @@ const (
featureAutomaticMyShows = "automatic_my_shows"
featureMyShowsNotification = "my_shows_notifications"
featureHEVCDirectPlay = "hevc_direct_play"
featureInstallPermission = "install_permission_prompt"
featureSubtitleDownload = "subtitle_download"
)
type featureDefinition struct {
@@ -55,6 +57,20 @@ var featureCatalogue = []featureDefinition{
DefaultEnabled: true, MinimumProtocol: 1, Capability: "video_hevc_decode",
Recovery: "Server-enforced; takes effect the next time playback starts or refreshes.",
},
{
Key: featureSubtitleDownload, Name: "Download missing subtitles", Area: "Playback",
Description: "Let a viewer fetch a subtitle through Bazarr from the player, for a " +
"title the library has none for.",
DefaultEnabled: true, MinimumProtocol: 1, Capability: "subtitle_download_v1",
Recovery: "Takes effect the next time playback starts; the option simply stops being offered.",
},
{
Key: featureInstallPermission, Name: "Ask TVs for install permission", Area: "Setup",
Description: "Ask a signed-in TV that cannot install its own updates to grant the " +
"permission, so a mandatory update is not the first time it comes up.",
DefaultEnabled: true, MinimumProtocol: 1, Capability: "install_permission_v1",
Recovery: "Appears on the next status poll, and only on a TV missing the permission.",
},
}
type evaluatedFeature struct {
@@ -103,7 +119,7 @@ func (s *Server) currentFeaturePolicy(ctx context.Context) store.FeaturePolicy {
}
policy, err := s.store.FeaturePolicy(ctx)
if err != nil {
s.log.Warn("feature policy unavailable; using safe defaults", "error", err)
s.loggerFor(ctx).Warn("feature policy unavailable; using safe defaults", "error", err)
return store.DefaultFeaturePolicy()
}
return policy
@@ -114,7 +130,7 @@ func (s *Server) featureEnabled(ctx context.Context, key string) bool {
if !ok {
return false
}
return evaluateFeature(s.currentFeaturePolicy(ctx), definition, membyProtocolVersion).Enabled
return evaluateFeature(s.currentFeaturePolicy(ctx), definition, ProtocolVersion).Enabled
}
func featurePayload(policy store.FeaturePolicy, protocol int, capabilities ...[]string) featureResponse {
@@ -204,13 +220,13 @@ func (s *Server) handleAdminFeaturePolicy(w http.ResponseWriter, r *http.Request
writeError(w, http.StatusConflict, "feature flags changed in another admin session; refresh before saving")
return
}
s.log.Error("feature policy write failed", "error", err)
s.loggerFor(r.Context()).Error("feature policy write failed", "error", err)
writeError(w, http.StatusInternalServerError, "could not save feature flags")
return
}
s.log.Info("feature policy changed", "action", req.Action, "revision", stored.Revision,
s.loggerFor(r.Context()).Info("feature policy changed", "action", req.Action, "revision", stored.Revision,
"safe_mode", stored.SafeMode, "overrides", len(stored.Overrides))
writeJSON(w, http.StatusOK, featurePayload(stored, membyProtocolVersion))
writeJSON(w, http.StatusOK, featurePayload(stored, ProtocolVersion))
}
func parseCapabilities(raw string) []string {