fix(tmdb): un titre sans identifiant IMDb sur TMDB remonte ses flux (FOXA-2841) #37

Merged
foXaCe merged 3 commits from agent/opencode-gratuit/ec44c9114e04 into main 2026-10-02 08:45:28 +02:00
Owner

Un film ou une série trop récent, ou trop niche, n'a pas encore de fiche IMDb sur TMDB. Le catalogue (AIOMetadata) sert alors un tmdb:<id> au lieu d'un tt… — et extract_media_info ne connaissait pas cette forme.

Le défaut, tel que mesuré

tmdb:335292:1:5   (série)
  →  imdb_id = "tmdb"        ← le littéral, pris pour un IMDb id
      season   = "335292"    ← l'identifiant, pris pour une saison
      episode  = "1"         ← décalé d'un cran

/find — qui exige un IMDb id — échouait toujours, et stream.py:610 rendait []. Zéro flux, sans erreur visible, sur un titre que le catalogue servait correctement. Le chemin est vivant, pas du code mort : wastream/utils/validators.py:141 (le parseur) → wastream/services/stream.py:586 (appel) → wastream/services/stream.py:605 (résolution TMDB par imdb_id).

Ce qui change

  1. Une forme tmdb: dédiée dans extract_media_info. Elle sort tmdb_id, jamais imdb_id ; les quatre branches rendent désormais la même clé tmdb_id, à None pour tt… et kitsu:. Un film reste sans saison ni épisode, comme la forme tt… d'un film ; c'est une série qui prend saison:episode à partir de la troisième et quatrième partie.
  2. La résolution par identifiant TMDB, sans /find : get_enhanced_metadata_by_tmdb_id / get_metadata_by_tmdb_id dans wastream/services/tmdb.py, _get_metadata_by_tmdb_id dans wastream/services/stream.py, et l'aiguillage dans get_streams.

L'amont dydy13014/wacustom (27bb2b9, 2026-10-01) prend la même direction et extrait les mêmes deux constructeurs. On ne recopie pas son patch : la résolution est écrite dans notre structure, et elle est plus complète sur trois points.

  • L'anime. genre_ids n'existe que sur un résultat de recherche ; la fiche /tv/{id} rend genres, une liste d'objets. Sans la reprise, la détection d'anime (genre 16 + mot-clé 210024) ne verrait jamais rien sur le chemin tmdb:<id> — tout anime servi en tmdb:<id> aurait été cherché comme une série, donc par les mauvais mots-clés.
  • L'IMDb id. external_ids entre dans l'append_to_response du film, que l'amont ne demande que sur le chemin tmdb:. Chez nous il est demandé dans les deux cas : c'est la même requête, et c'est le seul endroit où TMDB peut livrer un identifiant que le catalogue ne connaissait pas — un titre lié à une fiche IMDb depuis la dernière visite redevient lisible par les sources qui l'exigent. L'IMDb id connu du /find reste prioritaire.
  • L'année. Elle venait du résultat de /find ; à défaut, elle vient de la fiche. Un /find sans release_date ne coûte plus un filtre d'année dans la recherche.

Un piège, tenu : TMDB rend external_ids: null sur certaines fiches. Les deux constructeurs lisent la clé à travers un or {} — sans quoi toute la fiche part en erreur et l'addon rend zéro flux, exactement le symptôme qu'on corrige. Un identifiant tmdb: qui n'est pas un entier est refusé avant toute requête, et le parseur retombe alors sur le découpage générique : l'échec reste celui d'aujourd'hui, il n'y en a pas de nouveau.

Porte

Le chemin /find n'a pas bougé : deux requêtes, dans le même ordre, et les deux constructeurs rendent exactement les mêmes clés que les deux return d'avant — vérifié par lecture de l'arbre, pas à l'œil.

Rouge avant, vert après. Base 21dd7be, suite neuve seule, code d'avant :

$ python3 -m pytest -q tests/test_tmdb_sans_imdb.py
39 failed, 4 passed in 1.69s

Les 4 qui passent déjà sont les cas du chemin /find : ce sont des verrous de non-régression, pas des témoins du correctif. Après, sur l'arbre fusionné avec main (qui porte les six cas de FOXA-2842) :

$ python3 -m pytest -q
532 passed, 1 warning in 6.13s
$ python3 -m pytest --collect-only -q
532 tests collected in 0.80s

Écart 31 / 489 → 32 / 532 : +1 suite, +43 tests, aucun retrait. Les 43 cas de tests/test_tmdb_sans_imdb.py couvrent les trois formes (tt…, tmdb:, kitsu:) côte à côte, un film et une série résolus sans /find, l'anime lu sur la fiche, external_ids: null, le jeton vide et l'identifiant non numérique qui ne posent aucune requête, la fiche introuvable et la panne réseau qui rendent None sans lever, et l'aiguillage de get_streams avec la bonne saison et le bon épisode. Cinq assertions de test_validators.py complétées de la clé tmdb_id ; rien d'autre n'y change.

