diff --git a/js/downloads.js b/js/downloads.js index 90c186c..ad786ed 100644 --- a/js/downloads.js +++ b/js/downloads.js @@ -1,57 +1,10 @@ //js/downloads.js -import { buildTrackFilename, sanitizeForFilename, RATE_LIMIT_ERROR_MESSAGE, getTrackArtists, getTrackTitle, formatTemplate, SVG_CLOSE } from './utils.js'; +import { buildTrackFilename, sanitizeForFilename, RATE_LIMIT_ERROR_MESSAGE, getTrackArtists, getTrackTitle, formatTemplate, SVG_CLOSE, getCoverBlob } from './utils.js'; import { lyricsSettings } from './storage.js'; import { addMetadataToAudio } from './metadata.js'; const downloadTasks = new Map(); let downloadNotificationContainer = null; -const coverCache = new Map(); - -/** - * Fetches and caches cover art as a Blob - */ -async function getCoverBlob(api, coverId) { - if (!coverId) return null; - if (coverCache.has(coverId)) return coverCache.get(coverId); - - const fetchWithProxy = async (url) => { - try { - const proxyUrl = `https://corsproxy.io/?${encodeURIComponent(url)}`; - const response = await fetch(proxyUrl); - if (response.ok) return await response.blob(); - } catch (e) { - console.warn('Proxy fetch failed:', e); - } - return null; - }; - - try { - const url = api.getCoverUrl(coverId, '1280'); - // Try direct fetch first - const response = await fetch(url); - if (response.ok) { - const blob = await response.blob(); - coverCache.set(coverId, blob); - return blob; - } else { - // If direct fetch fails (e.g. 404 from SW due to CORS), try proxy - const blob = await fetchWithProxy(url); - if (blob) { - coverCache.set(coverId, blob); - return blob; - } - } - } catch (e) { - // Network error (CORS rejection not handled by SW), try proxy - const url = api.getCoverUrl(coverId, '1280'); - const blob = await fetchWithProxy(url); - if (blob) { - coverCache.set(coverId, blob); - return blob; - } - } - return null; -} /** * Adds a cover blob to a JSZip instance diff --git a/js/metadata.js b/js/metadata.js index aa61629..a2cfb39 100644 --- a/js/metadata.js +++ b/js/metadata.js @@ -1,4 +1,4 @@ -import { getExtensionForQuality } from './utils.js'; +import { getExtensionForQuality, getCoverBlob } from './utils.js'; const VENDOR_STRING = 'Monochrome'; const DEFAULT_TITLE = 'Unknown Title'; @@ -203,13 +203,11 @@ function createVorbisCommentBlock(track) { async function createFlacPictureBlock(coverId, api) { try { // Fetch album art - const coverUrl = api.getCoverUrl(coverId, '1280'); - const response = await fetch(coverUrl); - if (!response.ok) { + const imageBlob = await getCoverBlob(api, coverId); + if (!imageBlob) { throw new Error('Failed to fetch album art'); } - const imageBlob = await response.blob(); const imageBytes = new Uint8Array(await imageBlob.arrayBuffer()); // Detect MIME type from blob or use default @@ -497,13 +495,11 @@ function createMp4MetadataAtoms(track) { async function fetchAlbumArtForMp4(coverId, api) { try { - const coverUrl = api.getCoverUrl(coverId, '1280'); - const response = await fetch(coverUrl); - if (!response.ok) { + const imageBlob = await getCoverBlob(api, coverId); + if (!imageBlob) { return null; } - const imageBlob = await response.blob(); const imageBytes = new Uint8Array(await imageBlob.arrayBuffer()); return { diff --git a/js/utils.js b/js/utils.js index 4010e4d..69c8b5b 100644 --- a/js/utils.js +++ b/js/utils.js @@ -202,3 +202,52 @@ export const formatDuration = (seconds) => { } return `${minutes} min`; }; + +const coverCache = new Map(); + +/** + * Fetches and caches cover art as a Blob + */ +export async function getCoverBlob(api, coverId) { + if (!coverId) return null; + if (coverCache.has(coverId)) return coverCache.get(coverId); + + const fetchWithProxy = async (url) => { + try { + const proxyUrl = `https://corsproxy.io/?${encodeURIComponent(url)}`; + const response = await fetch(proxyUrl); + if (response.ok) return await response.blob(); + } catch (e) { + console.warn('Proxy fetch failed:', e); + } + return null; + }; + + try { + const url = api.getCoverUrl(coverId, '1280'); + // Try direct fetch first + const response = await fetch(url); + if (response.ok) { + const blob = await response.blob(); + coverCache.set(coverId, blob); + return blob; + } else { + // If direct fetch fails (e.g. 404 from SW due to CORS), try proxy + const blob = await fetchWithProxy(url); + if (blob) { + coverCache.set(coverId, blob); + return blob; + } + } + } catch (e) { + // Network error (CORS rejection not handled by SW), try proxy + const url = api.getCoverUrl(coverId, '1280'); + const blob = await fetchWithProxy(url); + if (blob) { + coverCache.set(coverId, blob); + return blob; + } + } + return null; +} +