bruno 1814334958
CI / build-and-test (push) Successful in 14m8s
fix(search): loading par frame sur les cartes, plus de cadres vides
- video-card: etat de chargement de la miniature (shimmer + spinner),
  fallback 'Apertu indisponible' avec retry, referrerpolicy no-referrer
- grid: eager sur les premieres cartes, bandeau de rafraichissement quand
  la grille est deja peuplee
- search: garde MIN_QUERY_LENGTH=2 (le loading partait pour une requete que
  request$ filtrait, grille figee sur des skeletons) + filet de securite 20s
- skeleton: ligne chaine pour coller a la hauteur reelle de la carte
2026-09-28 19:35:48 -04:00
2025-09-14 23:05:30 -04:00
2025-09-14 23:05:30 -04:00
2025-09-14 23:05:30 -04:00

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 🟣 ✅ ✅ N/A ✅ ⏳
PeerTube 🟢 (multi-instances) ✅ ✅ ⏳ ⏳ ⏳
Odysee 🟡 ✅ ✅ ⏳ ⏳ ⏳
Rumble 🟠 ✅ ✅ ⏳ ⏳ ⏳

* Playlists = intégration locale NewTube (création/gestion); la synchro native dépend de l’API publique de chaque fournisseur.


🔎 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
  • 🕘 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

  • ✅ Dist statique + API Node/Express
  • ✅ Playlists publiques/privées (tests de visibilité)
  • ✅ Barre de recherche Liked videos (filtrage serveur)
  • ✅ PeerTube multi-instances (activer/désactiver, set active)
  • ✅ Thème (système / dark / light / blue / black)
  • ✅ 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)
  • ✅ 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)
  • ✅ Navigation par thèmes (Trending, Live, Gaming, News, Finance, Tech, Science, Health, Music, Podcasts, Movies/TV, Education, Travel, Food, DIY, Auto…)
  • ⏳ Abonnements (routes + DB)
  • ⏳ Tags & recherche par tags
  • ⏳ Page “Shorts” unifiée (tous fournisseurs) + badges de durée
  • ✅ 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)
  • ✅ 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)
  • ⏳ PWA (installable, offline cache des métadonnées)
  • ⏳ Chromecast / AirPlay
  • ⏳ Mode “TV”
  • ⏳ Observabilité : healthcheck /healthz, métriques, page Admin (clés OK/KO, logs, versions)
  • ✅ 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
  • ⏳ Cache serveur configurable (TTL par provider)
  • ⏳ Qualité vidéo : sélecteur et “auto” intelligent
  • ⏳ Traduction UI (i18n) élargie
  • ⏳ Theming avancé (polices, densité, accents)

🔒 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

  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.

S
Description
Site Web permettant de centraliser les accès à plusieurs platformes de streaming.
Readme
10 MiB
Languages
TypeScript 44.6%
JavaScript 37.9%
HTML 14.7%
CSS 1.7%
Shell 0.7%
Other 0.2%