Release v0.2.34
This commit is contained in:
@@ -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"`
|
||||
|
||||
Reference in New Issue
Block a user