From d9878596e2dd6065cfe7cc4c2f47d0b3f81e34c3 Mon Sep 17 00:00:00 2001 From: binimum <61615730+binimum@users.noreply.github.com> Date: Sun, 22 Mar 2026 20:13:27 +0000 Subject: [PATCH] style: auto-fix linting issues --- js/HiFi.ts | 8 +++- js/api.js | 71 +++++++++++++++++------------ js/commandPalette.js | 12 +++-- js/music-api.js | 8 ++-- js/player.js | 106 ++++++++++++++++++++++++++----------------- js/settings.js | 12 +++-- 6 files changed, 135 insertions(+), 82 deletions(-) diff --git a/js/HiFi.ts b/js/HiFi.ts index 51ae7d0..0adc4b4 100644 --- a/js/HiFi.ts +++ b/js/HiFi.ts @@ -563,7 +563,13 @@ export class HiFiClient { [ q, 'https://api.tidal.com/v1/search', - { query: q, limit, offset, types: 'ARTISTS,ALBUMS,TRACKS,VIDEOS,PLAYLISTS', countryCode: this.countryCode }, + { + query: q, + limit, + offset, + types: 'ARTISTS,ALBUMS,TRACKS,VIDEOS,PLAYLISTS', + countryCode: this.countryCode, + }, ], [s, 'https://api.tidal.com/v1/search/tracks', { query: s, limit, offset, countryCode: this.countryCode }], [ diff --git a/js/api.js b/js/api.js index 8729058..77892c3 100644 --- a/js/api.js +++ b/js/api.js @@ -429,45 +429,47 @@ export class LosslessAPI { try { const response = await this.fetchWithRetry(`/search/?q=${encodeURIComponent(query)}`, options); const data = await response.json(); - + // Check if backend returned an error or if this looks like individual fallback if (data.error || (!data.tracks && !data.artists && !data.albums && (!data.data || !data.data.tracks))) { throw new Error('Fallback to individual searches'); } - + const extractSection = (key) => this.normalizeSearchResponse(data, key); - + const tracksData = extractSection('tracks'); const artistsData = extractSection('artists'); const albumsData = extractSection('albums'); const playlistsData = extractSection('playlists'); const videosData = extractSection('videos'); - + const results = { tracks: { ...tracksData, - items: tracksData.items.map(t => this.prepareTrack(t)) + items: tracksData.items.map((t) => this.prepareTrack(t)), }, artists: { ...artistsData, - items: artistsData.items.map(a => this.prepareArtist(a)) + items: artistsData.items.map((a) => this.prepareArtist(a)), }, albums: { ...albumsData, - items: albumsData.items.map(a => this.prepareAlbum(a)) + items: albumsData.items.map((a) => this.prepareAlbum(a)), }, - playlists: playlistsData ? { - ...playlistsData, - items: playlistsData.items.map(p => this.preparePlaylist(p)) - } : { items: [], limit: 0, offset: 0, totalNumberOfItems: 0 }, + playlists: playlistsData + ? { + ...playlistsData, + items: playlistsData.items.map((p) => this.preparePlaylist(p)), + } + : { items: [], limit: 0, offset: 0, totalNumberOfItems: 0 }, videos: { ...videosData, - items: videosData.items.map(v => this.prepareTrack(v)) - } + items: videosData.items.map((v) => this.prepareTrack(v)), + }, }; - + await this.cache.set('search_all', query, results); - + return results; } catch (error) { // Fallback to individual searches if the backend proxy doesn't support ?q= or throws @@ -476,11 +478,15 @@ export class LosslessAPI { this.searchVideos(query, options).catch(() => ({ items: [] })), this.searchArtists(query, options).catch(() => ({ items: [] })), this.searchAlbums(query, options).catch(() => ({ items: [] })), - this.searchPlaylists(query, options).catch(() => ({ items: [] })) + this.searchPlaylists(query, options).catch(() => ({ items: [] })), ]); - + return { - tracks, videos, artists, albums, playlists + tracks, + videos, + artists, + albums, + playlists, }; } } @@ -1432,21 +1438,25 @@ export class LosslessAPI { let isUsingManifestEndpoint = false; try { - const manifestType = (isIos || isSafari) ? 'HLS' : 'MPEG_DASH'; - + const manifestType = isIos || isSafari ? 'HLS' : 'MPEG_DASH'; + let canPlayAtmos = false; try { if (window.MediaSource && typeof window.MediaSource.isTypeSupported === 'function') { - canPlayAtmos = MediaSource.isTypeSupported('audio/mp4; codecs="ec-3"') || MediaSource.isTypeSupported('audio/mp4; codecs="eac3"'); + canPlayAtmos = + MediaSource.isTypeSupported('audio/mp4; codecs="ec-3"') || + MediaSource.isTypeSupported('audio/mp4; codecs="eac3"'); } if (!canPlayAtmos && typeof document !== 'undefined') { const a = document.createElement('audio'); - canPlayAtmos = !!(a.canPlayType('audio/mp4; codecs="ec-3"') || a.canPlayType('audio/mp4; codecs="eac3"')); + canPlayAtmos = !!( + a.canPlayType('audio/mp4; codecs="ec-3"') || a.canPlayType('audio/mp4; codecs="eac3"') + ); } } catch (e) {} const paramsArray = []; - + if (quality === 'LOW') { paramsArray.push(['formats', 'HEAACV1']); } else if (quality === 'HIGH') { @@ -1480,16 +1490,21 @@ export class LosslessAPI { const params = new URLSearchParams(paramsArray); - const response = await this.fetchWithRetry(`/trackManifests/?id=${id}&${params.toString()}`, { type: 'streaming', minVersion: '2.7' }); + const response = await this.fetchWithRetry(`/trackManifests/?id=${id}&${params.toString()}`, { + type: 'streaming', + minVersion: '2.7', + }); const jsonResponse = await response.json(); const url = jsonResponse?.data?.data?.attributes?.uri; if (url) { streamUrl = url; manifestRgInfo = { trackReplayGain: jsonResponse?.data?.data?.attributes?.trackAudioNormalizationData?.replayGain, - trackPeakAmplitude: jsonResponse?.data?.data?.attributes?.trackAudioNormalizationData?.peakAmplitude, + trackPeakAmplitude: + jsonResponse?.data?.data?.attributes?.trackAudioNormalizationData?.peakAmplitude, albumReplayGain: jsonResponse?.data?.data?.attributes?.albumAudioNormalizationData?.replayGain, - albumPeakAmplitude: jsonResponse?.data?.data?.attributes?.albumAudioNormalizationData?.peakAmplitude + albumPeakAmplitude: + jsonResponse?.data?.data?.attributes?.albumAudioNormalizationData?.peakAmplitude, }; isUsingManifestEndpoint = true; } else { @@ -1515,14 +1530,14 @@ export class LosslessAPI { trackReplayGain: lookup.info.trackReplayGain || lookup.info.replayGain, trackPeakAmplitude: lookup.info.trackPeakAmplitude || lookup.info.peakAmplitude, albumReplayGain: lookup.info.albumReplayGain, - albumPeakAmplitude: lookup.info.albumPeakAmplitude + albumPeakAmplitude: lookup.info.albumPeakAmplitude, }; } } const result = { url: streamUrl, rgInfo: manifestRgInfo }; this.streamCache.set(cacheKey, result); - + return result; } diff --git a/js/commandPalette.js b/js/commandPalette.js index 5a89706..5d45981 100644 --- a/js/commandPalette.js +++ b/js/commandPalette.js @@ -1196,18 +1196,24 @@ class CommandPalette { } async setQuality(quality) { - const qualityNames = { auto: 'Auto', LOW: 'Low', HIGH: 'High', LOSSLESS: 'Lossless', HI_RES_LOSSLESS: 'Hi-Res' }; + const qualityNames = { + auto: 'Auto', + LOW: 'Low', + HIGH: 'High', + LOSSLESS: 'Lossless', + HI_RES_LOSSLESS: 'Hi-Res', + }; if (Player.instance) { // Set fallback API quality (Auto maps back to Hi-Res) const apiQuality = quality === 'auto' ? 'HI_RES_LOSSLESS' : quality; Player.instance.setQuality(apiQuality); localStorage.setItem('playback-quality', apiQuality); - + // Set adaptive streaming quality localStorage.setItem('adaptive-playback-quality', quality); if (Player.instance.forceQuality) Player.instance.forceQuality(quality); - + const streamingSelect = document.getElementById('streaming-quality-setting'); if (streamingSelect) streamingSelect.value = quality; } diff --git a/js/music-api.js b/js/music-api.js index 6e5cf27..a608c10 100644 --- a/js/music-api.js +++ b/js/music-api.js @@ -50,22 +50,22 @@ export class MusicAPI { if (typeof api.search === 'function') { return api.search(query, options); } - + // Fallback for providers that don't implement unified search const [tracksResult, videosResult, artistsResult, albumsResult, playlistsResult] = await Promise.all([ api.searchTracks(query, options), api.searchVideos ? api.searchVideos(query, options) : Promise.resolve({ items: [] }), api.searchArtists(query, options), api.searchAlbums(query, options), - api.searchPlaylists ? api.searchPlaylists(query, options) : Promise.resolve({ items: [] }) + api.searchPlaylists ? api.searchPlaylists(query, options) : Promise.resolve({ items: [] }), ]); - + return { tracks: tracksResult, videos: videosResult, artists: artistsResult, albums: albumsResult, - playlists: playlistsResult + playlists: playlistsResult, }; } diff --git a/js/player.js b/js/player.js index 7e08b13..ac438c0 100644 --- a/js/player.js +++ b/js/player.js @@ -106,7 +106,7 @@ export class Player { bufferingGoal: 30, rebufferingGoal: 2, bufferBehind: 30, - jumpLargeGaps: true + jumpLargeGaps: true, }, abr: { enabled: true, @@ -115,11 +115,11 @@ export class Player { defaultBandwidthEstimate: 100000, switchInterval: 1, // Check more frequently bandwidthDowngradeTarget: 0.8, // Downgrade more aggressively if bandwidth drops - restrictToElementSize: false + restrictToElementSize: false, }, mediaSource: { - codecSwitchingStrategy: 'smooth' - } + codecSwitchingStrategy: 'smooth', + }, }); this.shakaPlayer.addEventListener('adaptation', this.updateAdaptiveQualityBadge.bind(this)); this.shakaPlayer.addEventListener('variantchanged', this.updateAdaptiveQualityBadge.bind(this)); @@ -501,7 +501,9 @@ export class Player { // Warm connection/cache // For Blob URLs (DASH), this head request is not needed and can cause errors. if (!streamInfo.url.startsWith('blob:')) { - fetch(streamInfo.url, { method: 'HEAD', signal: this.preloadAbortController.signal }).catch(() => {}); + fetch(streamInfo.url, { method: 'HEAD', signal: this.preloadAbortController.signal }).catch( + () => {} + ); } } catch (error) { if (error.name !== 'AbortError') { @@ -921,10 +923,10 @@ export class Player { await this.shakaPlayer.attach(activeElement); await this.shakaPlayer.load(streamUrl); this.shakaInitialized = true; - + const savedAdaptiveQuality = localStorage.getItem('adaptive-playback-quality') || 'auto'; this.forceQuality(savedAdaptiveQuality); - + this.updateAdaptiveQualityBadge(); } else { activeElement.src = streamUrl; @@ -963,7 +965,7 @@ export class Player { // We only need the legacy track info if we missed getting ReplayGain from the manifest endpoint const resolvedStreamInfo = await streamInfoPromise; if (this.playbackSequence !== currentSequence) return; - + streamUrl = resolvedStreamInfo.url; if (resolvedStreamInfo.rgInfo) { @@ -1001,10 +1003,10 @@ export class Player { } this.shakaInitialized = true; this.applyAudioEffects(); - + const savedAdaptiveQuality = localStorage.getItem('adaptive-playback-quality') || 'auto'; this.forceQuality(savedAdaptiveQuality); - + this.updateAdaptiveQualityBadge(); const canPlay = await this.waitForCanPlayOrTimeout(activeElement); @@ -1330,7 +1332,7 @@ export class Player { handlePlayPause() { const el = this.activeElement; const hasSource = el.src || el.currentSrc || el.srcObject || this.shakaInitialized; - + if (!hasSource || el.error) { if (this.currentTrack) { this.playTrackFromQueue(0, 0); @@ -1653,7 +1655,7 @@ export class Player { updateAdaptiveQualityBadge() { if (!this.currentTrack) return; - + try { const titleEl = document.querySelector('.now-playing-bar .title'); if (!titleEl) return; @@ -1662,11 +1664,12 @@ export class Player { // Determine if the track is inherently an Atmos track based on metadata const trackBaseQuality = deriveTrackQuality(this.currentTrack); - const isTrackAtmos = trackBaseQuality === 'DOLBY_ATMOS' || this.currentTrack?.audioQuality === 'DOLBY_ATMOS'; + const isTrackAtmos = + trackBaseQuality === 'DOLBY_ATMOS' || this.currentTrack?.audioQuality === 'DOLBY_ATMOS'; if (this.shakaInitialized) { const variants = this.shakaPlayer.getVariantTracks(); - const activeVariant = variants.find(t => t.active); + const activeVariant = variants.find((t) => t.active); if (activeVariant) { if (!badgeEl) { badgeEl = document.createElement('span'); @@ -1676,16 +1679,18 @@ export class Player { const staticBadge = titleEl.querySelector('.quality-badge:not(.shaka-quality-badge)'); if (staticBadge) staticBadge.style.display = 'none'; } - + let text = ''; let isAtmosPlaying = false; - + if (activeVariant.videoBandwidth && activeVariant.height) { text = `${activeVariant.height}p`; } else if (activeVariant.audioCodec) { const codec = activeVariant.audioCodec.toLowerCase(); if (codec.includes('flac')) { - const sampleRate = activeVariant.audioSamplingRate ? activeVariant.audioSamplingRate / 1000 : 44.1; + const sampleRate = activeVariant.audioSamplingRate + ? activeVariant.audioSamplingRate / 1000 + : 44.1; if (sampleRate > 48 || activeVariant.audioBandwidth > 1200000) { text = `HD 24/${sampleRate}`; } else { @@ -1702,13 +1707,18 @@ export class Player { } else { text = activeVariant.audioCodec; } - if (activeVariant.audioBandwidth && !text.includes('FLAC') && !text.includes('HD') && !isAtmosPlaying) { + if ( + activeVariant.audioBandwidth && + !text.includes('FLAC') && + !text.includes('HD') && + !isAtmosPlaying + ) { text += ` ${Math.round(activeVariant.audioBandwidth / 1000)}k`; } } else { text = 'Auto'; } - + if (isAtmosPlaying) { badgeEl.className = 'quality-badge quality-atmos shaka-quality-badge'; badgeEl.innerHTML = SVG_ATMOS(20); @@ -1716,9 +1726,14 @@ export class Player { badgeEl.className = 'quality-badge quality-hires shaka-quality-badge'; badgeEl.textContent = text; } - badgeEl.style.display = (text || isAtmosPlaying) ? 'inline-flex' : 'none'; + badgeEl.style.display = text || isAtmosPlaying ? 'inline-flex' : 'none'; } - } else if ((isIos || isSafari) && this.activeElement && this.activeElement.src && (this.activeElement.src.includes('.m3u8') || this.currentTrack)) { + } else if ( + (isIos || isSafari) && + this.activeElement && + this.activeElement.src && + (this.activeElement.src.includes('.m3u8') || this.currentTrack) + ) { if (!badgeEl) { badgeEl = document.createElement('span'); badgeEl.className = 'quality-badge quality-hires shaka-quality-badge'; @@ -1727,24 +1742,28 @@ export class Player { const staticBadge = titleEl.querySelector('.quality-badge:not(.shaka-quality-badge)'); if (staticBadge) staticBadge.style.display = 'none'; } - + let text = ''; - + // Ensure device can actually decode Atmos before rendering logo for HLS let deviceSupportsAtmos = false; try { if (window.MediaSource && typeof window.MediaSource.isTypeSupported === 'function') { - deviceSupportsAtmos = MediaSource.isTypeSupported('audio/mp4; codecs="ec-3"') || MediaSource.isTypeSupported('audio/mp4; codecs="eac3"'); + deviceSupportsAtmos = + MediaSource.isTypeSupported('audio/mp4; codecs="ec-3"') || + MediaSource.isTypeSupported('audio/mp4; codecs="eac3"'); } if (!deviceSupportsAtmos && typeof document !== 'undefined') { const a = document.createElement('audio'); - deviceSupportsAtmos = !!(a.canPlayType('audio/mp4; codecs="ec-3"') || a.canPlayType('audio/mp4; codecs="eac3"')); + deviceSupportsAtmos = !!( + a.canPlayType('audio/mp4; codecs="ec-3"') || a.canPlayType('audio/mp4; codecs="eac3"') + ); } } catch (e) {} let isAtmosPlaying = isTrackAtmos && deviceSupportsAtmos; const q = this.quality || localStorage.getItem('adaptive-playback-quality') || 'auto'; - + if (!isAtmosPlaying) { if (q === 'HI_RES_LOSSLESS') text = 'HD FLAC'; else if (q === 'LOSSLESS') text = 'FLAC'; @@ -1753,7 +1772,7 @@ export class Player { else if (q === 'auto') text = 'HLS Auto'; else text = 'HLS'; } - + if (isAtmosPlaying) { badgeEl.innerHTML = SVG_ATMOS(20); badgeEl.className = 'quality-badge quality-atmos shaka-quality-badge'; @@ -1771,7 +1790,8 @@ export class Player { } evaluateCrossCodecAbr() { - if (!this.shakaInitialized || !this.shakaPlayer || this.shakaPlayer.isBuffering() || this.activeElement.paused) return; + if (!this.shakaInitialized || !this.shakaPlayer || this.shakaPlayer.isBuffering() || this.activeElement.paused) + return; try { const stats = this.shakaPlayer.getStats(); @@ -1781,13 +1801,13 @@ export class Player { const variants = this.shakaPlayer.getVariantTracks(); if (variants.length < 2) return; - const activeVariant = variants.find(v => v.active); + const activeVariant = variants.find((v) => v.active); if (!activeVariant) return; // Sort variants by bandwidth descending const sortedVariants = [...variants].sort((a, b) => b.bandwidth - a.bandwidth); const safeUpBandwidth = estimatedBandwidth * 0.85; - + let bestVariant = sortedVariants[0]; for (const variant of sortedVariants) { if (variant.bandwidth <= safeUpBandwidth) { @@ -1795,7 +1815,7 @@ export class Player { break; } } - + if (sortedVariants[sortedVariants.length - 1].bandwidth > safeUpBandwidth) { bestVariant = sortedVariants[sortedVariants.length - 1]; } @@ -1817,9 +1837,9 @@ export class Player { try { if (quality === 'auto') { - this.shakaPlayer.configure({ + this.shakaPlayer.configure({ abr: { enabled: true }, - preferredAudioCodecs: [] + preferredAudioCodecs: [], }); return; } @@ -1828,12 +1848,12 @@ export class Player { if (variants.length === 0) return; let bestVariant = variants[0]; - + if (quality === 'LOW' || quality === 'HIGH') { const targetBandwidth = quality === 'LOW' ? 96000 : 320000; - const aacVariants = variants.filter(v => v.audioCodec && v.audioCodec.toLowerCase().includes('mp4a')); + const aacVariants = variants.filter((v) => v.audioCodec && v.audioCodec.toLowerCase().includes('mp4a')); const searchVariants = aacVariants.length > 0 ? aacVariants : variants; - + let minDiff = Infinity; for (const variant of searchVariants) { const bw = variant.audioBandwidth || variant.bandwidth; @@ -1844,22 +1864,24 @@ export class Player { } } } else if (quality === 'LOSSLESS' || quality === 'HI_RES_LOSSLESS') { - const flacVariants = variants.filter(v => v.audioCodec && v.audioCodec.toLowerCase().includes('flac')); - + const flacVariants = variants.filter( + (v) => v.audioCodec && v.audioCodec.toLowerCase().includes('flac') + ); + if (flacVariants.length > 0) { if (quality === 'HI_RES_LOSSLESS') { // Find highest quality FLAC bestVariant = flacVariants.reduce((prev, current) => { const prevBw = prev.audioBandwidth || prev.bandwidth || 0; const currBw = current.audioBandwidth || current.bandwidth || 0; - return (currBw > prevBw) ? current : prev; + return currBw > prevBw ? current : prev; }, flacVariants[0]); } else { // Find standard lossless (lowest bandwidth FLAC, usually 16-bit 44.1kHz) bestVariant = flacVariants.reduce((prev, current) => { const prevBw = prev.audioBandwidth || prev.bandwidth || 0; const currBw = current.audioBandwidth || current.bandwidth || 0; - return (currBw < prevBw) ? current : prev; + return currBw < prevBw ? current : prev; }, flacVariants[0]); } } else { @@ -1867,13 +1889,13 @@ export class Player { bestVariant = variants.reduce((prev, current) => { const prevBw = prev.audioBandwidth || prev.bandwidth || 0; const currBw = current.audioBandwidth || current.bandwidth || 0; - return (currBw > prevBw) ? current : prev; + return currBw > prevBw ? current : prev; }, variants[0]); } } this.shakaPlayer.configure({ abr: { enabled: false } }); - + if (bestVariant.audioCodec) { this.shakaPlayer.configure({ preferredAudioCodecs: [bestVariant.audioCodec] }); } diff --git a/js/settings.js b/js/settings.js index 02fd1af..1d4e71f 100644 --- a/js/settings.js +++ b/js/settings.js @@ -801,10 +801,14 @@ export async function initializeSettings(scrobbler, player, api, ui) { const streamingQualitySetting = document.getElementById('streaming-quality-setting'); if (streamingQualitySetting) { const savedAdaptiveQuality = localStorage.getItem('adaptive-playback-quality') || 'auto'; - + // Map the stored auto state to the dropdown, or if it doesn't match an option, use the playback-quality value - const optionExists = Array.from(streamingQualitySetting.options).some(opt => opt.value === savedAdaptiveQuality); - streamingQualitySetting.value = optionExists ? savedAdaptiveQuality : (localStorage.getItem('playback-quality') || 'auto'); + const optionExists = Array.from(streamingQualitySetting.options).some( + (opt) => opt.value === savedAdaptiveQuality + ); + streamingQualitySetting.value = optionExists + ? savedAdaptiveQuality + : localStorage.getItem('playback-quality') || 'auto'; // Apply initially if (player.forceQuality) player.forceQuality(streamingQualitySetting.value); @@ -813,7 +817,7 @@ export async function initializeSettings(scrobbler, player, api, ui) { streamingQualitySetting.addEventListener('change', (e) => { const val = e.target.value; - + // Set adaptive DASH quality localStorage.setItem('adaptive-playback-quality', val); if (player.forceQuality) player.forceQuality(val);