490 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
MasterAcnolo
ed4022dfd1 fix: website link in README 2026-09-04 20:36:03 +02:00
MasterAcnolo
fcbc3f95ac fix: remove old and unusued icons 2026-09-03 20:56:04 +02:00
MasterAcnolo
f3293ef89a fix: stopServer message logic 2026-09-01 16:17:58 +02:00
MasterAcnolo
05e864b93a refactor: getBrowser function, and better firefox management on linux 2026-08-22 22:29:38 +02:00
MasterAcnolo
8ec901dc07 feat: centralize isPlaylistUrl function as a helper that could be upgraded in the future 2026-08-22 22:09:01 +02:00
MasterAcnolo
104500a82a fix: english miss spell 2026-08-21 20:20:12 +02:00
MasterAcnolo
3444f2fc0b Fix: default config in README that was not accurate 2026-08-20 19:39:21 +02:00
MasterAcnolo
86b595beff Fix: default config in README that was not accurate 2026-08-20 19:33:08 +02:00
MasterAcnolo
ed6efcf812 fix: toggle to false the createPlaylistFolder feature 2026-08-19 19:44:56 +02:00
MasterAcnolo
2c84ac7a70 fix: metadata comment displayed version 2026-08-19 17:31:36 +02:00
Axel Nicolas
3c649173a2 Update CONTRIBUTING.md 2026-08-16 10:56:54 +02:00
Axel Nicolas
b4d4ff2665 Merge pull request #47 from MasterAcnolo/refactor-back
Documentation Enhancement
2026-08-16 10:54:30 +02:00
MasterAcnolo
c2584b6df3 fix: remove SUPPORT section 2026-08-16 10:51:26 +02:00
MasterAcnolo
ec16b5209d fix: reduce size of package.json 2026-08-16 10:50:12 +02:00
MasterAcnolo
f2f1649493 add: small notes about build:all script 2026-08-16 10:50:01 +02:00
MasterAcnolo
e5076b0e0b remove: project structure from README 2026-08-16 10:37:42 +02:00
MasterAcnolo
4573917541 add: DEVELOPMENT.md file and refactor contributing and README 2026-08-16 10:36:36 +02:00
MasterAcnolo
62c109939a fix: bump version to 1.6.3-preview and add a check in release.sh, we could'nt publish a release if it's still marked as a preview 2026-08-16 10:03:27 +02:00
MasterAcnolo
b56ec1e62e refactor: some tweaks in .github md templates 2026-08-16 09:58:32 +02:00
MasterAcnolo
1a68c58ee3 refactor: extract shutdown logic in a function, rename server => serverInstance and now we stop the server smoothly 2026-08-16 09:47:25 +02:00
MasterAcnolo
39c4a69be1 add: notes in BINARIES.md about yt-dlp 2026-08-16 09:06:35 +02:00
MasterAcnolo
1993566040 fix: undo last commit 2026-08-15 22:41:17 +02:00
MasterAcnolo
2d19a35ef8 fix: missing element and rename snap to snapcraft in package.json 2026-08-15 22:31:51 +02:00
MasterAcnolo
02c39d53b6 update: version because it's no longer a preview ! 2026-08-15 22:23:31 +02:00
MasterAcnolo
26059aa436 fix: change dark.zip quotes 2026-08-15 21:46:33 +02:00
MasterAcnolo
524cee1bac refactor: topbar.js name and order. And remove 'customconfiguration' declaration in index.html 2026-08-15 21:31:07 +02:00
MasterAcnolo
4de05fcd52 fix: rename appVersion to displayVersionLabel to enhance readability 2026-08-15 21:30:32 +02:00
MasterAcnolo
c2158d99cd fix: remove duplicate import of /config 2026-08-15 21:30:03 +02:00
MasterAcnolo
d84e63fb56 remove: useless code that was used for disable theme on Linux 2026-08-15 18:48:26 +02:00
MasterAcnolo
12d77abbd3 fix: discordRPC error display. I voluntarily disabled raw error printing, because it could have false positive. Could be reenable on purpose 2026-08-15 17:27:50 +02:00
MasterAcnolo
398de8113d fix: the defaultThemesRessourcesPath variable was aiming to and old path, causing warn at every launch 2026-08-15 17:27:08 +02:00
MasterAcnolo
5b13f5d3e0 fix: yt-dlp mismatch in gitignore 2026-08-15 15:21:40 +02:00
MasterAcnolo
7d27889743 refactor: delete yt-dlp binary from repo. 2026-08-15 14:32:50 +02:00
MasterAcnolo
dd4c93e293 add: systemTray information in README and add EXPERIMENTAL Tag 2026-08-15 14:31:12 +02:00
MasterAcnolo
e0d426aa9f add: experimental tag to SYSTEMTRAY Features 2026-08-15 14:20:27 +02:00
MasterAcnolo
c51f3eb25e fix: autoupdater disabled on Flatpak and Snap env 2026-08-15 13:44:47 +02:00
MasterAcnolo
4f8600dbbc add: Aurelien reference in README 2026-08-15 13:20:49 +02:00
MasterAcnolo
63d30720a8 fix(tray): dynamically create/destroy tray icon on setting toggle
Previously, toggling the system tray in the settings updated the configuration but did not instantiate or destroy the tray immediately. This caused a desynchronization bug where the application could minimize to a non-existent tray, creating a ghost instance.

