51 Commits
1.4.0 ... 1.4.4

Author SHA1 Message Date
MasterAcnolo
83ca963502 Merge with false positive
Merge with False positive
2026-02-18 15:10:01 +01:00
MasterAcnolo
9fb9295093 Fix: Try to re run workflow 2026-02-18 14:51:57 +01:00
MasterAcnolo
74fcdbb738 Fix: Undo Path Validation for coming back on a classic one 2026-02-18 14:43:40 +01:00
MasterAcnolo
32c9a66a35 Fix: Centralize default download path and enhance path validation logic 2026-02-17 23:29:31 +01:00
MasterAcnolo
45e0840fea Fix: Correct windowMs value in rate limit configuration to 15 minutes 2026-02-17 23:26:01 +01:00
MasterAcnolo
19b3f8b947 Fix: Refactor conditional statements for starting Discord RPC and Auto Update 2026-02-17 21:49:52 +01:00
MasterAcnolo
cc5bde5b48 Fix: Correct comment for rate limit window duration in rateLimit.js 2026-02-17 21:49:43 +01:00
MasterAcnolo
9a48ac05e3 Fix: Uncomplete comments and remove repeated code 2026-02-17 21:42:12 +01:00
MasterAcnolo
5750444655 Fix: Update full changelog link format in release template 2026-02-17 21:35:57 +01:00
MasterAcnolo
fdb22f2342 Fix: Improve browser detection logging and error handling 2026-02-17 21:29:51 +01:00
MasterAcnolo
760550e521 Fix: Centralize resource paths and icons 2026-02-17 21:28:46 +01:00
MasterAcnolo
334e83ae1f Fix: Small coms in AppVersion.js 2026-02-17 21:12:01 +01:00
MasterAcnolo
5d776594fc Fix: App Not Stopping correctly + Better Logs for end :) 2026-02-17 21:09:09 +01:00
MasterAcnolo
adb36e3fa2 Fix: We are not on apple system. Don't care about Darwin 2026-02-17 20:59:22 +01:00
MasterAcnolo
87945fba80 Fix: Missing semi colon, Better Folder Creation, And remove the fucking bad code who was repeat too much (wtf i did) 2026-02-17 18:05:07 +01:00
MasterAcnolo
2234f9ed81 Fix: Update version numbers to 1.4.4 in package.json and ReadME 2026-02-17 15:22:38 +01:00
MasterAcnolo
7cd3b4be06 Fix: Update playlist badge text to English 2026-02-17 15:19:23 +01:00
MasterAcnolo
a42b020b96 Fix: Update URL check to correctly identify playlist links 2026-02-17 15:19:17 +01:00
MasterAcnolo
c1e70dae80 Feat: Add Better playlist layout 2026-02-17 10:27:13 +01:00
MasterAcnolo
6877c51c0e Fix: Add Small Comms for clarity 2026-02-17 10:26:54 +01:00
MasterAcnolo
5a41090175 Fix: Remove Unusued Code 2026-02-17 10:26:39 +01:00
MasterAcnolo
5983f21759 Fix: Big mistake when estimating video format (? -> &) 2026-02-17 09:50:32 +01:00
MasterAcnolo
ee9e02410f Merge pull request #31 from MasterAcnolo/fix-input
1.4.3
2026-02-16 22:34:12 +01:00
MasterAcnolo
909480083e Fix: Update Dependencies 2026-02-16 22:00:31 +01:00
MasterAcnolo
ca0492ed04 Fix: Update ReadME App Version 2026-02-16 21:37:24 +01:00
MasterAcnolo
6c73dd75be Feat: Add Paste BTN URL Input 2026-02-16 21:34:39 +01:00
MasterAcnolo
556dccec08 Fix: Increment App version 2026-02-16 21:29:24 +01:00
MasterAcnolo
f720d7c889 Merge pull request #30 from MasterAcnolo/fix
1.4.2
2026-02-06 22:55:43 +01:00
MasterAcnolo
bb23f37fb5 Fix: Update release badge to version 1.4.2 2026-02-06 22:38:19 +01:00
MasterAcnolo
220e81d8b2 Fix: 1.4.2 Version 2026-02-06 22:37:05 +01:00
MasterAcnolo
44d8772827 Fix: yt-dlp update 2026-02-06 22:23:31 +01:00
MasterAcnolo
335fb9652b Fix: Dependencies Vulnerability 2026-02-06 22:11:43 +01:00
MasterAcnolo
243b98cbf9 Merge branch 'fix' of https://github.com/MasterAcnolo/Freedom-Loader into fix 2026-02-06 22:01:01 +01:00
MasterAcnolo
23da75a17b 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-02-06 22:00:50 +01:00
MasterAcnolo
756d2c048e Update: YT-DLP 2026-02-05 16:05:33 +01:00
MasterAcnolo
659c1d4cf9 Update acknowledgments in README.md
Added acknowledgment for Deno and Jacques Chirac.
2026-02-03 17:48:59 +01:00
MasterAcnolo
443fd31170 Add: Additional dependencies section in README with required binaries 2026-02-03 14:01:59 +01:00
MasterAcnolo
6d8095ceb2 Mark completed tasks in the roadmap as strikethrough
Updated the roadmap to mark completed tasks as strikethrough.
2026-01-26 10:09:33 +01:00
MasterAcnolo
c18cb6c8aa Fix: ReadME 2026-01-25 21:34:40 +01:00
MasterAcnolo
a3c5025434 Fix: Bug Report Template 2026-01-25 21:31:35 +01:00
MasterAcnolo
3d7986cb87 Fix: Security 2026-01-25 21:28:23 +01:00
MasterAcnolo
3f8701567f Fix: Security, Contributing, Code Of Conduct 2026-01-25 21:27:28 +01:00
MasterAcnolo
ccb16c49d5 Feat: Templates for PR and Issues 2026-01-25 21:22:09 +01:00
MasterAcnolo
86d9ac3592 Update GitHub username for SpicyFire21 2026-01-25 21:08:33 +01:00
MasterAcnolo
43d87c2b47 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-01-25 21:04:38 +01:00
MasterAcnolo
19aa8ed8fa Revise README for improved structure and clarity
Updated README with new structure and content for clarity and organization.
2026-01-25 20:51:18 +01:00
MasterAcnolo
3ddfaa999e Fix: ReadME Version 1.4.1 2026-01-25 18:05:07 +01:00
MasterAcnolo
3d7f3b7481 1.4.1
1.4.1
2026-01-25 17:55:19 +01:00
MasterAcnolo
cfe2e17313 Add: App Version for 1.4.1 2026-01-25 17:45:14 +01:00
MasterAcnolo
dedf10c6b3 Fix: Discord RPC not Stoping #26 2026-01-25 17:32:29 +01:00
MasterAcnolo
a27b3555f6 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>
2025-12-27 11:39:30 +01:00
36 changed files with 1936 additions and 894 deletions

View File

@@ -1,22 +0,0 @@
---
name: "[FEAT] Feature Template"
about: Suggest an idea for this project
title: "[FEAT] Name of the features"
labels: enhancement
assignees: MasterAcnolo
---
## Check Before Requesting
Make sure this feature doesn't already exist in the issues.
## Feature Description
Describe the feature you want to add or improve.
## Why is this Feature Useful?
Explain the benefit for users or the project.
## Examples / Context
Provide an example or use case if possible.
## Screenshots / Mockups (if applicable)

View File

@@ -1,9 +1,9 @@
--- ---
name: "[BUG] Issues Template " name: "Bug Report"
about: Report something that doesn't work fine about: Report something that doesn't work fine
title: "[BUG] Name of the bug / issues" title: "[BUG]"
labels: bug labels: bug
assignees: MasterAcnolo assignees: "MasterAcnolo"
--- ---

View File

@@ -0,0 +1,44 @@
---
name: Feature Request
about: Suggest a new feature or enhancement
title: "[FEATURE] "
labels: enhancement
assignees: 'MasterAcnolo'
---
## Feature Description
A clear and concise description of the feature you'd like to see.
## Problem it Solves
Describe the problem or limitation this feature would address.
## Proposed Solution
Describe how you envision this feature working.
## Alternative Solutions
Have you considered any alternative approaches? Describe them here.
## Use Case
Provide specific examples of how you would use this feature.
## Implementation Considerations
Are there any technical challenges or dependencies you're aware of?
## Priority
How important is this feature to you?
- [ ] Critical - Blocking my workflow
- [ ] High - Would significantly improve my experience
- [ ] Medium - Nice to have
- [ ] Low - Minor improvement
## Additional Context
Add any other context, mockups, or screenshots about the feature request here.

View File

@@ -0,0 +1,56 @@
---
name: Performance Issue
about: Report slow downloads, high CPU/memory usage, or crashes
title: "[PERFORMANCE] "
labels: performance
assignees: MasterAcnolo
---
## Performance Issue Type
- [ ] Slow download speed
- [ ] High CPU usage
- [ ] High memory usage
- [ ] Application freeze/hang
- [ ] Application crash
- [ ] Other (specify below)
## Description
Clear description of the performance issue.
## System Information
- **Freedom Loader Version**: [e.g. 1.4.1]
- **Windows Version**: [e.g. Windows 11 22H2]
- **CPU**: [e.g. Intel i7-9700K]
- **RAM**: [e.g. 16GB]
- **Storage**: [e.g. SSD/HDD]
- **Other Applications Opened**: [e.g. Discord/Opera GX]
## Reproduction Details
- **Download URL**: [if applicable]
- **File Size/Length**: [e.g. 2GB / 2 hours]
- **Quality Setting**: [e.g. 1080p, Best]
- **Audio Only**: Yes / No
## Expected Performance
What performance did you expect?
## Actual Performance
What performance are you experiencing? (Include metrics if possible: download speed, CPU %, memory usage)
## Logs
Attach logs with verbose mode enabled if possible:
```
C:\Users\[USERNAME]\AppData\Local\FreedomLoader\logs\LOGS-YYYY-MM-DD.log
```
## Additional Context
Any other relevant information (network speed, antivirus software, other running applications).

30
.github/ISSUE_TEMPLATE/question.md vendored Normal file
View File

