diff --git a/js/app.js b/js/app.js index 9f65dcf..44455fb 100644 --- a/js/app.js +++ b/js/app.js @@ -10,7 +10,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, downloadPlaylistAsZip } from './downloads.js'; +import { downloadAlbumAsZip, downloadDiscography, downloadPlaylistAsZip } from './downloads.js'; import { debounce, SVG_PLAY } from './utils.js'; function initializeCasting(audioPlayer, castBtn) { @@ -205,7 +205,7 @@ document.addEventListener('DOMContentLoaded', async () => { initializeSettings(scrobbler, player, api, ui); initializePlayerEvents(player, audioPlayer, scrobbler); - initializeTrackInteractions(player, api, document.querySelector('.main-content'), document.getElementById('context-menu')); + initializeTrackInteractions(player, api, document.querySelector('.main-content'), document.getElementById('context-menu'), lyricsManager); initializeUIInteractions(player, api); initializeKeyboardShortcuts(player, audioPlayer, lyricsPanel); initializeMediaSessionHandlers(player); @@ -268,7 +268,9 @@ document.addEventListener('DOMContentLoaded', async () => { }); document.getElementById('download-current-btn')?.addEventListener('click', () => { - downloadCurrentTrack(player.currentTrack, player.quality, api, lyricsManager); + if (player.currentTrack) { + downloadTrackWithMetadata(player.currentTrack, player.quality, api, lyricsManager); + } }); // Auto-update lyrics when track changes diff --git a/js/downloads.js b/js/downloads.js index ffea632..39e84a6 100644 --- a/js/downloads.js +++ b/js/downloads.js @@ -515,7 +515,8 @@ function completeBulkDownload(notifEl, success = true, message = null) { } } -export async function downloadCurrentTrack(track, quality, api, lyricsManager = null) { +export async function downloadTrackWithMetadata(track, quality, api, lyricsManager = null, abortController = null) { + if (!track) { alert('No track is currently playing'); return; @@ -523,8 +524,10 @@ export async function downloadCurrentTrack(track, quality, api, lyricsManager = const filename = buildTrackFilename(track, quality); + const controller = abortController || new AbortController(); + try { - const { taskEl, abortController } = addDownloadTask( + const { taskEl, taskAbortController } = addDownloadTask( track.id, track, filename, @@ -544,7 +547,7 @@ export async function downloadCurrentTrack(track, quality, api, lyricsManager = } } - const resp = await fetch(streamUrl, { signal: abortController.signal, cache: 'no-store' }); + const resp = await fetch(streamUrl, { signal: controller.signal, cache: 'no-store' }); if (!resp.ok) throw new Error(`Fetch failed: ${resp.status}`); const contentLength = resp.headers.get('Content-Length'); @@ -586,7 +589,8 @@ export async function downloadCurrentTrack(track, quality, api, lyricsManager = try { const meta = buildTrackMetadata(track, api); const metaFilename = filename.replace(/\.[^.]+$/, '.json'); - zip.file(metaFilename, JSON.stringify(meta, null, 2)); + const jsonContent = JSON.stringify(meta, null, 2); + zip.file(metaFilename, jsonContent); } catch (e) { console.warn('Could not create metadata for current track', e); } diff --git a/js/events.js b/js/events.js index 33814f3..821aa2c 100644 --- a/js/events.js +++ b/js/events.js @@ -1,7 +1,8 @@ //js/events.js import { SVG_PLAY, SVG_PAUSE, SVG_VOLUME, SVG_MUTE, REPEAT_MODE, trackDataStore, RATE_LIMIT_ERROR_MESSAGE, buildTrackFilename } from './utils.js'; import { lastFMStorage } from './storage.js'; -import { addDownloadTask, updateDownloadProgress, completeDownloadTask } from './downloads.js'; +import { addDownloadTask, updateDownloadProgress, completeDownloadTask, downloadTrackWithMetadata } from './downloads.js'; +import { lyricsSettings } from './storage.js'; import { updateTabTitle } from './router.js'; export function initializePlayerEvents(player, audioPlayer, scrobbler) { @@ -278,7 +279,7 @@ function initializeSmoothSliders(audioPlayer, player) { }); } -export function initializeTrackInteractions(player, api, mainContent, contextMenu) { +export function initializeTrackInteractions(player, api, mainContent, contextMenu, lyricsManager) { let contextTrack = null; mainContent.addEventListener('click', e => { @@ -337,33 +338,7 @@ export function initializeTrackInteractions(player, api, mainContent, contextMen player.addToQueue(contextTrack); renderQueue(player); } else if (action === 'download' && contextTrack) { - const quality = player.quality; - const filename = buildTrackFilename(contextTrack, quality); - - try { - const { taskEl, abortController } = addDownloadTask( - contextTrack.id, - contextTrack, - filename, - api - ); - - await api.downloadTrack(contextTrack.id, quality, filename, { - signal: abortController.signal, - onProgress: (progress) => { - updateDownloadProgress(contextTrack.id, progress); - } - }); - - completeDownloadTask(contextTrack.id, true); - } catch (error) { - if (error.name !== 'AbortError') { - const errorMsg = error.message === RATE_LIMIT_ERROR_MESSAGE - ? error.message - : 'Download failed. Please try again.'; - completeDownloadTask(contextTrack.id, false, errorMsg); - } - } + await downloadTrackWithMetadata(contextTrack, player.quality, api, lyricsManager); } contextMenu.style.display = 'none';