From b4242e42955fec8f5d17bb4d5ba9e4a17c23932c Mon Sep 17 00:00:00 2001
From: MasterAcnolo <68693319+MasterAcnolo@users.noreply.github.com>
Date: Sun, 29 Jun 2025 14:10:58 +0200
Subject: [PATCH 1/5] Correction Git Ignore
---
.gitignore | 1 -
forge.config.js | 44 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 44 insertions(+), 1 deletion(-)
create mode 100644 forge.config.js
diff --git a/.gitignore b/.gitignore
index afdf369..afc0ec8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,4 +6,3 @@
yt-dlp.exe
-forge.config.js
diff --git a/forge.config.js b/forge.config.js
new file mode 100644
index 0000000..fa4a113
--- /dev/null
+++ b/forge.config.js
@@ -0,0 +1,44 @@
+const { FusesPlugin } = require('@electron-forge/plugin-fuses');
+const { FuseV1Options, FuseVersion } = require('@electron/fuses');
+
+module.exports = {
+ packagerConfig: {
+ asar: true,
+ },
+ rebuildConfig: {},
+ makers: [
+ {
+ name: '@electron-forge/maker-squirrel',
+ config: {},
+ },
+ {
+ name: '@electron-forge/maker-zip',
+ platforms: ['darwin'],
+ },
+ {
+ name: '@electron-forge/maker-deb',
+ config: {},
+ },
+ {
+ name: '@electron-forge/maker-rpm',
+ config: {},
+ },
+ ],
+ plugins: [
+ {
+ name: '@electron-forge/plugin-auto-unpack-natives',
+ config: {},
+ },
+ // Fuses are used to enable/disable various Electron functionality
+ // at package time, before code signing the application
+ new FusesPlugin({
+ version: FuseVersion.V1,
+ [FuseV1Options.RunAsNode]: false,
+ [FuseV1Options.EnableCookieEncryption]: true,
+ [FuseV1Options.EnableNodeOptionsEnvironmentVariable]: false,
+ [FuseV1Options.EnableNodeCliInspectArguments]: false,
+ [FuseV1Options.EnableEmbeddedAsarIntegrityValidation]: true,
+ [FuseV1Options.OnlyLoadAppFromAsar]: true,
+ }),
+ ],
+};
From 9026c63979d78a04b40c8d9cd0f1eefd5ace87ec Mon Sep 17 00:00:00 2001
From: MasterAcnolo <68693319+MasterAcnolo@users.noreply.github.com>
Date: Sun, 29 Jun 2025 15:48:06 +0200
Subject: [PATCH 2/5] Clarification
---
server/logger.js | 41 +++++++++++++++++++++------------
server/routes/download.js | 48 +++++++++++++++++++++++++++------------
server/routes/info.js | 20 +++++++++++-----
server/server.js | 40 ++++++++++++++++++++------------
4 files changed, 100 insertions(+), 49 deletions(-)
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 };
From c8889465885d9899c656527ed2b74db96f94b262 Mon Sep 17 00:00:00 2001
From: MasterAcnolo <68693319+MasterAcnolo@users.noreply.github.com>
Date: Mon, 30 Jun 2025 13:49:02 +0200
Subject: [PATCH 3/5] Logs Name Ajust + 1.0.1 Prepa
---
README.md | 2 +-
package-lock.json | 2308 ++++++++++-----------------------------------
package.json | 2 +-
server/logger.js | 14 +-
4 files changed, 521 insertions(+), 1805 deletions(-)
diff --git a/README.md b/README.md
index eeb6799..fa8f82e 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# Freedom Loader
-
+
+
+
+
+
+
+
> This project aims to enable free, open-source, and unrestricted downloading of Video/Audio content. Eventually, it will allow users to choose
diff --git a/README.md b/README.md
index fa8f82e..20e6059 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,12 @@
# Freedom Loader
-
+
+
+
+
+
+
> Ce projet a pour but de permettre le téléchargement de contenu Vidéo/Audio de manière gratuite, open source et libre. Il permettra à terme de choisir
diff --git a/main.js b/main.js
index f25a12e..af0e2b8 100644
--- a/main.js
+++ b/main.js
@@ -24,17 +24,22 @@ let mainWindow;
async function createWindow() {
logger.info("Creation de la fenetre...");
+
if (mainWindow) {
logger.warn("La fenetre existe deja, pas de nouvelle creation");
return;
}
mainWindow = new BrowserWindow({
- width: 1000,
- height: 700,
+ title: "Freedom Loader",
+ width: 750,
+ height: 800,
+ minWidth: 750,
+ minHeight: 800,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
},
+ // titleBarStyle: 'hidden',
});
try {
@@ -71,4 +76,4 @@ app.on("window-all-closed", () => {
app.on("before-quit", () => {
logSessionEnd();
-});
\ No newline at end of file
+});
diff --git a/public/assets/icons/GitHub Icon.ico b/public/assets/icons/GitHub Icon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..798a0243427fd46f541c0dffec409ab6e454ed3d
GIT binary patch
literal 99678
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
+
+
diff --git a/public/index.html b/public/index.html
index 5d76ae1..f25656b 100644
--- a/public/index.html
+++ b/public/index.html
@@ -41,8 +41,13 @@
+
diff --git a/public/styles/layout/videoinfo.css b/public/styles/layout/videoinfo.css
index b47fc17..bc8a43c 100644
--- a/public/styles/layout/videoinfo.css
+++ b/public/styles/layout/videoinfo.css
@@ -4,7 +4,7 @@
margin-left: auto;
margin-right: auto;
padding: 1.5em 2em;
- background-color: #fff;
+ background-color: var(--infos-box-color);
border-radius: 12px;
box-shadow: 0 8px 16px rgba(0,0,0,0.1);
color: #222;
diff --git a/public/styles/styles.css b/public/styles/styles.css
index 8651900..1f8f64c 100644
--- a/public/styles/styles.css
+++ b/public/styles/styles.css
@@ -27,7 +27,7 @@ body {
background-attachment: fixed; */
width: 100%;
height: 100%;
- color: #F8FAFC;
+ color: var(--default-text-color);
display: flex;
justify-content: center;
align-items: center;
diff --git a/public/styles/variables.css b/public/styles/variables.css
index 8494afc..fb2b848 100644
--- a/public/styles/variables.css
+++ b/public/styles/variables.css
@@ -1,9 +1,11 @@
:root{
--background-color : #0F172A;
+ --infos-box-color: #f1f1f1 ;
--title-color: ;
--subtitle-color : #a3aab4;
--mp3-text-color: #0F172A;
--default-text-color: #F8FAFC;
--checkbox-pulse-color:#9bbcf1;
+
}
\ No newline at end of file
diff --git a/server/routes/download.js b/server/routes/download.js
index af916b7..bed0fe9 100644
--- a/server/routes/download.js
+++ b/server/routes/download.js
@@ -69,8 +69,48 @@ router.post("/", (req, res) => {
args.push("--write-subs", "--sub-lang", "en");
}
- // qualité souhaitée
- args.push("-f", options.quality);
+ // // qualité souhaitée
+ // args.push("-f", options.quality);
+
+ // 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