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
@@ -716,27 +716,79 @@ func rowKey(value string) string {
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// ImportIfDue runs an import only when the persisted state says one is owed. Due-ness is
|
||||
// measured from the last recorded import rather than from process uptime, so a restart —
|
||||
// a redeploy, a crash loop, a container the healthcheck bounced — cannot buy an extra
|
||||
// pass, and a gateway that is restarted more often than fullEvery still reconciles.
|
||||
func (s *Service) ImportIfDue(
|
||||
ctx context.Context,
|
||||
importEvery, fullEvery time.Duration,
|
||||
) (ImportResult, bool, error) {
|
||||
state, err := s.store.TracearrImportState(ctx)
|
||||
if err != nil {
|
||||
return ImportResult{}, false, err
|
||||
}
|
||||
full, due := importDue(state, time.Now().UTC(), importEvery, fullEvery)
|
||||
if !due {
|
||||
s.log.Debug("Tracearr import not due",
|
||||
"lastIncremental", state.LastIncrementalAt, "lastFull", state.LastFullAt)
|
||||
return ImportResult{}, false, nil
|
||||
}
|
||||
result, err := s.Import(ctx, full)
|
||||
return result, err == nil, err
|
||||
}
|
||||
|
||||
// importDue is the whole scheduling rule, kept pure so it can be tested without a
|
||||
// database. A stamp in the future is treated as due: a clock correction must not be able
|
||||
// to strand the importer for an arbitrary length of time.
|
||||
func importDue(
|
||||
state store.TracearrImportState,
|
||||
now time.Time,
|
||||
importEvery, fullEvery time.Duration,
|
||||
) (full bool, due bool) {
|
||||
elapsed := func(at *time.Time) (time.Duration, bool) {
|
||||
if at == nil {
|
||||
return 0, false
|
||||
}
|
||||
return now.Sub(*at), true
|
||||
}
|
||||
if fullEvery > 0 {
|
||||
since, recorded := elapsed(state.LastFullAt)
|
||||
if !recorded || since >= fullEvery || since < 0 {
|
||||
return true, true
|
||||
}
|
||||
}
|
||||
if importEvery > 0 {
|
||||
since, recorded := elapsed(state.LastIncrementalAt)
|
||||
if !recorded || since >= importEvery || since < 0 {
|
||||
return false, true
|
||||
}
|
||||
}
|
||||
return false, false
|
||||
}
|
||||
|
||||
func (s *Service) Schedule(
|
||||
ctx context.Context,
|
||||
importEvery, fullEvery time.Duration,
|
||||
rebuildHour int,
|
||||
) {
|
||||
// One ticker asks "is anything owed?"; the persisted stamps decide what and whether.
|
||||
// Two independent tickers measured process uptime, which is what let a restart reset
|
||||
// the cadence and a bounced container import far more often than configured.
|
||||
checkEvery := importEvery
|
||||
if checkEvery <= 0 || (fullEvery > 0 && fullEvery < checkEvery) {
|
||||
checkEvery = fullEvery
|
||||
}
|
||||
var importC <-chan time.Time
|
||||
var importTicker *time.Ticker
|
||||
if importEvery > 0 {
|
||||
importTicker = time.NewTicker(importEvery)
|
||||
if checkEvery > 0 {
|
||||
importTicker := time.NewTicker(checkEvery)
|
||||
importC = importTicker.C
|
||||
defer importTicker.Stop()
|
||||
s.log.Info("Tracearr auto-import scheduled",
|
||||
"incremental", importEvery.String(), "full", fullEvery.String())
|
||||
} else {
|
||||
s.log.Info("Tracearr auto-import disabled")
|
||||
}
|
||||
var fullC <-chan time.Time
|
||||
var fullTicker *time.Ticker
|
||||
if fullEvery > 0 {
|
||||
fullTicker = time.NewTicker(fullEvery)
|
||||
fullC = fullTicker.C
|
||||
defer fullTicker.Stop()
|
||||
}
|
||||
nextRebuild := nextDailyRebuild(time.Now(), s.location, rebuildHour)
|
||||
rebuildTimer := time.NewTimer(time.Until(nextRebuild))
|
||||
defer rebuildTimer.Stop()
|
||||
@@ -746,13 +798,9 @@ func (s *Service) Schedule(
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-importC:
|
||||
if _, err := s.Import(ctx, false); err != nil {
|
||||
if _, _, err := s.ImportIfDue(ctx, importEvery, fullEvery); err != nil {
|
||||
s.log.Warn("scheduled Tracearr import failed", "error", err)
|
||||
}
|
||||
case <-fullC:
|
||||
if _, err := s.Import(ctx, true); err != nil {
|
||||
s.log.Warn("scheduled full Tracearr import failed", "error", err)
|
||||
}
|
||||
case <-rebuildTimer.C:
|
||||
if err := s.RebuildAll(ctx, true); err != nil {
|
||||
s.log.Warn("scheduled daily For You rebuild failed", "error", err)
|
||||
|
||||
Reference in New Issue
Block a user