16 Commits

Author SHA1 Message Date
MasterAcnolo
7dee94da6f refactor: extract CHANGELOG into a separated file. Should avoid characters issue 2026-09-11 11:01:12 +02:00
MasterAcnolo
cd43ae10fc fix: bump app version to 1.6.3 since it's no longer a preview 2026-09-10 21:36:33 +02:00
Axel Nicolas
6978277b4b Update codeql.yml 2026-09-10 15:13:02 +02:00
MasterAcnolo
b91c20d450 fix: release.sh release message ending 2026-09-10 15:09:54 +02:00
Axel Nicolas
014b1ad52a Update codeql.yml 2026-09-10 15:09:19 +02:00
Axel Nicolas
ec93d20a39 Create codeql.yml 2026-09-10 15:06:52 +02:00
MasterAcnolo
09c511fcb8 fix: docs in path.helpers 2026-09-10 15:01:50 +02:00
MasterAcnolo
f343fbd148 fix(discord-rpc): remove unusued code 2026-09-10 15:00:00 +02:00
Axel Nicolas
6d99d5f91c Update README.md 2026-09-10 13:55:09 +02:00
MasterAcnolo
0f0bb874bf feat: add logs when minimize and maximize (tray and not) 2026-09-10 12:01:07 +02:00
MasterAcnolo
b9267ec408 fix: enhance package.json with metadata used by electron-builder on Linux 2026-09-10 12:00:20 +02:00
MasterAcnolo
311bff6df5 refactor: systemTray window logic. Window reference is now dynamic and guarded against destroyed states. Minimize can now put window in the systemTray if option is active 2026-09-10 10:27:11 +02:00
MasterAcnolo
e4243a01ae fix: devMode logic 2026-09-10 10:25:03 +02:00
MasterAcnolo
52be9c6b4f update: electron from 43.3.0 to 44.3.0 2026-09-10 10:23:29 +02:00
MasterAcnolo
e541eb442a fix:
- replace unusued throw new Error by logger.error
- remove unusued variables isWindows, err
- remove unusued logFormat
- remove unusued imports logSessionEnd
2026-09-09 09:21:11 +02:00
MasterAcnolo
e2c2dde4a2 fix: README.md 2026-09-09 08:59:55 +02:00
18 changed files with 119 additions and 62 deletions

46
.github/workflows/codeql.yml vendored Normal file
View File

@@ -0,0 +1,46 @@
name: "CodeQL PR"
on:
pull_request:
branches: [ "main" ]
schedule:
- cron: '00 00 * * 1'
jobs:
analyze:
name: Analyze (${{ matrix.language }})
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
permissions:
security-events: write
packages: read
actions: read
contents: read
strategy:
fail-fast: false
matrix:
include:
- language: javascript-typescript
build-mode: none
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
- name: Run manual build steps
if: matrix.build-mode == 'manual'
shell: bash
run: |
echo 'Manual build required'
exit 1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
with:
category: "/language:${{matrix.language}}"

View File

