diff --git a/static/app.js b/static/app.js index 963abe7..e799d60 100644 --- a/static/app.js +++ b/static/app.js @@ -53,6 +53,12 @@ const uploadStatusText = document.getElementById('upload-status-text'); const uploadsList = document.getElementById('uploads-list'); const emptyUploadsMsg = document.getElementById('empty-uploads-msg'); const uploadsCountBadge = document.getElementById('uploads-count'); +const btnOpenAllUploads = document.getElementById('btn-open-all-uploads'); +const btnAllUploadsCount = document.getElementById('btn-all-uploads-count'); +const uploadsModal = document.getElementById('uploads-modal'); +const btnCloseUploads = document.getElementById('btn-close-uploads'); +const modalUploadsList = document.getElementById('modal-uploads-list'); +const emptyModalUploadsMsg = document.getElementById('empty-modal-uploads-msg'); const historyList = document.getElementById('history-list'); const emptyHistoryMsg = document.getElementById('empty-history-msg'); const historyCountBadge = document.getElementById('history-count'); @@ -170,6 +176,15 @@ function bindEvents() { }); } + // Uploads Modal Events + if (btnOpenAllUploads) btnOpenAllUploads.addEventListener('click', openUploadsModal); + if (btnCloseUploads) btnCloseUploads.addEventListener('click', closeUploadsModal); + if (uploadsModal) { + uploadsModal.addEventListener('click', (e) => { + if (e.target === uploadsModal) closeUploadsModal(); + }); + } + // Upload Events if (btnSelectFile && fileInput) { btnSelectFile.addEventListener('click', () => fileInput.click()); @@ -848,20 +863,33 @@ async function fetchUploads() { } } -// Render Uploaded Files -function renderUploads(files) { - if (!uploadsList) return; - const items = uploadsList.querySelectorAll('.queue-item'); +let allUploadedFiles = []; + +// Open Uploads Modal +function openUploadsModal() { + if (uploadsModal) { + uploadsModal.style.display = 'flex'; + renderModalUploads(allUploadedFiles); + } +} + +// Close Uploads Modal +function closeUploadsModal() { + if (uploadsModal) uploadsModal.style.display = 'none'; +} + +// Render Modal Uploads (All files) +function renderModalUploads(files) { + if (!modalUploadsList) return; + const items = modalUploadsList.querySelectorAll('.queue-item'); items.forEach(el => el.remove()); if (!files || files.length === 0) { - emptyUploadsMsg.style.display = 'flex'; - uploadsCountBadge.textContent = '0 plików'; + if (emptyModalUploadsMsg) emptyModalUploadsMsg.style.display = 'flex'; return; } - emptyUploadsMsg.style.display = 'none'; - uploadsCountBadge.textContent = `${files.length} ${files.length === 1 ? 'plik' : files.length < 5 ? 'pliki' : 'plików'}`; + if (emptyModalUploadsMsg) emptyModalUploadsMsg.style.display = 'none'; files.forEach(file => { const li = document.createElement('li'); @@ -877,7 +905,55 @@ function renderUploads(files) {
+ +
+ `; + modalUploadsList.appendChild(li); + }); + lucide.createIcons(); +} + +// Render Uploaded Files (Top 10 most recent on dashboard panel) +function renderUploads(files) { + allUploadedFiles = files || []; + if (btnAllUploadsCount) btnAllUploadsCount.textContent = allUploadedFiles.length; + + if (!uploadsList) return; + const items = uploadsList.querySelectorAll('.queue-item'); + items.forEach(el => el.remove()); + + if (!files || files.length === 0) { + emptyUploadsMsg.style.display = 'flex'; + uploadsCountBadge.textContent = '0 plików'; + if (modalUploadsList) renderModalUploads([]); + return; + } + + emptyUploadsMsg.style.display = 'none'; + uploadsCountBadge.textContent = `${files.length} ${files.length === 1 ? 'plik' : files.length < 5 ? 'pliki' : 'plików'}`; + + // Take only top 10 most recently used/uploaded + const top10 = files.slice(0, 10); + + top10.forEach(file => { + const li = document.createElement('li'); + li.className = 'queue-item'; + + const safeFilename = file.filename.replace(/'/g, "\\'"); + + li.innerHTML = ` +
+
+
${file.filename}
+
${file.size_mb} MB
+
+
+
@@ -173,6 +173,9 @@ + @@ -234,6 +237,25 @@ + + +
diff --git a/static/style.css b/static/style.css index bee5e63..06eaca4 100644 --- a/static/style.css +++ b/static/style.css @@ -988,7 +988,7 @@ body { background: rgba(255, 255, 255, 0.02); } -#btn-close-settings { +#btn-close-settings, #btn-close-uploads { width: 42px; height: 42px; background: rgba(255, 255, 255, 0.12); @@ -1002,7 +1002,7 @@ body { cursor: pointer; } -#btn-close-settings:hover { +#btn-close-settings:hover, #btn-close-uploads:hover { background: rgba(239, 68, 68, 0.25); border-color: rgba(239, 68, 68, 0.6); color: #ff6b6b; diff --git a/web.py b/web.py index b68e6cc..338dd94 100644 --- a/web.py +++ b/web.py @@ -510,14 +510,17 @@ async def get_uploads(): uploads_dir = "uploads" os.makedirs(uploads_dir, exist_ok=True) files = [] - for f in sorted(os.listdir(uploads_dir)): + for f in os.listdir(uploads_dir): filepath = os.path.join(uploads_dir, f) if os.path.isfile(filepath) and not f.startswith("."): size_mb = round(os.path.getsize(filepath) / (1024 * 1024), 2) + mtime = os.path.getmtime(filepath) files.append({ "filename": f, - "size_mb": size_mb + "size_mb": size_mb, + "mtime": mtime }) + files.sort(key=lambda x: x["mtime"], reverse=True) return {"files": files} @app.post("/api/upload") @@ -567,6 +570,7 @@ async def api_play_file(req: PlayFileRequest): raise HTTPException(status_code=404, detail="Plik nie istnieje na serwerze") try: + os.utime(filepath, None) player = await YTDLSource.from_file(filepath, loop=bot.loop) res = await music_cog.enqueue_track(guild_id, player, from_web=True) await broadcast_status()