From ea19b3d95e8ed19ae643dbccb9672f50e7e2681a Mon Sep 17 00:00:00 2001 From: Eduard Prigoana Date: Sat, 8 Nov 2025 19:38:10 +0200 Subject: [PATCH] fix images --- index.html | 10 +++++- js/app.js | 25 +++++++++++++-- js/downloads.js | 71 ++++++++++++++++++++++++++++++++++++++--- js/ui.js | 85 +++++++++++++++++++++++++------------------------ 4 files changed, 142 insertions(+), 49 deletions(-) diff --git a/index.html b/index.html index a94f411..cb95c51 100644 --- a/index.html +++ b/index.html @@ -174,7 +174,7 @@
-
+
Playlist Cover
@@ -189,6 +189,14 @@ Play +
diff --git a/js/app.js b/js/app.js index 9f0fe89..62a90dd 100644 --- a/js/app.js +++ b/js/app.js @@ -8,7 +8,7 @@ import { createRouter, updateTabTitle } from './router.js'; import { initializeSettings } from './settings.js'; import { initializePlayerEvents, initializeTrackInteractions } from './events.js'; import { initializeUIInteractions } from './ui-interactions.js'; -import { downloadAlbumAsZip, downloadDiscography, downloadCurrentTrack } from './downloads.js'; +import { downloadAlbumAsZip, downloadDiscography, downloadCurrentTrack, downloadPlaylistAsZip } from './downloads.js'; import { debounce, SVG_PLAY } from './utils.js'; function initializeCasting(audioPlayer, castBtn) { @@ -291,7 +291,28 @@ document.addEventListener('DOMContentLoaded', async () => { alert('Failed to play album: ' + error.message); } } - + if (e.target.closest('#download-playlist-btn')) { + const btn = e.target.closest('#download-playlist-btn'); + if (btn.disabled) return; + + const playlistId = window.location.hash.split('/')[1]; + if (!playlistId) return; + + btn.disabled = true; + const originalHTML = btn.innerHTML; + btn.innerHTML = 'Downloading...'; + + try { + const { playlist, tracks } = await api.getPlaylist(playlistId); + await downloadPlaylistAsZip(playlist, tracks, api, player.quality, lyricsManager); + } catch (error) { + console.error('Playlist download failed:', error); + alert('Failed to download playlist: ' + error.message); + } finally { + btn.disabled = false; + btn.innerHTML = originalHTML; + } +} if (e.target.closest('#play-playlist-btn')) { const btn = e.target.closest('#play-playlist-btn'); if (btn.disabled) return; diff --git a/js/downloads.js b/js/downloads.js index eede5be..9904e7b 100644 --- a/js/downloads.js +++ b/js/downloads.js @@ -185,7 +185,70 @@ export async function downloadAlbumAsZip(album, tracks, api, quality, lyricsMana const blob = await downloadTrackBlob(track, quality, api); zip.file(`${folderName}/${filename}`, blob); - // Add LRC to zip if enabled + if (lyricsManager && lyricsSettings.shouldDownloadLyrics()) { + try { + const lyricsData = await lyricsManager.fetchLyrics(track.id); + if (lyricsData) { + const lrcContent = lyricsManager.generateLRCContent(lyricsData, track); + if (lrcContent) { + const lrcFilename = filename.replace(/\.[^.]+$/, '.lrc'); + zip.file(`${folderName}/${lrcFilename}`, lrcContent); + } + } + } catch (error) { + console.log('Could not add lyrics for:', trackTitle); + } + } + } + + updateBulkDownloadProgress(notification, tracks.length, tracks.length, 'Creating ZIP...'); + + const zipBlob = await zip.generateAsync({ + type: 'blob', + compression: 'DEFLATE', + compressionOptions: { level: 6 } + }); + + const url = URL.createObjectURL(zipBlob); + const a = document.createElement('a'); + a.href = url; + a.download = `${folderName}.zip`; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + + completeBulkDownload(notification, true); + } catch (error) { + completeBulkDownload(notification, false, error.message); + throw error; + } +} + +export async function downloadPlaylistAsZip(playlist, tracks, api, quality, lyricsManager = null) { + const JSZip = await loadJSZip(); + const zip = new JSZip(); + + const template = localStorage.getItem('zip-folder-template') || '{albumTitle} - {albumArtist} - monochrome.tf'; + const folderName = formatTemplate(template, { + albumTitle: playlist.title, + albumArtist: 'Playlist', + year: new Date().getFullYear() + }); + + const notification = createBulkDownloadNotification('playlist', playlist.title, tracks.length); + + try { + for (let i = 0; i < tracks.length; i++) { + const track = tracks[i]; + const filename = buildTrackFilename(track, quality); + const trackTitle = getTrackTitle(track); + + updateBulkDownloadProgress(notification, i, tracks.length, trackTitle); + + const blob = await downloadTrackBlob(track, quality, api); + zip.file(`${folderName}/${filename}`, blob); + if (lyricsManager && lyricsSettings.shouldDownloadLyrics()) { try { const lyricsData = await lyricsManager.fetchLyrics(track.id); @@ -255,7 +318,6 @@ export async function downloadDiscography(artist, api, quality, lyricsManager = const blob = await downloadTrackBlob(track, quality, api); zip.file(`${rootFolder}/${albumFolder}/${filename}`, blob); - // Add LRC to zip if enabled if (lyricsManager && lyricsSettings.shouldDownloadLyrics()) { try { const lyricsData = await lyricsManager.fetchLyrics(track.id); @@ -306,11 +368,13 @@ function createBulkDownloadNotification(type, name, totalItems) { const notifEl = document.createElement('div'); notifEl.className = 'download-task bulk-download'; + const typeLabel = type === 'album' ? 'Album' : type === 'playlist' ? 'Playlist' : 'Discography'; + notifEl.innerHTML = `
- Downloading ${type === 'album' ? 'Album' : 'Discography'} + Downloading ${typeLabel}
${name}
@@ -385,7 +449,6 @@ export async function downloadCurrentTrack(track, quality, api, lyricsManager = completeDownloadTask(track.id, true); - // Download LRC if enabled if (lyricsManager && lyricsSettings.shouldDownloadLyrics()) { try { const lyricsData = await lyricsManager.fetchLyrics(track.id); diff --git a/js/ui.js b/js/ui.js index c24dd3b..530be10 100644 --- a/js/ui.js +++ b/js/ui.js @@ -299,59 +299,60 @@ export class UIRenderer { } } - async renderPlaylistPage(playlistId) { - this.showPage('playlist'); +async renderPlaylistPage(playlistId) { + this.showPage('playlist'); + + const imageEl = document.getElementById('playlist-detail-image'); + const titleEl = document.getElementById('playlist-detail-title'); + const metaEl = document.getElementById('playlist-detail-meta'); + const descEl = document.getElementById('playlist-detail-description'); + const tracklistContainer = document.getElementById('playlist-detail-tracklist'); + + imageEl.src = ''; + imageEl.style.backgroundColor = 'var(--muted)'; + titleEl.innerHTML = '
'; + metaEl.innerHTML = '
'; + descEl.innerHTML = '
'; + tracklistContainer.innerHTML = ` +
+ # + Title + Duration +
+ ${this.createSkeletonTracks(10, true)} + `; + + try { + const { playlist, tracks } = await this.api.getPlaylist(playlistId); - const imageEl = document.getElementById('playlist-detail-image'); - const titleEl = document.getElementById('playlist-detail-title'); - const metaEl = document.getElementById('playlist-detail-meta'); - const descEl = document.getElementById('playlist-detail-description'); - const tracklistContainer = document.getElementById('playlist-detail-tracklist'); + const imageId = playlist.squareImage || playlist.image; + imageEl.src = this.api.getCoverUrl(imageId, '1080'); + imageEl.style.backgroundColor = ''; + + titleEl.textContent = playlist.title; + + const totalDuration = calculateTotalDuration(tracks); + + metaEl.textContent = `${playlist.numberOfTracks} tracks • ${formatDuration(totalDuration)}`; + + descEl.textContent = playlist.description || ''; - imageEl.src = ''; - imageEl.style.backgroundColor = 'var(--muted)'; - titleEl.innerHTML = '
'; - metaEl.innerHTML = '
'; - descEl.innerHTML = '
'; tracklistContainer.innerHTML = `
# Title Duration
- ${this.createSkeletonTracks(10, true)} `; - try { - const { playlist, tracks } = await this.api.getPlaylist(playlistId); - - imageEl.src = this.api.getCoverUrl(playlist.image || playlist.squareImage, '1280'); - imageEl.style.backgroundColor = ''; - - titleEl.textContent = playlist.title; - - const totalDuration = calculateTotalDuration(tracks); - - metaEl.textContent = `${playlist.numberOfTracks} tracks • ${formatDuration(totalDuration)}`; - - descEl.textContent = playlist.description || ''; - - tracklistContainer.innerHTML = ` -
- # - Title - Duration -
- `; - - this.renderListWithTracks(tracklistContainer, tracks, true); - - document.title = `${playlist.title} - Monochrome`; - } catch (error) { - console.error("Failed to load playlist:", error); - tracklistContainer.innerHTML = createPlaceholder(`Could not load playlist details. ${error.message}`); - } + this.renderListWithTracks(tracklistContainer, tracks, true); + + document.title = `${playlist.title} - Monochrome`; + } catch (error) { + console.error("Failed to load playlist:", error); + tracklistContainer.innerHTML = createPlaceholder(`Could not load playlist details. ${error.message}`); } +} async renderArtistPage(artistId) { this.showPage('artist');