diff --git a/main.js b/main.js index 8895502..e1ab17c 100644 --- a/main.js +++ b/main.js @@ -3,7 +3,7 @@ const { app, BrowserWindow, ipcMain, dialog, Menu, shell } = require("electron") const path = require("path"); const os = require("os"); const { logger, logSessionStart, logSessionEnd, logDir } = require("./server/logger"); -const {AutoUpdater} = require("./server/autoupdate.js"); +const {AutoUpdater} = require("./server/update.js"); const gotLock = app.requestSingleInstanceLock(); let mainWindow; diff --git a/public/script/fetchinfo.js b/public/script/fetchinfo.js index 306762a..d9e76c5 100644 --- a/public/script/fetchinfo.js +++ b/public/script/fetchinfo.js @@ -70,27 +70,89 @@ document.addEventListener("DOMContentLoaded", () => { } if (data.type === "playlist") { + + infoDiv.classList.add("playlist-mode"); infoDiv.innerHTML = ` -
Playlist détectée – ${data.count} vidéos
-Channel : ${data.channel || "Inconnu"}
+ +Channel : ${data.channel || "Unknown"}
`; const listDiv = document.getElementById("playlistVideos"); - data.videos.forEach(v => { + data.videos.forEach(v => { const durationStr = `${Math.floor(v.duration/60)}m ${(v.duration%60).toString().padStart(2,"0")}s`; + const videoUrl = v.id ? `https://www.youtube.com/watch?v=${v.id}` : v.url; + listDiv.innerHTML += ` `; }); + + listDiv.addEventListener("click", (e) => { + if (e.target.classList.contains("copy-btn")) { + const btn = e.target; + if (btn.disabled) return; + + btn.disabled = true; + const url = btn.dataset.url; + + navigator.clipboard.writeText(url) + .then(() => { + const original = btn.textContent; + + // fade out + shrink + btn.style.opacity = 0; + btn.style.transform = "scale(0.7)"; + + setTimeout(() => { + btn.textContent = "✅"; + btn.style.opacity = 1; + btn.style.transform = "scale(1)"; + + setTimeout(() => { + btn.style.opacity = 0; + btn.style.transform = "scale(0.7)"; + + setTimeout(() => { + btn.textContent = original; + btn.style.opacity = 1; + btn.style.transform = "scale(1)"; + btn.disabled = false; // réactive le bouton + }, 300); + + }, 1000); + + }, 300); + + }) + .catch(() => { + const original = btn.textContent; + btn.textContent = "❌"; + setTimeout(() => { + btn.textContent = original; + btn.disabled = false; + }, 1500); + }); + } + }); + + + infoDiv.classList.add("visible"); return; - } + } else { + infoDiv.classList.remove("playlist-mode"); +} // Vidéo normale diff --git a/public/styles/layout/videoinfo.css b/public/styles/layout/videoinfo.css index 4ed8b11..fd997d6 100644 --- a/public/styles/layout/videoinfo.css +++ b/public/styles/layout/videoinfo.css @@ -21,7 +21,6 @@ #videoInfo.visible { opacity: 1; max-height: 1500px; - overflow: visible; } #videoInfo h3 { @@ -68,6 +67,81 @@ text-decoration: underline; } +/* --- Playlist Mode --- */ +#videoInfo.playlist-mode { + max-width: 900px; +} + +/* Style du bloc "Playlist détectée" */ +#videoInfo.playlist-mode > p { + text-align: center; + font-size: 1.1rem; + font-weight: 600; +} + +/* Layout liste des vidéos */ +#playlistVideos { + margin-top: 1em; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); + gap: 18px; +} + +/* Un item de playlist */ +#playlistVideos > div { + background: #3f3f3f; + border-radius: 10px; + padding: 10px; + box-shadow: 0 4px 12px rgba(0,0,0,0.15); + text-align: center; + transition: transform 0.2s ease; +} + +#playlistVideos > div p { + display: flex; + align-items: center; + justify-content: center; + flex-wrap: wrap; +} + +#videoInfo.playlist-mode { + max-width: 95%; +} + +#playlistVideos > div:hover { + transform: scale(1.05); +} + +#playlistVideos img { + width: 100%; + border-radius: 8px; + margin-bottom: 8px; +} + +.copy-btn { + all: unset; + background: transparent; + border: none; + cursor: pointer; + display: inline-flex; + align-items: center; + justify-content: center; + padding: 4px; + vertical-align: middle; + transition: transform 0.1s ease, color 0.1s ease, opacity 0.15s ease; + font-size: 1em; +} + +#playlistVideos > div p a{ + text-decoration: none; + color: var(--video-info-link-color); +} + +#playlistVideos > div p a:hover{ + text-decoration: underline; +} + + /* Responsive */ @media (max-width: 480px) { #videoInfo { diff --git a/server/helpers/buildArgs.js b/server/helpers/buildArgs.js index 8185d10..417f815 100644 --- a/server/helpers/buildArgs.js +++ b/server/helpers/buildArgs.js @@ -12,7 +12,8 @@ function buildYtDlpArgs({ url, audioOnly, quality, outputFolder }) { "--concurrent-fragments", "8", "--retries", "10", "--fragment-retries", "10", - "--ffmpeg-location", path.join(process.resourcesPath, "ffmpeg.exe") + "--ffmpeg-location", path.join(process.resourcesPath, "ffmpeg.exe"), + "--extractor-args","youtube:player_client=default" ]; if (audioOnly) args.push("-f", "bestaudio", "--extract-audio", "--audio-format", "mp3"); diff --git a/server/autoupdate.js b/server/update.js similarity index 100% rename from server/autoupdate.js rename to server/update.js