From 47831812fedc0fd136f11da7dc0c82dee92b1ea5 Mon Sep 17 00:00:00 2001 From: sleepyfran Date: Fri, 25 Oct 2024 20:19:36 +0200 Subject: [PATCH] Dispatch player state as custom events Okay, final thing to support WebScrobbler, I promise. --- packages/components/player/src/player.ts | 40 +++++++++++++++---- .../src/stream-effect.controller.ts | 13 ++++++ 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/packages/components/player/src/player.ts b/packages/components/player/src/player.ts index 5fd6b21..ed505cc 100644 --- a/packages/components/player/src/player.ts +++ b/packages/components/player/src/player.ts @@ -17,7 +17,37 @@ import "@echo/components-icons"; */ @customElement("echo-player") export class EchoPlayer extends LitElement { - private _player = new StreamConsumer(this, PlayerService.observe); + private _player = new StreamConsumer(this, PlayerService.observe, { + item: (playerState) => { + /* + This pattern matches against the player state and dispatches custom events + that can be used to react to changes in the playing/stopped state of the player. + + This is not really meant to be used in the app itself, but mostly to + properly broadcast the player state to external components that might need + it, like WebScrobbler to scrobble tracks. + */ + Match.value(playerState.status).pipe( + Match.tag("Playing", ({ album, trackIndex }) => { + const track = album.tracks[trackIndex]; + this.dispatchEvent( + new CustomEvent("track-playing", { + detail: { + trackName: track.name, + artistName: album.artist.name, + albumName: album.name, + providerId: track.resource.provider, + }, + }), + ); + }), + Match.tag("Paused", "Stopped", () => { + this.dispatchEvent(new CustomEvent("track-paused")); + }), + Match.exhaustive, + ); + }, + }); private _togglePlayback = new EffectFn( this, () => PlayerService.togglePlayback, @@ -96,13 +126,7 @@ export class EchoPlayer extends LitElement { private _renderPlayer(player: PlayerState, album: Album, track: Track) { return html` -
+
${Option.isSome(album.embeddedCover) ? html` implements ReactiveController { private readonly _streamEffect: | OutputEffect | (() => OutputEffect), + private readonly _listeners?: Omit, "initial">, ) { (this.host = host).addController(this); } @@ -116,6 +117,18 @@ export class StreamConsumer implements ReactiveController { private handleUpdate$(state: StreamStatus) { return Effect.sync(() => { + switch (state._tag) { + case "Item": + this._listeners?.item?.(state.item); + break; + case "Complete": + this._listeners?.complete?.(); + break; + case "Error": + this._listeners?.error?.(state.error); + break; + } + this._status = state; this.host.requestUpdate(); });