soulsync/tests/video/test_discovery_recs.py
BoulderBadgeDad e7b1a239b4 video discover phase 2: blended 'Recommended for you' wall
A single personalized wall aggregating TMDB recommendations across many of your owned titles
(random_owned_titles seeds), ranked by consensus — a title recommended by more of your library
ranks higher (ties by rating then popularity), owned + seed titles excluded.
- core/video/discovery_recs.py: pure blend_recommendations (dedup/consensus/exclude), 7 tests.
- /api/video/discover/foryou aggregates ~12 seeds' recommendations.
- loadForYou() prepends the 'Recommended for you' rail on top of the stack; re-runs on the
  hide-owned toggle.
2026-06-23 00:05:26 -07:00

51 lines
1.7 KiB
Python

"""Blended 'Recommended for you' aggregation (#discover phase 2)."""
from __future__ import annotations
from core.video.discovery_recs import blend_recommendations
def _it(tid, kind="movie", rating=0, pop=0, owned=False):
d = {"tmdb_id": tid, "kind": kind, "rating": rating, "popularity": pop}
if owned:
d["library_id"] = 99
return d
def test_consensus_ranks_higher():
# title 2 recommended by 3 seeds, title 1 by 1 -> title 2 first
lists = [[_it(1), _it(2)], [_it(2)], [_it(2), _it(3)]]
out = blend_recommendations(lists)
assert [i["tmdb_id"] for i in out][0] == 2
def test_excludes_owned_and_seeds():
lists = [[_it(1), _it(2, owned=True), _it(3)]]
out = blend_recommendations(lists, exclude_ids=[1])
assert [i["tmdb_id"] for i in out] == [3] # 1 = seed, 2 = owned
def test_ties_break_by_rating_then_popularity():
lists = [[_it(1, rating=7, pop=10), _it(2, rating=9, pop=5), _it(3, rating=9, pop=50)]]
# all count=1 -> rating desc (3,2 tie at 9 -> pop desc: 3 then 2), then 1
assert [i["tmdb_id"] for i in blend_recommendations(lists)] == [3, 2, 1]
def test_dedup_same_title_across_lists_counts_once_per_list():
lists = [[_it(5)], [_it(5)], [_it(5)]]
out = blend_recommendations(lists)
assert len(out) == 1 and out[0]["tmdb_id"] == 5
def test_kind_distinguishes_same_tmdb_id():
lists = [[_it(7, kind="movie"), _it(7, kind="show")]]
assert len(blend_recommendations(lists)) == 2
def test_limit():
lists = [[_it(i, pop=i) for i in range(1, 11)]]
assert len(blend_recommendations(lists, limit=3)) == 3
def test_empty():
assert blend_recommendations([]) == []
assert blend_recommendations([[], None]) == []