diff --git a/js/audio-context.js b/js/audio-context.js index ef1375f..a36a53a 100644 --- a/js/audio-context.js +++ b/js/audio-context.js @@ -171,7 +171,9 @@ class AudioContextManager { // Fallback: direct connection try { this.source.connect(this.audioContext.destination); - } catch {} + } catch { + /* ignore */ + } } } diff --git a/js/db.js b/js/db.js index 7e581ab..d8e1656 100644 --- a/js/db.js +++ b/js/db.js @@ -111,7 +111,7 @@ export class MusicDatabase { store.put(entry); }; - cursorReq.onerror = (e) => { + cursorReq.onerror = (_e) => { // If cursor fails, just try to put (fallback) store.put(entry); }; diff --git a/js/equalizer.js b/js/equalizer.js index 69011d2..e372c42 100644 --- a/js/equalizer.js +++ b/js/equalizer.js @@ -164,7 +164,7 @@ export class Equalizer { * Calculate Q factor for each band * Using constant-Q design for consistent bandwidth */ - _calculateQ(index) { + _calculateQ(_index) { // For 16-band 1/2 octave spacing, Q ≈ 2.87 // Slightly lower Q for smoother response return 2.5; @@ -354,15 +354,21 @@ export class Equalizer { this.filters.forEach((filter) => { try { filter.disconnect(); - } catch {} + } catch { + /* ignore */ + } }); try { this.inputNode?.disconnect(); - } catch {} + } catch { + /* ignore */ + } try { this.outputNode?.disconnect(); - } catch {} + } catch { + /* ignore */ + } this.filters = []; this.inputNode = null; diff --git a/js/metadata.js b/js/metadata.js index 6f70c84..3a661ca 100644 --- a/js/metadata.js +++ b/js/metadata.js @@ -13,7 +13,7 @@ const DEFAULT_ALBUM = 'Unknown Album'; * @param {string} quality - Audio quality * @returns {Promise} - Audio blob with embedded metadata */ -export async function addMetadataToAudio(audioBlob, track, api, quality) { +export async function addMetadataToAudio(audioBlob, track, api, _quality) { // Always check actual file signature, not just quality setting // DASH Hi-Res streams may return fragmented MP4 instead of raw FLAC const buffer = await audioBlob.slice(0, 12).arrayBuffer(); diff --git a/js/playlist-generator.js b/js/playlist-generator.js index 932b357..731a193 100644 --- a/js/playlist-generator.js +++ b/js/playlist-generator.js @@ -1,4 +1,4 @@ -import { sanitizeForFilename, formatTemplate } from './utils.js'; +import { sanitizeForFilename } from './utils.js'; /** * Generates M3U playlist content diff --git a/js/settings.js b/js/settings.js index 8adc276..a30bfb2 100644 --- a/js/settings.js +++ b/js/settings.js @@ -102,7 +102,9 @@ export function initializeSettings(scrobbler, player, api, ui) { } try { await authManager.sendPasswordReset(email); - } catch {} + } catch { + /* ignore */ + } }); } diff --git a/js/storage.js b/js/storage.js index 5990ff8..39cf378 100644 --- a/js/storage.js +++ b/js/storage.js @@ -784,7 +784,9 @@ export const equalizerSettings = { return gains; } } - } catch {} + } catch { + /* ignore */ + } // Return flat EQ (all zeros) by default return new Array(16).fill(0); }, diff --git a/js/tracker.js b/js/tracker.js index fc2c00a..24a9f79 100644 --- a/js/tracker.js +++ b/js/tracker.js @@ -1,5 +1,5 @@ //js/tracker.js -import { escapeHtml, SVG_DOWNLOAD, SVG_MENU, SVG_PLAY, trackDataStore, formatTime, SVG_HEART } from './utils.js'; +import { escapeHtml, SVG_MENU, SVG_PLAY, trackDataStore, formatTime, SVG_HEART } from './utils.js'; import { navigate } from './router.js'; let artistsData = []; @@ -22,8 +22,8 @@ function cleanSongTitle(title) { if (!title) return ''; // Remove (prod. ...), (produced by ...), [prod. ...], etc. return title - .replace(/\s*[\(\[]\s*prod\.?\s+[^\)\]]+[\)\]]/gi, '') - .replace(/\s*[\(\[]\s*produced\s+by\s+[^\)\]]+[\)\]]/gi, '') + .replace(/\s*[([]\s*prod\.?\s+[^)\]]+[)\]]/gi, '') + .replace(/\s*[([]\s*produced\s+by\s+[^)\]]+[)\]]/gi, '') .replace(/\s+/g, ' ') .trim(); } @@ -134,7 +134,7 @@ function getDirectUrl(rawUrl) { } // Convert tracker song to standard track format -function createTrackFromSong(song, era, artistName, index, sheetId = '') { +export function createTrackFromSong(song, era, artistName, index, sheetId = '') { const isValidUrl = (u) => u && typeof u === 'string' && u.trim().length > 0; const rawUrl = (isValidUrl(song.url) ? song.url : null) || (song.urls ? song.urls.find(isValidUrl) : null); const directUrl = getDirectUrl(rawUrl); @@ -325,7 +325,7 @@ export async function renderTrackerArtistPage(sheetId, container) { if (era.data) { Object.values(era.data).forEach((songs) => { if (songs && songs.length) { - songs.forEach((song, index) => { + songs.forEach((song) => { const track = createTrackFromSong(song, era, artist.name, allTracks.length, sheetId); allTracks.push(track); }); @@ -406,7 +406,7 @@ export async function renderTrackerArtistPage(sheetId, container) { if (era.data) { Object.values(era.data).forEach((songs) => { if (songs && songs.length) { - songs.forEach((song, index) => { + songs.forEach((song) => { const track = createTrackFromSong(song, era, artist.name, eraTracks.length, sheetId); eraTracks.push(track); }); @@ -498,7 +498,7 @@ export async function renderTrackerArtistPage(sheetId, container) { } // Render individual tracker project page -export async function renderTrackerProjectPage(sheetId, projectName, container, ui) { +export async function renderTrackerProjectPage(sheetId, projectName, container, _ui) { if (!artistsData.length) { await loadArtistsData(); } @@ -526,7 +526,7 @@ export async function renderTrackerProjectPage(sheetId, projectName, container, if (era.data) { Object.values(era.data).forEach((songs) => { if (songs && songs.length) { - songs.forEach((song, index) => { + songs.forEach((song) => { const track = createTrackFromSong(song, era, artist.name, eraTracks.length, sheetId); eraTracks.push(track); }); @@ -645,13 +645,6 @@ export async function renderTrackerProjectPage(sheetId, projectName, container, const era = trackerData.eras[eraName]; if (!era) return; - let trackCount = 0; - if (era.data) { - Object.values(era.data).forEach((songs) => { - if (songs && songs.length) trackCount += songs.length; - }); - } - card.onclick = (e) => { if (e.target.closest('.card-play-btn')) { e.stopPropagation(); @@ -659,7 +652,7 @@ export async function renderTrackerProjectPage(sheetId, projectName, container, if (era.data) { Object.values(era.data).forEach((songs) => { if (songs && songs.length) { - songs.forEach((song, index) => { + songs.forEach((song) => { const track = createTrackFromSong( song, era, @@ -797,7 +790,7 @@ export async function renderUnreleasedPage(container) { } // Render track page for unreleased songs -export async function renderTrackerTrackPage(trackId, container, ui) { +export async function renderTrackerTrackPage(trackId, container, _ui) { // Parse track ID: tracker-{sheetId}-{eraName}-{index} const parts = trackId.split('-'); if (parts.length < 4) { @@ -834,17 +827,15 @@ export async function renderTrackerTrackPage(trackId, container, ui) { // Find the specific track let currentTrack = null; - let currentIndex = 0; let allTracks = []; Object.values(era.data).forEach((songs) => { if (songs && songs.length) { - songs.forEach((song, idx) => { + songs.forEach((song) => { const track = createTrackFromSong(song, era, artist.name, allTracks.length, sheetId); allTracks.push(track); if (allTracks.length - 1 === trackIndex) { currentTrack = track; - currentIndex = allTracks.length - 1; } }); } diff --git a/js/ui.js b/js/ui.js index d7cf94e..f90fdaf 100644 --- a/js/ui.js +++ b/js/ui.js @@ -33,6 +33,7 @@ import { findTrackerArtistByName, getArtistUnreleasedProjects, createProjectCardHTML, + createTrackFromSong, } from './tracker.js'; export class UIRenderer { @@ -2448,6 +2449,7 @@ export class UIRenderer { loadUnreleasedBtn.style.display = 'none'; // Add click handlers + const player = this.player; unreleasedContainer.querySelectorAll('.card').forEach((card) => { const eraName = decodeURIComponent(card.dataset.trackerProjectId); const era = eras.find((e) => e.name === eraName); @@ -2460,7 +2462,7 @@ export class UIRenderer { if (era.data) { Object.values(era.data).forEach((songs) => { if (songs && songs.length) { - songs.forEach((song, index) => { + songs.forEach((song) => { const track = createTrackFromSong( song, era, @@ -2475,8 +2477,8 @@ export class UIRenderer { } const availableTracks = eraTracks.filter((t) => !t.unavailable); if (availableTracks.length > 0) { - globalPlayer.setQueue(availableTracks, 0); - globalPlayer.playTrackFromQueue(); + player.setQueue(availableTracks, 0); + player.playTrackFromQueue(); } } else if (e.target.closest('.card-menu-btn')) { e.stopPropagation(); diff --git a/js/utils.js b/js/utils.js index 5ee3f40..6ec9fc6 100644 --- a/js/utils.js +++ b/js/utils.js @@ -143,7 +143,7 @@ export const buildTrackFilename = (track, quality, extension = null) => { album: track.album?.title, }; - return formatTemplate(template, data) + '.' + extension; + return formatTemplate(template, data) + '.' + ext; }; const sanitizeToken = (value) => { diff --git a/js/visualizers/lcd.js b/js/visualizers/lcd.js index b4ccae1..6382288 100644 --- a/js/visualizers/lcd.js +++ b/js/visualizers/lcd.js @@ -187,7 +187,7 @@ export class LCDPreset { draw(ctx, canvas, analyser, dataArray, params) { const { width, height } = canvas; - const { kick, intensity, primaryColor, mode } = params; + const { kick, primaryColor, mode } = params; this.primaryColor = primaryColor; const isDark = document.documentElement.getAttribute('data-theme') !== 'light'; diff --git a/js/visualizers/particles.js b/js/visualizers/particles.js index a76b701..be2d535 100644 --- a/js/visualizers/particles.js +++ b/js/visualizers/particles.js @@ -5,7 +5,7 @@ export class ParticlesPreset { this.particleCount = 180; } - resize(width, height) { + resize(_width, _height) { // Particles don't need explicit resize logic unless we want to respawn them, // but current logic handles boundaries in draw loop. } diff --git a/styles.css b/styles.css index a14f2a9..95fc90e 100644 --- a/styles.css +++ b/styles.css @@ -363,17 +363,6 @@ kbd { padding-bottom: 160px !important; } -/* Ensure content sits on top of background */ -.main-header { - position: relative; - z-index: 100; -} - -.page { - position: relative; - z-index: 1; -} - #page-background { position: absolute; top: 0; @@ -704,6 +693,8 @@ input[type='search']::-webkit-search-cancel-button { .page { display: none; + position: relative; + z-index: 1; } .page.active { @@ -1103,6 +1094,8 @@ input[type='search']::-webkit-search-cancel-button { .heart-icon.filled { color: #ef4444; fill: #ef4444; + stroke: #ef4444; + animation: heart-beat 0.4s var(--ease-elastic); } .track-item:hover .like-btn { @@ -1878,8 +1871,8 @@ input:checked + .slider::before { .player-controls .buttons button:active { transform: scale(0.9); - - /* Press effect */ + color: var(--foreground); + background-color: var(--secondary); } @media (hover: hover) { @@ -1889,11 +1882,6 @@ input:checked + .slider::before { } } -.player-controls .buttons button:active { - color: var(--foreground); - background-color: var(--secondary); -} - .player-controls .buttons button.active { color: var(--active-highlight); } @@ -3614,6 +3602,9 @@ img[src=''] { .modal.active { display: flex; + backdrop-filter: blur(4px); + background: rgb(0, 0, 0, 0.6); + transition: opacity var(--transition-normal); } .modal-overlay { @@ -5002,9 +4993,6 @@ body:has(#side-panel.active) .fullscreen-main-view { flex 0.3s ease, padding-right 0.3s ease, margin-right 0.3s ease; -} - -body:has(#side-panel.active) .fullscreen-main-view { padding-right: 600px; } @@ -5103,13 +5091,6 @@ textarea:focus { box-shadow: 0 0 0 3px rgb(var(--highlight-rgb), 0.2); } -/* Modals */ -.modal.active { - backdrop-filter: blur(4px); - background: rgb(0, 0, 0, 0.6); - transition: opacity var(--transition-normal); -} - .modal.active .modal-content { animation: pop-in var(--transition-normal) var(--ease-out-back); box-shadow: var(--shadow-2xl); @@ -5126,15 +5107,6 @@ textarea:focus { transform: scale(0.97); } -/* Heart Icon Animation */ -.heart-icon.filled { - animation: heart-beat 0.4s var(--ease-elastic); - fill: #ef4444; - - /* Standardize heart red */ - stroke: #ef4444; -} - /* ========================================= 16-Band Equalizer ========================================= */