Video Discover: split Top 10 today into Movies + TV Shows (Netflix-style)
The mixed /trending/all chart is movie-heavy — TV was nearly absent (only 1 of the top 10 was a show). Split it like Netflix: 'Top 10 today' now holds two ranked rails, Movies and TV Shows, from the dedicated /trending/movie/day and /trending/tv/day charts (full 10 of each). - client.trending(window, kind): single-type charts force the kind into _disc_map (those endpoints omit media_type). - engine.trending(window, kind): kind in the cache key. - /discover/list: key=trending_movies_today / trending_tv_today, both treated as single un-paged charts; inherit the same hide-owned + ranked rendering. - frontend: one 'Top 10 today' group, two ranked shelves titled Movies / TV Shows (group header carries the 'Top 10' framing).
This commit is contained in:
parent
e7552d3c62
commit
877a86c9b0
4 changed files with 31 additions and 16 deletions
|
|
@ -322,7 +322,11 @@ def register_routes(bp):
|
||||||
if key == "trending":
|
if key == "trending":
|
||||||
return eng.trending()
|
return eng.trending()
|
||||||
if key == "trending_today":
|
if key == "trending_today":
|
||||||
return eng.trending(window="day") # the ranked 'Top 10 today' chart
|
return eng.trending(window="day") # the ranked 'Top 10 today' chart (mixed)
|
||||||
|
if key == "trending_movies_today":
|
||||||
|
return eng.trending(window="day", kind="movie") # split 'Top 10 Movies Today'
|
||||||
|
if key == "trending_tv_today":
|
||||||
|
return eng.trending(window="day", kind="show") # split 'Top 10 TV Shows Today'
|
||||||
if key:
|
if key:
|
||||||
return eng.discover_curated(key, page=p)
|
return eng.discover_curated(key, page=p)
|
||||||
return eng.discover_filter(
|
return eng.discover_filter(
|
||||||
|
|
@ -360,7 +364,7 @@ def register_routes(bp):
|
||||||
# thread-safe) so digging 20 pages deep costs ~4 page-latencies, not 20.
|
# thread-safe) so digging 20 pages deep costs ~4 page-latencies, not 20.
|
||||||
need_fill = hide_owned or bool(prefer_langs)
|
need_fill = hide_owned or bool(prefer_langs)
|
||||||
target = 20 if need_fill else 0
|
target = 20 if need_fill else 0
|
||||||
if key in ("trending", "trending_today"):
|
if key in ("trending", "trending_today", "trending_movies_today", "trending_tv_today"):
|
||||||
consume(fetch(page)) # a single fixed chart; never paged
|
consume(fetch(page)) # a single fixed chart; never paged
|
||||||
elif not need_fill:
|
elif not need_fill:
|
||||||
for offset in range(pages): # no filtering: respect requested page count
|
for offset in range(pages): # no filtering: respect requested page count
|
||||||
|
|
|
||||||
|
|
@ -447,16 +447,22 @@ class TMDBClient:
|
||||||
"poster": (self.PROFILE + it["profile_path"]) if it.get("profile_path") else None})
|
"poster": (self.PROFILE + it["profile_path"]) if it.get("profile_path") else None})
|
||||||
return out
|
return out
|
||||||
|
|
||||||
def trending(self, window="week"):
|
def trending(self, window="week", kind=None):
|
||||||
"""Trending movies + shows this week — fills the search page when idle and
|
"""Trending titles. ``kind`` None = mixed movies + shows (/trending/all — the
|
||||||
the Discover hero slideshow (hence backdrops via _disc_map)."""
|
search-idle filler + Discover hero slideshow, hence backdrops via _disc_map).
|
||||||
|
``kind`` 'movie' / 'show' hit the dedicated single-type charts (/trending/movie,
|
||||||
|
/trending/tv) that power the split 'Top 10 Movies / TV Shows Today' rails.
|
||||||
|
Single-type endpoints omit media_type, so the kind is forced into _disc_map."""
|
||||||
if not self.api_key:
|
if not self.api_key:
|
||||||
return []
|
return []
|
||||||
import requests
|
import requests
|
||||||
r = requests.get(self.BASE + "/trending/all/" + window,
|
path = ("/trending/movie/" if kind == "movie"
|
||||||
params={"api_key": self.api_key}, timeout=15)
|
else "/trending/tv/" if kind == "show"
|
||||||
|
else "/trending/all/") + window
|
||||||
|
r = requests.get(self.BASE + path, params={"api_key": self.api_key}, timeout=15)
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
return self._disc_map((r.json() or {}).get("results"), None)[:20]
|
forced = kind if kind in ("movie", "show") else None
|
||||||
|
return self._disc_map((r.json() or {}).get("results"), forced)[:20]
|
||||||
|
|
||||||
# ── discover (browse TMDB by curated list / genre / year / decade) ────────
|
# ── discover (browse TMDB by curated list / genre / year / decade) ────────
|
||||||
def _disc_map(self, results, kind):
|
def _disc_map(self, results, kind):
|
||||||
|
|
|
||||||
|
|
@ -310,19 +310,21 @@ class VideoEnrichmentEngine:
|
||||||
return []
|
return []
|
||||||
return self._stamp_owned(results)
|
return self._stamp_owned(results)
|
||||||
|
|
||||||
def trending(self, window="week") -> list:
|
def trending(self, window="week", kind=None) -> list:
|
||||||
"""Trending titles, annotated owned/not. ``window='day'`` is the real-time
|
"""Trending titles, annotated owned/not. ``window='day'`` is the real-time
|
||||||
daily chart (the iconic 'Top 10 today' row); 'week' is the steadier hero/idle
|
daily chart (the iconic 'Top 10 today' rows); 'week' is the steadier hero/idle
|
||||||
feed. Order is preserved, so the caller can render rank numbers."""
|
feed. ``kind`` None = mixed; 'movie'/'show' = the dedicated single-type charts
|
||||||
|
(split 'Top 10 Movies / TV Shows Today'). Order is preserved for rank numbers."""
|
||||||
w = self.workers.get("tmdb")
|
w = self.workers.get("tmdb")
|
||||||
if not w or not w.enabled:
|
if not w or not w.enabled:
|
||||||
return []
|
return []
|
||||||
window = "day" if window == "day" else "week"
|
window = "day" if window == "day" else "week"
|
||||||
ck = ("trending", window)
|
kind = kind if kind in ("movie", "show") else None
|
||||||
|
ck = ("trending", window, kind)
|
||||||
cached = self._cache_get(ck)
|
cached = self._cache_get(ck)
|
||||||
if cached is None:
|
if cached is None:
|
||||||
try:
|
try:
|
||||||
cached = w.client.trending(window=window) or []
|
cached = w.client.trending(window=window, kind=kind) or []
|
||||||
self._cache_put(ck, cached, ttl=3600)
|
self._cache_put(ck, cached, ttl=3600)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("video trending failed")
|
logger.exception("video trending failed")
|
||||||
|
|
|
||||||
|
|
@ -49,8 +49,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── the rail stack (personalized + curated + genre + decade) ──────────────
|
// ── the rail stack (personalized + curated + genre + decade) ──────────────
|
||||||
// The iconic ranked "Top 10 today" row — daily TMDB trending, rendered with rank numbers.
|
// The iconic ranked "Top 10 today" rows — split Movies / TV like Netflix, so TV
|
||||||
var TOP10 = { title: 'Top 10 Today', q: 'key=trending_today&lang=any', ranked: true };
|
// isn't drowned out by the movie-heavy mixed chart. Daily TMDB trending, rank numbers.
|
||||||
|
// (Titles are terse because the group header already says "Top 10 today".)
|
||||||
|
var TOP10_MOVIES = { title: 'Movies', q: 'key=trending_movies_today&lang=any', ranked: true };
|
||||||
|
var TOP10_TV = { title: 'TV Shows', q: 'key=trending_tv_today&lang=any', ranked: true };
|
||||||
var CURATED = [
|
var CURATED = [
|
||||||
{ title: 'Trending This Week', q: 'key=trending' },
|
{ title: 'Trending This Week', q: 'key=trending' },
|
||||||
{ title: 'Popular Movies', q: 'key=popular_movies' },
|
{ title: 'Popular Movies', q: 'key=popular_movies' },
|
||||||
|
|
@ -155,7 +158,7 @@
|
||||||
// keep those group ids. Order of this array IS the on-screen order.
|
// keep those group ids. Order of this array IS the on-screen order.
|
||||||
return [
|
return [
|
||||||
{ id: 'foryou', label: 'For you', rails: foryou },
|
{ id: 'foryou', label: 'For you', rails: foryou },
|
||||||
{ id: 'topten', label: 'Top 10 today', rails: [TOP10] },
|
{ id: 'topten', label: 'Top 10 today', rails: [TOP10_MOVIES, TOP10_TV] },
|
||||||
{ id: 'new', label: 'New & noteworthy', rails: NEW_RAILS },
|
{ id: 'new', label: 'New & noteworthy', rails: NEW_RAILS },
|
||||||
{ id: 'collection', label: 'Finish your collection', rails: [] },
|
{ id: 'collection', label: 'Finish your collection', rails: [] },
|
||||||
{ id: 'taste', label: 'More of what you like', rails: taste },
|
{ id: 'taste', label: 'More of what you like', rails: taste },
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue