Commit Graph
12 Commits
Author SHA1 Message Date
bruno e48d76be57 refactor(rumble): un seul cœur de scraping, routeur fin, contrat 503 CF
CI / build-and-test (push) Successful in 14m57s
- server/providers/rumble.mjs devient l'unique implémentation : fetch
  (cookie jar __cf_bm + curl_cffi + cooldown global 60 s après un échec
  total), parsing liste/vidéo avec surcouche JSON-LD partagée, vues via
  parseRumbleViews partout (« 1,2K » n'est plus lu en 12 sur /api/rumble).
- server/rumble.mjs = couche HTTP mince : limiter 20/min, cache 60 s,
  5 routes qui délèguent au cœur ; /search passe par le registre de
  providers (cache SQLite, negative cache et channelRef partagés avec
  /api/search).
- Contrat d'erreur : un blocage Cloudflare est un 503
  rumble_cloudflare_challenge, plus jamais un 404 (qui faisait retirer
  les vidéos du flux Shorts lors d'un challenge) ; page reçue sans
  identité = 404 rumble_video_not_found.
- Code mort supprimé : scrapeRumbleVideo d'index.mjs (191 l., 3e
  implémentation jamais appelée), rumbleLimiter 10/min jamais monté,
  import cheerio devenu inutile. Net -238 lignes.
- Tests : parseur générique (vues, durées, ids /shorts/), contrat 503
  sans réseau via le cooldown, durées absentes -> undefined.
- Docs : API_MCP_GUIDE §5.5 (routes, rate-limit réel, erreurs) + note
  README « Rumble peut être temporairement absent selon le réseau ».
2026-10-02 08:36:48 -04:00
bruno f6fdf8ee08 feat(filtres): barre [Filtres] [Tri] unique sur recherche et themes
CI / build-and-test (push) Successful in 14m54s
Nouveau composant partage app-filter-toolbar, utilise tel quel par la page
de recherche ET les deux pages de themes (meme visuel, seul le cablage
differe : URL pour la recherche, signaux locaux pour les themes).

- Boutons : [Filtres] (pastilles de type, ligne Sources, selects periode /
  duree / langue, filtres actifs) et [Tri] (Pertinence, Date de mise en
  ligne, Vues, Note (likes), Duree).
- Page de recherche : le bouton oeil « filtres rapides » et le panneau de
  selects disparaissent, tout est sous [Filtres] ; le tri a son bouton.
- Sources : boutons Toutes / Aucune. « Aucune » masque tout (la garde
  « derniere source masquable » est retiree) et un bandeau vide dit de
  reactiver au moins un fournisseur.
- /t/:theme : les 3 pastilles maison sont remplacees par la barre ; les
  sources masquent des blocs entiers ; filtres et tri appliques sur les
  blocs deja charges.
- /p/:provider/t/:theme : les 5 selects et les pastilles sont remplaces par
  la barre ; signaux maison unifies sur le modele SearchFilters (defaut
  relevance) ; les onglets Twitch lisent/ecrivent filters().type.
- Tri etendu : FilterSort + rating + duration. Les providers ne connaissent
  que relevance/date/views (serveur inchange, repli relevance) : Note et
  Duree sont reordonnes cote client via sortItems(), partage avec les pages
  themes.
- Communs : sortItems / itemViews / itemRating / matchesLanguage /
  formatCount extraits de filters.ts (3 copies d'heuristiques supprimees) ;
  cles i18n fr+en (filters.sort.rating, filters.sort.duration,
  filters.sortHint, filters.localHint, « Date de mise en ligne »).

Tests : test:search, test:filters, test:suggest, test:kind, test:section
verts + test:api 41/41, build ng OK.
2026-10-01 16:26:29 -04:00
bruno 735255b7d8 feat(library): liste « À regarder plus tard » (watch later)
CI / build-and-test (push) Successful in 14m55s
Nouvelle liste dans la section Vous de la barre latérale, avec un bouton
horloge a cote du coeur sur toutes les cartes (accueil, themes, provider-
themes, recherche).

- db : les 4 fonctions de listes (like/unlike/isLiked/list) prennent un
  paramètre `tag` — la table `tags` sert déjà de mot-clés génériques, donc
  zéro migration.
- api : les 4 routes likes sont générées par registerVideoTagRoutes(prefix,
  tagName) ; /api/user/watch-later = même fabrique, même enrichissement
  yt-dlp, même middleware cookie-aware. Entrée OpenAPI ajoutée.
- front : LikesService prend un VideoTag, like-button un @Input() list
  (icône horloge, ambre quand enregistré, libellés a11y dédiés) — un seul
  composant pour les deux boutons, pas de copie.
- cartes de recherche : la grille partagée n'avait AUCUN bouton ; cœur +
  horloge ajoutés, masqués sur les lives et cartes chaîne (coin bas-gauche
  déjà occupé par le compteur de spectateurs / pas des vidéos).
- route /library/watch-later = LikedComponent via data.list (titre, icône,
  placeholder et état vide pilotés par la route), clés nav.watchLater FR/EN.

Tests : npm run test:api 41/41 (nouveau test watch-later : ajout, statut,
indépendance vis-à-vis des likes, liste, retrait, 400, 401) + 21 autres
suites vertes. Build OK, instance locale 4200 rebuildée. Sondage Playwright
desktop+mobile : 12/14 (2 faux positifs du sondage : accordéon replié sur
une route hors /library, 3×401 console pendant la phase déconnectée).
2026-10-01 14:13:43 -04:00
bruno f1f673040e feat(search): panneau de filtres unifié, clavier/focus du typeahead
CI / build-and-test (push) Successful in 14m10s
Barre de recherche
- Panneau de filtres unifié (remplace le ProviderPicker) : sources, type
  (vidéos/shorts/direct/chaînes), période (heure/jour/semaine/mois/année),
  durée (<4 / 4-20 / >20 min) et tri. Pastilles + roving tabindex
  (←/→ dans un groupe, ↑/↓ entre groupes), focus trap, Échap, Ctrl/⌘+Maj+F,
  recherches récentes, « mémoriser par défaut », « Réinitialiser ».
- Pastilles des filtres actifs sous la barre (retrait en un clic) + compteur.
- Typeahead : un seul keydown (fin du double Enter), ↑/↓ bouclants,
  Home/End, PageUp/PageDown, Entrée valide la ligne surlignée (sinon
  recherche brute), Tab complète sans chercher, Échap ferme puis vide,
  ligne « Rechercher <q> », défilement de l'option active, aria à jour.
- Focus : le panneau se referme dès que le focus quitte la barre
  (focusout + relatedTarget + activeElement), pointerdown neutralisé sur les
  lignes pour garder le focus dans l'input (plus de scintillement).
- Opérateurs en clair : `linux live:`, `tuto today: long:` deviennent des
  filtres et sont retirés de la requête (autocomplétion après le `:`).

Filtres côté serveur
- Modèle partagé : src/app/search/filters.ts + server/search-filters.mjs.
- /api/search?type=&duration=&period=&sort= : InnerTube (upload_date, type,
  duration, features), Data API v3 (type, videoDuration, publishedAfter),
  yt-dlp --dateafter ; post-filtrage pour les providers sans filtre natif.
  Un champ manquant ne fait jamais disparaître un résultat.
- Filtres dans l'URL (partageables) + adapters + SearchService (clé de cache).
- Correction : la recherche Shorts renvoyait 0 (ShortsLockupView/GridShelfView
  non mappés, id/titre dans l'endpoint) -> mappage + shelf + vignette/vues.

Tests / docs
- 23 tests unitaires (clavier, focus, opérateurs, panneau de filtres),
  server/tests/search-filters.test.mjs (npm run test:filters) + CI,
  scénarios e2e des filtres, README/api/README/todo/MCP à jour.
2026-09-28 17:42:19 -04:00
bruno df53445efe feat(oauth): import favoris/abos Google (YouTube) et Twitch vers abonnements/likes
- Backend: server/oauth.mjs (auth URL, echange code, refresh, fetch
  subscriptions/likes Google, follows Twitch), table oauth_connections
  + migration, routes /api/oauth/:provider/{url,callback,preview,import},
  /api/oauth/{status,connections}
- Front: /library/import (connect, preview, import), service
  oauth-import, lien depuis Abonnements
- Config: GOOGLE_CLIENT_ID/SECRET/REDIRECT_URI, TWITCH_REDIRECT_URI,
  OAUTH_APP_BASE_URL documentes (.env.example, docker-compose),
  passthrough docker-compose.local.yml, roadmap README cochee
2026-09-26 18:47:02 -04:00
bruno 4bdcd393fe feat(youtube): InnerTube-first search, transcripts and watch-next related (Steps 15-18)
- InnerTube layer via pinned youtubei.js 18.1.0 (no quota, no key):
  search with merged continuations (unlimited pages), watch-next
  related with LockupView mapping, caption-track discovery
- 3-layer dispatcher (YT_SEARCH_MODE, default innertube-first):
  innertube -> yt-dlp scrape -> official API, graceful errors.yt
- Robust yt-dlp binary resolution (YT_DLP_PATH > PATH > bundled)
  with systematic API fallback (fixes spawn ENOENT in UI)
- Transcript: InnerTube caption discovery (YT_TRANSCRIPT_SOURCE),
  reusing pickTrack/orderedTracks/parseTrackText; yt-dlp fallback kept
- Watch: sidebar uses real watch-next related[] (/api/details),
  title-search fallback for other providers
- Cache: memory LRU + SQLite (youtube_search_cache, youtube_metrics),
  never persist empty pages; /healthz observability; /api/trending
- Includes pending Step 15/16 leftovers in same files (suggest,
  test scripts); unrelated provider adapters left uncommitted
2026-09-25 19:38:46 -04:00
bruno 97e5de74f5 feat(channel): page details chaine multi-providers avec onglets et infinite scroll
CI / build-and-test (push) Successful in 13m34s
- Strategy Pattern front: ChannelProviderInterface + 6 providers (yt/dm/tw/pt/od/ru) + factory (ids longs/courts)
- ChannelContentService: cache par onglet/tri/recherche (TTL 5min), pagination nextPage
- ChannelPage: header (banniere/avatar/subs/description ...plus), tabs dynamiques par capabilities, grilles videos/shorts 9:16/playlists/live, recherche debounced, tri recent/populaire, infinite scroll IntersectionObserver, skeletons/empty/error
- Backend: GET /api/channels/:provider/:id/content (videos/shorts/playlists/live) natif par provider
2026-09-25 10:15:12 -04:00
bruno 4eb339eb22 chore: update Angular cache and TypeScript build info files 2025-09-22 09:34:22 -04:00
bruno 08fd0682a4 docs: revamp README with comprehensive features, deployment guide and roadmap 2025-09-21 17:01:45 -04:00
bruno 3f04191623 chore: update Angular cache and TypeScript build info 2025-09-20 22:33:11 -04:00
bruno b835bfcdbd docs: update README with emoji styling and improved feature documentation 2025-09-15 22:43:25 -04:00
bruno 2866d74b32 first commit 2025-09-14 23:05:30 -04:00