style: auto-fix linting issues

This commit is contained in:
SamidyFR 2026-01-24 12:13:44 +00:00 committed by github-actions[bot]
parent b7bc90f4f1
commit 4ede3b2664
23 changed files with 413 additions and 273 deletions

View file

@ -22,4 +22,4 @@ UI:
| QQDL | https://tidal.qqdl.site/ |
| Arjix | https://music.arjix.dev/ |
| Spofree | https://spo.free.nf |
| Mappl | https://mappl.tv/music |
| Mappl | https://mappl.tv/music |

View file

@ -67,7 +67,9 @@ class ServerAPI {
export async function onRequest(context) {
const { request, params, env } = context;
const userAgent = request.headers.get('User-Agent') || '';
const isBot = /discordbot|twitterbot|facebookexternalhit|bingbot|googlebot|slurp|whatsapp|pinterest|slackbot/i.test(userAgent);
const isBot = /discordbot|twitterbot|facebookexternalhit|bingbot|googlebot|slurp|whatsapp|pinterest|slackbot/i.test(
userAgent
);
const albumId = params.id;
if (isBot && albumId) {
@ -84,7 +86,9 @@ export async function onRequest(context) {
const trackCount = album.numberOfTracks || tracks.length;
const description = `Album by ${artist}${year}${trackCount} Tracks\nListen on Monochrome`;
const imageUrl = album.cover ? api.getCoverUrl(album.cover, '1280') : 'https://monochrome.samidy.com/assets/appicon.png';
const imageUrl = album.cover
? api.getCoverUrl(album.cover, '1280')
: 'https://monochrome.samidy.com/assets/appicon.png';
const pageUrl = new URL(request.url).href;
const metaHtml = `

View file

@ -67,7 +67,9 @@ class ServerAPI {
export async function onRequest(context) {
const { request, params, env } = context;
const userAgent = request.headers.get('User-Agent') || '';
const isBot = /discordbot|twitterbot|facebookexternalhit|bingbot|googlebot|slurp|whatsapp|pinterest|slackbot/i.test(userAgent);
const isBot = /discordbot|twitterbot|facebookexternalhit|bingbot|googlebot|slurp|whatsapp|pinterest|slackbot/i.test(
userAgent
);
const artistId = params.id;
if (isBot && artistId) {
@ -79,7 +81,9 @@ export async function onRequest(context) {
if (artist && (artist.name || artist.title)) {
const name = artist.name || artist.title;
const description = `Listen to ${name} on Monochrome`;
const imageUrl = artist.picture ? api.getArtistPictureUrl(artist.picture, '750') : 'https://monochrome.samidy.com/assets/appicon.png';
const imageUrl = artist.picture
? api.getArtistPictureUrl(artist.picture, '750')
: 'https://monochrome.samidy.com/assets/appicon.png';
const pageUrl = new URL(request.url).href;
const metaHtml = `

View file

@ -68,7 +68,9 @@ class ServerAPI {
export async function onRequest(context) {
const { request, params, env } = context;
const userAgent = request.headers.get('User-Agent') || '';
const isBot = /discordbot|twitterbot|facebookexternalhit|bingbot|googlebot|slurp|whatsapp|pinterest|slackbot/i.test(userAgent);
const isBot = /discordbot|twitterbot|facebookexternalhit|bingbot|googlebot|slurp|whatsapp|pinterest|slackbot/i.test(
userAgent
);
const playlistId = params.id;
if (isBot && playlistId) {
@ -82,7 +84,9 @@ export async function onRequest(context) {
const trackCount = playlist.numberOfTracks;
const description = `Playlist • ${trackCount} Tracks\nListen on Monochrome`;
const imageId = playlist.squareImage || playlist.image;
const imageUrl = imageId ? api.getCoverUrl(imageId, '1080') : 'https://monochrome.samidy.com/assets/appicon.png';
const imageUrl = imageId
? api.getCoverUrl(imageId, '1080')
: 'https://monochrome.samidy.com/assets/appicon.png';
const pageUrl = new URL(request.url).href;
const metaHtml = `

View file

@ -64,7 +64,7 @@ class ServerAPI {
const json = await response.json();
const data = json.data || json;
const items = Array.isArray(data) ? data : [data];
const found = items.find(i => (i.id == id) || (i.item && i.item.id == id));
const found = items.find((i) => i.id == id || (i.item && i.item.id == id));
if (found) {
return found.item || found;
}
@ -87,7 +87,9 @@ class ServerAPI {
export async function onRequest(context) {
const { request, params, env } = context;
const userAgent = request.headers.get('User-Agent') || '';
const isBot = /discordbot|twitterbot|facebookexternalhit|bingbot|googlebot|slurp|whatsapp|pinterest|slackbot/i.test(userAgent);
const isBot = /discordbot|twitterbot|facebookexternalhit|bingbot|googlebot|slurp|whatsapp|pinterest|slackbot/i.test(
userAgent
);
const trackId = params.id;
if (isBot && trackId) {
@ -112,12 +114,14 @@ export async function onRequest(context) {
}
}
// this prob wont work im js winging it
const audioMeta = audioUrl ? `
const audioMeta = audioUrl
? `
<meta property="og:audio" content="${audioUrl}">
<meta property="og:audio:type" content="audio/mp4">
<meta property="og:video" content="${audioUrl}">
<meta property="og:video:type" content="audio/mp4">
` : '';
`
: '';
const metaHtml = `
<!DOCTYPE html>

View file

@ -1,12 +1,13 @@
// functions/userplaylist/[id].js
// note that, since this NEEDS a playlist to yknow, be public, this only works for PUBLIC playlists (and you will need an account)
export async function onRequest(context) {
const { request, params, env } = context;
const userAgent = request.headers.get('User-Agent') || '';
const isBot = /discordbot|twitterbot|facebookexternalhit|bingbot|googlebot|slurp|whatsapp|pinterest|slackbot/i.test(userAgent);
const isBot = /discordbot|twitterbot|facebookexternalhit|bingbot|googlebot|slurp|whatsapp|pinterest|slackbot/i.test(
userAgent
);
const playlistId = params.id;
if (isBot && playlistId) {
@ -15,8 +16,8 @@ export async function onRequest(context) {
let response = await fetch(pbUrl);
if (!response.ok) {
pbUrl = `https://monodb.samidy.com/api/collections/public_playlists/records?filter=(uuid='${playlistId}')`;
response = await fetch(pbUrl);
pbUrl = `https://monodb.samidy.com/api/collections/public_playlists/records?filter=(uuid='${playlistId}')`;
response = await fetch(pbUrl);
}
if (response.ok) {
@ -30,7 +31,11 @@ export async function onRequest(context) {
const title = playlist.name || playlist.title || 'User Playlist';
let tracks = [];
try {
tracks = Array.isArray(playlist.tracks) ? playlist.tracks : (playlist.tracks ? JSON.parse(playlist.tracks) : []);
tracks = Array.isArray(playlist.tracks)
? playlist.tracks
: playlist.tracks
? JSON.parse(playlist.tracks)
: [];
} catch (e) {
console.error('Failed to parse tracks JSON', e);
}
@ -45,7 +50,12 @@ export async function onRequest(context) {
} else {
imageUrl = `https://monodb.samidy.com/api/files/${playlist.collectionId}/${playlist.id}/${playlist.cover}`;
}
} else if (tracks.length > 0 && typeof tracks[0] === 'object' && tracks[0].album && tracks[0].album.cover) {
} else if (
tracks.length > 0 &&
typeof tracks[0] === 'object' &&
tracks[0].album &&
tracks[0].album.cover
) {
const cover = tracks[0].album.cover;
imageUrl = `https://resources.tidal.com/images/${cover.replace(/-/g, '/')}/1280x1280.jpg`;
}

View file

@ -846,7 +846,7 @@ export class LosslessAPI {
let track;
const items = Array.isArray(data) ? data : [data];
const found = items.find(i => (i.id == id) || (i.item && i.item.id == id));
const found = items.find((i) => i.id == id || (i.item && i.item.id == id));
if (found) {
track = this.prepareTrack(found.item || found);

View file

@ -15,7 +15,13 @@ import { createRouter, updateTabTitle, navigate } from './router.js';
import { initializeSettings } from './settings.js';
import { initializePlayerEvents, initializeTrackInteractions, handleTrackAction } from './events.js';
import { initializeUIInteractions } from './ui-interactions.js';
import { downloadAlbumAsZip, downloadDiscography, downloadPlaylistAsZip, downloadLikedTracks, showNotification } from './downloads.js';
import {
downloadAlbumAsZip,
downloadDiscography,
downloadPlaylistAsZip,
downloadLikedTracks,
showNotification,
} from './downloads.js';
import { debounce, SVG_PLAY } from './utils.js';
import { sidePanelManager } from './side-panel.js';
import { db } from './db.js';
@ -192,7 +198,8 @@ document.addEventListener('DOMContentLoaded', async () => {
// i love ios and macos!!!! webkit fucking SUCKS BULLSHIT sorry ios/macos heads yall getting lossless only
const ua = navigator.userAgent.toLowerCase();
const isIOS = /iphone|ipad|ipod/.test(ua) || (ua.includes('mac') && navigator.maxTouchPoints > 1);
const isSafari = ua.includes('safari') && !ua.includes('chrome') && !ua.includes('crios') && !ua.includes('android');
const isSafari =
ua.includes('safari') && !ua.includes('chrome') && !ua.includes('crios') && !ua.includes('android');
if (isIOS || isSafari) {
const qualitySelect = document.getElementById('streaming-quality-setting');
@ -919,11 +926,15 @@ document.addEventListener('DOMContentLoaded', async () => {
return;
}
list.innerHTML = playlists.map(p => `
list.innerHTML = playlists
.map(
(p) => `
<div class="modal-option" data-id="${p.id}">
<span>${p.name}</span>
</div>
`).join('');
`
)
.join('');
const closeModal = () => {
modal.classList.remove('active');
@ -1207,7 +1218,6 @@ document.addEventListener('DOMContentLoaded', async () => {
updateTabTitle(player);
};
await handleRouteChange();
window.addEventListener('popstate', handleRouteChange);
@ -1215,7 +1225,12 @@ document.addEventListener('DOMContentLoaded', async () => {
document.body.addEventListener('click', (e) => {
const link = e.target.closest('a');
if (link && link.origin === window.location.origin && link.target !== '_blank' && !link.hasAttribute('download')) {
if (
link &&
link.origin === window.location.origin &&
link.target !== '_blank' &&
!link.hasAttribute('download')
) {
e.preventDefault();
navigate(link.pathname);
}

View file

@ -77,7 +77,7 @@ export class DashDownloader {
adaptationSets.sort((a, b) => {
const getMaxBandwidth = (set) => {
const reps = Array.from(set.querySelectorAll('Representation'));
return reps.length ? Math.max(...reps.map(r => parseInt(r.getAttribute('bandwidth') || '0', 10))) : 0;
return reps.length ? Math.max(...reps.map((r) => parseInt(r.getAttribute('bandwidth') || '0', 10))) : 0;
};
return getMaxBandwidth(b) - getMaxBandwidth(a);
});

View file

@ -545,7 +545,7 @@ export class MusicDatabase {
cover: cover,
playlists: [],
createdAt: Date.now(),
updatedAt: Date.now()
updatedAt: Date.now(),
};
await this.performTransaction('user_folders', 'readwrite', (store) => store.put(folder));
return folder;

View file

@ -545,7 +545,16 @@ function createBulkDownloadNotification(type, name, _totalItems) {
notifEl.dataset.bulkType = type;
notifEl.dataset.bulkName = name;
const typeLabel = type === 'album' ? 'Album' : type === 'playlist' ? 'Playlist' : type === 'liked' ? 'Liked Tracks' : type === 'queue' ? 'Queue' : 'Discography';
const typeLabel =
type === 'album'
? 'Album'
: type === 'playlist'
? 'Playlist'
: type === 'liked'
? 'Liked Tracks'
: type === 'queue'
? 'Queue'
: 'Discography';
notifEl.innerHTML = `
<div style="display: flex; align-items: start; gap: 0.75rem;">

View file

@ -170,7 +170,10 @@ export function initializePlayerEvents(player, audioPlayer, scrobbler, ui) {
const progressBar = document.getElementById('progress-bar');
const playerControls = document.querySelector('.player-controls');
const isTracker = player.currentTrack && (player.currentTrack.isTracker || (player.currentTrack.id && String(player.currentTrack.id).startsWith('tracker-')));
const isTracker =
player.currentTrack &&
(player.currentTrack.isTracker ||
(player.currentTrack.id && String(player.currentTrack.id).startsWith('tracker-')));
if (!waveformSettings.isEnabled() || !player.currentTrack || isTracker) {
if (progressBar) {

View file

@ -12,7 +12,7 @@ class GeniusManager {
}
getToken() {
return "QmS9OvsS-7ifRBKx_ochIPQU7oejIS9Eo_z5iWHmCPyhwLVQID3pYTHJmJTa6z8z"; // idgaf anymore im js hardcoding this lmaooo
return 'QmS9OvsS-7ifRBKx_ochIPQU7oejIS9Eo_z5iWHmCPyhwLVQID3pYTHJmJTa6z8z'; // idgaf anymore im js hardcoding this lmaooo
}
async searchTrack(title, artist) {
@ -22,7 +22,7 @@ class GeniusManager {
const url = `https://api.genius.com/search?q=${query}`;
const token = this.getToken();
const response = await fetch(`https://corsproxy.io/?${encodeURIComponent(url)}`, {
headers: { 'Authorization': `Bearer ${token}` }
headers: { Authorization: `Bearer ${token}` },
});
if (!response.ok) throw new Error('Failed to search Genius');
@ -30,10 +30,10 @@ class GeniusManager {
const data = await response.json();
if (data.response.hits.length === 0) return null;
const normalize = str => str.toLowerCase().replace(/[^\p{L}\p{N}]/gu, '');
const normalize = (str) => str.toLowerCase().replace(/[^\p{L}\p{N}]/gu, '');
const targetArtist = normalize(artist);
const hit = data.response.hits.find(h => {
const hit = data.response.hits.find((h) => {
const hitArtist = normalize(h.result.primary_artist.name);
return hitArtist.includes(targetArtist) || targetArtist.includes(hitArtist);
});
@ -45,7 +45,7 @@ class GeniusManager {
const token = this.getToken();
const url = `https://api.genius.com/referents?song_id=${songId}&text_format=plain&per_page=50`;
const response = await fetch(`https://corsproxy.io/?${encodeURIComponent(url)}`, {
headers: { 'Authorization': `Bearer ${token}` }
headers: { Authorization: `Bearer ${token}` },
});
if (!response.ok) throw new Error('Failed to fetch annotations');
@ -83,13 +83,18 @@ class GeniusManager {
findAnnotations(lineText, referents) {
if (!referents || !lineText) return [];
const normalize = str => str.toLowerCase().replace(/[^\p{L}\p{N}\s]/gu, '').replace(/\s+/g, ' ').trim();
const normalize = (str) =>
str
.toLowerCase()
.replace(/[^\p{L}\p{N}\s]/gu, '')
.replace(/\s+/g, ' ')
.trim();
const normLine = normalize(lineText);
const getWordSet = (str) => new Set(str.split(' ').filter(w => w.length > 0));
const getWordSet = (str) => new Set(str.split(' ').filter((w) => w.length > 0));
const lineWords = getWordSet(normLine);
return referents.filter(ref => {
return referents.filter((ref) => {
const normFragment = normalize(ref.fragment);
if (normLine.includes(normFragment) || normFragment.includes(normLine)) return true;
@ -98,9 +103,11 @@ class GeniusManager {
if (fragmentWords.size === 0 || lineWords.size === 0) return false;
let matchCount = 0;
fragmentWords.forEach(w => { if (lineWords.has(w)) matchCount++; });
fragmentWords.forEach((w) => {
if (lineWords.has(w)) matchCount++;
});
return (matchCount / Math.min(fragmentWords.size, lineWords.size)) > 0.6;
return matchCount / Math.min(fragmentWords.size, lineWords.size) > 0.6;
});
}
}
@ -454,19 +461,20 @@ export class LyricsManager {
this.romajiObserver = new MutationObserver((mutations) => {
// Check if any relevant mutation occurred
const hasRelevantChange = mutations.some((mutation) => {
if (mutation.type === 'childList') {
let relevant = false;
if (mutation.addedNodes.length > 0) {
for (const node of mutation.addedNodes) {
if (node.nodeType === Node.ELEMENT_NODE && node.classList.contains('genius-indicator')) continue;
if (node.nodeType === Node.ELEMENT_NODE && node.classList.contains('genius-indicator'))
continue;
relevant = true;
break;
}
}
if (!relevant && mutation.removedNodes.length > 0) {
for (const node of mutation.removedNodes) {
if (node.nodeType === Node.ELEMENT_NODE && node.classList.contains('genius-indicator')) continue;
if (node.nodeType === Node.ELEMENT_NODE && node.classList.contains('genius-indicator'))
continue;
relevant = true;
break;
}
@ -628,34 +636,38 @@ export class LyricsManager {
if (lineElements.length === 0) return;
lineElements.forEach(el => {
lineElements.forEach((el) => {
el.classList.remove('genius-annotated', 'genius-multi-start', 'genius-multi-end', 'genius-multi-mid');
delete el.__geniusAnnotations;
});
const normalize = (str) =>
str
.toLowerCase()
.replace(/[^\p{L}\p{N}\s]/gu, '')
.replace(/\s+/g, ' ')
.trim();
const normalize = str => str.toLowerCase().replace(/[^\p{L}\p{N}\s]/gu, '').replace(/\s+/g, ' ').trim();
referents.forEach(ref => {
referents.forEach((ref) => {
const fragment = normalize(ref.fragment);
if (!fragment) return;
for (let i = 0; i < lineElements.length; i++) {
let combinedText = "";
let combinedText = '';
let currentLines = [];
for (let j = i; j < lineElements.length; j++) {
const line = lineElements[j];
const lineClone = line.cloneNode(true);
lineClone.querySelectorAll('.time, .timestamp, [class*="time"], .genius-indicator').forEach(n => n.remove());
const text = normalize(lineClone.textContent || "");
lineClone
.querySelectorAll('.time, .timestamp, [class*="time"], .genius-indicator')
.forEach((n) => n.remove());
const text = normalize(lineClone.textContent || '');
if (!text) continue;
if (currentLines.length > 0) combinedText += " ";
if (currentLines.length > 0) combinedText += ' ';
combinedText += text;
currentLines.push(line);
@ -664,7 +676,7 @@ export class LyricsManager {
el.classList.add('genius-annotated');
if (!el.__geniusAnnotations) el.__geniusAnnotations = [];
if (!el.__geniusAnnotations.some(a => a.id === ref.id)) {
if (!el.__geniusAnnotations.some((a) => a.id === ref.id)) {
el.__geniusAnnotations.push(ref);
}
@ -686,7 +698,6 @@ export class LyricsManager {
break;
}
if (combinedText.length > fragment.length + 50) break;
}
}
@ -760,13 +771,16 @@ export function openLyricsPanel(track, audioPlayer, lyricsManager, forceOpen = f
geniusBtn.innerHTML = enabled ? SVG_GENIUS_ACTIVE : SVG_GENIUS_INACTIVE;
if (enabled) {
try {
geniusBtn.style.opacity = '0.5';
await manager.geniusManager.getDataForTrack(track);
manager.currentGeniusData = manager.geniusManager.cache.get(track.id);
const amLyrics = sidePanelManager.panel.querySelector('am-lyrics');
if (amLyrics) manager.applyGeniusAnnotations(amLyrics, manager.geniusManager.cache.get(track.id)?.referents);
if (amLyrics)
manager.applyGeniusAnnotations(
amLyrics,
manager.geniusManager.cache.get(track.id)?.referents
);
} catch (e) {
alert(e.message);
manager.isGeniusMode = false;
@ -776,13 +790,17 @@ export function openLyricsPanel(track, audioPlayer, lyricsManager, forceOpen = f
geniusBtn.style.opacity = '1';
}
} else {
const amLyrics = sidePanelManager.panel.querySelector('am-lyrics');
if (amLyrics) {
const root = amLyrics.shadowRoot || amLyrics;
const lineElements = Array.from(root.querySelectorAll('.genius-annotated'));
lineElements.forEach(el => {
el.classList.remove('genius-annotated', 'genius-multi-start', 'genius-multi-end', 'genius-multi-mid');
lineElements.forEach((el) => {
el.classList.remove(
'genius-annotated',
'genius-multi-start',
'genius-multi-end',
'genius-multi-mid'
);
delete el.__geniusAnnotations;
});
}
@ -848,18 +866,22 @@ async function renderLyricsComponent(container, track, audioPlayer, lyricsManage
await lyricsManager.loadKuroshiro();
}
lyricsManager.fetchLyrics(track.id, track).then(async () => {
if (lyricsManager.isGeniusMode) {
try {
const data = await lyricsManager.geniusManager.getDataForTrack(track);
if (data) {
lyricsManager.currentGeniusData = data;
lyricsManager.applyGeniusAnnotations(amLyrics, data.referents);
lyricsManager
.fetchLyrics(track.id, track)
.then(async () => {
if (lyricsManager.isGeniusMode) {
try {
const data = await lyricsManager.geniusManager.getDataForTrack(track);
if (data) {
lyricsManager.currentGeniusData = data;
lyricsManager.applyGeniusAnnotations(amLyrics, data.referents);
}
} catch (e) {
console.warn('Genius auto-load failed', e);
}
} catch (e) { console.warn('Genius auto-load failed', e); }
}
}).catch(e => console.warn('Background lyrics fetch failed', e));
}
})
.catch((e) => console.warn('Background lyrics fetch failed', e));
// Wait for lyrics to appear, then do an immediate conversion
const waitForLyrics = () => {
@ -955,20 +977,21 @@ function setupSync(track, audioPlayer, amLyrics, lyricsManager) {
const onLineClick = (e) => {
if (e.detail && e.detail.timestamp !== undefined) {
const manager = lyricsManager || sidePanelManager.panel.lyricsManager;
if (manager && manager.isGeniusMode) {
const timestampSeconds = e.detail.timestamp / 1000;
const lyricsData = manager.lyricsCache.get(track.id);
if (lyricsData && lyricsData.subtitles) {
const parsed = manager.parseSyncedLyrics(lyricsData.subtitles);
const line = parsed.find(l => Math.abs(l.time - timestampSeconds) < 1.0);
const line = parsed.find((l) => Math.abs(l.time - timestampSeconds) < 1.0);
if (line && line.text && manager.currentGeniusData) {
const annotations = manager.geniusManager.findAnnotations(line.text, manager.currentGeniusData.referents);
const annotations = manager.geniusManager.findAnnotations(
line.text,
manager.currentGeniusData.referents
);
showGeniusAnnotations(annotations, line.text);
}
}
@ -1003,7 +1026,6 @@ function setupSync(track, audioPlayer, amLyrics, lyricsManager) {
}
function showGeniusAnnotations(annotations, lineText) {
const existing = document.querySelector('.genius-annotation-modal');
if (existing) existing.remove();
@ -1026,7 +1048,7 @@ function showGeniusAnnotations(annotations, lineText) {
</div>
`;
} else {
annotations.forEach(ann => {
annotations.forEach((ann) => {
const body = ann.annotations[0].body.plain;
contentHtml += `
<div class="annotation-item">
@ -1043,7 +1065,9 @@ function showGeniusAnnotations(annotations, lineText) {
modal.querySelector('.close-genius').addEventListener('click', () => modal.remove());
modal.addEventListener('click', (e) => { if(e.target === modal) modal.remove(); });
modal.addEventListener('click', (e) => {
if (e.target === modal) modal.remove();
});
}
export async function renderLyricsInFullscreen(track, audioPlayer, lyricsManager, container) {

View file

@ -22,13 +22,14 @@ export async function addMetadataToAudio(audioBlob, track, api, quality) {
const buffer = await audioBlob.slice(0, 4).arrayBuffer();
const view = new DataView(buffer);
const isFlac = view.byteLength >= 4 &&
const isFlac =
view.byteLength >= 4 &&
view.getUint8(0) === 0x66 && // f
view.getUint8(1) === 0x4c && // L
view.getUint8(2) === 0x61 && // a
view.getUint8(3) === 0x43; // C
view.getUint8(3) === 0x43; // C
const mime = audioBlob.type;
const mime = audioBlob.type;
if (mime === 'audio/flac') {
return await addFlacMetadata(audioBlob, track, api);
@ -37,7 +38,6 @@ export async function addMetadataToAudio(audioBlob, track, api, quality) {
if (mime === 'audio/mp4') {
return await addM4aMetadata(audioBlob, track, api);
}
}
/**

View file

@ -319,7 +319,10 @@ export class Player {
}
streamUrl = track.audioUrl;
if ((!streamUrl || (typeof streamUrl === 'string' && streamUrl.startsWith('blob:'))) && track.remoteUrl) {
if (
(!streamUrl || (typeof streamUrl === 'string' && streamUrl.startsWith('blob:'))) &&
track.remoteUrl
) {
streamUrl = track.remoteUrl;
}

View file

@ -11,7 +11,6 @@ export function navigate(path) {
export function createRouter(ui) {
const router = async () => {
if (window.location.hash && window.location.hash.length > 1) {
const hash = window.location.hash.substring(1);
if (hash.includes('/')) {

View file

@ -10,14 +10,20 @@ async function loadArtistsData() {
const response = await fetch('./artists.ndjson');
if (!response.ok) throw new Error('Network response was not ok');
const text = await response.text();
artistsData = text.trim().split('\n')
.filter(line => line.trim())
.map(line => {
try { return JSON.parse(line); } catch (e) { return null; }
artistsData = text
.trim()
.split('\n')
.filter((line) => line.trim())
.map((line) => {
try {
return JSON.parse(line);
} catch (e) {
return null;
}
})
.filter(item => item !== null);
.filter((item) => item !== null);
} catch (e) {
console.error("Failed to load Artists LIst:", e);
console.error('Failed to load Artists LIst:', e);
}
}
@ -29,11 +35,13 @@ function getSheetId(url) {
async function fetchTrackerData(sheetId) {
try {
const response = await fetch(`https://corsproxy.io/?${encodeURIComponent(`https://tracker.israeli.ovh/get/${sheetId}`)}`);
const response = await fetch(
`https://corsproxy.io/?${encodeURIComponent(`https://tracker.israeli.ovh/get/${sheetId}`)}`
);
if (!response.ok) return null;
return await response.json();
} catch (e) {
console.error("Failed to fetch tracker data", e);
console.error('Failed to fetch tracker data', e);
return null;
}
}
@ -110,7 +118,7 @@ function renderTracker(trackerData, container, artistName) {
if (!trackerData.eras) return;
Object.values(trackerData.eras).forEach(era => {
Object.values(trackerData.eras).forEach((era) => {
const card = document.createElement('div');
card.className = 'card';
card.style.cursor = 'pointer';
@ -154,7 +162,6 @@ function showEraSongs(era, artistName) {
const overlay = modal.querySelector('.modal-overlay');
const closeBtn = document.getElementById('close-tracker-modal');
const img = document.getElementById('tracker-header-image');
const title = document.getElementById('tracker-header-title');
const meta = document.getElementById('tracker-header-meta');
@ -178,7 +185,7 @@ function showEraSongs(era, artistName) {
{ label: 'Special', emoji: '✨' },
{ label: 'Grails', emoji: '🏆' },
{ label: 'Wanted', emoji: '🥇' },
{ label: 'Worst Of', emoji: '🗑️' }
{ label: 'Worst Of', emoji: '🗑️' },
];
let activeFilter = '';
@ -186,7 +193,7 @@ function showEraSongs(era, artistName) {
const applyFilter = () => {
const items = trackList.querySelectorAll('.track-item');
items.forEach(item => {
items.forEach((item) => {
const titleEl = item.querySelector('.title');
if (titleEl) {
const title = titleEl.textContent.trim();
@ -199,11 +206,11 @@ function showEraSongs(era, artistName) {
});
const categories = trackList.querySelectorAll('h4');
categories.forEach(cat => {
categories.forEach((cat) => {
let next = cat.nextElementSibling;
let hasVisibleItems = false;
while(next && next.tagName !== 'H4') {
while (next && next.tagName !== 'H4') {
if (next.classList.contains('track-item') && next.style.display !== 'none') {
hasVisibleItems = true;
break;
@ -215,7 +222,7 @@ function showEraSongs(era, artistName) {
});
};
filters.forEach(filter => {
filters.forEach((filter) => {
const btn = document.createElement('button');
btn.className = 'btn-secondary';
btn.textContent = filter.emoji ? `${filter.emoji} ${filter.label}` : filter.label;
@ -224,12 +231,12 @@ function showEraSongs(era, artistName) {
btn.style.borderRadius = '2rem';
if (filter.emoji === '') {
btn.style.backgroundColor = 'var(--primary)';
btn.style.color = 'var(--primary-foreground)';
btn.style.backgroundColor = 'var(--primary)';
btn.style.color = 'var(--primary-foreground)';
}
btn.onclick = () => {
Array.from(filterContainer.children).forEach(b => {
Array.from(filterContainer.children).forEach((b) => {
b.style.backgroundColor = '';
b.style.color = '';
});
@ -260,7 +267,7 @@ function showEraSongs(era, artistName) {
const isValidUrl = (u) => u && typeof u === 'string' && u.trim().length > 0;
songs.forEach(song => {
songs.forEach((song) => {
const trackItem = document.createElement('div');
trackItem.className = 'track-item';
@ -287,27 +294,40 @@ function showEraSongs(era, artistName) {
e.preventDefault();
const contextMenu = document.getElementById('context-menu');
if (contextMenu) {
const rawUrl = (isValidUrl(song.url) ? song.url : null) || (song.urls ? song.urls.find(isValidUrl) : null);
const rawUrl =
(isValidUrl(song.url) ? song.url : null) || (song.urls ? song.urls.find(isValidUrl) : null);
const directUrl = getDirectUrl(rawUrl);
const track = {
id: `tracker-${song.name}`,
title: song.name,
artist: { name: artistName || document.getElementById('artist-detail-name')?.textContent || 'Unknown Artist' },
artists: [{ name: artistName || document.getElementById('artist-detail-name')?.textContent || 'Unknown Artist' }],
artist: {
name:
artistName ||
document.getElementById('artist-detail-name')?.textContent ||
'Unknown Artist',
},
artists: [
{
name:
artistName ||
document.getElementById('artist-detail-name')?.textContent ||
'Unknown Artist',
},
],
album: {
title: era.name,
cover: era.image
cover: era.image,
},
duration: parseDuration(song.track_length),
isTracker: true,
audioUrl: directUrl,
remoteUrl: directUrl
remoteUrl: directUrl,
};
contextMenu._contextTrack = track;
['go-to-album', 'go-to-artist', 'toggle-like', 'download', 'track-mix'].forEach(action => {
['go-to-album', 'go-to-artist', 'toggle-like', 'download', 'track-mix'].forEach((action) => {
const item = contextMenu.querySelector(`[data-action="${action}"]`);
if (item) item.style.display = 'none';
});
@ -326,7 +346,7 @@ function showEraSongs(era, artistName) {
if (hasValidUrl) {
trackItem.onclick = async () => {
if (song.track_length === '-') {
const targetUrl = (song.urls && song.urls.length > 0) ? song.urls[0] : song.url;
const targetUrl = song.urls && song.urls.length > 0 ? song.urls[0] : song.url;
if (targetUrl) window.open(targetUrl, '_blank');
return;
}
@ -337,7 +357,8 @@ function showEraSongs(era, artistName) {
trackItem.classList.add('loading');
const trackNumEl = trackItem.querySelector('.track-number');
const originalNum = trackNumEl.textContent;
trackNumEl.innerHTML = '<svg class="animate-spin" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"></circle></svg>';
trackNumEl.innerHTML =
'<svg class="animate-spin" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"></circle></svg>';
let urlsToTry = [];
if (isValidUrl(song.url)) {
@ -373,9 +394,11 @@ function showEraSongs(era, artistName) {
if (response.ok) {
const contentType = response.headers.get('content-type') || '';
if (contentType.includes('audio/') ||
if (
contentType.includes('audio/') ||
contentType.includes('mpeg') ||
contentType.includes('octet-stream')) {
contentType.includes('octet-stream')
) {
const arrayBuffer = await response.arrayBuffer();
if (arrayBuffer.byteLength > 1000) {
const blob = new Blob([arrayBuffer], { type: 'audio/mpeg' });
@ -396,7 +419,9 @@ function showEraSongs(era, artistName) {
trackNumEl.textContent = originalNum;
if (!audioUrl) {
alert(`Unable to load this track! :( The source may be unavailable.\n\nTried ${urlsToTry.length} URL(s)`);
alert(
`Unable to load this track! :( The source may be unavailable.\n\nTried ${urlsToTry.length} URL(s)`
);
return;
}
@ -404,16 +429,31 @@ function showEraSongs(era, artistName) {
const track = {
id: `tracker-${song.name}`,
title: song.name,
artist: { name: artistName || document.getElementById('artist-detail-name')?.textContent || 'Unknown Artist' },
artists: [{ name: artistName || document.getElementById('artist-detail-name')?.textContent || 'Unknown Artist' }],
artist: {
name:
artistName ||
document.getElementById('artist-detail-name')?.textContent ||
'Unknown Artist',
},
artists: [
{
name:
artistName ||
document.getElementById('artist-detail-name')?.textContent ||
'Unknown Artist',
},
],
album: {
title: era.name,
cover: era.image
cover: era.image,
},
duration: parseDuration(song.track_length),
isTracker: true,
audioUrl: audioUrl,
remoteUrl: successfulUrl || (urlsToTry.length > 0 ? getDirectUrl(urlsToTry[0]) : null) || getDirectUrl(song.url)
remoteUrl:
successfulUrl ||
(urlsToTry.length > 0 ? getDirectUrl(urlsToTry[0]) : null) ||
getDirectUrl(song.url),
};
globalPlayer.setQueue([track], 0);
@ -465,7 +505,7 @@ export async function initTracker(player, ui) {
trackerSection.innerHTML = '';
trackerSection.style.display = 'none';
const artistEntry = artistsData.find(a => a.name.toLowerCase() === artistName.toLowerCase());
const artistEntry = artistsData.find((a) => a.name.toLowerCase() === artistName.toLowerCase());
if (artistEntry && artistEntry.url) {
const sheetId = getSheetId(artistEntry.url);

View file

@ -824,9 +824,11 @@ export class UIRenderer {
lastPausedState = isPaused;
if (isPaused) {
playBtn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="5 3 19 12 5 21 5 3"></polygon></svg>';
playBtn.innerHTML =
'<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="5 3 19 12 5 21 5 3"></polygon></svg>';
} else {
playBtn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="6" y="4" width="4" height="16"></rect><rect x="14" y="4" width="4" height="16"></rect></svg>';
playBtn.innerHTML =
'<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="6" y="4" width="4" height="16"></rect><rect x="14" y="4" width="4" height="16"></rect></svg>';
}
};
@ -849,9 +851,11 @@ export class UIRenderer {
const mode = this.player.toggleRepeat();
repeatBtn.classList.toggle('active', mode !== 0);
if (mode === 2) {
repeatBtn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m17 2 4 4-4 4"/><path d="M3 11v-1a4 4 0 0 1 4-4h14"/><path d="m7 22-4-4 4-4"/><path d="M21 13v1a4 4 0 0 1-4 4H3"/><path d="M11 10h1v4"/></svg>';
repeatBtn.innerHTML =
'<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m17 2 4 4-4 4"/><path d="M3 11v-1a4 4 0 0 1 4-4h14"/><path d="m7 22-4-4 4-4"/><path d="M21 13v1a4 4 0 0 1-4 4H3"/><path d="M11 10h1v4"/></svg>';
} else {
repeatBtn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m17 2 4 4-4 4"/><path d="M3 11v-1a4 4 0 0 1 4-4h14"/><path d="m7 22-4-4 4-4"/><path d="M21 13v1a4 4 0 0 1-4 4H3"/></svg>';
repeatBtn.innerHTML =
'<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m17 2 4 4-4 4"/><path d="M3 11v-1a4 4 0 0 1 4-4h14"/><path d="m7 22-4-4 4-4"/><path d="M21 13v1a4 4 0 0 1-4 4H3"/></svg>';
}
};
@ -883,7 +887,8 @@ export class UIRenderer {
const mode = this.player.repeatMode;
repeatBtn.classList.toggle('active', mode !== 0);
if (mode === 2) {
repeatBtn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m17 2 4 4-4 4"/><path d="M3 11v-1a4 4 0 0 1 4-4h14"/><path d="m7 22-4-4 4-4"/><path d="M21 13v1a4 4 0 0 1-4 4H3"/><path d="M11 10h1v4"/></svg>';
repeatBtn.innerHTML =
'<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m17 2 4 4-4 4"/><path d="M3 11v-1a4 4 0 0 1 4-4h14"/><path d="m7 22-4-4 4-4"/><path d="M21 13v1a4 4 0 0 1-4 4H3"/><path d="M11 10h1v4"/></svg>';
}
const update = () => {
@ -913,7 +918,10 @@ export class UIRenderer {
});
document.querySelectorAll('.sidebar-nav a').forEach((link) => {
link.classList.toggle('active', link.pathname === `/${pageId}` || (pageId === 'home' && link.pathname === '/'));
link.classList.toggle(
'active',
link.pathname === `/${pageId}` || (pageId === 'home' && link.pathname === '/')
);
});
document.querySelector('.main-content').scrollTop = 0;
@ -1015,7 +1023,7 @@ export class UIRenderer {
if (tracks.length > 1) {
albumSection.style.display = 'block';
const otherTracks = tracks.filter(t => t.id !== track.id);
const otherTracks = tracks.filter((t) => t.id !== track.id);
this.renderListWithTracks(albumTracksContainer, otherTracks, false);
} else {
albumSection.style.display = 'none';
@ -1121,7 +1129,7 @@ export class UIRenderer {
const folders = await db.getFolders();
if (foldersContainer) {
foldersContainer.innerHTML = folders.map(f => this.createFolderCardHTML(f)).join('');
foldersContainer.innerHTML = folders.map((f) => this.createFolderCardHTML(f)).join('');
foldersContainer.style.display = folders.length ? 'grid' : 'none';
}
@ -1990,7 +1998,9 @@ export class UIRenderer {
} else if (sortType === 'added-oldest') {
currentTracks = [...originalTracks].sort((a, b) => (a.addedAt || 0) - (b.addedAt || 0));
} else if (sortType === 'title') {
currentTracks = [...originalTracks].sort((a, b) => (a.title || '').localeCompare(b.title || ''));
currentTracks = [...originalTracks].sort((a, b) =>
(a.title || '').localeCompare(b.title || '')
);
} else if (sortType === 'artist') {
currentTracks = [...originalTracks].sort((a, b) => {
const artistA = a.artist?.name || a.artists?.[0]?.name || '';
@ -2015,7 +2025,13 @@ export class UIRenderer {
}
// Render Actions (Shuffle, Edit, Delete, Share, Sort)
this.updatePlaylistHeaderActions(playlistData, !!ownedPlaylist, tracks, false, !!ownedPlaylist ? applySort : null);
this.updatePlaylistHeaderActions(
playlistData,
!!ownedPlaylist,
tracks,
false,
ownedPlaylist ? applySort : null
);
playBtn.onclick = () => {
this.player.setQueue(currentTracks, 0);
@ -2137,7 +2153,9 @@ export class UIRenderer {
if (!folder) throw new Error('Folder not found');
imageEl.src = folder.cover || '/assets/folder.png';
imageEl.onerror = () => { imageEl.src = '/assets/folder.png'; };
imageEl.onerror = () => {
imageEl.src = '/assets/folder.png';
};
imageEl.style.backgroundColor = '';
titleEl.textContent = folder.name;
@ -2467,7 +2485,13 @@ export class UIRenderer {
const actionsDiv = document.getElementById('page-playlist').querySelector('.detail-header-actions');
// Cleanup existing dynamic buttons
['shuffle-playlist-btn', 'edit-playlist-btn', 'delete-playlist-btn', 'share-playlist-btn', 'sort-playlist-btn'].forEach((id) => {
[
'shuffle-playlist-btn',
'edit-playlist-btn',
'delete-playlist-btn',
'share-playlist-btn',
'sort-playlist-btn',
].forEach((id) => {
const btn = actionsDiv.querySelector(`#${id}`);
if (btn) btn.remove();
});
@ -2776,7 +2800,8 @@ export class UIRenderer {
document.body.classList.add('sidebar-collapsed');
const toggleBtn = document.getElementById('sidebar-toggle');
if (toggleBtn) {
toggleBtn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>';
toggleBtn.innerHTML =
'<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>';
}
const imageEl = document.getElementById('track-detail-image');
@ -2893,7 +2918,7 @@ export class UIRenderer {
const tracks = albumData.tracks;
if (tracks.length > 1) {
albumSection.style.display = 'block';
const otherTracks = tracks.filter(t => t.id != track.id);
const otherTracks = tracks.filter((t) => t.id != track.id);
this.renderListWithTracks(albumTracksContainer, otherTracks, false, false, true);
}
} catch (err) {
@ -2901,14 +2926,17 @@ export class UIRenderer {
}
}
this.api.getRecommendedTracksForPlaylist([track], 5).then(similarTracks => {
if (similarTracks.length > 0) {
this.renderListWithTracks(similarTracksContainer, similarTracks, true);
similarSection.style.display = 'block';
} else {
similarSection.style.display = 'none';
}
}).catch(() => similarSection.style.display = 'none');
this.api
.getRecommendedTracksForPlaylist([track], 5)
.then((similarTracks) => {
if (similarTracks.length > 0) {
this.renderListWithTracks(similarTracksContainer, similarTracks, true);
similarSection.style.display = 'block';
} else {
similarSection.style.display = 'none';
}
})
.catch(() => (similarSection.style.display = 'none'));
document.title = `${displayTitle} - ${artistName}`;
} catch (e) {

View file

@ -285,10 +285,14 @@ function resizeImageBlob(blob, size) {
ctx.imageSmoothingEnabled = true;
ctx.imageSmoothingQuality = 'high';
ctx.drawImage(img, 0, 0, size, size);
canvas.toBlob((resizedBlob) => {
if (resizedBlob) resolve(resizedBlob);
else reject(new Error('Canvas toBlob failed'));
}, blob.type || 'image/jpeg', 0.9);
canvas.toBlob(
(resizedBlob) => {
if (resizedBlob) resolve(resizedBlob);
else reject(new Error('Canvas toBlob failed'));
},
blob.type || 'image/jpeg',
0.9
);
};
img.onerror = (e) => {
URL.revokeObjectURL(url);
@ -320,7 +324,7 @@ export async function getCoverBlob(api, coverId) {
const supportedSizes = [80, 160, 320, 640, 1280];
let fetchSize = 1280;
const bestSize = supportedSizes.find(s => s >= requestedSize);
const bestSize = supportedSizes.find((s) => s >= requestedSize);
if (bestSize) {
fetchSize = bestSize;
}

View file

@ -33,7 +33,7 @@ export class Visualizer {
this.source.connect(this.analyser);
this.analyser.connect(this.audioContext.destination);
} catch (e) {
console.warn("Visualizer init failed (likely CORS or already connected):", e);
console.warn('Visualizer init failed (likely CORS or already connected):', e);
}
}
@ -94,14 +94,11 @@ export class Visualizer {
for (let i = 0; i < 4; i++) bassSum += dataArray[i];
const bass = bassSum / 4 / 255;
const intensity = bass * bass;
this.energyAverage = this.energyAverage * 0.99 + intensity * 0.01;
this.upbeatSmoother = this.upbeatSmoother * 0.92 + intensity * 0.08;
if (visualizerSettings.isSmartIntensityEnabled()) {
let target = 0.1;
if (this.energyAverage > 0.4) {
@ -113,18 +110,14 @@ export class Visualizer {
sensitivity = target;
}
let threshold = 0.5;
if (this.energyAverage < 0.3) {
threshold = 0.5 + (0.3 - this.energyAverage) * 2;
}
const now = Date.now();
if (intensity > threshold) {
if (intensity > this.lastIntensity + 0.05 && now - this.lastBeatTime > 50) {
this.kick = 1.0;
this.lastBeatTime = now;
@ -145,7 +138,6 @@ export class Visualizer {
}
this.lastIntensity = intensity;
let shakeX = 0;
let shakeY = 0;
if (this.kick > 0.1) {
@ -154,8 +146,8 @@ export class Visualizer {
shakeY = (Math.random() - 0.5) * shakeAmt;
}
const primaryColor = getComputedStyle(document.documentElement).getPropertyValue('--primary').trim() || '#ffffff';
const primaryColor =
getComputedStyle(document.documentElement).getPropertyValue('--primary').trim() || '#ffffff';
const particleCount = 180;
if (this.particles.length !== particleCount) {
@ -167,7 +159,7 @@ export class Visualizer {
vx: (Math.random() - 0.5) * 2,
vy: (Math.random() - 0.5) * 2,
size: Math.random() * 3 + 1,
baseSize: Math.random() * 3 + 1
baseSize: Math.random() * 3 + 1,
});
}
}
@ -181,8 +173,7 @@ export class Visualizer {
for (let i = 0; i < this.particles.length; i++) {
let p = this.particles[i];
const speedMult = 1 + (intensity * 2) + this.kick * 8 * sensitivity;
const speedMult = 1 + intensity * 2 + this.kick * 8 * sensitivity;
p.x += p.vx * speedMult;
p.y += p.vy * speedMult;
@ -196,9 +187,8 @@ export class Visualizer {
if (p.y < 0) p.y = h;
if (p.y > h) p.y = 0;
const size = p.baseSize * (1 + intensity * 0.5 + this.kick * 0.8 * sensitivity);
ctx.globalAlpha = 0.4 + (intensity * 0.2) + this.kick * 0.15 * sensitivity;
ctx.globalAlpha = 0.4 + intensity * 0.2 + this.kick * 0.15 * sensitivity;
ctx.beginPath();
ctx.arc(p.x, p.y, size, 0, Math.PI * 2);
ctx.fill();
@ -207,7 +197,7 @@ export class Visualizer {
const p2 = this.particles[j];
const dx = p.x - p2.x;
const dy = p.y - p2.y;
const distSq = dx*dx + dy*dy;
const distSq = dx * dx + dy * dy;
const maxDist = 150 + intensity * 50 + this.kick * 50 * sensitivity;
const maxDistSq = maxDist * maxDist;

View file

@ -2076,7 +2076,7 @@ input:checked + .slider::before {
.fullscreen-progress-container .progress-bar {
flex: 1;
height: 6px;
background: rgba(255, 255, 255, 0.2);
background: rgb(255, 255, 255, 0.2);
border-radius: 3px;
cursor: pointer;
position: relative;
@ -2110,7 +2110,7 @@ input:checked + .slider::before {
}
.fullscreen-buttons button:hover {
background: rgba(255, 255, 255, 0.1);
background: rgb(255, 255, 255, 0.1);
transform: scale(1.1);
}
@ -3193,7 +3193,7 @@ img[src=''] {
flex-direction: column;
align-items: center;
justify-content: center;
padding: 6rem 2rem 2rem 2rem;
padding: 6rem 2rem 2rem;
transition: flex 0.3s ease;
}
@ -3627,7 +3627,7 @@ img[src=''] {
padding: var(--spacing-lg);
}
.now-playing-bar {
.now-playing-bar {
width: calc(96% - 160px) !important;
left: calc(160px + 2%);
}
@ -4348,7 +4348,6 @@ body:has(#fullscreen-cover-overlay:not([style*='display: none'])) .now-playing-b
text-overflow: ellipsis;
}
/* Genius i love genius brah!! */
.genius-annotation-modal {
position: fixed;
@ -4357,7 +4356,7 @@ body:has(#fullscreen-cover-overlay:not([style*='display: none'])) .now-playing-b
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.6);
background: rgb(0, 0, 0, 0.6);
backdrop-filter: blur(4px);
animation: fade-in 0.2s ease;
}
@ -4424,14 +4423,14 @@ body:has(#fullscreen-cover-overlay:not([style*='display: none'])) .now-playing-b
}
.genius-annotated {
background-color: rgba(255, 255, 100, 0.1);
background-color: rgb(255, 255, 100, 0.1);
cursor: pointer;
border-radius: 4px;
transition: background-color 0.2s;
}
.genius-annotated:hover {
background-color: rgba(255, 255, 100, 0.2);
background-color: rgb(255, 255, 100, 0.2);
}
.genius-multi-start {
@ -4491,7 +4490,7 @@ body.sidebar-collapsed #sidebar-toggle {
#page-track .detail-header-image {
width: 350px;
height: 350px;
box-shadow: 0 30px 60px rgba(0, 0, 0, 0.5);
box-shadow: 0 30px 60px rgb(0, 0, 0, 0.5);
}
#page-track .detail-header-info {
@ -4526,12 +4525,12 @@ body.sidebar-collapsed #sidebar-toggle {
width: 250px;
height: 250px;
}
#page-track .detail-header-info .title {
font-size: 2rem;
}
}
.tracker-modal .track-item.loading {
opacity: 0.7;
pointer-events: none;