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
+16 -5
View File
@@ -11,10 +11,11 @@ import (
"github.com/ponzischeme89/memby/server/internal/appupdate"
)
// membyProtocolVersion changes only when the client/server wire contract is no longer
// ProtocolVersion changes only when the client/server wire contract is no longer
// mutually compatible. App release versions remain independent and are handled by the
// update policy.
const membyProtocolVersion = 1
// update policy. Exported so the startup line can state which contract this build
// speaks, next to the build's own version.
const ProtocolVersion = 1
// updatePolicyCache keeps the policy in memory. It is read on every home request, and a
// database round trip per home load to answer "nothing to say" would be wasteful.
@@ -79,12 +80,12 @@ func clientProtocolNumber(r *http.Request) int {
func compatibilityFor(r *http.Request) (bool, string) {
reported, err := strconv.Atoi(clientProtocol(r))
if err != nil || reported != membyProtocolVersion {
if err != nil || reported != ProtocolVersion {
if clientProtocol(r) == "" {
return false, "This Memby app is too old to verify compatibility with the server. Update the app."
}
return false, "Memby app/server mismatch: app protocol " + clientProtocol(r) +
", server protocol " + strconv.Itoa(membyProtocolVersion) + ". Update the app or server."
", server protocol " + strconv.Itoa(ProtocolVersion) + ". Update the app or server."
}
return true, ""
}
@@ -96,5 +97,15 @@ func compatibilityFor(r *http.Request) (bool, string) {
// the answer comes from memory, so checking it never reads or mutates a user session.
func (s *Server) handleUpdate(w http.ResponseWriter, r *http.Request) {
decision := appupdate.Decide(s.updatePolicy.get(), clientVersion(r))
// Only a verdict that asks a television to do something is worth a line. Every TV
// checks on every launch, and "nothing to say" logged each time would bury the
// launch where an update was actually offered — or forced.
if decision.Status != "" && decision.Status != appupdate.StatusNone {
s.loggerFor(r.Context()).Info("update offered",
"status", decision.Status,
"from", clientLogValue(clientVersion(r)),
"to", decision.Version,
)
}
writeJSON(w, http.StatusOK, decision)
}