From 8cf7979d4768d46eff67bd7d8d8de96df422d8f7 Mon Sep 17 00:00:00 2001 From: Daniel <790119+DanTheMan827@users.noreply.github.com> Date: Wed, 11 Mar 2026 20:00:44 +0000 Subject: [PATCH] fix(downloads): cue generation now properly outputs correct tracks numbers and splits by disc --- js/downloads.js | 35 ++++++++++++++++++++++------------- js/playlist-generator.js | 35 +++++++++++++++++------------------ 2 files changed, 39 insertions(+), 31 deletions(-) diff --git a/js/downloads.js b/js/downloads.js index bbb9469..a093fb9 100644 --- a/js/downloads.js +++ b/js/downloads.js @@ -584,13 +584,25 @@ async function bulkDownloadToZipStream( // For albums, generate CUE file if (type === 'album' && playlistSettings.shouldGenerateCUE()) { - const audioFilename = `${sanitizeForFilename(folderName)}.flac`; // Assume FLAC for CUE - const cueContent = generateCUE(metadata, tracks, audioFilename); - yield { - name: `${folderName}/${sanitizeForFilename(folderName)}.cue`, - lastModified: new Date(), - input: cueContent, - }; + // Split tracks by volumeNumber and iterate those groups. + const tracksByVolume = Object.groupBy( + tracks.map((track, index) => ({ + ...track, + trackPath: trackPaths[index], + })), + (track) => String(getExplicitTrackDiscNumber(track) || 1) + ); + const multiDisc = Object.keys(tracksByVolume).length > 1; + + for (const [volumeNumber, tracks] of Object.entries(tracksByVolume)) { + const trackPaths = tracks.map((track) => track.trackPath); + const cueContent = generateCUE(metadata, tracks, sanitizeForFilename(folderName), trackPaths); + yield { + name: `${folderName}/${sanitizeForFilename(folderName)}${multiDisc ? ` - Disc ${volumeNumber}` : ''}.cue`, + lastModified: new Date(), + input: cueContent, + }; + } } // Generate m3u/m3u8 last, using actual track paths collected during download @@ -741,8 +753,7 @@ async function bulkDownloadToZipBlob( // For albums, generate CUE file if (type === 'album' && playlistSettings.shouldGenerateCUE()) { - const audioFilename = `${sanitizeForFilename(folderName)}.flac`; // Assume FLAC for CUE - const cueContent = generateCUE(metadata, tracks, audioFilename); + const cueContent = generateCUE(metadata, tracks, sanitizeForFilename(folderName), trackPaths); yield { name: `${folderName}/${sanitizeForFilename(folderName)}.cue`, lastModified: new Date(), @@ -899,8 +910,7 @@ async function bulkDownloadToZipNeutralino( // For albums, generate CUE file if (type === 'album' && playlistSettings.shouldGenerateCUE()) { - const audioFilename = `${sanitizeForFilename(folderName)}.flac`; // Assume FLAC for CUE - const cueContent = generateCUE(metadata, tracks, audioFilename); + const cueContent = generateCUE(metadata, tracks, sanitizeForFilename(folderName), trackPaths); yield { name: `${folderName}/${sanitizeForFilename(folderName)}.cue`, lastModified: new Date(), @@ -1246,8 +1256,7 @@ export async function downloadDiscography(artist, selectedReleases, api, quality } if (playlistSettings.shouldGenerateCUE()) { - const audioFilename = `${sanitizeForFilename(fullAlbum.title)}.flac`; - const cueContent = generateCUE(fullAlbum, tracks, audioFilename); + const cueContent = generateCUE(fullAlbum, tracks, sanitizeForFilename(fullAlbum.title), trackPaths); yield { name: `${fullFolderPath}/${sanitizeForFilename(fullAlbum.title)}.cue`, lastModified: new Date(), diff --git a/js/playlist-generator.js b/js/playlist-generator.js index a6b0e4e..59eb011 100644 --- a/js/playlist-generator.js +++ b/js/playlist-generator.js @@ -120,40 +120,39 @@ export function generateM3U8( * Generates CUE sheet content for albums * @param {Object} album - Album metadata * @param {Array} tracks - Array of track objects - * @param {string} audioFilename - The main audio file name + * @param {string} _audioFilenameBase - Unused; kept for API compatibility + * @param {Array|null} trackPaths - Actual per-track resolved paths; when provided, each track gets its own FILE entry + * @param {string} audioExtension - Audio file extension for generated paths (used when trackPaths is null) * @returns {string} CUE content */ -export function generateCUE(album, tracks, audioFilename) { +export function generateCUE(album, tracks, _audioFilenameBase, trackPaths = null, audioExtension = 'flac') { const performer = album.artist?.name || album.artist || 'Unknown Artist'; const title = album.title || 'Unknown Album'; let content = `PERFORMER "${performer}"\n`; content += `TITLE "${title}"\n`; - // Add file reference - const fileExtension = audioFilename.split('.').pop().toUpperCase(); - content += `FILE "${audioFilename}" ${fileExtension}\n`; - - let currentTime = 0; - tracks.forEach((track, index) => { + const resolvedPath = trackPaths ? trackPaths[index] : null; + if (trackPaths && !resolvedPath) return; + const trackNumber = String(track.trackNumber || index + 1).padStart(2, '0'); const trackTitle = track.title || 'Unknown Track'; const trackPerformer = track.artist?.name || getTrackArtists(track) || performer; - const duration = track.duration || 0; + const path = + resolvedPath ?? + (() => { + const filename = getTrackFilename(track, index + 1, audioExtension); + return filename; + })(); + + const fileExtension = path.split('.').pop().toUpperCase(); + content += `FILE "${path}" ${fileExtension}\n`; content += ` TRACK ${trackNumber} AUDIO\n`; content += ` TITLE "${trackTitle}"\n`; content += ` PERFORMER "${trackPerformer}"\n`; - - // Calculate time in MM:SS:FF format (Frames = 75 per second) - const minutes = Math.floor(currentTime / 60); - const seconds = Math.floor(currentTime % 60); - const frames = Math.floor((currentTime % 1) * 75); - - content += ` INDEX 01 ${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}:${String(frames).padStart(2, '0')}\n`; - - currentTime += duration; + content += ` INDEX 01 00:00:00\n`; }); return content;