The `set-feature` IPC handler now intercepts the `systemTray` key to apply the state at runtime before persisting the config.
2026-08-15 13:20:49 +02:00
MasterAcnolo
acf7e67d0d feat: custom signature in every file download, as a comment 2026-08-15 13:20:49 +02:00
MasterAcnolo
1a1acb53d1 fix: tray behaviror. App will shutdown gracefully 2026-08-15 13:20:49 +02:00
MasterAcnolo
c153db0244 fix: rename "listeners" in "progressListeners", that enhance readability 2026-08-15 13:20:49 +02:00
MasterAcnolo
4fda512ab5 fix: disable systemTray by default. Could be reenable by default on a next release. 2026-08-15 13:20:49 +02:00
MasterAcnolo
66471ce5ce update: package-lock.json 2026-08-15 13:20:49 +02:00
MasterAcnolo
027aaf7564 fix: log session date format (W Sweden) 2026-08-15 13:20:49 +02:00
MasterAcnolo
7f8d018bcb fix: false warn about DownloadPath at startup 2026-08-15 13:20:49 +02:00
MasterAcnolo
6c517255e3 feat: system tray. Working on Windows, should be tested on Linux (GNOME) env. More features to systemTray coming soon 2026-08-15 13:20:49 +02:00
MasterAcnolo
dc7239379d fix: missing config reference in main.js and tray.js 2026-08-15 13:20:49 +02:00
MasterAcnolo
225ccf4d2d feat: allow app to minimize on the systemTray. Could be disable on purpose. 2026-08-15 13:20:49 +02:00
MasterAcnolo
ce2a0485ab fix: discord RPC on Linux, creating symlink between Discord and Freedom Loader at start if needed 2026-08-15 13:20:49 +02:00
MasterAcnolo
1690cc18b2 refactor: app display in front end. To make difference more obvious between bundled and not 2026-08-15 13:20:49 +02:00
MasterAcnolo
a8c2cf4c81 add: somes startup configuration for dev 2026-08-15 13:20:48 +02:00
MasterAcnolo
f1962f7cd3 update: bump version to 1.6.2 2026-08-15 13:20:48 +02:00
MasterAcnolo
245a463d52 fix: small fixes in README and update project structure 2026-08-15 13:20:48 +02:00
MasterAcnolo
b404e1ba8c add: nodemon configuration for developpment env 2026-08-15 13:20:48 +02:00
MasterAcnolo
b7e54806fd add: nodemon configuration for developpment env 2026-08-15 13:20:48 +02:00
MasterAcnolo
4df373d52b fix: release.sh version format 2026-08-15 13:20:48 +02:00
MasterAcnolo
73aa81673b refactor: enhance log format by adding color when app is run in an IDE or a Terminal 2026-08-15 13:20:48 +02:00
MasterAcnolo
df0952c231 refactor: changed /info router from POST to GET 2026-08-15 13:20:48 +02:00
MasterAcnolo
c67a1511a5 refactor: moved "Auto Fetch Info" settings to advanced settings 2026-08-15 13:20:48 +02:00
MasterAcnolo
d3058a66cf fix: log start format and smalls coms fix 2026-08-15 13:20:48 +02:00
MasterAcnolo
90939e968a update: dependencies 2026-08-15 13:20:48 +02:00
MasterAcnolo
ec83c20e16 feat: automatic release creation with scripts 2026-08-15 13:20:48 +02:00
MasterAcnolo
3f3696e54e refactor: notify.helpers.js logic. Add a timeout and a better notification management to avoid race condition on Unix Systems 2026-08-15 13:20:48 +02:00
MasterAcnolo
321309e294 refactor: notify.helpers.js logic. Add a timeout and a better notification management to avoid race condition on Unix Systems 2026-08-15 13:20:48 +02:00
MasterAcnolo
b9334461bf fix: enhance readme with keepPlaylistOrder information. And fix some typo to fix Linux Env 2026-08-15 13:20:48 +02:00
MasterAcnolo
e09f1a8913 feat: Keep Playlist Order features. Add index before title if it's a playlist and we activate this feature. 2026-08-15 13:20:48 +02:00
MasterAcnolo
bbb0b58d17 fix: unit test wrong folder 2026-08-15 13:20:48 +02:00
MasterAcnolo
47f5b0efef fix: bump version to 1.6.1 and update electron to 43.3.0 2026-08-15 13:20:48 +02:00
MasterAcnolo
6b7645804a fix: force artifact name for exe 2026-08-15 13:20:48 +02:00
MasterAcnolo
9f85acbb59 fix: refresh config at every download. Used by CreatePlaylistFolder 2026-08-15 13:20:48 +02:00
MasterAcnolo
32eb849808 fix: exclude dist, release and build folder from jest config 2026-08-15 13:20:48 +02:00
MasterAcnolo
bc1c62dc35 fix: better dev mode comparaison, useful for tests 2026-08-15 13:20:48 +02:00
MasterAcnolo
72c152d964 feat: unit tests for buildArgs and Notify 2026-08-15 13:20:48 +02:00
MasterAcnolo
596a06a59d fix: snap Store package.json arguments 2026-08-15 13:20:48 +02:00
MasterAcnolo
6d4692486e fix: snap Store mention in SECURITY.md 2026-08-15 13:20:48 +02:00
MasterAcnolo
7e162debcc refactor: rpm package publish 2026-08-15 13:20:48 +02:00
MasterAcnolo
ae6b849aac refactor: rpm package publish 2026-08-15 13:20:48 +02:00
MasterAcnolo
bb8ddabb24 refactor: rpm package publish 2026-08-15 13:20:47 +02:00
MasterAcnolo
620ce7a40c fix: enhance package.json and rename linux package to freedom-loader 2026-08-15 13:20:47 +02:00
MasterAcnolo
9892c6a7dc fix: readme missing items and [!TIP] 2026-08-15 13:20:47 +02:00
MasterAcnolo
bc6283ec5c refactor: security.md 2026-08-15 13:20:47 +02:00
MasterAcnolo
b1377fe21e feat: enhance logger by displaying systeminfo at start of the app 2026-08-15 13:20:47 +02:00
MasterAcnolo
e62ef6bd0c sync: update dependencies and this is ready for windows 2026-08-15 13:20:47 +02:00
MasterAcnolo
49650ecb36 sync: to work on windows 2026-08-15 13:20:47 +02:00
MasterAcnolo
4a56af2491 fix: correct use of logger for path.helpers.js 2026-08-15 13:20:47 +02:00
MasterAcnolo
3cc9f90fdd fix: bring back theme features for Linux 2026-08-15 13:20:47 +02:00
MasterAcnolo
3b555d465e fix: bring back theme features for Linux 2026-08-15 13:20:47 +02:00
MasterAcnolo
857ff773b0 feat: publish package to fedora copr, add script to automaticaly build and upload the file.
Enhance readme and fix auto updater issue
2026-08-15 13:20:47 +02:00
MasterAcnolo
5f2bba2c85 add: BINARIES.md readme file. About how to install the binaries for dev mode 2026-08-15 13:20:47 +02:00
MasterAcnolo
c9a5820076 fix: don't show dialog box if no previous version is available. 2026-08-15 13:20:47 +02:00
MasterAcnolo
336aa33485 add: dark.zip and white.zip theme to the repo 2026-08-15 13:20:47 +02:00
MasterAcnolo
76ce2a8019 fix: small code quality and mismatch. 2026-08-15 13:20:47 +02:00
MasterAcnolo
5bdd13ad8e feat: support Linux. Refactor resources architecture. Create build script
Currently, themes are disabled and firefox notification too.
2026-08-15 13:20:46 +02:00
MasterAcnolo
14b7d944d1 add: default theme applied in app. If no theme is there 2026-08-15 13:20:46 +02:00
MasterAcnolo
3835806e1a fix: missing enableHardwareAcceleration settings in readme 2026-08-15 13:20:46 +02:00
MasterAcnolo
398e9acf66 fix: revert version to 1.5.1 in package.json and README.md 2026-08-15 13:20:46 +02:00
MasterAcnolo
2f203a0bc1 feat: add Devtron in dev mode
remove unused settings item
add setting to disable hardware acceleration (enabled by default)
optimize main.js startup loading
2026-08-15 13:20:46 +02:00
MasterAcnolo
acce91bed8 fix: display "dev" in app version if the app is not packaged 2026-08-15 13:20:46 +02:00
MasterAcnolo
6b33662f79 fix: fix width for form 2026-08-15 13:20:46 +02:00
MasterAcnolo
2805f5f0db refactor: add full documentation and fix theme loading robustness
- Add JSDoc comments across frontend and backend modules
- Improve code readability and maintainability with consistent documentation style
- Document Electron IPC handlers, UI utilities, and theme system
- Add missing function/class documentation in theme loader and app services
- Minor structural improvements and cleanup across Electron main process modules
- Minor fixes on variable names
2026-08-15 13:20:46 +02:00
MasterAcnolo
7ae0ef9a99 fix: app startup. Can't start another instance now. 2026-08-15 13:20:46 +02:00
MasterAcnolo
83dc0a3f2f fix: rename localMode => devMode for clarity 2026-08-15 13:20:46 +02:00
MasterAcnolo
3179b52908 fix: rename localMode => devMode for clarity 2026-08-15 13:20:46 +02:00
MasterAcnolo
9564260c57 fix: update dependencies 2026-08-15 13:20:46 +02:00
MasterAcnolo
d7c2339f61 update: version for 1.6.0 2026-08-15 13:20:46 +02:00
MasterAcnolo
12a6709391 fix: update dependencies 2026-08-15 13:20:46 +02:00
MasterAcnolo
6b625326a4 Fix: remove duplication roadmap (now full in website) 2026-08-15 13:20:46 +02:00
MasterAcnolo
8c40ac2b1c Fix: Configuration File Location 2026-08-15 13:20:46 +02:00
MasterAcnolo
71081163a6 Fix: missing options in readme 2026-08-15 13:20:46 +02:00
MasterAcnolo
4ab6257b39 Update Workshop link to GitHub repository 2026-08-15 13:20:45 +02:00
MasterAcnolo
0d0d60c692 Feat: Update README with detailed project structure and architecture overview 2026-08-15 13:20:45 +02:00
MasterAcnolo
504149a220 Fix: Remove emoji from error messages in download and info controllers 2026-08-15 13:20:45 +02:00
MasterAcnolo
a6ac2333c6 Feat: Add link to the most recent roadmap in README 2026-08-15 13:20:45 +02:00
MasterAcnolo
eb232de34e Fix: Remove z-index from container styles and add z-index to settings button 2026-08-15 13:20:45 +02:00
MasterAcnolo
cda79bd28f Feat: Add section for submitting community themes in README 2026-08-15 13:20:45 +02:00
MasterAcnolo
78e0bcab37 Fix: Save Themes inside "AppData" 2026-08-15 13:20:45 +02:00
MasterAcnolo
dbe313fac4 Feat: Add dynamic icon path for main window based on local mode 2026-08-15 13:20:45 +02:00
MasterAcnolo
2316b73adb Feat: Implement notification feature for download completion 2026-08-15 13:20:45 +02:00
MasterAcnolo
62140be696 Fix: Update links to Freedom-Loader-Site in documentation and IPC handlers 2026-08-15 13:20:45 +02:00
MasterAcnolo
aa3cb24b20 Feat: Add related repository links for website and workshop in README 2026-08-15 13:20:45 +02:00
MasterAcnolo
cbb847800a Fix: Update link to Freedom Loader Workshop in README 2026-08-15 13:20:45 +02:00
MasterAcnolo
8643ed2867 Fix: Correct typo in Workshop badge link in README 2026-08-15 13:20:45 +02:00
MasterAcnolo
e712163c14 Feat: Add Theme Workshop section to README and update features list 2026-08-15 13:20:45 +02:00
MasterAcnolo
edcd7c8f70 Fix: Update external links for website and wiki in IPC handlers 2026-08-15 13:20:45 +02:00
MasterAcnolo
e6b1c8ba62 Feat: Add "Theme Workshop" button and associated IPC handler for workshop access 2026-08-15 13:20:45 +02:00
MasterAcnolo
d90ae6f236 Feat: Enhance theme settings with new button styles for open theme and JSON options 2026-08-15 13:20:45 +02:00
MasterAcnolo
9d4eeca7ee Feat: Implement playlist download count tracking during downloads with UI updates 2026-08-15 13:20:45 +02:00
MasterAcnolo
68c0f24ebb Fix: Adjust z-index for reference 2026-08-15 13:20:45 +02:00
MasterAcnolo
ef8fe131f6 Fix: Moving Custom Top BAr Settings into advanced 2026-08-15 13:20:45 +02:00
MasterAcnolo
596a563e06 Feat: Add createPlaylistFolders feature to organize downloads into dedicated folders with toggle settings 2026-08-15 13:20:45 +02:00
MasterAcnolo
f583a006f2 Fix: Update save path display to include title attribute for better accessibility 2026-08-15 13:20:45 +02:00
MasterAcnolo
3e2b7e8190 Fix: Enhance error handling for playlist fetch failures by retrying with single video 2026-08-15 13:20:45 +02:00
MasterAcnolo
4b725bed8b Fix: Adjust stop button hover scale effect for better user experience 2026-08-15 13:20:45 +02:00
MasterAcnolo
cf596ddb13 Fix: Update button text 2026-08-15 13:20:45 +02:00
MasterAcnolo
6812296dbc Refactor: auto-update functionality with user prompts and download progress 2026-08-15 13:20:45 +02:00
MasterAcnolo
94003135fd Feat: Enhance download experience with progress indicators and toast notifications 2026-08-15 13:20:45 +02:00
MasterAcnolo
95688a5dff Fix: IPC button not working 2026-08-15 13:20:44 +02:00
MasterAcnolo
592cc3314a Feat: Implement download cancellation feature with stop button and progress handling 2026-08-15 13:20:44 +02:00
MasterAcnolo
67b058e1ac Feat: Add theme reloading functionality with refresh button in settings panel 2026-08-15 13:20:44 +02:00
MasterAcnolo
264ac071df Fix: Remove themes from the theme order array 2026-08-15 13:20:44 +02:00
MasterAcnolo
0bd3e10094 Feat: Implement splash screen with progress indicators during startup 2026-08-15 13:20:44 +02:00
MasterAcnolo
c656b30172 Fix: Display custom these with a basic order 2026-08-15 13:20:44 +02:00
MasterAcnolo
3d1920e6e9 Fix: Remove empty title tag from index.html and unused app styles from styles.css 2026-08-15 13:20:44 +02:00
MasterAcnolo
372fc921f0 Feat: Add theme folder access functionality in UI 2026-08-15 13:20:44 +02:00
MasterAcnolo
c32e382685 Feat: Custom Theme features and clean the old one 2026-08-15 13:20:44 +02:00
MasterAcnolo
4d77e843af Fix: Theme Saving 2026-08-15 13:20:44 +02:00
MasterAcnolo
3c30e49523 fix: move config to AppData to prevent settings loss on update, add config.dev.json for dev environment 2026-08-15 13:20:44 +02:00
MasterAcnolo
6b4310500e Refactor: split main.js into app/ modules 2026-08-15 13:20:44 +02:00
MasterAcnolo
252618dfd1 Update: Readme version and file structure 2026-08-15 13:20:44 +02:00
MasterAcnolo
fb7f0329b6 Fix: Remove unusued element 2026-08-15 13:20:44 +02:00
MasterAcnolo
4feea9c530 Fix: Update theme JSON structure for audio and button settings 2026-08-15 13:20:44 +02:00
MasterAcnolo
e8fe990e01 Refactor: Update CSS variables
Fix Progress bar
2026-08-15 13:20:44 +02:00
MasterAcnolo
086247068e Refactor: Restructure HTML layout and enhance CSS styles for improved UI 2026-08-15 13:20:44 +02:00
MasterAcnolo
48a218efca Fix: Settings Secondary Color in LIght THeme 2026-08-15 13:20:44 +02:00
MasterAcnolo
24fc685bf6 Fix: Remove unusued pictures 2026-08-15 13:20:44 +02:00
MasterAcnolo
731cdc38ec Update: App Version in ReadME 2026-08-15 13:20:44 +02:00
MasterAcnolo
391c343b6a Update: Dependencies 2026-08-15 13:20:44 +02:00
MasterAcnolo
cbe689e38e Fix: Wrong files Path 2026-08-15 13:20:44 +02:00
MasterAcnolo
1ac035e160 Update: YT-DLP 2026-08-15 13:20:43 +02:00
MasterAcnolo
fb8e2acda0 Refactor: Suffixe in helpers/ 2026-08-15 13:20:43 +02:00
MasterAcnolo
3dd9f53e80 Fix: Remove unusued modules and disable devTools when packaged 2026-08-15 13:20:43 +02:00
MasterAcnolo
269b9e69ba Fix: Update YT-DLP 2026-08-15 13:20:43 +02:00
MasterAcnolo
5f3b2a80a9 Fix: Increment app version 2026-08-15 13:20:43 +02:00
MasterAcnolo
fdda78c802 Fix: Dependencies 2026-08-15 13:20:43 +02:00
MasterAcnolo
a82e8ceefd Docs: Update ReadME 2026-08-15 13:20:42 +02:00
MasterAcnolo
c9dd09d341 Fix: Update Dependencies 2026-08-15 13:20:42 +02:00
MasterAcnolo
78191d27ad Fix: Increment App Version 2026-08-15 13:20:42 +02:00
MasterAcnolo
33290e029b Feat: You can now fetch image from all website. 2026-08-15 13:20:42 +02:00
MasterAcnolo
245cf01a43 Refactor: Translate all comms in English 2026-08-15 13:20:42 +02:00
MasterAcnolo
2689424d01 Fix: Remove Discord RPC Update. It's useless and take performance for nothing. 2026-08-15 13:20:42 +02:00
MasterAcnolo
bfb0873b3d Fix: Reduce Line Complexity featuresPath 2026-08-15 13:20:42 +02:00
MasterAcnolo
3a11395301 Fix: Try to re run workflow 2026-08-15 13:20:42 +02:00
MasterAcnolo
5dc7c229b0 Fix: Undo Path Validation for coming back on a classic one 2026-08-15 13:20:42 +02:00
MasterAcnolo
95497d252f Fix: Centralize default download path and enhance path validation logic 2026-08-15 13:20:42 +02:00
MasterAcnolo
784834cbbc Fix: Correct windowMs value in rate limit configuration to 15 minutes 2026-08-15 13:20:42 +02:00
MasterAcnolo
b6d77444e1 Fix: Refactor conditional statements for starting Discord RPC and Auto Update 2026-08-15 13:20:42 +02:00
MasterAcnolo
dd4d770102 Fix: Correct comment for rate limit window duration in rateLimit.js 2026-08-15 13:20:42 +02:00
MasterAcnolo
a374993ab1 Fix: Uncomplete comments and remove repeated code 2026-08-15 13:20:42 +02:00
MasterAcnolo
d99fad1c3d Fix: Update full changelog link format in release template 2026-08-15 13:20:42 +02:00
MasterAcnolo
9f4563dbd8 Fix: Improve browser detection logging and error handling 2026-08-15 13:20:42 +02:00
MasterAcnolo
5bb6b6c165 Fix: Centralize resource paths and icons 2026-08-15 13:20:42 +02:00
MasterAcnolo
f8e346b3cb Fix: Small coms in AppVersion.js 2026-08-15 13:20:42 +02:00
MasterAcnolo
ee685695de Fix: App Not Stopping correctly + Better Logs for end :) 2026-08-15 13:20:42 +02:00
MasterAcnolo
3e2ff74876 Fix: We are not on apple system. Don't care about Darwin 2026-08-15 13:20:42 +02:00
MasterAcnolo
bbdb6a37fb Fix: Missing semi colon, Better Folder Creation, And remove the fucking bad code who was repeat too much (wtf i did) 2026-08-15 13:20:42 +02:00
MasterAcnolo
7b55a13ce6 Fix: Update version numbers to 1.4.4 in package.json and ReadME 2026-08-15 13:20:42 +02:00
MasterAcnolo
a4efedbe19 Fix: Update playlist badge text to English 2026-08-15 13:20:42 +02:00
MasterAcnolo
c3d43cafbf Fix: Update URL check to correctly identify playlist links 2026-08-15 13:20:42 +02:00
MasterAcnolo
bf91df3a27 Feat: Add Better playlist layout 2026-08-15 13:20:42 +02:00
MasterAcnolo
28928ec174 Fix: Add Small Comms for clarity 2026-08-15 13:20:42 +02:00
MasterAcnolo
f5c51e254a Fix: Remove Unusued Code 2026-08-15 13:20:42 +02:00
MasterAcnolo
36deddd823 Fix: Big mistake when estimating video format (? -> &) 2026-08-15 13:20:42 +02:00
MasterAcnolo
f6eca7f3f6 Fix: Update Dependencies 2026-08-15 13:20:42 +02:00
MasterAcnolo
ccf9e5bc03 Fix: Update ReadME App Version 2026-08-15 13:20:42 +02:00
MasterAcnolo
082f19bda4 Feat: Add Paste BTN URL Input 2026-08-15 13:20:42 +02:00
MasterAcnolo
67ecd28aeb Fix: Increment App version 2026-08-15 13:20:41 +02:00
MasterAcnolo
02d60bddd2 Fix: Update release badge to version 1.4.2 2026-08-15 13:20:41 +02:00
MasterAcnolo
5236165b47 Fix: 1.4.2 Version 2026-08-15 13:20:41 +02:00
MasterAcnolo
044091a9c3 Fix: yt-dlp update 2026-08-15 13:20:41 +02:00
MasterAcnolo
a04730243d Fix: Dependencies Vulnerability 2026-08-15 13:20:41 +02:00
MasterAcnolo
b9fa931cd7 Potential fix for code scanning alert no. 5: Type confusion through parameter tampering
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2026-08-15 13:20:25 +02:00
MasterAcnolo
c1c1f9a870 Update: YT-DLP 2026-08-15 13:20:24 +02:00
MasterAcnolo
538cb6b51a Update acknowledgments in README.md
Added acknowledgment for Deno and Jacques Chirac.
2026-08-15 13:20:24 +02:00
MasterAcnolo
5319bee9ee Add: Additional dependencies section in README with required binaries 2026-08-15 13:20:24 +02:00
MasterAcnolo
1edec6509e Mark completed tasks in the roadmap as strikethrough
Updated the roadmap to mark completed tasks as strikethrough.
2026-08-15 13:20:24 +02:00
MasterAcnolo
22a3eb7524 Fix: ReadME 2026-08-15 13:20:24 +02:00
MasterAcnolo
b5d058902c Fix: Bug Report Template 2026-08-15 13:20:24 +02:00
MasterAcnolo
0e4134b172 Fix: Security 2026-08-15 13:20:24 +02:00
MasterAcnolo
02973067b1 Fix: Security, Contributing, Code Of Conduct 2026-08-15 13:20:24 +02:00
MasterAcnolo
b1064cceba Feat: Templates for PR and Issues 2026-08-15 13:20:24 +02:00
MasterAcnolo
536fb3678e Update GitHub username for SpicyFire21 2026-08-15 13:20:24 +02:00
MasterAcnolo
dc3f19e95e Enhance README with detailed features list
Expanded features section with detailed descriptions of new functionalities including multi-format downloads, video information preview, quality selection, and more.
2026-08-15 13:20:24 +02:00
MasterAcnolo
a575416801 Revise README for improved structure and clarity
Updated README with new structure and content for clarity and organization.
2026-08-15 13:20:24 +02:00
MasterAcnolo
e74ef502d4 Fix: ReadME Version 1.4.1 2026-08-15 13:20:24 +02:00
MasterAcnolo
ee7c4a1f09 Add: App Version for 1.4.1 2026-08-15 13:20:24 +02:00
MasterAcnolo
8771291e61 Fix: Discord RPC not Stoping #26 2026-08-15 13:20:24 +02:00
MasterAcnolo
769753f25e Fix: Auto Update mispelling (Am a monkey) 2026-08-15 13:20:24 +02:00
MasterAcnolo
455d51e7cd Add: Video Codec 2026-08-15 13:20:24 +02:00
MasterAcnolo
5ef9f42bd7 Fix: Remove unusued "Settings.svg" icon 2026-08-15 13:20:24 +02:00
MasterAcnolo
0c8322db13 Fix: Try to force AAC Audio Codec 2026-08-15 13:20:24 +02:00
MasterAcnolo
e653a7f82f Fix: Check Info config every request 2026-08-15 13:20:24 +02:00
MasterAcnolo
e5e9d90552 Fix: Config not working propely 2026-08-15 13:20:24 +02:00
MasterAcnolo
6a14056804 Fix: Remove Unusued Import 2026-08-15 13:20:24 +02:00
MasterAcnolo
1dd9bd59b5 Fix: Reference position bottom 2026-08-15 13:20:24 +02:00
MasterAcnolo
fb34eb9f80 Because why not ? 2026-08-15 13:20:24 +02:00
MasterAcnolo
ccbfccb76a Fix: Remove unusued files 2026-08-15 13:20:24 +02:00
MasterAcnolo
416f3ac4c5 Add: Settings theme color 2026-08-15 13:20:23 +02:00
MasterAcnolo
df1047c21e Fix: Neon Theme rename 2026-08-15 13:20:23 +02:00
MasterAcnolo
0a2fe5500c Feat: Settings Panel 2026-08-15 13:20:23 +02:00
MasterAcnolo
698d75184c Sync: Synchro Settings Panel 2026-08-15 13:20:23 +02:00
MasterAcnolo
2fa8951f67 Add: Settings Icon 2026-08-15 13:20:23 +02:00
MasterAcnolo
a480cecf39 Fix: Update Dependencies 2026-08-15 13:20:23 +02:00
MasterAcnolo
558408b12b Fix: Indent Index.html 2026-08-15 13:20:23 +02:00
MasterAcnolo
95909e7ba4 Fix: Update ReadME Banner Redirection 2026-08-15 13:20:23 +02:00
MasterAcnolo
c70218a966 Fix: Remove Unusued Dependencies 2026-08-15 13:20:23 +02:00
MasterAcnolo
de352039ea Fix: Default Theme Info Color 2026-08-15 13:20:23 +02:00
MasterAcnolo
0169f61614 FIx: Updated Appliction Version because i did too much work to be a "minor release" 2026-08-15 13:20:23 +02:00
MasterAcnolo
da2fd72984 Fix: Put config.json file into config folder for better readability + Fix Yt-DLP Path 2026-08-15 13:20:23 +02:00
MasterAcnolo
1c43c795af Fix: Remove unusued import server.js 2026-08-15 13:20:23 +02:00
MasterAcnolo
e7fa3d9639 Fix: Update Dependencies 2026-08-15 13:20:23 +02:00
MasterAcnolo
ecffb3eab1 Fix: Using ASAR for every parts of the projects (the server is now in it) and i put the binaries in a /binaries folder. It's better for readability of the resources folder 2026-08-15 13:20:23 +02:00
MasterAcnolo
e99b16eac6 Add: Better logs. Write logs when a download is init 2026-08-15 13:20:23 +02:00
MasterAcnolo
d3478b18f7 Fix: Remove Useless Console.log 2026-08-15 13:20:23 +02:00
MasterAcnolo
42a235d916 Feat: Add Configuration Button. user can edit config.json as wanted. Linked and IPC 2026-08-15 13:20:23 +02:00
MasterAcnolo
f716316244 Fix: Remove unusued Variable 2026-08-15 13:20:23 +02:00
MasterAcnolo
f059361b4e Feat: Add CustomVideo Codec Choice with verification in config.json 2026-08-15 13:20:23 +02:00
MasterAcnolo
5b4cabdbd0 Feat: Add Newline YT-DLP Args. Add Toggle Switches for verbose, autodownloadplaylist + Fix Thumbail misspell 2026-08-15 13:20:23 +02:00
MasterAcnolo
429720ae06 Fix: Correct Config.json file for packaged version 2026-08-15 13:20:23 +02:00
MasterAcnolo
4f1f4c948d Feat: Add version badge controlled. Before we needed to write with the hand for the front. But now there is an IPC event that provide App Version from package.json 2026-08-15 13:20:23 +02:00
MasterAcnolo
5855d7a933 Feat: Package Update + Add NSIS Installer Banner 2026-08-15 13:20:23 +02:00
MasterAcnolo
4c85c138b8 Feat: Path Verification (Security #4) 2026-08-15 13:20:23 +02:00
MasterAcnolo
9b10fab27b Fix: ReadME Badge Version 2026-08-15 13:20:23 +02:00
MasterAcnolo
0933cae8e1 Fix: Increment App Version for 1.3.2 2026-08-15 13:20:23 +02:00
MasterAcnolo
d16eed54ea Feat: Implement Config File. Read by Front and Back. It aims to give possibility to disable some features quickly. Currenty: DiscordRPC, AutoCheckInfo, AutoUpdate, Custom TopBar, AddMetadata, Add Thumbail were put and implement. Other need to be implement. That's the begin of Settings Panel 2026-08-15 13:20:22 +02:00
MasterAcnolo
83f7f843b0 Feat: Add Codec H264 to vidéo output 2026-08-15 13:20:22 +02:00
MasterAcnolo
a02264b8c9 Update: Update Script + Dependencies 2026-08-15 13:20:22 +02:00
MasterAcnolo
2eda434a78 Add: Some Variables in config.js. For Modules 2026-08-15 13:20:22 +02:00
MasterAcnolo
b8542b9c51 Fix: Remove Unsued Import in Controller 2026-08-15 13:20:22 +02:00
MasterAcnolo
c8376464e5 Add: Rate Limit to front rendering. Using Components for reusability 2026-08-15 13:20:22 +02:00
MasterAcnolo
d41006e5cb Fix: Error Handling Download Controller 2026-08-15 13:20:22 +02:00
MasterAcnolo
b3c13e59a7 Fix: Error Handling Info Controller 2026-08-15 13:20:22 +02:00
MasterAcnolo
d54120ba5a Fix: Security #5 2026-08-15 13:20:22 +02:00
MasterAcnolo
f1ce53a833 Add pull request template for consistent feature submissions 2026-08-15 13:20:22 +02:00
MasterAcnolo
2321acfe7d Replace feature and version templates in PULL_REQUEST_TEMPLATE directory 2026-08-15 13:20:22 +02:00
MasterAcnolo
c07ff64177 Add feature issue template for improved feature requests 2026-08-15 13:20:22 +02:00
MasterAcnolo
1ffc213fdd Add version template for changelog and roadmap 2026-08-15 13:20:22 +02:00
MasterAcnolo
aba339116c Create SECURITY.md 2026-08-15 13:20:22 +02:00
MasterAcnolo
0e3e65d36f Create CONTRIBUTING.md for project guidelines
Added a comprehensive contributing guide for Freedom Loader, outlining how to report issues, submit pull requests, and improve documentation.
2026-08-15 13:20:22 +02:00
MasterAcnolo
d4aa1f3b94 Add Contributor Covenant Code of Conduct
This document outlines the Contributor Covenant Code of Conduct, including our pledge, standards of behavior, enforcement responsibilities, and consequences for violations.
2026-08-15 13:20:22 +02:00
MasterAcnolo
3af1542ccd Update issue templates 2026-08-15 13:20:22 +02:00
MasterAcnolo
bfeb4da529 refactor: Remove setupMenu function and its invocation from app initialization 2026-08-15 13:20:22 +02:00
MasterAcnolo
28f7459d9c feat: Add version badge 2026-08-15 13:20:22 +02:00
MasterAcnolo
9a59479b5c style: Update z-index for theme switcher and topbar; add custom scrollbar styles 2026-08-15 13:20:22 +02:00
MasterAcnolo
07387351da fix: Update topbar width to use 100% instead of 100vw for better responsiveness 2026-08-15 13:20:22 +02:00
MasterAcnolo
295971ef68 Fix: Update release badge to version 1.3.1 in README 2026-08-15 13:20:22 +02:00
MasterAcnolo
967583894f feat: Implement topbar with custom window controls and actions 2026-08-15 13:20:22 +02:00
MasterAcnolo
170bfc13f6 Fix: Startup verif + Update Package 2026-08-15 13:20:22 +02:00
MasterAcnolo
54a81b8e81 Refactor badge links in README for improved layout and consistency 2026-08-15 13:20:22 +02:00
MasterAcnolo
34343ef692 Refactor dependencies section in README for improved layout and styling 2026-08-15 13:20:22 +02:00
MasterAcnolo
3152ae17dd Refactor layout of dependencies section in README for improved styling 2026-08-15 13:20:22 +02:00
MasterAcnolo
750b07b69a Refactor badge links in README for improved readability 2026-08-15 13:20:22 +02:00
MasterAcnolo
118e4aa6a7 Update version to 1.3.1 and add update script to package.json 2026-08-15 13:20:21 +02:00
MasterAcnolo
776105ce48 Add native dependencies check and error handling on startup 2026-08-15 13:20:21 +02:00
MasterAcnolo
838f5481b6 Bump version to 1.3.1 and fix Discord RPC state URL; enhance update notification with clickable link to releases 2026-08-15 13:20:21 +02:00
MasterAcnolo
2a66728f40 Merge Main 2026-08-15 13:20:21 +02:00
MasterAcnolo
07899d0a43 Update Freedom Loader link in README.md 2026-08-15 13:20:21 +02:00
MasterAcnolo
2dd4f016a7 Size Reduce README 2026-08-15 13:20:20 +02:00
MasterAcnolo
fdeca41ec7 Size Reduce README 2026-08-15 13:20:20 +02:00
MasterAcnolo
9d2ddc33df ReadME Firefox 2026-08-15 13:20:20 +02:00
MasterAcnolo
c17ab8c934 ReadME Firefox 2026-08-15 13:20:20 +02:00
MasterAcnolo
12464f84dd Added Website Top Menu Section 2026-08-15 13:20:20 +02:00
MasterAcnolo
b6583dc7ee Update Banner 2026-08-15 13:20:20 +02:00
MasterAcnolo
9fadb4339e Fix Wiki Path + Icon + Bring Back Spicy Theme + New banner (Logo) 2026-08-15 13:20:20 +02:00
MasterAcnolo
7c3fa0f69d Better RPC 2026-08-15 13:20:20 +02:00
MasterAcnolo
c27da25a2b Bring Back Spicy Fire Theme from darkness 2026-08-15 13:20:20 +02:00
MasterAcnolo
f36e456d1d ReadME Change Logo 2026-08-15 13:20:20 +02:00
MasterAcnolo
0af4c6e0a3 ReadME Fix 2026-08-15 13:20:20 +02:00
MasterAcnolo
4ee4053faf New Logo 2026-08-15 13:20:20 +02:00
MasterAcnolo
6f91a77692 main fix 2026-08-15 13:20:20 +02:00
MasterAcnolo
937866c60c ReadME 2026-08-15 13:20:20 +02:00
MasterAcnolo
4cc2dbe3b7 ReadME 2026-08-15 13:20:20 +02:00
MasterAcnolo
20f887d07f ReadME 2026-08-15 13:20:20 +02:00
MasterAcnolo
4dbbdb94d5 ReadME 2026-08-15 13:20:19 +02:00
MasterAcnolo
3b01c1d560 ReadME 2026-08-15 13:20:19 +02:00
MasterAcnolo
f86aaaff46 English Index.html audio 2026-08-15 13:20:19 +02:00
MasterAcnolo
778218aaca ReadME 2026-08-15 13:20:19 +02:00
MasterAcnolo
be44762483 ReadME 2026-08-15 13:20:19 +02:00
MasterAcnolo
689aa72e55 ReadME 2026-08-15 13:20:19 +02:00
MasterAcnolo
2d03c785ac ReadME 2026-08-15 13:20:19 +02:00
MasterAcnolo
37913b4edb ReadME 2026-08-15 13:20:19 +02:00
MasterAcnolo
399a50e64d ReadME 2026-08-15 13:20:19 +02:00
MasterAcnolo
50c93493a7 ReadME 2026-08-15 13:20:19 +02:00
MasterAcnolo
9206091cda English 2026-08-15 13:20:19 +02:00
MasterAcnolo
db5e3ec9ec Anglais Fetch Info.js + Removed Files Check 2026-08-15 13:20:19 +02:00
MasterAcnolo
3a9d4ecb80 Change Language Main.js 2026-08-15 13:20:19 +02:00
MasterAcnolo
57a9634ba6 Better Log Components Path 2026-08-15 13:20:19 +02:00
MasterAcnolo
fea24e4cb8 Translate English 2026-08-15 13:20:19 +02:00
MasterAcnolo
e5d06b25f4 NO AID HERE 2026-08-15 13:20:19 +02:00
MasterAcnolo
cd71d9ecbd NO AID HERE 2026-08-15 13:20:19 +02:00
MasterAcnolo
fcc58e54d3 Notify Logic 2026-08-15 13:20:19 +02:00
MasterAcnolo
8208bab1b5 Info Loading Bar 2026-08-15 13:20:19 +02:00
MasterAcnolo
67e42d6e78 Change Error Info Box + Clean HTML 2026-08-15 13:20:19 +02:00
MasterAcnolo
c5aefaef7f SongBird Theme + Playlist Background For Every Theme 2026-08-15 13:20:19 +02:00
MasterAcnolo
3d788285d7 Download Speed 2026-08-15 13:20:19 +02:00
MasterAcnolo
dec4f721cf Clean Code 2026-08-15 13:20:19 +02:00
MasterAcnolo
f0e91ef0a9 Loading Bar taskbar 2026-08-15 13:20:19 +02:00
MasterAcnolo
c53c9b9d43 Using Download Route MVC Model 2026-08-15 13:20:19 +02:00
MasterAcnolo
7c8b37a35d Push 2026-08-15 13:20:18 +02:00
MasterAcnolo
17e5157d9a Using MVC Model + AutoDeteck Package + Fix Download/Info 2026-08-15 13:20:18 +02:00
MasterAcnolo
027572d666 Fix Deno Not Using on Info.js 2026-08-15 13:20:18 +02:00
MasterAcnolo
2ed4d99c6f Deno Integré + Progress Bar Fix + Small Clean 2026-08-15 13:20:18 +02:00
MasterAcnolo
8fd3404064 Removed Cyberpunk | Spicy | Vilbrequin Theme, Info Fix, Fix Progress Bar. Centralized Ressources 2026-08-15 13:20:18 +02:00
MasterAcnolo
fcbc3b3b77 ProgressBar + Playlist 2026-08-15 13:20:18 +02:00
MasterAcnolo
7ea3e316ac Playlist Card + Safe Args 2026-08-15 13:20:18 +02:00
MasterAcnolo
cb20028119 UpperCase 2026-08-15 13:20:18 +02:00
MasterAcnolo
9f5a63042d Autoupdate + FreedomKiller Démarrage 2026-08-15 13:20:18 +02:00
MasterAcnolo
0708edeabc Fix AutoUpdate 2026-08-15 13:20:18 +02:00
MasterAcnolo
3190b7a835 Version Config Depend PackageJSON 2026-08-15 13:20:18 +02:00
MasterAcnolo
922315883d Auto Update GitHub 2026-08-15 13:20:18 +02:00
MasterAcnolo
2eff372377 Log Discord RPC 2026-08-15 12:33:16 +02:00
MasterAcnolo
ccf852fc97 Discord RPC Fix 2026-08-15 12:33:16 +02:00
MasterAcnolo
9139fa4545 Documentation Bouton + Package Lock Update 2026-08-15 12:33:16 +02:00
MasterAcnolo
21eccfaae2 Removed Licence Preload 2026-08-15 12:33:16 +02:00
MasterAcnolo
9e7e600645 Removed console.log RPC 2026-08-15 12:33:16 +02:00
MasterAcnolo
4bf0e55d70 Removed Else Statement 2026-08-15 12:33:16 +02:00
MasterAcnolo
d3191b3c3e Notify Error + Cookies Gestion 2026-08-15 12:33:15 +02:00
MasterAcnolo
f91cdbefca RPC Com 2026-08-15 12:33:15 +02:00
MasterAcnolo
3fdefad69b Port Lisible Server.js 2026-08-15 12:33:15 +02:00
MasterAcnolo
f910bf7543 YTDLP Path DevMode 2026-08-15 12:33:15 +02:00
MasterAcnolo
cfc29956d0 Fix Double Log Start 2026-08-15 12:33:15 +02:00
MasterAcnolo
737ed2b14e Helpers + Clean Download et Info 2026-08-15 12:33:14 +02:00
MasterAcnolo
338f712bd4 Push avant Clean 2026-08-15 12:33:14 +02:00
MasterAcnolo
0501496ee4 Préparation du Champ de Bataille 2026-08-15 12:33:12 +02:00
MasterAcnolo
05d0ad77f8 ReadME Okay for 1.3 2026-08-15 12:33:10 +02:00
MasterAcnolo
6403cb3c71 Fix Version Mismatch (1.3.1 -> 1.3.0) 2026-08-15 12:33:10 +02:00
MasterAcnolo
3d7eadab21 Package UPDATE For 1.3.1 2026-08-15 12:33:10 +02:00
MasterAcnolo
4759040daf Change log file path for bug reports
Updated log file path in README for bug reports.
2026-08-15 12:32:53 +02:00
MasterAcnolo
377f41e052 Change Apercu README 2026-08-15 12:32:53 +02:00
MasterAcnolo
581e8bc3af LatestRelease No Space 2026-08-15 12:32:53 +02:00
MasterAcnolo
5428e67451 LatestRelease Space 2026-08-15 12:32:53 +02:00
MasterAcnolo
fcad2e707a ReadME Fix 2026-08-15 12:32:53 +02:00
MasterAcnolo
ca66d9f668 ReadME Fix 2026-08-15 12:32:53 +02:00
MasterAcnolo
6e22a01e0f ReadME Fix 2026-08-15 12:32:53 +02:00
MasterAcnolo
d99b5ae2b3 ReadME Fix 2026-08-15 12:32:53 +02:00
MasterAcnolo
146ee1e17f Removed ReadME FR and Rework ReadME 2026-08-15 12:32:53 +02:00
MasterAcnolo
567e1dfb04 Change project license from MIT to GNU License 2026-08-15 12:32:53 +02:00
MasterAcnolo
6e047fb386 Update project purpose in README-FR.md 2026-08-15 12:32:06 +02:00
MasterAcnolo
f4db1381da Update README.md 2026-08-15 12:32:06 +02:00
MasterAcnolo
0f84bf275f Clear NPM Scripts 2026-08-15 12:32:06 +02:00
MasterAcnolo
01daca1d76 YT-DLP Maj Automatique 2026-08-15 12:32:06 +02:00
MasterAcnolo
c8a13dc671 Préparation 1.2.4 2026-08-15 12:32:06 +02:00
MasterAcnolo
15163009f6 ReadME Update 2026-08-15 12:32:05 +02:00
MasterAcnolo
b3b3b99a68 YT-DLP Update + Fix Crash 2026-08-15 12:32:05 +02:00
MasterAcnolo
5d7c592758 Fix Spicy Image 2026-08-15 12:32:05 +02:00
MasterAcnolo
30d7bc683a FFPROBE 2026-08-15 12:32:05 +02:00
MasterAcnolo
150505b8bb ReadME Update 2026-08-15 12:32:05 +02:00
MasterAcnolo
874007b229 Title Window 2026-08-15 12:32:05 +02:00
MasterAcnolo
7e5facdf2a Asar 2026-08-15 12:32:05 +02:00
MasterAcnolo
4a8422d002 Asar 2026-08-15 12:32:05 +02:00
MasterAcnolo
4d4211d288 Package Update + Notif Fix Issues#13 2026-08-15 12:32:05 +02:00
MasterAcnolo
6a945eb413 FFMPEG Location 2026-08-15 12:32:05 +02:00
MasterAcnolo
7a0122d160 Correct Log Folder Name 2026-08-15 12:32:05 +02:00
MasterAcnolo
c06f0355f2 Fix Server.js Path 2026-08-15 12:32:05 +02:00
MasterAcnolo
7bd4c7cf68 GitIgnore Fix 2026-08-15 12:32:05 +02:00
MasterAcnolo
7abdc30cb0 Package Updated for 1.2.1 + ReadME Fix 2026-08-15 12:32:05 +02:00
MasterAcnolo
6b8b5434f9 Package JSON Updated for 1.2.1 2026-08-15 12:32:04 +02:00
MasterAcnolo
54031e7ead ADD Licence 'discordRPC.js' 2026-08-15 12:32:04 +02:00
MasterAcnolo
d70e6fead7 YT-DLP Moove 2026-08-15 12:32:04 +02:00
MasterAcnolo
530c7f31e6 ReadME Update 2026-08-15 12:32:04 +02:00
MasterAcnolo
82ad264743 Package Update 2026-08-15 12:32:04 +02:00
MasterAcnolo
699696b4cf Package Update 2026-08-15 12:32:04 +02:00
MasterAcnolo
a3355341c1 Discord RPC 2026-08-15 12:32:04 +02:00
MasterAcnolo
a5e0c1d685 ReadME Update 2026-08-15 12:32:04 +02:00
MasterAcnolo
cdb4160411 Inversion ReadME Principal 2026-08-15 12:32:03 +02:00
MasterAcnolo
ba2b51fddc NF Theme + Ajustement Other Themes 2026-08-15 12:32:03 +02:00
MasterAcnolo
180b8f2407 Update YT-DLP 2026-08-15 12:32:03 +02:00
MasterAcnolo
f8eca37f7e Retrait '--Restrict Filenames' 2026-08-15 12:32:03 +02:00
MasterAcnolo
c9a3c0bda2 ReadME Update 2026-08-15 12:32:03 +02:00
MasterAcnolo
6787970343 NPM Update 2026-08-15 12:32:02 +02:00
MasterAcnolo
7eda7bbf96 Update YTDLP + New Icon 2026-08-15 12:32:02 +02:00
MasterAcnolo
7c3771e8eb Clarification 2026-08-15 12:32:02 +02:00
MasterAcnolo
35f8b5fbdd Add Metadata / Thumbail
Optimisation + Retries
2026-08-15 12:32:02 +02:00
MasterAcnolo
4a3a893f41 ReadME Fix 2026-08-15 12:32:02 +02:00
MasterAcnolo
9317f7b999 Logs Fix + Logs Button 2026-08-15 12:32:02 +02:00
MasterAcnolo
30ca987bf9 Fix Download 2026-08-15 12:32:02 +02:00
MasterAcnolo
296bb3762b Logger Error Fix 2026-08-15 12:32:02 +02:00
MasterAcnolo
0f712e9c42 NPM Update 2026-08-15 12:32:02 +02:00
MasterAcnolo
2b96ce0038 NPM Update 2026-08-15 12:32:02 +02:00
MasterAcnolo
1e3239e0d2 ReadME Update 2026-08-15 12:32:01 +02:00
MasterAcnolo
11e5da54e0 Notif + Update YT DLP 2026-08-15 12:32:01 +02:00
MasterAcnolo
a0cf89481b Notification + Open On CLICK 2026-08-15 12:32:01 +02:00
MasterAcnolo
5512e7b3ec NPM Update 2026-08-15 12:32:01 +02:00
MasterAcnolo
6219d07e35 ReadME Fix 2026-08-15 12:32:01 +02:00
MasterAcnolo
2d611ac06c ReadME Fix 2026-08-15 12:32:01 +02:00
MasterAcnolo
abc488b566 ReadME Fix 2026-08-15 12:32:01 +02:00
MasterAcnolo
824077a5b0 ReadME Fix 2026-08-15 12:32:01 +02:00
MasterAcnolo
a0401623fb ReadME Fix 2026-08-15 12:32:01 +02:00
MasterAcnolo
34d577d22a ReadME FIX 2026-08-15 12:31:54 +02:00
MasterAcnolo
4ac97a0221 Package Fix 2026-08-15 12:31:35 +02:00
MasterAcnolo
b3d11bb07c Logs Fix 2026-08-15 12:31:35 +02:00
MasterAcnolo
03c374493c ReadME Pictures Fix 2026-08-15 12:31:34 +02:00
MasterAcnolo
9aef213aa4 Undo last Commit 2026-08-15 12:31:34 +02:00
MasterAcnolo
ab53ff6661 ReadMe Fix 2026-08-15 12:31:34 +02:00
MasterAcnolo
476e5362bd ReadME Fix %20 2026-08-15 12:31:34 +02:00
MasterAcnolo
1686c872de ReadME Fix %20 2026-08-15 12:31:34 +02:00
MasterAcnolo
80564f59ce ReadME Fix 2026-08-15 12:31:34 +02:00
MasterAcnolo
5ed2b79781 ReadME Fix 2026-08-15 12:31:34 +02:00
MasterAcnolo
0504890686 ReadME Fix 2026-08-15 12:31:34 +02:00
MasterAcnolo
9a074e817d ReadME Fix 2026-08-15 12:31:34 +02:00
MasterAcnolo
a86ecda352 ReadME Fix 2026-08-15 12:31:34 +02:00
MasterAcnolo
5873fdff0f ReadME Fix 2026-08-15 12:31:34 +02:00
MasterAcnolo
0ea5f7fb57 ReadME Fix 2026-08-15 12:31:34 +02:00
MasterAcnolo
f2c18e427c ReadME Fix 2026-08-15 12:31:34 +02:00
MasterAcnolo
d859d04abf ReadME Fix 2026-08-15 12:31:34 +02:00
MasterAcnolo
62da2f862e ReadME Fix 2026-08-15 12:31:27 +02:00
MasterAcnolo
4942c9109e Update README-EN.md 2026-08-15 12:30:24 +02:00
MasterAcnolo
fedaa47d0e Update README.md 2026-08-15 12:30:24 +02:00
MasterAcnolo
35fc0c370b 1.1 2026-08-15 12:30:24 +02:00
MasterAcnolo
b6203f3546 Commentary + Custom PATH 2026-08-15 12:30:24 +02:00
MasterAcnolo
9e5c2ffddf Chirac Easter EGG 2026-08-15 12:30:24 +02:00
MasterAcnolo
343aa99fea Logs FIX + Bug FIX 2026-08-15 12:30:24 +02:00
MasterAcnolo
7dba6d8c6a Package FIX for 1.0.2 2026-08-15 12:30:24 +02:00
MasterAcnolo
4786bcee59 1.0.1 Commit 2026-08-15 12:30:24 +02:00
MasterAcnolo
191fa7627a CSS Variable Begin 2026-08-15 12:30:24 +02:00
MasterAcnolo
6606a5f9c1 Logs Name Ajust + 1.0.1 Prepa 2026-08-15 12:30:24 +02:00
MasterAcnolo
a2871fc8e2 Clarification 2026-08-15 12:30:24 +02:00
MasterAcnolo
97fc95eb44 Correction Git Ignore 2026-08-15 12:30:24 +02:00
MasterAcnolo
55a118f388 MD En + FR 2026-08-15 12:30:24 +02:00
MasterAcnolo
6294c4a764 Push 2026-08-15 12:30:24 +02:00
MasterAcnolo
8c15ce28bc Push 2026-08-15 12:30:24 +02:00
MasterAcnolo
521a0e41fe Correction 2026-08-15 12:30:23 +02:00
MasterAcnolo
90b5c14bd1 Correction Path 2026-08-15 12:30:23 +02:00
MasterAcnolo
4c5b963342 Push ReadME + License 2026-08-15 12:30:23 +02:00
MasterAcnolo
c42002d5db MD Push 2026-08-15 12:30:23 +02:00
MasterAcnolo
8e12ad742f Correction Path YT DLP 2026-08-15 12:30:23 +02:00
MasterAcnolo
c82dda321c Fix NPM RUN DIST 2026-08-15 12:30:23 +02:00
MasterAcnolo
05f6a892e0 Working on my machine 2026-08-15 12:30:23 +02:00
MasterAcnolo
2bd5369524 It Works on my machine 2026-08-15 12:30:23 +02:00
MasterAcnolo
fdd0a2fa16 Push 2026-08-15 12:30:23 +02:00
MasterAcnolo
d413e47d58 Push avant l'enfer (PRIDEFUl - NF) 2026-08-15 12:30:23 +02:00
MasterAcnolo
26949ef6e9 Aled 2026-08-15 12:30:23 +02:00
MasterAcnolo
1063494070 Push Version 1 2026-08-15 12:30:23 +02:00
MasterAcnolo
844b994eb8 Push avant modif DIV INFO 2026-08-15 12:30:23 +02:00
MasterAcnolo
72b9e45366 LE POULET 2026-08-15 12:30:23 +02:00
MasterAcnolo
65cf3fad21 CSS Beginning 2026-08-15 12:30:23 +02:00
MasterAcnolo
942e4032a0 Loading Screen 2026-08-15 12:30:23 +02:00
MasterAcnolo
f840d327f9 Fix Bug Download 2026-08-15 12:30:23 +02:00
MasterAcnolo
1da81e10d5 Diviser pour mieux régner 2026-08-15 12:30:23 +02:00
MasterAcnolo
d46c92d1e0 Informations Vidéo 2026-08-15 12:30:23 +02:00
MasterAcnolo
bf5d1642ed Working V1 2026-08-15 12:30:23 +02:00
MasterAcnolo
e8c1c2a5b1 Working V1 2026-08-15 12:30:23 +02:00
MasterAcnolo
b3d39b5322 Push 2026-08-15 12:30:15 +02:00
MasterAcnolo
e7bbc109ba OUII 2026-08-15 12:29:43 +02:00
MasterAcnolo
4ad514b064 CA MARCHE 2026-08-15 12:28:33 +02:00
49 changed files with 2214 additions and 1625 deletions

View File

@@ -29,6 +29,9 @@ Clear description of the performance issue.
- **Storage**: [e.g. SSD/HDD]
- **Other Applications Opened**: [e.g. Discord/Opera GX]
> [!INFO]
> At the start of the application, SystemInfo is printed in the logs, so you can just copy and paste it!
## Reproduction Details
- **Download URL**: [if applicable]

View File

@@ -9,8 +9,8 @@ assignees: 'MasterAcnolo'
## Pre-submission Checklist
- [ ] I have checked the [FAQ](https://masteracnolo.github.io/Freedom-Loader-Site/pages/faq)
- [ ] I have reviewed the [Wiki](https://masteracnolo.github.io/Freedom-Loader-Site/pages/wiki)
- [ ] 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 searched existing issues
## Question

View File

@@ -2,7 +2,7 @@
Brief description of what this PR does.
## Related Issue
## Related Issue (if applicable)
Fixes #(issue number)
Closes #(issue number)
@@ -30,7 +30,7 @@ Related to #(issue number)
- [ ] I have tested with different download scenarios (video/audio, playlists)
- [ ] I have checked the application logs for errors
- [ ] I have verified the UI changes (if applicable)
- [ ] I have tested on Windows 10 and/or Windows 11
- [ ] I have tested on Windows 10 and/or Windows 11 or in any Linux Distribution
## Screenshots (if applicable)

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}}"

4
.gitignore vendored
View File

@@ -17,8 +17,10 @@ config/config.dev.json
ffmpeg
ffprobe
deno
yt-dlp
# Windows Executable
ffmpeg.exe
ffprobe.exe
deno.exe
deno.exe
yt-dlp.exe

View File

@@ -93,4 +93,5 @@ chmod +x resources/binaries/linux/deno
## Notes
- These binaries are excluded from version control via `.gitignore`.
- Always use the latest stable release of each tool.
- Always use the latest stable release of each tool.
- **Yt-dlp** binaries don't need to be updated manually. At every startup of the application, the binary tries to update itself

View File

@@ -6,6 +6,7 @@ Welcome! Thanks for your interest in Freedom Loader. Your contributions—bug re
## Quick Links
- [Development Guide](DEVELOPMENT.md)
- [Code of Conduct](CODE_OF_CONDUCT.md)
- [Issue Templates](.github/ISSUE_TEMPLATE/)
- [Pull Request Templates](.github/PULL_REQUEST_TEMPLATE/)
@@ -23,7 +24,7 @@ Found a bug or unexpected behavior? Open an [**issue**](https://github.com/Maste
- Clear description of the bug
- Steps to reproduce
- App version and environment (Windows version, Firefox version if relevant)
- Logs from: `C:\Users\[USERNAME]\AppData\Local\FreedomLoader\logs\LOGS-YYYY-MM-DD.log`
- Logs from: `C:\Users\[USERNAME]\AppData\Local\FreedomLoader\logs\LOGS-YYYY-MM-DD.log` (Windows) or `~/.local/share/FreedomLoader/logs/` (Linux)
- Screenshots if applicable
### 2. Request a Feature
@@ -43,13 +44,11 @@ Have an idea to improve Freedom Loader? Open a [**Feature Request**](https://git
- Follow the project's code style and conventions
**Process:**
1. Fork the repository
2. Create a feature branch: `git checkout -b feature/your-feature-name`
3. Make your changes
4. Test thoroughly on Windows 10/11
5. Commit with clear messages: `git commit -m "Add feature X"`
6. Push to your fork: `git push origin feature/your-feature-name`
7. Open a Pull Request using the appropriate template
1. Fork the repository.
2. Check out our [**Developer Guide (DEVELOPMENT.md)**](./DEVELOPMENT.md) to set up your environment.
3. Create a feature branch following our branching conventions.
4. Make your changes and test thoroughly on Windows and/or Linux.
5. Open a Pull Request using the appropriate template.
**Code Guidelines:**
- Use camelCase for variables and functions
@@ -72,41 +71,9 @@ Small contributions matter—don't hesitate to submit documentation PRs.
## Development Setup
### Prerequisites
- Node.js 16.x or higher
- npm or yarn
- Git
- Windows 10/11 (for testing)
Want to write some code? Awesome!
### Setup
```bash
# Clone your fork
git clone https://github.com/YOUR-USERNAME/Freedom-Loader.git
cd Freedom-Loader
# Install dependencies
npm install
# Run in development mode
npm start
# Build for production
npm run build
```
### Project Structure
```
Freedom-Loader/
├── main.js # Electron main process
├── preload.js # Electron preload script
├── config.js # Global configuration
├── server/ # Express backend
│ ├── routes/ # API routes
│ ├── controller/ # Business logic
│ └── helpers/ # Utility functions
├── public/ # Frontend (HTML, CSS, JS)
└── resources/ # Binaries (yt-dlp, ffmpeg, etc.)
```
Please read our [**Developer Guide (DEVELOPMENT.md)**](./DEVELOPMENT.md) for instructions on how to set up your local environment, our project architecture, and our Git branching rules.
---
@@ -114,12 +81,9 @@ Freedom-Loader/
Before submitting a PR, verify:
- [ ] Download functionality works (video/audio)
- [ ] Playlist downloads work
- [ ] Settings panel functions correctly
- [ ] Custom path selection works
- [ ] No errors in application logs
- [ ] UI changes work on all themes (if applicable)
- [ ] Tested on Windows 10 and/or Windows 11
- [ ] Any features wasn't break during the development
- [ ] Tested on Windows 10/11 and/or any Linux Distribution
---
@@ -161,4 +125,4 @@ Check out his work: [IG @designmark_studio](https://www.instagram.com/designmark
---
**Thank you for contributing to Freedom Loader!**
**Thank you for contributing to Freedom Loader!**

388
DEVELOPMENT.md Normal file
View File

@@ -0,0 +1,388 @@
# Freedom Loader - Development Guide
Welcome to the Developer Guide! This document explains how to set up your local environment, project architecture, Git workflow, and the release process.
## Prerequisites
Before you begin, ensure you have the following installed on your machine:
- **Node.js** (v22 or higher recommended)
- **npm** (comes with Node.js)
- **Git**
- **yt-dlp binaries**: Freedom Loader requires native binaries to function. Read [BINARIES.md](./BINARIES.md) for download instructions and placement in `resources/binaries/`.
## Setup Instructions
### 1. Clone the repository
```bash
git clone https://github.com/MasterAcnolo/Freedom-Loader.git
cd Freedom-Loader
```
### 2. Install dependencies
```bash
npm install
```
### 3. Install the Binaries
Follow the instructions in `BINARIES.md` to place `yt-dlp`, `ffmpeg`, `ffprobe`, and `deno` in the correct `resources/binaries/` folder for your OS.
### 4. Run in Development Mode
```bash
npm start
```
**Note**: Development mode automatically enables detailed colored logs and the DevTron extension for debugging.
---
## Available Scripts
### Development
- `npm start` — Start the app in development mode (no warnings)
- `npm run start:warn` — Start with deprecation warnings visible
- `npm run start:debug` — Start with full debug logging (Wayland, Electron logging, stack dumps)
- `npm run dev` — Start with auto-reload on file changes (watches server/, app/, main.js, config.js)
- `npm run dev:warn` — Dev mode with warnings
- `npm run dev:debug` — Dev mode with full debug output and auto-reload
### Building
- `npm run build:linux` — Build Linux packages (AppImage, deb, snap)
- `npm run build:win` — Build Windows installer (.exe)
- `npm run build:rpm` — Build RPM and SRPM for COPR (Fedora)
- `npm run build:all` — Build both Windows and Linux (not recommended because it adds both OS binaries into a single package. The output file is 2x bigger than expected).
### Testing
- `npm test` — Run all tests (unit + integration)
- `npm run test:unit` — Run unit tests only
### Release Pipeline
- `npm run release` — Build all packages and create a draft GitHub release (no store publishing)
- `npm run release:publish` — Build all packages and publish to Snap Store + COPR
- `npm run release:dry-run` — Simulate the full release pipeline without publishing anything
### Maintenance
- `npm update` — Update npm dependencies
---
## Project Architecture
Freedom Loader is built with **Electron** (frontend) and **Node.js + Express** (backend), using a modular structure:
---
## Git & Branching Workflow
I use **Trunk-Based Development** (GitHub Flow):
### Core Principles
- **`main` is always deployable**: The `main` branch contains the latest stable code and must never be broken.
- **No long-lived branches**: I no longer maintain version branches like `v1.6` or `v1.7`.
- **Pull Requests for review**: All code changes go through PR review before merging to `main`.
### How to Contribute
#### 1. Create a feature branch
```bash
git checkout -b feat/add-new-button
# or for fixes:
git checkout -b fix/ui-bug
```
**Branch naming convention**:
- `feat/` — New features
- `fix/` — Bug fixes
- `refactor/` — Code cleanup (no behavior change)
- `docs/` — Documentation updates
- `chore/` — Dependency updates, build config, etc.
#### 2. Make commits
```bash
# Make changes
git add .
git commit -m "Brief, imperative description of the change"
```
**Commit message tips**:
- Use imperative mood: "Add theme caching" not "Added theme caching"
- Keep commits logical and focused (one feature per commit if possible)
- Link to issues if relevant: "Fix crash on download (fixes #42)"
#### 3. Push and open a PR
```bash
git push origin feat/add-new-button
```
Then open a PR on GitHub against `main`.
#### 4. Review and merge
- Address feedback in new commits (don't rebase — easier to review)
- Once approved, merge via GitHub (use "Squash and merge" for clean history if many small commits and you find it relevant)
---
## Release Process
**Note**: Only project maintainers release to production. This section documents the process for transparency and for future maintainers.
Releases are fully automated via the `release.sh` script. The process handles building, packaging, and publishing to **all distribution channels**:
- **GitHub Releases** (Windows .exe, Linux packages)
- **Fedora COPR** (automatic RPM builds for Fedora 43+)
- **Snap Store** (universal Linux)
### Before You Release
1. **Ensure `main` is green**: All tests pass, features are stable.
2. **Update `package.json` version**:
```json
"version": "1.6.1"
```
3. **Commit the version bump**: `git add package.json && git commit -m "chore: v1.6.1"`
4. **Push to `main`**: `git push origin main`
### Step 1: Local Build & Draft Release
```bash
npm run release
```
This:
1. Builds Linux packages (AppImage, deb, snap)
2. Builds RPM and SRPM for COPR
3. Builds Windows installer
4. Creates a draft GitHub release
### Step 2: Edit & Publish on GitHub
1. Go to https://github.com/MasterAcnolo/Freedom-Loader/releases
2. Edit the draft release — fill in changelog
3. Click **"Publish"** to make it live
### Step 3: Publish to Distribution Channels
```bash
npm run release:publish
```
This automatically:
- **Uploads to Snap Store**: Makes the app available via `snap install freedom-loader`
- **Submits to Fedora COPR**: Builds and publishes RPMs for Fedora 43+ (users can `dnf install freedom-loader` from the COPR repo)
Both happen in parallel — users across all platforms get the release simultaneously.
---
## Linux Development Considerations
### Cross-Distribution Compatibility
Freedom Loader targets **Debian-based** (Ubuntu, Debian) and **Fedora-based** (Fedora, RHEL, openSUSE) distributions. When developing features, keep this in mind:
#### What to test
- **Feature works on Fedora 44+** (primary Linux development environment)
- **Feature works on Ubuntu/Debian** (via deb package or AppImage)
- **Feature doesn't break Snap confinement** (Snap has restricted filesystem/IPC access)
- **Feature gracefully degrades on missing system dependencies**
#### Desktop Environment (DE) Compatibility
Test on at least **GNOME** and **KDE** (the most common DEs). Common pain points:
- **Themes**: May render differently on KDE vs GNOME — test both if possible
- **File dialogs**: Some DEs use native file pickers, others fall back to Electron's
- **Notifications**: System notification APIs vary (D-Bus, libnotify)
- **Tray icons**: May not work identically across DEs
#### How to check locally
```bash
# Test on Fedora (if available)
npm run build:linux
sudo dnf install dist/freedom-loader-*.x86_64.rpm
freedom-loader
# Test AppImage (works on any distro)
chmod +x dist/Freedom\ Loader-*.AppImage
./dist/Freedom\ Loader-*.AppImage
```
### CI/CD Gap
**Currently**: No automated testing across distros or DEs. Releases rely on manual testing before publish.
**Future improvement**: Automated tests via GitHub Actions (Ubuntu) + local testing on Fedora would catch cross-distro issues early. This is planned but not yet implemented.
For now, if you fix a Linux-specific bug or add a DE-dependent feature, **please mention it in your PR description** so reviewers can test extra carefully.
---
## Testing
### Unit Tests
Test individual functions in isolation (no Electron required).
```bash
npm run test:unit
```
Covered:
(Tests will come soon)
### End-to-End Tests (Playwright)
Test the actual Electron app launching and basic UI interactions.
```bash
npm run test
```
Covered:
(Tests will come soon)
### Running All Tests
```bash
npm test
```
Or include tests before release:
```bash
npm test && npm run release
```
---
## Common Tasks
### Add a new feature
1. Create a feature branch: `git checkout -b feat/my-feature`
2. Make changes, commit: `git add . && git commit -m "feat: my feature"`
3. Push: `git push origin feat/my-feature`
4. Open a PR on GitHub
5. Once approved, merge to `main`
6. (Later) Cut a release when ready: `npm run release`
### Fix a bug
Same as above, but use `fix/bug-name` branch and `git commit -m "fix: description"`.
### Test the app before releasing
```bash
npm run dev # Auto-reload on file changes
# Make changes, test in the UI
npm test # Run all tests
npm run release:dry-run # Simulate release (builds but doesn't publish)
```
### Update dependencies
```bash
npm update
git add package.json package-lock.json
git commit -m "chore: update dependencies"
git push origin main
```
### Rebuild only Linux (after quick fixes)
```bash
npm run build:linux
# Or just the RPM:
npm run build:rpm
```
### Publish to stores manually
If `npm run release:publish` fails partway:
```bash
# Just Snap:
snapcraft upload dist/freedom-loader_*.snap --release=stable
# Just COPR:
copr-cli build freedom-loader srpm-out/*.src.rpm
```
---
## Troubleshooting
### App won't start in dev mode
```bash
npm run start:debug
```
Check the debug output for errors. Most common:
- Missing binaries in `resources/binaries/`
- Port 8787 already in use
- A dead instance is still running — open Task Manager and kill the "Freedom Loader" process
### (Linux) Binaries in the right place but app still won't launch
You probably forgot to make them executable:
```bash
chmod +x resources/binaries/linux/*
```
The app will fail silently if binaries lack execute permissions.
### Build fails with "permission denied"
Make sure you have write access to `dist/` and `srpm-out/`:
```bash
chmod -R u+w dist srpm-out
npm run build:linux
```
### Tests fail
Check for:
- Missing test files in `tests/unit/`
- Node modules out of sync: `rm -rf node_modules && npm install`
### GitHub release publish fails
Verify `gh` CLI is installed and authenticated:
```bash
gh auth login
gh release list
```
---
## Questions?
For more info:
- **Electron docs**: https://www.electronjs.org/docs
- **electron-builder**: https://www.electron.build/
- **This repo**: https://github.com/MasterAcnolo/Freedom-Loader
Happy coding! 🎉

199
README.md
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.
@@ -25,7 +23,7 @@ The primary goal is to make media downloading accessible to users who want offli
### See related repo:
- **[Website](https://masteracnolo.github.io/Freedom-Loader-Site/)** - Official project website and documentation
- **[Website](https://github.com/MasterAcnolo/Freedom-Loader-Site/)** - Official project website and documentation
- **[Workshop](https://github.com/MasterAcnolo/Freedom-Loader-Workshop)** - Theme creation tool and theme browser
## Table of Contents
@@ -35,14 +33,12 @@ The primary goal is to make media downloading accessible to users who want offli
- [Usage](#usage)
- [Preview](#preview)
- [Configuration](#configuration)
- [Project Structure](#project-structure)
- [Theme Workshop](#Theme-Workshop)
- [Technology Stack](#technology-stack)
- [Development](#development)
- [Roadmap](#roadmap)
- [Contributing](#contributing)
- [Submitting a community theme](#Submitting-a-community-theme)
- [Support](#support)
- [License](#license)
## Features
@@ -61,9 +57,9 @@ 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 themes** — ZIP-based theme system with live preview
- **Theme Workshop** — web-based theme creator and browser
- **Toast notifications** — in-app feedback system
- **Custom themes** - ZIP-based theme system with live preview
- **Theme Workshop** - web-based theme creator and browser
- **Toast notifications** - in-app feedback system
- **Real-time progress tracking** - Live download progress bar with network speed indicator
- **Server-Sent Events (SSE)** - Non-blocking progress updates via event streaming
- **Settings panel** - In-app configuration interface with live updates
@@ -80,9 +76,11 @@ The primary goal is to make media downloading accessible to users who want offli
- **Rate limiting** - Express-based request throttling to prevent server overload
- **Graceful shutdown** - Proper cleanup of servers, logs, and RPC connections
- **System notifications** - Windows notifications on download completion (clickable to open folder)
- **Splash screen** — animated loading screen on startup
- **Stop download** — ability to cancel in-progress downloads
- **Playlist folders** — dedicated folder creation per playlist
- **Splash screen** - animated loading screen on startup
- **Stop download** - ability to cancel in-progress downloads
- **Playlist folders** - dedicated folder creation per playlist
- **Keep Playlist Order** - Add an Index before the title, could be useful for an album for example.
- **System Tray** - Could minimize the application in the system Tray, allow it to run in the background.
## Installation
@@ -105,7 +103,7 @@ The primary goal is to make media downloading accessible to users who want offli
### Linux
#### Fedora (via Copr — recommended)
#### Fedora (via Copr - recommended)
Installing via Copr lets `dnf` handle future updates automatically:
@@ -178,7 +176,8 @@ The application supports various configuration options:
- Custom download directory selection
- Discord Rich Presence activation
- Theme customization
- Log level configuration
- Log level
- And somes to see in the list [Configuration Options](#configuration-options) available
### Troubleshooting
@@ -219,13 +218,14 @@ Freedom Loader can be configured either through the settings panel in the UI or
#### Linux
```
~/.config/Freedom Loader/config.json
~/.config/Freedom\ Loader/config.json
```
### Available Options
```json
{
"autoUpdate": true,
"systemTray": true,
"discordRPC": true,
"customTopBar": true,
"autoCheckInfo": true,
@@ -233,6 +233,7 @@ Freedom Loader can be configured either through the settings panel in the UI or
"addMetadata": true,
"verboseLogs": false,
"autoDownloadPlaylist": true,
"keepPlaylistOrder": false,
"createPlaylistFolders": true,
"customCodec": "h264",
"logSystem": true,
@@ -249,6 +250,7 @@ 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] |
| `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 |
@@ -256,7 +258,8 @@ Freedom Loader can be configured either through the settings panel in the UI or
| `addMetadata` | boolean | `true` | Add metadata tags to downloaded files |
| `verboseLogs` | boolean | `false` | Enable detailed logging for debugging |
| `autoDownloadPlaylist` | boolean | `true` | Automatically download entire playlists |
| `createPlaylistFolders` | boolean | `true` | Automatically create a folder for a playlist |
| `keepPlaylistOrder` | boolean | `false` | Add index before title, usefull for album |
| `createPlaylistFolders` | boolean | `false` | Automatically create a folder for a playlist |
| `customCodec` | string | `"h264"` | Video codec for encoding (supported: h264, h265, vp9, av1) |
| `logSystem` | boolean | `true` | Enable application logging |
| `outputTitleCheck` | boolean | `true` | Verify output file titles |
@@ -276,95 +279,7 @@ Freedom Loader can be configured either through the settings panel in the UI or
- `theora` - Theora (legacy open codec)
> [!NOTE]
> Configuration changes may require an application restart to take effect.
## Project Structure
```
Freedom-Loader/
├── app/ # Electron main process modules
│ ├── autoUpdater.js
│ ├── dependencyCheck.js
│ ├── discordRPC.js
│ ├── ipcHandlers.js
│ ├── pathValidator.js
│ ├── splashManager.js
│ ├── themeManager.js
│ ├── windowManager.js
│ └── ytDlpUpdater.js
├── build/ # Build resources and assets
├── config/ # Configuration files
│ ├── config.default.json
│ └── config.dev.json
├── public/ # Frontend assets and UI
│ ├── index.html
│ ├── splash.html
│ ├── assets/
│ │ ├── icon/ # Application icons
│ │ └── logo/ # Logo assets
│ ├── script/ # Frontend JavaScript modules
│ │ ├── appVersion.js
│ │ ├── clipboardPaste.js
│ │ ├── custompath.js
│ │ ├── customthemes.js
│ │ ├── downloadstatus.js
│ │ ├── fetchinfo.js
│ │ ├── progressBar.js
│ │ ├── settingsPanel.js
│ │ ├── toast.js
│ │ └── topbar.js
│ └── styles/ # CSS stylesheets
│ ├── styles.css
│ ├── variables.css
│ ├── components/ # Component-specific styles
│ │ ├── checkbox.css
│ │ ├── editpathbutton.css
│ │ ├── loader.css
│ │ ├── progressBar.css
│ │ └── toast.css
│ └── layout/ # Layout styles
│ ├── container.css
│ ├── form.css
│ ├── header.css
│ ├── settingsPanel.css
│ ├── topbar.css
│ └── videoinfo.css
├── resources/ # Internal resources (binaries)
├── server/ # Express backend server
│ ├── logger.js
│ ├── server.js
│ ├── controller/ # Request handlers
│ │ ├── download.controller.js
│ │ └── info.controller.js
│ ├── helpers/ # Utility functions
│ │ ├── buildArgs.helpers.js
│ │ ├── getBrowser.helpers.js
│ │ ├── notify.helpers.js
│ │ ├── parseInfo.helpers.js
│ │ ├── path.helpers.js
│ │ ├── rateLimit.helpers.js
│ │ └── validation.helpers.js
│ ├── routes/ # API route definitions
│ │ ├── download.route.js
│ │ └── info.route.js
│ └── services/ # Business logic layer
│ ├── download.services.js
│ └── info.services.js
├── theme/ # Theme system
│ ├── template.theme.json # Theme template
│ ├── Dark/ # Default dark theme
│ │ └── dark.theme.json
│ └── Light/ # Default light theme
│ └── light.theme.json
├── main.js # Electron main process entry point
├── preload.js # Electron preload script (IPC bridge)
├── config.js # Global configuration loader
├── package.json # Project metadata and dependencies
├── CODE_OF_CONDUCT.md # Community guidelines
├── CONTRIBUTING.md # Contribution guidelines
├── LICENSE # GPLv3 license
└── README.md # This file
```
> Some configuration changes may require an application restart to take effect.
### Architecture Overview
@@ -389,7 +304,7 @@ Freedom Loader includes a web-based theme creator available at [Freedom Loader W
### Installing a custom theme
1. Download a `.zip` theme file
2. Drop it in the `theme/` folder of your Freedom Loader installation
3. Restart the app — your theme appears in the settings panel
3. Restart the app - your theme appears in the settings panel
## Technology Stack
@@ -412,28 +327,7 @@ Freedom Loader includes a web-based theme creator available at [Freedom Loader W
## Development
### Prerequisites
- Node.js 16.x or higher
- npm or yarn
- Git
### Setup
```bash
# Clone the repository
git clone https://github.com/MasterAcnolo/Freedom-Loader.git
cd Freedom-Loader
# Install dependencies
npm install
# Run in development mode
npm start
# Build for production
npm run build
```
If you are interested by running a local non compiled version of Freedom Loader or just interested by contributing, see [DEVELOPMENT](./DEVELOPMENT.md)
### Additional Dependencies
@@ -444,40 +338,19 @@ You must download the required binaries and place them in the `resources` folder
#### Required binaries
- **Deno**
- Download from: https://sourceforge.net/projects/deno.mirror/files/latest/download
- Rename to: `deno.exe`
- **FFmpeg**
- Download from: https://www.ffmpeg.org/download.html
- Required files:
- `ffmpeg.exe`
- `ffprobe.exe`
- **yt-dlp**
- Already bundled with the project
- No manual installation required
Final folder structure:
```
resources/
├── deno.exe
├── ffmpeg.exe
├── ffprobe.exe
└── yt-dlp.exe
```
> These binaries are required for the application to start correctly.
> If any of them are missing, an error message will be displayed at application startup.
See [BINARIES.md](./BINARIES.md) for how to setup the binaries.
### Development Guidelines
- Follow existing code style and conventions
- Write clear commit messages
- Test thoroughly before submitting changes
- Update documentation when adding features
- Maintain compatibility with Windows 10+ and Linux
- Write clear, imperative commit messages (e.g., "Add playlist indexing" not "Added")
- Test thoroughly on both Windows and Linux before submitting changes
- Update documentation (README, DEVELOPMENT.md) when adding features
- Maintain compatibility with:
- Windows 10+
- Fedora 43+ / Debian 11+ / Ubuntu 20.04+
- GNOME and KDE desktop environments (when possible)
- Consider Linux distribution differences early (see [DEVELOPMENT.md](./DEVELOPMENT.md#linux-development-considerations))
## Roadmap
@@ -519,15 +392,7 @@ Open a feature request issue with:
5. Update documentation as needed
6. Submit a pull request with a detailed description
Please read [CONTRIBUTING.md](CONTRIBUTING.md) and [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) before contributing.
## Support
If you find this project useful and want to support its development:
[![PayPal](https://img.shields.io/badge/PayPal-00457C?style=for-the-badge&logo=paypal&logoColor=white)](https://paypal.me/axelnicolas25)
Your support helps maintain infrastructure, develop new features, and keep the project free and ad-free.
Please read [CONTRIBUTING.md](CONTRIBUTING.md), [DEVELOPMENT.md](./DEVELOPMENT.md) and [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) before contributing.
## License
@@ -543,8 +408,8 @@ You are free to use, modify, and redistribute this software under the terms of t
- [Electron](https://www.electronjs.org/) for the desktop application framework
- The open-source community for continuous support and contributions
- [@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

@@ -121,8 +121,14 @@ function initAutoUpdater(mainWindow) {
*/
async function checkForUpdates() {
if (process.env.SNAP) {
logger.info("Running as Snap, update managed by snapd, skipping electron-updater");
/**
* 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;
}

View File

@@ -1,6 +1,8 @@
const config = require('../config');
const RPC = require("discord-rpc");
const { logger } = require("../server/logger");
const fs = require("fs");
const path = require("path");
/**
* Discord Application Client ID used to authenticate the RPC connection.
@@ -20,6 +22,51 @@ const rpc = new RPC.Client({ transport: "ipc" });
*/
let intervalId;
/**
* Automagically fixes Discord RPC on Linux for Sandboxed versions (Flatpak & Snap).
* It creates a symlink from the sandbox IPC socket to the standard system location.
*
* @returns {void}
*/
function fixLinuxDiscordRPC() {
// Only execute on Linux
if (process.platform !== "linux") return;
/**
* Get the user's runtime directory (e.g., /run/user/1000).
* @type {string}
*/
const xdgDir = process.env.XDG_RUNTIME_DIR || `/run/user/${process.getuid()}`;
const standardSocket = path.join(xdgDir, "discord-ipc-0");
// If the standard socket already exists (Native Discord), do nothing.
if (fs.existsSync(standardSocket)) return;
/**
* Paths for known sandboxed Discord IPC sockets.
*/
const flatpakSocket = path.join(xdgDir, "app/com.discordapp.Discord/discord-ipc-0");
const snapSocket = path.join(xdgDir, "snap.discord/discord-ipc-0");
let targetSocket = null;
if (fs.existsSync(flatpakSocket)) {
targetSocket = flatpakSocket;
} else if (fs.existsSync(snapSocket)) {
targetSocket = snapSocket;
}
// If a sandboxed socket is found, create the symlink stealthily.
if (targetSocket) {
try {
fs.symlinkSync(targetSocket, standardSocket);
logger.info(`Discord RPC bridge created automatically targeting: ${targetSocket}`);
} catch (err) {
logger.warn(`Failed to create Discord RPC bridge: ${err.message}`);
}
}
}
/**
* Initializes Discord Rich Presence (RPC) connection.
*
@@ -32,6 +79,11 @@ let intervalId;
*/
function startRPC() {
/**
* Patch that create Simlinks for Linux environment;
*/
fixLinuxDiscordRPC();
/**
* Rich Presence payload describing current application state
* shown in Discord user profile.
@@ -50,8 +102,10 @@ function startRPC() {
});
rpc.login({ clientId }).catch(err => {
logger.error("Unable to connect to the RPC:", 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");
});
}
@@ -60,28 +114,23 @@ function startRPC() {
*
* Responsibilities:
* - Clear any running intervals
* - Remove current activity from Discord
* - Remove current activity from Discord if connected
* - Destroy RPC transport connection
* - Handle cleanup errors safely
*/
async function stopRPC(){
async function stopRPC() {
if (rpc) {
try {
if (intervalId) clearInterval(intervalId);
/**
* Ensures RPC connection exists before attempting cleanup.
*/
if (rpc && rpc.transport) {
await rpc.clearActivity()
await rpc.destroy();
} else{
logger.error("Not Able to close RPC")
}
await rpc.destroy();
} catch (err) {
logger.error("Error while closing the RPC:", err);
// 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("Error while closing the RPC:", err);
}
}
}
module.exports = { startRPC, stopRPC};

View File

@@ -13,11 +13,12 @@
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");
const { userThemesPath } = require("../server/helpers/path.helpers");
const { createSystemTray, destroyTray } = require("./tray");
/**
* Security whitelist for feature flags that can be modified at runtime.
@@ -27,6 +28,7 @@ const { userThemesPath } = require("../server/helpers/path.helpers");
*/
const FEATURE_WHITELIST = new Set([
"autoUpdate",
"systemTray",
"discordRPC",
"customTopBar",
"autoCheckInfo",
@@ -34,6 +36,7 @@ const FEATURE_WHITELIST = new Set([
"addMetadata",
"verboseLogs",
"autoDownloadPlaylist",
"keepPlaylistOrder",
"customCodec",
"theme",
"createPlaylistFolders",
@@ -111,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.
@@ -212,6 +226,21 @@ function registerIpcHandlers(getMainWindow) {
return true;
}
/**
* Dynamically intercepts and applies System Tray state changes at runtime.
* Instantiates or destroys the tray icon immediately to prevent window
* lifecycle desynchronization (e.g., minimizing a window to a non-existent tray).
*/
if (key === "systemTray") {
if (value === true) {
logger.info("System Tray enabled dynamically.");
createSystemTray(devMode);
} else {
logger.info("System Tray disabled dynamically.");
destroyTray();
}
}
configFeatures[key] = value;
fs.writeFileSync(

View File

@@ -56,35 +56,30 @@ function validateDownloadPath(userPath) {
if (!userPath) return getDefaultDownloadPath();
try {
const absolutePath = path.resolve(userPath);
/**
* Resolves filesystem symlinks to ensure canonical absolute path.
* Required to prevent path traversal or alias bypass.
*/
const resolved = fs.realpathSync(path.resolve(userPath));
if (!fs.existsSync(absolutePath)) {
fs.mkdirSync(absolutePath, { recursive: true });
logger.info(`Download folder created: ${absolutePath}`);
}
if (!fs.existsSync(resolved)) {
fs.mkdirSync(resolved, { recursive: true });
logger.info(`Download folder created: ${resolved}`);
const real = fs.realpathSync(absolutePath);
if (!isSafePath(real)) {
logger.error("Path not allowed: system folders are blocked!");
}
return real;
}
const real = fs.realpathSync(resolved);
if (!isSafePath(resolved)) {
throw new Error("Path not allowed: system folders are blocked!");
catch (err) {
/**
* Handles invalid filesystem paths, permissions issues, or resolution failures.
* Logs diagnostic information before propagating a sanitized error.
*/
logger.error(`Invalid download path: ${userPath} — ${err.message}`);
throw new Error(`Invalid or inaccessible path: ${err.message}`);
}
return real;
}
catch (err) {
/**
* Handles invalid filesystem paths, permissions issues, or resolution failures.
* Logs diagnostic information before propagating a sanitized error.
*/
logger.error(`Invalid download path: ${userPath} — ${err.message}`);
throw new Error(`Invalid or inaccessible path: ${err.message}`);
}
}
module.exports = { validateDownloadPath, getDefaultDownloadPath };

117
app/tray.js Normal file
View File

@@ -0,0 +1,117 @@
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.
* @type {Tray | null}
*/
let tray = null;
/**
* Creates and configures the cross-platform System Tray.
*
* Responsibilities:
* - Loads the appropriate icon format based on the operating system.
* - Builds the right-click context menu.
* - Handles left-click behavior to toggle main window visibility.
*
* @param {boolean} devMode - Indicates whether the application is running in development mode.
* @returns {Tray} The created Tray instance.
*/
function createSystemTray(devMode) {
// Prevent creating multiple tray instances
if (tray) return tray;
const isWin = process.platform === "win32";
const iconName = isWin ? "app-icon.ico" : "app-icon-64x64.png";
// Attempt to load the icon from the local build directory first
let iconPath = path.join(__dirname, "..", "build", iconName);
// Fallback to the extracted resources path if running in production
if (!devMode && !fs.existsSync(iconPath)) {
iconPath = path.join(process.resourcesPath, iconName);
}
if (!fs.existsSync(iconPath)) {
logger.error(`System Tray icon not found at resolved path: ${iconPath}`);
} else {
logger.info("System Tray icon resolved successfully.");
}
const icon = nativeImage.createFromPath(iconPath);
if (icon.isEmpty()) {
logger.error("System Tray icon file was found but could not be decoded by Electron (image is empty).");
}
tray = new Tray(icon);
tray.setToolTip("Freedom Loader");
/**
* System Tray Context Menu (Right-click action).
*/
const contextMenu = Menu.buildFromTemplate([
{
label: "Show Freedom Loader",
click: () => {
const window = getMainWindow();
if (window && !window.isDestroyed()) {
window.show();
window.focus();
logger.info("Window Restored from SystemTray (Using Show Action)");
}
},
},
{ type: "separator" },
{
label: "Quit",
click: () => {
/**
* Flags the application for a deliberate exit,
* bypassing the default "minimize to tray" behavior.
*/
app.isQuitting = true;
app.quit();
},
},
]);
tray.setContextMenu(contextMenu);
/**
* Left-click behavior: Toggles main window visibility.
* but it remains standard practice for Windows and Linux environments.
*/
tray.on("click", () => {
const window = getMainWindow();
if (!window || window.isDestroyed()) return;
if (window.isVisible()) {
window.hide();
} else {
window.show();
window.focus();
logger.info("Window Restored from SystemTray (Double Click on Tray Icon)")
}
});
return tray;
}
/**
* Safely destroys the tray icon to prevent OS-level crashes (especially on Linux/Wayland).
* Should be called right before the application quits.
*/
function destroyTray() {
if (tray && !tray.isDestroyed()) {
tray.destroy();
tray = null;
logger.info("System Tray destroyed cleanly.");
}
}
module.exports = {createSystemTray, destroyTray};

View File

@@ -81,6 +81,21 @@ async function createMainWindow() {
throw err;
}
/**
* Intercepts the window close event.
* If the app is not explicitly quitting (via Tray menu),
* cancels the destruction and hides the window instead.
*/
mainWindow.on('close', (event) => {
if (!app.isQuitting && config.configFeatures.systemTray) {
event.preventDefault();
mainWindow.hide();
logger.info("Window Minimized in SystemTray (Using Close Button)");
return false;
}
});
/**
* Cleans up window reference when the main window is closed.
* Prevents memory leaks and allows recreation if needed.

BIN
build/app-icon-64x64.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

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.
@@ -68,13 +68,30 @@ function loadFeatures() {
return JSON.parse(raw);
}
/**
* Reloads feature flags from disk.
* Use this instead of the cached configFeatures snapshot
* when you need the latest user settings at call time.
*
* @returns {Object} Fresh feature flags from config file
*/
function reloadFeatures() {
try {
const raw = fs.readFileSync(featuresPath, "utf-8");
return JSON.parse(raw);
} catch (err) {
// Fallback to cached snapshot if file read fails
return configFeatures;
}
}
/**
* In-memory snapshot of application feature flags.
*
* Note: Changes to the config file are not automatically reflected.
* A restart or reload is required.
*/
const configFeatures = loadFeatures();
let configFeatures = loadFeatures();
module.exports = {
/**
@@ -102,6 +119,11 @@ module.exports = {
*/
configFeatures,
/**
* Function to reload the config
*/
reloadFeatures,
/**
* Path to the active configuration file
*/

View File

@@ -1,5 +1,6 @@
{
"autoUpdate": true,
"systemTray": false,
"discordRPC": true,
"customTopBar": true,
"autoCheckInfo": true,
@@ -7,7 +8,8 @@
"addMetadata": true,
"verboseLogs": false,
"autoDownloadPlaylist": true,
"createPlaylistFolders": true,
"keepPlaylistOrder": false,
"createPlaylistFolders": false,
"customCodec": "h264",
"logSystem": true,
"outputTitleCheck": true,

83
main.js
View File

@@ -38,10 +38,13 @@ 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");
/**
* On start, enable or disable hardWareAcceleration
* In-memory snapshot of application feature flags.
*
* Note: Changes to the config file are not automatically reflected.
* A restart could be required.
*/
if (!configFeatures.enableHardwareAcceleration){
app.disableHardwareAcceleration();
@@ -50,15 +53,7 @@ if (!configFeatures.enableHardwareAcceleration){
logger.info("Enable Hardware Acceleration")
}
/**
* In-memory snapshot of application feature flags.
*
* Note: Changes to the config file are not automatically reflected.
* A restart could be required.
*/
const config = require("./config");
if(config.devMode){
if(devMode){
/**
* Start devTron extensions - @see https://github.com/electron/devtron
*/
@@ -79,8 +74,45 @@ 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");
/**
* Global flag indicating if the application is intentionally shutting down.
* Used across the app to bypass the "minimize to tray on close" behavior.
* @type {boolean}
*/
app.isQuitting = false;
/**
* State flag to track the asynchronous cleanup process during shutdown.
* Prevents the application from exiting before services (RPC, Tray, Logs) are properly closed.
* @type {boolean}
*/
let isCleanedUp = false;
/**
* Performs a graceful shutdown of all application services asynchronously.
* Once finished, it re-triggers the app.quit() process.
*/
async function cleanShutdown() {
try {
destroyTray();
await stopRPC();
stopServer();
} catch (err) {
logger.error("Error during cleanup:", err);
} finally {
logger.info("All services stopped. Have a nice day!");
logSessionEnd();
isCleanedUp = true;
app.quit();
}
}
/**
* If another instance want to run
@@ -103,7 +135,7 @@ app.whenReady().then(async () => {
createSplashWindow();
if (!config.devMode && !checkNativeDependencies()) return;
if (!devMode && !checkNativeDependencies()) return;
const { userYtDlp } = require("./server/helpers/path.helpers");
updateYtDlp(userYtDlp);
@@ -127,6 +159,11 @@ app.whenReady().then(async () => {
closeSplashWindow();
getMainWindow().show();
if (configFeatures.systemTray) {
createSystemTray(devMode);
}
if (configFeatures.discordRPC) startRPC();
if (configFeatures.autoUpdate) initAutoUpdater(getMainWindow());
@@ -138,12 +175,20 @@ app.whenReady().then(async () => {
});
app.on("window-all-closed", () => {
logger.info("Shutting down...");
app.quit();
if (app.isQuitting || !configFeatures.systemTray) {
logger.info("Shutting down...");
app.quit();
} else if (process.platform !== "darwin") {
logger.info("Main window closed, app running in background (Tray).");
}
});
app.on("before-quit", async () => {
await stopRPC();
logger.info("All services stopped. Have a nice day!");
logSessionEnd();
app.on("before-quit", (event) => {
app.isQuitting = true;
if (!isCleanedUp) {
event.preventDefault();
cleanShutdown();
}
});

1730
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,8 @@
{
"name": "freedom-loader",
"desktopName": "com.masteracnolo.freedomloader",
"productName": "Freedom Loader",
"version": "1.6.0",
"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/",
@@ -15,17 +16,22 @@
},
"main": "main.js",
"scripts": {
"start": "electron --trace-warnings .",
"start": "electron --no-warnings .",
"start:warn": "electron --trace-warnings .",
"start:debug": "WAYLAND_DEBUG=1 ELECTRON_ENABLE_LOGGING=1 ELECTRON_ENABLE_STACK_DUMPING=1 electron --enable-logging --v=1 .",
"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",
"build:rpm": "bash scripts/create-copr-package.sh",
"build:all": "electron-builder -wl",
"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 tests/unit",
"test:unit": "jest test/unit",
"update": "npm update"
},
"dependencies": {
@@ -44,9 +50,10 @@
"@electron/devtron": "^2.1.1",
"@jest/globals": "^30.4.1",
"@playwright/test": "^1.62.1",
"electron": "^41.2.0",
"electron": "^44.3.0",
"electron-builder": "^26.15.3",
"jest": "^30.4.2",
"nodemon": "^3.1.14",
"playwright": "^1.62.1"
},
"build": {
@@ -75,47 +82,37 @@
"from": "theme",
"to": "theme"
},
{
"from": "build/confirm-icon.png",
"to": "confirm-icon.png"
},
{
"from": "build/banner.bmp",
"to": "banner.bmp"
},
{
"from": "build/banner.png",
"to": "banner.png"
},
{
"from": "build/error.png",
"to": "error.png"
},
{
"from": "config/config.default.json",
"to": "config/config.default.json"
},
{
"from": "build",
"to": ".",
"filter": [
"app-icon.ico",
"app-icon-64x64.png",
"confirm-icon.png",
"banner.bmp",
"banner.png",
"error.png"
]
}
],
"win": {
"target": "nsis",
"icon": "build/app-icon.ico",
"artifactName": "${productName}-Setup-${version}.${ext}",
"artifactName": "Freedom-Loader-Setup-${version}.${ext}",
"extraResources": [
{
"from": "resources/binaries/win-32/yt-dlp.exe",
"to": "binaries/yt-dlp.exe"
},
{
"from": "resources/binaries/win-32/ffmpeg.exe",
"to": "binaries/ffmpeg.exe"
},
{
"from": "resources/binaries/win-32/ffprobe.exe",
"to": "binaries/ffprobe.exe"
},
{
"from": "resources/binaries/win-32/deno.exe",
"to": "binaries/deno.exe"
"from": "resources/binaries/win-32",
"to": "binaries",
"filter": [
"yt-dlp.exe",
"ffmpeg.exe",
"ffprobe.exe",
"deno.exe"
]
}
]
},
@@ -137,24 +134,20 @@
"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": [
{
"from": "resources/binaries/linux/yt-dlp",
"to": "binaries/yt-dlp"
},
{
"from": "resources/binaries/linux/ffmpeg",
"to": "binaries/ffmpeg"
},
{
"from": "resources/binaries/linux/ffprobe",
"to": "binaries/ffprobe"
},
{
"from": "resources/binaries/linux/deno",
"to": "binaries/deno"
"from": "resources/binaries/linux",
"to": "binaries",
"filter": [
"yt-dlp",
"ffmpeg",
"ffprobe",
"deno"
]
}
]
},

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -56,9 +56,9 @@
<div class="setting-item" id="theme" style="margin-left: 32px;">
<div class="setting-info">
<div style="display: flex; align-items: center; gap: 10px;">
<span class="setting-title">Thème</span>
<span class="setting-title">Theme</span>
<select id="themeSelect" data-key="theme" aria-label="Choisir le thème">
<select id="themeSelect" data-key="theme" aria-label="Choose the theme">
</select>
<button id="refresh-themes-btn" title="Refresh themes" class="refresh-themes-btn">
@@ -106,10 +106,10 @@
</div>
<div class="setting-item">
<input type="checkbox" data-key="autoCheckInfo" id="autoCheckInfo">
<input data-key="keepPlaylistOrder" id="keepPlaylistOrder" type="checkbox">
<div class="setting-info">
<span class="setting-title">Auto Fetch Info</span>
<small>Automatically fetch information for new downloads.</small>
<span class="setting-title">Keep Playlist Order</span>
<small>Add index before output file, to keep the order.</small>
</div>
</div>
</div>
@@ -127,15 +127,31 @@
</div>
</div>
<!-- <div class="setting-item">
<input type="checkbox" data-key="outputTitleCheck" id="outputTitleCheck">
<div class="setting-info">
<span class="setting-title">Output title (special chars)</span>
<small>Check for unusual characters in output titles.</small>
<div class="setting-item">
<input data-key="systemTray" id="systemTray" type="checkbox">
<div class="setting-info">
<span class="setting-title">System Tray</span>
<small>Minimize app on close, in the System Tray. [EXPERIMENTAL]</small>
</div>
</div>
</div> -->
<div class="setting-item">
<!-- <div class="setting-item">
<input type="checkbox" data-key="outputTitleCheck" id="outputTitleCheck">
<div class="setting-info">
<span class="setting-title">Output title (special chars)</span>
<small>Check for unusual characters in output titles.</small>
</div>
</div> -->
<div class="setting-item">
<input data-key="autoCheckInfo" id="autoCheckInfo" type="checkbox">
<div class="setting-info">
<span class="setting-title">Auto Fetch Info</span>
<small>Automatically fetch information for new downloads.</small>
</div>
</div>
<div class="setting-item">
<input type="checkbox" data-key="addMetadata" id="addMetadata">
<div class="setting-info">
<span class="setting-title">Add Metadata</span>
@@ -246,7 +262,7 @@
</div>
<div class="download-path">
Will be save on <span id="savePath"></span>
Will be saved in <span id="savePath"></span>
<button id="changePath">Edit</button>
</div>
@@ -290,7 +306,6 @@
<!-- Scripts -->
<script src="script/toast.js"></script>
<script src="script/customConfiguration.js"></script>
<script src="script/clipboardPaste.js"></script>
<script src="script/custompath.js"></script>
<script src="script/settingsPanel.js"></script>

View File

@@ -1,21 +1,54 @@
/**
* Retrieves the application version from the Electron main process
* and displays it in the UI version badge (bottom-right corner).
* Retrieves the application version and build state from the backend.
* Displays a formatted string indicating packaging state and preview status.
* Hides the "Packaged" state for production builds to keep the UI clean.
*
* This is mainly used for debugging and build identification.
* Example outputs: "Not Packaged Preview v1.6.2" or "Preview v1.6.2"
*
* @returns {Promise<void>}
*/
async function versionLabel() {
async function displayVersionLabel() {
/**
* Raw version string from the backend (e.g., "dev-1.6.2-preview" or "v1.6.2")
* @type {string}
*/
const appVersion = await window.electronAPI.getVersion();
/**
* UI element displaying the current application version.
* Updated at runtime after IPC call resolves.
*/
const versionBadge = document.getElementById("version-badge");
/**
* UI element displaying the current application version.
* @type {HTMLElement | null}
*/
const versionBadge = document.getElementById("version-badge");
if (!versionBadge) return;
if (!versionBadge) return;
versionBadge.textContent = `${appVersion}`;
/**
* Extracts the numeric semantic version (e.g., "1.6.2").
* @type {RegExpMatchArray | null}
*/
const versionMatch = appVersion.match(/(\d+\.\d+\.\d+)/);
const baseVersion = versionMatch ? versionMatch[0] : "Unknown";
/**
* Determines the packaging state. The backend prepends "dev-" if unpackaged.
* @type {boolean}
*/
const isPackaged = !appVersion.includes("dev-");
/**
* Determines if the current build is a preview version.
* @type {boolean}
*/
const isPreview = appVersion.includes("preview");
/**
* Construct the final display string.
* If packaged, we don't mention it. If not, we explicitly say "Not Packaged ".
*/
const packageStateStr = isPackaged ? "" : "Not Packaged ";
const previewStr = isPreview ? "Preview " : "";
versionBadge.textContent = `${packageStateStr}${previewStr}v${baseVersion}`;
}
versionLabel();
displayVersionLabel();

View File

@@ -1,27 +0,0 @@
async function initPlatformRestrictions() {
const platform = await window.electronAPI.getProcessPlatform();
const themeSelect = document.getElementById("themeSelect");
const refreshBtn = document.getElementById("refresh-themes-btn");
/**
* Is the OS windows
* @type {boolean}
*/
const isWindows = platform === "win32";
console.log(isWindows);
// If it's windows, the button is not disabled.
themeSelect.disabled = !isWindows;
refreshBtn.disabled = !isWindows;
themeSelect.classList.toggle("disabled", !isWindows);
refreshBtn.classList.toggle("disabled", !isWindows);
if (!isWindows) {
themeSelect.title = "Themes are only available on Windows";
refreshBtn.title = "Themes are only available on Windows";
}
}

View File

@@ -121,7 +121,7 @@ async function initThemes() {
const defaultTheme = {
id: "dark",
name: "Dark",
subtitle: "Darkness is my ally",
subtitle: "Because Why Not ?",
style: {
background: {
size: "cover",

View File

@@ -30,15 +30,15 @@ function formatSize(bytes) {
*/
async function fetchVideoInfo(url) {
try {
const res = await fetch("/info", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({ url }),
const res = await fetch(`/info?url=${encodeURIComponent(url)}`, {
method: "GET",
headers: {"Accept": "application/json"}
});
if (!res.ok) return { error: `An Error occured when fetching info` };
const data = await res.json();
if (!res.ok) return {error: `An Error occured when fetching info`};
if (!data) return { error: "Data is Missing" };
return data;

View File

@@ -5,53 +5,55 @@
* This function is safe to call once DOM is ready.
* It silently aborts if the API or elements are missing.
*/
function setupTopbarListeners() {
export function initTopBar() {
const { topbarAPI } = window;
if (!topbarAPI) return;
const minBtn = document.getElementById("minimize-btn");
const maxBtn = document.getElementById("maximize-btn");
const closeBtn = document.getElementById("close-btn");
const devtoolsBtn = document.getElementById("devtools-btn");
const logsBtn = document.getElementById("logs-btn");
const websiteBtn = document.getElementById("website-btn");
const wikiBtn = document.getElementById("wiki-btn");
const workshopBtn = document.getElementById("workshop-btn");
if (minBtn) minBtn.onclick = () => topbarAPI.minimize();
const maxBtn = document.getElementById("maximize-btn");
if (maxBtn) maxBtn.onclick = () => topbarAPI.maximize();
const closeBtn = document.getElementById("close-btn");
if (closeBtn) closeBtn.onclick = () => topbarAPI.close();
const devtoolsBtn = document.getElementById("devtools-btn");
if (devtoolsBtn) devtoolsBtn.onclick = () => topbarAPI.openDevTools();
const logsBtn = document.getElementById("logs-btn");
if (logsBtn) logsBtn.onclick = () => topbarAPI.openLogs();
const websiteBtn = document.getElementById("website-btn");
if (websiteBtn) websiteBtn.onclick = () => topbarAPI.openWebsite();
const wikiBtn = document.getElementById("wiki-btn");
if (wikiBtn) wikiBtn.onclick = () => topbarAPI.openWiki();
const workshopBtn = document.getElementById("workshop-btn");
if (workshopBtn) workshopBtn.onclick = () => topbarAPI.openWorkshop();
}
/**
* Registers topbar event listeners after DOM is fully loaded.
*/
document.addEventListener("DOMContentLoaded", setupTopbarListeners);
/**
* Applies UI layout adjustments depending on feature flags
* provided by the Electron backend.
*
* Specifically handles:
* - hiding custom topbar
* - adjusting layout spacing
* - repositioning theme switcher
*/
(async function applyFeatureLayout() {
const features = await window.electronAPI.getFeatures();
if (!features.customTopBar) {
const topbar = document.getElementById("topbar");
const container = document.getElementById("container");
const themeSwitcher = document.getElementById("theme-switcher");
if (topbar) topbar.style.display = "none";
if (container) container.style.marginTop = "0";
if (themeSwitcher) themeSwitcher.style.top = "30px";
async function attachListeners(){
try{
const featuresList = await window.electronAPI.getFeatures();
if (!featuresList.customTopBar) {
const topbar = document.getElementById("topbar");
const container = document.getElementById("container");
const themeSwitcher = document.getElementById("theme-switcher");
if (topbar) topbar.style.display = "none";
if (container) container.style.marginTop = "0";
if (themeSwitcher) themeSwitcher.style.top = "30px";
}
} catch (error) {
console.error("Failed to load layout features for topbar:", error);
}
})();
}
initTopBar()
document.addEventListener("DOMContentLoaded", attachListeners)

View File

Binary file not shown.

View File

Binary file not shown.

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

@@ -6,15 +6,74 @@ ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
VERSION=$(node -p "require('$ROOT_DIR/package.json').version")
RPM_OUT="$ROOT_DIR/dist/freedom-loader-${VERSION}.x86_64.rpm"
# ------------------------------------------------------------
# Resolve fpm from electron-builder cache
# Excludes Ruby gem wrappers, targets standalone binaries only
# Prefers fpm@2.x over legacy fpm-1.9.x
# ------------------------------------------------------------
FPM=$(find ~/.cache/electron-builder -name "fpm" -type f 2>/dev/null \
| grep -v "/gems/bin/fpm" \
| grep -v "/gems/gems/" \
| grep -v "/lib/app/bin/fpm" \
| grep "fpm@" \
| head -1)
# Fallback to legacy fpm-1.9.x if fpm@2.x not found
if [ -z "$FPM" ]; then
FPM=$(find ~/.cache/electron-builder -name "fpm" -type f 2>/dev/null \
| grep -v "/gems/" \
| grep -v "/lib/app/bin/fpm" \
| head -1)
fi
if [ -z "$FPM" ]; then
echo "Error: fpm not found in electron-builder cache. Run 'npm run build:linux' once first to download it."
exit 1
fi
echo "Using fpm: $FPM"
# Check that linux-unpacked exists
if [ ! -d "$ROOT_DIR/dist/linux-unpacked" ]; then
echo "Error: dist/linux-unpacked not found. Run 'npm run build:linux' first."
exit 1
fi
# Step 1 — Build .rpm from linux-unpacked via fpm
# ------------------------------------------------------------
# Prepare staging files
# fpm requires exact file-to-destination mappings to avoid
# scanning system directories (which causes permission errors)
# ------------------------------------------------------------
# Shell wrapper for /usr/bin/freedom-loader
TMP_WRAPPER="$ROOT_DIR/.rpm-pkg-tmp-wrapper"
mkdir -p "$TMP_WRAPPER"
cat > "$TMP_WRAPPER/freedom-loader" << 'EOF'
#!/bin/sh
exec /opt/freedom-loader/freedom-loader "$@"
EOF
chmod +x "$TMP_WRAPPER/freedom-loader"
# Desktop entry file
TMP_DESKTOP="$ROOT_DIR/.rpm-pkg-tmp-desktop"
mkdir -p "$TMP_DESKTOP"
cat > "$TMP_DESKTOP/freedom-loader.desktop" << 'EOF'
[Desktop Entry]
Name=Freedom Loader
Exec=/opt/freedom-loader/freedom-loader %U
Icon=freedom-loader
Type=Application
Categories=AudioVideo;Utility;Network;
Comment=Free and open-source GUI for yt-dlp
StartupWMClass=Freedom Loader
EOF
# ------------------------------------------------------------
# Step 1 - Build .rpm from linux-unpacked via fpm
# ------------------------------------------------------------
echo "Building RPM..."
fpm -s dir -t rpm --force \
"$FPM" -s dir -t rpm --force \
--rpm-use-file-permissions \
--name freedom-loader \
--version "$VERSION" \
--architecture x86_64 \
@@ -26,14 +85,21 @@ fpm -s dir -t rpm --force \
--depends nss \
--depends libXScrnSaver \
--depends at-spi2-core \
--rpm-attr "4755,root,root:/opt/freedom-loader/chrome-sandbox" \
--package "$RPM_OUT" \
"$ROOT_DIR/dist/linux-unpacked/=/opt/freedom-loader" \
"$TMP_WRAPPER/freedom-loader=/usr/bin/freedom-loader" \
"$TMP_DESKTOP/freedom-loader.desktop=/usr/share/applications/freedom-loader.desktop" \
"$ROOT_DIR/build/app-icon.png=/usr/share/icons/hicolor/512x512/apps/freedom-loader.png" \
"$ROOT_DIR/package/com.masteracnolo.freedomloader.metainfo.xml=/usr/share/metainfo/com.masteracnolo.freedomloader.metainfo.xml"
rm -rf "$TMP_WRAPPER" "$TMP_DESKTOP"
echo "RPM built: $RPM_OUT"
# Step 2 — Build SRPM for COPR (wraps the .rpm above)
# ------------------------------------------------------------
# Step 2 - Build SRPM for COPR
# Wraps the .rpm above into a source RPM for COPR submission
# ------------------------------------------------------------
echo "Building SRPM for COPR..."
mkdir -p "$ROOT_DIR/srpm-out"

198
scripts/release.sh Executable file
View File

@@ -0,0 +1,198 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
VERSION=$(node -p "require('$ROOT_DIR/package.json').version")
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
fi
TAG="$VERSION"
# Parse arguments
PUBLISH_SNAP=false
PUBLISH_COPR=false
DRY_RUN=false
for arg in "$@"; do
case $arg in
--snap) PUBLISH_SNAP=true ;;
--copr) PUBLISH_COPR=true ;;
--publish) PUBLISH_SNAP=true; PUBLISH_COPR=true ;;
--dry-run) DRY_RUN=true ;;
*) echo "Unknown Argument : $arg" ; exit 1 ;;
esac
done
echo ""
echo "============================================"
echo " Freedom Loader - Release Pipeline"
echo " Version : $VERSION"
echo " Tag : $TAG"
echo " Snap : $PUBLISH_SNAP"
echo " COPR : $PUBLISH_COPR"
echo " Dry run : $DRY_RUN"
echo "============================================"
echo ""
# ------------------------------------------------------------
# Step 0.1 - Check Dependencies
# ------------------------------------------------------------
echo "[0/5] Checking dependencies..."
command -v gh >/dev/null 2>&1 || { echo "'gh' CLI missing, need to be installed (https://cli.github.com/)."; exit 1; }
if [ "$PUBLISH_SNAP" = true ] && [ "$DRY_RUN" = false ]; then
command -v snapcraft >/dev/null 2>&1 || { echo "'snapcraft' missing, need to be installed."; exit 1; }
fi
if [ "$PUBLISH_COPR" = true ] && [ "$DRY_RUN" = false ]; then
command -v copr-cli >/dev/null 2>&1 || { echo "'copr-cli' missing, need to be installed."; exit 1; }
fi
echo "All dependencies found"
# ------------------------------------------------------------
# Step 0.2 - Clean Workspace
# ------------------------------------------------------------
echo "[0/5] Cleaning workspace..."
# On supprime les dossiers générés lors des builds précédents
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)
# ------------------------------------------------------------
echo "[1/5] Building Linux packages..."
cd "$ROOT_DIR"
npm run build:linux
echo "Linux packages built"
# ------------------------------------------------------------
# Step 2 - Build RPM binary + SRPM for COPR
# Uses the create-copr-package.sh script which handles:
# - fpm binary resolution from electron-builder cache
# - .rpm generation from linux-unpacked
# - .src.rpm generation for COPR submission
# ------------------------------------------------------------
echo "[2/5] Building RPM and SRPM..."
bash "$ROOT_DIR/scripts/create-copr-package.sh"
echo "RPM and SRPM built"
# ------------------------------------------------------------
# Step 3 - Build Windows installer
# Note: requires Windows binaries to be present locally under
# resources/binaries/win-32/ (not committed to the repo)
# ------------------------------------------------------------
echo "[3/5] Building Windows installer..."
npm run build:win
echo " Windows installer built"
# ------------------------------------------------------------
# Step 4 - Create a draft release on GitHub
# Release notes are pre-filled with the standard template.
# Edit and publish manually from the GitHub web interface.
# Requires gh CLI to be installed and authenticated.
# ------------------------------------------------------------
echo "[4/5] Creating GitHub draft release $TAG..."
# Utilise une méthode un peu plus robuste pour trouver l'ancien tag (ignore le HEAD actuel s'il est déjà tagué)
PREV_TAG=$(git rev-list --tags --skip=1 --max-count=1 | xargs git describe --tags 2>/dev/null || echo "previous")
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.
**Full Changelog**: https://github.com/MasterAcnolo/Freedom-Loader/compare/${PREV_TAG}...${TAG}"
if [ "$DRY_RUN" = false ]; then
gh release create "$TAG" \
--title "$TAG" \
--notes "$RELEASE_NOTES" \
--draft \
"$ROOT_DIR/dist/Freedom-Loader-Setup-${VERSION}.exe" \
"$ROOT_DIR/dist/Freedom-Loader-Setup-${VERSION}.exe.blockmap" \
"$ROOT_DIR/dist/freedom-loader-${VERSION}.x86_64.rpm" \
"$ROOT_DIR/dist/freedom-loader_${VERSION}_amd64.deb" \
"$ROOT_DIR/dist/Freedom Loader-${VERSION}.AppImage" \
"$ROOT_DIR/dist/freedom-loader_${VERSION}_amd64.snap" \
"$ROOT_DIR/dist/latest.yml" \
"$ROOT_DIR/dist/latest-linux.yml"
echo "Draft release created: https://github.com/MasterAcnolo/Freedom-Loader/releases"
else
echo " [DRY RUN] gh release create $TAG (skipped)"
fi
# ------------------------------------------------------------
# Step 5 - Publish to package stores
# Snap and COPR are opt-in via --snap, --copr, or --publish.
# Without these flags, this step is skipped entirely.
# Run after the GitHub release is published (not the draft),
# so store users always get the same version as GitHub users.
# ------------------------------------------------------------
echo "[5/5] Publishing to package stores..."
if [ "$PUBLISH_SNAP" = true ]; then
echo " Publishing to Snap Store..."
if [ "$DRY_RUN" = false ]; then
export PATH=$PATH:/var/lib/snapd/snap/bin
snapcraft upload \
"$ROOT_DIR/dist/freedom-loader_${VERSION}_amd64.snap" \
--release=stable
echo " Snap published"
else
echo " [DRY RUN] snapcraft upload (skipped)"
fi
else
echo " Snap: skipped (use --snap or --publish to enable)"
fi
if [ "$PUBLISH_COPR" = true ]; then
echo " Publishing to COPR..."
if [ "$DRY_RUN" = false ]; then
copr-cli build freedom-loader \
"$ROOT_DIR/srpm-out/freedom-loader-${VERSION}-1"*.src.rpm
echo "COPR build triggered"
else
echo " [DRY RUN] copr-cli build (skipped)"
fi
else
echo " COPR: skipped (use --copr or --publish to enable)"
fi
# ------------------------------------------------------------
# Done - Summary
# ------------------------------------------------------------
echo ""
echo "============================================"
echo " Release pipeline complete."
echo ""
echo " Next steps:"
echo " 1. Fill in the release notes on GitHub"
echo " 2. Publish the draft release"
if [ "$PUBLISH_SNAP" = false ] || [ "$PUBLISH_COPR" = false ]; then
echo ""
echo " Store publishing (not done yet):"
[ "$PUBLISH_SNAP" = false ] && echo " - Snap : bash scripts/release.sh --snap"
[ "$PUBLISH_COPR" = false ] && echo " - COPR : bash scripts/release.sh --copr"
fi
echo "============================================"

View File

@@ -2,6 +2,7 @@ const { fetchInfo } = require("../services/info.services");
const { parseVideo, parsePlaylist } = require("../helpers/parseInfo.helpers");
const { logger } = require("../logger");
const { isValidUrl } = require("../helpers/validation.helpers");
const {isUrlPlaylist} = require("../helpers/playlist.helpers");
/**
* Handles metadata retrieval for a video or playlist.
@@ -18,7 +19,9 @@ const { isValidUrl } = require("../helpers/validation.helpers");
*/
async function infoController(req, res) {
const url = req.body.url || req.query.url; // Supports POST and GET requests
// Previously, i support both POST and GET element, but it's kinda weird. So now it only support GET.
const url = req.query.url;
const encodedUrl = encodeURIComponent(url)
/**
* Basic validation:
@@ -30,10 +33,11 @@ async function infoController(req, res) {
return res.status(400).send("Invalid URL Or Missing");
}
logger.info(`Info request received. URL: ${url}`);
logger.info(`Info request received. RAW URL: ${url}`);
logger.info(`Info request received. ENCODED: ${encodedUrl}`);
// Lightweight heuristic to detect playlist URLs
const isPlaylistUrl = url.includes("&list") || url.includes("?list");
// Lightweight heuristic to detect playlist URLs. It works for Youtube.
const isPlaylistUrl = isUrlPlaylist(url);
logger.info(
isPlaylistUrl
@@ -73,11 +77,11 @@ async function infoController(req, res) {
);
// Preserve original error context
throw err;
logger.error(err);
}
} else {
throw err;
logger.error(err);
}
}
@@ -86,6 +90,16 @@ async function infoController(req, res) {
*/
if (data._type === "playlist") {
// If the playlist got only one video, show it as a video.
if (data.entries && data.entries.length === 1) {
const singleVideo = parseVideo(data.entries[0]);
logger.info(`Playlist with 1 item converted to video: ${singleVideo.title}`);
return res.json(singleVideo);
}
const playlist = parsePlaylist(data);
logger.info(

View File

@@ -1,9 +1,10 @@
const path = require("path");
const getUserBrowser = require("./getBrowser.helpers.js");
const {getUserBrowser} = require("./getBrowser.helpers.js");
const { ffmpegPath, denoPath} = require("./path.helpers.js");
const { configFeatures } = require("../../config.js");
const { logger } = require("../logger.js");
const config = require("../../config")
/**
* Validates whether the provided codec is supported by the application.
@@ -62,7 +63,7 @@ function validateCodec(codec){
*
* @returns {string[]} Array of arguments ready to be passed to yt-dlp.
*/
function buildYtDlpArgs({ url, audioOnly, quality, outputFolder }) {
function buildYtDlpArgs({url, audioOnly, quality, outputFolder, isPlaylist}) {
logger.info("--- CONFIGURATION ---");
@@ -70,6 +71,7 @@ function buildYtDlpArgs({ url, audioOnly, quality, outputFolder }) {
logger.info(`CONFIG discordRPC: ${configFeatures.discordRPC}`);
logger.info(`CONFIG customTopBar: ${configFeatures.customTopBar}`);
logger.info(`CONFIG autoCheckInfo: ${configFeatures.autoCheckInfo}`);
logger.info(`CONFIG keepPlaylistOrder: ${configFeatures.keepPlaylistOrder}`);
logger.info(`CONFIG addThumbnail: ${configFeatures.addThumbnail}`);
logger.info(`CONFIG addMetadata: ${configFeatures.addMetadata}`);
logger.info(`CONFIG verboseLogs: ${configFeatures.verboseLogs}`);
@@ -117,7 +119,27 @@ function buildYtDlpArgs({ url, audioOnly, quality, outputFolder }) {
};
args.push("-f", qualityMap[quality] || "best");
args.push("-o", path.join(outputFolder, "%(title)s.%(ext)s"));
/**
* Default title output template
* @type {string}
*/
let outputTemplate = "%(title)s.%(ext)s";
// If it's a playlist, and we want to keep the playlist order. Add an index before the title.
if (isPlaylist && configFeatures.keepPlaylistOrder && configFeatures.autoDownloadPlaylist) {
outputTemplate = "%(playlist_index)02d - %(title)s.%(ext)s";
}
// Add a custom signature in each file downloaded with the app. It's a small things but i found it cool :)
if (configFeatures.addMetadata) {
args.push(
"--postprocessor-args",
`ffmpeg:-metadata comment="Downloaded with Freedom Loader ${config.version}"`
);
}
args.push("-o", path.join(outputFolder, outputTemplate));
args.push(url);
return args.filter(Boolean);

View File

@@ -1,7 +1,6 @@
const fs = require("fs");
const os = require("os");
const path = require("path");
const notify = require("./notify.helpers")
const notify = require("./notify.helpers");
const { logger } = require("../logger");
/**
@@ -22,41 +21,38 @@ const { logger } = require("../logger");
* @returns {string} The detected browser identifier.
*/
function getUserBrowser() {
//const userProfile = os.homedir();
const firefoxPath = getFirefoxProfilePath();
const platform = process.platform;
let isFirefoxHere = false;
/**
* Browsers supported by yt-dlp cookie extraction.
*
* Only Firefox is currently enabled and tested.
* Additional browsers can be enabled in the future
* once compatibility has been verified.
*/
const browsers = [
{ name: "firefox", path: firefoxPath },
// { name: "chrome", path: path.join(userProfile, "AppData", "Local", "Google", "Chrome", "User Data", "Default") },
// { name: "brave", path: path.join(userProfile, "AppData", "Local", "BraveSoftware", "Brave-Browser", "User Data", "Default") },
// { name: "edge", path: path.join(userProfile, "AppData", "Local", "Microsoft", "Edge", "User Data", "Default") },
// { name: "opera", path: path.join(userProfile, "AppData", "Roaming", "Opera Software", "Opera Stable") },
// { name: "vivaldi", path: path.join(userProfile, "AppData", "Local", "Vivaldi", "User Data", "Default") },
// { name: "safari", path: path.join(userProfile, "AppData", "Local", "Apple Computer", "Safari") },
// { name: "whale", path: path.join(userProfile, "AppData", "Local", "Naver", "Naver Whale", "User Data", "Default") }
];
if (platform === "win32") {
const paths = [
"C:\\Program Files\\Mozilla Firefox\\firefox.exe",
"C:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe",
`${process.env.LOCALAPPDATA}\\Mozilla Firefox\\firefox.exe`,
];
// Search for the first available browser profile.
for (const browser of browsers) {
if (browser.path && fs.existsSync(browser.path)) {
logger.info(`Browser found: ${browser.name}`);
return browser.name;
}
isFirefoxHere = paths.some(p => fs.existsSync(p));
}
// No supported browser found => Notify User
logger.warn("No supported browser found on the system");
if (platform === "linux") {
const paths = [
"/usr/bin/firefox",
"/usr/bin/firefox-esr",
"/usr/local/bin/firefox",
"/snap/bin/firefox",
"/var/lib/flatpak/exports/bin/org.mozilla.firefox",
`${os.homedir()}/.local/share/flatpak/exports/bin/org.mozilla.firefox`,
];
isFirefoxHere = paths.some(p => fs.existsSync(p));
}
// If you somehow managed to live without Firefox and need help installing it. - Don't applied to my Linux chad
if (process.platform === "win32") {
if (!isFirefoxHere) {
// If you somehow managed to live without Firefox and need help installing it. - Don't applied to my Linux chad
// No supported browser found => Notify User
logger.warn("No supported browser found on the system");
notify.notifyFirefoxBrowserMissing();
} else {
logger.info("Browser found: firefox");
}
// Fallback to Firefox and let yt-dlp handle the error gracefully.
@@ -64,39 +60,4 @@ function getUserBrowser() {
return "firefox";
}
function getFirefoxProfilePath() {
const home = os.homedir();
if (process.platform === "win32") {
return path.join(
home,
"AppData",
"Roaming",
"Mozilla",
"Firefox",
"Profiles"
);
}
if (process.platform === "linux") {
return path.join(
"usr",
"bin",
"firefox"
);
}
if (process.platform === "darwin") {
return path.join(
home,
"Library",
"Application Support",
"Firefox",
"Profiles"
);
}
return null;
}
module.exports = getUserBrowser
module.exports = {getUserBrowser};

View File

@@ -2,6 +2,12 @@ const { Notification, shell } = require("electron");
const { iconPaths } = require("./path.helpers");
const { logger } = require("../logger");
/**
* Set that is holding all notifications. Used to avoid Race Condition and notification crash
* @type {Set<any>}
*/
const activeNotifications = new Set();
/**
* Displays a system notification when a download completes successfully.
*
@@ -14,14 +20,34 @@ const { logger } = require("../logger");
function notifyDownloadFinished(folder, notifyEnabled = true) {
if (!notifyEnabled) return;
if (!folder) return;
const notif = new Notification({
title: "Freedom Loader",
body: "Your download is complete, click here to open it.",
icon: iconPaths.confirm,
});
notif.on("click", () => shell.openPath(folder));
// Protect notification to garbage collection and race condition, by adding a 150ms timeout before trying to open the folder
activeNotifications.add(notif);
notif.on("click", () => {
activeNotifications.delete(notif);
setTimeout(() => {
shell.openPath(folder).then((errorMessage) => {
if (errorMessage) {
logger.error(`Impossible d'ouvrir le dossier : ${errorMessage}`);
}
}).catch(err => {
logger.error(`Erreur inattendue de shell.openPath : ${err}`);
});
}, 150);
});
notif.on("close", () => {
activeNotifications.delete(notif);
});
notif.show();
}
@@ -41,11 +67,16 @@ function notifyCookiesBrowserError(){
icon: iconPaths.error,
});
notif.on("click", () =>
shell.openExternal(
"https://youtube.com/shorts/cN9f4s1Mf88?si=519QCVd_-fzJqRf1"
)
);
activeNotifications.add(notif);
notif.on("click", () => {
shell.openExternal("https://www.firefox.com/en-US/download/");
activeNotifications.delete(notif);
});
notif.on("close", () => {
activeNotifications.delete(notif);
});
notif.show();
}
@@ -65,11 +96,16 @@ function notifyFirefoxBrowserMissing() {
icon: iconPaths.error,
});
notif.on("click", () =>
shell.openExternal(
"https://youtube.com/shorts/cN9f4s1Mf88?si=519QCVd_-fzJqRf1"
)
);
activeNotifications.add(notif);
notif.on("click", () => {
shell.openExternal("https://www.firefox.com/en-US/download/");
activeNotifications.delete(notif);
});
notif.on("close", () => {
activeNotifications.delete(notif);
});
notif.show();
}

View File

@@ -150,7 +150,7 @@ const binaryPaths = {
* - user themes are persisted in userData to survive updates
*/
const userThemesPath = path.join(app.getPath("userData"), "themes");
const defaultThemesSourcePath = path.join(resourcesPath, "theme");
const defaultThemesSourcePath = path.join("theme");
/**
* Initializes user theme directory and ensures default themes exist.
@@ -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

@@ -0,0 +1,12 @@
/**
* Determines whether a URL targets a playlist.
*
* The detection is based on the presence of the YouTube
* playlist query parameter (`list`).
*
* @param {string} url - URL to inspect.
* @returns {boolean} True if the URL appears to be a playlist.
*/
export function isUrlPlaylist(url) {
return url.includes("?list=") || url.includes("&list=") || url.includes("@");
}

View File

@@ -17,9 +17,29 @@ 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}
*/
const fileFormat = format.combine(
format.timestamp({format: "YYYY-MM-DD HH:mm:ss"}),
format.errors({stack: true}), // Capture la stack trace complète des erreurs
format.printf(({timestamp, level, message, stack}) => {
return `${timestamp} | ${level.toUpperCase()} | ${stack || message}`;
})
);
/**
* Formated used by the IDE/Console
* @type {Format}
*/
const consoleFormat = format.combine(
format.colorize({all: true}),
format.timestamp({format: "HH:mm:ss"}),
format.errors({stack: true}),
format.printf(({timestamp, level, message, stack}) => {
return `[${timestamp}] ${level}: ${stack || message}`;
})
);
/**
@@ -33,7 +53,6 @@ const logFormat = format.combine(
*/
const logger = createLogger({
level: "info",
format: logFormat,
transports: [
new DailyRotateFile({
dirname: logDir,
@@ -41,11 +60,11 @@ const logger = createLogger({
datePattern: "YYYY-MM-DD",
zippedArchive: false,
maxFiles: "7d",
format: logFormat,
format: fileFormat,
options: { flags: "a" },
}),
new transports.Console({
format: logFormat,
format: consoleFormat,
}),
],
});
@@ -54,16 +73,17 @@ const logger = createLogger({
* Start Log Session
*/
function logSessionStart(logDir, downloadPath) {
logger.info(`--- Starting session: ${new Date().toISOString()} ---`);
logger.info(`--- Starting session: ${new Date().toLocaleString('sv-SE')} ---`);
logger.info("============================================================")
logger.info(`Application Version: ${config.version}`)
logSystemInfo(logger, logDir, downloadPath)
if (typeof logSystemInfo === 'function') logSystemInfo(logger, logDir, downloadPath);
}
/**
* Stop Log Session
*/
function logSessionEnd() {
logger.info(`--- Ending session: ${new Date().toISOString()} ---`);
logger.info(`--- Ending session: ${new Date().toLocaleString('sv-SE')} ---`);
}
module.exports = {

View File

@@ -2,6 +2,6 @@ const express = require("express");
const router = express.Router();
const { infoController } = require("../controller/info.controller");
router.post("/", infoController);
router.get("/", infoController);
module.exports = router;

View File

@@ -1,11 +1,17 @@
const express = require("express");
const path = require("path");
const { logger, logSessionEnd } = require("./logger");
const config = require("../config");
const {logger} = require("./logger");
const { rateLimit } = require("./helpers/rateLimit.helpers");
const app = express();
/**
* Store the state of the server
*/
let serverInstance = null;
// Middlewares
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
@@ -13,7 +19,9 @@ app.use(express.static(path.join(__dirname, "../public")));
// Routes
app.use("/download", require("./routes/download.route"));
app.use("/info", require("./routes/info.route"));
app.use("/info", require("./routes/info.route"));
// Interface
app.get("/", rateLimit, (req, res) => {
res.sendFile(path.join(__dirname, "../public/index.html"));
});
@@ -28,33 +36,32 @@ app.get("/", rateLimit, (req, res) => {
*/
async function startServer() {
return new Promise((resolve, reject) => {
const server = app.listen(config.applicationPort, () => {
const serverInstance = app.listen(config.applicationPort, () => {
logger.info(`Express server ready at http://localhost:${config.applicationPort}`);
resolve(server);
resolve(serverInstance);
});
server.on("error", (err) => {
serverInstance.on("error", (err) => {
logger.error("Express server error:", err);
reject(err);
});
/**
* Clean exit function
* - Stop Log
* - Stop Express Server
* - Stop Electron App
*/
const gracefulExit = () => {
logSessionEnd();
server.close(() => {
logger.info("Express server closed cleanly.");
process.exit();
});
};
process.on("SIGINT", gracefulExit);
process.on("SIGTERM", gracefulExit);
});
}
module.exports = { startServer };
/**
* Clean exit function called by Electron
*/
function stopServer() {
if (serverInstance) {
try{
serverInstance.close();
logger.info("Express server closed cleanly");
} catch (err){
logger.error("Express server was not closed cleanly:", err)
}
} else {
logger.info("Express server was already closed");
}
}
module.exports = { startServer, stopServer };

View File

@@ -6,7 +6,8 @@ const { buildYtDlpArgs } = require("../helpers/buildArgs.helpers");
const notify = require("../helpers/notify.helpers");
const path = require("path");
const { isSafePath } = require("../helpers/validation.helpers");
const { configFeatures } = require("../../config.js");
const {reloadFeatures} = require("../../config");
const {isUrlPlaylist} = require("../helpers/playlist.helpers");
/**
* Reference to the currently running yt-dlp process.
@@ -18,19 +19,6 @@ const { configFeatures } = require("../../config.js");
*/
let currentDownloadProcess = null;
/**
* Determines whether a URL targets a playlist.
*
* The detection is based on the presence of the YouTube
* playlist query parameter (`list`).
*
* @param {string} url - URL to inspect.
* @returns {boolean} True if the URL appears to be a playlist.
*/
function isPlaylistUrl(url) {
return url.includes("?list=") || url.includes("&list=");
}
/**
* Creates a dedicated download folder for a playlist.
*
@@ -49,23 +37,23 @@ function isPlaylistUrl(url) {
*/
function createPlaylistFolder(basePath, playlistTitle) {
const sanitizedTitle = playlistTitle
.replace(/[<>:"|?*]/g, '')
.replace(/\s+/g, ' ')
.replace(/[<>:"|?*]/g, '')
.replace(/\s+/g, ' ')
.trim()
.substring(0, 200);
const playlistPath = path.join(basePath, sanitizedTitle);
try {
if (!fs.existsSync(playlistPath)) {
fs.mkdirSync(playlistPath, { recursive: true });
logger.info(`Playlist folder created: ${playlistPath}`);
return playlistPath;
}
let counter = 1;
let newPath;
while (counter <= 1000) {
newPath = path.join(basePath, `${sanitizedTitle} ${counter}`);
if (!fs.existsSync(newPath)) {
@@ -75,17 +63,17 @@ function createPlaylistFolder(basePath, playlistTitle) {
}
counter++;
}
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}`);
// Fallback: create folder "Untitled Playlist X"
let counter = 1;
let fallbackPath;
while (counter <= 1000) {
fallbackPath = path.join(basePath, `Untitled Playlist ${counter}`);
if (!fs.existsSync(fallbackPath)) {
@@ -100,7 +88,7 @@ function createPlaylistFolder(basePath, playlistTitle) {
}
counter++;
}
logger.error(`Could not create playlist folder after 1000 attempts`);
throw new Error("Unable to create playlist folder");
}
@@ -123,7 +111,7 @@ function createPlaylistFolder(basePath, playlistTitle) {
* @param {string} [options.outputFolder] - Destination folder.
* @param {string} [options.playlistTitle] - Playlist title.
*
* @param {Function[]} listeners - Progress listeners.
* @param {Function[]} progressListeners - Progress listeners.
* @param {Function[]} speedListeners - Download speed listeners.
* @param {Function[]} stageListeners - Processing stage listeners.
* @param {Function[]} playlistInfoListeners - Playlist progress listeners.
@@ -132,21 +120,24 @@ function createPlaylistFolder(basePath, playlistTitle) {
* Resolves with the final output folder path once the
* download has completed successfully.
*/
function fetchDownload(options, listeners, speedListeners, stageListeners, playlistInfoListeners) {
function fetchDownload(options, progressListeners, speedListeners, stageListeners, playlistInfoListeners) {
return new Promise((resolve, reject) => {
logger.info(`CONFIG createPlaylistFolders: ${configFeatures.createPlaylistFolders}`);
const userConfig = reloadFeatures();
logger.info(`CONFIG createPlaylistFolders: ${userConfig.createPlaylistFolders}`);
let outputFolder = options.outputFolder || defaultDownloadFolder;
// Normalize path and validate it's safe (within Users folder)
let safeOutputFolder = path.resolve(outputFolder);
if (!isSafePath(safeOutputFolder)) {
logger.warn(`Path not allowed, using default instead: ${safeOutputFolder}`);
safeOutputFolder = path.resolve(defaultDownloadFolder);
}
// Create download folder if it doesn't exist
try {
fs.mkdirSync(safeOutputFolder, { recursive: true });
@@ -157,9 +148,9 @@ function fetchDownload(options, listeners, speedListeners, stageListeners, playl
}
// Détecte si c'est une playlist et crée un dossier approprié
const isPlaylist = options.playlistTitle || isPlaylistUrl(options.url);
if (isPlaylist && configFeatures.createPlaylistFolders) {
const isPlaylist = options.playlistTitle || isUrlPlaylist(options.url);
if (isPlaylist && userConfig.createPlaylistFolders) {
try {
const playlistName = options.playlistTitle || "Untitled Playlist";
safeOutputFolder = createPlaylistFolder(safeOutputFolder, playlistName);
@@ -168,11 +159,16 @@ function fetchDownload(options, listeners, speedListeners, stageListeners, playl
logger.error(`Failed to create playlist folder: ${err.message}`);
return reject(err);
}
} else if (isPlaylist && !configFeatures.createPlaylistFolders) {
} else if (isPlaylist && !userConfig.createPlaylistFolders) {
logger.info(`Playlist detected but createPlaylistFolders is disabled, using base folder`);
}
const args = buildYtDlpArgs({ ...options, outputFolder: safeOutputFolder });
const args = buildYtDlpArgs({
...options,
outputFolder: safeOutputFolder,
isPlaylist
});
logger.info(`[yt-dlp args] ${args.join(" ")}`);
const child = execFile(userYtDlp, args);
@@ -182,25 +178,25 @@ function fetchDownload(options, listeners, speedListeners, stageListeners, playl
child.on("close", code => {
currentDownloadProcess = null;
listeners.forEach(fn => fn("done"));
progressListeners.forEach(fn => fn("done"));
if (code === 0) resolve(safeOutputFolder);
else if (code === null) reject(new Error(`Download cancelled by user`));
else reject(new Error(`YT-DLP failed with code : ${code}`));
});
child.stdout.on("data", data => {
data.toString().split("\n").forEach(line => {
if (!line.trim()) return;
logger.info(`[yt-dlp] ${line}`);
// Progress Bar
// Progress Bar
if (line.startsWith("[download] Destination:")) {
listeners.forEach(fn => fn("reset"));
progressListeners.forEach(fn => fn("reset"));
stageListeners.forEach(fn => fn("Downloading..."));
}
const match = line.match(/\[download\]\s+(\d+\.\d+)%/);
if (match) listeners.forEach(fn => fn(parseFloat(match[1])));
if (match) progressListeners.forEach(fn => fn(parseFloat(match[1])));
if (line.includes("MiB/s") || line.includes("KiB/s")) {
const match = line.match(/\d+(\.\d+)?[KM]?iB\/s/);
@@ -245,7 +241,7 @@ function fetchDownload(options, listeners, speedListeners, stageListeners, playl
data.toString().split("\n").forEach(line => line.trim() && logger.error(`[yt-dlp stderr] ${line}`));
});
});
}
@@ -262,7 +258,7 @@ function fetchDownload(options, listeners, speedListeners, stageListeners, playl
function cancelDownload() {
if (currentDownloadProcess) {
logger.info("Cancelling download and killing all child processes...");
// Force kill the process and all its children with SIGKILL
try {
// Try to kill with SIGKILL on Windows (process group) or Unix
@@ -278,7 +274,7 @@ function cancelDownload() {
// Fallback to regular kill
currentDownloadProcess.kill('SIGKILL');
}
currentDownloadProcess = null;
logger.info("Download cancelled successfully");
return true;

View File

@@ -1,6 +1,6 @@
const { execFile } = require("child_process");
const { userYtDlp, denoPath } = require("../helpers/path.helpers");
const getUserBrowser = require("../helpers/getBrowser.helpers");
const {getUserBrowser} = require("../helpers/getBrowser.helpers");
const { logger } = require("../logger");
/**
@@ -61,6 +61,7 @@ function fetchInfo(url) {
try {
const data = JSON.parse(stdout);
resolve(data);
} catch (e) {
logger.error(`JSON Parsing Error: ${e.message}`);

Binary file not shown.