From f7abca90cb37403e2fd81f4146d8dc8f3eea9200 Mon Sep 17 00:00:00 2001
From: MasterAcnolo <68693319+MasterAcnolo@users.noreply.github.com>
Date: Sat, 28 Jun 2025 09:49:36 +0200
Subject: [PATCH] LE POULET
---
package.json | 6 +-
public/index.html | 35 ++++----
public/script/downloadstatus.js | 32 +++++++-
public/script/fetchinfo.js | 127 +++++++++++++++--------------
public/styles/layout/container.css | 13 +++
public/styles/layout/ctr-infos.css | 12 +++
public/styles/layout/form.css | 44 ++++++++++
public/styles/styles.css | 48 ++++++++++-
8 files changed, 234 insertions(+), 83 deletions(-)
create mode 100644 public/styles/layout/form.css
diff --git a/package.json b/package.json
index f6fc735..78c419a 100644
--- a/package.json
+++ b/package.json
@@ -1,5 +1,9 @@
{
"dependencies": {
"express": "^5.1.0"
- }
+ },
+ "scripts": {
+ "start": "node server/server.js",
+ "dev": "nodemon server/server.js"
+}
}
diff --git a/public/index.html b/public/index.html
index 5d846f2..38e370a 100644
--- a/public/index.html
+++ b/public/index.html
@@ -9,26 +9,29 @@
+
diff --git a/public/script/downloadstatus.js b/public/script/downloadstatus.js
index 5392ce7..9376789 100644
--- a/public/script/downloadstatus.js
+++ b/public/script/downloadstatus.js
@@ -1,29 +1,55 @@
+// Récupération du formulaire de téléchargement
const form = document.getElementById("downloadForm");
+
+// Div où on affiche l'état de progression (en cours, erreur, succès)
const statusDiv = document.getElementById("downloadStatus");
-form.addEventListener("submit", async (e) => {
- e.preventDefault(); // empêche la soumission classique (rechargement)
+// Récupération du bouton de validation pour pouvoir le désactiver pendant la requête
+const button = form.querySelector("button");
+// On intercepte la soumission du formulaire
+form.addEventListener("submit", async (e) => {
+ e.preventDefault(); // empêche le rechargement de la page par le navigateur
+
+ // On désactive le bouton pour éviter les clics multiples
+ button.disabled = true;
+
+ // Affiche un message d'attente à l'utilisateur
statusDiv.textContent = "⏳ Téléchargement en cours...";
+ // Prépare les données du formulaire sous forme URL-encoded
const formData = new FormData(form);
const params = new URLSearchParams(formData);
try {
+ // Envoi de la requête POST vers /download avec les données du formulaire
const res = await fetch("/download", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: params,
});
+ // Si la réponse n'est pas un succès (code 4xx ou 5xx)
if (!res.ok) {
statusDiv.textContent = "❌ Erreur pendant le téléchargement.";
return;
}
+ // On récupère le texte de la réponse du serveur
const text = await res.text();
- statusDiv.textContent = text; // affiche « Téléchargement terminé » ou autre message serveur
+
+ // Affiche le message de fin de téléchargement (par exemple « Téléchargement terminé »)
+ statusDiv.textContent = text;
} catch {
+ // Gestion d'erreur en cas de problème réseau ou autre
statusDiv.textContent = "❌ Une erreur s’est produite.";
+ } finally {
+ // Réactive le bouton pour autoriser d'autres soumissions
+ button.disabled = false;
+
+ // Efface le message de status après 5 secondes pour garder l'UI propre
+ setTimeout(() => {
+ statusDiv.textContent = "";
+ }, 5000);
}
});
diff --git a/public/script/fetchinfo.js b/public/script/fetchinfo.js
index 51c181b..89d81bf 100644
--- a/public/script/fetchinfo.js
+++ b/public/script/fetchinfo.js
@@ -1,73 +1,78 @@
-const urlInput = document.getElementById("UrlInput"); // Récupère le champ input où l'utilisateur entre l'URL
-const infoDiv = document.getElementById("videoInfo"); // Récupère la div où on affichera les infos
+document.addEventListener("DOMContentLoaded", () => {
+ const urlInput = document.getElementById("UrlInput");
+ const infoDiv = document.getElementById("videoInfo");
-let lastFetchedUrl = ""; // Pour éviter de refaire plusieurs requêtes pour la même URL
+ let lastFetchedUrl = "";
-// On écoute chaque changement dans le champ URL
-urlInput.addEventListener("input", async () => {
- const url = urlInput.value; // On prend la valeur actuelle du champ
+ urlInput.addEventListener("input", async () => {
+ const url = urlInput.value.trim();
+ console.log("Input détecté :", url);
- // Si pas d'URL, ou URL trop courte, ou déjà traitée, on sort
- if (!url || url.length < 5 || url === lastFetchedUrl) return;
-
- lastFetchedUrl = url; // On mémorise l'URL traitée
-
- try {
- // Envoi d'une requête POST vers le backend /info avec l'URL dans le corps
- const res = await fetch("/info", {
- method: "POST",
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
- body: new URLSearchParams({ url }) // Encode l'URL au format x-www-form-urlencoded
- });
-
- // Si la réponse n'est pas OK (erreur 4xx ou 5xx)
- if (!res.ok) {
- infoDiv.innerHTML = "❌ Impossible de récupérer les infos."; // Message d'erreur utilisateur
+ // Si URL vide ou trop courte : on vide et cache la div
+ if (!url || url.length < 5) {
+ infoDiv.innerHTML = "";
+ infoDiv.classList.remove("visible");
+ lastFetchedUrl = "";
return;
}
+ // Si même URL que précédemment, on ne fait rien
+ if (url === lastFetchedUrl) return;
- // On récupère la réponse JSON (les infos de la vidéo)
- const data = await res.json();
+ lastFetchedUrl = url;
- // Formatage de la durée en minutes et secondes (ex: 4m 35s)
- const minutes = Math.floor(data.duration / 60);
- const seconds = data.duration % 60;
- const durationStr = `${minutes}m ${seconds.toString().padStart(2, '0')}s`;
+ try {
+ const res = await fetch("/info", {
+ method: "POST",
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
+ body: new URLSearchParams({ url }),
+ });
- // Formatage de la date d'upload depuis YYYYMMDD vers JJ/MM/AAAA
- const date = data.upload_date || "";
- const readableDate = date
- ? `${date.slice(6, 8)}/${date.slice(4, 6)}/${date.slice(0, 4)}`
- : "Inconnue";
+ if (!res.ok) {
+ infoDiv.innerHTML = "❌ Impossible de récupérer les infos.";
+ infoDiv.classList.remove("visible");
+ return;
+ }
- // Taille estimée en Mo, ou "Inconnue" si pas dispo
- const sizeMB = data.filesize_approx
- ? (data.filesize_approx / (1024 * 1024)).toFixed(2) + " Mo"
- : "Inconnue";
+ const data = await res.json();
- // Catégories en chaîne séparée par des virgules, ou "Non spécifiées"
- const categories = data.categories
- ? data.categories.join(", ")
- : "Non spécifiées";
+ const minutes = Math.floor(data.duration / 60);
+ const seconds = data.duration % 60;
+ const durationStr = `${minutes}m ${seconds.toString().padStart(2, "0")}s`;
- // Mise à jour du contenu HTML de la div avec toutes les infos formatées
- infoDiv.innerHTML = `
- ${data.title}
-
-
- - Durée : ${durationStr}
- - Uploader : ${data.uploader || "Inconnu"}
- - Date d’upload : ${readableDate}
- - Vues : ${data.view_count?.toLocaleString() || "?"}
- - Likes : ${data.like_count?.toLocaleString() || "?"}
- - URL : ${data.webpage_url}
- - Channel : ${data.channel_url}
- - Taille estimée : ${sizeMB}
- - Catégories : ${categories}
-
- `;
- } catch (e) {
- // Si une erreur survient lors de la récupération ou du parsing
- infoDiv.innerHTML = "❌ Erreur lors de la récupération.";
- }
+ const date = data.upload_date || "";
+ const readableDate = date
+ ? `${date.slice(6, 8)}/${date.slice(4, 6)}/${date.slice(0, 4)}`
+ : "Inconnue";
+
+ const sizeMB = data.filesize_approx
+ ? (data.filesize_approx / (1024 * 1024)).toFixed(2) + " Mo"
+ : "Inconnue";
+
+ const categories = data.categories
+ ? data.categories.join(", ")
+ : "Non spécifiées";
+
+ infoDiv.innerHTML = `
+ ${data.title}
+
+
+ - Durée : ${durationStr}
+ - Uploader : ${data.uploader || "Inconnu"}
+ - Date d’upload : ${readableDate}
+ - Vues : ${data.view_count?.toLocaleString() || "?"}
+ - Likes : ${data.like_count?.toLocaleString() || "?"}
+ - URL : ${data.webpage_url}
+ - Channel : ${data.channel_url}
+ - Taille estimée : ${sizeMB}
+ - Catégories : ${categories}
+
+ `;
+ // Affiche la div avec la classe pour transition douce
+ infoDiv.classList.add("visible");
+ } catch (e) {
+ console.error("Erreur lors de la récupération :", e);
+ infoDiv.innerHTML = "❌ Erreur lors de la récupération.";
+ infoDiv.classList.remove("visible");
+ }
+ });
});
diff --git a/public/styles/layout/container.css b/public/styles/layout/container.css
index e69de29..15de33e 100644
--- a/public/styles/layout/container.css
+++ b/public/styles/layout/container.css
@@ -0,0 +1,13 @@
+.container {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ background-color: #121212;
+ padding: 1em;
+ box-shadow: 0 2px 5px rgba(0,0,0,0.5);
+ z-index: 1000;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
\ No newline at end of file
diff --git a/public/styles/layout/ctr-infos.css b/public/styles/layout/ctr-infos.css
index e69de29..ed71cf6 100644
--- a/public/styles/layout/ctr-infos.css
+++ b/public/styles/layout/ctr-infos.css
@@ -0,0 +1,12 @@
+#downloadStatus {
+ min-height: 1.5em; /* pour garder la place même vide */
+ font-weight: bold;
+ text-align: center;
+ color: #007bff;
+}
+
+label {
+ display: flex;
+ align-items: center;
+ font-size: 0.9em;
+}
\ No newline at end of file
diff --git a/public/styles/layout/form.css b/public/styles/layout/form.css
new file mode 100644
index 0000000..a85d857
--- /dev/null
+++ b/public/styles/layout/form.css
@@ -0,0 +1,44 @@
+form {
+ background-color: #fff;
+ padding: 2em;
+ border-radius: 12px;
+ box-shadow: 0 4px 12px rgba(0,0,0,0.1);
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ gap: 1em;
+}
+
+form input[type="text"],
+form input[type="url"],
+form select {
+ padding: 0.75em;
+ border: 1px solid #ccc;
+ border-radius: 8px;
+}
+
+form input[type="checkbox"] {
+ margin-right: 0.5em;
+}
+
+form button {
+ background-color: #007bff;
+ color: #fff;
+ border: none;
+ padding: 0.75em;
+ border-radius: 8px;
+ cursor: pointer;
+ transition: background-color 0.3s ease;
+}
+
+form button:hover {
+ background-color: #0056b3;
+}
+
+form#downloadForm {
+ display: flex;
+ gap: 0.5em;
+ max-width: 600px;
+ width: 100%;
+ justify-content: center;
+}
\ No newline at end of file
diff --git a/public/styles/styles.css b/public/styles/styles.css
index 4890f27..05ade30 100644
--- a/public/styles/styles.css
+++ b/public/styles/styles.css
@@ -1,3 +1,7 @@
+@import url("layout/form.css");
+@import url("layout/container.css");
+@import url("layout/ctr-infos.css");
+
*{
margin: 0;
padding: 0;
@@ -5,6 +9,46 @@
}
-body{
- background-color:rgb(83, 83, 83) ;
+body {
+ margin: 0;
+ font-family: Arial, sans-serif;
+ background-color: #f5f5f5;
+ color: #333;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+}
+
+#downloadStatus {
+ margin-top: 0.5em;
+ font-weight: bold;
+ color: #eee;
+ min-height: 1.5em;
+}
+
+#videoInfo {
+ margin-top: 100px;
+ padding: 1em;
+ max-width: 600px;
+ margin-left: auto;
+ margin-right: auto;
+ border-radius: 8px;
+ color: #fff;
+ 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;
+}
+
+label {
+ display: flex;
+ align-items: center;
+ font-size: 0.9em;
}
\ No newline at end of file