7.2 KiB
7.2 KiB
Ombrora-YTDLP — Design Spec
Date: 2026-08-10
Vue d'ensemble
Interface web publique permettant a n'importe quel utilisateur de soumettre une URL de video a telecharger via yt-dlp. Les telechargements sont traites en arriere-plan par un worker Node.js gere par Passenger sur o2switch. Les fichiers sont stockes localement dans un dossier prive et accessibles via un lien temporaire a duree de vie configurable.
Architecture generale
Approche retenue : Monorepo Next.js + Worker script Node.js separe.
src/: app Next.js (UI React + API routes)worker/: script Node.js long-running qui poll la DB et execute yt-dlpconfig/: configuration centralisee (TTL, concurrence, chemins)prisma/: schema et migrationsdocker-compose.yml: MariaDB pour le developpement local- Un seul
package.jsona la racine (contrainte o2switch/Passenger)
Structure du projet
Ombrora-YTDLP/
├── src/
│ ├── app/
│ │ ├── page.tsx # Formulaire de soumission
│ │ ├── status/[uuid]/page.tsx # Page de statut
│ │ └── api/
│ │ ├── downloads/route.ts # POST soumission, GET statut
│ │ ├── downloads/[uuid]/route.ts
│ │ └── download/[token]/route.ts # Stream fichier via token
│ ├── components/
│ └── lib/
│ ├── prisma.ts
│ ├── rate-limit.ts
│ └── token.ts
├── worker/
│ ├── index.ts # Worker long-running principal
│ ├── cron-check.ts # Verifie que le worker tourne (cron 5 min)
│ └── cron-cleanup.ts # Supprime les fichiers expires (cron 1x/jour)
├── config/
│ └── app.config.ts
├── prisma/
│ └── schema.prisma
├── docker-compose.yml
└── package.json
Schema base de donnees (Prisma / MariaDB)
model Download {
id String @id @default(cuid())
uuid String @unique @default(uuid()) // expose sur les interfaces
url String
status Status @default(PENDING)
format String // ex: "mp4", "mp3"
quality String // ex: "best", "1080p"
subtitles Boolean @default(false)
extraArgs String? // options yt-dlp (JSON array de strings, ex: ["--sponsorblock-remove","all"])
filePath String? // chemin absolu sur disque
fileName String? // nom original
fileSize BigInt? // taille en octets
errorMsg String?
ipAddress String
submittedAt DateTime @default(now())
startedAt DateTime?
completedAt DateTime?
deletedAt DateTime?
tokens DownloadToken[]
}
model DownloadToken {
id String @id @default(cuid())
download Download @relation(fields: [downloadId], references: [id])
downloadId String
token String @unique @default(uuid())
expiresAt DateTime
usedAt DateTime?
createdAt DateTime @default(now())
}
enum Status {
PENDING // en attente de traitement
PROCESSING // worker en cours
DONE // fichier disponible
FAILED // echec yt-dlp
FILE_DELETED // metadonnees conservees, fichier supprime du disque
}
Regles immuables :
- Aucune entree n'est jamais supprimee de la DB (historique complet).
id(cuid) usage interne uniquement ;uuidexpose sur toutes les interfaces.- Le fichier est stocke sous
{STORAGE_PATH}/{uuid}.{extension}.
Worker (worker/index.ts)
Processus Node.js long-running sous Passenger. Cycle a chaque iteration :
- Requete DB :
SELECTlesPENDINGordonnes parsubmittedAt, limite aWORKER_CONCURRENCY. - Passe les entrees selectionnees en
PROCESSING(atomique : UPDATE WHERE status=PENDING). - Spawne un
worker_threadpar telechargement. - Chaque thread construit la commande yt-dlp :
--no-playlisttoujours present-f {format}pour le format--write-sub --sub-lang fr,ensisubtitles=true-o {STORAGE_PATH}/{uuid}.%(ext)spour le chemin de sortie- extraArgs injectes depuis le champ JSON
- Fin du thread :
- Succes :
status=DONE, remplitfilePath,fileName,fileSize,completedAt, cree unDownloadTokenavecexpiresAt = now + DOWNLOAD_LINK_TTL_HOURS. - Echec :
status=FAILED, rempliterrorMsg(stderr yt-dlp).
- Succes :
- Attend
WORKER_POLL_INTERVAL_MSpuis recommence.
Crons o2switch (cPanel)
| Script | Frequence | Role |
|---|---|---|
worker/cron-check.ts |
Toutes les 5 min | Relance le worker s'il n'est pas actif |
worker/cron-cleanup.ts |
1x par jour | Supprime les fichiers dont completedAt + TTL < now, passe status=FILE_DELETED, remplit deletedAt |
API Routes (Next.js)
| Methode | Route | Description |
|---|---|---|
POST |
/api/downloads |
Valide IP (rate limit), cree l'entree DB, retourne { uuid } |
GET |
/api/downloads/[uuid] |
Retourne statut et metadonnees du telechargement |
GET |
/api/download/[token] |
Verifie token (existence, expiration), stream le fichier, marque usedAt |
Rate limiting (lib/rate-limit.ts) :
- Map en memoire : IP ->
{ count, resetAt } - Limite et fenetre configurables dans
app.config.ts - Pas de Redis requis
- Hypothese : Passenger tourne avec 1 seul processus Next.js sur o2switch (standard). Si plusieurs processus sont configures, la Map n'est pas partagee et le rate limit serait par-processus — a surveiller.
Configuration (config/app.config.ts)
export const config = {
DOWNLOAD_LINK_TTL_HOURS: 24, // duree de validite du lien temporaire
WORKER_CONCURRENCY: 3, // telechargements en parallele
WORKER_POLL_INTERVAL_MS: 10_000, // intervalle de poll de la DB
STORAGE_PATH: '/home/.../storage', // chemin absolu du dossier de stockage
RATE_LIMIT_MAX: 5, // soumissions max par IP
RATE_LIMIT_WINDOW_MS: 3_600_000, // fenetre de rate limit (1h)
}
Frontend (React / Next.js)
/ — Formulaire de soumission
- Champ URL
- Select format (mp4, mp3, webm, ...)
- Select qualite (best, 1080p, 720p, 480p, ...)
- Checkbox sous-titres
- Champ options avancees (optionnel, libre)
- Soumission -> POST
/api/downloads-> redirect vers/status/{uuid}
/status/[uuid] — Page de statut
- Polling
GET /api/downloads/[uuid]toutes les 5 secondes - Affichage selon statut :
PENDING/PROCESSING: indicateur de progressionDONE: bouton de telechargement (lien/api/download/{token})FAILED: message d'erreur yt-dlpFILE_DELETED: message "fichier expire, le telechargement n'est plus disponible"
Contraintes de deploiement o2switch
- Un seul
package.json/node_modulesa la racine (pas defrontend/node_modules). - Toute dependance runtime du frontend doit etre dans les
dependenciesde la racine ET dansfrontend/package.json(si sous-dossier). - yt-dlp doit etre installe comme binaire precompile (pas de build depuis les sources).
- Les dependances avec bindings natifs doivent publier des binaires precompiles pour la plateforme o2switch.