From a0e4407991f8805acca4434510481a77f712ec44 Mon Sep 17 00:00:00 2001 From: Steven Vandevelde Date: Wed, 20 May 2026 12:22:23 +0200 Subject: [PATCH] chore: media session improvements, enable in blur theme --- src/common/element.js | 27 +++++++----- src/components/engine/audio/element.js | 42 +++++++------------ .../orchestrator/media-session/element.js | 32 ++++++-------- src/facets/themes/blur/facet/index.inline.js | 1 + 4 files changed, 44 insertions(+), 58 deletions(-) diff --git a/src/common/element.js b/src/common/element.js index 33a3fd79..dbe2bf9f 100644 --- a/src/common/element.js +++ b/src/common/element.js @@ -353,6 +353,7 @@ export class BroadcastableDiffuseElement extends DiffuseElement { msg.port2.start(); async function anyoneWaiting() { + if (typeof navigator.locks?.query !== "function") return false; const state = await navigator.locks.query(); return !!state.pending?.length; } @@ -424,16 +425,22 @@ export class BroadcastableDiffuseElement extends DiffuseElement { const assumeLeadership = this.#broadcastingOptions?.assumeLeadership; if (assumeLeadership === undefined || assumeLeadership === true) { - navigator.locks.request( - `${this.channelName}/lock`, - assumeLeadership === true ? { steal: true } : { ifAvailable: true }, - (lock) => { - this.#status.resolve( - lock ? { leader: true, initialLeader: true } : { leader: false }, - ); - if (lock) return this.#lock.promise; - }, - ); + if (typeof navigator.locks?.request === "function") { + navigator.locks.request( + `${this.channelName}/lock`, + assumeLeadership === true ? { steal: true } : { ifAvailable: true }, + (lock) => { + this.#status.resolve( + lock ? { leader: true, initialLeader: true } : { leader: false }, + ); + if (lock) return this.#lock.promise; + }, + ); + } else { + this.#status.resolve( + { leader: true, initialLeader: true }, + ); + } } else { this.#status.resolve( { leader: false }, diff --git a/src/components/engine/audio/element.js b/src/components/engine/audio/element.js index d70bd41b..498c0a12 100644 --- a/src/components/engine/audio/element.js +++ b/src/components/engine/audio/element.js @@ -32,7 +32,6 @@ class AudioEngine extends BroadcastableDiffuseElement { constructor() { super(); - this.isPlaying = this.isPlaying.bind(this); this.state = this.state.bind(this); } @@ -52,6 +51,17 @@ class AudioEngine extends BroadcastableDiffuseElement { items = this.#items.get; volume = this.#volume.get; + isPlaying = computed(() => { + const item = this.items()?.[0]; + if (!item) return false; + + const state = this.state(item.id); + if (!state) return false; + + return state.isPlaying() || state.hasEnded() || + (state.duration() > 0 && state.currentTime() === state.duration()); + }); + // LIFECYCLE /** @@ -522,22 +532,6 @@ class AudioEngine extends BroadcastableDiffuseElement { // 🛠️ - /** - * Convenience signal to track if something is, or was, playing. - */ - _isPlaying() { - return computed(() => { - const item = this.items()?.[0]; - if (!item) return false; - - const state = this.state(item.id); - if (!state) return false; - - return state.isPlaying() || state.hasEnded() || - (state.duration() > 0 && state.currentTime() === state.duration()); - }); - } - /** * Get the state of a single audio item. * @@ -553,13 +547,6 @@ class AudioEngine extends BroadcastableDiffuseElement { }); } - /** - * Convenience signal to track if something is, or was, playing. - */ - isPlaying() { - return this._isPlaying()(); - } - /** * Get the state of a single audio item. * @@ -882,11 +869,10 @@ class AudioEngineItem extends BroadcastableDiffuseElement { const item = engineItem(audio); if (!item || item.hasAttribute("preload")) return; - const progress = - !isNaN(audio.duration) && audio.duration > 0 && + const progress = !isNaN(audio.duration) && audio.duration > 0 && audio.duration !== Infinity - ? audio.currentTime / audio.duration - : 0; + ? audio.currentTime / audio.duration + : 0; if (progress > 0) { item.setAttribute("initial-progress", JSON.stringify(progress)); diff --git a/src/components/orchestrator/media-session/element.js b/src/components/orchestrator/media-session/element.js index b667526a..9c5565aa 100644 --- a/src/components/orchestrator/media-session/element.js +++ b/src/components/orchestrator/media-session/element.js @@ -28,38 +28,27 @@ class MediaSessionOrchestrator extends BroadcastableDiffuseElement { * @override */ async connectedCallback() { - // Broadcast if needed if (this.hasAttribute("group")) { this.broadcast(this.identifier, {}); } - // Super super.connectedCallback(); if (!("mediaSession" in navigator)) return; - /** @type {import("~/components/engine/audio/element.js").CLASS} */ this.audio = query(this, "audio-engine-selector"); - - /** @type {import("~/components/engine/queue/element.js").CLASS} */ this.queue = query(this, "queue-engine-selector"); - - /** @type {OutputElement | null} */ this.output = queryOptional(this, "output-selector"); - - /** @type {ArtworkOrchestrator | null} */ this.artwork = queryOptional(this, "artwork-selector"); - // Wait until defined - await customElements.whenDefined(this.audio.localName); - await customElements.whenDefined(this.queue.localName); - if (this.output) await customElements.whenDefined(this.output.localName); - if (this.artwork) await customElements.whenDefined(this.artwork.localName); + await Promise.all([ + customElements.whenDefined(this.audio.localName), + customElements.whenDefined(this.queue.localName), + this.output && customElements.whenDefined(this.output.localName), + this.artwork && customElements.whenDefined(this.artwork.localName), + ].filter(Boolean)); - // Register Media Session action handlers this.#registerActionHandlers(); - - // Effects this.effect(() => this.#syncMetadata()); this.effect(() => this.#syncPlaybackState()); this.effect(() => this.#syncPositionState()); @@ -116,9 +105,12 @@ class MediaSessionOrchestrator extends BroadcastableDiffuseElement { // don't set the artwork. if (nowLater?.id !== now?.id) return; - navigator.mediaSession.metadata.artwork = [ - { src: url, type: mime }, - ]; + navigator.mediaSession.metadata = new MediaMetadata({ + title: tags.title ?? "", + artist: tags.artist ?? tags.albumartist ?? "", + album: tags.album ?? "", + artwork: [{ src: url, type: mime }], + }); } } } diff --git a/src/facets/themes/blur/facet/index.inline.js b/src/facets/themes/blur/facet/index.inline.js index 16f3b1d9..383da691 100644 --- a/src/facets/themes/blur/facet/index.inline.js +++ b/src/facets/themes/blur/facet/index.inline.js @@ -25,6 +25,7 @@ await foundation.orchestrator.sources(); await foundation.orchestrator.processTracks({ disableWhenReady: true }); await foundation.orchestrator.queueAudio(); await foundation.orchestrator.controller(); +await foundation.orchestrator.mediaSession(); await foundation.orchestrator.artwork(); await foundation.orchestrator.coverGroups(); await foundation.orchestrator.favourites(); -- 2.51.2