Files
NewTube/README.md
T
bruno 5f1bb78c3b
CI / build-and-test (push) Successful in 15m0s
docs(readme): roadmap restructuré (livré / partiel / à faire) + plan des nouveaux fournisseurs
- Légende ✅/🟡/⏳ et section « Pas encore démarré » regroupant les vrais restes
- Abonnements et page Shorts unifiée passés en ✅ (vérifiés câblés: routes /api/subscriptions + groupes, flux /shorts 6 fournisseurs)
- Partiellement livré: tags, observabilité (healthz+metrics faits, Admin non), cache TTL, qualité vidéo, i18n
- Matrice fournisseurs: Shorts ✅ pour Dailymotion/PeerTube/Odysee/Rumble, Twitch = Clips, notes de lecture (filtres durée par provider, rumble natif)
- Nouvelle section candidats Kick/Vimeo/SoundCloud/TikTok + chemin d'implémentation standard en 8 étapes
- Dédoublonnage du doublon « Recherche unifiée », cross-référence avec « Ajouter un provider en 5 minutes »
2026-10-02 17:25:27 -04:00

28 KiB
Raw Blame History

NewTube — Votre hub vidéo multi-plateformes 🎬🌐

Agrégez, explorez et regardez des vidéos depuis

  • YouTube 🔴
  • Dailymotion 🔵
  • Twitch 🟣
  • PeerTube 🟢 (multi-instances)
  • Odysee 🟡
  • Rumble 🟠

✨ Pourquoi NewTube ? (Objectifs clés)

  • 🔎 Recherche unifiée & tendances : un seul champ de recherche, des sections par fournisseur, filtres cohérents.
  • 🧭 Navigation claire : thèmes (Trending, Live, Gaming, News…), onglets Shorts, pages Playlists/History/Liked.
  • 👥 Multi-utilisateur : playlists publiques/privées, préférences, région/qualité par défaut.
  • ⚙️ Prod-ready : API Node/Express, SQLite intégré, rate-limit, logs, tests ciblés.
  • 📦 Ops friendly : image Docker, script maj.sh, variables d’env, astuces daemon.json pour registres HTTP.

🧩 Stack

  • 🅰️ Angular 20 (standalone), RxJS, TailwindCSS
  • 🟩 Node/Express (sert dist/ + API)
  • 🐳 Docker/Compose (déploiement)
  • 💾 SQLite (par défaut) – simple, portable

🎥 Fournisseurs supportés

Plateforme Recherche Lecture Shorts Live Playlists*
YouTube 🔴 ✅ ✅ ✅ ✅ ✅
Dailymotion 🔵 ✅ ✅ ✅ ✅ ⏳
Twitch 🟣 ✅ ✅ Clips ✅ ✅ ⏳
PeerTube 🟢 (multi-instances) ✅ ✅ ✅ ⏳ ⏳
Odysee 🟡 ✅ ✅ ✅ ⏳ ⏳
Rumble 🟠 ✅ ✅ ✅ ⏳ ⏳

* Playlists = intégration locale NewTube (création/gestion) ; la synchro native (ajout/suppression côté fournisseur) dépend de l’API publique de chaque fournisseur → ⏳ pour tous sauf YouTube.

Notes sur les colonnes :

  • Shorts : le flux /#/shorts tourne sur les 6 fournisseurs — catalogue /api/search?type=shorts (lot affiché s’il compte ≥ 3 résultats), sinon repli recherche avec requêtes dédiées (SHORTS_QUERIES). Filtre de durée côté serveur : YouTube (videoDuration=short), Dailymotion (shorter_than), PeerTube (durationMax) ; filtre client pour Odysee et Rumble ; Rumble : flux natif rumble.com/shorts ; Twitch : pas de format vertical natif, le flux sert les clips.

  • Live : lecture des streams live prouvée pour YouTube, Dailymotion et Twitch ; PeerTube / Odysee / Rumble ⏳ (voir « Fournisseurs existants — lacunes à combler »).

  • Rumble 🟠 : scraping maison derrière Cloudflare (fetch navigateur + helper curl_cffi). Si le réseau du serveur est challengé, l’API répond 503 { error: "rumble_cloudflare_challenge" } (« bloqué temporairement ») au lieu d’un résultat vide : Rumble peut donc être temporairement absent selon le réseau — voir server/providers/rumble.mjs.


