From ec6963cff3960970139efe2645fe7eb8b5d51334 Mon Sep 17 00:00:00 2001 From: MasterAcnolo <68693319+MasterAcnolo@users.noreply.github.com> Date: Wed, 2 Jul 2025 13:49:55 +0200 Subject: [PATCH] Logs FIX + Bug FIX --- server/routes/download.js | 110 ++++++++++---------------------------- server/routes/info.js | 70 ++++++++++++++---------- 2 files changed, 72 insertions(+), 108 deletions(-) diff --git a/server/routes/download.js b/server/routes/download.js index bed0fe9..9c9d7d8 100644 --- a/server/routes/download.js +++ b/server/routes/download.js @@ -16,150 +16,98 @@ along with this program. If not, see . */ -const express = require("express"); // importe le framework Express -const router = express.Router(); // crée un routeur Express pour modulariser les routes -const { execFile } = require("child_process"); // permet d’exécuter un binaire externe sans passer par un shell -const path = require("path"); // module Node.js pour gérer les chemins de fichiers -const fs = require("fs"); // module Node.js pour manipuler le système de fichiers +const express = require("express"); +const router = express.Router(); +const { execFile } = require("child_process"); +const path = require("path"); +const fs = require("fs"); -const logger = require("../logger").logger; // récupère l'instance de logger Winston personnalisée +const logger = require("../logger").logger; -// chemin vers l’exécutable yt-dlp packagé avec l’appli +// Path vers le .exe de YT-DLP const ytDlpPath = path.join(__dirname, '../../yt-dlp.exe'); -// route POST sur la racine (ex: POST /download) router.post("/", (req, res) => { try { - // récupère les options envoyées depuis le frontend (via formulaire ou requête AJAX) + // récupère les options envoyées depuis le frontend const options = { - url: req.body.url, // URL à télécharger - audioOnly: req.body.audioOnly === "1", // conversion audio uniquement (mp3) - quality: req.body.quality || "best", // qualité vidéo à télécharger - subtitles: req.body.subs === "1", // récupérer les sous-titres anglais + url: req.body.url, + audioOnly: req.body.audioOnly === "1", + quality: req.body.quality || "best", }; if (!options.url) { - // si l’URL est absente, on log et on renvoie 400 logger.warn("Requête POST /download sans URL"); return res.status(400).send("❌ URL manquante !"); } - // on récupère le dossier de sortie depuis app.locals (défini ailleurs dans le serveur) const outputFolder = req.app.locals.outputFolder; - // s'il n'existe pas, on le crée automatiquement if (!fs.existsSync(outputFolder)) { fs.mkdirSync(outputFolder, { recursive: true }); logger.info("Dossier Freedom Loader Output créé à la volée dans Téléchargements."); } - // template du nom de fichier de sortie (ex: MonTitre.mp4 ou MonTitre.mp3) const outputTemplate = path.join(outputFolder, "%(title)s.%(ext)s"); - // tableau d'arguments pour yt-dlp - const args = []; + // tableau d'arguments yt-dlp + const args = ["--no-continue", "--restrict-filenames"]; if (options.audioOnly) { - // si audioOnly, on force extraction audio en mp3 args.push("--extract-audio", "--audio-format", "mp3"); } - if (options.subtitles) { - // si l’utilisateur veut des sous-titres - args.push("--write-subs", "--sub-lang", "en"); - } + // gestion qualité + const qualityMap = { + best: "bestvideo+bestaudio/best", + medium: "bestvideo[height<=720]+bestaudio/best[height<=720]", + worst: "worstvideo+worstaudio/worst", + 1080: "bestvideo[height<=1080]+bestaudio/best[height<=1080]", + 720: "bestvideo[height<=720]+bestaudio/best[height<=720]", + 480: "bestvideo[height<=480]+bestaudio/best[height<=480]", + }; - // // qualité souhaitée - // args.push("-f", options.quality); + const format = qualityMap[options.quality] || "best"; + args.push("-f", format); - // let format; - // if (options.quality === "best") { - // format = "bestvideo+bestaudio/best"; - // } else if (options.quality === "worst") { - // format = "worstvideo+worstaudio/worst"; - // } else { - // format = "best"; // fallback - // } - // args.push("-f", format); - - args.push("--no-continue"); - - let format; - - switch (options.quality) { - case "best": - format = "bestvideo+bestaudio/best"; - break; - case "medium": - // qualité moyenne, ex: 720p vidéo + meilleur audio - format = "bestvideo[height<=720]+bestaudio/best[height<=720]"; - break; - case "worst": - format = "worstvideo+worstaudio/worst"; - break; - case "1080": - format = "bestvideo[height<=1080]+bestaudio/best[height<=1080]"; - break; - case "720": - format = "bestvideo[height<=720]+bestaudio/best[height<=720]"; - break; - case "480": - format = "bestvideo[height<=480]+bestaudio/best[height<=480]"; - break; - default: - format = "best"; - } - args.push("-f", format); - - // modèle de sortie args.push("-o", outputTemplate); - // enfin, l'URL à télécharger args.push(options.url); - // log de la commande complète pour debug + logger.info(`Téléchargement demandé : url=${options.url}, audioOnly=${options.audioOnly}, quality=${options.quality}`); logger.info(`Commande yt-dlp : ${ytDlpPath} ${args.join(" ")}`); - // exécution du binaire yt-dlp const child = execFile(ytDlpPath, args); - // gestion des logs stdout de yt-dlp child.stdout.on("data", (data) => { data.toString().split("\n").forEach(line => { - if(line.trim()) logger.info(`[yt-dlp stdout] ${line.trim()}`); + if (line.trim()) logger.info(`[yt-dlp stdout] ${line.trim()}`); }); }); - // gestion des logs stderr de yt-dlp (les erreurs) child.stderr.on("data", (data) => { data.toString().split("\n").forEach(line => { - if(line.trim()) logger.error(`[yt-dlp stderr] ${line.trim()}`); + if (line.trim()) logger.error(`[yt-dlp stderr] ${line.trim()}`); }); }); - // si une erreur système survient (par ex : yt-dlp.exe introuvable) child.on("error", (err) => { logger.error(`Erreur lancement yt-dlp : ${err.message}`); res.status(500).send(`❌ Erreur lors de l'exécution : ${err.message}`); }); - // callback quand le processus se termine child.on("close", (code) => { logger.info(`yt-dlp terminé avec code de sortie : ${code}`); - if(code === 0) { - // succès + if (code === 0) { res.send("✅ Téléchargement terminé !"); } else { - // yt-dlp a échoué res.status(500).send(`❌ yt-dlp a échoué avec le code : ${code}`); } }); } catch (err) { - // en cas d'erreur JavaScript logger.error(`Erreur serveur dans /download : ${err.message}`); res.status(500).send(`Erreur serveur : ${err.message}`); } }); -// on exporte ce routeur pour qu'il soit utilisé par l'app principale -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/server/routes/info.js b/server/routes/info.js index 372e823..b7739d4 100644 --- a/server/routes/info.js +++ b/server/routes/info.js @@ -16,40 +16,56 @@ along with this program. If not, see . */ -const express = require("express"); // importe Express -const router = express.Router(); // crée un routeur Express indépendant -const { execFile } = require("child_process"); // permet d’exécuter un programme externe -const path = require("path"); // module pour gérer les chemins de fichiers +const express = require("express"); +const router = express.Router(); +const { execFile } = require("child_process"); +const path = require("path"); +const fs = require("fs"); +const { logger } = require("../logger"); // on récupère ton logger Winston -// chemin vers le binaire yt-dlp embarqué const ytDlpPath = path.join(__dirname, "../../yt-dlp.exe"); -// route POST sur la racine de ce routeur +// Check si ya YT DLP +if (!fs.existsSync(ytDlpPath)) { + logger.error(`❌ yt-dlp.exe introuvable à ${ytDlpPath}`); + throw new Error(`yt-dlp.exe introuvable à ${ytDlpPath}`); +} + router.post("/", (req, res) => { - const url = req.body.url; // récupère l'URL depuis la requête POST - if (!url) return res.status(400).send("❌ URL manquante"); // validation simple + const url = req.body.url; + if (!url) { + logger.warn("Requête metadata sans URL"); + return res.status(400).send("❌ URL manquante"); + } - // exécute yt-dlp avec --dump-json pour récupérer les métadonnées de la vidéo - execFile(ytDlpPath, ["--dump-json", url], (error, stdout, stderr) => { - if (error) { - // gestion d’erreur : par exemple yt-dlp plante ou l’URL est invalide - console.error("Erreur lors de l'exécution de yt-dlp :", error); - console.error("stderr :", stderr); - return res.status(500).send("❌ Impossible de récupérer les infos."); - } + logger.info(`Requête metadata reçue pour ${url}`); - try { - // on parse le JSON envoyé par yt-dlp - const info = JSON.parse(stdout); - // on renvoie l’objet JSON directement au frontend - res.json(info); - } catch (e) { - // le JSON est mal formé - console.error("Erreur lors du parsing JSON :", e); - res.status(500).send("❌ JSON illisible."); + execFile( + ytDlpPath, + ["--dump-json", url], + { timeout: 10_000 }, // 10s de timeout + (error, stdout, stderr) => { + if (error) { + logger.error(`Erreur exécution yt-dlp: ${error.message}`); + logger.debug(`stderr: ${stderr}`); + return res.status(500).send("❌ Impossible de récupérer les infos."); + } + + try { + // découpe multi-JSON propre + const infos = stdout + .trim() + .split("\n") + .map(line => JSON.parse(line)); + + logger.info(`Infos récupérées pour ${url} (${infos.length} élément(s))`); + res.json(infos.length === 1 ? infos[0] : infos); + } catch (e) { + logger.error(`Erreur parsing JSON: ${e.message}`); + return res.status(500).send("❌ JSON illisible."); + } } - }); + ); }); -// on exporte le routeur pour être utilisé dans app.js ou ailleurs module.exports = router;