Skip to content

Commit f004aa4

Browse files
committed
feat(listenbrainz): Implement Now Playing functionality
1 parent 2104f2a commit f004aa4

File tree

4 files changed

+31
-6
lines changed

4 files changed

+31
-6
lines changed

docsite/docs/configuration/configuration.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2439,6 +2439,7 @@ Clients can customize the Now Playing behavior individually using [File or AIO s
24392439
//"nowPlaying": true
24402440
//
24412441
// OR define a list of Source *config* names that should be allowed to report Now Playing
2442+
// order of the list determines priority for reporting Now Playing
24422443
//"nowPlaying": ["mySpotify1","myJellyfin2"]
24432444
}
24442445
},
@@ -2448,7 +2449,7 @@ Clients can customize the Now Playing behavior individually using [File or AIO s
24482449
### Supported Scrobble Clients
24492450

24502451
* [Last.fm](#lastfm)
2451-
* More coming soon
2452+
* [Listenbrainz](#listenbrainz)
24522453

24532454
## Monitoring
24542455

src/backend/common/vendor/ListenbrainzApiClient.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { getBaseFromUrl, isPortReachableConnect, joinedUrl, normalizeWebAddress
2020
import { parseRegexSingleOrFail } from '../../utils.js';
2121
import {ListensResponse as KoitoListensResponse} from '../infrastructure/config/client/koito.js'
2222
import { listenObjectResponseToPlay } from './koito/KoitoApiClient.js';
23+
import { log } from 'console';
2324

2425

2526
export interface ArtistMBIDMapping {
@@ -97,11 +98,18 @@ export interface ListenPayload {
9798
track_metadata: TrackPayload;
9899
}
99100

101+
export type ListenType = 'single' | 'playing_now';
102+
100103
export interface SubmitPayload {
101-
listen_type: 'single' | 'playing_now',
104+
listen_type: ListenType,
102105
payload: [ListenPayload]
103106
}
104107

108+
interface SubmitOptions {
109+
log?: boolean
110+
listenType?: ListenType
111+
}
112+
105113
export interface TrackResponse extends MinimumTrack {
106114
duration: number
107115
additional_info: AdditionalTrackInfoResponse
@@ -320,9 +328,13 @@ export class ListenbrainzApiClient extends AbstractApiClient {
320328
}
321329

322330

323-
submitListen = async (play: PlayObject, log: boolean = false) => {
331+
submitListen = async (play: PlayObject, options: SubmitOptions = {}) => {
332+
const { log = false, listenType = 'single'} = options;
324333
try {
325-
const listenPayload: SubmitPayload = {listen_type: 'single', payload: [ListenbrainzApiClient.playToListenPayload(play)]};
334+
const listenPayload: SubmitPayload = {listen_type: listenType, payload: [ListenbrainzApiClient.playToListenPayload(play)]};
335+
if(listenType === 'playing_now') {
336+
delete listenPayload.payload[0].listened_at;
337+
}
326338
if(log) {
327339
this.logger.debug(`Submit Payload: ${JSON.stringify(listenPayload)}`);
328340
}

src/backend/scrobblers/LastfmScrobbler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ export default class LastfmScrobbler extends AbstractScrobbleClient {
189189
return response;
190190
} catch (e) {
191191
if (!(e instanceof UpstreamError)) {
192-
throw new UpstreamError('Error received from LastFM API', {cause: e, showStopper: true});
192+
throw new UpstreamError('Error received from LastFM API', {cause: e, showStopper: false});
193193
} else {
194194
throw e;
195195
}

src/backend/scrobblers/ListenbrainzScrobbler.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { ListenbrainzApiClient, ListenPayload } from "../common/vendor/Listenbra
1111
import { Notifiers } from "../notifier/Notifiers.js";
1212

1313
import AbstractScrobbleClient from "./AbstractScrobbleClient.js";
14+
import { isDebugMode } from "../utils.js";
1415

1516
export default class ListenbrainzScrobbler extends AbstractScrobbleClient {
1617

@@ -27,6 +28,7 @@ export default class ListenbrainzScrobbler extends AbstractScrobbleClient {
2728
// https://listenbrainz.readthedocs.io/en/latest/users/api/core.html#get--1-user-(user_name)-listens
2829
// 1000 is way too high. maxing at 100
2930
this.MAX_INITIAL_SCROBBLES_FETCH = 100;
31+
this.supportsNowPlaying = true;
3032
}
3133

3234
formatPlayObj = (obj: any, options: FormatPlayObjectOptions = {}) => ListenbrainzApiClient.formatPlayObj(obj, options);
@@ -80,7 +82,7 @@ export default class ListenbrainzScrobbler extends AbstractScrobbleClient {
8082
} = playObj;
8183

8284
try {
83-
await this.api.submitListen(playObj, true);
85+
await this.api.submitListen(playObj, { log: isDebugMode()});
8486

8587
if (newFromSource) {
8688
this.logger.info(`Scrobbled (New) => (${source}) ${buildTrackString(playObj)}`);
@@ -93,4 +95,14 @@ export default class ListenbrainzScrobbler extends AbstractScrobbleClient {
9395
throw new UpstreamError(`Error occurred while making Listenbrainz API scrobble request: ${e.message}`, {cause: e, showStopper: !(e instanceof UpstreamError)});
9496
}
9597
}
98+
99+
doPlayingNow = async (data: PlayObject) => {
100+
if(!this.isKoito) {
101+
try {
102+
await this.api.submitListen(data, { listenType: 'playing_now'});
103+
} catch (e) {
104+
throw new UpstreamError(`Error occurred while making Listenbrainz API Playing Now request: ${e.message}`, {cause: e, showStopper: !(e instanceof UpstreamError)});
105+
}
106+
}
107+
}
96108
}

0 commit comments

Comments
 (0)