js fixes
This commit is contained in:
parent
c3fd191072
commit
a419f38024
4 changed files with 62 additions and 76 deletions
|
|
@ -69,10 +69,7 @@ export class LosslessAPI {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.status === 401) {
|
if (response.status === 401) {
|
||||||
let errorData;
|
let errorData = await response.clone().json();
|
||||||
try {
|
|
||||||
errorData = await response.clone().json();
|
|
||||||
} catch {}
|
|
||||||
|
|
||||||
if (errorData?.subStatus === 11002) {
|
if (errorData?.subStatus === 11002) {
|
||||||
lastError = new Error(errorData?.userMessage || 'Authentication failed');
|
lastError = new Error(errorData?.userMessage || 'Authentication failed');
|
||||||
|
|
|
||||||
|
|
@ -166,12 +166,10 @@ export const apiSettings = {
|
||||||
async getInstances(type = 'api') {
|
async getInstances(type = 'api') {
|
||||||
let instancesObj;
|
let instancesObj;
|
||||||
|
|
||||||
try {
|
const stored = localStorage.getItem(this.STORAGE_KEY);
|
||||||
const stored = localStorage.getItem(this.STORAGE_KEY);
|
if (stored) {
|
||||||
if (stored) {
|
instancesObj = JSON.parse(stored);
|
||||||
instancesObj = JSON.parse(stored);
|
}
|
||||||
}
|
|
||||||
} catch (e) {}
|
|
||||||
|
|
||||||
if (!instancesObj) {
|
if (!instancesObj) {
|
||||||
instancesObj = await this.loadInstancesFromGitHub();
|
instancesObj = await this.loadInstancesFromGitHub();
|
||||||
|
|
|
||||||
7
js/ui.js
7
js/ui.js
|
|
@ -1311,12 +1311,7 @@ export class UIRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render API playlist
|
// Render API playlist
|
||||||
let apiResult;
|
let apiResult = await this.api.getPlaylist(playlistId);
|
||||||
try {
|
|
||||||
apiResult = await this.api.getPlaylist(playlistId);
|
|
||||||
} catch (error) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { playlist, tracks } = apiResult;
|
const { playlist, tracks } = apiResult;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -75,77 +75,73 @@ export function getVibrantColorFromImage(imgElement) {
|
||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
if (!ctx) return null;
|
if (!ctx) return null;
|
||||||
|
|
||||||
try {
|
const maxDimension = 64;
|
||||||
const maxDimension = 64;
|
let w = imgElement.naturalWidth || imgElement.width;
|
||||||
let w = imgElement.naturalWidth || imgElement.width;
|
let h = imgElement.naturalHeight || imgElement.height;
|
||||||
let h = imgElement.naturalHeight || imgElement.height;
|
|
||||||
|
|
||||||
if (w > maxDimension || h > maxDimension) {
|
if (w > maxDimension || h > maxDimension) {
|
||||||
const scale = Math.min(maxDimension / w, maxDimension / h);
|
const scale = Math.min(maxDimension / w, maxDimension / h);
|
||||||
w = Math.floor(w * scale);
|
w = Math.floor(w * scale);
|
||||||
h = Math.floor(h * 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;
|
// If no candidates found with strict criteria, relax criteria
|
||||||
canvas.height = h;
|
if (candidates.length === 0) {
|
||||||
|
|
||||||
// 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) {
|
for (let i = 0; i < pixels.length; i += 4) {
|
||||||
const r = pixels[i];
|
const r = pixels[i];
|
||||||
const g = pixels[i + 1];
|
const g = pixels[i + 1];
|
||||||
const b = pixels[i + 2];
|
const b = pixels[i + 2];
|
||||||
const a = pixels[i + 3];
|
const a = pixels[i + 3];
|
||||||
|
if (a < 125) continue;
|
||||||
if (a < 125) continue; // Skip transparent
|
|
||||||
|
|
||||||
const [h, s, l] = rgbToHsl(r, g, b);
|
const [h, s, l] = rgbToHsl(r, g, b);
|
||||||
|
// Allow anything not practically black or white
|
||||||
// Filter out very dark, very bright, or very desaturated pixels for the "vibrant" candidate list
|
if (l > 0.1 && l < 0.95) {
|
||||||
// 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 });
|
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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue