ecdfc95ff3d8aaf3550d8e32f41d334da37fee39
Passenger runs a literal JS entry file, not the next start CLI or an npm script, so a custom server was needed for cPanel's Node.js App startup file field.
Ombrora YTDLP
Interface web de téléchargement de vidéos via yt-dlp, avec file d'attente persistante, suivi en base de données et liens de téléchargement sécurisés.
Fonctionnement
- L'utilisateur colle une URL dans l'interface web. Elle est immédiatement analysée via un probe
yt-dlp -J(POST /api/probe), qui détermine les qualités, sous-titres et durée réellement disponibles pour cette vidéo. - Les options de téléchargement (format, qualité, sous-titres, découpe, qualité audio MP3) affichées à l'utilisateur sont dérivées de ce probe — la soumission est bloquée tant qu'il n'a pas réussi.
- La demande est enregistrée en base (statut
PENDING). - Le worker (Node.js, arrière-plan, supervisé par pm2) dépile les jobs et exécute
yt-dlpen parallèle (concurrence configurable). - Le fichier téléchargé est stocké localement ; un token signé (24h) est généré.
- L'utilisateur est redirigé vers une page de statut qui se rafraîchit jusqu'à ce que le fichier soit prêt.
- Un cron quotidien supprime les fichiers expirés et marque les entrées
FILE_DELETED— la DB conserve l'historique complet.
Stack technique
| Couche | Technologie |
|---|---|
| Framework | Next.js 16 (App Router) |
| Base de données | MariaDB 11 via Prisma 7 |
| Frontend | React 19, TypeScript, Tailwind CSS v4 |
| i18n | next-intl (EN, FR, ES, IT), routing par [locale] |
| Thème | next-themes (clair/sombre) |
| Analytics | PostHog (cookieless) |
| Worker | Node.js (tsx), supervisé par pm2 |
| Conteneur (dev) | Docker Compose |
Prérequis
- Node.js 20+
- Docker & Docker Compose (pour MariaDB en développement)
yt-dlp,ffmpegetffprobe(binaires précompilés dans./bin/ou dans lePATH)
Démarrage rapide
# 1. Cloner le dépôt
git clone <url> ombrora-ytdlp && cd ombrora-ytdlp
# 2. Copier et remplir les variables d'environnement
cp .env.example .env
# 3. Démarrer MariaDB
docker-compose up -d
# 4. Installer les dépendances
npm install
# 5. Appliquer les migrations Prisma
npm run db:migrate
# 6. Lancer le serveur Next.js
npm run dev
# 7. Dans un autre terminal, lancer le worker
npm run worker
L'interface est accessible sur http://localhost:3000.
Variables d'environnement
| Variable | Requis | Exemple | Description |
|---|---|---|---|
DATABASE_URL |
oui | mysql://ombrora:ombrora@localhost:3306/ombrora |
Connexion MariaDB |
STORAGE_PATH |
oui | /var/www/ombrora/storage |
Répertoire de stockage des fichiers téléchargés |
NEXT_PUBLIC_BASE_URL |
oui | https://example.com |
URL publique du site, utilisée pour le SEO (canonical, OpenGraph, JSON-LD, sitemap) |
BIN_DIR |
non | ./bin |
Répertoire contenant yt-dlp, ffmpeg et ffprobe (défaut : ./bin) — absent de .env.example, à ajouter manuellement si le défaut ne convient pas |
Scripts npm
| Script | Description |
|---|---|
npm run dev |
Serveur de développement Next.js |
npm run build |
Build de production |
npm start |
Serveur de production (server.js, requis par Passenger sur o2switch — next start seul n'est pas utilisable comme fichier de démarrage) |
npm test |
Tests Jest |
npm run worker |
Worker de téléchargement (boucle de polling, exécution directe/dev) |
npm run worker:pm2:start |
Démarre le worker sous pm2 (ecosystem.config.cjs), avec auto-restart |
npm run worker:pm2:stop |
Arrête le worker géré par pm2 |
npm run worker:pm2:restart |
Redémarre le worker géré par pm2 |
npm run worker:pm2:status |
Affiche l'état du process ombrora-worker |
npm run worker:pm2:logs |
Affiche les logs du worker géré par pm2 |
npm run worker:cleanup |
Expire et supprime les fichiers anciens (usage cron) |
npm run db:migrate |
Crée et applique les migrations Prisma (dev, interactif) |
npm run db:migrate:deploy |
Applique les migrations en attente (production, non interactif) |
npm run db:generate |
Régénère le client Prisma |
npm run deploy |
Déploiement complet sur o2switch (voir section ci-dessous) |
Configuration
Les paramètres applicatifs sont centralisés dans config/app.config.ts :
DOWNLOAD_LINK_TTL_HOURS: 24 // Durée de validité du token de téléchargement
WORKER_CONCURRENCY: 3 // Téléchargements parallèles maximum
WORKER_POLL_INTERVAL_MS: 10000 // Intervalle de polling du worker (ms)
RATE_LIMIT_MAX: 5 // Soumissions max par IP par fenêtre
RATE_LIMIT_WINDOW_MS: 3600000 // Fenêtre de rate-limit des soumissions (1h)
PROBE_RATE_LIMIT_MAX: 20 // Analyses (probe) max par IP par fenêtre
PROBE_RATE_LIMIT_WINDOW_MS: 3600000 // Fenêtre de rate-limit du probe (1h)
PROBE_TIMEOUT_MS: 20000 // Timeout du probe yt-dlp -J (ms)
API
| Méthode | Endpoint | Description |
|---|---|---|
POST |
/api/probe |
Analyse une URL via yt-dlp -J. Corps JSON : { url }. Retourne les qualités, sous-titres et durée réellement disponibles pour la vidéo, ou une erreur (422 si l'analyse échoue, 429 si rate-limit dépassé). |
POST |
/api/downloads |
Soumet une URL. Corps JSON : { url, format, quality, subtitles, subtitleLangs?, clipStart?, clipEnd?, audioQuality?, extraArgs? }. Retourne { uuid }. |
GET |
/api/downloads/:uuid |
Statut d'un téléchargement. Retourne { status, fileName, fileSize, downloadToken?, tokenExpiresAt?, errorMsg? }. |
GET |
/api/download/:token |
Téléchargement du fichier (token à usage unique, 24h). |
Architecture
src/
├── app/
│ ├── [locale]/
│ │ ├── layout.tsx # Header, thème, next-intl, JSON-LD, PostHog
│ │ ├── page.tsx # Accueil (hero, formulaire, sections SEO)
│ │ ├── [slug]/page.tsx # Pages SEO par plateforme (youtube-downloader, etc.)
│ │ ├── status/[uuid]/page.tsx # Page de suivi
│ │ └── supported-sites/page.tsx # Liste des sites supportés par yt-dlp
│ ├── sitemap.ts # Génération du sitemap.xml
│ └── api/
│ ├── downloads/route.ts # POST — création
│ ├── downloads/[uuid]/route.ts # GET — statut
│ ├── download/[token]/route.ts # GET — téléchargement
│ └── probe/route.ts # POST — analyse yt-dlp -J
├── components/
│ ├── SubmitForm.tsx # Formulaire de soumission (options dérivées du probe)
│ ├── StatusView.tsx # Vue de suivi (polling)
│ ├── Header.tsx, LanguageSwitcher.tsx, ThemeToggle.tsx
│ ├── HowItWorksSection.tsx, ReassuranceSection.tsx, SupportedPlatformsSection.tsx
│ └── FaqSection.tsx, FaqAccordion.tsx, SupportedSitesBrowser.tsx
├── i18n/
│ ├── routing.ts # Locales, préfixes d'URL
│ └── request.ts # Config next-intl côté serveur
├── proxy.ts # Middleware next-intl (détection/routage locale)
└── lib/
├── prisma.ts # Client Prisma (singleton)
├── rate-limit.ts # Factory createRateLimiter (downloads + probe)
├── token.ts # Génération/validation de tokens
├── ytdlp.ts # Construction des arguments/commande yt-dlp
├── ytdlp-options.ts # Formats/qualités/langues proposés
├── ytdlp-probe.ts # Analyse yt-dlp -J d'une URL
├── downloader-platforms.ts # Données des pages SEO par plateforme
└── supported-sites.ts # Liste des sites supportés par yt-dlp
worker/
├── index.ts # Boucle de polling principale
├── processor.ts # Exécution de yt-dlp, mise à jour DB
└── cron-cleanup.ts # Nettoyage des fichiers expirés
messages/ # Traductions (en.json, fr.json, es.json, it.json)
server.js # Serveur HTTP custom (fichier de démarrage Passenger sur o2switch)
ecosystem.config.cjs # Config pm2 du process "ombrora-worker"
prisma/
└── schema.prisma # Modèles : Download, DownloadToken
Déploiement (o2switch / Passenger)
Contraintes spécifiques à l'hébergement mutualisé
- Pas de compilation sur le serveur :
yt-dlp,ffmpegetffprobedoivent être des binaires précompilés pour la plateforme cible, placés dansBIN_DIR. node_modulesunique : Passenger ne crée qu'un seulnode_modulesà la racine. Le projet n'a qu'un seulpackage.json(pas de sous-dossier frontend séparé à synchroniser).- Worker : Passenger ne supervise que l'app Next.js. Le worker est un process à part, maintenu vivant par pm2 (auto-restart en cas de crash), à la manière de Passenger pour l'app web.
- Fichier de démarrage (Passenger) : Passenger exécute directement
node <fichier>— il ne peut pas lancer une commande CLI (next start) ni un script npm. Le dépôt fournit doncserver.jsà la racine (serveur HTTP custom minimal, cf. doc Next.js), qui appelle l'API programmatique de Next.js et écoute surprocess.env.PORT(port fourni par Passenger).
Étapes de déploiement
- Uploader les sources (hors
node_modules,.env,storage/). - Déposer
yt-dlpetffmpegdansBIN_DIR. - Remplir
.env.prodavec les valeurs de production (non versionné, à créer/mettre à jour manuellement sur le serveur). - Via cPanel > "Setup Node.js App" : créer l'app Node une première fois pour que Passenger et le nodevenv soient configurés (pointer sur le dépôt). Dans le champ "Application startup file", indiquer
server.js. - En SSH, dans l'environnement Node fourni par cPanel (celui activé par le lien "Enter to the virtual environment" de cPanel) :
Ce script (
npm run deployscripts/deploy.sh) enchaîne : copie de.env.prodvers.env,npm install, génération du client Prisma, application des migrations en attente (prisma migrate deploy),npm run build, puis démarrage/redémarrage du worker sous pm2 (pm2 startOrRestart+pm2 save, pour persister la liste des process en vue d'unpm2 resurrectaprès reboot). - Redémarrer l'app Next.js via le bouton "Restart" de cPanel > "Setup Node.js App" pour que Passenger recharge le nouveau build (le script ne pilote pas Passenger, qui est géré en dehors du SSH/nodevenv).
- Configurer un cron cPanel pour
npm run worker:cleanupune fois par jour (suppression des fichiers expirés). pm2 gère lui-même le redémarrage du worker en cas de crash ; il n'y a donc plus besoin de cron de supervision dédié. - Si l'hébergeur redémarre le serveur, relancer
pm2 resurrect(ounpm run worker:pm2:startsi la sauvegardepm2 saven'a pas été faite) pour reprendre le worker.
Pour les déploiements suivants, seule l'étape 5 (npm run deploy) et l'étape 6 (redémarrage Passenger) sont nécessaires.
Tests
npm test
Tests Jest (environnement Node, transpilation SWC). Les fichiers de test sont dans **/__tests__/**/*.test.ts.
Licence
Usage interne — non distribué.
Languages
TypeScript
98.8%
JavaScript
0.6%
Shell
0.5%
CSS
0.1%