diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md
index ecfa48e..5c74cfe 100644
--- a/.github/ISSUE_TEMPLATE/question.md
+++ b/.github/ISSUE_TEMPLATE/question.md
@@ -9,8 +9,8 @@ assignees: 'MasterAcnolo'
## Pre-submission Checklist
-- [ ] I have checked the [FAQ](https://masteracnolo.github.io/Freedom-Loader-Site/#/faq)
-- [ ] I have reviewed the [Wiki](https://masteracnolo.github.io/Freedom-Loader-Site/#/wiki)
+- [ ] I have checked the [FAQ](https://freedomloader.acnolo.fr/#/faq)
+- [ ] I have reviewed the [Wiki](https://freedomloader.acnolo.fr/#/wiki)
- [ ] I have searched existing issues
## Question
diff --git a/.gitignore b/.gitignore
index 81ae903..3e4ba25 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,12 +6,21 @@
/out
/srpm-out
+# .env
+*.env
+
# Config JSON File. Create it and fill it if you want to have dev only settings
config/config.dev.json
# IDE
-/.idea
-/.vscode
+.idea/
+.vscode/
+.zed/
+
+# OS generated files
+.DS_Store
+.DS_Store?
+Thumbs.db
# Linux executable
ffmpeg
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 5c2f407..ab56aaf 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -112,8 +112,8 @@ See [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) for details.
## Questions?
-- Check the [FAQ](https://masteracnolo.github.io/Freedom-Loader-Site/faq)
-- Review the [Wiki](https://masteracnolo.github.io/Freedom-Loader-Site/wiki)
+- Check the [FAQ](https://freedomloader.acnolo.fr/faq)
+- Review the [Wiki](https://freedomloader.acnolo.fr/wiki)
- Open a [Question issue](https://github.com/MasterAcnolo/Freedom-Loader/issues/new/choose)
---
diff --git a/README.md b/README.md
index 6944a2e..2b88d75 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-
@@ -9,7 +9,7 @@
[](https://github.com/MasterAcnolo/Freedom-Loader/releases)
[](https://www.gnu.org/licenses/gpl-3.0)
-[](https://masteracnolo.github.io/Freedom-Loader-Site/)
+[](https://freedomloader.acnolo.fr/)
[](https://masteracnolo.github.io/Freedom-Loader-Workshop/)
@@ -56,7 +56,7 @@ The primary goal is to make media downloading accessible to users who want offli
### User Interface
-- **Custom window controls** - Frameless window with custom top bar (optional)
+- **Custom window controls** - Frameless window with custom top bar
- **Custom themes** - ZIP-based theme system with live preview
- **Theme Workshop** - web-based theme creator and browser
- **Toast notifications** - in-app feedback system
@@ -151,6 +151,7 @@ freedom-loader
|---------------|---------------------------------------------------------|--------------------------------------|
| **Downloads** | `C:\Users\[USERNAME]\Downloads\Freedom Loader` | `~/Downloads/Freedom Loader` |
| **Logs** | `C:\Users\[USERNAME]\AppData\Local\FreedomLoader\logs\` | `~/.local/share/FreedomLoader/logs/` |
+| **Cache** | ``
> [!NOTE]
> The download folder can be changed anytime via the "Edit" button in the UI, on both platforms.
@@ -224,10 +225,8 @@ Freedom Loader can be configured either through the settings panel in the UI or
### Available Options
```json
{
- "autoUpdate": true,
"systemTray": true,
"discordRPC": true,
- "customTopBar": true,
"autoCheckInfo": true,
"addThumbnail": true,
"addMetadata": true,
@@ -241,6 +240,7 @@ Freedom Loader can be configured either through the settings panel in the UI or
"downloadSystem": true,
"notifySystem": true,
"enableHardwareAcceleration": true,
+ "showChanglogsAfterUpdate": true,
"theme": "dark"
}
```
@@ -249,10 +249,8 @@ Freedom Loader can be configured either through the settings panel in the UI or
| Option | Type | Default | Description |
|------------------------------|---------|----------|------------------------------------------------------------|
-| `autoUpdate` | boolean | `true` | Enable automatic application updates |
-| `systemTray` | boolean | `false` | Allow app to minimize on close [EXPERIMENTAL] |
+| `systemTray` | boolean | `false` | Allow app to minimize on close |
| `discordRPC` | boolean | `true` | Enable Discord Rich Presence integration |
-| `customTopBar` | boolean | `true` | Use custom application top bar |
| `autoCheckInfo` | boolean | `true` | Automatically fetch video information on URL paste |
| `addThumbnail` | boolean | `true` | Embed thumbnail in downloaded audio files |
| `addMetadata` | boolean | `true` | Add metadata tags to downloaded files |
@@ -266,6 +264,7 @@ Freedom Loader can be configured either through the settings panel in the UI or
| `downloadSystem` | boolean | `true` | Enable download system |
| `notifySystem` | boolean | `true` | Enable system notifications |
| `enableHardwareAcceleration` | boolean | `true` | Enable Hardware Acceleration |
+| `showChanglogsAfterUpdate` | boolean | `true` | Auto-display changelog popup after and update |
| `theme` | string | `dark` | Current Theme Name |
### Supported Codecs
@@ -354,7 +353,7 @@ See [BINARIES.md](./BINARIES.md) for how to setup the binaries.
## Roadmap
-See the most recent Roadmap [Here](https://masteracnolo.github.io/Freedom-Loader-Site/Roadmap).
+See the most recent Roadmap [Here](https://freedomloader.acnolo.fr/Roadmap).
## Contributing
@@ -367,13 +366,29 @@ Head over to the [Freedom Loader Workshop repository](https://github.com/MasterA
### Bug Reports
-Use the GitHub Issues system and include:
+You can report bugs either using the built-in application system or via the GitHub Issues system.
+
+#### Using the Built-in Report Tool
+
+1. Open Freedom Loader.
+2. Trigger the bug report interface.
+3. Fill in the title and description of the issue.
+4. Choose whether to include today's logs automatically.
+5. Submit the report.
+
+> [!NOTE]
+> If you need to attach extra context like screenshots or screen recordings, it is recommended to use the GitHub Issues
+system instead.
+
+#### Using GitHub Issues
+
+Open a new issue on the GitHub repository and include:
- Clear description of the issue
- Steps to reproduce
- Expected vs actual behavior
- Relevant logs from `AppData\Local\FreedomLoader\logs\` or `~/.local/share/FreedomLoader/logs/`
-- Screenshots if applicable
+- Screenshots or recordings if applicable
### Feature Requests
@@ -418,6 +433,6 @@ You are free to use, modify, and redistribute this software under the terms of t
**Freedom Loader** - Put freedom in your downloads
-[Website](https://masteracnolo.github.io/Freedom-Loader-Site/) • [Download](https://github.com/MasterAcnolo/Freedom-Loader/releases) • [Documentation](https://masteracnolo.github.io/Freedom-Loader-Site/wiki) • [Report Bug](https://github.com/MasterAcnolo/Freedom-Loader/issues)
+[Website](https://freedomloader.acnolo.fr/) • [Download](https://github.com/MasterAcnolo/Freedom-Loader/releases) • [Documentation](https://freedomloader.acnolo.fr/wiki) • [Report Bug](https://github.com/MasterAcnolo/Freedom-Loader/issues)
diff --git a/app/autoUpdater.js b/app/autoUpdater.js
index bbb800a..d3abab8 100644
--- a/app/autoUpdater.js
+++ b/app/autoUpdater.js
@@ -9,7 +9,6 @@
*/
const { autoUpdater } = require("electron-updater");
-const { dialog } = require("electron");
const { logger } = require("../server/logger");
/**
@@ -21,6 +20,26 @@ const { logger } = require("../server/logger");
autoUpdater.autoDownload = false;
autoUpdater.autoInstallOnAppQuit = false;
+let updateAvailable = false;
+
+/**
+ * Determines how the update should be handled based on the platform
+ * @returns {"auto" | "snap" | "flatpak" | "linux-native"}
+ */
+function getUpdateEnvironment() {
+ if (
+ process.env.APPIMAGE ||
+ process.platform === "win32" ||
+ process.platform === "darwin"
+ ) {
+ return "auto";
+ }
+ if (process.env.SNAP) return "snap";
+ if (process.env.FLATPAK_ID) return "flatpak";
+
+ return "linux-native";
+}
+
/**
* Initializes application auto-update lifecycle.
*
@@ -32,7 +51,6 @@ autoUpdater.autoInstallOnAppQuit = false;
* @param {BrowserWindow} mainWindow - main Electron window instance
*/
function initAutoUpdater(mainWindow) {
-
/**
* Triggered when a new version is detected.
* Prompts user to install or defer update.
@@ -40,23 +58,12 @@ function initAutoUpdater(mainWindow) {
autoUpdater.on("update-available", async (info) => {
logger.info(`Update available: ${info.version}`);
- const { response } = await dialog.showMessageBox(mainWindow, {
- type: "info",
- title: "Update Available",
- message: `Version ${info.version} is available.`,
- detail: "Would you like to download and install it now?",
- buttons: ["Install Update", "Maybe Later"],
- defaultId: 0,
- cancelId: 1,
+ mainWindow?.webContents.send("update-available", {
+ version: info.version,
+ environment: getUpdateEnvironment(),
});
+ updateAvailable = true;
- if (response === 0) {
- await autoUpdater.downloadUpdate();
- } else {
- mainWindow?.webContents.executeJavaScript(
- `window.showUpdateBadge && window.showUpdateBadge("${info.version}")`
- );
- }
});
/**
@@ -66,8 +73,10 @@ function initAutoUpdater(mainWindow) {
autoUpdater.on("download-progress", (progress) => {
logger.info(`Download progress: ${Math.round(progress.percent)}%`);
mainWindow?.webContents.send("update-progress", {
- percent: Math.round(progress.percent),
- speed: progress.bytesPerSecond,
+ percent: progress.percent,
+ bytesPerSecond: progress.bytesPerSecond,
+ transferred: progress.transferred,
+ total: progress.total,
});
});
@@ -78,17 +87,9 @@ function initAutoUpdater(mainWindow) {
autoUpdater.on("update-downloaded", async (info) => {
logger.info(`Update downloaded: ${info.version}`);
- const { response } = await dialog.showMessageBox(mainWindow, {
- type: "info",
- title: "Update Ready",
- message: `Version ${info.version} has been downloaded.`,
- detail: "The application will restart to apply the update.",
- buttons: ["Install Now", "Later"],
- defaultId: 0,
- cancelId: 1,
- });
+ mainWindow?.webContents.send("update-downloaded", info);
+
- if (response === 0) autoUpdater.quitAndInstall();
});
/**
@@ -102,14 +103,16 @@ function initAutoUpdater(mainWindow) {
* If no update is available, I put this because there is no Linux version before 1.6.0
* @type {boolean}
*/
- const isNoUpdateAvailable = /404/.test(msg) || /Cannot find latest.*\.yml/i.test(msg);
+ const isNoUpdateAvailable =
+ /404/.test(msg) || /Cannot find latest.*\.yml/i.test(msg);
if (isNoUpdateAvailable) {
logger.warn("Auto update: no update metadata found (probably no previous release), ignoring", msg);
return;
}
- logger.error("Auto update error:", msg);
+ logger.error("Auto update error:", err);
+ mainWindow?.webContents.send("update-error", msg);
});
checkForUpdates();
@@ -121,18 +124,9 @@ function initAutoUpdater(mainWindow) {
*/
async function checkForUpdates() {
- /**
- * Prevents electron-updater from running inside sandboxed environments.
- * Snap and Flatpak lock the file system in read-only mode, causing the updater to crash.
- * In these environments, updates are safely handled by snapd or the Flatpak runtime.
- * Standalone Linux packages (.deb, .rpm, AppImage) will bypass this and update normally.
- */
- if (process.env.SNAP || process.env.FLATPAK_ID) {
- logger.info("Running as Snap or Flatpak. Updates are managed by the OS store. Skipping electron-updater.");
- return;
- }
-
- if (!require("electron").app.isPackaged) return ;
+ // If app is was installed from SNAP or Flatpak.
+ if (process.env.SNAP || process.env.FLATPAK_ID) return;
+ if (!require("electron").app.isPackaged) return;
try {
await autoUpdater.checkForUpdates();
@@ -150,6 +144,7 @@ async function downloadUpdate() {
await autoUpdater.downloadUpdate();
} catch (err) {
logger.error("Download failed:", err.message);
+ throw err;
}
}
@@ -160,4 +155,13 @@ function installUpdate() {
autoUpdater.quitAndInstall();
}
-module.exports = { initAutoUpdater, downloadUpdate, installUpdate };
\ No newline at end of file
+function isUpdateAvailable() {
+ return updateAvailable;
+}
+
+module.exports = {
+ initAutoUpdater,
+ downloadUpdate,
+ installUpdate,
+ isUpdateAvailable,
+};
diff --git a/app/changelogManager.js b/app/changelogManager.js
new file mode 100644
index 0000000..449223b
--- /dev/null
+++ b/app/changelogManager.js
@@ -0,0 +1,177 @@
+const { app } = require("electron");
+const fs = require("fs");
+const path = require("path");
+const https = require("https");
+const config = require("../config");
+const { logger } = require("../server/logger");
+
+/** Maximum age of the cached release list before it must be refreshed. */
+const CACHE_TTL_MS = 24 * 60 * 60 * 1000;
+
+/** Filename used to persist changelog data in the Electron user data folder. */
+const CACHE_FILE_NAME = "changelog-cache.json";
+
+/** GitHub API endpoint containing the application's published releases. */
+const RELEASES_URL = "https://api.github.com/repos/MasterAcnolo/Freedom-Loader/releases";
+
+/** Returns the path of the local changelog cache file. */
+function getCachePath() {
+ return path.join(app.getPath("userData"), CACHE_FILE_NAME);
+}
+
+/**
+ * Extracts the numeric major, minor, and patch parts from a version string.
+ *
+ * @param {string} version - Version string or release tag
+ * @returns {number[]|null} Parsed version parts, or null when invalid
+ */
+function normalizeVersion(version) {
+ const match = String(version || "").match(/(\d+)\.(\d+)\.(\d+)/);
+ return match ? match.slice(1).map(Number) : null;
+}
+
+/**
+ * Compares two semantic version strings.
+ *
+ * @param {string} left - First version to compare
+ * @param {string} right - Second version to compare
+ * @returns {number} Negative, zero, or positive comparison result
+ */
+function compareVersions(left, right) {
+ const leftParts = normalizeVersion(left) || [0, 0, 0];
+ const rightParts = normalizeVersion(right) || [0, 0, 0];
+
+ for (let index = 0; index < 3; index += 1) {
+ if (leftParts[index] !== rightParts[index]) {
+ return leftParts[index] - rightParts[index];
+ }
+ }
+
+ return 0;
+}
+
+/** Reads and parses the cached changelog, if one is available. */
+function readCache() {
+ try {
+ return JSON.parse(fs.readFileSync(getCachePath(), "utf8"));
+ } catch {
+ return null;
+ }
+}
+
+/** Persists the fetched releases together with their cache timestamp. */
+function writeCache(releases) {
+ try {
+ fs.writeFileSync(
+ getCachePath(),
+ JSON.stringify({ cachedAt: Date.now(), releases }, null, 2),
+ "utf8",
+ );
+ } catch (error) {
+ logger.warn(`Unable to cache changelog: ${error.message}`);
+ }
+}
+
+/**
+ * Fetches published releases from GitHub and maps them to the UI data shape.
+ *
+ * @returns {Promise