🔎 Recherche unifiée (multi-providers)

Un seul champ, tous les fournisseurs — avec filtres, raccourcis et deep-links :

  • Panneau de filtres : bouton Filtres (ou Ctrl/⌘+Maj+F) — un seul écran pour toutes les dimensions : sources (multi), type de contenu (vidéos / shorts-clips / en direct / chaînes), période (dernière heure, aujourd’hui, semaine, mois, année), durée (courte < 4 min, moyenne 4-20 min, longue > 20 min) et tri. Pastilles, roving tabindex (←/→ dans un groupe, ↑/↓ entre groupes, Entrée pour choisir, Échap pour fermer), focus trap, « Réinitialiser », « Mémoriser comme sources par défaut » et les recherches récentes en raccourci
  • Filtres appliqués côté serveur : ?type=…&duration=…&period=…&sort=… voyagent dans l’URL et dans /api/search — YouTube filtre nativement (InnerTube upload_date/type/duration/features, Data API type/videoDuration/publishedAfter, yt-dlp --dateafter), les autres providers sont affinés en post-traitement (server/search-filters.mjs)
  • Opérateurs en clair : linux live:, tuto today: long:, concert shorts:… tapés dans la barre sont convertis en filtres et retirés de la requête (autocomplétion après le :)
  • Autocomplete @ : tapez @yt dans le champ pour cocher/décocher une source (↑/↓/Entrée, Échap pour fermer) — raccourcis Alt+1..6
  • Deep-links : /#/search?q=…&providers=yt,ru&period=week&type=live relance la recherche filtrée — partageable
  • Fallback préférence : URL sans providers → préférence defaultProviders de l’utilisateur → provider actif
  • Accessibilité : focus trap dans le panneau, Esc pour fermer, aria-combobox + aria-activedescendant sur le champ, focus restauré à la fermeture
  • Panneau de suggestions : sous l’input, ligne « Rechercher <q> » puis suggestions (recherches récentes 🕘 + groupes par provider YT/DM/…, sous-chaîne surlignée) — GET /api/search/suggest?q=…&providers=…&limit=… (min 2 caractères, debounce 250 ms, cache 5 min, dégradation [] par provider) ; ↑/↓ (bouclants), Home/End, PageUp/PageDown, Entrée (valide la ligne surlignée, sinon lance la recherche), Tab (complète sans chercher), Échap (ferme puis vide), priorité au popover @
  • Focus : le panneau se referme dès que le focus quitte la barre (focusout + relatedTarget, clic neutralisé sur les lignes pour garder le focus dans l’input, pas de scintillement au re-clic)

Endpoints API concernés

  • GET /api/search?q=…&providers=yt,dm&type=live&duration=short&period=week&sort=date — fan-out parallèle, réponse groupée par provider (page/pageSize/sort + filters ; YT sans quota via InnerTube + continuations)
  • GET /api/search/suggest?q=…&providers=yt,dm&limit=10 — typeahead { q, groups: { yt: string[], … } } (cache 5 min, rate-limit)
  • GET /api/details/youtube/:videoId — métadonnées + related[] (watch-next InnerTube, ?related=0 pour désactiver)
  • GET /api/trending?provider=yt&limit=… — tendances YT sans clé
  • GET /healthz (alias /api/healthz) — mode YT, binaire yt-dlp binOk, cache, métriques quota/jour, clés
  • GET /api/transcript/:provider/:videoId?lang=&instance=&slug=&sourceUrl= — transcript { lang, available, languages, lines: [{ t, dur, text }] } (cache 24 h, rate-limit 10/min ; absent → 200 { available: false }, échec → 502 ; YouTube : découverte des pistes via InnerTube, YT_TRANSCRIPT_SOURCE)
  • GET/PATCH /api/user/preferences — defaultProviders (tableau JSON, sanitizé serveur)
  • POST /api/telemetry/events — événements UX anonymes (whitelist : search_submit, provider_picker_open, provider_apply, at_autocomplete_use, quick_menu_open, filter_panel_open, filter_apply, suggest_shown, suggest_used)

Tests

