From f1c6c28b56a3b387b58a2ee72d7d40d61c6b65e8 Mon Sep 17 00:00:00 2001 From: Gabe Farrell Date: Wed, 19 Nov 2025 00:56:52 -0500 Subject: [PATCH] feat: add now playing --- client/app/components/LastPlays.tsx | 42 ++++++++++++++++++++- client/app/routes/Home.tsx | 2 +- engine/long_test.go | 57 +++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 3 deletions(-) diff --git a/client/app/components/LastPlays.tsx b/client/app/components/LastPlays.tsx index 9463245..aff08c8 100644 --- a/client/app/components/LastPlays.tsx +++ b/client/app/components/LastPlays.tsx @@ -1,8 +1,8 @@ -import { useState } from "react" +import { useEffect, useState } from "react" import { useQuery } from "@tanstack/react-query" import { timeSince } from "~/utils/utils" import ArtistLinks from "./ArtistLinks" -import { deleteListen, getLastListens, type getItemsArgs, type Listen } from "api/api" +import { deleteListen, getLastListens, type getItemsArgs, type Listen, type Track } from "api/api" import { Link } from "react-router" import { useAppContext } from "~/providers/AppProvider" @@ -12,6 +12,7 @@ interface Props { albumId?: Number trackId?: number hideArtists?: boolean + showNowPlaying?: boolean } export default function LastPlays(props: Props) { @@ -27,7 +28,20 @@ export default function LastPlays(props: Props) { queryFn: ({ queryKey }) => getLastListens(queryKey[1] as getItemsArgs), }) + const [items, setItems] = useState(null) + const [nowPlaying, setNowPlaying] = useState(undefined) + + useEffect(() => { + fetch('/apis/web/v1/now-playing') + .then(r => r.json()) + .then(r => { + console.log(r) + if (r.currently_playing) { + setNowPlaying(r.track) + } + }) + }, []) const handleDelete = async (listen: Listen) => { if (!data) return @@ -69,6 +83,30 @@ export default function LastPlays(props: Props) { + {props.showNowPlaying && nowPlaying && + + + + + + } {listens.map((item) => (
+ + Now Playing + + {props.hideArtists ? null : ( + <> + –{' '} + + )} + + {nowPlaying.title} + +
diff --git a/client/app/routes/Home.tsx b/client/app/routes/Home.tsx index 11ee11d..55c62bf 100644 --- a/client/app/routes/Home.tsx +++ b/client/app/routes/Home.tsx @@ -33,7 +33,7 @@ export default function Home() { - + diff --git a/engine/long_test.go b/engine/long_test.go index 5b19614..6b90a22 100644 --- a/engine/long_test.go +++ b/engine/long_test.go @@ -917,3 +917,60 @@ func TestManualListen(t *testing.T) { require.NoError(t, err) assert.Equal(t, http.StatusBadRequest, resp.StatusCode) } + +func TestNowPlaying(t *testing.T) { + + t.Run("Submit Listens", doSubmitListens) + + // no playing + resp, err := http.DefaultClient.Get(host() + "/apis/web/v1/now-playing") + require.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + var result handlers.NowPlayingResponse + require.NoError(t, json.NewDecoder(resp.Body).Decode(&result)) + require.False(t, result.CurrentlyPlaying) + + body := `{ + "listen_type": "playing_now", + "payload": [ + { + "track_metadata": { + "additional_info": { + "artist_mbids": [ + "efc787f0-046f-4a60-beff-77b398c8cdf4" + ], + "artist_names": [ + "さユり" + ], + "duration_ms": 275960, + "recording_mbid": "21524d55-b1f8-45d1-b172-976cba447199", + "release_group_mbid": "3281e0d9-fa44-4337-a8ce-6f264beeae16", + "release_mbid": "eb790e90-0065-4852-b47d-bbeede4aa9fc", + "submission_client": "navidrome", + "submission_client_version": "0.56.1 (fa2cf362)" + }, + "artist_name": "さユり", + "release_name": "酸欠少女", + "track_name": "花の塔" + } + } + ] + }` + + req, err := http.NewRequest("POST", host()+"/apis/listenbrainz/1/submit-listens", strings.NewReader(body)) + require.NoError(t, err) + req.Header.Add("Authorization", fmt.Sprintf("Token %s", apikey)) + resp, err = http.DefaultClient.Do(req) + require.NoError(t, err) + respBytes, err := io.ReadAll(resp.Body) + require.NoError(t, err) + assert.Equal(t, `{"status": "ok"}`, string(respBytes)) + + // yes playing + resp, err = http.DefaultClient.Get(host() + "/apis/web/v1/now-playing") + require.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + require.NoError(t, json.NewDecoder(resp.Body).Decode(&result)) + require.True(t, result.CurrentlyPlaying) + require.Equal(t, "花の塔", result.Track.Title) +}