@@ -15,8 +15,6 @@
</div>
---
## Overview
Freedom Loader is a desktop application built with Electron that provides a straightforward way to download video and audio content with full metadata support. The application focuses on reliability, transparency, and user privacy-no ads, no tracking, no questionable third-party services.
@@ -412,7 +410,6 @@ You are free to use, modify, and redistribute this software under the terms of t
- [@SpicyFire21](https://github.com/SpicyFire21) to be the spiciest one
- [@AureCX](https://github.com/AureCX) for being the Official Windows Tester!
- Zakaria for the website icon
- Jacques Chirac to love Apples
- All users who test, report issues, and help improve the application
---

View File

@@ -102,7 +102,7 @@ function startRPC() {
});
rpc.login({ clientId }).catch(err => {
rpc.login({ clientId }).catch(() => {
// Since 1.6.2, it no longer print the error. This is because if discord is not opened, it will send an error.
// logger.error("Unable to connect to the RPC:", err);
logger.info("Unable to connect to the Discord RPC. Discord is maybe not launched");
@@ -123,14 +123,6 @@ async function stopRPC() {
try {
if (intervalId) clearInterval(intervalId);
/**
* Ensures RPC connection AND the underlying socket exist
* before attempting to clear activity to prevent crash.
*/
if (rpc.transport && rpc.transport.socket) {
await rpc.clearActivity();
}
await rpc.destroy();
} catch (err) {

View File

@@ -13,7 +13,7 @@
const { ipcMain, dialog, shell } = require("electron");
const fs = require("fs");
const { logger, logDir } = require("../server/logger");
const { configFeatures, featuresPath } = require("../config");
const {configFeatures, featuresPath, devMode} = require("../config");
const { getThemes, reloadThemes } = require("./themeManager");
const config = require("../config");
const { validateDownloadPath, getDefaultDownloadPath } = require("./pathValidator");
@@ -114,7 +114,18 @@ function registerIpcHandlers(getMainWindow) {
/**
* Window minimize request from renderer.
*/
ipcMain.on("window-minimize", () => getMainWindow()?.minimize());
ipcMain.on("window-minimize", () => {
// Minimize to tray
if (configFeatures.systemTray) {
getMainWindow()?.hide()
logger.info("Window Minimized in SystemTray (Using Minimize Button)");
} else {
// Native minimize
getMainWindow()?.minimize()
logger.info("Window minimized normally");
}
}
);
/**
* Toggles maximize/unmaximize state of main window.
@@ -223,7 +234,7 @@ function registerIpcHandlers(getMainWindow) {
if (key === "systemTray") {
if (value === true) {
logger.info("System Tray enabled dynamically.");
createSystemTray(getMainWindow(), config.devMode);
createSystemTray(devMode);
} else {
logger.info("System Tray disabled dynamically.");
destroyTray();

View File

@@ -66,7 +66,7 @@ function validateDownloadPath(userPath) {
const real = fs.realpathSync(absolutePath);
if (!isSafePath(real)) {
throw new Error("Path not allowed: system folders are blocked!");
logger.error("Path not allowed: system folders are blocked!");
}
return real;

View File

@@ -2,6 +2,7 @@ const {app, Menu, Tray, nativeImage} = require("electron");
const path = require("path");
const {logger} = require("../server/logger"); // Ajuste le chemin si besoin
const fs = require("fs");
const {getMainWindow} = require("./windowManager");
/**
* Global reference to the Tray instance to prevent garbage collection.
@@ -17,11 +18,10 @@ let tray = null;
* - Builds the right-click context menu.
* - Handles left-click behavior to toggle main window visibility.
*
* @param {import('electron').BrowserWindow} mainWindow - The main application window.
* @param {boolean} devMode - Indicates whether the application is running in development mode.
* @returns {Tray} The created Tray instance.
*/
function createSystemTray(mainWindow, devMode) {
function createSystemTray(devMode) {
// Prevent creating multiple tray instances
if (tray) return tray;
@@ -58,7 +58,12 @@ function createSystemTray(mainWindow, devMode) {
{
label: "Show Freedom Loader",
click: () => {
mainWindow.show();
const window = getMainWindow();
if (window && !window.isDestroyed()) {
window.show();
window.focus();
logger.info("Window Restored from SystemTray (Using Show Action)");
}
},
},
{ type: "separator" },
@@ -82,11 +87,15 @@ function createSystemTray(mainWindow, devMode) {
* but it remains standard practice for Windows and Linux environments.
*/
tray.on("click", () => {
if (mainWindow.isVisible()) {
mainWindow.hide();
const window = getMainWindow();
if (!window || window.isDestroyed()) return;
if (window.isVisible()) {
window.hide();
} else {
mainWindow.show();
mainWindow.focus();
window.show();
window.focus();
logger.info("Window Restored from SystemTray (Double Click on Tray Icon)")
}
});

View File

@@ -87,9 +87,11 @@ async function createMainWindow() {
* cancels the destruction and hides the window instead.
*/
mainWindow.on('close', (event) => {
if (!app.isQuitting && config.systemTray ) {
if (!app.isQuitting && config.configFeatures.systemTray) {
event.preventDefault();
mainWindow.hide();
logger.info("Window Minimized in SystemTray (Using Close Button)");
return false;
}
});

View File

@@ -7,7 +7,7 @@ const path = require("path");
* Indicates whether the application is running in development mode.
* Determined by Electron's packaging state.
*/
const devMode = process.env.NODE_ENV === "test" || !app?.isPackaged;
const devMode = !app.isPackaged || process.env.NODE_ENV === "test";
/**
* Resolves the configuration file path depending on runtime environment.

10
main.js
View File

@@ -38,7 +38,7 @@ app.setAppUserModelId("com.masteracnolo.freedomloader");
* Load the app dependencies for hardware choice
*/
const { logger, logSessionStart, logSessionEnd, logDir } = require("./server/logger");
const { configFeatures } = require("./config");
const { configFeatures, devMode} = require("./config");
/**
* In-memory snapshot of application feature flags.
@@ -53,7 +53,7 @@ if (!configFeatures.enableHardwareAcceleration){
logger.info("Enable Hardware Acceleration")
}
if(configFeatures.devMode){
if(devMode){
/**
* Start devTron extensions - @see https://github.com/electron/devtron
*/
@@ -74,7 +74,7 @@ const { updateYtDlp } = require("./app/ytDlpUpdater");
const { createMainWindow, getMainWindow } = require("./app/windowManager");
const { registerIpcHandlers } = require("./app/ipcHandlers");
const { createSplashWindow, closeSplashWindow, setSplashProgress } = require("./app/splashManager");
const { userThemesPath, initUserThemes, isWindows, validateBinaries, defaultDownloadFolder } = require("./server/helpers/path.helpers");
const { userThemesPath, initUserThemes, validateBinaries, defaultDownloadFolder } = require("./server/helpers/path.helpers");
const {createSystemTray, destroyTray} = require("./app/tray");
const { stopServer } = require("./server/server");
@@ -135,7 +135,7 @@ app.whenReady().then(async () => {
createSplashWindow();
if (!configFeatures.devMode && !checkNativeDependencies()) return;
if (!devMode && !checkNativeDependencies()) return;
const { userYtDlp } = require("./server/helpers/path.helpers");
updateYtDlp(userYtDlp);
@@ -161,7 +161,7 @@ app.whenReady().then(async () => {
getMainWindow().show();
if (configFeatures.systemTray) {
createSystemTray(getMainWindow(), configFeatures.devMode);
createSystemTray(devMode);
}
if (configFeatures.discordRPC) startRPC();

12
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "freedom-loader",
"version": "1.6.2-preview",
"version": "1.6.3-preview",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "freedom-loader",
"version": "1.6.2-preview",
"version": "1.6.3-preview",
"license": "GPL-3.0-only",
"dependencies": {
"chalk": "^4.1.2",
@@ -24,7 +24,7 @@
"@electron/devtron": "^2.1.1",
"@jest/globals": "^30.4.1",
"@playwright/test": "^1.62.1",
"electron": "^43.3.0",
"electron": "^44.3.0",
"electron-builder": "^26.15.3",
"jest": "^30.4.2",
"nodemon": "^3.1.14",
@@ -3910,9 +3910,9 @@
}
},
"node_modules/electron": {
"version": "43.3.0",
"resolved": "https://registry.npmjs.org/electron/-/electron-43.3.0.tgz",
"integrity": "sha512-nLlvu0WFjftWsSaTkV2B/c4NDuJBspTyXu8vKSQ6vLvFt8uG3NgN49LLKcXddwX0GqVvAQDhciWp+4xOdTdhew==",
"version": "44.3.0",
"resolved": "https://registry.npmjs.org/electron/-/electron-44.3.0.tgz",
"integrity": "sha512-St9EV7F2VtYaYWD2qaAjBwUgKxx39eJOUsUJ5+/1113sqbVfNqv4Dbm/W1rN7qmYSPa+mWwR6yr+b7MfgjgVfQ==",
"dev": true,
"license": "MIT",
"dependencies": {

View File

@@ -1,7 +1,8 @@
{
"name": "freedom-loader",
"desktopName": "com.masteracnolo.freedomloader",
"productName": "Freedom Loader",
"version": "1.6.3-preview",
"version": "1.6.3",
"author": "MasterAcnolo <MasterAcnolo@users.noreply.github.com>",
"description": "Free and open-source GUI for yt-dlp — download video and audio from hundreds of platforms",
"homepage": "https://masteracnolo.github.io/Freedom-Loader-Site/",
@@ -21,7 +22,6 @@
"dev": "nodemon --watch server --watch app --watch main.js --watch config.js --ext js,json --exec \"electron --no-warnings .\"",
"dev:warn": "nodemon --watch server --watch app --watch main.js --watch config.js --ext js,json --exec \"electron --trace-warnings .\"",
"dev:debug": "nodemon --watch server --watch app --watch main.js --watch config.js --ext js,json --exec \"WAYLAND_DEBUG=1 ELECTRON_ENABLE_LOGGING=1 ELECTRON_ENABLE_STACK_DUMPING=1 electron --enable-logging --v=1 .\"",
"build": "electron-builder",
"build:win": "electron-builder --win",
"build:linux": "electron-builder --linux",
@@ -30,10 +30,8 @@
"release": "bash scripts/release.sh",
"release:publish": "bash scripts/release.sh --publish",
"release:dry-run": "bash scripts/release.sh --dry-run",
"test": "jest",
"test:unit": "jest test/unit",
"update": "npm update"
},
"dependencies": {
@@ -52,7 +50,7 @@
"@electron/devtron": "^2.1.1",
"@jest/globals": "^30.4.1",
"@playwright/test": "^1.62.1",
"electron": "^43.3.0",
"electron": "^44.3.0",
"electron-builder": "^26.15.3",
"jest": "^30.4.2",
"nodemon": "^3.1.14",
@@ -136,6 +134,8 @@
"snap"
],
"category": "Utility",
"synopsis": "GUI for yt-dlp to download video and audio with metadata",
"maintainer": "MasterAcnolo",
"icon": "build/app-icon.png",
"executableName": "freedom-loader",
"extraResources": [

1
scripts/CHANGELOG.md Normal file
View File

@@ -0,0 +1 @@
[//]: # (This file is used for the release CHANGELOG. Please use Markdown to describe changes.)

View File

@@ -5,7 +5,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
VERSION=$(node -p "require('$ROOT_DIR/package.json').version")
f [[ "$VERSION" == *"-preview"* ]]; then
if [[ "$VERSION" == *"-preview"* ]]; then
echo "Error: The current version ($VERSION) is a 'preview"
echo "Unable to launch release pipeline. Please update the package.json with a stable version.."
exit 1
@@ -64,6 +64,16 @@ echo "[0/5] Cleaning workspace..."
rm -rf "$ROOT_DIR/dist" "$ROOT_DIR/srpm-out"
echo "Workspace cleaned (dist/ and srpm-out/ removed)"
# ------------------------------------------------------------
# Step 0.3 - Retrieve Changelog
# ------------------------------------------------------------
if [ ! -f "$ROOT_DIR/CHANGELOG.md" ]; then
echo "Warning: CHANGELOG.md not found, release notes will be empty."
CHANGELOG=""
else
CHANGELOG="$(cat "$ROOT_DIR/CHANGELOG.md")"
echo "Changelog retrieved successfully"
fi
# ------------------------------------------------------------
# Step 1 - Build Linux packages (AppImage, deb, snap)
# ------------------------------------------------------------
@@ -105,19 +115,13 @@ PREV_TAG=$(git rev-list --tags --skip=1 --max-count=1 | xargs git describe --tag
RELEASE_NOTES="# Freedom Loader - $VERSION
## Changelog
$CHANGELOG
## Found a bug or issue?
Please report it in the [GitHub Issues](https://github.com/MasterAcnolo/Freedom-Loader/issues) section.
## Next Release (non-exhaustive roadmap)
- More format options
- Subtitle support
- Improved UI / UX
- Language selection
- Download specific parts of videos
- File renaming options
- Parallel downloads
- Skip sponsored parts automatically
**Full Changelog**: https://github.com/MasterAcnolo/Freedom-Loader/compare/${PREV_TAG}...${TAG}"
if [ "$DRY_RUN" = false ]; then

View File

@@ -77,11 +77,11 @@ async function infoController(req, res) {
);
// Preserve original error context
throw err;
logger.error(err);
}
} else {
throw err;
logger.error(err);
}
}

View File

@@ -260,7 +260,7 @@ function validateBinaries() {
/**
* Helper to lazy load logger, avoid circular import
* @returns {winston.Logger}
* @returns {import("winston").Logger}
*/
function getLogger() {
return require("../logger.js").logger;

View File

@@ -17,11 +17,6 @@ try {
console.error(`Failed to create log directory: ${error.message}`);
}
const logFormat = format.combine(
format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
format.printf(({ timestamp, level, message }) => `${timestamp} | ${level.toUpperCase()} | ${message}`)
);
/**
* Format used by the saved file
* @type {Format}

View File

@@ -2,7 +2,7 @@ const express = require("express");
const path = require("path");
const config = require("../config");
const { logger, logSessionEnd } = require("./logger");
const {logger} = require("./logger");
const { rateLimit } = require("./helpers/rateLimit.helpers");
const app = express();

View File

@@ -65,7 +65,7 @@ function createPlaylistFolder(basePath, playlistTitle) {
}
logger.error(`Could not find available playlist folder after 1000 attempts`);
throw new Error("Unable to create playlist folder");
logger.error("Unable to create playlist folder");
} catch (err) {
logger.warn(`Failed to create playlist folder with title "${sanitizedTitle}": ${err.message}`);