perf(details): Odysee par l'API LBRY + cache des métadonnées (45 s → 0,2 s)

Mesuré avant : `/api/details/odysee/…` = 44,7 s puis « No video formats
found! » — la page Watch restait une minute sans titre, vues, description ni
vignette. L'extracteur `lbry` de yt-dlp n'est pas utilisable ici.

- Nouvelle branche Odysee dans /api/details : `resolve` de l'API LBRY
  (`server/providers/odysee.mjs` → `resolveOdyseeVideo`) = 229 ms mesurés,
  titre/description/vignette/durée/date/canal + avatar. Claim introuvable →
  404 `odysee_claim_not_found` (le client garde les données du flux), panne →
  502 `odysee_details_failed`. Jamais de repli yt-dlp (45 s pour échouer).
- `views` volontairement OMIS pour Odysee : ni LBRY ni lighthouse n'exposent
  de compteur pour un claim isolé (l'`effective_amount` est un montant LBC).
- Cache mémoire de /api/details : 6 h, LRU 500 (`DETAILS_CACHE_TTL_MS`,
  `DETAILS_CACHE_MAX_ENTRIES`) — métadonnées immuables, chaque /watch
  relançait un dump complet.
- `views`/`duration` ne sont plus émis quand ils valent 0 : un 0 écrasait la
  valeur du flux côté client (merge sur `typeof === 'number'`), d'où les
  « 0 vue » sur les fournisseurs muets — même doctrine que
  `parseRumbleViews` (« jamais de compteur à 0 »).
- Mapping pur `odyseeClaimToVideo` testé hors ligne : 21 assertions,
  `npm run test:odysee`.
This commit is contained in:
2026-10-02 14:48:53 -04:00
parent 6f6d7ddde1
commit c45e04af42
5 changed files with 198 additions and 3 deletions
+2 -1
View File
@@ -254,7 +254,8 @@ Ajoutez au besoin `log-driver`, `log-opts`, `default-address-pools`, etc.
* ⏳ **Abonnements** (routes + DB)
* ⏳ **Tags** & recherche par tags
* ⏳ **Page “Shorts”** unifiée (tous fournisseurs) + badges de durée
* ✅ **UI/UX des Shorts** — le rail d'actions quitte les bords (les flèches latérales sont supprimées) et s'ancre au bord **droit** de la vidéo : **informations**, **flèches ↑/↓**, j'aime, s'abonner (pastille = avatar), commentaires, partager, **lecture auto** (interrupteur persisté, respecté par le minuteur ET la fin de vidéo YouTube) ; panneau **ⓘ** (titre, vues, engagement, chaîne, description, commentaires → Watch) en colonne à droite au bureau et en tiroir plein écran sur mobile, la vidéo rétrécissant sans jamais être masquée ; **transition glissée** d'un short à l'autre (le cadre suit le doigt, résistance aux extrémités, ressort sous 25 % de la hauteur puis éjection/emboîtement, `prefers-reduced-motion` respecté) ; FAB menu conservé (déplaçable, au-dessus de la vidéo). Le tap sur un contrôle du rail ne déclenche plus play/pause
* ✅ **UI/UX des Shorts** — le rail d'actions quitte les bords (les flèches latérales sont supprimées) et s'ancre au bord **droit** de la vidéo : **informations**, **flèches ↑/↓**, j'aime, **enregistrer** (liste « À regarder plus tard »), s'abonner (pastille = avatar), commentaires, partager, **lecture auto** (interrupteur persisté, respecté par le minuteur ET la fin de vidéo YouTube) ; panneau **ⓘ** (titre, vues, engagement, chaîne, description, renvoi vers la page complète — aucune source de commentaires dans l'app) en colonne à droite au bureau et en tiroir plein écran sur mobile, la vidéo rétrécissant sans jamais être masquée ; **transition glissée** d'un short à l'autre (le cadre suit le doigt, résistance aux extrémités, ressort sous 25 % de la hauteur puis éjection/emboîtement, `prefers-reduced-motion` respecté) ; FAB menu conservé (déplaçable, au-dessus de la vidéo). Le tap sur un contrôle du rail ne déclenche plus play/pause
* ✅ **Métadonnées Odysee sans yt-dlp** — `resolve` de l'API LBRY (**~0,2 s** contre **~45 s** mesurées avec l'extracteur `lbry` de yt-dlp, qui finissait sur « No video formats found! ») : titre, description, vignette, durée et date arrivent enfin sur `/watch` ; `views` volontairement omis (LBRY n'expose aucun compteur pour un claim isolé). Au passage : **cache `/api/details` 6 h** (LRU 500, `DETAILS_CACHE_TTL_MS`) et `views`/`duration` ne sont plus émis quand ils valent 0 (un 0 écrasait la valeur du flux : « 0 vue » sur les fournisseurs muets). Tests : `npm run test:odysee`
* ✅ **Téléchargements** intégrés — file d'attente **persistée en SQLite** (survit aux redémarrages API), jobs **par utilisateur** (répertoires isolés, ownership sur status/fichier/cancel), **quota de stockage** configurable (`DOWNLOAD_STORAGE_QUOTA_BYTES`, fenêtre `DOWNLOAD_QUOTA_WINDOW_MS`), **reprise** des jobs échoués/interrrompus (bouton Réessayer), **page Bibliothèque > Téléchargements** (filtres par état, progression live, quota), nettoyage auto des fichiers orphelins au boot
* 🔧 Variables : `DOWNLOAD_MAX_CONCURRENT` (2), `DOWNLOAD_STORAGE_QUOTA_BYTES` (5 GiB), `DOWNLOAD_QUOTA_WINDOW_MS` (30 j), `DOWNLOAD_PROVIDERS` (peertube,odysee)
* ⏳ **Import/Export** playlists (JSON / OPML-like)