Préparation du Champ de Bataille

This commit is contained in:
MasterAcnolo
2025-11-08 15:11:25 +01:00
parent 9944f05139
commit ac854ae559
53 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,48 @@
/*
This file is part of Freedom Loader.
Copyright (C) 2025 MasterAcnolo
Freedom Loader is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License.
Freedom Loader is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
const RPC = require("discord-rpc");
const clientId = "1410934537051181146";
const rpc = new RPC.Client({ transport: "ipc" });
function startRPC() {
rpc.on("ready", () => {
console.log("✅ Connecté à Discord !");
const presence = {
largeImageKey: "icon",
smallImageKey: "acnolo_pfp",
smallImageText: "By MasterAcnolo",
startTimestamp: new Date(), // compteur de temps
details: "github.com/MasterAcnolo/Freedom-Loader",
};
rpc.setActivity(presence);
// Mise à jour toutes les 15 secondes si tu veux garder le compteur à jour
setInterval(() => {
rpc.setActivity(presence);
}, 15000);
});
rpc.login({ clientId }).catch(err => {
console.error("Erreur Discord RPC :", err);
});
}
module.exports = { startRPC };

110
OLD CODE/server/logger.js Normal file
View File

@@ -0,0 +1,110 @@
/*
This file is part of Freedom Loader.
Copyright (C) 2025 MasterAcnolo
Freedom Loader is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License.
Freedom Loader is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
const { createLogger, format, transports } = require("winston");
const DailyRotateFile = require("winston-daily-rotate-file");
const fs = require("fs"); // Pour vérifier/créer le dossier de logs
const path = require("path"); // Pour gérer proprement les chemins
const os = require("os"); // Pour gérer les chemins aussi
// Définition du dossier où seront stockés les logs
// const logDir = path.join(__dirname, "../logs"); Désactivé parce que cela requiert d'exec en admin mode. Donc pas pratique pour les gens
let logDir;
if (process.platform === "win32") {
logDir = path.join(os.homedir(), "AppData", "Local", "FreedomLoader", "logs");
} else {
logDir = path.join(os.homedir(), ".freedomloader", "logs");
}
// Création du dossier logs sil nexiste pas encore
//if (!fs.existsSync(logDir)) fs.mkdirSync(logDir); OBSOLETE
fs.mkdirSync(logDir, { recursive: true }); // Evite les erreurs
// Fonction utilitaire pour générer une ligne indiquant le début dune session de logs
function getSessionStartLine() {
const now = new Date().toISOString();
return `--- Démarrage de la session : ${now} ---`;
}
// Fonction utilitaire pour générer une ligne indiquant la fin dune session de logs
function getSessionEndLine() {
const now = new Date().toISOString();
return `--- Fin de la session : ${now} ---`;
}
// Configuration principale de Winston
// - Niveau minimum décriture : info (enregistre info, warn, error, etc.)
// - Formatage des logs avec timestamp lisible et format personnalisé
// - Transports : écriture dans fichier journalier + console avec couleurs
const logger = createLogger({
level: "info",
format: format.combine(
format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
format.printf(({ timestamp, level, message }) =>
`${timestamp} | ${level.toUpperCase()} | ${message}`
)
),
transports: [
// Rotation quotidienne des fichiers de logs
new DailyRotateFile({
dirname: logDir, // dossier cible pour les logs
filename: "LOGS-%DATE%.log", // nom du fichier par date
datePattern: "YYYY-MM-DD", // pattern de date dans le nom
zippedArchive: false, // ne pas compresser les archives
maxFiles: "7d", // conserve 14 jours dhistorique
format: format.combine(
format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
format.printf(({ timestamp, level, message }) =>
`${timestamp} | ${level.toUpperCase()} | ${message}`
)
),
options: { flags: "a" }, // mode ajout à la fin (append)
}),
// Affichage des logs en console avec coloration et format simple
new transports.Console({
format: format.combine(
format.colorize(),
format.timestamp({ format: "HH:mm:ss" }),
format.printf(({ timestamp, level, message }) =>
`${timestamp} | ${level} | ${message}`
)
),
}),
],
});
// Helper pour marquer clairement le début de session dans les logs
function logSessionStart() {
logger.info(getSessionStartLine());
}
// Helper pour marquer la fin de session dans les logs
function logSessionEnd() {
logger.info(getSessionEndLine());
}
// Export du logger principal et des helpers pour usage dans dautres modules
module.exports = {
logger,
logSessionStart,
logSessionEnd,
logDir,
};

View File

@@ -0,0 +1,205 @@
/*
This file is part of Freedom Loader.
Copyright (C) 2025 MasterAcnolo
Freedom Loader is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License.
Freedom Loader is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
*/
const express = require("express");
const router = express.Router();
const { execFile } = require("child_process");
const path = require("path");
const fs = require("fs");
const { Notification } = require("electron");
const logger = require("../logger").logger;
// Path vers le fichier exécutable yt-dlp (outil tiers pour le téléchargement)
// const ytDlpPath = path.join(process.resourcesPath, '../../yt-dlp.exe');
const ytDlpPath = path.join(process.resourcesPath, 'yt-dlp.exe');
execFile(ytDlpPath, ["-U"], (err, stdout, stderr) => {
if (err) {
logger.error("Erreur update yt-dlp:", err);
return;
}
logger.info(`Update yt-dlp : ${stdout}`);
});
router.post("/", (req, res) => {
try {
// Récupération des options envoyées par le frontend via le formulaire
// On vérifie la présence de l'URL, le choix audioOnly, et la qualité demandée
const options = {
url: req.body.url,
audioOnly: req.body.audioOnly === "1",
quality: req.body.quality || "best",
};
// Validation simple : si l'URL est manquante, on rejette la requête
if (!options.url) {
logger.warn("Requête POST /download sans URL");
return res.status(400).send("❌ URL manquante !");
}
// Récupération du chemin de sauvegarde personnalisé envoyé depuis Electron,
// ou on utilise un chemin par défaut stocké dans app.locals
let requestedOutputFolder = req.body.savePath || req.app.locals.outputFolder;
// On normalise le chemin pour éviter des erreurs liées aux séparateurs ou chemins relatifs
requestedOutputFolder = path.normalize(requestedOutputFolder);
/*
Sécurité basique : On refuse certains chemins sensibles ou suspects
(ex: System32, /etc, Windows) pour éviter que l'application n'écrive dans des dossiers système.
On vérifie aussi la longueur minimale du chemin pour éviter des valeurs vides ou invalides.
*/
if (
!requestedOutputFolder ||
requestedOutputFolder.length < 3 ||
requestedOutputFolder.includes("System32") ||
requestedOutputFolder.includes("/etc") ||
requestedOutputFolder.includes("\\Windows")
) {
logger.warn(`Chemin potentiellement dangereux refusé : ${requestedOutputFolder}`);
return res.status(400).send("❌ Chemin de sauvegarde non autorisé.");
}
// Vérifie que le dossier de sortie existe, sinon le crée récursivement
if (!fs.existsSync(requestedOutputFolder)) {
fs.mkdirSync(requestedOutputFolder, { recursive: true });
logger.info(`Dossier de sortie créé : ${requestedOutputFolder}`);
}
// Construction du template de sortie pour yt-dlp (nom de fichier basé sur le titre)
const outputTemplate = path.join(requestedOutputFolder, "%(title)s.%(ext)s");
// Construction du tableau d'arguments à passer à yt-dlp
// --no-continue pour forcer un téléchargement propre
// --restrict-filenames pour éviter les caractères problématiques dans les noms
const args = [
"--no-continue", // pas de reprise, c'est un choix que j'ai fait voila
//"--restrict-filenames", // noms de fichiers sans caractères spéciaux (Cyrilique, accents, etc.)
"--no-overwrites", // évite d'écraser un fichier existant
"--embed-thumbnail", // ajoute la pochette
"--add-metadata", // ajoute les tags (titre, artiste, etc.)
"--concurrent-fragments", "8",// accélère le téléchargement
"--retries", "10", // réessaie jusqu'à 10 fois en cas d'erreur
"--fragment-retries", "10" , // réessaie aussi 10 fois chaque fragment
"--ffmpeg-location", path.join(process.resourcesPath, "ffmpeg.exe")
];
// Si l'option audioOnly est activée, on ajoute les flags pour extraction audio en mp3
if (options.audioOnly) {
args.push(
"-f",
"bestaudio",
"--extract-audio",
"--audio-format",
"mp3",
);
}
// Correspondance entre la qualité choisie et le format à demander à yt-dlp
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]",
};
// Sélection du format basé sur la qualité, par défaut 'best' si option invalide
const format = qualityMap[options.quality] || "best";
args.push("-f", format);
// Ajout du template de sortie et de l'URL à télécharger
args.push("-o", outputTemplate);
args.push(options.url);
// Log de la commande complète pour traçabilité et débogage
logger.info(`Téléchargement demandé : url=${options.url}, audioOnly=${options.audioOnly}, quality=${options.quality}`);
logger.info(`Commande yt-dlp : ${ytDlpPath} ${args.join(" ")}`);
// Lancement du processus yt-dlp avec les arguments définis
const child = execFile(ytDlpPath, args);
/*
Gestion des sorties standards (stdout) du processus,
on log chaque ligne pour suivre la progression ou informations diverses
*/
child.stdout.on("data", (data) => {
data.toString().split("\n").forEach(line => {
if (line.trim()) logger.info(`[yt-dlp stdout] ${line.trim()}`);
});
});
/*
Gestion des erreurs (stderr) du processus,
on log chaque ligne d'erreur pour diagnostic
*/
child.stderr.on("data", (data) => {
data.toString().split("\n").forEach(line => {
if (line.trim()) logger.error(`[yt-dlp stderr] ${line.trim()}`);
});
});
/*
En cas d'erreur lors du lancement du processus yt-dlp,
on log l'erreur et on répond au client avec un statut 500
*/
child.on("error", (err) => {
logger.error(`Erreur lancement yt-dlp : ${err.message}`);
res.status(500).send(`❌ Erreur lors de l'exécution : ${err.message}`);
});
/*
Quand le processus se termine, on vérifie le code de sortie.
Code 0 = succès, on informe le client que le téléchargement est terminé.
Sinon, on envoie une erreur au client avec le code d'échec.
*/
child.on("close", (code) => {
logger.info(`yt-dlp terminé avec code de sortie : ${code}`);
if (code === 0) {
const iconPath = path.join(process.resourcesPath, "confirm-icon.png"); // la sécurité pour toutes les machines
console.log("Icon path pour la notif :", iconPath);
const notif = new Notification({
title: "Freedom Loader",
body: "Ton téléchargement est terminé, clique ici pour l'ouvrir.",
icon: iconPath,
});
notif.on("click", () => {
console.log("Notification cliquée !");
//Pour pouvoir ouvrir le dossier de la vidéo
const { shell } = require("electron");
shell.openPath(requestedOutputFolder);
});
notif.show();
res.send("✅ Téléchargement terminé !");
} else {
res.status(500).send(`❌ yt-dlp a échoué avec le code : ${code}`);
}
});
} catch (err) {
// Capture toute autre erreur serveur non prévue, log et réponse 500
logger.error(`Erreur serveur dans /download : ${err.message}`);
res.status(500).send(`Erreur serveur : ${err.message}`);
}
});
module.exports = router;

