Compare commits
72 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4be5c14999 | ||
|
|
b92ffebdb4 | ||
|
|
7b6e48b09e | ||
|
|
368851b4bb | ||
|
|
fa2da5712d | ||
|
|
7df97d70bb | ||
|
|
5c5f595829 | ||
|
|
5b659e3d8a | ||
|
|
1a526777cc | ||
|
|
b0d99d4c29 | ||
|
|
c7554475a9 | ||
|
|
cab177e8a7 | ||
|
|
f7244a69ac | ||
|
|
c6ed79ed1d | ||
|
|
00e5358bbb | ||
|
|
598c35eb54 | ||
|
|
00c74ea6c6 | ||
|
|
0c415c9106 | ||
|
|
b0495d033d | ||
|
|
d72cc6e0ca | ||
|
|
b59cb9f57f | ||
|
|
af743be5b0 | ||
|
|
024d039701 | ||
|
|
e9a91656bc | ||
|
|
3505606b44 | ||
|
|
815abae0a0 | ||
|
|
004271241e | ||
|
|
8295967ff2 | ||
|
|
053dca365c | ||
|
|
62110b1a5b | ||
|
|
aae9b2866c | ||
|
|
0d8bddbc20 | ||
|
|
2a7d61bab7 | ||
|
|
de71035fa0 | ||
|
|
5368fb9311 | ||
|
|
3d583a9b6f | ||
|
|
94de0a42f1 | ||
|
|
5ba2901273 | ||
|
|
f5888b8382 | ||
|
|
6ece182629 | ||
|
|
777257b71d | ||
|
|
df0156d603 | ||
|
|
68cdad06ab | ||
|
|
ee7c6520b9 | ||
|
|
4ed1609e41 | ||
|
|
b64e61d45d | ||
|
|
b4c2c865d8 | ||
|
|
e32ffd8fa0 | ||
|
|
8be6a932e5 | ||
|
|
6ed59fd272 | ||
|
|
4df1cb207f | ||
|
|
d03e3bc7e5 | ||
|
|
df02acb97f | ||
|
|
6ef04700ab | ||
|
|
766694f330 | ||
|
|
93380b2f36 | ||
|
|
8c969d694f | ||
|
|
f2f3d42e8c | ||
|
|
8d8973acfc | ||
|
|
281bff2379 | ||
|
|
e30f54050f | ||
|
|
0ae808c1a0 | ||
|
|
2598573844 | ||
|
|
27782376a3 | ||
|
|
876f7e1db0 | ||
|
|
db654c5f82 | ||
|
|
751f3d5c3e | ||
|
|
2a8d307bc8 | ||
|
|
ec6963cff3 | ||
|
|
196a07977a | ||
|
|
f20366f0c7 | ||
|
|
f54dc18e85 |
6
.gitignore
vendored
@@ -1,8 +1,10 @@
|
||||
/node_modules
|
||||
/downloads
|
||||
/dist
|
||||
/logs
|
||||
/out
|
||||
|
||||
/ressources/ffmpeg.exe
|
||||
/ressources/ffprobe.exe
|
||||
|
||||
yt-dlp.exe
|
||||
logs/*.json
|
||||
logs/*.log
|
||||
103
README-EN.md
@@ -1,103 +0,0 @@
|
||||
# Freedom Loader
|
||||
|
||||
 
|
||||
|
||||
> This project aims to enable free, open-source, and unrestricted downloading of Video/Audio content. Eventually, it will allow users to choose
|
||||
|
||||
## 🚀 Features
|
||||
|
||||
✅ Video Download (MP4)
|
||||
✅ Audio Download (MP3)
|
||||
✅ Detailed Logs (Console + Logs)
|
||||
✅ Automatic Fetch of Information
|
||||
✅ Playlist Download
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Installation
|
||||
|
||||
To install the application, simply download the installer from the releases section. Take the latest version and run it.
|
||||
|
||||
---
|
||||
|
||||
## ❓ Frequently Asked Questions
|
||||
|
||||
### _Where are my downloaded files located?_
|
||||
|
||||
In your computer's Downloads folder (**C:\Users\YOUR NAME\Downloads**), inside a folder named **Freedom Loader Output**
|
||||
<br>
|
||||
|
||||
### _After installing and running the program, I get an error_
|
||||
|
||||
Try restarting the application, checking the logs, or opening the browser's developer console by pressing `CTRL + SHIFT + I` or by going to `View > Toggle Developer Tools`
|
||||
|
||||
<!--  -->
|
||||
<p align="center">
|
||||
<img src="build/example-developertools.png" alt="Example Developer Tools" />
|
||||
</p>
|
||||
|
||||
## 🏗️ Upcoming Features (Roadmap)
|
||||
|
||||
- [ ] More format options
|
||||
- [ ] Subtitle support
|
||||
- [ ] Improved UI / UX
|
||||
- [ ] Language selection
|
||||
- [ ] Download a specific part of the video
|
||||
- [ ] Rename output file
|
||||
- [ ] Extract / Split video using native chapters
|
||||
- [ ] Parallel downloads
|
||||
- [ ] Skip sponsored parts of videos
|
||||
- [ ] Custom output path selection (default **C:\Users\Username\Downloads** with a **Freedom Loader Output** folder created)
|
||||
|
||||
---
|
||||
|
||||
## 🖼️ Preview
|
||||
|
||||
<p align="center">
|
||||
<img src="build/exemple.png" alt="Example Image" />
|
||||
</p>
|
||||
|
||||
> Here is an example of the app after launch and entering a URL
|
||||
|
||||
---
|
||||
|
||||
## ℹ️ About
|
||||
|
||||
This project was built using [](https://github.com/yt-dlp/yt-dlp)
|
||||
Thanks to them for providing a free and open-source API.
|
||||
|
||||
This project is open-source and welcomes your contributions.
|
||||
Here is how to get involved:
|
||||
|
||||
- **🪲 Bug Reporting**
|
||||
Please use the [GitHub issues system](https://github.com/MasterAcnolo/Freedom-Loader/issues) to report any bugs you encounter.
|
||||
Try to provide clear reproduction steps and logs if possible. Logs are located in the installation folder of the program. Example path:
|
||||
`C:\Users\[YOUR NAME]\Desktop\Freedom Loader\resources\app\logs`
|
||||
The log file will be named something like `combined-20xx-xx-xx.log`
|
||||
<br>
|
||||
- **🛠️ Contribution**
|
||||
Pull Requests are welcome! Please fork the repo and submit your PR after ensuring you meet the contribution guidelines (tests, style, etc.).
|
||||
<br>
|
||||
- **💡 Feature Suggestions**
|
||||
You can propose new ideas via a [feature request issue](https://github.com/MasterAcnolo/Freedom-Loader/issues).
|
||||
Any clear and detailed proposal will be reviewed.
|
||||
<br>
|
||||
- **💰 Support the Project**
|
||||
<p>
|
||||
If you'd like to support me, you can donate via
|
||||
<a href="https://paypal.me/axelnicolas25">
|
||||
<img src="https://img.shields.io/badge/PayPal-00457C?style=for-the-badge&logo=paypal&logoColor=white" alt="PayPal">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
## 📄 License
|
||||
|
||||
This project is licensed under the **MIT License** — see the [LICENSE](./LICENSE) file for details.
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Disclaimer
|
||||
|
||||
I have obviously used AI assistance for some parts of this project. Use AI sparingly and always be aware of what you are doing.
|
||||
114
README-FR.md
Normal file
@@ -0,0 +1,114 @@
|
||||
# Freedom Loader
|
||||
|
||||
<!-- Release -->
|
||||
|
||||

|
||||
|
||||
<!-- Build (Next Release) -->
|
||||
|
||||

|
||||
|
||||
> Ce projet a pour but de permettre le téléchargement de contenu Vidéo/Audio de manière gratuite, open source et libre.
|
||||
|
||||
## 🚀 Fonctionnalités
|
||||
|
||||
- ✅ Téléchargement de vidéos avec les métadonnées (MP4)
|
||||
- ✅ Téléchargement audio uniquement, avec la couverture ainsi que les métadonnées (MP3)
|
||||
- ✅ Support des playlists complètes
|
||||
- ✅ Récupération automatique des métadonnées (titre, durée, etc.)
|
||||
- ✅ Logs détaillés : console et fichiers rotatifs (7 jours)
|
||||
- ✅ Choix du chemin de sortie personnalisé avec sauvegarde
|
||||
- ✅ Plusieurs thèmes graphiques au choix pour personnaliser l’interface
|
||||
- ✅ Interface simple, intuitive
|
||||
- ✅ Notification quand le téléchargement est fini (Click = Ouverture Dossier Output)
|
||||
- ✅ Discord Rich Presence
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Installation
|
||||
|
||||
Pour installer l'application, il vous suffit de télécharger l'installateur dans la section releases, de prendre la dernière version et de l'exécuter.
|
||||
|
||||
---
|
||||
|
||||
## ❓ Questions fréquentes
|
||||
|
||||
### _Où se situent mes fichiers une fois téléchargés ?_
|
||||
|
||||
Dans le dossier Téléchargements (**C:\Users\VOTRE NOM\Downloads**) de votre ordinateur, dans un dossier nommé **Freedom Loader Output**
|
||||
<br>
|
||||
|
||||
### _Une fois le programme installé et lancé, j'ai une erreur_
|
||||
|
||||
Essayez de relancer l'application, de consulter les logs ou d'ouvrir la console de développement du navigateur en faisant le raccourci `CTRL + SHIFT + I` ou via `View > Toggle Developer Tools`.
|
||||
|
||||
<!--  -->
|
||||
<p align="center">
|
||||
<img src="build/example-developertools.png" alt="Exemple Developer Tools" />
|
||||
</p>
|
||||
|
||||
## 🏗️ Fonctionnalités à venir (Roadmap)
|
||||
|
||||
- [ ] Plus de choix de formats
|
||||
- [ ] Support des sous-titres
|
||||
- [ ] Meilleure UI / UX
|
||||
- [ ] Choix des langues
|
||||
- [ ] Téléchargement d’une partie de la vidéo
|
||||
- [ ] Renommage du fichier de sortie
|
||||
- [x] ~~Extraction / découpage de la vidéo via chapitrage natif~~
|
||||
- [ ] Téléchargements parallèles
|
||||
- [ ] Suppression automatique des parties sponsorisées (skip sponsor)
|
||||
- [x] ~~Choix personnalisé du chemin de sortie (par défaut **C:\Users\Utilisateur\Downloads**, où un dossier **Freedom Loader Output** sera créé)~~
|
||||
|
||||
---
|
||||
|
||||
## 🖼️ Aperçu
|
||||
|
||||
<p align="center">
|
||||
<img src="build/apercu1.0.1.png" alt="Exemple d'application" />
|
||||
</p>
|
||||
|
||||
> Voici un exemple de l’application lancée après avoir saisi une URL.
|
||||
|
||||
---
|
||||
|
||||
## ℹ️ À propos
|
||||
|
||||
Ce projet a été réalisé en utilisant [](https://github.com/yt-dlp/yt-dlp)
|
||||
Merci à eux de nous permettre d’utiliser leur API gratuitement et en open source.
|
||||
|
||||
Ce projet est open source et accueille volontiers vos contributions.
|
||||
Voici comment participer :
|
||||
|
||||
- **🪲 Signalement de bugs**
|
||||
Merci d’utiliser le [système d’issues GitHub](https://github.com/MasterAcnolo/Freedom-Loader/issues) pour remonter les bugs rencontrés.
|
||||
Essayez de fournir une reproduction claire et les logs si possible. Les logs se trouvent dans le dossier d’installation du programme. Exemple de chemin :
|
||||
`C:\Users\[VOTRE NOM]\Desktop\Freedom Loader\resources\app\logs`
|
||||
Le fichier aura un nom du type `combined-20xx-xx-xx.log`.
|
||||
<br>
|
||||
- **🛠️ Contribution**
|
||||
Les Pull Requests sont les bienvenues ! Merci de forker le repo et de soumettre votre PR après avoir vérifié le respect des guidelines (tests, style, etc.).
|
||||
<br>
|
||||
- **💡 Suggestions de fonctionnalités**
|
||||
Vous pouvez proposer de nouvelles idées via une [issue de type feature request](https://github.com/MasterAcnolo/Freedom-Loader/issues).
|
||||
Toute proposition claire et détaillée sera étudiée.
|
||||
<br>
|
||||
- **💰 Soutenir le projet**
|
||||
<p>
|
||||
Si vous souhaitez m’encourager, vous pouvez faire un don sur
|
||||
<a href="https://paypal.me/axelnicolas25">
|
||||
<img src="https://img.shields.io/badge/PayPal-00457C?style=for-the-badge&logo=paypal&logoColor=white" alt="PayPal">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
## 📄 Licence
|
||||
|
||||
Ce projet est sous licence **MIT** — voir le fichier [LICENSE](./LICENSE) pour plus d’informations.
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Disclaimer
|
||||
|
||||
J’ai bien évidemment eu recours à l’IA pour m’aider sur certaines parties du projet. Utilisez l’IA avec parcimonie et soyez toujours conscient de ce que vous faites.
|
||||
109
README.md
@@ -1,90 +1,101 @@
|
||||
# Freedom Loader
|
||||
|
||||
 
|
||||
<!-- Release -->
|
||||
|
||||
> Ce projet a pour but de permettre le téléchargement de contenu Vidéo/Audio de manière gratuite, open source et libre. Il permettra à terme de choisir
|
||||

|
||||
|
||||
## 🚀 Fonctionnalités
|
||||
<!-- Build (Next Release) -->
|
||||
|
||||
✅ Téléchargement Vidéo (MP4)
|
||||
✅ Téléchargement Audio (MP3)
|
||||
✅ Logs détaillés (console + fichiers logs)
|
||||
✅ Récupération automatique des informations
|
||||
✅ Téléchargement des playlists
|
||||

|
||||
|
||||
> This project aims to enable free, open-source, and unrestricted downloading of Video/Audio content.
|
||||
|
||||
## 🚀 Features
|
||||
|
||||
- ✅ Video download with metadata(MP4)
|
||||
- ✅ Audio-only download with thumbail and metadata(MP3)
|
||||
- ✅ Full playlist support
|
||||
- ✅ Automatic metadata retrieval (title, duration, etc.)
|
||||
- ✅ Detailed logs: console and rotating files (7 days)
|
||||
- ✅ Custom output path selection with saving
|
||||
- ✅ Multiple graphical themes available for interface customization
|
||||
- ✅ Simple, intuitive, and responsive interface
|
||||
- ✅ Notification system when download finished (Click = Open Output Folder)
|
||||
- ✅ Discord Rich Presence
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Installation
|
||||
|
||||
Pour installer l'application, il vous suffit de télécharger l'installateur dans la section releases, de prendre la dernière version et de l'exécuter.
|
||||
To install the application, simply download the installer from the releases section. Take the latest version and run it.
|
||||
|
||||
---
|
||||
|
||||
## ❓ Questions fréquentes
|
||||
## ❓ Frequently Asked Questions
|
||||
|
||||
### _Où se situent mes fichiers une fois téléchargés ?_
|
||||
### _Where are my downloaded files located?_
|
||||
|
||||
Dans le dossier Téléchargements (**C:\Users\VOTRE NOM\Downloads**) de votre ordinateur, dans un dossier nommé **Freedom Loader Output**
|
||||
In your computer's Downloads folder (**C:\Users\YOUR NAME\Downloads**), inside a folder named **Freedom Loader Output**
|
||||
<br>
|
||||
|
||||
### _Une fois le programme installé et lancé, j'ai une erreur_
|
||||
### _After installing and running the program, I get an error_
|
||||
|
||||
Essayez de relancer l'application, de consulter les logs ou d'ouvrir la console de développement du navigateur en faisant le raccourci `CTRL + SHIFT + I` ou via `View > Toggle Developer Tools`.
|
||||
Try restarting the application, checking the logs, or opening the browser's developer console by pressing `CTRL + SHIFT + I` or by going to `View > Toggle Developer Tools`
|
||||
|
||||
<!--  -->
|
||||
<!--  -->
|
||||
<p align="center">
|
||||
<img src="build/example-developertools.png" alt="Exemple Developer Tools" />
|
||||
<img src="build/example-developertools.png" alt="Example Developer Tools" />
|
||||
</p>
|
||||
|
||||
## 🏗️ Fonctionnalités à venir (Roadmap)
|
||||
## 🏗️ Upcoming Features (Roadmap)
|
||||
|
||||
- [ ] Plus de choix de formats
|
||||
- [ ] Support des sous-titres
|
||||
- [ ] Meilleure UI / UX
|
||||
- [ ] Choix des langues
|
||||
- [ ] Téléchargement d’une partie de la vidéo
|
||||
- [ ] Renommage du fichier de sortie
|
||||
- [ ] Extraction / découpage de la vidéo via chapitrage natif
|
||||
- [ ] Téléchargements parallèles
|
||||
- [ ] Suppression automatique des parties sponsorisées (skip sponsor)
|
||||
- [ ] Choix personnalisé du chemin de sortie (par défaut **C:\Users\Utilisateur\Downloads**, où un dossier **Freedom Loader Output** sera créé)
|
||||
- [ ] More format options
|
||||
- [ ] Subtitle support
|
||||
- [ ] Improved UI / UX
|
||||
- [ ] Language selection
|
||||
- [ ] Download a specific part of the video
|
||||
- [ ] Rename output file
|
||||
- [x] ~~Extract / Split video using native chapters~~
|
||||
- [ ] Parallel downloads
|
||||
- [ ] Skip sponsored parts of videos
|
||||
- [x] ~~Custom output path selection (default **C:\Users\Username\Downloads** with a **Freedom Loader Output** folder created)~~
|
||||
|
||||
---
|
||||
|
||||
## 🖼️ Aperçu
|
||||
## 🖼️ Preview
|
||||
|
||||
<p align="center">
|
||||
<img src="build/exemple.png" alt="Exemple d'application" />
|
||||
<img src="build/apercu1.0.1.png" alt="Exemple d'application" />
|
||||
</p>
|
||||
|
||||
> Voici un exemple de l’application lancée après avoir saisi une URL.
|
||||
> Here is an example of the app after launch and entering a URL
|
||||
|
||||
---
|
||||
|
||||
## ℹ️ À propos
|
||||
## ℹ️ About
|
||||
|
||||
Ce projet a été réalisé en utilisant [](https://github.com/yt-dlp/yt-dlp)
|
||||
Merci à eux de nous permettre d’utiliser leur API gratuitement et en open source.
|
||||
This project was built using [](https://github.com/yt-dlp/yt-dlp)
|
||||
Thanks to them for providing a free and open-source API.
|
||||
|
||||
Ce projet est open source et accueille volontiers vos contributions.
|
||||
Voici comment participer :
|
||||
This project is open-source and welcomes your contributions.
|
||||
Here is how to get involved:
|
||||
|
||||
- **🪲 Signalement de bugs**
|
||||
Merci d’utiliser le [système d’issues GitHub](https://github.com/MasterAcnolo/Freedom-Loader/issues) pour remonter les bugs rencontrés.
|
||||
Essayez de fournir une reproduction claire et les logs si possible. Les logs se trouvent dans le dossier d’installation du programme. Exemple de chemin :
|
||||
`C:\Users\[VOTRE NOM]\Desktop\Freedom Loader\resources\app\logs`
|
||||
Le fichier aura un nom du type `combined-20xx-xx-xx.log`.
|
||||
- **🪲 Bug Reporting**
|
||||
Please use the [GitHub issues system](https://github.com/MasterAcnolo/Freedom-Loader/issues) to report any bugs you encounter.
|
||||
Try to provide clear reproduction steps and logs if possible. Logs are located in the installation folder of the program. Example path:
|
||||
`C:\Users\[YOUR NAME]\Desktop\Freedom Loader\resources\app\logs`
|
||||
The log file will be named something like `combined-20xx-xx-xx.log`
|
||||
<br>
|
||||
- **🛠️ Contribution**
|
||||
Les Pull Requests sont les bienvenues ! Merci de forker le repo et de soumettre votre PR après avoir vérifié le respect des guidelines (tests, style, etc.).
|
||||
Pull Requests are welcome! Please fork the repo and submit your PR after ensuring you meet the contribution guidelines (tests, style, etc.).
|
||||
<br>
|
||||
- **💡 Suggestions de fonctionnalités**
|
||||
Vous pouvez proposer de nouvelles idées via une [issue de type feature request](https://github.com/MasterAcnolo/Freedom-Loader/issues).
|
||||
Toute proposition claire et détaillée sera étudiée.
|
||||
- **💡 Feature Suggestions**
|
||||
You can propose new ideas via a [feature request issue](https://github.com/MasterAcnolo/Freedom-Loader/issues).
|
||||
Any clear and detailed proposal will be reviewed.
|
||||
<br>
|
||||
- **💰 Soutenir le projet**
|
||||
- **💰 Support the Project**
|
||||
<p>
|
||||
Si vous souhaitez m’encourager, vous pouvez faire un don sur
|
||||
If you'd like to support me, you can donate via
|
||||
<a href="https://paypal.me/axelnicolas25">
|
||||
<img src="https://img.shields.io/badge/PayPal-00457C?style=for-the-badge&logo=paypal&logoColor=white" alt="PayPal">
|
||||
</a>
|
||||
@@ -92,12 +103,12 @@ Voici comment participer :
|
||||
|
||||
---
|
||||
|
||||
## 📄 Licence
|
||||
## 📄 License
|
||||
|
||||
Ce projet est sous licence **MIT** — voir le fichier [LICENSE](./LICENSE) pour plus d’informations.
|
||||
This project is licensed under the **MIT License** — see the [LICENSE](./LICENSE) file for details.
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Disclaimer
|
||||
|
||||
J’ai bien évidemment eu recours à l’IA pour m’aider sur certaines parties du projet. Utilisez l’IA avec parcimonie et soyez toujours conscient de ce que vous faites.
|
||||
I have obviously used AI assistance for some parts of this project. Use AI sparingly and always be aware of what you are doing.
|
||||
|
||||
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 100 KiB |
BIN
build/apercu1.0.1.png
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
build/confirm-icon.png
Normal file
|
After Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 264 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 264 KiB After Width: | Height: | Size: 146 KiB |
114
main.js
@@ -16,21 +16,31 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const { app, BrowserWindow } = require("electron");
|
||||
const { app, BrowserWindow, ipcMain, dialog, Menu, shell } = require("electron");
|
||||
app.disableHardwareAcceleration(); // Désactive l'accélération GPU pour éviter les erreurs GPU sur certains systèmes
|
||||
const path = require("path");
|
||||
const { logger, logSessionStart, logSessionEnd } = require("./server/logger");
|
||||
const os = require("os");
|
||||
const { logger, logSessionStart, logSessionEnd, logDir } = require("./server/logger");
|
||||
|
||||
let mainWindow;
|
||||
|
||||
// Utilise le vrai dossier de logs défini dans logger.js
|
||||
const logsFolderPath = logDir;
|
||||
app.setAppUserModelId("com.masteracnolo.freedomloader"); /* Pour la notif */
|
||||
/*
|
||||
Fonction principale qui crée la fenêtre principale de l'application.
|
||||
Elle évite la création multiple et configure les dimensions et options de la fenêtre.
|
||||
Charge l'URL locale du serveur Express et gère les erreurs éventuelles.
|
||||
*/
|
||||
async function createWindow() {
|
||||
logger.info("Creation de la fenetre...");
|
||||
logger.info("Création de la fenêtre...");
|
||||
|
||||
if (mainWindow) {
|
||||
logger.warn("La fenetre existe deja, pas de nouvelle creation");
|
||||
logger.warn("La fenêtre existe déjà, pas de nouvelle création");
|
||||
return;
|
||||
}
|
||||
|
||||
mainWindow = new BrowserWindow({
|
||||
title: "Freedom Loader",
|
||||
title: "Freedom Loader 1.2.4", // A Changer à chaque Version
|
||||
width: 750,
|
||||
height: 800,
|
||||
minWidth: 750,
|
||||
@@ -38,42 +48,118 @@ async function createWindow() {
|
||||
webPreferences: {
|
||||
nodeIntegration: false,
|
||||
contextIsolation: true,
|
||||
preload: path.join(__dirname, "preload.js"), // Chargement du preload script sécurisé
|
||||
},
|
||||
// titleBarStyle: 'hidden',
|
||||
// titleBarStyle: 'hidden', // Option possible pour une barre de titre personnalisée
|
||||
});
|
||||
|
||||
try {
|
||||
await mainWindow.loadURL("http://localhost:8080");
|
||||
logger.info("Fenetre chargee");
|
||||
logger.info("Fenêtre chargée");
|
||||
} catch (err) {
|
||||
logger.error("Erreur chargement fenetre:", err);
|
||||
logger.error("Erreur chargement fenêtre:", err);
|
||||
}
|
||||
|
||||
// Événement déclenché à la fermeture de la fenêtre principale
|
||||
mainWindow.on("closed", () => {
|
||||
logger.info("Fenetre principale fermee");
|
||||
logger.info("Fenêtre principale fermée");
|
||||
mainWindow = null;
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
Définition du chemin par défaut pour les téléchargements.
|
||||
Ici, on utilise le dossier 'Downloads' de l'utilisateur avec un sous-dossier spécifique à l'application.
|
||||
*/
|
||||
const defaultDownloadPath = path.join(os.homedir(), "Downloads", "Freedom Loader");
|
||||
|
||||
/*
|
||||
Gestionnaire IPC qui permet au renderer de demander à l'utilisateur
|
||||
de sélectionner un dossier via une boîte de dialogue native.
|
||||
On retourne le chemin sélectionné ou null si annulation.
|
||||
*/
|
||||
ipcMain.handle("select-download-folder", async () => {
|
||||
logger.info("Demande de sélection d'un dossier reçue depuis le renderer");
|
||||
try {
|
||||
const result = await dialog.showOpenDialog({
|
||||
properties: ["openDirectory"]
|
||||
});
|
||||
if (!result.canceled && result.filePaths.length > 0) {
|
||||
logger.info(`Dossier sélectionné : ${result.filePaths[0]}`);
|
||||
return result.filePaths[0];
|
||||
}
|
||||
return null;
|
||||
} catch (err) {
|
||||
logger.error(`Erreur lors de la sélection de dossier : ${err.message}`);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
Gestionnaire IPC pour exposer au renderer le chemin par défaut de téléchargement
|
||||
afin qu'il puisse l'afficher ou l'utiliser comme valeur initiale.
|
||||
*/
|
||||
ipcMain.handle("get-default-download-path", () => {
|
||||
return defaultDownloadPath;
|
||||
});
|
||||
|
||||
/*
|
||||
Événement déclenché quand l'application est prête.
|
||||
On démarre le serveur Express, puis on crée la fenêtre principale.
|
||||
En cas d'erreur, on log et on quitte l'application proprement.
|
||||
*/
|
||||
app.whenReady().then(async () => {
|
||||
logSessionStart();
|
||||
logger.info("App prete, demarrage du serveur Express...");
|
||||
logger.info("App prête, démarrage du serveur Express...");
|
||||
const expressServer = require("./server/server.js");
|
||||
try {
|
||||
await expressServer.startServer();
|
||||
logger.info("Serveur Express demarre");
|
||||
logger.info("Serveur Express démarré");
|
||||
await createWindow();
|
||||
|
||||
// Ajout du menu personnalisé
|
||||
const menuTemplate = [
|
||||
// ... Menu standard Electron ...
|
||||
{
|
||||
label: "Logs",
|
||||
submenu: [
|
||||
{
|
||||
label: "Ouvrir les logs",
|
||||
click: () => {
|
||||
shell.openPath(logsFolderPath);
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
// Fusionner avec le menu par défaut
|
||||
const defaultMenu = Menu.getApplicationMenu();
|
||||
let template = menuTemplate;
|
||||
if (defaultMenu) {
|
||||
template = [...Menu.getApplicationMenu().items.map(item => item), ...menuTemplate];
|
||||
}
|
||||
const finalMenu = Menu.buildFromTemplate(template);
|
||||
Menu.setApplicationMenu(finalMenu);
|
||||
|
||||
} catch (error) {
|
||||
logger.error("Erreur serveur ou fenetre :", error);
|
||||
logger.error("Erreur serveur ou fenêtre :", error);
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
Quitte l'application lorsque toutes les fenêtres sont fermées,
|
||||
sauf sous macOS où il est habituel de garder l'application active.
|
||||
*/
|
||||
app.on("window-all-closed", () => {
|
||||
logger.info("Toutes fenetres fermees, quitte l'app");
|
||||
logger.info("Toutes fenêtres fermées, quitte l'app");
|
||||
if (process.platform !== "darwin") app.quit();
|
||||
});
|
||||
|
||||
/*
|
||||
Avant de quitter l'application, on log la fin de session pour traçabilité.
|
||||
*/
|
||||
app.on("before-quit", () => {
|
||||
logSessionEnd();
|
||||
});
|
||||
|
||||
1412
package-lock.json
generated
28
package.json
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "freedom-loader",
|
||||
"productName": "Freedom Loader",
|
||||
"version": "1.0.1",
|
||||
"version": "1.2.4",
|
||||
"author": "MasterAcnolo",
|
||||
"description": "Freedom Loader, parce que nous le valons bien",
|
||||
"description": "Freedom Loader",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
"start": "electron-forge start",
|
||||
@@ -14,6 +14,7 @@
|
||||
"dependencies": {
|
||||
"chalk": "^4.1.2",
|
||||
"debug": "^4.4.1",
|
||||
"discord-rpc": "^4.0.1",
|
||||
"electron-squirrel-startup": "^1.0.1",
|
||||
"express": "^5.1.0",
|
||||
"winston": "^3.17.0",
|
||||
@@ -36,12 +37,31 @@
|
||||
"productName": "Freedom Loader",
|
||||
"files": [
|
||||
"**/*",
|
||||
"node_modules/**/*"
|
||||
"node_modules/**/*",
|
||||
"!logs/*.json"
|
||||
],
|
||||
"directories": {
|
||||
"buildResources": "build"
|
||||
},
|
||||
"asar": false,
|
||||
"asar": true,
|
||||
"extraResources": [
|
||||
{
|
||||
"from": "build/confirm-icon.png",
|
||||
"to": "confirm-icon.png"
|
||||
},
|
||||
{
|
||||
"from": "ressources/yt-dlp.exe",
|
||||
"to": "yt-dlp.exe"
|
||||
},
|
||||
{
|
||||
"from": "ressources/ffmpeg.exe",
|
||||
"to": "ffmpeg.exe"
|
||||
},
|
||||
{
|
||||
"from": "ressources/ffprobe.exe",
|
||||
"to": "ffprobe.exe"
|
||||
}
|
||||
],
|
||||
"win": {
|
||||
"target": "nsis",
|
||||
"icon": "build/installer-icon.ico"
|
||||
|
||||
31
preload.js
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
This file is part of Freedom Loader.
|
||||
|
||||
Copyright (C) 2025 MasterAcnolo
|
||||
|
||||
Freedom Loader is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License.
|
||||
|
||||
Freedom Loader is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const { contextBridge, ipcRenderer } = require("electron");
|
||||
|
||||
/*
|
||||
Expose dans le contexte global (window.electronAPI) deux méthodes sécurisées
|
||||
pour que le renderer puisse interagir avec le main process via IPC.
|
||||
|
||||
- getDefaultDownloadPath : récupère le chemin de téléchargement par défaut.
|
||||
- selectDownloadFolder : ouvre la boîte de dialogue pour choisir un dossier.
|
||||
*/
|
||||
contextBridge.exposeInMainWorld("electronAPI", {
|
||||
getDefaultDownloadPath: () => ipcRenderer.invoke("get-default-download-path"),
|
||||
selectDownloadFolder: () => ipcRenderer.invoke("select-download-folder"),
|
||||
});
|
||||
BIN
public/assets/images/Chirac.png
Normal file
|
After Width: | Height: | Size: 3.2 MiB |
BIN
public/assets/images/Cyberpunk.webp
Normal file
|
After Width: | Height: | Size: 805 KiB |
BIN
public/assets/images/NF.jpg
Normal file
|
After Width: | Height: | Size: 139 KiB |
BIN
public/assets/images/Vilbrequin.webp
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
public/assets/images/spicy.webp
Normal file
|
After Width: | Height: | Size: 16 KiB |
@@ -9,7 +9,7 @@
|
||||
font-src https://fonts.gstatic.com;
|
||||
img-src 'self' data: https://i.ytimg.com;
|
||||
">
|
||||
<title>Freedom Loader</title>
|
||||
<title></title>
|
||||
|
||||
<!--CSS-->
|
||||
<link rel="stylesheet" href="styles/styles.css">
|
||||
@@ -22,8 +22,8 @@
|
||||
<div class="container">
|
||||
|
||||
<header>
|
||||
<h1> Freedom Loader </h1>
|
||||
<p> Because why not? </p>
|
||||
<h1 id="title"> Freedom Loader </h1>
|
||||
<p id="subtitle"> Because why not? </p>
|
||||
</header>
|
||||
|
||||
<form action="/download" method="POST" id="downloadForm">
|
||||
@@ -51,24 +51,40 @@
|
||||
<button type="submit">Télécharger</button>
|
||||
</form>
|
||||
|
||||
<div id="downloadStatus"></div>
|
||||
|
||||
<!--Il faut utiliser un formulaire, parce que on doit envoyer a un serveur
|
||||
les données à traiter. Pour cela on définit une route vers /download, ca va
|
||||
être l'espèce de "canal" attribué a ça. Ca va faire que si jamais on veut ensuite recup
|
||||
les infos du form, il faut aller sur ce canal précis (/download). La méthode POST dit
|
||||
que l'on envoie des données.-->
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="download-path">
|
||||
Sera enregistré dans <span id="savePath"></span>
|
||||
<button id="changePath">Modifier</button>
|
||||
</div>
|
||||
|
||||
<div id="downloadStatus"></div>
|
||||
|
||||
<div class="infos-container">
|
||||
<div id="videoInfo" style="margin-top: 20px;"></div>
|
||||
</div>
|
||||
|
||||
<footer class="theme-switcher">
|
||||
<label for="themeSelect">Thème :</label>
|
||||
<select id="themeSelect" aria-label="Choisir le thème">
|
||||
<!-- <option value="dark">Sombre</option>
|
||||
<option value="light">Clair</option>
|
||||
<option value="Chirac">Chirac</option>
|
||||
<option value="Fanatic">Fanatic</option> -->
|
||||
</select>
|
||||
</footer>
|
||||
|
||||
<script src="script/custompath.js"></script>
|
||||
<script src="script/downloadstatus.js"></script>
|
||||
<script src="script/fetchinfo.js"></script>
|
||||
<script src="script/chirac.js"></script>
|
||||
<script src="script/customthemes.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
33
public/script/chirac.js
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
This file is part of Freedom Loader.
|
||||
|
||||
Copyright (C) 2025 MasterAcnolo
|
||||
|
||||
Freedom Loader is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License.
|
||||
|
||||
Freedom Loader is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
function Chirac(){
|
||||
const title = document.getElementById("title");
|
||||
title.innerHTML = "Chirac Loader";
|
||||
|
||||
const subtitle = document.getElementById("subtitle");
|
||||
subtitle.innerHTML = "J'aime les pommes";
|
||||
subtitle.style.color= "black";
|
||||
|
||||
document.body.style.backgroundImage = "url('assets/images/goat2.webp')";
|
||||
document.body.style.backgroundSize = "cover";
|
||||
document.body.style.backgroundPosition = "center";
|
||||
document.body.style.backgroundAttachment = "fixed";
|
||||
|
||||
console.log("Je serai le président de tous les français");
|
||||
}
|
||||
71
public/script/custompath.js
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
This file is part of Freedom Loader.
|
||||
|
||||
Copyright (C) 2025 MasterAcnolo
|
||||
|
||||
Freedom Loader is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License.
|
||||
|
||||
Freedom Loader is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
Ce script attend que le DOM soit complètement chargé pour initialiser
|
||||
l'affichage et la gestion du chemin de sauvegarde personnalisé.
|
||||
|
||||
1. Récupère le chemin de téléchargement par défaut depuis le main process via l'API exposée.
|
||||
2. Met à jour le texte affiché dans l'élément avec l'id "savePath".
|
||||
3. Crée un input caché nommé "savePath" dans le formulaire pour envoyer ce chemin lors du submit.
|
||||
4. Ajoute un écouteur sur le bouton "changePath" pour permettre à l'utilisateur
|
||||
de choisir un dossier via une boîte de dialogue native.
|
||||
5. Met à jour l'affichage et la valeur cachée du chemin sélectionné.
|
||||
*/
|
||||
|
||||
window.addEventListener("DOMContentLoaded", async () => {
|
||||
const savePathElem = document.getElementById("savePath");
|
||||
|
||||
// 1️Essayer de charger depuis le localStorage
|
||||
let savedPath = localStorage.getItem("customDownloadPath");
|
||||
|
||||
// 2️Sinon demander le chemin par défaut à l'API Electron
|
||||
if (!savedPath) {
|
||||
savedPath = await window.electronAPI.getDefaultDownloadPath();
|
||||
}
|
||||
|
||||
// 3 Afficher le chemin
|
||||
if (savePathElem) {
|
||||
savePathElem.textContent = savedPath;
|
||||
}
|
||||
|
||||
// Créer l'input caché s'il n'existe pas déjà
|
||||
let hidden = document.getElementById("savePathInput");
|
||||
if (!hidden) {
|
||||
hidden = document.createElement("input");
|
||||
hidden.type = "hidden";
|
||||
hidden.name = "savePath";
|
||||
hidden.id = "savePathInput";
|
||||
document.getElementById("downloadForm").appendChild(hidden);
|
||||
}
|
||||
hidden.value = savedPath;
|
||||
|
||||
// Gestion du bouton de modification
|
||||
document.getElementById("changePath").addEventListener("click", async () => {
|
||||
const selectedPath = await window.electronAPI.selectDownloadFolder();
|
||||
if (selectedPath) {
|
||||
// Met à jour l'affichage
|
||||
savePathElem.textContent = selectedPath;
|
||||
hidden.value = selectedPath;
|
||||
|
||||
// Et le stocke en localStorage pour la prochaine fois
|
||||
localStorage.setItem("customDownloadPath", selectedPath);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
107
public/script/customthemes.js
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
This file is part of Freedom Loader.
|
||||
|
||||
Copyright (C) 2025 MasterAcnolo
|
||||
|
||||
Freedom Loader is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License.
|
||||
|
||||
Freedom Loader is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// Liste des thèmes disponibles (clé = nom, valeur = label affiché)
|
||||
const themes = {
|
||||
default: "Default",
|
||||
dark: "Sombre",
|
||||
light: "Clair",
|
||||
nf : "NF",
|
||||
chirac: "Chirac",
|
||||
fanatic: "Fanatic",
|
||||
cyberpunk: "Cyberpunk",
|
||||
spicy: "Spicy",
|
||||
vilbrequin: "Vilbrequin"
|
||||
};
|
||||
|
||||
const themeSubtitles = {
|
||||
default: "Because why not?",
|
||||
dark: "Darkness is my ally",
|
||||
light: "Qui aime ce thème ?",
|
||||
nf : "You call it music, i call it my Therapist",
|
||||
chirac: "J'aime les pommes",
|
||||
fanatic: "Always Fnatic !",
|
||||
cyberpunk: "Wake up, choom. We’ve got a city to burn.",
|
||||
spicy: "The Spiciest One",
|
||||
vilbrequin: "Rend l'argent"
|
||||
};
|
||||
|
||||
const themeSelect = document.getElementById("themeSelect");
|
||||
|
||||
// Remplir le select avec les options
|
||||
function populateThemeSelect() {
|
||||
for (const [key, label] of Object.entries(themes)) {
|
||||
const option = document.createElement("option");
|
||||
option.value = key;
|
||||
option.textContent = label;
|
||||
themeSelect.appendChild(option);
|
||||
}
|
||||
}
|
||||
|
||||
// Appliquer le thème sur le body et changer le subtitle
|
||||
function applyTheme(themeName) {
|
||||
// Supprime les classes thème précédentes
|
||||
document.body.classList.remove(...Object.keys(themes));
|
||||
// Ajoute la classe du thème actuel
|
||||
document.body.classList.add(themeName);
|
||||
|
||||
// Modifier le subtitle
|
||||
const subtitle = document.getElementById("subtitle");
|
||||
if (subtitle && themeSubtitles[themeName]) {
|
||||
subtitle.textContent = themeSubtitles[themeName];
|
||||
}
|
||||
|
||||
// Optionnel : animation spéciale Chirac
|
||||
if (themeName === "chirac") {
|
||||
requestAnimationFrame(() => {
|
||||
document.body.classList.add("theme-active");
|
||||
});
|
||||
} else {
|
||||
document.body.classList.remove("theme-active");
|
||||
}
|
||||
}
|
||||
|
||||
// Sauvegarder le thème en localStorage
|
||||
function saveTheme(themeName) {
|
||||
localStorage.setItem("selectedTheme", themeName);
|
||||
}
|
||||
|
||||
// Charger le thème au démarrage
|
||||
function loadTheme() {
|
||||
const savedTheme = localStorage.getItem("selectedTheme");
|
||||
if (savedTheme && themes[savedTheme]) {
|
||||
applyTheme(savedTheme);
|
||||
themeSelect.value = savedTheme;
|
||||
} else {
|
||||
applyTheme("dark");
|
||||
themeSelect.value = "dark";
|
||||
}
|
||||
}
|
||||
|
||||
// Quand l’utilisateur change le select
|
||||
themeSelect.addEventListener("change", (e) => {
|
||||
const selected = e.target.value;
|
||||
if (themes[selected]) {
|
||||
applyTheme(selected);
|
||||
saveTheme(selected);
|
||||
}
|
||||
});
|
||||
|
||||
// Initialisation
|
||||
populateThemeSelect();
|
||||
loadTheme();
|
||||
@@ -19,53 +19,53 @@
|
||||
// Récupération du formulaire de téléchargement
|
||||
const form = document.getElementById("downloadForm");
|
||||
|
||||
// Div où on affiche l'état de progression (en cours, erreur, succès)
|
||||
// Élément où on affichera l’état du téléchargement (en cours, erreur, succès)
|
||||
const statusDiv = document.getElementById("downloadStatus");
|
||||
|
||||
// Récupération du bouton de validation pour pouvoir le désactiver pendant la requête
|
||||
// Récupération du bouton de soumission pour pouvoir le désactiver pendant la requête
|
||||
const button = form.querySelector("button");
|
||||
|
||||
// On intercepte la soumission du formulaire
|
||||
// Écouteur d’événement pour intercepter la soumission du formulaire
|
||||
form.addEventListener("submit", async (e) => {
|
||||
e.preventDefault(); // empêche le rechargement de la page par le navigateur
|
||||
e.preventDefault(); // Empêche le rechargement automatique de la page
|
||||
|
||||
// On désactive le bouton pour éviter les clics multiples
|
||||
// Désactive le bouton pour éviter les clics multiples pendant la requête
|
||||
button.disabled = true;
|
||||
|
||||
// Affiche un message d'attente à l'utilisateur
|
||||
// Affiche un message d’attente pour informer l’utilisateur
|
||||
statusDiv.textContent = "Téléchargement en cours...";
|
||||
|
||||
// Prépare les données du formulaire sous forme URL-encoded
|
||||
// Prépare les données du formulaire au format URL-encoded
|
||||
const formData = new FormData(form);
|
||||
const params = new URLSearchParams(formData);
|
||||
|
||||
try {
|
||||
// Envoi de la requête POST vers /download avec les données du formulaire
|
||||
// Envoie la requête POST vers /download avec les données du formulaire
|
||||
const res = await fetch("/download", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||
body: params,
|
||||
});
|
||||
|
||||
// Si la réponse n'est pas un succès (code 4xx ou 5xx)
|
||||
// Si la réponse n’est pas un succès (code HTTP 4xx ou 5xx)
|
||||
if (!res.ok) {
|
||||
statusDiv.textContent = "❌ Erreur pendant le téléchargement.";
|
||||
return;
|
||||
}
|
||||
|
||||
// On récupère le texte de la réponse du serveur
|
||||
// Récupère le texte envoyé par le serveur (message de succès ou erreur)
|
||||
const text = await res.text();
|
||||
|
||||
// Affiche le message de fin de téléchargement (par exemple « Téléchargement terminé »)
|
||||
// Affiche le message retourné par le serveur
|
||||
statusDiv.textContent = text;
|
||||
} catch {
|
||||
// Gestion d'erreur en cas de problème réseau ou autre
|
||||
// Gestion des erreurs réseau ou autres exceptions
|
||||
statusDiv.textContent = "❌ Une erreur s’est produite.";
|
||||
} finally {
|
||||
// Réactive le bouton pour autoriser d'autres soumissions
|
||||
// Réactive le bouton pour permettre d’autres soumissions
|
||||
button.disabled = false;
|
||||
|
||||
// Efface le message de status après 5 secondes pour garder l'UI propre
|
||||
// Efface le message d’état après 5 secondes pour garder l’interface propre
|
||||
setTimeout(() => {
|
||||
statusDiv.textContent = "";
|
||||
}, 5000);
|
||||
|
||||
@@ -16,17 +16,25 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// On attend que le DOM soit complètement chargé
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
// Récupération des éléments du DOM pour interaction
|
||||
const urlInput = document.getElementById("UrlInput");
|
||||
const infoDiv = document.getElementById("videoInfo");
|
||||
|
||||
// Stocke la dernière URL fetchée pour éviter les requêtes répétées inutiles
|
||||
let lastFetchedUrl = "";
|
||||
|
||||
// Active les logs DEBUG pour le développement
|
||||
const DEBUG = true;
|
||||
|
||||
// Écoute les changements dans l'input URL
|
||||
urlInput.addEventListener("input", async () => {
|
||||
const url = urlInput.value.trim();
|
||||
|
||||
if (DEBUG) console.log("[DEBUG] Input détecté :", url);
|
||||
|
||||
// Si l'URL est vide ou trop courte, on reset l'affichage
|
||||
if (!url || url.length < 5) {
|
||||
if (DEBUG) console.log("[DEBUG] URL vide ou trop courte, reset affichage.");
|
||||
infoDiv.innerHTML = "";
|
||||
@@ -35,15 +43,19 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
return;
|
||||
}
|
||||
|
||||
// Si l'URL est identique à la dernière requêtée, on ne refait pas la requête
|
||||
if (url === lastFetchedUrl) {
|
||||
if (DEBUG) console.log("[DEBUG] Même URL que précédemment, pas de fetch.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Mémorise l'URL actuelle comme dernière URL traitée
|
||||
lastFetchedUrl = url;
|
||||
|
||||
try {
|
||||
if (DEBUG) console.log("[DEBUG] Envoi requête POST vers /info avec URL:", url);
|
||||
|
||||
// Envoi de la requête POST pour récupérer les infos de la vidéo
|
||||
const res = await fetch("/info", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||
@@ -52,6 +64,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
|
||||
if (DEBUG) console.log("[DEBUG] Réponse status:", res.status);
|
||||
|
||||
// Si la réponse HTTP est une erreur, on affiche un message
|
||||
if (!res.ok) {
|
||||
console.error(`[ERROR] Réponse HTTP ${res.status}: ${res.statusText}`);
|
||||
infoDiv.innerHTML = "❌ Impossible de récupérer les infos (erreur serveur).";
|
||||
@@ -59,10 +72,11 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse la réponse JSON contenant les infos vidéo
|
||||
const data = await res.json();
|
||||
if (DEBUG) console.log("[DEBUG] Données reçues:", data);
|
||||
|
||||
// Vérification minimaliste
|
||||
// Vérification simple que les données attendues sont présentes
|
||||
if (!data || !data.title || !data.thumbnail) {
|
||||
console.warn("[WARNING] Données incomplètes", data);
|
||||
infoDiv.innerHTML = "❌ Données incomplètes reçues.";
|
||||
@@ -70,23 +84,28 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
return;
|
||||
}
|
||||
|
||||
// Formatage de la durée en minutes et secondes
|
||||
const minutes = Math.floor(data.duration / 60);
|
||||
const seconds = data.duration % 60;
|
||||
const durationStr = `${minutes}m ${seconds.toString().padStart(2, "0")}s`;
|
||||
|
||||
// Formatage de la date d'upload au format JJ/MM/AAAA
|
||||
const date = data.upload_date || "";
|
||||
const readableDate = date
|
||||
? `${date.slice(6, 8)}/${date.slice(4, 6)}/${date.slice(0, 4)}`
|
||||
: "Inconnue";
|
||||
|
||||
// Conversion taille approximative en Mo
|
||||
const sizeMB = data.filesize_approx
|
||||
? (data.filesize_approx / (1024 * 1024)).toFixed(2) + " Mo"
|
||||
: "Inconnue";
|
||||
|
||||
// Gestion des catégories, affichage par défaut si absentes
|
||||
const categories = data.categories
|
||||
? data.categories.join(", ")
|
||||
: "Non spécifiées";
|
||||
|
||||
// Construction du HTML pour afficher les informations de la vidéo
|
||||
infoDiv.innerHTML = `
|
||||
<h3>${data.title}</h3>
|
||||
<img src="${data.thumbnail}" width="320" alt="Thumbnail">
|
||||
@@ -102,10 +121,13 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
<li><strong>Catégories :</strong> ${categories}</li>
|
||||
</ul>
|
||||
`;
|
||||
|
||||
// Affiche la div contenant les infos
|
||||
infoDiv.classList.add("visible");
|
||||
|
||||
if (DEBUG) console.log("[DEBUG] Info affichée avec succès.");
|
||||
} catch (e) {
|
||||
// En cas d'erreur réseau ou JSON, on affiche un message d'erreur
|
||||
console.error("[CRITICAL] Erreur lors de la récupération:", e);
|
||||
infoDiv.innerHTML = "❌ Erreur réseau ou JSON.";
|
||||
infoDiv.classList.remove("visible");
|
||||
|
||||
@@ -15,15 +15,15 @@
|
||||
}
|
||||
|
||||
.checkmark {
|
||||
--clr: #3B82F6 ;
|
||||
--clr: var(--checkbox-bg-checked);
|
||||
position: relative;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 1.3em;
|
||||
width: 1.3em;
|
||||
background-color: #ccc;
|
||||
border-radius: 50%;
|
||||
transition: 300ms;
|
||||
background-color: var(--checkbox-bg-default);
|
||||
border-radius: .5rem;
|
||||
transition: background-color 300ms, border-radius 300ms;
|
||||
}
|
||||
|
||||
.checkbox-mp3 input:checked ~ .checkmark {
|
||||
@@ -47,7 +47,7 @@
|
||||
top: 0.25em;
|
||||
width: 0.25em;
|
||||
height: 0.5em;
|
||||
border: solid #E0E0E2;
|
||||
border: solid var(--checkbox-checkmark-border-color);
|
||||
border-width: 0 0.15em 0.15em 0;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
42
public/styles/components/editpathbutton.css
Normal file
@@ -0,0 +1,42 @@
|
||||
.download-path {
|
||||
margin-top: 1em;
|
||||
font-size: 1rem;
|
||||
color: var(--default-text-color);
|
||||
}
|
||||
|
||||
.download-path #savePath:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.download-path #savePath {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
color: var(--form-input-text-color);
|
||||
padding: 0.3em 0.6em;
|
||||
border-radius: 6px;
|
||||
font-family: monospace;
|
||||
border: 1px dashed var(--form-input-border-color);
|
||||
word-break: break-all;
|
||||
display: inline-block;
|
||||
max-width: 80%;
|
||||
}
|
||||
|
||||
|
||||
.download-path button#changePath {
|
||||
margin-left: 0.5em;
|
||||
background-color: var(--form-button-bg-color);
|
||||
color: var(--form-button-text-color);
|
||||
border: none;
|
||||
padding: 0.3em 0.8em;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s ease, transform 0.2s ease;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
|
||||
font-size: 0.9rem;
|
||||
font-weight: 550;
|
||||
}
|
||||
|
||||
.download-path button#changePath:hover {
|
||||
background-color: var(--form-button-bg-hover-color);
|
||||
}
|
||||
38
public/styles/components/themebutton.css
Normal file
@@ -0,0 +1,38 @@
|
||||
.theme-switcher {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
background: #242424;
|
||||
padding: 8px 12px;
|
||||
border-radius: 8px;
|
||||
color: #fff;
|
||||
font-family: sans-serif;
|
||||
font-size: 0.9rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.theme-switcher label {
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.theme-switcher select {
|
||||
background: #fff;
|
||||
border: 1px solid #242424;
|
||||
border-radius: 6px;
|
||||
padding: 4px 8px;
|
||||
color: #000;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
transition: background 0.3s ease, color 0.3s ease;
|
||||
}
|
||||
|
||||
.theme-switcher select:hover,
|
||||
.theme-switcher select:focus {
|
||||
background: #474747;
|
||||
color: #fff;
|
||||
outline: none;
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
min-height: 1.5em;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
color: #007bff;
|
||||
color: var(--download-status-color);
|
||||
}
|
||||
|
||||
label {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
form {
|
||||
background-color: #fff;
|
||||
background-color: var(--form-bg-color);
|
||||
padding: 2em;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
||||
@@ -13,8 +13,10 @@ form input[type="text"],
|
||||
form input[type="url"],
|
||||
form select {
|
||||
padding: 0.75em;
|
||||
border: 1px solid #ccc;
|
||||
border: 1px solid var(--form-input-border-color);
|
||||
border-radius: 8px;
|
||||
background-color: var(--form-input-bg-color);
|
||||
color: var(--form-input-text-color);
|
||||
}
|
||||
|
||||
form input[type="checkbox"] {
|
||||
@@ -22,8 +24,8 @@ form input[type="checkbox"] {
|
||||
}
|
||||
|
||||
form button {
|
||||
background-color: #007bff;
|
||||
color: #fff;
|
||||
background-color: var(--form-button-bg-color);
|
||||
color: var(--form-button-text-color);
|
||||
border: none;
|
||||
padding: 0.75em;
|
||||
border-radius: 8px;
|
||||
@@ -32,7 +34,7 @@ form button {
|
||||
}
|
||||
|
||||
form button:hover {
|
||||
background-color: #0056b3;
|
||||
background-color: var(--form-button-bg-hover-color);
|
||||
}
|
||||
|
||||
form#downloadForm {
|
||||
@@ -48,26 +50,27 @@ form#downloadForm {
|
||||
align-items: center;
|
||||
align-self: center;
|
||||
gap: 0.5em;
|
||||
color: var(--form-input-text-color);
|
||||
}
|
||||
|
||||
#UrlInput {
|
||||
padding: 0.75em 1em;
|
||||
border-color: #ccc;
|
||||
border-color: var(--form-input-border-color);
|
||||
box-shadow: none;
|
||||
border-radius: 8px;
|
||||
font-size: 1rem;
|
||||
transition: border-color 0.3s ease, box-shadow 0.3s ease;
|
||||
background-color: #f9f9f9;
|
||||
color: #333;
|
||||
background-color: var(--form-input-bg-color);
|
||||
color: var(--form-input-text-color);
|
||||
}
|
||||
|
||||
#UrlInput:focus {
|
||||
border-color: #0056b3;
|
||||
border-color: var(--form-input-border-focus-color);
|
||||
box-shadow: 0 0 5px rgba(0,123,255,0.4);
|
||||
outline: none;
|
||||
background-color: #fff;
|
||||
background-color: var(--form-input-bg-color);
|
||||
}
|
||||
|
||||
#UrlInput::placeholder {
|
||||
color: #aaa;
|
||||
color: var(--form-input-placeholder-color);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
background-color: var(--infos-box-color);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 8px 16px rgba(0,0,0,0.1);
|
||||
color: #222;
|
||||
color: var(--video-info-text-color);
|
||||
font-size: 1rem;
|
||||
line-height: 1.5;
|
||||
opacity: 0;
|
||||
@@ -27,7 +27,7 @@
|
||||
#videoInfo h3 {
|
||||
font-size: 1.8rem;
|
||||
margin-bottom: 0.6em;
|
||||
color: #007bff;
|
||||
color: var(--video-info-heading-color);
|
||||
font-weight: 700;
|
||||
word-break: break-word;
|
||||
}
|
||||
@@ -51,15 +51,15 @@
|
||||
#videoInfo ul li {
|
||||
margin-bottom: 0.5em;
|
||||
font-weight: 500;
|
||||
color: #444;
|
||||
color: var(--video-info-list-color);
|
||||
}
|
||||
|
||||
#videoInfo ul li strong {
|
||||
color: #000;
|
||||
color: var(--video-info-list-strong-color);
|
||||
}
|
||||
|
||||
#videoInfo ul li a {
|
||||
color: #007bff;
|
||||
color: var(--video-info-link-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,22 @@
|
||||
@import url("layout/header.css");
|
||||
@import url("layout/videoinfo.css");
|
||||
@import url("components/checkbox.css");
|
||||
@import url("variables.css");
|
||||
@import url("components/themebutton.css");
|
||||
/* @import url("variables.css"); */
|
||||
@import url("theme/themeimport.css");
|
||||
@import url("components/editpathbutton.css");
|
||||
|
||||
@import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap');
|
||||
|
||||
#app {
|
||||
display: block;
|
||||
opacity: 0;
|
||||
transition: opacity 0.6s ease;
|
||||
}
|
||||
#app.visible {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
html,body{
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
@@ -35,6 +47,7 @@ body {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
|
||||
}
|
||||
|
||||
header p{
|
||||
@@ -45,22 +58,11 @@ header p{
|
||||
color: var(--mp3-text-color);
|
||||
}
|
||||
|
||||
/* #videoInfo {
|
||||
margin-top: 100px;
|
||||
padding: 1em;
|
||||
max-width: 600px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
border-radius: 8px;
|
||||
color: rgb(0, 0, 0);
|
||||
box-shadow: 0 0 10px rgba(0,0,0,0.5);
|
||||
transition: opacity 0.5s ease, max-height 0.5s ease;
|
||||
opacity: 0;
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
body,
|
||||
.container,
|
||||
input,
|
||||
button,
|
||||
select,
|
||||
label {
|
||||
transition: background-color 0.2s ease, color 0.5s ease, border-color 0.2s ease;
|
||||
}
|
||||
|
||||
#videoInfo.visible {
|
||||
opacity: 1;
|
||||
max-height: 1000px;
|
||||
} */
|
||||
|
||||
98
public/styles/theme/chirac-theme.css
Normal file
@@ -0,0 +1,98 @@
|
||||
body.chirac {
|
||||
/* Background image Chirac */
|
||||
background-image: url('../../assets/images/Chirac.png');
|
||||
/* background-position: right; */
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
background-attachment: fixed;
|
||||
|
||||
/* Couleurs drapeau français */
|
||||
--background-color: rgba(255, 255, 255, 0.85); /* blanc légèrement transparent pour lisibilité */
|
||||
--default-text-color: #002395; /* bleu foncé */
|
||||
--subtitle-color: #EF4135; /* rouge vif */
|
||||
--mp3-text-color: #002395; /* bleu */
|
||||
|
||||
/* Formulaire */
|
||||
--form-bg-color: rgba(255, 255, 255, 0.9);
|
||||
--form-input-bg-color: #f5f5f5;
|
||||
--form-input-border-color: #002395;
|
||||
--form-input-border-focus-color: #EF4135;
|
||||
--form-input-text-color: #002395;
|
||||
--form-input-placeholder-color: #999999;
|
||||
--form-button-bg-color: #002395; /* bleu */
|
||||
--form-button-text-color: #ffffff;
|
||||
--form-button-bg-hover-color: #EF4135; /* rouge */
|
||||
|
||||
/* Checkbox */
|
||||
--checkbox-bg-default: #cccccc;
|
||||
--checkbox-bg-checked: #002395;
|
||||
--checkbox-checkmark-border-color: #ffffff;
|
||||
--checkbox-pulse-color: rgba(0, 35, 149, 0.5);
|
||||
|
||||
/* Download status */
|
||||
--download-status-color: #002395;
|
||||
|
||||
/* Video Info Box */
|
||||
--infos-box-color: rgba(255, 255, 255, 0.9);
|
||||
--video-info-text-color: #002395;
|
||||
--video-info-heading-color: #EF4135;
|
||||
--video-info-list-color: #444444;
|
||||
--video-info-list-strong-color: #000000;
|
||||
--video-info-link-color: #002395;
|
||||
--video-info-link-hover-color: #EF4135;
|
||||
|
||||
/* animation d'apparition */
|
||||
opacity: 0;
|
||||
transition: opacity 0.8s ease, background-color 0.8s ease;
|
||||
}
|
||||
|
||||
body.chirac.theme-active {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Je serai le président de tous les Français
|
||||
Tous les Français
|
||||
Tous les Français
|
||||
Tous les Français
|
||||
Tous les Français
|
||||
Tous les Français
|
||||
Tous les Français
|
||||
Tous les Français
|
||||
Tous les Français
|
||||
Tous les Français
|
||||
Mes chers compatriotes
|
||||
Je serai le président de tous les Français, de tous les Français
|
||||
Je mesure la difficulté de la tâche qui nous attend
|
||||
Tous les Français
|
||||
Tous les Français
|
||||
Je veux un État, je veux les Français
|
||||
Je veux un État, je veux
|
||||
Je veux un État-tat-tat-tat vigoureux
|
||||
Un État-tat-tat-tat impartial
|
||||
Un État-tat-tat-tat vigoureux
|
||||
Un État-tat-tat-tat exigeant
|
||||
Un État-tat-tat-tat
|
||||
Je serai le président de tous les Français
|
||||
Tous les Français
|
||||
Tous les Français
|
||||
Tous les Français
|
||||
Un État qui n'isole pas, tous, pas, pas
|
||||
Tous, pas, tous, pas, pas
|
||||
Tous, pas, tous, pas, pas (les Français)
|
||||
Tous, tous, pas, tous, tous, pas, pas
|
||||
Tous, tous, pas, tous, tous, pas, pas
|
||||
Tous, tous, tous, pas, tous, pas, pas (les Français)
|
||||
Tous, pas, tous, tous, pas, pas
|
||||
Tous, tous, tous, pas, tous, tous, pas, pas
|
||||
Tous, tous, pas, tous, tous, pas, pas
|
||||
Tous, tous, tous-tous, tous-tous, pas, pas
|
||||
Tous, tous-tous, tous-tous, pas, pas
|
||||
Un État-tat-tat-tat vigoureux
|
||||
Un État-tat-tat-tat impartial
|
||||
Un État-tat-tat-tat exigeant
|
||||
Un État-tat-tat-tat
|
||||
Je serai le président de tous les Français
|
||||
|
||||
*/
|
||||
45
public/styles/theme/cyberpunk-theme.css
Normal file
@@ -0,0 +1,45 @@
|
||||
body.cyberpunk {
|
||||
/* Couleurs générales */
|
||||
--background-color: #0d0d0d; /* noir profond */
|
||||
--default-text-color: #e0e0e0; /* texte gris clair */
|
||||
--subtitle-color: #ffea00; /* turquoise néon pour les sous-titres */
|
||||
--mp3-text-color: #ffea00; /* jaune vif pour la checkbox */
|
||||
|
||||
/* Formulaire */
|
||||
--form-bg-color: #141414; /* fond formulaire sombre */
|
||||
--form-input-bg-color: #1a1a1a; /* inputs sombres */
|
||||
--form-input-border-color: #333333; /* bordure sobre */
|
||||
--form-input-border-focus-color: #ffea00; /* focus jaune néon */
|
||||
--form-input-text-color: #e0e0e0; /* texte clair */
|
||||
--form-input-placeholder-color: #777777; /* placeholder plus doux */
|
||||
--form-button-bg-color: #ffea00; /* bouton jaune */
|
||||
--form-button-text-color: #0d0d0d; /* texte bouton noir */
|
||||
--form-button-bg-hover-color: #d4c000; /* hover jaune plus foncé */
|
||||
|
||||
/* Checkbox */
|
||||
--checkbox-bg-default: #333333; /* fond checkbox sobre */
|
||||
--checkbox-bg-checked: #ffea00; /* coché en jaune vif */
|
||||
--checkbox-checkmark-border-color: #0d0d0d; /* checkmark noir */
|
||||
--checkbox-pulse-color: rgba(255, 234, 0, 0.5); /* pulse néon jaune */
|
||||
|
||||
/* Download status */
|
||||
--download-status-color: #fffbd1; /* turquoise néon */
|
||||
|
||||
/* Video Info Box */
|
||||
--infos-box-color: #1a1a1a; /* fond infos */
|
||||
--video-info-text-color: #e0e0e0; /* texte clair */
|
||||
--video-info-heading-color: #ffea00; /* titres turquoise néon */
|
||||
--video-info-list-color: #cccccc; /* texte liste gris clair */
|
||||
--video-info-list-strong-color: #ffffff; /* texte important blanc */
|
||||
--video-info-link-color: #ffea00; /* liens jaune néon */
|
||||
--video-info-link-hover-color: #d4c000; /* liens hover plus foncé */
|
||||
}
|
||||
|
||||
body.cyberpunk {
|
||||
background-image: url('../../assets/images/Cyberpunk.webp');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-attachment: fixed;
|
||||
}
|
||||
|
||||
36
public/styles/theme/dark-theme.css
Normal file
@@ -0,0 +1,36 @@
|
||||
body.dark {
|
||||
/* Couleurs générales */
|
||||
--background-color: #121212; /* noir/gris très foncé */
|
||||
--default-text-color: #e0e0e0; /* texte clair */
|
||||
--subtitle-color: #2196f3; /* bleu accent */
|
||||
--mp3-text-color: #f5f5f5; /* texte checkbox */
|
||||
|
||||
/* Formulaire */
|
||||
--form-bg-color: #1e1e1e; /* fond formulaire */
|
||||
--form-input-bg-color: #2a2a2a; /* fond inputs */
|
||||
--form-input-border-color: #444444; /* bordure */
|
||||
--form-input-border-focus-color: #2196f3; /* focus bleu */
|
||||
--form-input-text-color: #f5f5f5; /* texte clair */
|
||||
--form-input-placeholder-color: #888888; /* placeholder gris */
|
||||
--form-button-bg-color: #2196f3; /* bouton bleu */
|
||||
--form-button-text-color: #ffffff; /* texte bouton */
|
||||
--form-button-bg-hover-color: #1976d2; /* hover bleu plus foncé */
|
||||
|
||||
/* Checkbox */
|
||||
--checkbox-bg-default: #555555; /* gris moyen */
|
||||
--checkbox-bg-checked: #2196f3; /* bleu accent */
|
||||
--checkbox-checkmark-border-color: #ffffff; /* blanc */
|
||||
--checkbox-pulse-color: rgba(33, 150, 243, 0.5);
|
||||
|
||||
/* Download status */
|
||||
--download-status-color: #2196f3;
|
||||
|
||||
/* Video Info Box */
|
||||
--infos-box-color: #1a1a1a;
|
||||
--video-info-text-color: #e0e0e0;
|
||||
--video-info-heading-color: #2196f3;
|
||||
--video-info-list-color: #cccccc;
|
||||
--video-info-list-strong-color: #ffffff;
|
||||
--video-info-link-color: #2196f3;
|
||||
--video-info-link-hover-color: #1976d2;
|
||||
}
|
||||
40
public/styles/theme/default-theme.css
Normal file
@@ -0,0 +1,40 @@
|
||||
:root {
|
||||
/* Couleurs générales */
|
||||
--background-color: #001224; /* body background bleu nuit */
|
||||
--default-text-color: #eee; /* texte clair principal */
|
||||
--subtitle-color: #007bff; /* sous-titres, header p */
|
||||
--mp3-text-color: #000000; /* couleur texte checkbox inline */
|
||||
|
||||
/* Formulaire */
|
||||
--form-bg-color: #ffffff; /* fond du formulaire */
|
||||
--form-input-bg-color: #f9f9f9; /* fond inputs */
|
||||
--form-input-border-color: #cccccc; /* bordure inputs */
|
||||
--form-input-border-focus-color: #0056b3; /* bordure focus */
|
||||
--form-input-text-color: #333333; /* texte inputs */
|
||||
--form-input-placeholder-color: #aaaaaa; /* placeholder */
|
||||
--form-button-bg-color: #007bff; /* bouton bg */
|
||||
--form-button-text-color: #ffffff; /* texte bouton */
|
||||
--form-button-bg-hover-color: #0056b3; /* bouton hover */
|
||||
|
||||
/* Checkbox */
|
||||
--checkbox-bg-default: #cccccc;
|
||||
--checkbox-bg-checked: #3B82F6;
|
||||
--checkbox-checkmark-border-color: #E0E0E2;
|
||||
--checkbox-pulse-color: rgba(59, 130, 246, 0.5);
|
||||
|
||||
/* Download status */
|
||||
--download-status-color: #007bff;
|
||||
|
||||
/* Video Info Box */
|
||||
--infos-box-color: #f0f0f0;
|
||||
--video-info-text-color: #222222;
|
||||
--video-info-heading-color: #007bff;
|
||||
--video-info-list-color: #444444;
|
||||
--video-info-list-strong-color: #000000;
|
||||
--video-info-link-color: #007bff;
|
||||
--video-info-link-hover-color: #0056b3;
|
||||
}
|
||||
|
||||
body.default .download-path #savePath {
|
||||
color: #ffffff;
|
||||
}
|
||||
36
public/styles/theme/fanatic-theme.css
Normal file
@@ -0,0 +1,36 @@
|
||||
body.fanatic{
|
||||
/* Couleurs générales */
|
||||
--background-color: #121212; /* fond bleu nuit/noir */
|
||||
--default-text-color: #eee; /* texte clair principal */
|
||||
--subtitle-color: #FF5900; /* sous-titres, accent orange vif */
|
||||
--mp3-text-color: #ffffff; /* texte checkbox inline */
|
||||
|
||||
/* Formulaire */
|
||||
--form-bg-color: #1e1e1e; /* fond formulaire sombre */
|
||||
--form-input-bg-color: #2b2b2b; /* fond inputs sombres */
|
||||
--form-input-border-color: #333333; /* bordure inputs */
|
||||
--form-input-border-focus-color: #FF5900; /* bordure focus orange */
|
||||
--form-input-text-color: #eeeeee; /* texte inputs clair */
|
||||
--form-input-placeholder-color: #666666; /* placeholder gris foncé */
|
||||
--form-button-bg-color: #FF5900; /* bouton orange */
|
||||
--form-button-text-color: #121212; /* texte bouton sombre */
|
||||
--form-button-bg-hover-color: #e65500; /* bouton hover orange foncé */
|
||||
|
||||
/* Checkbox */
|
||||
--checkbox-bg-default: #555555; /* gris moyen */
|
||||
--checkbox-bg-checked: #ff6600; /* orange vif */
|
||||
--checkbox-checkmark-border-color: #eee; /* blanc cassé */
|
||||
--checkbox-pulse-color: rgba(255, 102, 0, 0.5); /* pulse orange translucide */
|
||||
|
||||
/* Download status */
|
||||
--download-status-color: #FF5900; /* orange */
|
||||
|
||||
/* Video Info Box */
|
||||
--infos-box-color: #222222; /* fond infos sombre */
|
||||
--video-info-text-color: #eee; /* texte clair */
|
||||
--video-info-heading-color: #FF5900; /* titres orange */
|
||||
--video-info-list-color: #ccc; /* texte liste gris clair */
|
||||
--video-info-list-strong-color: #fff; /* texte important blanc */
|
||||
--video-info-link-color: #FF5900; /* liens orange */
|
||||
--video-info-link-hover-color: #e65500; /* liens hover orange foncé */
|
||||
}
|
||||
36
public/styles/theme/light-theme.css
Normal file
@@ -0,0 +1,36 @@
|
||||
body.light {
|
||||
/* Couleurs générales */
|
||||
--background-color: #dadada; /* fond clair */
|
||||
--default-text-color: #222222; /* texte sombre */
|
||||
--subtitle-color: #007bff; /* bleu accent */
|
||||
--mp3-text-color: #000000; /* texte checkbox */
|
||||
|
||||
/* Formulaire */
|
||||
--form-bg-color: #ffffff; /* fond formulaire blanc */
|
||||
--form-input-bg-color: #fafafa; /* fond inputs */
|
||||
--form-input-border-color: #cccccc; /* bordure inputs */
|
||||
--form-input-border-focus-color: #007bff; /* focus bleu */
|
||||
--form-input-text-color: #333333; /* texte inputs */
|
||||
--form-input-placeholder-color: #888888; /* placeholder */
|
||||
--form-button-bg-color: #007bff; /* bouton bleu */
|
||||
--form-button-text-color: #ffffff; /* texte bouton */
|
||||
--form-button-bg-hover-color: #0056b3; /* hover bleu foncé */
|
||||
|
||||
/* Checkbox */
|
||||
--checkbox-bg-default: #cccccc;
|
||||
--checkbox-bg-checked: #007bff;
|
||||
--checkbox-checkmark-border-color: #ffffff;
|
||||
--checkbox-pulse-color: rgba(0, 123, 255, 0.4);
|
||||
|
||||
/* Download status */
|
||||
--download-status-color: #007bff;
|
||||
|
||||
/* Video Info Box */
|
||||
--infos-box-color: #ffffff;
|
||||
--video-info-text-color: #222222;
|
||||
--video-info-heading-color: #007bff;
|
||||
--video-info-list-color: #444444;
|
||||
--video-info-list-strong-color: #000000;
|
||||
--video-info-link-color: #007bff;
|
||||
--video-info-link-hover-color: #0056b3;
|
||||
}
|
||||
48
public/styles/theme/nf-theme.css
Normal file
@@ -0,0 +1,48 @@
|
||||
body.nf {
|
||||
/* Couleurs générales */
|
||||
--background-color: #1a1a1a; /* fond sombre neutre */
|
||||
--default-text-color: #e6dfd5; /* texte beige clair */
|
||||
--subtitle-color: #f5f0e6; /* beige très clair (titres) */
|
||||
--mp3-text-color: #d8cfc2; /* texte checkbox */
|
||||
|
||||
/* Formulaire */
|
||||
--form-bg-color: #2a2a2a; /* fond formulaire gris sombre */
|
||||
--form-input-bg-color: #222222; /* fond inputs sombre */
|
||||
--form-input-border-color: #4a4036; /* bordure beige/brun */
|
||||
--form-input-border-focus-color: #cbb89d; /* beige doré en focus */
|
||||
--form-input-text-color: #f0e8dd; /* texte clair/beige */
|
||||
--form-input-placeholder-color: #9a8f80; /* placeholder beige/gris */
|
||||
--form-button-bg-color: #cbb89d; /* bouton beige doré */
|
||||
--form-button-text-color: #1a1a1a; /* texte sombre */
|
||||
--form-button-bg-hover-color: #b7a588; /* hover beige plus foncé */
|
||||
|
||||
/* Checkbox */
|
||||
--checkbox-bg-default: #4a4036; /* brun/gris */
|
||||
--checkbox-bg-checked: #cbb89d; /* beige doré */
|
||||
--checkbox-checkmark-border-color: #1a1a1a; /* noir profond */
|
||||
--checkbox-pulse-color: rgba(203,184,157,0.3); /* halo beige doux */
|
||||
|
||||
/* Download status */
|
||||
--download-status-color: #cbb89d; /* beige doré */
|
||||
|
||||
/* Video Info Box */
|
||||
--infos-box-color: #2a2a2a; /* fond sombre */
|
||||
--video-info-text-color: #e6dfd5; /* texte beige clair */
|
||||
--video-info-heading-color: #f5f0e6; /* titres beige clair */
|
||||
--video-info-list-color: #d8cfc2; /* texte liste */
|
||||
--video-info-list-strong-color: #ffffff; /* texte fort blanc cassé */
|
||||
--video-info-link-color: #cbb89d; /* liens beige doré */
|
||||
--video-info-link-hover-color: #b7a588; /* hover beige plus foncé */
|
||||
}
|
||||
|
||||
/* Fond inspiré de ton image beige */
|
||||
body.nf {
|
||||
background-image: url('../../assets/images/NF.jpg'); /* mets ton image ici */
|
||||
background-size: cover;
|
||||
background-position: 70% center;
|
||||
background-attachment: fixed;
|
||||
}
|
||||
|
||||
body.nf .download-path {
|
||||
color: var(--default-text-color);
|
||||
}
|
||||
51
public/styles/theme/spicy-theme.css
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
body.spicy {
|
||||
/* Couleurs générales */
|
||||
--background-color: #121212; /* fond noir profond */
|
||||
--default-text-color: #581010; /* texte clair */
|
||||
--subtitle-color: #3d0909; /* accent rouge piment */
|
||||
--mp3-text-color: #ffffff; /* texte checkbox inline */
|
||||
|
||||
/* Formulaire */
|
||||
--form-bg-color: #1a1a1a; /* fond formulaire gris très foncé */
|
||||
--form-input-bg-color: #2b2b2b; /* fond inputs gris foncé */
|
||||
--form-input-border-color: #444444; /* bordure gris */
|
||||
--form-input-border-focus-color: #ff1a1a; /* focus rouge piment */
|
||||
--form-input-text-color: #eeeeee; /* texte inputs */
|
||||
--form-input-placeholder-color: #888888; /* placeholder gris moyen */
|
||||
--form-button-bg-color: #ff1a1a; /* bouton rouge vif */
|
||||
--form-button-text-color: #ffffff; /* texte bouton blanc */
|
||||
--form-button-bg-hover-color: #cc0000; /* hover rouge foncé */
|
||||
|
||||
/* Checkbox */
|
||||
--checkbox-bg-default: #555555; /* gris moyen */
|
||||
--checkbox-bg-checked: #ff1a1a; /* rouge piment */
|
||||
--checkbox-checkmark-border-color: #f5f5f5; /* blanc cassé */
|
||||
--checkbox-pulse-color: rgba(255, 26, 26, 0.5); /* pulse rouge translucide */
|
||||
|
||||
/* Download status */
|
||||
--download-status-color: #ffffff; /* rouge vif */
|
||||
|
||||
/* Video Info Box */
|
||||
--infos-box-color: #1a1a1a; /* fond infos gris foncé */
|
||||
--video-info-text-color: #f5f5f5; /* texte clair */
|
||||
--video-info-heading-color: #ff1a1a; /* titres rouges */
|
||||
--video-info-list-color: #cccccc; /* texte liste gris clair */
|
||||
--video-info-list-strong-color: #ffffff; /* texte important blanc */
|
||||
--video-info-link-color: #ff1a1a; /* liens rouges */
|
||||
--video-info-link-hover-color: #cc0000; /* liens hover rouge foncé */
|
||||
}
|
||||
|
||||
/* Optionnel: background piments */
|
||||
body.spicy {
|
||||
background-image: url('../../assets/images/spicy.webp');
|
||||
background-size:contain;
|
||||
background-position: center;
|
||||
background-attachment: fixed;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
9
public/styles/theme/themeimport.css
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
@import url("chirac-theme.css");
|
||||
@import url("dark-theme.css");
|
||||
@import url("default-theme.css");
|
||||
@import url("fanatic-theme.css");
|
||||
@import url("light-theme.css");
|
||||
@import url("cyberpunk-theme.css");
|
||||
@import url("spicy-theme.css");
|
||||
@import url("vilbrequin-theme.css");
|
||||
@import url("nf-theme.css");
|
||||
48
public/styles/theme/vilbrequin-theme.css
Normal file
@@ -0,0 +1,48 @@
|
||||
body.vilbrequin {
|
||||
/* Couleurs générales */
|
||||
--background-color: #0a1f3d; /* bleu Vilbrequin profond */
|
||||
--default-text-color: #535353; /* texte clair */
|
||||
--subtitle-color: #001696; /* bleu clair accent (turbo vibes) */
|
||||
--mp3-text-color: #ffffff; /* texte checkbox inline */
|
||||
|
||||
/* Formulaire */
|
||||
--form-bg-color: #12284f; /* fond formulaire bleu foncé */
|
||||
--form-input-bg-color: #1c3760; /* fond inputs bleu + clair */
|
||||
--form-input-border-color: #2a4b80; /* bordure bleue */
|
||||
--form-input-border-focus-color: #00bfff; /* focus bleu clair */
|
||||
--form-input-text-color: #f0f0f0; /* texte inputs clair */
|
||||
--form-input-placeholder-color: #b0c4de; /* placeholder gris bleuté */
|
||||
--form-button-bg-color: #ffd700; /* bouton jaune turbo */
|
||||
--form-button-text-color: #0a1f3d; /* texte bouton sombre */
|
||||
--form-button-bg-hover-color: #e6c200; /* hover jaune foncé */
|
||||
|
||||
/* Checkbox */
|
||||
--checkbox-bg-default: #2a4b80; /* bleu moyen */
|
||||
--checkbox-bg-checked: #00bfff; /* bleu clair accent */
|
||||
--checkbox-checkmark-border-color: #f0f0f0; /* blanc cassé */
|
||||
--checkbox-pulse-color: rgba(0,191,255,0.5); /* pulse bleu clair translucide */
|
||||
|
||||
/* Download status */
|
||||
--download-status-color: #ffd700; /* jaune turbo */
|
||||
|
||||
/* Video Info Box */
|
||||
--infos-box-color: #12284f; /* fond infos bleu foncé */
|
||||
--video-info-text-color: #f0f0f0; /* texte clair */
|
||||
--video-info-heading-color: #00bfff; /* titres bleu clair */
|
||||
--video-info-list-color: #dcdcdc; /* texte liste gris clair */
|
||||
--video-info-list-strong-color: #ffffff; /* texte important blanc */
|
||||
--video-info-link-color: #00bfff; /* liens bleu clair */
|
||||
--video-info-link-hover-color: #009acd; /* liens hover bleu plus foncé */
|
||||
}
|
||||
|
||||
/* Optionnel: motif turbo ou jantes */
|
||||
body.vilbrequin {
|
||||
background-image: url('../../assets/images/Vilbrequin.webp'); /* adapte ton image */
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-attachment: fixed;
|
||||
}
|
||||
|
||||
body.vilbrequin .download-path{
|
||||
color: white;
|
||||
}
|
||||
@@ -1,11 +1,36 @@
|
||||
:root{
|
||||
--background-color : #0F172A;
|
||||
--infos-box-color: #f1f1f1 ;
|
||||
--title-color: ;
|
||||
--subtitle-color : #a3aab4;
|
||||
--mp3-text-color: #0F172A;
|
||||
--default-text-color: #F8FAFC;
|
||||
:root {
|
||||
/* Couleurs générales */
|
||||
--background-color: #001224; /* body background bleu nuit */
|
||||
--default-text-color: #eee; /* texte clair principal */
|
||||
--subtitle-color: #007bff; /* sous-titres, header p */
|
||||
--mp3-text-color: #000000; /* couleur texte checkbox inline */
|
||||
|
||||
--checkbox-pulse-color:#9bbcf1;
|
||||
/* Formulaire */
|
||||
--form-bg-color: #ffffff; /* fond du formulaire */
|
||||
--form-input-bg-color: #f9f9f9; /* fond inputs */
|
||||
--form-input-border-color: #cccccc; /* bordure inputs */
|
||||
--form-input-border-focus-color: #0056b3; /* bordure focus */
|
||||
--form-input-text-color: #333333; /* texte inputs */
|
||||
--form-input-placeholder-color: #aaaaaa; /* placeholder */
|
||||
--form-button-bg-color: #007bff; /* bouton bg */
|
||||
--form-button-text-color: #ffffff; /* texte bouton */
|
||||
--form-button-bg-hover-color: #0056b3; /* bouton hover */
|
||||
|
||||
/* Checkbox */
|
||||
--checkbox-bg-default: #cccccc;
|
||||
--checkbox-bg-checked: #3B82F6;
|
||||
--checkbox-checkmark-border-color: #E0E0E2;
|
||||
--checkbox-pulse-color: rgba(59, 130, 246, 0.5);
|
||||
|
||||
/* Download status */
|
||||
--download-status-color: #007bff;
|
||||
|
||||
/* Video Info Box */
|
||||
--infos-box-color: #f0f0f0;
|
||||
--video-info-text-color: #222222;
|
||||
--video-info-heading-color: #007bff;
|
||||
--video-info-list-color: #444444;
|
||||
--video-info-list-strong-color: #000000;
|
||||
--video-info-link-color: #007bff;
|
||||
--video-info-link-hover-color: #0056b3;
|
||||
}
|
||||
BIN
ressources/yt-dlp.exe
Normal file
48
server/discordRPC.js
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
This file is part of Freedom Loader.
|
||||
|
||||
Copyright (C) 2025 MasterAcnolo
|
||||
|
||||
Freedom Loader is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License.
|
||||
|
||||
Freedom Loader is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const RPC = require("discord-rpc");
|
||||
const clientId = "1410934537051181146";
|
||||
const rpc = new RPC.Client({ transport: "ipc" });
|
||||
|
||||
function startRPC() {
|
||||
rpc.on("ready", () => {
|
||||
console.log("✅ Connecté à Discord !");
|
||||
|
||||
const presence = {
|
||||
largeImageKey: "icon",
|
||||
smallImageKey: "acnolo_pfp",
|
||||
smallImageText: "By MasterAcnolo",
|
||||
startTimestamp: new Date(), // compteur de temps
|
||||
details: "github.com/MasterAcnolo/Freedom-Loader",
|
||||
};
|
||||
|
||||
rpc.setActivity(presence);
|
||||
|
||||
// Mise à jour toutes les 15 secondes si tu veux garder le compteur à jour
|
||||
setInterval(() => {
|
||||
rpc.setActivity(presence);
|
||||
}, 15000);
|
||||
});
|
||||
|
||||
rpc.login({ clientId }).catch(err => {
|
||||
console.error("Erreur Discord RPC :", err);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { startRPC };
|
||||
@@ -18,51 +18,67 @@
|
||||
|
||||
const { createLogger, format, transports } = require("winston");
|
||||
const DailyRotateFile = require("winston-daily-rotate-file");
|
||||
const fs = require("fs"); // Pour vérifier/créer le dossier de logs
|
||||
const path = require("path"); // Pour gérer proprement les chemins
|
||||
const os = require("os"); // Pour gérer les chemins aussi
|
||||
|
||||
const fs = require("fs"); // pour vérifier/créer le dossier de logs
|
||||
const path = require("path"); // pour gérer proprement les chemins
|
||||
// Définition du dossier où seront stockés les logs
|
||||
// const logDir = path.join(__dirname, "../logs"); Désactivé parce que cela requiert d'exec en admin mode. Donc pas pratique pour les gens
|
||||
|
||||
// on définit le dossier de logs
|
||||
const logDir = path.join(__dirname, "../logs");
|
||||
// on le crée si inexistant
|
||||
if (!fs.existsSync(logDir)) fs.mkdirSync(logDir);
|
||||
let logDir;
|
||||
if (process.platform === "win32") {
|
||||
logDir = path.join(os.homedir(), "AppData", "Local", "FreedomLoader", "logs");
|
||||
} else {
|
||||
logDir = path.join(os.homedir(), ".freedomloader", "logs");
|
||||
}
|
||||
|
||||
// fonction pour générer une ligne de début de session
|
||||
// Création du dossier logs s’il n’existe pas encore
|
||||
//if (!fs.existsSync(logDir)) fs.mkdirSync(logDir); OBSOLETE
|
||||
|
||||
fs.mkdirSync(logDir, { recursive: true }); // Evite les erreurs
|
||||
|
||||
|
||||
// Fonction utilitaire pour générer une ligne indiquant le début d’une session de logs
|
||||
function getSessionStartLine() {
|
||||
const now = new Date().toISOString();
|
||||
return `--- Démarrage de la session : ${now} ---`;
|
||||
}
|
||||
|
||||
// fonction pour générer une ligne de fin de session
|
||||
// Fonction utilitaire pour générer une ligne indiquant la fin d’une session de logs
|
||||
function getSessionEndLine() {
|
||||
const now = new Date().toISOString();
|
||||
return `--- Fin de la session : ${now} ---`;
|
||||
}
|
||||
|
||||
// configuration principale de Winston
|
||||
// Configuration principale de Winston
|
||||
// - Niveau minimum d’écriture : info (enregistre info, warn, error, etc.)
|
||||
// - Formatage des logs avec timestamp lisible et format personnalisé
|
||||
// - Transports : écriture dans fichier journalier + console avec couleurs
|
||||
const logger = createLogger({
|
||||
level: "info", // niveau de log minimum (info, warn, error, etc.)
|
||||
format: format.combine(
|
||||
format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), // timestamp lisible
|
||||
format.printf(({ timestamp, level, message }) =>
|
||||
`${timestamp} | ${level.toUpperCase()} | ${message}` // format de ligne de log
|
||||
)
|
||||
),
|
||||
transports: [
|
||||
new DailyRotateFile({
|
||||
dirname: logDir, // dossier de logs
|
||||
filename: "LOGS-%DATE%.log", // nom de fichier journalier
|
||||
datePattern: "YYYY-MM-DD", // pattern de rotation
|
||||
zippedArchive: false, // true = compression des archives
|
||||
maxFiles: "14d", // garde 14 jours d’historique
|
||||
level: "info",
|
||||
format: format.combine(
|
||||
format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
|
||||
format.printf(({ timestamp, level, message }) =>
|
||||
`${timestamp} | ${level.toUpperCase()} | ${message}`
|
||||
)
|
||||
),
|
||||
options: { flags: "a" }, // mode append
|
||||
transports: [
|
||||
// Rotation quotidienne des fichiers de logs
|
||||
new DailyRotateFile({
|
||||
dirname: logDir, // dossier cible pour les logs
|
||||
filename: "LOGS-%DATE%.log", // nom du fichier par date
|
||||
datePattern: "YYYY-MM-DD", // pattern de date dans le nom
|
||||
zippedArchive: false, // ne pas compresser les archives
|
||||
maxFiles: "7d", // conserve 14 jours d’historique
|
||||
format: format.combine(
|
||||
format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
|
||||
format.printf(({ timestamp, level, message }) =>
|
||||
`${timestamp} | ${level.toUpperCase()} | ${message}`
|
||||
)
|
||||
),
|
||||
options: { flags: "a" }, // mode ajout à la fin (append)
|
||||
}),
|
||||
// Affichage des logs en console avec coloration et format simple
|
||||
new transports.Console({
|
||||
format: format.combine(
|
||||
format.colorize(),
|
||||
@@ -75,19 +91,20 @@ const logger = createLogger({
|
||||
],
|
||||
});
|
||||
|
||||
// fonction d'utilitaire pour marquer le début de la session
|
||||
// Helper pour marquer clairement le début de session dans les logs
|
||||
function logSessionStart() {
|
||||
logger.info(getSessionStartLine());
|
||||
}
|
||||
|
||||
// pour la fin de la session
|
||||
// Helper pour marquer la fin de session dans les logs
|
||||
function logSessionEnd() {
|
||||
logger.info(getSessionEndLine());
|
||||
}
|
||||
|
||||
// exporte le logger et les helpers pour les autres modules
|
||||
// Export du logger principal et des helpers pour usage dans d’autres modules
|
||||
module.exports = {
|
||||
logger,
|
||||
logSessionStart,
|
||||
logSessionEnd,
|
||||
logDir,
|
||||
};
|
||||
|
||||
@@ -11,155 +11,195 @@
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const express = require("express"); // importe le framework Express
|
||||
const router = express.Router(); // crée un routeur Express pour modulariser les routes
|
||||
const { execFile } = require("child_process"); // permet d’exécuter un binaire externe sans passer par un shell
|
||||
const path = require("path"); // module Node.js pour gérer les chemins de fichiers
|
||||
const fs = require("fs"); // module Node.js pour manipuler le système de fichiers
|
||||
const express = require("express");
|
||||
const router = express.Router();
|
||||
const { execFile } = require("child_process");
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
const { Notification } = require("electron");
|
||||
const logger = require("../logger").logger;
|
||||
|
||||
const logger = require("../logger").logger; // récupère l'instance de logger Winston personnalisée
|
||||
// Path vers le fichier exécutable yt-dlp (outil tiers pour le téléchargement)
|
||||
// const ytDlpPath = path.join(process.resourcesPath, '../../yt-dlp.exe');
|
||||
const ytDlpPath = path.join(process.resourcesPath, 'yt-dlp.exe');
|
||||
|
||||
// chemin vers l’exécutable yt-dlp packagé avec l’appli
|
||||
const ytDlpPath = path.join(__dirname, '../../yt-dlp.exe');
|
||||
execFile(ytDlpPath, ["-U"], (err, stdout, stderr) => {
|
||||
if (err) {
|
||||
logger.error("Erreur update yt-dlp:", err);
|
||||
return;
|
||||
}
|
||||
logger.info(`Update yt-dlp : ${stdout}`);
|
||||
});
|
||||
|
||||
// route POST sur la racine (ex: POST /download)
|
||||
router.post("/", (req, res) => {
|
||||
try {
|
||||
// récupère les options envoyées depuis le frontend (via formulaire ou requête AJAX)
|
||||
// Récupération des options envoyées par le frontend via le formulaire
|
||||
// On vérifie la présence de l'URL, le choix audioOnly, et la qualité demandée
|
||||
const options = {
|
||||
url: req.body.url, // URL à télécharger
|
||||
audioOnly: req.body.audioOnly === "1", // conversion audio uniquement (mp3)
|
||||
quality: req.body.quality || "best", // qualité vidéo à télécharger
|
||||
subtitles: req.body.subs === "1", // récupérer les sous-titres anglais
|
||||
url: req.body.url,
|
||||
audioOnly: req.body.audioOnly === "1",
|
||||
quality: req.body.quality || "best",
|
||||
};
|
||||
|
||||
// Validation simple : si l'URL est manquante, on rejette la requête
|
||||
if (!options.url) {
|
||||
// si l’URL est absente, on log et on renvoie 400
|
||||
logger.warn("Requête POST /download sans URL");
|
||||
return res.status(400).send("❌ URL manquante !");
|
||||
}
|
||||
|
||||
// on récupère le dossier de sortie depuis app.locals (défini ailleurs dans le serveur)
|
||||
const outputFolder = req.app.locals.outputFolder;
|
||||
// Récupération du chemin de sauvegarde personnalisé envoyé depuis Electron,
|
||||
// ou on utilise un chemin par défaut stocké dans app.locals
|
||||
let requestedOutputFolder = req.body.savePath || req.app.locals.outputFolder;
|
||||
|
||||
// s'il n'existe pas, on le crée automatiquement
|
||||
if (!fs.existsSync(outputFolder)) {
|
||||
fs.mkdirSync(outputFolder, { recursive: true });
|
||||
logger.info("Dossier Freedom Loader Output créé à la volée dans Téléchargements.");
|
||||
// On normalise le chemin pour éviter des erreurs liées aux séparateurs ou chemins relatifs
|
||||
requestedOutputFolder = path.normalize(requestedOutputFolder);
|
||||
|
||||
/*
|
||||
Sécurité basique : On refuse certains chemins sensibles ou suspects
|
||||
(ex: System32, /etc, Windows) pour éviter que l'application n'écrive dans des dossiers système.
|
||||
On vérifie aussi la longueur minimale du chemin pour éviter des valeurs vides ou invalides.
|
||||
*/
|
||||
if (
|
||||
!requestedOutputFolder ||
|
||||
requestedOutputFolder.length < 3 ||
|
||||
requestedOutputFolder.includes("System32") ||
|
||||
requestedOutputFolder.includes("/etc") ||
|
||||
requestedOutputFolder.includes("\\Windows")
|
||||
) {
|
||||
logger.warn(`Chemin potentiellement dangereux refusé : ${requestedOutputFolder}`);
|
||||
return res.status(400).send("❌ Chemin de sauvegarde non autorisé.");
|
||||
}
|
||||
|
||||
// template du nom de fichier de sortie (ex: MonTitre.mp4 ou MonTitre.mp3)
|
||||
const outputTemplate = path.join(outputFolder, "%(title)s.%(ext)s");
|
||||
// Vérifie que le dossier de sortie existe, sinon le crée récursivement
|
||||
if (!fs.existsSync(requestedOutputFolder)) {
|
||||
fs.mkdirSync(requestedOutputFolder, { recursive: true });
|
||||
logger.info(`Dossier de sortie créé : ${requestedOutputFolder}`);
|
||||
}
|
||||
|
||||
// tableau d'arguments pour yt-dlp
|
||||
const args = [];
|
||||
// Construction du template de sortie pour yt-dlp (nom de fichier basé sur le titre)
|
||||
const outputTemplate = path.join(requestedOutputFolder, "%(title)s.%(ext)s");
|
||||
|
||||
// Construction du tableau d'arguments à passer à yt-dlp
|
||||
// --no-continue pour forcer un téléchargement propre
|
||||
// --restrict-filenames pour éviter les caractères problématiques dans les noms
|
||||
const args = [
|
||||
"--no-continue", // pas de reprise, c'est un choix que j'ai fait voila
|
||||
//"--restrict-filenames", // noms de fichiers sans caractères spéciaux (Cyrilique, accents, etc.)
|
||||
"--no-overwrites", // évite d'écraser un fichier existant
|
||||
"--embed-thumbnail", // ajoute la pochette
|
||||
"--add-metadata", // ajoute les tags (titre, artiste, etc.)
|
||||
"--concurrent-fragments", "8",// accélère le téléchargement
|
||||
"--retries", "10", // réessaie jusqu'à 10 fois en cas d'erreur
|
||||
"--fragment-retries", "10" , // réessaie aussi 10 fois chaque fragment
|
||||
"--ffmpeg-location", path.join(process.resourcesPath, "ffmpeg.exe")
|
||||
];
|
||||
|
||||
// Si l'option audioOnly est activée, on ajoute les flags pour extraction audio en mp3
|
||||
if (options.audioOnly) {
|
||||
// si audioOnly, on force extraction audio en mp3
|
||||
args.push("--extract-audio", "--audio-format", "mp3");
|
||||
args.push(
|
||||
"-f",
|
||||
"bestaudio",
|
||||
"--extract-audio",
|
||||
"--audio-format",
|
||||
"mp3",
|
||||
);
|
||||
}
|
||||
|
||||
if (options.subtitles) {
|
||||
// si l’utilisateur veut des sous-titres
|
||||
args.push("--write-subs", "--sub-lang", "en");
|
||||
}
|
||||
|
||||
// // qualité souhaitée
|
||||
// args.push("-f", options.quality);
|
||||
// Correspondance entre la qualité choisie et le format à demander à yt-dlp
|
||||
const qualityMap = {
|
||||
best: "bestvideo+bestaudio/best",
|
||||
medium: "bestvideo[height<=720]+bestaudio/best[height<=720]",
|
||||
worst: "worstvideo+worstaudio/worst",
|
||||
1080: "bestvideo[height<=1080]+bestaudio/best[height<=1080]",
|
||||
720: "bestvideo[height<=720]+bestaudio/best[height<=720]",
|
||||
480: "bestvideo[height<=480]+bestaudio/best[height<=480]",
|
||||
};
|
||||
|
||||
// let format;
|
||||
// if (options.quality === "best") {
|
||||
// format = "bestvideo+bestaudio/best";
|
||||
// } else if (options.quality === "worst") {
|
||||
// format = "worstvideo+worstaudio/worst";
|
||||
// } else {
|
||||
// format = "best"; // fallback
|
||||
// }
|
||||
// args.push("-f", format);
|
||||
|
||||
args.push("--no-continue");
|
||||
|
||||
let format;
|
||||
|
||||
switch (options.quality) {
|
||||
case "best":
|
||||
format = "bestvideo+bestaudio/best";
|
||||
break;
|
||||
case "medium":
|
||||
// qualité moyenne, ex: 720p vidéo + meilleur audio
|
||||
format = "bestvideo[height<=720]+bestaudio/best[height<=720]";
|
||||
break;
|
||||
case "worst":
|
||||
format = "worstvideo+worstaudio/worst";
|
||||
break;
|
||||
case "1080":
|
||||
format = "bestvideo[height<=1080]+bestaudio/best[height<=1080]";
|
||||
break;
|
||||
case "720":
|
||||
format = "bestvideo[height<=720]+bestaudio/best[height<=720]";
|
||||
break;
|
||||
case "480":
|
||||
format = "bestvideo[height<=480]+bestaudio/best[height<=480]";
|
||||
break;
|
||||
default:
|
||||
format = "best";
|
||||
}
|
||||
// Sélection du format basé sur la qualité, par défaut 'best' si option invalide
|
||||
const format = qualityMap[options.quality] || "best";
|
||||
args.push("-f", format);
|
||||
|
||||
// modèle de sortie
|
||||
// Ajout du template de sortie et de l'URL à télécharger
|
||||
args.push("-o", outputTemplate);
|
||||
// enfin, l'URL à télécharger
|
||||
args.push(options.url);
|
||||
|
||||
// log de la commande complète pour debug
|
||||
// Log de la commande complète pour traçabilité et débogage
|
||||
logger.info(`Téléchargement demandé : url=${options.url}, audioOnly=${options.audioOnly}, quality=${options.quality}`);
|
||||
logger.info(`Commande yt-dlp : ${ytDlpPath} ${args.join(" ")}`);
|
||||
|
||||
// exécution du binaire yt-dlp
|
||||
// Lancement du processus yt-dlp avec les arguments définis
|
||||
const child = execFile(ytDlpPath, args);
|
||||
|
||||
// gestion des logs stdout de yt-dlp
|
||||
/*
|
||||
Gestion des sorties standards (stdout) du processus,
|
||||
on log chaque ligne pour suivre la progression ou informations diverses
|
||||
*/
|
||||
child.stdout.on("data", (data) => {
|
||||
data.toString().split("\n").forEach(line => {
|
||||
if(line.trim()) logger.info(`[yt-dlp stdout] ${line.trim()}`);
|
||||
if (line.trim()) logger.info(`[yt-dlp stdout] ${line.trim()}`);
|
||||
});
|
||||
});
|
||||
|
||||
// gestion des logs stderr de yt-dlp (les erreurs)
|
||||
/*
|
||||
Gestion des erreurs (stderr) du processus,
|
||||
on log chaque ligne d'erreur pour diagnostic
|
||||
*/
|
||||
child.stderr.on("data", (data) => {
|
||||
data.toString().split("\n").forEach(line => {
|
||||
if(line.trim()) logger.error(`[yt-dlp stderr] ${line.trim()}`);
|
||||
if (line.trim()) logger.error(`[yt-dlp stderr] ${line.trim()}`);
|
||||
});
|
||||
});
|
||||
|
||||
// si une erreur système survient (par ex : yt-dlp.exe introuvable)
|
||||
/*
|
||||
En cas d'erreur lors du lancement du processus yt-dlp,
|
||||
on log l'erreur et on répond au client avec un statut 500
|
||||
*/
|
||||
child.on("error", (err) => {
|
||||
logger.error(`Erreur lancement yt-dlp : ${err.message}`);
|
||||
res.status(500).send(`❌ Erreur lors de l'exécution : ${err.message}`);
|
||||
});
|
||||
|
||||
// callback quand le processus se termine
|
||||
/*
|
||||
Quand le processus se termine, on vérifie le code de sortie.
|
||||
Code 0 = succès, on informe le client que le téléchargement est terminé.
|
||||
Sinon, on envoie une erreur au client avec le code d'échec.
|
||||
*/
|
||||
child.on("close", (code) => {
|
||||
logger.info(`yt-dlp terminé avec code de sortie : ${code}`);
|
||||
if(code === 0) {
|
||||
// succès
|
||||
if (code === 0) {
|
||||
|
||||
const iconPath = path.join(process.resourcesPath, "confirm-icon.png"); // la sécurité pour toutes les machines
|
||||
|
||||
console.log("Icon path pour la notif :", iconPath);
|
||||
|
||||
const notif = new Notification({
|
||||
title: "Freedom Loader",
|
||||
body: "Ton téléchargement est terminé, clique ici pour l'ouvrir.",
|
||||
icon: iconPath,
|
||||
});
|
||||
|
||||
notif.on("click", () => {
|
||||
console.log("Notification cliquée !");
|
||||
//Pour pouvoir ouvrir le dossier de la vidéo
|
||||
const { shell } = require("electron");
|
||||
shell.openPath(requestedOutputFolder);
|
||||
});
|
||||
|
||||
notif.show();
|
||||
res.send("✅ Téléchargement terminé !");
|
||||
} else {
|
||||
// yt-dlp a échoué
|
||||
res.status(500).send(`❌ yt-dlp a échoué avec le code : ${code}`);
|
||||
}
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
// en cas d'erreur JavaScript
|
||||
// Capture toute autre erreur serveur non prévue, log et réponse 500
|
||||
logger.error(`Erreur serveur dans /download : ${err.message}`);
|
||||
res.status(500).send(`Erreur serveur : ${err.message}`);
|
||||
}
|
||||
});
|
||||
|
||||
// on exporte ce routeur pour qu'il soit utilisé par l'app principale
|
||||
module.exports = router;
|
||||
@@ -16,40 +16,66 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const express = require("express"); // importe Express
|
||||
const router = express.Router(); // crée un routeur Express indépendant
|
||||
const { execFile } = require("child_process"); // permet d’exécuter un programme externe
|
||||
const path = require("path"); // module pour gérer les chemins de fichiers
|
||||
const express = require("express");
|
||||
const router = express.Router();
|
||||
const { execFile } = require("child_process");
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
const { logger } = require("../logger"); // On récupère ton logger Winston
|
||||
|
||||
// chemin vers le binaire yt-dlp embarqué
|
||||
const ytDlpPath = path.join(__dirname, "../../yt-dlp.exe");
|
||||
// Path absolu vers l'exécutable yt-dlp
|
||||
const ytDlpPath = path.join(__dirname, '../../ressources/yt-dlp.exe');
|
||||
|
||||
// route POST sur la racine de ce routeur
|
||||
// Vérification que yt-dlp.exe existe bien au lancement du module
|
||||
if (!fs.existsSync(ytDlpPath)) {
|
||||
logger.error(`❌ yt-dlp.exe introuvable à ${ytDlpPath}`);
|
||||
throw new Error(`yt-dlp.exe introuvable à ${ytDlpPath}`);
|
||||
}
|
||||
|
||||
// Route POST pour récupérer les métadonnées d'une vidéo via yt-dlp
|
||||
router.post("/", (req, res) => {
|
||||
const url = req.body.url; // récupère l'URL depuis la requête POST
|
||||
if (!url) return res.status(400).send("❌ URL manquante"); // validation simple
|
||||
const url = req.body.url;
|
||||
|
||||
// exécute yt-dlp avec --dump-json pour récupérer les métadonnées de la vidéo
|
||||
execFile(ytDlpPath, ["--dump-json", url], (error, stdout, stderr) => {
|
||||
// Validation basique : on refuse la requête sans URL
|
||||
if (!url) {
|
||||
logger.warn("Requête metadata sans URL");
|
||||
return res.status(400).send("❌ URL manquante");
|
||||
}
|
||||
|
||||
logger.info(`Requête metadata reçue pour ${url}`);
|
||||
|
||||
// Exécution de yt-dlp avec l'option --dump-json pour récupérer les infos vidéo
|
||||
execFile(
|
||||
ytDlpPath,
|
||||
["--dump-json", url],
|
||||
{ timeout: 10_000 }, // Timeout fixé à 10 secondes pour éviter blocage
|
||||
(error, stdout, stderr) => {
|
||||
if (error) {
|
||||
// gestion d’erreur : par exemple yt-dlp plante ou l’URL est invalide
|
||||
console.error("Erreur lors de l'exécution de yt-dlp :", error);
|
||||
console.error("stderr :", stderr);
|
||||
// En cas d'erreur, on log l'erreur et le stderr, puis on renvoie un code 500
|
||||
logger.error(`Erreur exécution yt-dlp: ${error.message}`);
|
||||
logger.debug(`stderr: ${stderr}`);
|
||||
return res.status(500).send("❌ Impossible de récupérer les infos.");
|
||||
}
|
||||
|
||||
try {
|
||||
// on parse le JSON envoyé par yt-dlp
|
||||
const info = JSON.parse(stdout);
|
||||
// on renvoie l’objet JSON directement au frontend
|
||||
res.json(info);
|
||||
// yt-dlp peut retourner plusieurs JSON séparés par des sauts de ligne
|
||||
// On split et parse chacun proprement
|
||||
const infos = stdout
|
||||
.trim()
|
||||
.split("\n")
|
||||
.map(line => JSON.parse(line));
|
||||
|
||||
logger.info(`Infos récupérées pour ${url} (${infos.length} élément(s))`);
|
||||
|
||||
// Si on a un seul élément, on renvoie directement l'objet JSON, sinon un tableau
|
||||
res.json(infos.length === 1 ? infos[0] : infos);
|
||||
} catch (e) {
|
||||
// le JSON est mal formé
|
||||
console.error("Erreur lors du parsing JSON :", e);
|
||||
res.status(500).send("❌ JSON illisible.");
|
||||
// Si parsing JSON échoue, on log et renvoie une erreur 500
|
||||
logger.error(`Erreur parsing JSON: ${e.message}`);
|
||||
return res.status(500).send("❌ JSON illisible.");
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// on exporte le routeur pour être utilisé dans app.js ou ailleurs
|
||||
module.exports = router;
|
||||
|
||||
@@ -16,88 +16,91 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const express = require("express"); // framework web
|
||||
const { exec } = require("child_process"); // si besoin d’exécuter des commandes (pas utilisé ici directement)
|
||||
const fs = require("fs"); // accès au système de fichiers
|
||||
const path = require("path"); // gestion de chemins
|
||||
const express = require("express"); // Framework web pour créer l’API et servir les fichiers
|
||||
const { exec } = require("child_process"); // Permet d’exécuter des commandes systèmes (pas utilisé directement ici)
|
||||
const fs = require("fs"); // Module système pour gérer fichiers et dossiers
|
||||
const path = require("path"); // Gestion propre des chemins de fichiers et dossiers
|
||||
const debug = require("debug")("freedom-loader:server");
|
||||
// petit debugger coloré en console
|
||||
// Module de debug coloré en console, pratique pour dev
|
||||
|
||||
const { logger, logSessionStart, logSessionEnd } = require("./logger");
|
||||
// récupère notre logger Winston + helpers de session
|
||||
// Import du logger Winston et helpers pour marquer début/fin de session
|
||||
|
||||
const app = express(); // instancie l'application Express
|
||||
const app = express(); // Création de l’instance Express, notre serveur web
|
||||
|
||||
// logSessionStart(); // désactivé, mais pourrait logguer le début de session
|
||||
|
||||
// on définit le dossier de téléchargement dans le dossier Téléchargements de l’utilisateur
|
||||
// Définition du dossier par défaut où enregistrer les téléchargements
|
||||
// On prend le dossier Téléchargements de l’utilisateur Windows (USERPROFILE)
|
||||
const downloadsPath = path.join(process.env.USERPROFILE, "Downloads");
|
||||
const outputFolder = path.join(downloadsPath, "Freedom Loader Output");
|
||||
const outputFolder = path.join(downloadsPath, "Freedom Loader");
|
||||
|
||||
// création du dossier s’il n’existe pas
|
||||
// Création du dossier de sortie s’il n’existe pas déjà
|
||||
if (!fs.existsSync(outputFolder)) {
|
||||
try {
|
||||
fs.mkdirSync(outputFolder, { recursive: true });
|
||||
logger.info("Dossier Freedom Loader Output cree dans Telechargements.");
|
||||
fs.mkdirSync(outputFolder, { recursive: true }); // création récursive au cas où
|
||||
logger.info("Dossier Freedom Loader cree dans Telechargements.");
|
||||
} catch (err) {
|
||||
logger.error("Impossible de creer le dossier :", err);
|
||||
process.exit(1); // arrêt du programme en cas d’échec
|
||||
process.exit(1); // Arrêt du programme si dossier non créé (critique)
|
||||
}
|
||||
} else {
|
||||
logger.info("Dossier Freedom Loader Output deja existant.");
|
||||
logger.info("Dossier Freedom Loader deja existant.");
|
||||
}
|
||||
|
||||
// on rend ce dossier dispo via app.locals
|
||||
// On rend ce dossier accessible globalement via app.locals pour l’utiliser dans les routes
|
||||
app.locals.outputFolder = outputFolder;
|
||||
|
||||
// configuration pour parser les requêtes POST en urlencoded
|
||||
// Middleware pour parser le corps des requêtes POST en application/x-www-form-urlencoded
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
|
||||
// sert les fichiers statiques (frontend)
|
||||
// Définition du dossier contenant les fichiers statiques (frontend)
|
||||
// Permet d’accéder au HTML, CSS, JS côté client
|
||||
const staticPath = path.join(__dirname, "../public");
|
||||
debug("Serveur statique sur", staticPath);
|
||||
app.use(express.static(staticPath));
|
||||
|
||||
// enregistre les routes API
|
||||
// Import et enregistrement des routes API
|
||||
// Ces routes gèrent les requêtes pour les infos vidéo et téléchargement
|
||||
const infoRoute = require("./routes/info");
|
||||
const downloadRoute = require("./routes/download");
|
||||
debug("Routes /download et /info installees");
|
||||
app.use("/download", downloadRoute);
|
||||
app.use("/info", infoRoute);
|
||||
|
||||
// route GET / de base
|
||||
// Route GET / qui sert la page principale index.html
|
||||
app.get("/", (req, res) => {
|
||||
debug("Requete GET / servie");
|
||||
res.sendFile(path.join(__dirname, "../public/index.html"));
|
||||
});
|
||||
|
||||
// fonction pour démarrer le serveur (Promise pour pouvoir await ailleurs)
|
||||
// Fonction pour démarrer le serveur Express
|
||||
// Retourne une Promise pour pouvoir await le démarrage dans d’autres modules
|
||||
function startServer() {
|
||||
return new Promise((resolve, reject) => {
|
||||
logger.info("Demarrage du serveur Express...");
|
||||
const serverInstance = app.listen(8080, () => {
|
||||
logger.info("Serveur Express pret sur http://localhost:8080");
|
||||
resolve(serverInstance); // succès
|
||||
resolve(serverInstance); // Serveur prêt, on résout la promesse
|
||||
});
|
||||
// Gestion des erreurs serveur lors du démarrage
|
||||
serverInstance.on("error", (err) => {
|
||||
logger.error("Erreur serveur Express :", err);
|
||||
reject(err);
|
||||
reject(err); // Rejet de la promesse en cas d’erreur critique
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// gestion de la fermeture propre du process
|
||||
// process.on("exit", () => {
|
||||
// logSessionEnd();
|
||||
// });
|
||||
process.on("SIGINT", () => { // Ctrl+C
|
||||
// Gestion propre de la fermeture du process pour logger la fin de session
|
||||
process.on("SIGINT", () => { // Capture Ctrl+C (interruption)
|
||||
logSessionEnd();
|
||||
process.exit();
|
||||
});
|
||||
process.on("SIGTERM", () => { // kill
|
||||
process.on("SIGTERM", () => { // Capture kill ou arrêt du process
|
||||
logSessionEnd();
|
||||
process.exit();
|
||||
});
|
||||
|
||||
// on exporte la fonction startServer pour que d’autres modules puissent la lancer
|
||||
// Export de la fonction startServer pour permettre son appel depuis d’autres fichiers
|
||||
module.exports = { startServer };
|
||||
|
||||
const { startRPC } = require("./discordRPC");
|
||||
startRPC();
|
||||
|
||||