Skip to content

Commit 4bd996e

Browse files
committed
Merge branch 'develop'
2 parents af037ee + a6ddc2b commit 4bd996e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1205
-452
lines changed

.github/pull_request_template.md

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
## Checklist before requesting a review
2+
3+
- [ ] **I am opening this PR for the [`develop` branch](https://github.com/FoxxMD/multi-scrobbler/tree/develop) and NOT `master`.**
4+
- [ ] I have read the [contributing guidelines.](../CONTRIBUTING.md)
5+
6+
## Type of change
7+
8+
Please delete options that are not relevant.
9+
10+
- [ ] Bug fix (non-breaking change which fixes an issue)
11+
- [ ] New feature (non-breaking change which adds functionality)
12+
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
13+
- [ ] This change requires a documentation update
14+
15+
## Describe your changes
16+
17+
18+
19+
## Issue number and link, if applicable
20+

CONTRIBUTING.md

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Creating a Pull Request
2+
3+
Please follow these guidelines when contributing code to this repository:
4+
5+
* The PR **must be for the [`develop` branch.](https://github.com/FoxxMD/multi-scrobbler/tree/develop)** The `master` branch is for releases only.
6+
* Use [conventional commit](https://www.conventionalcommits.org/en/v1.0.0/#summary) format when creating commits.
7+
* Preferably, please use a [feature branch](https://stackoverflow.com/a/39586780/1469797) instead of committing directly to `develop`.
8+
* Ensure that if your code is covered by [an existing test](./src/backend/tests) that you have updated the test accordingly

config/webscrobbler.json.example

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
[
22
{
33
"name": "MyWebScrobbler",
4-
"slug": null,
54
"data": {
5+
"slug": null,
66
"whitelist": [],
77
"blacklist": []
88
}

docsite/docs/configuration/configuration.md

+24-20
Large diffs are not rendered by default.

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

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
</screenshot>
4444
</screenshots>
4545
<releases>
46+
<release version="0.6.3" date="2024-01-10"/>
4647
<release version="0.6.2" date="2023-11-29"/>
4748
<release version="0.6.1" date="2023-10-24"/>
4849
<release version="0.6.0" date="2023-10-04"/>

package-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "multi-scrobbler",
3-
"version": "0.6.2",
3+
"version": "0.6.3",
44
"description": "scrobble plays from multiple sources to multiple clients",
55
"scripts": {
66
"schema": "npm run -s schema-aio & npm run -s schema-source & npm run -s schema-client & npm run -s schema-aiosource & npm run -s schema-aioclient",
@@ -12,7 +12,7 @@
1212
"typedoc": "typedoc",
1313
"circular": "madge --circular --extensions ts src/index.ts",
1414
"test": "react-scripts test",
15-
"test:backend": "mocha --extension ts --reporter spec --recursive src/backend/tests/scrobbler/**/*.test.ts",
15+
"test:backend": "mocha --extension ts --reporter spec --recursive src/backend/tests/**/*.test.ts",
1616
"eject": "react-scripts eject",
1717
"dev": "concurrently -p name -c \"yellow,magenta,blue\" -n \"webpack-server,nodemon-server,CRA\" \"npm run dev:server:webpack\" \"npm run dev:server:nodemon\" \"npm run dev:client\"",
1818
"dev:client": "BROWSER=none REACT_APP_VERSION=$npm_package_version react-scripts start",

register.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,7 @@ tsNode.register({
2121
project: './src/backend/tsconfig.json'
2222
});
2323

24-
process.env.CONSOLE_LEVEL = parseBool(process.env.DEBUG_MODE) ? undefined : 'false';
24+
if(!parseBool(process.env.DEBUG_MODE)) {
25+
process.env.CONSOLE_LEVEL = 'false';
26+
}
2527
process.env.FILE_LEVEL = 'false';

src/backend/common/schema/aio.json

+8-1
Original file line numberDiff line numberDiff line change
@@ -1335,8 +1335,15 @@
13351335
"LogOptions": {
13361336
"properties": {
13371337
"console": {
1338-
"$ref": "#/definitions/LogLevel",
13391338
"description": "Specify the minimum log level streamed to the console (or docker container)",
1339+
"enum": [
1340+
"debug",
1341+
"error",
1342+
false,
1343+
"info",
1344+
"verbose",
1345+
"warn"
1346+
],
13401347
"title": "console"
13411348
},
13421349
"file": {

src/backend/common/vendor/LastfmApiClient.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import {DEFAULT_RETRY_MULTIPLIER, FormatPlayObjectOptions} from "../infrastructu
66
import { LastfmData } from "../infrastructure/config/client/lastfm";
77
import { PlayObject } from "../../../core/Atomic";
88
import {isNodeNetworkException} from "../errors/NodeErrors";
9+
import {nonEmptyStringOrDefault, splitByFirstFound} from "../../../core/StringUtils";
10+
import {source} from "common-tags";
911

1012
const badErrors = [
1113
'api key suspended',
@@ -61,7 +63,7 @@ export default class LastfmApiClient extends AbstractApiClient {
6163
mbid,
6264
} = obj;
6365
// arbitrary decision yikes
64-
let artistStrings = artists !== undefined ? artists.split(',') : [artistName];
66+
let artistStrings = splitByFirstFound(artists, [','], [artistName]);
6567
return {
6668
data: {
6769
artists: [...new Set(artistStrings)] as string[],
@@ -71,9 +73,9 @@ export default class LastfmApiClient extends AbstractApiClient {
7173
playDate: time !== undefined ? dayjs.unix(time) : undefined,
7274
meta: {
7375
brainz: {
74-
album: albumMbid === '' ? undefined : albumMbid,
75-
artist: artistMbid === '' ? undefined : artistMbid,
76-
track: mbid === '' ? undefined : mbid
76+
album: nonEmptyStringOrDefault<undefined>(albumMbid),
77+
artist: splitByFirstFound<undefined>(artistMbid, [',',';'], undefined),
78+
track: nonEmptyStringOrDefault<undefined>(mbid)
7779
}
7880
}
7981
},

src/backend/common/vendor/ListenbrainzApiClient.ts

+37-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import {
1515
parseTrackCredits,
1616
uniqueNormalizedStrArr
1717
} from "../../utils/StringUtils";
18+
import {UpstreamError} from "../errors/UpstreamError";
19+
import {getScrobbleTsSOCDate} from "../../utils/TimeUtils";
1820

1921

2022
export interface ArtistMBIDMapping {
@@ -137,7 +139,40 @@ export class ListenbrainzApiClient extends AbstractApiClient {
137139
} catch (e) {
138140
const {
139141
message,
142+
err,
143+
status,
144+
response: {
145+
body = undefined,
146+
text = undefined,
147+
} = {}
140148
} = e;
149+
// TODO check err for network exception
150+
if(status !== undefined) {
151+
const msgParts = [`(HTTP Status ${status})`];
152+
// if the response is 400 then its likely there was an issue with the data we sent rather than an error with the service
153+
let showStopper = status !== 400;
154+
if(body !== undefined) {
155+
if(typeof body === 'object') {
156+
if('code' in body) {
157+
msgParts.push(`Code ${body.code}`);
158+
}
159+
if('error' in body) {
160+
msgParts.push(`Error => ${body.error}`);
161+
}
162+
if('message' in body) {
163+
msgParts.push(`Message => ${body.error}`);
164+
}
165+
// if('track_metadata' in body) {
166+
// msgParts.push(`Track Metadata => ${JSON.stringify(body.track_metadata)}`);
167+
// }
168+
} else if(typeof body === 'string') {
169+
msgParts.push(`Response => ${body}`);
170+
}
171+
} else if (text !== undefined) {
172+
msgParts.push(`Response => ${text}`);
173+
}
174+
throw new UpstreamError(`Listenbrainz API Request Failed => ${msgParts.join(' | ')}`, {cause: e, showStopper});
175+
}
141176
throw e;
142177
}
143178
}
@@ -241,15 +276,15 @@ export class ListenbrainzApiClient extends AbstractApiClient {
241276
}
242277
} = play;
243278
return {
244-
listened_at: (playDate ?? dayjs()).unix(),
279+
listened_at: getScrobbleTsSOCDate(play).unix(),
245280
track_metadata: {
246281
artist_name: artists[0],
247282
track_name: track,
248283
release_name: album,
249284
additional_info: {
250285
duration: play.data.duration !== undefined ? Math.round(duration) : undefined,
251286
track_mbid: brainz.track,
252-
artist_mbids: brainz.artist !== undefined ? [brainz.artist] : undefined,
287+
artist_mbids: brainz.artist,
253288
release_mbid: brainz.album,
254289
release_group_mbid: brainz.releaseGroup
255290
}

src/backend/index.ts

+10-25
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`)
8080
initServer(logger, output);
8181

8282
if(process.env.IS_LOCAL === 'true') {
83-
logger.info('multi-scrobbler can be run as a background service! See: https://github.com/FoxxMD/multi-scrobbler/blob/develop/docs/service.md');
83+
logger.info('multi-scrobbler can be run as a background service! See: https://foxxmd.github.io/multi-scrobbler/docs/installation/service');
8484
}
8585

8686
if(appConfigFail !== undefined) {
@@ -98,6 +98,11 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`)
9898
await scrobbleClients.buildClientsFromConfig(notifiers);
9999
if (scrobbleClients.clients.length === 0) {
100100
logger.warn('No scrobble clients were configured!')
101+
} else {
102+
logger.info('Starting scrobble clients...');
103+
}
104+
for(const client of scrobbleClients.clients) {
105+
await client.initScrobbleMonitoring();
101106
}
102107

103108
const scrobbleSources = root.get('sources');//new ScrobbleSources(localUrl, configDir);
@@ -116,30 +121,10 @@ const configDir = process.env.CONFIG_DIR || path.resolve(projectDir, `./config`)
116121
let anyNotReady = false;
117122
for (const source of scrobbleSources.sources.filter(x => x.canPoll === true)) {
118123
await sleep(1500); // stagger polling by 1.5 seconds so that log messages for each source don't get mixed up
119-
switch (source.type) {
120-
case 'spotify':
121-
if ((source as SpotifySource).spotifyApi !== undefined) {
122-
if ((source as SpotifySource).spotifyApi.getAccessToken() === undefined) {
123-
anyNotReady = true;
124-
} else {
125-
(source as SpotifySource).poll();
126-
}
127-
}
128-
break;
129-
case 'lastfm':
130-
if(source.initialized === true) {
131-
source.poll();
132-
}
133-
break;
134-
default:
135-
if (source.poll !== undefined) {
136-
source.poll();
137-
}
138-
}
139-
}
140-
for(const client of scrobbleClients.clients) {
141-
if((await client.isReady())) {
142-
client.initScrobbleMonitoring();
124+
if(source.isReady()) {
125+
source.poll();
126+
} else {
127+
anyNotReady = true;
143128
}
144129
}
145130
if (anyNotReady) {

0 commit comments

Comments
 (0)