diff --git a/docsite/src/pages/index.mdx b/docsite/docs/index.mdx similarity index 99% rename from docsite/src/pages/index.mdx rename to docsite/docs/index.mdx index 67532a9d..c31ad527 100644 --- a/docsite/src/pages/index.mdx +++ b/docsite/docs/index.mdx @@ -1,5 +1,6 @@ --- title: Overview +slug: / --- # Overview diff --git a/docsite/docusaurus.config.ts b/docsite/docusaurus.config.ts index b36963a3..fc96050b 100644 --- a/docsite/docusaurus.config.ts +++ b/docsite/docusaurus.config.ts @@ -49,6 +49,7 @@ const config: Config = { { docs: { sidebarPath: './sidebars.ts', + routeBasePath: '/', // Please change this to your repo. // Remove this to remove the "edit this page" links. editUrl: -- 2.51.2 From 3e3473f6a80773d53d1e3e2791651e3f10095311 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Thu, 30 Oct 2025 15:19:14 +0000 Subject: [PATCH 2/8] chore: Ignore docusaurus build folders when searching --- .vscode/settings.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 6820c694..c22dca1f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,9 @@ { "mochaExplorer.require": "tsx/esm", "mochaExplorer.timeout": 1200000, - "mochaExplorer.exit": true + "mochaExplorer.exit": true, + "search.exclude": { + "**/.docusaurus": true, + "**/build": true + } } \ No newline at end of file -- 2.51.2 From a5d1cea910ab9fc008abc4234dbc3aed17f2d5d5 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Thu, 30 Oct 2025 15:21:08 +0000 Subject: [PATCH 3/8] Add some snippers --- docsite/docs/configuration/configuration.mdx | 24 ++--------------- .../snippets/_scrobble-threshold.mdx | 27 +++++++++++++++++++ .../components/snippets/_should-scrobble.mdx | 22 +++++++++++++++ .../src/components/snippets/ingress-short.mdx | 3 +++ 4 files changed, 54 insertions(+), 22 deletions(-) create mode 100644 docsite/src/components/snippets/_scrobble-threshold.mdx create mode 100644 docsite/src/components/snippets/_should-scrobble.mdx create mode 100644 docsite/src/components/snippets/ingress-short.mdx diff --git a/docsite/docs/configuration/configuration.mdx b/docsite/docs/configuration/configuration.mdx index e42748e9..11ffb6f3 100644 --- a/docsite/docs/configuration/configuration.mdx +++ b/docsite/docs/configuration/configuration.mdx @@ -9,6 +9,7 @@ import CodeBlock from '@theme/CodeBlock'; import SchemaLink from "../../src/components/SchemaLink"; import AIOExample from "../../src/components/AIOExample"; import FileExample from "../../src/components/FileExample"; +import ScrobbleThreshold from "@site/src/components/snippets/_scrobble-threshold.mdx" import AIOConfig from '!!raw-loader!../../../config/config.json.example'; import AzuracastConfig from '!!raw-loader!../../../config/azuracast.json.example'; @@ -337,28 +338,7 @@ If your Client is configured using [ENV](./?configType=env#configuration-types)

Scrobble Thresholds

-For Sources where multi-scrobbler monitors active listening, it uses one of two metrics to determine if a Song is scrobbable based on your listening: - -* **percent** of track listened to (50% or more) -* or amount of time (**duration**) track was listened to (4 minutes or more) - -These default values are based on [last.fm's scrobble guidance.](https://www.last.fm/api/scrobbling#when-is-a-scrobble-a-scrobble) - -Each Source can have these values customized using values in the Source's `options` in [file or aio config](./?configType=file#configuration-types): - -```json5 title="azuracast.json" -{ - "data": { - // ... - }, - "options": { - "scrobbleThresholds": { - "duration": 40, // scrobbable if listened to for 40 seconds or more - "percent": 20 // scrobbable if listened to for 20% or more of the track's length - } - } -} -``` + These can also be configured **globally** for all Sources using ENVs: diff --git a/docsite/src/components/snippets/_scrobble-threshold.mdx b/docsite/src/components/snippets/_scrobble-threshold.mdx new file mode 100644 index 00000000..2fc4a89b --- /dev/null +++ b/docsite/src/components/snippets/_scrobble-threshold.mdx @@ -0,0 +1,27 @@ +import CodeBlock from '@theme/CodeBlock'; + +A **Source** that monitors active listening uses one of two metrics to determine if a Song is scrobbable based on your listening: + +* **percent** of track listened to (50% or more) +* or amount of time (**duration**) track was listened to (4 minutes or more) + +These default values are based on [last.fm's scrobble guidance.](https://www.last.fm/api/scrobbling#when-is-a-scrobble-a-scrobble) + +These values can be customized in the `options` of Source's [file or aio config](/configuration#configuration-types): + +{`{ + "data": { + // ... + }, + "options": { + // highlight-start + "scrobbleThresholds": { + "duration": 40, // scrobbable if listened to for 40 seconds or more + "percent": 20 // scrobbable if listened to for 20% or more of the track's length + } + // highlight-end + } +}`} + \ No newline at end of file diff --git a/docsite/src/components/snippets/_should-scrobble.mdx b/docsite/src/components/snippets/_should-scrobble.mdx new file mode 100644 index 00000000..1974ecdf --- /dev/null +++ b/docsite/src/components/snippets/_should-scrobble.mdx @@ -0,0 +1,22 @@ +import CodeBlock from '@theme/CodeBlock'; + +This **Source** supports toggling scrobbling on or off manually. This action can be taken on the Web Dashboard by clicking the link next to **Should Scrobble** found in each Source's status card. + +Additionally, the default behavior for scrobbling can be explicitly set by adding the following value to a Source's `options` in [file or aio config](/configuration?configType=file#configuration-types): + + +{`{ + "data": { + // ... + }, + "options": { + // highlight-start + // set to true to always scrobble automatically + // set to false to never scrobble automatically + "systemScrobble": true + // highlight-end + } +}}`} + \ No newline at end of file diff --git a/docsite/src/components/snippets/ingress-short.mdx b/docsite/src/components/snippets/ingress-short.mdx new file mode 100644 index 00000000..403157ca --- /dev/null +++ b/docsite/src/components/snippets/ingress-short.mdx @@ -0,0 +1,3 @@ +This component **recieves data** from outside services, rather than actively contacting the outside service for data itself. + +In order for it to work multi-scrobbler must be accessible, on an open port or through reverse-proxy, to the service that will be sending data to it. \ No newline at end of file -- 2.51.2 From bc62b99401c53d77affe5d2fd6b745b973811fd5 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Thu, 30 Oct 2025 17:06:22 +0000 Subject: [PATCH 4/8] docs: Add commonly used components to global scope --- docsite/src/theme/MDXComponents.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 docsite/src/theme/MDXComponents.js diff --git a/docsite/src/theme/MDXComponents.js b/docsite/src/theme/MDXComponents.js new file mode 100644 index 00000000..f2dbe845 --- /dev/null +++ b/docsite/src/theme/MDXComponents.js @@ -0,0 +1,14 @@ +import React from 'react'; +// Import the original mapper +import MDXComponents from '@theme-original/MDXComponents'; +import SchemaLink from "@site/src/components/SchemaLink"; +import AIOExample from "@site/src/components/AIOExample"; +import FileExample from "@site/src/components/FileExample"; + +export default { + // Re-use the default mapping + ...MDXComponents, + SchemaLink, + AIOExample, + FileExample +}; \ No newline at end of file -- 2.51.2 From 9b91f6897ecbd905a5570be01dda16c3959ae58c Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Thu, 30 Oct 2025 17:06:39 +0000 Subject: [PATCH 5/8] Add new Source overview using category --- .../configuration/sources/_category_.json | 7 + .../docs/configuration/sources/overview.mdx | 141 ++++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 docsite/docs/configuration/sources/_category_.json create mode 100644 docsite/docs/configuration/sources/overview.mdx diff --git a/docsite/docs/configuration/sources/_category_.json b/docsite/docs/configuration/sources/_category_.json new file mode 100644 index 00000000..36e115eb --- /dev/null +++ b/docsite/docs/configuration/sources/_category_.json @@ -0,0 +1,7 @@ +{ + "label": "Scrobble Sources", + "link": { + "type": "doc", + "id": "configuration/sources/overview" + } +} diff --git a/docsite/docs/configuration/sources/overview.mdx b/docsite/docs/configuration/sources/overview.mdx new file mode 100644 index 00000000..1e705007 --- /dev/null +++ b/docsite/docs/configuration/sources/overview.mdx @@ -0,0 +1,141 @@ +--- +sidebar_position: 2 +title: Overview +toc_max_heading_level: 4 +--- + +import ScrobbleThreshold from "@site/src/components/snippets/_scrobble-threshold.mdx" + +A **Source** is a data source that contains information about tracks you are playing like a music player or platform. Examples are **Spotify, Jellyfin, Plex, Youtube Music, Airsonic**, etc... + +## Sources + +| Name | Networking | Scrobble SOT | [Multi Device/User](#multi-device-user) | [Scrobble Destination](#limiting-scrobble-destination) | [Thresholds](#scrobble-thresholds) | [Should Scrobble](#should-scrobble-behavior) | +| :-------------------------------------------------------------------------- | :------------------ | :-------------------- | :-------------------------------------- | :----------------------------------------------------- | :--------------------------------- | :------------------------------------------- | +| [Azuracast](/configuration/sources/azuracast) | [Active](#active) | [Activity](#activity) | ❌ | ✅ | ✅ | ✅ | +| [Deezer](/configuration/sources/deezer) | [Active](#active) | [History](#history) | ✅ | ✅ | ❌ | ❌ | +| [Google Cast (Chromecast)](/configuration/sources/google-cast-chromecast) | [Active](#active) | [Activity](#activity) | ✅ | ✅ | ✅ | ❌ | +| [Icecast](/configuration/sources/icecast) | [Active](#active) | [Activity](#activity) | ❌ | ✅ | ✅ | ✅ | +| [Jellyfin](/configuration/sources/jellyfin) | [Active](#active) | [Activity](#activity) | ✅ | ✅ | ✅ | ❌ | +| [JRiver](/configuration/sources/jriver) | [Active](#active) | [Activity](#activity) | ❌ | ✅ | ✅ | ❌ | +| [Kodi](/configuration/sources/kodi) | [Active](#active) | [Activity](#activity) | ❌ | ✅ | ✅ | ❌ | +| [Koito](/configuration/sources/koito-source) | [Active](#active) | [History](#history) | ❌ | ✅ | ❌ | ❌ | +| [Last.fm](/configuration/sources/lastfm-source) | [Active](#active) | [History](#history) | ❌ | ✅ | ❌ | ❌ | +| [Last.fm (Endpoint)](/configuration/sources/lastfm-endpoint) | [Ingress](#ingress) | [History](#history) | ❌ | ✅ | ❌ | ❌ | +| [ListenBrainz](configuration#/sources/istenbrainz-source) | [Active](#active) | [History](#history) | ❌ | ✅ | ❌ | ❌ | +| [ListenBrainz (Endpoint)](/configuration/sources/listenbrainz-endpoint) | [Ingress](#ingress) | [History](#history) | ❌ | ✅ | ❌ | ❌ | +| [Maloja](/configuration/sources/maloja-source) | [Active](#active) | [History](#history) | ❌ | ✅ | ❌ | ❌ | +| [Mopidy](/configuration/sources/mopidy) | [Active](#active) | [Activity](#activity) | ❌ | ✅ | ✅ | ❌ | +| [MPD (Music Player Daemon)](/configuration/sources/mpd-music-player-daemon) | [Active](#active) | [Activity](#activity) | ❌ | ✅ | ✅ | ❌ | +| [Musikcube](/configuration/sources/musikcube) | [Active](#active) | [Activity](#activity) | ❌ | ✅ | ✅ | ❌ | +| [Plex](/configuration/sources/plex) | [Active](#active) | [Activity](#activity) | ✅ | ✅ | ✅ | ❌ | +| [Spotify](/configuration/sources/spotify) | [Active](#active) | [Activity](#activity) | ✅ | ✅ | ✅ | ❌ | +| [Subsonic-compatible APIs](configuration/sources/subsonic) | [Active](#active) | [Activity](#activity) | ✅ | ✅ | ✅ | ❌ | +| [WebScrobbler](docs/configuration#webscrobbler) | [Ingress](#active) | [History](#history) | ❌ | ✅ | ❌ | ❌ | +| [VLC](/configuration/sources/vlc) | [Active](#active) | [Activity](#activity) | ❌ | ✅ | ✅ | ❌ | +| [Yamaha MusicCast](/configuration/sources/yamaha-musiccast) | [Active](#active) | [Activity](#activity) | ❌ | ✅ | ✅ | ❌ | +| [Youtube Music](/configuration/sources/youtube-music) | [Active](#active) | [History](#history) | ❌ | ✅ | ❌ | ❌ | + +## Features + +### Types of Sources + +The Sources implemented in multi-scrobbler can be broken down into two categories. + +#### By Communication Method + +How does this Source get data from the service/application? + +##### Active + +The MS Source makes network requests to the service which returns a response with data MS can use to monitor listening activity or scrobbles. + +##### Ingress + +The MS Source waits for the service/application to send data to multi-scrobbler. + +These types of Sources require that multi-scrobbler is accessible to the service, generally through an open port or reverse proxy. + +#### By Data Source of Truth + +How does MS determine if/when a song has been played and is scrobbable? + +##### Listening Activity {#activity} + +These Sources expose some kind of real-time listening data for users, like: + +* player state (paused, stopped, playing) +* player position (1:50/2:40) +* current listened duration (Track A playing for 1m:20s) + +For theses Sources multi-scrobbler keeps track of the real-time state of your activity and scrobbles a track after it meets certain [scrobble thresholds](#scrobble-thresholds). + +##### Listening History/Scrobbles {#history} + +Some Sources expose their own version of "this track has been scrobbled." + +* Listenbrainz, Koito, and Last.fm are all scrobble services that have "definitive" histories of scrobbled tracks. + +For these Sources multi-scrobbler monitors the listening history data and scrobbles when it sees a new entry in the list. + +### Multi-Device/User + +Some Sources report which User is playing a track and/or on what device the track is being played. These Sources can be filtered with configuration to only monitor activity for specific devices or users. + +### Limiting Scrobble Destination + +By default a Source will scrobble to **all** configured Clients. + +To limit which Clients a Source will scrobble to use the `clients` property in a Source's [File/AIO configuration](./?configType=file#configuration-types) to specify the **names** of the Clients that it should scrobble to: + +```json5 title="jellyfin.json" +{ + "name": "MyJellyfin", + "clients": ["myMaloja","koitoA","listenbrainzFoo"] + "data": { + // ... + }, +} +``` + +The Source above will only scrobble to Clients that are **named** (`name` property in Client config) either `myMaloja` `koitoA` or `listenbrainzFoo`. + +If your Client is configured using [ENV](./?configType=env#configuration-types) then they are given an automatically generated name. Check the multi-scrobbler Dashboard to find their name in a subheader under the Client type. Example: + +![Client Name Subheader](../client-subheader.png) + +### Scrobble Thresholds + + + +These can also be configured **globally** for all Sources using ENVs: + +``` +SOURCE_SCROBBLE_DURATION=40 +SOURCE_SCROBBLE_PERCENT=20 +``` + +### Should Scrobble Behavior + +:::note + +This feature is currently available only for some Sources. Check each Source's documentation to see if it supported. + +::: + +For _most_ Sources multi-scrobbler can determine if a user is listening and scrobble automatically. For Sources which multi-scrobbler cannot do this or require additional configuration, a user can indicate to MS that it should or should not scrobble. This action can be taken on the Web Dashboard by clicking the link next to **Should Scrobble** found in each Source's status card. + +Additionally, for these Sources, the default behavior for Scrobbling can be explicitly set by adding the following value to a Source's `options` in [file or aio config](./?configType=file#configuration-types): + +```json5 title="azuracast.json" +{ + "data": { + // ... + }, + "options": { + // set to true to always scrobble automatically + // set to false to never scrobble automatically + "systemScrobble": true + } +} +``` \ No newline at end of file -- 2.51.2 From 7ac3f33cd476dc0df319003ca8fbc8ce2f134c05 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Thu, 30 Oct 2025 18:35:58 +0000 Subject: [PATCH 6/8] docs: Move Source configs to own files --- docsite/docs/configuration/configuration.mdx | 1 - .../docs/configuration/sources/azuracast.mdx | 82 ++++++++ docsite/docs/configuration/sources/deezer.mdx | 153 ++++++++++++++ .../configuration/sources/google-cast.mdx | 184 +++++++++++++++++ .../docs/configuration/sources/icecast.mdx | 97 +++++++++ .../docs/configuration/sources/jellyfin.mdx | 67 +++++++ docsite/docs/configuration/sources/jriver.mdx | 76 +++++++ docsite/docs/configuration/sources/kodi.mdx | 59 ++++++ .../configuration/sources/koito-source.mdx | 31 +++ .../configuration/sources/lastfm-endpoint.mdx | 46 +++++ .../configuration/sources/lastfm-source.mdx | 29 +++ .../sources/listenbrainz-endpoint.mdx | 56 ++++++ .../sources/listenbrainz-source.mdx | 31 +++ docsite/docs/configuration/sources/maloja.mdx | 29 +++ docsite/docs/configuration/sources/mopidy.mdx | 102 ++++++++++ docsite/docs/configuration/sources/mpd.mdx | 28 +++ docsite/docs/configuration/sources/mpris.mdx | 79 ++++++++ .../docs/configuration/sources/musikcube.mdx | 38 ++++ docsite/docs/configuration/sources/plex.mdx | 79 ++++++++ .../docs/configuration/sources/spotify.mdx | 105 ++++++++++ .../docs/configuration/sources/subsonic.mdx | 31 +++ docsite/docs/configuration/sources/vlc.mdx | 94 +++++++++ .../configuration/sources/webscrobbler.mdx | 95 +++++++++ .../sources/yamaha-musiccast.mdx | 37 ++++ .../configuration/sources/youtube-music.mdx | 189 ++++++++++++++++++ .../src/components/GenericConfiguration.mdx | 28 +++ .../src/components/snippets/_aio-config.mdx | 2 + .../src/components/snippets/_env-config.mdx | 3 + .../src/components/snippets/_file-config.mdx | 3 + docsite/src/theme/MDXComponents.js | 10 +- 30 files changed, 1862 insertions(+), 2 deletions(-) create mode 100644 docsite/docs/configuration/sources/azuracast.mdx create mode 100644 docsite/docs/configuration/sources/deezer.mdx create mode 100644 docsite/docs/configuration/sources/google-cast.mdx create mode 100644 docsite/docs/configuration/sources/icecast.mdx create mode 100644 docsite/docs/configuration/sources/jellyfin.mdx create mode 100644 docsite/docs/configuration/sources/jriver.mdx create mode 100644 docsite/docs/configuration/sources/kodi.mdx create mode 100644 docsite/docs/configuration/sources/koito-source.mdx create mode 100644 docsite/docs/configuration/sources/lastfm-endpoint.mdx create mode 100644 docsite/docs/configuration/sources/lastfm-source.mdx create mode 100644 docsite/docs/configuration/sources/listenbrainz-endpoint.mdx create mode 100644 docsite/docs/configuration/sources/listenbrainz-source.mdx create mode 100644 docsite/docs/configuration/sources/maloja.mdx create mode 100644 docsite/docs/configuration/sources/mopidy.mdx create mode 100644 docsite/docs/configuration/sources/mpd.mdx create mode 100644 docsite/docs/configuration/sources/mpris.mdx create mode 100644 docsite/docs/configuration/sources/musikcube.mdx create mode 100644 docsite/docs/configuration/sources/plex.mdx create mode 100644 docsite/docs/configuration/sources/spotify.mdx create mode 100644 docsite/docs/configuration/sources/subsonic.mdx create mode 100644 docsite/docs/configuration/sources/vlc.mdx create mode 100644 docsite/docs/configuration/sources/webscrobbler.mdx create mode 100644 docsite/docs/configuration/sources/yamaha-musiccast.mdx create mode 100644 docsite/docs/configuration/sources/youtube-music.mdx create mode 100644 docsite/src/components/GenericConfiguration.mdx create mode 100644 docsite/src/components/snippets/_aio-config.mdx create mode 100644 docsite/src/components/snippets/_env-config.mdx create mode 100644 docsite/src/components/snippets/_file-config.mdx diff --git a/docsite/docs/configuration/configuration.mdx b/docsite/docs/configuration/configuration.mdx index 11ffb6f3..3495fa4e 100644 --- a/docsite/docs/configuration/configuration.mdx +++ b/docsite/docs/configuration/configuration.mdx @@ -2378,7 +2378,6 @@ EX - or | Environmental Variable | Required? | Default | Description | | :--------------------- | :-------- | ------- | :----------------------- | | `MCAST_URL` | Yes | | The Musiccast device URL | diff --git a/docsite/docs/configuration/sources/azuracast.mdx b/docsite/docs/configuration/sources/azuracast.mdx new file mode 100644 index 00000000..bf3558f8 --- /dev/null +++ b/docsite/docs/configuration/sources/azuracast.mdx @@ -0,0 +1,82 @@ +--- +title: Azuracast +toc_min_heading_level: 2 +toc_max_heading_level: 5 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from '@theme/CodeBlock'; +import AzuracastConfig from '!!raw-loader!@site/../config/azuracast.json.example'; +import ShouldScrobble from "@site/src/components/snippets/_should-scrobble.mdx" + +:::tip[Scrobbling Threshold] + +Azuracast may not correctly report track length or position. If this is the case you should reduce the "listened to" **duration** using [Scrobble Thresholds](#scrobble-thresholds) so that multi-scrobbler scrobbles correctly. + +::: + +The Azuracast server should have **Use High-Performance Now Playing Updates** enabled in _Administration -> System Settings_ + +#### URL + +The URL used by MS to connect to Azuracast has the syntax: + +``` +[ws|wss]://HOST:[PORT] +``` + +MS will automatically add the path required for websockets, [`/api/live/nowplaying/websocket`](https://www.azuracast.com/docs/developers/now-playing-data/#websockets), to your URL if none is provided. If you use a reverse proxy with a path-based URL or otherwise need a custom path to access the websockets path correctly then explicitly provide it. Examples: + +``` +URL From Config => MS Uses + +'ws://192.168.0.101' => ws://192.168.0.101/api/live/nowplaying/websocket +'ws://azura.mydomain.com' => ws://azura.mydomain.com.com/api/live/nowplaying/websocket +'wss://mydomain.com/custom/azura/ws' => wss://mydomain.com/custom/azura/ws +``` + +#### Scrobble Behavior + +:::info + + + +::: + +Multi-scrobbler will set the default **scrobbling behavior** based on if either of these settings is configured: + +* live status (`AZURA_LIVE` or `monitorWhenLive`) or +* listener count (`AZURA_LISTENERS_NUM` or `monitorWhenListeners`) + +If either is configured then MS will automatically scrobble based on when these conditions are met. + +If neither is configured automatic scrobble defaults to **off** and you should either manually initiate it or explicitly set the default behavior. + +## Configuration + + + + | Environmental Variable | Required? | Default | Description | + | :--------------------- | :-------- | :------ | ---------------------------------------------------------------------------------------------- | + | `AZ_URL` | Yes | | Azuracast URL *without station name* | + | `AZ_STATION` | Yes | | The station name shown on the public page | + | `AZURA_LIVE` | No | Yes | Only scrobble when station status is ONLINE | + | `AZURA_LISTENERS_NUM` | No | `true` | Only scrobble if station has any listeners (`true`) or listeners are equal-to/greater-than `X` | + + + + {AzuracastConfig} + + + or + + + + + + + or + + + diff --git a/docsite/docs/configuration/sources/deezer.mdx b/docsite/docs/configuration/sources/deezer.mdx new file mode 100644 index 00000000..ddc09b83 --- /dev/null +++ b/docsite/docs/configuration/sources/deezer.mdx @@ -0,0 +1,153 @@ +--- +title: Deezer +toc_min_heading_level: 2 +toc_max_heading_level: 5 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from '@theme/CodeBlock'; +import DeezerConfig from '!!raw-loader!@site/../config/deezer.json.example'; +import DeezerDeprecatedConfig from '!!raw-loader!@site/../config/deezer-deprecated.json.example'; + + + + :::warning + + This Source uses unofficial methods to access Deezer data that are likely in violation of Deezer's TOS. Deezer may change or remove these methods at any time, breaking functionality. Use this Source at your own risk. + + ::: + + :::info + + This Source will work only if you have a **Premium** Deezer account. + + ::: + + ### Retrieve ARL + + The `arl` is a browser cookie that Deezer uses to store your login information. Multi-scrobbler uses the `arl` value to make authorized requests to Deezer's API where it can retrieve listening history. + + **After logging into Deezer**, use one of the methods below to retrieve the `arl` cookie value: + + + + 1. On deezer.com, press F12 to open the Developer Tools window. + 2. Go to the Applications tab. Press the '>>' More tabs icon if it's hidden. + 3. Under the Cookies Filter, click deezer.com, then the text bar name `arl`. + 4. Copy all the contents shown in the **Cookie Value** pane. + + + 1. On deezer.com, hit Ctrl+Shift+I to open Developer Tools + 2. Go to the Storage Tab, then expand Cookies in the sidebar and select deezer.com + 3. Find the row with `arl` as the name, then double click the Value column and right click -> copy the value + + + + ## Configuration + + + + | Environmental Variable | Required? | Default | Description | + | :--------------------- | --------- | ------- | ------------------------------------------------ | + | `DEEZER_ARL` | Yes | | The ARL cookie value retrieved from your browser | + + + + {DeezerConfig} + + or + + + + + + or + + + + ### Duplicate detection + + Third party integrations with Deezer (Sonos) may cause Deezer to [report the same track many times in listening history.](https://github.com/FoxxMD/multi-scrobbler/pull/296#issuecomment-2922374738) If you experience this issue modify your Deezer Source config (file-based only) to include the option `"fuzzyDiscoveryIgnore": "aggressive"` + +
+ + deezer.json example + + ```json title="deezer.json" + [ + { + "name": "MyDeezer", + "data": { + "arl": "UOsRPjT3U5Dhaaup3x...", + }, + "options": { + "fuzzyDiscoveryIgnore": "aggressive" + } + } + ] + ``` +
+ + This option comes with some trade-offs: MS will aggressively detect repeated tracks within a window of time that should eliminate all duplicates. However, this will also prevent *intentionally* repeated tracks from being scrobbled. See [this thread](https://github.com/FoxxMD/multi-scrobbler/pull/296#issuecomment-2970417070) for more information on how this works. + +
+ + + :::warning + + **This Source is DEPRECATED because Deezer has dropped official API support.** This Source will **not** be removed but no further support or fixes will be given. [See this issue for more discussion.](https://github.com/FoxxMD/multi-scrobbler/issues/175#issuecomment-2296776625) + + Users cannot create new applications on Deezer Developers and there is no guarantee existing applications will continue to work. + + As a workaround consider: + + * Using the alternative [Deezer Source that uses unofficial, internal API via ARL](./?deezerSource=arl#deezer) + * Connect Deezer with Last.fm and then use [Last.fm as a Source](#lastfm-source) + + ::: + + Create a new application at [Deezer Developers](https://developers.deezer.com/myapps) + + * Application Domain must be the same as your multi-scrobbler domain. Default is `localhost:9078` + * Redirect URL must end in `deezer/callback` + * Default would be `http://localhost:9078/deezer/callback` + + After application creation you should have credentials displayed in the "My Apps" dashboard. You will need: + + * **Application ID** + * **Secret Key** + * **Redirect URL** (if not the default) + + **If no access token is provided...** + + After starting multi-scrobbler with credentials in-place open the dashboard (`http://localhost:9078`) and find your Deezer source. Click **(Re)authenticate and (re)start polling** to start the login process. After login is complete polling will begin automatically. + + ###### Configuration + + + + | Environmental Variable | Required? | Default | Description | + |------------------------|-----------|-----------------------------------------|-----------------------------------| + | `DEEZER_CLIENT_ID` | Yes | | Your **Application ID** | + | `DEEZER_CLIENT_SECRET` | Yes | | Your **Secret Key** | + | `DEEZER_REDIRECT_URI` | No | `http://localhost:9078/deezer/callback` | URI must end in `deezer/callback` | + + + + {DeezerDeprecatedConfig} + + + or + + + + + + + or + + + + +
\ No newline at end of file diff --git a/docsite/docs/configuration/sources/google-cast.mdx b/docsite/docs/configuration/sources/google-cast.mdx new file mode 100644 index 00000000..2d8aa9dc --- /dev/null +++ b/docsite/docs/configuration/sources/google-cast.mdx @@ -0,0 +1,184 @@ +--- +title: Google Cast (Chromecast) +toc_min_heading_level: 2 +toc_max_heading_level: 5 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from '@theme/CodeBlock'; +import ChromecastConfig from '!!raw-loader!@site/../config/chromecast.json.example'; + +If your media device can be **Cast** to using this button ![Chromecast Icon](https://upload.wikimedia.org/wikipedia/commons/2/26/Chromecast_cast_button_icon.svg) on your phone/computer then multi-scrobbler can monitor it in order to scrobble music you play. + + +:::note + +Google Cast support is **experimental**. You may experience crashes and errors while using this Source. Please open an issue if you experience problems and include all information detailed in the issue template to help debug your issue. + +::: + +:::note + +This source relies on common, **basic** music data provided by the cast device which will always be less exhaustive than data parsed from full source integrations. If there is an existing [Source](#source-configurations) it is recommended to configure for it and blacklist the app on Google Cast, rather than relying solely on Google Cast for scrobbling. + +::: + +#### Connecting Devices + +Cast devices can be manually configured using file-based configuration OR automatically discovered using **mDNS.** + +###### mDNS Discovery + +The host machine running multi-scrobbler must be configured to allow [mDNS traffic on port 5353/UDP](https://book.hacktricks.xyz/network-services-pentesting/5353-udp-multicast-dns-mdns). + +
+ + OS Specific Instructions + + + + **Docker** + + The host machine must have [avahi-daemon](https://avahi.org/) running to circumvent limitations with DNS resolution due to musl in Alpine. Most major linux distributions package avahi and many have it built-in. Once avahi is running you must pass D-Bus and the avahi daemon socket to your container like so: + + ``` + docker run ... -v /var/run/dbus:/var/run/dbus -v /var/run/avahi-daemon/socket:/var/run/avahi-daemon/socket ... foxxmd/multi-scrobbler + ``` + + **Flatpak/Nodejs** + + No additional steps are required. + + + **Docker** + + Unsupported at this time. + + **Nodejs** + + No additional steps are required. + + + +
+ +#### What Media Does MS Scrobble? + +Cast devices report what type of media the current activity is [(see `metadata` property here)](https://developers.google.com/cast/docs/media/messages#MediaInformation). The reported type is dependent on the application playing the media to correctly report it, the cast device does not magically know what the media is. If an application does not report a type it is always classified as `unknown`. + +**By default, MS will only track media that is reported as `MusicTrack`.** + +##### Allow Unknown Media Type + +Media with an Unknown (`Generic`) media type can be explicitly allowed by setting `"allowUnknownMedia": true` in the file-based configuration. This can also be configured to only allow unknown media types for specific applications by using a list of application names. + +
+ + Example + + ```json5 title="chromecast.json" + [ + { + "name": "MyCast", + "type": "chromecast", + "data": { + // only allow unknown if app name contains any of these phrases + "allowUnknownMedia": ["smarttube", "default media receiver"] + }, + } + ] + ``` + +
+ +##### Forcing Media Tracking + +MS can be forced to track media from an application regardless of media type. This is useful if an application incorrectly reports a media type you are sure should be music. Set `"forceMediaRecognitionOn"` in the file-based configuration to a list of application names that should always be tracked. + +
+ + Example + + ```json5 title="chromecast.json" + [ + { + "name": "MyCast", + "type": "chromecast", + "data": { + // media from applications that contains these phrases will always be tracked, regardless of media type reported + "forceMediaRecognitionOn": ["smarttube", "default media receiver"] + }, + } + ] + ``` + +
+ +#### Cast Troubleshooting + +Please include any/all logs with raw output if there are any errors encountered as this is critical to diagnosing issues. + +To diagnose bad/incomplete track information or strange MS player behavior please turn on **payload logging** and include log output of the source running to help diagnose this issue. Either enable in config using the below example OR enable [Debug Mode.](#debug-mode) + +
+ + Example + + ```json5 title="chromecast.json" + [ + { + "name": "MyCast", + "type": "chromecast", + "data": { + //... + }, + "options": { + "logPayload": true + } + } + ] + ``` + +
+ +## Configuration + + + + :::note + [Manually configuring cast device connections](#connecting-devices) is only available through file-based config. + ::: + + | Environmental Variable | Required? | Default | Description | + |------------------------|-----------|---------|--------------------------------------------------------------------------------------| + | CC_ENABLE | No | | Set to 'true' to enable Cast monitoring without needing to define other ENVs | + | CC_WHITELIST_DEVICES | No | | Only scrobble from these Cast devices. Comma-delimited list. EX mini-home, family-tv | + | CC_BLACKLIST_DEVICES | No | | Do not scrobble from these Cast devices. Comma-delimited list | + | CC_WHITELIST_APPS | No | | Only scrobble from these casted Apps. Comma-delimited list. EX spotify, pandora | + | CC_BLACKLIST_APPS | No | | Do not scrobble from these casted Apps. Comma-delimited list | + + + +
+ + Example + + {ChromecastConfig} + +
+ + or +
+ +
+ + Example + + + +
+ + or +
+
\ No newline at end of file diff --git a/docsite/docs/configuration/sources/icecast.mdx b/docsite/docs/configuration/sources/icecast.mdx new file mode 100644 index 00000000..6bbf1e94 --- /dev/null +++ b/docsite/docs/configuration/sources/icecast.mdx @@ -0,0 +1,97 @@ +--- +title: Icecast +toc_min_heading_level: 2 +toc_max_heading_level: 5 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from '@theme/CodeBlock'; +import IcecastConfig from '!!raw-loader!@site/../config/icecast.json.example'; +import ShouldScrobble from "@site/src/components/snippets/_should-scrobble.mdx" + +Parse metadata from any Icecast Station (streams) to use for scrobbling. + +:::note + +There is no standard format for presenting track information in an Icecast stream and the majority of streams that do include metadata only use a single string (`StreamTitle`) from which metadata can be parsed. + +Multi-scrobbler will do its best to parse Artist/Album/Track information from this data but the quality and consistency for this is solely based on how the Icecast broadcaster chooses to format their tracks. + +::: + +:::note[Scrobbling Behavior] + +Multi-scrobbler cannot automatically determine if you are listening to a Station. Therefore, **automatic scrobbling for Icecast Sources is off by default.** + +Use [Should Scrobble Behavior](/configuration/sources/overview#should-scrobble-behavior) to initiate scrobbling or configure automatic scrobble. + +::: + +:::tip[Scrobbling Threshold] + +Icecast does not report track lengths so multi-scrobbler cannot determine if it should scrobble based on percent played. You should instead set a shorter "listened to" **duration** using [Scrobble Thresholds.](/configuration/sources/overview#scrobble-thresholds) + +::: + + +#### URL and Sources + +The URL should be the URL you would use to stream audio. + +Additional sources are supported based on those found in [`icecast-metadata-stats`](https://github.com/eshaz/icecast-metadata-js/tree/main/src/icecast-metadata-stats#supported-status-apis). To use these with automatic parsing [based on the regular URL](https://github.com/eshaz/icecast-metadata-js/tree/main/src/icecast-metadata-stats#options) include a `sources` array in your config the same way as shown in `icecast-metadata-stats`: + +
+ + Example + +```json5 title="config.json" +[ + { + "name": "myIcy", + "enable": true, + "data": { + "url": "http://myIcecast.com/radio", + "sources": ["icy", "ogg", "sevenhtml"] + } + } + ] + +``` +
+ +To manually set these sources include the same [`*Endpoint` options](https://github.com/eshaz/icecast-metadata-js/tree/main/src/icecast-metadata-stats#options) found in `icecast-metadata-stats`. + +If no `sources` are configured for multi-scrobbler it will automatically try to use `icy` and `ogg`. + +## Configuration + + + + | Environmental Variable | Required? | Default | Description | + | :--------------------- | :-------- | :------ | ----------- | + | `ICECAST_URL` | Yes | | Icecast URL | + + +
+ + Example + + {IcecastConfig} + +
+ + or +
+ +
+ + Example + + + +
+ + or +
+
\ No newline at end of file diff --git a/docsite/docs/configuration/sources/jellyfin.mdx b/docsite/docs/configuration/sources/jellyfin.mdx new file mode 100644 index 00000000..65135f24 --- /dev/null +++ b/docsite/docs/configuration/sources/jellyfin.mdx @@ -0,0 +1,67 @@ +--- +title: Jellyfin +toc_min_heading_level: 2 +toc_max_heading_level: 5 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from '@theme/CodeBlock'; +import JellyfinConfig from '!!raw-loader!@site/../config/jellyfin.json.example'; + +Must be using Jellyfin 10.7 or greater + +* Create an **API Key** for multi-scrobbler + * In the Jellyfin desktop web UI Navigate to -> Administration -> Dashboard -> API Keys (`http://YOUR_JELLYIN_URL/web/index.html#!/apikeys.html`) + * Click `+` button and create a new key with **App name** `multi-scrobbler` + * Copy the created API Key value for use in configuration below + + +It is **recommended** to use API Key + username but if you are not an admin for your Jellyfin instance you can also authenticate with your Jellyfin username and **password.** + +:::tip[Important Defaults] + +By default... + +* multi-scrobbler will **only** scrobble for the user authenticated with the API. + * Allowed Users (`usersAllow` or `JELLYFIN_USERS_ALLOW`) are only necessary if you want to scrobble for additional users. +* multi-scrobbler will **only** scrobble media found in Jellyfin libraries that were labelled as **Music.** + * `librariesAllow` or `JELLYFIN_LIBRARIES_ALLOW` will override this + * OR use `additionalAllowedLibraryTypes` to allow more types (like `mixed` or `book` for audiobooks) +* multi-scrobbler will **only** scrobble media Jellyfin detects as **Audio.** + * To force multi-scrobbler to scrobble when media is detected as **Unknown** use `"allowUnknown": true` in file/aio configuration. + +::: + +#### Configuration + + + + | Environmental Variable | Required? | Default | Description | + | -------------------------- | --------- | ------- | ------------------------------------------------------------------------------------------ | + | `JELLYFIN_URL` | **Yes** | | The URL of the Jellyfin server IE `http://localhost:8096` | + | `JELLYFIN_USER` | **Yes** | | The user to authenticate with the API | + | `JELLYFIN_APIKEY` | No | | The API Key to use for authentication **(Must provide either apikey or password)** | + | `JELLYFIN_PASSWORD` | No | | The password of the user to authenticate for. **(Must provide either apikey or password)** | + | `JELLYFIN_USERS_ALLOW` | No | | Comma-separated list of usernames (from Jellyfin) to scrobble for | + | `JELLYFIN_USERS_BLOCK` | No | | Comma-separated list of usernames (from Jellyfin) to disallow scrobble for | + | `JELLYFIN_DEVICES_ALLOW` | No | | Comma-separated list of devices to scrobble from | + | `JELLYFIN_DEVICES_BLOCK` | No | | Comma-separated list of devices to disallow scrobbles from | + | `JELLYFIN_LIBRARIES_ALLOW` | No | | Comma-separated list of libraries to allow scrobbles from | + | `JELLYFIN_LIBRARIES_BLOCK` | No | | Comma-separated list of libraries to disallow scrobbles from | + + + + {JellyfinConfig} + + or + + + + + + + + or + + \ No newline at end of file diff --git a/docsite/docs/configuration/sources/jriver.mdx b/docsite/docs/configuration/sources/jriver.mdx new file mode 100644 index 00000000..1155ff12 --- /dev/null +++ b/docsite/docs/configuration/sources/jriver.mdx @@ -0,0 +1,76 @@ +--- +title: JRiver +toc_min_heading_level: 2 +toc_max_heading_level: 5 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from '@theme/CodeBlock'; +import JriverConfig from '!!raw-loader!@site/../config/jriver.json.example'; + +In order for multi-scrobbler to communicate with JRiver you must have [Web Server Interface](https://wiki.jriver.com/index.php/Web_Service_Interface#Documentation_of_Functions) enabled. This can can be in the JRiver GUI: + +* Tools -> Options -> Media Network + * Check `Use Media Network to share this library...` + * If you have `Authentication` checked you will need to provide the **Username** and **Password** in the ENV/File configuration below. + +### URL + +The URL used to connect to JRiver, set in your [file config as `url`.](#jriver-config) The URL used to connect ultimately must be formed like this: + +``` +Syntax => [protocol]://[hostname]:[port]/[path] +Default => http://localhost:52199/MCWS/v1/ +``` + +If any part of this URL is missing multi-scrobbler will use a default value. This also means that if any part of your URL is **not** standard you must explicitly define it. + +:::tip[Jriver Settings] + +* Make sure the port number matches what is found in `Advanced` section in the [Media Network](#jriver) options. +* If your installation is on the same machine but you cannot connect using `localhost` try `0.0.0.0` instead. + +::: + +
+ URL Transform Examples + + Examples of a given `url` in your file config and the final URL multi-scrobbler will use to connect to JRiver: + + | `url` | Transformed URL | + |------------------------------------------|---------------------------------------------| + | (none set) | `http://localhost:52199/MCWS/v1/` | + | `jriver.mydomain.com` | `http://jriver.mydomain.com:52199/MCWS/v1/` | + | `192.168.0.101:3456` | `http://192.168.0.101:3456/MCWS/v1/` | + | `mydomain.com:80/jriverReverse/MCWS/v1/` | `mydomain.com:80/jriverReverse/MCWS/v1/` | + +
+ +## Configuration + + + + + | Environmental Variable | Required | Default | Description | + |------------------------|----------|---------------------------------|------------------------------------------------| + | JRIVER_URL | Yes | http://localhost:52199/MCWS/v1/ | The URL of the JRiver server | + | JRIVER_USERNAME | No | | If authentication is enabled, the username set | + | JRIVER_PASSWORD | No | | If authenticated is enabled, the password set | + + + + + {JriverConfig} + + or + + + + + + + or + + + \ No newline at end of file diff --git a/docsite/docs/configuration/sources/kodi.mdx b/docsite/docs/configuration/sources/kodi.mdx new file mode 100644 index 00000000..7a5bf984 --- /dev/null +++ b/docsite/docs/configuration/sources/kodi.mdx @@ -0,0 +1,59 @@ +--- +title: Kodi +toc_min_heading_level: 2 +toc_max_heading_level: 5 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from '@theme/CodeBlock'; +import KodiConfig from '!!raw-loader!@site/../config/kodi.json.example'; +import Config from '@site/src/components/GenericConfiguration.mdx'; + +In order for multi-scrobbler to communicate with Kodi you must have the [Web Interface](https://kodi.wiki/view/Web_interface) enabled. This can be done in the Kodi GUI: + +* Settings -> Services -> Control + * Check `Allow remote control via HTTP` + * Ensure you have a **Username** and **Password** set, you will need to provide them in the ENV/File configuration below. + +### URL + +The URL used to connect to Kodi, set in your [file config as `url`.](#kodi-config) The URL used to connect ultimately must be formed like this: + +``` +Syntax => [protocol]://[hostname]:[port]/[path] +Default => http://localhost:8080/jsonrpc +``` + +If any part of this URL is missing multi-scrobbler will use a default value. This also means that if any part of your URL is **not** standard you must explicitly define it. + +:::tip[Kodi Settings] + +* Make sure the port number matches what is found in **Port** in the [Control](#kodi) section mentioned above. +* If your installation is on the same machine but you cannot connect using `localhost` try `0.0.0.0` instead. + +::: + +
+ URL Transform Examples + + Examples of a given `url` in your file config and the final URL multi-scrobbler will use to connect to Kodi: + + | `url` | Transformed URL | + |---------------------------------------|----------------------------------------------| + | (none set) | `http://localhost:8080/jsonrpc` | + | `kodi.mydomain.com` | `http://kodi.mydomain.com:8080/jsonrpc` | + | `192.168.0.101:3456` | `http://192.168.0.101:3456/jsonprc` | + | `mydomain.com:80/kodiReverse/jsonrpc` | `http://mydomain.com:80/kodiReverse/jsonrpc` | + +
+ +## Configuration + + + | Environmental Variable | Required | Default | Description | + |------------------------|----------|-------------------------------|----------------------------| + | KODI_URL | Yes | http://localhost:8080/jsonrpc | The URL of the Kodi server | + | KODI_USERNAME | No | | The username set | + | KODI_PASSWORD | No | | The password set | + \ No newline at end of file diff --git a/docsite/docs/configuration/sources/koito-source.mdx b/docsite/docs/configuration/sources/koito-source.mdx new file mode 100644 index 00000000..cb90afe7 --- /dev/null +++ b/docsite/docs/configuration/sources/koito-source.mdx @@ -0,0 +1,31 @@ +--- +title: Koito +toc_min_heading_level: 2 +toc_max_heading_level: 5 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from '@theme/CodeBlock'; +import KoitoConfig from '!!raw-loader!@site/../config/koito.json.example'; + +This Source monitors a Koito account's scrobble history and then re-scrobbles discovered tracks to configured [Clients.](/configuration/clients/overview) + +:::tip[Other Uses] + +To _scrobble to_ a Koito server, create a [Koito (Client)](/configuration/clients/koito) + +::: + +See the [Koito (Client)](/configuration/clients/koito) configuration for general setup. The only difference for **Source** configuration: + +* Cannot be setup with ENV config +* [File/AIO config](/configuration?configType=file#configuration-types) must include `"configureAs": "source"` + +#### Configuration + + +:::note + You cannot use ENV variables shown in the [Koito Client config](/configuration/clients/koito) -- multi-scrobbler assumes Koito ENVs are always used for the **client** configuration. You must use the [File or AIO](/configuration?configType=file#configuration-types) config to setup Koito as a Source. +::: + \ No newline at end of file diff --git a/docsite/docs/configuration/sources/lastfm-endpoint.mdx b/docsite/docs/configuration/sources/lastfm-endpoint.mdx new file mode 100644 index 00000000..bf99c870 --- /dev/null +++ b/docsite/docs/configuration/sources/lastfm-endpoint.mdx @@ -0,0 +1,46 @@ +--- +title: Lastfm (Endpoint) +toc_min_heading_level: 2 +toc_max_heading_level: 5 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from '@theme/CodeBlock'; +import LastfmEndpointConfig from '!!raw-loader!@site/../config/endpointlfm.json.example'; + +This Source enables multi-scrobbler to accept scrobbles from outside applications as if it was a Last.fm server. + +:::tip[Other Uses] + +* To **scrobble to** Last.fm, create a [Last.fm (Client)](/configuration/clients/lastfm) +* To monitor and re-scrobble activity from your Last.fm account, create a [Last.fm (Source)](/configuration/sources/lastfm-source) + +::: + +### URL + +If a **slug** is **not** provided in configuration then multi-scrobbler will accept Last.fm scrobbles at + +``` +http://localhost:9078/2.0/ +``` + +which is the "standard" Last.fm server path for scrobbling + +Use a slug only if you need to setup multiple Last.fm Endpoint sources and cannot use different tokens. + +If a slug is used then the URL will be: + +``` +http://localhost:9078/api/lastfm/mySlug +``` + +## Configuration + + + | Environmental Variable | Required? | Default | Description | + | :--------------------- | :-------- | ------- | ------------------------------------------------------------------------------------------------------------------ | + | `LFMENDPOINT_ENABLE` | No | | Use LFM Endpoint as a Source without any other configuration. Only required if slug/token are not provided as ENVs | + | `LFM_SLUG` | No | | (Optional) The URL suffix to use for accepting LFM scrobbles | + \ No newline at end of file diff --git a/docsite/docs/configuration/sources/lastfm-source.mdx b/docsite/docs/configuration/sources/lastfm-source.mdx new file mode 100644 index 00000000..d0de2e87 --- /dev/null +++ b/docsite/docs/configuration/sources/lastfm-source.mdx @@ -0,0 +1,29 @@ +--- +title: Lastfm +toc_min_heading_level: 2 +toc_max_heading_level: 5 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from '@theme/CodeBlock'; +import LastfmConfig from '!!raw-loader!@site/../config/lastfm.json.example'; + +This Source **monitors your Last.fm scrobble history** and re-scrobbles new activity to configured [Clients.](/configuration/clients/overview) + +:::tip[Other Uses] + +* To **scrobble to** Last.fm, create a [Last.fm (Client)](/configuration/clients/lastfm) +* To accept scrobbles from outside applications as if multi-scrobbler was Last.fm, create a [Last.fm (Endpoint)](/configuration/sources/lastfm-endpoint) + +::: + +To configure this Source use the same registration instructions and configuration data shown in [Last.fm (Client)](/configuration/clients/lastfm) with the difference being `"configureAs": "source"` defined in the File/AIO configuration below. + +## Configuration + + +:::note + You cannot use ENV variables shown in the [Last.fm Client config](/configuration/clients/lastfm) -- multi-scrobbler assumes Last.fm ENVs are always used for the **client** configuration. You must use the [File or AIO](/configuration?configType=file#configuration-types) config to setup Last.fm as a Source. +::: + \ No newline at end of file diff --git a/docsite/docs/configuration/sources/listenbrainz-endpoint.mdx b/docsite/docs/configuration/sources/listenbrainz-endpoint.mdx new file mode 100644 index 00000000..5caf8cbb --- /dev/null +++ b/docsite/docs/configuration/sources/listenbrainz-endpoint.mdx @@ -0,0 +1,56 @@ +--- +title: Listenbrainz (Endpoint) +toc_min_heading_level: 2 +toc_max_heading_level: 5 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from '@theme/CodeBlock'; +import ListenbrainzEndpointConfig from '!!raw-loader!@site/../config/endpointlz.json.example'; + +This Source enables multi-scrobbler to accept scrobbles from outside applications as if it was a Listenbrainz server. + +:::tip[Other Uses] + +* To **scrobble to** Listenbrainz, create a [Listenbrainz (Client)](/configuration/clients/listenbrainz) +* To monitor and re-scrobble activity from your Listenbrainz account, create a [Listenbrainz (Source)](/configuration/sources/listenbrainz-source) + +::: + +##### URL + +If a **slug** is **not** provided in configuration then multi-scrobbler will accept Listenbrainz scrobbles at + +``` +http://localhost:9078/1/submit-listens +``` + +which is the "standard" Listenbrainz server path for scrobbling. + +Use a slug only if you need to setup multiple Listenbrainz Endpoint sources and cannot use different tokens. + +If a slug is used then the URL will be: + +``` +http://localhost:9078/api/listenbrainz/mySlug +``` + +:::note + Some Listenbrainz applications may require custom Listenbrainz URLs to be a real domain (`example.com`) and/or use SSL (`https://`). In this case you should setup multi-scrobbler behind a reverse proxy to support this functionality. It is out of the scope of this project to do this solely within multi-scrobbler. +::: + +##### Token + +Most Listenbrainz clients require a token (Authentication Token) to be provided during setup. This value can be anything you want, just make sure to use the same value for `token` in your multi-scrobbler configuration for the endpoint. + + +## Configuration + + + | Environmental Variable | Required? | Default | Description | + | :--------------------- | :-------- | ------- | ----------------------------------------------------------------------------------------------------------------- | + | `LZENDPOINT_ENABLE` | No | | Use LZ Endpoint as a Source without any other configuration. Only required if slug/token are not provided as ENVs | + | `LZE_TOKEN` | No | | LZ "Authentication Token" you provided to the scrobbling application | + | `LZE_SLUG` | No | | (Optional) The URL suffix to use for accepting LZ scrobbles | + \ No newline at end of file diff --git a/docsite/docs/configuration/sources/listenbrainz-source.mdx b/docsite/docs/configuration/sources/listenbrainz-source.mdx new file mode 100644 index 00000000..fdff15af --- /dev/null +++ b/docsite/docs/configuration/sources/listenbrainz-source.mdx @@ -0,0 +1,31 @@ +--- +title: Listenbrainz +toc_min_heading_level: 2 +toc_max_heading_level: 5 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from '@theme/CodeBlock'; +import ListenbrainzConfig from '!!raw-loader!@site/../config/listenbrainz.json.example'; + +This Source monitors a Listenbrainz account's scrobble history and then re-scrobbles discovered tracks to configured clients. + +:::tip[Other Uses] + +* To **scrobble to** a Listenbrainz server, create a [Listenbrainz (Client)](/configuration/clients/listenbrainz) +* To accept scrobbles from outside applications as if multi-scrobbler was a Listenbrainz server, create a [Listenbrainz (Endpoint)](/configuration/sources/listenbrainz-endpoint) + +::: + +You will need to run your own Listenbrainz server or have an account [on the official instance](https://listenbrainz.org/login/). + +On your [profile page](https://listenbrainz.org/profile/) find your **User Token** to use in the configuration. + +## Configuration + + + :::note + You cannot use ENV variables shown in the [Listenbrainz Client config](/configuration/clients/listenbrainz) -- multi-scrobbler assumes Listenbrainz ENVs are always used for the **client** configuration. You must use the [File or AIO](/configuration?configType=file#configuration-types) config to setup Listenbrainz as a Source. + ::: + \ No newline at end of file diff --git a/docsite/docs/configuration/sources/maloja.mdx b/docsite/docs/configuration/sources/maloja.mdx new file mode 100644 index 00000000..acc4c4e8 --- /dev/null +++ b/docsite/docs/configuration/sources/maloja.mdx @@ -0,0 +1,29 @@ +--- +title: Maloja +toc_min_heading_level: 2 +toc_max_heading_level: 5 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from '@theme/CodeBlock'; +import MalojaConfig from '!!raw-loader!@site/../config/maloja.json.example'; + +:::tip[Other Uses] + +To _scrobble to_ a Maloja server, create a [Maloja (Client)](/configuration/clients/maloja) + +::: + +See the [Maloja (Client)](/configuration/clients/maloja) configuration for general setup. The only difference for **Source** configuration: + +* Cannot be setup with ENV config +* [File/AIO config](/configuration?configType=file#configuration-types) must include `"configureAs": "source"` + +## Configuration + + + :::note + You cannot use ENV variables shown in the [Maloja Client config](/configuration/clients/maloja) -- multi-scrobbler assumes Maloja ENVs are always used for the **client** configuration. You must use the [[File or AIO](/configuration?configType=file#configuration-types) config to setup Maloja as a Source. + ::: + \ No newline at end of file diff --git a/docsite/docs/configuration/sources/mopidy.mdx b/docsite/docs/configuration/sources/mopidy.mdx new file mode 100644 index 00000000..ef64c833 --- /dev/null +++ b/docsite/docs/configuration/sources/mopidy.mdx @@ -0,0 +1,102 @@ +--- +title: Mopidy +toc_min_heading_level: 2 +toc_max_heading_level: 5 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from '@theme/CodeBlock'; +import MopidyConfig from '!!raw-loader!@site/../config/mopidy.json.example'; + +Mopidy is a headless music server that supports playing music from many [standard and non-standard sources such as Pandora, Bandcamp, and Tunein.](https://mopidy.com/ext/) + +multi-scrobbler can scrobble tracks played from any Mopidy backend source, regardless of where you listen to them. + +:::note + +You must have [Mopidy-HTTP extension](https://mopidy.com/ext/http) enabled for this integration to work. + +::: + +#### URL + +The URL used to connect to the Mopidy server, set in your [file config as `url`.](#mopidy-config) The URL used to connect ultimately must be formed like this: + +``` +Syntax => [protocol]://[hostname]:[port]/[path] +Default => ws://localhost:6680/mopidy/ws +``` + +If any part of this URL is missing multi-scrobbler will use a default value. This also means that if any part of your URL is **not** standard you must explicitly define it. + +:::tip[Mopidy Configuration] + +Make sure the hostname and port number match what is found in the Mopidy configuration file `mopidy.conf`: + +```ini +[http] +hostname = localhost +port = 6680 +``` + +::: + +
+URL Transform Examples + + Examples of a given `url` in your file config and the final URL multi-scrobbler will use to connect to Mopidy: + + | `url` | Transformed URL | + |--------------------------------|--------------------------------------------| + | (none set) | `ws://localhost:6680/mopidy/ws` | + | `mopidy.mydomain.com` | `ws://mopidy.mydomain.com:6680/mopidy/ws/` | + | `192.168.0.101:3456` | `ws://192.168.0.101:3456/mopidy/ws/` | + | `mopidy.mydomain.com:80/MOPWS` | `ws://mopidy.mydomain.com:80/MOPWS` | + +
+ + +### URI Blacklist/Whitelist + +If you wish to disallow or only allow scrobbling from some sources played through Mopidy you can specify these using `uriBlacklist` or `uriWhitelist` in your config. multi-scrobbler will check the list to see if any string matches the START of the `uri` on a track. If whitelist is used then blacklist is ignored. All strings are case-insensitive. + +
+ Example + ```json + { + "uriBlacklist": ["soundcloud"] + } + ``` + + Will prevent multi-scrobbler from scrobbling any Mopidy track that start with a `uri` like `soundcloud:song:MySong-1234` +
+ + +### Album Blacklist + +For certain sources (Soundcloud) Mopidy does not have all track info (Album) and will instead use "Soundcloud" as the Album name. You can prevent multi-scrobbler from using this bad Album data by adding the fake Album name to this list. Multi-scrobbler will still scrobble the track, just without the bad data. All strings are case-insensitive. + +
+ Example + ```json + { + "albumBlacklist": ["SoundCloud", "Mixcloud"] + } + ``` + + If a track would be scrobbled like + ``` + Album: Soundcloud, Track: My Cool Track, Artist: A Cool Artist + ``` + then multi-scrobbler will instead scrobble + ``` + Track: My Cool Track, Artist: A Cool Artist + ``` +
+ +## Configuration + + +No ENV support + \ No newline at end of file diff --git a/docsite/docs/configuration/sources/mpd.mdx b/docsite/docs/configuration/sources/mpd.mdx new file mode 100644 index 00000000..491fbc34 --- /dev/null +++ b/docsite/docs/configuration/sources/mpd.mdx @@ -0,0 +1,28 @@ +--- +title: MPD (Music Player Daemon) +toc_min_heading_level: 2 +toc_max_heading_level: 5 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from '@theme/CodeBlock'; +import MPDConfig from '!!raw-loader!@site/../config/mpd.json.example'; + +MS communicates with MPD using the [TCP client connection.](https://mpd.readthedocs.io/en/stable/user.html#client-connections) + +You should uncomment/create the following settings in your mpd config: + +``` +bind_to_address "any" # or a specific ipv4/v6 address +port "6600" +``` + +## Configuration + + + | Environmental Variable | Required? | Default | Description | + |------------------------|-----------|------------------|-------------| + | `MPD_URL` | No | `localhost:6600` | | + | `MPD_PASSWORD` | No | | | + \ No newline at end of file diff --git a/docsite/docs/configuration/sources/mpris.mdx b/docsite/docs/configuration/sources/mpris.mdx new file mode 100644 index 00000000..ade9c149 --- /dev/null +++ b/docsite/docs/configuration/sources/mpris.mdx @@ -0,0 +1,79 @@ +--- +title: MPRIS +toc_min_heading_level: 2 +toc_max_heading_level: 5 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from '@theme/CodeBlock'; +import MPRISConfig from '!!raw-loader!@site/../config/mpris.json.example'; + +[MPRIS](https://specifications.freedesktop.org/mpris-spec/latest) is a standard interface for communicating with Music Players on **linux operating systems.** + +If you run Linux and have a notification tray that shows what media you are listening to, you likely have access to MPRIS. + +![Notification Tray](../mpris.jpg) + +multi-scrobbler can listen to this interface and scrobble tracks played by **any media player** that communicates to the operating system with MPRIS. + +##### Host Setup + +MPRIS communication requires multi-scrobbler to have access to the host's dbus-daemon. + + + + If multi-scrobbler is running as a [**Local Installation**](../../installation/installation.mdx#nodejs), directly with node/npm, on the same host as MPRIS then no setup is required. + + Remote setup is also possible, see the **Remote Unix Socket** section in the Docker tab. + + + If multi-scrobbler is running as a [**Docker Container**](../../installation/installation.mdx#docker) some modifications are required... + + ##### Local Unix Socket + + The most secure way is to **run a multi-scrobbler container on the same host as MPRIS.** In this scenario you can simply mount the **user session** dbus unix socket into the container. Make sure [`PUID` and `PGID` ENVs are configured](../installation/#linux-host) so the container has permissions to access the socket. Then, modify your compose stack with these additional values: + + ```yaml title="docker-compose.yaml" + + environment: + - DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/UID/bus + + volumes: + - /run/user/UID/bus:/run/user/UID/bus:ro + ``` + + ##### Remote Unix Socket + + :::warning + + This method is **insecure.** You should not expose dbus over a network unless access to the port is restricted and the network is trusted. + + ::: + + Use [`socat`](https://linux.die.net/man/1/socat) to bidirectionally relay communication from the host's dbus socket to a listening TCP port. + + To make dbus for user with uid `1000` available on port `6644`: + + ```shell + socat TCP-LISTEN:6644,reuseaddr,fork UNIX-CONNECT:/run/user/1000/bus + ``` + + Then, add the following value to multi-scrobbler's ENVs: + + ``` + DBUS_SESSION_BUS_ADDRESS=tcp:host=yourHostIP,port=6644 + ``` + + + + +## Configuration + + + | Environmental Variable | Required? | Default | Description | + |------------------------|-----------|---------|----------------------------------------------------------------------------------| + | MPRIS_ENABLE | No | | Use MPRIS as a Source (useful when you don't need any other options) | + | MPRIS_BLACKLIST | No | | Comma-delimited list of player names not to scrobble from | + | MPRIS_WHITELIST | No | | Comma-delimited list of players names to ONLY scrobble from. Overrides blacklist | + \ No newline at end of file diff --git a/docsite/docs/configuration/sources/musikcube.mdx b/docsite/docs/configuration/sources/musikcube.mdx new file mode 100644 index 00000000..cf427303 --- /dev/null +++ b/docsite/docs/configuration/sources/musikcube.mdx @@ -0,0 +1,38 @@ +--- +title: Musikcube +toc_min_heading_level: 2 +toc_max_heading_level: 5 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from '@theme/CodeBlock'; +import MusikcubeConfig from '!!raw-loader!@site/../config/musikcube.json.example'; + +In order to use [Musikcube](https://musikcube.com) configure it to accept [websocket connections](https://github.com/clangen/musikcube/wiki/remote-api-documentation) in **server setup**: + +* Enable the **Metadata Server** +* Set a **Password** + +Both of these settings are found in _Musikcube -> (s)ettings -> server setup_ + +![Server Setup](../musikcube.jpg) + +The URL used by MS has the syntax: + +``` +[ws|wss]://HOST:[PORT] +``` + +The **port** is the same as shown in the server setup screenshot from above, under **metadata server enabled**. If no port is provided to MS it will default to `7905`. + +If no URL is provided to MS it will try to use `ws://localhost:7905` + +## Configuration + + + | Environmental Variable | Required? | Default | Description | + |------------------------|-----------|-----------------------|--------------------------------------| + | `MC_URL` | No | `ws://localhost:7905` | Use port set for **metadata server** | + | `MC_PASSWORD` | Yes | | | + \ No newline at end of file diff --git a/docsite/docs/configuration/sources/plex.mdx b/docsite/docs/configuration/sources/plex.mdx new file mode 100644 index 00000000..799f50f4 --- /dev/null +++ b/docsite/docs/configuration/sources/plex.mdx @@ -0,0 +1,79 @@ +--- +title: Plex +toc_min_heading_level: 2 +toc_max_heading_level: 5 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from '@theme/CodeBlock'; +import AzuracastConfig from '!!raw-loader!@site/../config/azuracast.json.example'; +import PlexConfig from '!!raw-loader!@site/../config/plex.json.example'; + +Find your [**Plex Token**](https://support.plex.tv/articles/204059436-finding-an-authentication-token-x-plex-token/) and make note of the **URL** and **Port** used to connect to your Plex instance. + +
+ +Allowed Users and Defaults + +**Multi-scrobbler will automatically scrobble for these users by default:** + +* The User authenticated with the Plex Token +* and the **Local User** + +The Local User (`PLEX_LOCAL_USER`) is how Plex identifies anyone directly accessing the Plex UI from a local IP (who does not need to login). + +To allow MS to scrobble for other users use `usersAllow` or `PLEX_USERS_ALLOW` (env) from the below configuration docs. However, because you are overriding the default settings you must also explicitly list the authenticated user and the Local User if you want them to also be able to scrobble. + +
+ +Examples + +###### Defaults + +If `usersallow` and `PLEX_USERS_ALLOW` are not defined then the Plex Token authenticated User and Local User will be scrobbled for. + + +###### Only A Specific User + +* `"usersallow": ["SomeUser"]` or +* `PLEX_USERS_ALLOW: SomeUser` + +Only the Plex user `SomeUser` will be scrobbled for. The Plex Token authenticated user and the Local User will not be scrobbled for. + +###### A Specific User + Defaults + +(Assuming the plex authenticated user is `FoxxMD`) + +* `"usersallow": ["FoxxMD", "PLEX_LOCAL_USER", "SomeUser"]` or +* `PLEX_USERS_ALLOW: FoxxMD,PLEX_LOCAL_USER,SomeUser` + +The Plex user SomeUser, the Plex Token authenticated user (FoxxMD) and the Local User will be scrobbled for. + +
+ +
+ +
+ +Allowed Libraries and Defaults + +By default multi-scrobbler will only scrobble media found in Plex libraries that are labelled as **Music.** + * `librariesAllow` or `PLEX_LIBRARIES_ALLOW` will override this + +
+ +#### Configuration + + + | Environmental Variable | Required? | Default | Description | + | ---------------------- | --------- | ------- | ---------------------------------------------------------------------- | + | `PLEX_URL` | **Yes** | | The URL of the Plex server IE `http://localhost:32400` | + | `PLEX_TOKEN` | **Yes** | | The **Plex Token** to use with the API | + | `PLEX_USERS_ALLOW` | No | | Comma-separated list of usernames (from Plex) to scrobble for | + | `PLEX_USERS_BLOCK` | No | | Comma-separated list of usernames (from Plex) to disallow scrobble for | + | `PLEX_DEVICES_ALLOW` | No | | Comma-separated list of devices to scrobble from | + | `PLEX_DEVICES_BLOCK` | No | | Comma-separated list of devices to disallow scrobbles from | + | `PLEX_LIBRARIES_ALLOW` | No | | Comma-separated list of libraries to allow scrobbles from | + | `PLEX_LIBRARIES_BLOCK` | No | | Comma-separated list of libraries to disallow scrobbles from | + \ No newline at end of file diff --git a/docsite/docs/configuration/sources/spotify.mdx b/docsite/docs/configuration/sources/spotify.mdx new file mode 100644 index 00000000..bbf705a1 --- /dev/null +++ b/docsite/docs/configuration/sources/spotify.mdx @@ -0,0 +1,105 @@ +--- +title: Spotify +toc_min_heading_level: 2 +toc_max_heading_level: 5 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from '@theme/CodeBlock'; +import SpotifyConfig from '!!raw-loader!@site/../config/spotify.json.example'; + +
+ +Spotify and Automix + +If your Spotify player has [Automix](https://community.spotify.com/t5/FAQs/What-is-Automix/ta-p/5257278) enabled and Spotify uses it for your playlist/queue then MS cannot accurately determine when a track will end. This is because the track is "mixed" in your queue with a shorter play time than its actual length and [Spotify does not report this modified play time in its API.](https://community.spotify.com/t5/Spotify-for-Developers/Wrong-duration-ms-of-track-with-Automix/m-p/5429147) This **does not affect MS's ability to scrobble** from Spotify but it will affect the accuracy of the duration MS reports was played. + +
+ +#### Authenticate Spotify with Multi-Scrobbler + +To access your Spotify history you must [create a Spotify App](https://developer.spotify.com/dashboard) to get a +Client ID/Secret. + +1. **Login to https://developer.spotify.com** with your existing Spotify account and accept Developer Terms + +2. Navigate to your Spotify Developer [Dashboard](https://developer.spotify.com/dashboard) and start the **Create App** process + +3. Determine the correct **Redirect URI** to use and set it in your App settings + +
+ +Redirect URI Instructions + +A Redirect URI is the URL that Spotify will navigate your **browser** to after you complete authorization. The URL will contain the code necessary for multi-scrobbler to get a Spotify access token. + +Spotify [**no longer allows insecure URIs**](https://developer.spotify.com/documentation/web-api/tutorials/migration-insecure-redirect-uri) (start with `http://`) unless the address is `127.0.0.1` -- so `localhost` and internal IPs (`192.168.0.xxx`) no longer work. + +Use **one** of the following methods to specify a valid Redirect URI and complete Spotify authentication with multi-scrobbler: + + + + **Use a domain you control, with SSL certificates/HTTPS enabled, to create a valid redirect URI.** + + For example, if you are already running multi-scrobbler behind a reverse proxy (nginx/traefik/caddy) at `https://scrobbler.mydomain.com` then set the Spotify Redirect URI for your Spotify App, and multi-scrobbler `SPOTIFY_REDIRECT_URI` ENV, to `https://scrobbler.mydomain.com/callback` + + Alternatively, if multi-scrobbler is not accessible behind your domain, then use the [**Echo**](./?spotifyRedirect=echo#spotify) method with your own domain instead of 127.0.0.1. + + + **Run multi-scrobbler, configured for a Spotify Source, from the same machine as your browser in order to get credentials.** + + In this scenario you can use `http://127.0.0.1:9078/callback` as the Spotify Redirect URI and multi-scrobbler `SPOTIFY_REDIRECT_URI` ENV. + + After completing authentication, copy the contents of local multi-scrobbler's [configuration folder](../installation/installation.mdx#storage) to the final location of where multi-scrobbler will be run. + + + **Use a docker container on the same machine your browser is on to "intercept" the callback URL from Spotify authentication, then manually finish the process by changing the domain to your multi-scrobbler instance.** + + + 1. Create a container on the same machine as your browser using the [`mendhak/http-https-echo:36`](https://github.com/mendhak/docker-http-https-echo) image. This container runs a simple HTTP server that echos back any request it recieves. + + ```shell + docker run --rm -p 127.0.0.1:5079:8080 mendhak/http-https-echo:36 + ``` + + 2. Set the Spotify Redirect URI and multi-scrobbler `SPOTIFY_REDIRECT_URI` ENV as `http://127.0.0.1:5079/callback` + + 3. In [Authenticate Spotify with Multi-Scrobbler](#authenticate-spotify-with-multi-scrobbler) (outside these Redirect Instructions) go to **Step 4** and continue the instructions + + Eventually, Spotify will redirect you to a web page with a URL like `http://127.0.0.1:5079/callback?code=xxxxxx...` + + ![echo](../echo.png) + + 4. In the URL, replace `127.0.0.1:5079` with the address/IP for your multi-scrobbler instance so the URL is similar to `http://192.168.0.101:9078/callback?code=xxxxxx...`. Then navigate to this URL to complete authentication. + + + + + For your convenience, I run the [`mendhak/http-https-echo:36`](https://github.com/mendhak/docker-http-https-echo) container from the [**Echo**](./?spotifyRedirect=echo#spotify) approach on a domain I, the multi-scrobbler developer [FoxxMD](https://github.com/FoxxMD), control and self-host. Please be aware: + + * I **do not collect personal data** or any querystrings/data sent in requests to this site + * It's **not possible** for me to use the code sent by Spotify to this URL without also having your Client ID/Secret + * However, there is **always** risk involved with trusting a service you do not control. Evaluate the risk of using this site for yourself. + + Use the [**Echo**](./?spotifyRedirect=echo#spotify) approach, starting from **Step 2**, with **https://echo.multi-scrobbler.app/callback** as Spotify Redirect URI and multi-scrobbler `SPOTIFY_REDIRECT_URI` ENV. + + + +
+ +4. Save your new App, then copy the Client ID/Secret from the App's Basic Information page. + +5. Add the Client ID, Secret, and Redirect URI to the respective field/ENV in the [configuration section below](#spotify-config) + +6. Start multi-scrobbler, then visit the Web Dashboard and click **(Re)authenticate** on the Spotify card to start the auth process + +## Configuration + + + | Environmental Variable | Required? | Default | Description | + |----------------------------|-----------|----------------------------------|----------------------------------------------------| + | `SPOTIFY_CLIENT_ID` | Yes | | | + | `SPOTIFY_CLIENT_SECRET` | Yes | | | + | `SPOTIFY_REDIRECT_URI` | No | `http://localhost:9078/callback` | URI must end in `callback` | + \ No newline at end of file diff --git a/docsite/docs/configuration/sources/subsonic.mdx b/docsite/docs/configuration/sources/subsonic.mdx new file mode 100644 index 00000000..126b54da --- /dev/null +++ b/docsite/docs/configuration/sources/subsonic.mdx @@ -0,0 +1,31 @@ +--- +title: Subsonic +toc_min_heading_level: 2 +toc_max_heading_level: 5 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from '@theme/CodeBlock'; +import SubsonicConfig from '!!raw-loader!@site/../config/subsonic.json.example'; + +This Source can be used for any application that implements the [Subsonic API](http://www.subsonic.org/pages/api.jsp) and supports the [`getNowPlaying`](http://www.subsonic.org/pages/api.jsp#getNowPlaying) endpoint (such as [Airsonic](https://airsonic.github.io/) and [Navidrome](https://www.navidrome.org/)) + +:::warning[Known Issues] + +* Multiple artists are reported as one value and cannot be separated +* If using [Airsonic Advanced](https://github.com/airsonic-advanced/airsonic-advanced) the password used (under **Credentials**) must be **Decodable** + +::: + +Use the optional `usersAllow` property with **File** or **AIO** configuration to restrict scrobbling to a list of defined users. + +## Configuration + + + | Environmental Variable | Required? | Default | Description | + |------------------------|-----------|---------|--------------------------------------| + | `SUBSONIC_USER` | Yes | | | + | `SUBSONIC_PASSWORD` | Yes | | | + | `SUBSONIC_URL` | Yes | | Base url of your subsonic-api server | + \ No newline at end of file diff --git a/docsite/docs/configuration/sources/vlc.mdx b/docsite/docs/configuration/sources/vlc.mdx new file mode 100644 index 00000000..ad05cbb1 --- /dev/null +++ b/docsite/docs/configuration/sources/vlc.mdx @@ -0,0 +1,94 @@ +--- +title: VLC +toc_min_heading_level: 2 +toc_max_heading_level: 5 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from '@theme/CodeBlock'; +import JsonConfig from '!!raw-loader!@site/../config/vlc.json.example'; + +MS communicates with [VLC](https://www.videolan.org/vlc/) using the [Web (`http`) interface module](https://wiki.videolan.org/VLC_HTTP_requests/) + +#### Enable HTTP Interface + +Open VLC: + +* **Tools** Menu -> Preferences + * In the bottom left change **Show Settings** from `Simple` to `All` + * In the updated window select `Interface -> Main interfaces` + * In the **Extra interface modules** section enable **Web** and verify that `http` is shown in the textbox + * Select `Interface -> Main interfaces -> Lua` + * In Lua HTTP -> Password -> set a password in this box + * Click **Save** at the bottom to persist your changes +* Restart VLC +* Verify the HTTP interface is accessible by navigating to `http://localhost:8080` in your browser + +If the interface is accessible, after typing in your password, VLC is now ready to be used by MS. + +##### Setting VLC Interface and Port + +These can be set by starting VLC from command line with specific options: + +* `vlc --http-host yourHostIp:yourPort` or +* `vlc --http-port yourPort` or +* modify the configuration file for VLC by editing keys with the same values as from the command line (`http-host` and `http-port`) + + +#### Filename Parsing + +If the file being played in VLC does not have ID3 tags/metadata information multi-scrobbler can attempt to extract artist, title, and album information from the raw filename using regular expressions. Mutli-scrobbler will only do this if there is **no other information** reported by VLC about these fields. + +To extract this information use [named capture groups](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Regular_expressions/Named_capturing_group) in your expression matching the field that should be extracted: `artist` `title` and/or `album`. MS accepts multiple expressions, the first one that matches a filename will be used. + +Example usage in [a file-based config:](./?configType=file#webscrobbler-config) + +```json5 title="config/vlc.json" {10-15} +[ + { + "name": "MyVlc", + "enable": true, + "data": { + // ... + }, + "options": { + "filenamePatterns": [ + // Extracts artist and title from filenames that look like: + // My Artist - My Cool title.mp4 + "/^\\s*(?.+?) - (?.+?)\\.\\w+$/i" + ], + // logs to DEBUG when MS tries to extract data from a filename + "logFilenamePatterns": true, + } + } +] +``` + +#### Vlc Information Reporting + +If you find that VLC is incorrectly reporting track information (in its interface) but multi-scrobbler is not parsing it correctly it may be due to the _name_ of the fields VLC is reporting. mutli-scrobbler uses the most common field name but does not cover all use cases. Before reporting an issue please turn on metadata logging and include output from your logs while it is turned on: + +```json5 title="config/vlc.json" {9-9} + [ + { + "name": "MyVlc", + "enable": true, + "data": { + // ... + }, + "options": { + "dumpVlcMetadata": true + } + } + ] +``` + +## Configuration + +<Config config="VLCSourceConfig" fileContent={JsonConfig} name="vlc"> + | Environmental Variable | Required? | Default | Description | + |------------------------|------------|------------------|-------------| + | `VLC_URL` | No | `localhost:8080` | | + | `VLC_PASSWORD` | Yes | | | +</Config> \ No newline at end of file diff --git a/docsite/docs/configuration/sources/webscrobbler.mdx b/docsite/docs/configuration/sources/webscrobbler.mdx new file mode 100644 index 00000000..246922d0 --- /dev/null +++ b/docsite/docs/configuration/sources/webscrobbler.mdx @@ -0,0 +1,95 @@ +--- +title: Webscrobbler +toc_min_heading_level: 2 +toc_max_heading_level: 5 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from '@theme/CodeBlock'; +import JsonConfig from '!!raw-loader!@site/../config/webscrobbler.json.example'; + +After installing the [Webscrobbler extension](https://web-scrobbler.com/) open the preferences/settings for it: + +* Under **Accounts** + * **Add Webhook** + * API URL: `[YourMultiScrobblerInstance]/api/webscrobbler` EX `http://localhost:9078/api/webscrobbler` + * Application name: `(whatever you want)` + +Reload the extension after adding the webhook. + +:::note + +* **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 + +::: + +:::warning + +Multi-scrobbler is **not** designed to be publicly accessible from the internet. To use Webscrobbler outside your local network you should be accessing MS through a VPN or through a reverse proxy with some kind of authentication (though this is still not recommended). + +::: + +### 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.](/configuration?configType=file#configuration-types) + +<details> + + <summary>Example</summary> + + ```json title="webscrobbler.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` + + :::tip + + `http://localhost:9078/api/webscrobbler` is matched with the first source that _that does not have a slug defined._ + + ::: + +</details> + +### Connectors Black/Whitelist + +MS can be configured to only scrobble, or NOT scrobble, from some WS connectors. Use the name of the website from the [supported websites](https://web-scrobbler.com/) or from the **Connectors** tab in the extension. + +:::note + +This affects **only** MS's behavior and does not affect the general connector behavior you have configured within the WebScrobbler extension. + +::: + +## Configuration + +<Config config="WebscrobblerSourceConfig" fileContent={JsonConfig} name="webscrobbler"> + | 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 | +</Config> \ No newline at end of file diff --git a/docsite/docs/configuration/sources/yamaha-musiccast.mdx b/docsite/docs/configuration/sources/yamaha-musiccast.mdx new file mode 100644 index 00000000..d590456c --- /dev/null +++ b/docsite/docs/configuration/sources/yamaha-musiccast.mdx @@ -0,0 +1,37 @@ +--- +title: Yamaha MusicCast +toc_min_heading_level: 2 +toc_max_heading_level: 5 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from '@theme/CodeBlock'; +import JsonConfig from '!!raw-loader!@site/../config/musiccast.json.example'; + +Monitor [MusicCast](https://usa.yamaha.com/products/contents/audio_visual/musiccast/index.html) device/receivers for music played on Network/USB/CD inputs. + +:::tip[Scrobbling Threshold] + +Musiccast may not correctly report track length. If this is the case you should reduce the "listened to" **duration** using [Scrobble Thresholds](#scrobble-thresholds) so that multi-scrobbler scrobbles correctly. + +::: + +##### URL + +If only a domain/IP:PORT is provided to multi-scrobbler it will automatically append the default Musiccast API path. If your Musiccast device is located behind a custom URL (or reverse proxy) you should explicitly provide the base path to use. + +EX + + | `url` | Transformed URL | + | --------------------------------------- | :---------------------------------------------- | + | `http://192.168.0.101` | `http://192.168.0.101/YamahaExtendedControl/v1` | + | `http://mydomain.com/reverseProxy/cast` | `http://mydomain.com/reverseProxy/cast` | + +## Configuration + +<Config config="MusicCastSourceConfig" fileContent={JsonConfig} name="musiccast"> + | Environmental Variable | Required? | Default | Description | + | :--------------------- | :-------- | ------- | :----------------------- | + | `MCAST_URL` | Yes | | The Musiccast device URL | +</Config> \ No newline at end of file diff --git a/docsite/docs/configuration/sources/youtube-music.mdx b/docsite/docs/configuration/sources/youtube-music.mdx new file mode 100644 index 00000000..a5332c10 --- /dev/null +++ b/docsite/docs/configuration/sources/youtube-music.mdx @@ -0,0 +1,189 @@ +--- +title: Youtube Music +toc_min_heading_level: 2 +toc_max_heading_level: 5 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from '@theme/CodeBlock'; +import JsonConfig from '!!raw-loader!@site/../config/ytmusic.json.example'; + +:::warning + +Communication with YT Music is **unofficial** and not supported or endorsed by Google. This means that **this integration may stop working at any time** if Google decides to change how YT Music works in the browser. + +::: + +:::tip[Scrobble Troubleshooting] + +Due to monitoring being unofficial, listening history from YTM can be inconsistent and can cause missed scrobbles. + +[**See the FAQ**](../FAQ.md#youtube-music-misses-or-duplicates-scrobbles) for a detailed explanation, how to see more details about MS's detection of tracks, and how to properly report an issue. + +::: + +#### Authentication {#ytm-auth} + +Only one of these methods needs to be used. + +<Tabs groupId="ytmAuth" queryString> + <TabItem value="cookie" label="Cookies"> + + Use instructions from + + * https://github.com/patrickkfkan/Volumio-YouTube.js/wiki/How-to-obtain-Cookie or + * https://ytmusicapi.readthedocs.io/en/stable/setup/browser.html#copy-authentication-headers + + to get the **Cookie** value from a browser. + + It is highly recommended to [get the cookie from an Incognito/Private Session](https://github.com/LuanRT/YouTube.js/issues/803#issuecomment-2504032666) to limit the chance the session is invalidated from normal browsing. + + Add the cookie to your `ytmusic.json` config in `data` or as an ENV: + + ```json + { + "type": "ytmusic", + "enable": true, + "name": "MyYTM", + "data": { + "cookie": "__Secure-1PSIDTS=sidts-CjEB3EgAEvCd-......" + }, + "options": { + "logAuthUpdateChanges": true, + "logDiff": true + } + } + ``` + + If MS gives you authentication errors (session invalidated) at some point in the future follow the same instructions to get new cookies. + + </TabItem> + <TabItem value="oauth" label="OAuth Client"> + :::warning + + As of Sept 25' OAuth clients no longer seem to have permission to access the YTM service, as reported by the upstream YTM library developer and [several other users.](https://github.com/FoxxMD/multi-scrobbler/issues/345#issuecomment-3258518769) + + If you have existing, working OAuth credentials keep using them but there is no gaurantee they will continue to work. + + If you are setting up the YTM Source for the first time you should first try to use [Cookies](./?ytmAuth=cookie#ytm-auth). + + ::: + + [Based on the instructions from here...](https://github.com/LuanRT/YouTube.js/issues/803#issuecomment-2479689924) + + * Login to [Google Cloud console](https://console.cloud.google.com/) (create an account, if necessary) + * [Create a new project](https://console.cloud.google.com/projectcreate) + * Go to APIs and services. + * Configure the OAuth consent screen + * Use the old experience if possible + * If new is unavoidable then do not fill out any branding and under Authorized Domains you can delete the empty one (in order to save) + * Add yourself as an authorized user + * Navigate to Credentials + * Create Credentials -> choose "OAuth client ID" + * Application Type is **Web Application** + * **Name** is whatever you want, leave Authorization Javascript origins blank + * Authorized redirect URIs + * This must be **exactly** the same as what is displayed in MS! For now leave it blank so we can generate it from MS first + * Create + * In the newly created client popup save the **Client ID** and **Client Secret**, then copy them into `ytmusic.json` or appropriate ENVs: + + ```json + { + "type": "ytmusic", + "enable": true, + "name": "MyYTM", + "data": { + "clientId": "8910....6jqupl.apps.googleusercontent.com", + "clientSecret": "GOCSPX-WGXL6BSuQ343..." + }, + "options": { + "logAuthUpdateChanges": true, + "logDiff": true + } + } + ``` + + Now, start MS and during the YTMusic startup it will log something like this: + + ``` + Will use custom OAuth Client: + Client ID: ... + Client Secret: ... + Redirect URI: http://localhost:9078/api/ytmusic/callback?name=MyYTM + ``` + + If the beginning of the Redirect URI (before `api`) is EXACTLY how you would reach the MS dashboard from your browser (EX `http://localhost:9078`) then edit your google oauth client section for `Authorized redirect URIs` and add the URL MS has displayed. + + If it is NOT EXACTLY the same you either need to set MS's [base url](https://foxxmd.github.io/multi-scrobbler/docs/configuration/#base-url) or you can provide your own (Custom) Redirect URI for MS to use by setting it in `ytmusic.json` or ENV. + + <details> + + <summary>Using a Custom Redirect URI</summary> + + The three parts of the URL that must be the same: + + * it must start with `api` (after domain or subdirectory IE `my.domain.tld/api...` or `whatever.tld/subDir/api...` + * it must end in `ytmusic/callback` + * It must include `name=[NameOfSource]` in the query string + + Remember to add your custom URL to the `Authorized redirect URIs` section in the google oauth client! + + ```json + { + "type": "ytmusic", + "enable": true, + "name": "MyYTM", + "data": { + "clientId": "8910....6jqupl.apps.googleusercontent.com", + "clientSecret": "GOCSPX-WGXL6BSuQ343...", + "redirectUri": "http://my.custom.domain/api/ytmusic/callback?name=MyYTM" + }, + "options": { + "logAuthUpdateChanges": true, + "logDiff": true + } + } + ``` + + </details> + + AFTER changing the Authorized redirect URIs on Google Cloud console you may need to wait a few minutes for it to take affect. Then restart MS. From the dashboard click `(Re)authenticate` on the YTmusic source card and follow the auth flow: + + * On the screen about "testing" make sure you hit **Continue** (not Back To Safety) + * Make sure to select ALL scopes/permissions/grants it asks you about + * `Select what [YourAppName] can access` -> Select all + + Once the flow is finished MS will get the credentials and start polling automatically. You should not need to re-authenticate again after restarting MS as it saves the credentials to the `/config` folder. + + </TabItem> + <TabItem value="ytt" label="YoutubeTV"> + + :::warning + + Using the built-in YoutubeTV authentication is unlikely to work due to [Google restricting what permissions TV clients can have](https://github.com/yt-dlp/yt-dlp/issues/11462#issuecomment-2471703090). This authentication method should not be used. + + ::: + + To authenticate start multi-scrobbler with an empty YT Music configuration. An authentication URL/code will be logged in additon to being available from the dashboard. + + ``` + ERROR: Sign in with the code 'CLV-KFA-BVKY' using the authentication link on the dashboard or https://www.google.com/device + ``` + + Visit the authentication URL and enter the code that was provided (also available on the dashboard). After completing the setup flow MS will log `Auth success` and the YT Music dashboard card will display as **Idle** after refreshing. Click the **Start** link to begin monitoring. + + </TabItem> +</Tabs> + +## Configuration + +<Config config="YTMusicSourceConfig" fileContent={JsonConfig} name="ytmusic"> +| Environmental Variable | Required? | Default | Description | +|------------------------|-----------|---------|-----------------------------------------------| +| YTM_COOKIE | No | | Value for Cookie Authentication | +| YTM_CLIENT_ID | No | | Client ID for OAuth Athentication | +| YTM_CLIENT_SECRET | No | | Client Secret for OAuth Athentication | +| YTM_REDIRECT_URI | No | | A custom redirect URI for OAuth Athentication | +| YTM_LOG_DIFF | No | false | Log YTM history changes | +</Config> \ No newline at end of file diff --git a/docsite/src/components/GenericConfiguration.mdx b/docsite/src/components/GenericConfiguration.mdx new file mode 100644 index 00000000..837e6f2f --- /dev/null +++ b/docsite/src/components/GenericConfiguration.mdx @@ -0,0 +1,28 @@ +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from '@theme/CodeBlock'; + +<Tabs groupId="configType" queryString> + <TabItem value="env" label="ENV"> + <EnvType> + {props.children} + </EnvType> + </TabItem> + <TabItem value="file" label="File"> + <FileType> + + <FileExample title={`CONFIG/${props.name}.json`} client={props.client ?? false} data={props.fileContent}/> + + or <SchemaLink lower objectName={props.config}/> + </FileType> + </TabItem> + <TabItem value="aio" label="AIO"> + <AIOType> + + <AIOExample data={props.fileContent} client={props.client ?? false} name={props.name}/> + + + or <SchemaLink lower objectName={props.config}/> + </AIOType> + </TabItem> +</Tabs> diff --git a/docsite/src/components/snippets/_aio-config.mdx b/docsite/src/components/snippets/_aio-config.mdx new file mode 100644 index 00000000..3332733e --- /dev/null +++ b/docsite/src/components/snippets/_aio-config.mdx @@ -0,0 +1,2 @@ +[**AIO** Config Type](/configuration?configType=aio#configuration-types) +{props.children} \ No newline at end of file diff --git a/docsite/src/components/snippets/_env-config.mdx b/docsite/src/components/snippets/_env-config.mdx new file mode 100644 index 00000000..27927494 --- /dev/null +++ b/docsite/src/components/snippets/_env-config.mdx @@ -0,0 +1,3 @@ +[**ENV** Config Type](/configuration?configType=env#configuration-types) + +{props.children} \ No newline at end of file diff --git a/docsite/src/components/snippets/_file-config.mdx b/docsite/src/components/snippets/_file-config.mdx new file mode 100644 index 00000000..589f9338 --- /dev/null +++ b/docsite/src/components/snippets/_file-config.mdx @@ -0,0 +1,3 @@ +[**File** Config Type](/configuration?configType=file#configuration-types) + +{props.children} \ No newline at end of file diff --git a/docsite/src/theme/MDXComponents.js b/docsite/src/theme/MDXComponents.js index f2dbe845..1396825c 100644 --- a/docsite/src/theme/MDXComponents.js +++ b/docsite/src/theme/MDXComponents.js @@ -4,11 +4,19 @@ import MDXComponents from '@theme-original/MDXComponents'; import SchemaLink from "@site/src/components/SchemaLink"; import AIOExample from "@site/src/components/AIOExample"; import FileExample from "@site/src/components/FileExample"; +import EnvType from "@site/src/components/snippets/_env-config.mdx"; +import FileType from "@site/src/components/snippets/_file-config.mdx"; +import AIOType from "@site/src/components/snippets/_aio-config.mdx"; +import Config from "@site/src/components/GenericConfiguration.mdx"; export default { // Re-use the default mapping ...MDXComponents, SchemaLink, AIOExample, - FileExample + FileExample, + EnvType, + FileType, + AIOType, + Config }; \ No newline at end of file -- 2.51.2 From 2540ed394530f2af280afabebd12189051758580 Mon Sep 17 00:00:00 2001 From: FoxxMD <FoxxMD@users.noreply.github.com> Date: Thu, 30 Oct 2025 18:52:59 +0000 Subject: [PATCH 7/8] docs: Add client own files --- .../configuration/clients/_category_.json | 7 ++ docsite/docs/configuration/clients/koito.mdx | 60 +++++++++++++++++ docsite/docs/configuration/clients/lastfm.mdx | 36 ++++++++++ .../configuration/clients/listenbrainz.mdx | 31 +++++++++ docsite/docs/configuration/clients/maloja.mdx | 45 +++++++++++++ .../docs/configuration/clients/overview.mdx | 65 +++++++++++++++++++ .../docs/configuration/clients/rocksky.mdx | 22 +++++++ 7 files changed, 266 insertions(+) create mode 100644 docsite/docs/configuration/clients/_category_.json create mode 100644 docsite/docs/configuration/clients/koito.mdx create mode 100644 docsite/docs/configuration/clients/lastfm.mdx create mode 100644 docsite/docs/configuration/clients/listenbrainz.mdx create mode 100644 docsite/docs/configuration/clients/maloja.mdx create mode 100644 docsite/docs/configuration/clients/overview.mdx create mode 100644 docsite/docs/configuration/clients/rocksky.mdx diff --git a/docsite/docs/configuration/clients/_category_.json b/docsite/docs/configuration/clients/_category_.json new file mode 100644 index 00000000..e717c32d --- /dev/null +++ b/docsite/docs/configuration/clients/_category_.json @@ -0,0 +1,7 @@ +{ + "label": "Scrobble Clients", + "link": { + "type": "doc", + "id": "configuration/clients/overview" + } +} diff --git a/docsite/docs/configuration/clients/koito.mdx b/docsite/docs/configuration/clients/koito.mdx new file mode 100644 index 00000000..9ac18eaa --- /dev/null +++ b/docsite/docs/configuration/clients/koito.mdx @@ -0,0 +1,60 @@ +--- +title: Koito +toc_min_heading_level: 2 +toc_max_heading_level: 5 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from '@theme/CodeBlock'; +import JsonConfig from '!!raw-loader!@site/../config/koito.json.example'; + +:::tip[Other Uses] + +To monitor and re-scrobble activity from a Koito account create a [Koito (Source)](/configuration/sources/koito-source) + +::: + +Setup a [Koito server](https://koito.io/guides/installation/) if you have not already done this. + +* [Create (or get) an API Key](https://koito.io/guides/scrobbler/) + * From your Koito server dashboard navigate to **Settings** (bottom-left icon) => **API Keys** + * Create a new Key (or use an existing) and Copy the key using the copy icon to the right of the key + * EX `pM195xPV98CDpk0QW47FIIOR8AKATAX5DblBF-Jq0t1MbbKL` +* Take note of your Koito username (used below as `KOITO_USER`) +* Determine your Koito URL + * This is the URL you use to access your Koito dashboard OR whatever URL is accessible from your multi-scrobbler instance. + * EX dashboard at `http://192.168.0.100:4110` => `KOITO_URL` is set to `http://192.168.0.100:4110` + +:::note[Koito Base URL] + +Using the URL path `/apis/listenbrainz` [Koito docs describe](https://koito.io/guides/scrobbler/) **is not required.** The Koito URL used for multi-scrobbler should only be the **base** URL. Only include a URL path if your Koito **base** url is not at `host:port`. See the table below for examples. + +<details> + +<summary>Base URL Examples</summary> + | `KOITO_URL` | MS Detected Base URL | Example Scrobble Api URL | + | :-------------------------------------------- | :------------------------------ | ---------------------------------------------------- | + | `http://192.168.0.100:4110` | `http://192.168.0.100:4110` | `http://192.168.0.100:4110/apis/listenbrainz/1` | + | `https://koito.mydomain.com` | `https://koito.mydomain.com` | `https://koito.mydomain.com/apis/listenbrainz/1` | + | `http://192.168.0.100:4110/apis/listenbrainz` | `http://192.168.0.100:4110` | `http://192.168.0.100:4110/apis/listenbrainz/1` | + | `http://192.168.0.100:80/koito` | `http://192.168.0.100:80/koito` | `http://192.168.0.100:80/koito/apis/listenbrainz/1` | +</details> +::: + + +:::tip + +Ensure that Koito is configured to allow requests from multi-scrobbler! In Koito config set [`KOITO_ALLOWED_HOSTS`](https://koito.io/reference/configuration/#koito_allowed_hosts) to the IP the multi-scrobbler dashboard is accessible from. + +::: + +## Configuration + +<Config config="KoitoClientConfig" fileContent={JsonConfig} client name="koito"> + | Environmental Variable | Required? | Default | Description | + | :--------------------- | --------- | ------- | --------------------------------- | + | KOITO_TOKEN | Yes | | API Key from your Koito Account | + | KOITO_USER | Yes | | Your Koito username | + | KOITO_URL | No | | The base URL for the Koito server | +</Config> \ No newline at end of file diff --git a/docsite/docs/configuration/clients/lastfm.mdx b/docsite/docs/configuration/clients/lastfm.mdx new file mode 100644 index 00000000..23379498 --- /dev/null +++ b/docsite/docs/configuration/clients/lastfm.mdx @@ -0,0 +1,36 @@ +--- +title: Last.fm +toc_min_heading_level: 2 +toc_max_heading_level: 5 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from '@theme/CodeBlock'; +import JsonConfig from '!!raw-loader!@site/../config/lastfm.json.example'; + +:::tip[Other Users] + +* To monitor and re-scrobble activity from your Last.fm account create a [Last.fm (Source)](/configuration/sources/lastfm-source) +* To accept scrobbles from outside applications as if multi-scrobbler was Last.fm, create a [Last.fm (Endpoint)](/configuration/sources/lastfm-endpoint) + +::: + +[Register for an API account here.](https://www.last.fm/api/account/create) + +The Callback URL is actually specified by multi-scrobbler but to keep things consistent you should use +``` +http://localhost:9078/lastfm/callback +``` +or replace `localhost:9078` with your own base URL. + +## Configuration + +<Config config="LastfmClientConfig" fileContent={JsonConfig} client name="kodi"> + | Environmental Variable | Required? | Default | Description | + |------------------------|-----------|-----------------------------------------|-------------------------------------------------------------------------------| + | `LASTFM_API_KEY` | Yes | | Api Key from your API Account | + | `LASTFM_SECRET` | Yes | | Shared secret from your API Account | + | `LASTFM_REDIRECT_URI` | No | `http://localhost:9078/lastfm/callback` | Url to use for authentication. Must include `lastfm/callback` somewhere in it | + | `LASTFM_SESSION` | No | | Session id. Will be generated by authentication flow if not provided. | +</Config> \ No newline at end of file diff --git a/docsite/docs/configuration/clients/listenbrainz.mdx b/docsite/docs/configuration/clients/listenbrainz.mdx new file mode 100644 index 00000000..cfc53b48 --- /dev/null +++ b/docsite/docs/configuration/clients/listenbrainz.mdx @@ -0,0 +1,31 @@ +--- +title: Listenbrainz +toc_min_heading_level: 2 +toc_max_heading_level: 5 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from '@theme/CodeBlock'; +import JsonConfig from '!!raw-loader!@site/../config/listenbrainz.json.example'; + +:::tip[Other Users] + +* To monitor and re-scrobble activity from a Listenbrainz account create a [Listenbrainz (Source)](/configuration/sources/listenbrainz-source) +* To accept scrobbles from outside applications as if multi-scrobbler was a Listenbrainz server, create a [Listenbrainz (Endpoint)](/configuration/sources/listenbrainz-endpoint) + +::: + +You will need to run your own Listenbrainz server or have an account [on the official instance](https://listenbrainz.org/login/) + +On your [profile page](https://listenbrainz.org/profile/) find your **User Token** to use in the configuration. + +## Configuration + +<Config config="ListenbrainzClientConfig" fileContent={JsonConfig} name="listenbrainz"> + | Environmental Variable | Required? | Default | Description | + |------------------------|-----------|-------------------------------|---------------------------------| + | LZ_TOKEN | Yes | | User token from your LZ profile | + | LZ_USER | Yes | | Your LZ username | + | LZ_URL | No | https://api.listenbrainz.org/ | The base URL for the LZ server | +</Config> \ No newline at end of file diff --git a/docsite/docs/configuration/clients/maloja.mdx b/docsite/docs/configuration/clients/maloja.mdx new file mode 100644 index 00000000..8056f493 --- /dev/null +++ b/docsite/docs/configuration/clients/maloja.mdx @@ -0,0 +1,45 @@ +--- +title: Maloja +toc_min_heading_level: 2 +toc_max_heading_level: 5 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from '@theme/CodeBlock'; +import JsonConfig from '!!raw-loader!@site/../config/maloja.json.example'; + +Setup a [Maloja server](https://github.com/krateng/maloja?tab=readme-ov-file#how-to-install) if you have not already done this. + +<details> + + <summary>Maloja Setup Intructions</summary> + + Using Maloja's example `docker-compose.yml`: + + ```yaml reference title="~/malojaData/docker-compose.yml" + https://github.com/krateng/maloja/blob/master/example-compose.yml + ``` + + Uncomment `environment` and add `MALOJA_FORCE_PASSWORD=CHANGE_ME` to set an admin password + + Start the container: + + ```shell title="~/malojaData" + docker compose up -d + ``` +</details> + +* Navigate to the Admin Panel (Cog in upper-right corner) -> API Keys (or at http://myMalojaServerIP/admin_apikeys) + * Create a **New Key** and then copy the generated key value + +Finally, add the Maloja server URL and API Key to the configuration type you choose to use, below. + +## Configuration + +<Config config="MalojaClientConfig" fileContent={JsonConfig} name="maloja"> + | Environmental Variable | Required? | Default | Description | + |------------------------|-----------|---------|-------------------------------| + | `MALOJA_URL` | Yes | | Base URL of your installation | + | `MALOJA_API_KEY` | Yes | | Api Key | +</Config> \ No newline at end of file diff --git a/docsite/docs/configuration/clients/overview.mdx b/docsite/docs/configuration/clients/overview.mdx new file mode 100644 index 00000000..55337a65 --- /dev/null +++ b/docsite/docs/configuration/clients/overview.mdx @@ -0,0 +1,65 @@ +--- +sidebar_position: 2 +title: Overview +toc_max_heading_level: 4 +--- + +import ScrobbleThreshold from "@site/src/components/snippets/_scrobble-threshold.mdx" + +A **Client** is an application that stores the historical information about what songs you have played (scrobbles). Examples are **Maloja, Last.fm, Listenbrainz**... + +## Clients + +| Name | Now Playing | +| :-------------------------------------------------- | :---------- | +| [Last.fm](/configuration/clients/lastfm) | ✅ | +| [Listenbrainz](/configuration/clients/listenbrainz) | ✅ | +| [Koito](/configuration/clients/koito) | ❌ | +| [Maloja](/configuration/clients/maloja) | ❌ | +| [Rocksky](/configuration/clients/rockysky) | ❌ | + +## Features + +### Dead Scrobbles + +If multi-scrobbler is unable to submit a scrobble to a Client then it places the scrobble into a queue which is retried every 5 minutes for a number of times before it gives up. + +After it stops _automatically_ retrying the scrobble still exists and can be retried from the dashboard. Dead scrobbles persist between restart so you will never lose a scrobble that hasn't been successfully submitted yet. + +### Now Playing + +Multi-scrobbler can report the currently **playing** tracks it is monitoring to some Scrobble Clients via their individual **Now Playing** functionality. + +Now Playing is **default enabled** for all Clients that support it. + +The behavior multi-scrobbler uses for determining Now Playing reporting: + +* Now Playing can be explicitly enabled or disabled globally using ENV `NOW_PLAYING=true` or `NOW_PLAYING=false` + * This only affects Clients that don't have behavior set via File/AIO (below) +* Now Playing will be only be reported to the same Clients a Source is configured to scrobble to +* A Source's Player status must be **Playing**, IE **active**, for reporting to occur + * This status can be verified via Player UI on the Dashboard +* If multiple Sources having active Players then the scrobble Client will default to reporting the track based on Source **configuration** name, alphabetically + * This is the `name` property set in [File or AIO source configs](/configuration?configType=file#configuration-types) + +Clients can customize the Now Playing behavior individually using [File or AIO source configs:](/configuration?configType=file#configuration-types) + +```json5 title="CONFIG_DIR/lastfm.json" +[ + { + "name": "myLastFmClient", + "configureAs": "client", + "data": { + // ... + }, + "options": { + // disable or enable + //"nowPlaying": true + // + // OR define a list of Source *config* names that should be allowed to report Now Playing + // order of the list determines priority for reporting Now Playing + //"nowPlaying": ["mySpotify1","myJellyfin2"] + } + }, +] +``` \ No newline at end of file diff --git a/docsite/docs/configuration/clients/rocksky.mdx b/docsite/docs/configuration/clients/rocksky.mdx new file mode 100644 index 00000000..a3f3affd --- /dev/null +++ b/docsite/docs/configuration/clients/rocksky.mdx @@ -0,0 +1,22 @@ +--- +title: Rocksky +toc_min_heading_level: 2 +toc_max_heading_level: 5 +--- + +[**Rocksky**](https://docs.rocksky.app/) is music tracking and discovery platform built on the AT Protocol (Bluesky). It is compatible with the Listenbrainz API and so can be as a multi-scrobbler [Listenbrainz Client](#listenbrainz) using a custom URL. + +You will need a [Bluesky](https://bsky.app/) account to use Rockysky. + +:::note + +Usage is adapted from [the Listenbrainz to Rocksky docs](https://docs.rocksky.app/migrating-from-listenbrainz-to-rocksky-1040189m0) on [docs.rockysky.app](https://docs.rocksky.app) + +::: + +While logged in to Bluesky, navigate to [RockySky API Application](https://rocksky.app/apikeys) and obtain an API Key. + +Then, follow the directions to create a normal multi-scrobbler [Listenbrainz Client](/configuration/clients/listenbrainz) and use: + +* **Listenbrainz Token** (`LZ_TOKEN`) - The API Key from RockySky +* **Listenbrainz URL** (`LZ_URL`) - `https://audioscrobbler.rocksky.app` \ No newline at end of file -- 2.51.2 From 891e591ceb2bc74375861e3276007337b96a5eee Mon Sep 17 00:00:00 2001 From: FoxxMD <FoxxMD@users.noreply.github.com> Date: Thu, 30 Oct 2025 19:56:07 +0000 Subject: [PATCH 8/8] docs: Reorganize doc contents --- docsite/docs/FAQ.md | 16 +- docsite/docs/configuration/_category_.json | 6 +- .../configuration/clients/_category_.json | 2 +- .../clients/{overview.mdx => clients.mdx} | 2 +- .../docs/configuration/clients/rocksky.mdx | 2 +- docsite/docs/configuration/configuration.mdx | 2656 +---------------- docsite/docs/configuration/plex.md | 81 - .../configuration/sources/_category_.json | 2 +- .../docs/configuration/sources/azuracast.mdx | 2 +- docsite/docs/configuration/sources/deezer.mdx | 4 +- .../configuration/sources/google-cast.mdx | 4 +- .../docs/configuration/sources/icecast.mdx | 4 +- docsite/docs/configuration/sources/jriver.mdx | 4 +- docsite/docs/configuration/sources/kodi.mdx | 4 +- .../configuration/sources/koito-source.mdx | 2 +- .../configuration/sources/lastfm-source.mdx | 2 +- docsite/docs/configuration/sources/mopidy.mdx | 2 +- docsite/docs/configuration/sources/mpris.mdx | 2 +- .../docs/configuration/sources/overview.mdx | 141 - .../docs/configuration/sources/sources.mdx | 141 + .../docs/configuration/sources/spotify.mdx | 20 +- docsite/docs/configuration/sources/vlc.mdx | 2 +- .../sources/yamaha-musiccast.mdx | 2 +- .../configuration/sources/youtube-music.mdx | 2 +- docsite/docs/development/_category_.json | 2 +- docsite/docs/index.mdx | 89 +- docsite/docs/installation/_category_.json | 2 +- docsite/docs/installation/installation.mdx | 8 +- docsite/docs/quickstart.mdx | 26 +- docsite/docs/updating/_category_.json | 2 +- docsite/docusaurus.config.ts | 6 +- docsite/static/aio.json | 2 +- docsite/static/client.json | 2 +- docsite/static/source.json | 2 +- src/backend/common/schema/aio-client.json | 2 +- src/backend/common/schema/aio-source.json | 2 +- src/backend/common/schema/aio.json | 2 +- src/backend/common/schema/client.json | 2 +- src/backend/common/schema/source.json | 2 +- 39 files changed, 267 insertions(+), 2989 deletions(-) rename docsite/docs/configuration/clients/{overview.mdx => clients.mdx} (97%) delete mode 100644 docsite/docs/configuration/plex.md delete mode 100644 docsite/docs/configuration/sources/overview.mdx create mode 100644 docsite/docs/configuration/sources/sources.mdx diff --git a/docsite/docs/FAQ.md b/docsite/docs/FAQ.md index de5a5a18..60962cb9 100644 --- a/docsite/docs/FAQ.md +++ b/docsite/docs/FAQ.md @@ -33,7 +33,7 @@ First, turn on **debug** logging for multi-scrobbler by setting the environmenta ##### Check Host name and URL -The URLs examples in the [configuration](configuration/configuration.mdx) documentation assume you are running Webscrobbler/LFM Endpoint on the same server as multi-scrobbler. If these are not the same machine then you need to determine the IP address or domain name that multi-scrobbler is reachable at and use that instead of `localhost` when configuring these sources. **This is likely the same host name that you would use to access the web interface for multi-scrobbler.** +The URLs examples in the [configuration](/configuration#base-url) documentation assume you are running Webscrobbler/LFM Endpoint on the same server as multi-scrobbler. If these are not the same machine then you need to determine the IP address or domain name that multi-scrobbler is reachable at and use that instead of `localhost` when configuring these sources. **This is likely the same host name that you would use to access the web interface for multi-scrobbler.** EX `http://localhost:9078/api/webscrobbler` -> `http://192.168.0.140:9078/api/webscrobbler` @@ -70,7 +70,7 @@ Error: Could not send the specified request to browse. Status code: 401 then YTM has invalidated your authentication. -First, ensure you are NOT using [YoutubeTV authentication.](configuration/configuration.mdx?ytmAuth=ytt#youtube-music) If you completed authentication by entering a "User Code" you are using YoutubeTV which has stopped working. You should reauthenticate using **Cookies** or **Custom OAuth.** +First, ensure you are NOT using [YoutubeTV authentication.](/configuration/sources/youtube-music?ytmAuth=ytt#authentication) If you completed authentication by entering a "User Code" you are using YoutubeTV which has stopped working. You should reauthenticate using **Cookies** or **Custom OAuth.** #### When using OAuth Client Authentication @@ -80,7 +80,7 @@ Refresh your authentication by using the **(Re)authenticate** link from MS's web The library MS uses relies on scraping the YTM site by using cookies from your actual browser to pretend it is a browser. It does its best to keep these up to date but since this is not an official way to access the service YTM may invalidate your access _to the authenticated session_ at any time. How this is triggered is unknown and not something multi-scrobbler can control. You can help limit the chance of your session being invalidated by [getting the cookie from an Incognito/Private Session](https://github.com/LuanRT/YouTube.js/issues/803#issuecomment-2504032666) and then immediately closing the browser afterwards. -To re-authenticate MS [follow the YTM instructions to retrieve a new set of cookies for multi-scrobbler](configuration/configuration.mdx?ytmAuth=cookie#youtube-music) and then restart MS to potentially resolve the problem. +To re-authenticate MS [follow the YTM instructions to retrieve a new set of cookies for multi-scrobbler](/configuration/sources/youtube-music?ytmAuth=cookie#authentication) and then restart MS to potentially resolve the problem. ## Configuration Issues @@ -108,7 +108,7 @@ Multi-scrobbler works the same was the official Spotify-Last.fm integration work The Google Cast integration relies on a few common fields in the data it receives from your casting device. Every platform that can cast (Spotify, Pandora, etc...) *should* use these fields the same but there are slight differences between their implementations that may confuse multi-scrobbler. Specific platforms may also return more information in non-common fields that are undocumented. -To diagnose these issues you [**must enable payload logging**](configuration/configuration.mdx#cast-troubleshooting) for your google cast Source, run MS, and then include logs with this output from that run. Without the raw data logged from your cast device it will be nearly impossible to resolve your issue. +To diagnose these issues you [**must enable payload logging**](/configuration/sources/google-cast#cast-troubleshooting) for your google cast Source, run MS, and then include logs with this output from that run. Without the raw data logged from your cast device it will be nearly impossible to resolve your issue. ### Google Cast device does not track media @@ -120,17 +120,17 @@ MS logs will tell you what type the media is reported as with lines like: My Artist - Example Track has 'unknown' media type and allowUnknownMedia=false, will not track ``` -Refer to [Allow Unknown Media Type](configuration/configuration.mdx#allow-unknown-media-type) section to fix this +Refer to [Allow Unknown Media Type](/configuration/sources/google-cast#allow-unknown-media-type) section to fix this ``` My Artist - Example Track has 'movie' media type so will not track ``` -Refer to [Force Media Tracking](configuration/configuration.mdx#forcing-media-tracking) section to fix this +Refer to [Force Media Tracking](/configuration/sources/google-cast#forcing-media-tracking) section to fix this ### VLC is not scrobbling fields correctly -Before reporting an issue turn on metadata logging in the MS VLC configuration, [see the VLC documentation.](configuration/configuration.mdx#vlc-information-reporting) +Before reporting an issue turn on metadata logging in the MS VLC configuration, [see the VLC documentation.](/configuration/sources/vlc#vlc-information-reporting) ### Youtube Music misses or duplicates scrobbles @@ -169,7 +169,7 @@ In your YTM configuration (`ytmusic.json`) add `logDiff` under `options` like th or set either ENVs: * `YTM_LOG_DIFF=true` -* [`DEBUG_MODE=true`](configuration/configuration.mdx?#debug-mode) +* [`DEBUG_MODE=true`](/configuration#debug-mode) This will cause MS to log YTM history changes similar to this: diff --git a/docsite/docs/configuration/_category_.json b/docsite/docs/configuration/_category_.json index 01b0e3a7..c9a06052 100644 --- a/docsite/docs/configuration/_category_.json +++ b/docsite/docs/configuration/_category_.json @@ -1,8 +1,8 @@ { "label": "Configuration", - "position": 4, + "position": 5, "link": { - "type": "generated-index", - "description": "Configuring Multi-Scrobbler and examples" + "type": "doc", + "id": "configuration/configuration" } } diff --git a/docsite/docs/configuration/clients/_category_.json b/docsite/docs/configuration/clients/_category_.json index e717c32d..33faff7b 100644 --- a/docsite/docs/configuration/clients/_category_.json +++ b/docsite/docs/configuration/clients/_category_.json @@ -2,6 +2,6 @@ "label": "Scrobble Clients", "link": { "type": "doc", - "id": "configuration/clients/overview" + "id": "configuration/clients/clients" } } diff --git a/docsite/docs/configuration/clients/overview.mdx b/docsite/docs/configuration/clients/clients.mdx similarity index 97% rename from docsite/docs/configuration/clients/overview.mdx rename to docsite/docs/configuration/clients/clients.mdx index 55337a65..b3077e5e 100644 --- a/docsite/docs/configuration/clients/overview.mdx +++ b/docsite/docs/configuration/clients/clients.mdx @@ -16,7 +16,7 @@ A **Client** is an application that stores the historical information about what | [Listenbrainz](/configuration/clients/listenbrainz) | ✅ | | [Koito](/configuration/clients/koito) | ❌ | | [Maloja](/configuration/clients/maloja) | ❌ | -| [Rocksky](/configuration/clients/rockysky) | ❌ | +| [Rocksky](/configuration/clients/rocksky) | ❌ | ## Features diff --git a/docsite/docs/configuration/clients/rocksky.mdx b/docsite/docs/configuration/clients/rocksky.mdx index a3f3affd..c6ab39b0 100644 --- a/docsite/docs/configuration/clients/rocksky.mdx +++ b/docsite/docs/configuration/clients/rocksky.mdx @@ -4,7 +4,7 @@ toc_min_heading_level: 2 toc_max_heading_level: 5 --- -[**Rocksky**](https://docs.rocksky.app/) is music tracking and discovery platform built on the AT Protocol (Bluesky). It is compatible with the Listenbrainz API and so can be as a multi-scrobbler [Listenbrainz Client](#listenbrainz) using a custom URL. +[**Rocksky**](https://docs.rocksky.app/) is music tracking and discovery platform built on the AT Protocol (Bluesky). It is compatible with the Listenbrainz API and so can be as a multi-scrobbler [Listenbrainz Client](/configuration/clients/listenbrainz) using a custom URL. You will need a [Bluesky](https://bsky.app/) account to use Rockysky. diff --git a/docsite/docs/configuration/configuration.mdx b/docsite/docs/configuration/configuration.mdx index 3495fa4e..a67f61f1 100644 --- a/docsite/docs/configuration/configuration.mdx +++ b/docsite/docs/configuration/configuration.mdx @@ -12,35 +12,7 @@ import FileExample from "../../src/components/FileExample"; import ScrobbleThreshold from "@site/src/components/snippets/_scrobble-threshold.mdx" import AIOConfig from '!!raw-loader!../../../config/config.json.example'; -import AzuracastConfig from '!!raw-loader!../../../config/azuracast.json.example'; -import ChromecastConfig from '!!raw-loader!../../../config/chromecast.json.example'; -import DeezerConfig from '!!raw-loader!../../../config/deezer.json.example'; -import DeezerDeprecatedConfig from '!!raw-loader!../../../config/deezer-deprecated.json.example'; -import IcecastConfig from '!!raw-loader!../../../config/icecast.json.example'; -import JellyfinConfig from '!!raw-loader!../../../config/jellyfin.json.example'; -import JriverfinConfig from '!!raw-loader!../../../config/jriver.json.example'; -import KodiConfig from '!!raw-loader!../../../config/kodi.json.example'; -import KoitoConfig from '!!raw-loader!../../../config/koito.json.example'; -import LastfmConfig from '!!raw-loader!../../../config/lastfm.json.example'; -import LastfmEndpointConfig from '!!raw-loader!../../../config/endpointlfm.json.example'; -import ListenbrainzConfig from '!!raw-loader!../../../config/listenbrainz.json.example'; -import ListenbrainzEndpointConfig from '!!raw-loader!../../../config/endpointlz.json.example'; -import MalojaConfig from '!!raw-loader!../../../config/maloja.json.example'; -import MopidyConfig from '!!raw-loader!../../../config/mopidy.json.example'; -import MprisConfig from '!!raw-loader!../../../config/mpris.json.example'; -import MusikcubeConfig from '!!raw-loader!../../../config/musikcube.json.example'; -import MusiccastConfig from '!!raw-loader!../../../config/musiccast.json.example'; -import MPDConfig from '!!raw-loader!../../../config/mpd.json.example'; -import PlexConfig from '!!raw-loader!../../../config/plex.json.example'; -import PlexWebhookConfig from '!!raw-loader!../../../config/plex.webhook.json.example'; import SpotifyConfig from '!!raw-loader!../../../config/spotify.json.example'; -import SubsonicConfig from '!!raw-loader!../../../config/subsonic.json.example'; -import TautulliConfig from '!!raw-loader!../../../config/tautulli.json.example'; -import WebscrobblerConfig from '!!raw-loader!../../../config/webscrobbler.json.example'; -import YTMusicConfig from '!!raw-loader!../../../config/ytmusic.json.example'; -import VLCConfig from '!!raw-loader!../../../config/vlc.json.example'; - -# Configuration :::tip @@ -156,9 +128,9 @@ Check the [**FAQ**](../FAQ.md) if you have any issues after configuration! These options affect multi-scrobbler's behavior and are not specific to any source/client. -#### Base URL +### Base URL -Defines the URL that is used to generate default redirect URLs for authentication on [spotify](#spotify) and [lastfm](#lastfm) -- as well as some logging hints. +Defines the URL that is used to generate default redirect URLs for authentication on [spotify](/configuration/sources/spotify) and [lastfm](/configuration/clients/lastfm) -- as well as some logging hints. * Default => `http://localhost:9078` * Set with [ENV](./?configType=env#configuration-types) `BASE_URL` or `baseUrl` [all-in-one configuration](./?configType=aio#configuration-types) @@ -170,7 +142,7 @@ Useful when running with [docker](../installation/installation.mdx#docker) so th <summary>Example</summary> - EX Lastfm Redirect Url is `BASE_URL:PORT/lastfm/callback` (when no other redirectUri is specified for [lastfm configuration](#lastfm)) + EX Lastfm Redirect Url is `BASE_URL:PORT/lastfm/callback` (when no other redirectUri is specified for [lastfm configuration](/configuration/clients/lastfm)) | `BASE_URL` | Redirect URL | |-------------------------------------|----------------------------------------------------------| @@ -187,7 +159,7 @@ Useful when running with [docker](../installation/installation.mdx#docker) so th </details> -#### Caching +### Caching Multi-scrobbler can cache some of its data for the purpose of **persisting queued and dead scrobbles** after restarting. @@ -274,7 +246,7 @@ It is **enabled by default** if `CACHE_SCROBBLE`, or the respective AIO configur </Tabs> -#### Debug Mode +### Debug Mode Turning on Debug Mode will @@ -295,13 +267,13 @@ To set debug mode either add it to [AIO `config.json`](./?configType=aio#configu or set the ENV `DEBUG_MODE=true` -#### Disable Web +### Disable Web If you do not need the dashboard and/or ingress sources, or have security concerns about ingress and cannot control your hosting environment, the web server and API can be disabled. :::warning -Any **ingress-based sources will be unusable** (Tautulli, Webscrobbler, etc...) if this is disabled. +Any **[ingress-based sources](/configuration/sources#ingress) will be unusable** (Webscrobbler, etc...) if this is disabled. ::: @@ -310,2621 +282,9 @@ Disable using either: * ENV `DISABLE_WEB=true` * In [All-in-One File](./?configType=aio#configuration-types) use the top-level property `"disableWeb": true` -## Common Source Functionality - -Some options and functionality is shared between all or some Sources. Detailed below is information on what these features are and how to use/configured them. - -### Limiting Scrobble Destination - -By default a Source will scrobble to **all** configured Clients. - -To limit which Clients a Source will scrobble to use the `clients` property in a Source's [File/AIO configuration](./?configType=file#configuration-types) to specify the **names** of the Clients that it should scrobble to: - -```json5 title="jellyfin.json" -{ - "name": "MyJellyfin", - "clients": ["myMaloja","koitoA","listenbrainzFoo"] - "data": { - // ... - }, -} -``` - -The Source above will only scrobble to Clients that are **named** (`name` property in Client config) either `myMaloja` `koitoA` or `listenbrainzFoo`. - -If your Client is configured using [ENV](./?configType=env#configuration-types) then they are given an automatically generated name. Check the multi-scrobbler Dashboard to find their name in a subheader under the Client type. Example: - -![Client Name Subheader](client-subheader.png) - -<h3 id="scrobble-thresholds">Scrobble Thresholds</h3> - -<ScrobbleThreshold filename="spotify"/> - -These can also be configured **globally** for all Sources using ENVs: - -``` -SOURCE_SCROBBLE_DURATION=40 -SOURCE_SCROBBLE_PERCENT=20 -``` - -<h3 id="should-scrobble-behavior">Should Scrobble Behavior</h3> - -:::note - -This feature is currently available only for some Sources. Check each Source's documentation to see if it supported. - -::: - -For _most_ Sources multi-scrobbler can determine if a user is listening and scrobble automatically. For Sources which multi-scrobbler cannot do this or require additional configuration, a user can indicate to MS that it should or should not scrobble. This action can be taken on the Web Dashboard by clicking the link next to **Should Scrobble** found in each Source's status card. - -Additionally, for these Sources, the default behavior for Scrobbling can be explicitly set by adding the following value to a Source's `options` in [file or aio config](./?configType=file#configuration-types): - -```json5 title="azuracast.json" -{ - "data": { - // ... - }, - "options": { - // set to true to always scrobble automatically - // set to false to never scrobble automatically - "systemScrobble": true - } -} -``` - -## Source Configurations - -### [Azuracast](https://www.azuracast.com/) - -:::tip[Scrobbling Threshold] - -Azuracast may not correctly report track length or position. If this is the case you should reduce the "listened to" **duration** using [Scrobble Thresholds](#scrobble-thresholds) so that multi-scrobbler scrobbles correctly. - -::: - -The Azuracast server should have **Use High-Performance Now Playing Updates** enabled in _Administration -> System Settings_ - -##### URL - -The URL used by MS to connect to Azuracast has the syntax: - -``` -[ws|wss]://HOST:[PORT] -``` - -MS will automatically add the path required for websockets, [`/api/live/nowplaying/websocket`](https://www.azuracast.com/docs/developers/now-playing-data/#websockets), to your URL if none is provided. If you use a reverse proxy with a path-based URL or otherwise need a custom path to access the websockets path correctly then explicitly provide it. Examples: - -``` -URL From Config => MS Uses - -'ws://192.168.0.101' => ws://192.168.0.101/api/live/nowplaying/websocket -'ws://azura.mydomain.com' => ws://azura.mydomain.com.com/api/live/nowplaying/websocket -'wss://mydomain.com/custom/azura/ws' => wss://mydomain.com/custom/azura/ws -``` - -##### Scrobble Behavior - -Multi-scrobbler will set the default [**scrobbling behavior**](#should-scrobble-behavior) based on if either of these settings is configured: - -* live status (`AZURA_LIVE` or `monitorWhenLive`) or -* listener count (`AZURA_LISTENERS_NUM` or `monitorWhenListeners`) - -If either is configured then MS will automatically scrobble based on when these conditions are met. - -If neither is configured automatic scrobble defaults to **off** and you should either [manually initiate it or explicitly set the default behavior.](#should-scrobble-behavior) - -#### Configuration - -<Tabs groupId="configType" queryString> - <TabItem value="env" label="ENV"> - | Environmental Variable | Required? | Default | Description | - | :--------------------- | :-------- | :------ | ---------------------------------------------------------------------------------------------- | - | `AZ_URL` | Yes | | Azuracast URL *without station name* | - | `AZ_STATION` | Yes | | The station name shown on the public page | - | `AZURA_LIVE` | No | Yes | Only scrobble when station status is ONLINE | - | `AZURA_LISTENERS_NUM` | No | `true` | Only scrobble if station has any listeners (`true`) or listeners are equal-to/greater-than `X` | - </TabItem> - <TabItem value="file" label="File"> - <details> - - <summary>Example</summary> - - <CodeBlock title="CONFIG_DIR/azuracast.json" language="json5">{AzuracastConfig}</CodeBlock> - - </details> - - or <SchemaLink lower objectName="AzuracastSourceConfig"/> - </TabItem> - <TabItem value="aio" label="AIO"> - <details> - - <summary>Example</summary> - - <AIOExample data={AzuracastConfig} name="azuracast"/> - - </details> - - or <SchemaLink lower objectName="AzuracastSourceConfig"/> - </TabItem> -</Tabs> - -### [Deezer](https://deezer.com/) - -<Tabs groupId="deezerSource" queryString> - <TabItem value="arl" label="Unofficial ARL"> - :::warning - - This Source uses unofficial methods to access Deezer data that are likely in violation of Deezer's TOS. Deezer may change or remove these methods at any time, breaking functionality. Use this Source at your own risk. - - ::: - - :::info - - This Source will work only if you have a **Premium** Deezer account. - - ::: - - ##### Retrieve ARL - - The `arl` is a browser cookie that Deezer uses to store your login information. Multi-scrobbler uses the `arl` value to make authorized requests to Deezer's API where it can retrieve listening history. - - **After logging into Deezer**, use one of the methods below to retrieve the `arl` cookie value: - - <Tabs groupId="arlRetrival"> - <TabItem value="chrome" label="Chrome"> - 1. On deezer.com, press F12 to open the Developer Tools window. - 2. Go to the Applications tab. Press the '>>' More tabs icon if it's hidden. - 3. Under the Cookies Filter, click deezer.com, then the text bar name `arl`. - 4. Copy all the contents shown in the **Cookie Value** pane. - </TabItem> - <TabItem value="firefox" label="Firefox"> - 1. On deezer.com, hit Ctrl+Shift+I to open Developer Tools - 2. Go to the Storage Tab, then expand Cookies in the sidebar and select deezer.com - 3. Find the row with `arl` as the name, then double click the Value column and right click -> copy the value - </TabItem> - </Tabs> - - #### Configuration - - <Tabs groupId="configType" queryString> - <TabItem value="env" label="ENV"> - | Environmental Variable | Required? | Default | Description | - | :--------------------- | --------- | ------- | ------------------------------------------------ | - | `DEEZER_ARL` | Yes | | The ARL cookie value retrieved from your browser | - </TabItem> - <TabItem value="file" label="File"> - <details> - - <summary>Example</summary> - - <CodeBlock title="CONFIG_DIR/deezer.json" language="json5">{DeezerConfig}</CodeBlock> - - </details> - - or <SchemaLink lower objectName="DeezerSourceConfig"/> - </TabItem> - <TabItem value="aio" label="AIO"> - <details> - - <summary>Example</summary> - - <AIOExample data={DeezerConfig} name="deezer"/> - - </details> - - or <SchemaLink lower objectName="DeezerSourceConfig"/> - </TabItem> - </Tabs> - - - ##### Duplicate detection - - Third party integrations with Deezer (Sonos) may cause Deezer to [report the same track many times in listening history.](https://github.com/FoxxMD/multi-scrobbler/pull/296#issuecomment-2922374738) If you experience this issue modify your Deezer Source config (file-based only) to include the option `"fuzzyDiscoveryIgnore": "aggressive"` - - <details> - - <summary>deezer.json example</summary> - - ```json title="deezer.json" - [ - { - "name": "MyDeezer", - "data": { - "arl": "UOsRPjT3U5Dhaaup3x...", - }, - "options": { - "fuzzyDiscoveryIgnore": "aggressive" - } - } - ] - ``` - </details> - - This option comes with some trade-offs: MS will aggressively detect repeated tracks within a window of time that should eliminate all duplicates. However, this will also prevent *intentionally* repeated tracks from being scrobbled. See [this thread](https://github.com/FoxxMD/multi-scrobbler/pull/296#issuecomment-2970417070) for more information on how this works. - - </TabItem> - <TabItem value="official" label="Official API"> - - :::warning - - **This Source is DEPRECATED because Deezer has dropped official API support.** This Source will **not** be removed but no further support or fixes will be given. [See this issue for more discussion.](https://github.com/FoxxMD/multi-scrobbler/issues/175#issuecomment-2296776625) - - Users cannot create new applications on Deezer Developers and there is no guarantee existing applications will continue to work. - - As a workaround consider: - - * Using the alternative [Deezer Source that uses unofficial, internal API via ARL](./?deezerSource=arl#deezer) - * Connect Deezer with Last.fm and then use [Last.fm as a Source](#lastfm-source) - - ::: - - Create a new application at [Deezer Developers](https://developers.deezer.com/myapps) - - * Application Domain must be the same as your multi-scrobbler domain. Default is `localhost:9078` - * Redirect URL must end in `deezer/callback` - * Default would be `http://localhost:9078/deezer/callback` - - After application creation you should have credentials displayed in the "My Apps" dashboard. You will need: - - * **Application ID** - * **Secret Key** - * **Redirect URL** (if not the default) - - **If no access token is provided...** - - After starting multi-scrobbler with credentials in-place open the dashboard (`http://localhost:9078`) and find your Deezer source. Click **(Re)authenticate and (re)start polling** to start the login process. After login is complete polling will begin automatically. - - #### Configuration - - <Tabs groupId="configType" queryString> - <TabItem value="env" label="ENV"> - | Environmental Variable | Required? | Default | Description | - |------------------------|-----------|-----------------------------------------|-----------------------------------| - | `DEEZER_CLIENT_ID` | Yes | | Your **Application ID** | - | `DEEZER_CLIENT_SECRET` | Yes | | Your **Secret Key** | - | `DEEZER_REDIRECT_URI` | No | `http://localhost:9078/deezer/callback` | URI must end in `deezer/callback` | - </TabItem> - <TabItem value="file" label="File"> - <details> - - <summary>Example</summary> - - <CodeBlock title="CONFIG_DIR/deezer.json" language="json5">{DeezerDeprecatedConfig}</CodeBlock> - - </details> - - or <SchemaLink lower objectName="DeezerSourceConfig"/> - </TabItem> - <TabItem value="aio" label="AIO"> - <details> - - <summary>Example</summary> - - <AIOExample data={DeezerDeprecatedConfig} name="deezer"/> - - </details> - - or <SchemaLink lower objectName="DeezerSourceConfig"/> - </TabItem> - </Tabs> - - </TabItem> -</Tabs> - -### [Google Cast (Chromecast)](https://www.google.com/chromecast/built-in/) - -If your media device can be **Cast** to using this button ![Chromecast Icon](https://upload.wikimedia.org/wikipedia/commons/2/26/Chromecast_cast_button_icon.svg) on your phone/computer then multi-scrobbler can monitor it in order to scrobble music you play. - - -:::note - -Google Cast support is **experimental**. You may experience crashes and errors while using this Source. Please open an issue if you experience problems and include all information detailed in the issue template to help debug your issue. - -::: - -:::note - -This source relies on common, **basic** music data provided by the cast device which will always be less exhaustive than data parsed from full source integrations. If there is an existing [Source](#source-configurations) it is recommended to configure for it and blacklist the app on Google Cast, rather than relying solely on Google Cast for scrobbling. - -::: - -#### Configuration - -<Tabs groupId="configType" queryString> - <TabItem value="env" label="ENV"> - :::note - [Manually configuring cast device connections](#connecting-devices) is only available through file-based config. - ::: - - | Environmental Variable | Required? | Default | Description | - |------------------------|-----------|---------|--------------------------------------------------------------------------------------| - | CC_ENABLE | No | | Set to 'true' to enable Cast monitoring without needing to define other ENVs | - | CC_WHITELIST_DEVICES | No | | Only scrobble from these Cast devices. Comma-delimited list. EX mini-home, family-tv | - | CC_BLACKLIST_DEVICES | No | | Do not scrobble from these Cast devices. Comma-delimited list | - | CC_WHITELIST_APPS | No | | Only scrobble from these casted Apps. Comma-delimited list. EX spotify, pandora | - | CC_BLACKLIST_APPS | No | | Do not scrobble from these casted Apps. Comma-delimited list | - </TabItem> - <TabItem value="file" label="File"> - - <details> - - <summary>Example</summary> - - <CodeBlock title="CONFIG_DIR/chromecast.json" language="json5">{ChromecastConfig}</CodeBlock> - - </details> - - or <SchemaLink lower objectName="ChromecastSourceConfig"/> - </TabItem> - <TabItem value="aio" label="AIO"> - <details> - - <summary>Example</summary> - - <AIOExample data={ChromecastConfig} name="chromecast"/> - - </details> - - or <SchemaLink lower objectName="ChromecastSourceConfig"/> - </TabItem> -</Tabs> - -#### Connecting Devices - -Cast devices can be manually configured using file-based configuration OR automatically discovered using **mDNS.** - -###### mDNS Discovery - -The host machine running multi-scrobbler must be configured to allow [mDNS traffic on port 5353/UDP](https://book.hacktricks.xyz/network-services-pentesting/5353-udp-multicast-dns-mdns). - -<details> - - <summary>OS Specific Instructions</summary> - -<Tabs> - <TabItem value="linux" label="Linux"> - **Docker** - - The host machine must have [avahi-daemon](https://avahi.org/) running to circumvent limitations with DNS resolution due to musl in Alpine. Most major linux distributions package avahi and many have it built-in. Once avahi is running you must pass D-Bus and the avahi daemon socket to your container like so: - - ``` - docker run ... -v /var/run/dbus:/var/run/dbus -v /var/run/avahi-daemon/socket:/var/run/avahi-daemon/socket ... foxxmd/multi-scrobbler - ``` - - **Flatpak/Nodejs** - - No additional steps are required. - </TabItem> - <TabItem value="windows" label="Windows"> - **Docker** - - Unsupported at this time. - - **Nodejs** - - No additional steps are required. - </TabItem> -</Tabs> - -</details> - -#### What Media Does MS Scrobble? - -Cast devices report what type of media the current activity is [(see `metadata` property here)](https://developers.google.com/cast/docs/media/messages#MediaInformation). The reported type is dependent on the application playing the media to correctly report it, the cast device does not magically know what the media is. If an application does not report a type it is always classified as `unknown`. - -**By default, MS will only track media that is reported as `MusicTrack`.** - -##### Allow Unknown Media Type - -Media with an Unknown (`Generic`) media type can be explicitly allowed by setting `"allowUnknownMedia": true` in the file-based configuration. This can also be configured to only allow unknown media types for specific applications by using a list of application names. - -<details> - - <summary>Example</summary> - - ```json5 title="chromecast.json" - [ - { - "name": "MyCast", - "type": "chromecast", - "data": { - // only allow unknown if app name contains any of these phrases - "allowUnknownMedia": ["smarttube", "default media receiver"] - }, - } - ] - ``` - -</details> - -##### Forcing Media Tracking - -MS can be forced to track media from an application regardless of media type. This is useful if an application incorrectly reports a media type you are sure should be music. Set `"forceMediaRecognitionOn"` in the file-based configuration to a list of application names that should always be tracked. - -<details> - - <summary>Example</summary> - - ```json5 title="chromecast.json" - [ - { - "name": "MyCast", - "type": "chromecast", - "data": { - // media from applications that contains these phrases will always be tracked, regardless of media type reported - "forceMediaRecognitionOn": ["smarttube", "default media receiver"] - }, - } - ] - ``` - -</details> - -#### Cast Troubleshooting - -Please include any/all logs with raw output if there are any errors encountered as this is critical to diagnosing issues. - -To diagnose bad/incomplete track information or strange MS player behavior please turn on **payload logging** and include log output of the source running to help diagnose this issue. Either enable in config using the below example OR enable [Debug Mode.](#debug-mode) - -<details> - - <summary>Example</summary> - - ```json5 title="chromecast.json" - [ - { - "name": "MyCast", - "type": "chromecast", - "data": { - //... - }, - "options": { - "logPayload": true - } - } - ] - ``` - -</details> - -### [Icecast](https://icecast.org) - -Parse metadata from any Icecast Station (streams) to use for scrobbling. - -:::note - -There is no standard format for presenting track information in an Icecast stream and the majority of streams that do include metadata only use a single string (`StreamTitle`) from which metadata can be parsed. - -Multi-scrobbler will do its best to parse Artist/Album/Track information from this data but the quality and consistency for this is solely based on how the Icecast broadcaster chooses to format their tracks. - -::: - -:::tip[Scrobbling Behavior] - -Multi-scrobbler cannot automatically determine if you are listening to a Station. Therefore, **automatic scrobbling for Icecast Sources is off by default.** - -Use [Should Scrobble Behavior](#should-scrobble-behavior) to initiate scrobbling or configure automatic scrobble. - -::: - -:::tip[Scrobbling Threshold] - -Icecast does not report track lengths so multi-scrobbler cannot determine if it should scrobble based on percent played. You should instead set a shorter "listened to" **duration** using [Scrobble Thresholds.](#scrobble-thresholds) - -::: - - -##### URL and Sources - -The URL should be the URL you would use to stream audio. - -Additional sources are supported based on those found in [`icecast-metadata-stats`](https://github.com/eshaz/icecast-metadata-js/tree/main/src/icecast-metadata-stats#supported-status-apis). To use these with automatic parsing [based on the regular URL](https://github.com/eshaz/icecast-metadata-js/tree/main/src/icecast-metadata-stats#options) include a `sources` array in your config the same way as shown in `icecast-metadata-stats`: - -<details> - - <summary>Example</summary> - -```json5 title="config.json" -[ - { - "name": "myIcy", - "enable": true, - "data": { - "url": "http://myIcecast.com/radio", - "sources": ["icy", "ogg", "sevenhtml"] - } - } - ] - -``` -</details> - -To manually set these sources include the same [`*Endpoint` options](https://github.com/eshaz/icecast-metadata-js/tree/main/src/icecast-metadata-stats#options) found in `icecast-metadata-stats`. - -If no `sources` are configured for multi-scrobbler it will automatically try to use `icy` and `ogg`. - -#### Configuration - -<Tabs groupId="configType" queryString> - <TabItem value="env" label="ENV"> - | Environmental Variable | Required? | Default | Description | - | :--------------------- | :-------- | :------ | ----------- | - | `ICECAST_URL` | Yes | | Icecast URL | - </TabItem> - <TabItem value="file" label="File"> - <details> - - <summary>Example</summary> - - <CodeBlock title="CONFIG_DIR/icecast.json" language="json5">{IcecastConfig}</CodeBlock> - - </details> - - or <SchemaLink lower objectName="IcecastSourceConfig"/> - </TabItem> - <TabItem value="aio" label="AIO"> - <details> - - <summary>Example</summary> - - <AIOExample data={IcecastConfig} name="icecast"/> - - </details> - - or <SchemaLink lower objectName="IcecastSourceConfig"/> - </TabItem> -</Tabs> - -### [Jellyfin](https://jellyfin.org/) - -:::warning[Webhook Method Deprecated] - -In multi-scrobbler **below v0.8.4** communication with Jellyfin was done using Jellyfin's **Webhook** plugin. This has been deprecated in favor of directly using Jeyllfin's API for a better experience in multi-scrobbler. - -Webhook functionality will be removed in an upcoming release of MS. If you are still using the Webhook method you should migrate to the API method as soon as possible. - -::: - -<details> - - <summary>Migrating from Webhook (multi-scrobbler below `v0.8.4`) to API</summary> - - * Remove the Jellyfin **Webhook** plugin (if not using it for anything else) - * Follow the new instructions below (outside this section) to setup API usage - * The `servers` (`JELLYFIN_SERVERS`) setting is no longer available as MS only scrobbles from the server using the API anyways. - * If you need to scrobble for multiple servers set up each server as a separate Jellyfin source - * The `users` (`JELLYFIN_USER`) setting has been renamed `usersAllow` (`JELLYFIN_USERS_ALLOWED`) - * If you were using this filter to ensure only scrobbles from yourself were registered then you no longer need this setting -- by default MS will only scrobble for the user authenticated with Jellyfin's API. - -</details> - -Must be using Jellyfin 10.7 or greater - -* Create an **API Key** for multi-scrobbler - * In the Jellyfin desktop web UI Navigate to -> Administration -> Dashboard -> API Keys (`http://YOUR_JELLYIN_URL/web/index.html#!/apikeys.html`) - * Click `+` button and create a new key with **App name** `multi-scrobbler` - * Copy the created API Key value for use in configuration below - - -It is **recommended** to use API Key + username but if you are not an admin for your Jellyfin instance you can also authenticate with your Jellyfin username and **password.** - -:::tip[Important Defaults] - -By default... - -* multi-scrobbler will **only** scrobble for the user authenticated with the API. - * Allowed Users (`usersAllow` or `JELLYFIN_USERS_ALLOW`) are only necessary if you want to scrobble for additional users. -* multi-scrobbler will **only** scrobble media found in Jellyfin libraries that were labelled as **Music.** - * `librariesAllow` or `JELLYFIN_LIBRARIES_ALLOW` will override this - * OR use `additionalAllowedLibraryTypes` to allow more types (like `mixed` or `book` for audiobooks) -* multi-scrobbler will **only** scrobble media Jellyfin detects as **Audio.** - * To force multi-scrobbler to scrobble when media is detected as **Unknown** use `"allowUnknown": true` in file/aio configuration. - -::: - -#### Configuration - -<Tabs groupId="configType" queryString> - <TabItem value="env" label="ENV"> - | Environmental Variable | Required? | Default | Description | - | -------------------------- | --------- | ------- | ------------------------------------------------------------------------------------------ | - | `JELLYFIN_URL` | **Yes** | | The URL of the Jellyfin server IE `http://localhost:8096` | - | `JELLYFIN_USER` | **Yes** | | The user to authenticate with the API | - | `JELLYFIN_APIKEY` | No | | The API Key to use for authentication **(Must provide either apikey or password)** | - | `JELLYFIN_PASSWORD` | No | | The password of the user to authenticate for. **(Must provide either apikey or password)** | - | `JELLYFIN_USERS_ALLOW` | No | | Comma-separated list of usernames (from Jellyfin) to scrobble for | - | `JELLYFIN_USERS_BLOCK` | No | | Comma-separated list of usernames (from Jellyfin) to disallow scrobble for | - | `JELLYFIN_DEVICES_ALLOW` | No | | Comma-separated list of devices to scrobble from | - | `JELLYFIN_DEVICES_BLOCK` | No | | Comma-separated list of devices to disallow scrobbles from | - | `JELLYFIN_LIBRARIES_ALLOW` | No | | Comma-separated list of libraries to allow scrobbles from | - | `JELLYFIN_LIBRARIES_BLOCK` | No | | Comma-separated list of libraries to disallow scrobbles from | - </TabItem> - <TabItem value="file" label="File"> - <details> - - <summary>Example</summary> - - <CodeBlock title="CONFIG_DIR/jellyfin.json" language="json5">{JellyfinConfig}</CodeBlock> - - </details> - - or <SchemaLink lower objectName="JellyApiSourceConfig"/> - - </TabItem> - <TabItem value="aio" label="AIO"> - <details> - - <summary>Example</summary> - - <AIOExample data={JellyfinConfig} name="jellyfin"/> - - </details> - - or <SchemaLink lower objectName="JellyApiSourceConfig"/> - </TabItem> -</Tabs> - -### [JRiver](https://jriver.com/) - -In order for multi-scrobbler to communicate with JRiver you must have [Web Server Interface](https://wiki.jriver.com/index.php/Web_Service_Interface#Documentation_of_Functions) enabled. This can can be in the JRiver GUI: - -* Tools -> Options -> Media Network - * Check `Use Media Network to share this library...` - * If you have `Authentication` checked you will need to provide the **Username** and **Password** in the ENV/File configuration below. - -#### Configuration {#jriver-config} - -<Tabs groupId="configType" queryString> - <TabItem value="env" label="ENV"> - | Environmental Variable | Required | Default | Description | - |------------------------|----------|---------------------------------|------------------------------------------------| - | JRIVER_URL | Yes | http://localhost:52199/MCWS/v1/ | The URL of the JRiver server | - | JRIVER_USERNAME | No | | If authentication is enabled, the username set | - | JRIVER_PASSWORD | No | | If authenticated is enabled, the password set | - </TabItem> - <TabItem value="file" label="File"> - <details> - - <summary>Example</summary> - - <CodeBlock title="CONFIG_DIR/jriver.json" language="json5">{JriverfinConfig}</CodeBlock> - - </details> - - or <SchemaLink lower objectName="JRiverSourceConfig"/> - </TabItem> - <TabItem value="aio" label="AIO"> - <details> - - <summary>Example</summary> - - <AIOExample data={JriverfinConfig} name="jriver"/> - - </details> - - or <SchemaLink lower objectName="JRiverSourceConfig"/> - </TabItem> -</Tabs> - -##### URL - -The URL used to connect to JRiver, set in your [file config as `url`.](#jriver-config) The URL used to connect ultimately must be formed like this: - -``` -Syntax => [protocol]://[hostname]:[port]/[path] -Default => http://localhost:52199/MCWS/v1/ -``` - -If any part of this URL is missing multi-scrobbler will use a default value. This also means that if any part of your URL is **not** standard you must explicitly define it. - -:::tip[Jriver Settings] - -* Make sure the port number matches what is found in `Advanced` section in the [Media Network](#jriver) options. -* If your installation is on the same machine but you cannot connect using `localhost` try `0.0.0.0` instead. - -::: - -<details> - <summary>URL Transform Examples</summary> - - Examples of a given `url` in your file config and the final URL multi-scrobbler will use to connect to JRiver: - - | `url` | Transformed URL | - |------------------------------------------|---------------------------------------------| - | (none set) | `http://localhost:52199/MCWS/v1/` | - | `jriver.mydomain.com` | `http://jriver.mydomain.com:52199/MCWS/v1/` | - | `192.168.0.101:3456` | `http://192.168.0.101:3456/MCWS/v1/` | - | `mydomain.com:80/jriverReverse/MCWS/v1/` | `mydomain.com:80/jriverReverse/MCWS/v1/` | - -</details> - -### [Kodi](https://kodi.tv/) - -In order for multi-scrobbler to communicate with Kodi you must have the [Web Interface](https://kodi.wiki/view/Web_interface) enabled. This can be done in the Kodi GUI: - -* Settings -> Services -> Control - * Check `Allow remote control via HTTP` - * Ensure you have a **Username** and **Password** set, you will need to provide them in the ENV/File configuration below. - -#### Configuration {#kodi-config} - -<Tabs groupId="configType" queryString> - <TabItem value="env" label="ENV"> - | Environmental Variable | Required | Default | Description | - |------------------------|----------|-------------------------------|----------------------------| - | KODI_URL | Yes | http://localhost:8080/jsonrpc | The URL of the Kodi server | - | KODI_USERNAME | No | | The username set | - | KODI_PASSWORD | No | | The password set | - </TabItem> - <TabItem value="file" label="File"> - <details> - - <summary>Example</summary> - - <CodeBlock title="CONFIG_DIR/kodi.json" language="json5">{KodiConfig}</CodeBlock> - - </details> - - or <SchemaLink lower objectName="KodiSourceConfig"/> - </TabItem> - <TabItem value="aio" label="AIO"> - <details> - - <summary>Example</summary> - - <AIOExample data={KodiConfig} name="kodi"/> - - </details> - - or <SchemaLink lower objectName="KodiSourceConfig"/> - </TabItem> -</Tabs> - -##### URL - -The URL used to connect to Kodi, set in your [file config as `url`.](#kodi-config) The URL used to connect ultimately must be formed like this: - -``` -Syntax => [protocol]://[hostname]:[port]/[path] -Default => http://localhost:8080/jsonrpc -``` - -If any part of this URL is missing multi-scrobbler will use a default value. This also means that if any part of your URL is **not** standard you must explicitly define it. - -:::tip[Kodi Settings] - -* Make sure the port number matches what is found in **Port** in the [Control](#kodi) section mentioned above. -* If your installation is on the same machine but you cannot connect using `localhost` try `0.0.0.0` instead. - -::: - -<details> - <summary>URL Transform Examples</summary> - - Examples of a given `url` in your file config and the final URL multi-scrobbler will use to connect to Kodi: - - | `url` | Transformed URL | - |---------------------------------------|----------------------------------------------| - | (none set) | `http://localhost:8080/jsonrpc` | - | `kodi.mydomain.com` | `http://kodi.mydomain.com:8080/jsonrpc` | - | `192.168.0.101:3456` | `http://192.168.0.101:3456/jsonprc` | - | `mydomain.com:80/kodiReverse/jsonrpc` | `http://mydomain.com:80/kodiReverse/jsonrpc` | - -</details> - -### [Koito (Source)](https://koito.io/) - -This Source monitors a Koito account's scrobble history and then re-scrobbles discovered tracks to configured [Clients.](#client-configurations) - -:::tip[Other Uses] - -To _scrobble to_ a Koito server, create a [Koito (Client)](#koito) - -::: - -See the [Koito (Client)](#koito) configuration for general setup. The only difference for **Source** configuration: - -* Cannot be setup with ENV config -* [File/AIO config](./?configType=file#configuration-types) must include `"configureAs": "source"` - -#### Configuration - -<Tabs groupId="configType" queryString> - <TabItem value="env" label="ENV"> - :::note - You cannot use ENV variables shown in the [Koito Client config](#koito) -- multi-scrobbler assumes Koito ENVs are always used for the **client** configuration. You must use the [File or AIO](./?configType=file#configuration-types) config to setup Koito as a Source. - ::: - </TabItem> - <TabItem value="file" label="File"> - <details> - Change `configureAs` to `source` - - <summary>Example</summary> - - <FileExample title="CONFIG_DIR/koito.json" data={KoitoConfig}/> - - </details> - - or <SchemaLink lower objectName="KoitoSourceConfig"/> - </TabItem> - <TabItem value="aio" label="AIO"> - <details> - Change `configureAs` to `source` - - <summary>Example</summary> - - <AIOExample data={KoitoConfig} name="koito"/> - - </details> - - or <SchemaLink lower objectName="KoitoSourceConfig"/> - </TabItem> -</Tabs> - -### [Last.fm (Source)](https://www.last.fm) - -This Source **monitors your Last.fm scrobble history** and re-scrobbles new activity to configured [Clients.](#client-configurations) - -:::tip[Other Uses] - -* To **scrobble to** Last.fm, create a [Last.fm (Client)](#lastfm) -* To accept scrobbles from outside applications as if multi-scrobbler was Last.fm, create a [Last.fm (Endpoint)](#lastfm-endpoint) - -::: - -To configure this Source use the same registration instructions and configuration data shown in [Last.fm (Client)](#lastfm) with the difference being `"configureAs": "source"` defined in the File/AIO configuration below. - -#### Configuration - -<Tabs groupId="configType" queryString> - <TabItem value="env" label="ENV"> - No support for ENV based for Last.fm as a client (only source) - </TabItem> - <TabItem value="file" label="File"> - <details> - Change `configureAs` to `source` - - <summary>Example</summary> - - <FileExample title="CONFIG_DIR/lastfm.json" data={LastfmConfig}/> - - </details> - - or <SchemaLink lower objectName="LastfmSourceConfig"/> - </TabItem> - <TabItem value="aio" label="AIO"> - <details> - Change `configureAs` to `source` - - <summary>Example</summary> - - <AIOExample data={LastfmConfig} name="lastfm"/> - - </details> - - or <SchemaLink lower objectName="LastfmSourceConfig"/> - </TabItem> -</Tabs> - -### [Last.fm (Endpoint)](https://www.last.fm) - -This Source enables multi-scrobbler to accept scrobbles from outside applications as if it was a Last.fm server. - -:::tip[Other Uses] - -* To **scrobble to** Last.fm, create a [Last.fm (Client)](#lastfm) -* To monitor and re-scrobble activity from your Last.fm account, create a [Last.fm (Source)](#lastfm-source) - -::: - -##### URL - -If a **slug** is **not** provided in configuration then multi-scrobbler will accept Last.fm scrobbles at - -``` -http://localhost:9078/2.0/ -``` - -which is the "standard" Last.fm server path for scrobbling - -Use a slug only if you need to setup multiple Last.fm Endpoint sources and cannot use different tokens. - -If a slug is used then the URL will be: - -``` -http://localhost:9078/api/lastfm/mySlug -``` - -#### Configuration - -<Tabs groupId="configType" queryString> - <TabItem value="env" label="ENV"> - | Environmental Variable | Required? | Default | Description | - | :--------------------- | :-------- | ------- | ------------------------------------------------------------------------------------------------------------------ | - | `LFMENDPOINT_ENABLE` | No | | Use LFM Endpoint as a Source without any other configuration. Only required if slug/token are not provided as ENVs | - | `LFM_SLUG` | No | | (Optional) The URL suffix to use for accepting LFM scrobbles | - </TabItem> - <TabItem value="file" label="File"> - <details> - Change `configureAs` to `source` - - <summary>Example</summary> - - <CodeBlock title="CONFIG_DIR/endpointlfm.json" language="json5">{LastfmEndpointConfig}</CodeBlock> - - </details> - - or <SchemaLink lower objectName="LastFMEndpointSourceConfig"/> - </TabItem> - <TabItem value="aio" label="AIO"> - <details> - Change `configureAs` to `source` - - <summary>Example</summary> - - <AIOExample data={LastfmEndpointConfig} name="endpointlfm"/> - - </details> - - or <SchemaLink lower objectName="LastFMEndpointSourceConfig"/> - </TabItem> -</Tabs> - -### [Listenbrainz (Source)](https://listenbrainz.org) - -This Source monitors a Listenbrainz account's scrobble history and then re-scrobbles discovered tracks to configured clients. - -:::tip[Other Uses] - -* To **scrobble to** a Listenbrainz server, create a [Listenbrainz (Client)](#listenbrainz) -* To accept scrobbles from outside applications as if multi-scrobbler was a Listenbrainz server, create a [Listenbrainz (Endpoint)](#listenbrainz-endpoint) - -::: - -You will need to run your own Listenbrainz server or have an account [on the official instance](https://listenbrainz.org/login/). - -On your [profile page](https://listenbrainz.org/profile/) find your **User Token** to use in the configuration. - -#### Configuration - -<Tabs groupId="configType" queryString> - <TabItem value="env" label="ENV"> - :::note - You cannot use ENV variables shown in the [Listenbrainz Client config](#listenbrainz) -- multi-scrobbler assumes Listenbrainz ENVs are always used for the **client** configuration. You must use the **File** or AOI config to setup Listenbrainz as a Source. - ::: - </TabItem> - <TabItem value="file" label="File"> - <details> - Change `configureAs` to `source` - - <summary>Example</summary> - - <FileExample title="CONFIG_DIR/listenbrainz.json" data={ListenbrainzConfig}/> - - </details> - - or <SchemaLink lower objectName="ListenBrainzSourceConfig"/> - </TabItem> - <TabItem value="aio" label="AIO"> - <details> - Change `configureAs` to `source` - - <summary>Example</summary> - - <AIOExample data={ListenbrainzConfig} name="listenbrainz"/> - - </details> - - or <SchemaLink lower objectName="ListenBrainzSourceConfig"/> - </TabItem> -</Tabs> - -### [Listenbrainz (Endpoint)](https://listenbrainz.org) - -This Source enables multi-scrobbler to accept scrobbles from outside applications as if it was a Listenbrainz server. An example of this would be configuring [pano-scrobbler](https://github.com/kawaiiDango/pano-scrobbler) with a custom Listenbrainz URL to point to multi-scrobbler. - -:::tip[Other Uses] - -* To **scrobble to** a Listenbrainz server, create a [Listenbrainz (Client)](#listenbrainz) -* To monitor and re-scrobble activity from a Listenbrainz account, create a [Listenbrainz (Source)](#listenbrainz-source) - -::: - -##### URL - -If a **slug** is **not** provided in configuration then multi-scrobbler will accept Listenbrainz scrobbles at - -``` -http://localhost:9078/1/submit-listens -``` - -which is the "standard" Listenbrainz server path for scrobbling. - -Use a slug only if you need to setup multiple Listenbrainz Endpoint sources and cannot use different tokens. - -If a slug is used then the URL will be: - -``` -http://localhost:9078/api/listenbrainz/mySlug -``` - -:::note - Some Listenbrainz applications may require custom Listenbrainz URLs to be a real domain (`example.com`) and/or use SSL (`https://`). In this case you should setup multi-scrobbler behind a reverse proxy to support this functionality. It is out of the scope of this project to do this solely within multi-scrobbler. -::: - -##### Token - -Most Listenbrainz clients require a token (Authentication Token) to be provided during setup. This value can be anything you want, just make sure to use the same value for `token` in your multi-scrobbler configuration for the endpoint. - - -#### Configuration - -<Tabs groupId="configType" queryString> - <TabItem value="env" label="ENV"> - | Environmental Variable | Required? | Default | Description | - | :--------------------- | :-------- | ------- | ----------------------------------------------------------------------------------------------------------------- | - | `LZENDPOINT_ENABLE` | No | | Use LZ Endpoint as a Source without any other configuration. Only required if slug/token are not provided as ENVs | - | `LZE_TOKEN` | No | | LZ "Authentication Token" you provided to the scrobbling application | - | `LZE_SLUG` | No | | (Optional) The URL suffix to use for accepting LZ scrobbles | - </TabItem> - <TabItem value="file" label="File"> - <details> - Change `configureAs` to `source` - - <summary>Example</summary> - - <CodeBlock title="CONFIG_DIR/endpointlz.json" language="json5">{ListenbrainzEndpointConfig}</CodeBlock> - - </details> - - or <SchemaLink lower objectName="ListenbrainzEndpointSourceConfig"/> - </TabItem> - <TabItem value="aio" label="AIO"> - <details> - Change `configureAs` to `source` - - <summary>Example</summary> - - <AIOExample data={ListenbrainzEndpointConfig} name="endpointlz"/> - - </details> - - or <SchemaLink lower objectName="ListenbrainzEndpointSourceConfig"/> - </TabItem> -</Tabs> - -### [Maloja (Source)](https://github.com/krateng/maloja) - -This Source monitors a Maloja server's scrobble history and then re-scrobbles discovered tracks to configured [Clients.](#client-configurations) - -:::tip[Other Uses] - -To _scrobble to_ a Maloja server, create a [Maloja (Client)](#maloja) - -::: - -See the [Maloja (Client)](#maloja) configuration for general setup. The only difference for **Source** configuration: - -* Cannot be setup with ENV config -* [File/AIO config](./?configType=file#configuration-types) must include `"configureAs": "source"` - -#### Configuration - -<Tabs groupId="configType" queryString> - <TabItem value="env" label="ENV"> - :::note - You cannot use ENV variables shown in the [Maloja Client config](#maloja) -- multi-scrobbler assumes Maloja ENVs are always used for the **client** configuration. You must use the [File or AIO](./?configType=file#configuration-types) config to setup Maloja as a Source. - ::: - </TabItem> - <TabItem value="file" label="File"> - <details> - Change `configureAs` to `source` - - <summary>Example</summary> - - <FileExample title="CONFIG_DIR/maloja.json" data={MalojaConfig}/> - - </details> - - or <SchemaLink lower objectName="MalojaSourceConfig"/> - </TabItem> - <TabItem value="aio" label="AIO"> - <details> - Change `configureAs` to `source` - - <summary>Example</summary> - - <AIOExample data={MalojaConfig} name="maloja"/> - - </details> - - or <SchemaLink lower objectName="MalojaSourceConfig"/> - </TabItem> -</Tabs> - -### [Mopidy](https://mopidy.com/) - -Mopidy is a headless music server that supports playing music from many [standard and non-standard sources such as Pandora, Bandcamp, and Tunein.](https://mopidy.com/ext/) - -multi-scrobbler can scrobble tracks played from any Mopidy backend source, regardless of where you listen to them. - -:::note - -You must have [Mopidy-HTTP extension](https://mopidy.com/ext/http) enabled for this integration to work. - -::: - -#### Configuration {#mopidy-config} - -<Tabs groupId="configType" queryString> - <TabItem value="env" label="ENV"> - No ENV support - </TabItem> - <TabItem value="file" label="File"> - <details> - - <summary>Example</summary> - - <CodeBlock title="CONFIG_DIR/mopidy.json" language="json5">{MopidyConfig}</CodeBlock> - - </details> - - or <SchemaLink lower objectName="MopidySourceConfig"/> - </TabItem> - <TabItem value="aio" label="AIO"> - <details> - - <summary>Example</summary> - - <AIOExample data={MopidyConfig} name="mopidy"/> - - </details> - - or <SchemaLink lower objectName="MopidySourceConfig"/> - </TabItem> -</Tabs> - -#### Configuration Options - -###### URL - -The URL used to connect to the Mopidy server, set in your [file config as `url`.](#mopidy-config) The URL used to connect ultimately must be formed like this: - -``` -Syntax => [protocol]://[hostname]:[port]/[path] -Default => ws://localhost:6680/mopidy/ws -``` - -If any part of this URL is missing multi-scrobbler will use a default value. This also means that if any part of your URL is **not** standard you must explicitly define it. - -:::tip[Mopidy Configuration] - -Make sure the hostname and port number match what is found in the Mopidy configuration file `mopidy.conf`: - -```ini -[http] -hostname = localhost -port = 6680 -``` - -::: - -<details> -<summary>URL Transform Examples</summary> - - Examples of a given `url` in your file config and the final URL multi-scrobbler will use to connect to Mopidy: - - | `url` | Transformed URL | - |--------------------------------|--------------------------------------------| - | (none set) | `ws://localhost:6680/mopidy/ws` | - | `mopidy.mydomain.com` | `ws://mopidy.mydomain.com:6680/mopidy/ws/` | - | `192.168.0.101:3456` | `ws://192.168.0.101:3456/mopidy/ws/` | - | `mopidy.mydomain.com:80/MOPWS` | `ws://mopidy.mydomain.com:80/MOPWS` | - -</details> - - -##### URI Blacklist/Whitelist - -If you wish to disallow or only allow scrobbling from some sources played through Mopidy you can specify these using `uriBlacklist` or `uriWhitelist` in your config. multi-scrobbler will check the list to see if any string matches the START of the `uri` on a track. If whitelist is used then blacklist is ignored. All strings are case-insensitive. - -<details> - <summary>Example</summary> - ```json - { - "uriBlacklist": ["soundcloud"] - } - ``` - - Will prevent multi-scrobbler from scrobbling any Mopidy track that start with a `uri` like `soundcloud:song:MySong-1234` -</details> - - -##### Album Blacklist - -For certain sources (Soundcloud) Mopidy does not have all track info (Album) and will instead use "Soundcloud" as the Album name. You can prevent multi-scrobbler from using this bad Album data by adding the fake Album name to this list. Multi-scrobbler will still scrobble the track, just without the bad data. All strings are case-insensitive. - -<details> - <summary>Example</summary> - ```json - { - "albumBlacklist": ["SoundCloud", "Mixcloud"] - } - ``` - - If a track would be scrobbled like - ``` - Album: Soundcloud, Track: My Cool Track, Artist: A Cool Artist - ``` - then multi-scrobbler will instead scrobble - ``` - Track: My Cool Track, Artist: A Cool Artist - ``` -</details> - -### [MPD (Music Player Daemon)](https://www.musicpd.org/) - -MS communicates with MPD using the [TCP client connection.](https://mpd.readthedocs.io/en/stable/user.html#client-connections) - -You should uncomment/create the following settings in your mpd config: - -``` -bind_to_address "any" # or a specific ipv4/v6 address -port "6600" -``` - -<!-- MS will also discover MPD servers if [zeroconf support](https://mpd.readthedocs.io/en/stable/user.html#zeroconf) is enabled and MS is set up to utilize mDNS. This can be disabled in file-config options. --> - -#### Configuration - -<Tabs groupId="configType" queryString> - <TabItem value="env" label="ENV"> - | Environmental Variable | Required? | Default | Description | - |------------------------|-----------|------------------|-------------| - | `MPD_URL` | No | `localhost:6600` | | - | `MPD_PASSWORD` | No | | | - </TabItem> - <TabItem value="file" label="File"> - <details> - - <summary>Example</summary> - - <CodeBlock title="CONFIG_DIR/mpd.json" language="json5">{MPDConfig}</CodeBlock> - - </details> - - or <SchemaLink lower objectName="MPDSourceConfig"/> - </TabItem> - <TabItem value="aio" label="AIO"> - <details> - - <summary>Example</summary> - - <AIOExample data={MPDConfig} name="mpd"/> - - </details> - - or <SchemaLink lower objectName="MPDSourceConfig"/> - </TabItem> -</Tabs> - -### [MPRIS](https://specifications.freedesktop.org/mpris-spec/latest/) - -MPRIS is a standard interface for communicating with Music Players on **linux operating systems.** - -If you run Linux and have a notification tray that shows what media you are listening to, you likely have access to MPRIS. - -![Notification Tray](mpris.jpg) - -multi-scrobbler can listen to this interface and scrobble tracks played by **any media player** that communicates to the operating system with MPRIS. - -##### Host Setup - -MPRIS communication requires multi-scrobbler to have access to the host's dbus-daemon. - -<Tabs groupId="dbus" queryString> - <TabItem value="local" label="Local"> - If multi-scrobbler is running as a [**Local Installation**](../installation/installation.mdx#nodejs), directly with node/npm, on the same host as MPRIS then no setup is required. - - Remote setup is also possible, see the **Remote Unix Socket** section in the Docker tab. - </TabItem> - <TabItem value="docker" label="Docker"> - If multi-scrobbler is running as a [**Docker Container**](../installation/installation.mdx#docker) some modifications are required... - - ##### Local Unix Socket - - The most secure way is to **run a multi-scrobbler container on the same host as MPRIS.** In this scenario you can simply mount the **user session** dbus unix socket into the container. Make sure [`PUID` and `PGID` ENVs are configured](../installation/#linux-host) so the container has permissions to access the socket. Then, modify your compose stack with these additional values: - - ```yaml title="docker-compose.yaml" - - environment: - - DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/UID/bus - - volumes: - - /run/user/UID/bus:/run/user/UID/bus:ro - ``` - - ##### Remote Unix Socket - - :::warning - - This method is **insecure.** You should not expose dbus over a network unless access to the port is restricted and the network is trusted. - - ::: - - Use [`socat`](https://linux.die.net/man/1/socat) to bidirectionally relay communication from the host's dbus socket to a listening TCP port. - - To make dbus for user with uid `1000` available on port `6644`: - - ```shell - socat TCP-LISTEN:6644,reuseaddr,fork UNIX-CONNECT:/run/user/1000/bus - ``` - - Then, add the following value to multi-scrobbler's ENVs: - - ``` - DBUS_SESSION_BUS_ADDRESS=tcp:host=yourHostIP,port=6644 - ``` - - </TabItem> -</Tabs> - -#### Configuration - -<Tabs groupId="configType" queryString> - <TabItem value="env" label="ENV"> - | Environmental Variable | Required? | Default | Description | - |------------------------|-----------|---------|----------------------------------------------------------------------------------| - | MPRIS_ENABLE | No | | Use MPRIS as a Source (useful when you don't need any other options) | - | MPRIS_BLACKLIST | No | | Comma-delimited list of player names not to scrobble from | - | MPRIS_WHITELIST | No | | Comma-delimited list of players names to ONLY scrobble from. Overrides blacklist | - </TabItem> - <TabItem value="file" label="File"> - <details> - - <summary>Example</summary> - - <CodeBlock title="CONFIG_DIR/mpris.json" language="json5">{MprisConfig}</CodeBlock> - - </details> - - or <SchemaLink lower objectName="MPRISSourceConfig"/> - </TabItem> - <TabItem value="aio" label="AIO"> - <details> - - <summary>Example</summary> - - <AIOExample data={MprisConfig} name="mpris"/> - - </details> - - or <SchemaLink lower objectName="MPRISSourceConfig"/> - </TabItem> -</Tabs> - -### [Musikcube](https://musikcube.com) - -In order to use Musikcube configure it to accept [websocket connections](https://github.com/clangen/musikcube/wiki/remote-api-documentation) in **server setup**: - -* Enable the **Metadata Server** -* Set a **Password** - -Both of these settings are found in _Musikcube -> (s)ettings -> server setup_ - -![Server Setup](musikcube.jpg) - -The URL used by MS has the syntax: - -``` -[ws|wss]://HOST:[PORT] -``` - -The **port** is the same as shown in the server setup screenshot from above, under **metadata server enabled**. If no port is provided to MS it will default to `7905`. - -If no URL is provided to MS it will try to use `ws://localhost:7905` - -#### Configuration - -<Tabs groupId="configType" queryString> - <TabItem value="env" label="ENV"> - | Environmental Variable | Required? | Default | Description | - |------------------------|-----------|-----------------------|--------------------------------------| - | `MC_URL` | No | `ws://localhost:7905` | Use port set for **metadata server** | - | `MC_PASSWORD` | Yes | | | - </TabItem> - <TabItem value="file" label="File"> - <details> - - <summary>Example</summary> - - <CodeBlock title="CONFIG_DIR/musikcube.json" language="json5">{MusikcubeConfig}</CodeBlock> - - </details> - - or <SchemaLink lower objectName="MusikcubeSourceConfig"/> - </TabItem> - <TabItem value="aio" label="AIO"> - <details> - - <summary>Example</summary> - - <AIOExample data={MusikcubeConfig} name="musikcube"/> - - </details> - - or <SchemaLink lower objectName="MusikcubeSourceConfig"/> - </TabItem> -</Tabs> - -### [Plex](https://plex.tv) - -<Tabs groupId="plexType" queryString> -<TabItem value="api" label="API"> - -Find your [**Plex Token**](https://support.plex.tv/articles/204059436-finding-an-authentication-token-x-plex-token/) and make note of the **URL** and **Port** used to connect to your Plex instance. - -<details> - -<summary>Allowed Users and Defaults</summary> - -**Multi-scrobbler will automatically scrobble for these users by default:** - -* The User authenticated with the Plex Token -* and the **Local User** - -The Local User (`PLEX_LOCAL_USER`) is how Plex identifies anyone directly accessing the Plex UI from a local IP (who does not need to login). - -To allow MS to scrobble for other users use `usersAllow` or `PLEX_USERS_ALLOW` (env) from the below configuration docs. However, because you are overriding the default settings you must also explicitly list the authenticated user and the Local User if you want them to also be able to scrobble. - -<details> - -<summary>Examples</summary> - -###### Defaults - -If `usersallow` and `PLEX_USERS_ALLOW` are not defined then the Plex Token authenticated User and Local User will be scrobbled for. - - -###### Only A Specific User - -* `"usersallow": ["SomeUser"]` or -* `PLEX_USERS_ALLOW: SomeUser` - -Only the Plex user `SomeUser` will be scrobbled for. The Plex Token authenticated user and the Local User will not be scrobbled for. - -###### A Specific User + Defaults - -(Assuming the plex authenticated user is `FoxxMD`) - -* `"usersallow": ["FoxxMD", "PLEX_LOCAL_USER", "SomeUser"]` or -* `PLEX_USERS_ALLOW: FoxxMD,PLEX_LOCAL_USER,SomeUser` - -The Plex user SomeUser, the Plex Token authenticated user (FoxxMD) and the Local User will be scrobbled for. - -</details> - -</details> - -<details> - -<summary>Allowed Libraries and Defaults</summary> - -By default multi-scrobbler will only scrobble media found in Plex libraries that are labelled as **Music.** - * `librariesAllow` or `PLEX_LIBRARIES_ALLOW` will override this - -</details> - -#### Configuration - -<Tabs groupId="configType" queryString> - <TabItem value="env" label="ENV"> - | Environmental Variable | Required? | Default | Description | - | ---------------------- | --------- | ------- | ---------------------------------------------------------------------- | - | `PLEX_URL` | **Yes** | | The URL of the Plex server IE `http://localhost:32400` | - | `PLEX_TOKEN` | **Yes** | | The **Plex Token** to use with the API | - | `PLEX_USERS_ALLOW` | No | | Comma-separated list of usernames (from Plex) to scrobble for | - | `PLEX_USERS_BLOCK` | No | | Comma-separated list of usernames (from Plex) to disallow scrobble for | - | `PLEX_DEVICES_ALLOW` | No | | Comma-separated list of devices to scrobble from | - | `PLEX_DEVICES_BLOCK` | No | | Comma-separated list of devices to disallow scrobbles from | - | `PLEX_LIBRARIES_ALLOW` | No | | Comma-separated list of libraries to allow scrobbles from | - | `PLEX_LIBRARIES_BLOCK` | No | | Comma-separated list of libraries to disallow scrobbles from | - </TabItem> - <TabItem value="file" label="File"> - <details> - - <summary>Example</summary> - - <CodeBlock title="CONFIG_DIR/plex.json" language="json5">{PlexConfig}</CodeBlock> - - </details> - - or <SchemaLink lower objectName="PlexApiSourceConfig"/> - - </TabItem> - <TabItem value="aio" label="AIO"> - <details> - - <summary>Example</summary> - - <AIOExample data={PlexConfig} name="plex"/> - - </details> - - or <SchemaLink lower objectName="PlexApiSourceConfig"/> - </TabItem> -</Tabs> - -</TabItem> -<TabItem value="webhook" label="Webhook (Deprecated)"> - -:::warning[Deprecated] - -Multi-scrobbler < 0.8.7 used [webhooks](https://support.plex.tv/articles/115002267687-webhooks) to support Plex scrobbling. This approach has been deprecated in favor of using Plex's API directly which has many benefits including **not requiring Plex Pass.** - -The Plex Webhook Source will be removed in an upcoming release. You should migrate to the API source as soon as possible. - -::: - -<details> - - <summary>Migrating to API</summary> - - * Follow the instructions in the API tab - * The `user` (`PLEX_USER`) setting has been renamed `usersAllow` (`PLEX_USERS_ALLOW`) - * If you were using this filter to ensure only scrobbles from yourself were registered then you no longer need this setting -- by default MS will only scrobble for the user the Plex Token is from. - * The `servers` setting is no longer available as MS only scrobbles from the server the Plex token is from. - * If you need to scrobble for multiple servers set up each server as a separate Plex API source with a separate token. - * The `libraries` setting has been renamed to `librariesAllow` - -</details> - -* In the Plex dashboard Navigate to your **Account/Settings** and find the **Webhooks** page -* Click **Add Webhook** -* URL -- `http://localhost:9078/plex` (substitute your domain if different than the default) -* **Save Changes** - -##### Configuration - -<Tabs groupId="configType" queryString> - <TabItem value="env" label="ENV"> - | Environmental Variable | Required | Default | Description | - |------------------------|----------|---------|-----------------------------------------------------------------------------------------------------------------------------------------| - | `PLEX_USER` | No | | The a comma-delimited list of usernames to scrobble tracks for. No usernames specified means all tracks by all users will be scrobbled. | - </TabItem> - <TabItem value="file" label="File"> - - <details> - - <summary>Example</summary> - - <CodeBlock title="CONFIG_DIR/plex.json" language="json5">{PlexWebhookConfig}</CodeBlock> - - </details> - - or <SchemaLink lower objectName="PlexSourceConfig"/> - </TabItem> - <TabItem value="aio" label="AIO"> - <details> - - <summary>Example</summary> - - <AIOExample data={PlexWebhookConfig} name="plex"/> - - </details> - - or <SchemaLink lower objectName="PlexSourceConfig"/> - </TabItem> -</Tabs> - -</TabItem> -</Tabs> - -### [Spotify](https://www.spotify.com) - -<details> - -<summary>Spotify and Automix</summary> - -If your Spotify player has [Automix](https://community.spotify.com/t5/FAQs/What-is-Automix/ta-p/5257278) enabled and Spotify uses it for your playlist/queue then MS cannot accurately determine when a track will end. This is because the track is "mixed" in your queue with a shorter play time than its actual length and [Spotify does not report this modified play time in its API.](https://community.spotify.com/t5/Spotify-for-Developers/Wrong-duration-ms-of-track-with-Automix/m-p/5429147) This **does not affect MS's ability to scrobble** from Spotify but it will affect the accuracy of the duration MS reports was played. - -</details> - -#### Authenticate Spotify with Multi-Scrobbler - -To access your Spotify history you must [create a Spotify App](https://developer.spotify.com/dashboard) to get a -Client ID/Secret. - -1. **Login to https://developer.spotify.com** with your existing Spotify account and accept Developer Terms - -2. Navigate to your Spotify Developer [Dashboard](https://developer.spotify.com/dashboard) and start the **Create App** process - -3. Determine the correct **Redirect URI** to use and set it in your App settings - -<details> - -<summary>Redirect URI Instructions</summary> - -A Redirect URI is the URL that Spotify will navigate your **browser** to after you complete authorization. The URL will contain the code necessary for multi-scrobbler to get a Spotify access token. - -Spotify [**no longer allows insecure URIs**](https://developer.spotify.com/documentation/web-api/tutorials/migration-insecure-redirect-uri) (start with `http://`) unless the address is `127.0.0.1` -- so `localhost` and internal IPs (`192.168.0.xxx`) no longer work. - -Use **one** of the following methods to specify a valid Redirect URI and complete Spotify authentication with multi-scrobbler: - -<Tabs groupId="spotifyRedirect" queryString> - <TabItem value="https" label="HTTPS"> - **Use a domain you control, with SSL certificates/HTTPS enabled, to create a valid redirect URI.** - - For example, if you are already running multi-scrobbler behind a reverse proxy (nginx/traefik/caddy) at `https://scrobbler.mydomain.com` then set the Spotify Redirect URI for your Spotify App, and multi-scrobbler `SPOTIFY_REDIRECT_URI` ENV, to `https://scrobbler.mydomain.com/callback` - - Alternatively, if multi-scrobbler is not accessible behind your domain, then use the [**Echo**](./?spotifyRedirect=echo#spotify) method with your own domain instead of 127.0.0.1. - </TabItem> - <TabItem value="local" label="Run MS Locally"> - **Run multi-scrobbler, configured for a Spotify Source, from the same machine as your browser in order to get credentials.** - - In this scenario you can use `http://127.0.0.1:9078/callback` as the Spotify Redirect URI and multi-scrobbler `SPOTIFY_REDIRECT_URI` ENV. - - After completing authentication, copy the contents of local multi-scrobbler's [configuration folder](../installation/installation.mdx#storage) to the final location of where multi-scrobbler will be run. - </TabItem> - <TabItem value="echo" label="Echo"> - **Use a docker container on the same machine your browser is on to "intercept" the callback URL from Spotify authentication, then manually finish the process by changing the domain to your multi-scrobbler instance.** - - - 1. Create a container on the same machine as your browser using the [`mendhak/http-https-echo:36`](https://github.com/mendhak/docker-http-https-echo) image. This container runs a simple HTTP server that echos back any request it recieves. - - ```shell - docker run --rm -p 127.0.0.1:5079:8080 mendhak/http-https-echo:36 - ``` - - 2. Set the Spotify Redirect URI and multi-scrobbler `SPOTIFY_REDIRECT_URI` ENV as `http://127.0.0.1:5079/callback` - - 3. In [Authenticate Spotify with Multi-Scrobbler](#authenticate-spotify-with-multi-scrobbler) (outside these Redirect Instructions) go to **Step 4** and continue the instructions - - Eventually, Spotify will redirect you to a web page with a URL like `http://127.0.0.1:5079/callback?code=xxxxxx...` - - ![echo](echo.png) - - 4. In the URL, replace `127.0.0.1:5079` with the address/IP for your multi-scrobbler instance so the URL is similar to `http://192.168.0.101:9078/callback?code=xxxxxx...`. Then navigate to this URL to complete authentication. - - </TabItem> - - <TabItem value="msEcho" label="Echo from echo.multi-scrobbler.app"> - For your convenience, I run the [`mendhak/http-https-echo:36`](https://github.com/mendhak/docker-http-https-echo) container from the [**Echo**](./?spotifyRedirect=echo#spotify) approach on a domain I, the multi-scrobbler developer [FoxxMD](https://github.com/FoxxMD), control and self-host. Please be aware: - - * I **do not collect personal data** or any querystrings/data sent in requests to this site - * It's **not possible** for me to use the code sent by Spotify to this URL without also having your Client ID/Secret - * However, there is **always** risk involved with trusting a service you do not control. Evaluate the risk of using this site for yourself. - - Use the [**Echo**](./?spotifyRedirect=echo#spotify) approach, starting from **Step 2**, with **https://echo.multi-scrobbler.app/callback** as Spotify Redirect URI and multi-scrobbler `SPOTIFY_REDIRECT_URI` ENV. - </TabItem> -</Tabs> - -</details> - -4. Save your new App, then copy the Client ID/Secret from the App's Basic Information page. - -5. Add the Client ID, Secret, and Redirect URI to the respective field/ENV in the [configuration section below](#spotify-config) - -6. Start multi-scrobbler, then visit the Web Dashboard and click **(Re)authenticate** on the Spotify card to start the auth process - -#### Configuration {#spotify-config} - -<Tabs groupId="configType" queryString> -<TabItem value="env" label="ENV"> - | Environmental Variable | Required? | Default | Description | - |----------------------------|-----------|----------------------------------|----------------------------------------------------| - | `SPOTIFY_CLIENT_ID` | Yes | | | - | `SPOTIFY_CLIENT_SECRET` | Yes | | | - | `SPOTIFY_REDIRECT_URI` | No | `http://localhost:9078/callback` | URI must end in `callback` | -</TabItem> -<TabItem value="file" label="File"> - <details> - - <summary>Example</summary> - - <CodeBlock title="CONFIG_DIR/spotify.json" language="json5">{SpotifyConfig}</CodeBlock> - - </details> - - or <SchemaLink lower objectName="SpotifySourceConfig"/> -</TabItem> -<TabItem value="aio" label="AIO"> - <details> - - <summary>Example</summary> - - <AIOExample data={SpotifyConfig} name="spotify"/> - - </details> - or <SchemaLink lower objectName="SpotifySourceConfig"/> -</TabItem> -</Tabs> - -### [Subsonic](http://www.subsonic.org/) - -Can use this source for any application that implements the [Subsonic API](http://www.subsonic.org/pages/api.jsp) and supports the [`getNowPlaying`](http://www.subsonic.org/pages/api.jsp#getNowPlaying) endpoint (such as [Airsonic](https://airsonic.github.io/) and [Navidrome](https://www.navidrome.org/)) - -:::warning[Known Issues] - -* Multiple artists are reported as one value and cannot be separated -* If using [Airsonic Advanced](https://github.com/airsonic-advanced/airsonic-advanced) the password used (under **Credentials**) must be **Decodable** - -::: - -Use the optional `usersAllow` property with **File** or **AIO** configuration to restrict scrobbling to a list of defined users. - -#### Configuration - -<Tabs groupId="configType" queryString> - <TabItem value="env" label="ENV"> - | Environmental Variable | Required? | Default | Description | - |------------------------|-----------|---------|--------------------------------------| - | `SUBSONIC_USER` | Yes | | | - | `SUBSONIC_PASSWORD` | Yes | | | - | `SUBSONIC_URL` | Yes | | Base url of your subsonic-api server | - </TabItem> - <TabItem value="file" label="File"> - <details> - - <summary>Example</summary> - - <CodeBlock title="CONFIG_DIR/subsonic.json" language="json5">{SubsonicConfig}</CodeBlock> - - </details> - - or <SchemaLink lower objectName="SubSonicSourceConfig"/> - </TabItem> - <TabItem value="aio" label="AIO"> - <details> - - <summary>Example</summary> - - <AIOExample data={SubsonicConfig} name="subsonic"/> - - </details> - - or <SchemaLink lower objectName="SubSonicSourceConfig"/> - </TabItem> -</Tabs> - -### [Tautulli](https://tautulli.com) - -:::warning[Deprecated] - -Multi-scrobbler >= 0.8.8 supports a Plex Source that [directly uses the API](./?plexType=api#plex) and removes the need to use Tautulli since it does not require Plex Pass. - -The Tautuilli Source will be removed in an upcoming release. You should migrate to the Plex API source as soon as possible. - -::: - -Check the [instructions](plex.md) on how to setup a notification agent. - -#### Configuration - -<Tabs groupId="configType" queryString> - <TabItem value="env" label="ENV"> - | Environmental Variable | Required | Default | Description | - |------------------------|----------|---------|-----------------------------------------------------------------------------------------------------------------------------------------| - | `TAUTULLI_USER` | No | | The a comma-delimited list of usernames to scrobble tracks for. No usernames specified means all tracks by all users will be scrobbled. | - </TabItem> - <TabItem value="file" label="File"> - - <details> - - <summary>Example</summary> - - <CodeBlock title="CONFIG_DIR/tautulli.json" language="json5">{TautulliConfig}</CodeBlock> - - </details> - - or <SchemaLink lower objectName="TautulliSourceConfig"/> - </TabItem> - <TabItem value="aio" label="AIO"> - <details> - - <summary>Example</summary> - - <AIOExample data={TautulliConfig} name="tautulli"/> - - </details> - - or <SchemaLink lower objectName="TautulliSourceConfig"/> - </TabItem> -</Tabs> - -### [WebScrobbler](https://web-scrobbler.com/) - -After installing the extension open the preferences/settings for it: - -* Under **Accounts** - * **Add Webhook** - * API URL: `[YourMultiScrobblerInstance]/api/webscrobbler` EX `http://localhost:9078/api/webscrobbler` - * Application name: `(whatever you want)` - -Reload the extension after adding the webhook. - -:::note - -* **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 - -::: - -:::warning - -Multi-scrobbler is **not** designed to be publicly accessible from the internet. To use Webscrobbler outside your local network you should be accessing MS through a VPN or through a reverse proxy with some kind of authentication (though this is still not recommended). - -::: - -#### Configuration {#webscrobbler-config} - -<Tabs groupId="configType" queryString> - <TabItem value="env" label="ENV"> - | 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 | - </TabItem> - <TabItem value="file" label="File"> - <details> - - <summary>Example</summary> - - <CodeBlock title="CONFIG_DIR/webscrobbler.json" language="json5">{WebscrobblerConfig}</CodeBlock> - - </details> - - or <SchemaLink lower objectName="WebScrobblerSourceConfig"/> - </TabItem> - <TabItem value="aio" label="AIO"> - <details> - - <summary>Example</summary> - - <AIOExample data={WebscrobblerConfig} name="webscrobbler"/> - - </details> - - or <SchemaLink lower objectName="WebScrobblerSourceConfig"/> - </TabItem> -</Tabs> - -##### 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.](./?configType=file#webscrobbler-config) - -<details> - - <summary>Example</summary> - - ```json title="webscrobbler.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` - - :::tip - - `http://localhost:9078/api/webscrobbler` is matched with the first source that _that does not have a slug defined._ - - ::: - -</details> - -###### Connectors Black/Whitelist - -MS can be configured to only scrobble, or NOT scrobble, from some WS connectors. Use the name of the website from the [supported websites](https://web-scrobbler.com/) or from the **Connectors** tab in the extension. - -:::note - -This affects **only** MS's behavior and does not affect the general connector behavior you have configured within the WebScrobbler extension. - -::: - -### [VLC](https://www.videolan.org/vlc/) - -MS communicates with VLC using the [Web (`http`) interface module](https://wiki.videolan.org/VLC_HTTP_requests/) - -#### Enable HTTP Interface - -Open VLC: - -* **Tools** Menu -> Preferences - * In the bottom left change **Show Settings** from `Simple` to `All` - * In the updated window select `Interface -> Main interfaces` - * In the **Extra interface modules** section enable **Web** and verify that `http` is shown in the textbox - * Select `Interface -> Main interfaces -> Lua` - * In Lua HTTP -> Password -> set a password in this box - * Click **Save** at the bottom to persist your changes -* Restart VLC -* Verify the HTTP interface is accessible by navigating to `http://localhost:8080` in your browser - -If the interface is accessible, after typing in your password, VLC is now ready to be used by MS. - -##### Setting VLC Interface and Port - -These can be set by starting VLC from command line with specific options: - -* `vlc --http-host yourHostIp:yourPort` or -* `vlc --http-port yourPort` or -* modify the configuration file for VLC by editing keys with the same values as from the command line (`http-host` and `http-port`) - - -#### Configuration - -<Tabs groupId="configType" queryString> - <TabItem value="env" label="ENV"> - | Environmental Variable | Required? | Default | Description | - |------------------------|------------|------------------|-------------| - | `VLC_URL` | No | `localhost:8080` | | - | `VLC_PASSWORD` | Yes | | | - </TabItem> - <TabItem value="file" label="File"> - <details> - - <summary>Example</summary> - - <CodeBlock title="CONFIG_DIR/vlc.json" language="json5">{VLCConfig}</CodeBlock> - - </details> - - or <SchemaLink lower objectName="VLCSourceConfig"/> - </TabItem> - <TabItem value="aio" label="AIO"> - <details> - - <summary>Example</summary> - - <AIOExample data={VLCConfig} name="vlc"/> - - </details> - - or <SchemaLink lower objectName="VLCSourceConfig"/> - </TabItem> -</Tabs> - -#### Filename Parsing - -If the file being played in VLC does not have ID3 tags/metadata information multi-scrobbler can attempt to extract artist, title, and album information from the raw filename using regular expressions. Mutli-scrobbler will only do this if there is **no other information** reported by VLC about these fields. - -To extract this information use [named capture groups](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Regular_expressions/Named_capturing_group) in your expression matching the field that should be extracted: `artist` `title` and/or `album`. MS accepts multiple expressions, the first one that matches a filename will be used. - -Example usage in [a file-based config:](./?configType=file#webscrobbler-config) - -```json5 title="config/vlc.json" {10-15} -[ - { - "name": "MyVlc", - "enable": true, - "data": { - // ... - }, - "options": { - "filenamePatterns": [ - // Extracts artist and title from filenames that look like: - // My Artist - My Cool title.mp4 - "/^\\s*(?<artist>.+?) - (?<title>.+?)\\.\\w+$/i" - ], - // logs to DEBUG when MS tries to extract data from a filename - "logFilenamePatterns": true, - } - } -] -``` - -#### Vlc Information Reporting - -If you find that VLC is incorrectly reporting track information (in its interface) but multi-scrobbler is not parsing it correctly it may be due to the _name_ of the fields VLC is reporting. mutli-scrobbler uses the most common field name but does not cover all use cases. Before reporting an issue please turn on metadata logging and include output from your logs while it is turned on: - -```json5 title="config/vlc.json" {9-9} - [ - { - "name": "MyVlc", - "enable": true, - "data": { - // ... - }, - "options": { - "dumpVlcMetadata": true - } - } - ] -``` - -### [Yamaha MusicCast](https://usa.yamaha.com/products/contents/audio_visual/musiccast/index.html) - -Monitor Musiccast device/receivers for music played on Network/USB/CD inputs. - -:::tip[Scrobbling Threshold] - -Musiccast may not correctly report track length. If this is the case you should reduce the "listened to" **duration** using [Scrobble Thresholds](#scrobble-thresholds) so that multi-scrobbler scrobbles correctly. - -::: - -##### URL - -If only a domain/IP:PORT is provided to multi-scrobbler it will automatically append the default Musiccast API path. If your Musiccast device is located behind a custom URL (or reverse proxy) you should explicitly provide the base path to use. - -EX - - | `url` | Transformed URL | - | --------------------------------------- | :---------------------------------------------- | - | `http://192.168.0.101` | `http://192.168.0.101/YamahaExtendedControl/v1` | - | `http://mydomain.com/reverseProxy/cast` | `http://mydomain.com/reverseProxy/cast` | - -#### Configuration - -<Tabs groupId="configType" queryString> - <TabItem value="env" label="ENV"> - | Environmental Variable | Required? | Default | Description | - | :--------------------- | :-------- | ------- | :----------------------- | - | `MCAST_URL` | Yes | | The Musiccast device URL | - </TabItem> - <TabItem value="file" label="File"> - <details> - Change `configureAs` to `source` - - <summary>Example</summary> - - <CodeBlock title="CONFIG_DIR/musiccast.json" language="json5">{MusiccastConfig}</CodeBlock> - - </details> - - or <SchemaLink lower objectName="MusicCastSourceConfig"/> - </TabItem> - <TabItem value="aio" label="AIO"> - <details> - Change `configureAs` to `source` - - <summary>Example</summary> - - <AIOExample data={MusiccastConfig} name="musiccast"/> - - </details> - - or <SchemaLink lower objectName="MusicCastSourceConfig"/> - </TabItem> -</Tabs> - -### [Youtube Music](https://music.youtube.com) - -:::warning - -Communication with YT Music is **unofficial** and not supported or endorsed by Google. This means that **this integration may stop working at any time** if Google decides to change how YT Music works in the browser. - -::: - -:::tip[Scrobble Troubleshooting] - -Due to monitoring being unofficial, listening history from YTM can be inconsistent and can cause missed scrobbles. - -[**See the FAQ**](../FAQ.md#youtube-music-misses-or-duplicates-scrobbles) for a detailed explanation, how to see more details about MS's detection of tracks, and how to properly report an issue. - -::: - -#### Authentication {#ytm-auth} - -Only one of these methods needs to be used. - -<Tabs groupId="ytmAuth" queryString> - <TabItem value="cookie" label="Cookies"> - - Use instructions from - - * https://github.com/patrickkfkan/Volumio-YouTube.js/wiki/How-to-obtain-Cookie or - * https://ytmusicapi.readthedocs.io/en/stable/setup/browser.html#copy-authentication-headers - - to get the **Cookie** value from a browser. - - It is highly recommended to [get the cookie from an Incognito/Private Session](https://github.com/LuanRT/YouTube.js/issues/803#issuecomment-2504032666) to limit the chance the session is invalidated from normal browsing. - - Add the cookie to your `ytmusic.json` config in `data` or as an ENV: - - ```json - { - "type": "ytmusic", - "enable": true, - "name": "MyYTM", - "data": { - "cookie": "__Secure-1PSIDTS=sidts-CjEB3EgAEvCd-......" - }, - "options": { - "logAuthUpdateChanges": true, - "logDiff": true - } - } - ``` - - If MS gives you authentication errors (session invalidated) at some point in the future follow the same instructions to get new cookies. - - </TabItem> - <TabItem value="oauth" label="OAuth Client"> - :::warning - - As of Sept 25' OAuth clients no longer seem to have permission to access the YTM service, as reported by the upstream YTM library developer and [several other users.](https://github.com/FoxxMD/multi-scrobbler/issues/345#issuecomment-3258518769) - - If you have existing, working OAuth credentials keep using them but there is no gaurantee they will continue to work. - - If you are setting up the YTM Source for the first time you should first try to use [Cookies](./?ytmAuth=cookie#ytm-auth). - - ::: - - [Based on the instructions from here...](https://github.com/LuanRT/YouTube.js/issues/803#issuecomment-2479689924) - - * Login to [Google Cloud console](https://console.cloud.google.com/) (create an account, if necessary) - * [Create a new project](https://console.cloud.google.com/projectcreate) - * Go to APIs and services. - * Configure the OAuth consent screen - * Use the old experience if possible - * If new is unavoidable then do not fill out any branding and under Authorized Domains you can delete the empty one (in order to save) - * Add yourself as an authorized user - * Navigate to Credentials - * Create Credentials -> choose "OAuth client ID" - * Application Type is **Web Application** - * **Name** is whatever you want, leave Authorization Javascript origins blank - * Authorized redirect URIs - * This must be **exactly** the same as what is displayed in MS! For now leave it blank so we can generate it from MS first - * Create - * In the newly created client popup save the **Client ID** and **Client Secret**, then copy them into `ytmusic.json` or appropriate ENVs: - - ```json - { - "type": "ytmusic", - "enable": true, - "name": "MyYTM", - "data": { - "clientId": "8910....6jqupl.apps.googleusercontent.com", - "clientSecret": "GOCSPX-WGXL6BSuQ343..." - }, - "options": { - "logAuthUpdateChanges": true, - "logDiff": true - } - } - ``` - - Now, start MS and during the YTMusic startup it will log something like this: - - ``` - Will use custom OAuth Client: - Client ID: ... - Client Secret: ... - Redirect URI: http://localhost:9078/api/ytmusic/callback?name=MyYTM - ``` - - If the beginning of the Redirect URI (before `api`) is EXACTLY how you would reach the MS dashboard from your browser (EX `http://localhost:9078`) then edit your google oauth client section for `Authorized redirect URIs` and add the URL MS has displayed. - - If it is NOT EXACTLY the same you either need to set MS's [base url](https://foxxmd.github.io/multi-scrobbler/docs/configuration/#base-url) or you can provide your own (Custom) Redirect URI for MS to use by setting it in `ytmusic.json` or ENV. - - <details> - - <summary>Using a Custom Redirect URI</summary> - - The three parts of the URL that must be the same: - - * it must start with `api` (after domain or subdirectory IE `my.domain.tld/api...` or `whatever.tld/subDir/api...` - * it must end in `ytmusic/callback` - * It must include `name=[NameOfSource]` in the query string - - Remember to add your custom URL to the `Authorized redirect URIs` section in the google oauth client! - - ```json - { - "type": "ytmusic", - "enable": true, - "name": "MyYTM", - "data": { - "clientId": "8910....6jqupl.apps.googleusercontent.com", - "clientSecret": "GOCSPX-WGXL6BSuQ343...", - "redirectUri": "http://my.custom.domain/api/ytmusic/callback?name=MyYTM" - }, - "options": { - "logAuthUpdateChanges": true, - "logDiff": true - } - } - ``` - - </details> - - AFTER changing the Authorized redirect URIs on Google Cloud console you may need to wait a few minutes for it to take affect. Then restart MS. From the dashboard click `(Re)authenticate` on the YTmusic source card and follow the auth flow: - - * On the screen about "testing" make sure you hit **Continue** (not Back To Safety) - * Make sure to select ALL scopes/permissions/grants it asks you about - * `Select what [YourAppName] can access` -> Select all - - Once the flow is finished MS will get the credentials and start polling automatically. You should not need to re-authenticate again after restarting MS as it saves the credentials to the `/config` folder. - - </TabItem> - <TabItem value="ytt" label="YoutubeTV"> - - :::warning - - Using the built-in YoutubeTV authentication is unlikely to work due to [Google restricting what permissions TV clients can have](https://github.com/yt-dlp/yt-dlp/issues/11462#issuecomment-2471703090). This authentication method should not be used. - - ::: - - To authenticate start multi-scrobbler with an empty YT Music configuration. An authentication URL/code will be logged in additon to being available from the dashboard. - - ``` - ERROR: Sign in with the code 'CLV-KFA-BVKY' using the authentication link on the dashboard or https://www.google.com/device - ``` - - Visit the authentication URL and enter the code that was provided (also available on the dashboard). After completing the setup flow MS will log `Auth success` and the YT Music dashboard card will display as **Idle** after refreshing. Click the **Start** link to begin monitoring. - - </TabItem> -</Tabs> - -#### Configuration - -<Tabs groupId="configType" queryString> - <TabItem value="env" label="ENV"> - - - -| Environmental Variable | Required? | Default | Description | -|------------------------|-----------|---------|-----------------------------------------------| -| YTM_COOKIE | No | | Value for Cookie Authentication | -| YTM_CLIENT_ID | No | | Client ID for OAuth Athentication | -| YTM_CLIENT_SECRET | No | | Client Secret for OAuth Athentication | -| YTM_REDIRECT_URI | No | | A custom redirect URI for OAuth Athentication | -| YTM_LOG_DIFF | No | false | Log YTM history changes | - - - </TabItem> - <TabItem value="file" label="File"> - <details> - - <summary>Example</summary> - - <CodeBlock title="CONFIG_DIR/ytmusic.json" language="json5">{YTMusicConfig}</CodeBlock> - - </details> - - or <SchemaLink lower objectName="YTMusicSourceConfig"/> - </TabItem> - <TabItem value="aio" label="AIO"> - <details> - - <summary>Example</summary> - - <AIOExample data={YTMusicConfig} name="ytmusic"/> - - </details> - - or <SchemaLink lower objectName="YTMusicSourceConfig"/> - </TabItem> -</Tabs> - -## Client Configurations - -### [Koito](https://koito.io/) - -:::tip[Other Uses] - -To monitor and re-scrobble activity from a Koito account create a [Koito (Source)](#koito-source) - -::: - -Setup a [Koito server](https://koito.io/guides/installation/) if you have not already done this. - -* [Create (or get) an API Key](https://koito.io/guides/scrobbler/) - * From your Koito server dashboard navigate to **Settings** (bottom-left icon) => **API Keys** - * Create a new Key (or use an existing) and Copy the key using the copy icon to the right of the key - * EX `pM195xPV98CDpk0QW47FIIOR8AKATAX5DblBF-Jq0t1MbbKL` -* Take note of your Koito username (used below as `KOITO_USER`) -* Determine your Koito URL - * This is the URL you use to access your Koito dashboard OR whatever URL is accessible from your multi-scrobbler instance. - * EX dashboard at `http://192.168.0.100:4110` => `KOITO_URL` is set to `http://192.168.0.100:4110` - -:::note[Koito Base URL] - - Using the URL path `/apis/listenbrainz` [Koito docs describe](https://koito.io/guides/scrobbler/) **is not required.** The Koito URL used for multi-scrobbler should only be the **base** URL. Only include a URL path if your Koito **base** url is not at `host:port`. See the table below for examples. - -<details> - -<summary>Base URL Examples</summary> - | `KOITO_URL` | MS Detected Base URL | Example Scrobble Api URL | - | :-------------------------------------------- | :------------------------------ | ---------------------------------------------------- | - | `http://192.168.0.100:4110` | `http://192.168.0.100:4110` | `http://192.168.0.100:4110/apis/listenbrainz/1` | - | `https://koito.mydomain.com` | `https://koito.mydomain.com` | `https://koito.mydomain.com/apis/listenbrainz/1` | - | `http://192.168.0.100:4110/apis/listenbrainz` | `http://192.168.0.100:4110` | `http://192.168.0.100:4110/apis/listenbrainz/1` | - | `http://192.168.0.100:80/koito` | `http://192.168.0.100:80/koito` | `http://192.168.0.100:80/koito/apis/listenbrainz/1` | -</details> -::: - - -:::tip - -Ensure that Koito is configured to allow requests from multi-scrobbler! In Koito config set [`KOITO_ALLOWED_HOSTS`](https://koito.io/reference/configuration/#koito_allowed_hosts) to the IP the multi-scrobbler dashboard is accessible from. - -::: - -#### Configuration - -<Tabs groupId="configType" queryString> - <TabItem value="env" label="ENV"> - | Environmental Variable | Required? | Default | Description | - | :--------------------- | --------- | ------- | --------------------------------- | - | KOITO_TOKEN | Yes | | API Key from your Koito Account | - | KOITO_USER | Yes | | Your Koito username | - | KOITO_URL | No | | The base URL for the Koito server | - </TabItem> - <TabItem value="file" label="File"> - - <details> - - <summary>Example</summary> - - <FileExample title="CONFIG_DIR/koito.json" data={KoitoConfig} client/> - - </details> - - or <SchemaLink client lower objectName="KoitoClientConfig"/> - </TabItem> - <TabItem value="aio" label="AIO"> - <details> - - <summary>Example</summary> - - <AIOExample client data={KoitoConfig} name="koito" client/> - - </details> - - or <SchemaLink client lower objectName="KoitoClientConfig"/> - </TabItem> -</Tabs> - -### [Last.fm](https://www.last.fm) - -:::tip[Other Users] - -* To monitor and re-scrobble activity from your Last.fm account create a [Last.fm (Source)](#lastfm-source) -* To accept scrobbles from outside applications as if multi-scrobbler was Last.fm, create a [Last.fm (Endpoint)](#lastfm-endpoint) - -::: - -[Register for an API account here.](https://www.last.fm/api/account/create) - -The Callback URL is actually specified by multi-scrobbler but to keep things consistent you should use -``` -http://localhost:9078/lastfm/callback -``` -or replace `localhost:9078` with your own base URL. - -#### Configuration - -<Tabs groupId="configType" queryString> - <TabItem value="env" label="ENV"> - | Environmental Variable | Required? | Default | Description | - |------------------------|-----------|-----------------------------------------|-------------------------------------------------------------------------------| - | `LASTFM_API_KEY` | Yes | | Api Key from your API Account | - | `LASTFM_SECRET` | Yes | | Shared secret from your API Account | - | `LASTFM_REDIRECT_URI` | No | `http://localhost:9078/lastfm/callback` | Url to use for authentication. Must include `lastfm/callback` somewhere in it | - | `LASTFM_SESSION` | No | | Session id. Will be generated by authentication flow if not provided. | - </TabItem> - <TabItem value="file" label="File"> - - <details> - - <summary>Example</summary> - - <FileExample title="CONFIG_DIR/lastfm.json" data={LastfmConfig} client/> - - </details> - - or <SchemaLink client lower objectName="LastfmClientConfig"/> - </TabItem> - <TabItem value="aio" label="AIO"> - <details> - - <summary>Example</summary> - - <AIOExample client data={LastfmConfig} name="lastfm" client/> - - </details> - - or <SchemaLink client lower objectName="LastfmClientConfig"/> - </TabItem> -</Tabs> - -### [Listenbrainz](https://listenbrainz.org) - -:::tip[Other Users] - -* To monitor and re-scrobble activity from a Listenbrainz account create a [Listenbrainz (Source)](#listenbrainz-source) -* To accept scrobbles from outside applications as if multi-scrobbler was a Listenbrainz server, create a [Listenbrainz (Endpoint)](#listenbrainz-endpoint) - -::: - -You will need to run your own Listenbrainz server or have an account [on the official instance](https://listenbrainz.org/login/) - -On your [profile page](https://listenbrainz.org/profile/) find your **User Token** to use in the configuration. - -#### Configuration - -<Tabs groupId="configType" queryString> - <TabItem value="env" label="ENV"> - | Environmental Variable | Required? | Default | Description | - |------------------------|-----------|-------------------------------|---------------------------------| - | LZ_TOKEN | Yes | | User token from your LZ profile | - | LZ_USER | Yes | | Your LZ username | - | LZ_URL | No | https://api.listenbrainz.org/ | The base URL for the LZ server | - </TabItem> - <TabItem value="file" label="File"> - - <details> - - <summary>Example</summary> - - <FileExample title="CONFIG_DIR/listenbrainz.json" data={ListenbrainzConfig} client/> - - </details> - - or <SchemaLink client lower objectName="ListenBrainzClientConfig"/> - </TabItem> - <TabItem value="aio" label="AIO"> - <details> - - <summary>Example</summary> - - <AIOExample client data={ListenbrainzConfig} name="listenbrainz" client/> - - </details> - - or <SchemaLink client lower objectName="ListenBrainzClientConfig"/> - </TabItem> -</Tabs> - -### [Maloja](https://github.com/krateng/maloja) - -Setup a [Maloja server](https://github.com/krateng/maloja?tab=readme-ov-file#how-to-install) if you have not already done this. - -<details> - - <summary>Maloja Setup Intructions</summary> - - Using Maloja's example `docker-compose.yml`: - - ```yaml reference title="~/malojaData/docker-compose.yml" - https://github.com/krateng/maloja/blob/master/example-compose.yml - ``` - - Uncomment `environment` and add `MALOJA_FORCE_PASSWORD=CHANGE_ME` to set an admin password - - Start the container: - - ```shell title="~/malojaData" - docker compose up -d - ``` -</details> - -* Navigate to the Admin Panel (Cog in upper-right corner) -> API Keys (or at http://myMalojaServerIP/admin_apikeys) - * Create a **New Key** and then copy the generated key value - -Finally, add the Maloja server URL and API Key to the configuration type you choose to use, below. - -#### Configuration - -<Tabs groupId="configType" queryString> - <TabItem value="env" label="ENV"> - | Environmental Variable | Required? | Default | Description | - |------------------------|-----------|---------|-------------------------------| - | `MALOJA_URL` | Yes | | Base URL of your installation | - | `MALOJA_API_KEY` | Yes | | Api Key | - </TabItem> - <TabItem value="file" label="File"> - - <details> - - <summary>Example</summary> - - <CodeBlock title="CONFIG_DIR/maloja.json" language="json5">{MalojaConfig}</CodeBlock> - - </details> - - or <SchemaLink client lower objectName="MalojaClientConfig"/> - </TabItem> - <TabItem value="aio" label="AIO"> - <details> - - <summary>Example</summary> - - <AIOExample client data={MalojaConfig} name="maloja"/> - - </details> - - or <SchemaLink client lower objectName="MalojaClientConfig"/> - </TabItem> -</Tabs> - -### [Rocksky](https://docs.rocksky.app/) - -Rocksky is music tracking and discovery platform built on the AT Protocol (Bluesky). It is compatible with the Listenbrainz API and so can be as a multi-scrobbler [Listenbrainz Client](#listenbrainz) using a custom URL. - -You will need a [Bluesky](https://bsky.app/) account to use Rockysky. - -:::note - -Usage is adapted from [the Listenbrainz to Rocksky docs](https://docs.rocksky.app/migrating-from-listenbrainz-to-rocksky-1040189m0) on [docs.rockysky.app](https://docs.rocksky.app) - -::: - -While logged in to Bluesky, navigate to [RockySky API Application](https://rocksky.app/apikeys) and obtain an API Key. - -Then, follow the directions to create a normal multi-scrobbler [Listenbrainz Client](#listenbrainz) and use: - -* **Listenbrainz Token** (`LZ_TOKEN`) - The API Key from RockySky -* **Listenbrainz URL** (`LZ_URL`) - `https://audioscrobbler.rocksky.app` - -## Now Playing - -Multi-scrobbler can report the currently **playing** tracks it is monitoring to some Scrobble Clients via their individual **Now Playing** functionality. - -Now Playing is **default enabled** for all Clients that support it. - -The behavior multi-scrobbler uses for determining Now Playing reporting: - -* Now Playing can be explicitly enabled or disabled globally using ENV `NOW_PLAYING=true` or `NOW_PLAYING=false` - * This only affects Clients that don't have behavior set via File/AIO (below) -* Now Playing will be only be reported to the same Clients a Source is configured to scrobble to -* A Source's Player status must be **Playing**, IE **active**, for reporting to occur - * This status can be verified via Player UI on the Dashboard -* If multiple Sources having active Players then the scrobble Client will default to reporting the track based on Source **configuration** name, alphabetically - * This is the `name` property set in [File or AIO source configs](./?configType=file#configuration-types) - -Clients can customize the Now Playing behavior individually using [File or AIO source configs:](./?configType=file#configuration-types) - -```json5 title="CONFIG_DIR/lastfm.json" -[ - { - "name": "myLastFmClient", - "configureAs": "client", - "data": { - // ... - }, - "options": { - // disable or enable - //"nowPlaying": true - // - // OR define a list of Source *config* names that should be allowed to report Now Playing - // order of the list determines priority for reporting Now Playing - //"nowPlaying": ["mySpotify1","myJellyfin2"] - } - }, -] -``` - -### Supported Scrobble Clients - -* [Last.fm](#lastfm) -* [Listenbrainz](#listenbrainz) - ## Monitoring -multi-scrobbler supports some common webhooks and a healthcheck endpoint in order to monitor Sources and Clients for errors. +Multi-scrobbler supports some common webhooks and a healthcheck endpoint in order to monitor Sources and Clients for errors. ### Webhook Configurations diff --git a/docsite/docs/configuration/plex.md b/docsite/docs/configuration/plex.md deleted file mode 100644 index 66c9e249..00000000 --- a/docsite/docs/configuration/plex.md +++ /dev/null @@ -1,81 +0,0 @@ ---- -sidebar_position: 2 -title: Plex/Tautulli (Deprecated) ---- - -:::warning[Deprecated] - -Multi-scrobbler >= 0.8.8 supports a Plex Source that [directly uses the API](configuration/configuration.mdx?plexType=api#plex) which removes the need to use Tautulli since it does not require Plex Pass. - -The Tautulli source **and** Plex Webhook source are deprecated and will be removed in an upcoming release. Existing users should migrate to the API source as soon as possible. - -::: - -Tracks played on [Plex](https://plex.tv/) can be scrobbled either by: - * A [Tautulli](https://tautulli.com/) notification agent with a webhook. - * Using Plex [Webhooks](https://support.plex.tv/articles/115002267687-webhooks) (restricted to Plex Pass users) - -# Using Tautulli - -## Create a new Notification Agent - -* Navigate to the **Notification Agents** page in **Settings** -* Click **Add a new notification agent** -* Select **Webhook** - -## Configure the Agent - -The below sections correspond with the tabs available in the notification agent configuration popup. - -### Configuration -* Webhook URL -- `http://localhost:9078/tautulli` (substitute your domain if different than the default) -* Webhook Method -- POST - -### Triggers - -Select **Watched** - -### Conditions - -Refer to [Tautulli's documentation](https://github.com/Tautulli/Tautulli-Wiki/wiki/Custom-Notification-Conditions) if you need help here. It may be a good idea to restrict notifications to only one library (if you have a Music library, for instance) - -**This app will only scrobble an item if `media_type` is a "track", which is the default for all music.** - -### Data - -Expand the **Watched** dropdown and add the following code block to the **JSON Data** text field: - -``` -{ -"artist_name": "{artist_name}", -"track_name": "{track_name}", -"track_artist": "{track_artist}", -"album_name": "{album_name}", -"media_type": "{media_type}", -"title": "{title}", -"duration": "{duration_sec}", -"username": "{username}", -"server": "{server_name}", -"version": "{server_version}", -"library": "{library_name}", -"player": "{player}", -"device": "{device}", -"platform": "{platform}", -"action": "{action}", -"machine_id": "{machine_id}", -"session_key": "{session_key}" -} -``` - -**Click the Save button to finish.** - -Your agent is now configured and ready to scrobble. - -# Using Plex Webhooks - -* Navigate to your **Account/Settings** and find the **Webhooks** page -* Click **Add Webhook** -* URL -- `http://localhost:9078/plex` (substitute your domain if different than the default) -* **Save Changes** - -Plex is now configured to scrobble. diff --git a/docsite/docs/configuration/sources/_category_.json b/docsite/docs/configuration/sources/_category_.json index 36e115eb..abd4eaad 100644 --- a/docsite/docs/configuration/sources/_category_.json +++ b/docsite/docs/configuration/sources/_category_.json @@ -2,6 +2,6 @@ "label": "Scrobble Sources", "link": { "type": "doc", - "id": "configuration/sources/overview" + "id": "configuration/sources/sources" } } diff --git a/docsite/docs/configuration/sources/azuracast.mdx b/docsite/docs/configuration/sources/azuracast.mdx index bf3558f8..088d86f4 100644 --- a/docsite/docs/configuration/sources/azuracast.mdx +++ b/docsite/docs/configuration/sources/azuracast.mdx @@ -12,7 +12,7 @@ import ShouldScrobble from "@site/src/components/snippets/_should-scrobble.mdx" :::tip[Scrobbling Threshold] -Azuracast may not correctly report track length or position. If this is the case you should reduce the "listened to" **duration** using [Scrobble Thresholds](#scrobble-thresholds) so that multi-scrobbler scrobbles correctly. +Azuracast may not correctly report track length or position. If this is the case you should reduce the "listened to" **duration** using [Scrobble Thresholds](/configuration/sources#scrobble-thresholds) so that multi-scrobbler scrobbles correctly. ::: diff --git a/docsite/docs/configuration/sources/deezer.mdx b/docsite/docs/configuration/sources/deezer.mdx index ddc09b83..4e9a7ea0 100644 --- a/docsite/docs/configuration/sources/deezer.mdx +++ b/docsite/docs/configuration/sources/deezer.mdx @@ -102,8 +102,8 @@ import DeezerDeprecatedConfig from '!!raw-loader!@site/../config/deezer-deprecat As a workaround consider: - * Using the alternative [Deezer Source that uses unofficial, internal API via ARL](./?deezerSource=arl#deezer) - * Connect Deezer with Last.fm and then use [Last.fm as a Source](#lastfm-source) + * Using the alternative [Deezer Source that uses unofficial, internal API via ARL](./?deezerSource=arl) + * Connect Deezer with Last.fm and then use [Last.fm as a Source](/configuration/sources/lastfm-source) ::: diff --git a/docsite/docs/configuration/sources/google-cast.mdx b/docsite/docs/configuration/sources/google-cast.mdx index 2d8aa9dc..f9c12025 100644 --- a/docsite/docs/configuration/sources/google-cast.mdx +++ b/docsite/docs/configuration/sources/google-cast.mdx @@ -20,7 +20,7 @@ Google Cast support is **experimental**. You may experience crashes and errors w :::note -This source relies on common, **basic** music data provided by the cast device which will always be less exhaustive than data parsed from full source integrations. If there is an existing [Source](#source-configurations) it is recommended to configure for it and blacklist the app on Google Cast, rather than relying solely on Google Cast for scrobbling. +This source relies on common, **basic** music data provided by the cast device which will always be less exhaustive than data parsed from full source integrations. If there is an existing [Source](/configuration/sources) it is recommended to configure for it and blacklist the app on Google Cast, rather than relying solely on Google Cast for scrobbling. ::: @@ -119,7 +119,7 @@ MS can be forced to track media from an application regardless of media type. Th Please include any/all logs with raw output if there are any errors encountered as this is critical to diagnosing issues. -To diagnose bad/incomplete track information or strange MS player behavior please turn on **payload logging** and include log output of the source running to help diagnose this issue. Either enable in config using the below example OR enable [Debug Mode.](#debug-mode) +To diagnose bad/incomplete track information or strange MS player behavior please turn on **payload logging** and include log output of the source running to help diagnose this issue. Either enable in config using the below example OR enable [Debug Mode.](/configuration#debug-mode) <details> diff --git a/docsite/docs/configuration/sources/icecast.mdx b/docsite/docs/configuration/sources/icecast.mdx index 6bbf1e94..0ec367d4 100644 --- a/docsite/docs/configuration/sources/icecast.mdx +++ b/docsite/docs/configuration/sources/icecast.mdx @@ -24,13 +24,13 @@ Multi-scrobbler will do its best to parse Artist/Album/Track information from th Multi-scrobbler cannot automatically determine if you are listening to a Station. Therefore, **automatic scrobbling for Icecast Sources is off by default.** -Use [Should Scrobble Behavior](/configuration/sources/overview#should-scrobble-behavior) to initiate scrobbling or configure automatic scrobble. +Use [Should Scrobble Behavior](/configuration/sources#should-scrobble-behavior) to initiate scrobbling or configure automatic scrobble. ::: :::tip[Scrobbling Threshold] -Icecast does not report track lengths so multi-scrobbler cannot determine if it should scrobble based on percent played. You should instead set a shorter "listened to" **duration** using [Scrobble Thresholds.](/configuration/sources/overview#scrobble-thresholds) +Icecast does not report track lengths so multi-scrobbler cannot determine if it should scrobble based on percent played. You should instead set a shorter "listened to" **duration** using [Scrobble Thresholds.](/configuration/sources#scrobble-thresholds) ::: diff --git a/docsite/docs/configuration/sources/jriver.mdx b/docsite/docs/configuration/sources/jriver.mdx index 1155ff12..af48fda8 100644 --- a/docsite/docs/configuration/sources/jriver.mdx +++ b/docsite/docs/configuration/sources/jriver.mdx @@ -17,7 +17,7 @@ In order for multi-scrobbler to communicate with JRiver you must have [Web Serve ### URL -The URL used to connect to JRiver, set in your [file config as `url`.](#jriver-config) The URL used to connect ultimately must be formed like this: +The URL used to connect to JRiver, set in your [file config as `url`.](#configuration) The URL used to connect ultimately must be formed like this: ``` Syntax => [protocol]://[hostname]:[port]/[path] @@ -28,7 +28,7 @@ If any part of this URL is missing multi-scrobbler will use a default value. Thi :::tip[Jriver Settings] -* Make sure the port number matches what is found in `Advanced` section in the [Media Network](#jriver) options. +* Make sure the port number matches what is found in `Advanced` section in the Media Network options. * If your installation is on the same machine but you cannot connect using `localhost` try `0.0.0.0` instead. ::: diff --git a/docsite/docs/configuration/sources/kodi.mdx b/docsite/docs/configuration/sources/kodi.mdx index 7a5bf984..680caec7 100644 --- a/docsite/docs/configuration/sources/kodi.mdx +++ b/docsite/docs/configuration/sources/kodi.mdx @@ -18,7 +18,7 @@ In order for multi-scrobbler to communicate with Kodi you must have the [Web Int ### URL -The URL used to connect to Kodi, set in your [file config as `url`.](#kodi-config) The URL used to connect ultimately must be formed like this: +The URL used to connect to Kodi, set in your [file config as `url`.](#configuration) The URL used to connect ultimately must be formed like this: ``` Syntax => [protocol]://[hostname]:[port]/[path] @@ -29,7 +29,7 @@ If any part of this URL is missing multi-scrobbler will use a default value. Thi :::tip[Kodi Settings] -* Make sure the port number matches what is found in **Port** in the [Control](#kodi) section mentioned above. +* Make sure the port number matches what is found in **Port** in the Control section mentioned above. * If your installation is on the same machine but you cannot connect using `localhost` try `0.0.0.0` instead. ::: diff --git a/docsite/docs/configuration/sources/koito-source.mdx b/docsite/docs/configuration/sources/koito-source.mdx index cb90afe7..1174bf02 100644 --- a/docsite/docs/configuration/sources/koito-source.mdx +++ b/docsite/docs/configuration/sources/koito-source.mdx @@ -9,7 +9,7 @@ import TabItem from '@theme/TabItem'; import CodeBlock from '@theme/CodeBlock'; import KoitoConfig from '!!raw-loader!@site/../config/koito.json.example'; -This Source monitors a Koito account's scrobble history and then re-scrobbles discovered tracks to configured [Clients.](/configuration/clients/overview) +This Source monitors a Koito account's scrobble history and then re-scrobbles discovered tracks to configured [Clients.](/configuration/clients) :::tip[Other Uses] diff --git a/docsite/docs/configuration/sources/lastfm-source.mdx b/docsite/docs/configuration/sources/lastfm-source.mdx index d0de2e87..56c69a26 100644 --- a/docsite/docs/configuration/sources/lastfm-source.mdx +++ b/docsite/docs/configuration/sources/lastfm-source.mdx @@ -9,7 +9,7 @@ import TabItem from '@theme/TabItem'; import CodeBlock from '@theme/CodeBlock'; import LastfmConfig from '!!raw-loader!@site/../config/lastfm.json.example'; -This Source **monitors your Last.fm scrobble history** and re-scrobbles new activity to configured [Clients.](/configuration/clients/overview) +This Source **monitors your Last.fm scrobble history** and re-scrobbles new activity to configured [Clients.](/configuration/clients) :::tip[Other Uses] diff --git a/docsite/docs/configuration/sources/mopidy.mdx b/docsite/docs/configuration/sources/mopidy.mdx index ef64c833..6763ec77 100644 --- a/docsite/docs/configuration/sources/mopidy.mdx +++ b/docsite/docs/configuration/sources/mopidy.mdx @@ -21,7 +21,7 @@ You must have [Mopidy-HTTP extension](https://mopidy.com/ext/http) enabled for t #### URL -The URL used to connect to the Mopidy server, set in your [file config as `url`.](#mopidy-config) The URL used to connect ultimately must be formed like this: +The URL used to connect to the Mopidy server, set in your [file config as `url`.](#configuration) The URL used to connect ultimately must be formed like this: ``` Syntax => [protocol]://[hostname]:[port]/[path] diff --git a/docsite/docs/configuration/sources/mpris.mdx b/docsite/docs/configuration/sources/mpris.mdx index ade9c149..f04cd96a 100644 --- a/docsite/docs/configuration/sources/mpris.mdx +++ b/docsite/docs/configuration/sources/mpris.mdx @@ -32,7 +32,7 @@ MPRIS communication requires multi-scrobbler to have access to the host's dbus-d ##### Local Unix Socket - The most secure way is to **run a multi-scrobbler container on the same host as MPRIS.** In this scenario you can simply mount the **user session** dbus unix socket into the container. Make sure [`PUID` and `PGID` ENVs are configured](../installation/#linux-host) so the container has permissions to access the socket. Then, modify your compose stack with these additional values: + The most secure way is to **run a multi-scrobbler container on the same host as MPRIS.** In this scenario you can simply mount the **user session** dbus unix socket into the container. Make sure [`PUID` and `PGID` ENVs are configured](/installation/#linux-host) so the container has permissions to access the socket. Then, modify your compose stack with these additional values: ```yaml title="docker-compose.yaml" diff --git a/docsite/docs/configuration/sources/overview.mdx b/docsite/docs/configuration/sources/overview.mdx deleted file mode 100644 index 1e705007..00000000 --- a/docsite/docs/configuration/sources/overview.mdx +++ /dev/null @@ -1,141 +0,0 @@ ---- -sidebar_position: 2 -title: Overview -toc_max_heading_level: 4 ---- - -import ScrobbleThreshold from "@site/src/components/snippets/_scrobble-threshold.mdx" - -A **Source** is a data source that contains information about tracks you are playing like a music player or platform. Examples are **Spotify, Jellyfin, Plex, Youtube Music, Airsonic**, etc... - -## Sources - -| Name | Networking | Scrobble SOT | [Multi Device/User](#multi-device-user) | [Scrobble Destination](#limiting-scrobble-destination) | [Thresholds](#scrobble-thresholds) | [Should Scrobble](#should-scrobble-behavior) | -| :-------------------------------------------------------------------------- | :------------------ | :-------------------- | :-------------------------------------- | :----------------------------------------------------- | :--------------------------------- | :------------------------------------------- | -| [Azuracast](/configuration/sources/azuracast) | [Active](#active) | [Activity](#activity) | ❌ | ✅ | ✅ | ✅ | -| [Deezer](/configuration/sources/deezer) | [Active](#active) | [History](#history) | ✅ | ✅ | ❌ | ❌ | -| [Google Cast (Chromecast)](/configuration/sources/google-cast-chromecast) | [Active](#active) | [Activity](#activity) | ✅ | ✅ | ✅ | ❌ | -| [Icecast](/configuration/sources/icecast) | [Active](#active) | [Activity](#activity) | ❌ | ✅ | ✅ | ✅ | -| [Jellyfin](/configuration/sources/jellyfin) | [Active](#active) | [Activity](#activity) | ✅ | ✅ | ✅ | ❌ | -| [JRiver](/configuration/sources/jriver) | [Active](#active) | [Activity](#activity) | ❌ | ✅ | ✅ | ❌ | -| [Kodi](/configuration/sources/kodi) | [Active](#active) | [Activity](#activity) | ❌ | ✅ | ✅ | ❌ | -| [Koito](/configuration/sources/koito-source) | [Active](#active) | [History](#history) | ❌ | ✅ | ❌ | ❌ | -| [Last.fm](/configuration/sources/lastfm-source) | [Active](#active) | [History](#history) | ❌ | ✅ | ❌ | ❌ | -| [Last.fm (Endpoint)](/configuration/sources/lastfm-endpoint) | [Ingress](#ingress) | [History](#history) | ❌ | ✅ | ❌ | ❌ | -| [ListenBrainz](configuration#/sources/istenbrainz-source) | [Active](#active) | [History](#history) | ❌ | ✅ | ❌ | ❌ | -| [ListenBrainz (Endpoint)](/configuration/sources/listenbrainz-endpoint) | [Ingress](#ingress) | [History](#history) | ❌ | ✅ | ❌ | ❌ | -| [Maloja](/configuration/sources/maloja-source) | [Active](#active) | [History](#history) | ❌ | ✅ | ❌ | ❌ | -| [Mopidy](/configuration/sources/mopidy) | [Active](#active) | [Activity](#activity) | ❌ | ✅ | ✅ | ❌ | -| [MPD (Music Player Daemon)](/configuration/sources/mpd-music-player-daemon) | [Active](#active) | [Activity](#activity) | ❌ | ✅ | ✅ | ❌ | -| [Musikcube](/configuration/sources/musikcube) | [Active](#active) | [Activity](#activity) | ❌ | ✅ | ✅ | ❌ | -| [Plex](/configuration/sources/plex) | [Active](#active) | [Activity](#activity) | ✅ | ✅ | ✅ | ❌ | -| [Spotify](/configuration/sources/spotify) | [Active](#active) | [Activity](#activity) | ✅ | ✅ | ✅ | ❌ | -| [Subsonic-compatible APIs](configuration/sources/subsonic) | [Active](#active) | [Activity](#activity) | ✅ | ✅ | ✅ | ❌ | -| [WebScrobbler](docs/configuration#webscrobbler) | [Ingress](#active) | [History](#history) | ❌ | ✅ | ❌ | ❌ | -| [VLC](/configuration/sources/vlc) | [Active](#active) | [Activity](#activity) | ❌ | ✅ | ✅ | ❌ | -| [Yamaha MusicCast](/configuration/sources/yamaha-musiccast) | [Active](#active) | [Activity](#activity) | ❌ | ✅ | ✅ | ❌ | -| [Youtube Music](/configuration/sources/youtube-music) | [Active](#active) | [History](#history) | ❌ | ✅ | ❌ | ❌ | - -## Features - -### Types of Sources - -The Sources implemented in multi-scrobbler can be broken down into two categories. - -#### By Communication Method - -How does this Source get data from the service/application? - -##### Active - -The MS Source makes network requests to the service which returns a response with data MS can use to monitor listening activity or scrobbles. - -##### Ingress - -The MS Source waits for the service/application to send data to multi-scrobbler. - -These types of Sources require that multi-scrobbler is accessible to the service, generally through an open port or reverse proxy. - -#### By Data Source of Truth - -How does MS determine if/when a song has been played and is scrobbable? - -##### Listening Activity {#activity} - -These Sources expose some kind of real-time listening data for users, like: - -* player state (paused, stopped, playing) -* player position (1:50/2:40) -* current listened duration (Track A playing for 1m:20s) - -For theses Sources multi-scrobbler keeps track of the real-time state of your activity and scrobbles a track after it meets certain [scrobble thresholds](#scrobble-thresholds). - -##### Listening History/Scrobbles {#history} - -Some Sources expose their own version of "this track has been scrobbled." - -* Listenbrainz, Koito, and Last.fm are all scrobble services that have "definitive" histories of scrobbled tracks. - -For these Sources multi-scrobbler monitors the listening history data and scrobbles when it sees a new entry in the list. - -### Multi-Device/User - -Some Sources report which User is playing a track and/or on what device the track is being played. These Sources can be filtered with configuration to only monitor activity for specific devices or users. - -### Limiting Scrobble Destination - -By default a Source will scrobble to **all** configured Clients. - -To limit which Clients a Source will scrobble to use the `clients` property in a Source's [File/AIO configuration](./?configType=file#configuration-types) to specify the **names** of the Clients that it should scrobble to: - -```json5 title="jellyfin.json" -{ - "name": "MyJellyfin", - "clients": ["myMaloja","koitoA","listenbrainzFoo"] - "data": { - // ... - }, -} -``` - -The Source above will only scrobble to Clients that are **named** (`name` property in Client config) either `myMaloja` `koitoA` or `listenbrainzFoo`. - -If your Client is configured using [ENV](./?configType=env#configuration-types) then they are given an automatically generated name. Check the multi-scrobbler Dashboard to find their name in a subheader under the Client type. Example: - -![Client Name Subheader](../client-subheader.png) - -### Scrobble Thresholds - -<ScrobbleThreshold filename="spotify"/> - -These can also be configured **globally** for all Sources using ENVs: - -``` -SOURCE_SCROBBLE_DURATION=40 -SOURCE_SCROBBLE_PERCENT=20 -``` - -### Should Scrobble Behavior - -:::note - -This feature is currently available only for some Sources. Check each Source's documentation to see if it supported. - -::: - -For _most_ Sources multi-scrobbler can determine if a user is listening and scrobble automatically. For Sources which multi-scrobbler cannot do this or require additional configuration, a user can indicate to MS that it should or should not scrobble. This action can be taken on the Web Dashboard by clicking the link next to **Should Scrobble** found in each Source's status card. - -Additionally, for these Sources, the default behavior for Scrobbling can be explicitly set by adding the following value to a Source's `options` in [file or aio config](./?configType=file#configuration-types): - -```json5 title="azuracast.json" -{ - "data": { - // ... - }, - "options": { - // set to true to always scrobble automatically - // set to false to never scrobble automatically - "systemScrobble": true - } -} -``` \ No newline at end of file diff --git a/docsite/docs/configuration/sources/sources.mdx b/docsite/docs/configuration/sources/sources.mdx new file mode 100644 index 00000000..2ab9c19a --- /dev/null +++ b/docsite/docs/configuration/sources/sources.mdx @@ -0,0 +1,141 @@ +--- +sidebar_position: 2 +title: Overview +toc_max_heading_level: 4 +--- + +import ScrobbleThreshold from "@site/src/components/snippets/_scrobble-threshold.mdx" + +A **Source** is a data source that contains information about tracks you are playing like a music player or platform. Examples are **Spotify, Jellyfin, Plex, Youtube Music, Airsonic**, etc... + +## Sources + +| Name | Networking | Scrobble SOT | [Multi Device/User](#multi-deviceuser) | [Scrobble Destination](#limiting-scrobble-destination) | [Thresholds](#scrobble-thresholds) | [Should Scrobble](#should-scrobble-behavior) | +| :---------------------------------------------------------------------- | :------------------ | :-------------------- | :-------------------------------------- | :----------------------------------------------------- | :--------------------------------- | :------------------------------------------- | +| [Azuracast](/configuration/sources/azuracast) | [Active](#active) | [Activity](#activity) | ❌ | ✅ | ✅ | ✅ | +| [Deezer](/configuration/sources/deezer) | [Active](#active) | [History](#history) | ✅ | ✅ | ❌ | ❌ | +| [Google Cast (Chromecast)](/configuration/sources/google-cast) | [Active](#active) | [Activity](#activity) | ✅ | ✅ | ✅ | ❌ | +| [Icecast](/configuration/sources/icecast) | [Active](#active) | [Activity](#activity) | ❌ | ✅ | ✅ | ✅ | +| [Jellyfin](/configuration/sources/jellyfin) | [Active](#active) | [Activity](#activity) | ✅ | ✅ | ✅ | ❌ | +| [JRiver](/configuration/sources/jriver) | [Active](#active) | [Activity](#activity) | ❌ | ✅ | ✅ | ❌ | +| [Kodi](/configuration/sources/kodi) | [Active](#active) | [Activity](#activity) | ❌ | ✅ | ✅ | ❌ | +| [Koito](/configuration/sources/koito-source) | [Active](#active) | [History](#history) | ❌ | ✅ | ❌ | ❌ | +| [Last.fm](/configuration/sources/lastfm-source) | [Active](#active) | [History](#history) | ❌ | ✅ | ❌ | ❌ | +| [Last.fm (Endpoint)](/configuration/sources/lastfm-endpoint) | [Ingress](#ingress) | [History](#history) | ❌ | ✅ | ❌ | ❌ | +| [ListenBrainz](/configuration/sources/listenbrainz-source) | [Active](#active) | [History](#history) | ❌ | ✅ | ❌ | ❌ | +| [ListenBrainz (Endpoint)](/configuration/sources/listenbrainz-endpoint) | [Ingress](#ingress) | [History](#history) | ❌ | ✅ | ❌ | ❌ | +| [Maloja](/configuration/sources/maloja) | [Active](#active) | [History](#history) | ❌ | ✅ | ❌ | ❌ | +| [Mopidy](/configuration/sources/mopidy) | [Active](#active) | [Activity](#activity) | ❌ | ✅ | ✅ | ❌ | +| [MPD (Music Player Daemon)](/configuration/sources/mpd) | [Active](#active) | [Activity](#activity) | ❌ | ✅ | ✅ | ❌ | +| [Musikcube](/configuration/sources/musikcube) | [Active](#active) | [Activity](#activity) | ❌ | ✅ | ✅ | ❌ | +| [Plex](/configuration/sources/plex) | [Active](#active) | [Activity](#activity) | ✅ | ✅ | ✅ | ❌ | +| [Spotify](/configuration/sources/spotify) | [Active](#active) | [Activity](#activity) | ✅ | ✅ | ✅ | ❌ | +| [Subsonic-compatible APIs](/configuration/sources/subsonic) | [Active](#active) | [Activity](#activity) | ✅ | ✅ | ✅ | ❌ | +| [WebScrobbler](/configuration/sources/webscrobbler) | [Ingress](#active) | [History](#history) | ❌ | ✅ | ❌ | ❌ | +| [VLC](/configuration/sources/vlc) | [Active](#active) | [Activity](#activity) | ❌ | ✅ | ✅ | ❌ | +| [Yamaha MusicCast](/configuration/sources/yamaha-musiccast) | [Active](#active) | [Activity](#activity) | ❌ | ✅ | ✅ | ❌ | +| [Youtube Music](/configuration/sources/youtube-music) | [Active](#active) | [History](#history) | ❌ | ✅ | ❌ | ❌ | + +## Features + +### Types of Sources + +The Sources implemented in multi-scrobbler can be broken down into two categories. + +#### By Communication Method + +How does this Source get data from the service/application? + +##### Active + +The MS Source makes network requests to the service which returns a response with data MS can use to monitor listening activity or scrobbles. + +##### Ingress + +The MS Source waits for the service/application to send data to multi-scrobbler. + +These types of Sources require that multi-scrobbler is accessible to the service, generally through an open port or reverse proxy. + +#### By Data Source of Truth + +How does MS determine if/when a song has been played and is scrobbable? + +##### Listening Activity {#activity} + +These Sources expose some kind of real-time listening data for users, like: + +* player state (paused, stopped, playing) +* player position (1:50/2:40) +* current listened duration (Track A playing for 1m:20s) + +For theses Sources multi-scrobbler keeps track of the real-time state of your activity and scrobbles a track after it meets certain [scrobble thresholds](#scrobble-thresholds). + +##### Listening History/Scrobbles {#history} + +Some Sources expose their own version of "this track has been scrobbled." + +* Listenbrainz, Koito, and Last.fm are all scrobble services that have "definitive" histories of scrobbled tracks. + +For these Sources multi-scrobbler monitors the listening history data and scrobbles when it sees a new entry in the list. + +### Multi-Device/User + +Some Sources report which User is playing a track and/or on what device the track is being played. These Sources can be filtered with configuration to only monitor activity for specific devices or users. + +### Limiting Scrobble Destination + +By default a Source will scrobble to **all** configured Clients. + +To limit which Clients a Source will scrobble to use the `clients` property in a Source's [File/AIO configuration](/configuration?configType=file#configuration-types) to specify the **names** of the Clients that it should scrobble to: + +```json5 title="jellyfin.json" +{ + "name": "MyJellyfin", + "clients": ["myMaloja","koitoA","listenbrainzFoo"] + "data": { + // ... + }, +} +``` + +The Source above will only scrobble to Clients that are **named** (`name` property in Client config) either `myMaloja` `koitoA` or `listenbrainzFoo`. + +If your Client is configured using [ENV](/configuration?configType=env#configuration-types) then they are given an automatically generated name. Check the multi-scrobbler Dashboard to find their name in a subheader under the Client type. Example: + +![Client Name Subheader](../client-subheader.png) + +### Scrobble Thresholds + +<ScrobbleThreshold filename="spotify"/> + +These can also be configured **globally** for all Sources using ENVs: + +``` +SOURCE_SCROBBLE_DURATION=40 +SOURCE_SCROBBLE_PERCENT=20 +``` + +### Should Scrobble Behavior + +:::note + +This feature is currently available only for some Sources. Check each Source's documentation to see if it supported. + +::: + +For _most_ Sources multi-scrobbler can determine if a user is listening and scrobble automatically. For Sources which multi-scrobbler cannot do this or require additional configuration, a user can indicate to MS that it should or should not scrobble. This action can be taken on the Web Dashboard by clicking the link next to **Should Scrobble** found in each Source's status card. + +Additionally, for these Sources, the default behavior for Scrobbling can be explicitly set by adding the following value to a Source's `options` in [file or aio config](/configuration?configType=file#configuration-types): + +```json5 title="azuracast.json" +{ + "data": { + // ... + }, + "options": { + // set to true to always scrobble automatically + // set to false to never scrobble automatically + "systemScrobble": true + } +} +``` \ No newline at end of file diff --git a/docsite/docs/configuration/sources/spotify.mdx b/docsite/docs/configuration/sources/spotify.mdx index bbf705a1..565a812e 100644 --- a/docsite/docs/configuration/sources/spotify.mdx +++ b/docsite/docs/configuration/sources/spotify.mdx @@ -44,14 +44,14 @@ Use **one** of the following methods to specify a valid Redirect URI and complet For example, if you are already running multi-scrobbler behind a reverse proxy (nginx/traefik/caddy) at `https://scrobbler.mydomain.com` then set the Spotify Redirect URI for your Spotify App, and multi-scrobbler `SPOTIFY_REDIRECT_URI` ENV, to `https://scrobbler.mydomain.com/callback` - Alternatively, if multi-scrobbler is not accessible behind your domain, then use the [**Echo**](./?spotifyRedirect=echo#spotify) method with your own domain instead of 127.0.0.1. + Alternatively, if multi-scrobbler is not accessible behind your domain, then use the [**Echo**](./?spotifyRedirect=echo) method with your own domain instead of 127.0.0.1. </TabItem> <TabItem value="local" label="Run MS Locally"> **Run multi-scrobbler, configured for a Spotify Source, from the same machine as your browser in order to get credentials.** In this scenario you can use `http://127.0.0.1:9078/callback` as the Spotify Redirect URI and multi-scrobbler `SPOTIFY_REDIRECT_URI` ENV. - After completing authentication, copy the contents of local multi-scrobbler's [configuration folder](../installation/installation.mdx#storage) to the final location of where multi-scrobbler will be run. + After completing authentication, copy the contents of local multi-scrobbler's [configuration folder](/installation#storage) to the final location of where multi-scrobbler will be run. </TabItem> <TabItem value="echo" label="Echo"> **Use a docker container on the same machine your browser is on to "intercept" the callback URL from Spotify authentication, then manually finish the process by changing the domain to your multi-scrobbler instance.** @@ -76,13 +76,13 @@ Use **one** of the following methods to specify a valid Redirect URI and complet </TabItem> <TabItem value="msEcho" label="Echo from echo.multi-scrobbler.app"> - For your convenience, I run the [`mendhak/http-https-echo:36`](https://github.com/mendhak/docker-http-https-echo) container from the [**Echo**](./?spotifyRedirect=echo#spotify) approach on a domain I, the multi-scrobbler developer [FoxxMD](https://github.com/FoxxMD), control and self-host. Please be aware: + For your convenience, I run the [`mendhak/http-https-echo:36`](https://github.com/mendhak/docker-http-https-echo) container from the [**Echo**](./?spotifyRedirect=echo) approach on a domain I, the multi-scrobbler developer [FoxxMD](https://github.com/FoxxMD), control and self-host. Please be aware: * I **do not collect personal data** or any querystrings/data sent in requests to this site * It's **not possible** for me to use the code sent by Spotify to this URL without also having your Client ID/Secret * However, there is **always** risk involved with trusting a service you do not control. Evaluate the risk of using this site for yourself. - Use the [**Echo**](./?spotifyRedirect=echo#spotify) approach, starting from **Step 2**, with **https://echo.multi-scrobbler.app/callback** as Spotify Redirect URI and multi-scrobbler `SPOTIFY_REDIRECT_URI` ENV. + Use the [**Echo**](./?spotifyRedirect=echo) approach, starting from **Step 2**, with **https://echo.multi-scrobbler.app/callback** as Spotify Redirect URI and multi-scrobbler `SPOTIFY_REDIRECT_URI` ENV. </TabItem> </Tabs> @@ -90,16 +90,16 @@ Use **one** of the following methods to specify a valid Redirect URI and complet 4. Save your new App, then copy the Client ID/Secret from the App's Basic Information page. -5. Add the Client ID, Secret, and Redirect URI to the respective field/ENV in the [configuration section below](#spotify-config) +5. Add the Client ID, Secret, and Redirect URI to the respective field/ENV in the [configuration section below](#configuration) 6. Start multi-scrobbler, then visit the Web Dashboard and click **(Re)authenticate** on the Spotify card to start the auth process ## Configuration <Config config="SpotifySourceConfig" fileContent={SpotifyConfig} name="spotify"> - | Environmental Variable | Required? | Default | Description | - |----------------------------|-----------|----------------------------------|----------------------------------------------------| - | `SPOTIFY_CLIENT_ID` | Yes | | | - | `SPOTIFY_CLIENT_SECRET` | Yes | | | - | `SPOTIFY_REDIRECT_URI` | No | `http://localhost:9078/callback` | URI must end in `callback` | + | Environmental Variable | Required? | Default | Description | + | ----------------------- | --------- | -------------------------------- | -------------------------- | + | `SPOTIFY_CLIENT_ID` | Yes | | | + | `SPOTIFY_CLIENT_SECRET` | Yes | | | + | `SPOTIFY_REDIRECT_URI` | No | `http://localhost:9078/callback` | URI must end in `callback` | </Config> \ No newline at end of file diff --git a/docsite/docs/configuration/sources/vlc.mdx b/docsite/docs/configuration/sources/vlc.mdx index ad05cbb1..f5d31539 100644 --- a/docsite/docs/configuration/sources/vlc.mdx +++ b/docsite/docs/configuration/sources/vlc.mdx @@ -42,7 +42,7 @@ If the file being played in VLC does not have ID3 tags/metadata information mult To extract this information use [named capture groups](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Regular_expressions/Named_capturing_group) in your expression matching the field that should be extracted: `artist` `title` and/or `album`. MS accepts multiple expressions, the first one that matches a filename will be used. -Example usage in [a file-based config:](./?configType=file#webscrobbler-config) +Example usage in [a file-based config:](/configuration?configType=file#configuration-types) ```json5 title="config/vlc.json" {10-15} [ diff --git a/docsite/docs/configuration/sources/yamaha-musiccast.mdx b/docsite/docs/configuration/sources/yamaha-musiccast.mdx index d590456c..965165b5 100644 --- a/docsite/docs/configuration/sources/yamaha-musiccast.mdx +++ b/docsite/docs/configuration/sources/yamaha-musiccast.mdx @@ -13,7 +13,7 @@ Monitor [MusicCast](https://usa.yamaha.com/products/contents/audio_visual/musicc :::tip[Scrobbling Threshold] -Musiccast may not correctly report track length. If this is the case you should reduce the "listened to" **duration** using [Scrobble Thresholds](#scrobble-thresholds) so that multi-scrobbler scrobbles correctly. +Musiccast may not correctly report track length. If this is the case you should reduce the "listened to" **duration** using [Scrobble Thresholds](/configuration/sources#scrobble-thresholds) so that multi-scrobbler scrobbles correctly. ::: diff --git a/docsite/docs/configuration/sources/youtube-music.mdx b/docsite/docs/configuration/sources/youtube-music.mdx index a5332c10..00db1291 100644 --- a/docsite/docs/configuration/sources/youtube-music.mdx +++ b/docsite/docs/configuration/sources/youtube-music.mdx @@ -19,7 +19,7 @@ Communication with YT Music is **unofficial** and not supported or endorsed by G Due to monitoring being unofficial, listening history from YTM can be inconsistent and can cause missed scrobbles. -[**See the FAQ**](../FAQ.md#youtube-music-misses-or-duplicates-scrobbles) for a detailed explanation, how to see more details about MS's detection of tracks, and how to properly report an issue. +[**See the FAQ**](/FAQ.md#youtube-music-misses-or-duplicates-scrobbles) for a detailed explanation, how to see more details about MS's detection of tracks, and how to properly report an issue. ::: diff --git a/docsite/docs/development/_category_.json b/docsite/docs/development/_category_.json index a8346f29..b0fb50ed 100644 --- a/docsite/docs/development/_category_.json +++ b/docsite/docs/development/_category_.json @@ -1,6 +1,6 @@ { "label": "Development", - "position": 5, + "position": 6, "link": { "type": "generated-index", "description": "Developing for Multi-Scrobbler and tutorials" diff --git a/docsite/docs/index.mdx b/docsite/docs/index.mdx index c31ad527..2d4c1dc7 100644 --- a/docsite/docs/index.mdx +++ b/docsite/docs/index.mdx @@ -1,10 +1,9 @@ --- -title: Overview slug: / +title: Overview +sidebar_position: 1 --- -# Overview - ![Latest Release](https://img.shields.io/github/v/release/foxxmd/multi-scrobbler) ![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg) ![Docker Pulls](https://img.shields.io/docker/pulls/foxxmd/multi-scrobbler) @@ -13,48 +12,48 @@ slug: / A javascript app to scrobble music you listened to, to [Maloja](https://github.com/krateng/maloja), [Last.fm](https://www.last.fm), [ListenBrainz](https://listenbrainz.org), and [Koito](https://koito.io/). -* Supports scrobbling from many **Sources** - * [Azuracast](docs/configuration#azuracast) - * [Deezer](docs/configuration#deezer) - * [Google Cast (Chromecast)](docs/configuration#google-cast-chromecast) - * [Icecast](docs/configuration#icecast) - * [Jellyfin](docs/configuration#jellyfin) - * [JRiver](docs/configuration#jriver) - * [Kodi](docs/configuration#kodi) - * [Koito](docs/configuration#koito-source) - * [Last.fm](docs/configuration#lastfm-source) - * [Last.fm (Endpoint)](docs/configuration#lastfm-endpoint) - * [ListenBrainz](docs/configuration#listenbrainz-source) - * [ListenBrainz (Endpoint)](docs/configuration#listenbrainz-endpoint) - * [Maloja](docs/configuration#maloja-source) - * [Mopidy](docs/configuration#mopidy) - * [MPD (Music Player Daemon)](docs/configuration#mpd-music-player-daemon) - * [MPRIS (Linux Desktop)](docs/configuration#mpris) - * [Musikcube](docs/configuration#musikcube) - * [Plex](docs/configuration#plex) or [~~Tautulli~~](docs/configuration#tautulli) - * [Spotify](docs/configuration#spotify) - * [Subsonic-compatible APIs](docs/configuration#subsonic) (like [Airsonic](https://airsonic.github.io/) and [Navidrome](https://www.navidrome.org/)) - * [WebScrobbler](docs/configuration#webscrobbler) - * [VLC](docs/configuration#vlc) - * [Yamaha MusicCast](docs/configuration#yamaha-musiccast) - * [Youtube Music](docs/configuration#youtube-music) -* Supports scrobbling to many **Clients** - * [Koito](docs/configuration#koito) - * [Last.fm](docs/configuration#lastfm) - * [ListenBrainz](docs/configuration#listenbrainz) - * [Maloja](docs/configuration#maloja) - * [Rocksky](docs/configuration#rocksky) -* Monitor status of Sources and Clients using [webhooks (Gotify, Ntfy, Apprise)](docs/configuration#webhook-configurations) or [healthcheck endpoint](docs/configuration#health-endpoint) -* Supports [Now Playing](docs/configuration#now-playing) for scrobble Clients +* Supports scrobbling from many [**Sources**](/configuration/sources) + * [Azuracast](/configuration/sources/azuracast) + * [Deezer](/configuration/sources/deezer) + * [Google Cast (Chromecast)](/configuration/sources/google-cast) + * [Icecast](/configuration/sources/icecast) + * [Jellyfin](/configuration/sources/jellyfin) + * [JRiver](/configuration/sources/jriver) + * [Kodi](/configuration/sources/kodi) + * [Koito](/configuration/sources/koito-source) + * [Last.fm](/configuration/sources/lastfm-source) + * [Last.fm (Endpoint)](/configuration/sources/lastfm-endpoint) + * [ListenBrainz](/configuration/sources/listenbrainz-source) + * [ListenBrainz (Endpoint)](/configuration/sources/listenbrainz-endpoint) + * [Maloja](/configuration/sources/maloja) + * [Mopidy](/configuration/sources/mopidy) + * [MPD (Music Player Daemon)](/configuration/sources/mpd) + * [MPRIS (Linux Desktop)](/configuration/sources/mpris) + * [Musikcube](/configuration/sources/musikcube) + * [Plex](/configuration/sources/plex) + * [Spotify](/configuration/sources/spotify) + * [Subsonic-compatible APIs](/configuration/sources/subsonic) (like [Airsonic](https://airsonic.github.io/) and [Navidrome](https://www.navidrome.org/)) + * [WebScrobbler](/configuration/sources/webscrobbler) + * [VLC](/configuration/sources/vlc) + * [Yamaha MusicCast](/configuration/sources/yamaha-musiccast) + * [Youtube Music](/configuration/sources/youtube-music) +* Supports scrobbling to many [**Clients**](/configuration/clients) + * [Koito](/configuration/clients/koito) + * [Last.fm](/configuration/clients/lastfm) + * [ListenBrainz](/configuration/clients/listenbrainz) + * [Maloja](/configuration/clients/maloja) + * [Rocksky](/configuration/clients/rocksky) +* Monitor status of Sources and Clients using [webhooks (Gotify, Ntfy, Apprise)](/configuration#webhook-configurations) or [healthcheck endpoint](/configuration#health-endpoint) +* Supports [Now Playing](/configuration/clients#now-playing) for scrobble Clients * 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 -* Modify data before scrobbling with [regular expression or search patterns](docs/configuration/transforms) -* Install using [Docker images for x86/ARM](docs/installation#docker) or [locally with NodeJS](docs/installation#nodejs) +* Modify data before scrobbling with [regular expression or search patterns](/configuration/transforms) +* Install using [Docker images for x86/ARM](/installation#docker) or [locally with NodeJS](/installation#nodejs) -[**Quick Start Guide**](docs/quickstart) +[**Quick Start Guide**](/quickstart) <img src={require('/img/status-ui.png').default} width="800"/> @@ -67,7 +66,7 @@ A javascript app to scrobble music you listened to, to [Maloja](https://github.c **But I already scrobble my music to Last.fm/ListenBrainz, is multi-scrobbler for me?** -Yes! You can use [Last.fm as a **Source**](docs/configuration#lastfm-source) or [Listenbrainz as a **Source**](docs/configuration#listenbrainz-source) to forward scrobbles from your profile to any other Client! That way you can keep your current scrobble setup as-is but still get the benefit of capturing your data to a self-hosted location. +Yes! You can use [Last.fm as a **Source**](/configuration/sources/lastfm-source) or [Listenbrainz as a **Source**](configuration/sources/listenbrainz-source) to forward scrobbles from your profile to any other Client! That way you can keep your current scrobble setup as-is but still get the benefit of capturing your data to a self-hosted location. ## How Does multi-scrobbler (MS) Work? @@ -94,15 +93,15 @@ Client configurations consist of: ## Quick Start -[See the **Quick Start Guide**](docs/quickstart) +[See the **Quick Start Guide**](/quickstart) ## Installation -[See the **Installation** documentation](docs/installation) +[See the **Installation** documentation](/installation) ## Configuration -[See the **Configuration** documentation](docs/configuration) +[See the **Configuration** documentation](/configuration) ## Usage @@ -117,11 +116,11 @@ On first startup you may need to authorize Spotify and/or Last.fm by visiting th ## Help/FAQ -Having issues with connections or configuration? Check the [FAQ](docs/FAQ) before creating an issue! +Having issues with connections or configuration? Check the [FAQ](/FAQ) before creating an issue! ## Development -[Detailed architecture and development guides for Sources/Clients](docs/development/dev-common) +[Detailed architecture and development guides for Sources/Clients](/development/dev-common) ## License diff --git a/docsite/docs/installation/_category_.json b/docsite/docs/installation/_category_.json index 126765d8..41274db7 100644 --- a/docsite/docs/installation/_category_.json +++ b/docsite/docs/installation/_category_.json @@ -1,6 +1,6 @@ { "label": "Installation", - "position": 2, + "position": 3, "link": { "type": "generated-index", "description": "Way to install Multi-Scrobbler" diff --git a/docsite/docs/installation/installation.mdx b/docsite/docs/installation/installation.mdx index 39051c3c..8afaf796 100644 --- a/docsite/docs/installation/installation.mdx +++ b/docsite/docs/installation/installation.mdx @@ -166,16 +166,16 @@ To get the UID and GID for the current user run these commands from a terminal: :::tip -See the [**Quick Start Guide**](../quickstart.mdx) for another guided docker-compose example +See the [**Quick Start Guide**](/quickstart) for another guided docker-compose example ::: The example scenario: -* [Jellyfin **Source**](../configuration/configuration.mdx#jellyfin) -* [Maloja **Client**](../configuration/configuration.mdx#maloja) +* [Jellyfin **Source**](/configuration/sources/jellyfin) +* [Maloja **Client**](/configuration/sources/maloja) * Serving app on port `9078` -* Docker container located on a different IP (`192.168.0.100`) so use [Base URL](../configuration/configuration.mdx#base-url) +* Docker container located on a different IP (`192.168.0.100`) so use [Base URL](/configuration#base-url) * Config/data directory on host machine in a directory next to `docker-compose.yml` * Linux uid/gid is `1000:1000` diff --git a/docsite/docs/quickstart.mdx b/docsite/docs/quickstart.mdx index d619294a..5fd13e5a 100644 --- a/docsite/docs/quickstart.mdx +++ b/docsite/docs/quickstart.mdx @@ -1,6 +1,6 @@ --- title: 'Quickstart' -sidebar_position: 1 +sidebar_position: 2 --- import Tabs from '@theme/Tabs'; @@ -10,8 +10,8 @@ This guide will get you up and running with multi-scrobbler using [Docker](https * the dashboard served on port `9078` of a host machine which has an IP of `192.168.0.100` * data saved to the same directory as the `docker-compose.yml` file -* multi-scrobbler monitoring [Spotify](./configuration/configuration.mdx#spotify) and/or [Jellyfin](./configuration/configuration.mdx#jellyfin) for listening activity -* multi-scrobbler scrobbling to [Lastfm](./configuration/configuration.mdx#lastfm) and/or [Maloja](./configuration/configuration.mdx#maloja) +* multi-scrobbler monitoring [Spotify](/configuration/sources/spotify) and/or [Jellyfin](/configuration/sources/jellyfin) for listening activity +* multi-scrobbler scrobbling to [Lastfm](/configuration/clients/lastfm) and/or [Maloja](/configuration/clients/maloja) :::note @@ -46,7 +46,7 @@ services: <Tabs> <TabItem value="jellyfin" label="Jellyfin"> - Follow the Jellyfin configuration [instructions for setting up an **API Key**.](./configuration/configuration.mdx#jellyfin) + Follow the Jellyfin configuration [instructions for setting up an **API Key**.](/configuration/sources/jellyfin) Then, add these values to the `environment` section in the [`docker-compose.yml` you created.](#create-docker-compose-file) @@ -60,7 +60,7 @@ services: To access your Spotify activity you must [create a Spotify App](https://developer.spotify.com/dashboard) to get a **Client ID/Secret** and set a valid **Redirect URI**. - Refer to the [Spotify Authentication instructions in the docs.](./configuration/configuration.mdx#authenticate-spotify-with-multi-scrobbler) + Refer to the [Spotify Authentication instructions in the docs.](/configuration/sources/spotify#authenticate-spotify-with-multi-scrobbler) Assuming you used the **HTTPS** method for your **Redirect URI** from the instructions above...after the application is created add these values to the `environment` section in the [`docker-compose.yml` you created.](#create-docker-compose-file) @@ -138,7 +138,7 @@ services: * Determine your Koito URL * This is the URL you use to access your Koito dashboard OR whatever URL is accessible from your multi-scrobbler instance. * EX dashboard at `http://192.168.0.100:4110` => replace `myURL` below with `http://192.168.0.100:4110` - * [More info on the URL to use is the full docs](./configuration/configuration.mdx#koito) + * [More info on the URL to use is the full docs](/configuration/clients/koito) Add/Substitute values above into these examples values and add them to the `environment` section in the [`docker-compose.yml` you created for multi-scrobbler earlier.](#create-docker-compose-file) @@ -161,7 +161,7 @@ services: :::tip -If you are running your multi-scrobbler container on a Linux host see [these instructions for setting proper file permissions.](./installation/installation.mdx#linux-host) +If you are running your multi-scrobbler container on a Linux host see [these instructions for setting proper file permissions.](/installation#linux-host) ::: @@ -182,10 +182,10 @@ Visit `http://192.168.0.100:9078` to see the dashboard where ## Next Steps -* See more advanced docker options as well as other install methods in the [**Installation**](./installation/installation.mdx#docker) docs -* Review the [**Configuration**](./configuration/configuration.mdx) docs +* See more advanced docker options as well as other install methods in the [**Installation**](/installation#docker) docs +* Review the [**Configuration**](/configuration) docs * Learn about how to configure multi-scrobbler using files for more complicated Source/Client scenarios - * See all available Sources/Clients alongside configuration examples - * Learn how to set up [notification webhooks](./configuration/configuration.mdx#webhook-configurations) - * Check out the [kitchensink example](./configuration/kitchensink.md) -* Consult the [**FAQ**](./FAQ.md) for solutions to common problems + * See all available [**Sources**](/configuration/sources) and [**Clients**](/configuration/clients) alongside configuration examples + * Learn how to set up [notification webhooks](/configuration#webhook-configurations) + * Check out the [kitchensink example](/configuration/kitchensink) +* Consult the [**FAQ**](/FAQ) for solutions to common problems diff --git a/docsite/docs/updating/_category_.json b/docsite/docs/updating/_category_.json index a59bd3f3..dc1eb156 100644 --- a/docsite/docs/updating/_category_.json +++ b/docsite/docs/updating/_category_.json @@ -1,6 +1,6 @@ { "label": "Updating", - "position": 3, + "position": 4, "link": { "type": "generated-index", "description": "Way to update Multi-Scrobbler" diff --git a/docsite/docusaurus.config.ts b/docsite/docusaurus.config.ts index fc96050b..157f6c4c 100644 --- a/docsite/docusaurus.config.ts +++ b/docsite/docusaurus.config.ts @@ -138,15 +138,15 @@ const config: Config = { }, { label: 'Installation', - to: 'docs/installation', + to: '/installation', }, { label: 'Configuration', - to: 'docs/configuration', + to: '/configuration', }, { label: 'Development', - to: 'docs/development/dev-common', + to: '/development/dev-common', }, ], }, diff --git a/docsite/static/aio.json b/docsite/static/aio.json index 39edc4b8..2837ea82 100644 --- a/docsite/static/aio.json +++ b/docsite/static/aio.json @@ -1 +1 @@ -{"$schema":"http://json-schema.org/draft-07/schema#","type":"object","properties":{"sourceDefaults":{"$ref":"#/definitions/SourceDefaults"},"clientDefaults":{"$ref":"#/definitions/ClientDefaults"},"sources":{"type":"array","items":{"$ref":"#/definitions/SourceAIOConfig"}},"clients":{"type":"array","items":{"$ref":"#/definitions/ClientAIOConfig"}},"webhooks":{"type":"array","items":{"$ref":"#/definitions/WebhookConfig"}},"port":{"type":"number","description":"Set the port the multi-scrobbler UI will be served from","default":9078,"examples":[9078]},"baseUrl":{"type":"string","description":"Set the Base URL the application should assume the UI is served from.\n\nThis will affect how default redirect URLs are generated (spotify, lastfm, deezer) and some logging messages.\n\nIt will NOT set the actual interface/IP that the application is listening on.\n\nThis can also be set using the BASE_URL environmental variable.","default":"http://localhost","examples":["http://localhost","http://192.168.0.101","https://ms.myDomain.tld"]},"logging":{"$ref":"#/definitions/LogOptions"},"disableWeb":{"type":"boolean","description":"Disable web server from running/listening on port.\n\nThis will also make any ingress sources (Plex, Jellyfin, Tautulli, etc...) unusable"},"debugMode":{"type":"boolean","description":"Enables ALL relevant logging and debug options for all sources/clients, when none are defined.\n\nThis is a convenience shortcut for enabling all output needed to troubleshoot an issue and does not need to be on for normal operation.\n\nIt can also be enabled with the environmental variable DEBUG_MODE=true","default":false,"examples":[false]}},"definitions":{"SourceDefaults":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"SourceDefaults"},"LogLevel":{"type":"string","enum":["silent","fatal","error","warn","info","log","verbose","debug"],"description":"Names of log levels that can be invoked on the logger\n\nFrom lowest to highest:\n\n* `debug`\n* `verbose`\n* `log`\n* `info`\n* `warn`\n* `error`\n* `fatal`\n* `silent` (will never output anything)\n\nWhen used in `LogOptions` specifies the **minimum** level the output should log at.","title":"LogLevel"},"FileLogOptions":{"type":"object","properties":{"timestamp":{"type":"string","enum":["unix","iso","auto"],"description":"For rolling log files\n\nWhen\n* value passed to rolling destination is a string (`path` from LogOptions is a string) and\n* `frequency` is defined\n\nThis determines the format of the datetime inserted into the log file name:\n\n* `unix` - unix epoch timestamp in milliseconds\n* `iso` - Full [ISO8601](https://en.wikipedia.org/wiki/ISO_8601) datetime IE '2024-03-07T20:11:34Z'\n* `auto`\n * When frequency is `daily` only inserts date IE YYYY-MM-DD\n * Otherwise inserts full ISO8601 datetime","default":"auto"},"size":{"type":["number","string"],"description":"The maximum size of a given rolling log file.\n\nCan be combined with frequency. Use k, m and g to express values in KB, MB or GB.\n\nNumerical values will be considered as MB.","default":"10MB"},"frequency":{"anyOf":[{"type":"string","enum":["daily"]},{"type":"string","enum":["hourly"]},{"type":"number"}],"description":"The amount of time a given rolling log file is used. Can be combined with size.\n\nUse `daily` or `hourly` to rotate file every day (or every hour). Existing file within the current day (or hour) will be re-used.\n\nNumerical values will be considered as a number of milliseconds. Using a numerical value will always create a new file upon startup.","default":"daily"},"path":{"anyOf":[{"type":"string"},{"$comment":"() => string"}],"description":"The path and filename to use for log files.\n\nIf using rolling files the filename will be appended with `.N` (a number) BEFORE the extension based on rolling status.\n\nMay also be specified using env LOG_PATH or a function that returns a string.\n\nIf path is relative the absolute path will be derived from `logBaseDir` (in `LoggerAppExtras`) which defaults to CWD","default":"./logs/app.log"},"level":{"anyOf":[{"$ref":"#/definitions/LogLevel"},{"type":"boolean","const":false}],"description":"Specify the minimum log level to output to rotating files. If `false` no log files will be created."}},"title":"FileLogOptions"},"ScrobbleThresholds":{"type":"object","properties":{"duration":{"type":["number","null"],"description":"The number of seconds a track has been listened to before it should be considered scrobbled.\n\nSet to null to disable.","default":240,"examples":[240]},"percent":{"type":["number","null"],"description":"The percentage (as an integer) of a track that should have been seen played before it should be scrobbled. Only used if the Source provides information about how long the track is.\n\nSet to null to disable.\n\nNOTE: This should be used with care when the Source is a \"polling\" type (has an 'interval' property). If the track is short and the interval is too high MS may ignore the track if percentage is high because it had not \"seen\" the track for long enough from first discovery, even if you have been playing the track for longer.","default":50,"examples":[50]}},"title":"ScrobbleThresholds"},"PlayTransformOptions":{"type":"object","properties":{"log":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["all"]}]},"preCompare":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"},"compare":{"type":"object","properties":{"candidate":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"},"existing":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"}}},"postCompare":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"}},"title":"PlayTransformOptions"},"PlayTransformPartsConfig<SearchAndReplaceTerm>":{"anyOf":[{"$ref":"#/definitions/PlayTransformPartsArray%3CSearchAndReplaceTerm%3E"},{"$ref":"#/definitions/PlayTransformParts%3CSearchAndReplaceTerm%3E"}],"title":"PlayTransformPartsConfig<SearchAndReplaceTerm>"},"PlayTransformPartsArray<SearchAndReplaceTerm>":{"type":"array","items":{"$ref":"#/definitions/PlayTransformParts%3CSearchAndReplaceTerm%3E"},"title":"PlayTransformPartsArray<SearchAndReplaceTerm>"},"PlayTransformParts<SearchAndReplaceTerm>":{"type":"object","properties":{"when":{"$ref":"#/definitions/WhenConditionsConfig"},"title":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}},"artists":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}},"album":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}}},"title":"PlayTransformParts<SearchAndReplaceTerm>"},"WhenConditionsConfig":{"$ref":"#/definitions/WhenConditions%3Cstring%3E","title":"WhenConditionsConfig"},"WhenConditions<string>":{"type":"array","items":{"$ref":"#/definitions/WhenParts%3Cstring%3E"},"title":"WhenConditions<string>"},"WhenParts<string>":{"$ref":"#/definitions/PlayTransformPartsAtomic%3Cstring%3E","title":"WhenParts<string>"},"PlayTransformPartsAtomic<string>":{"type":"object","properties":{"title":{"type":"string"},"artists":{"type":"string"},"album":{"type":"string"}},"title":"PlayTransformPartsAtomic<string>"},"SearchAndReplaceTerm":{"anyOf":[{"type":"string"},{"$ref":"#/definitions/ConditionalSearchAndReplaceTerm"}],"title":"SearchAndReplaceTerm"},"ConditionalSearchAndReplaceTerm":{"type":"object","properties":{"when":{"$ref":"#/definitions/WhenConditionsConfig"},"search":{},"replace":{}},"required":["search","replace"],"title":"ConditionalSearchAndReplaceTerm"},"ClientDefaults":{"type":"object","properties":{"refreshEnabled":{"type":"boolean","description":"Try to get fresh scrobble history from client when tracks to be scrobbled are newer than the last scrobble found in client history","default":true,"examples":[true]},"refreshStaleAfter":{"type":"number","description":"Refresh scrobbled plays from upstream service if last refresh was at least X seconds ago\n\n**In most case this setting does NOT need to be changed.** The default value is sufficient for the majority of use-cases. Increasing this setting may increase upstream service load and slow down scrobbles.\n\nThis setting should only be changed in specific scenarios where MS is handling multiple \"relaying\" client-services (IE lfm -> lz -> lfm) and there is the potential for a client to be out of sync after more than a few seconds.","examples":[60],"default":60},"refreshMinInterval":{"type":"number","description":"Minimum time (milliseconds) required to pass before upstream scrobbles can be refreshed.\n\n**In most case this setting does NOT need to be changed.** This will always be equal to or smaller than `refreshStaleAfter`.","default":5000,"examples":[5000]},"refreshInitialCount":{"type":"number","description":"The number of tracks to retrieve on initial refresh (related to scrobbleBacklogCount). If not specified this is the maximum supported by the client in 1 API call."},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"checkExistingScrobbles":{"type":"boolean","description":"Check client for an existing scrobble at the same recorded time as the \"new\" track to be scrobbled. If an existing scrobble is found this track is not track scrobbled.","default":true,"examples":[true]},"verbose":{"type":"object","properties":{"match":{"$ref":"#/definitions/MatchLoggingOptions"}},"description":"Options used for increasing verbosity of logging in MS (used for debugging)"},"deadLetterRetries":{"type":"number","description":"Number of times MS should automatically retry scrobbles in dead letter queue","default":1,"examples":[1]},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"ClientDefaults"},"MatchLoggingOptions":{"type":"object","properties":{"onNoMatch":{"type":"boolean","description":"Log to DEBUG when a new track does NOT match an existing scrobble","default":false,"examples":[false]},"onMatch":{"type":"boolean","description":"Log to DEBUG when a new track DOES match an existing scrobble","default":false,"examples":[false]},"confidenceBreakdown":{"type":"boolean","description":"Include confidence breakdowns in track match logging, if applicable","default":false,"examples":[false]}},"description":"Scrobble matching (between new source track and existing client scrobbles) logging options. Used for debugging.","title":"MatchLoggingOptions"},"SourceAIOConfig":{"anyOf":[{"$ref":"#/definitions/SpotifySourceAIOConfig"},{"$ref":"#/definitions/PlexSourceAIOConfig"},{"$ref":"#/definitions/PlexApiSourceAIOConfig"},{"$ref":"#/definitions/TautulliSourceAIOConfig"},{"$ref":"#/definitions/DeezerSourceAIOConfig"},{"$ref":"#/definitions/ListenbrainzEndpointSourceAIOConfig"},{"$ref":"#/definitions/LastFMEndpointSourceAIOConfig"},{"$ref":"#/definitions/SubsonicSourceAIOConfig"},{"$ref":"#/definitions/JellySourceAIOConfig"},{"$ref":"#/definitions/JellyApiSourceAIOConfig"},{"$ref":"#/definitions/LastFmSouceAIOConfig"},{"$ref":"#/definitions/YTMusicSourceAIOConfig"},{"$ref":"#/definitions/MPRISSourceAIOConfig"},{"$ref":"#/definitions/MopidySourceAIOConfig"},{"$ref":"#/definitions/ListenBrainzSourceAIOConfig"},{"$ref":"#/definitions/JRiverSourceAIOConfig"},{"$ref":"#/definitions/KodiSourceAIOConfig"},{"$ref":"#/definitions/WebScrobblerSourceAIOConfig"},{"$ref":"#/definitions/ChromecastSourceAIOConfig"},{"$ref":"#/definitions/MusikcubeSourceAIOConfig"},{"$ref":"#/definitions/MusicCastSourceAIOConfig"},{"$ref":"#/definitions/MPDSourceAIOConfig"},{"$ref":"#/definitions/VLCSourceAIOConfig"},{"$ref":"#/definitions/IcecastSourceAIOConfig"},{"$ref":"#/definitions/AzuracastSourceAIOConfig"}],"title":"SourceAIOConfig"},"SpotifySourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/SpotifySourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["spotify"]}},"required":["data","type"],"title":"SpotifySourceAIOConfig"},"SpotifySourceData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)\n\nIt is unlikely you should need to change this unless you scrobble many very short tracks often\n\nReading:\n* https://developer.spotify.com/documentation/web-api/guides/rate-limits/\n* https://medium.com/mendix/limiting-your-amount-of-calls-in-mendix-most-of-the-time-rest-835dde55b10e\n * Rate limit may ~180 req/min\n* https://community.spotify.com/t5/Spotify-for-Developers/Web-API-ratelimit/m-p/5503150/highlight/true#M7930\n * Informally indicated as 20 req/sec? Probably for burstiness","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"clientId":{"type":"string","description":"spotify client id","examples":["787c921a2a2ab42320831aba0c8f2fc2"]},"clientSecret":{"type":"string","description":"spotify client secret","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"redirectUri":{"type":"string","description":"spotify redirect URI -- required only if not the default shown here. URI must end in \"callback\"","default":"http://localhost:9078/callback","examples":["http://localhost:9078/callback"]}},"required":["clientId","clientSecret"],"title":"SpotifySourceData"},"CommonSourceOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"CommonSourceOptions"},"PlexSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/PlexSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["plex"]}},"required":["data","type"],"title":"PlexSourceAIOConfig"},"PlexSourceData":{"type":"object","properties":{"user":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled","examples":[["MyUser1","MyUser2"]]},"libraries":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of libraries to scrobble tracks from\n\nIf none are provided tracks from all libraries will be scrobbled","examples":[["Audio","Music"]]},"servers":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled","examples":[["MyServerName"]]}},"title":"PlexSourceData"},"PlexApiSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/PlexApiData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/PlexApiOptions"},"type":{"type":"string","enum":["plex"]}},"required":["data","options","type"],"title":"PlexApiSourceAIOConfig"},"PlexApiData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"token":{"type":"string"},"url":{"type":"string","description":"http(s)://HOST:PORT of the Plex server to connect to"},"usersAllow":{"anyOf":[{"type":"string"},{"type":"boolean","enum":[true]},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble for specific users (case-insensitive)\n\nIf `true` MS will scrobble activity from all users"},"usersBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble for these users (case-insensitive)"},"devicesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if device or application name contains strings from this list (case-insensitive)"},"devicesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if device or application name contains strings from this list (case-insensitive)"},"librariesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if library name contains string from this list (case-insensitive)"},"librariesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if library name contains strings from this list (case-insensitive)"}},"required":["url"],"title":"PlexApiData"},"PlexApiOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"PlexApiOptions"},"TautulliSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/PlexSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["tautulli"]}},"required":["data","type"],"title":"TautulliSourceAIOConfig"},"DeezerSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/DeezerData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["deezer"]}},"required":["data","type"],"title":"DeezerSourceAIOConfig"},"DeezerData":{"type":"object","properties":{"clientId":{"type":"string","description":"deezer client id","examples":["a89cba1569901a0671d5a9875fed4be1"]},"clientSecret":{"type":"string","description":"deezer client secret","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"redirectUri":{"type":"string","description":"deezer redirect URI -- required only if not the default shown here. URI must end in \"callback\"","default":"http://localhost:9078/deezer/callback","examples":["http://localhost:9078/deezer/callback"]},"interval":{"type":"number","description":"optional, how long to wait before calling spotify for new tracks (in seconds)","default":60,"examples":[60]}},"required":["clientId","clientSecret"],"title":"DeezerData"},"ListenbrainzEndpointSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/ListenbrainzEndpointData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["endpointlz"]}},"required":["type"],"title":"ListenbrainzEndpointSourceAIOConfig"},"ListenbrainzEndpointData":{"type":"object","properties":{"slug":{"type":["string","null"],"description":"The URL ending that should be used to identify scrobbles for this source\n\nIf you are using multiple Listenbrainz endpoint sources (scrobbles for many users) you can use a slug to match Sources with individual users/origins\n\nExample:\n\n* slug: 'usera' => API URL: http://localhost:9078/api/listenbrainz/usera\n* slug: 'originb' => API URL: http://localhost:9078/api/listenbrainz/originb\n\nIf no slug is found from an extension's incoming webhook event the first Listenbrainz source without a slug will be used"},"token":{"type":["string","null"],"description":"If an LZ submission request contains this token in the Authorization Header it will be used to match the submission with this Source\n\nSee: https://listenbrainz.readthedocs.io/en/latest/users/api/index.html#add-the-user-token-to-your-requests"}},"title":"ListenbrainzEndpointData"},"LastFMEndpointSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/LastFMEndpointData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["endpointlfm"]}},"required":["type"],"title":"LastFMEndpointSourceAIOConfig"},"LastFMEndpointData":{"type":"object","properties":{"slug":{"type":["string","null"],"description":"The URL ending that should be used to identify scrobbles for this source\n\nIf you are using multiple Last.fm endpoint sources (scrobbles for many users) you can use a slug to match Sources with individual users/origins\n\nExample:\n\n* slug: 'usera' => API URL: http://localhost:9078/api/lastfm/usera\n* slug: 'originb' => API URL: http://localhost:9078/api/lastfm/originb\n\nIf no slug is found from an extension's incoming webhook event the first Last.fm source without a slug will be used"}},"title":"LastFMEndpointData"},"SubsonicSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/SubsonicData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["subsonic"]}},"required":["data","type"],"title":"SubsonicSourceAIOConfig"},"SubsonicData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the subsonic media server to query","examples":["http://airsonic.local"]},"user":{"type":"string","description":"Username to login to the server with","examples":[["MyUser"]]},"password":{"type":"string","description":"Password for the user to login to the server with","examples":["MyPassword"]},"ignoreTlsErrors":{"type":"boolean","description":"If your subsonic server is using self-signed certs you may need to disable TLS errors in order to get a connection\n\nWARNING: This should be used with caution as your traffic may not be encrypted.","default":false},"legacyAuthentication":{"type":"boolean","description":"Older Subsonic versions, and some badly implemented servers (Nextcloud), use legacy authentication which sends your password in CLEAR TEXT. This is less secure than the newer, recommended hashing authentication method but in some cases it is needed. See \"Authentication\" section here => https://www.subsonic.org/pages/api.jsp\n\nIf this option is not specified it will be turned on if the subsonic server responds with error code 41 \"Token authentication not supported for LDAP users.\" -- See Error Handling section => https://www.subsonic.org/pages/api.jsp","default":false}},"required":["url","user","password"],"title":"SubsonicData"},"JellySourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/JellyData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["jellyfin"]}},"required":["data","type"],"title":"JellySourceAIOConfig"},"JellyData":{"type":"object","properties":{"users":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled","examples":[["MyUser1","MyUser2"]]},"servers":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled","examples":[["MyServerName1"]]}},"title":"JellyData"},"JellyApiSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/JellyApiData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/JellyApiOptions"},"type":{"type":"string","enum":["jellyfin"]}},"required":["data","options","type"],"title":"JellyApiSourceAIOConfig"},"JellyApiData":{"type":"object","properties":{"url":{"type":"string","description":"HOST:PORT of the Jellyfin server to connect to"},"user":{"type":"string","description":"The username of the user to authenticate for or track scrobbles for"},"password":{"type":"string","description":"Password of the username to authenticate for\n\nRequired if `apiKey` is not provided."},"apiKey":{"type":"string","description":"API Key to authenticate with.\n\nRequired if `password` is not provided."},"usersAllow":{"anyOf":[{"type":"string"},{"type":"boolean","enum":[true]},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble for specific users (case-insensitive)\n\nIf `true` MS will scrobble activity from all users"},"usersBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble for these users (case-insensitive)"},"devicesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if device or application name contains strings from this list (case-insensitive)"},"devicesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if device or application name contains strings from this list (case-insensitive)"},"librariesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if library name contains string from this list (case-insensitive)"},"librariesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if library name contains strings from this list (case-insensitive)"},"additionalAllowedLibraryTypes":{"type":"array","items":{},"description":"Allow MS to scrobble audio media in libraries classified other than 'music'\n\n`librariesAllow` will achieve the same result as this but this is more convenient if you do not want to explicitly list every library name or are only using `librariesBlock`"},"allowUnknown":{"type":"boolean","description":"Force media with a type of \"Unknown\" to be counted as Audio","default":false}},"required":["url","user"],"title":"JellyApiData"},"JellyApiOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"JellyApiOptions"},"LastFmSouceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/LastFmSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"configureAs":{"type":"string","enum":["source"],"description":"When used in `lastfm.config` this tells multi-scrobbler whether to use this data to configure a source or client.","default":"source","examples":["source"]},"type":{"type":"string","enum":["lastfm"]}},"required":["data","type"],"title":"LastFmSouceAIOConfig"},"LastFmSourceData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"apiKey":{"type":"string","description":"API Key generated from Last.fm account","examples":["787c921a2a2ab42320831aba0c8f2fc2"]},"secret":{"type":"string","description":"Secret generated from Last.fm account","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"session":{"type":"string","description":"Optional session id returned from a completed auth flow"},"redirectUri":{"type":"string","description":"Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.","default":"http://localhost:9078/lastfm/callback","examples":["http://localhost:9078/lastfm/callback"]},"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"}},"required":["apiKey","secret"],"title":"LastFmSourceData"},"YTMusicSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/YTMusicData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"type":"object","properties":{"logAuth":{"type":"boolean","description":"When true MS will log to DEBUG all of the credentials data it receives from YTM"},"logDiff":{"type":"boolean","description":"Always log history diff\n\nBy default MS will log to `WARN` if history diff is inconsistent but does not log if diff is expected (on new tracks found)\nSet this to `true` to ALWAYS log diff on new tracks. Expected diffs will log to `DEBUG` and inconsistent diffs will continue to log to `WARN`","default":false},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}}},"type":{"type":"string","enum":["ytmusic"]}},"required":["type"],"title":"YTMusicSourceAIOConfig"},"YTMusicData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"cookie":{"type":"string","description":"The cookie retrieved from the Request Headers of music.youtube.com after logging in.\n\nSee https://ytmusicapi.readthedocs.io/en/stable/setup/browser.html#copy-authentication-headers for how to retrieve this value.","examples":["VISITOR_INFO1_LIVE=jMp2xA1Xz2_PbVc; __Secure-3PAPISID=3AxsXpy0M/AkISpjek; ..."]},"clientId":{"type":"string","description":"Google Cloud Console project OAuth Client ID\n\nGenerated from a custom OAuth Client, see docs"},"clientSecret":{"type":"string","description":"Google Cloud Console project OAuth Client Secret\n\nGenerated from a custom OAuth Client, see docs"},"redirectUri":{"type":"string","description":"Google Cloud Console project OAuth Client Authorized redirect URI\n\nGenerated from a custom OAuth Client, see docs. multi-scrobbler will generate a default based on BASE_URL.\nOnly specify this if the default does not work for you."},"innertubeOptions":{"$ref":"#/definitions/InnertubeOptions","description":"Additional options for authorization and tailoring YTM client"}},"title":"YTMusicData"},"InnertubeOptions":{"type":"object","properties":{"po_token":{"type":"string","description":"Proof of Origin token\n\nMay be required if YTM starts returning 403"},"visitor_data":{"type":"string","description":"Visitor ID value found in VISITOR_INFO1_LIVE or visitorData cookie\n\nMay be required if YTM starts returning 403"},"account_index":{"type":"number","description":"If account login results in being able to choose multiple account, use a zero-based index to choose which one to monitor","examples":[0,1]},"location":{"type":"string"},"lang":{"type":"string"},"generate_session_locally":{"type":"boolean"},"device_category":{"type":"string"},"client_type":{"type":"string"},"timezone":{"type":"string"}},"title":"InnertubeOptions"},"MPRISSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MPRISData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["mpris"]}},"required":["data","type"],"title":"MPRISSourceAIOConfig"},"MPRISData":{"type":"object","properties":{"blacklist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"DO NOT scrobble from any players that START WITH these values, case-insensitive","examples":[["spotify","vlc"]]},"whitelist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"ONLY from any players that START WITH these values, case-insensitive\n\nIf whitelist is present then blacklist is ignored","examples":[["spotify","vlc"]]}},"title":"MPRISData"},"MopidySourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MopidyData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["mopidy"]}},"required":["data","type"],"title":"MopidySourceAIOConfig"},"MopidyData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the Mopidy HTTP server to connect to\n\nYou MUST have Mopidy-HTTP extension enabled: https://mopidy.com/ext/http\n\nmulti-scrobbler connects to the WebSocket endpoint that ultimately looks like this => `ws://localhost:6680/mopidy/ws/`\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `ws://`\n* Hostname => `localhost`\n* Port => `6680`\n* Path => `/mopidy/ws/`","examples":["ws://localhost:6680/mopidy/ws/"],"default":"ws://localhost:6680/mopidy/ws/"},"uriBlacklist":{"type":"array","items":{"type":"string"},"description":"Do not scrobble tracks whose URI STARTS WITH any of these strings, case-insensitive\n\nEX: Don't scrobble tracks from soundcloud by adding 'soundcloud' to this list.\n\nList is ignored if uriWhitelist is used."},"uriWhitelist":{"type":"array","items":{"type":"string"},"description":"Only scrobble tracks whose URI STARTS WITH any of these strings, case-insensitive\n\nEX: Only scrobble tracks from soundcloud by adding 'soundcloud' to this list."},"albumBlacklist":{"type":"array","items":{"type":"string"},"description":"Remove album data that matches any case-insensitive string from this list when scrobbling,\n\nFor certain sources (Soundcloud) Mopidy does not have all track info (Album) and will instead use \"Soundcloud\" as the Album name. You can prevent multi-scrobbler from using this bad Album data by adding the fake name to this list. Multi-scrobbler will still scrobble the track, just without the bad data.","examples":[["Soundcloud","Mixcloud"]],"default":["Soundcloud"]}},"title":"MopidyData"},"ListenBrainzSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/ListenBrainzSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"configureAs":{"type":"string","enum":["source"],"description":"When used in `listenbrainz.config` this tells multi-scrobbler whether to use this data to configure a source or client.","default":"source","examples":["source"]},"type":{"type":"string","enum":["listenbrainz"]}},"required":["data","type"],"title":"ListenBrainzSourceAIOConfig"},"ListenBrainzSourceData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for the ListenBrainz server, if not using the default","examples":["https://api.listenbrainz.org/"],"default":"https://api.listenbrainz.org/"},"token":{"type":"string","description":"User token for the user to scrobble for","examples":["6794186bf-1157-4de6-80e5-uvb411f3ea2b"]},"username":{"type":"string","description":"Username of the user to scrobble for"}},"required":["token","username"],"title":"ListenBrainzSourceData"},"JRiverSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/JRiverData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["jriver"]}},"required":["data","type"],"title":"JRiverSourceAIOConfig"},"JRiverData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the JRiver HTTP server to connect to\n\nmulti-scrobbler connects to the Web Service Interface endpoint that ultimately looks like this => `http://yourDomain:52199/MCWS/v1/`\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `http://`\n* Hostname => `localhost`\n* Port => `52199`\n* Path => `/MCWS/v1/`","examples":["http://localhost:52199/MCWS/v1/"],"default":"http://localhost:52199/MCWS/v1/"},"username":{"type":"string","description":"If you have enabled authentication, the username you set"},"password":{"type":"string","description":"If you have enabled authentication, the password you set"}},"required":["url"],"title":"JRiverData"},"KodiSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/KodiData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["kodi"]}},"required":["data","type"],"title":"KodiSourceAIOConfig"},"KodiData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the Kodi HTTP server to connect to\n\nmulti-scrobbler connects to the Web Service Interface endpoint that ultimately looks like this => `http://yourDomain:8080/jsonrpc`\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `http://`\n* Hostname => `localhost`\n* Port => `8080`\n* Path => `/jsonrpc`","examples":["http://localhost:8080/jsonrpc"],"default":"http://localhost:8080/jsonrpc"},"username":{"type":"string","description":"The username set for Remote Control via Web Sever"},"password":{"type":"string","description":"The password set for Remote Control via Web Sever"}},"required":["url","username","password"],"title":"KodiData"},"WebScrobblerSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/WebScrobblerData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["webscrobbler"]}},"required":["type"],"title":"WebScrobblerSourceAIOConfig"},"WebScrobblerData":{"type":"object","properties":{"slug":{"type":["string","null"],"description":"The URL ending that should be used to identify scrobbles for this source\n\nIn WebScrobbler's Webhook you must set an 'API URL'. All MS WebScrobbler sources must start like:\n\nhttp://localhost:9078/api/webscrobbler\n\nIf you are using multiple WebScrobbler sources (scrobbles for many users) you must use a slug to match Sources with each users extension.\n\nExample:\n\n* slug: 'usera' => API URL: http://localhost:9078/api/webscrobbler/usera\n* slug: 'userb' => API URL: http://localhost:9078/api/webscrobbler/userb\n\nIf no slug is found from an extension's incoming webhook event the first WebScrobbler source without a slug will be used"},"blacklist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Block scrobbling from specific WebScrobbler Connectors","examples":[["youtube"]]},"whitelist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only allow scrobbling from specific WebScrobbler Connectors","examples":[["mixcloud","soundcloud","bandcamp"]]}},"title":"WebScrobblerData"},"ChromecastSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/ChromecastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["chromecast"]}},"required":["data","type"],"title":"ChromecastSourceAIOConfig"},"ChromecastData":{"type":"object","properties":{"blacklistDevices":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"DO NOT scrobble from any cast devices that START WITH these values, case-insensitive\n\nUseful when used with auto discovery","examples":[["home-mini","family-tv"]]},"whitelistDevices":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"ONLY scrobble from any cast device that START WITH these values, case-insensitive\n\nIf whitelist is present then blacklist is ignored\n\nUseful when used with auto discovery","examples":[["home-mini","family-tv"]]},"blacklistApps":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"DO NOT scrobble from any application that START WITH these values, case-insensitive","examples":[["spotify","pandora"]]},"whitelistApps":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"ONLY scrobble from any application that START WITH these values, case-insensitive\n\nIf whitelist is present then blacklist is ignored","examples":[["spotify","pandora"]]},"useAvahi":{"type":"boolean","description":"Try to use Avahi and avahi-browse to resolve mDNS devices instead of native mDNS querying\n\nUseful for docker (alpine) container where mDNS resolution is not yet supported. Avahi socket must be exposed to the container and avahi-tools must be installed.","default":false},"useAutoDiscovery":{"type":"boolean","description":"Use mDNS to discovery Google Cast devices on your next automatically?\n\nIf not explicitly set then it is TRUE if `devices` is not set"},"devices":{"type":"array","items":{"$ref":"#/definitions/ChromecastDeviceInfo"},"description":"A list of Google Cast devices to monitor\n\nIf this is used then `useAutoDiscovery` is set to FALSE, if not explicitly set"},"allowUnknownMedia":{"anyOf":[{"type":"boolean"},{"type":"array","items":{"type":"string"}}],"description":"Chromecast Apps report a \"media type\" in the status info returned for whatever is currently playing\n\n* If set to TRUE then Music AND Generic/Unknown media will be tracked for ALL APPS\n* If set to FALSE then only media explicitly typed as Music will be tracked for ALL APPS\n* If set to a list then only Apps whose name contain one of these values, case-insensitive, will have Music AND Generic/Unknown tracked\n\nSee https://developers.google.com/cast/docs/media/messages#MediaInformation \"metadata\" property","default":false},"forceMediaRecognitionOn":{"type":"array","items":{"type":"string"},"description":"Media provided by any App whose name is listed here will ALWAYS be tracked, regardless of the \"media type\" reported\n\nApps will be recognized if they CONTAIN any of these values, case-insensitive"}},"title":"ChromecastData"},"ChromecastDeviceInfo":{"type":"object","properties":{"name":{"type":"string","description":"A friendly name to identify this device","examples":["MySmartTV"]},"address":{"type":"string","description":"The IP address of the device","examples":["192.168.0.115"]}},"required":["name","address"],"title":"ChromecastDeviceInfo"},"MusikcubeSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MusikcubeData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["musikcube"]}},"required":["data","type"],"title":"MusikcubeSourceAIOConfig"},"MusikcubeData":{"type":"object","properties":{"url":{"type":"string","description":"URL of the Musikcube Websocket (Metadata) server to connect to\n\nYou MUST have enabled 'metadata' server and set a password: https://github.com/clangen/musikcube/wiki/remote-api-documentation\n * musikcube -> settings -> server setup\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `ws://`\n* Hostname => `localhost`\n* Port => `7905`","examples":["ws://localhost:7905"],"default":"ws://localhost:7905"},"password":{"type":"string","description":"Password set in Musikcube https://github.com/clangen/musikcube/wiki/remote-api-documentation\n\n* musikcube -> settings -> server setup -> password"},"device_id":{"type":"string"}},"required":["password"],"title":"MusikcubeData"},"MusicCastSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MusicCastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["musiccast"]}},"required":["data","type"],"title":"MusicCastSourceAIOConfig"},"MusicCastData":{"type":"object","properties":{"url":{"type":"string","description":"The host or URL of the YamahaExtendedControl endpoint to use","examples":[["192.168.0.101","http://192.168.0.101/YamahaExtendedControl"]]}},"required":["url"],"title":"MusicCastData"},"MPDSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MPDData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/MPDSourceOptions"},"type":{"type":"string","enum":["mpd"]}},"required":["data","options","type"],"title":"MPDSourceAIOConfig"},"MPDData":{"type":"object","properties":{"url":{"type":"string","description":"URL:PORT of the MPD server to connect to\n\nTo use this you must have TCP connections enabled for your MPD server https://mpd.readthedocs.io/en/stable/user.html#client-connections","examples":["localhost:6600"],"default":"localhost:6600"},"path":{"type":"string","description":"If using socket specify the path instead of url.\n\ntrailing `~` is replaced by your home directory"},"password":{"type":"string","description":"Password for the server, if set https://mpd.readthedocs.io/en/stable/user.html#permissions-and-passwords"}},"title":"MPDData"},"MPDSourceOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"MPDSourceOptions"},"VLCSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/VLCData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/VLCSourceOptions"},"type":{"type":"string","enum":["vlc"]}},"required":["data","type"],"title":"VLCSourceAIOConfig"},"VLCData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL:PORT of the VLC server to connect to\n\nTo use this you must have the Web (http) interface module enabled and a password set https://foxxmd.github.io/multi-scrobbler/docs/configuration#vlc","examples":["localhost:8080"],"default":"localhost:8080"},"password":{"type":"string","description":"Password for the server"}},"required":["password"],"title":"VLCData"},"VLCSourceOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"},"filenamePatterns":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"A list of regular expressions to use to extract metadata (title, album, artist) from a filename\n\nUsed when VLC reports only the filename for the current audio track"},"logFilenamePatterns":{"type":"boolean","description":"Log to DEBUG when a filename-only track is matched or not matched by filenamePatterns","default":false},"dumpVlcMetadata":{"type":"boolean","description":"Dump all the metadata VLC reports for an audio track to DEBUG.\n\nUse this if reporting an issue with VLC not correctly capturing metadata for a track.","default":false}},"title":"VLCSourceOptions"},"IcecastSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/IcecastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/IcecastSourceOptions"},"type":{"type":"string","enum":["icecast"]}},"required":["data","type"],"title":"IcecastSourceAIOConfig"},"IcecastData":{"type":"object","properties":{"sources":{"type":"array","items":{"$ref":"#/definitions/IcecastSource"}},"icestatsEndpoint":{"type":"string"},"statsEndpoint":{"type":"string"},"nextsongsEndpoint":{"type":"string"},"sevenhtmlEndpoint":{"type":"string"},"icyMetaInt":{"type":"number"},"url":{"type":"string","description":"The Icecast stream URL"}},"required":["url"],"title":"IcecastData"},"IcecastSource":{"type":"string","enum":["icy","ogg","icestats","stats","sevenhtml","nextsongs"],"title":"IcecastSource"},"IcecastSourceOptions":{"type":"object","properties":{"systemScrobble":{"type":"boolean","description":"For Sources that support manual listening, should MS default to scrobbling when no user interaction has occurred?\n\nIf not specified MS will use a Source's specific behavior, see Source's documentation."},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"IcecastSourceOptions"},"AzuracastSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/AzuracastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["azuracast"]}},"required":["data","type"],"title":"AzuracastSourceAIOConfig"},"AzuracastData":{"type":"object","properties":{"url":{"type":"string","description":"Base URL of the Azuracast instance\n\nThis does NOT include the station. If a station is included it will be ignored. Use `station` field to specify station, if necessary","examples":["https://radio.mydomain.tld","http://localhost:80"]},"station":{"type":"string","description":"The specific station to monitor\n\nScrobbling will only occur if any of the monitor conditions are met AND the station is ONLINE.\n\nTo monitor multiple stations create a Source for each station.","examples":["my-station-1"]},"monitorWhenListeners":{"type":["boolean","number"],"description":"Only activate scrobble monitoring if station\n\n* `true` => has any current listeners\n* `number` => has EQUAL TO or MORE THAN X number of listeners"},"monitorWhenLive":{"type":"boolean","description":"Only activate scrobble monitoring if station has a live DJ/Streamer","default":true},"apiKey":{"type":"string","description":"API Key used to access data about private streams\n\nhttps://www.azuracast.com/docs/developers/apis/#api-authentication"}},"required":["url","station"],"title":"AzuracastData"},"ClientAIOConfig":{"anyOf":[{"$ref":"#/definitions/MalojaClientAIOConfig"},{"$ref":"#/definitions/LastfmClientAIOConfig"},{"$ref":"#/definitions/ListenBrainzClientAIOConfig"}],"title":"ClientAIOConfig"},"MalojaClientAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this client. Used with sources to restrict where scrobbles are sent.","examples":["MyConfig"]},"data":{"$ref":"#/definitions/MalojaClientData","description":"Specific data required to configure this client"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"options":{"$ref":"#/definitions/CommonClientOptions"},"type":{"type":"string","enum":["maloja"]}},"required":["data","name","type"],"title":"MalojaClientAIOConfig"},"MalojaClientData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for maloja server","examples":["http://localhost:42010"]},"apiKey":{"type":"string","description":"API Key for Maloja server","examples":["myApiKey"]}},"required":["url","apiKey"],"title":"MalojaClientData"},"CommonClientOptions":{"type":"object","properties":{"refreshEnabled":{"type":"boolean","description":"Try to get fresh scrobble history from client when tracks to be scrobbled are newer than the last scrobble found in client history","default":true,"examples":[true]},"refreshStaleAfter":{"type":"number","description":"Refresh scrobbled plays from upstream service if last refresh was at least X seconds ago\n\n**In most case this setting does NOT need to be changed.** The default value is sufficient for the majority of use-cases. Increasing this setting may increase upstream service load and slow down scrobbles.\n\nThis setting should only be changed in specific scenarios where MS is handling multiple \"relaying\" client-services (IE lfm -> lz -> lfm) and there is the potential for a client to be out of sync after more than a few seconds.","examples":[60],"default":60},"refreshMinInterval":{"type":"number","description":"Minimum time (milliseconds) required to pass before upstream scrobbles can be refreshed.\n\n**In most case this setting does NOT need to be changed.** This will always be equal to or smaller than `refreshStaleAfter`.","default":5000,"examples":[5000]},"refreshInitialCount":{"type":"number","description":"The number of tracks to retrieve on initial refresh (related to scrobbleBacklogCount). If not specified this is the maximum supported by the client in 1 API call."},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"checkExistingScrobbles":{"type":"boolean","description":"Check client for an existing scrobble at the same recorded time as the \"new\" track to be scrobbled. If an existing scrobble is found this track is not track scrobbled.","default":true,"examples":[true]},"verbose":{"type":"object","properties":{"match":{"$ref":"#/definitions/MatchLoggingOptions"}},"description":"Options used for increasing verbosity of logging in MS (used for debugging)"},"deadLetterRetries":{"type":"number","description":"Number of times MS should automatically retry scrobbles in dead letter queue","default":1,"examples":[1]},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"CommonClientOptions"},"LastfmClientAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this client. Used with sources to restrict where scrobbles are sent.","examples":["MyConfig"]},"data":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"apiKey":{"type":"string","description":"API Key generated from Last.fm account","examples":["787c921a2a2ab42320831aba0c8f2fc2"]},"secret":{"type":"string","description":"Secret generated from Last.fm account","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"session":{"type":"string","description":"Optional session id returned from a completed auth flow"},"redirectUri":{"type":"string","description":"Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.","default":"http://localhost:9078/lastfm/callback","examples":["http://localhost:9078/lastfm/callback"]}},"required":["apiKey","secret"],"description":"Specific data required to configure this client"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"options":{"$ref":"#/definitions/CommonClientOptions"},"configureAs":{"type":"string","enum":["client","source"],"description":"Should always be `client` when using LastFM as a client","default":"client","examples":["client"]},"type":{"type":"string","enum":["lastfm"]}},"required":["data","name","type"],"title":"LastfmClientAIOConfig"},"ListenBrainzClientAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this client. Used with sources to restrict where scrobbles are sent.","examples":["MyConfig"]},"data":{"$ref":"#/definitions/ListenBrainzClientData","description":"Specific data required to configure this client"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"options":{"$ref":"#/definitions/CommonClientOptions"},"configureAs":{"type":"string","enum":["client","source"],"description":"Should always be `client` when using Listenbrainz as a client","default":"client","examples":["client"]},"type":{"type":"string","enum":["listenbrainz"]}},"required":["data","name","type"],"title":"ListenBrainzClientAIOConfig"},"ListenBrainzClientData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for the ListenBrainz server, if not using the default","examples":["https://api.listenbrainz.org/"],"default":"https://api.listenbrainz.org/"},"token":{"type":"string","description":"User token for the user to scrobble for","examples":["6794186bf-1157-4de6-80e5-uvb411f3ea2b"]},"username":{"type":"string","description":"Username of the user to scrobble for"}},"required":["token","username"],"title":"ListenBrainzClientData"},"WebhookConfig":{"anyOf":[{"$ref":"#/definitions/GotifyConfig"},{"$ref":"#/definitions/NtfyConfig"},{"$ref":"#/definitions/AppriseConfig"}],"title":"WebhookConfig"},"GotifyConfig":{"type":"object","properties":{"type":{"type":"string","enum":["gotify","ntfy","apprise"],"description":"Webhook type. Valid values are:\n\n* gotify\n* ntfy","examples":["gotify"]},"name":{"type":"string","description":"A friendly name used to identify webhook config in logs"},"url":{"type":"string","description":"The URL of the Gotify server. Same URL that would be used to reach the Gotify UI","examples":["http://192.168.0.100:8078"]},"token":{"type":"string","description":"The token created for this Application in Gotify","examples":["AQZI58fA.rfSZbm"]},"priorities":{"$ref":"#/definitions/PrioritiesConfig","description":"Priority of messages\n\n* Info -> 5\n* Warn -> 7\n* Error -> 10"}},"required":["token","type","url"],"title":"GotifyConfig"},"PrioritiesConfig":{"type":"object","properties":{"info":{"type":"number","examples":[5]},"warn":{"type":"number","examples":[7]},"error":{"type":"number","examples":[10]}},"required":["info","warn","error"],"title":"PrioritiesConfig"},"NtfyConfig":{"type":"object","properties":{"type":{"type":"string","enum":["gotify","ntfy","apprise"],"description":"Webhook type. Valid values are:\n\n* gotify\n* ntfy","examples":["gotify"]},"name":{"type":"string","description":"A friendly name used to identify webhook config in logs"},"url":{"type":"string","description":"The URL of the Ntfy server","examples":["http://192.168.0.100:8078"]},"topic":{"type":"string","description":"The topic mutli-scrobbler should POST to"},"username":{"type":"string","description":"Required if topic is protected"},"password":{"type":"string","description":"Required if topic is protected"},"priorities":{"$ref":"#/definitions/PrioritiesConfig","description":"Priority of messages\n\n* Info -> 3\n* Warn -> 4\n* Error -> 5"}},"required":["topic","type","url"],"title":"NtfyConfig"},"AppriseConfig":{"type":"object","properties":{"type":{"type":"string","enum":["gotify","ntfy","apprise"],"description":"Webhook type. Valid values are:\n\n* gotify\n* ntfy","examples":["gotify"]},"name":{"type":"string","description":"A friendly name used to identify webhook config in logs"},"host":{"type":"string","description":"The URL of the apprise-api server","examples":["http://192.168.0.100:8078"]},"urls":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"If using [Stateless Endpoints](https://github.com/caronc/apprise-api?tab=readme-ov-file#stateless-solution) the Apprise config URL(s) to send"},"keys":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"If using [Persistent Store Endpoints](https://github.com/caronc/apprise-api?tab=readme-ov-file#persistent-storage-solution) the Configuration ID(s) to send to\n\nNote: If multiple keys are defined then MS will attempt to POST to each one individually"},"tags":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Optional [tag(s)](https://github.com/caronc/apprise-api?tab=readme-ov-file#tagging) to send in the notification payload"}},"required":["host","type"],"title":"AppriseConfig"},"LogOptions":{"type":"object","properties":{"level":{"$ref":"#/definitions/LogLevel","description":"Specify the minimum log level for all log outputs without their own level specified.\n\nDefaults to env `LOG_LEVEL` or `info` if not specified.","default":"info"},"file":{"anyOf":[{"$ref":"#/definitions/LogLevel"},{"type":"boolean","const":false},{"$ref":"#/definitions/FileLogOptions"}],"description":"Specify the minimum log level to output to rotating files or file output options. If `false` no log files will be created."},"console":{"$ref":"#/definitions/LogLevel","description":"Specify the minimum log level streamed to the console (or docker container)"}},"description":"Configure log levels and file options for an AppLogger.\n\n```ts\nconst infoLogger = loggerApp({\n level: 'info' // console and file will log any levels `info` and above\n});\n\nconst logger = loggerApp({\n console: 'debug', // console will log `debug` and higher\n file: 'warn' // file will log `warn` and higher\n});\n\nconst fileLogger = loggerRollingApp({\n console: 'debug', // console will log `debug` and higher\n file: {\n level: 'warn', // file will log `warn` and higher\n path: '/my/cool/path/output.log', // optionally, output to log file at this path\n frequency: 'hourly', // optionally, rotate hourly\n }\n});\n```","title":"LogOptions"}}} \ No newline at end of file +{"$schema":"http://json-schema.org/draft-07/schema#","type":"object","properties":{"sourceDefaults":{"$ref":"#/definitions/SourceDefaults"},"clientDefaults":{"$ref":"#/definitions/ClientDefaults"},"sources":{"type":"array","items":{"$ref":"#/definitions/SourceAIOConfig"}},"clients":{"type":"array","items":{"$ref":"#/definitions/ClientAIOConfig"}},"webhooks":{"type":"array","items":{"$ref":"#/definitions/WebhookConfig"}},"port":{"type":"number","description":"Set the port the multi-scrobbler UI will be served from","default":9078,"examples":[9078]},"baseUrl":{"type":"string","description":"Set the Base URL the application should assume the UI is served from.\n\nThis will affect how default redirect URLs are generated (spotify, lastfm, deezer) and some logging messages.\n\nIt will NOT set the actual interface/IP that the application is listening on.\n\nThis can also be set using the BASE_URL environmental variable.","default":"http://localhost","examples":["http://localhost","http://192.168.0.101","https://ms.myDomain.tld"]},"logging":{"$ref":"#/definitions/LogOptions"},"disableWeb":{"type":"boolean","description":"Disable web server from running/listening on port.\n\nThis will also make any ingress sources (Plex, Jellyfin, Tautulli, etc...) unusable"},"debugMode":{"type":"boolean","description":"Enables ALL relevant logging and debug options for all sources/clients, when none are defined.\n\nThis is a convenience shortcut for enabling all output needed to troubleshoot an issue and does not need to be on for normal operation.\n\nIt can also be enabled with the environmental variable DEBUG_MODE=true","default":false,"examples":[false]},"cache":{"$ref":"#/definitions/CacheConfigOptions"}},"definitions":{"SourceDefaults":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"SourceDefaults"},"LogLevel":{"type":"string","enum":["silent","fatal","error","warn","info","log","verbose","debug"],"description":"Names of log levels that can be invoked on the logger\n\nFrom lowest to highest:\n\n* `debug`\n* `verbose`\n* `log`\n* `info`\n* `warn`\n* `error`\n* `fatal`\n* `silent` (will never output anything)\n\nWhen used in `LogOptions` specifies the **minimum** level the output should log at.","title":"LogLevel"},"FileLogOptions":{"type":"object","properties":{"timestamp":{"type":"string","enum":["unix","iso","auto"],"description":"For rolling log files\n\nWhen\n* value passed to rolling destination is a string (`path` from LogOptions is a string) and\n* `frequency` is defined\n\nThis determines the format of the datetime inserted into the log file name:\n\n* `unix` - unix epoch timestamp in milliseconds\n* `iso` - Full [ISO8601](https://en.wikipedia.org/wiki/ISO_8601) datetime IE '2024-03-07T20:11:34Z'\n* `auto`\n * When frequency is `daily` only inserts date IE YYYY-MM-DD\n * Otherwise inserts full ISO8601 datetime","default":"auto"},"size":{"type":["number","string"],"description":"The maximum size of a given rolling log file.\n\nCan be combined with frequency. Use k, m and g to express values in KB, MB or GB.\n\nNumerical values will be considered as MB.","default":"10MB"},"frequency":{"anyOf":[{"type":"string","enum":["daily"]},{"type":"string","enum":["hourly"]},{"type":"number"}],"description":"The amount of time a given rolling log file is used. Can be combined with size.\n\nUse `daily` or `hourly` to rotate file every day (or every hour). Existing file within the current day (or hour) will be re-used.\n\nNumerical values will be considered as a number of milliseconds. Using a numerical value will always create a new file upon startup.","default":"daily"},"path":{"anyOf":[{"type":"string"},{"$comment":"() => string"}],"description":"The path and filename to use for log files.\n\nIf using rolling files the filename will be appended with `.N` (a number) BEFORE the extension based on rolling status.\n\nMay also be specified using env LOG_PATH or a function that returns a string.\n\nIf path is relative the absolute path will be derived from `logBaseDir` (in `LoggerAppExtras`) which defaults to CWD","default":"./logs/app.log"},"level":{"anyOf":[{"$ref":"#/definitions/LogLevel"},{"type":"boolean","const":false}],"description":"Specify the minimum log level to output to rotating files. If `false` no log files will be created."}},"title":"FileLogOptions"},"ScrobbleThresholds":{"type":"object","properties":{"duration":{"type":["number","null"],"description":"The number of seconds a track has been listened to before it should be considered scrobbled.\n\nSet to null to disable.","default":240,"examples":[240]},"percent":{"type":["number","null"],"description":"The percentage (as an integer) of a track that should have been seen played before it should be scrobbled. Only used if the Source provides information about how long the track is.\n\nSet to null to disable.\n\nNOTE: This should be used with care when the Source is a \"polling\" type (has an 'interval' property). If the track is short and the interval is too high MS may ignore the track if percentage is high because it had not \"seen\" the track for long enough from first discovery, even if you have been playing the track for longer.","default":50,"examples":[50]}},"title":"ScrobbleThresholds"},"PlayTransformOptions":{"type":"object","properties":{"log":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["all"]}]},"preCompare":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"},"compare":{"type":"object","properties":{"candidate":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"},"existing":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"}}},"postCompare":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"}},"title":"PlayTransformOptions"},"PlayTransformPartsConfig<SearchAndReplaceTerm>":{"anyOf":[{"$ref":"#/definitions/PlayTransformPartsArray%3CSearchAndReplaceTerm%3E"},{"$ref":"#/definitions/PlayTransformParts%3CSearchAndReplaceTerm%3E"}],"title":"PlayTransformPartsConfig<SearchAndReplaceTerm>"},"PlayTransformPartsArray<SearchAndReplaceTerm>":{"type":"array","items":{"$ref":"#/definitions/PlayTransformParts%3CSearchAndReplaceTerm%3E"},"title":"PlayTransformPartsArray<SearchAndReplaceTerm>"},"PlayTransformParts<SearchAndReplaceTerm>":{"type":"object","properties":{"when":{"$ref":"#/definitions/WhenConditionsConfig"},"title":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}},"artists":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}},"album":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}}},"title":"PlayTransformParts<SearchAndReplaceTerm>"},"WhenConditionsConfig":{"$ref":"#/definitions/WhenConditions%3Cstring%3E","title":"WhenConditionsConfig"},"WhenConditions<string>":{"type":"array","items":{"$ref":"#/definitions/WhenParts%3Cstring%3E"},"title":"WhenConditions<string>"},"WhenParts<string>":{"$ref":"#/definitions/PlayTransformPartsAtomic%3Cstring%3E","title":"WhenParts<string>"},"PlayTransformPartsAtomic<string>":{"type":"object","properties":{"title":{"type":"string"},"artists":{"type":"string"},"album":{"type":"string"}},"title":"PlayTransformPartsAtomic<string>"},"SearchAndReplaceTerm":{"anyOf":[{"type":"string"},{"$ref":"#/definitions/ConditionalSearchAndReplaceTerm"}],"title":"SearchAndReplaceTerm"},"ConditionalSearchAndReplaceTerm":{"type":"object","properties":{"when":{"$ref":"#/definitions/WhenConditionsConfig"},"search":{},"replace":{}},"required":["search","replace"],"title":"ConditionalSearchAndReplaceTerm"},"ClientDefaults":{"type":"object","properties":{"refreshEnabled":{"type":"boolean","description":"Try to get fresh scrobble history from client when tracks to be scrobbled are newer than the last scrobble found in client history","default":true,"examples":[true]},"refreshStaleAfter":{"type":"number","description":"Refresh scrobbled plays from upstream service if last refresh was at least X seconds ago\n\n**In most case this setting does NOT need to be changed.** The default value is sufficient for the majority of use-cases. Increasing this setting may increase upstream service load and slow down scrobbles.\n\nThis setting should only be changed in specific scenarios where MS is handling multiple \"relaying\" client-services (IE lfm -> lz -> lfm) and there is the potential for a client to be out of sync after more than a few seconds.","examples":[60],"default":60},"refreshMinInterval":{"type":"number","description":"Minimum time (milliseconds) required to pass before upstream scrobbles can be refreshed.\n\n**In most case this setting does NOT need to be changed.** This will always be equal to or smaller than `refreshStaleAfter`.","default":5000,"examples":[5000]},"refreshInitialCount":{"type":"number","description":"The number of tracks to retrieve on initial refresh (related to scrobbleBacklogCount). If not specified this is the maximum supported by the client in 1 API call."},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"checkExistingScrobbles":{"type":"boolean","description":"Check client for an existing scrobble at the same recorded time as the \"new\" track to be scrobbled. If an existing scrobble is found this track is not track scrobbled.","default":true,"examples":[true]},"verbose":{"type":"object","properties":{"match":{"$ref":"#/definitions/MatchLoggingOptions"}},"description":"Options used for increasing verbosity of logging in MS (used for debugging)"},"deadLetterRetries":{"type":"number","description":"Number of times MS should automatically retry scrobbles in dead letter queue","default":1,"examples":[1]},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"ClientDefaults"},"MatchLoggingOptions":{"type":"object","properties":{"onNoMatch":{"type":"boolean","description":"Log to DEBUG when a new track does NOT match an existing scrobble","default":false,"examples":[false]},"onMatch":{"type":"boolean","description":"Log to DEBUG when a new track DOES match an existing scrobble","default":false,"examples":[false]},"confidenceBreakdown":{"type":"boolean","description":"Include confidence breakdowns in track match logging, if applicable","default":false,"examples":[false]}},"description":"Scrobble matching (between new source track and existing client scrobbles) logging options. Used for debugging.","title":"MatchLoggingOptions"},"SourceAIOConfig":{"anyOf":[{"$ref":"#/definitions/SpotifySourceAIOConfig"},{"$ref":"#/definitions/PlexSourceAIOConfig"},{"$ref":"#/definitions/PlexApiSourceAIOConfig"},{"$ref":"#/definitions/TautulliSourceAIOConfig"},{"$ref":"#/definitions/DeezerSourceAIOConfig"},{"$ref":"#/definitions/DeezerInternalAIOConfig"},{"$ref":"#/definitions/ListenbrainzEndpointSourceAIOConfig"},{"$ref":"#/definitions/LastFMEndpointSourceAIOConfig"},{"$ref":"#/definitions/SubsonicSourceAIOConfig"},{"$ref":"#/definitions/JellySourceAIOConfig"},{"$ref":"#/definitions/JellyApiSourceAIOConfig"},{"$ref":"#/definitions/LastFmSouceAIOConfig"},{"$ref":"#/definitions/YTMusicSourceAIOConfig"},{"$ref":"#/definitions/MPRISSourceAIOConfig"},{"$ref":"#/definitions/MopidySourceAIOConfig"},{"$ref":"#/definitions/ListenBrainzSourceAIOConfig"},{"$ref":"#/definitions/JRiverSourceAIOConfig"},{"$ref":"#/definitions/KodiSourceAIOConfig"},{"$ref":"#/definitions/WebScrobblerSourceAIOConfig"},{"$ref":"#/definitions/ChromecastSourceAIOConfig"},{"$ref":"#/definitions/MalojaSourceAIOConfig"},{"$ref":"#/definitions/MusikcubeSourceAIOConfig"},{"$ref":"#/definitions/MusicCastSourceAIOConfig"},{"$ref":"#/definitions/MPDSourceAIOConfig"},{"$ref":"#/definitions/VLCSourceAIOConfig"},{"$ref":"#/definitions/IcecastSourceAIOConfig"},{"$ref":"#/definitions/AzuracastSourceAIOConfig"},{"$ref":"#/definitions/KoitoSourceAIOConfig"}],"title":"SourceAIOConfig"},"SpotifySourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/SpotifySourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["spotify"]}},"required":["data","type"],"title":"SpotifySourceAIOConfig"},"SpotifySourceData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)\n\nIt is unlikely you should need to change this unless you scrobble many very short tracks often\n\nReading:\n* https://developer.spotify.com/documentation/web-api/guides/rate-limits/\n* https://medium.com/mendix/limiting-your-amount-of-calls-in-mendix-most-of-the-time-rest-835dde55b10e\n * Rate limit may ~180 req/min\n* https://community.spotify.com/t5/Spotify-for-Developers/Web-API-ratelimit/m-p/5503150/highlight/true#M7930\n * Informally indicated as 20 req/sec? Probably for burstiness","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"clientId":{"type":"string","description":"spotify client id","examples":["787c921a2a2ab42320831aba0c8f2fc2"]},"clientSecret":{"type":"string","description":"spotify client secret","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"redirectUri":{"type":"string","description":"spotify redirect URI -- required only if not the default shown here. URI must end in \"callback\"","default":"http://localhost:9078/callback","examples":["http://localhost:9078/callback"]}},"required":["clientId","clientSecret"],"title":"SpotifySourceData"},"CommonSourceOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"CommonSourceOptions"},"PlexSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/PlexSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["plex"]}},"required":["data","type"],"title":"PlexSourceAIOConfig"},"PlexSourceData":{"type":"object","properties":{"user":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled","examples":[["MyUser1","MyUser2"]]},"libraries":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of libraries to scrobble tracks from\n\nIf none are provided tracks from all libraries will be scrobbled","examples":[["Audio","Music"]]},"servers":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled","examples":[["MyServerName"]]}},"title":"PlexSourceData"},"PlexApiSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/PlexApiData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/PlexApiOptions"},"type":{"type":"string","enum":["plex"]}},"required":["data","options","type"],"title":"PlexApiSourceAIOConfig"},"PlexApiData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"token":{"type":"string"},"url":{"type":"string","description":"http(s)://HOST:PORT of the Plex server to connect to"},"usersAllow":{"anyOf":[{"type":"string"},{"type":"boolean","enum":[true]},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble for specific users (case-insensitive)\n\nIf `true` MS will scrobble activity from all users"},"usersBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble for these users (case-insensitive)"},"devicesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if device or application name contains strings from this list (case-insensitive)"},"devicesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if device or application name contains strings from this list (case-insensitive)"},"librariesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if library name contains string from this list (case-insensitive)"},"librariesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if library name contains strings from this list (case-insensitive)"}},"required":["url"],"title":"PlexApiData"},"PlexApiOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"},"ignoreInvalidCert":{"type":"boolean","description":"Ignore invalid cert errors when connecting to Plex\n\nUseful for Plex servers using \"Required\" Secure Connections with self-signed certificates\n\nDo not enable unless you know you need this.","default":false}},"title":"PlexApiOptions"},"TautulliSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/PlexSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["tautulli"]}},"required":["data","type"],"title":"TautulliSourceAIOConfig"},"DeezerSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/DeezerData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["deezer"]}},"required":["data","type"],"title":"DeezerSourceAIOConfig"},"DeezerData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"clientId":{"type":"string","description":"deezer client id","examples":["a89cba1569901a0671d5a9875fed4be1"]},"clientSecret":{"type":"string","description":"deezer client secret","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"redirectUri":{"type":"string","description":"deezer redirect URI -- required only if not the default shown here. URI must end in \"callback\"","default":"http://localhost:9078/deezer/callback","examples":["http://localhost:9078/deezer/callback"]}},"required":["clientId","clientSecret"],"title":"DeezerData"},"DeezerInternalAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/DeezerInternalData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/DeezerInternalSourceOptions"},"type":{"type":"string","enum":["deezer"]}},"required":["data","type"],"title":"DeezerInternalAIOConfig"},"DeezerInternalData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"arl":{"type":"string","description":"ARL retrieved from Deezer response header"},"userAgent":{"type":"string","description":"User agent","default":"Mozilla/5.0 (X11; Linux i686; rv:135.0) Gecko/20100101 Firefox/135.0"}},"required":["arl"],"title":"DeezerInternalData"},"DeezerInternalSourceOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"},"fuzzyDiscoveryIgnore":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["aggressive"]}]}},"title":"DeezerInternalSourceOptions"},"ListenbrainzEndpointSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/ListenbrainzEndpointData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["endpointlz"]}},"required":["type"],"title":"ListenbrainzEndpointSourceAIOConfig"},"ListenbrainzEndpointData":{"type":"object","properties":{"slug":{"type":["string","null"],"description":"The URL ending that should be used to identify scrobbles for this source\n\nIf you are using multiple Listenbrainz endpoint sources (scrobbles for many users) you can use a slug to match Sources with individual users/origins\n\nExample:\n\n* slug: 'usera' => API URL: http://localhost:9078/api/listenbrainz/usera\n* slug: 'originb' => API URL: http://localhost:9078/api/listenbrainz/originb\n\nIf no slug is found from an extension's incoming webhook event the first Listenbrainz source without a slug will be used"},"token":{"type":["string","null"],"description":"If an LZ submission request contains this token in the Authorization Header it will be used to match the submission with this Source\n\nSee: https://listenbrainz.readthedocs.io/en/latest/users/api/index.html#add-the-user-token-to-your-requests"}},"title":"ListenbrainzEndpointData"},"LastFMEndpointSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/LastFMEndpointData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["endpointlfm"]}},"required":["type"],"title":"LastFMEndpointSourceAIOConfig"},"LastFMEndpointData":{"type":"object","properties":{"slug":{"type":["string","null"],"description":"The URL ending that should be used to identify scrobbles for this source\n\nIf you are using multiple Last.fm endpoint sources (scrobbles for many users) you can use a slug to match Sources with individual users/origins\n\nExample:\n\n* slug: 'usera' => API URL: http://localhost:9078/api/lastfm/usera\n* slug: 'originb' => API URL: http://localhost:9078/api/lastfm/originb\n\nIf no slug is found from an extension's incoming webhook event the first Last.fm source without a slug will be used"}},"title":"LastFMEndpointData"},"SubsonicSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/SubsonicData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["subsonic"]}},"required":["data","type"],"title":"SubsonicSourceAIOConfig"},"SubsonicData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the subsonic media server to query","examples":["http://airsonic.local"]},"user":{"type":"string","description":"Username to login to the server with","examples":[["MyUser"]]},"password":{"type":"string","description":"Password for the user to login to the server with","examples":["MyPassword"]},"ignoreTlsErrors":{"type":"boolean","description":"If your subsonic server is using self-signed certs you may need to disable TLS errors in order to get a connection\n\nWARNING: This should be used with caution as your traffic may not be encrypted.","default":false},"legacyAuthentication":{"type":"boolean","description":"Older Subsonic versions, and some badly implemented servers (Nextcloud), use legacy authentication which sends your password in CLEAR TEXT. This is less secure than the newer, recommended hashing authentication method but in some cases it is needed. See \"Authentication\" section here => https://www.subsonic.org/pages/api.jsp\n\nIf this option is not specified it will be turned on if the subsonic server responds with error code 41 \"Token authentication not supported for LDAP users.\" -- See Error Handling section => https://www.subsonic.org/pages/api.jsp","default":false},"usersAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble for specific users (case-insensitive)\n\nIf undefined or an empty string/list MS will scrobble activity from all users"}},"required":["url","user","password"],"title":"SubsonicData"},"JellySourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/JellyData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["jellyfin"]}},"required":["data","type"],"title":"JellySourceAIOConfig"},"JellyData":{"type":"object","properties":{"users":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled","examples":[["MyUser1","MyUser2"]]},"servers":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled","examples":[["MyServerName1"]]}},"title":"JellyData"},"JellyApiSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/JellyApiData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/JellyApiOptions"},"type":{"type":"string","enum":["jellyfin"]}},"required":["data","options","type"],"title":"JellyApiSourceAIOConfig"},"JellyApiData":{"type":"object","properties":{"url":{"type":"string","description":"HOST:PORT of the Jellyfin server to connect to"},"user":{"type":"string","description":"The username of the user to authenticate for or track scrobbles for"},"password":{"type":"string","description":"Password of the username to authenticate for\n\nRequired if `apiKey` is not provided."},"apiKey":{"type":"string","description":"API Key to authenticate with.\n\nRequired if `password` is not provided."},"usersAllow":{"anyOf":[{"type":"string"},{"type":"boolean","enum":[true]},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble for specific users (case-insensitive)\n\nIf `true` MS will scrobble activity from all users"},"usersBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble for these users (case-insensitive)"},"devicesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if device or application name contains strings from this list (case-insensitive)"},"devicesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if device or application name contains strings from this list (case-insensitive)"},"librariesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if library name contains string from this list (case-insensitive)"},"librariesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if library name contains strings from this list (case-insensitive)"},"additionalAllowedLibraryTypes":{"type":"array","items":{},"description":"Allow MS to scrobble audio media in libraries classified other than 'music'\n\n`librariesAllow` will achieve the same result as this but this is more convenient if you do not want to explicitly list every library name or are only using `librariesBlock`"},"allowUnknown":{"type":"boolean","description":"Force media with a type of \"Unknown\" to be counted as Audio","default":false},"frontendUrlOverride":{"type":"string","description":"HOST:PORT of the Jellyfin server that your browser will be able to access from the frontend (and thus load images and links from)\nIf unspecified it will use the normal server HOST and PORT from the `url`\nNecessary if you are using a reverse proxy or other network configuration that prevents the frontend from accessing the server directly"}},"required":["url","user"],"title":"JellyApiData"},"JellyApiOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"JellyApiOptions"},"LastFmSouceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/LastFmSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"configureAs":{"type":"string","enum":["source"],"description":"When used in `lastfm.config` this tells multi-scrobbler whether to use this data to configure a source or client.","default":"source","examples":["source"]},"type":{"type":"string","enum":["lastfm"]}},"required":["data","type"],"title":"LastFmSouceAIOConfig"},"LastFmSourceData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"apiKey":{"type":"string","description":"API Key generated from Last.fm account","examples":["787c921a2a2ab42320831aba0c8f2fc2"]},"secret":{"type":"string","description":"Secret generated from Last.fm account","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"session":{"type":"string","description":"Optional session id returned from a completed auth flow"},"redirectUri":{"type":"string","description":"Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.","default":"http://localhost:9078/lastfm/callback","examples":["http://localhost:9078/lastfm/callback"]},"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"}},"required":["apiKey","secret"],"title":"LastFmSourceData"},"YTMusicSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/YTMusicData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"type":"object","properties":{"logAuth":{"type":"boolean","description":"When true MS will log to DEBUG all of the credentials data it receives from YTM"},"logDiff":{"type":"boolean","description":"Always log history diff\n\nBy default MS will log to `WARN` if history diff is inconsistent but does not log if diff is expected (on new tracks found)\nSet this to `true` to ALWAYS log diff on new tracks. Expected diffs will log to `DEBUG` and inconsistent diffs will continue to log to `WARN`","default":false},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}}},"type":{"type":"string","enum":["ytmusic"]}},"required":["type"],"title":"YTMusicSourceAIOConfig"},"YTMusicData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"cookie":{"type":"string","description":"The cookie retrieved from the Request Headers of music.youtube.com after logging in.\n\nSee https://ytmusicapi.readthedocs.io/en/stable/setup/browser.html#copy-authentication-headers for how to retrieve this value.","examples":["VISITOR_INFO1_LIVE=jMp2xA1Xz2_PbVc; __Secure-3PAPISID=3AxsXpy0M/AkISpjek; ..."]},"clientId":{"type":"string","description":"Google Cloud Console project OAuth Client ID\n\nGenerated from a custom OAuth Client, see docs"},"clientSecret":{"type":"string","description":"Google Cloud Console project OAuth Client Secret\n\nGenerated from a custom OAuth Client, see docs"},"redirectUri":{"type":"string","description":"Google Cloud Console project OAuth Client Authorized redirect URI\n\nGenerated from a custom OAuth Client, see docs. multi-scrobbler will generate a default based on BASE_URL.\nOnly specify this if the default does not work for you."},"innertubeOptions":{"$ref":"#/definitions/InnertubeOptions","description":"Additional options for authorization and tailoring YTM client"}},"title":"YTMusicData"},"InnertubeOptions":{"type":"object","properties":{"po_token":{"type":"string","description":"Proof of Origin token\n\nMay be required if YTM starts returning 403"},"visitor_data":{"type":"string","description":"Visitor ID value found in VISITOR_INFO1_LIVE or visitorData cookie\n\nMay be required if YTM starts returning 403"},"account_index":{"type":"number","description":"If account login results in being able to choose multiple account, use a zero-based index to choose which one to monitor","examples":[0,1]},"location":{"type":"string"},"lang":{"type":"string"},"generate_session_locally":{"type":"boolean"},"device_category":{"type":"string"},"client_type":{"type":"string"},"timezone":{"type":"string"}},"title":"InnertubeOptions"},"MPRISSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MPRISData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["mpris"]}},"required":["data","type"],"title":"MPRISSourceAIOConfig"},"MPRISData":{"type":"object","properties":{"blacklist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"DO NOT scrobble from any players that START WITH these values, case-insensitive","examples":[["spotify","vlc"]]},"whitelist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"ONLY from any players that START WITH these values, case-insensitive\n\nIf whitelist is present then blacklist is ignored","examples":[["spotify","vlc"]]}},"title":"MPRISData"},"MopidySourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MopidyData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["mopidy"]}},"required":["data","type"],"title":"MopidySourceAIOConfig"},"MopidyData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the Mopidy HTTP server to connect to\n\nYou MUST have Mopidy-HTTP extension enabled: https://mopidy.com/ext/http\n\nmulti-scrobbler connects to the WebSocket endpoint that ultimately looks like this => `ws://localhost:6680/mopidy/ws/`\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `ws://`\n* Hostname => `localhost`\n* Port => `6680`\n* Path => `/mopidy/ws/`","examples":["ws://localhost:6680/mopidy/ws/"],"default":"ws://localhost:6680/mopidy/ws/"},"uriBlacklist":{"type":"array","items":{"type":"string"},"description":"Do not scrobble tracks whose URI STARTS WITH any of these strings, case-insensitive\n\nEX: Don't scrobble tracks from soundcloud by adding 'soundcloud' to this list.\n\nList is ignored if uriWhitelist is used."},"uriWhitelist":{"type":"array","items":{"type":"string"},"description":"Only scrobble tracks whose URI STARTS WITH any of these strings, case-insensitive\n\nEX: Only scrobble tracks from soundcloud by adding 'soundcloud' to this list."},"albumBlacklist":{"type":"array","items":{"type":"string"},"description":"Remove album data that matches any case-insensitive string from this list when scrobbling,\n\nFor certain sources (Soundcloud) Mopidy does not have all track info (Album) and will instead use \"Soundcloud\" as the Album name. You can prevent multi-scrobbler from using this bad Album data by adding the fake name to this list. Multi-scrobbler will still scrobble the track, just without the bad data.","examples":[["Soundcloud","Mixcloud"]],"default":["Soundcloud"]}},"title":"MopidyData"},"ListenBrainzSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/ListenBrainzSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"configureAs":{"type":"string","enum":["source"],"description":"When used in `listenbrainz.config` this tells multi-scrobbler whether to use this data to configure a source or client.","default":"source","examples":["source"]},"type":{"type":"string","enum":["listenbrainz"]}},"required":["data","type"],"title":"ListenBrainzSourceAIOConfig"},"ListenBrainzSourceData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for the ListenBrainz server, if not using the default","examples":["https://api.listenbrainz.org/"],"default":"https://api.listenbrainz.org/"},"token":{"type":"string","description":"User token for the user to scrobble for","examples":["6794186bf-1157-4de6-80e5-uvb411f3ea2b"]},"username":{"type":"string","description":"Username of the user to scrobble for"}},"required":["token","username"],"title":"ListenBrainzSourceData"},"JRiverSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/JRiverData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["jriver"]}},"required":["data","type"],"title":"JRiverSourceAIOConfig"},"JRiverData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the JRiver HTTP server to connect to\n\nmulti-scrobbler connects to the Web Service Interface endpoint that ultimately looks like this => `http://yourDomain:52199/MCWS/v1/`\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `http://`\n* Hostname => `localhost`\n* Port => `52199`\n* Path => `/MCWS/v1/`","examples":["http://localhost:52199/MCWS/v1/"],"default":"http://localhost:52199/MCWS/v1/"},"username":{"type":"string","description":"If you have enabled authentication, the username you set"},"password":{"type":"string","description":"If you have enabled authentication, the password you set"}},"required":["url"],"title":"JRiverData"},"KodiSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/KodiData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["kodi"]}},"required":["data","type"],"title":"KodiSourceAIOConfig"},"KodiData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the Kodi HTTP server to connect to\n\nmulti-scrobbler connects to the Web Service Interface endpoint that ultimately looks like this => `http://yourDomain:8080/jsonrpc`\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `http://`\n* Hostname => `localhost`\n* Port => `8080`\n* Path => `/jsonrpc`","examples":["http://localhost:8080/jsonrpc"],"default":"http://localhost:8080/jsonrpc"},"username":{"type":"string","description":"The username set for Remote Control via Web Sever"},"password":{"type":"string","description":"The password set for Remote Control via Web Sever"}},"required":["url","username","password"],"title":"KodiData"},"WebScrobblerSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/WebScrobblerData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["webscrobbler"]}},"required":["type"],"title":"WebScrobblerSourceAIOConfig"},"WebScrobblerData":{"type":"object","properties":{"slug":{"type":["string","null"],"description":"The URL ending that should be used to identify scrobbles for this source\n\nIn WebScrobbler's Webhook you must set an 'API URL'. All MS WebScrobbler sources must start like:\n\nhttp://localhost:9078/api/webscrobbler\n\nIf you are using multiple WebScrobbler sources (scrobbles for many users) you must use a slug to match Sources with each users extension.\n\nExample:\n\n* slug: 'usera' => API URL: http://localhost:9078/api/webscrobbler/usera\n* slug: 'userb' => API URL: http://localhost:9078/api/webscrobbler/userb\n\nIf no slug is found from an extension's incoming webhook event the first WebScrobbler source without a slug will be used"},"blacklist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Block scrobbling from specific WebScrobbler Connectors","examples":[["youtube"]]},"whitelist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only allow scrobbling from specific WebScrobbler Connectors","examples":[["mixcloud","soundcloud","bandcamp"]]}},"title":"WebScrobblerData"},"ChromecastSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/ChromecastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["chromecast"]}},"required":["data","type"],"title":"ChromecastSourceAIOConfig"},"ChromecastData":{"type":"object","properties":{"blacklistDevices":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"DO NOT scrobble from any cast devices that START WITH these values, case-insensitive\n\nUseful when used with auto discovery","examples":[["home-mini","family-tv"]]},"whitelistDevices":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"ONLY scrobble from any cast device that START WITH these values, case-insensitive\n\nIf whitelist is present then blacklist is ignored\n\nUseful when used with auto discovery","examples":[["home-mini","family-tv"]]},"blacklistApps":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"DO NOT scrobble from any application that START WITH these values, case-insensitive","examples":[["spotify","pandora"]]},"whitelistApps":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"ONLY scrobble from any application that START WITH these values, case-insensitive\n\nIf whitelist is present then blacklist is ignored","examples":[["spotify","pandora"]]},"useAvahi":{"type":"boolean","description":"Try to use Avahi and avahi-browse to resolve mDNS devices instead of native mDNS querying\n\nUseful for docker (alpine) container where mDNS resolution is not yet supported. Avahi socket must be exposed to the container and avahi-tools must be installed.","default":false},"useAutoDiscovery":{"type":"boolean","description":"Use mDNS to discovery Google Cast devices on your next automatically?\n\nIf not explicitly set then it is TRUE if `devices` is not set"},"devices":{"type":"array","items":{"$ref":"#/definitions/ChromecastDeviceInfo"},"description":"A list of Google Cast devices to monitor\n\nIf this is used then `useAutoDiscovery` is set to FALSE, if not explicitly set"},"allowUnknownMedia":{"anyOf":[{"type":"boolean"},{"type":"array","items":{"type":"string"}}],"description":"Chromecast Apps report a \"media type\" in the status info returned for whatever is currently playing\n\n* If set to TRUE then Music AND Generic/Unknown media will be tracked for ALL APPS\n* If set to FALSE then only media explicitly typed as Music will be tracked for ALL APPS\n* If set to a list then only Apps whose name contain one of these values, case-insensitive, will have Music AND Generic/Unknown tracked\n\nSee https://developers.google.com/cast/docs/media/messages#MediaInformation \"metadata\" property","default":false},"forceMediaRecognitionOn":{"type":"array","items":{"type":"string"},"description":"Media provided by any App whose name is listed here will ALWAYS be tracked, regardless of the \"media type\" reported\n\nApps will be recognized if they CONTAIN any of these values, case-insensitive"}},"title":"ChromecastData"},"ChromecastDeviceInfo":{"type":"object","properties":{"name":{"type":"string","description":"A friendly name to identify this device","examples":["MySmartTV"]},"address":{"type":"string","description":"The IP address of the device","examples":["192.168.0.115"]}},"required":["name","address"],"title":"ChromecastDeviceInfo"},"MalojaSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MalojaSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"configureAs":{"type":"string","enum":["source"],"description":"When used in `maloja.config` this tells multi-scrobbler whether to use this data to configure a source or client.","default":"source","examples":["source"]},"type":{"type":"string","enum":["maloja"]}},"required":["data","type"],"title":"MalojaSourceAIOConfig"},"MalojaSourceData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for maloja server","examples":["http://localhost:42010"]},"apiKey":{"type":"string","description":"API Key for Maloja server","examples":["myApiKey"]}},"required":["apiKey","url"],"title":"MalojaSourceData"},"MusikcubeSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MusikcubeData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["musikcube"]}},"required":["data","type"],"title":"MusikcubeSourceAIOConfig"},"MusikcubeData":{"type":"object","properties":{"url":{"type":"string","description":"URL of the Musikcube Websocket (Metadata) server to connect to\n\nYou MUST have enabled 'metadata' server and set a password: https://github.com/clangen/musikcube/wiki/remote-api-documentation\n * musikcube -> settings -> server setup\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `ws://`\n* Hostname => `localhost`\n* Port => `7905`","examples":["ws://localhost:7905"],"default":"ws://localhost:7905"},"password":{"type":"string","description":"Password set in Musikcube https://github.com/clangen/musikcube/wiki/remote-api-documentation\n\n* musikcube -> settings -> server setup -> password"},"device_id":{"type":"string"}},"required":["password"],"title":"MusikcubeData"},"MusicCastSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MusicCastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["musiccast"]}},"required":["data","type"],"title":"MusicCastSourceAIOConfig"},"MusicCastData":{"type":"object","properties":{"url":{"type":"string","description":"The host or URL of the YamahaExtendedControl endpoint to use","examples":[["192.168.0.101","http://192.168.0.101/YamahaExtendedControl"]]}},"required":["url"],"title":"MusicCastData"},"MPDSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MPDData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/MPDSourceOptions"},"type":{"type":"string","enum":["mpd"]}},"required":["data","options","type"],"title":"MPDSourceAIOConfig"},"MPDData":{"type":"object","properties":{"url":{"type":"string","description":"URL:PORT of the MPD server to connect to\n\nTo use this you must have TCP connections enabled for your MPD server https://mpd.readthedocs.io/en/stable/user.html#client-connections","examples":["localhost:6600"],"default":"localhost:6600"},"path":{"type":"string","description":"If using socket specify the path instead of url.\n\ntrailing `~` is replaced by your home directory"},"password":{"type":"string","description":"Password for the server, if set https://mpd.readthedocs.io/en/stable/user.html#permissions-and-passwords"}},"title":"MPDData"},"MPDSourceOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"MPDSourceOptions"},"VLCSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/VLCData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/VLCSourceOptions"},"type":{"type":"string","enum":["vlc"]}},"required":["data","type"],"title":"VLCSourceAIOConfig"},"VLCData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL:PORT of the VLC server to connect to\n\nTo use this you must have the Web (http) interface module enabled and a password set https://foxxmd.github.io/multi-scrobbler/docs/configuration#vlc","examples":["localhost:8080"],"default":"localhost:8080"},"password":{"type":"string","description":"Password for the server"}},"required":["password"],"title":"VLCData"},"VLCSourceOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"},"filenamePatterns":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"A list of regular expressions to use to extract metadata (title, album, artist) from a filename\n\nUsed when VLC reports only the filename for the current audio track"},"logFilenamePatterns":{"type":"boolean","description":"Log to DEBUG when a filename-only track is matched or not matched by filenamePatterns","default":false},"dumpVlcMetadata":{"type":"boolean","description":"Dump all the metadata VLC reports for an audio track to DEBUG.\n\nUse this if reporting an issue with VLC not correctly capturing metadata for a track.","default":false}},"title":"VLCSourceOptions"},"IcecastSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/IcecastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/IcecastSourceOptions"},"type":{"type":"string","enum":["icecast"]}},"required":["data","type"],"title":"IcecastSourceAIOConfig"},"IcecastData":{"type":"object","properties":{"sources":{"type":"array","items":{"$ref":"#/definitions/IcecastSource"}},"icestatsEndpoint":{"type":"string"},"statsEndpoint":{"type":"string"},"nextsongsEndpoint":{"type":"string"},"sevenhtmlEndpoint":{"type":"string"},"icyMetaInt":{"type":"number"},"url":{"type":"string","description":"The Icecast stream URL"}},"required":["url"],"title":"IcecastData"},"IcecastSource":{"type":"string","enum":["icy","ogg","icestats","stats","sevenhtml","nextsongs"],"title":"IcecastSource"},"IcecastSourceOptions":{"type":"object","properties":{"systemScrobble":{"type":"boolean","description":"For Sources that support manual listening, should MS default to scrobbling when no user interaction has occurred?\n\nIf not specified MS will use a Source's specific behavior, see Source's documentation."},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"IcecastSourceOptions"},"AzuracastSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/AzuracastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["azuracast"]}},"required":["data","type"],"title":"AzuracastSourceAIOConfig"},"AzuracastData":{"type":"object","properties":{"url":{"type":"string","description":"Base URL of the Azuracast instance\n\nThis does NOT include the station. If a station is included it will be ignored. Use `station` field to specify station, if necessary","examples":["https://radio.mydomain.tld","http://localhost:80"]},"station":{"type":"string","description":"The specific station to monitor\n\nScrobbling will only occur if any of the monitor conditions are met AND the station is ONLINE.\n\nTo monitor multiple stations create a Source for each station.","examples":["my-station-1"]},"monitorWhenListeners":{"type":["boolean","number"],"description":"Only activate scrobble monitoring if station\n\n* `true` => has any current listeners\n* `number` => has EQUAL TO or MORE THAN X number of listeners"},"monitorWhenLive":{"type":"boolean","description":"Only activate scrobble monitoring if station has a live DJ/Streamer","default":true},"apiKey":{"type":"string","description":"API Key used to access data about private streams\n\nhttps://www.azuracast.com/docs/developers/apis/#api-authentication"}},"required":["url","station"],"title":"AzuracastData"},"KoitoSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/KoitoSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"configureAs":{"type":"string","enum":["source"],"description":"When used in `koito.config` this tells multi-scrobbler whether to use this data to configure a source or client.","default":"source","examples":["source"]},"type":{"type":"string","enum":["koito"]}},"required":["data","type"],"title":"KoitoSourceAIOConfig"},"KoitoSourceData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for the Koito server","examples":["http://192.168.0.100:4110"]},"token":{"type":"string","description":"User token for the user to scrobble for","examples":["pM195xPV98CDpk0QW47FIIOR8AKATAX5DblBF-Jq0t1MbbKL"]},"username":{"type":"string","description":"Username of the user to scrobble for"}},"required":["token","url","username"],"title":"KoitoSourceData"},"ClientAIOConfig":{"anyOf":[{"$ref":"#/definitions/MalojaClientAIOConfig"},{"$ref":"#/definitions/LastfmClientAIOConfig"},{"$ref":"#/definitions/ListenBrainzClientAIOConfig"},{"$ref":"#/definitions/KoitoClientAIOConfig"}],"title":"ClientAIOConfig"},"MalojaClientAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this client. Used with sources to restrict where scrobbles are sent.","examples":["MyConfig"]},"data":{"$ref":"#/definitions/MalojaClientData","description":"Specific data required to configure this client"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"options":{"$ref":"#/definitions/CommonClientOptions"},"configureAs":{"type":"string","enum":["client","source"],"description":"Should always be `client` when using Maloja as a client","default":"client","examples":["client"]},"type":{"type":"string","enum":["maloja"]}},"required":["data","name","type"],"title":"MalojaClientAIOConfig"},"MalojaClientData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for maloja server","examples":["http://localhost:42010"]},"apiKey":{"type":"string","description":"API Key for Maloja server","examples":["myApiKey"]}},"required":["apiKey","url"],"title":"MalojaClientData"},"CommonClientOptions":{"type":"object","properties":{"refreshEnabled":{"type":"boolean","description":"Try to get fresh scrobble history from client when tracks to be scrobbled are newer than the last scrobble found in client history","default":true,"examples":[true]},"refreshStaleAfter":{"type":"number","description":"Refresh scrobbled plays from upstream service if last refresh was at least X seconds ago\n\n**In most case this setting does NOT need to be changed.** The default value is sufficient for the majority of use-cases. Increasing this setting may increase upstream service load and slow down scrobbles.\n\nThis setting should only be changed in specific scenarios where MS is handling multiple \"relaying\" client-services (IE lfm -> lz -> lfm) and there is the potential for a client to be out of sync after more than a few seconds.","examples":[60],"default":60},"refreshMinInterval":{"type":"number","description":"Minimum time (milliseconds) required to pass before upstream scrobbles can be refreshed.\n\n**In most case this setting does NOT need to be changed.** This will always be equal to or smaller than `refreshStaleAfter`.","default":5000,"examples":[5000]},"refreshInitialCount":{"type":"number","description":"The number of tracks to retrieve on initial refresh (related to scrobbleBacklogCount). If not specified this is the maximum supported by the client in 1 API call."},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"checkExistingScrobbles":{"type":"boolean","description":"Check client for an existing scrobble at the same recorded time as the \"new\" track to be scrobbled. If an existing scrobble is found this track is not track scrobbled.","default":true,"examples":[true]},"verbose":{"type":"object","properties":{"match":{"$ref":"#/definitions/MatchLoggingOptions"}},"description":"Options used for increasing verbosity of logging in MS (used for debugging)"},"deadLetterRetries":{"type":"number","description":"Number of times MS should automatically retry scrobbles in dead letter queue","default":1,"examples":[1]},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"CommonClientOptions"},"LastfmClientAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this client. Used with sources to restrict where scrobbles are sent.","examples":["MyConfig"]},"data":{"$ref":"#/definitions/LastfmData","description":"Specific data required to configure this client"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"options":{"$ref":"#/definitions/LastfmClientOptions"},"configureAs":{"type":"string","enum":["client","source"],"description":"Should always be `client` when using LastFM as a client","default":"client","examples":["client"]},"type":{"type":"string","enum":["lastfm"]}},"required":["data","name","type"],"title":"LastfmClientAIOConfig"},"LastfmData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"apiKey":{"type":"string","description":"API Key generated from Last.fm account","examples":["787c921a2a2ab42320831aba0c8f2fc2"]},"secret":{"type":"string","description":"Secret generated from Last.fm account","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"session":{"type":"string","description":"Optional session id returned from a completed auth flow"},"redirectUri":{"type":"string","description":"Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.","default":"http://localhost:9078/lastfm/callback","examples":["http://localhost:9078/lastfm/callback"]}},"required":["apiKey","secret"],"title":"LastfmData"},"LastfmClientOptions":{"type":"object","properties":{"nowPlaying":{"anyOf":[{"type":"boolean"},{"type":"array","items":{"type":"string"}}],"description":"Configure if this Client should report Now Playing from Sources that can scrobble to it\n\n* `true` (default) => Report Now Playing from any eligible Source. \n * If multiple Sources are Playing then reported Play is based on alphabetical order of Source names\n* `false` => Do not report Now Playing\n* `string` list => list of Source `names` that should be allowed to report Now Playing. Order of list determine priority of Play to Report.","default":true},"refreshEnabled":{"type":"boolean","description":"Try to get fresh scrobble history from client when tracks to be scrobbled are newer than the last scrobble found in client history","default":true,"examples":[true]},"refreshStaleAfter":{"type":"number","description":"Refresh scrobbled plays from upstream service if last refresh was at least X seconds ago\n\n**In most case this setting does NOT need to be changed.** The default value is sufficient for the majority of use-cases. Increasing this setting may increase upstream service load and slow down scrobbles.\n\nThis setting should only be changed in specific scenarios where MS is handling multiple \"relaying\" client-services (IE lfm -> lz -> lfm) and there is the potential for a client to be out of sync after more than a few seconds.","examples":[60],"default":60},"refreshMinInterval":{"type":"number","description":"Minimum time (milliseconds) required to pass before upstream scrobbles can be refreshed.\n\n**In most case this setting does NOT need to be changed.** This will always be equal to or smaller than `refreshStaleAfter`.","default":5000,"examples":[5000]},"refreshInitialCount":{"type":"number","description":"The number of tracks to retrieve on initial refresh (related to scrobbleBacklogCount). If not specified this is the maximum supported by the client in 1 API call."},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"checkExistingScrobbles":{"type":"boolean","description":"Check client for an existing scrobble at the same recorded time as the \"new\" track to be scrobbled. If an existing scrobble is found this track is not track scrobbled.","default":true,"examples":[true]},"verbose":{"type":"object","properties":{"match":{"$ref":"#/definitions/MatchLoggingOptions"}},"description":"Options used for increasing verbosity of logging in MS (used for debugging)"},"deadLetterRetries":{"type":"number","description":"Number of times MS should automatically retry scrobbles in dead letter queue","default":1,"examples":[1]},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"LastfmClientOptions"},"ListenBrainzClientAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this client. Used with sources to restrict where scrobbles are sent.","examples":["MyConfig"]},"data":{"$ref":"#/definitions/ListenBrainzClientData","description":"Specific data required to configure this client"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"options":{"$ref":"#/definitions/CommonClientOptions"},"configureAs":{"type":"string","enum":["client","source"],"description":"Should always be `client` when using Listenbrainz as a client","default":"client","examples":["client"]},"type":{"type":"string","enum":["listenbrainz"]}},"required":["data","name","type"],"title":"ListenBrainzClientAIOConfig"},"ListenBrainzClientData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for the ListenBrainz server, if not using the default","examples":["https://api.listenbrainz.org/"],"default":"https://api.listenbrainz.org/"},"token":{"type":"string","description":"User token for the user to scrobble for","examples":["6794186bf-1157-4de6-80e5-uvb411f3ea2b"]},"username":{"type":"string","description":"Username of the user to scrobble for"}},"required":["token","username"],"title":"ListenBrainzClientData"},"KoitoClientAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this client. Used with sources to restrict where scrobbles are sent.","examples":["MyConfig"]},"data":{"$ref":"#/definitions/KoitoClientData","description":"Specific data required to configure this client"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"options":{"$ref":"#/definitions/CommonClientOptions"},"configureAs":{"type":"string","enum":["client","source"],"description":"Should always be `client` when using Koito as a client","default":"client","examples":["client"]},"type":{"type":"string","enum":["koito"]}},"required":["data","name","type"],"title":"KoitoClientAIOConfig"},"KoitoClientData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for the Koito server","examples":["http://192.168.0.100:4110"]},"token":{"type":"string","description":"User token for the user to scrobble for","examples":["pM195xPV98CDpk0QW47FIIOR8AKATAX5DblBF-Jq0t1MbbKL"]},"username":{"type":"string","description":"Username of the user to scrobble for"}},"required":["token","url","username"],"title":"KoitoClientData"},"WebhookConfig":{"anyOf":[{"$ref":"#/definitions/GotifyConfig"},{"$ref":"#/definitions/NtfyConfig"},{"$ref":"#/definitions/AppriseConfig"}],"title":"WebhookConfig"},"GotifyConfig":{"type":"object","properties":{"type":{"type":"string","enum":["gotify","ntfy","apprise"],"description":"Webhook type. Valid values are:\n\n* gotify\n* ntfy","examples":["gotify"]},"name":{"type":"string","description":"A friendly name used to identify webhook config in logs"},"url":{"type":"string","description":"The URL of the Gotify server. Same URL that would be used to reach the Gotify UI","examples":["http://192.168.0.100:8078"]},"token":{"type":"string","description":"The token created for this Application in Gotify","examples":["AQZI58fA.rfSZbm"]},"priorities":{"$ref":"#/definitions/PrioritiesConfig","description":"Priority of messages\n\n* Info -> 5\n* Warn -> 7\n* Error -> 10"}},"required":["token","type","url"],"title":"GotifyConfig"},"PrioritiesConfig":{"type":"object","properties":{"info":{"type":"number","examples":[5]},"warn":{"type":"number","examples":[7]},"error":{"type":"number","examples":[10]}},"required":["info","warn","error"],"title":"PrioritiesConfig"},"NtfyConfig":{"type":"object","properties":{"type":{"type":"string","enum":["gotify","ntfy","apprise"],"description":"Webhook type. Valid values are:\n\n* gotify\n* ntfy","examples":["gotify"]},"name":{"type":"string","description":"A friendly name used to identify webhook config in logs"},"url":{"type":"string","description":"The URL of the Ntfy server","examples":["http://192.168.0.100:8078"]},"topic":{"type":"string","description":"The topic mutli-scrobbler should POST to"},"username":{"type":"string","description":"Required if topic is protected"},"password":{"type":"string","description":"Required if topic is protected"},"token":{"type":"string","description":"Use instead of username/password, required if topic is protected"},"priorities":{"$ref":"#/definitions/PrioritiesConfig","description":"Priority of messages\n\n* Info -> 3\n* Warn -> 4\n* Error -> 5"}},"required":["topic","type","url"],"title":"NtfyConfig"},"AppriseConfig":{"type":"object","properties":{"type":{"type":"string","enum":["gotify","ntfy","apprise"],"description":"Webhook type. Valid values are:\n\n* gotify\n* ntfy","examples":["gotify"]},"name":{"type":"string","description":"A friendly name used to identify webhook config in logs"},"host":{"type":"string","description":"The URL of the apprise-api server","examples":["http://192.168.0.100:8078"]},"urls":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"If using [Stateless Endpoints](https://github.com/caronc/apprise-api?tab=readme-ov-file#stateless-solution) the Apprise config URL(s) to send"},"keys":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"If using [Persistent Store Endpoints](https://github.com/caronc/apprise-api?tab=readme-ov-file#persistent-storage-solution) the Configuration ID(s) to send to\n\nNote: If multiple keys are defined then MS will attempt to POST to each one individually"},"tags":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Optional [tag(s)](https://github.com/caronc/apprise-api?tab=readme-ov-file#tagging) to send in the notification payload"}},"required":["host","type"],"title":"AppriseConfig"},"LogOptions":{"type":"object","properties":{"level":{"$ref":"#/definitions/LogLevel","description":"Specify the minimum log level for all log outputs without their own level specified.\n\nDefaults to env `LOG_LEVEL` or `info` if not specified.","default":"info"},"file":{"anyOf":[{"$ref":"#/definitions/LogLevel"},{"type":"boolean","const":false},{"$ref":"#/definitions/FileLogOptions"}],"description":"Specify the minimum log level to output to rotating files or file output options. If `false` no log files will be created."},"console":{"$ref":"#/definitions/LogLevel","description":"Specify the minimum log level streamed to the console (or docker container)"}},"description":"Configure log levels and file options for an AppLogger.\n\n```ts\nconst infoLogger = loggerApp({\n level: 'info' // console and file will log any levels `info` and above\n});\n\nconst logger = loggerApp({\n console: 'debug', // console will log `debug` and higher\n file: 'warn' // file will log `warn` and higher\n});\n\nconst fileLogger = loggerRollingApp({\n console: 'debug', // console will log `debug` and higher\n file: {\n level: 'warn', // file will log `warn` and higher\n path: '/my/cool/path/output.log', // optionally, output to log file at this path\n frequency: 'hourly', // optionally, rotate hourly\n }\n});\n```","title":"LogOptions"},"CacheConfigOptions":{"type":"object","properties":{"metadata":{"$ref":"#/definitions/CacheMetadataConfig"},"scrobble":{"$ref":"#/definitions/CacheScrobbleConfig"}},"title":"CacheConfigOptions"},"CacheMetadataConfig":{"$ref":"#/definitions/CacheConfig%3CCacheMetadaProvider%3E","title":"CacheMetadataConfig"},"CacheConfig<CacheMetadaProvider>":{"type":"object","properties":{"provider":{"$ref":"#/definitions/CacheMetadaProvider"},"connection":{"type":"string"}},"required":["provider"],"title":"CacheConfig<CacheMetadaProvider>"},"CacheMetadaProvider":{"$ref":"#/definitions/CacheProvider","title":"CacheMetadaProvider"},"CacheProvider":{"type":["string","boolean"],"enum":["memory","valkey","file",false],"title":"CacheProvider"},"CacheScrobbleConfig":{"$ref":"#/definitions/CacheConfig%3CCacheScrobbleProvider%3E","title":"CacheScrobbleConfig"},"CacheConfig<CacheScrobbleProvider>":{"type":"object","properties":{"provider":{"$ref":"#/definitions/CacheScrobbleProvider"},"connection":{"type":"string"}},"required":["provider"],"title":"CacheConfig<CacheScrobbleProvider>"},"CacheScrobbleProvider":{"$ref":"#/definitions/CacheProvider","title":"CacheScrobbleProvider"}}} \ No newline at end of file diff --git a/docsite/static/client.json b/docsite/static/client.json index 6f07a98e..61de23f1 100644 --- a/docsite/static/client.json +++ b/docsite/static/client.json @@ -1 +1 @@ -{"$schema":"http://json-schema.org/draft-07/schema#","anyOf":[{"$ref":"#/definitions/MalojaClientConfig"},{"$ref":"#/definitions/LastfmClientConfig"},{"$ref":"#/definitions/ListenBrainzClientConfig"}],"definitions":{"MalojaClientConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this client. Used with sources to restrict where scrobbles are sent.","examples":["MyConfig"]},"data":{"$ref":"#/definitions/MalojaClientData","description":"Specific data required to configure this client"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"options":{"$ref":"#/definitions/CommonClientOptions"}},"required":["data","name"],"title":"MalojaClientConfig"},"MalojaClientData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for maloja server","examples":["http://localhost:42010"]},"apiKey":{"type":"string","description":"API Key for Maloja server","examples":["myApiKey"]}},"required":["url","apiKey"],"title":"MalojaClientData"},"CommonClientOptions":{"type":"object","properties":{"refreshEnabled":{"type":"boolean","description":"Try to get fresh scrobble history from client when tracks to be scrobbled are newer than the last scrobble found in client history","default":true,"examples":[true]},"refreshStaleAfter":{"type":"number","description":"Refresh scrobbled plays from upstream service if last refresh was at least X seconds ago\n\n**In most case this setting does NOT need to be changed.** The default value is sufficient for the majority of use-cases. Increasing this setting may increase upstream service load and slow down scrobbles.\n\nThis setting should only be changed in specific scenarios where MS is handling multiple \"relaying\" client-services (IE lfm -> lz -> lfm) and there is the potential for a client to be out of sync after more than a few seconds.","examples":[60],"default":60},"refreshMinInterval":{"type":"number","description":"Minimum time (milliseconds) required to pass before upstream scrobbles can be refreshed.\n\n**In most case this setting does NOT need to be changed.** This will always be equal to or smaller than `refreshStaleAfter`.","default":5000,"examples":[5000]},"refreshInitialCount":{"type":"number","description":"The number of tracks to retrieve on initial refresh (related to scrobbleBacklogCount). If not specified this is the maximum supported by the client in 1 API call."},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"checkExistingScrobbles":{"type":"boolean","description":"Check client for an existing scrobble at the same recorded time as the \"new\" track to be scrobbled. If an existing scrobble is found this track is not track scrobbled.","default":true,"examples":[true]},"verbose":{"type":"object","properties":{"match":{"$ref":"#/definitions/MatchLoggingOptions"}},"description":"Options used for increasing verbosity of logging in MS (used for debugging)"},"deadLetterRetries":{"type":"number","description":"Number of times MS should automatically retry scrobbles in dead letter queue","default":1,"examples":[1]},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"CommonClientOptions"},"MatchLoggingOptions":{"type":"object","properties":{"onNoMatch":{"type":"boolean","description":"Log to DEBUG when a new track does NOT match an existing scrobble","default":false,"examples":[false]},"onMatch":{"type":"boolean","description":"Log to DEBUG when a new track DOES match an existing scrobble","default":false,"examples":[false]},"confidenceBreakdown":{"type":"boolean","description":"Include confidence breakdowns in track match logging, if applicable","default":false,"examples":[false]}},"description":"Scrobble matching (between new source track and existing client scrobbles) logging options. Used for debugging.","title":"MatchLoggingOptions"},"PlayTransformOptions":{"type":"object","properties":{"log":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["all"]}]},"preCompare":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"},"compare":{"type":"object","properties":{"candidate":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"},"existing":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"}}},"postCompare":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"}},"title":"PlayTransformOptions"},"PlayTransformPartsConfig<SearchAndReplaceTerm>":{"anyOf":[{"$ref":"#/definitions/PlayTransformPartsArray%3CSearchAndReplaceTerm%3E"},{"$ref":"#/definitions/PlayTransformParts%3CSearchAndReplaceTerm%3E"}],"title":"PlayTransformPartsConfig<SearchAndReplaceTerm>"},"PlayTransformPartsArray<SearchAndReplaceTerm>":{"type":"array","items":{"$ref":"#/definitions/PlayTransformParts%3CSearchAndReplaceTerm%3E"},"title":"PlayTransformPartsArray<SearchAndReplaceTerm>"},"PlayTransformParts<SearchAndReplaceTerm>":{"type":"object","properties":{"when":{"$ref":"#/definitions/WhenConditionsConfig"},"title":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}},"artists":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}},"album":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}}},"title":"PlayTransformParts<SearchAndReplaceTerm>"},"WhenConditionsConfig":{"$ref":"#/definitions/WhenConditions%3Cstring%3E","title":"WhenConditionsConfig"},"WhenConditions<string>":{"type":"array","items":{"$ref":"#/definitions/WhenParts%3Cstring%3E"},"title":"WhenConditions<string>"},"WhenParts<string>":{"$ref":"#/definitions/PlayTransformPartsAtomic%3Cstring%3E","title":"WhenParts<string>"},"PlayTransformPartsAtomic<string>":{"type":"object","properties":{"title":{"type":"string"},"artists":{"type":"string"},"album":{"type":"string"}},"title":"PlayTransformPartsAtomic<string>"},"SearchAndReplaceTerm":{"anyOf":[{"type":"string"},{"$ref":"#/definitions/ConditionalSearchAndReplaceTerm"}],"title":"SearchAndReplaceTerm"},"ConditionalSearchAndReplaceTerm":{"type":"object","properties":{"when":{"$ref":"#/definitions/WhenConditionsConfig"},"search":{},"replace":{}},"required":["search","replace"],"title":"ConditionalSearchAndReplaceTerm"},"LastfmClientConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this client. Used with sources to restrict where scrobbles are sent.","examples":["MyConfig"]},"data":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"apiKey":{"type":"string","description":"API Key generated from Last.fm account","examples":["787c921a2a2ab42320831aba0c8f2fc2"]},"secret":{"type":"string","description":"Secret generated from Last.fm account","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"session":{"type":"string","description":"Optional session id returned from a completed auth flow"},"redirectUri":{"type":"string","description":"Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.","default":"http://localhost:9078/lastfm/callback","examples":["http://localhost:9078/lastfm/callback"]}},"required":["apiKey","secret"],"description":"Specific data required to configure this client"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"options":{"$ref":"#/definitions/CommonClientOptions"},"configureAs":{"type":"string","enum":["client","source"],"description":"Should always be `client` when using LastFM as a client","default":"client","examples":["client"]}},"required":["data","name"],"title":"LastfmClientConfig"},"ListenBrainzClientConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this client. Used with sources to restrict where scrobbles are sent.","examples":["MyConfig"]},"data":{"$ref":"#/definitions/ListenBrainzClientData","description":"Specific data required to configure this client"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"options":{"$ref":"#/definitions/CommonClientOptions"},"configureAs":{"type":"string","enum":["client","source"],"description":"Should always be `client` when using Listenbrainz as a client","default":"client","examples":["client"]}},"required":["data","name"],"title":"ListenBrainzClientConfig"},"ListenBrainzClientData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for the ListenBrainz server, if not using the default","examples":["https://api.listenbrainz.org/"],"default":"https://api.listenbrainz.org/"},"token":{"type":"string","description":"User token for the user to scrobble for","examples":["6794186bf-1157-4de6-80e5-uvb411f3ea2b"]},"username":{"type":"string","description":"Username of the user to scrobble for"}},"required":["token","username"],"title":"ListenBrainzClientData"}}} \ No newline at end of file +{"$schema":"http://json-schema.org/draft-07/schema#","anyOf":[{"$ref":"#/definitions/MalojaClientConfig"},{"$ref":"#/definitions/LastfmClientConfig"},{"$ref":"#/definitions/ListenBrainzClientConfig"},{"$ref":"#/definitions/KoitoClientConfig"}],"definitions":{"MalojaClientConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this client. Used with sources to restrict where scrobbles are sent.","examples":["MyConfig"]},"data":{"$ref":"#/definitions/MalojaClientData","description":"Specific data required to configure this client"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"options":{"$ref":"#/definitions/CommonClientOptions"},"configureAs":{"type":"string","enum":["client","source"],"description":"Should always be `client` when using Maloja as a client","default":"client","examples":["client"]}},"required":["data","name"],"title":"MalojaClientConfig"},"MalojaClientData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for maloja server","examples":["http://localhost:42010"]},"apiKey":{"type":"string","description":"API Key for Maloja server","examples":["myApiKey"]}},"required":["apiKey","url"],"title":"MalojaClientData"},"CommonClientOptions":{"type":"object","properties":{"refreshEnabled":{"type":"boolean","description":"Try to get fresh scrobble history from client when tracks to be scrobbled are newer than the last scrobble found in client history","default":true,"examples":[true]},"refreshStaleAfter":{"type":"number","description":"Refresh scrobbled plays from upstream service if last refresh was at least X seconds ago\n\n**In most case this setting does NOT need to be changed.** The default value is sufficient for the majority of use-cases. Increasing this setting may increase upstream service load and slow down scrobbles.\n\nThis setting should only be changed in specific scenarios where MS is handling multiple \"relaying\" client-services (IE lfm -> lz -> lfm) and there is the potential for a client to be out of sync after more than a few seconds.","examples":[60],"default":60},"refreshMinInterval":{"type":"number","description":"Minimum time (milliseconds) required to pass before upstream scrobbles can be refreshed.\n\n**In most case this setting does NOT need to be changed.** This will always be equal to or smaller than `refreshStaleAfter`.","default":5000,"examples":[5000]},"refreshInitialCount":{"type":"number","description":"The number of tracks to retrieve on initial refresh (related to scrobbleBacklogCount). If not specified this is the maximum supported by the client in 1 API call."},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"checkExistingScrobbles":{"type":"boolean","description":"Check client for an existing scrobble at the same recorded time as the \"new\" track to be scrobbled. If an existing scrobble is found this track is not track scrobbled.","default":true,"examples":[true]},"verbose":{"type":"object","properties":{"match":{"$ref":"#/definitions/MatchLoggingOptions"}},"description":"Options used for increasing verbosity of logging in MS (used for debugging)"},"deadLetterRetries":{"type":"number","description":"Number of times MS should automatically retry scrobbles in dead letter queue","default":1,"examples":[1]},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"CommonClientOptions"},"MatchLoggingOptions":{"type":"object","properties":{"onNoMatch":{"type":"boolean","description":"Log to DEBUG when a new track does NOT match an existing scrobble","default":false,"examples":[false]},"onMatch":{"type":"boolean","description":"Log to DEBUG when a new track DOES match an existing scrobble","default":false,"examples":[false]},"confidenceBreakdown":{"type":"boolean","description":"Include confidence breakdowns in track match logging, if applicable","default":false,"examples":[false]}},"description":"Scrobble matching (between new source track and existing client scrobbles) logging options. Used for debugging.","title":"MatchLoggingOptions"},"PlayTransformOptions":{"type":"object","properties":{"log":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["all"]}]},"preCompare":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"},"compare":{"type":"object","properties":{"candidate":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"},"existing":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"}}},"postCompare":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"}},"title":"PlayTransformOptions"},"PlayTransformPartsConfig<SearchAndReplaceTerm>":{"anyOf":[{"$ref":"#/definitions/PlayTransformPartsArray%3CSearchAndReplaceTerm%3E"},{"$ref":"#/definitions/PlayTransformParts%3CSearchAndReplaceTerm%3E"}],"title":"PlayTransformPartsConfig<SearchAndReplaceTerm>"},"PlayTransformPartsArray<SearchAndReplaceTerm>":{"type":"array","items":{"$ref":"#/definitions/PlayTransformParts%3CSearchAndReplaceTerm%3E"},"title":"PlayTransformPartsArray<SearchAndReplaceTerm>"},"PlayTransformParts<SearchAndReplaceTerm>":{"type":"object","properties":{"when":{"$ref":"#/definitions/WhenConditionsConfig"},"title":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}},"artists":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}},"album":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}}},"title":"PlayTransformParts<SearchAndReplaceTerm>"},"WhenConditionsConfig":{"$ref":"#/definitions/WhenConditions%3Cstring%3E","title":"WhenConditionsConfig"},"WhenConditions<string>":{"type":"array","items":{"$ref":"#/definitions/WhenParts%3Cstring%3E"},"title":"WhenConditions<string>"},"WhenParts<string>":{"$ref":"#/definitions/PlayTransformPartsAtomic%3Cstring%3E","title":"WhenParts<string>"},"PlayTransformPartsAtomic<string>":{"type":"object","properties":{"title":{"type":"string"},"artists":{"type":"string"},"album":{"type":"string"}},"title":"PlayTransformPartsAtomic<string>"},"SearchAndReplaceTerm":{"anyOf":[{"type":"string"},{"$ref":"#/definitions/ConditionalSearchAndReplaceTerm"}],"title":"SearchAndReplaceTerm"},"ConditionalSearchAndReplaceTerm":{"type":"object","properties":{"when":{"$ref":"#/definitions/WhenConditionsConfig"},"search":{},"replace":{}},"required":["search","replace"],"title":"ConditionalSearchAndReplaceTerm"},"LastfmClientConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this client. Used with sources to restrict where scrobbles are sent.","examples":["MyConfig"]},"data":{"$ref":"#/definitions/LastfmData","description":"Specific data required to configure this client"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"options":{"$ref":"#/definitions/LastfmClientOptions"},"configureAs":{"type":"string","enum":["client","source"],"description":"Should always be `client` when using LastFM as a client","default":"client","examples":["client"]}},"required":["data","name"],"title":"LastfmClientConfig"},"LastfmData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"apiKey":{"type":"string","description":"API Key generated from Last.fm account","examples":["787c921a2a2ab42320831aba0c8f2fc2"]},"secret":{"type":"string","description":"Secret generated from Last.fm account","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"session":{"type":"string","description":"Optional session id returned from a completed auth flow"},"redirectUri":{"type":"string","description":"Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.","default":"http://localhost:9078/lastfm/callback","examples":["http://localhost:9078/lastfm/callback"]}},"required":["apiKey","secret"],"title":"LastfmData"},"LastfmClientOptions":{"type":"object","properties":{"nowPlaying":{"anyOf":[{"type":"boolean"},{"type":"array","items":{"type":"string"}}],"description":"Configure if this Client should report Now Playing from Sources that can scrobble to it\n\n* `true` (default) => Report Now Playing from any eligible Source. \n * If multiple Sources are Playing then reported Play is based on alphabetical order of Source names\n* `false` => Do not report Now Playing\n* `string` list => list of Source `names` that should be allowed to report Now Playing. Order of list determine priority of Play to Report.","default":true},"refreshEnabled":{"type":"boolean","description":"Try to get fresh scrobble history from client when tracks to be scrobbled are newer than the last scrobble found in client history","default":true,"examples":[true]},"refreshStaleAfter":{"type":"number","description":"Refresh scrobbled plays from upstream service if last refresh was at least X seconds ago\n\n**In most case this setting does NOT need to be changed.** The default value is sufficient for the majority of use-cases. Increasing this setting may increase upstream service load and slow down scrobbles.\n\nThis setting should only be changed in specific scenarios where MS is handling multiple \"relaying\" client-services (IE lfm -> lz -> lfm) and there is the potential for a client to be out of sync after more than a few seconds.","examples":[60],"default":60},"refreshMinInterval":{"type":"number","description":"Minimum time (milliseconds) required to pass before upstream scrobbles can be refreshed.\n\n**In most case this setting does NOT need to be changed.** This will always be equal to or smaller than `refreshStaleAfter`.","default":5000,"examples":[5000]},"refreshInitialCount":{"type":"number","description":"The number of tracks to retrieve on initial refresh (related to scrobbleBacklogCount). If not specified this is the maximum supported by the client in 1 API call."},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"checkExistingScrobbles":{"type":"boolean","description":"Check client for an existing scrobble at the same recorded time as the \"new\" track to be scrobbled. If an existing scrobble is found this track is not track scrobbled.","default":true,"examples":[true]},"verbose":{"type":"object","properties":{"match":{"$ref":"#/definitions/MatchLoggingOptions"}},"description":"Options used for increasing verbosity of logging in MS (used for debugging)"},"deadLetterRetries":{"type":"number","description":"Number of times MS should automatically retry scrobbles in dead letter queue","default":1,"examples":[1]},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"LastfmClientOptions"},"ListenBrainzClientConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this client. Used with sources to restrict where scrobbles are sent.","examples":["MyConfig"]},"data":{"$ref":"#/definitions/ListenBrainzClientData","description":"Specific data required to configure this client"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"options":{"$ref":"#/definitions/CommonClientOptions"},"configureAs":{"type":"string","enum":["client","source"],"description":"Should always be `client` when using Listenbrainz as a client","default":"client","examples":["client"]}},"required":["data","name"],"title":"ListenBrainzClientConfig"},"ListenBrainzClientData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for the ListenBrainz server, if not using the default","examples":["https://api.listenbrainz.org/"],"default":"https://api.listenbrainz.org/"},"token":{"type":"string","description":"User token for the user to scrobble for","examples":["6794186bf-1157-4de6-80e5-uvb411f3ea2b"]},"username":{"type":"string","description":"Username of the user to scrobble for"}},"required":["token","username"],"title":"ListenBrainzClientData"},"KoitoClientConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this client. Used with sources to restrict where scrobbles are sent.","examples":["MyConfig"]},"data":{"$ref":"#/definitions/KoitoClientData","description":"Specific data required to configure this client"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"options":{"$ref":"#/definitions/CommonClientOptions"},"configureAs":{"type":"string","enum":["client","source"],"description":"Should always be `client` when using Koito as a client","default":"client","examples":["client"]}},"required":["data","name"],"title":"KoitoClientConfig"},"KoitoClientData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for the Koito server","examples":["http://192.168.0.100:4110"]},"token":{"type":"string","description":"User token for the user to scrobble for","examples":["pM195xPV98CDpk0QW47FIIOR8AKATAX5DblBF-Jq0t1MbbKL"]},"username":{"type":"string","description":"Username of the user to scrobble for"}},"required":["token","url","username"],"title":"KoitoClientData"}}} \ No newline at end of file diff --git a/docsite/static/source.json b/docsite/static/source.json index c07e706f..08d89836 100644 --- a/docsite/static/source.json +++ b/docsite/static/source.json @@ -1 +1 @@ -{"$schema":"http://json-schema.org/draft-07/schema#","anyOf":[{"$ref":"#/definitions/SpotifySourceConfig"},{"$ref":"#/definitions/PlexSourceConfig"},{"$ref":"#/definitions/PlexApiSourceConfig"},{"$ref":"#/definitions/TautulliSourceConfig"},{"$ref":"#/definitions/DeezerSourceConfig"},{"$ref":"#/definitions/ListenbrainzEndpointSourceConfig"},{"$ref":"#/definitions/LastFMEndpointSourceConfig"},{"$ref":"#/definitions/SubSonicSourceConfig"},{"$ref":"#/definitions/JellySourceConfig"},{"$ref":"#/definitions/JellyApiSourceConfig"},{"$ref":"#/definitions/LastfmSourceConfig"},{"$ref":"#/definitions/YTMusicSourceConfig"},{"$ref":"#/definitions/MPRISSourceConfig"},{"$ref":"#/definitions/MopidySourceConfig"},{"$ref":"#/definitions/ListenBrainzSourceConfig"},{"$ref":"#/definitions/JRiverSourceConfig"},{"$ref":"#/definitions/KodiSourceConfig"},{"$ref":"#/definitions/WebScrobblerSourceConfig"},{"$ref":"#/definitions/ChromecastSourceConfig"},{"$ref":"#/definitions/MusikcubeSourceConfig"},{"$ref":"#/definitions/MusicCastSourceConfig"},{"$ref":"#/definitions/MPDSourceConfig"},{"$ref":"#/definitions/VLCSourceConfig"},{"$ref":"#/definitions/IcecastSourceConfig"},{"$ref":"#/definitions/AzuracastSourceConfig"}],"definitions":{"SpotifySourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/SpotifySourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"SpotifySourceConfig"},"SpotifySourceData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)\n\nIt is unlikely you should need to change this unless you scrobble many very short tracks often\n\nReading:\n* https://developer.spotify.com/documentation/web-api/guides/rate-limits/\n* https://medium.com/mendix/limiting-your-amount-of-calls-in-mendix-most-of-the-time-rest-835dde55b10e\n * Rate limit may ~180 req/min\n* https://community.spotify.com/t5/Spotify-for-Developers/Web-API-ratelimit/m-p/5503150/highlight/true#M7930\n * Informally indicated as 20 req/sec? Probably for burstiness","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"clientId":{"type":"string","description":"spotify client id","examples":["787c921a2a2ab42320831aba0c8f2fc2"]},"clientSecret":{"type":"string","description":"spotify client secret","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"redirectUri":{"type":"string","description":"spotify redirect URI -- required only if not the default shown here. URI must end in \"callback\"","default":"http://localhost:9078/callback","examples":["http://localhost:9078/callback"]}},"required":["clientId","clientSecret"],"title":"SpotifySourceData"},"CommonSourceOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"CommonSourceOptions"},"LogLevel":{"type":"string","enum":["silent","fatal","error","warn","info","log","verbose","debug"],"description":"Names of log levels that can be invoked on the logger\n\nFrom lowest to highest:\n\n* `debug`\n* `verbose`\n* `log`\n* `info`\n* `warn`\n* `error`\n* `fatal`\n* `silent` (will never output anything)\n\nWhen used in `LogOptions` specifies the **minimum** level the output should log at.","title":"LogLevel"},"FileLogOptions":{"type":"object","properties":{"timestamp":{"type":"string","enum":["unix","iso","auto"],"description":"For rolling log files\n\nWhen\n* value passed to rolling destination is a string (`path` from LogOptions is a string) and\n* `frequency` is defined\n\nThis determines the format of the datetime inserted into the log file name:\n\n* `unix` - unix epoch timestamp in milliseconds\n* `iso` - Full [ISO8601](https://en.wikipedia.org/wiki/ISO_8601) datetime IE '2024-03-07T20:11:34Z'\n* `auto`\n * When frequency is `daily` only inserts date IE YYYY-MM-DD\n * Otherwise inserts full ISO8601 datetime","default":"auto"},"size":{"type":["number","string"],"description":"The maximum size of a given rolling log file.\n\nCan be combined with frequency. Use k, m and g to express values in KB, MB or GB.\n\nNumerical values will be considered as MB.","default":"10MB"},"frequency":{"anyOf":[{"type":"string","enum":["daily"]},{"type":"string","enum":["hourly"]},{"type":"number"}],"description":"The amount of time a given rolling log file is used. Can be combined with size.\n\nUse `daily` or `hourly` to rotate file every day (or every hour). Existing file within the current day (or hour) will be re-used.\n\nNumerical values will be considered as a number of milliseconds. Using a numerical value will always create a new file upon startup.","default":"daily"},"path":{"anyOf":[{"type":"string"},{"$comment":"() => string"}],"description":"The path and filename to use for log files.\n\nIf using rolling files the filename will be appended with `.N` (a number) BEFORE the extension based on rolling status.\n\nMay also be specified using env LOG_PATH or a function that returns a string.\n\nIf path is relative the absolute path will be derived from `logBaseDir` (in `LoggerAppExtras`) which defaults to CWD","default":"./logs/app.log"},"level":{"anyOf":[{"$ref":"#/definitions/LogLevel"},{"type":"boolean","const":false}],"description":"Specify the minimum log level to output to rotating files. If `false` no log files will be created."}},"title":"FileLogOptions"},"ScrobbleThresholds":{"type":"object","properties":{"duration":{"type":["number","null"],"description":"The number of seconds a track has been listened to before it should be considered scrobbled.\n\nSet to null to disable.","default":240,"examples":[240]},"percent":{"type":["number","null"],"description":"The percentage (as an integer) of a track that should have been seen played before it should be scrobbled. Only used if the Source provides information about how long the track is.\n\nSet to null to disable.\n\nNOTE: This should be used with care when the Source is a \"polling\" type (has an 'interval' property). If the track is short and the interval is too high MS may ignore the track if percentage is high because it had not \"seen\" the track for long enough from first discovery, even if you have been playing the track for longer.","default":50,"examples":[50]}},"title":"ScrobbleThresholds"},"PlayTransformOptions":{"type":"object","properties":{"log":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["all"]}]},"preCompare":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"},"compare":{"type":"object","properties":{"candidate":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"},"existing":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"}}},"postCompare":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"}},"title":"PlayTransformOptions"},"PlayTransformPartsConfig<SearchAndReplaceTerm>":{"anyOf":[{"$ref":"#/definitions/PlayTransformPartsArray%3CSearchAndReplaceTerm%3E"},{"$ref":"#/definitions/PlayTransformParts%3CSearchAndReplaceTerm%3E"}],"title":"PlayTransformPartsConfig<SearchAndReplaceTerm>"},"PlayTransformPartsArray<SearchAndReplaceTerm>":{"type":"array","items":{"$ref":"#/definitions/PlayTransformParts%3CSearchAndReplaceTerm%3E"},"title":"PlayTransformPartsArray<SearchAndReplaceTerm>"},"PlayTransformParts<SearchAndReplaceTerm>":{"type":"object","properties":{"when":{"$ref":"#/definitions/WhenConditionsConfig"},"title":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}},"artists":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}},"album":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}}},"title":"PlayTransformParts<SearchAndReplaceTerm>"},"WhenConditionsConfig":{"$ref":"#/definitions/WhenConditions%3Cstring%3E","title":"WhenConditionsConfig"},"WhenConditions<string>":{"type":"array","items":{"$ref":"#/definitions/WhenParts%3Cstring%3E"},"title":"WhenConditions<string>"},"WhenParts<string>":{"$ref":"#/definitions/PlayTransformPartsAtomic%3Cstring%3E","title":"WhenParts<string>"},"PlayTransformPartsAtomic<string>":{"type":"object","properties":{"title":{"type":"string"},"artists":{"type":"string"},"album":{"type":"string"}},"title":"PlayTransformPartsAtomic<string>"},"SearchAndReplaceTerm":{"anyOf":[{"type":"string"},{"$ref":"#/definitions/ConditionalSearchAndReplaceTerm"}],"title":"SearchAndReplaceTerm"},"ConditionalSearchAndReplaceTerm":{"type":"object","properties":{"when":{"$ref":"#/definitions/WhenConditionsConfig"},"search":{},"replace":{}},"required":["search","replace"],"title":"ConditionalSearchAndReplaceTerm"},"PlexSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/PlexSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"PlexSourceConfig"},"PlexSourceData":{"type":"object","properties":{"user":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled","examples":[["MyUser1","MyUser2"]]},"libraries":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of libraries to scrobble tracks from\n\nIf none are provided tracks from all libraries will be scrobbled","examples":[["Audio","Music"]]},"servers":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled","examples":[["MyServerName"]]}},"title":"PlexSourceData"},"PlexApiSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/PlexApiData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/PlexApiOptions"}},"required":["data","options"],"title":"PlexApiSourceConfig"},"PlexApiData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"token":{"type":"string"},"url":{"type":"string","description":"http(s)://HOST:PORT of the Plex server to connect to"},"usersAllow":{"anyOf":[{"type":"string"},{"type":"boolean","enum":[true]},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble for specific users (case-insensitive)\n\nIf `true` MS will scrobble activity from all users"},"usersBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble for these users (case-insensitive)"},"devicesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if device or application name contains strings from this list (case-insensitive)"},"devicesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if device or application name contains strings from this list (case-insensitive)"},"librariesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if library name contains string from this list (case-insensitive)"},"librariesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if library name contains strings from this list (case-insensitive)"}},"required":["url"],"title":"PlexApiData"},"PlexApiOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"PlexApiOptions"},"TautulliSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/PlexSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"TautulliSourceConfig"},"DeezerSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/DeezerData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"DeezerSourceConfig"},"DeezerData":{"type":"object","properties":{"clientId":{"type":"string","description":"deezer client id","examples":["a89cba1569901a0671d5a9875fed4be1"]},"clientSecret":{"type":"string","description":"deezer client secret","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"redirectUri":{"type":"string","description":"deezer redirect URI -- required only if not the default shown here. URI must end in \"callback\"","default":"http://localhost:9078/deezer/callback","examples":["http://localhost:9078/deezer/callback"]},"interval":{"type":"number","description":"optional, how long to wait before calling spotify for new tracks (in seconds)","default":60,"examples":[60]}},"required":["clientId","clientSecret"],"title":"DeezerData"},"ListenbrainzEndpointSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/ListenbrainzEndpointData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"title":"ListenbrainzEndpointSourceConfig"},"ListenbrainzEndpointData":{"type":"object","properties":{"slug":{"type":["string","null"],"description":"The URL ending that should be used to identify scrobbles for this source\n\nIf you are using multiple Listenbrainz endpoint sources (scrobbles for many users) you can use a slug to match Sources with individual users/origins\n\nExample:\n\n* slug: 'usera' => API URL: http://localhost:9078/api/listenbrainz/usera\n* slug: 'originb' => API URL: http://localhost:9078/api/listenbrainz/originb\n\nIf no slug is found from an extension's incoming webhook event the first Listenbrainz source without a slug will be used"},"token":{"type":["string","null"],"description":"If an LZ submission request contains this token in the Authorization Header it will be used to match the submission with this Source\n\nSee: https://listenbrainz.readthedocs.io/en/latest/users/api/index.html#add-the-user-token-to-your-requests"}},"title":"ListenbrainzEndpointData"},"LastFMEndpointSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/LastFMEndpointData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"title":"LastFMEndpointSourceConfig"},"LastFMEndpointData":{"type":"object","properties":{"slug":{"type":["string","null"],"description":"The URL ending that should be used to identify scrobbles for this source\n\nIf you are using multiple Last.fm endpoint sources (scrobbles for many users) you can use a slug to match Sources with individual users/origins\n\nExample:\n\n* slug: 'usera' => API URL: http://localhost:9078/api/lastfm/usera\n* slug: 'originb' => API URL: http://localhost:9078/api/lastfm/originb\n\nIf no slug is found from an extension's incoming webhook event the first Last.fm source without a slug will be used"}},"title":"LastFMEndpointData"},"SubSonicSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/SubsonicData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"SubSonicSourceConfig"},"SubsonicData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the subsonic media server to query","examples":["http://airsonic.local"]},"user":{"type":"string","description":"Username to login to the server with","examples":[["MyUser"]]},"password":{"type":"string","description":"Password for the user to login to the server with","examples":["MyPassword"]},"ignoreTlsErrors":{"type":"boolean","description":"If your subsonic server is using self-signed certs you may need to disable TLS errors in order to get a connection\n\nWARNING: This should be used with caution as your traffic may not be encrypted.","default":false},"legacyAuthentication":{"type":"boolean","description":"Older Subsonic versions, and some badly implemented servers (Nextcloud), use legacy authentication which sends your password in CLEAR TEXT. This is less secure than the newer, recommended hashing authentication method but in some cases it is needed. See \"Authentication\" section here => https://www.subsonic.org/pages/api.jsp\n\nIf this option is not specified it will be turned on if the subsonic server responds with error code 41 \"Token authentication not supported for LDAP users.\" -- See Error Handling section => https://www.subsonic.org/pages/api.jsp","default":false}},"required":["url","user","password"],"title":"SubsonicData"},"JellySourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/JellyData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"JellySourceConfig"},"JellyData":{"type":"object","properties":{"users":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled","examples":[["MyUser1","MyUser2"]]},"servers":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled","examples":[["MyServerName1"]]}},"title":"JellyData"},"JellyApiSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/JellyApiData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/JellyApiOptions"}},"required":["data","options"],"title":"JellyApiSourceConfig"},"JellyApiData":{"type":"object","properties":{"url":{"type":"string","description":"HOST:PORT of the Jellyfin server to connect to"},"user":{"type":"string","description":"The username of the user to authenticate for or track scrobbles for"},"password":{"type":"string","description":"Password of the username to authenticate for\n\nRequired if `apiKey` is not provided."},"apiKey":{"type":"string","description":"API Key to authenticate with.\n\nRequired if `password` is not provided."},"usersAllow":{"anyOf":[{"type":"string"},{"type":"boolean","enum":[true]},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble for specific users (case-insensitive)\n\nIf `true` MS will scrobble activity from all users"},"usersBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble for these users (case-insensitive)"},"devicesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if device or application name contains strings from this list (case-insensitive)"},"devicesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if device or application name contains strings from this list (case-insensitive)"},"librariesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if library name contains string from this list (case-insensitive)"},"librariesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if library name contains strings from this list (case-insensitive)"},"additionalAllowedLibraryTypes":{"type":"array","items":{},"description":"Allow MS to scrobble audio media in libraries classified other than 'music'\n\n`librariesAllow` will achieve the same result as this but this is more convenient if you do not want to explicitly list every library name or are only using `librariesBlock`"},"allowUnknown":{"type":"boolean","description":"Force media with a type of \"Unknown\" to be counted as Audio","default":false}},"required":["url","user"],"title":"JellyApiData"},"JellyApiOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"JellyApiOptions"},"LastfmSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/LastFmSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"configureAs":{"type":"string","enum":["source"],"description":"When used in `lastfm.config` this tells multi-scrobbler whether to use this data to configure a source or client.","default":"source","examples":["source"]}},"required":["data"],"title":"LastfmSourceConfig"},"LastFmSourceData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"apiKey":{"type":"string","description":"API Key generated from Last.fm account","examples":["787c921a2a2ab42320831aba0c8f2fc2"]},"secret":{"type":"string","description":"Secret generated from Last.fm account","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"session":{"type":"string","description":"Optional session id returned from a completed auth flow"},"redirectUri":{"type":"string","description":"Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.","default":"http://localhost:9078/lastfm/callback","examples":["http://localhost:9078/lastfm/callback"]},"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"}},"required":["apiKey","secret"],"title":"LastFmSourceData"},"YTMusicSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/YTMusicData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"type":"object","properties":{"logAuth":{"type":"boolean","description":"When true MS will log to DEBUG all of the credentials data it receives from YTM"},"logDiff":{"type":"boolean","description":"Always log history diff\n\nBy default MS will log to `WARN` if history diff is inconsistent but does not log if diff is expected (on new tracks found)\nSet this to `true` to ALWAYS log diff on new tracks. Expected diffs will log to `DEBUG` and inconsistent diffs will continue to log to `WARN`","default":false},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}}}},"title":"YTMusicSourceConfig"},"YTMusicData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"cookie":{"type":"string","description":"The cookie retrieved from the Request Headers of music.youtube.com after logging in.\n\nSee https://ytmusicapi.readthedocs.io/en/stable/setup/browser.html#copy-authentication-headers for how to retrieve this value.","examples":["VISITOR_INFO1_LIVE=jMp2xA1Xz2_PbVc; __Secure-3PAPISID=3AxsXpy0M/AkISpjek; ..."]},"clientId":{"type":"string","description":"Google Cloud Console project OAuth Client ID\n\nGenerated from a custom OAuth Client, see docs"},"clientSecret":{"type":"string","description":"Google Cloud Console project OAuth Client Secret\n\nGenerated from a custom OAuth Client, see docs"},"redirectUri":{"type":"string","description":"Google Cloud Console project OAuth Client Authorized redirect URI\n\nGenerated from a custom OAuth Client, see docs. multi-scrobbler will generate a default based on BASE_URL.\nOnly specify this if the default does not work for you."},"innertubeOptions":{"$ref":"#/definitions/InnertubeOptions","description":"Additional options for authorization and tailoring YTM client"}},"title":"YTMusicData"},"InnertubeOptions":{"type":"object","properties":{"po_token":{"type":"string","description":"Proof of Origin token\n\nMay be required if YTM starts returning 403"},"visitor_data":{"type":"string","description":"Visitor ID value found in VISITOR_INFO1_LIVE or visitorData cookie\n\nMay be required if YTM starts returning 403"},"account_index":{"type":"number","description":"If account login results in being able to choose multiple account, use a zero-based index to choose which one to monitor","examples":[0,1]},"location":{"type":"string"},"lang":{"type":"string"},"generate_session_locally":{"type":"boolean"},"device_category":{"type":"string"},"client_type":{"type":"string"},"timezone":{"type":"string"}},"title":"InnertubeOptions"},"MPRISSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MPRISData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"MPRISSourceConfig"},"MPRISData":{"type":"object","properties":{"blacklist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"DO NOT scrobble from any players that START WITH these values, case-insensitive","examples":[["spotify","vlc"]]},"whitelist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"ONLY from any players that START WITH these values, case-insensitive\n\nIf whitelist is present then blacklist is ignored","examples":[["spotify","vlc"]]}},"title":"MPRISData"},"MopidySourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MopidyData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"MopidySourceConfig"},"MopidyData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the Mopidy HTTP server to connect to\n\nYou MUST have Mopidy-HTTP extension enabled: https://mopidy.com/ext/http\n\nmulti-scrobbler connects to the WebSocket endpoint that ultimately looks like this => `ws://localhost:6680/mopidy/ws/`\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `ws://`\n* Hostname => `localhost`\n* Port => `6680`\n* Path => `/mopidy/ws/`","examples":["ws://localhost:6680/mopidy/ws/"],"default":"ws://localhost:6680/mopidy/ws/"},"uriBlacklist":{"type":"array","items":{"type":"string"},"description":"Do not scrobble tracks whose URI STARTS WITH any of these strings, case-insensitive\n\nEX: Don't scrobble tracks from soundcloud by adding 'soundcloud' to this list.\n\nList is ignored if uriWhitelist is used."},"uriWhitelist":{"type":"array","items":{"type":"string"},"description":"Only scrobble tracks whose URI STARTS WITH any of these strings, case-insensitive\n\nEX: Only scrobble tracks from soundcloud by adding 'soundcloud' to this list."},"albumBlacklist":{"type":"array","items":{"type":"string"},"description":"Remove album data that matches any case-insensitive string from this list when scrobbling,\n\nFor certain sources (Soundcloud) Mopidy does not have all track info (Album) and will instead use \"Soundcloud\" as the Album name. You can prevent multi-scrobbler from using this bad Album data by adding the fake name to this list. Multi-scrobbler will still scrobble the track, just without the bad data.","examples":[["Soundcloud","Mixcloud"]],"default":["Soundcloud"]}},"title":"MopidyData"},"ListenBrainzSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/ListenBrainzSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"configureAs":{"type":"string","enum":["source"],"description":"When used in `listenbrainz.config` this tells multi-scrobbler whether to use this data to configure a source or client.","default":"source","examples":["source"]}},"required":["data"],"title":"ListenBrainzSourceConfig"},"ListenBrainzSourceData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for the ListenBrainz server, if not using the default","examples":["https://api.listenbrainz.org/"],"default":"https://api.listenbrainz.org/"},"token":{"type":"string","description":"User token for the user to scrobble for","examples":["6794186bf-1157-4de6-80e5-uvb411f3ea2b"]},"username":{"type":"string","description":"Username of the user to scrobble for"}},"required":["token","username"],"title":"ListenBrainzSourceData"},"JRiverSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/JRiverData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"JRiverSourceConfig"},"JRiverData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the JRiver HTTP server to connect to\n\nmulti-scrobbler connects to the Web Service Interface endpoint that ultimately looks like this => `http://yourDomain:52199/MCWS/v1/`\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `http://`\n* Hostname => `localhost`\n* Port => `52199`\n* Path => `/MCWS/v1/`","examples":["http://localhost:52199/MCWS/v1/"],"default":"http://localhost:52199/MCWS/v1/"},"username":{"type":"string","description":"If you have enabled authentication, the username you set"},"password":{"type":"string","description":"If you have enabled authentication, the password you set"}},"required":["url"],"title":"JRiverData"},"KodiSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/KodiData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"KodiSourceConfig"},"KodiData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the Kodi HTTP server to connect to\n\nmulti-scrobbler connects to the Web Service Interface endpoint that ultimately looks like this => `http://yourDomain:8080/jsonrpc`\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `http://`\n* Hostname => `localhost`\n* Port => `8080`\n* Path => `/jsonrpc`","examples":["http://localhost:8080/jsonrpc"],"default":"http://localhost:8080/jsonrpc"},"username":{"type":"string","description":"The username set for Remote Control via Web Sever"},"password":{"type":"string","description":"The password set for Remote Control via Web Sever"}},"required":["url","username","password"],"title":"KodiData"},"WebScrobblerSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/WebScrobblerData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"title":"WebScrobblerSourceConfig"},"WebScrobblerData":{"type":"object","properties":{"slug":{"type":["string","null"],"description":"The URL ending that should be used to identify scrobbles for this source\n\nIn WebScrobbler's Webhook you must set an 'API URL'. All MS WebScrobbler sources must start like:\n\nhttp://localhost:9078/api/webscrobbler\n\nIf you are using multiple WebScrobbler sources (scrobbles for many users) you must use a slug to match Sources with each users extension.\n\nExample:\n\n* slug: 'usera' => API URL: http://localhost:9078/api/webscrobbler/usera\n* slug: 'userb' => API URL: http://localhost:9078/api/webscrobbler/userb\n\nIf no slug is found from an extension's incoming webhook event the first WebScrobbler source without a slug will be used"},"blacklist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Block scrobbling from specific WebScrobbler Connectors","examples":[["youtube"]]},"whitelist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only allow scrobbling from specific WebScrobbler Connectors","examples":[["mixcloud","soundcloud","bandcamp"]]}},"title":"WebScrobblerData"},"ChromecastSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/ChromecastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"ChromecastSourceConfig"},"ChromecastData":{"type":"object","properties":{"blacklistDevices":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"DO NOT scrobble from any cast devices that START WITH these values, case-insensitive\n\nUseful when used with auto discovery","examples":[["home-mini","family-tv"]]},"whitelistDevices":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"ONLY scrobble from any cast device that START WITH these values, case-insensitive\n\nIf whitelist is present then blacklist is ignored\n\nUseful when used with auto discovery","examples":[["home-mini","family-tv"]]},"blacklistApps":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"DO NOT scrobble from any application that START WITH these values, case-insensitive","examples":[["spotify","pandora"]]},"whitelistApps":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"ONLY scrobble from any application that START WITH these values, case-insensitive\n\nIf whitelist is present then blacklist is ignored","examples":[["spotify","pandora"]]},"useAvahi":{"type":"boolean","description":"Try to use Avahi and avahi-browse to resolve mDNS devices instead of native mDNS querying\n\nUseful for docker (alpine) container where mDNS resolution is not yet supported. Avahi socket must be exposed to the container and avahi-tools must be installed.","default":false},"useAutoDiscovery":{"type":"boolean","description":"Use mDNS to discovery Google Cast devices on your next automatically?\n\nIf not explicitly set then it is TRUE if `devices` is not set"},"devices":{"type":"array","items":{"$ref":"#/definitions/ChromecastDeviceInfo"},"description":"A list of Google Cast devices to monitor\n\nIf this is used then `useAutoDiscovery` is set to FALSE, if not explicitly set"},"allowUnknownMedia":{"anyOf":[{"type":"boolean"},{"type":"array","items":{"type":"string"}}],"description":"Chromecast Apps report a \"media type\" in the status info returned for whatever is currently playing\n\n* If set to TRUE then Music AND Generic/Unknown media will be tracked for ALL APPS\n* If set to FALSE then only media explicitly typed as Music will be tracked for ALL APPS\n* If set to a list then only Apps whose name contain one of these values, case-insensitive, will have Music AND Generic/Unknown tracked\n\nSee https://developers.google.com/cast/docs/media/messages#MediaInformation \"metadata\" property","default":false},"forceMediaRecognitionOn":{"type":"array","items":{"type":"string"},"description":"Media provided by any App whose name is listed here will ALWAYS be tracked, regardless of the \"media type\" reported\n\nApps will be recognized if they CONTAIN any of these values, case-insensitive"}},"title":"ChromecastData"},"ChromecastDeviceInfo":{"type":"object","properties":{"name":{"type":"string","description":"A friendly name to identify this device","examples":["MySmartTV"]},"address":{"type":"string","description":"The IP address of the device","examples":["192.168.0.115"]}},"required":["name","address"],"title":"ChromecastDeviceInfo"},"MusikcubeSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MusikcubeData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"MusikcubeSourceConfig"},"MusikcubeData":{"type":"object","properties":{"url":{"type":"string","description":"URL of the Musikcube Websocket (Metadata) server to connect to\n\nYou MUST have enabled 'metadata' server and set a password: https://github.com/clangen/musikcube/wiki/remote-api-documentation\n * musikcube -> settings -> server setup\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `ws://`\n* Hostname => `localhost`\n* Port => `7905`","examples":["ws://localhost:7905"],"default":"ws://localhost:7905"},"password":{"type":"string","description":"Password set in Musikcube https://github.com/clangen/musikcube/wiki/remote-api-documentation\n\n* musikcube -> settings -> server setup -> password"},"device_id":{"type":"string"}},"required":["password"],"title":"MusikcubeData"},"MusicCastSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MusicCastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"MusicCastSourceConfig"},"MusicCastData":{"type":"object","properties":{"url":{"type":"string","description":"The host or URL of the YamahaExtendedControl endpoint to use","examples":[["192.168.0.101","http://192.168.0.101/YamahaExtendedControl"]]}},"required":["url"],"title":"MusicCastData"},"MPDSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MPDData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/MPDSourceOptions"}},"required":["data","options"],"title":"MPDSourceConfig"},"MPDData":{"type":"object","properties":{"url":{"type":"string","description":"URL:PORT of the MPD server to connect to\n\nTo use this you must have TCP connections enabled for your MPD server https://mpd.readthedocs.io/en/stable/user.html#client-connections","examples":["localhost:6600"],"default":"localhost:6600"},"path":{"type":"string","description":"If using socket specify the path instead of url.\n\ntrailing `~` is replaced by your home directory"},"password":{"type":"string","description":"Password for the server, if set https://mpd.readthedocs.io/en/stable/user.html#permissions-and-passwords"}},"title":"MPDData"},"MPDSourceOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"MPDSourceOptions"},"VLCSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/VLCData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/VLCSourceOptions"}},"required":["data"],"title":"VLCSourceConfig"},"VLCData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL:PORT of the VLC server to connect to\n\nTo use this you must have the Web (http) interface module enabled and a password set https://foxxmd.github.io/multi-scrobbler/docs/configuration#vlc","examples":["localhost:8080"],"default":"localhost:8080"},"password":{"type":"string","description":"Password for the server"}},"required":["password"],"title":"VLCData"},"VLCSourceOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"},"filenamePatterns":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"A list of regular expressions to use to extract metadata (title, album, artist) from a filename\n\nUsed when VLC reports only the filename for the current audio track"},"logFilenamePatterns":{"type":"boolean","description":"Log to DEBUG when a filename-only track is matched or not matched by filenamePatterns","default":false},"dumpVlcMetadata":{"type":"boolean","description":"Dump all the metadata VLC reports for an audio track to DEBUG.\n\nUse this if reporting an issue with VLC not correctly capturing metadata for a track.","default":false}},"title":"VLCSourceOptions"},"IcecastSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/IcecastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/IcecastSourceOptions"}},"required":["data"],"title":"IcecastSourceConfig"},"IcecastData":{"type":"object","properties":{"sources":{"type":"array","items":{"$ref":"#/definitions/IcecastSource"}},"icestatsEndpoint":{"type":"string"},"statsEndpoint":{"type":"string"},"nextsongsEndpoint":{"type":"string"},"sevenhtmlEndpoint":{"type":"string"},"icyMetaInt":{"type":"number"},"url":{"type":"string","description":"The Icecast stream URL"}},"required":["url"],"title":"IcecastData"},"IcecastSource":{"type":"string","enum":["icy","ogg","icestats","stats","sevenhtml","nextsongs"],"title":"IcecastSource"},"IcecastSourceOptions":{"type":"object","properties":{"systemScrobble":{"type":"boolean","description":"For Sources that support manual listening, should MS default to scrobbling when no user interaction has occurred?\n\nIf not specified MS will use a Source's specific behavior, see Source's documentation."},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"IcecastSourceOptions"},"AzuracastSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/AzuracastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"AzuracastSourceConfig"},"AzuracastData":{"type":"object","properties":{"url":{"type":"string","description":"Base URL of the Azuracast instance\n\nThis does NOT include the station. If a station is included it will be ignored. Use `station` field to specify station, if necessary","examples":["https://radio.mydomain.tld","http://localhost:80"]},"station":{"type":"string","description":"The specific station to monitor\n\nScrobbling will only occur if any of the monitor conditions are met AND the station is ONLINE.\n\nTo monitor multiple stations create a Source for each station.","examples":["my-station-1"]},"monitorWhenListeners":{"type":["boolean","number"],"description":"Only activate scrobble monitoring if station\n\n* `true` => has any current listeners\n* `number` => has EQUAL TO or MORE THAN X number of listeners"},"monitorWhenLive":{"type":"boolean","description":"Only activate scrobble monitoring if station has a live DJ/Streamer","default":true},"apiKey":{"type":"string","description":"API Key used to access data about private streams\n\nhttps://www.azuracast.com/docs/developers/apis/#api-authentication"}},"required":["url","station"],"title":"AzuracastData"}}} \ No newline at end of file +{"$schema":"http://json-schema.org/draft-07/schema#","anyOf":[{"$ref":"#/definitions/SpotifySourceConfig"},{"$ref":"#/definitions/PlexSourceConfig"},{"$ref":"#/definitions/PlexApiSourceConfig"},{"$ref":"#/definitions/TautulliSourceConfig"},{"$ref":"#/definitions/DeezerSourceConfig"},{"$ref":"#/definitions/DeezerInternalSourceConfig"},{"$ref":"#/definitions/ListenbrainzEndpointSourceConfig"},{"$ref":"#/definitions/LastFMEndpointSourceConfig"},{"$ref":"#/definitions/SubSonicSourceConfig"},{"$ref":"#/definitions/JellySourceConfig"},{"$ref":"#/definitions/JellyApiSourceConfig"},{"$ref":"#/definitions/LastfmSourceConfig"},{"$ref":"#/definitions/YTMusicSourceConfig"},{"$ref":"#/definitions/MPRISSourceConfig"},{"$ref":"#/definitions/MopidySourceConfig"},{"$ref":"#/definitions/ListenBrainzSourceConfig"},{"$ref":"#/definitions/JRiverSourceConfig"},{"$ref":"#/definitions/KodiSourceConfig"},{"$ref":"#/definitions/WebScrobblerSourceConfig"},{"$ref":"#/definitions/ChromecastSourceConfig"},{"$ref":"#/definitions/MalojaSourceConfig"},{"$ref":"#/definitions/MusikcubeSourceConfig"},{"$ref":"#/definitions/MusicCastSourceConfig"},{"$ref":"#/definitions/MPDSourceConfig"},{"$ref":"#/definitions/VLCSourceConfig"},{"$ref":"#/definitions/IcecastSourceConfig"},{"$ref":"#/definitions/AzuracastSourceConfig"},{"$ref":"#/definitions/KoitoSourceConfig"}],"definitions":{"SpotifySourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/SpotifySourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"SpotifySourceConfig"},"SpotifySourceData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)\n\nIt is unlikely you should need to change this unless you scrobble many very short tracks often\n\nReading:\n* https://developer.spotify.com/documentation/web-api/guides/rate-limits/\n* https://medium.com/mendix/limiting-your-amount-of-calls-in-mendix-most-of-the-time-rest-835dde55b10e\n * Rate limit may ~180 req/min\n* https://community.spotify.com/t5/Spotify-for-Developers/Web-API-ratelimit/m-p/5503150/highlight/true#M7930\n * Informally indicated as 20 req/sec? Probably for burstiness","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"clientId":{"type":"string","description":"spotify client id","examples":["787c921a2a2ab42320831aba0c8f2fc2"]},"clientSecret":{"type":"string","description":"spotify client secret","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"redirectUri":{"type":"string","description":"spotify redirect URI -- required only if not the default shown here. URI must end in \"callback\"","default":"http://localhost:9078/callback","examples":["http://localhost:9078/callback"]}},"required":["clientId","clientSecret"],"title":"SpotifySourceData"},"CommonSourceOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"CommonSourceOptions"},"LogLevel":{"type":"string","enum":["silent","fatal","error","warn","info","log","verbose","debug"],"description":"Names of log levels that can be invoked on the logger\n\nFrom lowest to highest:\n\n* `debug`\n* `verbose`\n* `log`\n* `info`\n* `warn`\n* `error`\n* `fatal`\n* `silent` (will never output anything)\n\nWhen used in `LogOptions` specifies the **minimum** level the output should log at.","title":"LogLevel"},"FileLogOptions":{"type":"object","properties":{"timestamp":{"type":"string","enum":["unix","iso","auto"],"description":"For rolling log files\n\nWhen\n* value passed to rolling destination is a string (`path` from LogOptions is a string) and\n* `frequency` is defined\n\nThis determines the format of the datetime inserted into the log file name:\n\n* `unix` - unix epoch timestamp in milliseconds\n* `iso` - Full [ISO8601](https://en.wikipedia.org/wiki/ISO_8601) datetime IE '2024-03-07T20:11:34Z'\n* `auto`\n * When frequency is `daily` only inserts date IE YYYY-MM-DD\n * Otherwise inserts full ISO8601 datetime","default":"auto"},"size":{"type":["number","string"],"description":"The maximum size of a given rolling log file.\n\nCan be combined with frequency. Use k, m and g to express values in KB, MB or GB.\n\nNumerical values will be considered as MB.","default":"10MB"},"frequency":{"anyOf":[{"type":"string","enum":["daily"]},{"type":"string","enum":["hourly"]},{"type":"number"}],"description":"The amount of time a given rolling log file is used. Can be combined with size.\n\nUse `daily` or `hourly` to rotate file every day (or every hour). Existing file within the current day (or hour) will be re-used.\n\nNumerical values will be considered as a number of milliseconds. Using a numerical value will always create a new file upon startup.","default":"daily"},"path":{"anyOf":[{"type":"string"},{"$comment":"() => string"}],"description":"The path and filename to use for log files.\n\nIf using rolling files the filename will be appended with `.N` (a number) BEFORE the extension based on rolling status.\n\nMay also be specified using env LOG_PATH or a function that returns a string.\n\nIf path is relative the absolute path will be derived from `logBaseDir` (in `LoggerAppExtras`) which defaults to CWD","default":"./logs/app.log"},"level":{"anyOf":[{"$ref":"#/definitions/LogLevel"},{"type":"boolean","const":false}],"description":"Specify the minimum log level to output to rotating files. If `false` no log files will be created."}},"title":"FileLogOptions"},"ScrobbleThresholds":{"type":"object","properties":{"duration":{"type":["number","null"],"description":"The number of seconds a track has been listened to before it should be considered scrobbled.\n\nSet to null to disable.","default":240,"examples":[240]},"percent":{"type":["number","null"],"description":"The percentage (as an integer) of a track that should have been seen played before it should be scrobbled. Only used if the Source provides information about how long the track is.\n\nSet to null to disable.\n\nNOTE: This should be used with care when the Source is a \"polling\" type (has an 'interval' property). If the track is short and the interval is too high MS may ignore the track if percentage is high because it had not \"seen\" the track for long enough from first discovery, even if you have been playing the track for longer.","default":50,"examples":[50]}},"title":"ScrobbleThresholds"},"PlayTransformOptions":{"type":"object","properties":{"log":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["all"]}]},"preCompare":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"},"compare":{"type":"object","properties":{"candidate":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"},"existing":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"}}},"postCompare":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"}},"title":"PlayTransformOptions"},"PlayTransformPartsConfig<SearchAndReplaceTerm>":{"anyOf":[{"$ref":"#/definitions/PlayTransformPartsArray%3CSearchAndReplaceTerm%3E"},{"$ref":"#/definitions/PlayTransformParts%3CSearchAndReplaceTerm%3E"}],"title":"PlayTransformPartsConfig<SearchAndReplaceTerm>"},"PlayTransformPartsArray<SearchAndReplaceTerm>":{"type":"array","items":{"$ref":"#/definitions/PlayTransformParts%3CSearchAndReplaceTerm%3E"},"title":"PlayTransformPartsArray<SearchAndReplaceTerm>"},"PlayTransformParts<SearchAndReplaceTerm>":{"type":"object","properties":{"when":{"$ref":"#/definitions/WhenConditionsConfig"},"title":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}},"artists":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}},"album":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}}},"title":"PlayTransformParts<SearchAndReplaceTerm>"},"WhenConditionsConfig":{"$ref":"#/definitions/WhenConditions%3Cstring%3E","title":"WhenConditionsConfig"},"WhenConditions<string>":{"type":"array","items":{"$ref":"#/definitions/WhenParts%3Cstring%3E"},"title":"WhenConditions<string>"},"WhenParts<string>":{"$ref":"#/definitions/PlayTransformPartsAtomic%3Cstring%3E","title":"WhenParts<string>"},"PlayTransformPartsAtomic<string>":{"type":"object","properties":{"title":{"type":"string"},"artists":{"type":"string"},"album":{"type":"string"}},"title":"PlayTransformPartsAtomic<string>"},"SearchAndReplaceTerm":{"anyOf":[{"type":"string"},{"$ref":"#/definitions/ConditionalSearchAndReplaceTerm"}],"title":"SearchAndReplaceTerm"},"ConditionalSearchAndReplaceTerm":{"type":"object","properties":{"when":{"$ref":"#/definitions/WhenConditionsConfig"},"search":{},"replace":{}},"required":["search","replace"],"title":"ConditionalSearchAndReplaceTerm"},"PlexSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/PlexSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"PlexSourceConfig"},"PlexSourceData":{"type":"object","properties":{"user":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled","examples":[["MyUser1","MyUser2"]]},"libraries":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of libraries to scrobble tracks from\n\nIf none are provided tracks from all libraries will be scrobbled","examples":[["Audio","Music"]]},"servers":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled","examples":[["MyServerName"]]}},"title":"PlexSourceData"},"PlexApiSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/PlexApiData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/PlexApiOptions"}},"required":["data","options"],"title":"PlexApiSourceConfig"},"PlexApiData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"token":{"type":"string"},"url":{"type":"string","description":"http(s)://HOST:PORT of the Plex server to connect to"},"usersAllow":{"anyOf":[{"type":"string"},{"type":"boolean","enum":[true]},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble for specific users (case-insensitive)\n\nIf `true` MS will scrobble activity from all users"},"usersBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble for these users (case-insensitive)"},"devicesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if device or application name contains strings from this list (case-insensitive)"},"devicesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if device or application name contains strings from this list (case-insensitive)"},"librariesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if library name contains string from this list (case-insensitive)"},"librariesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if library name contains strings from this list (case-insensitive)"}},"required":["url"],"title":"PlexApiData"},"PlexApiOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"},"ignoreInvalidCert":{"type":"boolean","description":"Ignore invalid cert errors when connecting to Plex\n\nUseful for Plex servers using \"Required\" Secure Connections with self-signed certificates\n\nDo not enable unless you know you need this.","default":false}},"title":"PlexApiOptions"},"TautulliSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/PlexSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"TautulliSourceConfig"},"DeezerSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/DeezerData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"DeezerSourceConfig"},"DeezerData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"clientId":{"type":"string","description":"deezer client id","examples":["a89cba1569901a0671d5a9875fed4be1"]},"clientSecret":{"type":"string","description":"deezer client secret","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"redirectUri":{"type":"string","description":"deezer redirect URI -- required only if not the default shown here. URI must end in \"callback\"","default":"http://localhost:9078/deezer/callback","examples":["http://localhost:9078/deezer/callback"]}},"required":["clientId","clientSecret"],"title":"DeezerData"},"DeezerInternalSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/DeezerInternalData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/DeezerInternalSourceOptions"}},"required":["data"],"title":"DeezerInternalSourceConfig"},"DeezerInternalData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"arl":{"type":"string","description":"ARL retrieved from Deezer response header"},"userAgent":{"type":"string","description":"User agent","default":"Mozilla/5.0 (X11; Linux i686; rv:135.0) Gecko/20100101 Firefox/135.0"}},"required":["arl"],"title":"DeezerInternalData"},"DeezerInternalSourceOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"},"fuzzyDiscoveryIgnore":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["aggressive"]}]}},"title":"DeezerInternalSourceOptions"},"ListenbrainzEndpointSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/ListenbrainzEndpointData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"title":"ListenbrainzEndpointSourceConfig"},"ListenbrainzEndpointData":{"type":"object","properties":{"slug":{"type":["string","null"],"description":"The URL ending that should be used to identify scrobbles for this source\n\nIf you are using multiple Listenbrainz endpoint sources (scrobbles for many users) you can use a slug to match Sources with individual users/origins\n\nExample:\n\n* slug: 'usera' => API URL: http://localhost:9078/api/listenbrainz/usera\n* slug: 'originb' => API URL: http://localhost:9078/api/listenbrainz/originb\n\nIf no slug is found from an extension's incoming webhook event the first Listenbrainz source without a slug will be used"},"token":{"type":["string","null"],"description":"If an LZ submission request contains this token in the Authorization Header it will be used to match the submission with this Source\n\nSee: https://listenbrainz.readthedocs.io/en/latest/users/api/index.html#add-the-user-token-to-your-requests"}},"title":"ListenbrainzEndpointData"},"LastFMEndpointSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/LastFMEndpointData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"title":"LastFMEndpointSourceConfig"},"LastFMEndpointData":{"type":"object","properties":{"slug":{"type":["string","null"],"description":"The URL ending that should be used to identify scrobbles for this source\n\nIf you are using multiple Last.fm endpoint sources (scrobbles for many users) you can use a slug to match Sources with individual users/origins\n\nExample:\n\n* slug: 'usera' => API URL: http://localhost:9078/api/lastfm/usera\n* slug: 'originb' => API URL: http://localhost:9078/api/lastfm/originb\n\nIf no slug is found from an extension's incoming webhook event the first Last.fm source without a slug will be used"}},"title":"LastFMEndpointData"},"SubSonicSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/SubsonicData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"SubSonicSourceConfig"},"SubsonicData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the subsonic media server to query","examples":["http://airsonic.local"]},"user":{"type":"string","description":"Username to login to the server with","examples":[["MyUser"]]},"password":{"type":"string","description":"Password for the user to login to the server with","examples":["MyPassword"]},"ignoreTlsErrors":{"type":"boolean","description":"If your subsonic server is using self-signed certs you may need to disable TLS errors in order to get a connection\n\nWARNING: This should be used with caution as your traffic may not be encrypted.","default":false},"legacyAuthentication":{"type":"boolean","description":"Older Subsonic versions, and some badly implemented servers (Nextcloud), use legacy authentication which sends your password in CLEAR TEXT. This is less secure than the newer, recommended hashing authentication method but in some cases it is needed. See \"Authentication\" section here => https://www.subsonic.org/pages/api.jsp\n\nIf this option is not specified it will be turned on if the subsonic server responds with error code 41 \"Token authentication not supported for LDAP users.\" -- See Error Handling section => https://www.subsonic.org/pages/api.jsp","default":false},"usersAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble for specific users (case-insensitive)\n\nIf undefined or an empty string/list MS will scrobble activity from all users"}},"required":["url","user","password"],"title":"SubsonicData"},"JellySourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/JellyData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"JellySourceConfig"},"JellyData":{"type":"object","properties":{"users":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled","examples":[["MyUser1","MyUser2"]]},"servers":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled","examples":[["MyServerName1"]]}},"title":"JellyData"},"JellyApiSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/JellyApiData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/JellyApiOptions"}},"required":["data","options"],"title":"JellyApiSourceConfig"},"JellyApiData":{"type":"object","properties":{"url":{"type":"string","description":"HOST:PORT of the Jellyfin server to connect to"},"user":{"type":"string","description":"The username of the user to authenticate for or track scrobbles for"},"password":{"type":"string","description":"Password of the username to authenticate for\n\nRequired if `apiKey` is not provided."},"apiKey":{"type":"string","description":"API Key to authenticate with.\n\nRequired if `password` is not provided."},"usersAllow":{"anyOf":[{"type":"string"},{"type":"boolean","enum":[true]},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble for specific users (case-insensitive)\n\nIf `true` MS will scrobble activity from all users"},"usersBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble for these users (case-insensitive)"},"devicesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if device or application name contains strings from this list (case-insensitive)"},"devicesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if device or application name contains strings from this list (case-insensitive)"},"librariesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if library name contains string from this list (case-insensitive)"},"librariesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if library name contains strings from this list (case-insensitive)"},"additionalAllowedLibraryTypes":{"type":"array","items":{},"description":"Allow MS to scrobble audio media in libraries classified other than 'music'\n\n`librariesAllow` will achieve the same result as this but this is more convenient if you do not want to explicitly list every library name or are only using `librariesBlock`"},"allowUnknown":{"type":"boolean","description":"Force media with a type of \"Unknown\" to be counted as Audio","default":false},"frontendUrlOverride":{"type":"string","description":"HOST:PORT of the Jellyfin server that your browser will be able to access from the frontend (and thus load images and links from)\nIf unspecified it will use the normal server HOST and PORT from the `url`\nNecessary if you are using a reverse proxy or other network configuration that prevents the frontend from accessing the server directly"}},"required":["url","user"],"title":"JellyApiData"},"JellyApiOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"JellyApiOptions"},"LastfmSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/LastFmSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"configureAs":{"type":"string","enum":["source"],"description":"When used in `lastfm.config` this tells multi-scrobbler whether to use this data to configure a source or client.","default":"source","examples":["source"]}},"required":["data"],"title":"LastfmSourceConfig"},"LastFmSourceData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"apiKey":{"type":"string","description":"API Key generated from Last.fm account","examples":["787c921a2a2ab42320831aba0c8f2fc2"]},"secret":{"type":"string","description":"Secret generated from Last.fm account","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"session":{"type":"string","description":"Optional session id returned from a completed auth flow"},"redirectUri":{"type":"string","description":"Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.","default":"http://localhost:9078/lastfm/callback","examples":["http://localhost:9078/lastfm/callback"]},"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"}},"required":["apiKey","secret"],"title":"LastFmSourceData"},"YTMusicSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/YTMusicData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"type":"object","properties":{"logAuth":{"type":"boolean","description":"When true MS will log to DEBUG all of the credentials data it receives from YTM"},"logDiff":{"type":"boolean","description":"Always log history diff\n\nBy default MS will log to `WARN` if history diff is inconsistent but does not log if diff is expected (on new tracks found)\nSet this to `true` to ALWAYS log diff on new tracks. Expected diffs will log to `DEBUG` and inconsistent diffs will continue to log to `WARN`","default":false},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}}}},"title":"YTMusicSourceConfig"},"YTMusicData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"cookie":{"type":"string","description":"The cookie retrieved from the Request Headers of music.youtube.com after logging in.\n\nSee https://ytmusicapi.readthedocs.io/en/stable/setup/browser.html#copy-authentication-headers for how to retrieve this value.","examples":["VISITOR_INFO1_LIVE=jMp2xA1Xz2_PbVc; __Secure-3PAPISID=3AxsXpy0M/AkISpjek; ..."]},"clientId":{"type":"string","description":"Google Cloud Console project OAuth Client ID\n\nGenerated from a custom OAuth Client, see docs"},"clientSecret":{"type":"string","description":"Google Cloud Console project OAuth Client Secret\n\nGenerated from a custom OAuth Client, see docs"},"redirectUri":{"type":"string","description":"Google Cloud Console project OAuth Client Authorized redirect URI\n\nGenerated from a custom OAuth Client, see docs. multi-scrobbler will generate a default based on BASE_URL.\nOnly specify this if the default does not work for you."},"innertubeOptions":{"$ref":"#/definitions/InnertubeOptions","description":"Additional options for authorization and tailoring YTM client"}},"title":"YTMusicData"},"InnertubeOptions":{"type":"object","properties":{"po_token":{"type":"string","description":"Proof of Origin token\n\nMay be required if YTM starts returning 403"},"visitor_data":{"type":"string","description":"Visitor ID value found in VISITOR_INFO1_LIVE or visitorData cookie\n\nMay be required if YTM starts returning 403"},"account_index":{"type":"number","description":"If account login results in being able to choose multiple account, use a zero-based index to choose which one to monitor","examples":[0,1]},"location":{"type":"string"},"lang":{"type":"string"},"generate_session_locally":{"type":"boolean"},"device_category":{"type":"string"},"client_type":{"type":"string"},"timezone":{"type":"string"}},"title":"InnertubeOptions"},"MPRISSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MPRISData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"MPRISSourceConfig"},"MPRISData":{"type":"object","properties":{"blacklist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"DO NOT scrobble from any players that START WITH these values, case-insensitive","examples":[["spotify","vlc"]]},"whitelist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"ONLY from any players that START WITH these values, case-insensitive\n\nIf whitelist is present then blacklist is ignored","examples":[["spotify","vlc"]]}},"title":"MPRISData"},"MopidySourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MopidyData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"MopidySourceConfig"},"MopidyData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the Mopidy HTTP server to connect to\n\nYou MUST have Mopidy-HTTP extension enabled: https://mopidy.com/ext/http\n\nmulti-scrobbler connects to the WebSocket endpoint that ultimately looks like this => `ws://localhost:6680/mopidy/ws/`\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `ws://`\n* Hostname => `localhost`\n* Port => `6680`\n* Path => `/mopidy/ws/`","examples":["ws://localhost:6680/mopidy/ws/"],"default":"ws://localhost:6680/mopidy/ws/"},"uriBlacklist":{"type":"array","items":{"type":"string"},"description":"Do not scrobble tracks whose URI STARTS WITH any of these strings, case-insensitive\n\nEX: Don't scrobble tracks from soundcloud by adding 'soundcloud' to this list.\n\nList is ignored if uriWhitelist is used."},"uriWhitelist":{"type":"array","items":{"type":"string"},"description":"Only scrobble tracks whose URI STARTS WITH any of these strings, case-insensitive\n\nEX: Only scrobble tracks from soundcloud by adding 'soundcloud' to this list."},"albumBlacklist":{"type":"array","items":{"type":"string"},"description":"Remove album data that matches any case-insensitive string from this list when scrobbling,\n\nFor certain sources (Soundcloud) Mopidy does not have all track info (Album) and will instead use \"Soundcloud\" as the Album name. You can prevent multi-scrobbler from using this bad Album data by adding the fake name to this list. Multi-scrobbler will still scrobble the track, just without the bad data.","examples":[["Soundcloud","Mixcloud"]],"default":["Soundcloud"]}},"title":"MopidyData"},"ListenBrainzSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/ListenBrainzSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"configureAs":{"type":"string","enum":["source"],"description":"When used in `listenbrainz.config` this tells multi-scrobbler whether to use this data to configure a source or client.","default":"source","examples":["source"]}},"required":["data"],"title":"ListenBrainzSourceConfig"},"ListenBrainzSourceData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for the ListenBrainz server, if not using the default","examples":["https://api.listenbrainz.org/"],"default":"https://api.listenbrainz.org/"},"token":{"type":"string","description":"User token for the user to scrobble for","examples":["6794186bf-1157-4de6-80e5-uvb411f3ea2b"]},"username":{"type":"string","description":"Username of the user to scrobble for"}},"required":["token","username"],"title":"ListenBrainzSourceData"},"JRiverSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/JRiverData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"JRiverSourceConfig"},"JRiverData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the JRiver HTTP server to connect to\n\nmulti-scrobbler connects to the Web Service Interface endpoint that ultimately looks like this => `http://yourDomain:52199/MCWS/v1/`\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `http://`\n* Hostname => `localhost`\n* Port => `52199`\n* Path => `/MCWS/v1/`","examples":["http://localhost:52199/MCWS/v1/"],"default":"http://localhost:52199/MCWS/v1/"},"username":{"type":"string","description":"If you have enabled authentication, the username you set"},"password":{"type":"string","description":"If you have enabled authentication, the password you set"}},"required":["url"],"title":"JRiverData"},"KodiSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/KodiData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"KodiSourceConfig"},"KodiData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the Kodi HTTP server to connect to\n\nmulti-scrobbler connects to the Web Service Interface endpoint that ultimately looks like this => `http://yourDomain:8080/jsonrpc`\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `http://`\n* Hostname => `localhost`\n* Port => `8080`\n* Path => `/jsonrpc`","examples":["http://localhost:8080/jsonrpc"],"default":"http://localhost:8080/jsonrpc"},"username":{"type":"string","description":"The username set for Remote Control via Web Sever"},"password":{"type":"string","description":"The password set for Remote Control via Web Sever"}},"required":["url","username","password"],"title":"KodiData"},"WebScrobblerSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/WebScrobblerData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"title":"WebScrobblerSourceConfig"},"WebScrobblerData":{"type":"object","properties":{"slug":{"type":["string","null"],"description":"The URL ending that should be used to identify scrobbles for this source\n\nIn WebScrobbler's Webhook you must set an 'API URL'. All MS WebScrobbler sources must start like:\n\nhttp://localhost:9078/api/webscrobbler\n\nIf you are using multiple WebScrobbler sources (scrobbles for many users) you must use a slug to match Sources with each users extension.\n\nExample:\n\n* slug: 'usera' => API URL: http://localhost:9078/api/webscrobbler/usera\n* slug: 'userb' => API URL: http://localhost:9078/api/webscrobbler/userb\n\nIf no slug is found from an extension's incoming webhook event the first WebScrobbler source without a slug will be used"},"blacklist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Block scrobbling from specific WebScrobbler Connectors","examples":[["youtube"]]},"whitelist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only allow scrobbling from specific WebScrobbler Connectors","examples":[["mixcloud","soundcloud","bandcamp"]]}},"title":"WebScrobblerData"},"ChromecastSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/ChromecastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"ChromecastSourceConfig"},"ChromecastData":{"type":"object","properties":{"blacklistDevices":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"DO NOT scrobble from any cast devices that START WITH these values, case-insensitive\n\nUseful when used with auto discovery","examples":[["home-mini","family-tv"]]},"whitelistDevices":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"ONLY scrobble from any cast device that START WITH these values, case-insensitive\n\nIf whitelist is present then blacklist is ignored\n\nUseful when used with auto discovery","examples":[["home-mini","family-tv"]]},"blacklistApps":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"DO NOT scrobble from any application that START WITH these values, case-insensitive","examples":[["spotify","pandora"]]},"whitelistApps":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"ONLY scrobble from any application that START WITH these values, case-insensitive\n\nIf whitelist is present then blacklist is ignored","examples":[["spotify","pandora"]]},"useAvahi":{"type":"boolean","description":"Try to use Avahi and avahi-browse to resolve mDNS devices instead of native mDNS querying\n\nUseful for docker (alpine) container where mDNS resolution is not yet supported. Avahi socket must be exposed to the container and avahi-tools must be installed.","default":false},"useAutoDiscovery":{"type":"boolean","description":"Use mDNS to discovery Google Cast devices on your next automatically?\n\nIf not explicitly set then it is TRUE if `devices` is not set"},"devices":{"type":"array","items":{"$ref":"#/definitions/ChromecastDeviceInfo"},"description":"A list of Google Cast devices to monitor\n\nIf this is used then `useAutoDiscovery` is set to FALSE, if not explicitly set"},"allowUnknownMedia":{"anyOf":[{"type":"boolean"},{"type":"array","items":{"type":"string"}}],"description":"Chromecast Apps report a \"media type\" in the status info returned for whatever is currently playing\n\n* If set to TRUE then Music AND Generic/Unknown media will be tracked for ALL APPS\n* If set to FALSE then only media explicitly typed as Music will be tracked for ALL APPS\n* If set to a list then only Apps whose name contain one of these values, case-insensitive, will have Music AND Generic/Unknown tracked\n\nSee https://developers.google.com/cast/docs/media/messages#MediaInformation \"metadata\" property","default":false},"forceMediaRecognitionOn":{"type":"array","items":{"type":"string"},"description":"Media provided by any App whose name is listed here will ALWAYS be tracked, regardless of the \"media type\" reported\n\nApps will be recognized if they CONTAIN any of these values, case-insensitive"}},"title":"ChromecastData"},"ChromecastDeviceInfo":{"type":"object","properties":{"name":{"type":"string","description":"A friendly name to identify this device","examples":["MySmartTV"]},"address":{"type":"string","description":"The IP address of the device","examples":["192.168.0.115"]}},"required":["name","address"],"title":"ChromecastDeviceInfo"},"MalojaSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MalojaSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"configureAs":{"type":"string","enum":["source"],"description":"When used in `maloja.config` this tells multi-scrobbler whether to use this data to configure a source or client.","default":"source","examples":["source"]}},"required":["data"],"title":"MalojaSourceConfig"},"MalojaSourceData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for maloja server","examples":["http://localhost:42010"]},"apiKey":{"type":"string","description":"API Key for Maloja server","examples":["myApiKey"]}},"required":["apiKey","url"],"title":"MalojaSourceData"},"MusikcubeSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MusikcubeData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"MusikcubeSourceConfig"},"MusikcubeData":{"type":"object","properties":{"url":{"type":"string","description":"URL of the Musikcube Websocket (Metadata) server to connect to\n\nYou MUST have enabled 'metadata' server and set a password: https://github.com/clangen/musikcube/wiki/remote-api-documentation\n * musikcube -> settings -> server setup\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `ws://`\n* Hostname => `localhost`\n* Port => `7905`","examples":["ws://localhost:7905"],"default":"ws://localhost:7905"},"password":{"type":"string","description":"Password set in Musikcube https://github.com/clangen/musikcube/wiki/remote-api-documentation\n\n* musikcube -> settings -> server setup -> password"},"device_id":{"type":"string"}},"required":["password"],"title":"MusikcubeData"},"MusicCastSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MusicCastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"MusicCastSourceConfig"},"MusicCastData":{"type":"object","properties":{"url":{"type":"string","description":"The host or URL of the YamahaExtendedControl endpoint to use","examples":[["192.168.0.101","http://192.168.0.101/YamahaExtendedControl"]]}},"required":["url"],"title":"MusicCastData"},"MPDSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MPDData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/MPDSourceOptions"}},"required":["data","options"],"title":"MPDSourceConfig"},"MPDData":{"type":"object","properties":{"url":{"type":"string","description":"URL:PORT of the MPD server to connect to\n\nTo use this you must have TCP connections enabled for your MPD server https://mpd.readthedocs.io/en/stable/user.html#client-connections","examples":["localhost:6600"],"default":"localhost:6600"},"path":{"type":"string","description":"If using socket specify the path instead of url.\n\ntrailing `~` is replaced by your home directory"},"password":{"type":"string","description":"Password for the server, if set https://mpd.readthedocs.io/en/stable/user.html#permissions-and-passwords"}},"title":"MPDData"},"MPDSourceOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"MPDSourceOptions"},"VLCSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/VLCData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/VLCSourceOptions"}},"required":["data"],"title":"VLCSourceConfig"},"VLCData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL:PORT of the VLC server to connect to\n\nTo use this you must have the Web (http) interface module enabled and a password set https://foxxmd.github.io/multi-scrobbler/docs/configuration#vlc","examples":["localhost:8080"],"default":"localhost:8080"},"password":{"type":"string","description":"Password for the server"}},"required":["password"],"title":"VLCData"},"VLCSourceOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"},"filenamePatterns":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"A list of regular expressions to use to extract metadata (title, album, artist) from a filename\n\nUsed when VLC reports only the filename for the current audio track"},"logFilenamePatterns":{"type":"boolean","description":"Log to DEBUG when a filename-only track is matched or not matched by filenamePatterns","default":false},"dumpVlcMetadata":{"type":"boolean","description":"Dump all the metadata VLC reports for an audio track to DEBUG.\n\nUse this if reporting an issue with VLC not correctly capturing metadata for a track.","default":false}},"title":"VLCSourceOptions"},"IcecastSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/IcecastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/IcecastSourceOptions"}},"required":["data"],"title":"IcecastSourceConfig"},"IcecastData":{"type":"object","properties":{"sources":{"type":"array","items":{"$ref":"#/definitions/IcecastSource"}},"icestatsEndpoint":{"type":"string"},"statsEndpoint":{"type":"string"},"nextsongsEndpoint":{"type":"string"},"sevenhtmlEndpoint":{"type":"string"},"icyMetaInt":{"type":"number"},"url":{"type":"string","description":"The Icecast stream URL"}},"required":["url"],"title":"IcecastData"},"IcecastSource":{"type":"string","enum":["icy","ogg","icestats","stats","sevenhtml","nextsongs"],"title":"IcecastSource"},"IcecastSourceOptions":{"type":"object","properties":{"systemScrobble":{"type":"boolean","description":"For Sources that support manual listening, should MS default to scrobbling when no user interaction has occurred?\n\nIf not specified MS will use a Source's specific behavior, see Source's documentation."},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"IcecastSourceOptions"},"AzuracastSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/AzuracastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"AzuracastSourceConfig"},"AzuracastData":{"type":"object","properties":{"url":{"type":"string","description":"Base URL of the Azuracast instance\n\nThis does NOT include the station. If a station is included it will be ignored. Use `station` field to specify station, if necessary","examples":["https://radio.mydomain.tld","http://localhost:80"]},"station":{"type":"string","description":"The specific station to monitor\n\nScrobbling will only occur if any of the monitor conditions are met AND the station is ONLINE.\n\nTo monitor multiple stations create a Source for each station.","examples":["my-station-1"]},"monitorWhenListeners":{"type":["boolean","number"],"description":"Only activate scrobble monitoring if station\n\n* `true` => has any current listeners\n* `number` => has EQUAL TO or MORE THAN X number of listeners"},"monitorWhenLive":{"type":"boolean","description":"Only activate scrobble monitoring if station has a live DJ/Streamer","default":true},"apiKey":{"type":"string","description":"API Key used to access data about private streams\n\nhttps://www.azuracast.com/docs/developers/apis/#api-authentication"}},"required":["url","station"],"title":"AzuracastData"},"KoitoSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/KoitoSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"configureAs":{"type":"string","enum":["source"],"description":"When used in `koito.config` this tells multi-scrobbler whether to use this data to configure a source or client.","default":"source","examples":["source"]}},"required":["data"],"title":"KoitoSourceConfig"},"KoitoSourceData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for the Koito server","examples":["http://192.168.0.100:4110"]},"token":{"type":"string","description":"User token for the user to scrobble for","examples":["pM195xPV98CDpk0QW47FIIOR8AKATAX5DblBF-Jq0t1MbbKL"]},"username":{"type":"string","description":"Username of the user to scrobble for"}},"required":["token","url","username"],"title":"KoitoSourceData"}}} \ No newline at end of file diff --git a/src/backend/common/schema/aio-client.json b/src/backend/common/schema/aio-client.json index 86fe28bd..4c4e122f 100644 --- a/src/backend/common/schema/aio-client.json +++ b/src/backend/common/schema/aio-client.json @@ -1 +1 @@ -{"$schema":"http://json-schema.org/draft-07/schema#","type":"object","properties":{"clientDefaults":{"$ref":"#/definitions/RequestRetryOptions"},"clients":{"type":"array","items":{"$ref":"#/definitions/ClientAIOConfig"}}},"definitions":{"RequestRetryOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]}},"title":"RequestRetryOptions"},"ClientAIOConfig":{"anyOf":[{"$ref":"#/definitions/MalojaClientAIOConfig"},{"$ref":"#/definitions/LastfmClientAIOConfig"},{"$ref":"#/definitions/ListenBrainzClientAIOConfig"}],"title":"ClientAIOConfig"},"MalojaClientAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this client. Used with sources to restrict where scrobbles are sent.","examples":["MyConfig"]},"data":{"$ref":"#/definitions/MalojaClientData","description":"Specific data required to configure this client"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"options":{"$ref":"#/definitions/CommonClientOptions"},"type":{"type":"string","enum":["maloja"]}},"required":["data","name","type"],"title":"MalojaClientAIOConfig"},"MalojaClientData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for maloja server","examples":["http://localhost:42010"]},"apiKey":{"type":"string","description":"API Key for Maloja server","examples":["myApiKey"]}},"required":["url","apiKey"],"title":"MalojaClientData"},"CommonClientOptions":{"type":"object","properties":{"refreshEnabled":{"type":"boolean","description":"Try to get fresh scrobble history from client when tracks to be scrobbled are newer than the last scrobble found in client history","default":true,"examples":[true]},"refreshStaleAfter":{"type":"number","description":"Refresh scrobbled plays from upstream service if last refresh was at least X seconds ago\n\n**In most case this setting does NOT need to be changed.** The default value is sufficient for the majority of use-cases. Increasing this setting may increase upstream service load and slow down scrobbles.\n\nThis setting should only be changed in specific scenarios where MS is handling multiple \"relaying\" client-services (IE lfm -> lz -> lfm) and there is the potential for a client to be out of sync after more than a few seconds.","examples":[60],"default":60},"refreshMinInterval":{"type":"number","description":"Minimum time (milliseconds) required to pass before upstream scrobbles can be refreshed.\n\n**In most case this setting does NOT need to be changed.** This will always be equal to or smaller than `refreshStaleAfter`.","default":5000,"examples":[5000]},"refreshInitialCount":{"type":"number","description":"The number of tracks to retrieve on initial refresh (related to scrobbleBacklogCount). If not specified this is the maximum supported by the client in 1 API call."},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"checkExistingScrobbles":{"type":"boolean","description":"Check client for an existing scrobble at the same recorded time as the \"new\" track to be scrobbled. If an existing scrobble is found this track is not track scrobbled.","default":true,"examples":[true]},"verbose":{"type":"object","properties":{"match":{"$ref":"#/definitions/MatchLoggingOptions"}},"description":"Options used for increasing verbosity of logging in MS (used for debugging)"},"deadLetterRetries":{"type":"number","description":"Number of times MS should automatically retry scrobbles in dead letter queue","default":1,"examples":[1]},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"CommonClientOptions"},"MatchLoggingOptions":{"type":"object","properties":{"onNoMatch":{"type":"boolean","description":"Log to DEBUG when a new track does NOT match an existing scrobble","default":false,"examples":[false]},"onMatch":{"type":"boolean","description":"Log to DEBUG when a new track DOES match an existing scrobble","default":false,"examples":[false]},"confidenceBreakdown":{"type":"boolean","description":"Include confidence breakdowns in track match logging, if applicable","default":false,"examples":[false]}},"description":"Scrobble matching (between new source track and existing client scrobbles) logging options. Used for debugging.","title":"MatchLoggingOptions"},"PlayTransformOptions":{"type":"object","properties":{"log":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["all"]}]},"preCompare":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"},"compare":{"type":"object","properties":{"candidate":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"},"existing":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"}}},"postCompare":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"}},"title":"PlayTransformOptions"},"PlayTransformPartsConfig<SearchAndReplaceTerm>":{"anyOf":[{"$ref":"#/definitions/PlayTransformPartsArray%3CSearchAndReplaceTerm%3E"},{"$ref":"#/definitions/PlayTransformParts%3CSearchAndReplaceTerm%3E"}],"title":"PlayTransformPartsConfig<SearchAndReplaceTerm>"},"PlayTransformPartsArray<SearchAndReplaceTerm>":{"type":"array","items":{"$ref":"#/definitions/PlayTransformParts%3CSearchAndReplaceTerm%3E"},"title":"PlayTransformPartsArray<SearchAndReplaceTerm>"},"PlayTransformParts<SearchAndReplaceTerm>":{"type":"object","properties":{"when":{"$ref":"#/definitions/WhenConditionsConfig"},"title":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}},"artists":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}},"album":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}}},"title":"PlayTransformParts<SearchAndReplaceTerm>"},"WhenConditionsConfig":{"$ref":"#/definitions/WhenConditions%3Cstring%3E","title":"WhenConditionsConfig"},"WhenConditions<string>":{"type":"array","items":{"$ref":"#/definitions/WhenParts%3Cstring%3E"},"title":"WhenConditions<string>"},"WhenParts<string>":{"$ref":"#/definitions/PlayTransformPartsAtomic%3Cstring%3E","title":"WhenParts<string>"},"PlayTransformPartsAtomic<string>":{"type":"object","properties":{"title":{"type":"string"},"artists":{"type":"string"},"album":{"type":"string"}},"title":"PlayTransformPartsAtomic<string>"},"SearchAndReplaceTerm":{"anyOf":[{"type":"string"},{"$ref":"#/definitions/ConditionalSearchAndReplaceTerm"}],"title":"SearchAndReplaceTerm"},"ConditionalSearchAndReplaceTerm":{"type":"object","properties":{"when":{"$ref":"#/definitions/WhenConditionsConfig"},"search":{},"replace":{}},"required":["search","replace"],"title":"ConditionalSearchAndReplaceTerm"},"LastfmClientAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this client. Used with sources to restrict where scrobbles are sent.","examples":["MyConfig"]},"data":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"apiKey":{"type":"string","description":"API Key generated from Last.fm account","examples":["787c921a2a2ab42320831aba0c8f2fc2"]},"secret":{"type":"string","description":"Secret generated from Last.fm account","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"session":{"type":"string","description":"Optional session id returned from a completed auth flow"},"redirectUri":{"type":"string","description":"Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.","default":"http://localhost:9078/lastfm/callback","examples":["http://localhost:9078/lastfm/callback"]}},"required":["apiKey","secret"],"description":"Specific data required to configure this client"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"options":{"$ref":"#/definitions/CommonClientOptions"},"configureAs":{"type":"string","enum":["client","source"],"description":"Should always be `client` when using LastFM as a client","default":"client","examples":["client"]},"type":{"type":"string","enum":["lastfm"]}},"required":["data","name","type"],"title":"LastfmClientAIOConfig"},"ListenBrainzClientAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this client. Used with sources to restrict where scrobbles are sent.","examples":["MyConfig"]},"data":{"$ref":"#/definitions/ListenBrainzClientData","description":"Specific data required to configure this client"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"options":{"$ref":"#/definitions/CommonClientOptions"},"configureAs":{"type":"string","enum":["client","source"],"description":"Should always be `client` when using Listenbrainz as a client","default":"client","examples":["client"]},"type":{"type":"string","enum":["listenbrainz"]}},"required":["data","name","type"],"title":"ListenBrainzClientAIOConfig"},"ListenBrainzClientData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for the ListenBrainz server, if not using the default","examples":["https://api.listenbrainz.org/"],"default":"https://api.listenbrainz.org/"},"token":{"type":"string","description":"User token for the user to scrobble for","examples":["6794186bf-1157-4de6-80e5-uvb411f3ea2b"]},"username":{"type":"string","description":"Username of the user to scrobble for"}},"required":["token","username"],"title":"ListenBrainzClientData"}}} \ No newline at end of file +{"$schema":"http://json-schema.org/draft-07/schema#","type":"object","properties":{"clientDefaults":{"$ref":"#/definitions/RequestRetryOptions"},"clients":{"type":"array","items":{"$ref":"#/definitions/ClientAIOConfig"}}},"definitions":{"RequestRetryOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]}},"title":"RequestRetryOptions"},"ClientAIOConfig":{"anyOf":[{"$ref":"#/definitions/MalojaClientAIOConfig"},{"$ref":"#/definitions/LastfmClientAIOConfig"},{"$ref":"#/definitions/ListenBrainzClientAIOConfig"},{"$ref":"#/definitions/KoitoClientAIOConfig"}],"title":"ClientAIOConfig"},"MalojaClientAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this client. Used with sources to restrict where scrobbles are sent.","examples":["MyConfig"]},"data":{"$ref":"#/definitions/MalojaClientData","description":"Specific data required to configure this client"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"options":{"$ref":"#/definitions/CommonClientOptions"},"configureAs":{"type":"string","enum":["client","source"],"description":"Should always be `client` when using Maloja as a client","default":"client","examples":["client"]},"type":{"type":"string","enum":["maloja"]}},"required":["data","name","type"],"title":"MalojaClientAIOConfig"},"MalojaClientData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for maloja server","examples":["http://localhost:42010"]},"apiKey":{"type":"string","description":"API Key for Maloja server","examples":["myApiKey"]}},"required":["apiKey","url"],"title":"MalojaClientData"},"CommonClientOptions":{"type":"object","properties":{"refreshEnabled":{"type":"boolean","description":"Try to get fresh scrobble history from client when tracks to be scrobbled are newer than the last scrobble found in client history","default":true,"examples":[true]},"refreshStaleAfter":{"type":"number","description":"Refresh scrobbled plays from upstream service if last refresh was at least X seconds ago\n\n**In most case this setting does NOT need to be changed.** The default value is sufficient for the majority of use-cases. Increasing this setting may increase upstream service load and slow down scrobbles.\n\nThis setting should only be changed in specific scenarios where MS is handling multiple \"relaying\" client-services (IE lfm -> lz -> lfm) and there is the potential for a client to be out of sync after more than a few seconds.","examples":[60],"default":60},"refreshMinInterval":{"type":"number","description":"Minimum time (milliseconds) required to pass before upstream scrobbles can be refreshed.\n\n**In most case this setting does NOT need to be changed.** This will always be equal to or smaller than `refreshStaleAfter`.","default":5000,"examples":[5000]},"refreshInitialCount":{"type":"number","description":"The number of tracks to retrieve on initial refresh (related to scrobbleBacklogCount). If not specified this is the maximum supported by the client in 1 API call."},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"checkExistingScrobbles":{"type":"boolean","description":"Check client for an existing scrobble at the same recorded time as the \"new\" track to be scrobbled. If an existing scrobble is found this track is not track scrobbled.","default":true,"examples":[true]},"verbose":{"type":"object","properties":{"match":{"$ref":"#/definitions/MatchLoggingOptions"}},"description":"Options used for increasing verbosity of logging in MS (used for debugging)"},"deadLetterRetries":{"type":"number","description":"Number of times MS should automatically retry scrobbles in dead letter queue","default":1,"examples":[1]},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"CommonClientOptions"},"MatchLoggingOptions":{"type":"object","properties":{"onNoMatch":{"type":"boolean","description":"Log to DEBUG when a new track does NOT match an existing scrobble","default":false,"examples":[false]},"onMatch":{"type":"boolean","description":"Log to DEBUG when a new track DOES match an existing scrobble","default":false,"examples":[false]},"confidenceBreakdown":{"type":"boolean","description":"Include confidence breakdowns in track match logging, if applicable","default":false,"examples":[false]}},"description":"Scrobble matching (between new source track and existing client scrobbles) logging options. Used for debugging.","title":"MatchLoggingOptions"},"PlayTransformOptions":{"type":"object","properties":{"log":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["all"]}]},"preCompare":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"},"compare":{"type":"object","properties":{"candidate":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"},"existing":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"}}},"postCompare":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"}},"title":"PlayTransformOptions"},"PlayTransformPartsConfig<SearchAndReplaceTerm>":{"anyOf":[{"$ref":"#/definitions/PlayTransformPartsArray%3CSearchAndReplaceTerm%3E"},{"$ref":"#/definitions/PlayTransformParts%3CSearchAndReplaceTerm%3E"}],"title":"PlayTransformPartsConfig<SearchAndReplaceTerm>"},"PlayTransformPartsArray<SearchAndReplaceTerm>":{"type":"array","items":{"$ref":"#/definitions/PlayTransformParts%3CSearchAndReplaceTerm%3E"},"title":"PlayTransformPartsArray<SearchAndReplaceTerm>"},"PlayTransformParts<SearchAndReplaceTerm>":{"type":"object","properties":{"when":{"$ref":"#/definitions/WhenConditionsConfig"},"title":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}},"artists":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}},"album":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}}},"title":"PlayTransformParts<SearchAndReplaceTerm>"},"WhenConditionsConfig":{"$ref":"#/definitions/WhenConditions%3Cstring%3E","title":"WhenConditionsConfig"},"WhenConditions<string>":{"type":"array","items":{"$ref":"#/definitions/WhenParts%3Cstring%3E"},"title":"WhenConditions<string>"},"WhenParts<string>":{"$ref":"#/definitions/PlayTransformPartsAtomic%3Cstring%3E","title":"WhenParts<string>"},"PlayTransformPartsAtomic<string>":{"type":"object","properties":{"title":{"type":"string"},"artists":{"type":"string"},"album":{"type":"string"}},"title":"PlayTransformPartsAtomic<string>"},"SearchAndReplaceTerm":{"anyOf":[{"type":"string"},{"$ref":"#/definitions/ConditionalSearchAndReplaceTerm"}],"title":"SearchAndReplaceTerm"},"ConditionalSearchAndReplaceTerm":{"type":"object","properties":{"when":{"$ref":"#/definitions/WhenConditionsConfig"},"search":{},"replace":{}},"required":["search","replace"],"title":"ConditionalSearchAndReplaceTerm"},"LastfmClientAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this client. Used with sources to restrict where scrobbles are sent.","examples":["MyConfig"]},"data":{"$ref":"#/definitions/LastfmData","description":"Specific data required to configure this client"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"options":{"$ref":"#/definitions/LastfmClientOptions"},"configureAs":{"type":"string","enum":["client","source"],"description":"Should always be `client` when using LastFM as a client","default":"client","examples":["client"]},"type":{"type":"string","enum":["lastfm"]}},"required":["data","name","type"],"title":"LastfmClientAIOConfig"},"LastfmData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"apiKey":{"type":"string","description":"API Key generated from Last.fm account","examples":["787c921a2a2ab42320831aba0c8f2fc2"]},"secret":{"type":"string","description":"Secret generated from Last.fm account","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"session":{"type":"string","description":"Optional session id returned from a completed auth flow"},"redirectUri":{"type":"string","description":"Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.","default":"http://localhost:9078/lastfm/callback","examples":["http://localhost:9078/lastfm/callback"]}},"required":["apiKey","secret"],"title":"LastfmData"},"LastfmClientOptions":{"type":"object","properties":{"nowPlaying":{"anyOf":[{"type":"boolean"},{"type":"array","items":{"type":"string"}}],"description":"Configure if this Client should report Now Playing from Sources that can scrobble to it\n\n* `true` (default) => Report Now Playing from any eligible Source. \n * If multiple Sources are Playing then reported Play is based on alphabetical order of Source names\n* `false` => Do not report Now Playing\n* `string` list => list of Source `names` that should be allowed to report Now Playing. Order of list determine priority of Play to Report.","default":true},"refreshEnabled":{"type":"boolean","description":"Try to get fresh scrobble history from client when tracks to be scrobbled are newer than the last scrobble found in client history","default":true,"examples":[true]},"refreshStaleAfter":{"type":"number","description":"Refresh scrobbled plays from upstream service if last refresh was at least X seconds ago\n\n**In most case this setting does NOT need to be changed.** The default value is sufficient for the majority of use-cases. Increasing this setting may increase upstream service load and slow down scrobbles.\n\nThis setting should only be changed in specific scenarios where MS is handling multiple \"relaying\" client-services (IE lfm -> lz -> lfm) and there is the potential for a client to be out of sync after more than a few seconds.","examples":[60],"default":60},"refreshMinInterval":{"type":"number","description":"Minimum time (milliseconds) required to pass before upstream scrobbles can be refreshed.\n\n**In most case this setting does NOT need to be changed.** This will always be equal to or smaller than `refreshStaleAfter`.","default":5000,"examples":[5000]},"refreshInitialCount":{"type":"number","description":"The number of tracks to retrieve on initial refresh (related to scrobbleBacklogCount). If not specified this is the maximum supported by the client in 1 API call."},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"checkExistingScrobbles":{"type":"boolean","description":"Check client for an existing scrobble at the same recorded time as the \"new\" track to be scrobbled. If an existing scrobble is found this track is not track scrobbled.","default":true,"examples":[true]},"verbose":{"type":"object","properties":{"match":{"$ref":"#/definitions/MatchLoggingOptions"}},"description":"Options used for increasing verbosity of logging in MS (used for debugging)"},"deadLetterRetries":{"type":"number","description":"Number of times MS should automatically retry scrobbles in dead letter queue","default":1,"examples":[1]},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"LastfmClientOptions"},"ListenBrainzClientAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this client. Used with sources to restrict where scrobbles are sent.","examples":["MyConfig"]},"data":{"$ref":"#/definitions/ListenBrainzClientData","description":"Specific data required to configure this client"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"options":{"$ref":"#/definitions/CommonClientOptions"},"configureAs":{"type":"string","enum":["client","source"],"description":"Should always be `client` when using Listenbrainz as a client","default":"client","examples":["client"]},"type":{"type":"string","enum":["listenbrainz"]}},"required":["data","name","type"],"title":"ListenBrainzClientAIOConfig"},"ListenBrainzClientData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for the ListenBrainz server, if not using the default","examples":["https://api.listenbrainz.org/"],"default":"https://api.listenbrainz.org/"},"token":{"type":"string","description":"User token for the user to scrobble for","examples":["6794186bf-1157-4de6-80e5-uvb411f3ea2b"]},"username":{"type":"string","description":"Username of the user to scrobble for"}},"required":["token","username"],"title":"ListenBrainzClientData"},"KoitoClientAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this client. Used with sources to restrict where scrobbles are sent.","examples":["MyConfig"]},"data":{"$ref":"#/definitions/KoitoClientData","description":"Specific data required to configure this client"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"options":{"$ref":"#/definitions/CommonClientOptions"},"configureAs":{"type":"string","enum":["client","source"],"description":"Should always be `client` when using Koito as a client","default":"client","examples":["client"]},"type":{"type":"string","enum":["koito"]}},"required":["data","name","type"],"title":"KoitoClientAIOConfig"},"KoitoClientData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for the Koito server","examples":["http://192.168.0.100:4110"]},"token":{"type":"string","description":"User token for the user to scrobble for","examples":["pM195xPV98CDpk0QW47FIIOR8AKATAX5DblBF-Jq0t1MbbKL"]},"username":{"type":"string","description":"Username of the user to scrobble for"}},"required":["token","url","username"],"title":"KoitoClientData"}}} \ No newline at end of file diff --git a/src/backend/common/schema/aio-source.json b/src/backend/common/schema/aio-source.json index 3f6654d3..cb7d81e7 100644 --- a/src/backend/common/schema/aio-source.json +++ b/src/backend/common/schema/aio-source.json @@ -1 +1 @@ -{"$schema":"http://json-schema.org/draft-07/schema#","type":"object","properties":{"sourceDefaults":{"$ref":"#/definitions/SourceRetryOptions"},"sources":{"type":"array","items":{"$ref":"#/definitions/SourceAIOConfig"}}},"definitions":{"SourceRetryOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]}},"title":"SourceRetryOptions"},"SourceAIOConfig":{"anyOf":[{"$ref":"#/definitions/SpotifySourceAIOConfig"},{"$ref":"#/definitions/PlexSourceAIOConfig"},{"$ref":"#/definitions/PlexApiSourceAIOConfig"},{"$ref":"#/definitions/TautulliSourceAIOConfig"},{"$ref":"#/definitions/DeezerSourceAIOConfig"},{"$ref":"#/definitions/ListenbrainzEndpointSourceAIOConfig"},{"$ref":"#/definitions/LastFMEndpointSourceAIOConfig"},{"$ref":"#/definitions/SubsonicSourceAIOConfig"},{"$ref":"#/definitions/JellySourceAIOConfig"},{"$ref":"#/definitions/JellyApiSourceAIOConfig"},{"$ref":"#/definitions/LastFmSouceAIOConfig"},{"$ref":"#/definitions/YTMusicSourceAIOConfig"},{"$ref":"#/definitions/MPRISSourceAIOConfig"},{"$ref":"#/definitions/MopidySourceAIOConfig"},{"$ref":"#/definitions/ListenBrainzSourceAIOConfig"},{"$ref":"#/definitions/JRiverSourceAIOConfig"},{"$ref":"#/definitions/KodiSourceAIOConfig"},{"$ref":"#/definitions/WebScrobblerSourceAIOConfig"},{"$ref":"#/definitions/ChromecastSourceAIOConfig"},{"$ref":"#/definitions/MusikcubeSourceAIOConfig"},{"$ref":"#/definitions/MusicCastSourceAIOConfig"},{"$ref":"#/definitions/MPDSourceAIOConfig"},{"$ref":"#/definitions/VLCSourceAIOConfig"},{"$ref":"#/definitions/IcecastSourceAIOConfig"},{"$ref":"#/definitions/AzuracastSourceAIOConfig"}],"title":"SourceAIOConfig"},"SpotifySourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/SpotifySourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["spotify"]}},"required":["data","type"],"title":"SpotifySourceAIOConfig"},"SpotifySourceData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)\n\nIt is unlikely you should need to change this unless you scrobble many very short tracks often\n\nReading:\n* https://developer.spotify.com/documentation/web-api/guides/rate-limits/\n* https://medium.com/mendix/limiting-your-amount-of-calls-in-mendix-most-of-the-time-rest-835dde55b10e\n * Rate limit may ~180 req/min\n* https://community.spotify.com/t5/Spotify-for-Developers/Web-API-ratelimit/m-p/5503150/highlight/true#M7930\n * Informally indicated as 20 req/sec? Probably for burstiness","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"clientId":{"type":"string","description":"spotify client id","examples":["787c921a2a2ab42320831aba0c8f2fc2"]},"clientSecret":{"type":"string","description":"spotify client secret","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"redirectUri":{"type":"string","description":"spotify redirect URI -- required only if not the default shown here. URI must end in \"callback\"","default":"http://localhost:9078/callback","examples":["http://localhost:9078/callback"]}},"required":["clientId","clientSecret"],"title":"SpotifySourceData"},"CommonSourceOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"CommonSourceOptions"},"LogLevel":{"type":"string","enum":["silent","fatal","error","warn","info","log","verbose","debug"],"description":"Names of log levels that can be invoked on the logger\n\nFrom lowest to highest:\n\n* `debug`\n* `verbose`\n* `log`\n* `info`\n* `warn`\n* `error`\n* `fatal`\n* `silent` (will never output anything)\n\nWhen used in `LogOptions` specifies the **minimum** level the output should log at.","title":"LogLevel"},"FileLogOptions":{"type":"object","properties":{"timestamp":{"type":"string","enum":["unix","iso","auto"],"description":"For rolling log files\n\nWhen\n* value passed to rolling destination is a string (`path` from LogOptions is a string) and\n* `frequency` is defined\n\nThis determines the format of the datetime inserted into the log file name:\n\n* `unix` - unix epoch timestamp in milliseconds\n* `iso` - Full [ISO8601](https://en.wikipedia.org/wiki/ISO_8601) datetime IE '2024-03-07T20:11:34Z'\n* `auto`\n * When frequency is `daily` only inserts date IE YYYY-MM-DD\n * Otherwise inserts full ISO8601 datetime","default":"auto"},"size":{"type":["number","string"],"description":"The maximum size of a given rolling log file.\n\nCan be combined with frequency. Use k, m and g to express values in KB, MB or GB.\n\nNumerical values will be considered as MB.","default":"10MB"},"frequency":{"anyOf":[{"type":"string","enum":["daily"]},{"type":"string","enum":["hourly"]},{"type":"number"}],"description":"The amount of time a given rolling log file is used. Can be combined with size.\n\nUse `daily` or `hourly` to rotate file every day (or every hour). Existing file within the current day (or hour) will be re-used.\n\nNumerical values will be considered as a number of milliseconds. Using a numerical value will always create a new file upon startup.","default":"daily"},"path":{"anyOf":[{"type":"string"},{"$comment":"() => string"}],"description":"The path and filename to use for log files.\n\nIf using rolling files the filename will be appended with `.N` (a number) BEFORE the extension based on rolling status.\n\nMay also be specified using env LOG_PATH or a function that returns a string.\n\nIf path is relative the absolute path will be derived from `logBaseDir` (in `LoggerAppExtras`) which defaults to CWD","default":"./logs/app.log"},"level":{"anyOf":[{"$ref":"#/definitions/LogLevel"},{"type":"boolean","const":false}],"description":"Specify the minimum log level to output to rotating files. If `false` no log files will be created."}},"title":"FileLogOptions"},"ScrobbleThresholds":{"type":"object","properties":{"duration":{"type":["number","null"],"description":"The number of seconds a track has been listened to before it should be considered scrobbled.\n\nSet to null to disable.","default":240,"examples":[240]},"percent":{"type":["number","null"],"description":"The percentage (as an integer) of a track that should have been seen played before it should be scrobbled. Only used if the Source provides information about how long the track is.\n\nSet to null to disable.\n\nNOTE: This should be used with care when the Source is a \"polling\" type (has an 'interval' property). If the track is short and the interval is too high MS may ignore the track if percentage is high because it had not \"seen\" the track for long enough from first discovery, even if you have been playing the track for longer.","default":50,"examples":[50]}},"title":"ScrobbleThresholds"},"PlayTransformOptions":{"type":"object","properties":{"log":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["all"]}]},"preCompare":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"},"compare":{"type":"object","properties":{"candidate":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"},"existing":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"}}},"postCompare":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"}},"title":"PlayTransformOptions"},"PlayTransformPartsConfig<SearchAndReplaceTerm>":{"anyOf":[{"$ref":"#/definitions/PlayTransformPartsArray%3CSearchAndReplaceTerm%3E"},{"$ref":"#/definitions/PlayTransformParts%3CSearchAndReplaceTerm%3E"}],"title":"PlayTransformPartsConfig<SearchAndReplaceTerm>"},"PlayTransformPartsArray<SearchAndReplaceTerm>":{"type":"array","items":{"$ref":"#/definitions/PlayTransformParts%3CSearchAndReplaceTerm%3E"},"title":"PlayTransformPartsArray<SearchAndReplaceTerm>"},"PlayTransformParts<SearchAndReplaceTerm>":{"type":"object","properties":{"when":{"$ref":"#/definitions/WhenConditionsConfig"},"title":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}},"artists":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}},"album":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}}},"title":"PlayTransformParts<SearchAndReplaceTerm>"},"WhenConditionsConfig":{"$ref":"#/definitions/WhenConditions%3Cstring%3E","title":"WhenConditionsConfig"},"WhenConditions<string>":{"type":"array","items":{"$ref":"#/definitions/WhenParts%3Cstring%3E"},"title":"WhenConditions<string>"},"WhenParts<string>":{"$ref":"#/definitions/PlayTransformPartsAtomic%3Cstring%3E","title":"WhenParts<string>"},"PlayTransformPartsAtomic<string>":{"type":"object","properties":{"title":{"type":"string"},"artists":{"type":"string"},"album":{"type":"string"}},"title":"PlayTransformPartsAtomic<string>"},"SearchAndReplaceTerm":{"anyOf":[{"type":"string"},{"$ref":"#/definitions/ConditionalSearchAndReplaceTerm"}],"title":"SearchAndReplaceTerm"},"ConditionalSearchAndReplaceTerm":{"type":"object","properties":{"when":{"$ref":"#/definitions/WhenConditionsConfig"},"search":{},"replace":{}},"required":["search","replace"],"title":"ConditionalSearchAndReplaceTerm"},"PlexSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/PlexSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["plex"]}},"required":["data","type"],"title":"PlexSourceAIOConfig"},"PlexSourceData":{"type":"object","properties":{"user":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled","examples":[["MyUser1","MyUser2"]]},"libraries":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of libraries to scrobble tracks from\n\nIf none are provided tracks from all libraries will be scrobbled","examples":[["Audio","Music"]]},"servers":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled","examples":[["MyServerName"]]}},"title":"PlexSourceData"},"PlexApiSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/PlexApiData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/PlexApiOptions"},"type":{"type":"string","enum":["plex"]}},"required":["data","options","type"],"title":"PlexApiSourceAIOConfig"},"PlexApiData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"token":{"type":"string"},"url":{"type":"string","description":"http(s)://HOST:PORT of the Plex server to connect to"},"usersAllow":{"anyOf":[{"type":"string"},{"type":"boolean","enum":[true]},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble for specific users (case-insensitive)\n\nIf `true` MS will scrobble activity from all users"},"usersBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble for these users (case-insensitive)"},"devicesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if device or application name contains strings from this list (case-insensitive)"},"devicesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if device or application name contains strings from this list (case-insensitive)"},"librariesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if library name contains string from this list (case-insensitive)"},"librariesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if library name contains strings from this list (case-insensitive)"}},"required":["url"],"title":"PlexApiData"},"PlexApiOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"PlexApiOptions"},"TautulliSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/PlexSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["tautulli"]}},"required":["data","type"],"title":"TautulliSourceAIOConfig"},"DeezerSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/DeezerData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["deezer"]}},"required":["data","type"],"title":"DeezerSourceAIOConfig"},"DeezerData":{"type":"object","properties":{"clientId":{"type":"string","description":"deezer client id","examples":["a89cba1569901a0671d5a9875fed4be1"]},"clientSecret":{"type":"string","description":"deezer client secret","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"redirectUri":{"type":"string","description":"deezer redirect URI -- required only if not the default shown here. URI must end in \"callback\"","default":"http://localhost:9078/deezer/callback","examples":["http://localhost:9078/deezer/callback"]},"interval":{"type":"number","description":"optional, how long to wait before calling spotify for new tracks (in seconds)","default":60,"examples":[60]}},"required":["clientId","clientSecret"],"title":"DeezerData"},"ListenbrainzEndpointSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/ListenbrainzEndpointData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["endpointlz"]}},"required":["type"],"title":"ListenbrainzEndpointSourceAIOConfig"},"ListenbrainzEndpointData":{"type":"object","properties":{"slug":{"type":["string","null"],"description":"The URL ending that should be used to identify scrobbles for this source\n\nIf you are using multiple Listenbrainz endpoint sources (scrobbles for many users) you can use a slug to match Sources with individual users/origins\n\nExample:\n\n* slug: 'usera' => API URL: http://localhost:9078/api/listenbrainz/usera\n* slug: 'originb' => API URL: http://localhost:9078/api/listenbrainz/originb\n\nIf no slug is found from an extension's incoming webhook event the first Listenbrainz source without a slug will be used"},"token":{"type":["string","null"],"description":"If an LZ submission request contains this token in the Authorization Header it will be used to match the submission with this Source\n\nSee: https://listenbrainz.readthedocs.io/en/latest/users/api/index.html#add-the-user-token-to-your-requests"}},"title":"ListenbrainzEndpointData"},"LastFMEndpointSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/LastFMEndpointData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["endpointlfm"]}},"required":["type"],"title":"LastFMEndpointSourceAIOConfig"},"LastFMEndpointData":{"type":"object","properties":{"slug":{"type":["string","null"],"description":"The URL ending that should be used to identify scrobbles for this source\n\nIf you are using multiple Last.fm endpoint sources (scrobbles for many users) you can use a slug to match Sources with individual users/origins\n\nExample:\n\n* slug: 'usera' => API URL: http://localhost:9078/api/lastfm/usera\n* slug: 'originb' => API URL: http://localhost:9078/api/lastfm/originb\n\nIf no slug is found from an extension's incoming webhook event the first Last.fm source without a slug will be used"}},"title":"LastFMEndpointData"},"SubsonicSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/SubsonicData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["subsonic"]}},"required":["data","type"],"title":"SubsonicSourceAIOConfig"},"SubsonicData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the subsonic media server to query","examples":["http://airsonic.local"]},"user":{"type":"string","description":"Username to login to the server with","examples":[["MyUser"]]},"password":{"type":"string","description":"Password for the user to login to the server with","examples":["MyPassword"]},"ignoreTlsErrors":{"type":"boolean","description":"If your subsonic server is using self-signed certs you may need to disable TLS errors in order to get a connection\n\nWARNING: This should be used with caution as your traffic may not be encrypted.","default":false},"legacyAuthentication":{"type":"boolean","description":"Older Subsonic versions, and some badly implemented servers (Nextcloud), use legacy authentication which sends your password in CLEAR TEXT. This is less secure than the newer, recommended hashing authentication method but in some cases it is needed. See \"Authentication\" section here => https://www.subsonic.org/pages/api.jsp\n\nIf this option is not specified it will be turned on if the subsonic server responds with error code 41 \"Token authentication not supported for LDAP users.\" -- See Error Handling section => https://www.subsonic.org/pages/api.jsp","default":false}},"required":["url","user","password"],"title":"SubsonicData"},"JellySourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/JellyData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["jellyfin"]}},"required":["data","type"],"title":"JellySourceAIOConfig"},"JellyData":{"type":"object","properties":{"users":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled","examples":[["MyUser1","MyUser2"]]},"servers":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled","examples":[["MyServerName1"]]}},"title":"JellyData"},"JellyApiSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/JellyApiData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/JellyApiOptions"},"type":{"type":"string","enum":["jellyfin"]}},"required":["data","options","type"],"title":"JellyApiSourceAIOConfig"},"JellyApiData":{"type":"object","properties":{"url":{"type":"string","description":"HOST:PORT of the Jellyfin server to connect to"},"user":{"type":"string","description":"The username of the user to authenticate for or track scrobbles for"},"password":{"type":"string","description":"Password of the username to authenticate for\n\nRequired if `apiKey` is not provided."},"apiKey":{"type":"string","description":"API Key to authenticate with.\n\nRequired if `password` is not provided."},"usersAllow":{"anyOf":[{"type":"string"},{"type":"boolean","enum":[true]},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble for specific users (case-insensitive)\n\nIf `true` MS will scrobble activity from all users"},"usersBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble for these users (case-insensitive)"},"devicesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if device or application name contains strings from this list (case-insensitive)"},"devicesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if device or application name contains strings from this list (case-insensitive)"},"librariesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if library name contains string from this list (case-insensitive)"},"librariesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if library name contains strings from this list (case-insensitive)"},"additionalAllowedLibraryTypes":{"type":"array","items":{},"description":"Allow MS to scrobble audio media in libraries classified other than 'music'\n\n`librariesAllow` will achieve the same result as this but this is more convenient if you do not want to explicitly list every library name or are only using `librariesBlock`"},"allowUnknown":{"type":"boolean","description":"Force media with a type of \"Unknown\" to be counted as Audio","default":false}},"required":["url","user"],"title":"JellyApiData"},"JellyApiOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"JellyApiOptions"},"LastFmSouceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/LastFmSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"configureAs":{"type":"string","enum":["source"],"description":"When used in `lastfm.config` this tells multi-scrobbler whether to use this data to configure a source or client.","default":"source","examples":["source"]},"type":{"type":"string","enum":["lastfm"]}},"required":["data","type"],"title":"LastFmSouceAIOConfig"},"LastFmSourceData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"apiKey":{"type":"string","description":"API Key generated from Last.fm account","examples":["787c921a2a2ab42320831aba0c8f2fc2"]},"secret":{"type":"string","description":"Secret generated from Last.fm account","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"session":{"type":"string","description":"Optional session id returned from a completed auth flow"},"redirectUri":{"type":"string","description":"Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.","default":"http://localhost:9078/lastfm/callback","examples":["http://localhost:9078/lastfm/callback"]},"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"}},"required":["apiKey","secret"],"title":"LastFmSourceData"},"YTMusicSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/YTMusicData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"type":"object","properties":{"logAuth":{"type":"boolean","description":"When true MS will log to DEBUG all of the credentials data it receives from YTM"},"logDiff":{"type":"boolean","description":"Always log history diff\n\nBy default MS will log to `WARN` if history diff is inconsistent but does not log if diff is expected (on new tracks found)\nSet this to `true` to ALWAYS log diff on new tracks. Expected diffs will log to `DEBUG` and inconsistent diffs will continue to log to `WARN`","default":false},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}}},"type":{"type":"string","enum":["ytmusic"]}},"required":["type"],"title":"YTMusicSourceAIOConfig"},"YTMusicData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"cookie":{"type":"string","description":"The cookie retrieved from the Request Headers of music.youtube.com after logging in.\n\nSee https://ytmusicapi.readthedocs.io/en/stable/setup/browser.html#copy-authentication-headers for how to retrieve this value.","examples":["VISITOR_INFO1_LIVE=jMp2xA1Xz2_PbVc; __Secure-3PAPISID=3AxsXpy0M/AkISpjek; ..."]},"clientId":{"type":"string","description":"Google Cloud Console project OAuth Client ID\n\nGenerated from a custom OAuth Client, see docs"},"clientSecret":{"type":"string","description":"Google Cloud Console project OAuth Client Secret\n\nGenerated from a custom OAuth Client, see docs"},"redirectUri":{"type":"string","description":"Google Cloud Console project OAuth Client Authorized redirect URI\n\nGenerated from a custom OAuth Client, see docs. multi-scrobbler will generate a default based on BASE_URL.\nOnly specify this if the default does not work for you."},"innertubeOptions":{"$ref":"#/definitions/InnertubeOptions","description":"Additional options for authorization and tailoring YTM client"}},"title":"YTMusicData"},"InnertubeOptions":{"type":"object","properties":{"po_token":{"type":"string","description":"Proof of Origin token\n\nMay be required if YTM starts returning 403"},"visitor_data":{"type":"string","description":"Visitor ID value found in VISITOR_INFO1_LIVE or visitorData cookie\n\nMay be required if YTM starts returning 403"},"account_index":{"type":"number","description":"If account login results in being able to choose multiple account, use a zero-based index to choose which one to monitor","examples":[0,1]},"location":{"type":"string"},"lang":{"type":"string"},"generate_session_locally":{"type":"boolean"},"device_category":{"type":"string"},"client_type":{"type":"string"},"timezone":{"type":"string"}},"title":"InnertubeOptions"},"MPRISSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MPRISData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["mpris"]}},"required":["data","type"],"title":"MPRISSourceAIOConfig"},"MPRISData":{"type":"object","properties":{"blacklist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"DO NOT scrobble from any players that START WITH these values, case-insensitive","examples":[["spotify","vlc"]]},"whitelist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"ONLY from any players that START WITH these values, case-insensitive\n\nIf whitelist is present then blacklist is ignored","examples":[["spotify","vlc"]]}},"title":"MPRISData"},"MopidySourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MopidyData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["mopidy"]}},"required":["data","type"],"title":"MopidySourceAIOConfig"},"MopidyData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the Mopidy HTTP server to connect to\n\nYou MUST have Mopidy-HTTP extension enabled: https://mopidy.com/ext/http\n\nmulti-scrobbler connects to the WebSocket endpoint that ultimately looks like this => `ws://localhost:6680/mopidy/ws/`\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `ws://`\n* Hostname => `localhost`\n* Port => `6680`\n* Path => `/mopidy/ws/`","examples":["ws://localhost:6680/mopidy/ws/"],"default":"ws://localhost:6680/mopidy/ws/"},"uriBlacklist":{"type":"array","items":{"type":"string"},"description":"Do not scrobble tracks whose URI STARTS WITH any of these strings, case-insensitive\n\nEX: Don't scrobble tracks from soundcloud by adding 'soundcloud' to this list.\n\nList is ignored if uriWhitelist is used."},"uriWhitelist":{"type":"array","items":{"type":"string"},"description":"Only scrobble tracks whose URI STARTS WITH any of these strings, case-insensitive\n\nEX: Only scrobble tracks from soundcloud by adding 'soundcloud' to this list."},"albumBlacklist":{"type":"array","items":{"type":"string"},"description":"Remove album data that matches any case-insensitive string from this list when scrobbling,\n\nFor certain sources (Soundcloud) Mopidy does not have all track info (Album) and will instead use \"Soundcloud\" as the Album name. You can prevent multi-scrobbler from using this bad Album data by adding the fake name to this list. Multi-scrobbler will still scrobble the track, just without the bad data.","examples":[["Soundcloud","Mixcloud"]],"default":["Soundcloud"]}},"title":"MopidyData"},"ListenBrainzSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/ListenBrainzSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"configureAs":{"type":"string","enum":["source"],"description":"When used in `listenbrainz.config` this tells multi-scrobbler whether to use this data to configure a source or client.","default":"source","examples":["source"]},"type":{"type":"string","enum":["listenbrainz"]}},"required":["data","type"],"title":"ListenBrainzSourceAIOConfig"},"ListenBrainzSourceData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for the ListenBrainz server, if not using the default","examples":["https://api.listenbrainz.org/"],"default":"https://api.listenbrainz.org/"},"token":{"type":"string","description":"User token for the user to scrobble for","examples":["6794186bf-1157-4de6-80e5-uvb411f3ea2b"]},"username":{"type":"string","description":"Username of the user to scrobble for"}},"required":["token","username"],"title":"ListenBrainzSourceData"},"JRiverSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/JRiverData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["jriver"]}},"required":["data","type"],"title":"JRiverSourceAIOConfig"},"JRiverData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the JRiver HTTP server to connect to\n\nmulti-scrobbler connects to the Web Service Interface endpoint that ultimately looks like this => `http://yourDomain:52199/MCWS/v1/`\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `http://`\n* Hostname => `localhost`\n* Port => `52199`\n* Path => `/MCWS/v1/`","examples":["http://localhost:52199/MCWS/v1/"],"default":"http://localhost:52199/MCWS/v1/"},"username":{"type":"string","description":"If you have enabled authentication, the username you set"},"password":{"type":"string","description":"If you have enabled authentication, the password you set"}},"required":["url"],"title":"JRiverData"},"KodiSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/KodiData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["kodi"]}},"required":["data","type"],"title":"KodiSourceAIOConfig"},"KodiData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the Kodi HTTP server to connect to\n\nmulti-scrobbler connects to the Web Service Interface endpoint that ultimately looks like this => `http://yourDomain:8080/jsonrpc`\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `http://`\n* Hostname => `localhost`\n* Port => `8080`\n* Path => `/jsonrpc`","examples":["http://localhost:8080/jsonrpc"],"default":"http://localhost:8080/jsonrpc"},"username":{"type":"string","description":"The username set for Remote Control via Web Sever"},"password":{"type":"string","description":"The password set for Remote Control via Web Sever"}},"required":["url","username","password"],"title":"KodiData"},"WebScrobblerSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/WebScrobblerData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["webscrobbler"]}},"required":["type"],"title":"WebScrobblerSourceAIOConfig"},"WebScrobblerData":{"type":"object","properties":{"slug":{"type":["string","null"],"description":"The URL ending that should be used to identify scrobbles for this source\n\nIn WebScrobbler's Webhook you must set an 'API URL'. All MS WebScrobbler sources must start like:\n\nhttp://localhost:9078/api/webscrobbler\n\nIf you are using multiple WebScrobbler sources (scrobbles for many users) you must use a slug to match Sources with each users extension.\n\nExample:\n\n* slug: 'usera' => API URL: http://localhost:9078/api/webscrobbler/usera\n* slug: 'userb' => API URL: http://localhost:9078/api/webscrobbler/userb\n\nIf no slug is found from an extension's incoming webhook event the first WebScrobbler source without a slug will be used"},"blacklist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Block scrobbling from specific WebScrobbler Connectors","examples":[["youtube"]]},"whitelist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only allow scrobbling from specific WebScrobbler Connectors","examples":[["mixcloud","soundcloud","bandcamp"]]}},"title":"WebScrobblerData"},"ChromecastSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/ChromecastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["chromecast"]}},"required":["data","type"],"title":"ChromecastSourceAIOConfig"},"ChromecastData":{"type":"object","properties":{"blacklistDevices":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"DO NOT scrobble from any cast devices that START WITH these values, case-insensitive\n\nUseful when used with auto discovery","examples":[["home-mini","family-tv"]]},"whitelistDevices":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"ONLY scrobble from any cast device that START WITH these values, case-insensitive\n\nIf whitelist is present then blacklist is ignored\n\nUseful when used with auto discovery","examples":[["home-mini","family-tv"]]},"blacklistApps":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"DO NOT scrobble from any application that START WITH these values, case-insensitive","examples":[["spotify","pandora"]]},"whitelistApps":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"ONLY scrobble from any application that START WITH these values, case-insensitive\n\nIf whitelist is present then blacklist is ignored","examples":[["spotify","pandora"]]},"useAvahi":{"type":"boolean","description":"Try to use Avahi and avahi-browse to resolve mDNS devices instead of native mDNS querying\n\nUseful for docker (alpine) container where mDNS resolution is not yet supported. Avahi socket must be exposed to the container and avahi-tools must be installed.","default":false},"useAutoDiscovery":{"type":"boolean","description":"Use mDNS to discovery Google Cast devices on your next automatically?\n\nIf not explicitly set then it is TRUE if `devices` is not set"},"devices":{"type":"array","items":{"$ref":"#/definitions/ChromecastDeviceInfo"},"description":"A list of Google Cast devices to monitor\n\nIf this is used then `useAutoDiscovery` is set to FALSE, if not explicitly set"},"allowUnknownMedia":{"anyOf":[{"type":"boolean"},{"type":"array","items":{"type":"string"}}],"description":"Chromecast Apps report a \"media type\" in the status info returned for whatever is currently playing\n\n* If set to TRUE then Music AND Generic/Unknown media will be tracked for ALL APPS\n* If set to FALSE then only media explicitly typed as Music will be tracked for ALL APPS\n* If set to a list then only Apps whose name contain one of these values, case-insensitive, will have Music AND Generic/Unknown tracked\n\nSee https://developers.google.com/cast/docs/media/messages#MediaInformation \"metadata\" property","default":false},"forceMediaRecognitionOn":{"type":"array","items":{"type":"string"},"description":"Media provided by any App whose name is listed here will ALWAYS be tracked, regardless of the \"media type\" reported\n\nApps will be recognized if they CONTAIN any of these values, case-insensitive"}},"title":"ChromecastData"},"ChromecastDeviceInfo":{"type":"object","properties":{"name":{"type":"string","description":"A friendly name to identify this device","examples":["MySmartTV"]},"address":{"type":"string","description":"The IP address of the device","examples":["192.168.0.115"]}},"required":["name","address"],"title":"ChromecastDeviceInfo"},"MusikcubeSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MusikcubeData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["musikcube"]}},"required":["data","type"],"title":"MusikcubeSourceAIOConfig"},"MusikcubeData":{"type":"object","properties":{"url":{"type":"string","description":"URL of the Musikcube Websocket (Metadata) server to connect to\n\nYou MUST have enabled 'metadata' server and set a password: https://github.com/clangen/musikcube/wiki/remote-api-documentation\n * musikcube -> settings -> server setup\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `ws://`\n* Hostname => `localhost`\n* Port => `7905`","examples":["ws://localhost:7905"],"default":"ws://localhost:7905"},"password":{"type":"string","description":"Password set in Musikcube https://github.com/clangen/musikcube/wiki/remote-api-documentation\n\n* musikcube -> settings -> server setup -> password"},"device_id":{"type":"string"}},"required":["password"],"title":"MusikcubeData"},"MusicCastSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MusicCastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["musiccast"]}},"required":["data","type"],"title":"MusicCastSourceAIOConfig"},"MusicCastData":{"type":"object","properties":{"url":{"type":"string","description":"The host or URL of the YamahaExtendedControl endpoint to use","examples":[["192.168.0.101","http://192.168.0.101/YamahaExtendedControl"]]}},"required":["url"],"title":"MusicCastData"},"MPDSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MPDData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/MPDSourceOptions"},"type":{"type":"string","enum":["mpd"]}},"required":["data","options","type"],"title":"MPDSourceAIOConfig"},"MPDData":{"type":"object","properties":{"url":{"type":"string","description":"URL:PORT of the MPD server to connect to\n\nTo use this you must have TCP connections enabled for your MPD server https://mpd.readthedocs.io/en/stable/user.html#client-connections","examples":["localhost:6600"],"default":"localhost:6600"},"path":{"type":"string","description":"If using socket specify the path instead of url.\n\ntrailing `~` is replaced by your home directory"},"password":{"type":"string","description":"Password for the server, if set https://mpd.readthedocs.io/en/stable/user.html#permissions-and-passwords"}},"title":"MPDData"},"MPDSourceOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"MPDSourceOptions"},"VLCSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/VLCData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/VLCSourceOptions"},"type":{"type":"string","enum":["vlc"]}},"required":["data","type"],"title":"VLCSourceAIOConfig"},"VLCData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL:PORT of the VLC server to connect to\n\nTo use this you must have the Web (http) interface module enabled and a password set https://foxxmd.github.io/multi-scrobbler/docs/configuration#vlc","examples":["localhost:8080"],"default":"localhost:8080"},"password":{"type":"string","description":"Password for the server"}},"required":["password"],"title":"VLCData"},"VLCSourceOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"},"filenamePatterns":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"A list of regular expressions to use to extract metadata (title, album, artist) from a filename\n\nUsed when VLC reports only the filename for the current audio track"},"logFilenamePatterns":{"type":"boolean","description":"Log to DEBUG when a filename-only track is matched or not matched by filenamePatterns","default":false},"dumpVlcMetadata":{"type":"boolean","description":"Dump all the metadata VLC reports for an audio track to DEBUG.\n\nUse this if reporting an issue with VLC not correctly capturing metadata for a track.","default":false}},"title":"VLCSourceOptions"},"IcecastSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/IcecastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/IcecastSourceOptions"},"type":{"type":"string","enum":["icecast"]}},"required":["data","type"],"title":"IcecastSourceAIOConfig"},"IcecastData":{"type":"object","properties":{"sources":{"type":"array","items":{"$ref":"#/definitions/IcecastSource"}},"icestatsEndpoint":{"type":"string"},"statsEndpoint":{"type":"string"},"nextsongsEndpoint":{"type":"string"},"sevenhtmlEndpoint":{"type":"string"},"icyMetaInt":{"type":"number"},"url":{"type":"string","description":"The Icecast stream URL"}},"required":["url"],"title":"IcecastData"},"IcecastSource":{"type":"string","enum":["icy","ogg","icestats","stats","sevenhtml","nextsongs"],"title":"IcecastSource"},"IcecastSourceOptions":{"type":"object","properties":{"systemScrobble":{"type":"boolean","description":"For Sources that support manual listening, should MS default to scrobbling when no user interaction has occurred?\n\nIf not specified MS will use a Source's specific behavior, see Source's documentation."},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"IcecastSourceOptions"},"AzuracastSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/AzuracastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["azuracast"]}},"required":["data","type"],"title":"AzuracastSourceAIOConfig"},"AzuracastData":{"type":"object","properties":{"url":{"type":"string","description":"Base URL of the Azuracast instance\n\nThis does NOT include the station. If a station is included it will be ignored. Use `station` field to specify station, if necessary","examples":["https://radio.mydomain.tld","http://localhost:80"]},"station":{"type":"string","description":"The specific station to monitor\n\nScrobbling will only occur if any of the monitor conditions are met AND the station is ONLINE.\n\nTo monitor multiple stations create a Source for each station.","examples":["my-station-1"]},"monitorWhenListeners":{"type":["boolean","number"],"description":"Only activate scrobble monitoring if station\n\n* `true` => has any current listeners\n* `number` => has EQUAL TO or MORE THAN X number of listeners"},"monitorWhenLive":{"type":"boolean","description":"Only activate scrobble monitoring if station has a live DJ/Streamer","default":true},"apiKey":{"type":"string","description":"API Key used to access data about private streams\n\nhttps://www.azuracast.com/docs/developers/apis/#api-authentication"}},"required":["url","station"],"title":"AzuracastData"}}} \ No newline at end of file +{"$schema":"http://json-schema.org/draft-07/schema#","type":"object","properties":{"sourceDefaults":{"$ref":"#/definitions/SourceRetryOptions"},"sources":{"type":"array","items":{"$ref":"#/definitions/SourceAIOConfig"}}},"definitions":{"SourceRetryOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]}},"title":"SourceRetryOptions"},"SourceAIOConfig":{"anyOf":[{"$ref":"#/definitions/SpotifySourceAIOConfig"},{"$ref":"#/definitions/PlexSourceAIOConfig"},{"$ref":"#/definitions/PlexApiSourceAIOConfig"},{"$ref":"#/definitions/TautulliSourceAIOConfig"},{"$ref":"#/definitions/DeezerSourceAIOConfig"},{"$ref":"#/definitions/DeezerInternalAIOConfig"},{"$ref":"#/definitions/ListenbrainzEndpointSourceAIOConfig"},{"$ref":"#/definitions/LastFMEndpointSourceAIOConfig"},{"$ref":"#/definitions/SubsonicSourceAIOConfig"},{"$ref":"#/definitions/JellySourceAIOConfig"},{"$ref":"#/definitions/JellyApiSourceAIOConfig"},{"$ref":"#/definitions/LastFmSouceAIOConfig"},{"$ref":"#/definitions/YTMusicSourceAIOConfig"},{"$ref":"#/definitions/MPRISSourceAIOConfig"},{"$ref":"#/definitions/MopidySourceAIOConfig"},{"$ref":"#/definitions/ListenBrainzSourceAIOConfig"},{"$ref":"#/definitions/JRiverSourceAIOConfig"},{"$ref":"#/definitions/KodiSourceAIOConfig"},{"$ref":"#/definitions/WebScrobblerSourceAIOConfig"},{"$ref":"#/definitions/ChromecastSourceAIOConfig"},{"$ref":"#/definitions/MalojaSourceAIOConfig"},{"$ref":"#/definitions/MusikcubeSourceAIOConfig"},{"$ref":"#/definitions/MusicCastSourceAIOConfig"},{"$ref":"#/definitions/MPDSourceAIOConfig"},{"$ref":"#/definitions/VLCSourceAIOConfig"},{"$ref":"#/definitions/IcecastSourceAIOConfig"},{"$ref":"#/definitions/AzuracastSourceAIOConfig"},{"$ref":"#/definitions/KoitoSourceAIOConfig"}],"title":"SourceAIOConfig"},"SpotifySourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/SpotifySourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["spotify"]}},"required":["data","type"],"title":"SpotifySourceAIOConfig"},"SpotifySourceData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)\n\nIt is unlikely you should need to change this unless you scrobble many very short tracks often\n\nReading:\n* https://developer.spotify.com/documentation/web-api/guides/rate-limits/\n* https://medium.com/mendix/limiting-your-amount-of-calls-in-mendix-most-of-the-time-rest-835dde55b10e\n * Rate limit may ~180 req/min\n* https://community.spotify.com/t5/Spotify-for-Developers/Web-API-ratelimit/m-p/5503150/highlight/true#M7930\n * Informally indicated as 20 req/sec? Probably for burstiness","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"clientId":{"type":"string","description":"spotify client id","examples":["787c921a2a2ab42320831aba0c8f2fc2"]},"clientSecret":{"type":"string","description":"spotify client secret","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"redirectUri":{"type":"string","description":"spotify redirect URI -- required only if not the default shown here. URI must end in \"callback\"","default":"http://localhost:9078/callback","examples":["http://localhost:9078/callback"]}},"required":["clientId","clientSecret"],"title":"SpotifySourceData"},"CommonSourceOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"CommonSourceOptions"},"LogLevel":{"type":"string","enum":["silent","fatal","error","warn","info","log","verbose","debug"],"description":"Names of log levels that can be invoked on the logger\n\nFrom lowest to highest:\n\n* `debug`\n* `verbose`\n* `log`\n* `info`\n* `warn`\n* `error`\n* `fatal`\n* `silent` (will never output anything)\n\nWhen used in `LogOptions` specifies the **minimum** level the output should log at.","title":"LogLevel"},"FileLogOptions":{"type":"object","properties":{"timestamp":{"type":"string","enum":["unix","iso","auto"],"description":"For rolling log files\n\nWhen\n* value passed to rolling destination is a string (`path` from LogOptions is a string) and\n* `frequency` is defined\n\nThis determines the format of the datetime inserted into the log file name:\n\n* `unix` - unix epoch timestamp in milliseconds\n* `iso` - Full [ISO8601](https://en.wikipedia.org/wiki/ISO_8601) datetime IE '2024-03-07T20:11:34Z'\n* `auto`\n * When frequency is `daily` only inserts date IE YYYY-MM-DD\n * Otherwise inserts full ISO8601 datetime","default":"auto"},"size":{"type":["number","string"],"description":"The maximum size of a given rolling log file.\n\nCan be combined with frequency. Use k, m and g to express values in KB, MB or GB.\n\nNumerical values will be considered as MB.","default":"10MB"},"frequency":{"anyOf":[{"type":"string","enum":["daily"]},{"type":"string","enum":["hourly"]},{"type":"number"}],"description":"The amount of time a given rolling log file is used. Can be combined with size.\n\nUse `daily` or `hourly` to rotate file every day (or every hour). Existing file within the current day (or hour) will be re-used.\n\nNumerical values will be considered as a number of milliseconds. Using a numerical value will always create a new file upon startup.","default":"daily"},"path":{"anyOf":[{"type":"string"},{"$comment":"() => string"}],"description":"The path and filename to use for log files.\n\nIf using rolling files the filename will be appended with `.N` (a number) BEFORE the extension based on rolling status.\n\nMay also be specified using env LOG_PATH or a function that returns a string.\n\nIf path is relative the absolute path will be derived from `logBaseDir` (in `LoggerAppExtras`) which defaults to CWD","default":"./logs/app.log"},"level":{"anyOf":[{"$ref":"#/definitions/LogLevel"},{"type":"boolean","const":false}],"description":"Specify the minimum log level to output to rotating files. If `false` no log files will be created."}},"title":"FileLogOptions"},"ScrobbleThresholds":{"type":"object","properties":{"duration":{"type":["number","null"],"description":"The number of seconds a track has been listened to before it should be considered scrobbled.\n\nSet to null to disable.","default":240,"examples":[240]},"percent":{"type":["number","null"],"description":"The percentage (as an integer) of a track that should have been seen played before it should be scrobbled. Only used if the Source provides information about how long the track is.\n\nSet to null to disable.\n\nNOTE: This should be used with care when the Source is a \"polling\" type (has an 'interval' property). If the track is short and the interval is too high MS may ignore the track if percentage is high because it had not \"seen\" the track for long enough from first discovery, even if you have been playing the track for longer.","default":50,"examples":[50]}},"title":"ScrobbleThresholds"},"PlayTransformOptions":{"type":"object","properties":{"log":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["all"]}]},"preCompare":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"},"compare":{"type":"object","properties":{"candidate":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"},"existing":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"}}},"postCompare":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"}},"title":"PlayTransformOptions"},"PlayTransformPartsConfig<SearchAndReplaceTerm>":{"anyOf":[{"$ref":"#/definitions/PlayTransformPartsArray%3CSearchAndReplaceTerm%3E"},{"$ref":"#/definitions/PlayTransformParts%3CSearchAndReplaceTerm%3E"}],"title":"PlayTransformPartsConfig<SearchAndReplaceTerm>"},"PlayTransformPartsArray<SearchAndReplaceTerm>":{"type":"array","items":{"$ref":"#/definitions/PlayTransformParts%3CSearchAndReplaceTerm%3E"},"title":"PlayTransformPartsArray<SearchAndReplaceTerm>"},"PlayTransformParts<SearchAndReplaceTerm>":{"type":"object","properties":{"when":{"$ref":"#/definitions/WhenConditionsConfig"},"title":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}},"artists":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}},"album":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}}},"title":"PlayTransformParts<SearchAndReplaceTerm>"},"WhenConditionsConfig":{"$ref":"#/definitions/WhenConditions%3Cstring%3E","title":"WhenConditionsConfig"},"WhenConditions<string>":{"type":"array","items":{"$ref":"#/definitions/WhenParts%3Cstring%3E"},"title":"WhenConditions<string>"},"WhenParts<string>":{"$ref":"#/definitions/PlayTransformPartsAtomic%3Cstring%3E","title":"WhenParts<string>"},"PlayTransformPartsAtomic<string>":{"type":"object","properties":{"title":{"type":"string"},"artists":{"type":"string"},"album":{"type":"string"}},"title":"PlayTransformPartsAtomic<string>"},"SearchAndReplaceTerm":{"anyOf":[{"type":"string"},{"$ref":"#/definitions/ConditionalSearchAndReplaceTerm"}],"title":"SearchAndReplaceTerm"},"ConditionalSearchAndReplaceTerm":{"type":"object","properties":{"when":{"$ref":"#/definitions/WhenConditionsConfig"},"search":{},"replace":{}},"required":["search","replace"],"title":"ConditionalSearchAndReplaceTerm"},"PlexSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/PlexSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["plex"]}},"required":["data","type"],"title":"PlexSourceAIOConfig"},"PlexSourceData":{"type":"object","properties":{"user":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled","examples":[["MyUser1","MyUser2"]]},"libraries":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of libraries to scrobble tracks from\n\nIf none are provided tracks from all libraries will be scrobbled","examples":[["Audio","Music"]]},"servers":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled","examples":[["MyServerName"]]}},"title":"PlexSourceData"},"PlexApiSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/PlexApiData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/PlexApiOptions"},"type":{"type":"string","enum":["plex"]}},"required":["data","options","type"],"title":"PlexApiSourceAIOConfig"},"PlexApiData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"token":{"type":"string"},"url":{"type":"string","description":"http(s)://HOST:PORT of the Plex server to connect to"},"usersAllow":{"anyOf":[{"type":"string"},{"type":"boolean","enum":[true]},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble for specific users (case-insensitive)\n\nIf `true` MS will scrobble activity from all users"},"usersBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble for these users (case-insensitive)"},"devicesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if device or application name contains strings from this list (case-insensitive)"},"devicesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if device or application name contains strings from this list (case-insensitive)"},"librariesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if library name contains string from this list (case-insensitive)"},"librariesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if library name contains strings from this list (case-insensitive)"}},"required":["url"],"title":"PlexApiData"},"PlexApiOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"},"ignoreInvalidCert":{"type":"boolean","description":"Ignore invalid cert errors when connecting to Plex\n\nUseful for Plex servers using \"Required\" Secure Connections with self-signed certificates\n\nDo not enable unless you know you need this.","default":false}},"title":"PlexApiOptions"},"TautulliSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/PlexSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["tautulli"]}},"required":["data","type"],"title":"TautulliSourceAIOConfig"},"DeezerSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/DeezerData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["deezer"]}},"required":["data","type"],"title":"DeezerSourceAIOConfig"},"DeezerData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"clientId":{"type":"string","description":"deezer client id","examples":["a89cba1569901a0671d5a9875fed4be1"]},"clientSecret":{"type":"string","description":"deezer client secret","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"redirectUri":{"type":"string","description":"deezer redirect URI -- required only if not the default shown here. URI must end in \"callback\"","default":"http://localhost:9078/deezer/callback","examples":["http://localhost:9078/deezer/callback"]}},"required":["clientId","clientSecret"],"title":"DeezerData"},"DeezerInternalAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/DeezerInternalData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/DeezerInternalSourceOptions"},"type":{"type":"string","enum":["deezer"]}},"required":["data","type"],"title":"DeezerInternalAIOConfig"},"DeezerInternalData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"arl":{"type":"string","description":"ARL retrieved from Deezer response header"},"userAgent":{"type":"string","description":"User agent","default":"Mozilla/5.0 (X11; Linux i686; rv:135.0) Gecko/20100101 Firefox/135.0"}},"required":["arl"],"title":"DeezerInternalData"},"DeezerInternalSourceOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"},"fuzzyDiscoveryIgnore":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["aggressive"]}]}},"title":"DeezerInternalSourceOptions"},"ListenbrainzEndpointSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/ListenbrainzEndpointData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["endpointlz"]}},"required":["type"],"title":"ListenbrainzEndpointSourceAIOConfig"},"ListenbrainzEndpointData":{"type":"object","properties":{"slug":{"type":["string","null"],"description":"The URL ending that should be used to identify scrobbles for this source\n\nIf you are using multiple Listenbrainz endpoint sources (scrobbles for many users) you can use a slug to match Sources with individual users/origins\n\nExample:\n\n* slug: 'usera' => API URL: http://localhost:9078/api/listenbrainz/usera\n* slug: 'originb' => API URL: http://localhost:9078/api/listenbrainz/originb\n\nIf no slug is found from an extension's incoming webhook event the first Listenbrainz source without a slug will be used"},"token":{"type":["string","null"],"description":"If an LZ submission request contains this token in the Authorization Header it will be used to match the submission with this Source\n\nSee: https://listenbrainz.readthedocs.io/en/latest/users/api/index.html#add-the-user-token-to-your-requests"}},"title":"ListenbrainzEndpointData"},"LastFMEndpointSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/LastFMEndpointData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["endpointlfm"]}},"required":["type"],"title":"LastFMEndpointSourceAIOConfig"},"LastFMEndpointData":{"type":"object","properties":{"slug":{"type":["string","null"],"description":"The URL ending that should be used to identify scrobbles for this source\n\nIf you are using multiple Last.fm endpoint sources (scrobbles for many users) you can use a slug to match Sources with individual users/origins\n\nExample:\n\n* slug: 'usera' => API URL: http://localhost:9078/api/lastfm/usera\n* slug: 'originb' => API URL: http://localhost:9078/api/lastfm/originb\n\nIf no slug is found from an extension's incoming webhook event the first Last.fm source without a slug will be used"}},"title":"LastFMEndpointData"},"SubsonicSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/SubsonicData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["subsonic"]}},"required":["data","type"],"title":"SubsonicSourceAIOConfig"},"SubsonicData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the subsonic media server to query","examples":["http://airsonic.local"]},"user":{"type":"string","description":"Username to login to the server with","examples":[["MyUser"]]},"password":{"type":"string","description":"Password for the user to login to the server with","examples":["MyPassword"]},"ignoreTlsErrors":{"type":"boolean","description":"If your subsonic server is using self-signed certs you may need to disable TLS errors in order to get a connection\n\nWARNING: This should be used with caution as your traffic may not be encrypted.","default":false},"legacyAuthentication":{"type":"boolean","description":"Older Subsonic versions, and some badly implemented servers (Nextcloud), use legacy authentication which sends your password in CLEAR TEXT. This is less secure than the newer, recommended hashing authentication method but in some cases it is needed. See \"Authentication\" section here => https://www.subsonic.org/pages/api.jsp\n\nIf this option is not specified it will be turned on if the subsonic server responds with error code 41 \"Token authentication not supported for LDAP users.\" -- See Error Handling section => https://www.subsonic.org/pages/api.jsp","default":false},"usersAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble for specific users (case-insensitive)\n\nIf undefined or an empty string/list MS will scrobble activity from all users"}},"required":["url","user","password"],"title":"SubsonicData"},"JellySourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/JellyData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["jellyfin"]}},"required":["data","type"],"title":"JellySourceAIOConfig"},"JellyData":{"type":"object","properties":{"users":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled","examples":[["MyUser1","MyUser2"]]},"servers":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled","examples":[["MyServerName1"]]}},"title":"JellyData"},"JellyApiSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/JellyApiData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/JellyApiOptions"},"type":{"type":"string","enum":["jellyfin"]}},"required":["data","options","type"],"title":"JellyApiSourceAIOConfig"},"JellyApiData":{"type":"object","properties":{"url":{"type":"string","description":"HOST:PORT of the Jellyfin server to connect to"},"user":{"type":"string","description":"The username of the user to authenticate for or track scrobbles for"},"password":{"type":"string","description":"Password of the username to authenticate for\n\nRequired if `apiKey` is not provided."},"apiKey":{"type":"string","description":"API Key to authenticate with.\n\nRequired if `password` is not provided."},"usersAllow":{"anyOf":[{"type":"string"},{"type":"boolean","enum":[true]},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble for specific users (case-insensitive)\n\nIf `true` MS will scrobble activity from all users"},"usersBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble for these users (case-insensitive)"},"devicesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if device or application name contains strings from this list (case-insensitive)"},"devicesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if device or application name contains strings from this list (case-insensitive)"},"librariesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if library name contains string from this list (case-insensitive)"},"librariesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if library name contains strings from this list (case-insensitive)"},"additionalAllowedLibraryTypes":{"type":"array","items":{},"description":"Allow MS to scrobble audio media in libraries classified other than 'music'\n\n`librariesAllow` will achieve the same result as this but this is more convenient if you do not want to explicitly list every library name or are only using `librariesBlock`"},"allowUnknown":{"type":"boolean","description":"Force media with a type of \"Unknown\" to be counted as Audio","default":false},"frontendUrlOverride":{"type":"string","description":"HOST:PORT of the Jellyfin server that your browser will be able to access from the frontend (and thus load images and links from)\nIf unspecified it will use the normal server HOST and PORT from the `url`\nNecessary if you are using a reverse proxy or other network configuration that prevents the frontend from accessing the server directly"}},"required":["url","user"],"title":"JellyApiData"},"JellyApiOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"JellyApiOptions"},"LastFmSouceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/LastFmSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"configureAs":{"type":"string","enum":["source"],"description":"When used in `lastfm.config` this tells multi-scrobbler whether to use this data to configure a source or client.","default":"source","examples":["source"]},"type":{"type":"string","enum":["lastfm"]}},"required":["data","type"],"title":"LastFmSouceAIOConfig"},"LastFmSourceData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"apiKey":{"type":"string","description":"API Key generated from Last.fm account","examples":["787c921a2a2ab42320831aba0c8f2fc2"]},"secret":{"type":"string","description":"Secret generated from Last.fm account","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"session":{"type":"string","description":"Optional session id returned from a completed auth flow"},"redirectUri":{"type":"string","description":"Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.","default":"http://localhost:9078/lastfm/callback","examples":["http://localhost:9078/lastfm/callback"]},"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"}},"required":["apiKey","secret"],"title":"LastFmSourceData"},"YTMusicSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/YTMusicData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"type":"object","properties":{"logAuth":{"type":"boolean","description":"When true MS will log to DEBUG all of the credentials data it receives from YTM"},"logDiff":{"type":"boolean","description":"Always log history diff\n\nBy default MS will log to `WARN` if history diff is inconsistent but does not log if diff is expected (on new tracks found)\nSet this to `true` to ALWAYS log diff on new tracks. Expected diffs will log to `DEBUG` and inconsistent diffs will continue to log to `WARN`","default":false},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}}},"type":{"type":"string","enum":["ytmusic"]}},"required":["type"],"title":"YTMusicSourceAIOConfig"},"YTMusicData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"cookie":{"type":"string","description":"The cookie retrieved from the Request Headers of music.youtube.com after logging in.\n\nSee https://ytmusicapi.readthedocs.io/en/stable/setup/browser.html#copy-authentication-headers for how to retrieve this value.","examples":["VISITOR_INFO1_LIVE=jMp2xA1Xz2_PbVc; __Secure-3PAPISID=3AxsXpy0M/AkISpjek; ..."]},"clientId":{"type":"string","description":"Google Cloud Console project OAuth Client ID\n\nGenerated from a custom OAuth Client, see docs"},"clientSecret":{"type":"string","description":"Google Cloud Console project OAuth Client Secret\n\nGenerated from a custom OAuth Client, see docs"},"redirectUri":{"type":"string","description":"Google Cloud Console project OAuth Client Authorized redirect URI\n\nGenerated from a custom OAuth Client, see docs. multi-scrobbler will generate a default based on BASE_URL.\nOnly specify this if the default does not work for you."},"innertubeOptions":{"$ref":"#/definitions/InnertubeOptions","description":"Additional options for authorization and tailoring YTM client"}},"title":"YTMusicData"},"InnertubeOptions":{"type":"object","properties":{"po_token":{"type":"string","description":"Proof of Origin token\n\nMay be required if YTM starts returning 403"},"visitor_data":{"type":"string","description":"Visitor ID value found in VISITOR_INFO1_LIVE or visitorData cookie\n\nMay be required if YTM starts returning 403"},"account_index":{"type":"number","description":"If account login results in being able to choose multiple account, use a zero-based index to choose which one to monitor","examples":[0,1]},"location":{"type":"string"},"lang":{"type":"string"},"generate_session_locally":{"type":"boolean"},"device_category":{"type":"string"},"client_type":{"type":"string"},"timezone":{"type":"string"}},"title":"InnertubeOptions"},"MPRISSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MPRISData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["mpris"]}},"required":["data","type"],"title":"MPRISSourceAIOConfig"},"MPRISData":{"type":"object","properties":{"blacklist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"DO NOT scrobble from any players that START WITH these values, case-insensitive","examples":[["spotify","vlc"]]},"whitelist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"ONLY from any players that START WITH these values, case-insensitive\n\nIf whitelist is present then blacklist is ignored","examples":[["spotify","vlc"]]}},"title":"MPRISData"},"MopidySourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MopidyData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["mopidy"]}},"required":["data","type"],"title":"MopidySourceAIOConfig"},"MopidyData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the Mopidy HTTP server to connect to\n\nYou MUST have Mopidy-HTTP extension enabled: https://mopidy.com/ext/http\n\nmulti-scrobbler connects to the WebSocket endpoint that ultimately looks like this => `ws://localhost:6680/mopidy/ws/`\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `ws://`\n* Hostname => `localhost`\n* Port => `6680`\n* Path => `/mopidy/ws/`","examples":["ws://localhost:6680/mopidy/ws/"],"default":"ws://localhost:6680/mopidy/ws/"},"uriBlacklist":{"type":"array","items":{"type":"string"},"description":"Do not scrobble tracks whose URI STARTS WITH any of these strings, case-insensitive\n\nEX: Don't scrobble tracks from soundcloud by adding 'soundcloud' to this list.\n\nList is ignored if uriWhitelist is used."},"uriWhitelist":{"type":"array","items":{"type":"string"},"description":"Only scrobble tracks whose URI STARTS WITH any of these strings, case-insensitive\n\nEX: Only scrobble tracks from soundcloud by adding 'soundcloud' to this list."},"albumBlacklist":{"type":"array","items":{"type":"string"},"description":"Remove album data that matches any case-insensitive string from this list when scrobbling,\n\nFor certain sources (Soundcloud) Mopidy does not have all track info (Album) and will instead use \"Soundcloud\" as the Album name. You can prevent multi-scrobbler from using this bad Album data by adding the fake name to this list. Multi-scrobbler will still scrobble the track, just without the bad data.","examples":[["Soundcloud","Mixcloud"]],"default":["Soundcloud"]}},"title":"MopidyData"},"ListenBrainzSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/ListenBrainzSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"configureAs":{"type":"string","enum":["source"],"description":"When used in `listenbrainz.config` this tells multi-scrobbler whether to use this data to configure a source or client.","default":"source","examples":["source"]},"type":{"type":"string","enum":["listenbrainz"]}},"required":["data","type"],"title":"ListenBrainzSourceAIOConfig"},"ListenBrainzSourceData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for the ListenBrainz server, if not using the default","examples":["https://api.listenbrainz.org/"],"default":"https://api.listenbrainz.org/"},"token":{"type":"string","description":"User token for the user to scrobble for","examples":["6794186bf-1157-4de6-80e5-uvb411f3ea2b"]},"username":{"type":"string","description":"Username of the user to scrobble for"}},"required":["token","username"],"title":"ListenBrainzSourceData"},"JRiverSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/JRiverData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["jriver"]}},"required":["data","type"],"title":"JRiverSourceAIOConfig"},"JRiverData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the JRiver HTTP server to connect to\n\nmulti-scrobbler connects to the Web Service Interface endpoint that ultimately looks like this => `http://yourDomain:52199/MCWS/v1/`\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `http://`\n* Hostname => `localhost`\n* Port => `52199`\n* Path => `/MCWS/v1/`","examples":["http://localhost:52199/MCWS/v1/"],"default":"http://localhost:52199/MCWS/v1/"},"username":{"type":"string","description":"If you have enabled authentication, the username you set"},"password":{"type":"string","description":"If you have enabled authentication, the password you set"}},"required":["url"],"title":"JRiverData"},"KodiSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/KodiData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["kodi"]}},"required":["data","type"],"title":"KodiSourceAIOConfig"},"KodiData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the Kodi HTTP server to connect to\n\nmulti-scrobbler connects to the Web Service Interface endpoint that ultimately looks like this => `http://yourDomain:8080/jsonrpc`\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `http://`\n* Hostname => `localhost`\n* Port => `8080`\n* Path => `/jsonrpc`","examples":["http://localhost:8080/jsonrpc"],"default":"http://localhost:8080/jsonrpc"},"username":{"type":"string","description":"The username set for Remote Control via Web Sever"},"password":{"type":"string","description":"The password set for Remote Control via Web Sever"}},"required":["url","username","password"],"title":"KodiData"},"WebScrobblerSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/WebScrobblerData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["webscrobbler"]}},"required":["type"],"title":"WebScrobblerSourceAIOConfig"},"WebScrobblerData":{"type":"object","properties":{"slug":{"type":["string","null"],"description":"The URL ending that should be used to identify scrobbles for this source\n\nIn WebScrobbler's Webhook you must set an 'API URL'. All MS WebScrobbler sources must start like:\n\nhttp://localhost:9078/api/webscrobbler\n\nIf you are using multiple WebScrobbler sources (scrobbles for many users) you must use a slug to match Sources with each users extension.\n\nExample:\n\n* slug: 'usera' => API URL: http://localhost:9078/api/webscrobbler/usera\n* slug: 'userb' => API URL: http://localhost:9078/api/webscrobbler/userb\n\nIf no slug is found from an extension's incoming webhook event the first WebScrobbler source without a slug will be used"},"blacklist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Block scrobbling from specific WebScrobbler Connectors","examples":[["youtube"]]},"whitelist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only allow scrobbling from specific WebScrobbler Connectors","examples":[["mixcloud","soundcloud","bandcamp"]]}},"title":"WebScrobblerData"},"ChromecastSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/ChromecastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["chromecast"]}},"required":["data","type"],"title":"ChromecastSourceAIOConfig"},"ChromecastData":{"type":"object","properties":{"blacklistDevices":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"DO NOT scrobble from any cast devices that START WITH these values, case-insensitive\n\nUseful when used with auto discovery","examples":[["home-mini","family-tv"]]},"whitelistDevices":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"ONLY scrobble from any cast device that START WITH these values, case-insensitive\n\nIf whitelist is present then blacklist is ignored\n\nUseful when used with auto discovery","examples":[["home-mini","family-tv"]]},"blacklistApps":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"DO NOT scrobble from any application that START WITH these values, case-insensitive","examples":[["spotify","pandora"]]},"whitelistApps":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"ONLY scrobble from any application that START WITH these values, case-insensitive\n\nIf whitelist is present then blacklist is ignored","examples":[["spotify","pandora"]]},"useAvahi":{"type":"boolean","description":"Try to use Avahi and avahi-browse to resolve mDNS devices instead of native mDNS querying\n\nUseful for docker (alpine) container where mDNS resolution is not yet supported. Avahi socket must be exposed to the container and avahi-tools must be installed.","default":false},"useAutoDiscovery":{"type":"boolean","description":"Use mDNS to discovery Google Cast devices on your next automatically?\n\nIf not explicitly set then it is TRUE if `devices` is not set"},"devices":{"type":"array","items":{"$ref":"#/definitions/ChromecastDeviceInfo"},"description":"A list of Google Cast devices to monitor\n\nIf this is used then `useAutoDiscovery` is set to FALSE, if not explicitly set"},"allowUnknownMedia":{"anyOf":[{"type":"boolean"},{"type":"array","items":{"type":"string"}}],"description":"Chromecast Apps report a \"media type\" in the status info returned for whatever is currently playing\n\n* If set to TRUE then Music AND Generic/Unknown media will be tracked for ALL APPS\n* If set to FALSE then only media explicitly typed as Music will be tracked for ALL APPS\n* If set to a list then only Apps whose name contain one of these values, case-insensitive, will have Music AND Generic/Unknown tracked\n\nSee https://developers.google.com/cast/docs/media/messages#MediaInformation \"metadata\" property","default":false},"forceMediaRecognitionOn":{"type":"array","items":{"type":"string"},"description":"Media provided by any App whose name is listed here will ALWAYS be tracked, regardless of the \"media type\" reported\n\nApps will be recognized if they CONTAIN any of these values, case-insensitive"}},"title":"ChromecastData"},"ChromecastDeviceInfo":{"type":"object","properties":{"name":{"type":"string","description":"A friendly name to identify this device","examples":["MySmartTV"]},"address":{"type":"string","description":"The IP address of the device","examples":["192.168.0.115"]}},"required":["name","address"],"title":"ChromecastDeviceInfo"},"MalojaSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MalojaSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"configureAs":{"type":"string","enum":["source"],"description":"When used in `maloja.config` this tells multi-scrobbler whether to use this data to configure a source or client.","default":"source","examples":["source"]},"type":{"type":"string","enum":["maloja"]}},"required":["data","type"],"title":"MalojaSourceAIOConfig"},"MalojaSourceData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for maloja server","examples":["http://localhost:42010"]},"apiKey":{"type":"string","description":"API Key for Maloja server","examples":["myApiKey"]}},"required":["apiKey","url"],"title":"MalojaSourceData"},"MusikcubeSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MusikcubeData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["musikcube"]}},"required":["data","type"],"title":"MusikcubeSourceAIOConfig"},"MusikcubeData":{"type":"object","properties":{"url":{"type":"string","description":"URL of the Musikcube Websocket (Metadata) server to connect to\n\nYou MUST have enabled 'metadata' server and set a password: https://github.com/clangen/musikcube/wiki/remote-api-documentation\n * musikcube -> settings -> server setup\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `ws://`\n* Hostname => `localhost`\n* Port => `7905`","examples":["ws://localhost:7905"],"default":"ws://localhost:7905"},"password":{"type":"string","description":"Password set in Musikcube https://github.com/clangen/musikcube/wiki/remote-api-documentation\n\n* musikcube -> settings -> server setup -> password"},"device_id":{"type":"string"}},"required":["password"],"title":"MusikcubeData"},"MusicCastSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MusicCastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["musiccast"]}},"required":["data","type"],"title":"MusicCastSourceAIOConfig"},"MusicCastData":{"type":"object","properties":{"url":{"type":"string","description":"The host or URL of the YamahaExtendedControl endpoint to use","examples":[["192.168.0.101","http://192.168.0.101/YamahaExtendedControl"]]}},"required":["url"],"title":"MusicCastData"},"MPDSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MPDData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/MPDSourceOptions"},"type":{"type":"string","enum":["mpd"]}},"required":["data","options","type"],"title":"MPDSourceAIOConfig"},"MPDData":{"type":"object","properties":{"url":{"type":"string","description":"URL:PORT of the MPD server to connect to\n\nTo use this you must have TCP connections enabled for your MPD server https://mpd.readthedocs.io/en/stable/user.html#client-connections","examples":["localhost:6600"],"default":"localhost:6600"},"path":{"type":"string","description":"If using socket specify the path instead of url.\n\ntrailing `~` is replaced by your home directory"},"password":{"type":"string","description":"Password for the server, if set https://mpd.readthedocs.io/en/stable/user.html#permissions-and-passwords"}},"title":"MPDData"},"MPDSourceOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"MPDSourceOptions"},"VLCSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/VLCData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/VLCSourceOptions"},"type":{"type":"string","enum":["vlc"]}},"required":["data","type"],"title":"VLCSourceAIOConfig"},"VLCData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL:PORT of the VLC server to connect to\n\nTo use this you must have the Web (http) interface module enabled and a password set https://foxxmd.github.io/multi-scrobbler/docs/configuration#vlc","examples":["localhost:8080"],"default":"localhost:8080"},"password":{"type":"string","description":"Password for the server"}},"required":["password"],"title":"VLCData"},"VLCSourceOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"},"filenamePatterns":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"A list of regular expressions to use to extract metadata (title, album, artist) from a filename\n\nUsed when VLC reports only the filename for the current audio track"},"logFilenamePatterns":{"type":"boolean","description":"Log to DEBUG when a filename-only track is matched or not matched by filenamePatterns","default":false},"dumpVlcMetadata":{"type":"boolean","description":"Dump all the metadata VLC reports for an audio track to DEBUG.\n\nUse this if reporting an issue with VLC not correctly capturing metadata for a track.","default":false}},"title":"VLCSourceOptions"},"IcecastSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/IcecastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/IcecastSourceOptions"},"type":{"type":"string","enum":["icecast"]}},"required":["data","type"],"title":"IcecastSourceAIOConfig"},"IcecastData":{"type":"object","properties":{"sources":{"type":"array","items":{"$ref":"#/definitions/IcecastSource"}},"icestatsEndpoint":{"type":"string"},"statsEndpoint":{"type":"string"},"nextsongsEndpoint":{"type":"string"},"sevenhtmlEndpoint":{"type":"string"},"icyMetaInt":{"type":"number"},"url":{"type":"string","description":"The Icecast stream URL"}},"required":["url"],"title":"IcecastData"},"IcecastSource":{"type":"string","enum":["icy","ogg","icestats","stats","sevenhtml","nextsongs"],"title":"IcecastSource"},"IcecastSourceOptions":{"type":"object","properties":{"systemScrobble":{"type":"boolean","description":"For Sources that support manual listening, should MS default to scrobbling when no user interaction has occurred?\n\nIf not specified MS will use a Source's specific behavior, see Source's documentation."},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"IcecastSourceOptions"},"AzuracastSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/AzuracastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["azuracast"]}},"required":["data","type"],"title":"AzuracastSourceAIOConfig"},"AzuracastData":{"type":"object","properties":{"url":{"type":"string","description":"Base URL of the Azuracast instance\n\nThis does NOT include the station. If a station is included it will be ignored. Use `station` field to specify station, if necessary","examples":["https://radio.mydomain.tld","http://localhost:80"]},"station":{"type":"string","description":"The specific station to monitor\n\nScrobbling will only occur if any of the monitor conditions are met AND the station is ONLINE.\n\nTo monitor multiple stations create a Source for each station.","examples":["my-station-1"]},"monitorWhenListeners":{"type":["boolean","number"],"description":"Only activate scrobble monitoring if station\n\n* `true` => has any current listeners\n* `number` => has EQUAL TO or MORE THAN X number of listeners"},"monitorWhenLive":{"type":"boolean","description":"Only activate scrobble monitoring if station has a live DJ/Streamer","default":true},"apiKey":{"type":"string","description":"API Key used to access data about private streams\n\nhttps://www.azuracast.com/docs/developers/apis/#api-authentication"}},"required":["url","station"],"title":"AzuracastData"},"KoitoSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/KoitoSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"configureAs":{"type":"string","enum":["source"],"description":"When used in `koito.config` this tells multi-scrobbler whether to use this data to configure a source or client.","default":"source","examples":["source"]},"type":{"type":"string","enum":["koito"]}},"required":["data","type"],"title":"KoitoSourceAIOConfig"},"KoitoSourceData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for the Koito server","examples":["http://192.168.0.100:4110"]},"token":{"type":"string","description":"User token for the user to scrobble for","examples":["pM195xPV98CDpk0QW47FIIOR8AKATAX5DblBF-Jq0t1MbbKL"]},"username":{"type":"string","description":"Username of the user to scrobble for"}},"required":["token","url","username"],"title":"KoitoSourceData"}}} \ No newline at end of file diff --git a/src/backend/common/schema/aio.json b/src/backend/common/schema/aio.json index 39edc4b8..2837ea82 100644 --- a/src/backend/common/schema/aio.json +++ b/src/backend/common/schema/aio.json @@ -1 +1 @@ -{"$schema":"http://json-schema.org/draft-07/schema#","type":"object","properties":{"sourceDefaults":{"$ref":"#/definitions/SourceDefaults"},"clientDefaults":{"$ref":"#/definitions/ClientDefaults"},"sources":{"type":"array","items":{"$ref":"#/definitions/SourceAIOConfig"}},"clients":{"type":"array","items":{"$ref":"#/definitions/ClientAIOConfig"}},"webhooks":{"type":"array","items":{"$ref":"#/definitions/WebhookConfig"}},"port":{"type":"number","description":"Set the port the multi-scrobbler UI will be served from","default":9078,"examples":[9078]},"baseUrl":{"type":"string","description":"Set the Base URL the application should assume the UI is served from.\n\nThis will affect how default redirect URLs are generated (spotify, lastfm, deezer) and some logging messages.\n\nIt will NOT set the actual interface/IP that the application is listening on.\n\nThis can also be set using the BASE_URL environmental variable.","default":"http://localhost","examples":["http://localhost","http://192.168.0.101","https://ms.myDomain.tld"]},"logging":{"$ref":"#/definitions/LogOptions"},"disableWeb":{"type":"boolean","description":"Disable web server from running/listening on port.\n\nThis will also make any ingress sources (Plex, Jellyfin, Tautulli, etc...) unusable"},"debugMode":{"type":"boolean","description":"Enables ALL relevant logging and debug options for all sources/clients, when none are defined.\n\nThis is a convenience shortcut for enabling all output needed to troubleshoot an issue and does not need to be on for normal operation.\n\nIt can also be enabled with the environmental variable DEBUG_MODE=true","default":false,"examples":[false]}},"definitions":{"SourceDefaults":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"SourceDefaults"},"LogLevel":{"type":"string","enum":["silent","fatal","error","warn","info","log","verbose","debug"],"description":"Names of log levels that can be invoked on the logger\n\nFrom lowest to highest:\n\n* `debug`\n* `verbose`\n* `log`\n* `info`\n* `warn`\n* `error`\n* `fatal`\n* `silent` (will never output anything)\n\nWhen used in `LogOptions` specifies the **minimum** level the output should log at.","title":"LogLevel"},"FileLogOptions":{"type":"object","properties":{"timestamp":{"type":"string","enum":["unix","iso","auto"],"description":"For rolling log files\n\nWhen\n* value passed to rolling destination is a string (`path` from LogOptions is a string) and\n* `frequency` is defined\n\nThis determines the format of the datetime inserted into the log file name:\n\n* `unix` - unix epoch timestamp in milliseconds\n* `iso` - Full [ISO8601](https://en.wikipedia.org/wiki/ISO_8601) datetime IE '2024-03-07T20:11:34Z'\n* `auto`\n * When frequency is `daily` only inserts date IE YYYY-MM-DD\n * Otherwise inserts full ISO8601 datetime","default":"auto"},"size":{"type":["number","string"],"description":"The maximum size of a given rolling log file.\n\nCan be combined with frequency. Use k, m and g to express values in KB, MB or GB.\n\nNumerical values will be considered as MB.","default":"10MB"},"frequency":{"anyOf":[{"type":"string","enum":["daily"]},{"type":"string","enum":["hourly"]},{"type":"number"}],"description":"The amount of time a given rolling log file is used. Can be combined with size.\n\nUse `daily` or `hourly` to rotate file every day (or every hour). Existing file within the current day (or hour) will be re-used.\n\nNumerical values will be considered as a number of milliseconds. Using a numerical value will always create a new file upon startup.","default":"daily"},"path":{"anyOf":[{"type":"string"},{"$comment":"() => string"}],"description":"The path and filename to use for log files.\n\nIf using rolling files the filename will be appended with `.N` (a number) BEFORE the extension based on rolling status.\n\nMay also be specified using env LOG_PATH or a function that returns a string.\n\nIf path is relative the absolute path will be derived from `logBaseDir` (in `LoggerAppExtras`) which defaults to CWD","default":"./logs/app.log"},"level":{"anyOf":[{"$ref":"#/definitions/LogLevel"},{"type":"boolean","const":false}],"description":"Specify the minimum log level to output to rotating files. If `false` no log files will be created."}},"title":"FileLogOptions"},"ScrobbleThresholds":{"type":"object","properties":{"duration":{"type":["number","null"],"description":"The number of seconds a track has been listened to before it should be considered scrobbled.\n\nSet to null to disable.","default":240,"examples":[240]},"percent":{"type":["number","null"],"description":"The percentage (as an integer) of a track that should have been seen played before it should be scrobbled. Only used if the Source provides information about how long the track is.\n\nSet to null to disable.\n\nNOTE: This should be used with care when the Source is a \"polling\" type (has an 'interval' property). If the track is short and the interval is too high MS may ignore the track if percentage is high because it had not \"seen\" the track for long enough from first discovery, even if you have been playing the track for longer.","default":50,"examples":[50]}},"title":"ScrobbleThresholds"},"PlayTransformOptions":{"type":"object","properties":{"log":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["all"]}]},"preCompare":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"},"compare":{"type":"object","properties":{"candidate":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"},"existing":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"}}},"postCompare":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"}},"title":"PlayTransformOptions"},"PlayTransformPartsConfig<SearchAndReplaceTerm>":{"anyOf":[{"$ref":"#/definitions/PlayTransformPartsArray%3CSearchAndReplaceTerm%3E"},{"$ref":"#/definitions/PlayTransformParts%3CSearchAndReplaceTerm%3E"}],"title":"PlayTransformPartsConfig<SearchAndReplaceTerm>"},"PlayTransformPartsArray<SearchAndReplaceTerm>":{"type":"array","items":{"$ref":"#/definitions/PlayTransformParts%3CSearchAndReplaceTerm%3E"},"title":"PlayTransformPartsArray<SearchAndReplaceTerm>"},"PlayTransformParts<SearchAndReplaceTerm>":{"type":"object","properties":{"when":{"$ref":"#/definitions/WhenConditionsConfig"},"title":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}},"artists":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}},"album":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}}},"title":"PlayTransformParts<SearchAndReplaceTerm>"},"WhenConditionsConfig":{"$ref":"#/definitions/WhenConditions%3Cstring%3E","title":"WhenConditionsConfig"},"WhenConditions<string>":{"type":"array","items":{"$ref":"#/definitions/WhenParts%3Cstring%3E"},"title":"WhenConditions<string>"},"WhenParts<string>":{"$ref":"#/definitions/PlayTransformPartsAtomic%3Cstring%3E","title":"WhenParts<string>"},"PlayTransformPartsAtomic<string>":{"type":"object","properties":{"title":{"type":"string"},"artists":{"type":"string"},"album":{"type":"string"}},"title":"PlayTransformPartsAtomic<string>"},"SearchAndReplaceTerm":{"anyOf":[{"type":"string"},{"$ref":"#/definitions/ConditionalSearchAndReplaceTerm"}],"title":"SearchAndReplaceTerm"},"ConditionalSearchAndReplaceTerm":{"type":"object","properties":{"when":{"$ref":"#/definitions/WhenConditionsConfig"},"search":{},"replace":{}},"required":["search","replace"],"title":"ConditionalSearchAndReplaceTerm"},"ClientDefaults":{"type":"object","properties":{"refreshEnabled":{"type":"boolean","description":"Try to get fresh scrobble history from client when tracks to be scrobbled are newer than the last scrobble found in client history","default":true,"examples":[true]},"refreshStaleAfter":{"type":"number","description":"Refresh scrobbled plays from upstream service if last refresh was at least X seconds ago\n\n**In most case this setting does NOT need to be changed.** The default value is sufficient for the majority of use-cases. Increasing this setting may increase upstream service load and slow down scrobbles.\n\nThis setting should only be changed in specific scenarios where MS is handling multiple \"relaying\" client-services (IE lfm -> lz -> lfm) and there is the potential for a client to be out of sync after more than a few seconds.","examples":[60],"default":60},"refreshMinInterval":{"type":"number","description":"Minimum time (milliseconds) required to pass before upstream scrobbles can be refreshed.\n\n**In most case this setting does NOT need to be changed.** This will always be equal to or smaller than `refreshStaleAfter`.","default":5000,"examples":[5000]},"refreshInitialCount":{"type":"number","description":"The number of tracks to retrieve on initial refresh (related to scrobbleBacklogCount). If not specified this is the maximum supported by the client in 1 API call."},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"checkExistingScrobbles":{"type":"boolean","description":"Check client for an existing scrobble at the same recorded time as the \"new\" track to be scrobbled. If an existing scrobble is found this track is not track scrobbled.","default":true,"examples":[true]},"verbose":{"type":"object","properties":{"match":{"$ref":"#/definitions/MatchLoggingOptions"}},"description":"Options used for increasing verbosity of logging in MS (used for debugging)"},"deadLetterRetries":{"type":"number","description":"Number of times MS should automatically retry scrobbles in dead letter queue","default":1,"examples":[1]},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"ClientDefaults"},"MatchLoggingOptions":{"type":"object","properties":{"onNoMatch":{"type":"boolean","description":"Log to DEBUG when a new track does NOT match an existing scrobble","default":false,"examples":[false]},"onMatch":{"type":"boolean","description":"Log to DEBUG when a new track DOES match an existing scrobble","default":false,"examples":[false]},"confidenceBreakdown":{"type":"boolean","description":"Include confidence breakdowns in track match logging, if applicable","default":false,"examples":[false]}},"description":"Scrobble matching (between new source track and existing client scrobbles) logging options. Used for debugging.","title":"MatchLoggingOptions"},"SourceAIOConfig":{"anyOf":[{"$ref":"#/definitions/SpotifySourceAIOConfig"},{"$ref":"#/definitions/PlexSourceAIOConfig"},{"$ref":"#/definitions/PlexApiSourceAIOConfig"},{"$ref":"#/definitions/TautulliSourceAIOConfig"},{"$ref":"#/definitions/DeezerSourceAIOConfig"},{"$ref":"#/definitions/ListenbrainzEndpointSourceAIOConfig"},{"$ref":"#/definitions/LastFMEndpointSourceAIOConfig"},{"$ref":"#/definitions/SubsonicSourceAIOConfig"},{"$ref":"#/definitions/JellySourceAIOConfig"},{"$ref":"#/definitions/JellyApiSourceAIOConfig"},{"$ref":"#/definitions/LastFmSouceAIOConfig"},{"$ref":"#/definitions/YTMusicSourceAIOConfig"},{"$ref":"#/definitions/MPRISSourceAIOConfig"},{"$ref":"#/definitions/MopidySourceAIOConfig"},{"$ref":"#/definitions/ListenBrainzSourceAIOConfig"},{"$ref":"#/definitions/JRiverSourceAIOConfig"},{"$ref":"#/definitions/KodiSourceAIOConfig"},{"$ref":"#/definitions/WebScrobblerSourceAIOConfig"},{"$ref":"#/definitions/ChromecastSourceAIOConfig"},{"$ref":"#/definitions/MusikcubeSourceAIOConfig"},{"$ref":"#/definitions/MusicCastSourceAIOConfig"},{"$ref":"#/definitions/MPDSourceAIOConfig"},{"$ref":"#/definitions/VLCSourceAIOConfig"},{"$ref":"#/definitions/IcecastSourceAIOConfig"},{"$ref":"#/definitions/AzuracastSourceAIOConfig"}],"title":"SourceAIOConfig"},"SpotifySourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/SpotifySourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["spotify"]}},"required":["data","type"],"title":"SpotifySourceAIOConfig"},"SpotifySourceData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)\n\nIt is unlikely you should need to change this unless you scrobble many very short tracks often\n\nReading:\n* https://developer.spotify.com/documentation/web-api/guides/rate-limits/\n* https://medium.com/mendix/limiting-your-amount-of-calls-in-mendix-most-of-the-time-rest-835dde55b10e\n * Rate limit may ~180 req/min\n* https://community.spotify.com/t5/Spotify-for-Developers/Web-API-ratelimit/m-p/5503150/highlight/true#M7930\n * Informally indicated as 20 req/sec? Probably for burstiness","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"clientId":{"type":"string","description":"spotify client id","examples":["787c921a2a2ab42320831aba0c8f2fc2"]},"clientSecret":{"type":"string","description":"spotify client secret","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"redirectUri":{"type":"string","description":"spotify redirect URI -- required only if not the default shown here. URI must end in \"callback\"","default":"http://localhost:9078/callback","examples":["http://localhost:9078/callback"]}},"required":["clientId","clientSecret"],"title":"SpotifySourceData"},"CommonSourceOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"CommonSourceOptions"},"PlexSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/PlexSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["plex"]}},"required":["data","type"],"title":"PlexSourceAIOConfig"},"PlexSourceData":{"type":"object","properties":{"user":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled","examples":[["MyUser1","MyUser2"]]},"libraries":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of libraries to scrobble tracks from\n\nIf none are provided tracks from all libraries will be scrobbled","examples":[["Audio","Music"]]},"servers":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled","examples":[["MyServerName"]]}},"title":"PlexSourceData"},"PlexApiSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/PlexApiData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/PlexApiOptions"},"type":{"type":"string","enum":["plex"]}},"required":["data","options","type"],"title":"PlexApiSourceAIOConfig"},"PlexApiData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"token":{"type":"string"},"url":{"type":"string","description":"http(s)://HOST:PORT of the Plex server to connect to"},"usersAllow":{"anyOf":[{"type":"string"},{"type":"boolean","enum":[true]},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble for specific users (case-insensitive)\n\nIf `true` MS will scrobble activity from all users"},"usersBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble for these users (case-insensitive)"},"devicesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if device or application name contains strings from this list (case-insensitive)"},"devicesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if device or application name contains strings from this list (case-insensitive)"},"librariesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if library name contains string from this list (case-insensitive)"},"librariesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if library name contains strings from this list (case-insensitive)"}},"required":["url"],"title":"PlexApiData"},"PlexApiOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"PlexApiOptions"},"TautulliSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/PlexSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["tautulli"]}},"required":["data","type"],"title":"TautulliSourceAIOConfig"},"DeezerSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/DeezerData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["deezer"]}},"required":["data","type"],"title":"DeezerSourceAIOConfig"},"DeezerData":{"type":"object","properties":{"clientId":{"type":"string","description":"deezer client id","examples":["a89cba1569901a0671d5a9875fed4be1"]},"clientSecret":{"type":"string","description":"deezer client secret","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"redirectUri":{"type":"string","description":"deezer redirect URI -- required only if not the default shown here. URI must end in \"callback\"","default":"http://localhost:9078/deezer/callback","examples":["http://localhost:9078/deezer/callback"]},"interval":{"type":"number","description":"optional, how long to wait before calling spotify for new tracks (in seconds)","default":60,"examples":[60]}},"required":["clientId","clientSecret"],"title":"DeezerData"},"ListenbrainzEndpointSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/ListenbrainzEndpointData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["endpointlz"]}},"required":["type"],"title":"ListenbrainzEndpointSourceAIOConfig"},"ListenbrainzEndpointData":{"type":"object","properties":{"slug":{"type":["string","null"],"description":"The URL ending that should be used to identify scrobbles for this source\n\nIf you are using multiple Listenbrainz endpoint sources (scrobbles for many users) you can use a slug to match Sources with individual users/origins\n\nExample:\n\n* slug: 'usera' => API URL: http://localhost:9078/api/listenbrainz/usera\n* slug: 'originb' => API URL: http://localhost:9078/api/listenbrainz/originb\n\nIf no slug is found from an extension's incoming webhook event the first Listenbrainz source without a slug will be used"},"token":{"type":["string","null"],"description":"If an LZ submission request contains this token in the Authorization Header it will be used to match the submission with this Source\n\nSee: https://listenbrainz.readthedocs.io/en/latest/users/api/index.html#add-the-user-token-to-your-requests"}},"title":"ListenbrainzEndpointData"},"LastFMEndpointSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/LastFMEndpointData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["endpointlfm"]}},"required":["type"],"title":"LastFMEndpointSourceAIOConfig"},"LastFMEndpointData":{"type":"object","properties":{"slug":{"type":["string","null"],"description":"The URL ending that should be used to identify scrobbles for this source\n\nIf you are using multiple Last.fm endpoint sources (scrobbles for many users) you can use a slug to match Sources with individual users/origins\n\nExample:\n\n* slug: 'usera' => API URL: http://localhost:9078/api/lastfm/usera\n* slug: 'originb' => API URL: http://localhost:9078/api/lastfm/originb\n\nIf no slug is found from an extension's incoming webhook event the first Last.fm source without a slug will be used"}},"title":"LastFMEndpointData"},"SubsonicSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/SubsonicData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["subsonic"]}},"required":["data","type"],"title":"SubsonicSourceAIOConfig"},"SubsonicData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the subsonic media server to query","examples":["http://airsonic.local"]},"user":{"type":"string","description":"Username to login to the server with","examples":[["MyUser"]]},"password":{"type":"string","description":"Password for the user to login to the server with","examples":["MyPassword"]},"ignoreTlsErrors":{"type":"boolean","description":"If your subsonic server is using self-signed certs you may need to disable TLS errors in order to get a connection\n\nWARNING: This should be used with caution as your traffic may not be encrypted.","default":false},"legacyAuthentication":{"type":"boolean","description":"Older Subsonic versions, and some badly implemented servers (Nextcloud), use legacy authentication which sends your password in CLEAR TEXT. This is less secure than the newer, recommended hashing authentication method but in some cases it is needed. See \"Authentication\" section here => https://www.subsonic.org/pages/api.jsp\n\nIf this option is not specified it will be turned on if the subsonic server responds with error code 41 \"Token authentication not supported for LDAP users.\" -- See Error Handling section => https://www.subsonic.org/pages/api.jsp","default":false}},"required":["url","user","password"],"title":"SubsonicData"},"JellySourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/JellyData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["jellyfin"]}},"required":["data","type"],"title":"JellySourceAIOConfig"},"JellyData":{"type":"object","properties":{"users":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled","examples":[["MyUser1","MyUser2"]]},"servers":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled","examples":[["MyServerName1"]]}},"title":"JellyData"},"JellyApiSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/JellyApiData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/JellyApiOptions"},"type":{"type":"string","enum":["jellyfin"]}},"required":["data","options","type"],"title":"JellyApiSourceAIOConfig"},"JellyApiData":{"type":"object","properties":{"url":{"type":"string","description":"HOST:PORT of the Jellyfin server to connect to"},"user":{"type":"string","description":"The username of the user to authenticate for or track scrobbles for"},"password":{"type":"string","description":"Password of the username to authenticate for\n\nRequired if `apiKey` is not provided."},"apiKey":{"type":"string","description":"API Key to authenticate with.\n\nRequired if `password` is not provided."},"usersAllow":{"anyOf":[{"type":"string"},{"type":"boolean","enum":[true]},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble for specific users (case-insensitive)\n\nIf `true` MS will scrobble activity from all users"},"usersBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble for these users (case-insensitive)"},"devicesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if device or application name contains strings from this list (case-insensitive)"},"devicesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if device or application name contains strings from this list (case-insensitive)"},"librariesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if library name contains string from this list (case-insensitive)"},"librariesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if library name contains strings from this list (case-insensitive)"},"additionalAllowedLibraryTypes":{"type":"array","items":{},"description":"Allow MS to scrobble audio media in libraries classified other than 'music'\n\n`librariesAllow` will achieve the same result as this but this is more convenient if you do not want to explicitly list every library name or are only using `librariesBlock`"},"allowUnknown":{"type":"boolean","description":"Force media with a type of \"Unknown\" to be counted as Audio","default":false}},"required":["url","user"],"title":"JellyApiData"},"JellyApiOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"JellyApiOptions"},"LastFmSouceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/LastFmSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"configureAs":{"type":"string","enum":["source"],"description":"When used in `lastfm.config` this tells multi-scrobbler whether to use this data to configure a source or client.","default":"source","examples":["source"]},"type":{"type":"string","enum":["lastfm"]}},"required":["data","type"],"title":"LastFmSouceAIOConfig"},"LastFmSourceData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"apiKey":{"type":"string","description":"API Key generated from Last.fm account","examples":["787c921a2a2ab42320831aba0c8f2fc2"]},"secret":{"type":"string","description":"Secret generated from Last.fm account","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"session":{"type":"string","description":"Optional session id returned from a completed auth flow"},"redirectUri":{"type":"string","description":"Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.","default":"http://localhost:9078/lastfm/callback","examples":["http://localhost:9078/lastfm/callback"]},"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"}},"required":["apiKey","secret"],"title":"LastFmSourceData"},"YTMusicSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/YTMusicData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"type":"object","properties":{"logAuth":{"type":"boolean","description":"When true MS will log to DEBUG all of the credentials data it receives from YTM"},"logDiff":{"type":"boolean","description":"Always log history diff\n\nBy default MS will log to `WARN` if history diff is inconsistent but does not log if diff is expected (on new tracks found)\nSet this to `true` to ALWAYS log diff on new tracks. Expected diffs will log to `DEBUG` and inconsistent diffs will continue to log to `WARN`","default":false},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}}},"type":{"type":"string","enum":["ytmusic"]}},"required":["type"],"title":"YTMusicSourceAIOConfig"},"YTMusicData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"cookie":{"type":"string","description":"The cookie retrieved from the Request Headers of music.youtube.com after logging in.\n\nSee https://ytmusicapi.readthedocs.io/en/stable/setup/browser.html#copy-authentication-headers for how to retrieve this value.","examples":["VISITOR_INFO1_LIVE=jMp2xA1Xz2_PbVc; __Secure-3PAPISID=3AxsXpy0M/AkISpjek; ..."]},"clientId":{"type":"string","description":"Google Cloud Console project OAuth Client ID\n\nGenerated from a custom OAuth Client, see docs"},"clientSecret":{"type":"string","description":"Google Cloud Console project OAuth Client Secret\n\nGenerated from a custom OAuth Client, see docs"},"redirectUri":{"type":"string","description":"Google Cloud Console project OAuth Client Authorized redirect URI\n\nGenerated from a custom OAuth Client, see docs. multi-scrobbler will generate a default based on BASE_URL.\nOnly specify this if the default does not work for you."},"innertubeOptions":{"$ref":"#/definitions/InnertubeOptions","description":"Additional options for authorization and tailoring YTM client"}},"title":"YTMusicData"},"InnertubeOptions":{"type":"object","properties":{"po_token":{"type":"string","description":"Proof of Origin token\n\nMay be required if YTM starts returning 403"},"visitor_data":{"type":"string","description":"Visitor ID value found in VISITOR_INFO1_LIVE or visitorData cookie\n\nMay be required if YTM starts returning 403"},"account_index":{"type":"number","description":"If account login results in being able to choose multiple account, use a zero-based index to choose which one to monitor","examples":[0,1]},"location":{"type":"string"},"lang":{"type":"string"},"generate_session_locally":{"type":"boolean"},"device_category":{"type":"string"},"client_type":{"type":"string"},"timezone":{"type":"string"}},"title":"InnertubeOptions"},"MPRISSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MPRISData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["mpris"]}},"required":["data","type"],"title":"MPRISSourceAIOConfig"},"MPRISData":{"type":"object","properties":{"blacklist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"DO NOT scrobble from any players that START WITH these values, case-insensitive","examples":[["spotify","vlc"]]},"whitelist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"ONLY from any players that START WITH these values, case-insensitive\n\nIf whitelist is present then blacklist is ignored","examples":[["spotify","vlc"]]}},"title":"MPRISData"},"MopidySourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MopidyData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["mopidy"]}},"required":["data","type"],"title":"MopidySourceAIOConfig"},"MopidyData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the Mopidy HTTP server to connect to\n\nYou MUST have Mopidy-HTTP extension enabled: https://mopidy.com/ext/http\n\nmulti-scrobbler connects to the WebSocket endpoint that ultimately looks like this => `ws://localhost:6680/mopidy/ws/`\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `ws://`\n* Hostname => `localhost`\n* Port => `6680`\n* Path => `/mopidy/ws/`","examples":["ws://localhost:6680/mopidy/ws/"],"default":"ws://localhost:6680/mopidy/ws/"},"uriBlacklist":{"type":"array","items":{"type":"string"},"description":"Do not scrobble tracks whose URI STARTS WITH any of these strings, case-insensitive\n\nEX: Don't scrobble tracks from soundcloud by adding 'soundcloud' to this list.\n\nList is ignored if uriWhitelist is used."},"uriWhitelist":{"type":"array","items":{"type":"string"},"description":"Only scrobble tracks whose URI STARTS WITH any of these strings, case-insensitive\n\nEX: Only scrobble tracks from soundcloud by adding 'soundcloud' to this list."},"albumBlacklist":{"type":"array","items":{"type":"string"},"description":"Remove album data that matches any case-insensitive string from this list when scrobbling,\n\nFor certain sources (Soundcloud) Mopidy does not have all track info (Album) and will instead use \"Soundcloud\" as the Album name. You can prevent multi-scrobbler from using this bad Album data by adding the fake name to this list. Multi-scrobbler will still scrobble the track, just without the bad data.","examples":[["Soundcloud","Mixcloud"]],"default":["Soundcloud"]}},"title":"MopidyData"},"ListenBrainzSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/ListenBrainzSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"configureAs":{"type":"string","enum":["source"],"description":"When used in `listenbrainz.config` this tells multi-scrobbler whether to use this data to configure a source or client.","default":"source","examples":["source"]},"type":{"type":"string","enum":["listenbrainz"]}},"required":["data","type"],"title":"ListenBrainzSourceAIOConfig"},"ListenBrainzSourceData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for the ListenBrainz server, if not using the default","examples":["https://api.listenbrainz.org/"],"default":"https://api.listenbrainz.org/"},"token":{"type":"string","description":"User token for the user to scrobble for","examples":["6794186bf-1157-4de6-80e5-uvb411f3ea2b"]},"username":{"type":"string","description":"Username of the user to scrobble for"}},"required":["token","username"],"title":"ListenBrainzSourceData"},"JRiverSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/JRiverData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["jriver"]}},"required":["data","type"],"title":"JRiverSourceAIOConfig"},"JRiverData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the JRiver HTTP server to connect to\n\nmulti-scrobbler connects to the Web Service Interface endpoint that ultimately looks like this => `http://yourDomain:52199/MCWS/v1/`\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `http://`\n* Hostname => `localhost`\n* Port => `52199`\n* Path => `/MCWS/v1/`","examples":["http://localhost:52199/MCWS/v1/"],"default":"http://localhost:52199/MCWS/v1/"},"username":{"type":"string","description":"If you have enabled authentication, the username you set"},"password":{"type":"string","description":"If you have enabled authentication, the password you set"}},"required":["url"],"title":"JRiverData"},"KodiSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/KodiData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["kodi"]}},"required":["data","type"],"title":"KodiSourceAIOConfig"},"KodiData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the Kodi HTTP server to connect to\n\nmulti-scrobbler connects to the Web Service Interface endpoint that ultimately looks like this => `http://yourDomain:8080/jsonrpc`\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `http://`\n* Hostname => `localhost`\n* Port => `8080`\n* Path => `/jsonrpc`","examples":["http://localhost:8080/jsonrpc"],"default":"http://localhost:8080/jsonrpc"},"username":{"type":"string","description":"The username set for Remote Control via Web Sever"},"password":{"type":"string","description":"The password set for Remote Control via Web Sever"}},"required":["url","username","password"],"title":"KodiData"},"WebScrobblerSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/WebScrobblerData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["webscrobbler"]}},"required":["type"],"title":"WebScrobblerSourceAIOConfig"},"WebScrobblerData":{"type":"object","properties":{"slug":{"type":["string","null"],"description":"The URL ending that should be used to identify scrobbles for this source\n\nIn WebScrobbler's Webhook you must set an 'API URL'. All MS WebScrobbler sources must start like:\n\nhttp://localhost:9078/api/webscrobbler\n\nIf you are using multiple WebScrobbler sources (scrobbles for many users) you must use a slug to match Sources with each users extension.\n\nExample:\n\n* slug: 'usera' => API URL: http://localhost:9078/api/webscrobbler/usera\n* slug: 'userb' => API URL: http://localhost:9078/api/webscrobbler/userb\n\nIf no slug is found from an extension's incoming webhook event the first WebScrobbler source without a slug will be used"},"blacklist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Block scrobbling from specific WebScrobbler Connectors","examples":[["youtube"]]},"whitelist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only allow scrobbling from specific WebScrobbler Connectors","examples":[["mixcloud","soundcloud","bandcamp"]]}},"title":"WebScrobblerData"},"ChromecastSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/ChromecastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["chromecast"]}},"required":["data","type"],"title":"ChromecastSourceAIOConfig"},"ChromecastData":{"type":"object","properties":{"blacklistDevices":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"DO NOT scrobble from any cast devices that START WITH these values, case-insensitive\n\nUseful when used with auto discovery","examples":[["home-mini","family-tv"]]},"whitelistDevices":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"ONLY scrobble from any cast device that START WITH these values, case-insensitive\n\nIf whitelist is present then blacklist is ignored\n\nUseful when used with auto discovery","examples":[["home-mini","family-tv"]]},"blacklistApps":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"DO NOT scrobble from any application that START WITH these values, case-insensitive","examples":[["spotify","pandora"]]},"whitelistApps":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"ONLY scrobble from any application that START WITH these values, case-insensitive\n\nIf whitelist is present then blacklist is ignored","examples":[["spotify","pandora"]]},"useAvahi":{"type":"boolean","description":"Try to use Avahi and avahi-browse to resolve mDNS devices instead of native mDNS querying\n\nUseful for docker (alpine) container where mDNS resolution is not yet supported. Avahi socket must be exposed to the container and avahi-tools must be installed.","default":false},"useAutoDiscovery":{"type":"boolean","description":"Use mDNS to discovery Google Cast devices on your next automatically?\n\nIf not explicitly set then it is TRUE if `devices` is not set"},"devices":{"type":"array","items":{"$ref":"#/definitions/ChromecastDeviceInfo"},"description":"A list of Google Cast devices to monitor\n\nIf this is used then `useAutoDiscovery` is set to FALSE, if not explicitly set"},"allowUnknownMedia":{"anyOf":[{"type":"boolean"},{"type":"array","items":{"type":"string"}}],"description":"Chromecast Apps report a \"media type\" in the status info returned for whatever is currently playing\n\n* If set to TRUE then Music AND Generic/Unknown media will be tracked for ALL APPS\n* If set to FALSE then only media explicitly typed as Music will be tracked for ALL APPS\n* If set to a list then only Apps whose name contain one of these values, case-insensitive, will have Music AND Generic/Unknown tracked\n\nSee https://developers.google.com/cast/docs/media/messages#MediaInformation \"metadata\" property","default":false},"forceMediaRecognitionOn":{"type":"array","items":{"type":"string"},"description":"Media provided by any App whose name is listed here will ALWAYS be tracked, regardless of the \"media type\" reported\n\nApps will be recognized if they CONTAIN any of these values, case-insensitive"}},"title":"ChromecastData"},"ChromecastDeviceInfo":{"type":"object","properties":{"name":{"type":"string","description":"A friendly name to identify this device","examples":["MySmartTV"]},"address":{"type":"string","description":"The IP address of the device","examples":["192.168.0.115"]}},"required":["name","address"],"title":"ChromecastDeviceInfo"},"MusikcubeSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MusikcubeData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["musikcube"]}},"required":["data","type"],"title":"MusikcubeSourceAIOConfig"},"MusikcubeData":{"type":"object","properties":{"url":{"type":"string","description":"URL of the Musikcube Websocket (Metadata) server to connect to\n\nYou MUST have enabled 'metadata' server and set a password: https://github.com/clangen/musikcube/wiki/remote-api-documentation\n * musikcube -> settings -> server setup\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `ws://`\n* Hostname => `localhost`\n* Port => `7905`","examples":["ws://localhost:7905"],"default":"ws://localhost:7905"},"password":{"type":"string","description":"Password set in Musikcube https://github.com/clangen/musikcube/wiki/remote-api-documentation\n\n* musikcube -> settings -> server setup -> password"},"device_id":{"type":"string"}},"required":["password"],"title":"MusikcubeData"},"MusicCastSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MusicCastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["musiccast"]}},"required":["data","type"],"title":"MusicCastSourceAIOConfig"},"MusicCastData":{"type":"object","properties":{"url":{"type":"string","description":"The host or URL of the YamahaExtendedControl endpoint to use","examples":[["192.168.0.101","http://192.168.0.101/YamahaExtendedControl"]]}},"required":["url"],"title":"MusicCastData"},"MPDSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MPDData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/MPDSourceOptions"},"type":{"type":"string","enum":["mpd"]}},"required":["data","options","type"],"title":"MPDSourceAIOConfig"},"MPDData":{"type":"object","properties":{"url":{"type":"string","description":"URL:PORT of the MPD server to connect to\n\nTo use this you must have TCP connections enabled for your MPD server https://mpd.readthedocs.io/en/stable/user.html#client-connections","examples":["localhost:6600"],"default":"localhost:6600"},"path":{"type":"string","description":"If using socket specify the path instead of url.\n\ntrailing `~` is replaced by your home directory"},"password":{"type":"string","description":"Password for the server, if set https://mpd.readthedocs.io/en/stable/user.html#permissions-and-passwords"}},"title":"MPDData"},"MPDSourceOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"MPDSourceOptions"},"VLCSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/VLCData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/VLCSourceOptions"},"type":{"type":"string","enum":["vlc"]}},"required":["data","type"],"title":"VLCSourceAIOConfig"},"VLCData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL:PORT of the VLC server to connect to\n\nTo use this you must have the Web (http) interface module enabled and a password set https://foxxmd.github.io/multi-scrobbler/docs/configuration#vlc","examples":["localhost:8080"],"default":"localhost:8080"},"password":{"type":"string","description":"Password for the server"}},"required":["password"],"title":"VLCData"},"VLCSourceOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"},"filenamePatterns":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"A list of regular expressions to use to extract metadata (title, album, artist) from a filename\n\nUsed when VLC reports only the filename for the current audio track"},"logFilenamePatterns":{"type":"boolean","description":"Log to DEBUG when a filename-only track is matched or not matched by filenamePatterns","default":false},"dumpVlcMetadata":{"type":"boolean","description":"Dump all the metadata VLC reports for an audio track to DEBUG.\n\nUse this if reporting an issue with VLC not correctly capturing metadata for a track.","default":false}},"title":"VLCSourceOptions"},"IcecastSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/IcecastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/IcecastSourceOptions"},"type":{"type":"string","enum":["icecast"]}},"required":["data","type"],"title":"IcecastSourceAIOConfig"},"IcecastData":{"type":"object","properties":{"sources":{"type":"array","items":{"$ref":"#/definitions/IcecastSource"}},"icestatsEndpoint":{"type":"string"},"statsEndpoint":{"type":"string"},"nextsongsEndpoint":{"type":"string"},"sevenhtmlEndpoint":{"type":"string"},"icyMetaInt":{"type":"number"},"url":{"type":"string","description":"The Icecast stream URL"}},"required":["url"],"title":"IcecastData"},"IcecastSource":{"type":"string","enum":["icy","ogg","icestats","stats","sevenhtml","nextsongs"],"title":"IcecastSource"},"IcecastSourceOptions":{"type":"object","properties":{"systemScrobble":{"type":"boolean","description":"For Sources that support manual listening, should MS default to scrobbling when no user interaction has occurred?\n\nIf not specified MS will use a Source's specific behavior, see Source's documentation."},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"IcecastSourceOptions"},"AzuracastSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/AzuracastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["azuracast"]}},"required":["data","type"],"title":"AzuracastSourceAIOConfig"},"AzuracastData":{"type":"object","properties":{"url":{"type":"string","description":"Base URL of the Azuracast instance\n\nThis does NOT include the station. If a station is included it will be ignored. Use `station` field to specify station, if necessary","examples":["https://radio.mydomain.tld","http://localhost:80"]},"station":{"type":"string","description":"The specific station to monitor\n\nScrobbling will only occur if any of the monitor conditions are met AND the station is ONLINE.\n\nTo monitor multiple stations create a Source for each station.","examples":["my-station-1"]},"monitorWhenListeners":{"type":["boolean","number"],"description":"Only activate scrobble monitoring if station\n\n* `true` => has any current listeners\n* `number` => has EQUAL TO or MORE THAN X number of listeners"},"monitorWhenLive":{"type":"boolean","description":"Only activate scrobble monitoring if station has a live DJ/Streamer","default":true},"apiKey":{"type":"string","description":"API Key used to access data about private streams\n\nhttps://www.azuracast.com/docs/developers/apis/#api-authentication"}},"required":["url","station"],"title":"AzuracastData"},"ClientAIOConfig":{"anyOf":[{"$ref":"#/definitions/MalojaClientAIOConfig"},{"$ref":"#/definitions/LastfmClientAIOConfig"},{"$ref":"#/definitions/ListenBrainzClientAIOConfig"}],"title":"ClientAIOConfig"},"MalojaClientAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this client. Used with sources to restrict where scrobbles are sent.","examples":["MyConfig"]},"data":{"$ref":"#/definitions/MalojaClientData","description":"Specific data required to configure this client"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"options":{"$ref":"#/definitions/CommonClientOptions"},"type":{"type":"string","enum":["maloja"]}},"required":["data","name","type"],"title":"MalojaClientAIOConfig"},"MalojaClientData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for maloja server","examples":["http://localhost:42010"]},"apiKey":{"type":"string","description":"API Key for Maloja server","examples":["myApiKey"]}},"required":["url","apiKey"],"title":"MalojaClientData"},"CommonClientOptions":{"type":"object","properties":{"refreshEnabled":{"type":"boolean","description":"Try to get fresh scrobble history from client when tracks to be scrobbled are newer than the last scrobble found in client history","default":true,"examples":[true]},"refreshStaleAfter":{"type":"number","description":"Refresh scrobbled plays from upstream service if last refresh was at least X seconds ago\n\n**In most case this setting does NOT need to be changed.** The default value is sufficient for the majority of use-cases. Increasing this setting may increase upstream service load and slow down scrobbles.\n\nThis setting should only be changed in specific scenarios where MS is handling multiple \"relaying\" client-services (IE lfm -> lz -> lfm) and there is the potential for a client to be out of sync after more than a few seconds.","examples":[60],"default":60},"refreshMinInterval":{"type":"number","description":"Minimum time (milliseconds) required to pass before upstream scrobbles can be refreshed.\n\n**In most case this setting does NOT need to be changed.** This will always be equal to or smaller than `refreshStaleAfter`.","default":5000,"examples":[5000]},"refreshInitialCount":{"type":"number","description":"The number of tracks to retrieve on initial refresh (related to scrobbleBacklogCount). If not specified this is the maximum supported by the client in 1 API call."},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"checkExistingScrobbles":{"type":"boolean","description":"Check client for an existing scrobble at the same recorded time as the \"new\" track to be scrobbled. If an existing scrobble is found this track is not track scrobbled.","default":true,"examples":[true]},"verbose":{"type":"object","properties":{"match":{"$ref":"#/definitions/MatchLoggingOptions"}},"description":"Options used for increasing verbosity of logging in MS (used for debugging)"},"deadLetterRetries":{"type":"number","description":"Number of times MS should automatically retry scrobbles in dead letter queue","default":1,"examples":[1]},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"CommonClientOptions"},"LastfmClientAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this client. Used with sources to restrict where scrobbles are sent.","examples":["MyConfig"]},"data":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"apiKey":{"type":"string","description":"API Key generated from Last.fm account","examples":["787c921a2a2ab42320831aba0c8f2fc2"]},"secret":{"type":"string","description":"Secret generated from Last.fm account","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"session":{"type":"string","description":"Optional session id returned from a completed auth flow"},"redirectUri":{"type":"string","description":"Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.","default":"http://localhost:9078/lastfm/callback","examples":["http://localhost:9078/lastfm/callback"]}},"required":["apiKey","secret"],"description":"Specific data required to configure this client"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"options":{"$ref":"#/definitions/CommonClientOptions"},"configureAs":{"type":"string","enum":["client","source"],"description":"Should always be `client` when using LastFM as a client","default":"client","examples":["client"]},"type":{"type":"string","enum":["lastfm"]}},"required":["data","name","type"],"title":"LastfmClientAIOConfig"},"ListenBrainzClientAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this client. Used with sources to restrict where scrobbles are sent.","examples":["MyConfig"]},"data":{"$ref":"#/definitions/ListenBrainzClientData","description":"Specific data required to configure this client"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"options":{"$ref":"#/definitions/CommonClientOptions"},"configureAs":{"type":"string","enum":["client","source"],"description":"Should always be `client` when using Listenbrainz as a client","default":"client","examples":["client"]},"type":{"type":"string","enum":["listenbrainz"]}},"required":["data","name","type"],"title":"ListenBrainzClientAIOConfig"},"ListenBrainzClientData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for the ListenBrainz server, if not using the default","examples":["https://api.listenbrainz.org/"],"default":"https://api.listenbrainz.org/"},"token":{"type":"string","description":"User token for the user to scrobble for","examples":["6794186bf-1157-4de6-80e5-uvb411f3ea2b"]},"username":{"type":"string","description":"Username of the user to scrobble for"}},"required":["token","username"],"title":"ListenBrainzClientData"},"WebhookConfig":{"anyOf":[{"$ref":"#/definitions/GotifyConfig"},{"$ref":"#/definitions/NtfyConfig"},{"$ref":"#/definitions/AppriseConfig"}],"title":"WebhookConfig"},"GotifyConfig":{"type":"object","properties":{"type":{"type":"string","enum":["gotify","ntfy","apprise"],"description":"Webhook type. Valid values are:\n\n* gotify\n* ntfy","examples":["gotify"]},"name":{"type":"string","description":"A friendly name used to identify webhook config in logs"},"url":{"type":"string","description":"The URL of the Gotify server. Same URL that would be used to reach the Gotify UI","examples":["http://192.168.0.100:8078"]},"token":{"type":"string","description":"The token created for this Application in Gotify","examples":["AQZI58fA.rfSZbm"]},"priorities":{"$ref":"#/definitions/PrioritiesConfig","description":"Priority of messages\n\n* Info -> 5\n* Warn -> 7\n* Error -> 10"}},"required":["token","type","url"],"title":"GotifyConfig"},"PrioritiesConfig":{"type":"object","properties":{"info":{"type":"number","examples":[5]},"warn":{"type":"number","examples":[7]},"error":{"type":"number","examples":[10]}},"required":["info","warn","error"],"title":"PrioritiesConfig"},"NtfyConfig":{"type":"object","properties":{"type":{"type":"string","enum":["gotify","ntfy","apprise"],"description":"Webhook type. Valid values are:\n\n* gotify\n* ntfy","examples":["gotify"]},"name":{"type":"string","description":"A friendly name used to identify webhook config in logs"},"url":{"type":"string","description":"The URL of the Ntfy server","examples":["http://192.168.0.100:8078"]},"topic":{"type":"string","description":"The topic mutli-scrobbler should POST to"},"username":{"type":"string","description":"Required if topic is protected"},"password":{"type":"string","description":"Required if topic is protected"},"priorities":{"$ref":"#/definitions/PrioritiesConfig","description":"Priority of messages\n\n* Info -> 3\n* Warn -> 4\n* Error -> 5"}},"required":["topic","type","url"],"title":"NtfyConfig"},"AppriseConfig":{"type":"object","properties":{"type":{"type":"string","enum":["gotify","ntfy","apprise"],"description":"Webhook type. Valid values are:\n\n* gotify\n* ntfy","examples":["gotify"]},"name":{"type":"string","description":"A friendly name used to identify webhook config in logs"},"host":{"type":"string","description":"The URL of the apprise-api server","examples":["http://192.168.0.100:8078"]},"urls":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"If using [Stateless Endpoints](https://github.com/caronc/apprise-api?tab=readme-ov-file#stateless-solution) the Apprise config URL(s) to send"},"keys":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"If using [Persistent Store Endpoints](https://github.com/caronc/apprise-api?tab=readme-ov-file#persistent-storage-solution) the Configuration ID(s) to send to\n\nNote: If multiple keys are defined then MS will attempt to POST to each one individually"},"tags":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Optional [tag(s)](https://github.com/caronc/apprise-api?tab=readme-ov-file#tagging) to send in the notification payload"}},"required":["host","type"],"title":"AppriseConfig"},"LogOptions":{"type":"object","properties":{"level":{"$ref":"#/definitions/LogLevel","description":"Specify the minimum log level for all log outputs without their own level specified.\n\nDefaults to env `LOG_LEVEL` or `info` if not specified.","default":"info"},"file":{"anyOf":[{"$ref":"#/definitions/LogLevel"},{"type":"boolean","const":false},{"$ref":"#/definitions/FileLogOptions"}],"description":"Specify the minimum log level to output to rotating files or file output options. If `false` no log files will be created."},"console":{"$ref":"#/definitions/LogLevel","description":"Specify the minimum log level streamed to the console (or docker container)"}},"description":"Configure log levels and file options for an AppLogger.\n\n```ts\nconst infoLogger = loggerApp({\n level: 'info' // console and file will log any levels `info` and above\n});\n\nconst logger = loggerApp({\n console: 'debug', // console will log `debug` and higher\n file: 'warn' // file will log `warn` and higher\n});\n\nconst fileLogger = loggerRollingApp({\n console: 'debug', // console will log `debug` and higher\n file: {\n level: 'warn', // file will log `warn` and higher\n path: '/my/cool/path/output.log', // optionally, output to log file at this path\n frequency: 'hourly', // optionally, rotate hourly\n }\n});\n```","title":"LogOptions"}}} \ No newline at end of file +{"$schema":"http://json-schema.org/draft-07/schema#","type":"object","properties":{"sourceDefaults":{"$ref":"#/definitions/SourceDefaults"},"clientDefaults":{"$ref":"#/definitions/ClientDefaults"},"sources":{"type":"array","items":{"$ref":"#/definitions/SourceAIOConfig"}},"clients":{"type":"array","items":{"$ref":"#/definitions/ClientAIOConfig"}},"webhooks":{"type":"array","items":{"$ref":"#/definitions/WebhookConfig"}},"port":{"type":"number","description":"Set the port the multi-scrobbler UI will be served from","default":9078,"examples":[9078]},"baseUrl":{"type":"string","description":"Set the Base URL the application should assume the UI is served from.\n\nThis will affect how default redirect URLs are generated (spotify, lastfm, deezer) and some logging messages.\n\nIt will NOT set the actual interface/IP that the application is listening on.\n\nThis can also be set using the BASE_URL environmental variable.","default":"http://localhost","examples":["http://localhost","http://192.168.0.101","https://ms.myDomain.tld"]},"logging":{"$ref":"#/definitions/LogOptions"},"disableWeb":{"type":"boolean","description":"Disable web server from running/listening on port.\n\nThis will also make any ingress sources (Plex, Jellyfin, Tautulli, etc...) unusable"},"debugMode":{"type":"boolean","description":"Enables ALL relevant logging and debug options for all sources/clients, when none are defined.\n\nThis is a convenience shortcut for enabling all output needed to troubleshoot an issue and does not need to be on for normal operation.\n\nIt can also be enabled with the environmental variable DEBUG_MODE=true","default":false,"examples":[false]},"cache":{"$ref":"#/definitions/CacheConfigOptions"}},"definitions":{"SourceDefaults":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"SourceDefaults"},"LogLevel":{"type":"string","enum":["silent","fatal","error","warn","info","log","verbose","debug"],"description":"Names of log levels that can be invoked on the logger\n\nFrom lowest to highest:\n\n* `debug`\n* `verbose`\n* `log`\n* `info`\n* `warn`\n* `error`\n* `fatal`\n* `silent` (will never output anything)\n\nWhen used in `LogOptions` specifies the **minimum** level the output should log at.","title":"LogLevel"},"FileLogOptions":{"type":"object","properties":{"timestamp":{"type":"string","enum":["unix","iso","auto"],"description":"For rolling log files\n\nWhen\n* value passed to rolling destination is a string (`path` from LogOptions is a string) and\n* `frequency` is defined\n\nThis determines the format of the datetime inserted into the log file name:\n\n* `unix` - unix epoch timestamp in milliseconds\n* `iso` - Full [ISO8601](https://en.wikipedia.org/wiki/ISO_8601) datetime IE '2024-03-07T20:11:34Z'\n* `auto`\n * When frequency is `daily` only inserts date IE YYYY-MM-DD\n * Otherwise inserts full ISO8601 datetime","default":"auto"},"size":{"type":["number","string"],"description":"The maximum size of a given rolling log file.\n\nCan be combined with frequency. Use k, m and g to express values in KB, MB or GB.\n\nNumerical values will be considered as MB.","default":"10MB"},"frequency":{"anyOf":[{"type":"string","enum":["daily"]},{"type":"string","enum":["hourly"]},{"type":"number"}],"description":"The amount of time a given rolling log file is used. Can be combined with size.\n\nUse `daily` or `hourly` to rotate file every day (or every hour). Existing file within the current day (or hour) will be re-used.\n\nNumerical values will be considered as a number of milliseconds. Using a numerical value will always create a new file upon startup.","default":"daily"},"path":{"anyOf":[{"type":"string"},{"$comment":"() => string"}],"description":"The path and filename to use for log files.\n\nIf using rolling files the filename will be appended with `.N` (a number) BEFORE the extension based on rolling status.\n\nMay also be specified using env LOG_PATH or a function that returns a string.\n\nIf path is relative the absolute path will be derived from `logBaseDir` (in `LoggerAppExtras`) which defaults to CWD","default":"./logs/app.log"},"level":{"anyOf":[{"$ref":"#/definitions/LogLevel"},{"type":"boolean","const":false}],"description":"Specify the minimum log level to output to rotating files. If `false` no log files will be created."}},"title":"FileLogOptions"},"ScrobbleThresholds":{"type":"object","properties":{"duration":{"type":["number","null"],"description":"The number of seconds a track has been listened to before it should be considered scrobbled.\n\nSet to null to disable.","default":240,"examples":[240]},"percent":{"type":["number","null"],"description":"The percentage (as an integer) of a track that should have been seen played before it should be scrobbled. Only used if the Source provides information about how long the track is.\n\nSet to null to disable.\n\nNOTE: This should be used with care when the Source is a \"polling\" type (has an 'interval' property). If the track is short and the interval is too high MS may ignore the track if percentage is high because it had not \"seen\" the track for long enough from first discovery, even if you have been playing the track for longer.","default":50,"examples":[50]}},"title":"ScrobbleThresholds"},"PlayTransformOptions":{"type":"object","properties":{"log":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["all"]}]},"preCompare":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"},"compare":{"type":"object","properties":{"candidate":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"},"existing":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"}}},"postCompare":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"}},"title":"PlayTransformOptions"},"PlayTransformPartsConfig<SearchAndReplaceTerm>":{"anyOf":[{"$ref":"#/definitions/PlayTransformPartsArray%3CSearchAndReplaceTerm%3E"},{"$ref":"#/definitions/PlayTransformParts%3CSearchAndReplaceTerm%3E"}],"title":"PlayTransformPartsConfig<SearchAndReplaceTerm>"},"PlayTransformPartsArray<SearchAndReplaceTerm>":{"type":"array","items":{"$ref":"#/definitions/PlayTransformParts%3CSearchAndReplaceTerm%3E"},"title":"PlayTransformPartsArray<SearchAndReplaceTerm>"},"PlayTransformParts<SearchAndReplaceTerm>":{"type":"object","properties":{"when":{"$ref":"#/definitions/WhenConditionsConfig"},"title":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}},"artists":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}},"album":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}}},"title":"PlayTransformParts<SearchAndReplaceTerm>"},"WhenConditionsConfig":{"$ref":"#/definitions/WhenConditions%3Cstring%3E","title":"WhenConditionsConfig"},"WhenConditions<string>":{"type":"array","items":{"$ref":"#/definitions/WhenParts%3Cstring%3E"},"title":"WhenConditions<string>"},"WhenParts<string>":{"$ref":"#/definitions/PlayTransformPartsAtomic%3Cstring%3E","title":"WhenParts<string>"},"PlayTransformPartsAtomic<string>":{"type":"object","properties":{"title":{"type":"string"},"artists":{"type":"string"},"album":{"type":"string"}},"title":"PlayTransformPartsAtomic<string>"},"SearchAndReplaceTerm":{"anyOf":[{"type":"string"},{"$ref":"#/definitions/ConditionalSearchAndReplaceTerm"}],"title":"SearchAndReplaceTerm"},"ConditionalSearchAndReplaceTerm":{"type":"object","properties":{"when":{"$ref":"#/definitions/WhenConditionsConfig"},"search":{},"replace":{}},"required":["search","replace"],"title":"ConditionalSearchAndReplaceTerm"},"ClientDefaults":{"type":"object","properties":{"refreshEnabled":{"type":"boolean","description":"Try to get fresh scrobble history from client when tracks to be scrobbled are newer than the last scrobble found in client history","default":true,"examples":[true]},"refreshStaleAfter":{"type":"number","description":"Refresh scrobbled plays from upstream service if last refresh was at least X seconds ago\n\n**In most case this setting does NOT need to be changed.** The default value is sufficient for the majority of use-cases. Increasing this setting may increase upstream service load and slow down scrobbles.\n\nThis setting should only be changed in specific scenarios where MS is handling multiple \"relaying\" client-services (IE lfm -> lz -> lfm) and there is the potential for a client to be out of sync after more than a few seconds.","examples":[60],"default":60},"refreshMinInterval":{"type":"number","description":"Minimum time (milliseconds) required to pass before upstream scrobbles can be refreshed.\n\n**In most case this setting does NOT need to be changed.** This will always be equal to or smaller than `refreshStaleAfter`.","default":5000,"examples":[5000]},"refreshInitialCount":{"type":"number","description":"The number of tracks to retrieve on initial refresh (related to scrobbleBacklogCount). If not specified this is the maximum supported by the client in 1 API call."},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"checkExistingScrobbles":{"type":"boolean","description":"Check client for an existing scrobble at the same recorded time as the \"new\" track to be scrobbled. If an existing scrobble is found this track is not track scrobbled.","default":true,"examples":[true]},"verbose":{"type":"object","properties":{"match":{"$ref":"#/definitions/MatchLoggingOptions"}},"description":"Options used for increasing verbosity of logging in MS (used for debugging)"},"deadLetterRetries":{"type":"number","description":"Number of times MS should automatically retry scrobbles in dead letter queue","default":1,"examples":[1]},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"ClientDefaults"},"MatchLoggingOptions":{"type":"object","properties":{"onNoMatch":{"type":"boolean","description":"Log to DEBUG when a new track does NOT match an existing scrobble","default":false,"examples":[false]},"onMatch":{"type":"boolean","description":"Log to DEBUG when a new track DOES match an existing scrobble","default":false,"examples":[false]},"confidenceBreakdown":{"type":"boolean","description":"Include confidence breakdowns in track match logging, if applicable","default":false,"examples":[false]}},"description":"Scrobble matching (between new source track and existing client scrobbles) logging options. Used for debugging.","title":"MatchLoggingOptions"},"SourceAIOConfig":{"anyOf":[{"$ref":"#/definitions/SpotifySourceAIOConfig"},{"$ref":"#/definitions/PlexSourceAIOConfig"},{"$ref":"#/definitions/PlexApiSourceAIOConfig"},{"$ref":"#/definitions/TautulliSourceAIOConfig"},{"$ref":"#/definitions/DeezerSourceAIOConfig"},{"$ref":"#/definitions/DeezerInternalAIOConfig"},{"$ref":"#/definitions/ListenbrainzEndpointSourceAIOConfig"},{"$ref":"#/definitions/LastFMEndpointSourceAIOConfig"},{"$ref":"#/definitions/SubsonicSourceAIOConfig"},{"$ref":"#/definitions/JellySourceAIOConfig"},{"$ref":"#/definitions/JellyApiSourceAIOConfig"},{"$ref":"#/definitions/LastFmSouceAIOConfig"},{"$ref":"#/definitions/YTMusicSourceAIOConfig"},{"$ref":"#/definitions/MPRISSourceAIOConfig"},{"$ref":"#/definitions/MopidySourceAIOConfig"},{"$ref":"#/definitions/ListenBrainzSourceAIOConfig"},{"$ref":"#/definitions/JRiverSourceAIOConfig"},{"$ref":"#/definitions/KodiSourceAIOConfig"},{"$ref":"#/definitions/WebScrobblerSourceAIOConfig"},{"$ref":"#/definitions/ChromecastSourceAIOConfig"},{"$ref":"#/definitions/MalojaSourceAIOConfig"},{"$ref":"#/definitions/MusikcubeSourceAIOConfig"},{"$ref":"#/definitions/MusicCastSourceAIOConfig"},{"$ref":"#/definitions/MPDSourceAIOConfig"},{"$ref":"#/definitions/VLCSourceAIOConfig"},{"$ref":"#/definitions/IcecastSourceAIOConfig"},{"$ref":"#/definitions/AzuracastSourceAIOConfig"},{"$ref":"#/definitions/KoitoSourceAIOConfig"}],"title":"SourceAIOConfig"},"SpotifySourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/SpotifySourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["spotify"]}},"required":["data","type"],"title":"SpotifySourceAIOConfig"},"SpotifySourceData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)\n\nIt is unlikely you should need to change this unless you scrobble many very short tracks often\n\nReading:\n* https://developer.spotify.com/documentation/web-api/guides/rate-limits/\n* https://medium.com/mendix/limiting-your-amount-of-calls-in-mendix-most-of-the-time-rest-835dde55b10e\n * Rate limit may ~180 req/min\n* https://community.spotify.com/t5/Spotify-for-Developers/Web-API-ratelimit/m-p/5503150/highlight/true#M7930\n * Informally indicated as 20 req/sec? Probably for burstiness","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"clientId":{"type":"string","description":"spotify client id","examples":["787c921a2a2ab42320831aba0c8f2fc2"]},"clientSecret":{"type":"string","description":"spotify client secret","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"redirectUri":{"type":"string","description":"spotify redirect URI -- required only if not the default shown here. URI must end in \"callback\"","default":"http://localhost:9078/callback","examples":["http://localhost:9078/callback"]}},"required":["clientId","clientSecret"],"title":"SpotifySourceData"},"CommonSourceOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"CommonSourceOptions"},"PlexSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/PlexSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["plex"]}},"required":["data","type"],"title":"PlexSourceAIOConfig"},"PlexSourceData":{"type":"object","properties":{"user":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled","examples":[["MyUser1","MyUser2"]]},"libraries":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of libraries to scrobble tracks from\n\nIf none are provided tracks from all libraries will be scrobbled","examples":[["Audio","Music"]]},"servers":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled","examples":[["MyServerName"]]}},"title":"PlexSourceData"},"PlexApiSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/PlexApiData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/PlexApiOptions"},"type":{"type":"string","enum":["plex"]}},"required":["data","options","type"],"title":"PlexApiSourceAIOConfig"},"PlexApiData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"token":{"type":"string"},"url":{"type":"string","description":"http(s)://HOST:PORT of the Plex server to connect to"},"usersAllow":{"anyOf":[{"type":"string"},{"type":"boolean","enum":[true]},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble for specific users (case-insensitive)\n\nIf `true` MS will scrobble activity from all users"},"usersBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble for these users (case-insensitive)"},"devicesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if device or application name contains strings from this list (case-insensitive)"},"devicesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if device or application name contains strings from this list (case-insensitive)"},"librariesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if library name contains string from this list (case-insensitive)"},"librariesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if library name contains strings from this list (case-insensitive)"}},"required":["url"],"title":"PlexApiData"},"PlexApiOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"},"ignoreInvalidCert":{"type":"boolean","description":"Ignore invalid cert errors when connecting to Plex\n\nUseful for Plex servers using \"Required\" Secure Connections with self-signed certificates\n\nDo not enable unless you know you need this.","default":false}},"title":"PlexApiOptions"},"TautulliSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/PlexSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["tautulli"]}},"required":["data","type"],"title":"TautulliSourceAIOConfig"},"DeezerSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/DeezerData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["deezer"]}},"required":["data","type"],"title":"DeezerSourceAIOConfig"},"DeezerData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"clientId":{"type":"string","description":"deezer client id","examples":["a89cba1569901a0671d5a9875fed4be1"]},"clientSecret":{"type":"string","description":"deezer client secret","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"redirectUri":{"type":"string","description":"deezer redirect URI -- required only if not the default shown here. URI must end in \"callback\"","default":"http://localhost:9078/deezer/callback","examples":["http://localhost:9078/deezer/callback"]}},"required":["clientId","clientSecret"],"title":"DeezerData"},"DeezerInternalAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/DeezerInternalData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/DeezerInternalSourceOptions"},"type":{"type":"string","enum":["deezer"]}},"required":["data","type"],"title":"DeezerInternalAIOConfig"},"DeezerInternalData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"arl":{"type":"string","description":"ARL retrieved from Deezer response header"},"userAgent":{"type":"string","description":"User agent","default":"Mozilla/5.0 (X11; Linux i686; rv:135.0) Gecko/20100101 Firefox/135.0"}},"required":["arl"],"title":"DeezerInternalData"},"DeezerInternalSourceOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"},"fuzzyDiscoveryIgnore":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["aggressive"]}]}},"title":"DeezerInternalSourceOptions"},"ListenbrainzEndpointSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/ListenbrainzEndpointData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["endpointlz"]}},"required":["type"],"title":"ListenbrainzEndpointSourceAIOConfig"},"ListenbrainzEndpointData":{"type":"object","properties":{"slug":{"type":["string","null"],"description":"The URL ending that should be used to identify scrobbles for this source\n\nIf you are using multiple Listenbrainz endpoint sources (scrobbles for many users) you can use a slug to match Sources with individual users/origins\n\nExample:\n\n* slug: 'usera' => API URL: http://localhost:9078/api/listenbrainz/usera\n* slug: 'originb' => API URL: http://localhost:9078/api/listenbrainz/originb\n\nIf no slug is found from an extension's incoming webhook event the first Listenbrainz source without a slug will be used"},"token":{"type":["string","null"],"description":"If an LZ submission request contains this token in the Authorization Header it will be used to match the submission with this Source\n\nSee: https://listenbrainz.readthedocs.io/en/latest/users/api/index.html#add-the-user-token-to-your-requests"}},"title":"ListenbrainzEndpointData"},"LastFMEndpointSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/LastFMEndpointData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["endpointlfm"]}},"required":["type"],"title":"LastFMEndpointSourceAIOConfig"},"LastFMEndpointData":{"type":"object","properties":{"slug":{"type":["string","null"],"description":"The URL ending that should be used to identify scrobbles for this source\n\nIf you are using multiple Last.fm endpoint sources (scrobbles for many users) you can use a slug to match Sources with individual users/origins\n\nExample:\n\n* slug: 'usera' => API URL: http://localhost:9078/api/lastfm/usera\n* slug: 'originb' => API URL: http://localhost:9078/api/lastfm/originb\n\nIf no slug is found from an extension's incoming webhook event the first Last.fm source without a slug will be used"}},"title":"LastFMEndpointData"},"SubsonicSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/SubsonicData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["subsonic"]}},"required":["data","type"],"title":"SubsonicSourceAIOConfig"},"SubsonicData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the subsonic media server to query","examples":["http://airsonic.local"]},"user":{"type":"string","description":"Username to login to the server with","examples":[["MyUser"]]},"password":{"type":"string","description":"Password for the user to login to the server with","examples":["MyPassword"]},"ignoreTlsErrors":{"type":"boolean","description":"If your subsonic server is using self-signed certs you may need to disable TLS errors in order to get a connection\n\nWARNING: This should be used with caution as your traffic may not be encrypted.","default":false},"legacyAuthentication":{"type":"boolean","description":"Older Subsonic versions, and some badly implemented servers (Nextcloud), use legacy authentication which sends your password in CLEAR TEXT. This is less secure than the newer, recommended hashing authentication method but in some cases it is needed. See \"Authentication\" section here => https://www.subsonic.org/pages/api.jsp\n\nIf this option is not specified it will be turned on if the subsonic server responds with error code 41 \"Token authentication not supported for LDAP users.\" -- See Error Handling section => https://www.subsonic.org/pages/api.jsp","default":false},"usersAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble for specific users (case-insensitive)\n\nIf undefined or an empty string/list MS will scrobble activity from all users"}},"required":["url","user","password"],"title":"SubsonicData"},"JellySourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/JellyData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["jellyfin"]}},"required":["data","type"],"title":"JellySourceAIOConfig"},"JellyData":{"type":"object","properties":{"users":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled","examples":[["MyUser1","MyUser2"]]},"servers":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled","examples":[["MyServerName1"]]}},"title":"JellyData"},"JellyApiSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/JellyApiData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/JellyApiOptions"},"type":{"type":"string","enum":["jellyfin"]}},"required":["data","options","type"],"title":"JellyApiSourceAIOConfig"},"JellyApiData":{"type":"object","properties":{"url":{"type":"string","description":"HOST:PORT of the Jellyfin server to connect to"},"user":{"type":"string","description":"The username of the user to authenticate for or track scrobbles for"},"password":{"type":"string","description":"Password of the username to authenticate for\n\nRequired if `apiKey` is not provided."},"apiKey":{"type":"string","description":"API Key to authenticate with.\n\nRequired if `password` is not provided."},"usersAllow":{"anyOf":[{"type":"string"},{"type":"boolean","enum":[true]},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble for specific users (case-insensitive)\n\nIf `true` MS will scrobble activity from all users"},"usersBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble for these users (case-insensitive)"},"devicesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if device or application name contains strings from this list (case-insensitive)"},"devicesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if device or application name contains strings from this list (case-insensitive)"},"librariesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if library name contains string from this list (case-insensitive)"},"librariesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if library name contains strings from this list (case-insensitive)"},"additionalAllowedLibraryTypes":{"type":"array","items":{},"description":"Allow MS to scrobble audio media in libraries classified other than 'music'\n\n`librariesAllow` will achieve the same result as this but this is more convenient if you do not want to explicitly list every library name or are only using `librariesBlock`"},"allowUnknown":{"type":"boolean","description":"Force media with a type of \"Unknown\" to be counted as Audio","default":false},"frontendUrlOverride":{"type":"string","description":"HOST:PORT of the Jellyfin server that your browser will be able to access from the frontend (and thus load images and links from)\nIf unspecified it will use the normal server HOST and PORT from the `url`\nNecessary if you are using a reverse proxy or other network configuration that prevents the frontend from accessing the server directly"}},"required":["url","user"],"title":"JellyApiData"},"JellyApiOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"JellyApiOptions"},"LastFmSouceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/LastFmSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"configureAs":{"type":"string","enum":["source"],"description":"When used in `lastfm.config` this tells multi-scrobbler whether to use this data to configure a source or client.","default":"source","examples":["source"]},"type":{"type":"string","enum":["lastfm"]}},"required":["data","type"],"title":"LastFmSouceAIOConfig"},"LastFmSourceData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"apiKey":{"type":"string","description":"API Key generated from Last.fm account","examples":["787c921a2a2ab42320831aba0c8f2fc2"]},"secret":{"type":"string","description":"Secret generated from Last.fm account","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"session":{"type":"string","description":"Optional session id returned from a completed auth flow"},"redirectUri":{"type":"string","description":"Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.","default":"http://localhost:9078/lastfm/callback","examples":["http://localhost:9078/lastfm/callback"]},"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"}},"required":["apiKey","secret"],"title":"LastFmSourceData"},"YTMusicSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/YTMusicData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"type":"object","properties":{"logAuth":{"type":"boolean","description":"When true MS will log to DEBUG all of the credentials data it receives from YTM"},"logDiff":{"type":"boolean","description":"Always log history diff\n\nBy default MS will log to `WARN` if history diff is inconsistent but does not log if diff is expected (on new tracks found)\nSet this to `true` to ALWAYS log diff on new tracks. Expected diffs will log to `DEBUG` and inconsistent diffs will continue to log to `WARN`","default":false},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}}},"type":{"type":"string","enum":["ytmusic"]}},"required":["type"],"title":"YTMusicSourceAIOConfig"},"YTMusicData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"cookie":{"type":"string","description":"The cookie retrieved from the Request Headers of music.youtube.com after logging in.\n\nSee https://ytmusicapi.readthedocs.io/en/stable/setup/browser.html#copy-authentication-headers for how to retrieve this value.","examples":["VISITOR_INFO1_LIVE=jMp2xA1Xz2_PbVc; __Secure-3PAPISID=3AxsXpy0M/AkISpjek; ..."]},"clientId":{"type":"string","description":"Google Cloud Console project OAuth Client ID\n\nGenerated from a custom OAuth Client, see docs"},"clientSecret":{"type":"string","description":"Google Cloud Console project OAuth Client Secret\n\nGenerated from a custom OAuth Client, see docs"},"redirectUri":{"type":"string","description":"Google Cloud Console project OAuth Client Authorized redirect URI\n\nGenerated from a custom OAuth Client, see docs. multi-scrobbler will generate a default based on BASE_URL.\nOnly specify this if the default does not work for you."},"innertubeOptions":{"$ref":"#/definitions/InnertubeOptions","description":"Additional options for authorization and tailoring YTM client"}},"title":"YTMusicData"},"InnertubeOptions":{"type":"object","properties":{"po_token":{"type":"string","description":"Proof of Origin token\n\nMay be required if YTM starts returning 403"},"visitor_data":{"type":"string","description":"Visitor ID value found in VISITOR_INFO1_LIVE or visitorData cookie\n\nMay be required if YTM starts returning 403"},"account_index":{"type":"number","description":"If account login results in being able to choose multiple account, use a zero-based index to choose which one to monitor","examples":[0,1]},"location":{"type":"string"},"lang":{"type":"string"},"generate_session_locally":{"type":"boolean"},"device_category":{"type":"string"},"client_type":{"type":"string"},"timezone":{"type":"string"}},"title":"InnertubeOptions"},"MPRISSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MPRISData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["mpris"]}},"required":["data","type"],"title":"MPRISSourceAIOConfig"},"MPRISData":{"type":"object","properties":{"blacklist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"DO NOT scrobble from any players that START WITH these values, case-insensitive","examples":[["spotify","vlc"]]},"whitelist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"ONLY from any players that START WITH these values, case-insensitive\n\nIf whitelist is present then blacklist is ignored","examples":[["spotify","vlc"]]}},"title":"MPRISData"},"MopidySourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MopidyData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["mopidy"]}},"required":["data","type"],"title":"MopidySourceAIOConfig"},"MopidyData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the Mopidy HTTP server to connect to\n\nYou MUST have Mopidy-HTTP extension enabled: https://mopidy.com/ext/http\n\nmulti-scrobbler connects to the WebSocket endpoint that ultimately looks like this => `ws://localhost:6680/mopidy/ws/`\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `ws://`\n* Hostname => `localhost`\n* Port => `6680`\n* Path => `/mopidy/ws/`","examples":["ws://localhost:6680/mopidy/ws/"],"default":"ws://localhost:6680/mopidy/ws/"},"uriBlacklist":{"type":"array","items":{"type":"string"},"description":"Do not scrobble tracks whose URI STARTS WITH any of these strings, case-insensitive\n\nEX: Don't scrobble tracks from soundcloud by adding 'soundcloud' to this list.\n\nList is ignored if uriWhitelist is used."},"uriWhitelist":{"type":"array","items":{"type":"string"},"description":"Only scrobble tracks whose URI STARTS WITH any of these strings, case-insensitive\n\nEX: Only scrobble tracks from soundcloud by adding 'soundcloud' to this list."},"albumBlacklist":{"type":"array","items":{"type":"string"},"description":"Remove album data that matches any case-insensitive string from this list when scrobbling,\n\nFor certain sources (Soundcloud) Mopidy does not have all track info (Album) and will instead use \"Soundcloud\" as the Album name. You can prevent multi-scrobbler from using this bad Album data by adding the fake name to this list. Multi-scrobbler will still scrobble the track, just without the bad data.","examples":[["Soundcloud","Mixcloud"]],"default":["Soundcloud"]}},"title":"MopidyData"},"ListenBrainzSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/ListenBrainzSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"configureAs":{"type":"string","enum":["source"],"description":"When used in `listenbrainz.config` this tells multi-scrobbler whether to use this data to configure a source or client.","default":"source","examples":["source"]},"type":{"type":"string","enum":["listenbrainz"]}},"required":["data","type"],"title":"ListenBrainzSourceAIOConfig"},"ListenBrainzSourceData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for the ListenBrainz server, if not using the default","examples":["https://api.listenbrainz.org/"],"default":"https://api.listenbrainz.org/"},"token":{"type":"string","description":"User token for the user to scrobble for","examples":["6794186bf-1157-4de6-80e5-uvb411f3ea2b"]},"username":{"type":"string","description":"Username of the user to scrobble for"}},"required":["token","username"],"title":"ListenBrainzSourceData"},"JRiverSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/JRiverData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["jriver"]}},"required":["data","type"],"title":"JRiverSourceAIOConfig"},"JRiverData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the JRiver HTTP server to connect to\n\nmulti-scrobbler connects to the Web Service Interface endpoint that ultimately looks like this => `http://yourDomain:52199/MCWS/v1/`\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `http://`\n* Hostname => `localhost`\n* Port => `52199`\n* Path => `/MCWS/v1/`","examples":["http://localhost:52199/MCWS/v1/"],"default":"http://localhost:52199/MCWS/v1/"},"username":{"type":"string","description":"If you have enabled authentication, the username you set"},"password":{"type":"string","description":"If you have enabled authentication, the password you set"}},"required":["url"],"title":"JRiverData"},"KodiSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/KodiData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["kodi"]}},"required":["data","type"],"title":"KodiSourceAIOConfig"},"KodiData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the Kodi HTTP server to connect to\n\nmulti-scrobbler connects to the Web Service Interface endpoint that ultimately looks like this => `http://yourDomain:8080/jsonrpc`\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `http://`\n* Hostname => `localhost`\n* Port => `8080`\n* Path => `/jsonrpc`","examples":["http://localhost:8080/jsonrpc"],"default":"http://localhost:8080/jsonrpc"},"username":{"type":"string","description":"The username set for Remote Control via Web Sever"},"password":{"type":"string","description":"The password set for Remote Control via Web Sever"}},"required":["url","username","password"],"title":"KodiData"},"WebScrobblerSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/WebScrobblerData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["webscrobbler"]}},"required":["type"],"title":"WebScrobblerSourceAIOConfig"},"WebScrobblerData":{"type":"object","properties":{"slug":{"type":["string","null"],"description":"The URL ending that should be used to identify scrobbles for this source\n\nIn WebScrobbler's Webhook you must set an 'API URL'. All MS WebScrobbler sources must start like:\n\nhttp://localhost:9078/api/webscrobbler\n\nIf you are using multiple WebScrobbler sources (scrobbles for many users) you must use a slug to match Sources with each users extension.\n\nExample:\n\n* slug: 'usera' => API URL: http://localhost:9078/api/webscrobbler/usera\n* slug: 'userb' => API URL: http://localhost:9078/api/webscrobbler/userb\n\nIf no slug is found from an extension's incoming webhook event the first WebScrobbler source without a slug will be used"},"blacklist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Block scrobbling from specific WebScrobbler Connectors","examples":[["youtube"]]},"whitelist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only allow scrobbling from specific WebScrobbler Connectors","examples":[["mixcloud","soundcloud","bandcamp"]]}},"title":"WebScrobblerData"},"ChromecastSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/ChromecastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["chromecast"]}},"required":["data","type"],"title":"ChromecastSourceAIOConfig"},"ChromecastData":{"type":"object","properties":{"blacklistDevices":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"DO NOT scrobble from any cast devices that START WITH these values, case-insensitive\n\nUseful when used with auto discovery","examples":[["home-mini","family-tv"]]},"whitelistDevices":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"ONLY scrobble from any cast device that START WITH these values, case-insensitive\n\nIf whitelist is present then blacklist is ignored\n\nUseful when used with auto discovery","examples":[["home-mini","family-tv"]]},"blacklistApps":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"DO NOT scrobble from any application that START WITH these values, case-insensitive","examples":[["spotify","pandora"]]},"whitelistApps":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"ONLY scrobble from any application that START WITH these values, case-insensitive\n\nIf whitelist is present then blacklist is ignored","examples":[["spotify","pandora"]]},"useAvahi":{"type":"boolean","description":"Try to use Avahi and avahi-browse to resolve mDNS devices instead of native mDNS querying\n\nUseful for docker (alpine) container where mDNS resolution is not yet supported. Avahi socket must be exposed to the container and avahi-tools must be installed.","default":false},"useAutoDiscovery":{"type":"boolean","description":"Use mDNS to discovery Google Cast devices on your next automatically?\n\nIf not explicitly set then it is TRUE if `devices` is not set"},"devices":{"type":"array","items":{"$ref":"#/definitions/ChromecastDeviceInfo"},"description":"A list of Google Cast devices to monitor\n\nIf this is used then `useAutoDiscovery` is set to FALSE, if not explicitly set"},"allowUnknownMedia":{"anyOf":[{"type":"boolean"},{"type":"array","items":{"type":"string"}}],"description":"Chromecast Apps report a \"media type\" in the status info returned for whatever is currently playing\n\n* If set to TRUE then Music AND Generic/Unknown media will be tracked for ALL APPS\n* If set to FALSE then only media explicitly typed as Music will be tracked for ALL APPS\n* If set to a list then only Apps whose name contain one of these values, case-insensitive, will have Music AND Generic/Unknown tracked\n\nSee https://developers.google.com/cast/docs/media/messages#MediaInformation \"metadata\" property","default":false},"forceMediaRecognitionOn":{"type":"array","items":{"type":"string"},"description":"Media provided by any App whose name is listed here will ALWAYS be tracked, regardless of the \"media type\" reported\n\nApps will be recognized if they CONTAIN any of these values, case-insensitive"}},"title":"ChromecastData"},"ChromecastDeviceInfo":{"type":"object","properties":{"name":{"type":"string","description":"A friendly name to identify this device","examples":["MySmartTV"]},"address":{"type":"string","description":"The IP address of the device","examples":["192.168.0.115"]}},"required":["name","address"],"title":"ChromecastDeviceInfo"},"MalojaSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MalojaSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"configureAs":{"type":"string","enum":["source"],"description":"When used in `maloja.config` this tells multi-scrobbler whether to use this data to configure a source or client.","default":"source","examples":["source"]},"type":{"type":"string","enum":["maloja"]}},"required":["data","type"],"title":"MalojaSourceAIOConfig"},"MalojaSourceData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for maloja server","examples":["http://localhost:42010"]},"apiKey":{"type":"string","description":"API Key for Maloja server","examples":["myApiKey"]}},"required":["apiKey","url"],"title":"MalojaSourceData"},"MusikcubeSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MusikcubeData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["musikcube"]}},"required":["data","type"],"title":"MusikcubeSourceAIOConfig"},"MusikcubeData":{"type":"object","properties":{"url":{"type":"string","description":"URL of the Musikcube Websocket (Metadata) server to connect to\n\nYou MUST have enabled 'metadata' server and set a password: https://github.com/clangen/musikcube/wiki/remote-api-documentation\n * musikcube -> settings -> server setup\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `ws://`\n* Hostname => `localhost`\n* Port => `7905`","examples":["ws://localhost:7905"],"default":"ws://localhost:7905"},"password":{"type":"string","description":"Password set in Musikcube https://github.com/clangen/musikcube/wiki/remote-api-documentation\n\n* musikcube -> settings -> server setup -> password"},"device_id":{"type":"string"}},"required":["password"],"title":"MusikcubeData"},"MusicCastSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MusicCastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["musiccast"]}},"required":["data","type"],"title":"MusicCastSourceAIOConfig"},"MusicCastData":{"type":"object","properties":{"url":{"type":"string","description":"The host or URL of the YamahaExtendedControl endpoint to use","examples":[["192.168.0.101","http://192.168.0.101/YamahaExtendedControl"]]}},"required":["url"],"title":"MusicCastData"},"MPDSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MPDData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/MPDSourceOptions"},"type":{"type":"string","enum":["mpd"]}},"required":["data","options","type"],"title":"MPDSourceAIOConfig"},"MPDData":{"type":"object","properties":{"url":{"type":"string","description":"URL:PORT of the MPD server to connect to\n\nTo use this you must have TCP connections enabled for your MPD server https://mpd.readthedocs.io/en/stable/user.html#client-connections","examples":["localhost:6600"],"default":"localhost:6600"},"path":{"type":"string","description":"If using socket specify the path instead of url.\n\ntrailing `~` is replaced by your home directory"},"password":{"type":"string","description":"Password for the server, if set https://mpd.readthedocs.io/en/stable/user.html#permissions-and-passwords"}},"title":"MPDData"},"MPDSourceOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"MPDSourceOptions"},"VLCSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/VLCData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/VLCSourceOptions"},"type":{"type":"string","enum":["vlc"]}},"required":["data","type"],"title":"VLCSourceAIOConfig"},"VLCData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL:PORT of the VLC server to connect to\n\nTo use this you must have the Web (http) interface module enabled and a password set https://foxxmd.github.io/multi-scrobbler/docs/configuration#vlc","examples":["localhost:8080"],"default":"localhost:8080"},"password":{"type":"string","description":"Password for the server"}},"required":["password"],"title":"VLCData"},"VLCSourceOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"},"filenamePatterns":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"A list of regular expressions to use to extract metadata (title, album, artist) from a filename\n\nUsed when VLC reports only the filename for the current audio track"},"logFilenamePatterns":{"type":"boolean","description":"Log to DEBUG when a filename-only track is matched or not matched by filenamePatterns","default":false},"dumpVlcMetadata":{"type":"boolean","description":"Dump all the metadata VLC reports for an audio track to DEBUG.\n\nUse this if reporting an issue with VLC not correctly capturing metadata for a track.","default":false}},"title":"VLCSourceOptions"},"IcecastSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/IcecastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/IcecastSourceOptions"},"type":{"type":"string","enum":["icecast"]}},"required":["data","type"],"title":"IcecastSourceAIOConfig"},"IcecastData":{"type":"object","properties":{"sources":{"type":"array","items":{"$ref":"#/definitions/IcecastSource"}},"icestatsEndpoint":{"type":"string"},"statsEndpoint":{"type":"string"},"nextsongsEndpoint":{"type":"string"},"sevenhtmlEndpoint":{"type":"string"},"icyMetaInt":{"type":"number"},"url":{"type":"string","description":"The Icecast stream URL"}},"required":["url"],"title":"IcecastData"},"IcecastSource":{"type":"string","enum":["icy","ogg","icestats","stats","sevenhtml","nextsongs"],"title":"IcecastSource"},"IcecastSourceOptions":{"type":"object","properties":{"systemScrobble":{"type":"boolean","description":"For Sources that support manual listening, should MS default to scrobbling when no user interaction has occurred?\n\nIf not specified MS will use a Source's specific behavior, see Source's documentation."},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"IcecastSourceOptions"},"AzuracastSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/AzuracastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"type":{"type":"string","enum":["azuracast"]}},"required":["data","type"],"title":"AzuracastSourceAIOConfig"},"AzuracastData":{"type":"object","properties":{"url":{"type":"string","description":"Base URL of the Azuracast instance\n\nThis does NOT include the station. If a station is included it will be ignored. Use `station` field to specify station, if necessary","examples":["https://radio.mydomain.tld","http://localhost:80"]},"station":{"type":"string","description":"The specific station to monitor\n\nScrobbling will only occur if any of the monitor conditions are met AND the station is ONLINE.\n\nTo monitor multiple stations create a Source for each station.","examples":["my-station-1"]},"monitorWhenListeners":{"type":["boolean","number"],"description":"Only activate scrobble monitoring if station\n\n* `true` => has any current listeners\n* `number` => has EQUAL TO or MORE THAN X number of listeners"},"monitorWhenLive":{"type":"boolean","description":"Only activate scrobble monitoring if station has a live DJ/Streamer","default":true},"apiKey":{"type":"string","description":"API Key used to access data about private streams\n\nhttps://www.azuracast.com/docs/developers/apis/#api-authentication"}},"required":["url","station"],"title":"AzuracastData"},"KoitoSourceAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/KoitoSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"configureAs":{"type":"string","enum":["source"],"description":"When used in `koito.config` this tells multi-scrobbler whether to use this data to configure a source or client.","default":"source","examples":["source"]},"type":{"type":"string","enum":["koito"]}},"required":["data","type"],"title":"KoitoSourceAIOConfig"},"KoitoSourceData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for the Koito server","examples":["http://192.168.0.100:4110"]},"token":{"type":"string","description":"User token for the user to scrobble for","examples":["pM195xPV98CDpk0QW47FIIOR8AKATAX5DblBF-Jq0t1MbbKL"]},"username":{"type":"string","description":"Username of the user to scrobble for"}},"required":["token","url","username"],"title":"KoitoSourceData"},"ClientAIOConfig":{"anyOf":[{"$ref":"#/definitions/MalojaClientAIOConfig"},{"$ref":"#/definitions/LastfmClientAIOConfig"},{"$ref":"#/definitions/ListenBrainzClientAIOConfig"},{"$ref":"#/definitions/KoitoClientAIOConfig"}],"title":"ClientAIOConfig"},"MalojaClientAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this client. Used with sources to restrict where scrobbles are sent.","examples":["MyConfig"]},"data":{"$ref":"#/definitions/MalojaClientData","description":"Specific data required to configure this client"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"options":{"$ref":"#/definitions/CommonClientOptions"},"configureAs":{"type":"string","enum":["client","source"],"description":"Should always be `client` when using Maloja as a client","default":"client","examples":["client"]},"type":{"type":"string","enum":["maloja"]}},"required":["data","name","type"],"title":"MalojaClientAIOConfig"},"MalojaClientData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for maloja server","examples":["http://localhost:42010"]},"apiKey":{"type":"string","description":"API Key for Maloja server","examples":["myApiKey"]}},"required":["apiKey","url"],"title":"MalojaClientData"},"CommonClientOptions":{"type":"object","properties":{"refreshEnabled":{"type":"boolean","description":"Try to get fresh scrobble history from client when tracks to be scrobbled are newer than the last scrobble found in client history","default":true,"examples":[true]},"refreshStaleAfter":{"type":"number","description":"Refresh scrobbled plays from upstream service if last refresh was at least X seconds ago\n\n**In most case this setting does NOT need to be changed.** The default value is sufficient for the majority of use-cases. Increasing this setting may increase upstream service load and slow down scrobbles.\n\nThis setting should only be changed in specific scenarios where MS is handling multiple \"relaying\" client-services (IE lfm -> lz -> lfm) and there is the potential for a client to be out of sync after more than a few seconds.","examples":[60],"default":60},"refreshMinInterval":{"type":"number","description":"Minimum time (milliseconds) required to pass before upstream scrobbles can be refreshed.\n\n**In most case this setting does NOT need to be changed.** This will always be equal to or smaller than `refreshStaleAfter`.","default":5000,"examples":[5000]},"refreshInitialCount":{"type":"number","description":"The number of tracks to retrieve on initial refresh (related to scrobbleBacklogCount). If not specified this is the maximum supported by the client in 1 API call."},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"checkExistingScrobbles":{"type":"boolean","description":"Check client for an existing scrobble at the same recorded time as the \"new\" track to be scrobbled. If an existing scrobble is found this track is not track scrobbled.","default":true,"examples":[true]},"verbose":{"type":"object","properties":{"match":{"$ref":"#/definitions/MatchLoggingOptions"}},"description":"Options used for increasing verbosity of logging in MS (used for debugging)"},"deadLetterRetries":{"type":"number","description":"Number of times MS should automatically retry scrobbles in dead letter queue","default":1,"examples":[1]},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"CommonClientOptions"},"LastfmClientAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this client. Used with sources to restrict where scrobbles are sent.","examples":["MyConfig"]},"data":{"$ref":"#/definitions/LastfmData","description":"Specific data required to configure this client"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"options":{"$ref":"#/definitions/LastfmClientOptions"},"configureAs":{"type":"string","enum":["client","source"],"description":"Should always be `client` when using LastFM as a client","default":"client","examples":["client"]},"type":{"type":"string","enum":["lastfm"]}},"required":["data","name","type"],"title":"LastfmClientAIOConfig"},"LastfmData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"apiKey":{"type":"string","description":"API Key generated from Last.fm account","examples":["787c921a2a2ab42320831aba0c8f2fc2"]},"secret":{"type":"string","description":"Secret generated from Last.fm account","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"session":{"type":"string","description":"Optional session id returned from a completed auth flow"},"redirectUri":{"type":"string","description":"Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.","default":"http://localhost:9078/lastfm/callback","examples":["http://localhost:9078/lastfm/callback"]}},"required":["apiKey","secret"],"title":"LastfmData"},"LastfmClientOptions":{"type":"object","properties":{"nowPlaying":{"anyOf":[{"type":"boolean"},{"type":"array","items":{"type":"string"}}],"description":"Configure if this Client should report Now Playing from Sources that can scrobble to it\n\n* `true` (default) => Report Now Playing from any eligible Source. \n * If multiple Sources are Playing then reported Play is based on alphabetical order of Source names\n* `false` => Do not report Now Playing\n* `string` list => list of Source `names` that should be allowed to report Now Playing. Order of list determine priority of Play to Report.","default":true},"refreshEnabled":{"type":"boolean","description":"Try to get fresh scrobble history from client when tracks to be scrobbled are newer than the last scrobble found in client history","default":true,"examples":[true]},"refreshStaleAfter":{"type":"number","description":"Refresh scrobbled plays from upstream service if last refresh was at least X seconds ago\n\n**In most case this setting does NOT need to be changed.** The default value is sufficient for the majority of use-cases. Increasing this setting may increase upstream service load and slow down scrobbles.\n\nThis setting should only be changed in specific scenarios where MS is handling multiple \"relaying\" client-services (IE lfm -> lz -> lfm) and there is the potential for a client to be out of sync after more than a few seconds.","examples":[60],"default":60},"refreshMinInterval":{"type":"number","description":"Minimum time (milliseconds) required to pass before upstream scrobbles can be refreshed.\n\n**In most case this setting does NOT need to be changed.** This will always be equal to or smaller than `refreshStaleAfter`.","default":5000,"examples":[5000]},"refreshInitialCount":{"type":"number","description":"The number of tracks to retrieve on initial refresh (related to scrobbleBacklogCount). If not specified this is the maximum supported by the client in 1 API call."},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"checkExistingScrobbles":{"type":"boolean","description":"Check client for an existing scrobble at the same recorded time as the \"new\" track to be scrobbled. If an existing scrobble is found this track is not track scrobbled.","default":true,"examples":[true]},"verbose":{"type":"object","properties":{"match":{"$ref":"#/definitions/MatchLoggingOptions"}},"description":"Options used for increasing verbosity of logging in MS (used for debugging)"},"deadLetterRetries":{"type":"number","description":"Number of times MS should automatically retry scrobbles in dead letter queue","default":1,"examples":[1]},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"LastfmClientOptions"},"ListenBrainzClientAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this client. Used with sources to restrict where scrobbles are sent.","examples":["MyConfig"]},"data":{"$ref":"#/definitions/ListenBrainzClientData","description":"Specific data required to configure this client"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"options":{"$ref":"#/definitions/CommonClientOptions"},"configureAs":{"type":"string","enum":["client","source"],"description":"Should always be `client` when using Listenbrainz as a client","default":"client","examples":["client"]},"type":{"type":"string","enum":["listenbrainz"]}},"required":["data","name","type"],"title":"ListenBrainzClientAIOConfig"},"ListenBrainzClientData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for the ListenBrainz server, if not using the default","examples":["https://api.listenbrainz.org/"],"default":"https://api.listenbrainz.org/"},"token":{"type":"string","description":"User token for the user to scrobble for","examples":["6794186bf-1157-4de6-80e5-uvb411f3ea2b"]},"username":{"type":"string","description":"Username of the user to scrobble for"}},"required":["token","username"],"title":"ListenBrainzClientData"},"KoitoClientAIOConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this client. Used with sources to restrict where scrobbles are sent.","examples":["MyConfig"]},"data":{"$ref":"#/definitions/KoitoClientData","description":"Specific data required to configure this client"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"options":{"$ref":"#/definitions/CommonClientOptions"},"configureAs":{"type":"string","enum":["client","source"],"description":"Should always be `client` when using Koito as a client","default":"client","examples":["client"]},"type":{"type":"string","enum":["koito"]}},"required":["data","name","type"],"title":"KoitoClientAIOConfig"},"KoitoClientData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for the Koito server","examples":["http://192.168.0.100:4110"]},"token":{"type":"string","description":"User token for the user to scrobble for","examples":["pM195xPV98CDpk0QW47FIIOR8AKATAX5DblBF-Jq0t1MbbKL"]},"username":{"type":"string","description":"Username of the user to scrobble for"}},"required":["token","url","username"],"title":"KoitoClientData"},"WebhookConfig":{"anyOf":[{"$ref":"#/definitions/GotifyConfig"},{"$ref":"#/definitions/NtfyConfig"},{"$ref":"#/definitions/AppriseConfig"}],"title":"WebhookConfig"},"GotifyConfig":{"type":"object","properties":{"type":{"type":"string","enum":["gotify","ntfy","apprise"],"description":"Webhook type. Valid values are:\n\n* gotify\n* ntfy","examples":["gotify"]},"name":{"type":"string","description":"A friendly name used to identify webhook config in logs"},"url":{"type":"string","description":"The URL of the Gotify server. Same URL that would be used to reach the Gotify UI","examples":["http://192.168.0.100:8078"]},"token":{"type":"string","description":"The token created for this Application in Gotify","examples":["AQZI58fA.rfSZbm"]},"priorities":{"$ref":"#/definitions/PrioritiesConfig","description":"Priority of messages\n\n* Info -> 5\n* Warn -> 7\n* Error -> 10"}},"required":["token","type","url"],"title":"GotifyConfig"},"PrioritiesConfig":{"type":"object","properties":{"info":{"type":"number","examples":[5]},"warn":{"type":"number","examples":[7]},"error":{"type":"number","examples":[10]}},"required":["info","warn","error"],"title":"PrioritiesConfig"},"NtfyConfig":{"type":"object","properties":{"type":{"type":"string","enum":["gotify","ntfy","apprise"],"description":"Webhook type. Valid values are:\n\n* gotify\n* ntfy","examples":["gotify"]},"name":{"type":"string","description":"A friendly name used to identify webhook config in logs"},"url":{"type":"string","description":"The URL of the Ntfy server","examples":["http://192.168.0.100:8078"]},"topic":{"type":"string","description":"The topic mutli-scrobbler should POST to"},"username":{"type":"string","description":"Required if topic is protected"},"password":{"type":"string","description":"Required if topic is protected"},"token":{"type":"string","description":"Use instead of username/password, required if topic is protected"},"priorities":{"$ref":"#/definitions/PrioritiesConfig","description":"Priority of messages\n\n* Info -> 3\n* Warn -> 4\n* Error -> 5"}},"required":["topic","type","url"],"title":"NtfyConfig"},"AppriseConfig":{"type":"object","properties":{"type":{"type":"string","enum":["gotify","ntfy","apprise"],"description":"Webhook type. Valid values are:\n\n* gotify\n* ntfy","examples":["gotify"]},"name":{"type":"string","description":"A friendly name used to identify webhook config in logs"},"host":{"type":"string","description":"The URL of the apprise-api server","examples":["http://192.168.0.100:8078"]},"urls":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"If using [Stateless Endpoints](https://github.com/caronc/apprise-api?tab=readme-ov-file#stateless-solution) the Apprise config URL(s) to send"},"keys":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"If using [Persistent Store Endpoints](https://github.com/caronc/apprise-api?tab=readme-ov-file#persistent-storage-solution) the Configuration ID(s) to send to\n\nNote: If multiple keys are defined then MS will attempt to POST to each one individually"},"tags":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Optional [tag(s)](https://github.com/caronc/apprise-api?tab=readme-ov-file#tagging) to send in the notification payload"}},"required":["host","type"],"title":"AppriseConfig"},"LogOptions":{"type":"object","properties":{"level":{"$ref":"#/definitions/LogLevel","description":"Specify the minimum log level for all log outputs without their own level specified.\n\nDefaults to env `LOG_LEVEL` or `info` if not specified.","default":"info"},"file":{"anyOf":[{"$ref":"#/definitions/LogLevel"},{"type":"boolean","const":false},{"$ref":"#/definitions/FileLogOptions"}],"description":"Specify the minimum log level to output to rotating files or file output options. If `false` no log files will be created."},"console":{"$ref":"#/definitions/LogLevel","description":"Specify the minimum log level streamed to the console (or docker container)"}},"description":"Configure log levels and file options for an AppLogger.\n\n```ts\nconst infoLogger = loggerApp({\n level: 'info' // console and file will log any levels `info` and above\n});\n\nconst logger = loggerApp({\n console: 'debug', // console will log `debug` and higher\n file: 'warn' // file will log `warn` and higher\n});\n\nconst fileLogger = loggerRollingApp({\n console: 'debug', // console will log `debug` and higher\n file: {\n level: 'warn', // file will log `warn` and higher\n path: '/my/cool/path/output.log', // optionally, output to log file at this path\n frequency: 'hourly', // optionally, rotate hourly\n }\n});\n```","title":"LogOptions"},"CacheConfigOptions":{"type":"object","properties":{"metadata":{"$ref":"#/definitions/CacheMetadataConfig"},"scrobble":{"$ref":"#/definitions/CacheScrobbleConfig"}},"title":"CacheConfigOptions"},"CacheMetadataConfig":{"$ref":"#/definitions/CacheConfig%3CCacheMetadaProvider%3E","title":"CacheMetadataConfig"},"CacheConfig<CacheMetadaProvider>":{"type":"object","properties":{"provider":{"$ref":"#/definitions/CacheMetadaProvider"},"connection":{"type":"string"}},"required":["provider"],"title":"CacheConfig<CacheMetadaProvider>"},"CacheMetadaProvider":{"$ref":"#/definitions/CacheProvider","title":"CacheMetadaProvider"},"CacheProvider":{"type":["string","boolean"],"enum":["memory","valkey","file",false],"title":"CacheProvider"},"CacheScrobbleConfig":{"$ref":"#/definitions/CacheConfig%3CCacheScrobbleProvider%3E","title":"CacheScrobbleConfig"},"CacheConfig<CacheScrobbleProvider>":{"type":"object","properties":{"provider":{"$ref":"#/definitions/CacheScrobbleProvider"},"connection":{"type":"string"}},"required":["provider"],"title":"CacheConfig<CacheScrobbleProvider>"},"CacheScrobbleProvider":{"$ref":"#/definitions/CacheProvider","title":"CacheScrobbleProvider"}}} \ No newline at end of file diff --git a/src/backend/common/schema/client.json b/src/backend/common/schema/client.json index 6f07a98e..61de23f1 100644 --- a/src/backend/common/schema/client.json +++ b/src/backend/common/schema/client.json @@ -1 +1 @@ -{"$schema":"http://json-schema.org/draft-07/schema#","anyOf":[{"$ref":"#/definitions/MalojaClientConfig"},{"$ref":"#/definitions/LastfmClientConfig"},{"$ref":"#/definitions/ListenBrainzClientConfig"}],"definitions":{"MalojaClientConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this client. Used with sources to restrict where scrobbles are sent.","examples":["MyConfig"]},"data":{"$ref":"#/definitions/MalojaClientData","description":"Specific data required to configure this client"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"options":{"$ref":"#/definitions/CommonClientOptions"}},"required":["data","name"],"title":"MalojaClientConfig"},"MalojaClientData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for maloja server","examples":["http://localhost:42010"]},"apiKey":{"type":"string","description":"API Key for Maloja server","examples":["myApiKey"]}},"required":["url","apiKey"],"title":"MalojaClientData"},"CommonClientOptions":{"type":"object","properties":{"refreshEnabled":{"type":"boolean","description":"Try to get fresh scrobble history from client when tracks to be scrobbled are newer than the last scrobble found in client history","default":true,"examples":[true]},"refreshStaleAfter":{"type":"number","description":"Refresh scrobbled plays from upstream service if last refresh was at least X seconds ago\n\n**In most case this setting does NOT need to be changed.** The default value is sufficient for the majority of use-cases. Increasing this setting may increase upstream service load and slow down scrobbles.\n\nThis setting should only be changed in specific scenarios where MS is handling multiple \"relaying\" client-services (IE lfm -> lz -> lfm) and there is the potential for a client to be out of sync after more than a few seconds.","examples":[60],"default":60},"refreshMinInterval":{"type":"number","description":"Minimum time (milliseconds) required to pass before upstream scrobbles can be refreshed.\n\n**In most case this setting does NOT need to be changed.** This will always be equal to or smaller than `refreshStaleAfter`.","default":5000,"examples":[5000]},"refreshInitialCount":{"type":"number","description":"The number of tracks to retrieve on initial refresh (related to scrobbleBacklogCount). If not specified this is the maximum supported by the client in 1 API call."},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"checkExistingScrobbles":{"type":"boolean","description":"Check client for an existing scrobble at the same recorded time as the \"new\" track to be scrobbled. If an existing scrobble is found this track is not track scrobbled.","default":true,"examples":[true]},"verbose":{"type":"object","properties":{"match":{"$ref":"#/definitions/MatchLoggingOptions"}},"description":"Options used for increasing verbosity of logging in MS (used for debugging)"},"deadLetterRetries":{"type":"number","description":"Number of times MS should automatically retry scrobbles in dead letter queue","default":1,"examples":[1]},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"CommonClientOptions"},"MatchLoggingOptions":{"type":"object","properties":{"onNoMatch":{"type":"boolean","description":"Log to DEBUG when a new track does NOT match an existing scrobble","default":false,"examples":[false]},"onMatch":{"type":"boolean","description":"Log to DEBUG when a new track DOES match an existing scrobble","default":false,"examples":[false]},"confidenceBreakdown":{"type":"boolean","description":"Include confidence breakdowns in track match logging, if applicable","default":false,"examples":[false]}},"description":"Scrobble matching (between new source track and existing client scrobbles) logging options. Used for debugging.","title":"MatchLoggingOptions"},"PlayTransformOptions":{"type":"object","properties":{"log":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["all"]}]},"preCompare":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"},"compare":{"type":"object","properties":{"candidate":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"},"existing":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"}}},"postCompare":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"}},"title":"PlayTransformOptions"},"PlayTransformPartsConfig<SearchAndReplaceTerm>":{"anyOf":[{"$ref":"#/definitions/PlayTransformPartsArray%3CSearchAndReplaceTerm%3E"},{"$ref":"#/definitions/PlayTransformParts%3CSearchAndReplaceTerm%3E"}],"title":"PlayTransformPartsConfig<SearchAndReplaceTerm>"},"PlayTransformPartsArray<SearchAndReplaceTerm>":{"type":"array","items":{"$ref":"#/definitions/PlayTransformParts%3CSearchAndReplaceTerm%3E"},"title":"PlayTransformPartsArray<SearchAndReplaceTerm>"},"PlayTransformParts<SearchAndReplaceTerm>":{"type":"object","properties":{"when":{"$ref":"#/definitions/WhenConditionsConfig"},"title":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}},"artists":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}},"album":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}}},"title":"PlayTransformParts<SearchAndReplaceTerm>"},"WhenConditionsConfig":{"$ref":"#/definitions/WhenConditions%3Cstring%3E","title":"WhenConditionsConfig"},"WhenConditions<string>":{"type":"array","items":{"$ref":"#/definitions/WhenParts%3Cstring%3E"},"title":"WhenConditions<string>"},"WhenParts<string>":{"$ref":"#/definitions/PlayTransformPartsAtomic%3Cstring%3E","title":"WhenParts<string>"},"PlayTransformPartsAtomic<string>":{"type":"object","properties":{"title":{"type":"string"},"artists":{"type":"string"},"album":{"type":"string"}},"title":"PlayTransformPartsAtomic<string>"},"SearchAndReplaceTerm":{"anyOf":[{"type":"string"},{"$ref":"#/definitions/ConditionalSearchAndReplaceTerm"}],"title":"SearchAndReplaceTerm"},"ConditionalSearchAndReplaceTerm":{"type":"object","properties":{"when":{"$ref":"#/definitions/WhenConditionsConfig"},"search":{},"replace":{}},"required":["search","replace"],"title":"ConditionalSearchAndReplaceTerm"},"LastfmClientConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this client. Used with sources to restrict where scrobbles are sent.","examples":["MyConfig"]},"data":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"apiKey":{"type":"string","description":"API Key generated from Last.fm account","examples":["787c921a2a2ab42320831aba0c8f2fc2"]},"secret":{"type":"string","description":"Secret generated from Last.fm account","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"session":{"type":"string","description":"Optional session id returned from a completed auth flow"},"redirectUri":{"type":"string","description":"Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.","default":"http://localhost:9078/lastfm/callback","examples":["http://localhost:9078/lastfm/callback"]}},"required":["apiKey","secret"],"description":"Specific data required to configure this client"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"options":{"$ref":"#/definitions/CommonClientOptions"},"configureAs":{"type":"string","enum":["client","source"],"description":"Should always be `client` when using LastFM as a client","default":"client","examples":["client"]}},"required":["data","name"],"title":"LastfmClientConfig"},"ListenBrainzClientConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this client. Used with sources to restrict where scrobbles are sent.","examples":["MyConfig"]},"data":{"$ref":"#/definitions/ListenBrainzClientData","description":"Specific data required to configure this client"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"options":{"$ref":"#/definitions/CommonClientOptions"},"configureAs":{"type":"string","enum":["client","source"],"description":"Should always be `client` when using Listenbrainz as a client","default":"client","examples":["client"]}},"required":["data","name"],"title":"ListenBrainzClientConfig"},"ListenBrainzClientData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for the ListenBrainz server, if not using the default","examples":["https://api.listenbrainz.org/"],"default":"https://api.listenbrainz.org/"},"token":{"type":"string","description":"User token for the user to scrobble for","examples":["6794186bf-1157-4de6-80e5-uvb411f3ea2b"]},"username":{"type":"string","description":"Username of the user to scrobble for"}},"required":["token","username"],"title":"ListenBrainzClientData"}}} \ No newline at end of file +{"$schema":"http://json-schema.org/draft-07/schema#","anyOf":[{"$ref":"#/definitions/MalojaClientConfig"},{"$ref":"#/definitions/LastfmClientConfig"},{"$ref":"#/definitions/ListenBrainzClientConfig"},{"$ref":"#/definitions/KoitoClientConfig"}],"definitions":{"MalojaClientConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this client. Used with sources to restrict where scrobbles are sent.","examples":["MyConfig"]},"data":{"$ref":"#/definitions/MalojaClientData","description":"Specific data required to configure this client"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"options":{"$ref":"#/definitions/CommonClientOptions"},"configureAs":{"type":"string","enum":["client","source"],"description":"Should always be `client` when using Maloja as a client","default":"client","examples":["client"]}},"required":["data","name"],"title":"MalojaClientConfig"},"MalojaClientData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for maloja server","examples":["http://localhost:42010"]},"apiKey":{"type":"string","description":"API Key for Maloja server","examples":["myApiKey"]}},"required":["apiKey","url"],"title":"MalojaClientData"},"CommonClientOptions":{"type":"object","properties":{"refreshEnabled":{"type":"boolean","description":"Try to get fresh scrobble history from client when tracks to be scrobbled are newer than the last scrobble found in client history","default":true,"examples":[true]},"refreshStaleAfter":{"type":"number","description":"Refresh scrobbled plays from upstream service if last refresh was at least X seconds ago\n\n**In most case this setting does NOT need to be changed.** The default value is sufficient for the majority of use-cases. Increasing this setting may increase upstream service load and slow down scrobbles.\n\nThis setting should only be changed in specific scenarios where MS is handling multiple \"relaying\" client-services (IE lfm -> lz -> lfm) and there is the potential for a client to be out of sync after more than a few seconds.","examples":[60],"default":60},"refreshMinInterval":{"type":"number","description":"Minimum time (milliseconds) required to pass before upstream scrobbles can be refreshed.\n\n**In most case this setting does NOT need to be changed.** This will always be equal to or smaller than `refreshStaleAfter`.","default":5000,"examples":[5000]},"refreshInitialCount":{"type":"number","description":"The number of tracks to retrieve on initial refresh (related to scrobbleBacklogCount). If not specified this is the maximum supported by the client in 1 API call."},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"checkExistingScrobbles":{"type":"boolean","description":"Check client for an existing scrobble at the same recorded time as the \"new\" track to be scrobbled. If an existing scrobble is found this track is not track scrobbled.","default":true,"examples":[true]},"verbose":{"type":"object","properties":{"match":{"$ref":"#/definitions/MatchLoggingOptions"}},"description":"Options used for increasing verbosity of logging in MS (used for debugging)"},"deadLetterRetries":{"type":"number","description":"Number of times MS should automatically retry scrobbles in dead letter queue","default":1,"examples":[1]},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"CommonClientOptions"},"MatchLoggingOptions":{"type":"object","properties":{"onNoMatch":{"type":"boolean","description":"Log to DEBUG when a new track does NOT match an existing scrobble","default":false,"examples":[false]},"onMatch":{"type":"boolean","description":"Log to DEBUG when a new track DOES match an existing scrobble","default":false,"examples":[false]},"confidenceBreakdown":{"type":"boolean","description":"Include confidence breakdowns in track match logging, if applicable","default":false,"examples":[false]}},"description":"Scrobble matching (between new source track and existing client scrobbles) logging options. Used for debugging.","title":"MatchLoggingOptions"},"PlayTransformOptions":{"type":"object","properties":{"log":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["all"]}]},"preCompare":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"},"compare":{"type":"object","properties":{"candidate":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"},"existing":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"}}},"postCompare":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"}},"title":"PlayTransformOptions"},"PlayTransformPartsConfig<SearchAndReplaceTerm>":{"anyOf":[{"$ref":"#/definitions/PlayTransformPartsArray%3CSearchAndReplaceTerm%3E"},{"$ref":"#/definitions/PlayTransformParts%3CSearchAndReplaceTerm%3E"}],"title":"PlayTransformPartsConfig<SearchAndReplaceTerm>"},"PlayTransformPartsArray<SearchAndReplaceTerm>":{"type":"array","items":{"$ref":"#/definitions/PlayTransformParts%3CSearchAndReplaceTerm%3E"},"title":"PlayTransformPartsArray<SearchAndReplaceTerm>"},"PlayTransformParts<SearchAndReplaceTerm>":{"type":"object","properties":{"when":{"$ref":"#/definitions/WhenConditionsConfig"},"title":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}},"artists":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}},"album":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}}},"title":"PlayTransformParts<SearchAndReplaceTerm>"},"WhenConditionsConfig":{"$ref":"#/definitions/WhenConditions%3Cstring%3E","title":"WhenConditionsConfig"},"WhenConditions<string>":{"type":"array","items":{"$ref":"#/definitions/WhenParts%3Cstring%3E"},"title":"WhenConditions<string>"},"WhenParts<string>":{"$ref":"#/definitions/PlayTransformPartsAtomic%3Cstring%3E","title":"WhenParts<string>"},"PlayTransformPartsAtomic<string>":{"type":"object","properties":{"title":{"type":"string"},"artists":{"type":"string"},"album":{"type":"string"}},"title":"PlayTransformPartsAtomic<string>"},"SearchAndReplaceTerm":{"anyOf":[{"type":"string"},{"$ref":"#/definitions/ConditionalSearchAndReplaceTerm"}],"title":"SearchAndReplaceTerm"},"ConditionalSearchAndReplaceTerm":{"type":"object","properties":{"when":{"$ref":"#/definitions/WhenConditionsConfig"},"search":{},"replace":{}},"required":["search","replace"],"title":"ConditionalSearchAndReplaceTerm"},"LastfmClientConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this client. Used with sources to restrict where scrobbles are sent.","examples":["MyConfig"]},"data":{"$ref":"#/definitions/LastfmData","description":"Specific data required to configure this client"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"options":{"$ref":"#/definitions/LastfmClientOptions"},"configureAs":{"type":"string","enum":["client","source"],"description":"Should always be `client` when using LastFM as a client","default":"client","examples":["client"]}},"required":["data","name"],"title":"LastfmClientConfig"},"LastfmData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"apiKey":{"type":"string","description":"API Key generated from Last.fm account","examples":["787c921a2a2ab42320831aba0c8f2fc2"]},"secret":{"type":"string","description":"Secret generated from Last.fm account","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"session":{"type":"string","description":"Optional session id returned from a completed auth flow"},"redirectUri":{"type":"string","description":"Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.","default":"http://localhost:9078/lastfm/callback","examples":["http://localhost:9078/lastfm/callback"]}},"required":["apiKey","secret"],"title":"LastfmData"},"LastfmClientOptions":{"type":"object","properties":{"nowPlaying":{"anyOf":[{"type":"boolean"},{"type":"array","items":{"type":"string"}}],"description":"Configure if this Client should report Now Playing from Sources that can scrobble to it\n\n* `true` (default) => Report Now Playing from any eligible Source. \n * If multiple Sources are Playing then reported Play is based on alphabetical order of Source names\n* `false` => Do not report Now Playing\n* `string` list => list of Source `names` that should be allowed to report Now Playing. Order of list determine priority of Play to Report.","default":true},"refreshEnabled":{"type":"boolean","description":"Try to get fresh scrobble history from client when tracks to be scrobbled are newer than the last scrobble found in client history","default":true,"examples":[true]},"refreshStaleAfter":{"type":"number","description":"Refresh scrobbled plays from upstream service if last refresh was at least X seconds ago\n\n**In most case this setting does NOT need to be changed.** The default value is sufficient for the majority of use-cases. Increasing this setting may increase upstream service load and slow down scrobbles.\n\nThis setting should only be changed in specific scenarios where MS is handling multiple \"relaying\" client-services (IE lfm -> lz -> lfm) and there is the potential for a client to be out of sync after more than a few seconds.","examples":[60],"default":60},"refreshMinInterval":{"type":"number","description":"Minimum time (milliseconds) required to pass before upstream scrobbles can be refreshed.\n\n**In most case this setting does NOT need to be changed.** This will always be equal to or smaller than `refreshStaleAfter`.","default":5000,"examples":[5000]},"refreshInitialCount":{"type":"number","description":"The number of tracks to retrieve on initial refresh (related to scrobbleBacklogCount). If not specified this is the maximum supported by the client in 1 API call."},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"checkExistingScrobbles":{"type":"boolean","description":"Check client for an existing scrobble at the same recorded time as the \"new\" track to be scrobbled. If an existing scrobble is found this track is not track scrobbled.","default":true,"examples":[true]},"verbose":{"type":"object","properties":{"match":{"$ref":"#/definitions/MatchLoggingOptions"}},"description":"Options used for increasing verbosity of logging in MS (used for debugging)"},"deadLetterRetries":{"type":"number","description":"Number of times MS should automatically retry scrobbles in dead letter queue","default":1,"examples":[1]},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"LastfmClientOptions"},"ListenBrainzClientConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this client. Used with sources to restrict where scrobbles are sent.","examples":["MyConfig"]},"data":{"$ref":"#/definitions/ListenBrainzClientData","description":"Specific data required to configure this client"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"options":{"$ref":"#/definitions/CommonClientOptions"},"configureAs":{"type":"string","enum":["client","source"],"description":"Should always be `client` when using Listenbrainz as a client","default":"client","examples":["client"]}},"required":["data","name"],"title":"ListenBrainzClientConfig"},"ListenBrainzClientData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for the ListenBrainz server, if not using the default","examples":["https://api.listenbrainz.org/"],"default":"https://api.listenbrainz.org/"},"token":{"type":"string","description":"User token for the user to scrobble for","examples":["6794186bf-1157-4de6-80e5-uvb411f3ea2b"]},"username":{"type":"string","description":"Username of the user to scrobble for"}},"required":["token","username"],"title":"ListenBrainzClientData"},"KoitoClientConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this client. Used with sources to restrict where scrobbles are sent.","examples":["MyConfig"]},"data":{"$ref":"#/definitions/KoitoClientData","description":"Specific data required to configure this client"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"options":{"$ref":"#/definitions/CommonClientOptions"},"configureAs":{"type":"string","enum":["client","source"],"description":"Should always be `client` when using Koito as a client","default":"client","examples":["client"]}},"required":["data","name"],"title":"KoitoClientConfig"},"KoitoClientData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for the Koito server","examples":["http://192.168.0.100:4110"]},"token":{"type":"string","description":"User token for the user to scrobble for","examples":["pM195xPV98CDpk0QW47FIIOR8AKATAX5DblBF-Jq0t1MbbKL"]},"username":{"type":"string","description":"Username of the user to scrobble for"}},"required":["token","url","username"],"title":"KoitoClientData"}}} \ No newline at end of file diff --git a/src/backend/common/schema/source.json b/src/backend/common/schema/source.json index c07e706f..08d89836 100644 --- a/src/backend/common/schema/source.json +++ b/src/backend/common/schema/source.json @@ -1 +1 @@ -{"$schema":"http://json-schema.org/draft-07/schema#","anyOf":[{"$ref":"#/definitions/SpotifySourceConfig"},{"$ref":"#/definitions/PlexSourceConfig"},{"$ref":"#/definitions/PlexApiSourceConfig"},{"$ref":"#/definitions/TautulliSourceConfig"},{"$ref":"#/definitions/DeezerSourceConfig"},{"$ref":"#/definitions/ListenbrainzEndpointSourceConfig"},{"$ref":"#/definitions/LastFMEndpointSourceConfig"},{"$ref":"#/definitions/SubSonicSourceConfig"},{"$ref":"#/definitions/JellySourceConfig"},{"$ref":"#/definitions/JellyApiSourceConfig"},{"$ref":"#/definitions/LastfmSourceConfig"},{"$ref":"#/definitions/YTMusicSourceConfig"},{"$ref":"#/definitions/MPRISSourceConfig"},{"$ref":"#/definitions/MopidySourceConfig"},{"$ref":"#/definitions/ListenBrainzSourceConfig"},{"$ref":"#/definitions/JRiverSourceConfig"},{"$ref":"#/definitions/KodiSourceConfig"},{"$ref":"#/definitions/WebScrobblerSourceConfig"},{"$ref":"#/definitions/ChromecastSourceConfig"},{"$ref":"#/definitions/MusikcubeSourceConfig"},{"$ref":"#/definitions/MusicCastSourceConfig"},{"$ref":"#/definitions/MPDSourceConfig"},{"$ref":"#/definitions/VLCSourceConfig"},{"$ref":"#/definitions/IcecastSourceConfig"},{"$ref":"#/definitions/AzuracastSourceConfig"}],"definitions":{"SpotifySourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/SpotifySourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"SpotifySourceConfig"},"SpotifySourceData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)\n\nIt is unlikely you should need to change this unless you scrobble many very short tracks often\n\nReading:\n* https://developer.spotify.com/documentation/web-api/guides/rate-limits/\n* https://medium.com/mendix/limiting-your-amount-of-calls-in-mendix-most-of-the-time-rest-835dde55b10e\n * Rate limit may ~180 req/min\n* https://community.spotify.com/t5/Spotify-for-Developers/Web-API-ratelimit/m-p/5503150/highlight/true#M7930\n * Informally indicated as 20 req/sec? Probably for burstiness","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"clientId":{"type":"string","description":"spotify client id","examples":["787c921a2a2ab42320831aba0c8f2fc2"]},"clientSecret":{"type":"string","description":"spotify client secret","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"redirectUri":{"type":"string","description":"spotify redirect URI -- required only if not the default shown here. URI must end in \"callback\"","default":"http://localhost:9078/callback","examples":["http://localhost:9078/callback"]}},"required":["clientId","clientSecret"],"title":"SpotifySourceData"},"CommonSourceOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"CommonSourceOptions"},"LogLevel":{"type":"string","enum":["silent","fatal","error","warn","info","log","verbose","debug"],"description":"Names of log levels that can be invoked on the logger\n\nFrom lowest to highest:\n\n* `debug`\n* `verbose`\n* `log`\n* `info`\n* `warn`\n* `error`\n* `fatal`\n* `silent` (will never output anything)\n\nWhen used in `LogOptions` specifies the **minimum** level the output should log at.","title":"LogLevel"},"FileLogOptions":{"type":"object","properties":{"timestamp":{"type":"string","enum":["unix","iso","auto"],"description":"For rolling log files\n\nWhen\n* value passed to rolling destination is a string (`path` from LogOptions is a string) and\n* `frequency` is defined\n\nThis determines the format of the datetime inserted into the log file name:\n\n* `unix` - unix epoch timestamp in milliseconds\n* `iso` - Full [ISO8601](https://en.wikipedia.org/wiki/ISO_8601) datetime IE '2024-03-07T20:11:34Z'\n* `auto`\n * When frequency is `daily` only inserts date IE YYYY-MM-DD\n * Otherwise inserts full ISO8601 datetime","default":"auto"},"size":{"type":["number","string"],"description":"The maximum size of a given rolling log file.\n\nCan be combined with frequency. Use k, m and g to express values in KB, MB or GB.\n\nNumerical values will be considered as MB.","default":"10MB"},"frequency":{"anyOf":[{"type":"string","enum":["daily"]},{"type":"string","enum":["hourly"]},{"type":"number"}],"description":"The amount of time a given rolling log file is used. Can be combined with size.\n\nUse `daily` or `hourly` to rotate file every day (or every hour). Existing file within the current day (or hour) will be re-used.\n\nNumerical values will be considered as a number of milliseconds. Using a numerical value will always create a new file upon startup.","default":"daily"},"path":{"anyOf":[{"type":"string"},{"$comment":"() => string"}],"description":"The path and filename to use for log files.\n\nIf using rolling files the filename will be appended with `.N` (a number) BEFORE the extension based on rolling status.\n\nMay also be specified using env LOG_PATH or a function that returns a string.\n\nIf path is relative the absolute path will be derived from `logBaseDir` (in `LoggerAppExtras`) which defaults to CWD","default":"./logs/app.log"},"level":{"anyOf":[{"$ref":"#/definitions/LogLevel"},{"type":"boolean","const":false}],"description":"Specify the minimum log level to output to rotating files. If `false` no log files will be created."}},"title":"FileLogOptions"},"ScrobbleThresholds":{"type":"object","properties":{"duration":{"type":["number","null"],"description":"The number of seconds a track has been listened to before it should be considered scrobbled.\n\nSet to null to disable.","default":240,"examples":[240]},"percent":{"type":["number","null"],"description":"The percentage (as an integer) of a track that should have been seen played before it should be scrobbled. Only used if the Source provides information about how long the track is.\n\nSet to null to disable.\n\nNOTE: This should be used with care when the Source is a \"polling\" type (has an 'interval' property). If the track is short and the interval is too high MS may ignore the track if percentage is high because it had not \"seen\" the track for long enough from first discovery, even if you have been playing the track for longer.","default":50,"examples":[50]}},"title":"ScrobbleThresholds"},"PlayTransformOptions":{"type":"object","properties":{"log":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["all"]}]},"preCompare":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"},"compare":{"type":"object","properties":{"candidate":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"},"existing":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"}}},"postCompare":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"}},"title":"PlayTransformOptions"},"PlayTransformPartsConfig<SearchAndReplaceTerm>":{"anyOf":[{"$ref":"#/definitions/PlayTransformPartsArray%3CSearchAndReplaceTerm%3E"},{"$ref":"#/definitions/PlayTransformParts%3CSearchAndReplaceTerm%3E"}],"title":"PlayTransformPartsConfig<SearchAndReplaceTerm>"},"PlayTransformPartsArray<SearchAndReplaceTerm>":{"type":"array","items":{"$ref":"#/definitions/PlayTransformParts%3CSearchAndReplaceTerm%3E"},"title":"PlayTransformPartsArray<SearchAndReplaceTerm>"},"PlayTransformParts<SearchAndReplaceTerm>":{"type":"object","properties":{"when":{"$ref":"#/definitions/WhenConditionsConfig"},"title":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}},"artists":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}},"album":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}}},"title":"PlayTransformParts<SearchAndReplaceTerm>"},"WhenConditionsConfig":{"$ref":"#/definitions/WhenConditions%3Cstring%3E","title":"WhenConditionsConfig"},"WhenConditions<string>":{"type":"array","items":{"$ref":"#/definitions/WhenParts%3Cstring%3E"},"title":"WhenConditions<string>"},"WhenParts<string>":{"$ref":"#/definitions/PlayTransformPartsAtomic%3Cstring%3E","title":"WhenParts<string>"},"PlayTransformPartsAtomic<string>":{"type":"object","properties":{"title":{"type":"string"},"artists":{"type":"string"},"album":{"type":"string"}},"title":"PlayTransformPartsAtomic<string>"},"SearchAndReplaceTerm":{"anyOf":[{"type":"string"},{"$ref":"#/definitions/ConditionalSearchAndReplaceTerm"}],"title":"SearchAndReplaceTerm"},"ConditionalSearchAndReplaceTerm":{"type":"object","properties":{"when":{"$ref":"#/definitions/WhenConditionsConfig"},"search":{},"replace":{}},"required":["search","replace"],"title":"ConditionalSearchAndReplaceTerm"},"PlexSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/PlexSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"PlexSourceConfig"},"PlexSourceData":{"type":"object","properties":{"user":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled","examples":[["MyUser1","MyUser2"]]},"libraries":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of libraries to scrobble tracks from\n\nIf none are provided tracks from all libraries will be scrobbled","examples":[["Audio","Music"]]},"servers":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled","examples":[["MyServerName"]]}},"title":"PlexSourceData"},"PlexApiSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/PlexApiData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/PlexApiOptions"}},"required":["data","options"],"title":"PlexApiSourceConfig"},"PlexApiData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"token":{"type":"string"},"url":{"type":"string","description":"http(s)://HOST:PORT of the Plex server to connect to"},"usersAllow":{"anyOf":[{"type":"string"},{"type":"boolean","enum":[true]},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble for specific users (case-insensitive)\n\nIf `true` MS will scrobble activity from all users"},"usersBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble for these users (case-insensitive)"},"devicesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if device or application name contains strings from this list (case-insensitive)"},"devicesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if device or application name contains strings from this list (case-insensitive)"},"librariesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if library name contains string from this list (case-insensitive)"},"librariesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if library name contains strings from this list (case-insensitive)"}},"required":["url"],"title":"PlexApiData"},"PlexApiOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"PlexApiOptions"},"TautulliSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/PlexSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"TautulliSourceConfig"},"DeezerSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/DeezerData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"DeezerSourceConfig"},"DeezerData":{"type":"object","properties":{"clientId":{"type":"string","description":"deezer client id","examples":["a89cba1569901a0671d5a9875fed4be1"]},"clientSecret":{"type":"string","description":"deezer client secret","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"redirectUri":{"type":"string","description":"deezer redirect URI -- required only if not the default shown here. URI must end in \"callback\"","default":"http://localhost:9078/deezer/callback","examples":["http://localhost:9078/deezer/callback"]},"interval":{"type":"number","description":"optional, how long to wait before calling spotify for new tracks (in seconds)","default":60,"examples":[60]}},"required":["clientId","clientSecret"],"title":"DeezerData"},"ListenbrainzEndpointSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/ListenbrainzEndpointData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"title":"ListenbrainzEndpointSourceConfig"},"ListenbrainzEndpointData":{"type":"object","properties":{"slug":{"type":["string","null"],"description":"The URL ending that should be used to identify scrobbles for this source\n\nIf you are using multiple Listenbrainz endpoint sources (scrobbles for many users) you can use a slug to match Sources with individual users/origins\n\nExample:\n\n* slug: 'usera' => API URL: http://localhost:9078/api/listenbrainz/usera\n* slug: 'originb' => API URL: http://localhost:9078/api/listenbrainz/originb\n\nIf no slug is found from an extension's incoming webhook event the first Listenbrainz source without a slug will be used"},"token":{"type":["string","null"],"description":"If an LZ submission request contains this token in the Authorization Header it will be used to match the submission with this Source\n\nSee: https://listenbrainz.readthedocs.io/en/latest/users/api/index.html#add-the-user-token-to-your-requests"}},"title":"ListenbrainzEndpointData"},"LastFMEndpointSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/LastFMEndpointData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"title":"LastFMEndpointSourceConfig"},"LastFMEndpointData":{"type":"object","properties":{"slug":{"type":["string","null"],"description":"The URL ending that should be used to identify scrobbles for this source\n\nIf you are using multiple Last.fm endpoint sources (scrobbles for many users) you can use a slug to match Sources with individual users/origins\n\nExample:\n\n* slug: 'usera' => API URL: http://localhost:9078/api/lastfm/usera\n* slug: 'originb' => API URL: http://localhost:9078/api/lastfm/originb\n\nIf no slug is found from an extension's incoming webhook event the first Last.fm source without a slug will be used"}},"title":"LastFMEndpointData"},"SubSonicSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/SubsonicData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"SubSonicSourceConfig"},"SubsonicData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the subsonic media server to query","examples":["http://airsonic.local"]},"user":{"type":"string","description":"Username to login to the server with","examples":[["MyUser"]]},"password":{"type":"string","description":"Password for the user to login to the server with","examples":["MyPassword"]},"ignoreTlsErrors":{"type":"boolean","description":"If your subsonic server is using self-signed certs you may need to disable TLS errors in order to get a connection\n\nWARNING: This should be used with caution as your traffic may not be encrypted.","default":false},"legacyAuthentication":{"type":"boolean","description":"Older Subsonic versions, and some badly implemented servers (Nextcloud), use legacy authentication which sends your password in CLEAR TEXT. This is less secure than the newer, recommended hashing authentication method but in some cases it is needed. See \"Authentication\" section here => https://www.subsonic.org/pages/api.jsp\n\nIf this option is not specified it will be turned on if the subsonic server responds with error code 41 \"Token authentication not supported for LDAP users.\" -- See Error Handling section => https://www.subsonic.org/pages/api.jsp","default":false}},"required":["url","user","password"],"title":"SubsonicData"},"JellySourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/JellyData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"JellySourceConfig"},"JellyData":{"type":"object","properties":{"users":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled","examples":[["MyUser1","MyUser2"]]},"servers":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled","examples":[["MyServerName1"]]}},"title":"JellyData"},"JellyApiSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/JellyApiData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/JellyApiOptions"}},"required":["data","options"],"title":"JellyApiSourceConfig"},"JellyApiData":{"type":"object","properties":{"url":{"type":"string","description":"HOST:PORT of the Jellyfin server to connect to"},"user":{"type":"string","description":"The username of the user to authenticate for or track scrobbles for"},"password":{"type":"string","description":"Password of the username to authenticate for\n\nRequired if `apiKey` is not provided."},"apiKey":{"type":"string","description":"API Key to authenticate with.\n\nRequired if `password` is not provided."},"usersAllow":{"anyOf":[{"type":"string"},{"type":"boolean","enum":[true]},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble for specific users (case-insensitive)\n\nIf `true` MS will scrobble activity from all users"},"usersBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble for these users (case-insensitive)"},"devicesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if device or application name contains strings from this list (case-insensitive)"},"devicesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if device or application name contains strings from this list (case-insensitive)"},"librariesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if library name contains string from this list (case-insensitive)"},"librariesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if library name contains strings from this list (case-insensitive)"},"additionalAllowedLibraryTypes":{"type":"array","items":{},"description":"Allow MS to scrobble audio media in libraries classified other than 'music'\n\n`librariesAllow` will achieve the same result as this but this is more convenient if you do not want to explicitly list every library name or are only using `librariesBlock`"},"allowUnknown":{"type":"boolean","description":"Force media with a type of \"Unknown\" to be counted as Audio","default":false}},"required":["url","user"],"title":"JellyApiData"},"JellyApiOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"JellyApiOptions"},"LastfmSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/LastFmSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"configureAs":{"type":"string","enum":["source"],"description":"When used in `lastfm.config` this tells multi-scrobbler whether to use this data to configure a source or client.","default":"source","examples":["source"]}},"required":["data"],"title":"LastfmSourceConfig"},"LastFmSourceData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"apiKey":{"type":"string","description":"API Key generated from Last.fm account","examples":["787c921a2a2ab42320831aba0c8f2fc2"]},"secret":{"type":"string","description":"Secret generated from Last.fm account","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"session":{"type":"string","description":"Optional session id returned from a completed auth flow"},"redirectUri":{"type":"string","description":"Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.","default":"http://localhost:9078/lastfm/callback","examples":["http://localhost:9078/lastfm/callback"]},"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"}},"required":["apiKey","secret"],"title":"LastFmSourceData"},"YTMusicSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/YTMusicData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"type":"object","properties":{"logAuth":{"type":"boolean","description":"When true MS will log to DEBUG all of the credentials data it receives from YTM"},"logDiff":{"type":"boolean","description":"Always log history diff\n\nBy default MS will log to `WARN` if history diff is inconsistent but does not log if diff is expected (on new tracks found)\nSet this to `true` to ALWAYS log diff on new tracks. Expected diffs will log to `DEBUG` and inconsistent diffs will continue to log to `WARN`","default":false},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}}}},"title":"YTMusicSourceConfig"},"YTMusicData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"cookie":{"type":"string","description":"The cookie retrieved from the Request Headers of music.youtube.com after logging in.\n\nSee https://ytmusicapi.readthedocs.io/en/stable/setup/browser.html#copy-authentication-headers for how to retrieve this value.","examples":["VISITOR_INFO1_LIVE=jMp2xA1Xz2_PbVc; __Secure-3PAPISID=3AxsXpy0M/AkISpjek; ..."]},"clientId":{"type":"string","description":"Google Cloud Console project OAuth Client ID\n\nGenerated from a custom OAuth Client, see docs"},"clientSecret":{"type":"string","description":"Google Cloud Console project OAuth Client Secret\n\nGenerated from a custom OAuth Client, see docs"},"redirectUri":{"type":"string","description":"Google Cloud Console project OAuth Client Authorized redirect URI\n\nGenerated from a custom OAuth Client, see docs. multi-scrobbler will generate a default based on BASE_URL.\nOnly specify this if the default does not work for you."},"innertubeOptions":{"$ref":"#/definitions/InnertubeOptions","description":"Additional options for authorization and tailoring YTM client"}},"title":"YTMusicData"},"InnertubeOptions":{"type":"object","properties":{"po_token":{"type":"string","description":"Proof of Origin token\n\nMay be required if YTM starts returning 403"},"visitor_data":{"type":"string","description":"Visitor ID value found in VISITOR_INFO1_LIVE or visitorData cookie\n\nMay be required if YTM starts returning 403"},"account_index":{"type":"number","description":"If account login results in being able to choose multiple account, use a zero-based index to choose which one to monitor","examples":[0,1]},"location":{"type":"string"},"lang":{"type":"string"},"generate_session_locally":{"type":"boolean"},"device_category":{"type":"string"},"client_type":{"type":"string"},"timezone":{"type":"string"}},"title":"InnertubeOptions"},"MPRISSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MPRISData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"MPRISSourceConfig"},"MPRISData":{"type":"object","properties":{"blacklist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"DO NOT scrobble from any players that START WITH these values, case-insensitive","examples":[["spotify","vlc"]]},"whitelist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"ONLY from any players that START WITH these values, case-insensitive\n\nIf whitelist is present then blacklist is ignored","examples":[["spotify","vlc"]]}},"title":"MPRISData"},"MopidySourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MopidyData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"MopidySourceConfig"},"MopidyData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the Mopidy HTTP server to connect to\n\nYou MUST have Mopidy-HTTP extension enabled: https://mopidy.com/ext/http\n\nmulti-scrobbler connects to the WebSocket endpoint that ultimately looks like this => `ws://localhost:6680/mopidy/ws/`\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `ws://`\n* Hostname => `localhost`\n* Port => `6680`\n* Path => `/mopidy/ws/`","examples":["ws://localhost:6680/mopidy/ws/"],"default":"ws://localhost:6680/mopidy/ws/"},"uriBlacklist":{"type":"array","items":{"type":"string"},"description":"Do not scrobble tracks whose URI STARTS WITH any of these strings, case-insensitive\n\nEX: Don't scrobble tracks from soundcloud by adding 'soundcloud' to this list.\n\nList is ignored if uriWhitelist is used."},"uriWhitelist":{"type":"array","items":{"type":"string"},"description":"Only scrobble tracks whose URI STARTS WITH any of these strings, case-insensitive\n\nEX: Only scrobble tracks from soundcloud by adding 'soundcloud' to this list."},"albumBlacklist":{"type":"array","items":{"type":"string"},"description":"Remove album data that matches any case-insensitive string from this list when scrobbling,\n\nFor certain sources (Soundcloud) Mopidy does not have all track info (Album) and will instead use \"Soundcloud\" as the Album name. You can prevent multi-scrobbler from using this bad Album data by adding the fake name to this list. Multi-scrobbler will still scrobble the track, just without the bad data.","examples":[["Soundcloud","Mixcloud"]],"default":["Soundcloud"]}},"title":"MopidyData"},"ListenBrainzSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/ListenBrainzSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"configureAs":{"type":"string","enum":["source"],"description":"When used in `listenbrainz.config` this tells multi-scrobbler whether to use this data to configure a source or client.","default":"source","examples":["source"]}},"required":["data"],"title":"ListenBrainzSourceConfig"},"ListenBrainzSourceData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for the ListenBrainz server, if not using the default","examples":["https://api.listenbrainz.org/"],"default":"https://api.listenbrainz.org/"},"token":{"type":"string","description":"User token for the user to scrobble for","examples":["6794186bf-1157-4de6-80e5-uvb411f3ea2b"]},"username":{"type":"string","description":"Username of the user to scrobble for"}},"required":["token","username"],"title":"ListenBrainzSourceData"},"JRiverSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/JRiverData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"JRiverSourceConfig"},"JRiverData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the JRiver HTTP server to connect to\n\nmulti-scrobbler connects to the Web Service Interface endpoint that ultimately looks like this => `http://yourDomain:52199/MCWS/v1/`\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `http://`\n* Hostname => `localhost`\n* Port => `52199`\n* Path => `/MCWS/v1/`","examples":["http://localhost:52199/MCWS/v1/"],"default":"http://localhost:52199/MCWS/v1/"},"username":{"type":"string","description":"If you have enabled authentication, the username you set"},"password":{"type":"string","description":"If you have enabled authentication, the password you set"}},"required":["url"],"title":"JRiverData"},"KodiSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/KodiData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"KodiSourceConfig"},"KodiData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the Kodi HTTP server to connect to\n\nmulti-scrobbler connects to the Web Service Interface endpoint that ultimately looks like this => `http://yourDomain:8080/jsonrpc`\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `http://`\n* Hostname => `localhost`\n* Port => `8080`\n* Path => `/jsonrpc`","examples":["http://localhost:8080/jsonrpc"],"default":"http://localhost:8080/jsonrpc"},"username":{"type":"string","description":"The username set for Remote Control via Web Sever"},"password":{"type":"string","description":"The password set for Remote Control via Web Sever"}},"required":["url","username","password"],"title":"KodiData"},"WebScrobblerSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/WebScrobblerData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"title":"WebScrobblerSourceConfig"},"WebScrobblerData":{"type":"object","properties":{"slug":{"type":["string","null"],"description":"The URL ending that should be used to identify scrobbles for this source\n\nIn WebScrobbler's Webhook you must set an 'API URL'. All MS WebScrobbler sources must start like:\n\nhttp://localhost:9078/api/webscrobbler\n\nIf you are using multiple WebScrobbler sources (scrobbles for many users) you must use a slug to match Sources with each users extension.\n\nExample:\n\n* slug: 'usera' => API URL: http://localhost:9078/api/webscrobbler/usera\n* slug: 'userb' => API URL: http://localhost:9078/api/webscrobbler/userb\n\nIf no slug is found from an extension's incoming webhook event the first WebScrobbler source without a slug will be used"},"blacklist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Block scrobbling from specific WebScrobbler Connectors","examples":[["youtube"]]},"whitelist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only allow scrobbling from specific WebScrobbler Connectors","examples":[["mixcloud","soundcloud","bandcamp"]]}},"title":"WebScrobblerData"},"ChromecastSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/ChromecastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"ChromecastSourceConfig"},"ChromecastData":{"type":"object","properties":{"blacklistDevices":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"DO NOT scrobble from any cast devices that START WITH these values, case-insensitive\n\nUseful when used with auto discovery","examples":[["home-mini","family-tv"]]},"whitelistDevices":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"ONLY scrobble from any cast device that START WITH these values, case-insensitive\n\nIf whitelist is present then blacklist is ignored\n\nUseful when used with auto discovery","examples":[["home-mini","family-tv"]]},"blacklistApps":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"DO NOT scrobble from any application that START WITH these values, case-insensitive","examples":[["spotify","pandora"]]},"whitelistApps":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"ONLY scrobble from any application that START WITH these values, case-insensitive\n\nIf whitelist is present then blacklist is ignored","examples":[["spotify","pandora"]]},"useAvahi":{"type":"boolean","description":"Try to use Avahi and avahi-browse to resolve mDNS devices instead of native mDNS querying\n\nUseful for docker (alpine) container where mDNS resolution is not yet supported. Avahi socket must be exposed to the container and avahi-tools must be installed.","default":false},"useAutoDiscovery":{"type":"boolean","description":"Use mDNS to discovery Google Cast devices on your next automatically?\n\nIf not explicitly set then it is TRUE if `devices` is not set"},"devices":{"type":"array","items":{"$ref":"#/definitions/ChromecastDeviceInfo"},"description":"A list of Google Cast devices to monitor\n\nIf this is used then `useAutoDiscovery` is set to FALSE, if not explicitly set"},"allowUnknownMedia":{"anyOf":[{"type":"boolean"},{"type":"array","items":{"type":"string"}}],"description":"Chromecast Apps report a \"media type\" in the status info returned for whatever is currently playing\n\n* If set to TRUE then Music AND Generic/Unknown media will be tracked for ALL APPS\n* If set to FALSE then only media explicitly typed as Music will be tracked for ALL APPS\n* If set to a list then only Apps whose name contain one of these values, case-insensitive, will have Music AND Generic/Unknown tracked\n\nSee https://developers.google.com/cast/docs/media/messages#MediaInformation \"metadata\" property","default":false},"forceMediaRecognitionOn":{"type":"array","items":{"type":"string"},"description":"Media provided by any App whose name is listed here will ALWAYS be tracked, regardless of the \"media type\" reported\n\nApps will be recognized if they CONTAIN any of these values, case-insensitive"}},"title":"ChromecastData"},"ChromecastDeviceInfo":{"type":"object","properties":{"name":{"type":"string","description":"A friendly name to identify this device","examples":["MySmartTV"]},"address":{"type":"string","description":"The IP address of the device","examples":["192.168.0.115"]}},"required":["name","address"],"title":"ChromecastDeviceInfo"},"MusikcubeSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MusikcubeData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"MusikcubeSourceConfig"},"MusikcubeData":{"type":"object","properties":{"url":{"type":"string","description":"URL of the Musikcube Websocket (Metadata) server to connect to\n\nYou MUST have enabled 'metadata' server and set a password: https://github.com/clangen/musikcube/wiki/remote-api-documentation\n * musikcube -> settings -> server setup\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `ws://`\n* Hostname => `localhost`\n* Port => `7905`","examples":["ws://localhost:7905"],"default":"ws://localhost:7905"},"password":{"type":"string","description":"Password set in Musikcube https://github.com/clangen/musikcube/wiki/remote-api-documentation\n\n* musikcube -> settings -> server setup -> password"},"device_id":{"type":"string"}},"required":["password"],"title":"MusikcubeData"},"MusicCastSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MusicCastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"MusicCastSourceConfig"},"MusicCastData":{"type":"object","properties":{"url":{"type":"string","description":"The host or URL of the YamahaExtendedControl endpoint to use","examples":[["192.168.0.101","http://192.168.0.101/YamahaExtendedControl"]]}},"required":["url"],"title":"MusicCastData"},"MPDSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MPDData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/MPDSourceOptions"}},"required":["data","options"],"title":"MPDSourceConfig"},"MPDData":{"type":"object","properties":{"url":{"type":"string","description":"URL:PORT of the MPD server to connect to\n\nTo use this you must have TCP connections enabled for your MPD server https://mpd.readthedocs.io/en/stable/user.html#client-connections","examples":["localhost:6600"],"default":"localhost:6600"},"path":{"type":"string","description":"If using socket specify the path instead of url.\n\ntrailing `~` is replaced by your home directory"},"password":{"type":"string","description":"Password for the server, if set https://mpd.readthedocs.io/en/stable/user.html#permissions-and-passwords"}},"title":"MPDData"},"MPDSourceOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"MPDSourceOptions"},"VLCSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/VLCData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/VLCSourceOptions"}},"required":["data"],"title":"VLCSourceConfig"},"VLCData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL:PORT of the VLC server to connect to\n\nTo use this you must have the Web (http) interface module enabled and a password set https://foxxmd.github.io/multi-scrobbler/docs/configuration#vlc","examples":["localhost:8080"],"default":"localhost:8080"},"password":{"type":"string","description":"Password for the server"}},"required":["password"],"title":"VLCData"},"VLCSourceOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"},"filenamePatterns":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"A list of regular expressions to use to extract metadata (title, album, artist) from a filename\n\nUsed when VLC reports only the filename for the current audio track"},"logFilenamePatterns":{"type":"boolean","description":"Log to DEBUG when a filename-only track is matched or not matched by filenamePatterns","default":false},"dumpVlcMetadata":{"type":"boolean","description":"Dump all the metadata VLC reports for an audio track to DEBUG.\n\nUse this if reporting an issue with VLC not correctly capturing metadata for a track.","default":false}},"title":"VLCSourceOptions"},"IcecastSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/IcecastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/IcecastSourceOptions"}},"required":["data"],"title":"IcecastSourceConfig"},"IcecastData":{"type":"object","properties":{"sources":{"type":"array","items":{"$ref":"#/definitions/IcecastSource"}},"icestatsEndpoint":{"type":"string"},"statsEndpoint":{"type":"string"},"nextsongsEndpoint":{"type":"string"},"sevenhtmlEndpoint":{"type":"string"},"icyMetaInt":{"type":"number"},"url":{"type":"string","description":"The Icecast stream URL"}},"required":["url"],"title":"IcecastData"},"IcecastSource":{"type":"string","enum":["icy","ogg","icestats","stats","sevenhtml","nextsongs"],"title":"IcecastSource"},"IcecastSourceOptions":{"type":"object","properties":{"systemScrobble":{"type":"boolean","description":"For Sources that support manual listening, should MS default to scrobbling when no user interaction has occurred?\n\nIf not specified MS will use a Source's specific behavior, see Source's documentation."},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"IcecastSourceOptions"},"AzuracastSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/AzuracastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"AzuracastSourceConfig"},"AzuracastData":{"type":"object","properties":{"url":{"type":"string","description":"Base URL of the Azuracast instance\n\nThis does NOT include the station. If a station is included it will be ignored. Use `station` field to specify station, if necessary","examples":["https://radio.mydomain.tld","http://localhost:80"]},"station":{"type":"string","description":"The specific station to monitor\n\nScrobbling will only occur if any of the monitor conditions are met AND the station is ONLINE.\n\nTo monitor multiple stations create a Source for each station.","examples":["my-station-1"]},"monitorWhenListeners":{"type":["boolean","number"],"description":"Only activate scrobble monitoring if station\n\n* `true` => has any current listeners\n* `number` => has EQUAL TO or MORE THAN X number of listeners"},"monitorWhenLive":{"type":"boolean","description":"Only activate scrobble monitoring if station has a live DJ/Streamer","default":true},"apiKey":{"type":"string","description":"API Key used to access data about private streams\n\nhttps://www.azuracast.com/docs/developers/apis/#api-authentication"}},"required":["url","station"],"title":"AzuracastData"}}} \ No newline at end of file +{"$schema":"http://json-schema.org/draft-07/schema#","anyOf":[{"$ref":"#/definitions/SpotifySourceConfig"},{"$ref":"#/definitions/PlexSourceConfig"},{"$ref":"#/definitions/PlexApiSourceConfig"},{"$ref":"#/definitions/TautulliSourceConfig"},{"$ref":"#/definitions/DeezerSourceConfig"},{"$ref":"#/definitions/DeezerInternalSourceConfig"},{"$ref":"#/definitions/ListenbrainzEndpointSourceConfig"},{"$ref":"#/definitions/LastFMEndpointSourceConfig"},{"$ref":"#/definitions/SubSonicSourceConfig"},{"$ref":"#/definitions/JellySourceConfig"},{"$ref":"#/definitions/JellyApiSourceConfig"},{"$ref":"#/definitions/LastfmSourceConfig"},{"$ref":"#/definitions/YTMusicSourceConfig"},{"$ref":"#/definitions/MPRISSourceConfig"},{"$ref":"#/definitions/MopidySourceConfig"},{"$ref":"#/definitions/ListenBrainzSourceConfig"},{"$ref":"#/definitions/JRiverSourceConfig"},{"$ref":"#/definitions/KodiSourceConfig"},{"$ref":"#/definitions/WebScrobblerSourceConfig"},{"$ref":"#/definitions/ChromecastSourceConfig"},{"$ref":"#/definitions/MalojaSourceConfig"},{"$ref":"#/definitions/MusikcubeSourceConfig"},{"$ref":"#/definitions/MusicCastSourceConfig"},{"$ref":"#/definitions/MPDSourceConfig"},{"$ref":"#/definitions/VLCSourceConfig"},{"$ref":"#/definitions/IcecastSourceConfig"},{"$ref":"#/definitions/AzuracastSourceConfig"},{"$ref":"#/definitions/KoitoSourceConfig"}],"definitions":{"SpotifySourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/SpotifySourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"SpotifySourceConfig"},"SpotifySourceData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)\n\nIt is unlikely you should need to change this unless you scrobble many very short tracks often\n\nReading:\n* https://developer.spotify.com/documentation/web-api/guides/rate-limits/\n* https://medium.com/mendix/limiting-your-amount-of-calls-in-mendix-most-of-the-time-rest-835dde55b10e\n * Rate limit may ~180 req/min\n* https://community.spotify.com/t5/Spotify-for-Developers/Web-API-ratelimit/m-p/5503150/highlight/true#M7930\n * Informally indicated as 20 req/sec? Probably for burstiness","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"clientId":{"type":"string","description":"spotify client id","examples":["787c921a2a2ab42320831aba0c8f2fc2"]},"clientSecret":{"type":"string","description":"spotify client secret","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"redirectUri":{"type":"string","description":"spotify redirect URI -- required only if not the default shown here. URI must end in \"callback\"","default":"http://localhost:9078/callback","examples":["http://localhost:9078/callback"]}},"required":["clientId","clientSecret"],"title":"SpotifySourceData"},"CommonSourceOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"CommonSourceOptions"},"LogLevel":{"type":"string","enum":["silent","fatal","error","warn","info","log","verbose","debug"],"description":"Names of log levels that can be invoked on the logger\n\nFrom lowest to highest:\n\n* `debug`\n* `verbose`\n* `log`\n* `info`\n* `warn`\n* `error`\n* `fatal`\n* `silent` (will never output anything)\n\nWhen used in `LogOptions` specifies the **minimum** level the output should log at.","title":"LogLevel"},"FileLogOptions":{"type":"object","properties":{"timestamp":{"type":"string","enum":["unix","iso","auto"],"description":"For rolling log files\n\nWhen\n* value passed to rolling destination is a string (`path` from LogOptions is a string) and\n* `frequency` is defined\n\nThis determines the format of the datetime inserted into the log file name:\n\n* `unix` - unix epoch timestamp in milliseconds\n* `iso` - Full [ISO8601](https://en.wikipedia.org/wiki/ISO_8601) datetime IE '2024-03-07T20:11:34Z'\n* `auto`\n * When frequency is `daily` only inserts date IE YYYY-MM-DD\n * Otherwise inserts full ISO8601 datetime","default":"auto"},"size":{"type":["number","string"],"description":"The maximum size of a given rolling log file.\n\nCan be combined with frequency. Use k, m and g to express values in KB, MB or GB.\n\nNumerical values will be considered as MB.","default":"10MB"},"frequency":{"anyOf":[{"type":"string","enum":["daily"]},{"type":"string","enum":["hourly"]},{"type":"number"}],"description":"The amount of time a given rolling log file is used. Can be combined with size.\n\nUse `daily` or `hourly` to rotate file every day (or every hour). Existing file within the current day (or hour) will be re-used.\n\nNumerical values will be considered as a number of milliseconds. Using a numerical value will always create a new file upon startup.","default":"daily"},"path":{"anyOf":[{"type":"string"},{"$comment":"() => string"}],"description":"The path and filename to use for log files.\n\nIf using rolling files the filename will be appended with `.N` (a number) BEFORE the extension based on rolling status.\n\nMay also be specified using env LOG_PATH or a function that returns a string.\n\nIf path is relative the absolute path will be derived from `logBaseDir` (in `LoggerAppExtras`) which defaults to CWD","default":"./logs/app.log"},"level":{"anyOf":[{"$ref":"#/definitions/LogLevel"},{"type":"boolean","const":false}],"description":"Specify the minimum log level to output to rotating files. If `false` no log files will be created."}},"title":"FileLogOptions"},"ScrobbleThresholds":{"type":"object","properties":{"duration":{"type":["number","null"],"description":"The number of seconds a track has been listened to before it should be considered scrobbled.\n\nSet to null to disable.","default":240,"examples":[240]},"percent":{"type":["number","null"],"description":"The percentage (as an integer) of a track that should have been seen played before it should be scrobbled. Only used if the Source provides information about how long the track is.\n\nSet to null to disable.\n\nNOTE: This should be used with care when the Source is a \"polling\" type (has an 'interval' property). If the track is short and the interval is too high MS may ignore the track if percentage is high because it had not \"seen\" the track for long enough from first discovery, even if you have been playing the track for longer.","default":50,"examples":[50]}},"title":"ScrobbleThresholds"},"PlayTransformOptions":{"type":"object","properties":{"log":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["all"]}]},"preCompare":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"},"compare":{"type":"object","properties":{"candidate":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"},"existing":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"}}},"postCompare":{"$ref":"#/definitions/PlayTransformPartsConfig%3CSearchAndReplaceTerm%3E"}},"title":"PlayTransformOptions"},"PlayTransformPartsConfig<SearchAndReplaceTerm>":{"anyOf":[{"$ref":"#/definitions/PlayTransformPartsArray%3CSearchAndReplaceTerm%3E"},{"$ref":"#/definitions/PlayTransformParts%3CSearchAndReplaceTerm%3E"}],"title":"PlayTransformPartsConfig<SearchAndReplaceTerm>"},"PlayTransformPartsArray<SearchAndReplaceTerm>":{"type":"array","items":{"$ref":"#/definitions/PlayTransformParts%3CSearchAndReplaceTerm%3E"},"title":"PlayTransformPartsArray<SearchAndReplaceTerm>"},"PlayTransformParts<SearchAndReplaceTerm>":{"type":"object","properties":{"when":{"$ref":"#/definitions/WhenConditionsConfig"},"title":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}},"artists":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}},"album":{"type":"array","items":{"$ref":"#/definitions/SearchAndReplaceTerm"}}},"title":"PlayTransformParts<SearchAndReplaceTerm>"},"WhenConditionsConfig":{"$ref":"#/definitions/WhenConditions%3Cstring%3E","title":"WhenConditionsConfig"},"WhenConditions<string>":{"type":"array","items":{"$ref":"#/definitions/WhenParts%3Cstring%3E"},"title":"WhenConditions<string>"},"WhenParts<string>":{"$ref":"#/definitions/PlayTransformPartsAtomic%3Cstring%3E","title":"WhenParts<string>"},"PlayTransformPartsAtomic<string>":{"type":"object","properties":{"title":{"type":"string"},"artists":{"type":"string"},"album":{"type":"string"}},"title":"PlayTransformPartsAtomic<string>"},"SearchAndReplaceTerm":{"anyOf":[{"type":"string"},{"$ref":"#/definitions/ConditionalSearchAndReplaceTerm"}],"title":"SearchAndReplaceTerm"},"ConditionalSearchAndReplaceTerm":{"type":"object","properties":{"when":{"$ref":"#/definitions/WhenConditionsConfig"},"search":{},"replace":{}},"required":["search","replace"],"title":"ConditionalSearchAndReplaceTerm"},"PlexSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/PlexSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"PlexSourceConfig"},"PlexSourceData":{"type":"object","properties":{"user":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled","examples":[["MyUser1","MyUser2"]]},"libraries":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of libraries to scrobble tracks from\n\nIf none are provided tracks from all libraries will be scrobbled","examples":[["Audio","Music"]]},"servers":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled","examples":[["MyServerName"]]}},"title":"PlexSourceData"},"PlexApiSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/PlexApiData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/PlexApiOptions"}},"required":["data","options"],"title":"PlexApiSourceConfig"},"PlexApiData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"token":{"type":"string"},"url":{"type":"string","description":"http(s)://HOST:PORT of the Plex server to connect to"},"usersAllow":{"anyOf":[{"type":"string"},{"type":"boolean","enum":[true]},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble for specific users (case-insensitive)\n\nIf `true` MS will scrobble activity from all users"},"usersBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble for these users (case-insensitive)"},"devicesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if device or application name contains strings from this list (case-insensitive)"},"devicesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if device or application name contains strings from this list (case-insensitive)"},"librariesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if library name contains string from this list (case-insensitive)"},"librariesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if library name contains strings from this list (case-insensitive)"}},"required":["url"],"title":"PlexApiData"},"PlexApiOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"},"ignoreInvalidCert":{"type":"boolean","description":"Ignore invalid cert errors when connecting to Plex\n\nUseful for Plex servers using \"Required\" Secure Connections with self-signed certificates\n\nDo not enable unless you know you need this.","default":false}},"title":"PlexApiOptions"},"TautulliSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/PlexSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"TautulliSourceConfig"},"DeezerSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/DeezerData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"DeezerSourceConfig"},"DeezerData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"clientId":{"type":"string","description":"deezer client id","examples":["a89cba1569901a0671d5a9875fed4be1"]},"clientSecret":{"type":"string","description":"deezer client secret","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"redirectUri":{"type":"string","description":"deezer redirect URI -- required only if not the default shown here. URI must end in \"callback\"","default":"http://localhost:9078/deezer/callback","examples":["http://localhost:9078/deezer/callback"]}},"required":["clientId","clientSecret"],"title":"DeezerData"},"DeezerInternalSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/DeezerInternalData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/DeezerInternalSourceOptions"}},"required":["data"],"title":"DeezerInternalSourceConfig"},"DeezerInternalData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"arl":{"type":"string","description":"ARL retrieved from Deezer response header"},"userAgent":{"type":"string","description":"User agent","default":"Mozilla/5.0 (X11; Linux i686; rv:135.0) Gecko/20100101 Firefox/135.0"}},"required":["arl"],"title":"DeezerInternalData"},"DeezerInternalSourceOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"},"fuzzyDiscoveryIgnore":{"anyOf":[{"type":"boolean"},{"type":"string","enum":["aggressive"]}]}},"title":"DeezerInternalSourceOptions"},"ListenbrainzEndpointSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/ListenbrainzEndpointData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"title":"ListenbrainzEndpointSourceConfig"},"ListenbrainzEndpointData":{"type":"object","properties":{"slug":{"type":["string","null"],"description":"The URL ending that should be used to identify scrobbles for this source\n\nIf you are using multiple Listenbrainz endpoint sources (scrobbles for many users) you can use a slug to match Sources with individual users/origins\n\nExample:\n\n* slug: 'usera' => API URL: http://localhost:9078/api/listenbrainz/usera\n* slug: 'originb' => API URL: http://localhost:9078/api/listenbrainz/originb\n\nIf no slug is found from an extension's incoming webhook event the first Listenbrainz source without a slug will be used"},"token":{"type":["string","null"],"description":"If an LZ submission request contains this token in the Authorization Header it will be used to match the submission with this Source\n\nSee: https://listenbrainz.readthedocs.io/en/latest/users/api/index.html#add-the-user-token-to-your-requests"}},"title":"ListenbrainzEndpointData"},"LastFMEndpointSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/LastFMEndpointData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"title":"LastFMEndpointSourceConfig"},"LastFMEndpointData":{"type":"object","properties":{"slug":{"type":["string","null"],"description":"The URL ending that should be used to identify scrobbles for this source\n\nIf you are using multiple Last.fm endpoint sources (scrobbles for many users) you can use a slug to match Sources with individual users/origins\n\nExample:\n\n* slug: 'usera' => API URL: http://localhost:9078/api/lastfm/usera\n* slug: 'originb' => API URL: http://localhost:9078/api/lastfm/originb\n\nIf no slug is found from an extension's incoming webhook event the first Last.fm source without a slug will be used"}},"title":"LastFMEndpointData"},"SubSonicSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/SubsonicData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"SubSonicSourceConfig"},"SubsonicData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the subsonic media server to query","examples":["http://airsonic.local"]},"user":{"type":"string","description":"Username to login to the server with","examples":[["MyUser"]]},"password":{"type":"string","description":"Password for the user to login to the server with","examples":["MyPassword"]},"ignoreTlsErrors":{"type":"boolean","description":"If your subsonic server is using self-signed certs you may need to disable TLS errors in order to get a connection\n\nWARNING: This should be used with caution as your traffic may not be encrypted.","default":false},"legacyAuthentication":{"type":"boolean","description":"Older Subsonic versions, and some badly implemented servers (Nextcloud), use legacy authentication which sends your password in CLEAR TEXT. This is less secure than the newer, recommended hashing authentication method but in some cases it is needed. See \"Authentication\" section here => https://www.subsonic.org/pages/api.jsp\n\nIf this option is not specified it will be turned on if the subsonic server responds with error code 41 \"Token authentication not supported for LDAP users.\" -- See Error Handling section => https://www.subsonic.org/pages/api.jsp","default":false},"usersAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble for specific users (case-insensitive)\n\nIf undefined or an empty string/list MS will scrobble activity from all users"}},"required":["url","user","password"],"title":"SubsonicData"},"JellySourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/JellyData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"JellySourceConfig"},"JellyData":{"type":"object","properties":{"users":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of users to scrobble tracks from\n\nIf none are provided tracks from all users will be scrobbled","examples":[["MyUser1","MyUser2"]]},"servers":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"optional list of servers to scrobble tracks from\n\nIf none are provided tracks from all servers will be scrobbled","examples":[["MyServerName1"]]}},"title":"JellyData"},"JellyApiSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/JellyApiData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/JellyApiOptions"}},"required":["data","options"],"title":"JellyApiSourceConfig"},"JellyApiData":{"type":"object","properties":{"url":{"type":"string","description":"HOST:PORT of the Jellyfin server to connect to"},"user":{"type":"string","description":"The username of the user to authenticate for or track scrobbles for"},"password":{"type":"string","description":"Password of the username to authenticate for\n\nRequired if `apiKey` is not provided."},"apiKey":{"type":"string","description":"API Key to authenticate with.\n\nRequired if `password` is not provided."},"usersAllow":{"anyOf":[{"type":"string"},{"type":"boolean","enum":[true]},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble for specific users (case-insensitive)\n\nIf `true` MS will scrobble activity from all users"},"usersBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble for these users (case-insensitive)"},"devicesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if device or application name contains strings from this list (case-insensitive)"},"devicesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if device or application name contains strings from this list (case-insensitive)"},"librariesAllow":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only scrobble if library name contains string from this list (case-insensitive)"},"librariesBlock":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Do not scrobble if library name contains strings from this list (case-insensitive)"},"additionalAllowedLibraryTypes":{"type":"array","items":{},"description":"Allow MS to scrobble audio media in libraries classified other than 'music'\n\n`librariesAllow` will achieve the same result as this but this is more convenient if you do not want to explicitly list every library name or are only using `librariesBlock`"},"allowUnknown":{"type":"boolean","description":"Force media with a type of \"Unknown\" to be counted as Audio","default":false},"frontendUrlOverride":{"type":"string","description":"HOST:PORT of the Jellyfin server that your browser will be able to access from the frontend (and thus load images and links from)\nIf unspecified it will use the normal server HOST and PORT from the `url`\nNecessary if you are using a reverse proxy or other network configuration that prevents the frontend from accessing the server directly"}},"required":["url","user"],"title":"JellyApiData"},"JellyApiOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"JellyApiOptions"},"LastfmSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/LastFmSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"configureAs":{"type":"string","enum":["source"],"description":"When used in `lastfm.config` this tells multi-scrobbler whether to use this data to configure a source or client.","default":"source","examples":["source"]}},"required":["data"],"title":"LastfmSourceConfig"},"LastFmSourceData":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"apiKey":{"type":"string","description":"API Key generated from Last.fm account","examples":["787c921a2a2ab42320831aba0c8f2fc2"]},"secret":{"type":"string","description":"Secret generated from Last.fm account","examples":["ec42e09d5ae0ee0f0816ca151008412a"]},"session":{"type":"string","description":"Optional session id returned from a completed auth flow"},"redirectUri":{"type":"string","description":"Optional URI to use for callback. Specify this if callback should be different than the default. MUST have \"lastfm/callback\" in the URL somewhere.","default":"http://localhost:9078/lastfm/callback","examples":["http://localhost:9078/lastfm/callback"]},"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"}},"required":["apiKey","secret"],"title":"LastFmSourceData"},"YTMusicSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/YTMusicData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"type":"object","properties":{"logAuth":{"type":"boolean","description":"When true MS will log to DEBUG all of the credentials data it receives from YTM"},"logDiff":{"type":"boolean","description":"Always log history diff\n\nBy default MS will log to `WARN` if history diff is inconsistent but does not log if diff is expected (on new tracks found)\nSet this to `true` to ALWAYS log diff on new tracks. Expected diffs will log to `DEBUG` and inconsistent diffs will continue to log to `WARN`","default":false},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}}}},"title":"YTMusicSourceConfig"},"YTMusicData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"cookie":{"type":"string","description":"The cookie retrieved from the Request Headers of music.youtube.com after logging in.\n\nSee https://ytmusicapi.readthedocs.io/en/stable/setup/browser.html#copy-authentication-headers for how to retrieve this value.","examples":["VISITOR_INFO1_LIVE=jMp2xA1Xz2_PbVc; __Secure-3PAPISID=3AxsXpy0M/AkISpjek; ..."]},"clientId":{"type":"string","description":"Google Cloud Console project OAuth Client ID\n\nGenerated from a custom OAuth Client, see docs"},"clientSecret":{"type":"string","description":"Google Cloud Console project OAuth Client Secret\n\nGenerated from a custom OAuth Client, see docs"},"redirectUri":{"type":"string","description":"Google Cloud Console project OAuth Client Authorized redirect URI\n\nGenerated from a custom OAuth Client, see docs. multi-scrobbler will generate a default based on BASE_URL.\nOnly specify this if the default does not work for you."},"innertubeOptions":{"$ref":"#/definitions/InnertubeOptions","description":"Additional options for authorization and tailoring YTM client"}},"title":"YTMusicData"},"InnertubeOptions":{"type":"object","properties":{"po_token":{"type":"string","description":"Proof of Origin token\n\nMay be required if YTM starts returning 403"},"visitor_data":{"type":"string","description":"Visitor ID value found in VISITOR_INFO1_LIVE or visitorData cookie\n\nMay be required if YTM starts returning 403"},"account_index":{"type":"number","description":"If account login results in being able to choose multiple account, use a zero-based index to choose which one to monitor","examples":[0,1]},"location":{"type":"string"},"lang":{"type":"string"},"generate_session_locally":{"type":"boolean"},"device_category":{"type":"string"},"client_type":{"type":"string"},"timezone":{"type":"string"}},"title":"InnertubeOptions"},"MPRISSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MPRISData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"MPRISSourceConfig"},"MPRISData":{"type":"object","properties":{"blacklist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"DO NOT scrobble from any players that START WITH these values, case-insensitive","examples":[["spotify","vlc"]]},"whitelist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"ONLY from any players that START WITH these values, case-insensitive\n\nIf whitelist is present then blacklist is ignored","examples":[["spotify","vlc"]]}},"title":"MPRISData"},"MopidySourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MopidyData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"MopidySourceConfig"},"MopidyData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the Mopidy HTTP server to connect to\n\nYou MUST have Mopidy-HTTP extension enabled: https://mopidy.com/ext/http\n\nmulti-scrobbler connects to the WebSocket endpoint that ultimately looks like this => `ws://localhost:6680/mopidy/ws/`\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `ws://`\n* Hostname => `localhost`\n* Port => `6680`\n* Path => `/mopidy/ws/`","examples":["ws://localhost:6680/mopidy/ws/"],"default":"ws://localhost:6680/mopidy/ws/"},"uriBlacklist":{"type":"array","items":{"type":"string"},"description":"Do not scrobble tracks whose URI STARTS WITH any of these strings, case-insensitive\n\nEX: Don't scrobble tracks from soundcloud by adding 'soundcloud' to this list.\n\nList is ignored if uriWhitelist is used."},"uriWhitelist":{"type":"array","items":{"type":"string"},"description":"Only scrobble tracks whose URI STARTS WITH any of these strings, case-insensitive\n\nEX: Only scrobble tracks from soundcloud by adding 'soundcloud' to this list."},"albumBlacklist":{"type":"array","items":{"type":"string"},"description":"Remove album data that matches any case-insensitive string from this list when scrobbling,\n\nFor certain sources (Soundcloud) Mopidy does not have all track info (Album) and will instead use \"Soundcloud\" as the Album name. You can prevent multi-scrobbler from using this bad Album data by adding the fake name to this list. Multi-scrobbler will still scrobble the track, just without the bad data.","examples":[["Soundcloud","Mixcloud"]],"default":["Soundcloud"]}},"title":"MopidyData"},"ListenBrainzSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/ListenBrainzSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"configureAs":{"type":"string","enum":["source"],"description":"When used in `listenbrainz.config` this tells multi-scrobbler whether to use this data to configure a source or client.","default":"source","examples":["source"]}},"required":["data"],"title":"ListenBrainzSourceConfig"},"ListenBrainzSourceData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for the ListenBrainz server, if not using the default","examples":["https://api.listenbrainz.org/"],"default":"https://api.listenbrainz.org/"},"token":{"type":"string","description":"User token for the user to scrobble for","examples":["6794186bf-1157-4de6-80e5-uvb411f3ea2b"]},"username":{"type":"string","description":"Username of the user to scrobble for"}},"required":["token","username"],"title":"ListenBrainzSourceData"},"JRiverSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/JRiverData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"JRiverSourceConfig"},"JRiverData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the JRiver HTTP server to connect to\n\nmulti-scrobbler connects to the Web Service Interface endpoint that ultimately looks like this => `http://yourDomain:52199/MCWS/v1/`\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `http://`\n* Hostname => `localhost`\n* Port => `52199`\n* Path => `/MCWS/v1/`","examples":["http://localhost:52199/MCWS/v1/"],"default":"http://localhost:52199/MCWS/v1/"},"username":{"type":"string","description":"If you have enabled authentication, the username you set"},"password":{"type":"string","description":"If you have enabled authentication, the password you set"}},"required":["url"],"title":"JRiverData"},"KodiSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/KodiData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"KodiSourceConfig"},"KodiData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL of the Kodi HTTP server to connect to\n\nmulti-scrobbler connects to the Web Service Interface endpoint that ultimately looks like this => `http://yourDomain:8080/jsonrpc`\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `http://`\n* Hostname => `localhost`\n* Port => `8080`\n* Path => `/jsonrpc`","examples":["http://localhost:8080/jsonrpc"],"default":"http://localhost:8080/jsonrpc"},"username":{"type":"string","description":"The username set for Remote Control via Web Sever"},"password":{"type":"string","description":"The password set for Remote Control via Web Sever"}},"required":["url","username","password"],"title":"KodiData"},"WebScrobblerSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/WebScrobblerData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"title":"WebScrobblerSourceConfig"},"WebScrobblerData":{"type":"object","properties":{"slug":{"type":["string","null"],"description":"The URL ending that should be used to identify scrobbles for this source\n\nIn WebScrobbler's Webhook you must set an 'API URL'. All MS WebScrobbler sources must start like:\n\nhttp://localhost:9078/api/webscrobbler\n\nIf you are using multiple WebScrobbler sources (scrobbles for many users) you must use a slug to match Sources with each users extension.\n\nExample:\n\n* slug: 'usera' => API URL: http://localhost:9078/api/webscrobbler/usera\n* slug: 'userb' => API URL: http://localhost:9078/api/webscrobbler/userb\n\nIf no slug is found from an extension's incoming webhook event the first WebScrobbler source without a slug will be used"},"blacklist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Block scrobbling from specific WebScrobbler Connectors","examples":[["youtube"]]},"whitelist":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Only allow scrobbling from specific WebScrobbler Connectors","examples":[["mixcloud","soundcloud","bandcamp"]]}},"title":"WebScrobblerData"},"ChromecastSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/ChromecastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"ChromecastSourceConfig"},"ChromecastData":{"type":"object","properties":{"blacklistDevices":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"DO NOT scrobble from any cast devices that START WITH these values, case-insensitive\n\nUseful when used with auto discovery","examples":[["home-mini","family-tv"]]},"whitelistDevices":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"ONLY scrobble from any cast device that START WITH these values, case-insensitive\n\nIf whitelist is present then blacklist is ignored\n\nUseful when used with auto discovery","examples":[["home-mini","family-tv"]]},"blacklistApps":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"DO NOT scrobble from any application that START WITH these values, case-insensitive","examples":[["spotify","pandora"]]},"whitelistApps":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"ONLY scrobble from any application that START WITH these values, case-insensitive\n\nIf whitelist is present then blacklist is ignored","examples":[["spotify","pandora"]]},"useAvahi":{"type":"boolean","description":"Try to use Avahi and avahi-browse to resolve mDNS devices instead of native mDNS querying\n\nUseful for docker (alpine) container where mDNS resolution is not yet supported. Avahi socket must be exposed to the container and avahi-tools must be installed.","default":false},"useAutoDiscovery":{"type":"boolean","description":"Use mDNS to discovery Google Cast devices on your next automatically?\n\nIf not explicitly set then it is TRUE if `devices` is not set"},"devices":{"type":"array","items":{"$ref":"#/definitions/ChromecastDeviceInfo"},"description":"A list of Google Cast devices to monitor\n\nIf this is used then `useAutoDiscovery` is set to FALSE, if not explicitly set"},"allowUnknownMedia":{"anyOf":[{"type":"boolean"},{"type":"array","items":{"type":"string"}}],"description":"Chromecast Apps report a \"media type\" in the status info returned for whatever is currently playing\n\n* If set to TRUE then Music AND Generic/Unknown media will be tracked for ALL APPS\n* If set to FALSE then only media explicitly typed as Music will be tracked for ALL APPS\n* If set to a list then only Apps whose name contain one of these values, case-insensitive, will have Music AND Generic/Unknown tracked\n\nSee https://developers.google.com/cast/docs/media/messages#MediaInformation \"metadata\" property","default":false},"forceMediaRecognitionOn":{"type":"array","items":{"type":"string"},"description":"Media provided by any App whose name is listed here will ALWAYS be tracked, regardless of the \"media type\" reported\n\nApps will be recognized if they CONTAIN any of these values, case-insensitive"}},"title":"ChromecastData"},"ChromecastDeviceInfo":{"type":"object","properties":{"name":{"type":"string","description":"A friendly name to identify this device","examples":["MySmartTV"]},"address":{"type":"string","description":"The IP address of the device","examples":["192.168.0.115"]}},"required":["name","address"],"title":"ChromecastDeviceInfo"},"MalojaSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MalojaSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"configureAs":{"type":"string","enum":["source"],"description":"When used in `maloja.config` this tells multi-scrobbler whether to use this data to configure a source or client.","default":"source","examples":["source"]}},"required":["data"],"title":"MalojaSourceConfig"},"MalojaSourceData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for maloja server","examples":["http://localhost:42010"]},"apiKey":{"type":"string","description":"API Key for Maloja server","examples":["myApiKey"]}},"required":["apiKey","url"],"title":"MalojaSourceData"},"MusikcubeSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MusikcubeData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"MusikcubeSourceConfig"},"MusikcubeData":{"type":"object","properties":{"url":{"type":"string","description":"URL of the Musikcube Websocket (Metadata) server to connect to\n\nYou MUST have enabled 'metadata' server and set a password: https://github.com/clangen/musikcube/wiki/remote-api-documentation\n * musikcube -> settings -> server setup\n\nThe URL you provide here will have all parts not explicitly defined filled in for you so if these are not the default you must define them.\n\nParts => [default value]\n\n* Protocol => `ws://`\n* Hostname => `localhost`\n* Port => `7905`","examples":["ws://localhost:7905"],"default":"ws://localhost:7905"},"password":{"type":"string","description":"Password set in Musikcube https://github.com/clangen/musikcube/wiki/remote-api-documentation\n\n* musikcube -> settings -> server setup -> password"},"device_id":{"type":"string"}},"required":["password"],"title":"MusikcubeData"},"MusicCastSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MusicCastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"MusicCastSourceConfig"},"MusicCastData":{"type":"object","properties":{"url":{"type":"string","description":"The host or URL of the YamahaExtendedControl endpoint to use","examples":[["192.168.0.101","http://192.168.0.101/YamahaExtendedControl"]]}},"required":["url"],"title":"MusicCastData"},"MPDSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/MPDData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/MPDSourceOptions"}},"required":["data","options"],"title":"MPDSourceConfig"},"MPDData":{"type":"object","properties":{"url":{"type":"string","description":"URL:PORT of the MPD server to connect to\n\nTo use this you must have TCP connections enabled for your MPD server https://mpd.readthedocs.io/en/stable/user.html#client-connections","examples":["localhost:6600"],"default":"localhost:6600"},"path":{"type":"string","description":"If using socket specify the path instead of url.\n\ntrailing `~` is replaced by your home directory"},"password":{"type":"string","description":"Password for the server, if set https://mpd.readthedocs.io/en/stable/user.html#permissions-and-passwords"}},"title":"MPDData"},"MPDSourceOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"MPDSourceOptions"},"VLCSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/VLCData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/VLCSourceOptions"}},"required":["data"],"title":"VLCSourceConfig"},"VLCData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"url":{"type":"string","description":"URL:PORT of the VLC server to connect to\n\nTo use this you must have the Web (http) interface module enabled and a password set https://foxxmd.github.io/multi-scrobbler/docs/configuration#vlc","examples":["localhost:8080"],"default":"localhost:8080"},"password":{"type":"string","description":"Password for the server"}},"required":["password"],"title":"VLCData"},"VLCSourceOptions":{"type":"object","properties":{"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"},"filenamePatterns":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"A list of regular expressions to use to extract metadata (title, album, artist) from a filename\n\nUsed when VLC reports only the filename for the current audio track"},"logFilenamePatterns":{"type":"boolean","description":"Log to DEBUG when a filename-only track is matched or not matched by filenamePatterns","default":false},"dumpVlcMetadata":{"type":"boolean","description":"Dump all the metadata VLC reports for an audio track to DEBUG.\n\nUse this if reporting an issue with VLC not correctly capturing metadata for a track.","default":false}},"title":"VLCSourceOptions"},"IcecastSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/IcecastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/IcecastSourceOptions"}},"required":["data"],"title":"IcecastSourceConfig"},"IcecastData":{"type":"object","properties":{"sources":{"type":"array","items":{"$ref":"#/definitions/IcecastSource"}},"icestatsEndpoint":{"type":"string"},"statsEndpoint":{"type":"string"},"nextsongsEndpoint":{"type":"string"},"sevenhtmlEndpoint":{"type":"string"},"icyMetaInt":{"type":"number"},"url":{"type":"string","description":"The Icecast stream URL"}},"required":["url"],"title":"IcecastData"},"IcecastSource":{"type":"string","enum":["icy","ogg","icestats","stats","sevenhtml","nextsongs"],"title":"IcecastSource"},"IcecastSourceOptions":{"type":"object","properties":{"systemScrobble":{"type":"boolean","description":"For Sources that support manual listening, should MS default to scrobbling when no user interaction has occurred?\n\nIf not specified MS will use a Source's specific behavior, see Source's documentation."},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"maxPollRetries":{"type":"number","description":"default # of automatic polling restarts on error","default":5,"examples":[5]},"logPayload":{"type":"boolean","description":"* If this source has INGRESS to MS (sends a payload, rather than MS GETTING requesting a payload) then setting this option to true will make MS log the payload JSON to DEBUG output\n* If this source is POLLING then it will log the raw data for each unique track/response the first time it is seen","default":false,"examples":[false]},"logFilterFailure":{"type":["boolean","string"],"enum":[false,"debug","warn"],"description":"If this source has INGRESS to MS and has filters this determines how MS logs when a payload (event) fails a defined filter (IE users/servers/library filters)\n\n* `false` => do not log\n* `debug` => log to DEBUG level\n* `warn` => log to WARN level (default)\n\nHint: This is useful if you are sure this source is setup correctly and you have multiple other sources. Set to `debug` or `false` to reduce log noise.","default":"warn","examples":["warn"]},"logPlayerState":{"type":"boolean","description":"For Sources that track Player State (currently playing) this logs a simple player state/summary to DEBUG output","default":false,"examples":[false]},"logToFile":{"anyOf":[{"type":"boolean","enum":[true]},{"$ref":"#/definitions/LogLevel"},{"$ref":"#/definitions/FileLogOptions"}],"description":"**Exprimental:** Log to a separate file for this Source.\n\nUseful for debugging long-running Sources"},"scrobbleBacklog":{"type":"boolean","description":"If this source\n\n* supports fetching a listen history\n* and this option is enabled\n\nthen on startup MS will attempt to scrobble the recent listens from that history","default":true,"examples":[true,false]},"scrobbleThresholds":{"$ref":"#/definitions/ScrobbleThresholds","description":"Set thresholds for when multi-scrobbler should consider a tracked play to be \"scrobbable\". If both duration and percent are defined then if either condition is met the track is scrobbled."},"scrobbleBacklogCount":{"type":"number","description":"The number of listens to fetch when scrobbling from backlog\n\n* Only applies if this source supports fetching a listen history\n* If not specified it defaults to the maximum number of listens the source API supports"},"playTransform":{"$ref":"#/definitions/PlayTransformOptions"}},"title":"IcecastSourceOptions"},"AzuracastSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/AzuracastData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"}},"required":["data"],"title":"AzuracastSourceConfig"},"AzuracastData":{"type":"object","properties":{"url":{"type":"string","description":"Base URL of the Azuracast instance\n\nThis does NOT include the station. If a station is included it will be ignored. Use `station` field to specify station, if necessary","examples":["https://radio.mydomain.tld","http://localhost:80"]},"station":{"type":"string","description":"The specific station to monitor\n\nScrobbling will only occur if any of the monitor conditions are met AND the station is ONLINE.\n\nTo monitor multiple stations create a Source for each station.","examples":["my-station-1"]},"monitorWhenListeners":{"type":["boolean","number"],"description":"Only activate scrobble monitoring if station\n\n* `true` => has any current listeners\n* `number` => has EQUAL TO or MORE THAN X number of listeners"},"monitorWhenLive":{"type":"boolean","description":"Only activate scrobble monitoring if station has a live DJ/Streamer","default":true},"apiKey":{"type":"string","description":"API Key used to access data about private streams\n\nhttps://www.azuracast.com/docs/developers/apis/#api-authentication"}},"required":["url","station"],"title":"AzuracastData"},"KoitoSourceConfig":{"type":"object","properties":{"name":{"type":"string","description":"Unique identifier for this source."},"data":{"$ref":"#/definitions/KoitoSourceData"},"enable":{"type":"boolean","description":"Should MS use this client/source? Defaults to true","default":true,"examples":[true]},"clients":{"type":"array","items":{"type":"string"},"description":"Restrict scrobbling tracks played from this source to Clients with names from this list. If list is empty is not present Source scrobbles to all configured Clients.","examples":[["MyMalojaConfigName","MyLastFMConfigName"]]},"options":{"$ref":"#/definitions/CommonSourceOptions"},"configureAs":{"type":"string","enum":["source"],"description":"When used in `koito.config` this tells multi-scrobbler whether to use this data to configure a source or client.","default":"source","examples":["source"]}},"required":["data"],"title":"KoitoSourceConfig"},"KoitoSourceData":{"type":"object","properties":{"interval":{"type":"number","description":"How long to wait before polling the source API for new tracks (in seconds)","default":10,"examples":[10]},"maxInterval":{"type":"number","description":"When there has been no new activity from the Source API multi-scrobbler will gradually increase the wait time between polling up to this value (in seconds)","default":30,"examples":[30]},"staleAfter":{"type":"number","description":"Number of seconds after which A Player is considered Stale\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Stale. When the Player becomes Stale:\n\n* The current listening session is ended. If the Player becomes active again a new listening session is started (Player will miss `interval` seconds of listening)\n* If the player has an existing session w/ track then MS attempts to scrobble it\n\nThis option DOES NOT need to be set. It is automatically calculated as (`interval` * 3) when not defined."},"orphanedAfter":{"type":"number","description":"Number of seconds after which A Player is considered Orphaned\n\nWhen Polling the source does not recieve data about a specific Player after X seconds it becomes Orphaned. When the Player becomes Orphaned:\n\n* The current Player session is ended and the Player is removed from MS\n* MS attempts to scrobble, if the Player has an existing session w/ track\n\nA Player should become Orphaned EQUAL TO OR AFTER it becomes Stale.\n\n* This option DOES NOT need to be set. It is automatically calculated as (`interval` * 5) when not defined.\n* If it is set it must be equal to or larger than `staleAfter` or (`interval * 3`)"},"maxRequestRetries":{"type":"number","description":"default # of http request retries a source/client can make before error is thrown","default":1,"examples":[1]},"retryMultiplier":{"type":"number","description":"default retry delay multiplier (retry attempt * multiplier = # of seconds to wait before retrying)","default":1.5,"examples":[1.5]},"url":{"type":"string","description":"URL for the Koito server","examples":["http://192.168.0.100:4110"]},"token":{"type":"string","description":"User token for the user to scrobble for","examples":["pM195xPV98CDpk0QW47FIIOR8AKATAX5DblBF-Jq0t1MbbKL"]},"username":{"type":"string","description":"Username of the user to scrobble for"}},"required":["token","url","username"],"title":"KoitoSourceData"}}} \ No newline at end of file