Compare commits

...
3 Commits
2 changed files with 67 additions and 12 deletions
+3 -3
View File
@@ -1,5 +1,5 @@
{
"VERSION": "v0.7.3",
"VERSION_NUMBER": "0.7.3",
"BUILD_DATE": "2026-04-19T17:43:13.562Z"
"VERSION": "v0.7.6",
"VERSION_NUMBER": "0.7.6",
"BUILD_DATE": "2026-04-21T10:32:47.088Z"
}
+64 -9
View File
@@ -1367,7 +1367,6 @@
import { HamburgerMorphing } from "./hamburger_morphing/hamburger.mjs";
import { initFooterRelayStatus, updateFooterRelayStatus, initSidenavRelaySection, updateSidenavRelaySection, setRelayActivityState } from './js/relay-ui.mjs';
import { initBlossomSection, updateBlossomSection } from './js/blossom-ui.mjs';
import { initAiSectionWithLocalConfig } from './js/ai-ui.mjs';
import { GreyscaleAPI } from './js/greyscale-api.mjs';
import { SimplePlayer } from './js/greyscale-player.mjs';
@@ -4342,14 +4341,70 @@ import { GreyscaleAPI } from './js/greyscale-api.mjs';
async function fetchDownloadBlobForTrack(track) {
if (!track?.id) throw new Error('Track not available for download.');
const stream = await musicApi.getTrackStream(track.id, 'LOSSLESS');
const streamUrl = stream?.streamUrl;
if (!streamUrl) throw new Error('No stream URL for download');
const res = await fetch(streamUrl);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const blob = await res.blob();
const ext = extensionFromMime(blob.type);
return { blob, ext };
console.debug('[music][download] start', {
trackId: track.id,
title: track.title || '',
});
const qualityCandidates = ['HIGH', 'MP3_320', 'LOSSLESS'];
let lastError = null;
for (const quality of qualityCandidates) {
try {
console.debug('[music][download] requesting stream', { trackId: track.id, quality });
const stream = await musicApi.getTrackStream(track.id, quality);
const streamUrl = stream?.streamUrl;
if (!streamUrl) {
lastError = new Error(`No stream URL for quality ${quality}`);
console.warn('[music][download] missing stream URL', { trackId: track.id, quality });
continue;
}
console.debug('[music][download] fetching stream URL', {
trackId: track.id,
quality,
streamUrl,
});
const res = await fetch(streamUrl);
if (!res.ok) {
lastError = new Error(`HTTP ${res.status} for quality ${quality}`);
console.warn('[music][download] fetch failed', {
trackId: track.id,
quality,
status: res.status,
statusText: res.statusText,
});
continue;
}
const blob = await res.blob();
const ext = extensionFromMime(blob.type);
console.debug('[music][download] success', {
trackId: track.id,
quality,
mimeType: blob.type,
size: blob.size,
ext,
});
return { blob, ext };
} catch (error) {
lastError = error instanceof Error ? error : new Error(String(error || 'Unknown error'));
console.warn('[music][download] quality attempt threw', {
trackId: track.id,
quality,
error: lastError.message,
});
}
}
console.error('[music][download] all quality attempts failed', {
trackId: track.id,
error: lastError?.message || 'No stream URL for download',
});
throw (lastError || new Error('No stream URL for download'));
}
async function downloadTrackFile(track, { suppressStatus = false } = {}) {