Skip to content

Commit b8ee0c6

Browse files
refactor: simplify recommendation filtering and enhance theme-based service logic
1 parent ed55206 commit b8ee0c6

File tree

3 files changed

+33
-11
lines changed

3 files changed

+33
-11
lines changed

app/services/recommendation/filtering.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,6 @@ def get_genre_multiplier(genre_ids: list[int] | None, whitelist: set[int]) -> fl
143143
if not gids:
144144
return 1.0
145145

146-
# Special handling for Animation (16): Heavy penalty if not in whitelist
147-
if 16 in gids and 16 not in whitelist:
148-
return 0.1
149-
150146
# If it has at least one preferred genre, full score
151147
if gids & whitelist:
152148
return 1.0

app/services/recommendation/item_based.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,13 @@ async def _fetch_candidates(self, tmdb_id: int, mtype: str) -> list[dict[str, An
9898
return_exceptions=True,
9999
)
100100

101+
if not results:
102+
# fetch similar
103+
results = await asyncio.gather(
104+
*[self.tmdb_service.get_similar(tmdb_id, mtype, page=p) for p in [1, 2]],
105+
return_exceptions=True,
106+
)
107+
101108
for res in results:
102109
if isinstance(res, Exception):
103110
logger.warning(f"Error fetching recommendations for {tmdb_id}: {res}")

app/services/recommendation/theme_based.py

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
import asyncio
2+
from datetime import datetime
23
from typing import Any
34

45
from loguru import logger
56

67
from app.models.taste_profile import TasteProfile
8+
from app.services.profile.constants import TOP_PICKS_MIN_RATING, TOP_PICKS_MIN_VOTE_COUNT
79
from app.services.profile.scorer import ProfileScorer
810
from app.services.recommendation.filtering import RecommendationFiltering
911
from app.services.recommendation.metadata import RecommendationMetadata
1012
from app.services.recommendation.scoring import RecommendationScoring
1113
from app.services.recommendation.utils import content_type_to_mtype, filter_by_genres, filter_watched_by_imdb
14+
from app.services.tmdb.service import TMDBService
1215

1316

1417
class ThemeBasedService:
@@ -24,7 +27,7 @@ class ThemeBasedService:
2427
"""
2528

2629
def __init__(self, tmdb_service: Any, user_settings: Any = None):
27-
self.tmdb_service = tmdb_service
30+
self.tmdb_service: TMDBService = tmdb_service
2831
self.user_settings = user_settings
2932
self.scorer = ProfileScorer()
3033

@@ -77,11 +80,13 @@ async def get_recommendations_for_theme(
7780
# Fetch candidates
7881
candidates = await self._fetch_discover_candidates(content_type, params, pages_to_fetch)
7982

80-
# Use provided whitelist (or empty set if not provided)
81-
whitelist = whitelist or set()
83+
# # Use provided whitelist (or empty set if not provided)
84+
# whitelist = whitelist or set()
8285

83-
# Initial filter (watched + genre whitelist)
84-
filtered = self._filter_candidates(candidates, watched_tmdb, whitelist)
86+
# # Initial filter (watched + genre whitelist)
87+
# filtered = self._filter_candidates(candidates, watched_tmdb, whitelist)
88+
89+
filtered = candidates
8590

8691
# If not enough candidates, fetch more pages
8792
if len(filtered) < limit * 2 and max(pages_to_fetch) < 15:
@@ -123,6 +128,9 @@ async def get_recommendations_for_theme(
123128
scored.sort(key=lambda x: x[0], reverse=True)
124129
filtered = [item for _, item in scored]
125130

131+
# limit items to limit *2
132+
filtered = filtered[: limit * 2]
133+
126134
# Enrich metadata
127135
enriched = await RecommendationMetadata.fetch_batch(
128136
self.tmdb_service, filtered, content_type, user_settings=self.user_settings
@@ -168,17 +176,28 @@ def _parse_theme_id(self, theme_id: str, content_type: str) -> dict[str, Any]:
168176
is_tv = content_type in ("tv", "series")
169177
prefix = "first_air_date" if is_tv else "primary_release_date"
170178
params[f"{prefix}.gte"] = f"{year}-01-01"
171-
params[f"{prefix}.lte"] = f"{year+9}-12-31"
179+
end_year = year + 9
180+
end_date_str = f"{end_year}-12-31"
181+
today_str = datetime.now().strftime("%Y-%m-%d")
182+
# If the calculated end date is after today, use today
183+
if end_date_str > today_str:
184+
params[f"{prefix}.lte"] = today_str
185+
else:
186+
params[f"{prefix}.lte"] = end_date_str
172187
except Exception:
173188
pass
174189
elif part == "sort-vote":
175190
params["sort_by"] = "vote_average.desc"
176-
params["vote_count.gte"] = 200
191+
params["vote_count.gte"] = TOP_PICKS_MIN_VOTE_COUNT
177192

178193
# Default sort
179194
if "sort_by" not in params:
180195
params["sort_by"] = "popularity.desc"
181196

197+
# add default vote count and vote average
198+
params["vote_count.gte"] = TOP_PICKS_MIN_VOTE_COUNT
199+
params["vote_average.gte"] = TOP_PICKS_MIN_RATING
200+
182201
return params
183202

184203
def _calculate_pages_to_fetch(self, num_excluded_genres: int) -> list[int]:

0 commit comments

Comments
 (0)