Release v0.2.34

This commit is contained in:
ponzischeme89
2026-08-09 08:25:50 +12:00
parent b1128bcce2
commit fdd9e6cab2
116 changed files with 11418 additions and 879 deletions
+59
View File
@@ -18,8 +18,67 @@ import (
// deployment until the previous daily cache expires.
const sonarrCalendarCachePrefix = "sonarr:calendar:v4:"
const sonarrPrerollCachePrefix = "sonarr:preroll:v2:"
const sonarrSeriesCacheKey = "sonarr:series:v1"
const sonarrScheduleDays = 5
// sonarrSeriesCatalogue is Sonarr's whole series list, cached the way the calendar is.
//
// Every caller here wants the same thing — the lifecycle, monitored flag and next airing
// for one or two shows — and each was paying `/api/v3/series` in full to get it. On a
// household with a few hundred followed shows that is a large response Sonarr assembles
// from its own database, and it sat in front of things a viewer is waiting on: adding a
// show to My Shows, which fetches it *after* the write, and the season-finale lookup on a
// detail page. This is where the second or two came from.
//
// It is shared rather than per user — Sonarr's catalogue belongs to the household, not to
// whoever asked — and it takes MEMBY_SONARR_TTL, the same five minutes the calendar rows
// take. Short enough that following a show in Sonarr shows up on the next visit, long
// enough that a viewer working through My Shows pays for it once.
//
// Every failure degrades to asking Sonarr directly: a cache that is down must cost latency,
// never the answer.
func (s *Server) sonarrSeriesCatalogue(ctx context.Context) ([]sonarr.Series, error) {
if s.sonarr == nil {
return nil, fmt.Errorf("sonarr: not configured")
}
if series := s.cachedSonarrSeries(ctx); series != nil {
return series, nil
}
// The same kind of shared lock the calendar takes, for the same reason: several
// televisions opening together must not each stampede Sonarr on the one miss. Its own
// mutex rather than sonarrMu, so an add to My Shows never waits behind a launcher
// rebuilding the schedule row.
s.sonarrSeriesMu.Lock()
defer s.sonarrSeriesMu.Unlock()
if series := s.cachedSonarrSeries(ctx); series != nil {
return series, nil
}
series, err := s.sonarr.Series(ctx)
if err != nil {
return nil, err
}
if body, marshalErr := json.Marshal(series); marshalErr == nil {
if cacheErr := s.cache.Set(ctx, sonarrSeriesCacheKey, body, s.cfg.SonarrTTL); cacheErr != nil {
s.loggerFor(ctx).Warn("sonarr series cache write failed", "error", cacheErr)
}
}
return series, nil
}
func (s *Server) cachedSonarrSeries(ctx context.Context) []sonarr.Series {
raw, err := s.cache.Get(ctx, sonarrSeriesCacheKey)
if err != nil {
return nil
}
var series []sonarr.Series
if json.Unmarshal(raw, &series) != nil {
return nil
}
return series
}
type prerollScheduleResponse struct {
Today []prerollScheduleEntry `json:"today"`
ThisWeek []prerollScheduleEntry `json:"thisWeek"`