Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
FoxxMD committed Oct 24, 2023
2 parents be599b5 + 9616860 commit 9ec22e9
Show file tree
Hide file tree
Showing 43 changed files with 1,722 additions and 132 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ A javascript app to scrobble music you listened to, to [Maloja](https://github.c
* [Plex](/docsite/docs/configuration/configuration.md#plex) or [Tautulli](/docsite/docs/configuration/configuration.md#tautulli)
* [Subsonic-compatible APIs](/docsite/docs/configuration/configuration.md#subsonic) (like [Airsonic](https://airsonic.github.io/) and [Navidrome](https://www.navidrome.org/))
* [Jellyfin](/docsite/docs/configuration/configuration.md#jellyfin)
* [WebScrobbler](/docsite/docs/configuration/configuration.md#webscrobbler)
* [Youtube Music](/docsite/docs/configuration/configuration.md#youtube-music)
* [Last.fm](/docsite/docs/configuration/configuration.md#lastfm-source)
* [ListenBrainz](/docsite/docs/configuration/configuration.md#listenbrainz--source-)
Expand All @@ -29,6 +30,7 @@ A javascript app to scrobble music you listened to, to [Maloja](https://github.c
* Monitor status of Sources and Clients using [webhooks (Gotify or Ntfy)](/docsite/docs/configuration/configuration.md#webhook-configurations) or [healthcheck endpoint](/docsite/docs/configuration/configuration.md#health-endpoint)
* Supports configuring for single or multiple users (scrobbling for your friends and family!)
* Web server interface for stats, basic control, and detailed logs
* Graceful network and client failure handling (queued scrobbles that auto-retry)
* Smart handling of credentials (persistent, authorization through app)
* Easy configuration through ENVs or JSON
* Install using [Docker images for x86/ARM](/docsite/docs/installation/installation.md#docker), [flatpak](/docsite/docs/installation/installation.md#flatpak), or [locally with NodeJS](/docsite/docs/installation/installation.md#nodejs)
Expand Down
2 changes: 1 addition & 1 deletion config/subsonic.json.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"data": {
"url": "http://localhost:4040/airsonic",
"user": "yourUser",
"password": "yourPassword",
"password": "yourPassword"
}
}
]
10 changes: 10 additions & 0 deletions config/webscrobbler.json.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[
{
"name": "MyWebScrobbler",
"slug": null,
"data": {
"whitelist": [],
"blacklist": []
}
}
]
64 changes: 64 additions & 0 deletions docsite/docs/configuration/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ title: Overview
* [Mopidy](#mopidy)
* [JRiver](#jriver)
* [Kodi](#kodi)
* [WebScrobbler](#webscrobbler)
* [Client Configurations](#client-configurations)
* [Maloja](#maloja)
* [Last.fm](#lastfm)
Expand Down Expand Up @@ -624,6 +625,69 @@ MS transforms this to: `http://mydomain.com:80/kodiReverse/jsonrpc`

See [`kodi.json.example`](https://github.com/FoxxMD/multi-scrobbler/blob/master/config/kodi.json.example) or [explore the schema with an example and live editor/validator](https://json-schema.app/view/%23%2Fdefinitions%2FKodiSourceConfig/%23%2Fdefinitions%2FKodiData?url=https%3A%2F%2Fraw.luolix.top%2FFoxxMD%2Fmulti-scrobbler%2Fdevelop%2Fsrc%2Fcommon%2Fschema%2Fsource.json)

## [WebScrobbler](https://web-scrobbler.com/)

After installing the extension open the preferences/settings for it:

* Under **Accounts**
* **Add Webhook**
* API URL: `http://localhost:9078/api/webscrobbler`
* Application name: `(whatever you want)`

Reload the extension after adding the webhook.

* **On Firefox** - Only FQNs (domain.tld), `localhost`, and `127.0.0.1` are supported for API URL due to [firefox requiring https](https://github.com/web-scrobbler/web-scrobbler/issues/4183#issuecomment-1749222006)
* **On Chromium-based Browsers** - Any domain will work for API URL
* All Other browsers are untested

#### Multiple Users

If you would like use multiple WebScrobbler sources they can be matched using a **slug** at the end of the **API URL.** This requires using [a file-based config.](#file-based-configuration)

Example:

In `webscrobbler.json`

```json
[
{
"name": "aUserWS",
"clients": [
"client1Maloja"
],
"data": {
"slug": "usera"
}
},
{
"name": "bUserWS",
"clients": [
"client2Maloja"
],
"data": {
"slug": "userb"
}
}
]
```

* To use `aUserWS` source set **API URL** to `http://localhost:9078/api/webscrobbler/usera`
* To use `bUserWS` source set **API URL** to `http://localhost:9078/api/webscrobbler/userb`

Note: `http://localhost:9078/api/webscrobbler` is matched with the first source that _that does not have a slug defined._

### ENV-Based

| Environmental Variable | Required? | Default | Description |
|------------------------|-----------|---------|--------------------------------------------------------------------------|
| WS_ENABLE | No | | Set to 'true' to enable WS without needing to define other ENVs |
| WS_WHITELIST | No | | Only scrobble from these WebScrobbler Connectors. Comma-delimited list |
| WS_BLACKLIST | No | | Do not scrobble from these WebScrobbler Connectors. Comma-delimited list |

### File-Based

See [`webscrobbler.json.example`](https://github.com/FoxxMD/multi-scrobbler/blob/master/config/webscrobbler.json.example) or [explore the schema with an example and live editor/validator](https://json-schema.app/view/%23%2Fdefinitions%2FWebScrobblerSourceConfig/%23%2Fdefinitions%2FWebScrobblerData?url=https%3A%2F%2Fraw.luolix.top%2FFoxxMD%2Fmulti-scrobbler%2Fdevelop%2Fsrc%2Fcommon%2Fschema%2Fsource.json)

# Client Configurations

## [Maloja](https://github.com/krateng/maloja)
Expand Down
2 changes: 2 additions & 0 deletions docsite/src/pages/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ A javascript app to scrobble music you listened to, to [Maloja](https://github.c
* [Plex](docs/configuration#plex) or [Tautulli](docs/configuration#tautulli)
* [Subsonic-compatible APIs](docs/configuration#subsonic) (like [Airsonic](https://airsonic.github.io/) and [Navidrome](https://www.navidrome.org/))
* [Jellyfin](docs/configuration#jellyfin)
* [WebScrobbler](docs/configuration#webscrobbler)
* [Youtube Music](docs/configuration#youtube-music)
* [Last.fm](docs/configuration#lastfm-source)
* [ListenBrainz](docs/configuration#listenbrainz--source-)
Expand All @@ -32,6 +33,7 @@ A javascript app to scrobble music you listened to, to [Maloja](https://github.c
* Monitor status of Sources and Clients using [webhooks (Gotify or Ntfy)](docs/configuration#webhook-configurations) or [healthcheck endpoint](docs/configuration#health-endpoint)
* Supports configuring for single or multiple users (scrobbling for your friends and family!)
* Web server interface for stats, basic control, and detailed logs
* Graceful network and client failure handling (queued scrobbles that auto-retry)
* Smart handling of credentials (persistent, authorization through app)
* Easy configuration through ENVs or JSON
* Install using [Docker images for x86/ARM](docs/installation#docker), [flatpak](docs/installation#flatpak), or [locally with NodeJS](docs/installation#nodejs)
Expand Down
1 change: 1 addition & 0 deletions flatpak/io.github.foxxmd.multiscrobbler.metainfo.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
</screenshot>
</screenshots>
<releases>
<release version="0.6.1" date="2023-10-24"/>
<release version="0.6.0" date="2023-10-04"/>
<release version="0.5.2" date="2023-09-21"/>
<release version="0.5.0" date="2023-08-29"/>
Expand Down
6 changes: 3 additions & 3 deletions flatpak/io.github.foxxmd.multiscrobbler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ modules:
sources:
- type: git
url: https://github.com/FoxxMD/multi-scrobbler
branch: develop
#branch: develop
# in official builds use tag/commit instead of branch
# tag: 0.6.0
# commit: 7037d6f8332f323312ffc5712f48a1ce96026d97
tag: 0.6.0
commit: 7037d6f8332f323312ffc5712f48a1ce96026d97
dest: main
# Wrapper to launch the app
- type: script
Expand Down
13 changes: 11 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "multi-scrobbler",
"version": "0.6.0",
"version": "0.6.1",
"description": "scrobble plays from multiple sources to multiple clients",
"scripts": {
"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",
Expand Down Expand Up @@ -60,6 +60,7 @@
"ajv": "^7.2.4",
"better-sse": "^0.8.0",
"body-parser": "^1.19.0",
"clsx": "^2.0.0",
"common-tags": "^1.8.2",
"compare-versions": "^4.1.2",
"concat-stream": "^2.0.0",
Expand Down
10 changes: 3 additions & 7 deletions src/backend/common/infrastructure/Atomic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import {Logger} from '@foxxmd/winston';
import TupleMap from "../TupleMap";
import {Request, Response} from "express";
import {NextFunction, ParamsDictionary, Query} from "express-serve-static-core";
import { LogLevel, logLevels, PlayMeta, PlayObject } from "../../../core/Atomic";
import {LogLevel, logLevels, PlayMeta, PlayObject} from "../../../core/Atomic";

export type SourceType = 'spotify' | 'plex' | 'tautulli' | 'subsonic' | 'jellyfin' | 'lastfm' | 'deezer' | 'ytmusic' | 'mpris' | 'mopidy' | 'listenbrainz' | 'jriver' | 'kodi';
export const sourceTypes: SourceType[] = ['spotify', 'plex', 'tautulli', 'subsonic', 'jellyfin', 'lastfm', 'deezer', 'ytmusic', 'mpris', 'mopidy', 'listenbrainz', 'jriver', 'kodi'];
export type SourceType = 'spotify' | 'plex' | 'tautulli' | 'subsonic' | 'jellyfin' | 'lastfm' | 'deezer' | 'ytmusic' | 'mpris' | 'mopidy' | 'listenbrainz' | 'jriver' | 'kodi' | 'webscrobbler';
export const sourceTypes: SourceType[] = ['spotify', 'plex', 'tautulli', 'subsonic', 'jellyfin', 'lastfm', 'deezer', 'ytmusic', 'mpris', 'mopidy', 'listenbrainz', 'jriver', 'kodi', 'webscrobbler'];

export const lowGranularitySources: SourceType[] = ['subsonic','ytmusic'];

Expand Down Expand Up @@ -213,7 +213,3 @@ export const TIME_WEIGHT = 0.5;
export const REFERENCE_WEIGHT = 0.5;
export const DUP_SCORE_THRESHOLD = 1;

export interface SourceScrobble {
source: string
play: PlayObject
}
6 changes: 4 additions & 2 deletions src/backend/common/infrastructure/config/source/sources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ import { MopidySourceAIOConfig, MopidySourceConfig } from "./mopidy";
import { ListenBrainzSourceAIOConfig, ListenBrainzSourceConfig } from "./listenbrainz";
import { JRiverSourceAIOConfig, JRiverSourceConfig } from "./jriver";
import { KodiSourceAIOConfig, KodiSourceConfig } from "./kodi";
import { WebScrobblerSourceAIOConfig, WebScrobblerSourceConfig } from "./webscrobbler";

export type SourceConfig = SpotifySourceConfig | PlexSourceConfig | TautulliSourceConfig | DeezerSourceConfig | SubSonicSourceConfig | JellySourceConfig | LastfmSourceConfig | YTMusicSourceConfig | MPRISSourceConfig | MopidySourceConfig | ListenBrainzSourceConfig | JRiverSourceConfig | KodiSourceConfig;

export type SourceAIOConfig = SpotifySourceAIOConfig | PlexSourceAIOConfig | TautulliSourceAIOConfig | DeezerSourceAIOConfig | SubsonicSourceAIOConfig | JellySourceAIOConfig | LastFmSouceAIOConfig | YTMusicSourceAIOConfig | MPRISSourceAIOConfig | MopidySourceAIOConfig | ListenBrainzSourceAIOConfig | JRiverSourceAIOConfig | KodiSourceAIOConfig;
export type SourceConfig = SpotifySourceConfig | PlexSourceConfig | TautulliSourceConfig | DeezerSourceConfig | SubSonicSourceConfig | JellySourceConfig | LastfmSourceConfig | YTMusicSourceConfig | MPRISSourceConfig | MopidySourceConfig | ListenBrainzSourceConfig | JRiverSourceConfig | KodiSourceConfig | WebScrobblerSourceConfig;

export type SourceAIOConfig = SpotifySourceAIOConfig | PlexSourceAIOConfig | TautulliSourceAIOConfig | DeezerSourceAIOConfig | SubsonicSourceAIOConfig | JellySourceAIOConfig | LastFmSouceAIOConfig | YTMusicSourceAIOConfig | MPRISSourceAIOConfig | MopidySourceAIOConfig | ListenBrainzSourceAIOConfig | JRiverSourceAIOConfig | KodiSourceAIOConfig | WebScrobblerSourceAIOConfig;
68 changes: 68 additions & 0 deletions src/backend/common/infrastructure/config/source/webscrobbler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { CommonSourceConfig, CommonSourceData } from "./index";

export interface WebScrobblerData extends CommonSourceData {
/**
* The URL ending that should be used to identify scrobbles for this source
*
* In WebScrobbler's Webhook you must set an 'API URL'. All MS WebScrobbler sources must start like:
*
* http://localhost:9078/api/webscrobbler
*
* If you are using multiple WebScrobbler sources (scrobbles for many users) you must use a slug to match Sources with each users extension.
*
* Example:
*
* * slug: 'usera' => API URL: http://localhost:9078/api/webscrobbler/usera
* * slug: 'userb' => API URL: http://localhost:9078/api/webscrobbler/userb
*
* If no slug is found from an extension's incoming webhook event the first WebScrobbler source without a slug will be used
* */
slug?: string | null

/**
* Block scrobbling from specific WebScrobbler Connectors
*
* @examples [["youtube"]]
* */
blacklist?: string | string[]

/**
* Only allow scrobbling from specific WebScrobbler Connectors
*
* @examples [["mixcloud","soundcloud","bandcamp"]]
* */
whitelist?: string | string[]

/**
* Additional options for WebScrobbler logging and tuning
* */
options?: {
/**
* Log raw WebScrobbler webhook payload to debug
*
* @default false
* @examples [false]
* */
logPayload?: boolean

/**
* How MS should log when a WebScrobbler event fails a defined filter
*
* * `false` => do not log
* * `debug` => log to DEBUG level
* * `warn` => log to WARN level (default)
*
* @default warn
* @examples ["warn"]
* */
logFilterFailure?: false | 'debug' | 'warn'
}
}

export interface WebScrobblerSourceConfig extends CommonSourceConfig {
data?: WebScrobblerData
}

export interface WebScrobblerSourceAIOConfig extends WebScrobblerSourceConfig {
type: 'webscrobbler'
}
15 changes: 15 additions & 0 deletions src/backend/common/logging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ export const defaultFormat = (defaultLabel = 'App') => printf(({
durationMs,
[SPLAT]: splatObj,
stack,
id,
...rest
}) => {
const keys = Object.keys(rest);
Expand Down Expand Up @@ -168,6 +169,9 @@ export const defaultFormat = (defaultLabel = 'App') => printf(({
return `${timestamp} ${level.padEnd(8)}: ${labelContent} ${msg}${stringifyValue !== '' ? ` ${stringifyValue}` : ''}${stackMsg}`;
});

// https://knowyourmeme.com/memes/cereal-guy
// this number will never overflow
let seqId: number = 0;
export const labelledFormat = (labelName = 'App') => {
const l = label({label: labelName, message: false});
return combine(
Expand All @@ -176,6 +180,13 @@ export const labelledFormat = (labelName = 'App') => {
format: () => dayjs().local().format(),
}
),
{
transform: (info, opts) => {
info.id = seqId;
seqId++;
return info;
}
},
l,
s,
errorAwareFormat,
Expand Down Expand Up @@ -215,6 +226,10 @@ export const isLogLineMinLevel = (log: string | LogInfo, minLevelText: LogLevel)
return level <= minLevel;
}

export const isLogLevelMinLevel = (levelStr: LogLevel, minLevelStr: LogLevel): boolean => {
return logLevels[levelStr] <= logLevels[minLevelStr];
}

const isProbablyError = (val: any, explicitErrorName?: string) => {
if(typeof val !== 'object' || val === null) {
return false;
Expand Down
Loading

0 comments on commit 9ec22e9

Please sign in to comment.