Découverte : le filtre par préférence s'applique avant la troncature du rail
En mode séries, le rail Nouveautés était vide : discover.latest() triait par date de sortie Kitsu (catalogue animés uniquement) puis tronquait à 24 AVANT le filtrage — les séries et films réels, absents de Kitsu (donc sans date), étaient systématiquement évincés. - allowed (types autorisés par préférence) descend dans discover.latest() et filtre dès la collecte des sources - remplissage du rail équilibré par type (round-robin) : en mode « les deux », 8 animés / 8 séries / 8 films au lieu de 24 animés - test de non-régression : 9 séries conservées face à 30 animés datés
This commit is contained in:
@@ -23,7 +23,7 @@ async def get_discover(
|
||||
) -> dict:
|
||||
"""Les trois sections de découverte en une requête (sections vides si source KO)."""
|
||||
allowed = _allowed_media_types(user.content_preference)
|
||||
latest = [i for i in await discover.latest(latest_limit) if i.get("media_type", "anime") in allowed]
|
||||
latest = await discover.latest(latest_limit, allowed)
|
||||
# Incontournables et Pour toi sont issus de Kitsu (catalogue animés) :
|
||||
# en mode séries, elles n'ont pas de sens — on ne les calcule même pas.
|
||||
if "anime" in allowed:
|
||||
|
||||
@@ -92,20 +92,29 @@ class DiscoverService:
|
||||
|
||||
# ------------------------------------------------------------ nouveautés
|
||||
|
||||
async def latest(self, limit: int = 24) -> list[dict]:
|
||||
async def latest(self, limit: int = 24, allowed: set[str] | None = None) -> list[dict]:
|
||||
"""Nouveautés toutes sources confondues, triées par date de sortie réelle.
|
||||
|
||||
Les « récemment ajoutés » de chaque source sont fusionnés (doublons retirés),
|
||||
enrichis via Kitsu (date de début, statut de diffusion) puis triés du plus
|
||||
récent au plus ancien — ce qui sort / vient de sortir en premier.
|
||||
|
||||
``allowed`` restreint aux types de médias autorisés (préférence du compte),
|
||||
AVANT fusion/tri/troncature : sans lui, les titres hors Kitsu (séries, films
|
||||
réel) — sans date de sortie — seraient évincés du rail par la troncature.
|
||||
Le remplissage final équilibre les types présents (round-robin) pour qu'aucun
|
||||
ne soit écrasé par les autres en mode « les deux ».
|
||||
"""
|
||||
cached = self._cache.get(f"latest:{limit}")
|
||||
cache_key = f"latest:{limit}:{','.join(sorted(allowed)) if allowed else 'all'}"
|
||||
cached = self._cache.get(cache_key)
|
||||
if cached is not None:
|
||||
return cached # type: ignore[return-value]
|
||||
|
||||
sources = [s for s in all_sources() if await is_source_enabled(s.name)]
|
||||
outcomes = await asyncio.gather(*(self._latest_of(source) for source in sources))
|
||||
items = [item for outcome in outcomes for item in (outcome or [])]
|
||||
if allowed is not None:
|
||||
items = [item for item in items if item.get("media_type", "anime") in allowed]
|
||||
|
||||
merged: dict[str, dict] = {}
|
||||
for item in items:
|
||||
@@ -120,9 +129,20 @@ class DiscoverService:
|
||||
return await self._with_release_info(item)
|
||||
|
||||
enriched = await asyncio.gather(*(bounded(item) for item in merged.values()))
|
||||
result = sorted(enriched, key=lambda it: it.get("start_date") or "", reverse=True)
|
||||
result = result[:limit]
|
||||
self._cache.set(f"latest:{limit}", result, _LATEST_TTL_SECONDS)
|
||||
by_type: dict[str, list[dict]] = {}
|
||||
for item in sorted(enriched, key=lambda it: it.get("start_date") or "", reverse=True):
|
||||
by_type.setdefault(item.get("media_type", "anime"), []).append(item)
|
||||
|
||||
result: list[dict] = []
|
||||
pools = [list(pool) for pool in by_type.values()]
|
||||
while len(result) < limit and pools:
|
||||
for pool in pools[:]:
|
||||
result.append(pool.pop(0))
|
||||
if len(result) >= limit:
|
||||
break
|
||||
if not pool:
|
||||
pools.remove(pool)
|
||||
self._cache.set(cache_key, result, _LATEST_TTL_SECONDS)
|
||||
return result
|
||||
|
||||
async def _latest_of(self, source: SourceScraper) -> list[dict] | None:
|
||||
|
||||
Reference in New Issue
Block a user