La référence

main a avancé entre le premier jet et cette fusion : la référence passe à 32 / 532, et l'écart honnête est 489 + 43, pas 483 + 43. Les deux Blocs de conflit (CLAUDE.md, JOURNAL.md) sont résolus à la main, les trois valeurs dans l'ordre, l'entrée de FOXA-2842 replantée avant la mienne.

Un film ou une série **trop récent, ou trop niche**, n'a pas encore de fiche IMDb sur TMDB. Le catalogue (AIOMetadata) sert alors un `tmdb:<id>` au lieu d'un `tt…` — et `extract_media_info` ne connaissait pas cette forme. ## Le défaut, tel que mesuré ``` tmdb:335292:1:5 (série) → imdb_id = "tmdb" ← le littéral, pris pour un IMDb id season = "335292" ← l'identifiant, pris pour une saison episode = "1" ← décalé d'un cran ``` `/find` — qui exige un IMDb id — échouait toujours, et `stream.py:610` rendait `[]`. **Zéro flux, sans erreur visible**, sur un titre que le catalogue servait correctement. Le chemin est vivant, pas du code mort : `wastream/utils/validators.py:141` (le parseur) → `wastream/services/stream.py:586` (appel) → `wastream/services/stream.py:605` (résolution TMDB par `imdb_id`). ## Ce qui change 1. **Une forme `tmdb:` dédiée** dans `extract_media_info`. Elle sort `tmdb_id`, jamais `imdb_id` ; les quatre branches rendent désormais la même clé `tmdb_id`, à `None` pour `tt…` et `kitsu:`. Un film reste sans saison ni épisode, comme la forme `tt…` d'un film ; c'est une série qui prend `saison:episode` à partir de la troisième et quatrième partie. 2. **La résolution par identifiant TMDB, sans `/find`** : `get_enhanced_metadata_by_tmdb_id` / `get_metadata_by_tmdb_id` dans `wastream/services/tmdb.py`, `_get_metadata_by_tmdb_id` dans `wastream/services/stream.py`, et l'aiguillage dans `get_streams`. L'amont `dydy13014/wacustom` (`27bb2b9`, 2026-10-01) prend la même direction et extrait les mêmes deux constructeurs. **On ne recopie pas son patch** : la résolution est écrite dans notre structure, et elle est plus complète sur trois points. - **L'anime.** `genre_ids` n'existe que sur un résultat de recherche ; la fiche `/tv/{id}` rend `genres`, une liste d'objets. Sans la reprise, la détection d'anime (genre 16 + mot-clé 210024) ne verrait jamais rien sur le chemin `tmdb:<id>` — tout anime servi en `tmdb:<id>` aurait été cherché comme une série, donc par les mauvais mots-clés. - **L'IMDb id.** `external_ids` entre dans l'`append_to_response` du **film**, que l'amont ne demande que sur le chemin `tmdb:`. Chez nous il est demandé dans les deux cas : c'est la même requête, et c'est le seul endroit où TMDB peut livrer un identifiant que le catalogue ne connaissait pas — un titre lié à une fiche IMDb depuis la dernière visite redevient lisible par les sources qui l'exigent. L'IMDb id connu du `/find` reste prioritaire. - **L'année.** Elle venait du résultat de `/find` ; à défaut, elle vient de la fiche. Un `/find` sans `release_date` ne coûte plus un filtre d'année dans la recherche. **Un piège, tenu** : TMDB rend `external_ids: null` sur certaines fiches. Les deux constructeurs lisent la clé à travers un `or {}` — sans quoi toute la fiche part en erreur et l'addon rend zéro flux, exactement le symptôme qu'on corrige. Un identifiant `tmdb:` qui n'est pas un entier est refusé **avant** toute requête, et le parseur retombe alors sur le découpage générique : l'échec reste celui d'aujourd'hui, il n'y en a pas de nouveau. ## Porte Le chemin `/find` n'a pas bougé : deux requêtes, dans le même ordre, et les deux constructeurs rendent **exactement** les mêmes clés que les deux `return` d'avant — vérifié par lecture de l'arbre, pas à l'œil. **Rouge avant, vert après.** Base `21dd7be`, suite neuve seule, code d'avant : ``` $ python3 -m pytest -q tests/test_tmdb_sans_imdb.py 39 failed, 4 passed in 1.69s ``` Les 4 qui passent déjà sont les cas du chemin `/find` : ce sont des verrous de non-régression, pas des témoins du correctif. Après, sur l'arbre **fusionné avec `main`** (qui porte les six cas de `FOXA-2842`) : ``` $ python3 -m pytest -q 532 passed, 1 warning in 6.13s $ python3 -m pytest --collect-only -q 532 tests collected in 0.80s ``` Écart **31 / 489 → 32 / 532** : +1 suite, +43 tests, aucun retrait. Les 43 cas de `tests/test_tmdb_sans_imdb.py` couvrent les trois formes (`tt…`, `tmdb:`, `kitsu:`) côte à côte, un film et une série résolus sans `/find`, l'anime lu sur la fiche, `external_ids: null`, le jeton vide et l'identifiant non numérique qui ne posent aucune requête, la fiche introuvable et la panne réseau qui rendent `None` sans lever, et l'aiguillage de `get_streams` avec la bonne saison et le bon épisode. Cinq assertions de `test_validators.py` complétées de la clé `tmdb_id` ; rien d'autre n'y change. ## La référence `main` a avancé entre le premier jet et cette fusion : la référence passe à **32 / 532**, et l'écart honnête est `489 + 43`, pas `483 + 43`. Les deux Blocs de conflit (`CLAUDE.md`, `JOURNAL.md`) sont résolus à la main, les trois valeurs dans l'ordre, l'entrée de `FOXA-2842` replantée avant la mienne.
Un film ou une série trop récent, ou trop niche, n'a pas encore de fiche
IMDb sur TMDB : le catalogue (AIOMetadata) sert alors un `tmdb:<id>` au
lieu d'un `tt…`. `extract_media_info` ne connaissait pas cette forme —
le découpage générique prenait le littéral `tmdb` pour un IMDb id,
décalait saison et épisode d'un cran, et `/find` (qui exige un IMDb id)
échouait toujours. L'addon rendait zéro flux pour ces titres.

