7 Commits
1.4.4 ... 1.4.5

Author SHA1 Message Date
MasterAcnolo
71dbd104f8 Merge pull request #35 from MasterAcnolo/working
1.4.5
2026-02-21 13:00:36 +01:00
MasterAcnolo
57a5defb08 Fix: Update Dependencies 2026-02-21 12:43:36 +01:00
MasterAcnolo
25c78ff8b7 Fix: Increment App Version 2026-02-21 12:40:27 +01:00
MasterAcnolo
792a59d8d7 Feat: You can now fetch image from all website. 2026-02-21 12:38:57 +01:00
MasterAcnolo
b13c98dda2 Refactor: Translate all comms in English 2026-02-21 12:27:13 +01:00
MasterAcnolo
1f103c20b9 Fix: Remove Discord RPC Update. It's useless and take performance for nothing. 2026-02-19 22:23:30 +01:00
MasterAcnolo
70d62959ca Fix: Reduce Line Complexity featuresPath 2026-02-19 22:08:56 +01:00
19 changed files with 1338 additions and 908 deletions

View File

@@ -7,7 +7,7 @@
### **A clean, open-source multimedia downloader for Windows** ### **A clean, open-source multimedia downloader for Windows**
[![Release](https://img.shields.io/badge/Release-1.4.4-blue?style=for-the-badge)](https://github.com/MasterAcnolo/Freedom-Loader/releases) [![Release](https://img.shields.io/badge/Release-1.4.5-blue?style=for-the-badge)](https://github.com/MasterAcnolo/Freedom-Loader/releases)
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-red.svg?style=for-the-badge)](https://www.gnu.org/licenses/gpl-3.0) [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-red.svg?style=for-the-badge)](https://www.gnu.org/licenses/gpl-3.0)
[![Website](https://img.shields.io/badge/Website-Visit-404040?style=for-the-badge)](https://masteracnolo.github.io/FreedomLoader/) [![Website](https://img.shields.io/badge/Website-Visit-404040?style=for-the-badge)](https://masteracnolo.github.io/FreedomLoader/)
<a href="https://www.firefox.com/fr/?utm_campaign=SET_DEFAULT_BROWSER"><img src="https://img.shields.io/badge/Require Firefox-E66000?style=for-the-badge&logo=Firefox-Browser&logoColor=white"></a> <a href="https://www.firefox.com/fr/?utm_campaign=SET_DEFAULT_BROWSER"><img src="https://img.shields.io/badge/Require Firefox-E66000?style=for-the-badge&logo=Firefox-Browser&logoColor=white"></a>

View File

@@ -5,7 +5,8 @@ const path = require("path");
const localMode = !app.isPackaged; const localMode = !app.isPackaged;
const featuresPath = localMode ? path.join(__dirname, "./config/config.json") : path.join(path.join(process.resourcesPath,"config/" ,"config.json")); // Change path for JSON file
const featuresPath = path.join(path.join(`${localMode ? __dirname : process.resourcesPath}`,"config/" , "config.json"));
let features = {}; let features = {};

2173
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{ {
"name": "freedom-loader", "name": "freedom-loader",
"productName": "Freedom Loader", "productName": "Freedom Loader",
"version": "1.4.4", "version": "1.4.5",
"author": "MasterAcnolo", "author": "MasterAcnolo",
"description": "Freedom Loader", "description": "Freedom Loader",
"main": "main.js", "main": "main.js",
@@ -23,7 +23,7 @@
}, },
"devDependencies": { "devDependencies": {
"electron": "^37.1.0", "electron": "^37.1.0",
"electron-builder": "^26.4.0" "electron-builder": "^25.1.8"
}, },
"build": { "build": {
"appId": "com.masteracnolo.freedomloader", "appId": "com.masteracnolo.freedomloader",

View File

@@ -7,7 +7,7 @@
default-src 'self' 'unsafe-inline' data:; default-src 'self' 'unsafe-inline' data:;
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
font-src https://fonts.gstatic.com; font-src https://fonts.gstatic.com;
img-src 'self' data: https://i.ytimg.com; img-src 'self' data: https:;
"> ">
<title></title> <title></title>

View File

@@ -43,12 +43,12 @@ function loadTheme() {
applyTheme(savedTheme); applyTheme(savedTheme);
themeSelect.value = savedTheme; themeSelect.value = savedTheme;
} else { } else {
applyTheme("dark"); // thème par défaut applyTheme("dark");
themeSelect.value = "dark"; themeSelect.value = "dark";
} }
} }
// Quand l'utilisateur change le thème depuis le select // When the user changes the theme from the select
themeSelect.addEventListener("change", (event) => { themeSelect.addEventListener("change", (event) => {
const selectedTheme = event.target.value; const selectedTheme = event.target.value;
if (themes[selectedTheme]) { if (themes[selectedTheme]) {

View File

@@ -4,7 +4,7 @@ const button = form.querySelector("button");
form.addEventListener("submit", async (e) => { form.addEventListener("submit", async (e) => {
e.preventDefault(); e.preventDefault();
button.disabled = true; // Empêche les clics multiples button.disabled = true; // Avoid multiple clicks
statusDiv.textContent = "Download in progress..."; statusDiv.textContent = "Download in progress...";
const formData = new FormData(form); const formData = new FormData(form);

View File

@@ -43,7 +43,7 @@ async function init() {
const url = urlInput.value.trim(); const url = urlInput.value.trim();
// Si champ vide -> reset total // IF field empty -> total reset
if (!url || url.length < 2) { if (!url || url.length < 2) {
infoDiv.innerHTML = ""; infoDiv.innerHTML = "";
infoDiv.classList.remove("visible", "playlist-mode"); infoDiv.classList.remove("visible", "playlist-mode");
@@ -58,7 +58,7 @@ async function init() {
const data = await fetchVideoInfo(url); const data = await fetchVideoInfo(url);
loaderBox.style.display = "none"; loaderBox.style.display = "none";
// Gestion des erreurs
if (data.error) { if (data.error) {
infoDiv.innerHTML = ` infoDiv.innerHTML = `
<div style=" <div style="
@@ -139,7 +139,7 @@ async function init() {
`; `;
}); });
// Gestion du bouton copier // Copy Button
listDiv.addEventListener("click", (event) => { listDiv.addEventListener("click", (event) => {
if (event.target.classList.contains("copy-btn") || event.target.closest(".copy-btn")) { if (event.target.classList.contains("copy-btn") || event.target.closest(".copy-btn")) {
const btn = event.target.closest(".copy-btn") || event.target; const btn = event.target.closest(".copy-btn") || event.target;

View File

@@ -30,7 +30,7 @@ function resetProgress() {
speedElement.style.display = "none"; speedElement.style.display = "none";
} }
// Connexion SSE // SSE Connexion
const evtSource = new EventSource("/download/progress"); const evtSource = new EventSource("/download/progress");
evtSource.onmessage = e => { evtSource.onmessage = e => {
if (e.data === "reset") { if (e.data === "reset") {
@@ -49,10 +49,10 @@ evtSource.onmessage = e => {
if (!isNaN(percent)) { if (!isNaN(percent)) {
updateProgress(percent); updateProgress(percent);
window.electronAPI.setProgress(percent); // update barre des tâches window.electronAPI.setProgress(percent); // Update Task Bar
if (percent >= 100) setTimeout(() => { if (percent >= 100) setTimeout(() => {
resetProgress(); resetProgress();
window.electronAPI.setProgress(-1); // retire la barre window.electronAPI.setProgress(-1); // Remove the bar
}, 500); }, 500);
} }
}; };

View File

@@ -15,7 +15,7 @@ function closePanel() {
settingsPanel.style.display = "none"; settingsPanel.style.display = "none";
} }
// charge les features
async function loadSettings() { async function loadSettings() {
const features = await window.electronAPI.getFeatures(); const features = await window.electronAPI.getFeatures();
@@ -33,7 +33,7 @@ async function loadSettings() {
}); });
} }
// ouvrir le JSON // Open The JSON
document.getElementById("open-json-btn").addEventListener("click", () => { document.getElementById("open-json-btn").addEventListener("click", () => {
window.topbarAPI.openConfig(); // ton IPC existant window.topbarAPI.openConfig(); // ton IPC existant
}); });

View File

@@ -5,10 +5,11 @@ const { isValidUrl } = require("../helpers/validation");
async function infoController(req, res){ async function infoController(req, res){
const url = req.body.url || req.query.url; // Gérer POST et GET const url = req.body.url || req.query.url; // POST et GET
/* Si pas d'url, url non-string ou url invalide*/ // If no url, non-string url or invalid url
if (!url || typeof url !== "string" || !isValidUrl(url)) return res.status(400).send("❌ Invalid URL Or Missing"); if (!url || typeof url !== "string" || !isValidUrl(url)) return res.status(400).send("❌ Invalid URL Or Missing");
logger.info(`/Info Request receive by the Info Controller. URL: ${url}`); logger.info(`/Info Request receive by the Info Controller. URL: ${url}`);

View File

@@ -20,10 +20,6 @@ function startRPC() {
rpc.clearActivity() rpc.clearActivity()
rpc.setActivity(presence); rpc.setActivity(presence);
// Met à jour la présence toutes les 15s
intervalId = setInterval(() => {
rpc.setActivity(presence);
}, 15000);
}); });
rpc.login({ clientId }).catch(err => { rpc.login({ clientId }).catch(err => {

View File

@@ -7,7 +7,7 @@ const { logger } = require("../logger");
function getUserBrowser() { function getUserBrowser() {
const userProfile = os.homedir(); const userProfile = os.homedir();
// Liste des navigateurs supportés par yt-dlp (Actuellement je peux que garantir Firefox, Désolé) // List of browsers supported by yt-dlp (Currently I can only guarantee Firefox, Sorry)
const browsers = [ const browsers = [
{ name: "firefox", path: path.join(userProfile, "AppData", "Roaming", "Mozilla", "Firefox", "Profiles") }, { name: "firefox", path: path.join(userProfile, "AppData", "Roaming", "Mozilla", "Firefox", "Profiles") },
// { name: "chrome", path: path.join(userProfile, "AppData", "Local", "Google", "Chrome", "User Data", "Default") }, // { name: "chrome", path: path.join(userProfile, "AppData", "Local", "Google", "Chrome", "User Data", "Default") },
@@ -19,7 +19,7 @@ function getUserBrowser() {
// { name: "whale", path: path.join(userProfile, "AppData", "Local", "Naver", "Naver Whale", "User Data", "Default") } // { name: "whale", path: path.join(userProfile, "AppData", "Local", "Naver", "Naver Whale", "User Data", "Default") }
]; ];
// Chercher un navigateur disponible // Search for an available browser
for (const browser of browsers) { for (const browser of browsers) {
if (fs.existsSync(browser.path)) { if (fs.existsSync(browser.path)) {
logger.info(`Browser found: ${browser.name}`); logger.info(`Browser found: ${browser.name}`);
@@ -27,12 +27,12 @@ function getUserBrowser() {
} }
} }
// Aucun navigateur trouvé - notifier l'utilisateur // No browser found - notify user
logger.warn("No supported browser found on the system"); logger.warn("No supported browser found on the system");
notify.notifyFirefoxBrowserMissing(); notify.notifyFirefoxBrowserMissing();
// Fallback: retourner "firefox" pour laisser yt-dlp gérer l'erreur // Fallback: return "Firefox" to let YT-DLP manage error
// plutôt que de crasher l'application // this avoid app crash
return "firefox"; return "firefox";
} }

View File

@@ -23,7 +23,7 @@ const sourceYtDlp = path.join(resourcesPath, "binaries","yt-dlp.exe");
if (config.localMode) { if (config.localMode) {
userYtDlp = path.join(__dirname, "../../ressources/yt-dlp.exe"); userYtDlp = path.join(__dirname, "../../ressources/yt-dlp.exe");
ffmpegPath = path.join(__dirname, "../../ressources/"); // <- contient ffmpeg.exe et ffprobe.exe ffmpegPath = path.join(__dirname, "../../ressources/"); // <- has ffmpeg.exe and ffprobe.exe
denoPath = path.join(__dirname, "../../ressources/deno.exe"); denoPath = path.join(__dirname, "../../ressources/deno.exe");
} else { } else {
userYtDlp = path.join(app.getPath("userData"),"yt-dlp.exe"); userYtDlp = path.join(app.getPath("userData"),"yt-dlp.exe");
@@ -33,7 +33,6 @@ if (config.localMode) {
if (!fs.existsSync(userYtDlp)) { if (!fs.existsSync(userYtDlp)) {
fs.copyFileSync(sourceYtDlp, userYtDlp); fs.copyFileSync(sourceYtDlp, userYtDlp);
} }
} }
// Notification icon paths // Notification icon paths

View File

@@ -5,10 +5,10 @@ const path = require("path");
const os = require("os"); const os = require("os");
const config = require("../config"); const config = require("../config");
// Dossier de logs Windows // Logs folder in Windows
const logDir = path.join(os.homedir(), "AppData", "Local", "FreedomLoader", "logs"); const logDir = path.join(os.homedir(), "AppData", "Local", "FreedomLoader", "logs");
// Création du dossier "logs" si nécessaire // Create "Logs" folder if needed
try { try {
fs.mkdirSync(logDir, { recursive: true }); fs.mkdirSync(logDir, { recursive: true });
} catch (error) { } catch (error) {
@@ -20,7 +20,7 @@ const logFormat = format.combine(
format.printf(({ timestamp, level, message }) => `${timestamp} | ${level.toUpperCase()} | ${message}`) format.printf(({ timestamp, level, message }) => `${timestamp} | ${level.toUpperCase()} | ${message}`)
); );
// Configuration du logger // Logger configuration
const logger = createLogger({ const logger = createLogger({
level: "info", level: "info",
format: logFormat, format: logFormat,

View File

@@ -4,7 +4,7 @@ const { downloadController, progressController, speedController } = require("../
router.post("/", downloadController); router.post("/", downloadController);
// SSE pour la progression du téléchargement // SSE for download progress
router.get("/progress", progressController); router.get("/progress", progressController);
router.get("/speed", speedController); router.get("/speed", speedController);

View File

@@ -10,7 +10,7 @@ const app = express();
app.use(express.json()); app.use(express.json());
// Mise à jour yt-dlp au démarrage // Update YT-DLP on Startup
execFile(userYtDlp, ["-U"], (err, stdout, stderr) => { execFile(userYtDlp, ["-U"], (err, stdout, stderr) => {
if (err) logger.warn("yt-dlp update error:", err); if (err) logger.warn("yt-dlp update error:", err);
else logger.info(`Update yt-dlp : ${stdout}`); else logger.info(`Update yt-dlp : ${stdout}`);
@@ -24,11 +24,11 @@ app.use(express.static(path.join(__dirname, "../public")));
app.use("/download", require("./routes/download.route")); app.use("/download", require("./routes/download.route"));
app.use("/info", require("./routes/info.route")); app.use("/info", require("./routes/info.route"));
// When we get API Root, it return the front pages.
app.get("/", rateLimite ,(req, res) => { app.get("/", rateLimite ,(req, res) => {
res.sendFile(path.join(__dirname, "../public/index.html")); res.sendFile(path.join(__dirname, "../public/index.html"));
}); });
// Fonction pour démarrer le serveur
async function startServer() { async function startServer() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const server = app.listen(config.applicationPort, () => { const server = app.listen(config.applicationPort, () => {

View File

@@ -48,7 +48,7 @@ function fetchDownload(options, listeners, speedListeners) {
if (!line.trim()) return; if (!line.trim()) return;
logger.info(`[yt-dlp] ${line}`); logger.info(`[yt-dlp] ${line}`);
/* Progress Bar */ // Progress Bar
if (line.startsWith("[download] Destination:")) listeners.forEach(fn => fn("reset")); if (line.startsWith("[download] Destination:")) listeners.forEach(fn => fn("reset"));
const match = line.match(/\[download\]\s+(\d+\.\d+)%/); const match = line.match(/\[download\]\s+(\d+\.\d+)%/);
if (match) listeners.forEach(fn => fn(parseFloat(match[1]))); if (match) listeners.forEach(fn => fn(parseFloat(match[1])));

View File

@@ -17,8 +17,8 @@ function fetchInfo(url) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
execFile(userYtDlp,[...args, url], execFile(userYtDlp,[...args, url],
{ timeout: 30_000, // 30s, si jamais plus, abandon de la requête { timeout: 30_000, // 30s, if more timeout
maxBuffer: 10 * 1024 * 1024 }, // 10MO de réponse max (Par défaut: 200ko) maxBuffer: 10 * 1024 * 1024 }, // 10MO max response (Default: 200ko)
(error, stdout, stderr) => { (error, stdout, stderr) => {