Discovery settings (admin)
HTTP routes and behavior for discovery settings (admin).
GET /api/admin/discovery-settings # source, english_only, hidden_when_unconfigured, sources, trakt_configuredPUT /api/admin/discovery-settings # atomic partial update; omitted fields/preferences surviveDecides what backs the headline row on the Movies and TV tabs. source is one of tmdb_trending (TMDB’s weekly trending feed), trakt_trending (Trakt trending, ranked by who is watching right now), or tmdb_popular (TMDB’s lifetime popularity ranking). TMDB’s popularity value is a lifetime score, so tmdb_popular fills with long-running catalogue shows and nightly talk shows: the trending feeds are short-window and self-correcting, which is why one of them is the default. trakt_configured reports whether the Trakt source can be selected at all (true out of the box on builds with the built-in Trakt application); picking it while Trakt is unavailable falls back to tmdb_trending rather than blanking the row: and so does a Trakt call that fails, since a third-party outage is not the admin’s configuration problem and the headline row is the landing screen. Either fallback is visible in the response rather than silent: source always names the feed that actually answered, so the client retitles the row. An outage fallback is cached against the chosen source, so a sustained one costs a single upstream attempt per TTL instead of one per request, and recovery is picked up on the next miss. The server logs each fallback.
Until an admin saves a choice, the default source follows Trakt availability: trakt_trending whenever Trakt can answer: out of the box on stock builds, via the built-in Trakt application: and tmdb_trending otherwise (a build without a built-in app and no stored client ID). An admin-stored client ID replaces the built-in app rather than changing the default. That default is derived per read, never written: a Trakt that stops being available moves the rows back instead of stranding them on a source that can no longer answer, and adoption never masquerades as an admin’s decision.
english_only drops titles whose original language is not English from the discovery and recommendation feeds, and from every page of the grids that continue them. It is on by default and stays on until an admin saves a choice: an untranslated title a household cannot watch costs a slot on every row and every page, and an admin who wants everything flips one switch while an admin who never opens the screen would otherwise never learn the filter exists. It never applies to search or to detail lookups: a title you went looking for stays findable: nor to a browse query that names a language of its own, since asking for Korean films is as explicit as searching for one; and a title the metadata source did not classify is kept rather than hidden.
A PUT replaces both fields, so a caller that omits english_only is turning the filter off, not leaving it alone; send the full pair. That PUT is also what satisfies the discovery_prefs step of the setup checklist. Every source and either english_only value is a legitimate answer, so that step grades whether the admin has saved a choice at all: not which choice. Until then the stored discovery_source is empty, which is the one marker for “never decided”: it is what both defaults above key on, and it is how a deliberate pick stays distinguishable from the same value arrived at by default. Any future setter that writes one discovery field must write the other too.