@@ -0,0 +1,30 @@
---
name: Question or Support
about: Ask a question about using Freedom Loader
title: "[QUESTION] "
labels: question
assignees: 'MasterAcnolo'
---
## Pre-submission Checklist
- [ ] I have checked the [FAQ](https://masteracnolo.github.io/FreedomLoader/pages/faq.html)
- [ ] I have reviewed the [Wiki](https://masteracnolo.github.io/FreedomLoader/pages/wiki.html)
- [ ] I have searched existing issues
## Question
What would you like to know?
## What I've Tried
Describe what you've already attempted to solve this yourself.
## Environment (if relevant)
- **Freedom Loader Version**: [e.g. 1.4.1]
## Additional Context
Any other information that might be helpful.

View File

@@ -0,0 +1,60 @@
## Description
Brief description of what this PR does.
## Related Issue
Fixes #(issue number)
Closes #(issue number)
Related to #(issue number)
## Type of Change
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Code refactoring (no functional changes)
- [ ] Documentation update
- [ ] Performance improvement
- [ ] UI/UX enhancement
## Changes Made
- Change 1
- Change 2
- Change 3
## Testing
- [ ] I have tested this code locally
- [ ] 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
## Screenshots (if applicable)
**Before:**
[Add screenshot]
**After:**
[Add screenshot]
## Checklist
- [ ] My code follows the project's code style
- [ ] I have performed a self-review of my code
- [ ] I have commented my code where necessary
- [ ] I have updated the documentation (if applicable)
- [ ] My changes generate no new warnings or errors
- [ ] I have not modified the version number (this will be done during release)
## Additional Notes
Any additional information or context about the PR.
---
**Note for Maintainer:** This is a contribution PR and should NOT include version bumps.

View File

@@ -1,29 +0,0 @@
## Description
Describe the feature or improvement you are adding.
Include why it's useful and what problem it solves.
## Type of Change
- [ ] New feature
- [ ] Improvement / Refactor
- [ ] Documentation update
## Checklist
- [ ] Code tested locally
- [ ] No linting errors
- [ ] Added or updated tests if relevant
- [ ] Updated documentation if relevant
## Verification Steps
Explain how reviewers can test the new feature or improvement.
## App Version
State the version of Freedom Loader you are targeting.
## Notes / Additional Info
Any context, screenshots, or logs that can help reviewing the PR.

View File

@@ -0,0 +1,10 @@
# Freedom Loader - 1.X.Y
## Changelog
-
## Found a bug or issue?
- Please report it in the GitHub Issues section
**Full Changelog**: https://github.com/MasterAcnolo/Freedom-Loader/compare/1.W.X...1.Y.Z

View File

@@ -1,25 +0,0 @@
# Freedom Loader - x.y.z
## Changelog
- List all changes made in this release (features, bug fixes, improvements)
- Use bullet points, keep it concise
- Example:
- `YT-DLP` Will now update when the application is launched
- Improved UI / UX
## Bug Reports / Issues
- Please report any bugs in the GitHub Issues section
## Next Release Roadmap (non-exhaustive)
- List upcoming features or improvements
- Example:
- More format options
- Subtitle support
- Language selection
- Parallel downloads
- Skip sponsored parts automatically
**Full Changelog**: https://github.com/MasterAcnolo/Freedom-Loader/compare/[previous-version]...[new-version]

View File

@@ -2,127 +2,110 @@
## Our Pledge ## Our Pledge
We as members, contributors, and leaders pledge to make participation in our We as members, contributors, and leaders pledge to make participation in the Freedom Loader community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming, We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
diverse, inclusive, and healthy community.
## Our Standards ## Our Standards
Examples of behavior that contributes to a positive environment for our Examples of behavior that contributes to a positive environment for our community include:
community include:
* Demonstrating empathy and kindness toward other people * Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences * Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback * Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes, * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
and learning from the experience * Focusing on what is best not just for us as individuals, but for the overall community
* Focusing on what is best not just for us as individuals, but for the * Supporting fellow contributors and helping newcomers feel welcome
overall community
Examples of unacceptable behavior include: Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or * The use of sexualized language or imagery, and sexual attention or advances of any kind
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks * Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment * Public or private harassment
* Publishing others' private information, such as a physical or email * Publishing others' private information, such as a physical or email address, without their explicit permission
address, without their explicit permission * Sustained disruption of discussions, issues, or pull requests
* Other conduct which could reasonably be considered inappropriate in a * Other conduct which could reasonably be considered inappropriate in a professional setting
professional setting
## Enforcement Responsibilities ## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of Community leaders and project maintainers are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope ## Scope
This Code of Conduct applies within all community spaces, and also applies when This Code of Conduct applies within all community spaces, including:
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address, - GitHub repository (issues, pull requests, discussions, wiki)
posting via an official social media account, or acting as an appointed - Project documentation and website
representative at an online or offline event. - Official communication channels (Discord, email, etc.)
- Social media accounts representing the project
- Project events and meetings (online or offline)
This Code of Conduct also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official email address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
## Enforcement ## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at **masteracnolo25@gmail.com**.
reported to the community leaders responsible for enforcement at
masteracnolo25@gmail.com.
All complaints will be reviewed and investigated promptly and fairly. All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the All community leaders are obligated to respect the privacy and security of the reporter of any incident.
reporter of any incident.
## Enforcement Guidelines ## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction ### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning ### 2. Warning
**Community Impact**: A violation through a single incident or series **Community Impact**: A violation through a single incident or series of actions.
of actions.
**Consequence**: A warning with consequences for continued behavior. No **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban ### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban ### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within **Consequence**: A permanent ban from any sort of public interaction within the community.
the community.
## Our Commitment to Contributors
At Freedom Loader, we value every contribution—whether it's a bug report, feature suggestion, code improvement, or documentation fix. We strive to:
- Respond to issues and pull requests in a timely manner
- Provide constructive feedback on contributions
- Help new contributors get started
- Maintain a welcoming environment for all skill levels
- Credit contributors appropriately for their work
## Questions and Clarifications
If you have questions about this Code of Conduct or need clarification on what constitutes acceptable behavior, please:
- Open a [discussion](https://github.com/MasterAcnolo/Freedom-Loader/discussions) on GitHub
- Contact the maintainers at masteracnolo25@gmail.com
- Review the [Contributing Guidelines](CONTRIBUTING.md)
## Attribution ## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.0, available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations.
For answers to common questions about this code of conduct, see the FAQ at ---
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations. **Remember**: Freedom Loader is built by people who care. Let's keep this community a place where everyone feels welcome to contribute.

View File

@@ -1,66 +1,164 @@
# Contributing to Freedom Loader # Contributing to Freedom Loader
Welcome! Thanks for being interested in Freedom Loader. Your contributions-bug reports, feature ideas, or code improvements-are always appreciated. Every contribution makes Freedom Loader better 💪 Welcome! Thanks for your interest in Freedom Loader. Your contributionsbug reports, feature ideas, or code improvementsare always appreciated. Every contribution makes Freedom Loader better.
--- ---
## 1. Check Before Contributing ## Quick Links
- Make sure your issue, bug, or feature request doesn't already exist.
- Search issues and pull requests before creating a new one. - [Code of Conduct](CODE_OF_CONDUCT.md)
- Follow the [Code of Conduct](CODE_OF_CONDUCT.md). - [Issue Templates](.github/ISSUE_TEMPLATE/)
- [Pull Request Templates](.github/PULL_REQUEST_TEMPLATE/)
- [Security Policy](SECURITY.md)
--- ---
## 2. Report an Issue ## Ways to Contribute
Found a bug, weird behavior, or have an idea? Open an [**issue**](https://github.com/MasterAcnolo/Freedom-Loader/issues) using the **Bug Report template**.
Make sure to include:
- Steps to reproduce the issue
- App version and environment (OS, Node, Browser, etc.)
- Logs if possible: `C:\Users\[USERNAME]\AppData\Local\FreedomLoader\logs\LOGS-20xx-xx-xx.log`
- Screenshots if relevant
--- ### 1. Report a Bug
## 3. Submit a Pull Request Found a bug or unexpected behavior? Open an [**issue**](https://github.com/MasterAcnolo/Freedom-Loader/issues) using the **Bug Report** template.
To fix a bug, add a feature, or improve docs:
**Please include:**
- 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`
- Screenshots if applicable
### 2. Request a Feature
Have an idea to improve Freedom Loader? Open a [**Feature Request**](https://github.com/MasterAcnolo/Freedom-Loader/issues/new/choose).
**Tips:**
- Describe the problem it solves
- Explain your proposed solution
- Include use cases and examples
### 3. Submit Code Changes
**Before starting:**
- Check existing issues and PRs to avoid duplicates
- For major changes, open an issue first to discuss
- Follow the project's code style and conventions
**Process:**
1. Fork the repository 1. Fork the repository
2. Create a branch: `git checkout -b feature/my-awesome-feature` 2. Create a feature branch: `git checkout -b feature/your-feature-name`
3. Make your changes 3. Make your changes
4. Add tests or verification steps 4. Test thoroughly on Windows 10/11
5. Test everything locally 5. Commit with clear messages: `git commit -m "Add feature X"`
6. Open a PR to the `main` branch 6. Push to your fork: `git push origin feature/your-feature-name`
7. Open a Pull Request using the appropriate template
**Tips for PRs:** **Code Guidelines:**
- Keep titles and descriptions clear and concise - Use camelCase for variables and functions
- Document what you change and why - Comment complex logic
- Follow existing code style (camelCase for variables/functions) - Keep commits focused and atomic
- Small commits focused on a single purpose are easier to review - Update documentation if needed
- Don't modify version numbers (done during release)
### 4. Improve Documentation
Documentation improvements are always welcome:
- Fix typos or unclear sections
- Add missing examples
- Update outdated information
- Improve README, wiki, or guides
Small contributions matter—don't hesitate to submit documentation PRs.
--- ---
## 4. Improve the Documentation ## Development Setup
Docs are important: README, wiki, guides… If something is unclear, improve it!
Small contributions like fixing typos or adding examples are always welcome. ### Prerequisites
- Node.js 16.x or higher
- npm or yarn
- Git
- Windows 10/11 (for testing)
### 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)
└── ressources/ # Binaries (yt-dlp, ffmpeg, etc.)
```
--- ---
## 5. Testing & Verification ## Testing
- Test new features thoroughly
- Ensure bug fixes actually fix the reported issue Before submitting a PR, verify:
- Include any relevant logs or screenshots - [ ] 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
--- ---
## 6. Review & Merge ## Pull Request Review Process
- After submitting a PR, it will be reviewed by maintainers
- Be open to feedback and changes 1. **Submission**: Open PR with clear description using the template
- We try to respond quickly, but sometimes have other priorities 2. **Review**: Maintainers review code and provide feedback
3. **Updates**: Address feedback and push updates
4. **Approval**: Once approved, PR will be merged
5. **Release**: Changes included in next release
**Response times:**
- Bug fixes: Usually reviewed within 2-3 days
- Features: May take longer depending on complexity
- Documentation: Often reviewed quickly
--- ---
## 7. Code of Conduct ## Code of Conduct
Please contribute respectfully and constructively. Everyone is welcome as long as discussions remain polite and productive.
Be respectful and constructive in all interactions. We welcome everyone as long as discussions remain polite and productive.
See [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) for details.
--- ---
Thanks to Zakaria for the website icon. Check him out here: [IG Link](https://www.instagram.com/designmark_studio/) 🔥 ## Questions?
- Check the [FAQ](https://masteracnolo.github.io/FreedomLoader/pages/faq.html)
- Review the [Wiki](https://masteracnolo.github.io/FreedomLoader/pages/wiki.html)
- Open a [Question issue](https://github.com/MasterAcnolo/Freedom-Loader/issues/new/choose)
---
## Credits
Thanks to **Zakaria** for the website icon design.
Check out his work: [IG @designmark_studio](https://www.instagram.com/designmark_studio/)
---
**Thank you for contributing to Freedom Loader!**

450
README.md
View File

@@ -1,150 +1,422 @@
<div align="center"> <div align="center">
<a href="https://masteracnolo.github.io/FreedomLoader/"><img <a href="https://masteracnolo.github.io/FreedomLoader/"><img
src="./build/banner.png" src="./build/banner.png"
alt="Banner" alt="Banner"
style="width: 50%;;"/></a> style="width: 50%;"/></a>
</div>
<div align="center"> ### **A clean, open-source multimedia downloader for Windows**
<a href="https://github.com/MasterAcnolo/Freedom-Loader/releases"><img src="https://img.shields.io/badge/Release-1.4.0-blue?style=for-the-badge"></a>
<a href="https://masteracnolo.github.io/FreedomLoader/index.html"><img src="https://img.shields.io/badge/Freedom%20Loader-Open%20Page-111111?style=for-the-badge&logo=terminal&logoColor=white"></a> [![Release](https://img.shields.io/badge/Release-1.4.4-blue?style=for-the-badge)](https://github.com/MasterAcnolo/Freedom-Loader/releases)
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-red.svg?style=for-the-badge)](https://www.gnu.org/licenses/gpl-3.0)
[![Website](https://img.shields.io/badge/Website-Visit-404040?style=for-the-badge)](https://masteracnolo.github.io/FreedomLoader/)
<a href="https://www.firefox.com/fr/?utm_campaign=SET_DEFAULT_BROWSER"><img src="https://img.shields.io/badge/Require Firefox-E66000?style=for-the-badge&logo=Firefox-Browser&logoColor=white"></a> <a href="https://www.firefox.com/fr/?utm_campaign=SET_DEFAULT_BROWSER"><img src="https://img.shields.io/badge/Require Firefox-E66000?style=for-the-badge&logo=Firefox-Browser&logoColor=white"></a>
</div>
<br>
Freedom Loader is a **Windows desktop** application built with **Electron**. </div>
It provides a simple and reliable way to download video or audio content with metadata and advanced options, without ads or questionable third-party services.
The goal is to offer a clean, user-friendly, and transparent download experience. It's important for me to make media content downloading accessible to people who love music or videos and dont always have an internet connection.
--- ---
## 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.
The primary goal is to make media downloading accessible to users who want offline access to their favorite content, particularly in situations where internet connectivity is unreliable or unavailable.
## Table of Contents
- [Features](#features)
- [Installation](#installation)
- [Usage](#usage)
- [Preview](#preview)
- [Configuration](#configuration)
- [Project Structure](#project-structure)
- [Technology Stack](#technology-stack)
- [Development](#development)
- [Roadmap](#roadmap)
- [Contributing](#contributing)
- [Support](#support)
- [License](#license)
## Features ## Features
- Video download with metadata (MP4) ### Core Functionality
- Audio-only download with thumbnail and tags (MP3)
- Full playlist support and better display in UI
- Automatic metadata retrieval (title, duration, author, etc.)
- Detailed logs (console + rotating files for 7 days)
- Progress Bar for download and network speed indicator
- Better UX when fetching info
- Custom output path with persistence
- Automatic dependency updates (YT-DLP)
- Automatic Application Update
- Multiple UI themes
- Simple, responsive interface
- Notifications on download completion (click to open folder)
- Discord Rich Presence
--- - **Multi-format downloads** - MP4 video with full metadata or MP3 audio with embedded artwork
- **Video information preview** - Fetch and display video metadata before downloading
- **Quality selection** - Choose from best, medium, lowest quality or specific resolutions (1080p, 720p, 480p)
- **Playlist support** - Download entire playlists with optional auto-download mode
- **Metadata embedding** - Automatic retrieval and embedding of title, artist, duration, and thumbnail
- **Video codec options** - Support for H.264, H.265, AV1, VP9, VP9.2, and Theora codecs
- **Custom output directory** - User-configurable download location with path validation
- **Cookie integration** - Firefox cookie support for accessing age-restricted and member-only content
### User Interface
- **Custom window controls** - Frameless window with custom top bar (optional)
- **Theme system** - Multiple pre-built themes with dynamic switching
- **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
- **Quick access toolbar** - Direct links to logs, website, wiki, and developer tools
### System Features
- **Automatic updates** - Electron-updater integration with GitHub releases
- **YT-DLP auto-update** - Keeps the download engine up-to-date on startup
- **Structured logging** - Winston-based logging with daily file rotation (7-day retention)
- **Discord Rich Presence** - Optional integration showing current download activity
- **Single instance enforcement** - Prevents multiple app instances from running simultaneously
- **Native dependency validation** - Startup checks for yt-dlp, FFmpeg, FFprobe, and Deno binaries
- **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)
## Installation ## Installation
Download the latest installer from the **Releases** page and run it (Defender could stop you but click on "Run Anyway" and it's good). ### Requirements
Actually, **Firefox Browser** is the only browser supported for get cookies. You should install it. Follow the tutorial in the wiki page if you need some help, Firefox Section. [Link](https://masteracnolo.github.io/FreedomLoader/pages/wiki.html)
--- - **Operating System**: Windows 10 or later
- **Browser**: Mozilla Firefox (required for cookie extraction)
- [Download Firefox](https://www.firefox.com)
## FAQ ### Standard Installation
### Where are my downloaded files stored? 1. Download the latest release from the [Releases page](https://github.com/MasterAcnolo/Freedom-Loader/releases)
2. Run the downloaded `.exe` installer
3. If Windows Defender shows a warning, click "More info" then "Run anyway"
4. Follow the installation wizard
In your **Downloads** folder, inside **Freedom Loader**. ### File Locations
Example path:
`C:\Users\[YOUR NAME]\Downloads\Freedom Loader`
### I get an error after launching the app Downloaded files are stored in:
```
C:\Users\[USERNAME]\Downloads\Freedom Loader
```
> [!NOTE]
> *You can change it whenever you want. Just click the "Edit" button in the UI.*
Try restarting the application, checking the logs, or opening the developer console: Application logs can be found at:
`CTRL + SHIFT + I` -> “Toggle Developer Tools”. ```
C:\Users\[USERNAME]\AppData\Local\FreedomLoader\logs\
```
<p align="center"> ## Usage
<img src="build/example-developertools.png" width="20%" />
</p>
--- ### Basic Download
## Roadmap 1. Launch Freedom Loader
2. Paste a video or playlist URL into the input field
3. Select the desired format (Video MP4 or Audio MP3)
4. Click the download button
5. Monitor progress in real-time
6. Receive a notification when the download completes
- [x] ~~Website for Freedom Loader~~ ### Advanced Configuration
- [ ] More format options
- [ ] Linux version
- [x] ~~Auto-updating YT-DLP~~
- [x] ~~Automatic project updates~~
- [x] ~~Better download status~~
- [ ] Better website support (currently only YouTube is fully supported, other site could work but i can't prove it)
- [ ] Subtitle support
- [x] ~~UI/UX improvements~~
- [ ] Language selection
- [ ] Download specific parts of a video
- [ ] Custom file naming
- [ ] Parallel downloads
- [ ] Automatic sponsor skipping
- [x] ~~Extract/split video using native chapters~~
- [x] ~~Custom output path selection~~
--- The application supports various configuration options:
- Custom download directory selection
- Discord Rich Presence activation
- Theme customization
- Log level configuration
### Troubleshooting
If you encounter issues:
1. Restart the application
2. Check the logs in `AppData\Local\FreedomLoader\logs\`
3. Open the developer console: `CTRL + SHIFT + I` → "Toggle Developer Tools"
4. Create an issue on GitHub with logs and reproduction steps
## Preview ## Preview
<p align="center"> <div align="center">
<img src="build/apercu1.2.4.png" width="40%" /> <table>
</p> <tr>
<td width="50%" align="center">
<img src="https://github.com/user-attachments/assets/d4de9079-7c82-44d3-ba22-8246bb467db3" alt="Download Preview" width="75%" />
<p><em>Real-time download progress with network speed indicator</em></p>
</td>
<td width="50%" align="center">
<img src="https://github.com/user-attachments/assets/d59aff95-6754-486a-b880-d70730146dc3" alt="Settings Panel" width="40%" />
<p><em>Settings panel with theme and feature customization</em></p>
</td>
</tr>
</table>
</div>
--- ## Configuration
## Technologies Freedom Loader can be configured either through the settings panel in the UI or by directly editing the `config.json` file located in the application data directory.
This project uses: ### Configuration File Location
```
C:\Users\[USERNAME]\AppData\Roaming\FreedomLoader\config.json
```
<p> ### Available Options
<a href="https://github.com/yt-dlp/yt-dlp"><img src="https://img.shields.io/badge/yt--dlp-Visit%20Repo-cf0000?style=for-the-badge&logo=github&logoColor=white"></a> ```json
<a href="https://github.com/FFmpeg/FFmpeg"><img src="https://img.shields.io/badge/FFmpeg-Visit%20Repo-666666?style=for-the-badge&logo=ffmpeg&logoColor=white"></a> {
<a href="https://nodejs.org/"><img src="https://img.shields.io/badge/Node.js-Visit%20Repo-339933?style=for-the-badge&logo=node.js&logoColor=white"></a> "autoUpdate": true,
<a href="https://github.com/electron/electron"><img src="https://img.shields.io/badge/Electron-Visit%20Repo-47848F?style=for-the-badge&logo=electron&logoColor=white"></a> "discordRPC": true,
<a href="https://github.com/denoland/deno/"><img src="https://img.shields.io/badge/Deno-Visit%20Repo-14e0cc?style=for-the-badge&logo=deno&logoColor=white"></a> "customTopBar": true,
</p> "autoCheckInfo": true,
"addThumbnail": true,
"addMetadata": true,
"verboseLogs": false,
"autoDownloadPlaylist": true,
"customCodec": "h264",
"logSystem": true,
"outputTitleCheck": true,
"downloadSystem": true,
"notifySystem": true
}
```
--- ### Configuration Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `autoUpdate` | boolean | `true` | Enable automatic application updates |
| `discordRPC` | boolean | `true` | Enable Discord Rich Presence integration |
| `customTopBar` | boolean | `true` | Use custom application top bar |
| `autoCheckInfo` | boolean | `true` | Automatically fetch video information on URL paste |
| `addThumbnail` | boolean | `true` | Embed thumbnail in downloaded audio files |
| `addMetadata` | boolean | `true` | Add metadata tags to downloaded files |
| `verboseLogs` | boolean | `false` | Enable detailed logging for debugging |
| `autoDownloadPlaylist` | boolean | `true` | Automatically download entire playlists |
| `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 |
| `downloadSystem` | boolean | `true` | Enable download system |
| `notifySystem` | boolean | `true` | Enable system notifications |
### Supported Codecs
- `h264` - H.264/AVC (widely compatible, recommended default)
- `h265` - H.265/HEVC (better compression, less compatible)
- `av1` - AV1 (modern codec, excellent compression)
- `av01` - AV1 variant
- `vp9` - VP9 (open format, good quality)
- `vp9.2` - VP9 Profile 2 (HDR support)
- `theora` - Theora (legacy open codec)
> [!NOTE]
> Configuration changes may require an application restart to take effect.
## Project Structure
```
Freedom-Loader/
├── build/ # Build resources and assets
├── config/ # Configuration files
├── public/ # Frontend assets (HTML, CSS, JavaScript)
├── ressources/ # Internal resources (icons, binaries)
├── server/ # Express server code
│ ├── routes/ # API route handlers
│ ├── services/ # Business logic
│ └── utils/ # Server utilities
├── .github/ # GitHub configuration and workflows
├── main.js # Electron main process
├── preload.js # Electron preload script
├── config.js # Global application configuration
└── package.json # Dependencies and npm scripts
```
### Architecture Overview
Freedom Loader uses a client-server architecture within a single Electron application:
- **Frontend**: HTML/CSS/JavaScript served via Electron's renderer process
- **Backend**: Express.js server running locally for download management
- **IPC Bridge**: Secure communication via Electron's preload script
- **Logging**: Winston-based structured logging with file rotation
- **Updates**: Automatic checking and installation via electron-updater
## Technology Stack
### Core Technologies
[![yt-dlp](https://img.shields.io/badge/yt--dlp-cf0000?style=for-the-badge&logo=youtube&logoColor=white)](https://github.com/yt-dlp/yt-dlp)
[![FFmpeg](https://img.shields.io/badge/FFmpeg-007808?style=for-the-badge&logo=ffmpeg&logoColor=white)](https://ffmpeg.org/)
[![Electron](https://img.shields.io/badge/Electron-47848F?style=for-the-badge&logo=electron&logoColor=white)](https://www.electronjs.org/)
[![Node.js](https://img.shields.io/badge/Node.js-339933?style=for-the-badge&logo=node.js&logoColor=white)](https://nodejs.org/)
### Key Dependencies
- **Electron**: Cross-platform desktop application framework
- **Express**: Web framework for local HTTP server
- **yt-dlp**: Media download engine
- **FFmpeg**: Media processing and conversion
- **Winston**: Logging library
- **discord-rpc**: Discord Rich Presence integration
- **electron-updater**: Automatic application updates
## 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
```
### Additional Dependencies
This project relies on several external binaries.
Some of them are **not included** in the public repository and must be added manually.
You must download the required binaries and place them in the `ressources` folder, using the exact filenames listed below.
#### 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:
```
ressources/
├── 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.
### 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+
## Roadmap
### Completed
- [x] ~~Website and documentation~~
- [x] ~~Automatic YT-DLP updates~~
- [x] ~~Automatic application updates~~
- [x] ~~Enhanced download status display~~
- [x] ~~Video chapter extraction~~
- [x] ~~Custom output path selection~~
- [x] ~~Custom codec selection~~
- [x] ~~Settings Panel with toggle switch for features.~~
### In Progress
- [ ] Additional format support (WebM, OGG, etc.)
- [ ] Refactoring and cleaning the code base.
- [ ] Chrome/Edge cookie support
- [ ] Improved multi-site support
### Planned
- [ ] Subtitle download and embedding
- [ ] Multiple language support
- [ ] Partial video downloads
- [ ] Custom file naming patterns
- [ ] Parallel downloads
- [ ] Automatic sponsor detection (SponsorBlock integration)
- [ ] Download history
- [ ] Linux version
## Contributing ## Contributing
Contributions are welcome. Contributions are welcome and appreciated. This project benefits from community involvement.
### Bug Reports ### Bug Reports
Use the GitHub Issues system and include reproduction steps and logs when possible. Use the GitHub Issues system and include:
Logs can be found here:
`C:\Users\[USERNAME]\AppData\Local\FreedomLoader\logs\LOGS-20xx-xx-xx.log` - Clear description of the issue
- Steps to reproduce
- Expected vs actual behavior
- Relevant logs from `AppData\Local\FreedomLoader\logs\`
- Screenshots if applicable
### Feature Requests ### Feature Requests
Open a feature request issue with a clear description and use cases. Open a feature request issue with:
- Clear description of the proposed feature
- Use cases and benefits
- Potential implementation approach (if applicable)
### Pull Requests ### Pull Requests
Fork the repository, make your changes, and submit a PR once everything is tested and clean. 1. Fork the repository
2. Create a feature branch (`git checkout -b feature/your-feature`)
3. Make your changes with clear, descriptive commits
4. Test thoroughly
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 the Project ## Support
If you'd like to support development: If you find this project useful and want to support its development:
<a href="https://paypal.me/axelnicolas25"> [![PayPal](https://img.shields.io/badge/PayPal-00457C?style=for-the-badge&logo=paypal&logoColor=white)](https://paypal.me/axelnicolas25)
<img src="https://img.shields.io/badge/PayPal-00457C?style=for-the-badge&logo=paypal&logoColor=white" />
</a>
--- Your support helps maintain infrastructure, develop new features, and keep the project free and ad-free.
## License ## License
Freedom Loader is released under the **GNU General Public License v3.0 (GPLv3)**. Freedom Loader is released under the **GNU General Public License v3.0 (GPLv3)**.
You are free to use, modify, and redistribute the software under the terms of this license.
See the full license in the [LICENSE](./LICENSE) file. You are free to use, modify, and redistribute this software under the terms of the GPL-3.0 license. See the [LICENSE](LICENSE) file for complete details.
## Acknowledgments
- [yt-dlp](https://github.com/yt-dlp/yt-dlp) for the robust download engine
- [FFmpeg](https://ffmpeg.org/) for media processing capabilities
- [Deno](https://deno.com/) for helping me bypassing challenges
- [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
- Jacques Chirac to love Apples
- All users who test, report issues, and help improve the application
--- ---
## Note <div align="center">
AI assistance was used occasionally to speed up development, but all code and decisions were reviewed and validated manually. **Freedom Loader** - Put freedom in your downloads
[Website](https://masteracnolo.github.io/FreedomLoader/) • [Download](https://github.com/MasterAcnolo/Freedom-Loader/releases) • [Documentation](https://masteracnolo.github.io/FreedomLoader/pages/wiki.html) • [Report Bug](https://github.com/MasterAcnolo/Freedom-Loader/issues)
</div>

View File

@@ -2,39 +2,156 @@
## Supported Versions ## Supported Versions
We actively maintain security updates for the following versions (Since the 1.3.0 the application auto-update, patch will be apply easily): We actively maintain and provide security updates for the following versions of Freedom Loader:
| Version | Supported | | Version | Supported |
| -------- | ----------------- | | ------- | ------------------ |
| 1.4.x | :white_check_mark: |
| 1.3.x | :white_check_mark: | | 1.3.x | :white_check_mark: |
| 1.2.x | :white_check_mark: | | < 1.3.0 | :x: |
| 1.1.x | :white_check_mark:|
| <1.1 | :white_check_mark: |
> Only versions marked with ✅ receive security updates. Older versions may be vulnerable. **Note**: We recommend always using the latest version to benefit from the most recent security patches and features.
---
## Reporting a Vulnerability ## Reporting a Vulnerability
If you discover a security vulnerability in Freedom Loader, **do not create a public issue**. Report it privately: If you discover a security vulnerability in Freedom Loader, please help us keep the project and its users safe by reporting it responsibly.
- Email: **masteracnolo25@gmail.com** ### How to Report
- Include:
**DO NOT** open a public issue for security vulnerabilities.
Instead, please report security issues privately by:
1. **Email**: Send details to **masteracnolo25@gmail.com** with the subject line: `[SECURITY] Vulnerability Report`
2. **Include**:
- Description of the vulnerability - Description of the vulnerability
- Steps to reproduce it - Steps to reproduce the issue
- App version and environment (OS, Node, Browser if relevant) - Potential impact and severity
- Logs or screenshots if possible (`C:\Users\[USERNAME]\AppData\Local\FreedomLoader\logs\LOGS-20xx-xx-xx.log`) - Suggested fix (if available)
- Your contact information for follow-up
We will respond as quickly as possible, typically within **48 hours**, and keep you updated on the fix. ### What to Expect
We may acknowledge responsible disclosures in release notes unless you request anonymity. - **Acknowledgment**: We will acknowledge receipt of your report within 48 hours
- **Assessment**: We will assess the vulnerability and determine its severity
- **Updates**: We will keep you informed of our progress
- **Resolution**: We aim to release a fix within 7-14 days for critical vulnerabilities
- **Credit**: We will credit you in the release notes (unless you prefer to remain anonymous)
## Security Best Practices for Users
When using Freedom Loader, we recommend:
### General Security
- **Keep Updated**: Always use the latest version of Freedom Loader
- **Official Sources**: Download only from official releases on [GitHub](https://github.com/MasterAcnolo/Freedom-Loader/releases)
- **Verify Downloads**: Check that installers are properly signed (Windows SmartScreen may show warnings for new releases)
- **Antivirus**: Keep your antivirus software up to date
### Configuration Security
- **Download Path**: Only set download paths within your user directory (`C:\Users\[USERNAME]\...`)
- **Cookies**: Be aware that Firefox cookies are used for authenticationkeep Firefox secure
- **Logs**: Logs may contain sensitive informationavoid sharing them publicly without review
### Privacy Considerations
Freedom Loader respects your privacy:
- **No Data Collection**: We don't collect, store, or transmit your personal data
- **No Telemetry**: No usage tracking or analytics
- **Local Operation**: All downloads are processed locally on your machine
- **Optional Features**: Discord RPC is optional and can be disabled
## Known Security Considerations
### Browser Cookie Access
Freedom Loader accesses Firefox cookies to download protected content. This is:
- **By Design**: Required for age-restricted or member-only content
- **Local Only**: Cookies are read locally and never transmitted
- **User Controlled**: You can control what content you download
### Native Dependencies
Freedom Loader bundles native binaries:
- **yt-dlp**: Official builds from [yt-dlp/yt-dlp](https://github.com/yt-dlp/yt-dlp)
- **FFmpeg**: Official builds from [FFmpeg.org](https://ffmpeg.org/)
- **Deno**: Official builds from [Deno.land](https://deno.land/)
These dependencies are verified and updated regularly.
### Windows Defender Warnings
New releases may trigger Windows Defender warnings because:
- The application is not yet widely distributed
- Code signing certificates are expensive for open-source projects
This is expected behavior for new releases. The warning will decrease as more users download the software.
## Vulnerability Disclosure Policy
### Our Commitment
We are committed to:
- Responding promptly to security reports
- Working with security researchers to verify and address issues
- Keeping users informed about security updates
- Crediting researchers who report vulnerabilities responsibly
### Disclosure Timeline
1. **Day 0**: Vulnerability reported privately
2. **Day 1-2**: Acknowledgment sent to reporter
3. **Day 3-7**: Vulnerability assessed and fix developed
4. **Day 7-14**: Fix released (critical vulnerabilities prioritized)
5. **Day 14+**: Public disclosure (coordinated with reporter)
### Scope
Security issues we're most interested in:
- **Code Execution**: Arbitrary code execution vulnerabilities
- **Path Traversal**: Issues with file system access controls
- **Injection**: Command injection or similar vulnerabilities
- **Authentication**: Bypass of security controls
- **Data Exposure**: Unintended exposure of sensitive data
### Out of Scope
The following are generally not considered security vulnerabilities:
- Issues requiring physical access to the user's machine
- Social engineering attacks
- Denial of service against third-party services
- Issues in third-party dependencies (report to upstream projects)
- Missing security headers on local HTTP server (no remote access)
## Security Updates
Security updates are released as:
- **Patch Releases**: For critical security fixes (e.g., 1.4.1 1.4.2)
- **Automatic Updates**: Users are notified via the built-in update system
- **Release Notes**: Security fixes are clearly marked in changelog
## Additional Resources
- [Contributing Guidelines](CONTRIBUTING.md)
- [Code of Conduct](CODE_OF_CONDUCT.md)
- [GitHub Security Advisories](https://github.com/MasterAcnolo/Freedom-Loader/security/advisories)
## Contact
For security-related questions or concerns:
- **GitHub**: [@MasterAcnolo](https://github.com/MasterAcnolo)
--- ---
## What Happens Next **Thank you for helping keep Freedom Loader and its users safe!**
- We will confirm receipt of your report
- Investigate and reproduce the issue
- Deploy a fix and release an update
- Notify you when the fix is live

86
main.js
View File

@@ -7,7 +7,7 @@ const fs = require("fs");
const { logger, logSessionStart, logSessionEnd, logDir } = require("./server/logger"); const { logger, logSessionStart, logSessionEnd, logDir } = require("./server/logger");
const { AutoUpdater } = require("./server/update.js"); const { AutoUpdater } = require("./server/update.js");
const { configFeatures } = require("./config.js"); const { configFeatures } = require("./config.js");
const { startRPC } = require("./server/discordRPC"); const { startRPC, stopRPC} = require("./server/discordRPC");
let mainWindow; let mainWindow;
const logsFolderPath = logDir; const logsFolderPath = logDir;
@@ -18,7 +18,8 @@ const basePath = config.localMode
const configFolderPath = path.join(basePath, "config" ,"config.json"); const configFolderPath = path.join(basePath, "config" ,"config.json");
const defaultDownloadPath = path.join(os.homedir(), "Downloads", "Freedom Loader"); // Default download path (centralized, lazy loaded)
let defaultDownloadPath;
app.setAppUserModelId("com.masteracnolo.freedomloader"); // pour notifications Windows app.setAppUserModelId("com.masteracnolo.freedomloader"); // pour notifications Windows
app.disableHardwareAcceleration(); app.disableHardwareAcceleration();
@@ -30,11 +31,14 @@ const gotLock = app.requestSingleInstanceLock();
// Native dependencies check (yt-dlp.exe, ffmpeg.exe, ffprobe.exe, Deno) // Native dependencies check (yt-dlp.exe, ffmpeg.exe, ffprobe.exe, Deno)
function checkNativeDependencies() { function checkNativeDependencies() {
// Import centralized paths after app initialization
const { binaryPaths } = require("./server/helpers/path");
const deps = [ const deps = [
{ name: "yt-dlp.exe", path: path.join(process.resourcesPath, "binaries","yt-dlp.exe") }, { name: "yt-dlp.exe", path: binaryPaths.ytDlp },
{ name: "ffmpeg.exe", path: path.join(process.resourcesPath, "binaries", "ffmpeg.exe") }, { name: "ffmpeg.exe", path: binaryPaths.ffmpeg },
{ name: "ffprobe.exe", path: path.join(process.resourcesPath, "binaries", "ffprobe.exe") }, { name: "ffprobe.exe", path: binaryPaths.ffprobe },
{ name: "deno.exe", path: path.join(process.resourcesPath, "binaries", "deno.exe") }, { name: "deno.exe", path: binaryPaths.deno },
]; ];
const missing = deps.filter(dep => !fs.existsSync(dep.path)); const missing = deps.filter(dep => !fs.existsSync(dep.path));
let errorMsg = ""; let errorMsg = "";
@@ -110,36 +114,52 @@ async function createMainWindow() {
} }
function validateDownloadPath(userPath) { function validateDownloadPath(userPath) {
const userHome = os.homedir(); // C:\Users\<User> const { isSafePath } = require("./server/helpers/validation");
if (!userPath) return path.join(userHome, "Downloads", "Freedom Loader"); // Lazy load default path
if (!defaultDownloadPath) {
const { defaultDownloadFolder } = require("./server/helpers/path");
defaultDownloadPath = defaultDownloadFolder;
}
// Résolution canonique et suivi des symlinks if (!userPath) return defaultDownloadPath;
try {
// Canonical resolution and symlink following
const resolved = fs.realpathSync(path.resolve(userPath)); const resolved = fs.realpathSync(path.resolve(userPath));
const normalizedHome = path.resolve(userHome) + path.sep;
if (!resolved.startsWith(normalizedHome)) { // Use the same validation as backend (allows all drives except system folders)
throw new Error("Chemin non autorisé : uniquement les sous-dossiers du dossier utilisateur sont permis !"); if (!isSafePath(resolved)) {
throw new Error("Path not allowed: system folders are blocked!");
} }
return resolved; return resolved;
} catch (err) {
logger.error(`Invalid download path: ${userPath} - ${err.message}`);
throw new Error(`Invalid or inaccessible path: ${err.message}`);
}
} }
// IPC // IPC
ipcMain.handle("select-download-folder", async () => { ipcMain.handle("select-download-folder", async () => {
try {
const result = await dialog.showOpenDialog({ properties: ["openDirectory"] }); const result = await dialog.showOpenDialog({ properties: ["openDirectory"] });
if (!result.canceled && result.filePaths.length > 0) { if (result.canceled) {
const validatedPath = validateDownloadPath(result.filePaths[0]); logger.info("Folder selection cancelled by user");
logger.info(`Folder Checked and Valid : ${validatedPath}`); return null;
}
if (result.filePaths.length > 0) {
const selectedPath = result.filePaths[0];
try {
const validatedPath = validateDownloadPath(selectedPath);
logger.info(`Folder selected and validated: ${validatedPath}`);
return validatedPath; return validatedPath;
}
return null;
} catch (err) { } catch (err) {
logger.error(`An Error Occured when validating folder : ${err.message}`); logger.warn(`Unsafe or invalid folder rejected: ${err.message}`);
return null; throw err; // Propagate error to UI
} }
}
return null;
}); });
ipcMain.handle("validate-download-path", (event, userPath) => { ipcMain.handle("validate-download-path", (event, userPath) => {
@@ -147,7 +167,13 @@ ipcMain.handle("validate-download-path", (event, userPath) => {
}); });
ipcMain.handle("get-default-download-path", () => defaultDownloadPath); ipcMain.handle("get-default-download-path", () => {
if (!defaultDownloadPath) {
const { defaultDownloadFolder } = require("./server/helpers/path");
defaultDownloadPath = defaultDownloadFolder;
}
return defaultDownloadPath;
});
ipcMain.on("set-progress", (event, percent) => { ipcMain.on("set-progress", (event, percent) => {
if (mainWindow) mainWindow.setProgressBar(percent / 100); // Electron attend 0 → 1 if (mainWindow) mainWindow.setProgressBar(percent / 100); // Electron attend 0 → 1
@@ -157,6 +183,8 @@ ipcMain.on("set-progress", (event, percent) => {
ipcMain.on("window-minimize", () => { ipcMain.on("window-minimize", () => {
if (mainWindow) mainWindow.minimize(); if (mainWindow) mainWindow.minimize();
}); });
// Toggle Maximize -> UnMaximize
ipcMain.on("window-maximize", () => { ipcMain.on("window-maximize", () => {
if (mainWindow) { if (mainWindow) {
if (mainWindow.isMaximized()) { if (mainWindow.isMaximized()) {
@@ -166,6 +194,7 @@ ipcMain.on("window-maximize", () => {
} }
} }
}); });
ipcMain.on("window-close", () => { ipcMain.on("window-close", () => {
if (mainWindow) mainWindow.close(); if (mainWindow) mainWindow.close();
}); });
@@ -248,10 +277,11 @@ app.whenReady().then(async () => {
}); });
configFeatures.discordRPC ? startRPC() : ""; if (configFeatures.discordRPC) startRPC(); // Discord RPC
await createMainWindow(); await createMainWindow();
configFeatures.autoUpdate ? AutoUpdater(mainWindow) : ""; // Auto Update
if (configFeatures.autoUpdate) AutoUpdater(mainWindow); // Auto Update
} catch (err) { } catch (err) {
logger.error("Window or Server error :", err); logger.error("Window or Server error :", err);
@@ -260,8 +290,12 @@ app.whenReady().then(async () => {
}); });
app.on("window-all-closed", () => { app.on("window-all-closed", () => {
logger.info("All Window Closed, shuting down app"); logger.info("Shuting Down App...");
if (process.platform !== "darwin") app.quit(); app.quit();
}); });
app.on("before-quit", () => logSessionEnd()); app.on("before-quit", async () => {
await stopRPC();
logger.info("All Services Stopped. Have a nice day!")
logSessionEnd();
});

850
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -206,7 +206,14 @@
</header> </header>
<form action="/download" method="POST" id="downloadForm"> <form action="/download" method="POST" id="downloadForm">
<input name="url" placeholder="URL" id="UrlInput"> <div class="url-input-container">
<input type="text" name="url" placeholder="URL" id="UrlInput">
<button type="button" id="pasteBtn" class="paste-btn" title="Paste from clipboard">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9 5H7C5.89543 5 5 5.89543 5 7V19C5 20.1046 5.89543 21 7 21H17C18.1046 21 19 20.1046 19 19V7C19 5.89543 18.1046 5 17 5H15M9 5C9 6.10457 9.89543 7 11 7H13C14.1046 7 15 6.10457 15 5M9 5C9 3.89543 9.89543 3 11 3H13C14.1046 3 15 3.89543 15 5M12 12H15M12 16H15M9 12H9.01M9 16H9.01" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
</div>
<label class="checkbox-inline"> <label class="checkbox-inline">
@@ -263,6 +270,7 @@
<!-- Scripts --> <!-- Scripts -->
<script src="script/clipboardPaste.js"></script>
<script src="script/custompath.js"></script> <script src="script/custompath.js"></script>
<script src="script/settingsPanel.js"></script> <script src="script/settingsPanel.js"></script>
<script src="script/appVersion.js"></script> <script src="script/appVersion.js"></script>

View File

@@ -1,5 +1,7 @@
async function versionLabel(){ async function versionLabel(){
const appVersion = await window.electronAPI.getVersion(); const appVersion = await window.electronAPI.getVersion();
// Write in front the app version for debugging (bottom right)
document.getElementById("version-badge").textContent = `v${appVersion}`; document.getElementById("version-badge").textContent = `v${appVersion}`;
}; };

View File

@@ -0,0 +1,35 @@
// Clipboard paste functionality for URL input
document.addEventListener('DOMContentLoaded', () => {
const pasteBtn = document.getElementById('pasteBtn');
const urlInput = document.getElementById('UrlInput');
if (pasteBtn && urlInput) {
pasteBtn.addEventListener('click', async () => {
try {
// Read text from clipboard
const text = await navigator.clipboard.readText();
// Paste into the URL input
urlInput.value = text;
// Trigger input event to ensure any listeners are notified
urlInput.dispatchEvent(new Event('input', { bubbles: true }));
// Optional: Focus the input
urlInput.focus();
// Visual feedback
pasteBtn.style.backgroundColor = 'var(--form-button-bg-hover-color)';
setTimeout(() => {
pasteBtn.style.backgroundColor = '';
}, 200);
} catch (err) {
console.error('Failed to read clipboard:', err);
// Fallback: prompt user if clipboard API fails
alert('Unable to access clipboard. Please paste manually using Ctrl+V.');
}
});
}
});

View File

@@ -45,18 +45,15 @@ window.addEventListener("DOMContentLoaded", async () => {
.getElementById("changePath") .getElementById("changePath")
.addEventListener("click", async () => { .addEventListener("click", async () => {
try { try {
const selectedPath = // selectDownloadFolder already returns a validated path
const validatedPath =
await window.electronAPI.selectDownloadFolder(); await window.electronAPI.selectDownloadFolder();
if (!selectedPath) return; // annulé if (!validatedPath) return; // cancelled
// Validation back obligatoire
const validatedPath =
await window.electronAPI.getValidatedDownloadPath(selectedPath);
await applyPathFromBack(validatedPath); await applyPathFromBack(validatedPath);
} catch (err) { } catch (err) {
alert("Dossier non autorisé."); alert("This folder is not allowed. Only specific folders (Users, Downloads, Documents) are authorized for downloads.");
} }
}); });
}); });

View File

@@ -78,9 +78,27 @@ async function init() {
if (data.type === "playlist") { if (data.type === "playlist") {
infoDiv.classList.add("playlist-mode"); infoDiv.classList.add("playlist-mode");
infoDiv.innerHTML = ` infoDiv.innerHTML = `
<h3 style="color:var(--video-info-heading-color);"><strong>Playlist Detected: ${data.title}</strong></h3> <div class="playlist-header">
<h3 style="color:var(--video-info-heading-color);"><strong>Video Count: ${data.count}</strong></h3> <div class="playlist-info">
<p><strong>Channel :</strong> ${data.channel || "Unknown"}</p> <div class="playlist-badge">Detected Playlist</div>
<h3 class="playlist-title">${data.title}</h3>
<div class="playlist-meta">
<span class="playlist-count">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 6H20M4 12H20M4 18H11" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>
${data.count} vidéos
</span>
${data.channel ? `<span class="playlist-channel">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="8" r="4" stroke="currentColor" stroke-width="2"/>
<path d="M5 20C5 16.134 8.134 13 12 13C15.866 13 19 16.134 19 20" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>
${data.channel}
</span>` : ''}
</div>
</div>
</div>
<div id="playlistVideos"></div> <div id="playlistVideos"></div>
`; `;
@@ -88,39 +106,58 @@ async function init() {
data.videos.forEach(v => { data.videos.forEach(v => {
const durationStr = v.duration const durationStr = v.duration
? `${Math.floor(v.duration / 60)}m ${(v.duration % 60).toString().padStart(2,"0")}s` : "Inconnue"; ? `${Math.floor(v.duration / 60)}:${(v.duration % 60).toString().padStart(2,"0")}` : "--:--";
const videoUrl = v.id ? `https://www.youtube.com/watch?v=${v.id}` : v.url; const videoUrl = v.id ? `https://www.youtube.com/watch?v=${v.id}` : v.url;
listDiv.innerHTML += ` listDiv.innerHTML += `
<div style="margin-bottom:12px;"> <div class="playlist-video-card">
<img src="${v.thumbnail}" width="160" alt="Thumbnail"> <div class="video-thumbnail-wrapper">
<p><strong>${v.title}</strong></p> <img src="${v.thumbnail}" alt="${v.title}" class="video-thumbnail">
<p>Duration : ${durationStr}</p> <span class="video-duration">${durationStr}</span>
<p><a href="${videoUrl}" target="_blank">URL</a> </div>
<button class="copy-btn" data-url="${videoUrl}">📋</button> <div class="video-content">
</p> <h4 class="video-title" title="${v.title}">${v.title}</h4>
${v.uploader ? `<p class="video-uploader">${v.uploader}</p>` : ''}
<div class="video-actions">
<a href="${videoUrl}" target="_blank" class="video-link">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>
Ouvrir
</a>
<button class="copy-btn" data-url="${videoUrl}" title="Copier l'URL">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="8" y="8" width="12" height="12" rx="2" stroke="currentColor" stroke-width="2"/>
<path d="M16 8V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h2" stroke="currentColor" stroke-width="2"/>
</svg>
</button>
</div>
</div>
</div> </div>
`; `;
}); });
// Gestion du bouton copier // Gestion du bouton copier
listDiv.addEventListener("click", (event) => { listDiv.addEventListener("click", (event) => {
if (event.target.classList.contains("copy-btn")) { if (event.target.classList.contains("copy-btn") || event.target.closest(".copy-btn")) {
const btn = event.target; const btn = event.target.closest(".copy-btn") || event.target;
if (btn.disabled) return; if (btn.disabled) return;
btn.disabled = true; btn.disabled = true;
const url = btn.dataset.url; const url = btn.dataset.url;
navigator.clipboard.writeText(url) navigator.clipboard.writeText(url)
.then(() => { .then(() => {
const original = btn.textContent; const original = btn.innerHTML;
btn.style.opacity = 0; btn.style.opacity = 0;
btn.style.transform = "scale(0.7)"; btn.style.transform = "scale(0.7)";
setTimeout(() => { setTimeout(() => {
btn.textContent = "✅"; btn.innerHTML = `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5 13l4 4L19 7" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>`;
btn.style.opacity = 1; btn.style.opacity = 1;
btn.style.transform = "scale(1)"; btn.style.transform = "scale(1)";
@@ -129,7 +166,7 @@ async function init() {
btn.style.transform = "scale(0.7)"; btn.style.transform = "scale(0.7)";
setTimeout(() => { setTimeout(() => {
btn.textContent = original; btn.innerHTML = original;
btn.style.opacity = 1; btn.style.opacity = 1;
btn.style.transform = "scale(1)"; btn.style.transform = "scale(1)";
btn.disabled = false; btn.disabled = false;
@@ -140,10 +177,12 @@ async function init() {
}, 300); }, 300);
}) })
.catch(() => { .catch(() => {
const original = btn.textContent; const original = btn.innerHTML;
btn.textContent = "❌"; btn.innerHTML = `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18 6L6 18M6 6l12 12" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"/>
</svg>`;
setTimeout(() => { setTimeout(() => {
btn.textContent = original; btn.innerHTML = original;
btn.disabled = false; btn.disabled = false;
}, 1500); }, 1500);
}); });

View File

@@ -62,6 +62,7 @@ form#downloadForm {
transition: border-color 0.3s ease, box-shadow 0.3s ease; transition: border-color 0.3s ease, box-shadow 0.3s ease;
background-color: var(--form-input-bg-color); background-color: var(--form-input-bg-color);
color: var(--form-input-text-color); color: var(--form-input-text-color);
flex: 1;
} }
#UrlInput:focus { #UrlInput:focus {
@@ -74,3 +75,39 @@ form#downloadForm {
#UrlInput::placeholder { #UrlInput::placeholder {
color: var(--form-input-placeholder-color); color: var(--form-input-placeholder-color);
} }
.url-input-container {
display: flex;
align-items: center;
gap: 0.5em;
width: 100%;
}
.paste-btn {
background-color: var(--form-button-bg-color);
color: var(--form-button-text-color);
border: none;
padding: 0.75em;
border-radius: 8px;
cursor: pointer;
transition: background-color 0.3s ease, transform 0.2s ease;
display: flex;
align-items: center;
justify-content: center;
min-width: 44px;
height: 44px;
}
.paste-btn:hover {
background-color: var(--form-button-bg-hover-color);
transform: scale(1.05);
}
.paste-btn:active {
transform: scale(0.95);
}
.paste-btn svg {
width: 30px;
height: 30px;
}

View File

@@ -69,80 +69,293 @@
/* --- Playlist Mode --- */ /* --- Playlist Mode --- */
#videoInfo.playlist-mode { #videoInfo.playlist-mode {
max-width: 900px; max-width: 95%;
} }
/* Style du bloc "Playlist détectée" */ /* Playlist Header */
#videoInfo.playlist-mode > p { .playlist-header {
text-align: center; display: flex;
font-size: 1.1rem; flex-direction: column;
padding: 2rem;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0.03));
border-radius: 16px;
margin-bottom: 2rem;
border: 1px solid rgba(255, 255, 255, 0.12);
backdrop-filter: blur(10px);
}
.playlist-badge {
display: inline-flex;
align-items: center;
width: fit-content;
padding: 0.4rem 0.9rem;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 6px;
font-size: 0.8rem;
font-weight: 600; font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
color: var(--video-info-text-color);
margin-bottom: 1rem;
}
.playlist-info {
flex: 1;
}
.playlist-title {
font-size: 1.9rem;
margin: 0 0 0.6rem 0;
color: var(--video-info-heading-color);
font-weight: 700;
line-height: 1.2;
}
.playlist-meta {
display: flex;
gap: 1.5rem;
font-size: 0.95rem;
color: var(--video-info-text-color);
opacity: 0.85;
flex-wrap: wrap;
}
.playlist-count,
.playlist-channel {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.4rem 0.8rem;
background: rgba(255, 255, 255, 0.05);
border-radius: 8px;
border: 1px solid rgba(255, 255, 255, 0.08);
font-weight: 500;
}
.playlist-count svg,
.playlist-channel svg {
width: 16px;
height: 16px;
flex-shrink: 0;
} }
/* Layout liste des vidéos */ /* Layout liste des vidéos */
#playlistVideos { #playlistVideos {
margin-top: 1em; margin-top: 1em;
display: grid; display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 18px; gap: 1.5rem;
} }
/* Un item de playlist */ /* Carte vidéo playlist */
#playlistVideos > div { .playlist-video-card {
background: var(--playlist-background-color); background: var(--playlist-background-color);
border-radius: 10px; border-radius: 14px;
padding: 10px; overflow: hidden;
box-shadow: 0 4px 12px rgba(0,0,0,0.15); box-shadow: 0 2px 12px rgba(0,0,0,0.12);
text-align: center; transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
transition: transform 0.2s ease;
}
#playlistVideos > div p {
display: flex; display: flex;
align-items: center; flex-direction: column;
justify-content: center; border: 1px solid rgba(255, 255, 255, 0.1);
flex-wrap: wrap; position: relative;
} }
#videoInfo.playlist-mode { .playlist-video-card::before {
max-width: 95%; content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
background: linear-gradient(90deg, var(--video-info-link-color), transparent);
opacity: 0;
transition: opacity 0.25s ease;
} }
#playlistVideos > div:hover { .playlist-video-card:hover {
transform: translateY(-6px);
box-shadow: 0 12px 32px rgba(0,0,0,0.25);
border-color: rgba(255, 255, 255, 0.15);
}
.playlist-video-card:hover::before {
opacity: 1;
}
/* Thumbnail wrapper avec durée */
.video-thumbnail-wrapper {
position: relative;
width: 100%;
aspect-ratio: 16 / 9;
overflow: hidden;
background: #000;
}
.video-thumbnail {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
transition: transform 0.3s ease;
}
.playlist-video-card:hover .video-thumbnail {
transform: scale(1.05); transform: scale(1.05);
} }
#playlistVideos img { .video-duration {
width: 100%; position: absolute;
bottom: 8px;
right: 8px;
background: rgba(0, 0, 0, 0.9);
color: white;
padding: 4px 8px;
border-radius: 6px;
font-size: 0.85rem;
font-weight: 700;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', monospace;
letter-spacing: 0.5px;
backdrop-filter: blur(4px);
}
/* Contenu vidéo */
.video-content {
padding: 1.2rem;
display: flex;
flex-direction: column;
gap: 0.6rem;
flex: 1;
background: linear-gradient(180deg, transparent, rgba(0, 0, 0, 0.02));
}
.video-title {
font-size: 1rem;
font-weight: 600;
margin: 0;
color: var(--video-info-heading-color);
line-height: 1.4;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
min-height: 2.8em;
transition: color 0.2s ease;
}
.playlist-video-card:hover .video-title {
color: var(--video-info-link-color);
}
.video-uploader {
font-size: 0.9rem;
color: var(--video-info-text-color);
opacity: 0.7;
margin: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* Actions vidéo */
.video-actions {
display: flex;
align-items: center;
gap: 0.6rem;
margin-top: auto;
padding-top: 0.8rem;
border-top: 1px solid rgba(255, 255, 255, 0.08);
}
.video-link {
flex: 1;
text-decoration: none;
color: var(--video-info-link-color);
font-size: 0.9rem;
padding: 0.5rem 0.9rem;
border-radius: 8px; border-radius: 8px;
margin-bottom: 8px; background: rgba(255, 255, 255, 0.06);
border: 1px solid rgba(255, 255, 255, 0.08);
transition: all 0.2s ease;
text-align: center;
font-weight: 500;
display: flex;
align-items: center;
justify-content: center;
gap: 0.4rem;
}
.video-link svg {
width: 16px;
height: 16px;
flex-shrink: 0;
}
.video-link:hover {
background: rgba(255, 255, 255, 0.12);
border-color: rgba(255, 255, 255, 0.15);
text-decoration: none;
transform: translateY(-1px);
} }
.copy-btn { .copy-btn {
all: unset; background: rgba(255, 255, 255, 0.06);
background: transparent; border: 1px solid rgba(255, 255, 255, 0.08);
border: none;
cursor: pointer; cursor: pointer;
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 4px; padding: 0.5rem;
vertical-align: middle; border-radius: 8px;
transition: transform 0.1s ease, color 0.1s ease, opacity 0.15s ease; transition: all 0.2s ease;
font-size: 1em; width: 40px;
height: 40px;
flex-shrink: 0;
} }
#playlistVideos > div p a{ .copy-btn svg {
text-decoration: none; width: 18px;
color: var(--video-info-link-color); height: 18px;
color: var(--video-info-text-color);
} }
#playlistVideos > div p a:hover{ .copy-btn:hover {
text-decoration: underline; background: rgba(255, 255, 255, 0.12);
border-color: rgba(255, 255, 255, 0.15);
transform: translateY(-1px);
}
.copy-btn:active {
transform: scale(0.95);
}
.copy-btn:disabled {
cursor: not-allowed;
opacity: 0.6;
} }
/* Responsive */ /* Responsive */
@media (max-width: 768px) {
#playlistVideos {
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: 1rem;
}
.playlist-header {
padding: 1.5rem;
}
.playlist-title {
font-size: 1.5rem;
}
.playlist-meta {
gap: 0.8rem;
}
}
@media (max-width: 480px) { @media (max-width: 480px) {
#videoInfo { #videoInfo {
padding: 1em; padding: 1em;
@@ -152,4 +365,21 @@
#videoInfo h3 { #videoInfo h3 {
font-size: 1.4rem; font-size: 1.4rem;
} }
#playlistVideos {
grid-template-columns: 1fr;
}
.playlist-header {
padding: 1.2rem;
}
.playlist-title {
font-size: 1.3rem;
}
.playlist-badge {
font-size: 0.75rem;
padding: 0.4rem 0.8rem;
}
} }

Binary file not shown.

View File

@@ -16,10 +16,14 @@ async function downloadController(req, res) {
}; };
if (!options.url || !isValidUrl(options.url)) return res.status(400).send("❌ Invalid URL !"); if (!options.url || !isValidUrl(options.url)) return res.status(400).send("❌ Invalid URL !");
if (options.outputFolder && !isSafePath(options.outputFolder)) return res.status(400).send("❌ Save Path Not Allowed."); if (options.outputFolder && !isSafePath(options.outputFolder)) {
logger.warn(`Unsafe download path rejected: ${options.outputFolder}`);
return res.status(400).send("❌ Save Path Not Allowed.");
}
const filePath = await fetchDownload(options, listeners, speedListeners); // Get output folder when the download is finished
notifyDownloadFinished(filePath); const outputFolder = await fetchDownload(options, listeners, speedListeners);
notifyDownloadFinished(outputFolder);
res.send("✅ Download Done !"); res.send("✅ Download Done !");
} catch (err) { } catch (err) {

View File

@@ -8,12 +8,12 @@ async function infoController(req, res){
const url = req.body.url || req.query.url; // Gérer POST et GET const url = req.body.url || req.query.url; // Gérer POST et GET
/* Si pas d'url ou url invalide*/ /* Si pas d'url, url non-string ou url invalide*/
if (!url || typeof url !== "string" || !isValidUrl(url)) return res.status(400).send("❌ Invalid URL Or Missing"); if (!url || typeof url !== "string" || !isValidUrl(url)) return res.status(400).send("❌ Invalid URL Or Missing");
logger.info(`/Info Request receive by the Info Controller. URL: ${url}`); logger.info(`/Info Request receive by the Info Controller. URL: ${url}`);
if (url.includes("?list")) { if (url.includes("&list") || url.includes("?list")) {
logger.info("Estimated Data Type: Playlist") logger.info("Estimated Data Type: Playlist")
} else{ } else{

View File

@@ -17,7 +17,7 @@ function startRPC() {
details: `Open Source Download Tools - ${config.version}`, details: `Open Source Download Tools - ${config.version}`,
state: "masteracnolo.github.io/FreedomLoader", state: "masteracnolo.github.io/FreedomLoader",
}; };
rpc.clearActivity()
rpc.setActivity(presence); rpc.setActivity(presence);
// Met à jour la présence toutes les 15s // Met à jour la présence toutes les 15s
@@ -26,27 +26,26 @@ function startRPC() {
}, 15000); }, 15000);
}); });
async function cleanExit() {
try {
if (intervalId) clearInterval(intervalId);
if (rpc && rpc.transport) {
await rpc.destroy();
}
} catch (err) {
logger.error("Error while closing the RPC:", err);
} finally {
process.exit();
}
}
process.on("exit", cleanExit);
process.on("SIGINT", cleanExit);
process.on("SIGTERM", cleanExit);
rpc.login({ clientId }).catch(err => { rpc.login({ clientId }).catch(err => {
logger.error("Unable to connect to the RPC:", err); logger.error("Unable to connect to the RPC:", err);
}); });
} }
module.exports = { startRPC }; async function stopRPC(){
try {
if (intervalId) clearInterval(intervalId);
if (rpc && rpc.transport) {
await rpc.clearActivity()
await rpc.destroy();
} else{
logger.error("Not Able to close RPC")
}
} catch (err) {
logger.error("Error while closing the RPC:", err);
}
}
module.exports = { startRPC, stopRPC};

View File

@@ -2,6 +2,7 @@ const fs = require("fs");
const os = require("os"); const os = require("os");
const path = require("path"); const path = require("path");
const notify = require("./notify") const notify = require("./notify")
const { logger } = require("../logger");
function getUserBrowser() { function getUserBrowser() {
const userProfile = os.homedir(); const userProfile = os.homedir();
@@ -18,15 +19,21 @@ function getUserBrowser() {
// { name: "whale", path: path.join(userProfile, "AppData", "Local", "Naver", "Naver Whale", "User Data", "Default") } // { name: "whale", path: path.join(userProfile, "AppData", "Local", "Naver", "Naver Whale", "User Data", "Default") }
]; ];
// Chercher un navigateur disponible
for (const browser of browsers) { for (const browser of browsers) {
if (fs.existsSync(browser.path)) { if (fs.existsSync(browser.path)) {
logger.info(`Browser found: ${browser.name}`);
return browser.name; return browser.name;
} else{ }
}
// Aucun navigateur trouvé - notifier l'utilisateur
logger.warn("No supported browser found on the system");
notify.notifyFirefoxBrowserMissing(); notify.notifyFirefoxBrowserMissing();
}
} // Fallback: retourner "firefox" pour laisser yt-dlp gérer l'erreur
notify.notifyCookiesBrowserError() // plutôt que de crasher l'application
return ; return "firefox";
} }
module.exports = getUserBrowser module.exports = getUserBrowser

View File

@@ -1,12 +1,11 @@
const { Notification, shell } = require("electron"); const { Notification, shell } = require("electron");
const path = require("path"); const { iconPaths } = require("./path");
function notifyDownloadFinished(folder) { function notifyDownloadFinished(folder) {
const iconPath = path.join(process.resourcesPath, "confirm-icon.png");
const notif = new Notification({ const notif = new Notification({
title: "Freedom Loader", title: "Freedom Loader",
body: "Your download is complete, click here to open it.", body: "Your download is complete, click here to open it.",
icon: iconPath, icon: iconPaths.confirm,
}); });
notif.on("click", () => shell.openPath(folder)); notif.on("click", () => shell.openPath(folder));
@@ -14,11 +13,10 @@ function notifyDownloadFinished(folder) {
} }
function notifyCookiesBrowserError(){ function notifyCookiesBrowserError(){
const iconPath = path.join(process.resourcesPath, "error.png");
const notif = new Notification({ const notif = new Notification({
title: "Cookies Error", title: "Cookies Error",
body: "Unable to retrieve cookies. Please log in to your browser and click here to view the tutorial.", body: "Unable to retrieve cookies. Please log in to your browser and click here to view the tutorial.",
icon: iconPath, icon: iconPaths.error,
}); });
notif.on("click", () => shell.openExternal("https://youtube.com/shorts/cN9f4s1Mf88?si=519QCVd_-fzJqRf1")); notif.on("click", () => shell.openExternal("https://youtube.com/shorts/cN9f4s1Mf88?si=519QCVd_-fzJqRf1"));
@@ -26,11 +24,10 @@ function notifyCookiesBrowserError(){
} }
function notifyFirefoxBrowserMissing(){ function notifyFirefoxBrowserMissing(){
const iconPath = path.join(process.ressourcesPath, "error.png");
const notif = new Notification({ const notif = new Notification({
title: "Firefox Missing", title: "Firefox Missing",
body: "Firefox was not found on your system. Click here to follow the installation guide", body: "Firefox was not found on your system. Click here to follow the installation guide",
icon: iconPath, icon: iconPaths.error,
}); });
notif.on("click", () => shell.openExternal("https://youtube.com/shorts/cN9f4s1Mf88?si=519QCVd_-fzJqRf1")) notif.on("click", () => shell.openExternal("https://youtube.com/shorts/cN9f4s1Mf88?si=519QCVd_-fzJqRf1"))

View File

@@ -2,12 +2,6 @@ function parseVideo(data) {
return { return {
type: "video", type: "video",
// id: data.id,
// title: data.title,
// url: data.webpage_url,
// duration: data.duration,
// thumbnail: data.thumbnail,
// uploader: data.uploader
...data ...data
}; };
} }

View File

@@ -1,15 +1,25 @@
const path = require("path"); const path = require("path");
const fs = require("fs"); const fs = require("fs");
const os = require("os");
const { app } = require("electron"); const { app } = require("electron");
const config = require("../../config"); const config = require("../../config");
const { logger } = require("../logger.js"); const { logger } = require("../logger.js");
// Centralized resource paths
const resourcesPath = config.localMode
? path.join(__dirname, "../../ressources")
: process.resourcesPath;
// Default download folder (centralized)
const defaultDownloadFolder = path.join(os.homedir(), "Downloads", "Freedom Loader");
// Binary paths
let userYtDlp; let userYtDlp;
let ffmpegPath; let ffmpegPath;
let denoPath; let denoPath;
const sourceYtDlp = path.join(process.resourcesPath, "binaries","yt-dlp.exe"); const sourceYtDlp = path.join(resourcesPath, "binaries","yt-dlp.exe");
if (config.localMode) { if (config.localMode) {
userYtDlp = path.join(__dirname, "../../ressources/yt-dlp.exe"); userYtDlp = path.join(__dirname, "../../ressources/yt-dlp.exe");
@@ -17,8 +27,8 @@ if (config.localMode) {
denoPath = path.join(__dirname, "../../ressources/deno.exe"); denoPath = path.join(__dirname, "../../ressources/deno.exe");
} else { } else {
userYtDlp = path.join(app.getPath("userData"),"yt-dlp.exe"); userYtDlp = path.join(app.getPath("userData"),"yt-dlp.exe");
ffmpegPath = path.join(process.resourcesPath, "binaries","ffmpeg.exe"); ffmpegPath = path.join(resourcesPath, "binaries","ffmpeg.exe");
denoPath = path.join(process.resourcesPath, "binaries","deno.exe"); denoPath = path.join(resourcesPath, "binaries","deno.exe");
if (!fs.existsSync(userYtDlp)) { if (!fs.existsSync(userYtDlp)) {
fs.copyFileSync(sourceYtDlp, userYtDlp); fs.copyFileSync(sourceYtDlp, userYtDlp);
@@ -26,8 +36,23 @@ if (config.localMode) {
} }
// Notification icon paths
const iconPaths = {
confirm: path.join(resourcesPath, "confirm-icon.png"),
error: path.join(resourcesPath, "error.png"),
app: path.join(resourcesPath, "app-icon.ico")
};
// Binary paths for verification
const binaryPaths = {
ytDlp: path.join(resourcesPath, "binaries", "yt-dlp.exe"),
ffmpeg: path.join(resourcesPath, "binaries", "ffmpeg.exe"),
ffprobe: path.join(resourcesPath, "binaries", "ffprobe.exe"),
deno: path.join(resourcesPath, "binaries", "deno.exe")
};
if (!userYtDlp){ logger.error("Missing YT-DLP")} if (!userYtDlp){ logger.error("Missing YT-DLP")}
if (!ffmpegPath){ logger.error("Missing FFMPEG")} if (!ffmpegPath){ logger.error("Missing FFMPEG")}
if (!denoPath){ logger.error("Missing DENO")} if (!denoPath){ logger.error("Missing DENO")}
module.exports = { userYtDlp, ffmpegPath, denoPath }; module.exports = { userYtDlp, ffmpegPath, denoPath, iconPaths, binaryPaths, resourcesPath, defaultDownloadFolder };

View File

@@ -3,7 +3,7 @@ const RateLimit = require("express-rate-limit");
/* Rate Limite */ /* Rate Limite */
const rateLimite = RateLimit({ const rateLimite = RateLimit({
windowMs: 15 * 60, // 15 minutes windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // limit each IP to 100 requests per windowMs on the root path max: 5, // limit each IP to 100 requests per windowMs on the root path
message: "Too many requests, please try again later.", message: "Too many requests, please try again later.",
statusCode: 429, // HTTP status code for "Too Many Requests" statusCode: 429, // HTTP status code for "Too Many Requests"

View File

@@ -13,7 +13,7 @@ function isValidUrl(url) {
function isSafePath(folder) { function isSafePath(folder) {
if (!folder || folder.length < 3) return false; if (!folder || folder.length < 3) return false;
const unsafe = ["System32", "/etc", "\\Windows"]; const unsafe = ["System32", "\\Windows"];
const resolved = path.resolve(folder); const resolved = path.resolve(folder);
return !unsafe.some(u => resolved.includes(u)); return !unsafe.some(u => resolved.includes(u));
} }

View File

@@ -3,13 +3,17 @@ const DailyRotateFile = require("winston-daily-rotate-file");
const fs = require("fs"); const fs = require("fs");
const path = require("path"); const path = require("path");
const os = require("os"); const os = require("os");
const config = require("../config") const config = require("../config");
// Dossier de logs Windows // Dossier de logs Windows
const logDir = path.join(os.homedir(), "AppData", "Local", "FreedomLoader", "logs"); const logDir = path.join(os.homedir(), "AppData", "Local", "FreedomLoader", "logs");
// Création du dossier "logs" si nécessaire // Création du dossier "logs" si nécessaire
fs.mkdirSync(logDir, { recursive: true }); try {
fs.mkdirSync(logDir, { recursive: true });
} catch (error) {
console.error(`Failed to create log directory: ${error.message}`);
}
const logFormat = format.combine( const logFormat = format.combine(
format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
@@ -36,21 +40,13 @@ const logger = createLogger({
], ],
}); });
function getSessionStartLine() {
return `--- Starting session: ${new Date().toISOString()} ---`;
}
function getSessionEndLine() {
return `--- Ending session: ${new Date().toISOString()} ---`;
}
function logSessionStart() { function logSessionStart() {
logger.info(getSessionStartLine()); logger.info(`--- Starting session: ${new Date().toISOString()} ---`);
logger.info(`Application Version: ${config.version}`) logger.info(`Application Version: ${config.version}`)
} }
function logSessionEnd() { function logSessionEnd() {
logger.info(getSessionEndLine()); logger.info(`--- Ending session: ${new Date().toISOString()} ---`);
} }
module.exports = { module.exports = {

View File

@@ -1,5 +1,4 @@
const express = require("express"); const express = require("express");
const fs = require("fs");
const path = require("path"); const path = require("path");
const { logger, logSessionEnd } = require("./logger"); const { logger, logSessionEnd } = require("./logger");
const config = require("../config"); const config = require("../config");
@@ -11,19 +10,7 @@ const app = express();
app.use(express.json()); app.use(express.json());
// Dossier de téléchargement // Mise à jour yt-dlp au démarrage
const outputFolder = path.join(process.env.USERPROFILE, "Downloads", "Freedom Loader");
// Création du dossier si nécessaire
try {
fs.mkdirSync(outputFolder, { recursive: true });
logger.info(`Freedom Loader folder ready in ${outputFolder}`);
} catch (err) {
logger.error("Unable to create folder:", err);
process.exit(1);
}
// Mise à jour yt-dlp au
execFile(userYtDlp, ["-U"], (err, stdout, stderr) => { execFile(userYtDlp, ["-U"], (err, stdout, stderr) => {
if (err) logger.warn("yt-dlp update error:", err); if (err) logger.warn("yt-dlp update error:", err);
else logger.info(`Update yt-dlp : ${stdout}`); else logger.info(`Update yt-dlp : ${stdout}`);

View File

@@ -1,18 +1,35 @@
const { execFile } = require("child_process"); const { execFile } = require("child_process");
const { userYtDlp } = require("../helpers/path"); const { userYtDlp, defaultDownloadFolder } = require("../helpers/path");
const path = require("path");
const fs = require("fs"); const fs = require("fs");
const { logger } = require("../logger"); const { logger } = require("../logger");
const { buildYtDlpArgs } = require("../helpers/buildArgs"); const { buildYtDlpArgs } = require("../helpers/buildArgs");
const notify = require("../helpers/notify") const notify = require("../helpers/notify");
const path = require("path");
const { isSafePath } = require("../helpers/validation");
function fetchDownload(options, listeners, speedListeners) { function fetchDownload(options, listeners, speedListeners) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const outputFolder = options.outputFolder || path.join(process.env.USERPROFILE, "Downloads", "Freedom Loader"); const outputFolder = options.outputFolder || defaultDownloadFolder;
fs.mkdirSync(outputFolder, { recursive: true });
const args = buildYtDlpArgs({ ...options, outputFolder }); // 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 });
logger.info(`Output folder ready: ${safeOutputFolder}`);
} catch (err) {
logger.error(`Failed to create output folder: ${err.message}`);
return reject(new Error(`Unable to create download folder: ${err.message}`));
}
const args = buildYtDlpArgs({ ...options, outputFolder: safeOutputFolder });
logger.info(`[yt-dlp args] ${args.join(" ")}`); logger.info(`[yt-dlp args] ${args.join(" ")}`);
const child = execFile(userYtDlp, args); const child = execFile(userYtDlp, args);
@@ -21,7 +38,7 @@ function fetchDownload(options, listeners, speedListeners) {
child.on("close", code => { child.on("close", code => {
listeners.forEach(fn => fn("done")); listeners.forEach(fn => fn("done"));
if (code === 0) resolve(outputFolder); if (code === 0) resolve(safeOutputFolder);
else reject(new Error(`YT-DLP failed with code : ${code}`)); else reject(new Error(`YT-DLP failed with code : ${code}`));
}); });
@@ -31,7 +48,7 @@ function fetchDownload(options, listeners, speedListeners) {
if (!line.trim()) return; if (!line.trim()) return;
logger.info(`[yt-dlp] ${line}`); logger.info(`[yt-dlp] ${line}`);
/* Barre de Chargement*/ /* Progress Bar */
if (line.startsWith("[download] Destination:")) listeners.forEach(fn => fn("reset")); if (line.startsWith("[download] Destination:")) listeners.forEach(fn => fn("reset"));
const match = line.match(/\[download\]\s+(\d+\.\d+)%/); const match = line.match(/\[download\]\s+(\d+\.\d+)%/);
if (match) listeners.forEach(fn => fn(parseFloat(match[1]))); if (match) listeners.forEach(fn => fn(parseFloat(match[1])));