package api import ( "context" "encoding/json" "strconv" "strings" ) // decorateSeriesStatuses makes Status on an ordinary Emby series the canonical detail // answer. Sonarr's daily observation wins when it is recognised; otherwise Emby's field // is left untouched as the fallback. func (s *Server) decorateSeriesStatuses(ctx context.Context, collections ...[]json.RawMessage) { if s.store == nil { return } tvdbIDs := []int{} seen := map[int]bool{} identities := make([]map[int]int, len(collections)) for collectionIndex, items := range collections { identities[collectionIndex] = map[int]int{} for itemIndex, raw := range items { tvdbID := seriesTVDBID(raw) if tvdbID <= 0 { continue } identities[collectionIndex][itemIndex] = tvdbID if !seen[tvdbID] { seen[tvdbID] = true tvdbIDs = append(tvdbIDs, tvdbID) } } } if len(tvdbIDs) == 0 { return } statuses, err := s.store.LatestSonarrSeriesStatuses(ctx, tvdbIDs) if err != nil { s.loggerFor(ctx).Warn("stored Sonarr series statuses unavailable", "error", err) return } for collectionIndex, itemIndexes := range identities { for itemIndex, tvdbID := range itemIndexes { status := canonicalSonarrSeriesStatus(statuses[tvdbID]) if status != "" { collections[collectionIndex][itemIndex] = injectSeriesStatus( collections[collectionIndex][itemIndex], status, ) } } } } func seriesTVDBID(raw json.RawMessage) int { var item struct { Type string `json:"Type"` ProviderIDs map[string]string `json:"ProviderIds"` } if json.Unmarshal(raw, &item) != nil || !strings.EqualFold(item.Type, "Series") { return 0 } id, _ := strconv.Atoi(providerID(item.ProviderIDs, "tvdb")) return id } // canonicalSonarrSeriesStatus is deliberately conservative. Unknown values are not // copied over a usable Emby status, and American spelling is normalised at the render // boundary before it can become viewer-facing text. func canonicalSonarrSeriesStatus(status string) string { switch strings.ToLower(strings.TrimSpace(status)) { case "continuing": return "Continuing" case "ended": return "Ended" case "cancelled", "canceled": return "Cancelled" case "upcoming": return "Upcoming" default: return "" } } func injectSeriesStatus(raw json.RawMessage, status string) json.RawMessage { if status == "" { return raw } var members map[string]json.RawMessage if json.Unmarshal(raw, &members) != nil || members == nil { return raw } encoded, err := json.Marshal(status) if err != nil { return raw } members["Status"] = encoded out, err := json.Marshal(members) if err != nil { return raw } return out }