Cœur du pipeline : ajoute une file d'attente exécutant les téléchargements
yt-dlp avec une concurrence bornée, et livre le fichier produit au
navigateur (pas de dépôt durable côté serveur).
- downloadPlan.ts : construction des arguments yt-dlp par mode (vidéo avec
sous-titres/miniature en option, audio dans un format courant, miniature
seule), toujours avec séparateur `--` avant l'URL. Parsing de la
progression depuis stdout (`[download] NN.N%`).
- videoUrl.ts : valide qu'une URL cible bien youtube.com/youtu.be avant de
la passer à yt-dlp - ferme le risque de relais SSRF via l'extracteur
générique de yt-dlp (des centaines de sites supportés) si une URL
arbitraire était acceptée.
- QueueService.ts : file en mémoire, N téléchargements simultanés
(MAX_CONCURRENT_DOWNLOADS), annulation via AbortSignal, nettoyage du
dossier de sortie après échec/annulation ou après livraison du fichier.
- ytDlpRunner.ts : étendu pour le suivi ligne par ligne de stdout
(progression) et l'annulation (AbortSignal passé à child_process.spawn).
- routes/queue.ts : POST/GET /api/queue, DELETE /api/queue/:id,
GET /api/queue/:id/file (streaming + nettoyage après envoi).
45 nouveaux tests (59 au total) : fonctions pures, services (vrai système
de fichiers temporaire, yt-dlp mocké), routes. Vérifié aussi en conditions
réelles (serveur démarré localement) : validation d'URL, ajout en file,
et passage correct à l'état "failed" + nettoyage quand le binaire yt-dlp
est absent (ENOENT).
Empilée sur feature/channel-discovery (réutilise ytDlpRunner.ts).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Première brique du pipeline : lister les vidéos/streams d'une chaîne
YouTube (yt-dlp --flat-playlist), filtrables par mots-clés sur le titre.
- ytDlpRunner.ts : exécution de yt-dlp via child_process.spawn (jamais de
shell, arguments en tableau - exclut l'injection de commande)
- ChannelDiscoveryService.ts : validation stricte du handle/URL de chaîne
+ séparateur `--` avant l'URL (défense en profondeur contre l'injection
d'options yt-dlp), parsing tolérant des lignes JSON, filtrage mots-clés
- routes/channel.ts : GET /api/channel (validation de schéma Fastify,
400 si entrée invalide, 502 si yt-dlp échoue)
- 14 tests (unitaires services + intégration route via app.inject) - un
test de route a révélé une regex de validation trop permissive qu'un
test de service avait manquée, corrigée avant ce commit
La file d'attente et le téléchargement suivront dans une PR séparée.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Pose l'infrastructure de base avant l'implémentation des fonctionnalités :
- server/ : Fastify + TypeScript (ESM), route /health, sert client/dist
- client/ : TypeScript vanilla + esbuild/sass, vue placeholder (shell)
- server/Dockerfile : image multi-étapes avec yt-dlp + ffmpeg embarqués
- .forgejo/workflows/publish-docker.yml : test puis publication sur le
registre de conteneurs Forgejo (nécessite le secret REGISTRY_TOKEN)
- biome.json : lint du repo entier (downloader/ exclu)
- spec/architecture.md : documentation de ces choix
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>