mirror of
https://github.com/MasterAcnolo/Freedom-Loader.git
synced 2026-07-29 18:25:47 +02:00
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
### **A clean, open-source multimedia downloader for Windows**
|
### **A clean, open-source multimedia downloader for Windows**
|
||||||
|
|
||||||
[](https://github.com/MasterAcnolo/Freedom-Loader/releases)
|
[](https://github.com/MasterAcnolo/Freedom-Loader/releases)
|
||||||
[](https://www.gnu.org/licenses/gpl-3.0)
|
[](https://www.gnu.org/licenses/gpl-3.0)
|
||||||
[](https://masteracnolo.github.io/FreedomLoader/)
|
[](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>
|
||||||
|
|||||||
@@ -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
2173
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -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",
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -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]) {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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}`);
|
||||||
|
|||||||
@@ -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 => {
|
||||||
|
|||||||
@@ -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";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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, () => {
|
||||||
|
|||||||
@@ -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])));
|
||||||
|
|||||||
@@ -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) => {
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user