This commit is contained in:
Julien Maille 2026-01-11 00:58:55 +01:00
parent c3fd191072
commit a419f38024
4 changed files with 62 additions and 76 deletions

View file

@ -69,10 +69,7 @@ export class LosslessAPI {
}
if (response.status === 401) {
let errorData;
try {
errorData = await response.clone().json();
} catch {}
let errorData = await response.clone().json();
if (errorData?.subStatus === 11002) {
lastError = new Error(errorData?.userMessage || 'Authentication failed');

View file

@ -166,12 +166,10 @@ export const apiSettings = {
async getInstances(type = 'api') {
let instancesObj;
try {
const stored = localStorage.getItem(this.STORAGE_KEY);
if (stored) {
instancesObj = JSON.parse(stored);
}
} catch (e) {}
const stored = localStorage.getItem(this.STORAGE_KEY);
if (stored) {
instancesObj = JSON.parse(stored);
}
if (!instancesObj) {
instancesObj = await this.loadInstancesFromGitHub();

View file

@ -1311,12 +1311,7 @@ export class UIRenderer {
}
// Render API playlist
let apiResult;
try {
apiResult = await this.api.getPlaylist(playlistId);
} catch (error) {
throw error;
}
let apiResult = await this.api.getPlaylist(playlistId);
const { playlist, tracks } = apiResult;

View file

@ -75,77 +75,73 @@ export function getVibrantColorFromImage(imgElement) {
const ctx = canvas.getContext('2d');
if (!ctx) return null;
try {
const maxDimension = 64;
let w = imgElement.naturalWidth || imgElement.width;
let h = imgElement.naturalHeight || imgElement.height;
const maxDimension = 64;
let w = imgElement.naturalWidth || imgElement.width;
let h = imgElement.naturalHeight || imgElement.height;
if (w > maxDimension || h > maxDimension) {
const scale = Math.min(maxDimension / w, maxDimension / h);
w = Math.floor(w * scale);
h = Math.floor(h * scale);
if (w > maxDimension || h > maxDimension) {
const scale = Math.min(maxDimension / w, maxDimension / h);
w = Math.floor(w * scale);
h = Math.floor(h * scale);
}
canvas.width = w;
canvas.height = h;
// Draw image directly at small size
// Note: For best quality downscaling, one might step down, but for color extraction,
// direct browser downscaling is sufficient and much faster/lighter.
ctx.drawImage(imgElement, 0, 0, w, h);
const imageData = ctx.getImageData(0, 0, w, h);
const pixels = imageData.data;
const candidates = [];
// Iterate through pixels
for (let i = 0; i < pixels.length; i += 4) {
const r = pixels[i];
const g = pixels[i + 1];
const b = pixels[i + 2];
const a = pixels[i + 3];
if (a < 125) continue; // Skip transparent
const [h, s, l] = rgbToHsl(r, g, b);
// Filter out very dark, very bright, or very desaturated pixels for the "vibrant" candidate list
// Vibrant: High saturation (s > 0.3), Moderate lightness (0.3 < l < 0.8)
if (s >= 0.3 && l >= 0.3 && l <= 0.8) {
candidates.push({ r, g, b, h, s, l });
}
}
canvas.width = w;
canvas.height = h;
// Draw image directly at small size
// Note: For best quality downscaling, one might step down, but for color extraction,
// direct browser downscaling is sufficient and much faster/lighter.
ctx.drawImage(imgElement, 0, 0, w, h);
const imageData = ctx.getImageData(0, 0, w, h);
const pixels = imageData.data;
const candidates = [];
// Iterate through pixels
// If no candidates found with strict criteria, relax criteria
if (candidates.length === 0) {
for (let i = 0; i < pixels.length; i += 4) {
const r = pixels[i];
const g = pixels[i + 1];
const b = pixels[i + 2];
const a = pixels[i + 3];
if (a < 125) continue; // Skip transparent
if (a < 125) continue;
const [h, s, l] = rgbToHsl(r, g, b);
// Filter out very dark, very bright, or very desaturated pixels for the "vibrant" candidate list
// Vibrant: High saturation (s > 0.3), Moderate lightness (0.3 < l < 0.8)
if (s >= 0.3 && l >= 0.3 && l <= 0.8) {
// Allow anything not practically black or white
if (l > 0.1 && l < 0.95) {
candidates.push({ r, g, b, h, s, l });
}
}
// If no candidates found with strict criteria, relax criteria
if (candidates.length === 0) {
for (let i = 0; i < pixels.length; i += 4) {
const r = pixels[i];
const g = pixels[i + 1];
const b = pixels[i + 2];
const a = pixels[i + 3];
if (a < 125) continue;
const [h, s, l] = rgbToHsl(r, g, b);
// Allow anything not practically black or white
if (l > 0.1 && l < 0.95) {
candidates.push({ r, g, b, h, s, l });
}
}
}
// If still no candidates, return null (caller will handle fallback to default)
if (candidates.length === 0) return null;
// Sort by saturation (descending) then lightness (proximity to 0.5)
candidates.sort((c1, c2) => {
return c2.s - c1.s || 0.5 - Math.abs(c1.l - 0.5) - (0.5 - Math.abs(c2.l - 0.5));
});
// Pick the top candidate (most vibrant)
// Optionally averaging top N could be done, but simplified "best single pixel" is usually sufficient for "Vibrant"
const best = candidates[0];
return hslToHex(best.h, best.s, best.l);
} catch (e) {
throw e; // Re-throw to allow UI to handle CORS retry
}
// If still no candidates, return null (caller will handle fallback to default)
if (candidates.length === 0) return null;
// Sort by saturation (descending) then lightness (proximity to 0.5)
candidates.sort((c1, c2) => {
return c2.s - c1.s || 0.5 - Math.abs(c1.l - 0.5) - (0.5 - Math.abs(c2.l - 0.5));
});
// Pick the top candidate (most vibrant)
// Optionally averaging top N could be done, but simplified "best single pixel" is usually sufficient for "Vibrant"
const best = candidates[0];
return hslToHex(best.h, best.s, best.l);
}