diff --git a/server/logger.js b/server/logger.js
index 8e1cc7a..295268d 100644
--- a/server/logger.js
+++ b/server/logger.js
@@ -16,51 +16,61 @@
along with this program. If not, see .
*/
-const { createLogger, format, transports } = require("winston");
-const DailyRotateFile = require("winston-daily-rotate-file");
-const fs = require("fs");
-const path = require("path");
+const { createLogger, format, transports } = require("winston");
+// on importe Winston (logger populaire pour Node.js)
+const DailyRotateFile = require("winston-daily-rotate-file");
+// plugin pour Winston qui permet la rotation automatique des fichiers de logs
+
+const fs = require("fs"); // pour vérifier/créer le dossier de logs
+const path = require("path"); // pour gérer proprement les chemins
+
+// on définit le dossier de logs
const logDir = path.join(__dirname, "../logs");
+// on le crée si inexistant
if (!fs.existsSync(logDir)) fs.mkdirSync(logDir);
+// fonction pour générer une ligne de début de session (date ISO)
function getSessionStartLine() {
const now = new Date().toISOString();
return `--- Démarrage de la session : ${now} ---`;
}
+// fonction pour générer une ligne de fin de session (date ISO)
function getSessionEndLine() {
const now = new Date().toISOString();
return `--- Fin de la session : ${now} ---`;
}
+// configuration principale de Winston
const logger = createLogger({
- level: "info",
+ level: "info", // niveau de log minimum (info, warn, error, etc.)
format: format.combine(
- format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
+ format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), // timestamp lisible
format.printf(({ timestamp, level, message }) =>
- `${timestamp} | ${level.toUpperCase()} | ${message}`
+ `${timestamp} | ${level.toUpperCase()} | ${message}` // format de ligne de log
)
),
transports: [
new DailyRotateFile({
- dirname: logDir,
- filename: "combined-%DATE%.log",
- datePattern: "YYYY-MM-DD",
- zippedArchive: false, // tu peux mettre true si tu veux compresser
- maxFiles: "14d", // conserve les logs des 14 derniers jours
+ dirname: logDir, // dossier de logs
+ filename: "combined-%DATE%.log", // nom de fichier journalier
+ datePattern: "YYYY-MM-DD", // pattern de rotation
+ zippedArchive: false, // true = compression des archives
+ maxFiles: "14d", // garde 14 jours d’historique
format: format.combine(
format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
format.printf(({ timestamp, level, message }) =>
`${timestamp} | ${level.toUpperCase()} | ${message}`
)
),
- options: { flags: "a" },
+ options: { flags: "a" }, // mode append
}),
new transports.Console({
+ // sortie console colorisée (dev en local)
format: format.combine(
format.colorize(),
- format.timestamp({ format: "HH:mm:ss" }),
+ format.timestamp({ format: "HH:mm:ss" }), // horodatage plus court
format.printf(({ timestamp, level, message }) =>
`${timestamp} | ${level} | ${message}`
)
@@ -69,14 +79,17 @@ const logger = createLogger({
],
});
+// fonction d'utilitaire pour marquer le début de la session
function logSessionStart() {
logger.info(getSessionStartLine());
}
+// idem pour la fin de la session
function logSessionEnd() {
logger.info(getSessionEndLine());
}
+// exporte le logger et les helpers pour les autres modules
module.exports = {
logger,
logSessionStart,
diff --git a/server/routes/download.js b/server/routes/download.js
index 3bac042..af916b7 100644
--- a/server/routes/download.js
+++ b/server/routes/download.js
@@ -16,90 +16,110 @@
along with this program. If not, see .
*/
-const express = require("express");
-const router = express.Router();
-const { execFile } = require("child_process");
-const path = require("path");
-const fs = require("fs");
+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 logger = require("../logger").logger; // importe ton logger Winston
+const logger = require("../logger").logger; // récupère l'instance de logger Winston personnalisée
+// chemin vers l’exécutable yt-dlp packagé avec l’appli
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)
const options = {
- url: req.body.url,
- audioOnly: req.body.audioOnly === "1",
- quality: req.body.quality || "best",
- subtitles: req.body.subs === "1",
+ 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
};
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 !");
}
- // Récupérer le chemin Freedom Loader Output depuis app.locals
+ // 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.");
}
- // Construire le template de sortie
+ // template du nom de fichier de sortie (ex: MonTitre.mp4 ou MonTitre.mp3)
const outputTemplate = path.join(outputFolder, "%(title)s.%(ext)s");
- // Construire les arguments pour execFile
+ // tableau d'arguments pour yt-dlp
const args = [];
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");
}
+ // qualité souhaitée
args.push("-f", options.quality);
+ // 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(`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()}`);
});
});
+ // 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()}`);
});
});
+ // 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
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}`);
}
});
-module.exports = router;
+// on exporte ce routeur pour qu'il soit utilisé par l'app principale
+module.exports = router;
\ No newline at end of file
diff --git a/server/routes/info.js b/server/routes/info.js
index 23eb652..372e823 100644
--- a/server/routes/info.js
+++ b/server/routes/info.js
@@ -16,32 +16,40 @@
along with this program. If not, see .
*/
-const express = require("express");
-const router = express.Router();
-const { execFile } = require("child_process");
-const path = require("path");
+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
+// chemin vers le binaire yt-dlp embarqué
const ytDlpPath = path.join(__dirname, "../../yt-dlp.exe");
+// route POST sur la racine de ce routeur
router.post("/", (req, res) => {
- const url = req.body.url;
- if (!url) return res.status(400).send("❌ URL manquante");
+ 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
+ // 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.");
}
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.");
}
});
});
+// on exporte le routeur pour être utilisé dans app.js ou ailleurs
module.exports = router;
diff --git a/server/server.js b/server/server.js
index c2e98c3..ba05351 100644
--- a/server/server.js
+++ b/server/server.js
@@ -16,59 +16,68 @@
along with this program. If not, see .
*/
-const express = require("express");
-const { exec } = require("child_process");
-const fs = require("fs");
-const path = require("path");
-const debug = require("debug")("freedom-loader:server");
+const express = require("express"); // framework web
+const { exec } = require("child_process"); // si besoin d’exécuter des commandes (pas utilisé ici directement)
+const fs = require("fs"); // accès au système de fichiers
+const path = require("path"); // gestion de chemins
+const debug = require("debug")("freedom-loader:server");
+// petit debugger coloré en console
+
const { logger, logSessionStart, logSessionEnd } = require("./logger");
+// récupère notre logger Winston + helpers de session
-const app = express();
+const app = express(); // instancie l'application Express
-// // Lancement d'une nouvelle session de log
-// logSessionStart();
+// logSessionStart(); // désactivé, mais pourrait logguer le début de session
-// dossier de sortie
+// on définit le dossier de téléchargement dans le dossier Téléchargements de l’utilisateur
const downloadsPath = path.join(process.env.USERPROFILE, "Downloads");
const outputFolder = path.join(downloadsPath, "Freedom Loader Output");
+// création du dossier s’il n’existe pas
if (!fs.existsSync(outputFolder)) {
try {
fs.mkdirSync(outputFolder, { recursive: true });
logger.info("Dossier Freedom Loader Output cree dans Telechargements.");
} catch (err) {
logger.error("Impossible de creer le dossier :", err);
- process.exit(1);
+ process.exit(1); // arrêt du programme en cas d’échec
}
} else {
logger.info("Dossier Freedom Loader Output deja existant.");
}
+// on rend ce dossier dispo via app.locals
app.locals.outputFolder = outputFolder;
+
+// configuration pour parser les requêtes POST en urlencoded
app.use(express.urlencoded({ extended: true }));
+// sert les fichiers statiques (frontend)
const staticPath = path.join(__dirname, "../public");
debug("Serveur statique sur", staticPath);
app.use(express.static(staticPath));
-// routes
+// enregistre les routes API
const infoRoute = require("./routes/info");
const downloadRoute = require("./routes/download");
debug("Routes /download et /info installees");
app.use("/download", downloadRoute);
app.use("/info", infoRoute);
+// route GET / de base
app.get("/", (req, res) => {
debug("Requete GET / servie");
res.sendFile(path.join(__dirname, "../public/index.html"));
});
+// fonction pour démarrer le serveur (Promise pour pouvoir await ailleurs)
function startServer() {
return new Promise((resolve, reject) => {
logger.info("Demarrage du serveur Express...");
const serverInstance = app.listen(8080, () => {
logger.info("Serveur Express pret sur http://localhost:8080");
- resolve(serverInstance);
+ resolve(serverInstance); // succès
});
serverInstance.on("error", (err) => {
logger.error("Erreur serveur Express :", err);
@@ -77,17 +86,18 @@ function startServer() {
});
}
-// Gestion propre de la fin de session à la fermeture du process
+// gestion de la fermeture propre du process
// process.on("exit", () => {
// logSessionEnd();
// });
-process.on("SIGINT", () => {
+process.on("SIGINT", () => { // Ctrl+C
logSessionEnd();
process.exit();
});
-process.on("SIGTERM", () => {
+process.on("SIGTERM", () => { // kill
logSessionEnd();
process.exit();
});
+// on exporte la fonction startServer pour que d’autres modules puissent la lancer
module.exports = { startServer };