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:
co-authored by
Claude Opus 5
parent
2675e6d82b
commit
4a4df7a73c
@@ -28,8 +28,51 @@ const (
|
||||
// One timeout is a hiccup — a restart, a slow scan — and telling a room full of
|
||||
// people about it is worse than saying nothing.
|
||||
embyFailureThreshold = 3
|
||||
|
||||
// A deployment is announced before it happens, and the window is the whole delivery
|
||||
// mechanism: it must stay open across the image build, which is the several minutes
|
||||
// during which the gateway is still answering and every open TV will poll at least
|
||||
// once. It cannot be made to cover the outage itself — the alert list lives in a
|
||||
// Redis with no persistence and no volume, so the swap takes it with everything else.
|
||||
// That is why this is published early rather than at the swap.
|
||||
deploymentAlertWindow = 20 * time.Minute
|
||||
)
|
||||
|
||||
// Wording is fixed here rather than taken from the caller: the deployment script is not a
|
||||
// place for on-screen copy, and a notice that reads differently each release is one
|
||||
// viewers have to read twice.
|
||||
const (
|
||||
deploymentAlertTitle = "Memby server in deployment mode"
|
||||
deploymentAlertMessage = "New update coming will be available soon."
|
||||
)
|
||||
|
||||
// AnnounceDeployment tells every open TV that the gateway is about to be replaced.
|
||||
//
|
||||
// It is published by the deploying operator *before* the old container stops — in fact
|
||||
// before the image is even built — because that is the only time there is anything left
|
||||
// to say it with. Once the stack is down the status poll fails, and a television shows a
|
||||
// connection error with no idea that somebody meant it to happen; publishing at the swap
|
||||
// would be too late for the same reason it is needed.
|
||||
//
|
||||
// Nothing here waits for the deployment to finish. The gateway that publishes this is the
|
||||
// one being retired; the one that comes back has no memory of having said it.
|
||||
func (s *Server) AnnounceDeployment(ctx context.Context) {
|
||||
s.publishAlert(ctx, deploymentAlert(time.Now().UTC()), deploymentAlertWindow)
|
||||
}
|
||||
|
||||
func deploymentAlert(now time.Time) clientAlert {
|
||||
return clientAlert{
|
||||
// Keyed on the second, so a redeployment ten minutes later is a second notice
|
||||
// rather than one the fleet has already dismissed as seen.
|
||||
ID: fmt.Sprintf("deploy:%d", now.Unix()),
|
||||
Kind: alertKindDeploying,
|
||||
Label: "SERVER UPDATING",
|
||||
Title: deploymentAlertTitle,
|
||||
Message: deploymentAlertMessage,
|
||||
AiredAt: now.Format(time.RFC3339),
|
||||
}
|
||||
}
|
||||
|
||||
// AnnounceLibrarySync tells every TV that the catalogue moved.
|
||||
//
|
||||
// Only a run that actually changed something is announced: the import is scheduled, so
|
||||
@@ -80,6 +123,10 @@ func (s *Server) WatchEmbyReachability(ctx context.Context, interval time.Durati
|
||||
// open with a banner about a state nobody has seen change.
|
||||
reachable := true
|
||||
failures := 0
|
||||
// The same probe feeds the live state on /v1/status, so one request to Emby answers
|
||||
// both "did this just change" and "is it working right now". Declared before the
|
||||
// first tick so a client asking during the opening minute learns the retry interval.
|
||||
s.embyHealth.begin(interval, time.Now().UTC())
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
@@ -88,18 +135,20 @@ func (s *Server) WatchEmbyReachability(ctx context.Context, interval time.Durati
|
||||
probeCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
err := s.emby.Ping(probeCtx)
|
||||
cancel()
|
||||
s.embyHealth.record(err == nil, time.Now().UTC())
|
||||
|
||||
if err != nil {
|
||||
failures++
|
||||
if reachable && failures >= embyFailureThreshold {
|
||||
reachable = false
|
||||
s.log.Warn("emby unreachable, announcing", "error", err)
|
||||
s.log.Warn("emby unreachable, announcing",
|
||||
"component", "emby-health", "failures", failures, "error", err)
|
||||
s.publishAlert(ctx, s.reachabilityAlert(false), reachabilityAlertWindow)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if !reachable {
|
||||
s.log.Info("emby reachable again, announcing")
|
||||
s.log.Info("emby reachable again, announcing", "component", "emby-health")
|
||||
s.publishAlert(ctx, s.reachabilityAlert(true), reachabilityAlertWindow)
|
||||
}
|
||||
reachable = true
|
||||
|
||||
Reference in New Issue
Block a user