diff --git a/OLD CODE/build/apercu1.0.0.png b/OLD CODE/build/apercu1.0.0.png
deleted file mode 100644
index 9f0e1ee..0000000
Binary files a/OLD CODE/build/apercu1.0.0.png and /dev/null differ
diff --git a/OLD CODE/build/apercu1.0.1.png b/OLD CODE/build/apercu1.0.1.png
deleted file mode 100644
index 212f852..0000000
Binary files a/OLD CODE/build/apercu1.0.1.png and /dev/null differ
diff --git a/OLD CODE/main.js b/OLD CODE/main.js
deleted file mode 100644
index a835d2d..0000000
--- a/OLD CODE/main.js
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- 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 .
-*/
-
-const { app, BrowserWindow, ipcMain, dialog, Menu, shell } = require("electron");
-app.disableHardwareAcceleration(); // Désactive l'accélération GPU pour éviter les erreurs GPU sur certains systèmes
-const path = require("path");
-const os = require("os");
-const { logger, logSessionStart, logSessionEnd, logDir } = require("./server/logger");
-
-let mainWindow;
-// Utilise le vrai dossier de logs défini dans logger.js
-const logsFolderPath = logDir;
-app.setAppUserModelId("com.masteracnolo.freedomloader"); /* Pour la notif */
-/*
- Fonction principale qui crée la fenêtre principale de l'application.
- Elle évite la création multiple et configure les dimensions et options de la fenêtre.
- Charge l'URL locale du serveur Express et gère les erreurs éventuelles.
-*/
-async function createWindow() {
- logger.info("Création de la fenêtre...");
-
- if (mainWindow) {
- logger.warn("La fenêtre existe déjà, pas de nouvelle création");
- return;
- }
-
- mainWindow = new BrowserWindow({
- title: "Freedom Loader 1.2.4", // A Changer à chaque Version
- width: 750,
- height: 800,
- minWidth: 750,
- minHeight: 800,
- webPreferences: {
- nodeIntegration: false,
- contextIsolation: true,
- preload: path.join(__dirname, "preload.js"), // Chargement du preload script sécurisé
- },
- // titleBarStyle: 'hidden', // Option possible pour une barre de titre personnalisée
- });
-
- try {
- await mainWindow.loadURL("http://localhost:8080");
- logger.info("Fenêtre chargée");
- } catch (err) {
- logger.error("Erreur chargement fenêtre:", err);
- }
-
- // Événement déclenché à la fermeture de la fenêtre principale
- mainWindow.on("closed", () => {
- logger.info("Fenêtre principale fermée");
- mainWindow = null;
- });
-}
-
-/*
- Définition du chemin par défaut pour les téléchargements.
- Ici, on utilise le dossier 'Downloads' de l'utilisateur avec un sous-dossier spécifique à l'application.
-*/
-const defaultDownloadPath = path.join(os.homedir(), "Downloads", "Freedom Loader");
-
-/*
- Gestionnaire IPC qui permet au renderer de demander à l'utilisateur
- de sélectionner un dossier via une boîte de dialogue native.
- On retourne le chemin sélectionné ou null si annulation.
-*/
-ipcMain.handle("select-download-folder", async () => {
- logger.info("Demande de sélection d'un dossier reçue depuis le renderer");
- try {
- const result = await dialog.showOpenDialog({
- properties: ["openDirectory"]
- });
- if (!result.canceled && result.filePaths.length > 0) {
- logger.info(`Dossier sélectionné : ${result.filePaths[0]}`);
- return result.filePaths[0];
- }
- return null;
- } catch (err) {
- logger.error(`Erreur lors de la sélection de dossier : ${err.message}`);
- return null;
- }
-});
-
-/*
- Gestionnaire IPC pour exposer au renderer le chemin par défaut de téléchargement
- afin qu'il puisse l'afficher ou l'utiliser comme valeur initiale.
-*/
-ipcMain.handle("get-default-download-path", () => {
- return defaultDownloadPath;
-});
-
-/*
- Événement déclenché quand l'application est prête.
- On démarre le serveur Express, puis on crée la fenêtre principale.
- En cas d'erreur, on log et on quitte l'application proprement.
-*/
-app.whenReady().then(async () => {
- logSessionStart();
- logger.info("App prête, démarrage du serveur Express...");
- const expressServer = require("./server/server.js");
- try {
- await expressServer.startServer();
- logger.info("Serveur Express démarré");
- await createWindow();
-
- // Ajout du menu personnalisé
- const menuTemplate = [
- // ... Menu standard Electron ...
- {
- label: "Logs",
- submenu: [
- {
- label: "Ouvrir les logs",
- click: () => {
- shell.openPath(logsFolderPath);
- }
- }
- ]
- }
- ];
-
- // Fusionner avec le menu par défaut
- const defaultMenu = Menu.getApplicationMenu();
- let template = menuTemplate;
- if (defaultMenu) {
- template = [...Menu.getApplicationMenu().items.map(item => item), ...menuTemplate];
- }
- const finalMenu = Menu.buildFromTemplate(template);
- Menu.setApplicationMenu(finalMenu);
-
- } catch (error) {
- logger.error("Erreur serveur ou fenêtre :", error);
- app.quit();
- }
-});
-
-/*
- Quitte l'application lorsque toutes les fenêtres sont fermées,
- sauf sous macOS où il est habituel de garder l'application active.
-*/
-app.on("window-all-closed", () => {
- logger.info("Toutes fenêtres fermées, quitte l'app");
- if (process.platform !== "darwin") app.quit();
-});
-
-/*
- Avant de quitter l'application, on log la fin de session pour traçabilité.
-*/
-app.on("before-quit", () => {
- logSessionEnd();
-});
diff --git a/OLD CODE/public/assets/favicon.ico b/OLD CODE/public/assets/favicon.ico
deleted file mode 100644
index 679f4f9..0000000
Binary files a/OLD CODE/public/assets/favicon.ico and /dev/null differ
diff --git a/OLD CODE/public/assets/icons/GitHub Icon.ico b/OLD CODE/public/assets/icons/GitHub Icon.ico
deleted file mode 100644
index 798a024..0000000
Binary files a/OLD CODE/public/assets/icons/GitHub Icon.ico and /dev/null differ
diff --git a/OLD CODE/public/assets/icons/GitHub Icon.svg b/OLD CODE/public/assets/icons/GitHub Icon.svg
deleted file mode 100644
index a8d1174..0000000
--- a/OLD CODE/public/assets/icons/GitHub Icon.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/OLD CODE/public/assets/icons/file-audio-solid.svg b/OLD CODE/public/assets/icons/file-audio-solid.svg
deleted file mode 100644
index d0582af..0000000
--- a/OLD CODE/public/assets/icons/file-audio-solid.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/OLD CODE/public/script/chirac.js b/OLD CODE/public/script/chirac.js
deleted file mode 100644
index fab95b6..0000000
--- a/OLD CODE/public/script/chirac.js
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- 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 .
-*/
-
-function Chirac(){
- const title = document.getElementById("title");
- title.innerHTML = "Chirac Loader";
-
- const subtitle = document.getElementById("subtitle");
- subtitle.innerHTML = "J'aime les pommes";
- subtitle.style.color= "black";
-
- document.body.style.backgroundImage = "url('assets/images/goat2.webp')";
- document.body.style.backgroundSize = "cover";
- document.body.style.backgroundPosition = "center";
- document.body.style.backgroundAttachment = "fixed";
-
- console.log("Je serai le président de tous les français");
-}
\ No newline at end of file
diff --git a/OLD CODE/public/script/customthemes.js b/OLD CODE/public/script/customthemes.js
deleted file mode 100644
index 0510f8d..0000000
--- a/OLD CODE/public/script/customthemes.js
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- 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 .
-*/
-
-// Liste des thèmes disponibles (clé = nom, valeur = label affiché)
-const themes = {
- default: "Default",
- dark: "Sombre",
- light: "Clair",
- nf : "NF",
- chirac: "Chirac",
- fanatic: "Fanatic",
- cyberpunk: "Cyberpunk",
- spicy: "Spicy",
- vilbrequin: "Vilbrequin"
-};
-
-const themeSubtitles = {
- default: "Because why not?",
- dark: "Darkness is my ally",
- light: "Qui aime ce thème ?",
- nf : "You call it music, i call it my Therapist",
- chirac: "J'aime les pommes",
- fanatic: "Always Fnatic !",
- cyberpunk: "Wake up, choom. We’ve got a city to burn.",
- spicy: "The Spiciest One",
- vilbrequin: "Rend l'argent"
-};
-
-const themeSelect = document.getElementById("themeSelect");
-
-// Remplir le select avec les options
-function populateThemeSelect() {
- for (const [key, label] of Object.entries(themes)) {
- const option = document.createElement("option");
- option.value = key;
- option.textContent = label;
- themeSelect.appendChild(option);
- }
-}
-
-// Appliquer le thème sur le body et changer le subtitle
-function applyTheme(themeName) {
- // Supprime les classes thème précédentes
- document.body.classList.remove(...Object.keys(themes));
- // Ajoute la classe du thème actuel
- document.body.classList.add(themeName);
-
- // Modifier le subtitle
- const subtitle = document.getElementById("subtitle");
- if (subtitle && themeSubtitles[themeName]) {
- subtitle.textContent = themeSubtitles[themeName];
- }
-
- // Optionnel : animation spéciale Chirac
- if (themeName === "chirac") {
- requestAnimationFrame(() => {
- document.body.classList.add("theme-active");
- });
- } else {
- document.body.classList.remove("theme-active");
- }
-}
-
-// Sauvegarder le thème en localStorage
-function saveTheme(themeName) {
- localStorage.setItem("selectedTheme", themeName);
-}
-
-// Charger le thème au démarrage
-function loadTheme() {
- const savedTheme = localStorage.getItem("selectedTheme");
- if (savedTheme && themes[savedTheme]) {
- applyTheme(savedTheme);
- themeSelect.value = savedTheme;
- } else {
- applyTheme("dark");
- themeSelect.value = "dark";
- }
-}
-
-// Quand l’utilisateur change le select
-themeSelect.addEventListener("change", (e) => {
- const selected = e.target.value;
- if (themes[selected]) {
- applyTheme(selected);
- saveTheme(selected);
- }
-});
-
-// Initialisation
-populateThemeSelect();
-loadTheme();
diff --git a/OLD CODE/public/script/fetchinfo.js b/OLD CODE/public/script/fetchinfo.js
deleted file mode 100644
index 4287f97..0000000
--- a/OLD CODE/public/script/fetchinfo.js
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- 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 .
-*/
-
-// On attend que le DOM soit complètement chargé
-document.addEventListener("DOMContentLoaded", () => {
- // Récupération des éléments du DOM pour interaction
- const urlInput = document.getElementById("UrlInput");
- const infoDiv = document.getElementById("videoInfo");
-
- // Stocke la dernière URL fetchée pour éviter les requêtes répétées inutiles
- let lastFetchedUrl = "";
-
- // Active les logs DEBUG pour le développement
- const DEBUG = true;
-
- // Écoute les changements dans l'input URL
- urlInput.addEventListener("input", async () => {
- const url = urlInput.value.trim();
-
- if (DEBUG) console.log("[DEBUG] Input détecté :", url);
-
- // Si l'URL est vide ou trop courte, on reset l'affichage
- if (!url || url.length < 5) {
- if (DEBUG) console.log("[DEBUG] URL vide ou trop courte, reset affichage.");
- infoDiv.innerHTML = "";
- infoDiv.classList.remove("visible");
- lastFetchedUrl = "";
- return;
- }
-
- // Si l'URL est identique à la dernière requêtée, on ne refait pas la requête
- if (url === lastFetchedUrl) {
- if (DEBUG) console.log("[DEBUG] Même URL que précédemment, pas de fetch.");
- return;
- }
-
- // Mémorise l'URL actuelle comme dernière URL traitée
- lastFetchedUrl = url;
-
- try {
- if (DEBUG) console.log("[DEBUG] Envoi requête POST vers /info avec URL:", url);
-
- // Envoi de la requête POST pour récupérer les infos de la vidéo
- const res = await fetch("/info", {
- method: "POST",
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
- body: new URLSearchParams({ url }),
- });
-
- if (DEBUG) console.log("[DEBUG] Réponse status:", res.status);
-
- // Si la réponse HTTP est une erreur, on affiche un message
- if (!res.ok) {
- console.error(`[ERROR] Réponse HTTP ${res.status}: ${res.statusText}`);
- infoDiv.innerHTML = "❌ Impossible de récupérer les infos (erreur serveur).";
- infoDiv.classList.remove("visible");
- return;
- }
-
- // Parse la réponse JSON contenant les infos vidéo
- const data = await res.json();
- if (DEBUG) console.log("[DEBUG] Données reçues:", data);
-
- // Vérification simple que les données attendues sont présentes
- if (!data || !data.title || !data.thumbnail) {
- console.warn("[WARNING] Données incomplètes", data);
- infoDiv.innerHTML = "❌ Données incomplètes reçues.";
- infoDiv.classList.remove("visible");
- return;
- }
-
- // Formatage de la durée en minutes et secondes
- const minutes = Math.floor(data.duration / 60);
- const seconds = data.duration % 60;
- const durationStr = `${minutes}m ${seconds.toString().padStart(2, "0")}s`;
-
- // Formatage de la date d'upload au format JJ/MM/AAAA
- const date = data.upload_date || "";
- const readableDate = date
- ? `${date.slice(6, 8)}/${date.slice(4, 6)}/${date.slice(0, 4)}`
- : "Inconnue";
-
- // Conversion taille approximative en Mo
- const sizeMB = data.filesize_approx
- ? (data.filesize_approx / (1024 * 1024)).toFixed(2) + " Mo"
- : "Inconnue";
-
- // Gestion des catégories, affichage par défaut si absentes
- const categories = data.categories
- ? data.categories.join(", ")
- : "Non spécifiées";
-
- // Construction du HTML pour afficher les informations de la vidéo
- infoDiv.innerHTML = `
-
${data.title}
-
-
- Durée : ${durationStr}
- Uploader : ${data.uploader || "Inconnu"}
- Date d’upload : ${readableDate}
- Vues : ${data.view_count?.toLocaleString() || "?"}
- Likes : ${data.like_count?.toLocaleString() || "?"}
- URL : ${data.webpage_url}
- Channel : ${data.channel_url}
- Taille estimée : ${sizeMB}
- Catégories : ${categories}
-
- `;
-
- // Affiche la div contenant les infos
- infoDiv.classList.add("visible");
-
- if (DEBUG) console.log("[DEBUG] Info affichée avec succès.");
- } catch (e) {
- // En cas d'erreur réseau ou JSON, on affiche un message d'erreur
- console.error("[CRITICAL] Erreur lors de la récupération:", e);
- infoDiv.innerHTML = "❌ Erreur réseau ou JSON.";
- infoDiv.classList.remove("visible");
- }
- });
-});
diff --git a/OLD CODE/public/styles/layout/container.css b/OLD CODE/public/styles/layout/container.css
deleted file mode 100644
index bcf2d87..0000000
--- a/OLD CODE/public/styles/layout/container.css
+++ /dev/null
@@ -1,12 +0,0 @@
-.container, .infos-container {
- position: relative;
- top: 0;
- left: 0;
- right: 0;
- /* height: 10000px; */
- padding: 1em;
- z-index: 1000;
- display: flex;
- flex-direction: column;
- align-items: center;
-}
diff --git a/OLD CODE/public/styles/layout/ctr-infos.css b/OLD CODE/public/styles/layout/ctr-infos.css
deleted file mode 100644
index 1eb8e74..0000000
--- a/OLD CODE/public/styles/layout/ctr-infos.css
+++ /dev/null
@@ -1,14 +0,0 @@
-#downloadStatus {
-
- margin-top: 1em;
- min-height: 1.5em;
- font-weight: bold;
- text-align: center;
- color: var(--download-status-color);
-}
-
-label {
- display: flex;
- align-items: center;
- font-size: 0.9em;
-}
\ No newline at end of file
diff --git a/OLD CODE/public/styles/theme/cyberpunk-theme.css b/OLD CODE/public/styles/theme/cyberpunk-theme.css
deleted file mode 100644
index 0cecee6..0000000
--- a/OLD CODE/public/styles/theme/cyberpunk-theme.css
+++ /dev/null
@@ -1,45 +0,0 @@
-body.cyberpunk {
- /* Couleurs générales */
- --background-color: #0d0d0d; /* noir profond */
- --default-text-color: #e0e0e0; /* texte gris clair */
- --subtitle-color: #ffea00; /* turquoise néon pour les sous-titres */
- --mp3-text-color: #ffea00; /* jaune vif pour la checkbox */
-
- /* Formulaire */
- --form-bg-color: #141414; /* fond formulaire sombre */
- --form-input-bg-color: #1a1a1a; /* inputs sombres */
- --form-input-border-color: #333333; /* bordure sobre */
- --form-input-border-focus-color: #ffea00; /* focus jaune néon */
- --form-input-text-color: #e0e0e0; /* texte clair */
- --form-input-placeholder-color: #777777; /* placeholder plus doux */
- --form-button-bg-color: #ffea00; /* bouton jaune */
- --form-button-text-color: #0d0d0d; /* texte bouton noir */
- --form-button-bg-hover-color: #d4c000; /* hover jaune plus foncé */
-
- /* Checkbox */
- --checkbox-bg-default: #333333; /* fond checkbox sobre */
- --checkbox-bg-checked: #ffea00; /* coché en jaune vif */
- --checkbox-checkmark-border-color: #0d0d0d; /* checkmark noir */
- --checkbox-pulse-color: rgba(255, 234, 0, 0.5); /* pulse néon jaune */
-
- /* Download status */
- --download-status-color: #fffbd1; /* turquoise néon */
-
- /* Video Info Box */
- --infos-box-color: #1a1a1a; /* fond infos */
- --video-info-text-color: #e0e0e0; /* texte clair */
- --video-info-heading-color: #ffea00; /* titres turquoise néon */
- --video-info-list-color: #cccccc; /* texte liste gris clair */
- --video-info-list-strong-color: #ffffff; /* texte important blanc */
- --video-info-link-color: #ffea00; /* liens jaune néon */
- --video-info-link-hover-color: #d4c000; /* liens hover plus foncé */
-}
-
-body.cyberpunk {
- background-image: url('../../assets/images/Cyberpunk.webp');
- background-size: cover;
- background-position: center;
- background-repeat: no-repeat;
- background-attachment: fixed;
-}
-
diff --git a/OLD CODE/public/styles/theme/fanatic-theme.css b/OLD CODE/public/styles/theme/fanatic-theme.css
deleted file mode 100644
index 8553866..0000000
--- a/OLD CODE/public/styles/theme/fanatic-theme.css
+++ /dev/null
@@ -1,36 +0,0 @@
-body.fanatic{
- /* Couleurs générales */
- --background-color: #121212; /* fond bleu nuit/noir */
- --default-text-color: #eee; /* texte clair principal */
- --subtitle-color: #FF5900; /* sous-titres, accent orange vif */
- --mp3-text-color: #ffffff; /* texte checkbox inline */
-
- /* Formulaire */
- --form-bg-color: #1e1e1e; /* fond formulaire sombre */
- --form-input-bg-color: #2b2b2b; /* fond inputs sombres */
- --form-input-border-color: #333333; /* bordure inputs */
- --form-input-border-focus-color: #FF5900; /* bordure focus orange */
- --form-input-text-color: #eeeeee; /* texte inputs clair */
- --form-input-placeholder-color: #666666; /* placeholder gris foncé */
- --form-button-bg-color: #FF5900; /* bouton orange */
- --form-button-text-color: #121212; /* texte bouton sombre */
- --form-button-bg-hover-color: #e65500; /* bouton hover orange foncé */
-
- /* Checkbox */
- --checkbox-bg-default: #555555; /* gris moyen */
- --checkbox-bg-checked: #ff6600; /* orange vif */
- --checkbox-checkmark-border-color: #eee; /* blanc cassé */
- --checkbox-pulse-color: rgba(255, 102, 0, 0.5); /* pulse orange translucide */
-
- /* Download status */
- --download-status-color: #FF5900; /* orange */
-
- /* Video Info Box */
- --infos-box-color: #222222; /* fond infos sombre */
- --video-info-text-color: #eee; /* texte clair */
- --video-info-heading-color: #FF5900; /* titres orange */
- --video-info-list-color: #ccc; /* texte liste gris clair */
- --video-info-list-strong-color: #fff; /* texte important blanc */
- --video-info-link-color: #FF5900; /* liens orange */
- --video-info-link-hover-color: #e65500; /* liens hover orange foncé */
-}
diff --git a/OLD CODE/public/styles/theme/light-theme.css b/OLD CODE/public/styles/theme/light-theme.css
deleted file mode 100644
index cd562b7..0000000
--- a/OLD CODE/public/styles/theme/light-theme.css
+++ /dev/null
@@ -1,36 +0,0 @@
-body.light {
- /* Couleurs générales */
- --background-color: #dadada; /* fond clair */
- --default-text-color: #222222; /* texte sombre */
- --subtitle-color: #007bff; /* bleu accent */
- --mp3-text-color: #000000; /* texte checkbox */
-
- /* Formulaire */
- --form-bg-color: #ffffff; /* fond formulaire blanc */
- --form-input-bg-color: #fafafa; /* fond inputs */
- --form-input-border-color: #cccccc; /* bordure inputs */
- --form-input-border-focus-color: #007bff; /* focus bleu */
- --form-input-text-color: #333333; /* texte inputs */
- --form-input-placeholder-color: #888888; /* placeholder */
- --form-button-bg-color: #007bff; /* bouton bleu */
- --form-button-text-color: #ffffff; /* texte bouton */
- --form-button-bg-hover-color: #0056b3; /* hover bleu foncé */
-
- /* Checkbox */
- --checkbox-bg-default: #cccccc;
- --checkbox-bg-checked: #007bff;
- --checkbox-checkmark-border-color: #ffffff;
- --checkbox-pulse-color: rgba(0, 123, 255, 0.4);
-
- /* Download status */
- --download-status-color: #007bff;
-
- /* Video Info Box */
- --infos-box-color: #ffffff;
- --video-info-text-color: #222222;
- --video-info-heading-color: #007bff;
- --video-info-list-color: #444444;
- --video-info-list-strong-color: #000000;
- --video-info-link-color: #007bff;
- --video-info-link-hover-color: #0056b3;
-}
diff --git a/OLD CODE/public/styles/theme/nf-theme.css b/OLD CODE/public/styles/theme/nf-theme.css
deleted file mode 100644
index 198b6a7..0000000
--- a/OLD CODE/public/styles/theme/nf-theme.css
+++ /dev/null
@@ -1,48 +0,0 @@
-body.nf {
- /* Couleurs générales */
- --background-color: #1a1a1a; /* fond sombre neutre */
- --default-text-color: #e6dfd5; /* texte beige clair */
- --subtitle-color: #f5f0e6; /* beige très clair (titres) */
- --mp3-text-color: #d8cfc2; /* texte checkbox */
-
- /* Formulaire */
- --form-bg-color: #2a2a2a; /* fond formulaire gris sombre */
- --form-input-bg-color: #222222; /* fond inputs sombre */
- --form-input-border-color: #4a4036; /* bordure beige/brun */
- --form-input-border-focus-color: #cbb89d; /* beige doré en focus */
- --form-input-text-color: #f0e8dd; /* texte clair/beige */
- --form-input-placeholder-color: #9a8f80; /* placeholder beige/gris */
- --form-button-bg-color: #cbb89d; /* bouton beige doré */
- --form-button-text-color: #1a1a1a; /* texte sombre */
- --form-button-bg-hover-color: #b7a588; /* hover beige plus foncé */
-
- /* Checkbox */
- --checkbox-bg-default: #4a4036; /* brun/gris */
- --checkbox-bg-checked: #cbb89d; /* beige doré */
- --checkbox-checkmark-border-color: #1a1a1a; /* noir profond */
- --checkbox-pulse-color: rgba(203,184,157,0.3); /* halo beige doux */
-
- /* Download status */
- --download-status-color: #cbb89d; /* beige doré */
-
- /* Video Info Box */
- --infos-box-color: #2a2a2a; /* fond sombre */
- --video-info-text-color: #e6dfd5; /* texte beige clair */
- --video-info-heading-color: #f5f0e6; /* titres beige clair */
- --video-info-list-color: #d8cfc2; /* texte liste */
- --video-info-list-strong-color: #ffffff; /* texte fort blanc cassé */
- --video-info-link-color: #cbb89d; /* liens beige doré */
- --video-info-link-hover-color: #b7a588; /* hover beige plus foncé */
-}
-
-/* Fond inspiré de ton image beige */
-body.nf {
- background-image: url('../../assets/images/NF.jpg'); /* mets ton image ici */
- background-size: cover;
- background-position: 70% center;
- background-attachment: fixed;
-}
-
-body.nf .download-path {
- color: var(--default-text-color);
-}
diff --git a/OLD CODE/public/styles/theme/spicy-theme.css b/OLD CODE/public/styles/theme/spicy-theme.css
deleted file mode 100644
index 948f133..0000000
--- a/OLD CODE/public/styles/theme/spicy-theme.css
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
-
-
-
-*/
-
-body.spicy {
- /* Couleurs générales */
- --background-color: #121212; /* fond noir profond */
- --default-text-color: #581010; /* texte clair */
- --subtitle-color: #3d0909; /* accent rouge piment */
- --mp3-text-color: #ffffff; /* texte checkbox inline */
-
- /* Formulaire */
- --form-bg-color: #1a1a1a; /* fond formulaire gris très foncé */
- --form-input-bg-color: #2b2b2b; /* fond inputs gris foncé */
- --form-input-border-color: #444444; /* bordure gris */
- --form-input-border-focus-color: #ff1a1a; /* focus rouge piment */
- --form-input-text-color: #eeeeee; /* texte inputs */
- --form-input-placeholder-color: #888888; /* placeholder gris moyen */
- --form-button-bg-color: #ff1a1a; /* bouton rouge vif */
- --form-button-text-color: #ffffff; /* texte bouton blanc */
- --form-button-bg-hover-color: #cc0000; /* hover rouge foncé */
-
- /* Checkbox */
- --checkbox-bg-default: #555555; /* gris moyen */
- --checkbox-bg-checked: #ff1a1a; /* rouge piment */
- --checkbox-checkmark-border-color: #f5f5f5; /* blanc cassé */
- --checkbox-pulse-color: rgba(255, 26, 26, 0.5); /* pulse rouge translucide */
-
- /* Download status */
- --download-status-color: #ffffff; /* rouge vif */
-
- /* Video Info Box */
- --infos-box-color: #1a1a1a; /* fond infos gris foncé */
- --video-info-text-color: #f5f5f5; /* texte clair */
- --video-info-heading-color: #ff1a1a; /* titres rouges */
- --video-info-list-color: #cccccc; /* texte liste gris clair */
- --video-info-list-strong-color: #ffffff; /* texte important blanc */
- --video-info-link-color: #ff1a1a; /* liens rouges */
- --video-info-link-hover-color: #cc0000; /* liens hover rouge foncé */
-}
-
-/* Optionnel: background piments */
-body.spicy {
- background-image: url('../../assets/images/spicy.webp');
- background-size:contain;
- background-position: center;
- background-attachment: fixed;
- background-repeat: no-repeat;
-}
\ No newline at end of file
diff --git a/OLD CODE/public/styles/theme/vilbrequin-theme.css b/OLD CODE/public/styles/theme/vilbrequin-theme.css
deleted file mode 100644
index ec9df97..0000000
--- a/OLD CODE/public/styles/theme/vilbrequin-theme.css
+++ /dev/null
@@ -1,48 +0,0 @@
-body.vilbrequin {
- /* Couleurs générales */
- --background-color: #0a1f3d; /* bleu Vilbrequin profond */
- --default-text-color: #535353; /* texte clair */
- --subtitle-color: #001696; /* bleu clair accent (turbo vibes) */
- --mp3-text-color: #ffffff; /* texte checkbox inline */
-
- /* Formulaire */
- --form-bg-color: #12284f; /* fond formulaire bleu foncé */
- --form-input-bg-color: #1c3760; /* fond inputs bleu + clair */
- --form-input-border-color: #2a4b80; /* bordure bleue */
- --form-input-border-focus-color: #00bfff; /* focus bleu clair */
- --form-input-text-color: #f0f0f0; /* texte inputs clair */
- --form-input-placeholder-color: #b0c4de; /* placeholder gris bleuté */
- --form-button-bg-color: #ffd700; /* bouton jaune turbo */
- --form-button-text-color: #0a1f3d; /* texte bouton sombre */
- --form-button-bg-hover-color: #e6c200; /* hover jaune foncé */
-
- /* Checkbox */
- --checkbox-bg-default: #2a4b80; /* bleu moyen */
- --checkbox-bg-checked: #00bfff; /* bleu clair accent */
- --checkbox-checkmark-border-color: #f0f0f0; /* blanc cassé */
- --checkbox-pulse-color: rgba(0,191,255,0.5); /* pulse bleu clair translucide */
-
- /* Download status */
- --download-status-color: #ffd700; /* jaune turbo */
-
- /* Video Info Box */
- --infos-box-color: #12284f; /* fond infos bleu foncé */
- --video-info-text-color: #f0f0f0; /* texte clair */
- --video-info-heading-color: #00bfff; /* titres bleu clair */
- --video-info-list-color: #dcdcdc; /* texte liste gris clair */
- --video-info-list-strong-color: #ffffff; /* texte important blanc */
- --video-info-link-color: #00bfff; /* liens bleu clair */
- --video-info-link-hover-color: #009acd; /* liens hover bleu plus foncé */
-}
-
-/* Optionnel: motif turbo ou jantes */
-body.vilbrequin {
- background-image: url('../../assets/images/Vilbrequin.webp'); /* adapte ton image */
- background-size: cover;
- background-position: center;
- background-attachment: fixed;
-}
-
-body.vilbrequin .download-path{
- color: white;
-}
\ No newline at end of file
diff --git a/OLD CODE/public/styles/variables.css b/OLD CODE/public/styles/variables.css
deleted file mode 100644
index 7061e41..0000000
--- a/OLD CODE/public/styles/variables.css
+++ /dev/null
@@ -1,36 +0,0 @@
-:root {
- /* Couleurs générales */
- --background-color: #001224; /* body background bleu nuit */
- --default-text-color: #eee; /* texte clair principal */
- --subtitle-color: #007bff; /* sous-titres, header p */
- --mp3-text-color: #000000; /* couleur texte checkbox inline */
-
- /* Formulaire */
- --form-bg-color: #ffffff; /* fond du formulaire */
- --form-input-bg-color: #f9f9f9; /* fond inputs */
- --form-input-border-color: #cccccc; /* bordure inputs */
- --form-input-border-focus-color: #0056b3; /* bordure focus */
- --form-input-text-color: #333333; /* texte inputs */
- --form-input-placeholder-color: #aaaaaa; /* placeholder */
- --form-button-bg-color: #007bff; /* bouton bg */
- --form-button-text-color: #ffffff; /* texte bouton */
- --form-button-bg-hover-color: #0056b3; /* bouton hover */
-
- /* Checkbox */
- --checkbox-bg-default: #cccccc;
- --checkbox-bg-checked: #3B82F6;
- --checkbox-checkmark-border-color: #E0E0E2;
- --checkbox-pulse-color: rgba(59, 130, 246, 0.5);
-
- /* Download status */
- --download-status-color: #007bff;
-
- /* Video Info Box */
- --infos-box-color: #f0f0f0;
- --video-info-text-color: #222222;
- --video-info-heading-color: #007bff;
- --video-info-list-color: #444444;
- --video-info-list-strong-color: #000000;
- --video-info-link-color: #007bff;
- --video-info-link-hover-color: #0056b3;
-}
diff --git a/OLD CODE/ressources/ffmpeg.exe b/OLD CODE/ressources/ffmpeg.exe
deleted file mode 100644
index 588d161..0000000
Binary files a/OLD CODE/ressources/ffmpeg.exe and /dev/null differ
diff --git a/OLD CODE/ressources/ffprobe.exe b/OLD CODE/ressources/ffprobe.exe
deleted file mode 100644
index 5d52e65..0000000
Binary files a/OLD CODE/ressources/ffprobe.exe and /dev/null differ
diff --git a/OLD CODE/server/logger.js b/OLD CODE/server/logger.js
deleted file mode 100644
index c795e39..0000000
--- a/OLD CODE/server/logger.js
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- 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 .
-*/
-
-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 s’il n’existe 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 d’une 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 d’une 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 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" }, // 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 d’autres modules
-module.exports = {
- logger,
- logSessionStart,
- logSessionEnd,
- logDir,
-};
diff --git a/OLD CODE/server/routes/download.js b/OLD CODE/server/routes/download.js
deleted file mode 100644
index 04f93d9..0000000
--- a/OLD CODE/server/routes/download.js
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- 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;
diff --git a/OLD CODE/server/routes/info.js b/OLD CODE/server/routes/info.js
deleted file mode 100644
index a14fffb..0000000
--- a/OLD CODE/server/routes/info.js
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- 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 .
-*/
-
-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;
diff --git a/OLD CODE/server/server.js b/OLD CODE/server/server.js
deleted file mode 100644
index daa9cce..0000000
--- a/OLD CODE/server/server.js
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- 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 .
-*/
-
-const express = require("express"); // Framework web pour créer l’API et servir les fichiers
-const { exec } = require("child_process"); // Permet d’exé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 l’instance 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 l’utilisateur Windows (USERPROFILE)
-const downloadsPath = path.join(process.env.USERPROFILE, "Downloads");
-const outputFolder = path.join(downloadsPath, "Freedom Loader");
-
-// Création du dossier de sortie s’il n’existe 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 l’utiliser 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 d’accé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 d’autres 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 d’erreur 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 d’autres fichiers
-module.exports = { startServer };
-
-const { startRPC } = require("./discordRPC");
-startRPC();
diff --git a/OLD CODE/build/apercu1.2.4.png b/build/apercu1.2.4.png
similarity index 100%
rename from OLD CODE/build/apercu1.2.4.png
rename to build/apercu1.2.4.png
diff --git a/OLD CODE/build/confirm-icon.png b/build/confirm-icon.png
similarity index 100%
rename from OLD CODE/build/confirm-icon.png
rename to build/confirm-icon.png
diff --git a/OLD CODE/build/example-developertools.png b/build/example-developertools.png
similarity index 100%
rename from OLD CODE/build/example-developertools.png
rename to build/example-developertools.png
diff --git a/OLD CODE/build/installer-icon.ico b/build/installer-icon.ico
similarity index 100%
rename from OLD CODE/build/installer-icon.ico
rename to build/installer-icon.ico
diff --git a/OLD CODE/build/uninstaller-icon.ico b/build/uninstaller-icon.ico
similarity index 100%
rename from OLD CODE/build/uninstaller-icon.ico
rename to build/uninstaller-icon.ico
diff --git a/OLD CODE/forge.config.js b/forge.config.js
similarity index 58%
rename from OLD CODE/forge.config.js
rename to forge.config.js
index fa4a113..dcbb74d 100644
--- a/OLD CODE/forge.config.js
+++ b/forge.config.js
@@ -2,35 +2,16 @@ const { FusesPlugin } = require('@electron-forge/plugin-fuses');
const { FuseV1Options, FuseVersion } = require('@electron/fuses');
module.exports = {
- packagerConfig: {
- asar: true,
- },
+ 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
+ { name: '@electron-forge/plugin-auto-unpack-natives', config: {} },
new FusesPlugin({
version: FuseVersion.V1,
[FuseV1Options.RunAsNode]: false,
diff --git a/main.js b/main.js
new file mode 100644
index 0000000..fa1b9e9
--- /dev/null
+++ b/main.js
@@ -0,0 +1,124 @@
+/*
+ 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 .
+*/
+
+const { app, BrowserWindow, ipcMain, dialog, Menu, shell } = require("electron");
+const path = require("path");
+const os = require("os");
+const { logger, logSessionStart, logSessionEnd, logDir } = require("./server/logger");
+
+app.disableHardwareAcceleration(); // safe sur GPU old / soucis Electron
+
+let mainWindow;
+const logsFolderPath = logDir;
+const defaultDownloadPath = path.join(os.homedir(), "Downloads", "Freedom Loader");
+
+app.setAppUserModelId("com.masteracnolo.freedomloader"); // pour notifications Windows
+
+// ---------- FENÊTRE ---------- //
+async function createWindow() {
+ if (mainWindow) {
+ logger.warn("La fenêtre existe déjà, pas de nouvelle création");
+ return;
+ }
+
+ mainWindow = new BrowserWindow({
+ title: "Freedom Loader 1.3.0",
+ width: 750,
+ height: 800,
+ minWidth: 750,
+ minHeight: 800,
+ webPreferences: {
+ nodeIntegration: false,
+ contextIsolation: true,
+ preload: path.join(__dirname, "preload.js"),
+ },
+ });
+
+ try {
+ await mainWindow.loadURL(`http://localhost:8787`);
+ logger.info("Fenêtre chargée");
+ } catch (err) {
+ logger.error("Erreur chargement fenêtre:", err);
+ }
+
+ mainWindow.on("closed", () => {
+ logger.info("Fenêtre principale fermée");
+ mainWindow = null;
+ });
+}
+
+// IPC
+ipcMain.handle("select-download-folder", async () => {
+ try {
+ const result = await dialog.showOpenDialog({ properties: ["openDirectory"] });
+ if (!result.canceled && result.filePaths.length > 0) {
+ logger.info(`Dossier sélectionné : ${result.filePaths[0]}`);
+ return result.filePaths[0];
+ }
+ return null;
+ } catch (err) {
+ logger.error(`Erreur lors de la sélection de dossier : ${err.message}`);
+ return null;
+ }
+});
+
+ipcMain.handle("get-default-download-path", () => defaultDownloadPath);
+
+function setupMenu() {
+ const menuTemplate = [
+ {
+ label: "Logs",
+ submenu: [
+ {
+ label: "Ouvrir les logs",
+ click: () => shell.openPath(logsFolderPath),
+ },
+ ],
+ },
+ ];
+
+ const defaultMenu = Menu.getApplicationMenu();
+ const mergedTemplate = defaultMenu
+ ? [...defaultMenu.items.map(item => item), ...menuTemplate]
+ : menuTemplate;
+
+ Menu.setApplicationMenu(Menu.buildFromTemplate(mergedTemplate));
+}
+
+app.whenReady().then(async () => {
+ logSessionStart();
+ logger.info("App prête, démarrage du serveur Express...");
+
+ const expressServer = require("./server/server.js");
+ try {
+ await expressServer.startServer();
+ logger.info("Serveur Express démarré");
+ await createWindow();
+ setupMenu();
+ } catch (err) {
+ logger.error("Erreur serveur ou fenêtre :", err);
+ app.quit();
+ }
+});
+
+app.on("window-all-closed", () => {
+ logger.info("Toutes fenêtres fermées, quitte l'app");
+ if (process.platform !== "darwin") app.quit();
+});
+
+app.on("before-quit", () => logSessionEnd());
diff --git a/OLD CODE/preload.js b/preload.js
similarity index 100%
rename from OLD CODE/preload.js
rename to preload.js
diff --git a/OLD CODE/public/assets/images/Chirac.png b/public/assets/images/Chirac.png
similarity index 100%
rename from OLD CODE/public/assets/images/Chirac.png
rename to public/assets/images/Chirac.png
diff --git a/OLD CODE/public/assets/images/Cyberpunk.webp b/public/assets/images/Cyberpunk.webp
similarity index 100%
rename from OLD CODE/public/assets/images/Cyberpunk.webp
rename to public/assets/images/Cyberpunk.webp
diff --git a/public/assets/images/Drift.jpg b/public/assets/images/Drift.jpg
new file mode 100644
index 0000000..9fe9e29
Binary files /dev/null and b/public/assets/images/Drift.jpg differ
diff --git a/OLD CODE/public/assets/images/NF.jpg b/public/assets/images/NF.jpg
similarity index 100%
rename from OLD CODE/public/assets/images/NF.jpg
rename to public/assets/images/NF.jpg
diff --git a/OLD CODE/public/assets/images/Vilbrequin.webp b/public/assets/images/Vilbrequin.webp
similarity index 100%
rename from OLD CODE/public/assets/images/Vilbrequin.webp
rename to public/assets/images/Vilbrequin.webp
diff --git a/OLD CODE/public/assets/images/spicy.webp b/public/assets/images/spicy.webp
similarity index 100%
rename from OLD CODE/public/assets/images/spicy.webp
rename to public/assets/images/spicy.webp
diff --git a/OLD CODE/public/index.html b/public/index.html
similarity index 96%
rename from OLD CODE/public/index.html
rename to public/index.html
index ab90727..248ba02 100644
--- a/OLD CODE/public/index.html
+++ b/public/index.html
@@ -14,8 +14,6 @@
-
-
diff --git a/OLD CODE/public/script/custompath.js b/public/script/custompath.js
similarity index 100%
rename from OLD CODE/public/script/custompath.js
rename to public/script/custompath.js
diff --git a/public/script/customthemes.js b/public/script/customthemes.js
new file mode 100644
index 0000000..e2228a9
--- /dev/null
+++ b/public/script/customthemes.js
@@ -0,0 +1,92 @@
+/*
+ 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 .
+*/
+
+// Définition des thèmes disponibles
+// Chaque thème a un label (affiché dans le select) et un subtitle (texte sous le titre)
+const themes = {
+ default: { label: "Default", subtitle: "Because why not?" },
+ dark: { label: "Sombre", subtitle: "Darkness is my ally" },
+ light: { label: "Clair", subtitle: "Qui aime ce thème ?" },
+ neon: { label: "Néon", subtitle: "How was your day ?"},
+ nf: { label: "NF", subtitle: "You call it music, i call it my Therapist" },
+ drift: { label: "Drift", subtitle: "Si la route t'appelle, contre appel" },
+ fanatic: { label: "Fanatic", subtitle: "Always Fnatic !" },
+ cyberpunk: { label: "Cyberpunk", subtitle: "Wake up, choom. We’ve got a city to burn." },
+ chirac: { label: "Chirac", subtitle: "J'aime les pommes" },
+ spicy: { label: "Spicy", subtitle: "The Spiciest One" },
+ vilbrequin: { label: "Vilbrequin", subtitle: "Rend l'argent" }
+};
+
+
+const themeSelect = document.getElementById("themeSelect");
+
+// Remplir le select avec les options à partir du dictionnaire
+function populateThemeSelect() {
+ for (const [themeKey, themeInfo] of Object.entries(themes)) {
+ const option = document.createElement("option");
+ option.value = themeKey;
+ option.textContent = themeInfo.label;
+ themeSelect.appendChild(option);
+ }
+}
+
+// Appliquer un thème sur le body et mettre à jour le subtitle
+function applyTheme(themeKey) {
+ // Supprimer les classes de tous les thèmes
+ document.body.classList.remove(...Object.keys(themes));
+ // Ajouter la classe correspondant au thème sélectionné
+ document.body.classList.add(themeKey);
+
+ // Mettre à jour le subtitle
+ const subtitleElement = document.getElementById("subtitle");
+ if (subtitleElement && themes[themeKey]) {
+ subtitleElement.textContent = themes[themeKey].subtitle;
+ }
+
+}
+
+// Sauvegarder le thème choisi dans le navigateur
+function saveTheme(themeKey) {
+ localStorage.setItem("selectedTheme", themeKey);
+}
+
+// Charger le thème sauvegardé au démarrage
+function loadTheme() {
+ const savedTheme = localStorage.getItem("selectedTheme");
+
+ if (savedTheme && themes[savedTheme]) {
+ applyTheme(savedTheme);
+ themeSelect.value = savedTheme;
+ } else {
+ applyTheme("dark"); // thème par défaut
+ themeSelect.value = "dark";
+ }
+}
+
+// Quand l'utilisateur change le thème depuis le select
+themeSelect.addEventListener("change", (event) => {
+ const selectedTheme = event.target.value;
+ if (themes[selectedTheme]) {
+ applyTheme(selectedTheme);
+ saveTheme(selectedTheme);
+ }
+});
+
+// Initialisation
+populateThemeSelect();
+loadTheme();
\ No newline at end of file
diff --git a/OLD CODE/public/script/downloadstatus.js b/public/script/downloadstatus.js
similarity index 59%
rename from OLD CODE/public/script/downloadstatus.js
rename to public/script/downloadstatus.js
index 9f30b03..315cbc2 100644
--- a/OLD CODE/public/script/downloadstatus.js
+++ b/public/script/downloadstatus.js
@@ -16,56 +16,38 @@
along with this program. If not, see .
*/
-// Récupération du formulaire de téléchargement
const form = document.getElementById("downloadForm");
-
-// Élément où on affichera l’état du téléchargement (en cours, erreur, succès)
const statusDiv = document.getElementById("downloadStatus");
-
-// Récupération du bouton de soumission pour pouvoir le désactiver pendant la requête
const button = form.querySelector("button");
-// Écouteur d’événement pour intercepter la soumission du formulaire
form.addEventListener("submit", async (e) => {
- e.preventDefault(); // Empêche le rechargement automatique de la page
-
- // Désactive le bouton pour éviter les clics multiples pendant la requête
- button.disabled = true;
-
- // Affiche un message d’attente pour informer l’utilisateur
+ e.preventDefault();
+ button.disabled = true; // Empêche les clics multiples
statusDiv.textContent = "Téléchargement en cours...";
- // Prépare les données du formulaire au format URL-encoded
const formData = new FormData(form);
const params = new URLSearchParams(formData);
try {
- // Envoie la requête POST vers /download avec les données du formulaire
const res = await fetch("/download", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: params,
});
- // Si la réponse n’est pas un succès (code HTTP 4xx ou 5xx)
if (!res.ok) {
statusDiv.textContent = "❌ Erreur pendant le téléchargement.";
return;
}
- // Récupère le texte envoyé par le serveur (message de succès ou erreur)
const text = await res.text();
-
- // Affiche le message retourné par le serveur
statusDiv.textContent = text;
+
} catch {
- // Gestion des erreurs réseau ou autres exceptions
statusDiv.textContent = "❌ Une erreur s’est produite.";
} finally {
- // Réactive le bouton pour permettre d’autres soumissions
button.disabled = false;
- // Efface le message d’état après 5 secondes pour garder l’interface propre
setTimeout(() => {
statusDiv.textContent = "";
}, 5000);
diff --git a/public/script/fetchinfo.js b/public/script/fetchinfo.js
new file mode 100644
index 0000000..16d768e
--- /dev/null
+++ b/public/script/fetchinfo.js
@@ -0,0 +1,122 @@
+/*
+ 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 .
+*/
+
+function formatDate(dateStr) {
+ if (!dateStr || dateStr.length !== 8) return "Inconnue";
+ return `${dateStr.slice(6,8)}/${dateStr.slice(4,6)}/${dateStr.slice(0,4)}`;
+}
+
+function formatSize(bytes) {
+ return bytes ? (bytes / (1024*1024)).toFixed(2) + " Mo" : "Inconnue";
+}
+
+async function fetchVideoInfo(url) {
+ try {
+ const res = await fetch("/info", {
+ method: "POST",
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
+ body: new URLSearchParams({ url }),
+ });
+
+ if (!res.ok) return { error: `Erreur serveur: ${res.status}` };
+
+ const data = await res.json();
+ if (!data) return { error: "Données manquantes" };
+
+ return data;
+ } catch (e) {
+ return { error: "Erreur réseau ou JSON" };
+ }
+}
+
+document.addEventListener("DOMContentLoaded", () => {
+ const urlInput = document.getElementById("UrlInput");
+ const infoDiv = document.getElementById("videoInfo");
+ let lastFetchedUrl = "";
+
+ urlInput.addEventListener("input", async () => {
+ const url = urlInput.value.trim();
+
+ if (!url || url.length < 5) {
+ infoDiv.innerHTML = "";
+ infoDiv.classList.remove("visible");
+ lastFetchedUrl = "";
+ return;
+ }
+
+ if (url === lastFetchedUrl) return;
+ lastFetchedUrl = url;
+
+ const data = await fetchVideoInfo(url);
+ if (data.error) {
+ infoDiv.innerHTML = `❌ ${data.error}`;
+ infoDiv.classList.remove("visible");
+ return;
+ }
+
+ // Playlist
+ if (data.type === "playlist") {
+ infoDiv.innerHTML = `
+ Playlist détectée – ${data.count} vidéos
+ ${data.title}
+ Channel : ${data.channel || "Inconnu"}
+
+ `;
+ const listDiv = document.getElementById("playlistVideos");
+ data.videos.forEach(v => {
+ const durationStr = `${Math.floor(v.duration/60)}m ${(v.duration%60).toString().padStart(2,"0")}s`;
+ listDiv.innerHTML += `
+
+ `;
+ });
+ infoDiv.classList.add("visible");
+ return;
+ }
+
+
+ // Vidéo normale
+ const durationStr = `${Math.floor(data.duration/60)}m ${(data.duration%60)
+ .toString()
+ .padStart(2,"0")}s`;
+ const sizeStr = formatSize(data.filesize_approx);
+ const readableDate = formatDate(data.upload_date);
+ const categories = data.categories?.join(", ") || "Non spécifiées";
+
+ infoDiv.innerHTML = `
+ ${data.title}
+
+
+ Durée : ${durationStr}
+ Uploader : ${data.uploader || "Inconnu"}
+ Date d’upload : ${readableDate}
+ Vues : ${data.view_count?.toLocaleString() || "?"}
+ Likes : ${data.like_count?.toLocaleString() || "?"}
+ URL : ${data.webpage_url}
+ Channel : ${data.channel_url}
+ Taille estimée : ${sizeStr}
+ Catégories : ${categories}
+
+ `;
+ infoDiv.classList.add("visible");
+ });
+})
\ No newline at end of file
diff --git a/OLD CODE/public/styles/components/checkbox.css b/public/styles/components/checkbox.css
similarity index 100%
rename from OLD CODE/public/styles/components/checkbox.css
rename to public/styles/components/checkbox.css
diff --git a/OLD CODE/public/styles/components/editpathbutton.css b/public/styles/components/editpathbutton.css
similarity index 100%
rename from OLD CODE/public/styles/components/editpathbutton.css
rename to public/styles/components/editpathbutton.css
diff --git a/OLD CODE/public/styles/components/themebutton.css b/public/styles/components/themebutton.css
similarity index 100%
rename from OLD CODE/public/styles/components/themebutton.css
rename to public/styles/components/themebutton.css
diff --git a/public/styles/layout/container.css b/public/styles/layout/container.css
new file mode 100644
index 0000000..14e1ad4
--- /dev/null
+++ b/public/styles/layout/container.css
@@ -0,0 +1,25 @@
+.container, .infos-container {
+ position: relative;
+ top: 0;
+ left: 0;
+ right: 0;
+ padding: 1em;
+ z-index: 1000;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+#downloadStatus {
+ margin-top: 1em;
+ min-height: 1.5em;
+ font-weight: bold;
+ text-align: center;
+ color: var(--download-status-color);
+}
+
+label {
+ display: flex;
+ align-items: center;
+ font-size: 0.9em;
+}
\ No newline at end of file
diff --git a/OLD CODE/public/styles/layout/form.css b/public/styles/layout/form.css
similarity index 100%
rename from OLD CODE/public/styles/layout/form.css
rename to public/styles/layout/form.css
diff --git a/OLD CODE/public/styles/layout/header.css b/public/styles/layout/header.css
similarity index 100%
rename from OLD CODE/public/styles/layout/header.css
rename to public/styles/layout/header.css
diff --git a/OLD CODE/public/styles/layout/videoinfo.css b/public/styles/layout/videoinfo.css
similarity index 100%
rename from OLD CODE/public/styles/layout/videoinfo.css
rename to public/styles/layout/videoinfo.css
diff --git a/OLD CODE/public/styles/styles.css b/public/styles/styles.css
similarity index 79%
rename from OLD CODE/public/styles/styles.css
rename to public/styles/styles.css
index 16c0fa0..d385576 100644
--- a/OLD CODE/public/styles/styles.css
+++ b/public/styles/styles.css
@@ -1,11 +1,13 @@
+/* Layout */
@import url("layout/form.css");
@import url("layout/container.css");
-@import url("layout/ctr-infos.css");
@import url("layout/header.css");
@import url("layout/videoinfo.css");
+
+/* Components */
@import url("components/checkbox.css");
@import url("components/themebutton.css");
-/* @import url("variables.css"); */
+
@import url("theme/themeimport.css");
@import url("components/editpathbutton.css");
@@ -24,7 +26,7 @@ html,body{
margin: 0;
padding: 0;
scroll-behavior: smooth;
- /* outline: 2px solid red; */ /*<-- Utile pour le début ça */
+ /*outline: 2px solid red; /*<-- Utile pour le debug ça */
font-family: "Poppins", sans-serif;
font-weight: 500;
font-style: normal;
@@ -33,10 +35,6 @@ html,body{
body {
margin: 0;
background-color: var(--background-color);
- /* background-image: linear-gradient(to left top, #838383, #a1a1a1, #bfbfbf, #dfdfdf, #ffffff);
- background-size: cover;
- background-position: center;
- background-attachment: fixed; */
width: 100%;
height: 100%;
color: var(--default-text-color);
@@ -46,7 +44,6 @@ body {
display: flex;
flex-direction: column;
align-items: center;
-
}
diff --git a/OLD CODE/public/styles/theme/chirac-theme.css b/public/styles/theme/chirac-theme.css
similarity index 79%
rename from OLD CODE/public/styles/theme/chirac-theme.css
rename to public/styles/theme/chirac-theme.css
index 8efbaac..6818b5c 100644
--- a/OLD CODE/public/styles/theme/chirac-theme.css
+++ b/public/styles/theme/chirac-theme.css
@@ -1,16 +1,15 @@
body.chirac {
- /* Background image Chirac */
+
background-image: url('../../assets/images/Chirac.png');
- /* background-position: right; */
background-repeat: no-repeat;
background-size: cover;
background-attachment: fixed;
- /* Couleurs drapeau français */
- --background-color: rgba(255, 255, 255, 0.85); /* blanc légèrement transparent pour lisibilité */
- --default-text-color: #002395; /* bleu foncé */
- --subtitle-color: #EF4135; /* rouge vif */
- --mp3-text-color: #002395; /* bleu */
+
+ --background-color: rgba(255, 255, 255, 0.85);
+ --default-text-color: #002395;
+ --subtitle-color: #EF4135;
+ --mp3-text-color: #002395;
/* Formulaire */
--form-bg-color: rgba(255, 255, 255, 0.9);
@@ -19,9 +18,9 @@ body.chirac {
--form-input-border-focus-color: #EF4135;
--form-input-text-color: #002395;
--form-input-placeholder-color: #999999;
- --form-button-bg-color: #002395; /* bleu */
+ --form-button-bg-color: #002395;
--form-button-text-color: #ffffff;
- --form-button-bg-hover-color: #EF4135; /* rouge */
+ --form-button-bg-hover-color: #EF4135;
/* Checkbox */
--checkbox-bg-default: #cccccc;
@@ -40,15 +39,8 @@ body.chirac {
--video-info-list-strong-color: #000000;
--video-info-link-color: #002395;
--video-info-link-hover-color: #EF4135;
-
- /* animation d'apparition */
- opacity: 0;
- transition: opacity 0.8s ease, background-color 0.8s ease;
}
-body.chirac.theme-active {
- opacity: 1;
-}
/*
diff --git a/public/styles/theme/cyberpunk-theme.css b/public/styles/theme/cyberpunk-theme.css
new file mode 100644
index 0000000..8c0ac6d
--- /dev/null
+++ b/public/styles/theme/cyberpunk-theme.css
@@ -0,0 +1,43 @@
+body.cyberpunk {
+
+ background-image: url('../../assets/images/Cyberpunk.webp');
+ background-size: cover;
+ background-position: center;
+ background-repeat: no-repeat;
+ background-attachment: fixed;
+
+ /* Couleurs générales */
+ --background-color: #0d0d0d;
+ --default-text-color: #e0e0e0;
+ --subtitle-color: #ffea00;
+ --mp3-text-color: #ffea00;
+
+ /* Formulaire */
+ --form-bg-color: #141414;
+ --form-input-bg-color: #1a1a1a;
+ --form-input-border-color: #333333;
+ --form-input-border-focus-color: #ffea00;
+ --form-input-text-color: #e0e0e0;
+ --form-input-placeholder-color: #777777;
+ --form-button-bg-color: #ffea00;
+ --form-button-text-color: #0d0d0d;
+ --form-button-bg-hover-color: #d4c000;
+
+ /* Checkbox */
+ --checkbox-bg-default: #333333;
+ --checkbox-bg-checked: #ffea00;
+ --checkbox-checkmark-border-color: #0d0d0d;
+ --checkbox-pulse-color: rgba(255, 234, 0, 0.5);
+
+ /* Download status */
+ --download-status-color: #fffbd1;
+
+ /* Video Info Box */
+ --infos-box-color: #1a1a1a;
+ --video-info-text-color: #e0e0e0;
+ --video-info-heading-color: #ffea00;
+ --video-info-list-color: #cccccc;
+ --video-info-list-strong-color: #ffffff;
+ --video-info-link-color: #ffea00;
+ --video-info-link-hover-color: #d4c000;
+}
\ No newline at end of file
diff --git a/OLD CODE/public/styles/theme/dark-theme.css b/public/styles/theme/dark-theme.css
similarity index 100%
rename from OLD CODE/public/styles/theme/dark-theme.css
rename to public/styles/theme/dark-theme.css
diff --git a/OLD CODE/public/styles/theme/default-theme.css b/public/styles/theme/default-theme.css
similarity index 100%
rename from OLD CODE/public/styles/theme/default-theme.css
rename to public/styles/theme/default-theme.css
diff --git a/public/styles/theme/drift-theme.css b/public/styles/theme/drift-theme.css
new file mode 100644
index 0000000..c88693d
--- /dev/null
+++ b/public/styles/theme/drift-theme.css
@@ -0,0 +1,47 @@
+body.drift {
+
+ background-image: url('../../assets/images/Drift.jpg');
+ background-size: cover;
+ background-position: center;
+ background-attachment: fixed;
+
+ /* Palette principale */
+ --background-color: #0f1218;
+ --default-text-color: #3d4b61;
+ --subtitle-color: #1a1a1a;
+ --mp3-text-color: #c0d4ff;
+
+ /* Formulaire */
+ --form-bg-color: #1e222a;
+ --form-input-bg-color: #13171d;
+ --form-input-border-color: #3b4b6d;
+ --form-input-border-focus-color: #6ea8ff;
+ --form-input-text-color: #f0f4ff;
+ --form-input-placeholder-color: #7a8fae;
+ --form-button-bg-color: #2a2979;
+ --form-button-text-color: #ffffff;
+ --form-button-bg-hover-color: #353393;
+
+ /* Checkbox */
+ --checkbox-bg-default: #2a2e38;
+ --checkbox-bg-checked: #2a2979;
+ --checkbox-checkmark-border-color: #ffffff;
+ --checkbox-pulse-color: rgba(76,108,255,0.3);
+
+ /* Download status */
+ --download-status-color: #ff8e8e;
+
+ /* Video Info Box */
+ --infos-box-color: #1e222a;
+ --video-info-text-color: #e8e8f0;
+ --video-info-heading-color: #9ab6ff;
+ --video-info-list-color: #c0d4ff;
+ --video-info-list-strong-color: #ffffff;
+ --video-info-link-color: #4c6cff;
+ --video-info-link-hover-color: #6c8dff;
+ }
+
+
+body.drift .download-path{
+ color: #ededed;
+}
diff --git a/public/styles/theme/fanatic-theme.css b/public/styles/theme/fanatic-theme.css
new file mode 100644
index 0000000..3601445
--- /dev/null
+++ b/public/styles/theme/fanatic-theme.css
@@ -0,0 +1,36 @@
+body.fanatic{
+ /* Couleurs générales */
+ --background-color: #121212;
+ --default-text-color: #eee;
+ --subtitle-color: #FF5900;
+ --mp3-text-color: #ffffff;
+
+ /* Formulaire */
+ --form-bg-color: #1e1e1e;
+ --form-input-bg-color: #2b2b2b;
+ --form-input-border-color: #333333;
+ --form-input-border-focus-color: #FF5900;
+ --form-input-text-color: #eeeeee;
+ --form-input-placeholder-color: #666666;
+ --form-button-bg-color: #FF5900;
+ --form-button-text-color: #121212;
+ --form-button-bg-hover-color: #e65500;
+
+ /* Checkbox */
+ --checkbox-bg-default: #555555;
+ --checkbox-bg-checked: #ff6600;
+ --checkbox-checkmark-border-color: #eee;
+ --checkbox-pulse-color: rgba(255, 102, 0, 0.5);
+
+ /* Download status */
+ --download-status-color: #FF5900;
+
+ /* Video Info Box */
+ --infos-box-color: #222222;
+ --video-info-text-color: #eee;
+ --video-info-heading-color: #FF5900;
+ --video-info-list-color: #ccc;
+ --video-info-list-strong-color: #fff;
+ --video-info-link-color: #FF5900;
+ --video-info-link-hover-color: #e65500;
+}
diff --git a/public/styles/theme/light-theme.css b/public/styles/theme/light-theme.css
new file mode 100644
index 0000000..12d7724
--- /dev/null
+++ b/public/styles/theme/light-theme.css
@@ -0,0 +1,36 @@
+body.light {
+ /* Couleurs générales */
+ --background-color: #dadada;
+ --default-text-color: #222222;
+ --subtitle-color: #007bff;
+ --mp3-text-color: #000000;
+
+ /* Formulaire */
+ --form-bg-color: #ffffff;
+ --form-input-bg-color: #fafafa;
+ --form-input-border-color: #cccccc;
+ --form-input-border-focus-color: #007bff;
+ --form-input-text-color: #333333;
+ --form-input-placeholder-color: #888888;
+ --form-button-bg-color: #007bff;
+ --form-button-text-color: #ffffff;
+ --form-button-bg-hover-color: #0056b3;
+
+ /* Checkbox */
+ --checkbox-bg-default: #cccccc;
+ --checkbox-bg-checked: #007bff;
+ --checkbox-checkmark-border-color: #ffffff;
+ --checkbox-pulse-color: rgba(0, 123, 255, 0.4);
+
+ /* Download status */
+ --download-status-color: #007bff;
+
+ /* Video Info Box */
+ --infos-box-color: #ffffff;
+ --video-info-text-color: #222222;
+ --video-info-heading-color: #007bff;
+ --video-info-list-color: #444444;
+ --video-info-list-strong-color: #000000;
+ --video-info-link-color: #007bff;
+ --video-info-link-hover-color: #0056b3;
+}
diff --git a/public/styles/theme/neon-theme.css b/public/styles/theme/neon-theme.css
new file mode 100644
index 0000000..544a23a
--- /dev/null
+++ b/public/styles/theme/neon-theme.css
@@ -0,0 +1,47 @@
+body.neon {
+
+ background: (url("../../assets/images/drift.jpg"));
+ background-image: linear-gradient(135deg, #0f0c29, #302b63, #24243e); /* dégradé sombre violet/bleu */
+ background-size: cover;
+ background-position: center;
+ background-attachment: fixed;
+
+ /* Couleurs générales */
+ --background-color: #1b1b2f;
+ --default-text-color: #e0e0ff;
+ --subtitle-color: #a3a3ff;
+ --mp3-text-color: #c0c0ff;
+
+ /* Formulaire */
+ --form-bg-color: #2c2c4a;
+ --form-input-bg-color: #1f1f3a;
+ --form-input-border-color: #5858f0;
+ --form-input-border-focus-color: #a3a3ff;
+ --form-input-text-color: #ffffff;
+ --form-input-placeholder-color: #8888ff;
+ --form-button-bg-color: #5858f0;
+ --form-button-text-color: #ffffff;
+ --form-button-bg-hover-color: #7a7aff;
+
+ /* Checkbox */
+ --checkbox-bg-default: #302b63;
+ --checkbox-bg-checked: #5858f0;
+ --checkbox-checkmark-border-color: #ffffff;
+ --checkbox-pulse-color: rgba(88,88,240,0.3);
+
+ /* Download status */
+ --download-status-color: #a3a3ff;
+
+ /* Video Info Box */
+ --infos-box-color: #2c2c4a;
+ --video-info-text-color: #e0e0ff;
+ --video-info-heading-color: #a3a3ff;
+ --video-info-list-color: #c0c0ff;
+ --video-info-list-strong-color: #ffffff;
+ --video-info-link-color: #5858f0;
+ --video-info-link-hover-color: #7a7aff;
+}
+
+body.neon .download-path {
+ color: var(--default-text-color);
+}
diff --git a/public/styles/theme/nf-theme.css b/public/styles/theme/nf-theme.css
new file mode 100644
index 0000000..5bef409
--- /dev/null
+++ b/public/styles/theme/nf-theme.css
@@ -0,0 +1,46 @@
+body.nf {
+
+ background-image: url('../../assets/images/NF.jpg');
+ background-size: cover;
+ background-position: 70% center;
+ background-attachment: fixed;
+
+ /* Couleurs générales */
+ --background-color: #1a1a1a;
+ --default-text-color: #e6dfd5;
+ --subtitle-color: #f5f0e6;
+ --mp3-text-color: #d8cfc2;
+
+ /* Formulaire */
+ --form-bg-color: #2a2a2a;
+ --form-input-bg-color: #222222;
+ --form-input-border-color: #4a4036;
+ --form-input-border-focus-color: #cbb89d;
+ --form-input-text-color: #f0e8dd;
+ --form-input-placeholder-color: #9a8f80;
+ --form-button-bg-color: #cbb89d;
+ --form-button-text-color: #1a1a1a;
+ --form-button-bg-hover-color: #b7a588;
+
+ /* Checkbox */
+ --checkbox-bg-default: #4a4036;
+ --checkbox-bg-checked: #cbb89d;
+ --checkbox-checkmark-border-color: #1a1a1a;
+ --checkbox-pulse-color: rgba(203,184,157,0.3);
+
+ /* Download status */
+ --download-status-color: #cbb89d;
+
+ /* Video Info Box */
+ --infos-box-color: #2a2a2a;
+ --video-info-text-color: #e6dfd5;
+ --video-info-heading-color: #f5f0e6;
+ --video-info-list-color: #d8cfc2;
+ --video-info-list-strong-color: #ffffff;
+ --video-info-link-color: #cbb89d;
+ --video-info-link-hover-color: #b7a588;
+}
+
+body.nf .download-path {
+ color: var(--default-text-color);
+}
diff --git a/public/styles/theme/spicy-theme.css b/public/styles/theme/spicy-theme.css
new file mode 100644
index 0000000..3ee396d
--- /dev/null
+++ b/public/styles/theme/spicy-theme.css
@@ -0,0 +1,43 @@
+body.spicy {
+
+ background-image: url('../../assets/images/spicy.webp');
+ background-size:contain;
+ background-position: center;
+ background-attachment: fixed;
+ background-repeat: no-repeat;
+
+ /* Couleurs générales */
+ --background-color: #121212;
+ --default-text-color: #581010;
+ --subtitle-color: #3d0909;
+ --mp3-text-color: #ffffff;
+
+ /* Formulaire */
+ --form-bg-color: #1a1a1a;
+ --form-input-bg-color: #2b2b2b;
+ --form-input-border-color: #444444;
+ --form-input-border-focus-color: #ff1a1a;
+ --form-input-text-color: #eeeeee;
+ --form-input-placeholder-color: #888888;
+ --form-button-bg-color: #ff1a1a;
+ --form-button-text-color: #ffffff;
+ --form-button-bg-hover-color: #cc0000;
+
+ /* Checkbox */
+ --checkbox-bg-default: #555555;
+ --checkbox-bg-checked: #ff1a1a;
+ --checkbox-checkmark-border-color: #f5f5f5;
+ --checkbox-pulse-color: rgba(255, 26, 26, 0.5);
+
+ /* Download status */
+ --download-status-color: #ffffff;
+
+ /* Video Info Box */
+ --infos-box-color: #1a1a1a;
+ --video-info-text-color: #f5f5f5;
+ --video-info-heading-color: #ff1a1a;
+ --video-info-list-color: #cccccc;
+ --video-info-list-strong-color: #ffffff;
+ --video-info-link-color: #ff1a1a;
+ --video-info-link-hover-color: #cc0000;
+}
\ No newline at end of file
diff --git a/OLD CODE/public/styles/theme/themeimport.css b/public/styles/theme/themeimport.css
similarity index 74%
rename from OLD CODE/public/styles/theme/themeimport.css
rename to public/styles/theme/themeimport.css
index 3748f0c..887fb90 100644
--- a/OLD CODE/public/styles/theme/themeimport.css
+++ b/public/styles/theme/themeimport.css
@@ -6,4 +6,6 @@
@import url("cyberpunk-theme.css");
@import url("spicy-theme.css");
@import url("vilbrequin-theme.css");
-@import url("nf-theme.css");
\ No newline at end of file
+@import url("nf-theme.css");
+@import url("drift-theme.css");
+@import url("neon-theme.css");
\ No newline at end of file
diff --git a/public/styles/theme/vilbrequin-theme.css b/public/styles/theme/vilbrequin-theme.css
new file mode 100644
index 0000000..d42799f
--- /dev/null
+++ b/public/styles/theme/vilbrequin-theme.css
@@ -0,0 +1,46 @@
+body.vilbrequin {
+
+ background-image: url('../../assets/images/Vilbrequin.webp');
+ background-size: cover;
+ background-position: center;
+ background-attachment: fixed;
+
+ /* Couleurs générales */
+ --background-color: #0a1f3d;
+ --default-text-color: #535353;
+ --subtitle-color: #001696;
+ --mp3-text-color: #ffffff;
+
+ /* Formulaire */
+ --form-bg-color: #12284f;
+ --form-input-bg-color: #1c3760;
+ --form-input-border-color: #2a4b80;
+ --form-input-border-focus-color: #00bfff;
+ --form-input-text-color: #f0f0f0;
+ --form-input-placeholder-color: #b0c4de;
+ --form-button-bg-color: #ffd700;
+ --form-button-text-color: #0a1f3d;
+ --form-button-bg-hover-color: #e6c200;
+
+ /* Checkbox */
+ --checkbox-bg-default: #2a4b80;
+ --checkbox-bg-checked: #00bfff;
+ --checkbox-checkmark-border-color: #f0f0f0;
+ --checkbox-pulse-color: rgba(0,191,255,0.5);
+
+ /* Download status */
+ --download-status-color: #ffd700;
+
+ /* Video Info Box */
+ --infos-box-color: #12284f;
+ --video-info-text-color: #f0f0f0;
+ --video-info-heading-color: #00bfff;
+ --video-info-list-color: #dcdcdc;
+ --video-info-list-strong-color: #ffffff;
+ --video-info-link-color: #00bfff;
+ --video-info-link-hover-color: #009acd;
+}
+
+body.vilbrequin .download-path{
+ color: white;
+}
\ No newline at end of file
diff --git a/public/styles/variables.css b/public/styles/variables.css
new file mode 100644
index 0000000..cfbb85d
--- /dev/null
+++ b/public/styles/variables.css
@@ -0,0 +1,36 @@
+:root {
+ /* Couleurs générales */
+ --background-color: #001224;
+ --default-text-color: #eee;
+ --subtitle-color: #007bff;
+ --mp3-text-color: #000000;
+
+ /* Formulaire */
+ --form-bg-color: #ffffff;
+ --form-input-bg-color: #f9f9f9;
+ --form-input-border-color: #cccccc;
+ --form-input-border-focus-color: #0056b3;
+ --form-input-text-color: #333333;
+ --form-input-placeholder-color: #aaaaaa;
+ --form-button-bg-color: #007bff;
+ --form-button-text-color: #ffffff;
+ --form-button-bg-hover-color: #0056b3;
+
+ /* Checkbox */
+ --checkbox-bg-default: #cccccc;
+ --checkbox-bg-checked: #3B82F6;
+ --checkbox-checkmark-border-color: #E0E0E2;
+ --checkbox-pulse-color: rgba(59, 130, 246, 0.5);
+
+ /* Download status */
+ --download-status-color: #007bff;
+
+ /* Video Info Box */
+ --infos-box-color: #f0f0f0;
+ --video-info-text-color: #222222;
+ --video-info-heading-color: #007bff;
+ --video-info-list-color: #444444;
+ --video-info-list-strong-color: #000000;
+ --video-info-link-color: #007bff;
+ --video-info-link-hover-color: #0056b3;
+}
diff --git a/OLD CODE/ressources/yt-dlp.exe b/ressources/yt-dlp.exe
similarity index 100%
rename from OLD CODE/ressources/yt-dlp.exe
rename to ressources/yt-dlp.exe
diff --git a/OLD CODE/server/discordRPC.js b/server/discordRPC.js
similarity index 59%
rename from OLD CODE/server/discordRPC.js
rename to server/discordRPC.js
index d48a77a..47abb5d 100644
--- a/OLD CODE/server/discordRPC.js
+++ b/server/discordRPC.js
@@ -20,22 +20,24 @@ const RPC = require("discord-rpc");
const clientId = "1410934537051181146";
const rpc = new RPC.Client({ transport: "ipc" });
+let intervalId;
+
function startRPC() {
rpc.on("ready", () => {
- console.log("✅ Connecté à Discord !");
+ 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",
+ largeImageKey: "icon",
+ smallImageKey: "acnolo_pfp",
+ smallImageText: "By MasterAcnolo",
+ startTimestamp: new Date(),
+ details: "github.com/MasterAcnolo/Freedom-Loader",
};
rpc.setActivity(presence);
- // Mise à jour toutes les 15 secondes si tu veux garder le compteur à jour
- setInterval(() => {
+ // Met à jour la présence toutes les 15s
+ intervalId = setInterval(() => {
rpc.setActivity(presence);
}, 15000);
});
@@ -43,6 +45,23 @@ function startRPC() {
rpc.login({ clientId }).catch(err => {
console.error("Erreur Discord RPC :", err);
});
+
+ // Gestion propre de la fermeture
+ const cleanExit = () => {
+ if (intervalId) clearInterval(intervalId); // stop interval
+ rpc.destroy(); // déconnecte proprement
+ console.log("Discord RPC arrêté proprement.");
+ };
+
+ process.on("exit", cleanExit);
+ process.on("SIGINT", () => {
+ cleanExit();
+ process.exit();
+ });
+ process.on("SIGTERM", () => {
+ cleanExit();
+ process.exit();
+ });
}
module.exports = { startRPC };
diff --git a/server/logger.js b/server/logger.js
new file mode 100644
index 0000000..9597671
--- /dev/null
+++ b/server/logger.js
@@ -0,0 +1,86 @@
+/*
+ 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 .
+*/
+
+const { createLogger, format, transports } = require("winston");
+const DailyRotateFile = require("winston-daily-rotate-file");
+const fs = require("fs");
+const path = require("path");
+const os = require("os");
+
+// Dossier de logs Windows
+const logDir = path.join(os.homedir(), "AppData", "Local", "FreedomLoader", "logs");
+
+// Création du dossier si nécessaire
+fs.mkdirSync(logDir, { recursive: true });
+
+// Format commun pour tous les logs
+const logFormat = format.combine(
+ format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
+ format.printf(({ timestamp, level, message }) => `${timestamp} | ${level.toUpperCase()} | ${message}`)
+);
+
+const consoleFormat = format.combine(
+ format.colorize(),
+ format.timestamp({ format: "HH:mm:ss" }),
+ format.printf(({ timestamp, level, message }) => `${timestamp} | ${level} | ${message}`)
+);
+
+// Configuration du logger
+const logger = createLogger({
+ level: "info",
+ format: logFormat,
+ transports: [
+ new DailyRotateFile({
+ dirname: logDir,
+ filename: "LOGS-%DATE%.log",
+ datePattern: "YYYY-MM-DD",
+ zippedArchive: false,
+ maxFiles: "7d",
+ format: logFormat,
+ options: { flags: "a" },
+ }),
+ new transports.Console({
+ format: consoleFormat,
+ }),
+ ],
+});
+
+// Helpers pour sessions
+function getSessionStartLine() {
+ return `--- Démarrage de la session : ${new Date().toISOString()} ---`;
+}
+
+function getSessionEndLine() {
+ return `--- Fin de la session : ${new Date().toISOString()} ---`;
+}
+
+function logSessionStart() {
+ logger.info(getSessionStartLine());
+}
+
+function logSessionEnd() {
+ logger.info(getSessionEndLine());
+}
+
+// Export
+module.exports = {
+ logger,
+ logSessionStart,
+ logSessionEnd,
+ logDir,
+};
diff --git a/server/routes/download.js b/server/routes/download.js
new file mode 100644
index 0000000..0db6fe2
--- /dev/null
+++ b/server/routes/download.js
@@ -0,0 +1,172 @@
+/*
+ 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 { app } = require("electron");
+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;
+const userYtDlp = path.join(app.getPath("userData"), "yt-dlp.exe");
+
+// Path vers yt-dlp
+const ytDlpPath = path.join(process.resourcesPath, "yt-dlp.exe");
+const sourceYtDlp = path.join(process.resourcesPath, "yt-dlp.exe");
+
+
+if (!fs.existsSync(userYtDlp)) {
+ fs.copyFileSync(sourceYtDlp, userYtDlp);
+ logger.info(`yt-dlp copié dans le dossier utilisateur : ${userYtDlp}`);
+}
+
+// Lancement de la mise à jour une seule fois au démarrage
+execFile(userYtDlp, ["-U"], (err, stdout, stderr) => {
+ if (err) {
+ logger.warn("Erreur update yt-dlp:", err);
+ logger.debug(err);
+ return;
+ }
+ logger.info(`Update yt-dlp : ${stdout}`);
+});
+
+// Validation simple d'une URL
+function isValidUrl(url) {
+ return /^https?:\/\/.+/.test(url); // Petit Regex qui renvoie true si l'URL est bonne et False si elle est pas bonne
+}
+
+router.post("/", (req, res) => {
+ try {
+ const options = {
+ url: req.body.url,
+ audioOnly: req.body.audioOnly === "1",
+ quality: req.body.quality || "best",
+ };
+
+ if (!options.url) {
+ logger.warn("Requête POST /download sans URL");
+ return res.status(400).send("❌ URL manquante !");
+ }
+
+ if (!isValidUrl(options.url)) {
+ logger.warn(`URL invalide : ${options.url}`);
+ return res.status(400).send("❌ URL invalide !");
+ }
+
+ let requestedOutputFolder = req.body.savePath || req.app.locals.outputFolder;
+ requestedOutputFolder = path.normalize(requestedOutputFolder);
+
+ 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é.");
+ }
+
+ if (!fs.existsSync(requestedOutputFolder)) {
+ fs.mkdirSync(requestedOutputFolder, { recursive: true });
+ logger.info(`Dossier de sortie créé : ${requestedOutputFolder}`);
+ }
+
+ const outputTemplate = path.join(requestedOutputFolder, "%(title)s.%(ext)s");
+
+ const args = [
+ "--no-continue",
+ // "--restrict-filenames",
+ "--no-overwrites",
+
+ "--embed-thumbnail",
+ "--add-metadata",
+ "--concurrent-fragments", "8",
+ "--retries", "10",
+ "--fragment-retries", "10",
+ "--ffmpeg-location", path.join(process.resourcesPath, "ffmpeg.exe")
+ ];
+
+ if (options.audioOnly) {
+ args.push("-f", "bestaudio", "--extract-audio", "--audio-format", "mp3");
+ } else
+ args.push("--merge-output","mp4")
+
+ const qualityMap = {
+ best: "bestvideo+bestaudio/best/mp4",
+ medium: "bestvideo[height<=720]+bestaudio/best[height<=720]/mp4",
+ worst: "worstvideo+worstaudio/worst/mp4",
+ 1080: "bestvideo[height<=1080]+bestaudio/best[height<=1080]/mp4",
+ 720: "bestvideo[height<=720]+bestaudio/best[height<=720]/mp4",
+ 480: "bestvideo[height<=480]+bestaudio/best[height<=480]/mp4",
+ };
+
+ const format = qualityMap[options.quality] || "best";
+ args.push("-f", format);
+
+ args.push("-o", outputTemplate);
+ args.push(options.url);
+
+ logger.info(`Téléchargement demandé : url=${options.url}, audioOnly=${options.audioOnly}, quality=${options.quality}`);
+ logger.info(`Commande yt-dlp : ${ytDlpPath} ${args.join(" ")}`);
+
+ const child = execFile(userYtDlp, args);
+
+ child.stdout.on("data", (data) => {
+ data.toString().split("\n").forEach(line => {
+ if (line.trim()) logger.info(`[yt-dlp stdout] ${line.trim()}`);
+ });
+ });
+
+ child.stderr.on("data", (data) => {
+ data.toString().split("\n").forEach(line => {
+ if (line.trim()) logger.error(`[yt-dlp stderr] ${line.trim()}`);
+ });
+ });
+
+ child.on("error", (err) => {
+ logger.error(`Erreur lancement yt-dlp : ${err.message}`);
+ res.status(500).send(`❌ Erreur lors de l'exécution : ${err.message}`);
+ });
+
+ 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");
+ const notif = new Notification({
+ title: "Freedom Loader",
+ body: "Ton téléchargement est terminé, clique ici pour l'ouvrir.",
+ icon: iconPath,
+ });
+
+ notif.on("click", () => {
+ 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) {
+ logger.error(`Erreur serveur dans /download : ${err.message}`);
+ res.status(500).send(`Erreur serveur : ${err.message}`);
+ }
+});
+
+module.exports = router;
diff --git a/server/routes/info.js b/server/routes/info.js
new file mode 100644
index 0000000..4c43d46
--- /dev/null
+++ b/server/routes/info.js
@@ -0,0 +1,101 @@
+/*
+ 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 .
+*/
+
+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");
+const { app } = require("electron");
+const userYtDlp = path.join(app.getPath("userData"), "yt-dlp.exe");
+
+const ytDlpPath = path.join(__dirname, '../../ressources/yt-dlp.exe');
+
+// Copie yt-dlp si besoin
+if (!fs.existsSync(userYtDlp)) {
+ fs.copyFileSync(ytDlpPath, userYtDlp);
+ logger.info(`yt-dlp copié dans le dossier utilisateur : ${userYtDlp}`);
+}
+
+if (!fs.existsSync(ytDlpPath)) {
+ logger.error(`❌ yt-dlp introuvable à ${ytDlpPath}`);
+ throw new Error(`yt-dlp introuvable à ${ytDlpPath}`);
+}
+
+function isValidUrl(url) {
+ return typeof url === "string" && /^https?:\/\//.test(url);
+}
+
+router.post("/", (req, res) => {
+ const url = req.body.url;
+ if (!url || !isValidUrl(url)) {
+ logger.warn("Requête /info invalide ou URL manquante");
+ return res.status(400).send("❌ URL invalide ou manquante");
+ }
+
+ logger.info(`Requête /info reçue pour ${url}`);
+
+ const args = ["--dump-single-json", "--ignore-errors", "--yes-playlist", url];
+
+ execFile(userYtDlp, args, { timeout: 30_000 }, (error, stdout, stderr) => {
+ if (error) {
+ logger.error(`Erreur exécution yt-dlp: ${error.message}`);
+ if (stderr) logger.debug(`stderr: ${stderr}`);
+ return res.status(500).send("❌ Impossible de récupérer les infos.");
+ }
+
+ try {
+ const data = JSON.parse(stdout);
+
+ if (data._type === "playlist") {
+ logger.info(`Playlist détectée : ${data.title} (${data.entries.length} vidéos)`);
+
+ const playlistPayload = {
+ type: "playlist",
+ title: data.title || data.id,
+ channel: data.uploader,
+ count: data.entries.length,
+ videos: (data.entries || []).map(v => ({
+ id: v.id,
+ title: v.title,
+ webpage_url: v.webpage_url,
+ duration: v.duration,
+ thumbnail: v.thumbnail,
+ uploader: v.uploader
+ }))
+ };
+
+ return res.json(playlistPayload);
+ }
+
+ // Vidéo unique
+ logger.info(`Vidéo unique récupérée : ${data.title}`);
+ res.json({
+ type: "video",
+ ...data
+ });
+
+ } catch (e) {
+ logger.error(`Erreur parsing JSON: ${e.message}`);
+ return res.status(500).send("❌ JSON illisible.");
+ }
+ });
+});
+
+module.exports = router;
diff --git a/server/server.js b/server/server.js
new file mode 100644
index 0000000..a739d8b
--- /dev/null
+++ b/server/server.js
@@ -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 .
+*/
+
+const express = require("express");
+const fs = require("fs");
+const path = require("path");
+const { logger, logSessionStart, logSessionEnd } = require("./logger");
+const debug = require("debug")("freedom-loader:server");
+
+const app = express();
+
+const PORT = 8787; // Port de l'app
+
+// Dossier de téléchargement Windows
+const downloadsPath = path.join(process.env.USERPROFILE, "Downloads");
+const outputFolder = path.join(downloadsPath, "Freedom Loader");
+
+// Création du dossier si inexistant
+if (!fs.existsSync(outputFolder)) {
+ try {
+ fs.mkdirSync(outputFolder, { recursive: true });
+ logger.info("Dossier Freedom Loader créé dans Téléchargements.");
+ } catch (err) {
+ logger.error("Impossible de créer le dossier :", err);
+ process.exit(1);
+ }
+} else {
+ logger.info("Dossier Freedom Loader déjà existant.");
+}
+
+app.locals.outputFolder = outputFolder;
+
+app.use(express.urlencoded({ extended: true })); // Middleware pour parser les POST en x-www-form-urlencoded
+
+// Fichiers statiques (frontend)
+const staticPath = path.join(__dirname, "../public");
+debug("Serveur statique sur", staticPath);
+app.use(express.static(staticPath));
+
+// Routes API
+const infoRoute = require("./routes/info");
+const downloadRoute = require("./routes/download");
+debug("Routes /download et /info installées");
+app.use("/download", downloadRoute);
+app.use("/info", infoRoute);
+
+// Route principale
+app.get("/", (req, res) => {
+ debug("Requête GET / servie");
+ res.sendFile(path.join(__dirname, "../public/index.html"));
+});
+
+// Fonction pour démarrer le serveur Express
+async function startServer() {
+ return new Promise((resolve, reject) => {
+ logSessionStart(); // Début de session
+
+ const serverInstance = app.listen(PORT, () => {
+ logger.info(`Serveur Express prêt sur http://localhost:${PORT}`);
+ resolve(serverInstance);
+ });
+
+ // Gestion des erreurs serveur
+ serverInstance.on("error", (err) => {
+ logger.error("Erreur serveur Express :", err);
+ reject(err);
+ });
+
+ // Gestion propre de la fermeture du serveur
+ const gracefulExit = () => {
+ logSessionEnd();
+ serverInstance.close(() => {
+ logger.info("Serveur Express fermé proprement.");
+ process.exit();
+ });
+ };
+
+ process.on("SIGINT", gracefulExit);
+ process.on("SIGTERM", gracefulExit);
+ });
+}
+
+// Export de startServer
+module.exports = { startServer };
+
+// Lancement du Discord RPC après que le serveur soit prêt
+(async () => {
+ const { startRPC } = require("./discordRPC");
+ await startServer(); // on attend que le serveur démarre
+ startRPC();
+})();