npm run test:search        # unitaires SearchService + clavier/focus du panneau de suggestions + panneau de filtres
npm run test:search-e2e    # scénarios e2e (serveur réel isolé)
npm run test:filters       # filtres de recherche : normalisation, bornes, mapping providers (offline)
npm run test:suggest        # typeahead : parsing/dédup + contrat /api/search/suggest
npm run test:transcript     # transcripts : parseurs json3/vtt + contrat /api/transcript
npm run test:preferences   # persistance defaultProviders
npm run test:telemetry     # télémétrie minimale

🖥️ Fonctionnalités (vue d’ensemble)

  • 🧭 Accueil “Tendances & Viral” par fournisseurs
  • 🧩 Thèmes : Trending, Live, Gaming, Sports, News, Finance, Tech, Science, Health, Music, Podcasts, Movies/TV, Education, Travel, Food, DIY, Auto…
  • 🎯 Shorts : affichage dédié (séparé des vidéos longues)
  • ❤️ Liked videos avec recherche serveur
  • ⏰ À regarder plus tard (Watch later) : bouton horloge à côté du cœur sur toutes les cartes (accueil, thèmes, recherche), liste Vous → À regarder plus tard (/#/library/watch-later), API /api/user/watch-later
  • 🕘 History (recherches + visionnage)
  • 📚 Playlists publiques/privées (CRUD, compteurs, dates MAJ)
  • 🔐 Auth légère (JWT), rate-limit API
  • ⚙️ Préférences : langue, thème (système / dark / light / blue / black), région, qualité par défaut
  • 🧩 PeerTube multi-instances : activer/désactiver, choisir l’instance active

🗂️ Structure du repo

  • docker-compose/

    • docker-compose.yml — service newtube (image, ports, env, volumes, restart)
    • .env.example — modèle d’environnement
    • maj.sh — pull image + restart stack
    • init.sh — init des dossiers/volumes & .env
  • docker/

    • Dockerfile — build Node/Express (sert dist/ + API)
    • scripts/env-dump.sh — génère assets/config.js depuis l’env (option NGINX)
    • config/nginx.conf — exemple (si variante NGINX)
  • server/

    • index.mjs — routes API, statiques dist/, downloads
    • db.mjs — SQLite + migrations légères
    • tests/playlist_visibility.test.mjs — tests playlists
  • db/ — schema.sql + migrations/

  • assets/ — config.local.example.js

  • src/, app/, public/ — front Angular

  • package.json — scripts (dev, build, api, api:watch, test:playlists)


🧱 Prérequis

  • 🐧 Linux (recommandé) / macOS / Windows (WSL2 ok)
  • 🐳 Docker Engine ≥ 24, Compose v2
  • 🟩 Node.js ≥ 20 (dev local)
  • 🔐 Accès au registre d’images (ex. docker-registry.dev.home:5000)

🚀 Installation

Option A — Docker (recommandé)

cp docker-compose/.env.example docker-compose/.env
# Éditez docker-compose/.env (hostnames, clés API, secrets…)
cd docker-compose
docker compose up -d

Application : http://localhost:8080 (mappage 8080:4000)

Option B — Dev local

npm install
cp assets/config.local.example.js assets/config.local.js   # (optionnel)
npm run dev                  # front + api dev proxy
# API seule :
npm run api
npm run api:watch
# Build prod :
npm run build

🔁 Déploiement & mises à jour (maj.sh)

chmod +x docker-compose/maj.sh
./docker-compose/maj.sh

Ce script fait :

  1. docker image pull <registre>/newtube-angular:latest
  2. docker compose down
  3. docker compose up -d

Vérifications : docker compose ps, docker logs newtube --tail=200, curl -I http://localhost:8080


🧰 Astuce Ops — Docker /etc/docker/daemon.json (registres HTTP/insecure)

À configurer sur la machine qui exécute docker compose / maj.sh.

sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json >/dev/null <<'JSON'
{
  "insecure-registries": ["docker-registry.dev.home:5000"],
  "registry-mirrors": [],
  "debug": false
}
JSON
# debian linux
sudo systemctl daemon-reload
sudo systemctl restart docker
# Alpine linux
sudo rc-service docker restart
sudo rc-update add docker

docker info | grep -i registry

Ajoutez au besoin log-driver, log-opts, default-address-pools, etc.


🔐 Variables d’environnement (exemples)

Service / compose

  • NGINX_HOSTNAME, DIR_NEWTUBE, TZ

App / serveur

  • PORT (4000), NODE_ENV, JWT_SECRET
  • ACCESS_TTL_MIN, REFRESH_TTL_DAYS, REMEMBER_TTL_DAYS
  • YT_CACHE_TTL_MS
  • Clés API : GEMINI_API_KEY, YOUTUBE_API_KEY ou YOUTUBE_API_KEYS (CSV), VIMEO_ACCESS_TOKEN, TWITCH_CLIENT_ID, TWITCH_CLIENT_SECRET
  • NEWTUBE_DB_FILE (chemin SQLite alternatif)

Voir docker-compose/.env.example pour un point de départ.


🩺 Troubleshooting (rapide)

  • 🧾 Certif/registre : x509… unknown authority → configurez daemon.json (ci-dessus) puis systemctl restart docker
  • 🔌 Port 8080 occupé : changez le mappage dans docker-compose.yml
  • 🗄️ Permissions volumes : vérifiez que DIR_NEWTUBE existe et est accessible par Docker
  • 🧩 Variables manquantes : complétez .env (clés API, JWT_SECRET, etc.)
  • 🌐 CORS en dev : utilisez le proxy.conf.json et npm run dev

🗺️ Roadmap

Légende : ✅ livré (câblé de bout en bout) · 🟡 partiellement livré (détail sous chaque item) · ⏳ pas encore démarré

✅ Livré

  • ✅ Dist statique + API Node/Express
  • ✅ Playlists publiques/privées (tests de visibilité)
  • ✅ Barre de recherche Liked videos (filtrage serveur)
  • ✅ Liste « À regarder plus tard » (watch later) — même stockage « tags » que les likes (aucune migration), bouton horloge à côté du cœur sur toutes les cartes, entrée « Vous » de la barre latérale, page /#/library/watch-later avec recherche serveur
  • ✅ PeerTube multi-instances (activer/désactiver, set active)
  • ✅ Thème (système / dark / light / blue / black)
  • ✅ Harmonisation visuelle — tous les utilitaires neutres (slate/zinc/gray, fonds, textes, bordures, anneaux, placeholders, états hover:/disabled:) sont mappés sur les variables du thème (75 jetons, 36 fichiers) : barre de recherche, menus, panneaux et cartes suivent enfin le thème appliqué ; panneau ⓘ des Shorts re-colorié dans son périmètre (le chrome au-dessus de la vidéo reste blanc) ; marqueur border-l-4 des titres de page sur l'accent du thème ; cartes vidéo harmonisées (text-sm / font-semibold / méta text-xs / rayon rounded-lg, pastille de durée sans font-mono) ; contour fournisseur 1 px à la couleur de la pastille autour de chaque cadre vidéo (grilles, recherche, channel, watch — lecteur et « à suivre » —, shorts, historique, aimés, playlist)
  • ✅ Préférences (langue, thème, région, qualité par défaut)
  • ✅ Auth légère (JWT), rate-limit API
  • ✅ Recherche unifiée multi-providers — panneau de filtres (sources / type / période / durée / tri, Ctrl/⌘+Maj+F), autocomplete @ + opérateurs live:/today:, deep-links ?providers=…&type=…&period=…, préférence defaultProviders persistée, filtres serveur, télémétrie minimale, a11y (focus trap, Esc, clavier complet sur le panneau de suggestions)
  • ✅ Filtres & tri unifiés — barre partagée [Filtres] [Tri] sur la page de recherche ET sur les pages thèmes (pastilles de type, ligne Sources Toutes / Aucune, période / durée / langue, filtres actifs en pastilles) ; bouton « filtres rapides » supprimé, tout est sous [Filtres]. Tri : Pertinence, Date de mise en ligne, Vues, Note (likes), Durée (les deux derniers réordonnent côté client)
  • ✅ Navigation par thèmes (Trending, Live, Gaming, News, Finance, Tech, Science, Health, Music, Podcasts, Movies/TV, Education, Travel, Food, DIY, Auto…)
  • ✅ Abonnements (routes + DB) — GET/POST/DELETE /api/subscriptions (+ POST /api/subscriptions/batch), groupes façon PocketTube (/api/subscription-groups CRUD + affectation de membres), tables SQLite subscriptions / groupes, page /#/library/subscriptions, bouton « S'abonner » partagé (subscribe-button) + entrée barre latérale ; alimentés par l'import OAuth (YouTube/Twitch). Tests : npm run test:subscriptions
  • ✅ Page « Shorts » unifiée (tous fournisseurs) — flux immersif /#/shorts sur les 6 fournisseurs (catalogue /api/search?type=shorts sinon repli recherche, dédup centralisée), durées : filtres côté serveur (YouTube videoDuration=short, Dailymotion shorter_than, PeerTube durationMax) + filtre client ailleurs, pastille de durée sur les cartes de la recherche filtrée et durée affichée dans le panneau ⓘ, clips Twitch, mode « Pour toi »
  • ✅ 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 et vues demandées à la source à l'ouverture via /api/details — squelette de chargement, échec silencieux —, 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)
  • ✅ Sous-titres & transcripts — GET /api/transcript/:provider/:videoId (yt-dlp subtitles/automatic_captions, parsing json3/vtt, cache 24 h, rate-limit), panneau Transcript sur la page Watch (sélecteur de langue, dégradation propre si indisponible)
  • ✅ YouTube sans quota (InnerTube façon SmartTube) — youtubei.js pinné, chaîne innertube → scrape yt-dlp → API officielle (YT_SEARCH_MODE, défaut innertube-first), pagination illimitée via continuations (scroll infini, page=2,3…), vidéos connexes watch-next dans GET /api/details/youtube/:videoId → sidebar Watch, GET /api/trending?provider=yt, cache mémoire + SQLite, anti-ban (YT_COOKIES_FILE, YT_PO_TOKEN, YT_EGRESS_PROXY), observabilité /healthz (mode, binaire binOk, cache, quota jour, clés)
  • ✅ OAuth (Google/Twitch) pour import favoris/abonnements — /library/import : abonnements + likes YouTube (lecture seule), follows Twitch → abonnements, preview + import, GET /api/oauth/status|connections|preview, POST /api/oauth/:provider/import

🟡 Partiellement livré

  • 🟡 Tags — ✅ tables tags / video_tags (stockage des listes « Aimés » et « À regarder plus tard », recherche serveur dans ces listes) ; ⏳ tags libres créés par l'utilisateur sur n'importe quelle vidéo + recherche de vidéos par tag
  • 🟡 Observabilité — ✅ GET /healthz (mode YT_SEARCH_MODE, binaire yt-dlp, cache, quota du jour, clés masquées — testé par test:api) + GET /api/providers/metrics ; ⏳ page Admin (clés OK/KO, logs, versions) + exposition métriques au format Prometheus
  • 🟡 Cache serveur configurable — ✅ TTL réglable par variable d'environnement sur tous les caches, la recherche étant déjà par provider (SEARCH_CACHE_TTL_MS_<PROVIDER> / SEARCH_CACHE_TTL_MS_DEFAULT), plus YT_CACHE_TTL_MS, YT_RELATED_TTL_MS, DETAILS_CACHE_TTL_MS, TRANSCRIPT_CACHE_TTL, SUGGEST_CACHE_TTL_MS, CHANNEL_TTL_MS ; ⏳ étendre le TTL par provider aux caches details / transcripts / suggest (aujourd'hui un seul TTL global chacun)
  • 🟡 Qualité vidéo — ✅ sélecteur de qualité sur /#/watch (formats yt-dlp, tri décroissant 1080p → 144p, reprise de la lecture sur le flux choisi) ; ⏳ mode « auto » intelligent (choix automatique du flux selon la bande passante)
  • 🟡 Traduction UI (i18n) — ✅ socle FR/EN (src/services/i18n.service.ts, chaque clé déclarée des deux côtés) couvrant barre de recherche, barre de filtres, pages thèmes ; ⏳ couverture élargie aux écrans encore en texte FR hardcodé (/watch, /shorts, bibliothèque)

