From 81cceb1d3f7998fcca140d27d8b504236ea3574c Mon Sep 17 00:00:00 2001 From: Steven Vandevelde Date: Sat, 5 Jul 2025 22:00:17 +0200 Subject: [PATCH] feat: various artwork improvements --- .../blur/artwork-controller/_applet.astro | 20 ++----- .../orchestrator/queue-audio/_applet.astro | 12 ++++- src/scripts/processor/artwork/types.d.ts | 1 + src/scripts/processor/artwork/worker.ts | 54 ++++++++++++++++--- 4 files changed, 61 insertions(+), 26 deletions(-) diff --git a/src/pages/constituent/blur/artwork-controller/_applet.astro b/src/pages/constituent/blur/artwork-controller/_applet.astro index 617e1dc..74588ab 100644 --- a/src/pages/constituent/blur/artwork-controller/_applet.astro +++ b/src/pages/constituent/blur/artwork-controller/_applet.astro @@ -63,7 +63,6 @@ import "@styles/diffuse/fonts.css"; left: 0; object-fit: cover; opacity: 0; - pointer-events: none; position: absolute; top: 0; transition-duration: var(--transition-durition); @@ -150,7 +149,6 @@ import "@styles/diffuse/fonts.css"; cite { display: block; font-style: normal; - line-height: var(--leading-snug); text-shadow: var(--text-shadow-sm); } @@ -482,23 +480,14 @@ import "@styles/diffuse/fonts.css"; reactive( engine.queue, - (data) => comparable(data.future), - () => { - const track = engine.queue.data.now; - - if (!track) { - setActiveTrack(undefined); - return; - } - - setActiveTrack(track); - }, + (data) => data.now, + (track) => setActiveTrack(track || undefined), ); // Changed artwork based on active queue item. // (debounced) - reactive(engine.queue, (data) => comparable(data.future), debounce(2000, changeArtwork)); + reactive(engine.queue, (data) => data.now, debounce(2000, changeArtwork)); async function changeArtwork() { const track = engine.queue.data.now; @@ -595,7 +584,6 @@ import "@styles/diffuse/fonts.css"; const color = fac.getColor(img as HTMLImageElement); const rgb = color.value; const o = Math.round((rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000); - console.log(o); setArtworkColor(color.rgba); setArtworkLightMode(o > 165); @@ -657,7 +645,7 @@ import "@styles/diffuse/fonts.css"; computed( () => activeTrack()?.tags?.artist || - (isMainGroup() && !activeTrack() ? "Waiting for tracks to be queued ..." : ""), + (isMainGroup() && !activeTrack() ? "Waiting on queue ..." : ""), ), ), ), diff --git a/src/pages/orchestrator/queue-audio/_applet.astro b/src/pages/orchestrator/queue-audio/_applet.astro index 3521ad8..726fe53 100644 --- a/src/pages/orchestrator/queue-audio/_applet.astro +++ b/src/pages/orchestrator/queue-audio/_applet.astro @@ -60,6 +60,14 @@ const activeTrack = engine.queue.data.now; const isPlaying = engine.audio.data.isPlaying; + // Resolve URIs + const url = activeTrack + ? await inputUrl(configurator.input, activeTrack.uri).then((a) => a?.url) + : undefined; + + // Check if we still need to render + if (engine.queue.data.now?.id !== activeTrack?.id) return; + // Play new active queue item // TODO: Take URL expiration timestamp into account // TODO: Preload next queue item @@ -71,10 +79,10 @@ { id: activeTrack.id, isPreload: false, - url: await inputUrl(configurator.input, activeTrack.uri).then((a) => a?.url), + url, }, ] - : // TODO: This probably isn't correct, keep preloads? + : // TODO: Keep preloads [], play: activeTrack && isPlaying ? { audioId: activeTrack.id } : undefined, }, diff --git a/src/scripts/processor/artwork/types.d.ts b/src/scripts/processor/artwork/types.d.ts index fc6eaef..a8d316f 100644 --- a/src/scripts/processor/artwork/types.d.ts +++ b/src/scripts/processor/artwork/types.d.ts @@ -11,6 +11,7 @@ export type ArtworkRequest = { stream?: ReadableStream; tags?: Tags; urls?: Urls; + variousArtists?: boolean; }; // export type State = { diff --git a/src/scripts/processor/artwork/worker.ts b/src/scripts/processor/artwork/worker.ts index 86cfa02..5576bf4 100644 --- a/src/scripts/processor/artwork/worker.ts +++ b/src/scripts/processor/artwork/worker.ts @@ -35,6 +35,36 @@ function supply(items: ArtworkRequest[]) { //////////////////////////////////////////// // 🛠️ //////////////////////////////////////////// +function escapeLucene(str: string) { + return [].map + .call(str, (char) => { + if ( + char === "+" || + char === "-" || + char === "&" || + char === "|" || + char === "!" || + char === "(" || + char === ")" || + char === "{" || + char === "}" || + char === "[" || + char === "]" || + char === "^" || + char === '"' || + char === "~" || + char === "*" || + char === "?" || + char === ":" || + char === "\\" || + char === "/" + ) + return "\\" + char; + else return char; + }) + .join(""); +} + async function lastFm(req: ArtworkRequest): Promise { if (!navigator.onLine) return []; @@ -77,37 +107,45 @@ async function musicBrainz(req: ArtworkRequest): Promise { if (!navigator.onLine) return []; if (!album && !artist) return []; - // TODO - const variousArtists = false; - - const query = `release:"${album}"` + (variousArtists ? `` : ` AND artist:"${artist}"`); + const query = + `release:"${escapeLucene(album || "")}"` + + (req.variousArtists ? `` : ` AND artistname:"${escapeLucene(artist || "")}"`); const encodedQuery = encodeURIComponent(query); return await fetch(`https://musicbrainz.org/ws/2/release/?query=${encodedQuery}&fmt=json`) .then((r) => r.json()) - .then((r) => musicBrainzCover(r.releases)) + .then((r) => { + if (r.releases.length === 0 && !req.variousArtists) { + return musicBrainz({ ...req, variousArtists: true }); + } else { + return musicBrainzCover(r.releases, req); + } + }) .catch((err) => { console.error(err); return []; }); } -async function musicBrainzCover(remainingReleases: any[]): Promise { +async function musicBrainzCover(remainingReleases: any[], req: ArtworkRequest): Promise { const release = remainingReleases[0]; if (!release) return []; + const credit = release?.["artist-credit"]?.[0]?.name; + if (req.variousArtists && credit !== "Various Artists" && credit !== req.tags?.artist) return []; + return await fetch(`https://coverartarchive.org/release/${release.id}/front-500`) .then((r) => r.blob()) .then(async (b) => { if (b.type.startsWith("image/")) { return [{ bytes: await b.arrayBuffer().then((buf) => new Uint8Array(buf)), mime: b.type }]; } else { - return musicBrainzCover(remainingReleases.slice(1)); + return musicBrainzCover(remainingReleases.slice(1), req); } }) .catch((err) => { console.error(err); - return musicBrainzCover(remainingReleases.slice(1)); + return musicBrainzCover(remainingReleases.slice(1), req); }); } -- 2.51.2