Une forme `tmdb:` dédiée dans le parseur, et une résolution TMDB directe
par identifiant (`get_enhanced_metadata_by_tmdb_id`,
`get_metadata_by_tmdb_id`) qui interroge la fiche sans passer par `/find`.
L'amont `dydy13014/wacustom` (27bb2b9) prend la même direction ; on ne
recopie pas son patch, on écrit la résolution dans notre structure.

Trois points où la notre est plus complète :

  - `genre_ids` n'existe que sur un résultat de recherche. La fiche
    `/tv/{id}` rend `genres` ; sans la reprise, la détection d'anime
    (genre 16 + mot-clé 210024) ne verrait jamais rien sur le chemin
    `tmdb:<id>` et tout anime y serait cherché comme une série.
  - `external_ids` entre dans l'`append_to_response` du film, qui ne le
    demandait pas : c'est le seul endroit où TMDB peut livrer un IMDb id
    que le catalogue ne connaissait pas (titre lié depuis la dernière
    visite). Aucun surcoût — c'est la même requête.
  - l'année d'un film ou d'une série vient de la fiche quand le résultat
    de recherche n'en porte pas ; la perdre ne coûtait qu'un filtre
    d'année en moins dans la recherche.

`external_ids: null` est une réponse réelle de TMDB : les deux builders
tolèrent la clé nulle. Un identifiant `tmdb:` qui n'est pas un entier
retombe sur le découpage générique — donc sur l'échec d'aujourd'hui, pas
sur un nouveau. Le chemin `/find` n'a pas bougé : deux requêtes, dans
l'ordre, mêmes clés rendues.

Écart : 31 suites / 483 tests → 32 suites / 526 tests, 0 échec.
`tests/test_tmdb_sans_imdb.py` (43 cas) fige les trois formes
(`tt…`, `tmdb:`, `kitsu:`) côte à côte, la résolution directe, et
l'aiguillage de `get_streams`. Les quatre cas du chemin `/find` passent
aussi sur le code d'avant : ce sont des verrous de non-régression.
32 suites / 526 tests, mesurés sur `f0a9fe4`. La valeur précédente (31 /
483, `52438ea`) passe en « valeur précédente » plutôt que d'être effacée.
Récit de l'écart dans `JOURNAL.md`, comme pour chaque passe.
Merge de 'main' dans la branche de FOXA-2841, et la référence reprise sur l'arbre fusionné
All checks were successful
Porte / porte (pull_request) Successful in 13s
Porte / porte (push) Successful in 12s
4cce035f6b
FOXA-2842 a été fusionné dans `main` entre le premier jet et celui-ci : les
six cas de `test_torznab_relevance.py` sont entrés dans l'arbre. La référence
n'est donc plus 32 / 526 — elle est 32 / 532, et l'écart honnête est
489 + 43, pas 483 + 43. Les deux suites se recoupent dans `CLAUDE.md` comme
dans `JOURNAL.md`, où l'entrée de FOXA-2842 est replantée avant la mienne.

Les deux Blocs de conflit sont résolus à la main : `CLAUDE.md` porte les
trois valeurs dans l'ordre (32 / 532, puis 31 / 489, puis 31 / 483),
`JOURNAL.md` garde les deux récits séparés.

Porte : 532 tests collectés, 532 passés, 0 échec.
foXaCe merged commit 4cce035f6b into main 2026-10-02 08:45:28 +02:00
foXaCe deleted branch agent/opencode-gratuit/ec44c9114e04 2026-10-02 08:45:28 +02:00
Sign in to join this conversation.
No reviewers
No labels
montee-majeure
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
foXaCe/wacustom!37
No description provided.