View File

@@ -0,0 +1,81 @@
/*
This file is part of Freedom Loader.
Copyright (C) 2025 MasterAcnolo
Freedom Loader is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License.
Freedom Loader is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
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
// Path absolu vers l'exécutable yt-dlp
const ytDlpPath = path.join(__dirname, '../../ressources/yt-dlp.exe');
// Vérification que yt-dlp.exe existe bien au lancement du module
if (!fs.existsSync(ytDlpPath)) {
logger.error(`❌ yt-dlp.exe introuvable à ${ytDlpPath}`);
throw new Error(`yt-dlp.exe introuvable à ${ytDlpPath}`);
}
// Route POST pour récupérer les métadonnées d'une vidéo via yt-dlp
router.post("/", (req, res) => {
const url = req.body.url;
// Validation basique : on refuse la requête sans URL
if (!url) {
logger.warn("Requête metadata sans URL");
return res.status(400).send("❌ URL manquante");
}
logger.info(`Requête metadata reçue pour ${url}`);
// Exécution de yt-dlp avec l'option --dump-json pour récupérer les infos vidéo
execFile(
ytDlpPath,
["--dump-json", url],
{ timeout: 10_000 }, // Timeout fixé à 10 secondes pour éviter blocage
(error, stdout, stderr) => {
if (error) {
// En cas d'erreur, on log l'erreur et le stderr, puis on renvoie un code 500
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 {
// yt-dlp peut retourner plusieurs JSON séparés par des sauts de ligne
// On split et parse chacun proprement
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))`);
// Si on a un seul élément, on renvoie directement l'objet JSON, sinon un tableau
res.json(infos.length === 1 ? infos[0] : infos);
} catch (e) {
// Si parsing JSON échoue, on log et renvoie une erreur 500
logger.error(`Erreur parsing JSON: ${e.message}`);
return res.status(500).send("❌ JSON illisible.");
}
}
);
});
module.exports = router;

106
OLD CODE/server/server.js Normal file
View File

@@ -0,0 +1,106 @@
/*
This file is part of Freedom Loader.
Copyright (C) 2025 MasterAcnolo
Freedom Loader is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License.
Freedom Loader is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
const express = require("express"); // Framework web pour créer lAPI et servir les fichiers
const { exec } = require("child_process"); // Permet dexécuter des commandes systèmes (pas utilisé directement ici)
const fs = require("fs"); // Module système pour gérer fichiers et dossiers
const path = require("path"); // Gestion propre des chemins de fichiers et dossiers
const debug = require("debug")("freedom-loader:server");
// Module de debug coloré en console, pratique pour dev
const { logger, logSessionStart, logSessionEnd } = require("./logger");
// Import du logger Winston et helpers pour marquer début/fin de session
const app = express(); // Création de linstance Express, notre serveur web
// Définition du dossier par défaut où enregistrer les téléchargements
// On prend le dossier Téléchargements de lutilisateur Windows (USERPROFILE)
const downloadsPath = path.join(process.env.USERPROFILE, "Downloads");
const outputFolder = path.join(downloadsPath, "Freedom Loader");
// Création du dossier de sortie sil nexiste pas déjà
if (!fs.existsSync(outputFolder)) {
try {
fs.mkdirSync(outputFolder, { recursive: true }); // création récursive au cas où
logger.info("Dossier Freedom Loader cree dans Telechargements.");
} catch (err) {
logger.error("Impossible de creer le dossier :", err);
process.exit(1); // Arrêt du programme si dossier non créé (critique)
}
} else {
logger.info("Dossier Freedom Loader deja existant.");
}
// On rend ce dossier accessible globalement via app.locals pour lutiliser dans les routes
app.locals.outputFolder = outputFolder;
// Middleware pour parser le corps des requêtes POST en application/x-www-form-urlencoded
app.use(express.urlencoded({ extended: true }));
// Définition du dossier contenant les fichiers statiques (frontend)
// Permet daccéder au HTML, CSS, JS côté client
const staticPath = path.join(__dirname, "../public");
debug("Serveur statique sur", staticPath);
app.use(express.static(staticPath));
// Import et enregistrement des routes API
// Ces routes gèrent les requêtes pour les infos vidéo et téléchargement
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 / qui sert la page principale index.html
app.get("/", (req, res) => {
debug("Requete GET / servie");
res.sendFile(path.join(__dirname, "../public/index.html"));
});
// Fonction pour démarrer le serveur Express
// Retourne une Promise pour pouvoir await le démarrage dans dautres modules
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); // Serveur prêt, on résout la promesse
});
// Gestion des erreurs serveur lors du démarrage
serverInstance.on("error", (err) => {
logger.error("Erreur serveur Express :", err);
reject(err); // Rejet de la promesse en cas derreur critique
});
});
}
// Gestion propre de la fermeture du process pour logger la fin de session
process.on("SIGINT", () => { // Capture Ctrl+C (interruption)
logSessionEnd();
process.exit();
});
process.on("SIGTERM", () => { // Capture kill ou arrêt du process
logSessionEnd();
process.exit();
});
// Export de la fonction startServer pour permettre son appel depuis dautres fichiers
module.exports = { startServer };
const { startRPC } = require("./discordRPC");
startRPC();