Skip to content

Commit b90ea03

Browse files
committed
Merge branch 'develop'
2 parents 5e905ea + 88efe2a commit b90ea03

19 files changed

+265
-149
lines changed

config/listenbrainz.json.example

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[
22
{
33
"name": "brainz",
4-
"configureAs": "client"
4+
"configureAs": "client",
55
"data": {
66
"token": "029b081ba-9156-4pe7-88e5-3be671f5ea2b",
77
"username": "FoxxMD"

flatpak/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
NOTE: This steps are for building the flatpak entirely locally, from source. If you want to install the application normally then [get it through flathub](/docs/installation.md#flatpak)
22

3-
Flatpak build is a little convoluted until someone sets me straight...
3+
The final build repo for the flathub version can be found at [flathub/io.github.foxxmd.multiscrobbler](https://github.com/flathub/io.github.foxxmd.multiscrobbler)
44

55
# 1. Install Requirements
66

flatpak/io.github.foxxmd.multiscrobbler.metainfo.xml

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
</screenshot>
4242
</screenshots>
4343
<releases>
44+
<release version="0.4.6" date="2023-06-07"/>
4445
<release version="0.4.4" date="2023-03-20"/>
4546
</releases>
4647
<categories>

flatpak/io.github.foxxmd.multiscrobbler.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ modules:
5555
sources:
5656
- type: git
5757
url: https://github.com/FoxxMD/multi-scrobbler
58-
commit: 9a578e54a89f87c82ff811bb04257c9b641b199a
58+
tag: 0.4.6
59+
commit: 5e905eae1798482dbcef504500d26a028c2e15cb
5960
dest: main
6061
# Wrapper to launch the app
6162
- type: script

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "multi-scrobbler",
3-
"version": "0.4.0",
3+
"version": "0.4.6",
44
"description": "scrobble plays from multiple sources to multiple clients",
55
"main": "src/index.js",
66
"scripts": {

src/apis/KodiApiClient.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export class KodiApiClient extends AbstractApiClient {
5353
const {
5454
url = 'http://localhost:8080/jsonrpc'
5555
} = config;
56-
this.url = KodiApiClient.parseConnectionUrl('http://localhost:8080');
56+
this.url = KodiApiClient.parseConnectionUrl(url);
5757
const auth = new Buffer(`${config.username}:${config.password}`).toString('base64');
5858
this.client = new KodiClient({
5959
clientType: this.url.protocol.replace(':', '') as ('http' | 'https'),

src/apis/maloja/interfaces.ts

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
export interface MalojaV2ScrobbleData {
2+
artists: string[]
3+
title: string
4+
album: string
5+
/**
6+
* Length of the track
7+
* */
8+
duration: number
9+
/**
10+
* unix timestamp (seconds) scrobble was made at
11+
* */
12+
time: number
13+
}
14+
15+
export interface MalojaAlbumData {
16+
name?: string
17+
albumtitle?: string
18+
artists: string[]
19+
}
20+
21+
export interface MalojaV3ScrobbleData {
22+
/**
23+
* unix timestamp (seconds) scrobble was made at
24+
* */
25+
time: number
26+
track: {
27+
artists: string[]
28+
title: string
29+
album?: MalojaAlbumData | null
30+
/**
31+
* length of the track
32+
* */
33+
length: number
34+
}
35+
/**
36+
* how long the track was listened to before it was scrobbled
37+
* */
38+
duration: number
39+
}
40+
41+
export type MalojaScrobbleData = MalojaV2ScrobbleData | MalojaV3ScrobbleData;
42+
43+
export interface MalojaScrobbleRequestData {
44+
key: string
45+
title: string
46+
album: string
47+
time: number
48+
length: number
49+
}
50+
51+
export interface MalojaScrobbleV2RequestData extends MalojaScrobbleRequestData {
52+
artist: string
53+
}
54+
55+
export interface MalojaScrobbleV3RequestData extends MalojaScrobbleRequestData {
56+
artists: string[]
57+
}
58+
59+
interface MalojaScrobbleWarning {
60+
type: string
61+
value: string[] | string
62+
desc: string
63+
}
64+
65+
export interface MalojaResponseV3CommonData {
66+
status: 'failure' | 'error' | 'success' | 'ok'
67+
error?: {
68+
type: string
69+
value?: string | object
70+
desc: string
71+
}
72+
}
73+
74+
export interface MalojaScrobbleV3ResponseData extends MalojaResponseV3CommonData {
75+
track?: {
76+
title: string
77+
artists: string[]
78+
album?: MalojaAlbumData | null
79+
}
80+
desc: string
81+
warnings?: MalojaScrobbleWarning[]
82+
}

src/clients/MalojaScrobbler.ts

+101-44
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,18 @@ import {
1414
import {
1515
FormatPlayObjectOptions,
1616
INITIALIZING,
17-
MalojaScrobbleData,
18-
MalojaScrobbleRequestData,
19-
MalojaScrobbleV2RequestData,
20-
MalojaScrobbleV3RequestData,
21-
MalojaV2ScrobbleData,
22-
MalojaV3ScrobbleData,
2317
PlayObject,
2418
TrackStringOptions
2519
} from "../common/infrastructure/Atomic.js";
2620
import {MalojaClientConfig} from "../common/infrastructure/config/client/maloja.js";
2721
import {Notifiers} from "../notifier/Notifiers.js";
2822
import {Logger} from '@foxxmd/winston';
23+
import {
24+
MalojaScrobbleData,
25+
MalojaScrobbleRequestData,
26+
MalojaScrobbleV2RequestData,
27+
MalojaScrobbleV3RequestData, MalojaScrobbleV3ResponseData, MalojaV2ScrobbleData, MalojaV3ScrobbleData
28+
} from "../apis/maloja/interfaces";
2929

3030
const feat = ["ft.", "ft", "feat.", "feat", "featuring", "Ft.", "Ft", "Feat.", "Feat", "Featuring"];
3131

@@ -65,10 +65,7 @@ export default class MalojaScrobbler extends AbstractScrobbleClient {
6565
track: {
6666
artists: mArtists,
6767
title: mTitle,
68-
album: {
69-
name: mAlbum,
70-
artists: albumArtists
71-
} = {},
68+
album: mAlbum,
7269
// length of the track
7370
length: mLength,
7471
} = {},
@@ -79,7 +76,14 @@ export default class MalojaScrobbler extends AbstractScrobbleClient {
7976
time = mTime;
8077
title = mTitle;
8178
duration = mLength;
82-
album = mAlbum;
79+
if(mAlbum !== null) {
80+
const {
81+
albumtitle,
82+
name: mAlbumName,
83+
artists: albumArtists
84+
} = mAlbum || {};
85+
album = albumtitle ?? mAlbumName;
86+
}
8387
} else {
8488
// scrobble data structure for v2 and below
8589
const {
@@ -384,6 +388,8 @@ export default class MalojaScrobbler extends AbstractScrobbleClient {
384388
length: duration,
385389
};
386390

391+
let responseBody: MalojaScrobbleV3ResponseData;
392+
387393
try {
388394
// 3.0.3 has a BC for something (maybe seconds => length ?) -- see #42 in repo
389395
if(this.serverVersion === undefined || compareVersions(this.serverVersion, '3.0.2') > 0) {
@@ -398,53 +404,77 @@ export default class MalojaScrobbler extends AbstractScrobbleClient {
398404
.type('json')
399405
.send(scrobbleData));
400406

401-
let scrobbleResponse = {};
407+
let scrobbleResponse: any | undefined = undefined,
408+
scrobbledPlay: PlayObject;
402409

403410
if(this.serverVersion === undefined || compareVersions(this.serverVersion, '3.0.0') >= 0) {
411+
responseBody = response.body;
404412
const {
405-
body: {
406-
// @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message
407413
track,
408-
} = {}
409-
} = response;
410-
scrobbleResponse = {
411-
time: playDate.unix(),
412-
track: {
413-
...track,
414-
length: duration
415-
},
416-
}
417-
if(album !== undefined) {
418-
const {
419-
album: malojaAlbum = {},
420-
} = track;
421-
// @ts-expect-error TS(2339): Property 'track' does not exist on type '{}'.
422-
scrobbleResponse.track.album = {
423-
...malojaAlbum,
424-
name: album
414+
status,
415+
warnings = [],
416+
} = responseBody;
417+
if(status === 'success') {
418+
if(track !== undefined) {
419+
scrobbleResponse = {
420+
time: playDate.unix(),
421+
track: {
422+
...track,
423+
length: duration
424+
},
425+
}
426+
if (album !== undefined) {
427+
const {
428+
album: malojaAlbum = {},
429+
} = track;
430+
scrobbleResponse.track.album = {
431+
...malojaAlbum,
432+
name: album
433+
}
434+
}
425435
}
436+
if(warnings.length > 0) {
437+
for(const w of warnings) {
438+
this.logger.warn(`Maloja Warning: ${w.desc} => ${JSON.stringify(w.value)}`)
439+
}
440+
}
441+
} else {
442+
throw new Error(buildErrorString(response));
426443
}
427444
} else {
428-
const {body: {
429-
// @ts-expect-error TS(2525): Initializer provides no value for this binding ele... Remove this comment to see the full error message
430-
track: {
431-
time: mTime = playDate.unix(),
432-
duration: mDuration = duration,
433-
album: mAlbum = album,
434-
...rest
435-
}
436-
} = {}} = response;
445+
const {
446+
body: {
447+
track: {
448+
time: mTime = playDate.unix(),
449+
duration: mDuration = duration,
450+
album: mAlbum = album,
451+
...rest
452+
} = {}
453+
} = {}
454+
} = response;
437455
scrobbleResponse = {...rest, album: mAlbum, time: mTime, duration: mDuration};
438456
}
439-
this.addScrobbledTrack(playObj, this.formatPlayObj(scrobbleResponse));
440-
if (newFromSource) {
441-
this.logger.info(`Scrobbled (New) => (${source}) ${buildTrackString(playObj)}`);
457+
let warning = '';
458+
if(scrobbleResponse === undefined) {
459+
warning = `WARNING: Maloja did not return track data in scrobble response! Maybe it didn't scrobble correctly??`;
460+
scrobbledPlay = playObj;
461+
} else {
462+
scrobbledPlay = this.formatPlayObj(scrobbleResponse)
463+
}
464+
this.addScrobbledTrack(playObj, scrobbledPlay);
465+
const scrobbleInfo = `Scrobbled (${newFromSource ? 'New' : 'Backlog'}) => (${source}) ${buildTrackString(playObj)}`;
466+
if(warning !== '') {
467+
this.logger.warn(`${scrobbleInfo} | ${warning}`);
468+
this.logger.debug(`Response: ${this.logger.debug(JSON.stringify(response.body))}`);
442469
} else {
443-
this.logger.info(`Scrobbled (Backlog) => (${source}) ${buildTrackString(playObj)}`);
470+
this.logger.info(scrobbleInfo);
444471
}
445472
} catch (e) {
446473
await this.notifier.notify({title: `Client - ${capitalize(this.type)} - ${this.name} - Scrobble Error`, message: `Failed to scrobble => ${buildTrackString(playObj)} | Error: ${e.message}`, priority: 'error'});
447474
this.logger.error(`Scrobble Error (${sType})`, {playInfo: buildTrackString(playObj), payload: scrobbleData});
475+
if(responseBody !== undefined) {
476+
this.logger.error('Raw Response:', responseBody);
477+
}
448478
throw e;
449479
} finally {
450480
this.logger.debug('Raw Payload:', scrobbleData);
@@ -453,3 +483,30 @@ export default class MalojaScrobbler extends AbstractScrobbleClient {
453483
return true;
454484
}
455485
}
486+
487+
const buildErrorString = (body: MalojaScrobbleV3ResponseData) => {
488+
let valString: string | undefined = undefined;
489+
const {
490+
status,
491+
error: {
492+
type,
493+
value,
494+
desc
495+
} = {}
496+
} = body;
497+
if(value !== undefined && value !== null) {
498+
if(typeof value === 'string') {
499+
valString = value;
500+
} else if(Array.isArray(value)) {
501+
valString = value.map(x => {
502+
if(typeof x === 'string') {
503+
return x;
504+
}
505+
return JSON.stringify(x);
506+
}).join(', ');
507+
} else {
508+
valString = JSON.stringify(value);
509+
}
510+
}
511+
return `Maloja API returned ${status} of type ${type} "${desc}"${valString !== undefined ? `: ${valString}` : ''}`;
512+
}

src/common/infrastructure/Atomic.ts

-54
Original file line numberDiff line numberDiff line change
@@ -134,60 +134,6 @@ export interface ScrobbledPlayObject {
134134
scrobble: PlayObject
135135
}
136136

137-
export interface MalojaV2ScrobbleData {
138-
artists: string[]
139-
title: string
140-
album: string
141-
/**
142-
* Length of the track
143-
* */
144-
duration: number
145-
/**
146-
* unix timestamp (seconds) scrobble was made at
147-
* */
148-
time: number
149-
}
150-
151-
export interface MalojaV3ScrobbleData {
152-
/**
153-
* unix timestamp (seconds) scrobble was made at
154-
* */
155-
time: number
156-
track: {
157-
artists: string[]
158-
title: string
159-
album?: {
160-
name: string
161-
artists: string[]
162-
}
163-
/**
164-
* length of the track
165-
* */
166-
length: number
167-
}
168-
/**
169-
* how long the track was listened to before it was scrobbled
170-
* */
171-
duration: number
172-
}
173-
174-
export type MalojaScrobbleData = MalojaV2ScrobbleData | MalojaV3ScrobbleData;
175-
176-
export interface MalojaScrobbleRequestData {
177-
key: string
178-
title: string
179-
album: string
180-
time: number
181-
length: number
182-
}
183-
184-
export interface MalojaScrobbleV2RequestData extends MalojaScrobbleRequestData {
185-
artist: string
186-
}
187-
188-
export interface MalojaScrobbleV3RequestData extends MalojaScrobbleRequestData {
189-
artists: string[]
190-
}
191137

192138
export interface RemoteIdentityParts {
193139
host: string,

0 commit comments

Comments
 (0)