Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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,6 @@
|
|||||||
/node_modules
|
/node_modules
|
||||||
/downloads
|
/downloads
|
||||||
/dist
|
/dist
|
||||||
/logs
|
logs/*.json
|
||||||
|
logs/*.log
|
||||||
/out
|
/out
|
||||||
|
|
||||||
|
|
||||||
yt-dlp.exe
|
|
||||||
|
|||||||
26
README-EN.md
@@ -1,16 +1,26 @@
|
|||||||
# Freedom Loader
|
# Freedom Loader
|
||||||
|
|
||||||
 
|
<!-- Release -->
|
||||||
|
|
||||||
|

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

|
||||||
|
|
||||||
> This project aims to enable free, open-source, and unrestricted downloading of Video/Audio content. Eventually, it will allow users to choose
|
> This project aims to enable free, open-source, and unrestricted downloading of Video/Audio content. Eventually, it will allow users to choose
|
||||||
|
|
||||||
## 🚀 Features
|
## 🚀 Features
|
||||||
|
|
||||||
✅ Video Download (MP4)
|
- ✅ Video download with metadata(MP4)
|
||||||
✅ Audio Download (MP3)
|
- ✅ Audio-only download with thumbail and metadata(MP3)
|
||||||
✅ Detailed Logs (Console + Logs)
|
- ✅ Full playlist support
|
||||||
✅ Automatic Fetch of Information
|
- ✅ Automatic metadata retrieval (title, duration, etc.)
|
||||||
✅ Playlist Download
|
- ✅ 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)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -47,14 +57,14 @@ Try restarting the application, checking the logs, or opening the browser's deve
|
|||||||
- [ ] Extract / Split video using native chapters
|
- [ ] Extract / Split video using native chapters
|
||||||
- [ ] Parallel downloads
|
- [ ] Parallel downloads
|
||||||
- [ ] Skip sponsored parts of videos
|
- [ ] Skip sponsored parts of videos
|
||||||
- [ ] Custom output path selection (default **C:\Users\Username\Downloads** with a **Freedom Loader Output** folder created)
|
- [x] ~~Custom output path selection (default **C:\Users\Username\Downloads** with a **Freedom Loader Output** folder created)~~
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🖼️ Preview
|
## 🖼️ Preview
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="build/exemple.png" alt="Example Image" />
|
<img src="build/apercu1.0.1.png" alt="Exemple d'application" />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
> Here is an example of the app after launch and entering a URL
|
> Here is an example of the app after launch and entering a URL
|
||||||
|
|||||||
26
README.md
@@ -1,16 +1,26 @@
|
|||||||
# Freedom Loader
|
# 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. Il permettra à terme de choisir
|
> 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
|
## 🚀 Fonctionnalités
|
||||||
|
|
||||||
✅ Téléchargement Vidéo (MP4)
|
- ✅ Téléchargement de vidéos avec les métadonnées (MP4)
|
||||||
✅ Téléchargement Audio (MP3)
|
- ✅ Téléchargement audio uniquement, avec la couverture ainsi que les métadonnées (MP3)
|
||||||
✅ Logs détaillés (console + fichiers logs)
|
- ✅ Support des playlists complètes
|
||||||
✅ Récupération automatique des informations
|
- ✅ Récupération automatique des métadonnées (titre, durée, etc.)
|
||||||
✅ Téléchargement des playlists
|
- ✅ 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)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -47,14 +57,14 @@ Essayez de relancer l'application, de consulter les logs ou d'ouvrir la console
|
|||||||
- [ ] Extraction / découpage de la vidéo via chapitrage natif
|
- [ ] Extraction / découpage de la vidéo via chapitrage natif
|
||||||
- [ ] Téléchargements parallèles
|
- [ ] Téléchargements parallèles
|
||||||
- [ ] Suppression automatique des parties sponsorisées (skip sponsor)
|
- [ ] 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éé)
|
- [x] ~~Choix personnalisé du chemin de sortie (par défaut **C:\Users\Utilisateur\Downloads**, où un dossier **Freedom Loader Output** sera créé)~~
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🖼️ Aperçu
|
## 🖼️ Aperçu
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="build/exemple.png" alt="Exemple d'application" />
|
<img src="build/apercu1.0.1.png" alt="Exemple d'application" />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
> Voici un exemple de l’application lancée après avoir saisi une URL.
|
> Voici un exemple de l’application lancée après avoir saisi une URL.
|
||||||
|
|||||||
|
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 |
110
main.js
@@ -16,19 +16,29 @@
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
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 path = require("path");
|
||||||
const { logger, logSessionStart, logSessionEnd } = require("./server/logger");
|
const os = require("os");
|
||||||
|
const { logger, logSessionStart, logSessionEnd, logDir } = require("./server/logger");
|
||||||
|
|
||||||
let mainWindow;
|
let mainWindow;
|
||||||
|
// Utilise le vrai dossier de logs défini dans logger.js
|
||||||
|
const logsFolderPath = logDir;
|
||||||
|
|
||||||
|
/*
|
||||||
|
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() {
|
async function createWindow() {
|
||||||
logger.info("Creation de la fenetre...");
|
logger.info("Création de la fenêtre...");
|
||||||
|
|
||||||
if (mainWindow) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mainWindow = new BrowserWindow({
|
mainWindow = new BrowserWindow({
|
||||||
title: "Freedom Loader",
|
title: "Freedom Loader",
|
||||||
width: 750,
|
width: 750,
|
||||||
@@ -38,42 +48,118 @@ async function createWindow() {
|
|||||||
webPreferences: {
|
webPreferences: {
|
||||||
nodeIntegration: false,
|
nodeIntegration: false,
|
||||||
contextIsolation: true,
|
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 {
|
try {
|
||||||
await mainWindow.loadURL("http://localhost:8080");
|
await mainWindow.loadURL("http://localhost:8080");
|
||||||
logger.info("Fenetre chargee");
|
logger.info("Fenêtre chargée");
|
||||||
} catch (err) {
|
} 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", () => {
|
mainWindow.on("closed", () => {
|
||||||
logger.info("Fenetre principale fermee");
|
logger.info("Fenêtre principale fermée");
|
||||||
mainWindow = null;
|
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 () => {
|
app.whenReady().then(async () => {
|
||||||
logSessionStart();
|
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");
|
const expressServer = require("./server/server.js");
|
||||||
try {
|
try {
|
||||||
await expressServer.startServer();
|
await expressServer.startServer();
|
||||||
logger.info("Serveur Express demarre");
|
logger.info("Serveur Express démarré");
|
||||||
await createWindow();
|
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) {
|
} catch (error) {
|
||||||
logger.error("Erreur serveur ou fenetre :", error);
|
logger.error("Erreur serveur ou fenêtre :", error);
|
||||||
app.quit();
|
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", () => {
|
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();
|
if (process.platform !== "darwin") app.quit();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
Avant de quitter l'application, on log la fin de session pour traçabilité.
|
||||||
|
*/
|
||||||
app.on("before-quit", () => {
|
app.on("before-quit", () => {
|
||||||
logSessionEnd();
|
logSessionEnd();
|
||||||
});
|
});
|
||||||
|
|||||||
362
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "freedom-loader",
|
"name": "freedom-loader",
|
||||||
"version": "1.0.1",
|
"version": "1.1.3",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "freedom-loader",
|
"name": "freedom-loader",
|
||||||
"version": "1.0.1",
|
"version": "1.1.3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chalk": "^4.1.2",
|
"chalk": "^4.1.2",
|
||||||
"debug": "^4.4.1",
|
"debug": "^4.4.1",
|
||||||
@@ -67,9 +67,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@electron-forge/cli": {
|
"node_modules/@electron-forge/cli": {
|
||||||
"version": "7.8.1",
|
"version": "7.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/@electron-forge/cli/-/cli-7.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/@electron-forge/cli/-/cli-7.8.3.tgz",
|
||||||
"integrity": "sha512-QI3EShutfq9Y+2TWWrPjm4JZM3eSAKzoQvRZdVhAfVpUbyJ8K23VqJShg3kGKlPf9BXHAGvE+8LyH5s2yDr1qA==",
|
"integrity": "sha512-BSAjGGfVf0yp3NQhXYmyCw9T//YCQHuktMv4HXfDVfo7AoV6DA1oEhqldI4Q7aHKeRob5+yBkvRRYPiu5ayCPw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -83,9 +83,9 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron-forge/core": "7.8.1",
|
"@electron-forge/core": "7.8.3",
|
||||||
"@electron-forge/core-utils": "7.8.1",
|
"@electron-forge/core-utils": "7.8.3",
|
||||||
"@electron-forge/shared-types": "7.8.1",
|
"@electron-forge/shared-types": "7.8.3",
|
||||||
"@electron/get": "^3.0.0",
|
"@electron/get": "^3.0.0",
|
||||||
"chalk": "^4.0.0",
|
"chalk": "^4.0.0",
|
||||||
"commander": "^11.1.0",
|
"commander": "^11.1.0",
|
||||||
@@ -105,9 +105,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@electron-forge/core": {
|
"node_modules/@electron-forge/core": {
|
||||||
"version": "7.8.1",
|
"version": "7.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/@electron-forge/core/-/core-7.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/@electron-forge/core/-/core-7.8.3.tgz",
|
||||||
"integrity": "sha512-jkh0QPW5p0zmruu1E8+2XNufc4UMxy13WLJcm7hn9jbaXKLkMbKuEvhrN1tH/9uGp1mhr/t8sC4N67gP+gS87w==",
|
"integrity": "sha512-qX2vi/LP3HcSqSfLfzMeH2ll8SFZQnOk8VN/b3bq6XrBCbrfrSsTYYWakN6mmfalLJcQRm4jCEc6gcyuGO4i6Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -121,17 +121,17 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron-forge/core-utils": "7.8.1",
|
"@electron-forge/core-utils": "7.8.3",
|
||||||
"@electron-forge/maker-base": "7.8.1",
|
"@electron-forge/maker-base": "7.8.3",
|
||||||
"@electron-forge/plugin-base": "7.8.1",
|
"@electron-forge/plugin-base": "7.8.3",
|
||||||
"@electron-forge/publisher-base": "7.8.1",
|
"@electron-forge/publisher-base": "7.8.3",
|
||||||
"@electron-forge/shared-types": "7.8.1",
|
"@electron-forge/shared-types": "7.8.3",
|
||||||
"@electron-forge/template-base": "7.8.1",
|
"@electron-forge/template-base": "7.8.3",
|
||||||
"@electron-forge/template-vite": "7.8.1",
|
"@electron-forge/template-vite": "7.8.3",
|
||||||
"@electron-forge/template-vite-typescript": "7.8.1",
|
"@electron-forge/template-vite-typescript": "7.8.3",
|
||||||
"@electron-forge/template-webpack": "7.8.1",
|
"@electron-forge/template-webpack": "7.8.3",
|
||||||
"@electron-forge/template-webpack-typescript": "7.8.1",
|
"@electron-forge/template-webpack-typescript": "7.8.3",
|
||||||
"@electron-forge/tracer": "7.8.1",
|
"@electron-forge/tracer": "7.8.3",
|
||||||
"@electron/get": "^3.0.0",
|
"@electron/get": "^3.0.0",
|
||||||
"@electron/packager": "^18.3.5",
|
"@electron/packager": "^18.3.5",
|
||||||
"@electron/rebuild": "^3.7.0",
|
"@electron/rebuild": "^3.7.0",
|
||||||
@@ -161,13 +161,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@electron-forge/core-utils": {
|
"node_modules/@electron-forge/core-utils": {
|
||||||
"version": "7.8.1",
|
"version": "7.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/@electron-forge/core-utils/-/core-utils-7.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/@electron-forge/core-utils/-/core-utils-7.8.3.tgz",
|
||||||
"integrity": "sha512-mRoPLDNZgmjyOURE/K0D3Op53XGFmFRgfIvFC7c9S/BqsRpovVblrqI4XxPRdNmH9dvhd8On9gGz+XIYAKD3aQ==",
|
"integrity": "sha512-8jhK7AvUKEqDyTMMuRhvS1TkE73YEnHYAxVvkJq35e1KdCwGbGnSRhaEs7OOqRfkKxfepdJ7BMKFSoKFeP+z0Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron-forge/shared-types": "7.8.1",
|
"@electron-forge/shared-types": "7.8.3",
|
||||||
"@electron/rebuild": "^3.7.0",
|
"@electron/rebuild": "^3.7.0",
|
||||||
"@malept/cross-spawn-promise": "^2.0.0",
|
"@malept/cross-spawn-promise": "^2.0.0",
|
||||||
"chalk": "^4.0.0",
|
"chalk": "^4.0.0",
|
||||||
@@ -182,13 +182,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@electron-forge/maker-base": {
|
"node_modules/@electron-forge/maker-base": {
|
||||||
"version": "7.8.1",
|
"version": "7.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/@electron-forge/maker-base/-/maker-base-7.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/@electron-forge/maker-base/-/maker-base-7.8.3.tgz",
|
||||||
"integrity": "sha512-GUZqschGuEBzSzE0bMeDip65IDds48DZXzldlRwQ+85SYVA6RMU2AwDDqx3YiYsvP2OuxKruuqIJZtOF5ps4FQ==",
|
"integrity": "sha512-WmF66cHdziaK8Asi7IRTLxZjCZ8IqXXHr6IPl4d5oatN6s5RG+HHzG1hiJ7LzlOEntqdSpE8Wh2nB2TmyR4huQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron-forge/shared-types": "7.8.1",
|
"@electron-forge/shared-types": "7.8.3",
|
||||||
"fs-extra": "^10.0.0",
|
"fs-extra": "^10.0.0",
|
||||||
"which": "^2.0.2"
|
"which": "^2.0.2"
|
||||||
},
|
},
|
||||||
@@ -197,14 +197,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@electron-forge/maker-deb": {
|
"node_modules/@electron-forge/maker-deb": {
|
||||||
"version": "7.8.1",
|
"version": "7.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/@electron-forge/maker-deb/-/maker-deb-7.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/@electron-forge/maker-deb/-/maker-deb-7.8.3.tgz",
|
||||||
"integrity": "sha512-tjjeesQtCP5Xht1X7gl4+K9bwoETPmQfBkOVAY/FZIxPj40uQh/hOUtLX2tYENNGNVZ1ryDYRs8TuPi+I41Vfw==",
|
"integrity": "sha512-GZHJU06LRFJkv1wMc3bjMVzedQhyxnyMZqgDJjE9TIlXXZkN2EqGmKLw/HxwNRA1R8yuQeA4Ih6/dEB4bZTevg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron-forge/maker-base": "7.8.1",
|
"@electron-forge/maker-base": "7.8.3",
|
||||||
"@electron-forge/shared-types": "7.8.1"
|
"@electron-forge/shared-types": "7.8.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 16.4.0"
|
"node": ">= 16.4.0"
|
||||||
@@ -214,14 +214,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@electron-forge/maker-rpm": {
|
"node_modules/@electron-forge/maker-rpm": {
|
||||||
"version": "7.8.1",
|
"version": "7.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/@electron-forge/maker-rpm/-/maker-rpm-7.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/@electron-forge/maker-rpm/-/maker-rpm-7.8.3.tgz",
|
||||||
"integrity": "sha512-TF6wylft3BHkw9zdHcxmjEPBZYgTIc0jE31skFnMEQ/aExbNRiNaCZvsXy+7ptTWZxhxUKRc9KHhLFRMCmOK8g==",
|
"integrity": "sha512-TqHHdF5D3Dy4NgG+pi8eRc18P1D0FF1LE+CqQF4MRof+XrpHhRtog5Ac4gWjvg1Wma6WjPyTdryHAkURsoBbuQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron-forge/maker-base": "7.8.1",
|
"@electron-forge/maker-base": "7.8.3",
|
||||||
"@electron-forge/shared-types": "7.8.1"
|
"@electron-forge/shared-types": "7.8.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 16.4.0"
|
"node": ">= 16.4.0"
|
||||||
@@ -231,14 +231,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@electron-forge/maker-squirrel": {
|
"node_modules/@electron-forge/maker-squirrel": {
|
||||||
"version": "7.8.1",
|
"version": "7.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/@electron-forge/maker-squirrel/-/maker-squirrel-7.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/@electron-forge/maker-squirrel/-/maker-squirrel-7.8.3.tgz",
|
||||||
"integrity": "sha512-qT1PMvT7ALF0ONOkxlA0oc0PiFuKCAKgoMPoxYo9gGOqFvnAb+TBcnLxflQ4ashE/ZkrHpykr4LcDJxqythQTA==",
|
"integrity": "sha512-6XSEhZMbgfjAaCm8A54pNjn4ghfxJgPu4i7ok3PhP44WOrFPaPivLttpvKRnxRb0PGZstPjPBFcwL1F9S1trjA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron-forge/maker-base": "7.8.1",
|
"@electron-forge/maker-base": "7.8.3",
|
||||||
"@electron-forge/shared-types": "7.8.1",
|
"@electron-forge/shared-types": "7.8.3",
|
||||||
"fs-extra": "^10.0.0"
|
"fs-extra": "^10.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -249,14 +249,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@electron-forge/maker-zip": {
|
"node_modules/@electron-forge/maker-zip": {
|
||||||
"version": "7.8.1",
|
"version": "7.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/@electron-forge/maker-zip/-/maker-zip-7.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/@electron-forge/maker-zip/-/maker-zip-7.8.3.tgz",
|
||||||
"integrity": "sha512-unIxEoV1lnK4BLVqCy3L2y897fTyg8nKY1WT4rrpv0MUKnQG4qmigDfST5zZNNHHaulEn/ElAic2GEiP7d6bhQ==",
|
"integrity": "sha512-ytao285wKAjKBO6eULzLeqUDP5Zh7beQlGyHjgOMknk7FI0sNy+zGdh3CrCGIhkXSHU/DpukPwRu2SiKvIaIGA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron-forge/maker-base": "7.8.1",
|
"@electron-forge/maker-base": "7.8.3",
|
||||||
"@electron-forge/shared-types": "7.8.1",
|
"@electron-forge/shared-types": "7.8.3",
|
||||||
"cross-zip": "^4.0.0",
|
"cross-zip": "^4.0.0",
|
||||||
"fs-extra": "^10.0.0",
|
"fs-extra": "^10.0.0",
|
||||||
"got": "^11.8.5"
|
"got": "^11.8.5"
|
||||||
@@ -266,70 +266,70 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@electron-forge/plugin-auto-unpack-natives": {
|
"node_modules/@electron-forge/plugin-auto-unpack-natives": {
|
||||||
"version": "7.8.1",
|
"version": "7.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/@electron-forge/plugin-auto-unpack-natives/-/plugin-auto-unpack-natives-7.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/@electron-forge/plugin-auto-unpack-natives/-/plugin-auto-unpack-natives-7.8.3.tgz",
|
||||||
"integrity": "sha512-4URAgWX9qqqKe6Bfad0VmpFRrwINYMODfKGd2nFQrfHxmBtdpXnsWlLwVGE/wGssIQaTMI5bWQ6F2RNeXTgnhA==",
|
"integrity": "sha512-7tRhySvfdTrBC4PEhAzjzjJGT5dBSU6jhy3XlQ8LJH50jHIT9bioQq6dfn/3iw/VaD1ftGWKx+HlntYCmBhViw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron-forge/plugin-base": "7.8.1",
|
"@electron-forge/plugin-base": "7.8.3",
|
||||||
"@electron-forge/shared-types": "7.8.1"
|
"@electron-forge/shared-types": "7.8.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 16.4.0"
|
"node": ">= 16.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@electron-forge/plugin-base": {
|
"node_modules/@electron-forge/plugin-base": {
|
||||||
"version": "7.8.1",
|
"version": "7.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/@electron-forge/plugin-base/-/plugin-base-7.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/@electron-forge/plugin-base/-/plugin-base-7.8.3.tgz",
|
||||||
"integrity": "sha512-iCZC2d7CbsZ9l6j5d+KPIiyQx0U1QBfWAbKnnQhWCSizjcrZ7A9V4sMFZeTO6+PVm48b/r9GFPm+slpgZtYQLg==",
|
"integrity": "sha512-0CzPQlO3BGu5bLCrx2Xqo6B1yoHhA9wG9boZE58ANr8Qma1NQfAWZU3LnMmF3EdWNTX76PxpZeeb3QbPNcxSuA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron-forge/shared-types": "7.8.1"
|
"@electron-forge/shared-types": "7.8.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 16.4.0"
|
"node": ">= 16.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@electron-forge/plugin-fuses": {
|
"node_modules/@electron-forge/plugin-fuses": {
|
||||||
"version": "7.8.1",
|
"version": "7.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/@electron-forge/plugin-fuses/-/plugin-fuses-7.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/@electron-forge/plugin-fuses/-/plugin-fuses-7.8.3.tgz",
|
||||||
"integrity": "sha512-dYTwvbV1HcDOIQ0wTybpdtPq6YoBYXIWBTb7DJuvFu/c/thj1eoEdnbwr8mT9hEivjlu5p4ls46n16P5EtZ0oA==",
|
"integrity": "sha512-OPWfoVsQ3xzK7GB3C6zoGlUu21RCOEQAm5+zg9VP1HJg5dsrKkqU8A2DsHGxe7PHthUCuQUgcls0ciDOHvckCQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron-forge/plugin-base": "7.8.1",
|
"@electron-forge/plugin-base": "7.8.3",
|
||||||
"@electron-forge/shared-types": "7.8.1"
|
"@electron-forge/shared-types": "7.8.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 16.4.0"
|
"node": ">= 16.4.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@electron/fuses": ">=1.0.0"
|
"@electron/fuses": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@electron-forge/publisher-base": {
|
"node_modules/@electron-forge/publisher-base": {
|
||||||
"version": "7.8.1",
|
"version": "7.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/@electron-forge/publisher-base/-/publisher-base-7.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/@electron-forge/publisher-base/-/publisher-base-7.8.3.tgz",
|
||||||
"integrity": "sha512-z2C+C4pcFxyCXIFwXGDcxhU8qtVUPZa3sPL6tH5RuMxJi77768chLw2quDWk2/dfupcSELXcOMYCs7aLysCzeQ==",
|
"integrity": "sha512-kurRKVNyLsK2JgmVl88UHu0+qSH+PysMtk/xP0YX5sYNMVxRay8+S1T10tAh6Qom94KwpF3CLYtkebvxb/fq+A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron-forge/shared-types": "7.8.1"
|
"@electron-forge/shared-types": "7.8.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 16.4.0"
|
"node": ">= 16.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@electron-forge/shared-types": {
|
"node_modules/@electron-forge/shared-types": {
|
||||||
"version": "7.8.1",
|
"version": "7.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/@electron-forge/shared-types/-/shared-types-7.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/@electron-forge/shared-types/-/shared-types-7.8.3.tgz",
|
||||||
"integrity": "sha512-guLyGjIISKQQRWHX+ugmcjIOjn2q/BEzCo3ioJXFowxiFwmZw/oCZ2KlPig/t6dMqgUrHTH5W/F0WKu0EY4M+Q==",
|
"integrity": "sha512-gkZtD7ALXHPDOthJo1rQYLDNfG09fdDRMWvjEgaXdF3Z69xXFfnOWPNuOkRUODNalMnuuGs6l7jDl+QFQgHlDg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron-forge/tracer": "7.8.1",
|
"@electron-forge/tracer": "7.8.3",
|
||||||
"@electron/packager": "^18.3.5",
|
"@electron/packager": "^18.3.5",
|
||||||
"@electron/rebuild": "^3.7.0",
|
"@electron/rebuild": "^3.7.0",
|
||||||
"listr2": "^7.0.2"
|
"listr2": "^7.0.2"
|
||||||
@@ -339,14 +339,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@electron-forge/template-base": {
|
"node_modules/@electron-forge/template-base": {
|
||||||
"version": "7.8.1",
|
"version": "7.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/@electron-forge/template-base/-/template-base-7.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/@electron-forge/template-base/-/template-base-7.8.3.tgz",
|
||||||
"integrity": "sha512-k8jEUr0zWFWb16ZGho+Es2OFeKkcbTgbC6mcH4eNyF/sumh/4XZMcwRtX1i7EiZAYiL9sVxyI6KVwGu254g+0g==",
|
"integrity": "sha512-C0tVODDNKoqhCf7T1HRONJs9DKAmjmk8Of0t8rVjT0ERDzMvLGlBByd785v5lFlKbGERyoaXsYltxPAu92G3aA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron-forge/core-utils": "7.8.1",
|
"@electron-forge/core-utils": "7.8.3",
|
||||||
"@electron-forge/shared-types": "7.8.1",
|
"@electron-forge/shared-types": "7.8.3",
|
||||||
"@malept/cross-spawn-promise": "^2.0.0",
|
"@malept/cross-spawn-promise": "^2.0.0",
|
||||||
"debug": "^4.3.1",
|
"debug": "^4.3.1",
|
||||||
"fs-extra": "^10.0.0",
|
"fs-extra": "^10.0.0",
|
||||||
@@ -357,14 +357,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@electron-forge/template-vite": {
|
"node_modules/@electron-forge/template-vite": {
|
||||||
"version": "7.8.1",
|
"version": "7.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/@electron-forge/template-vite/-/template-vite-7.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/@electron-forge/template-vite/-/template-vite-7.8.3.tgz",
|
||||||
"integrity": "sha512-qzSlJaBYYqQAbBdLk4DqAE3HCNz4yXbpkb+VC74ddL4JGwPdPU57DjCthr6YetKJ2FsOVy9ipovA8HX5UbXpAg==",
|
"integrity": "sha512-tDL5h+UO5iOzdEYNVsSu4zkUVN4RTxti5iXzcBquKd9Kgt/A/M7xHeuKj7g1Ds7Ul/n2XcFvcfgLcRmiXQeVDA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron-forge/shared-types": "7.8.1",
|
"@electron-forge/shared-types": "7.8.3",
|
||||||
"@electron-forge/template-base": "7.8.1",
|
"@electron-forge/template-base": "7.8.3",
|
||||||
"fs-extra": "^10.0.0"
|
"fs-extra": "^10.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -372,14 +372,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@electron-forge/template-vite-typescript": {
|
"node_modules/@electron-forge/template-vite-typescript": {
|
||||||
"version": "7.8.1",
|
"version": "7.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/@electron-forge/template-vite-typescript/-/template-vite-typescript-7.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/@electron-forge/template-vite-typescript/-/template-vite-typescript-7.8.3.tgz",
|
||||||
"integrity": "sha512-CccQhwUjZcc6svzuOi3BtbDal591DzyX2J5GPa6mwVutDP8EMtqJL1VyOHdcWO/7XjI6GNAD0fiXySOJiUAECA==",
|
"integrity": "sha512-HJjjY9xmlpl0vx10mrXdWn2RYHExfazACxmDNNmGO1eq3eqrQk/3R+NDGyqMJ7ajBsRVbkQNt+wayH7HkRJUjA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron-forge/shared-types": "7.8.1",
|
"@electron-forge/shared-types": "7.8.3",
|
||||||
"@electron-forge/template-base": "7.8.1",
|
"@electron-forge/template-base": "7.8.3",
|
||||||
"fs-extra": "^10.0.0"
|
"fs-extra": "^10.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -387,14 +387,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@electron-forge/template-webpack": {
|
"node_modules/@electron-forge/template-webpack": {
|
||||||
"version": "7.8.1",
|
"version": "7.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/@electron-forge/template-webpack/-/template-webpack-7.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/@electron-forge/template-webpack/-/template-webpack-7.8.3.tgz",
|
||||||
"integrity": "sha512-DA77o9kTCHrq+W211pyNP49DyAt0d1mzMp2gisyNz7a+iKvlv2DsMAeRieLoCQ44akb/z8ZsL0YLteSjKLy4AA==",
|
"integrity": "sha512-1zkji5px1kDXbigFN5959anCf3HAVBvam3bHh2VejnCScegLQP3JBjAn2Nw2ILWq6ej5JNuA/V99b1dr11hPOw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron-forge/shared-types": "7.8.1",
|
"@electron-forge/shared-types": "7.8.3",
|
||||||
"@electron-forge/template-base": "7.8.1",
|
"@electron-forge/template-base": "7.8.3",
|
||||||
"fs-extra": "^10.0.0"
|
"fs-extra": "^10.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -402,14 +402,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@electron-forge/template-webpack-typescript": {
|
"node_modules/@electron-forge/template-webpack-typescript": {
|
||||||
"version": "7.8.1",
|
"version": "7.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/@electron-forge/template-webpack-typescript/-/template-webpack-typescript-7.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/@electron-forge/template-webpack-typescript/-/template-webpack-typescript-7.8.3.tgz",
|
||||||
"integrity": "sha512-h922E+6zWwym1RT6WKD79BLTc4H8YxEMJ7wPWkBX59kw/exsTB/KFdiJq6r82ON5jSJ+Q8sDGqSmDWdyCfo+Gg==",
|
"integrity": "sha512-Xz3X7YJvot08Xm+0BLIS28GJH+0z9vEN9xYX76SOL4jqDcHH8lRFpNWcE2HsxxxRbCVH+s0etVmACZcWoujUrw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron-forge/shared-types": "7.8.1",
|
"@electron-forge/shared-types": "7.8.3",
|
||||||
"@electron-forge/template-base": "7.8.1",
|
"@electron-forge/template-base": "7.8.3",
|
||||||
"fs-extra": "^10.0.0"
|
"fs-extra": "^10.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -417,9 +417,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@electron-forge/tracer": {
|
"node_modules/@electron-forge/tracer": {
|
||||||
"version": "7.8.1",
|
"version": "7.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/@electron-forge/tracer/-/tracer-7.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/@electron-forge/tracer/-/tracer-7.8.3.tgz",
|
||||||
"integrity": "sha512-r2i7aHVp2fylGQSPDw3aTcdNfVX9cpL1iL2MKHrCRNwgrfR+nryGYg434T745GGm1rNQIv5Egdkh5G9xf00oWA==",
|
"integrity": "sha512-YVVDaPEUOvR1z+DDdj8wR/vO9OSlC91wz4/2Iqe9rqQz8sztnnQBClMAZTqu9bkDTFyHrns8j8v7tPVuVS6ULQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -678,9 +678,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@electron/packager": {
|
"node_modules/@electron/packager": {
|
||||||
"version": "18.3.6",
|
"version": "18.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/@electron/packager/-/packager-18.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/@electron/packager/-/packager-18.4.2.tgz",
|
||||||
"integrity": "sha512-1eXHB5t+SQKvUiDpWGpvr90ZSSbXj+isrh3YbjCTjKT4bE4SQrKSBfukEAaBvp67+GXHFtCHjQgN9qSTFIge+Q==",
|
"integrity": "sha512-5wr/6YoOhBlQoAloquKksY21Pa/2Eg4kq4y/+jOX+9smAFS4fcM3kG3KyhU/zEuwitxRjquq9/KxDNNhJbhc+w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -690,6 +690,7 @@
|
|||||||
"@electron/osx-sign": "^1.0.5",
|
"@electron/osx-sign": "^1.0.5",
|
||||||
"@electron/universal": "^2.0.1",
|
"@electron/universal": "^2.0.1",
|
||||||
"@electron/windows-sign": "^1.0.0",
|
"@electron/windows-sign": "^1.0.0",
|
||||||
|
"@malept/cross-spawn-promise": "^2.0.0",
|
||||||
"debug": "^4.0.1",
|
"debug": "^4.0.1",
|
||||||
"extract-zip": "^2.0.0",
|
"extract-zip": "^2.0.0",
|
||||||
"filenamify": "^4.1.0",
|
"filenamify": "^4.1.0",
|
||||||
@@ -699,6 +700,7 @@
|
|||||||
"junk": "^3.1.0",
|
"junk": "^3.1.0",
|
||||||
"parse-author": "^2.0.0",
|
"parse-author": "^2.0.0",
|
||||||
"plist": "^3.0.0",
|
"plist": "^3.0.0",
|
||||||
|
"prettier": "^3.4.2",
|
||||||
"resedit": "^2.0.0",
|
"resedit": "^2.0.0",
|
||||||
"resolve": "^1.1.6",
|
"resolve": "^1.1.6",
|
||||||
"semver": "^7.1.3",
|
"semver": "^7.1.3",
|
||||||
@@ -715,9 +717,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@electron/packager/node_modules/fs-extra": {
|
"node_modules/@electron/packager/node_modules/fs-extra": {
|
||||||
"version": "11.3.0",
|
"version": "11.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.1.tgz",
|
||||||
"integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==",
|
"integrity": "sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -788,9 +790,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@electron/universal/node_modules/fs-extra": {
|
"node_modules/@electron/universal/node_modules/fs-extra": {
|
||||||
"version": "11.3.0",
|
"version": "11.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.1.tgz",
|
||||||
"integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==",
|
"integrity": "sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -839,9 +841,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@electron/windows-sign/node_modules/fs-extra": {
|
"node_modules/@electron/windows-sign/node_modules/fs-extra": {
|
||||||
"version": "11.3.0",
|
"version": "11.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.1.tgz",
|
||||||
"integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==",
|
"integrity": "sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -1153,9 +1155,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "22.15.34",
|
"version": "22.17.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.34.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.17.2.tgz",
|
||||||
"integrity": "sha512-8Y6E5WUupYy1Dd0II32BsWAx5MWdcnRd8L84Oys3veg1YrYtNtzgO4CFhiBg6MDSjk7Ay36HYOnU7/tuOzIzcw==",
|
"integrity": "sha512-gL6z5N9Jm9mhY+U2KXZpteb+09zyffliRkZyZOHODGATyC5B1Jt/7TzuuiLkFsSUMLbS1OLmlj/E+/3KF4Q/4w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -1210,9 +1212,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@xmldom/xmldom": {
|
"node_modules/@xmldom/xmldom": {
|
||||||
"version": "0.8.10",
|
"version": "0.8.11",
|
||||||
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz",
|
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz",
|
||||||
"integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==",
|
"integrity": "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -1247,9 +1249,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/agent-base": {
|
"node_modules/agent-base": {
|
||||||
"version": "7.1.3",
|
"version": "7.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
|
||||||
"integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==",
|
"integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -1327,9 +1329,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ansi-regex": {
|
"node_modules/ansi-regex": {
|
||||||
"version": "6.1.0",
|
"version": "6.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz",
|
||||||
"integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
|
"integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -1535,9 +1537,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/app-builder-lib/node_modules/@electron/universal/node_modules/fs-extra": {
|
"node_modules/app-builder-lib/node_modules/@electron/universal/node_modules/fs-extra": {
|
||||||
"version": "11.3.0",
|
"version": "11.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.1.tgz",
|
||||||
"integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==",
|
"integrity": "sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -2792,9 +2794,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/electron": {
|
"node_modules/electron": {
|
||||||
"version": "37.1.0",
|
"version": "37.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/electron/-/electron-37.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/electron/-/electron-37.3.1.tgz",
|
||||||
"integrity": "sha512-Fcr3yfAw4oU392waVZSlrFUQx4P+h/k31+PRgkBY9tFx9E/zxzdPQQj0achZlG1HRDusw3ooQB+OXb9PvufdzA==",
|
"integrity": "sha512-7DhktRLqhe6OJh/Bo75bTI0puUYEmIwSzMinocgO63mx3MVjtIn2tYMzLmAleNIlud2htkjpsMG2zT4PiTCloA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -3976,9 +3978,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/form-data": {
|
"node_modules/form-data": {
|
||||||
"version": "4.0.3",
|
"version": "4.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
|
||||||
"integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==",
|
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -4622,15 +4624,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ip-address": {
|
"node_modules/ip-address": {
|
||||||
"version": "9.0.5",
|
"version": "10.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz",
|
||||||
"integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==",
|
"integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
|
||||||
"jsbn": "1.1.0",
|
|
||||||
"sprintf-js": "^1.1.3"
|
|
||||||
},
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12"
|
"node": ">= 12"
|
||||||
}
|
}
|
||||||
@@ -4806,16 +4804,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/jake": {
|
"node_modules/jake": {
|
||||||
"version": "10.9.2",
|
"version": "10.9.4",
|
||||||
"resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz",
|
"resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz",
|
||||||
"integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==",
|
"integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"async": "^3.2.3",
|
"async": "^3.2.6",
|
||||||
"chalk": "^4.0.2",
|
|
||||||
"filelist": "^1.0.4",
|
"filelist": "^1.0.4",
|
||||||
"minimatch": "^3.1.2"
|
"picocolors": "^1.1.1"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"jake": "bin/cli.js"
|
"jake": "bin/cli.js"
|
||||||
@@ -4825,9 +4822,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/jiti": {
|
"node_modules/jiti": {
|
||||||
"version": "2.4.2",
|
"version": "2.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz",
|
||||||
"integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==",
|
"integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -4847,13 +4844,6 @@
|
|||||||
"js-yaml": "bin/js-yaml.js"
|
"js-yaml": "bin/js-yaml.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/jsbn": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/json-buffer": {
|
"node_modules/json-buffer": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
|
||||||
@@ -4890,9 +4880,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/jsonfile": {
|
"node_modules/jsonfile": {
|
||||||
"version": "6.1.0",
|
"version": "6.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
|
||||||
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
"integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -6150,6 +6140,13 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/picocolors": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/picomatch": {
|
"node_modules/picomatch": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||||
@@ -6214,6 +6211,22 @@
|
|||||||
"node": "^12.20.0 || >=14"
|
"node": "^12.20.0 || >=14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/prettier": {
|
||||||
|
"version": "3.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz",
|
||||||
|
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"prettier": "bin/prettier.cjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/proc-log": {
|
"node_modules/proc-log": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/proc-log/-/proc-log-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/proc-log/-/proc-log-2.0.1.tgz",
|
||||||
@@ -6989,13 +7002,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/socks": {
|
"node_modules/socks": {
|
||||||
"version": "2.8.5",
|
"version": "2.8.7",
|
||||||
"resolved": "https://registry.npmjs.org/socks/-/socks-2.8.5.tgz",
|
"resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz",
|
||||||
"integrity": "sha512-iF+tNDQla22geJdTyJB1wM/qrX9DMRwWrciEPwWLPRWAUEM8sQiyxgckLxWT1f7+9VabJS0jTGGr4QgBuvi6Ww==",
|
"integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ip-address": "^9.0.5",
|
"ip-address": "^10.0.1",
|
||||||
"smart-buffer": "^4.2.0"
|
"smart-buffer": "^4.2.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -7082,9 +7095,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/spdx-license-ids": {
|
"node_modules/spdx-license-ids": {
|
||||||
"version": "3.0.21",
|
"version": "3.0.22",
|
||||||
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz",
|
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz",
|
||||||
"integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==",
|
"integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "CC0-1.0"
|
"license": "CC0-1.0"
|
||||||
},
|
},
|
||||||
@@ -7093,7 +7106,8 @@
|
|||||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
|
||||||
"integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==",
|
"integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-3-Clause"
|
"license": "BSD-3-Clause",
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"node_modules/ssri": {
|
"node_modules/ssri": {
|
||||||
"version": "9.0.1",
|
"version": "9.0.1",
|
||||||
@@ -7466,9 +7480,9 @@
|
|||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
"node_modules/tmp": {
|
"node_modules/tmp": {
|
||||||
"version": "0.2.3",
|
"version": "0.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz",
|
||||||
"integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==",
|
"integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -7584,9 +7598,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "5.8.3",
|
"version": "5.9.2",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz",
|
||||||
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|||||||
13
package.json
@@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"name": "freedom-loader",
|
"name": "freedom-loader",
|
||||||
"productName": "Freedom Loader",
|
"productName": "Freedom Loader",
|
||||||
"version": "1.0.1",
|
"version": "1.1.3",
|
||||||
"author": "MasterAcnolo",
|
"author": "MasterAcnolo",
|
||||||
"description": "Freedom Loader, parce que nous le valons bien",
|
"description": "Freedom Loader",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "electron-forge start",
|
"start": "electron-forge start",
|
||||||
@@ -36,12 +36,19 @@
|
|||||||
"productName": "Freedom Loader",
|
"productName": "Freedom Loader",
|
||||||
"files": [
|
"files": [
|
||||||
"**/*",
|
"**/*",
|
||||||
"node_modules/**/*"
|
"node_modules/**/*",
|
||||||
|
"!logs/*.json"
|
||||||
],
|
],
|
||||||
"directories": {
|
"directories": {
|
||||||
"buildResources": "build"
|
"buildResources": "build"
|
||||||
},
|
},
|
||||||
"asar": false,
|
"asar": false,
|
||||||
|
"extraResources": [
|
||||||
|
{
|
||||||
|
"from": "build/confirm-icon.png",
|
||||||
|
"to": "confirm-icon.png"
|
||||||
|
}
|
||||||
|
],
|
||||||
"win": {
|
"win": {
|
||||||
"target": "nsis",
|
"target": "nsis",
|
||||||
"icon": "build/installer-icon.ico"
|
"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/Vilbrequin.avif
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
public/assets/images/cyberpunk4.webp
Normal file
|
After Width: | Height: | Size: 805 KiB |
BIN
public/assets/images/goat.webp
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/assets/images/spicy.webp
Normal file
|
After Width: | Height: | Size: 35 KiB |
@@ -22,8 +22,8 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
|
|
||||||
<header>
|
<header>
|
||||||
<h1> Freedom Loader </h1>
|
<h1 id="title"> Freedom Loader </h1>
|
||||||
<p> Because why not? </p>
|
<p id="subtitle"> Because why not? </p>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<form action="/download" method="POST" id="downloadForm">
|
<form action="/download" method="POST" id="downloadForm">
|
||||||
@@ -51,24 +51,40 @@
|
|||||||
<button type="submit">Télécharger</button>
|
<button type="submit">Télécharger</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div id="downloadStatus"></div>
|
|
||||||
|
|
||||||
<!--Il faut utiliser un formulaire, parce que on doit envoyer a un serveur
|
<!--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
|
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
|
ê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
|
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.-->
|
que l'on envoie des données.-->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</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 class="infos-container">
|
||||||
<div id="videoInfo" style="margin-top: 20px;"></div>
|
<div id="videoInfo" style="margin-top: 20px;"></div>
|
||||||
</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/downloadstatus.js"></script>
|
||||||
<script src="script/fetchinfo.js"></script>
|
<script src="script/fetchinfo.js"></script>
|
||||||
|
<script src="script/chirac.js"></script>
|
||||||
|
<script src="script/customthemes.js"></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
105
public/script/customthemes.js
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
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",
|
||||||
|
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 ?",
|
||||||
|
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
|
// Récupération du formulaire de téléchargement
|
||||||
const form = document.getElementById("downloadForm");
|
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");
|
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");
|
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) => {
|
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;
|
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...";
|
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 formData = new FormData(form);
|
||||||
const params = new URLSearchParams(formData);
|
const params = new URLSearchParams(formData);
|
||||||
|
|
||||||
try {
|
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", {
|
const res = await fetch("/download", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||||
body: params,
|
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) {
|
if (!res.ok) {
|
||||||
statusDiv.textContent = "❌ Erreur pendant le téléchargement.";
|
statusDiv.textContent = "❌ Erreur pendant le téléchargement.";
|
||||||
return;
|
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();
|
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;
|
statusDiv.textContent = text;
|
||||||
} catch {
|
} 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.";
|
statusDiv.textContent = "❌ Une erreur s’est produite.";
|
||||||
} finally {
|
} finally {
|
||||||
// Réactive le bouton pour autoriser d'autres soumissions
|
// Réactive le bouton pour permettre d’autres soumissions
|
||||||
button.disabled = false;
|
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(() => {
|
setTimeout(() => {
|
||||||
statusDiv.textContent = "";
|
statusDiv.textContent = "";
|
||||||
}, 5000);
|
}, 5000);
|
||||||
|
|||||||
@@ -16,17 +16,25 @@
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// On attend que le DOM soit complètement chargé
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
// Récupération des éléments du DOM pour interaction
|
||||||
const urlInput = document.getElementById("UrlInput");
|
const urlInput = document.getElementById("UrlInput");
|
||||||
const infoDiv = document.getElementById("videoInfo");
|
const infoDiv = document.getElementById("videoInfo");
|
||||||
|
|
||||||
|
// Stocke la dernière URL fetchée pour éviter les requêtes répétées inutiles
|
||||||
let lastFetchedUrl = "";
|
let lastFetchedUrl = "";
|
||||||
|
|
||||||
|
// Active les logs DEBUG pour le développement
|
||||||
const DEBUG = true;
|
const DEBUG = true;
|
||||||
|
|
||||||
|
// Écoute les changements dans l'input URL
|
||||||
urlInput.addEventListener("input", async () => {
|
urlInput.addEventListener("input", async () => {
|
||||||
const url = urlInput.value.trim();
|
const url = urlInput.value.trim();
|
||||||
|
|
||||||
if (DEBUG) console.log("[DEBUG] Input détecté :", url);
|
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 (!url || url.length < 5) {
|
||||||
if (DEBUG) console.log("[DEBUG] URL vide ou trop courte, reset affichage.");
|
if (DEBUG) console.log("[DEBUG] URL vide ou trop courte, reset affichage.");
|
||||||
infoDiv.innerHTML = "";
|
infoDiv.innerHTML = "";
|
||||||
@@ -35,15 +43,19 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Si l'URL est identique à la dernière requêtée, on ne refait pas la requête
|
||||||
if (url === lastFetchedUrl) {
|
if (url === lastFetchedUrl) {
|
||||||
if (DEBUG) console.log("[DEBUG] Même URL que précédemment, pas de fetch.");
|
if (DEBUG) console.log("[DEBUG] Même URL que précédemment, pas de fetch.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mémorise l'URL actuelle comme dernière URL traitée
|
||||||
lastFetchedUrl = url;
|
lastFetchedUrl = url;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (DEBUG) console.log("[DEBUG] Envoi requête POST vers /info avec URL:", url);
|
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", {
|
const res = await fetch("/info", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
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);
|
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) {
|
if (!res.ok) {
|
||||||
console.error(`[ERROR] Réponse HTTP ${res.status}: ${res.statusText}`);
|
console.error(`[ERROR] Réponse HTTP ${res.status}: ${res.statusText}`);
|
||||||
infoDiv.innerHTML = "❌ Impossible de récupérer les infos (erreur serveur).";
|
infoDiv.innerHTML = "❌ Impossible de récupérer les infos (erreur serveur).";
|
||||||
@@ -59,10 +72,11 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse la réponse JSON contenant les infos vidéo
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
if (DEBUG) console.log("[DEBUG] Données reçues:", data);
|
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) {
|
if (!data || !data.title || !data.thumbnail) {
|
||||||
console.warn("[WARNING] Données incomplètes", data);
|
console.warn("[WARNING] Données incomplètes", data);
|
||||||
infoDiv.innerHTML = "❌ Données incomplètes reçues.";
|
infoDiv.innerHTML = "❌ Données incomplètes reçues.";
|
||||||
@@ -70,23 +84,28 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Formatage de la durée en minutes et secondes
|
||||||
const minutes = Math.floor(data.duration / 60);
|
const minutes = Math.floor(data.duration / 60);
|
||||||
const seconds = data.duration % 60;
|
const seconds = data.duration % 60;
|
||||||
const durationStr = `${minutes}m ${seconds.toString().padStart(2, "0")}s`;
|
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 date = data.upload_date || "";
|
||||||
const readableDate = date
|
const readableDate = date
|
||||||
? `${date.slice(6, 8)}/${date.slice(4, 6)}/${date.slice(0, 4)}`
|
? `${date.slice(6, 8)}/${date.slice(4, 6)}/${date.slice(0, 4)}`
|
||||||
: "Inconnue";
|
: "Inconnue";
|
||||||
|
|
||||||
|
// Conversion taille approximative en Mo
|
||||||
const sizeMB = data.filesize_approx
|
const sizeMB = data.filesize_approx
|
||||||
? (data.filesize_approx / (1024 * 1024)).toFixed(2) + " Mo"
|
? (data.filesize_approx / (1024 * 1024)).toFixed(2) + " Mo"
|
||||||
: "Inconnue";
|
: "Inconnue";
|
||||||
|
|
||||||
|
// Gestion des catégories, affichage par défaut si absentes
|
||||||
const categories = data.categories
|
const categories = data.categories
|
||||||
? data.categories.join(", ")
|
? data.categories.join(", ")
|
||||||
: "Non spécifiées";
|
: "Non spécifiées";
|
||||||
|
|
||||||
|
// Construction du HTML pour afficher les informations de la vidéo
|
||||||
infoDiv.innerHTML = `
|
infoDiv.innerHTML = `
|
||||||
<h3>${data.title}</h3>
|
<h3>${data.title}</h3>
|
||||||
<img src="${data.thumbnail}" width="320" alt="Thumbnail">
|
<img src="${data.thumbnail}" width="320" alt="Thumbnail">
|
||||||
@@ -102,10 +121,13 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
<li><strong>Catégories :</strong> ${categories}</li>
|
<li><strong>Catégories :</strong> ${categories}</li>
|
||||||
</ul>
|
</ul>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
// Affiche la div contenant les infos
|
||||||
infoDiv.classList.add("visible");
|
infoDiv.classList.add("visible");
|
||||||
|
|
||||||
if (DEBUG) console.log("[DEBUG] Info affichée avec succès.");
|
if (DEBUG) console.log("[DEBUG] Info affichée avec succès.");
|
||||||
} catch (e) {
|
} 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);
|
console.error("[CRITICAL] Erreur lors de la récupération:", e);
|
||||||
infoDiv.innerHTML = "❌ Erreur réseau ou JSON.";
|
infoDiv.innerHTML = "❌ Erreur réseau ou JSON.";
|
||||||
infoDiv.classList.remove("visible");
|
infoDiv.classList.remove("visible");
|
||||||
|
|||||||
@@ -15,15 +15,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.checkmark {
|
.checkmark {
|
||||||
--clr: #3B82F6 ;
|
--clr: var(--checkbox-bg-checked);
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
height: 1.3em;
|
height: 1.3em;
|
||||||
width: 1.3em;
|
width: 1.3em;
|
||||||
background-color: #ccc;
|
background-color: var(--checkbox-bg-default);
|
||||||
border-radius: 50%;
|
border-radius: .5rem;
|
||||||
transition: 300ms;
|
transition: background-color 300ms, border-radius 300ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkbox-mp3 input:checked ~ .checkmark {
|
.checkbox-mp3 input:checked ~ .checkmark {
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
top: 0.25em;
|
top: 0.25em;
|
||||||
width: 0.25em;
|
width: 0.25em;
|
||||||
height: 0.5em;
|
height: 0.5em;
|
||||||
border: solid #E0E0E2;
|
border: solid var(--checkbox-checkmark-border-color);
|
||||||
border-width: 0 0.15em 0.15em 0;
|
border-width: 0 0.15em 0.15em 0;
|
||||||
transform: rotate(45deg);
|
transform: rotate(45deg);
|
||||||
}
|
}
|
||||||
|
|||||||
41
public/styles/components/editpathbutton.css
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
.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: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s ease, transform 0.2s ease;
|
||||||
|
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-path button#changePath:hover {
|
||||||
|
background-color: var(--form-button-bg-hover-color);
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
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;
|
min-height: 1.5em;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #007bff;
|
color: var(--download-status-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
label {
|
label {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
form {
|
form {
|
||||||
background-color: #fff;
|
background-color: var(--form-bg-color);
|
||||||
padding: 2em;
|
padding: 2em;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
||||||
@@ -13,8 +13,10 @@ form input[type="text"],
|
|||||||
form input[type="url"],
|
form input[type="url"],
|
||||||
form select {
|
form select {
|
||||||
padding: 0.75em;
|
padding: 0.75em;
|
||||||
border: 1px solid #ccc;
|
border: 1px solid var(--form-input-border-color);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
background-color: var(--form-input-bg-color);
|
||||||
|
color: var(--form-input-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
form input[type="checkbox"] {
|
form input[type="checkbox"] {
|
||||||
@@ -22,8 +24,8 @@ form input[type="checkbox"] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
form button {
|
form button {
|
||||||
background-color: #007bff;
|
background-color: var(--form-button-bg-color);
|
||||||
color: #fff;
|
color: var(--form-button-text-color);
|
||||||
border: none;
|
border: none;
|
||||||
padding: 0.75em;
|
padding: 0.75em;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
@@ -32,7 +34,7 @@ form button {
|
|||||||
}
|
}
|
||||||
|
|
||||||
form button:hover {
|
form button:hover {
|
||||||
background-color: #0056b3;
|
background-color: var(--form-button-bg-hover-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
form#downloadForm {
|
form#downloadForm {
|
||||||
@@ -48,26 +50,27 @@ form#downloadForm {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
align-self: center;
|
align-self: center;
|
||||||
gap: 0.5em;
|
gap: 0.5em;
|
||||||
|
color: var(--form-input-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
#UrlInput {
|
#UrlInput {
|
||||||
padding: 0.75em 1em;
|
padding: 0.75em 1em;
|
||||||
border-color: #ccc;
|
border-color: var(--form-input-border-color);
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
transition: border-color 0.3s ease, box-shadow 0.3s ease;
|
transition: border-color 0.3s ease, box-shadow 0.3s ease;
|
||||||
background-color: #f9f9f9;
|
background-color: var(--form-input-bg-color);
|
||||||
color: #333;
|
color: var(--form-input-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
#UrlInput:focus {
|
#UrlInput:focus {
|
||||||
border-color: #0056b3;
|
border-color: var(--form-input-border-focus-color);
|
||||||
box-shadow: 0 0 5px rgba(0,123,255,0.4);
|
box-shadow: 0 0 5px rgba(0,123,255,0.4);
|
||||||
outline: none;
|
outline: none;
|
||||||
background-color: #fff;
|
background-color: var(--form-input-bg-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
#UrlInput::placeholder {
|
#UrlInput::placeholder {
|
||||||
color: #aaa;
|
color: var(--form-input-placeholder-color);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
background-color: var(--infos-box-color);
|
background-color: var(--infos-box-color);
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
box-shadow: 0 8px 16px rgba(0,0,0,0.1);
|
box-shadow: 0 8px 16px rgba(0,0,0,0.1);
|
||||||
color: #222;
|
color: var(--video-info-text-color);
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
#videoInfo h3 {
|
#videoInfo h3 {
|
||||||
font-size: 1.8rem;
|
font-size: 1.8rem;
|
||||||
margin-bottom: 0.6em;
|
margin-bottom: 0.6em;
|
||||||
color: #007bff;
|
color: var(--video-info-heading-color);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
@@ -51,15 +51,15 @@
|
|||||||
#videoInfo ul li {
|
#videoInfo ul li {
|
||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.5em;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #444;
|
color: var(--video-info-list-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
#videoInfo ul li strong {
|
#videoInfo ul li strong {
|
||||||
color: #000;
|
color: var(--video-info-list-strong-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
#videoInfo ul li a {
|
#videoInfo ul li a {
|
||||||
color: #007bff;
|
color: var(--video-info-link-color);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,22 @@
|
|||||||
@import url("layout/header.css");
|
@import url("layout/header.css");
|
||||||
@import url("layout/videoinfo.css");
|
@import url("layout/videoinfo.css");
|
||||||
@import url("components/checkbox.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');
|
@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{
|
html,body{
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@@ -35,6 +47,7 @@ body {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
header p{
|
header p{
|
||||||
@@ -45,22 +58,11 @@ header p{
|
|||||||
color: var(--mp3-text-color);
|
color: var(--mp3-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* #videoInfo {
|
body,
|
||||||
margin-top: 100px;
|
.container,
|
||||||
padding: 1em;
|
input,
|
||||||
max-width: 600px;
|
button,
|
||||||
margin-left: auto;
|
select,
|
||||||
margin-right: auto;
|
label {
|
||||||
border-radius: 8px;
|
transition: background-color 0.2s ease, color 0.5s ease, border-color 0.2s ease;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#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/goat.webp');
|
||||||
|
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/cyberpunk4.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;
|
||||||
|
}
|
||||||
44
public/styles/theme/spicy-theme.css
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
body.spicy {
|
||||||
|
/* Couleurs générales */
|
||||||
|
--background-color: #121212; /* fond noir profond */
|
||||||
|
--default-text-color: #f5f5f5; /* texte clair */
|
||||||
|
--subtitle-color: #e29191; /* 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: cover;
|
||||||
|
background-position: center;
|
||||||
|
background-attachment: fixed;
|
||||||
|
}
|
||||||
8
public/styles/theme/themeimport.css
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
@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");
|
||||||
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.avif'); /* adapte ton image */
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center;
|
||||||
|
background-attachment: fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.vilbrequin .download-path{
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
@@ -1,11 +1,36 @@
|
|||||||
:root{
|
:root {
|
||||||
--background-color : #0F172A;
|
/* Couleurs générales */
|
||||||
--infos-box-color: #f1f1f1 ;
|
--background-color: #001224; /* body background bleu nuit */
|
||||||
--title-color: ;
|
--default-text-color: #eee; /* texte clair principal */
|
||||||
--subtitle-color : #a3aab4;
|
--subtitle-color: #007bff; /* sous-titres, header p */
|
||||||
--mp3-text-color: #0F172A;
|
--mp3-text-color: #000000; /* couleur texte checkbox inline */
|
||||||
--default-text-color: #F8FAFC;
|
|
||||||
|
|
||||||
--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;
|
||||||
}
|
}
|
||||||
@@ -18,51 +18,67 @@
|
|||||||
|
|
||||||
const { createLogger, format, transports } = require("winston");
|
const { createLogger, format, transports } = require("winston");
|
||||||
const DailyRotateFile = require("winston-daily-rotate-file");
|
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
|
// Définition du dossier où seront stockés les logs
|
||||||
const path = require("path"); // pour gérer proprement les chemins
|
// 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
|
let logDir;
|
||||||
const logDir = path.join(__dirname, "../logs");
|
if (process.platform === "win32") {
|
||||||
// on le crée si inexistant
|
logDir = path.join(os.homedir(), "AppData", "Local", "FreedomLoader", "logs");
|
||||||
if (!fs.existsSync(logDir)) fs.mkdirSync(logDir);
|
} 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() {
|
function getSessionStartLine() {
|
||||||
const now = new Date().toISOString();
|
const now = new Date().toISOString();
|
||||||
return `--- Démarrage de la session : ${now} ---`;
|
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() {
|
function getSessionEndLine() {
|
||||||
const now = new Date().toISOString();
|
const now = new Date().toISOString();
|
||||||
return `--- Fin de la session : ${now} ---`;
|
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({
|
const logger = createLogger({
|
||||||
level: "info", // niveau de log minimum (info, warn, error, etc.)
|
level: "info",
|
||||||
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
|
|
||||||
format: format.combine(
|
format: format.combine(
|
||||||
format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
|
format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
|
||||||
format.printf(({ timestamp, level, message }) =>
|
format.printf(({ timestamp, level, message }) =>
|
||||||
`${timestamp} | ${level.toUpperCase()} | ${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({
|
new transports.Console({
|
||||||
format: format.combine(
|
format: format.combine(
|
||||||
format.colorize(),
|
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() {
|
function logSessionStart() {
|
||||||
logger.info(getSessionStartLine());
|
logger.info(getSessionStartLine());
|
||||||
}
|
}
|
||||||
|
|
||||||
// pour la fin de la session
|
// Helper pour marquer la fin de session dans les logs
|
||||||
function logSessionEnd() {
|
function logSessionEnd() {
|
||||||
logger.info(getSessionEndLine());
|
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 = {
|
module.exports = {
|
||||||
logger,
|
logger,
|
||||||
logSessionStart,
|
logSessionStart,
|
||||||
logSessionEnd,
|
logSessionEnd,
|
||||||
|
logDir,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,155 +11,186 @@
|
|||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
See the GNU General Public License for more details.
|
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 express = require("express");
|
||||||
const router = express.Router(); // crée un routeur Express pour modulariser les routes
|
const router = express.Router();
|
||||||
const { execFile } = require("child_process"); // permet d’exécuter un binaire externe sans passer par un shell
|
const { execFile } = require("child_process");
|
||||||
const path = require("path"); // module Node.js pour gérer les chemins de fichiers
|
const path = require("path");
|
||||||
const fs = require("fs"); // module Node.js pour manipuler le système de fichiers
|
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(__dirname, '../../yt-dlp.exe');
|
||||||
|
const ytDlpPath = path.join(__dirname, '../../yt-dlp 2025.08.20.exe');
|
||||||
|
|
||||||
// chemin vers l’exécutable yt-dlp packagé avec l’appli
|
|
||||||
const ytDlpPath = path.join(__dirname, '../../yt-dlp.exe');
|
|
||||||
|
|
||||||
// route POST sur la racine (ex: POST /download)
|
|
||||||
router.post("/", (req, res) => {
|
router.post("/", (req, res) => {
|
||||||
try {
|
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 = {
|
const options = {
|
||||||
url: req.body.url, // URL à télécharger
|
url: req.body.url,
|
||||||
audioOnly: req.body.audioOnly === "1", // conversion audio uniquement (mp3)
|
audioOnly: req.body.audioOnly === "1",
|
||||||
quality: req.body.quality || "best", // qualité vidéo à télécharger
|
quality: req.body.quality || "best",
|
||||||
subtitles: req.body.subs === "1", // récupérer les sous-titres anglais
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Validation simple : si l'URL est manquante, on rejette la requête
|
||||||
if (!options.url) {
|
if (!options.url) {
|
||||||
// si l’URL est absente, on log et on renvoie 400
|
|
||||||
logger.warn("Requête POST /download sans URL");
|
logger.warn("Requête POST /download sans URL");
|
||||||
return res.status(400).send("❌ URL manquante !");
|
return res.status(400).send("❌ URL manquante !");
|
||||||
}
|
}
|
||||||
|
|
||||||
// on récupère le dossier de sortie depuis app.locals (défini ailleurs dans le serveur)
|
// Récupération du chemin de sauvegarde personnalisé envoyé depuis Electron,
|
||||||
const outputFolder = req.app.locals.outputFolder;
|
// 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
|
// On normalise le chemin pour éviter des erreurs liées aux séparateurs ou chemins relatifs
|
||||||
if (!fs.existsSync(outputFolder)) {
|
requestedOutputFolder = path.normalize(requestedOutputFolder);
|
||||||
fs.mkdirSync(outputFolder, { recursive: true });
|
|
||||||
logger.info("Dossier Freedom Loader Output créé à la volée dans Téléchargements.");
|
/*
|
||||||
|
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)
|
// Vérifie que le dossier de sortie existe, sinon le crée récursivement
|
||||||
const outputTemplate = path.join(outputFolder, "%(title)s.%(ext)s");
|
if (!fs.existsSync(requestedOutputFolder)) {
|
||||||
|
fs.mkdirSync(requestedOutputFolder, { recursive: true });
|
||||||
|
logger.info(`Dossier de sortie créé : ${requestedOutputFolder}`);
|
||||||
|
}
|
||||||
|
|
||||||
// tableau d'arguments pour yt-dlp
|
// Construction du template de sortie pour yt-dlp (nom de fichier basé sur le titre)
|
||||||
const args = [];
|
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
|
||||||
|
];
|
||||||
|
|
||||||
|
// Si l'option audioOnly est activée, on ajoute les flags pour extraction audio en mp3
|
||||||
if (options.audioOnly) {
|
if (options.audioOnly) {
|
||||||
// si audioOnly, on force extraction audio en mp3
|
args.push(
|
||||||
args.push("--extract-audio", "--audio-format", "mp3");
|
"-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
|
// Correspondance entre la qualité choisie et le format à demander à yt-dlp
|
||||||
// args.push("-f", options.quality);
|
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;
|
// Sélection du format basé sur la qualité, par défaut 'best' si option invalide
|
||||||
// if (options.quality === "best") {
|
const format = qualityMap[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";
|
|
||||||
}
|
|
||||||
args.push("-f", format);
|
args.push("-f", format);
|
||||||
|
|
||||||
// modèle de sortie
|
// Ajout du template de sortie et de l'URL à télécharger
|
||||||
args.push("-o", outputTemplate);
|
args.push("-o", outputTemplate);
|
||||||
// enfin, l'URL à télécharger
|
|
||||||
args.push(options.url);
|
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(" ")}`);
|
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);
|
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) => {
|
child.stdout.on("data", (data) => {
|
||||||
data.toString().split("\n").forEach(line => {
|
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) => {
|
child.stderr.on("data", (data) => {
|
||||||
data.toString().split("\n").forEach(line => {
|
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) => {
|
child.on("error", (err) => {
|
||||||
logger.error(`Erreur lancement yt-dlp : ${err.message}`);
|
logger.error(`Erreur lancement yt-dlp : ${err.message}`);
|
||||||
res.status(500).send(`❌ Erreur lors de l'exécution : ${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) => {
|
child.on("close", (code) => {
|
||||||
logger.info(`yt-dlp terminé avec code de sortie : ${code}`);
|
logger.info(`yt-dlp terminé avec code de sortie : ${code}`);
|
||||||
if(code === 0) {
|
if (code === 0) {
|
||||||
// succès
|
|
||||||
|
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é !");
|
res.send("✅ Téléchargement terminé !");
|
||||||
} else {
|
} else {
|
||||||
// yt-dlp a échoué
|
|
||||||
res.status(500).send(`❌ yt-dlp a échoué avec le code : ${code}`);
|
res.status(500).send(`❌ yt-dlp a échoué avec le code : ${code}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (err) {
|
} 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}`);
|
logger.error(`Erreur serveur dans /download : ${err.message}`);
|
||||||
res.status(500).send(`Erreur serveur : ${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;
|
module.exports = router;
|
||||||
@@ -16,40 +16,66 @@
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const express = require("express"); // importe Express
|
const express = require("express");
|
||||||
const router = express.Router(); // crée un routeur Express indépendant
|
const router = express.Router();
|
||||||
const { execFile } = require("child_process"); // permet d’exécuter un programme externe
|
const { execFile } = require("child_process");
|
||||||
const path = require("path"); // module pour gérer les chemins de fichiers
|
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é
|
// Path absolu vers l'exécutable yt-dlp
|
||||||
const ytDlpPath = path.join(__dirname, "../../yt-dlp.exe");
|
const ytDlpPath = path.join(__dirname, '../../yt-dlp 2025.08.20.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) => {
|
router.post("/", (req, res) => {
|
||||||
const url = req.body.url; // récupère l'URL depuis la requête POST
|
const url = req.body.url;
|
||||||
if (!url) return res.status(400).send("❌ URL manquante"); // validation simple
|
|
||||||
|
|
||||||
// exécute yt-dlp avec --dump-json pour récupérer les métadonnées de la vidéo
|
// Validation basique : on refuse la requête sans URL
|
||||||
execFile(ytDlpPath, ["--dump-json", url], (error, stdout, stderr) => {
|
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) {
|
if (error) {
|
||||||
// gestion d’erreur : par exemple yt-dlp plante ou l’URL est invalide
|
// En cas d'erreur, on log l'erreur et le stderr, puis on renvoie un code 500
|
||||||
console.error("Erreur lors de l'exécution de yt-dlp :", error);
|
logger.error(`Erreur exécution yt-dlp: ${error.message}`);
|
||||||
console.error("stderr :", stderr);
|
logger.debug(`stderr: ${stderr}`);
|
||||||
return res.status(500).send("❌ Impossible de récupérer les infos.");
|
return res.status(500).send("❌ Impossible de récupérer les infos.");
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// on parse le JSON envoyé par yt-dlp
|
// yt-dlp peut retourner plusieurs JSON séparés par des sauts de ligne
|
||||||
const info = JSON.parse(stdout);
|
// On split et parse chacun proprement
|
||||||
// on renvoie l’objet JSON directement au frontend
|
const infos = stdout
|
||||||
res.json(info);
|
.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) {
|
} catch (e) {
|
||||||
// le JSON est mal formé
|
// Si parsing JSON échoue, on log et renvoie une erreur 500
|
||||||
console.error("Erreur lors du parsing JSON :", e);
|
logger.error(`Erreur parsing JSON: ${e.message}`);
|
||||||
res.status(500).send("❌ JSON illisible.");
|
return res.status(500).send("❌ JSON illisible.");
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// on exporte le routeur pour être utilisé dans app.js ou ailleurs
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|||||||
@@ -16,88 +16,88 @@
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const express = require("express"); // framework web
|
const express = require("express"); // Framework web pour créer l’API et servir les fichiers
|
||||||
const { exec } = require("child_process"); // si besoin d’exécuter des commandes (pas utilisé ici directement)
|
const { exec } = require("child_process"); // Permet d’exécuter des commandes systèmes (pas utilisé directement ici)
|
||||||
const fs = require("fs"); // accès au système de fichiers
|
const fs = require("fs"); // Module système pour gérer fichiers et dossiers
|
||||||
const path = require("path"); // gestion de chemins
|
const path = require("path"); // Gestion propre des chemins de fichiers et dossiers
|
||||||
const debug = require("debug")("freedom-loader:server");
|
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");
|
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
|
// 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)
|
||||||
// on définit le dossier de téléchargement dans le dossier Téléchargements de l’utilisateur
|
|
||||||
const downloadsPath = path.join(process.env.USERPROFILE, "Downloads");
|
const downloadsPath = path.join(process.env.USERPROFILE, "Downloads");
|
||||||
const outputFolder = path.join(downloadsPath, "Freedom Loader Output");
|
const outputFolder = path.join(downloadsPath, "Freedom Loader Output");
|
||||||
|
|
||||||
// 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)) {
|
if (!fs.existsSync(outputFolder)) {
|
||||||
try {
|
try {
|
||||||
fs.mkdirSync(outputFolder, { recursive: true });
|
fs.mkdirSync(outputFolder, { recursive: true }); // création récursive au cas où
|
||||||
logger.info("Dossier Freedom Loader Output cree dans Telechargements.");
|
logger.info("Dossier Freedom Loader Output cree dans Telechargements.");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error("Impossible de creer le dossier :", 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 {
|
} else {
|
||||||
logger.info("Dossier Freedom Loader Output deja existant.");
|
logger.info("Dossier Freedom Loader Output 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;
|
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 }));
|
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");
|
const staticPath = path.join(__dirname, "../public");
|
||||||
debug("Serveur statique sur", staticPath);
|
debug("Serveur statique sur", staticPath);
|
||||||
app.use(express.static(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 infoRoute = require("./routes/info");
|
||||||
const downloadRoute = require("./routes/download");
|
const downloadRoute = require("./routes/download");
|
||||||
debug("Routes /download et /info installees");
|
debug("Routes /download et /info installees");
|
||||||
app.use("/download", downloadRoute);
|
app.use("/download", downloadRoute);
|
||||||
app.use("/info", infoRoute);
|
app.use("/info", infoRoute);
|
||||||
|
|
||||||
// route GET / de base
|
// Route GET / qui sert la page principale index.html
|
||||||
app.get("/", (req, res) => {
|
app.get("/", (req, res) => {
|
||||||
debug("Requete GET / servie");
|
debug("Requete GET / servie");
|
||||||
res.sendFile(path.join(__dirname, "../public/index.html"));
|
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() {
|
function startServer() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
logger.info("Demarrage du serveur Express...");
|
logger.info("Demarrage du serveur Express...");
|
||||||
const serverInstance = app.listen(8080, () => {
|
const serverInstance = app.listen(8080, () => {
|
||||||
logger.info("Serveur Express pret sur http://localhost: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) => {
|
serverInstance.on("error", (err) => {
|
||||||
logger.error("Erreur serveur Express :", 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
|
// Gestion propre de la fermeture du process pour logger la fin de session
|
||||||
// process.on("exit", () => {
|
process.on("SIGINT", () => { // Capture Ctrl+C (interruption)
|
||||||
// logSessionEnd();
|
|
||||||
// });
|
|
||||||
process.on("SIGINT", () => { // Ctrl+C
|
|
||||||
logSessionEnd();
|
logSessionEnd();
|
||||||
process.exit();
|
process.exit();
|
||||||
});
|
});
|
||||||
process.on("SIGTERM", () => { // kill
|
process.on("SIGTERM", () => { // Capture kill ou arrêt du process
|
||||||
logSessionEnd();
|
logSessionEnd();
|
||||||
process.exit();
|
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 };
|
module.exports = { startServer };
|
||||||
|
|||||||