diff --git a/js/api.js b/js/api.js index 75b0c8a..696b8c8 100644 --- a/js/api.js +++ b/js/api.js @@ -6,6 +6,7 @@ import { isTrackUnavailable, getExtensionFromBlob, getTrackDiscNumber, + normalizeQualityToken, } from './utils.js'; import { preferDolbyAtmosSettings, trackDateSettings, devModeSettings } from './storage.js'; import { APICache } from './cache.js'; @@ -1455,6 +1456,104 @@ export class LosslessAPI { return [trackStub, raw]; } + getTrackManifestFormats(quality) { + switch (normalizeQualityToken(quality) || quality) { + case 'DOLBY_ATMOS': + return ['EAC3_JOC']; + case 'HI_RES_LOSSLESS': + return ['FLAC_HIRES']; + case 'LOSSLESS': + return ['FLAC']; + case 'HIGH': + return ['AACLC']; + case 'LOW': + return ['HEAACV1']; + default: + return ['FLAC']; + } + } + + getAdaptiveTrackManifestFormats() { + return ['FLAC_HIRES', 'FLAC', 'AACLC', 'HEAACV1', 'EAC3_JOC']; + } + + shouldUseAdaptiveTrackManifest(download = false) { + if (download || typeof localStorage === 'undefined') { + return false; + } + + try { + return (localStorage.getItem('adaptive-playback-quality') || '').toLowerCase() === 'auto'; + } catch { + return false; + } + } + + getAudioQualityFromManifestFormats(formats = []) { + if (formats.includes('EAC3_JOC')) return 'DOLBY_ATMOS'; + if (formats.includes('FLAC_HIRES')) return 'HI_RES_LOSSLESS'; + if (formats.includes('FLAC')) return 'LOSSLESS'; + if (formats.includes('AACLC')) return 'HIGH'; + if (formats.includes('HEAACV1')) return 'LOW'; + return null; + } + + async normalizeTrackManifestResponse(apiResponse, quality) { + if (!apiResponse || typeof apiResponse !== 'object') { + return apiResponse; + } + + const raw = apiResponse.data?.data ?? apiResponse.data ?? apiResponse; + const attributes = raw?.attributes ?? {}; + const manifestUrl = attributes.uri; + + if (!manifestUrl) { + throw new Error('Malformed track manifests response'); + } + + const manifestResponse = await fetch(manifestUrl); + if (!manifestResponse.ok) { + throw new Error(`Failed to fetch signed track manifest: HTTP ${manifestResponse.status}`); + } + + const manifestText = await manifestResponse.text(); + const manifestMimeType = + manifestResponse.headers.get('content-type') || + (manifestText.includes('