⏳ Pas encore démarré

  • ⏳ Import/Export playlists (JSON / OPML-like)
  • ⏳ PWA (installable, offline cache des métadonnées)
  • ⏳ Chromecast / AirPlay
  • ⏳ Mode « TV »
  • ⏳ Theming avancé (polices, densité, accents)
  • ⏳ Fournisseurs existants — lacunes à combler (détail dans la matrice en haut) : lecture Live sur PeerTube, Odysee, Rumble ; playlists natives (synchro côté fournisseur) pour Dailymotion, Twitch, PeerTube, Odysee, Rumble
  • ⏳ Nouveaux fournisseurs — plan détaillé ci-dessous

🆕 Nouveaux fournisseurs — plan d'implémentation

Candidats (par rapport / effort ; endpoints et quotas à re-valider au démarrage de chacun) :

  1. Kick — API JSON publique (sans quota clé à confirmer), modèle identique à Twitch (live / vod / clip) : recherche + métadonnées via l'API, lecture via l'embed officiel, feature-flag FF_KICK. Priorité haute : seul candidat avec un vrai live concurrent de Twitch.
  2. Vimeo — recherche et détails via l'API Data (clé de développeur), formats/téléchargement via yt-dlp (extracteur Vimeo mature), lecture via l'embed officiel player.vimeo.com.
  3. SoundCloud — recherche via yt-dlp, lecture via le widget iframe officiel ; audio seul → carte avec pastille « audio », hors flux Shorts.
  4. TikTok — recherche via yt-dlp + embed officiel ; risque élevé (anti-bot de la même classe que Cloudflare/Rumble) : ne commencer qu'après les 1-3, avec contrat d'erreur strict (503 = challenge, 404 = introuvable prouvé — jamais l'inverse).

Chemin d'implémentation standard (identique pour chaque fournisseur, c'est celui des 6 existants ; la procédure détaillée côté recherche/filtres est aussi dans « Ajouter un provider en 5 minutes » en fin de README) :

  1. IDs & URLs — id court dans src/app/shared/providers/provider-ids.ts + couleur du badge fournisseur ; brancher providerUrlFrom() (server/index.mjs) — jamais d'URL provider écrite à la main ; transmettre tels quels instance / slug / sourceUrl.
  2. Métadonnées — privilégier l'API native du provider quand elle existe (modèle Odysee : resolve LBRY, ~0,2 s), sinon tuyau yt-dlp youtubedl(url, {dumpSingleJson, skipDownload}) qui alimente gratuitement /api/details/:provider/:videoId et /api/download/:provider/:videoId/formats.
  3. Recherche — server/providers/<p>.mjs = un seul cœur fetch + parse ; enregistrer search() dans registry.mjs (fan-out), les chaînes dans channel-registry.mjs ; rate-limit + cache dans un routeur fin dédié si besoin (modèle server/rumble.mjs). Ne jamais créer un second parseur.
  4. Capacités & robustesse — déclarer suggest / live / channelMeta / channelContent dans le bloc capacités de registry.mjs ; feature-flag FF_<ID> automatique via feature-flags.mjs ; dégradation toujours 200 + available: false + état « pas de données » dans l'UI, jamais une erreur ; contrat d'erreur net : 404 = introuvable prouvé (HTML reçu), 503 = blocage/rate-limit.
  5. Shorts — requêtes dédiées dans SHORTS_QUERIES + filtre de durée (serveur si l'API le supporte, client sinon), branche catalogue type=shorts ; tout chemin d'alimentation du flux passe par dedup() (sinon feed vide en silence) ; cadre vidéo avec attribut data-video-provider (contour 1 px).
  6. UI — cartes, /watch et /shorts réutilisent les composants existants, aucun composant par fournisseur ; texte FR hardcodé sur /watch et /shorts (pas de clés i18n), ailleurs | t: 'repli FR' déclaré des deux côtés de src/services/i18n.service.ts (dictionnaire EN et dictionnaire FR).
  7. Tests — server/tests/<p>*.test.mjs hors ligne (fixtures HTML/JSON, zéro spawn yt-dlp, zéro appel réseau) + script test:<p> dans package.json ; passer aussi test:contract et test:shapes.
  8. Livraison — tests verts avant commit ; matrice fournisseurs + ce roadmap mis à jour (✅ uniquement une fois câblé de bout en bout) ; vérif navigateur desktop 1280×900 ET mobile 390×844 (dont /watch et /shorts, FAB menu visible sur tous les états) ; instance locale rebuildée, puis image prod docker/build-img.ps1 + déploiement docker/deploy-img.ps1.

🔒 Notes de sécurité

  • ❌ Ne jamais versionner de secrets réels
  • 🔑 Utiliser .env hors VCS + restrictions par referer côté fournisseurs
  • 🚧 Limiter l’exposition publique de l’API, activer rate-limit (déjà en place)
  • 🧪 Ajouter des tests e2e/units pour les zones critiques (auth, playlists, downloads)

🤝 Contribuer

Issues & PR bienvenues ! Proposez des connecteurs, des règles de parsing plus robustes ou des idées d’UX. Astuce : ouvrez une PR “Draft” tôt pour discussion/feedback.


📜 Licence

MIT (voir LICENSE)


💡 Tip produit : gardez l’UX simple — Thèmes fixes sous le header, Shorts séparés, CTA clairs (“View All”, “Add to playlist”), et utilisez les états vides (empty states) sur History/Liked/Playlists pour guider l’utilisateur.


🧩 Ajouter un provider en 5 minutes

Complète le plan général « 🆕 Nouveaux fournisseurs » du roadmap : cette section détaille le côté recherche / filtres / deep-link, le plan du roadmap couvre IDs, URLs, métadonnées, shorts, capacités et livraison.

  1. Front — Registry
  • Éditez src/app/core/providers/provider-registry.ts et ajoutez une entrée ProviderSpec avec:
    • id (ex. "yt"), displayName, shortLabel, icon, colorClass
    • supports (search/shorts/live/playlists)
    • buildSearchUrl(q) (facultatif côté front)
  1. Back — Handler
  • Créez un fichier server/providers/<provider>.mjs qui exporte default avec:
    • id, label
    • async search(q, { limit, page }) → Promise<Suggestion[]>
  • async search(q, { limit, page, sort, filters }) → Promise<Suggestion[]> (filters = { type, duration, period, sort } normalisé par server/search-filters.mjs ; ignoré par défaut, affiné en post‑traitement si besoin).
  • Enregistrez-le dans server/providers/registry.mjs pour être éligible au fan‑out /api/search.
  1. API — Recherche multi‑providers
  • L’endpoint GET /api/search?q=...&providers=yt,dm,ru&limit=... valide les providers et déclenche les recherches en parallèle (timeouts), puis retourne:
{
  "q": "documentary",
  "providers": ["yt","dm","ru"],
  "groups": {
    "yt": [{ "title": "...", "id": "...", "duration": 192, "isShort": false }],
    "dm": [],
    "ru": [{ "title": "...", "id": "...", "isShort": true }]
  }
}
  1. UI — Panneau de filtres
  • Le SearchBoxComponent (standalone) rend le champ + le bouton Filtres (SearchFilterPanelComponent : sources, type, période, durée, tri, recherches récentes) et les pastilles des filtres actifs.
  • Le composant émet (submitted) avec { q, providers, filters } et (filtersChange) avec les filtres seuls ; SearchService (RxJS state) porte filters$ et l’envoie à chaque adapter.
  • Le modèle de filtres est partagé : src/app/search/filters.ts (front) et server/search-filters.mjs (API) implémentent les mêmes règles.
  1. Deep‑link
  • Les URLs du type ?q=…&providers=yt,ru relancent la même recherche. Assurez-vous de propager le paramètre providers lors des navigations.
  1. Tests
  • Unit (npm run test:search) : parsing @yt, clavier/focus du panneau de suggestions, opérateurs live:, brouillon + apply du panneau de filtres, composition et fan-out de SearchService. npm run test:filters` : normalisation/bornes/post-filtrage/mapping providers (offline).
  • e2e (npm run test:search-e2e) : providers=yt,dm → réponse ciblée sur [yt,dm]; deep-link providers=pt; fallback registry complet; préférence persistée; route SPA.

Astuce: l’ajout d’un provider ne nécessite pas de modifier les composants — il suffit d’ajouter une entrée dans le registry front + un handler API.