From fe9f49779c8b1d7ded51e4749c3f71ea5ff9d655 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Thu, 19 Oct 2023 13:40:43 -0400 Subject: [PATCH] feat: Implement queued scrobbles display * Show number of queued scrobbles for clients in ui * Implement live updates to queue count --- .../scrobblers/AbstractScrobbleClient.ts | 5 ++++- src/backend/server/api.ts | 5 +++-- .../statusCard/ClientStatusCard.tsx | 6 +++-- src/client/status/ducks.ts | 22 ++++++++++++++++--- src/core/Atomic.ts | 3 ++- 5 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/backend/scrobblers/AbstractScrobbleClient.ts b/src/backend/scrobblers/AbstractScrobbleClient.ts index 717de209..0ddd5eac 100644 --- a/src/backend/scrobblers/AbstractScrobbleClient.ts +++ b/src/backend/scrobblers/AbstractScrobbleClient.ts @@ -553,6 +553,7 @@ ${closestMatch.breakdowns.join('\n')}`); // processing play may have changed index while we were scrobbling const pIndex = this.queuedScrobbles.findIndex(x => x.id === currQueuedPlay.id); if (pIndex !== -1) { + this.emitEvent('scrobbleDequeued', {queuedScrobble: currQueuedPlay}) this.queuedScrobbles.splice(pIndex, 1); } } @@ -668,7 +669,9 @@ ${closestMatch.breakdowns.join('\n')}`); queueScrobble = (data: PlayObject | PlayObject[], source: string) => { const plays = Array.isArray(data) ? data : [data]; for(const p of plays) { - this.queuedScrobbles.push({id: nanoid(), source, play: p}); + const queuedPlay = {id: nanoid(), source, play: p} + this.emitEvent('scrobbleQueued', {queuedPlay: queuedPlay}); + this.queuedScrobbles.push(queuedPlay); } this.queuedScrobbles.sort((a, b) => sortByOldestPlayDate(a.play, b.play)); } diff --git a/src/backend/server/api.ts b/src/backend/server/api.ts index b58905f4..5aa9deba 100644 --- a/src/backend/server/api.ts +++ b/src/backend/server/api.ts @@ -227,13 +227,14 @@ export const setupApi = (app: ExpressWithAsync, logger: Logger, initialLogOutput status: '', type, display: capitalize(type), - tracksDiscovered: tracksScrobbled, + scrobbled: tracksScrobbled, name, hasAuth: requiresAuth, hasAuthInteraction: requiresAuthInteraction, authed, initialized, - deadLetterScrobbles: x.deadLetterScrobbles.length + deadLetterScrobbles: x.deadLetterScrobbles.length, + queued: x.queuedScrobbles.length }; if (!initialized) { base.status = 'Not Initialized'; diff --git a/src/client/components/statusCard/ClientStatusCard.tsx b/src/client/components/statusCard/ClientStatusCard.tsx index 5e6ff689..6e0bacd3 100644 --- a/src/client/components/statusCard/ClientStatusCard.tsx +++ b/src/client/components/statusCard/ClientStatusCard.tsx @@ -31,7 +31,8 @@ const ClientStatusCard = (props: ClientStatusCardData) => { type, display, status, - tracksDiscovered = 0, + scrobbled: scrobbledCount = 0, + queued = 0, deadLetterScrobbles = 0 } = {} } = props; @@ -52,7 +53,8 @@ const ClientStatusCard = (props: ClientStatusCardData) => { // TODO links body = ( -
{scrobbled}: {tracksDiscovered}
+
{scrobbled}: {scrobbledCount}
+
Queued Scrobbles: {queued}
Failed Scrobbles: {deadLetterScrobbles}
{hasAuth ? (Re)authenticate or initialize : null}
); diff --git a/src/client/status/ducks.ts b/src/client/status/ducks.ts index 7cdb14a6..d96604c7 100644 --- a/src/client/status/ducks.ts +++ b/src/client/status/ducks.ts @@ -47,7 +47,7 @@ const sourceSlice = createSlice({ (action) => sourceUpdate.match(action) && action.payload.event === 'discovered', (state, action) => { if(state.entities[action.payload.id] !== undefined) { - state.entities[action.payload.id].tracksDiscovered = state.entities[action.payload.id].tracksDiscovered + 1; + state.entities[action.payload.id].tracksDiscovered++; } } ).addMatcher( @@ -94,7 +94,7 @@ const clientSlice = createSlice({ (action) => clientUpdate.match(action) && action.payload.event === 'scrobble', (state, action) => { if(state.entities[action.payload.id] !== undefined) { - state.entities[action.payload.id].tracksDiscovered = state.entities[action.payload.id].tracksDiscovered + 1; + state.entities[action.payload.id].scrobbled++; } } ) @@ -102,7 +102,7 @@ const clientSlice = createSlice({ (action) => clientUpdate.match(action) && action.payload.event === 'deadLetter', (state, action) => { if(state.entities[action.payload.id] !== undefined) { - state.entities[action.payload.id].deadLetterScrobbles = state.entities[action.payload.id].deadLetterScrobbles + 1; + state.entities[action.payload.id].deadLetterScrobbles++; } } ) @@ -114,6 +114,22 @@ const clientSlice = createSlice({ } } ) + .addMatcher( + (action) => clientUpdate.match(action) && action.payload.event === 'scrobbleQueued', + (state, action) => { + if(state.entities[action.payload.id] !== undefined) { + state.entities[action.payload.id].queued++; + } + } + ) + .addMatcher( + (action) => clientUpdate.match(action) && action.payload.event === 'scrobbleDequeued', + (state, action) => { + if(state.entities[action.payload.id] !== undefined) { + state.entities[action.payload.id].queued--; + } + } + ) } }); diff --git a/src/core/Atomic.ts b/src/core/Atomic.ts index f6025111..29e93092 100644 --- a/src/core/Atomic.ts +++ b/src/core/Atomic.ts @@ -19,8 +19,9 @@ export interface ClientStatusData { status: string; type: "maloja" | "lastfm" | "listenbrainz"; display: string; - tracksDiscovered: number; + scrobbled: number; deadLetterScrobbles: number + queued: number name: string; hasAuth: boolean; hasAuthInteraction: boolean;