Merge pull request #22 from JulienMaille/fixes

Fixes
This commit is contained in:
Julien 2025-12-26 12:29:59 +01:00 committed by GitHub
commit f3a0e40a1a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 184 additions and 139 deletions

View file

@ -491,9 +491,6 @@
</div>
</footer>
</div>
<script defer data-domain="monochrome.samidy.com" src="https://plausible.canine.tools/js/script.file-downloads.hash.outbound-links.pageview-props.revenue.tagged-events.js"></script>
<script>window.plausible = window.plausible || function() { (window.plausible.q = window.plausible.q || []).push(arguments) }</script>
<script type="module" src="js/app.js"></script>
</body>
</html>

View file

@ -175,12 +175,12 @@ function hideOfflineNotification() {
document.addEventListener('DOMContentLoaded', async () => {
const api = new LosslessAPI(apiSettings);
const ui = new UIRenderer(api);
const audioPlayer = document.getElementById('audio-player');
const currentQuality = localStorage.getItem('playback-quality') || 'LOSSLESS';
const player = new Player(audioPlayer, api, currentQuality);
const ui = new UIRenderer(api, player);
const scrobbler = new LastFMScrobbler();
const lyricsManager = new LyricsManager(api);
const lyricsPanel = createLyricsPanel();

View file

@ -244,10 +244,10 @@ export async function downloadAlbumAsZip(album, tracks, api, quality, lyricsMana
updateBulkDownloadProgress(notification, i, tracks.length, trackTitle);
try {
const blob = await downloadTrackBlob(track, quality, api);
zip.file(`${folderName}/${filename}`, blob);
try {
const meta = buildTrackMetadata(track, api);
const metaFilename = filename.replace(/\.[^.]+$/, '.json');
@ -276,6 +276,9 @@ export async function downloadAlbumAsZip(album, tracks, api, quality, lyricsMana
console.log('Could not add lyrics for:', trackTitle);
}
}
} catch (err) {
console.error(`Failed to download track ${trackTitle}:`, err);
}
}
updateBulkDownloadProgress(notification, tracks.length, tracks.length, 'Creating ZIP...');
@ -323,6 +326,7 @@ export async function downloadPlaylistAsZip(playlist, tracks, api, quality, lyri
updateBulkDownloadProgress(notification, i, tracks.length, trackTitle);
try {
const blob = await downloadTrackBlob(track, quality, api);
zip.file(`${folderName}/${filename}`, blob);
@ -354,6 +358,9 @@ export async function downloadPlaylistAsZip(playlist, tracks, api, quality, lyri
console.log('Could not add lyrics for:', trackTitle);
}
}
} catch (err) {
console.error(`Failed to download track ${trackTitle}:`, err);
}
}
updateBulkDownloadProgress(notification, tracks.length, tracks.length, 'Creating ZIP...');
@ -410,6 +417,8 @@ export async function downloadDiscography(artist, api, quality, lyricsManager =
for (const track of tracks) {
const filename = buildTrackFilename(track, quality);
try {
const blob = await downloadTrackBlob(track, quality, api);
zip.file(`${rootFolder}/${albumFolder}/${filename}`, blob);
@ -439,6 +448,9 @@ export async function downloadDiscography(artist, api, quality, lyricsManager =
console.log('Could not add lyrics for:', track.title);
}
}
} catch (err) {
console.error(`Failed to download track ${track.title} in album ${album.title}:`, err);
}
}
} catch (error) {
console.error(`Failed to download album ${album.title}:`, error);

View file

@ -3,8 +3,9 @@ import { SVG_PLAY, SVG_DOWNLOAD, SVG_MENU, formatTime, createPlaceholder, trackD
import { recentActivityManager, backgroundSettings, trackListSettings } from './storage.js';
export class UIRenderer {
constructor(api) {
constructor(api, player) {
this.api = api;
this.player = player;
this.currentTrack = null;
this.searchAbortController = null;
}
@ -72,6 +73,7 @@ export class UIRenderer {
const explicitBadge = hasExplicitContent(track) ? this.createExplicitBadge() : '';
const trackArtists = getTrackArtists(track);
const trackTitle = getTrackTitle(track);
const isCurrentTrack = this.player?.currentTrack?.id === track.id;
let yearDisplay = '';
const releaseDate = track.album?.releaseDate || track.streamStartDate;
@ -112,7 +114,7 @@ export class UIRenderer {
`;
return `
<div class="track-item" data-track-id="${track.id}">
<div class="track-item ${isCurrentTrack ? 'playing' : ''}" data-track-id="${track.id}">
${trackNumberHTML}
<div class="track-item-info">
<div class="track-item-details">
@ -266,24 +268,60 @@ export class UIRenderer {
if (!color) return;
const root = document.documentElement;
const theme = root.getAttribute('data-theme');
const isLightMode = theme === 'light';
// Calculate contrast text color
const hex = color.replace('#', '');
const r = parseInt(hex.substr(0, 2), 16);
const g = parseInt(hex.substr(2, 2), 16);
const b = parseInt(hex.substr(4, 2), 16);
const brightness = ((r * 299) + (g * 587) + (b * 114)) / 1000;
let hex = color.replace('#', '');
// Handle shorthand hex
if (hex.length === 3) {
hex = hex.split('').map(char => char + char).join('');
}
let r = parseInt(hex.substr(0, 2), 16);
let g = parseInt(hex.substr(2, 2), 16);
let b = parseInt(hex.substr(4, 2), 16);
// Calculate perceived brightness
let brightness = ((r * 299) + (g * 587) + (b * 114)) / 1000;
if (isLightMode) {
// In light mode, the background is white.
// We need the color (used for text/highlights) to be dark enough.
// If brightness is too high (> 150), darken it.
while (brightness > 150) {
r = Math.floor(r * 0.9);
g = Math.floor(g * 0.9);
b = Math.floor(b * 0.9);
brightness = ((r * 299) + (g * 587) + (b * 114)) / 1000;
}
} else {
// In dark mode, the background is dark.
// We need the color to be light enough.
// If brightness is too low (< 80), lighten it.
while (brightness < 80) {
r = Math.min(255, Math.floor(r * 1.15));
g = Math.min(255, Math.floor(g * 1.15));
b = Math.min(255, Math.floor(b * 1.15));
brightness = ((r * 299) + (g * 587) + (b * 114)) / 1000;
// Break if we hit white or can't get brighter to avoid infinite loop
if (r >= 255 && g >= 255 && b >= 255) break;
}
}
const adjustedColor = `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`;
// Calculate contrast text color for buttons (text on top of the vibrant color)
const foreground = brightness > 128 ? '#000000' : '#ffffff';
// Set global CSS variables
root.style.setProperty('--primary', color);
root.style.setProperty('--primary', adjustedColor);
root.style.setProperty('--primary-foreground', foreground);
root.style.setProperty('--highlight', color);
root.style.setProperty('--highlight', adjustedColor);
root.style.setProperty('--highlight-rgb', `${r}, ${g}, ${b}`);
root.style.setProperty('--active-highlight', color);
root.style.setProperty('--ring', color);
root.style.setProperty('--active-highlight', adjustedColor);
root.style.setProperty('--ring', adjustedColor);
// Calculate a safe hover color (darken if too light)
// Calculate a safe hover color
let hoverColor;
if (brightness > 200) {
const dr = Math.floor(r * 0.85);
@ -627,7 +665,7 @@ export class UIRenderer {
}
}
async renderPlaylistPage(playlistId) {
async renderPlaylistPage(playlistId) {
this.showPage('playlist');
const imageEl = document.getElementById('playlist-detail-image');
@ -662,13 +700,11 @@ async renderPlaylistPage(playlistId) {
imageEl.style.backgroundColor = '';
titleEl.textContent = playlist.title;
this.adjustTitleFontSize(titleEl, playlist.title);
const totalDuration = calculateTotalDuration(tracks);
metaEl.textContent = `${playlist.numberOfTracks} tracks • ${formatDuration(totalDuration)}`;
descEl.textContent = playlist.description || '';
tracklistContainer.innerHTML = `
@ -680,14 +716,14 @@ async renderPlaylistPage(playlistId) {
`;
this.renderListWithTracks(tracklistContainer, tracks, true);
recentActivityManager.addPlaylist(playlist);
document.title = `${playlist.title || 'Artist Mix'} - Monochrome`; } catch (error) {
document.title = `${playlist.title || 'Artist Mix'} - Monochrome`;
} catch (error) {
console.error("Failed to load playlist:", error);
tracklistContainer.innerHTML = createPlaceholder(`Could not load playlist details. ${error.message}`);
}
}
}
async renderArtistPage(artistId) {
this.showPage('artist');