From 2b8322bd2e376cef00a860800a8eae696f9c0e3b Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Sat, 17 Oct 2026 18:48:46 +0000 Subject: [PATCH] retrofit remaining transform pages with codeblocks --- .../transforms/coverartarchive.mdx | 133 +++++---- .../docs/configuration/transforms/native.mdx | 88 +++--- .../docs/configuration/transforms/rocksky.mdx | 253 ++++++++++-------- .../docs/configuration/transforms/spotify.mdx | 66 +++-- .../docs/configuration/transforms/user.mdx | 46 ++-- 5 files changed, 325 insertions(+), 261 deletions(-) diff --git a/docsite/docs/configuration/transforms/coverartarchive.mdx b/docsite/docs/configuration/transforms/coverartarchive.mdx index a491b749..cd9c173c 100644 --- a/docsite/docs/configuration/transforms/coverartarchive.mdx +++ b/docsite/docs/configuration/transforms/coverartarchive.mdx @@ -51,9 +51,10 @@ Use `url` to define the base URL of the Cover Art Archive server to use. If `url Example of multiple servers: -```json5 title="config.json" +```json5 title="config.json" brackets { - // ... + // [!link /AIO Config/ /configuration?configType=aio#configuration-types] + // ...your AIO Config "transformers": [ { "type": "coverartarchive", @@ -61,13 +62,12 @@ Example of multiple servers: "data": { "apis": [ { - "enable": true - // uses default Cover Art Archive server https://coverartarchive.org + "enable": true // [!ref] uses default Cover Art Archive server https://coverartarchive.org }, - // additional server + // [!ref] additional server { "enable": true, - "url": "https://my.caa.mirror.domain.com" + "url": "https://my.caa.mirror.domain.com" // [!code highlight] } ] }, @@ -84,9 +84,10 @@ Rate limiting is defined by **max number of requests** within **timespan of N se Example of configuring rate limit: -```json5 title="config.json" +```json5 title="config.json" brackets { - // ... + // [!link /AIO Config/ /configuration?configType=aio#configuration-types] + // ...your AIO Config "transformers": [ { "type": "coverartarchive", @@ -95,10 +96,10 @@ Example of configuring rate limit: "apis": [ { "enable": true, - "rate": { - // IE 10 req/s - "requests": 10, // maximum of 10 requests - "perTime": 1 // can be made within 1 second + // [!code highlight:4] + "rate": { // [!ref] IE 10 req/s + "requests": 10, // [!ref] maximum of 10 requests + "perTime": 1 // [!ref] can be made within 1 second } } ] @@ -116,13 +117,15 @@ All of the properties found in the [**Finding Art**](#finding-art) section are c Example: -```json5 title="config.json" +```json5 title="config.json" brackets { - // ... + // [!link /AIO Config/ /configuration?configType=aio#configuration-types] + // ...your AIO Config "transformers": [ { "type": "coverartarchive", "name": "MyCAA", + // [!code highlight:4] "defaults": { "allowedTypes": ["any"], "preferredSizes": ["500"] @@ -136,11 +139,15 @@ Example: [Add your Stage](/configuration/transforms/#stage) to a Source or Client by specifying it in a [Hook](/configuration/transforms/#hook): -```json5 title="discord.json" +```json5 title="discord.json" brackets [ { + // [!link /Discord/ /configuration/clients/discord] + // [!link /File Config/ /configuration?configType=file#configuration-types] + // ...your Discord File Config "name": "MyDiscord", "data": { /* ... */}, + // [!code focus:10] "options": { "playTransform": { "preCompare": [ @@ -161,11 +168,15 @@ This Stage only has one [**Stage Rule**](/configuration/transforms#stage-rules): Example -```json5 title="discord.json" +```json5 title="discord.json" brackets [ { + // [!link /Discord/ /configuration/clients/discord] + // [!link /File Config/ /configuration?configType=file#configuration-types] + // ...your Discord File Config "name": "MyDiscord", "data": { /* ... */}, + // [!code focus:13] "options": { "playTransform": { "preCompare": [ @@ -173,7 +184,8 @@ This Stage only has one [**Stage Rule**](/configuration/transforms#stage-rules): "type": "coverartarchive", "name": "MSDefault", "art": { - "when": {/* ... */}, // will only apply art to Play if "when" is satisfied + // [!link /when/ /configuration/transforms#conditional-modification] + "when": {/* ... */}, // [!ref] will only apply art to Play if `"when"` is satisfied } } ] @@ -193,18 +205,22 @@ The `defaults` you set in [Stage Configuration](#stage-configuration) [can be ov Example -```json5 title="discord.json" +```json5 title="discord.json" brackets [ { + // [!link /Discord/ /configuration/clients/discord] + // [!link /File Config/ /configuration?configType=file#configuration-types] + // ...your Discord File Config "name": "MyDiscord", "data": { /* ... */}, + // [!code focus:11] "options": { "playTransform": { "preCompare": [ { "type": "coverartarchive", "name": "MSDefault", - "allowedTypes": ["any"], // override from defaults + "allowedTypes": ["any"], // [!code highlight] [!ref] overrides `"allowedTypes"` from `"defaults"` } ] } @@ -245,21 +261,19 @@ Stages in `*_TRANSFORMS` run in the order they are written. To [get MBIDs first] Using [Jellyfin](/quickstart#create-docker-compose-file) example from Quickstart with the Musicbrainz Stage and the Cover Art Archive Stage: -```yaml +```yaml title="docker-compose.yml" services: multi-scrobbler: image: foxxmd/multi-scrobbler container_name: multi-scrobbler environment: - MB_PRESETS=default - # [!code highlight:2] - # allow any type of art, not only front covers + # [!code highlight] [!ref] allow any type of art, not only front covers - CAA_PRESETS=any - JELLYFIN_URL=192.168.0.110:8096 - JELLYFIN_APIKEY=c9fae8756fbf481ebd9c5bb56b - JELLYFIN_USER=MyUser - # [!code highlight:2] - # adds MBIDs with Musicbrainz Stage, then finds art with Cover Art Archive Stage + # [!code highlight] [!ref] adds MBIDs with Musicbrainz Stage, then finds art with Cover Art Archive Stage - JELLYFIN_TRANSFORMS=musicbrainz,coverartarchive - MALOJA_URL=http://domain.tld:42010 - MALOJA_API_KEY=1234 @@ -295,12 +309,11 @@ Use these options to change when MS searches: Example -[Stage Configuration](/configuration/transforms#configuring-stages) example: - -```json5 -// ... +```json5 brackets +// [!link /Stage Configuration/ /configuration/transforms#configuring-stages] +// ...your Stage Configuration "defaults": { - // uncomment to make the stage always search, even if album art is present + // [!ref] uncomment to make the stage always search, even if album art is present //"forceSearch": true } ``` @@ -341,11 +354,11 @@ If you list more than one type then an image must be labeled with **all** of the ::: -```json5 -// ... +```json5 brackets +// [!link /Stage Configuration/ /configuration/transforms#configuring-stages] +// ...your Stage Configuration "defaults": { - // allow any image - "allowedTypes": ["any"] + "allowedTypes": ["any"] // [!ref] allow any image } ``` @@ -362,13 +375,12 @@ There are two options for sizes: * `allowedSizes` (default `["any"]`) - Only use images that are available in these sizes. An image must be available in **at least one** size you list, or use `any` to allow any image. * `preferredSizes` (default `["250", "500", "1200"]`) - The sizes you want, in order. MS uses the first size in your list that is available. If none are available, MS uses any size the image has. -```json5 -// ... +```json5 brackets +// [!link /Stage Configuration/ /configuration/transforms#configuring-stages] +// ...your Stage Configuration "defaults": { - // allow art with either 1200 or 500 sizes - "allowedSizes": ["1200","500"], - // prefer the largest size first - "preferredSizes": ["1200", "500"] + "allowedSizes": ["1200","500"], // [!ref] allow art with either 1200 or 500 sizes + "preferredSizes": ["1200", "500"] // [!ref] prefer the largest size first } ``` @@ -388,25 +400,28 @@ Set [`failureReturnPartial: true`](/configuration/transforms/#flow-control) on t Example -```json5 title="jellyfin.json" +```json5 title="jellyfin.json" brackets [ { + // [!link /Jellyfin/ /configuration/sources/jellyfin] + // [!link /File Config/ /configuration?configType=file#configuration-types] + // ...your Jellyfin File Config "name": "MyJellyfin", "data": { /* ... */}, + // [!code focus:15] "options": { "playTransform": { "preCompare": [ { - // adds MBIDs to the Play - "type": "musicbrainz", + // [!link /musicbrainz/ /configuration/transforms/musicbrainz] + "type": "musicbrainz", // [!ref] adds MBIDs to the Play "name": "MSDefault" }, { - // uses the MBIDs to find art - "type": "coverartarchive", + "type": "coverartarchive", // [!ref] uses the MBIDs to find art "name": "MSDefault", - // keep Musicbrainz changes even if no art is found - "failureReturnPartial": true + // [!link /failureReturnPartial/ /configuration/transforms/#flow-control] + "failureReturnPartial": true // [!code highlight] [!ref] keep Musicbrainz changes even if no art is found } ] } @@ -425,13 +440,15 @@ Set [`failureReturnPartial: true`](/configuration/transforms/#flow-control) on t Example -In a [Jellyfin Source](/configuration/sources/jellyfin) [File Config](/configuration?configType=file#configuration-types): - -```json5 title="jellyfin.json" +```json5 title="jellyfin.json" brackets [ { + // [!link /Jellyfin/ /configuration/sources/jellyfin] + // [!link /File Config/ /configuration?configType=file#configuration-types] + // ...your Jellyfin File Config "name": "MyJellyfin", "data": { /* ... */}, + // [!code focus:10] "options": { "playTransform": { "preCompare": [ @@ -448,15 +465,14 @@ In a [Jellyfin Source](/configuration/sources/jellyfin) [File Config](/configura Or using a [Jellyfin Source](/configuration/sources/jellyfin) with [ENV Config](/configuration?configType=env#configuration-types): -```yaml +```yaml title="docker-compose.yml" services: multi-scrobbler: image: foxxmd/multi-scrobbler environment: # ... your source ENVs go here # - # [!code highlight:2] - # applies Cover Art Archive Stage to preCompare of Jellyfin Source + # [!code highlight] [!ref] applies Cover Art Archive Stage to preCompare of Jellyfin Source - JELLYFIN_TRANSFORMS=coverartarchive volumes: @@ -486,13 +502,15 @@ The [Discord](/configuration/clients/discord) Client shows album art when your P ``` - In your [Discord](/configuration/clients/discord) [File Config](/configuration?configType=file#configuration-types): - - ```json5 title="discord.json" + ```json5 title="discord.json" brackets [ { + // [!link /Discord/ /configuration/clients/discord] + // [!link /File Config/ /configuration?configType=file#configuration-types] + // ...your Discord File Config "name": "MyDiscord", "data": { /* ... */}, + // [!code focus:15] "options": { "playTransform": { "preCompare": [ @@ -515,9 +533,10 @@ The [Discord](/configuration/clients/discord) Client shows album art when your P Your [AIO Config](/configuration?configType=aio#configuration-types): - ```json5 title="config.json" + ```json5 title="config.json" brackets { - // ... + // [!link /AIO Config/ /configuration?configType=aio#configuration-types] + // ...your AIO Config "clients": [ { "name": "MyDiscord", diff --git a/docsite/docs/configuration/transforms/native.mdx b/docsite/docs/configuration/transforms/native.mdx index d2ac8db0..797f8253 100644 --- a/docsite/docs/configuration/transforms/native.mdx +++ b/docsite/docs/configuration/transforms/native.mdx @@ -21,23 +21,22 @@ A non-exhaustive list of heuristics: A practical example of what the Native Stage will do to Play data: -This Scrobble from [Subsonic](/configuration/sources/subsonic) +This Scrobble from [Subsonic](/configuration/sources/subsonic) is transformed: -```json +:::code-tabs +```json label="Before" { - "title": "Endless Possibility (feat. Wheatus)", - "artists": ["Bowling For Soup & Punk Rock Factory"] + "title": "Endless Possibility (feat. Wheatus)", // [!info /(feat. Wheatus)/] Artist extracted from title + "artists": ["Bowling For Soup & Punk Rock Factory"] // [!info /Bowling For Soup & Punk Rock Factory/] Artists split on delimiter } ``` - -Will be transformed into this - -```json +```json label="After" { "title": "Endless Possibility", "artists": ["Bowling For Soup", "Punk Rock Factory", "Wheatus"] } ``` +::: To use the [default Stage](/configuration/transforms/#stage-names-and-default-stages) either omit `name` or specify it as `"name": "MSDefault"`. @@ -80,16 +79,17 @@ Config => extracted artists Each [Rule](/configuration/transforms#stage-rules) should be either a boolean, specifying if the transformed data should be used for this field, or a [`when` condition.](/configuration/transforms#conditional-modification): -```json5 +```json5 brackets { "type": "native", // ... - "title": false, // will not apply any changes to Play title + "title": false, // [!ref] will not apply any changes to Play title "artists": { - "when": {/* ... */}, // will only apply changes to Play artists if "when" is satisfied + // [!link /when/ /configuration/transforms#conditional-modification] + "when": {/* ... */}, // [!ref] will only apply changes to Play artists if `"when"` is satisfied /* ... */ }, - "album": true // will always apply changes to Play album + "album": true // [!ref] will always apply changes to Play album } ``` @@ -105,19 +105,20 @@ This can be used by omitting the `name` in your [modification Stage](/configurat Example -In a [Subsonic](/configuration/sources/subsonic) [File Config](/configuration?configType=file#configuration-types): - -```json5 title="subsonic.json" +```json5 title="subsonic.json" brackets [ { + // [!link /Subsonic/ /configuration/sources/subsonic] + // [!link /File Config/ /configuration?configType=file#configuration-types] + // ...your Subsonic File Config "name": "MySubsonic", "data": { /* ... */}, + // [!code focus:9] "options": { "playTransform": { "preCompare": [ { - "type": "native", - // do not include name, the default native stage will be used + "type": "native", // [!callout /native/] do not include `name`, the default native stage will be used } ] } @@ -144,7 +145,7 @@ The `*` stands for the prefix used for each Source/Client's ENV keys. Refer to t Using [Jellyfin + Maloja](/quickstart#create-docker-compose-file) example from Quickstart: -```yaml +```yaml title="docker-compose.yml" services: multi-scrobbler: image: foxxmd/multi-scrobbler @@ -153,9 +154,9 @@ services: - JELLYFIN_URL=192.168.0.110:8096 - JELLYFIN_APIKEY=c9fae8756fbf481ebd9c5bb56b - JELLYFIN_USER=MyUser - - JELLYFIN_TRANSFORMS=native # applies native Stage to preTransform of Jellyfin source + - JELLYFIN_TRANSFORMS=native # [!code highlight] [!ref] applies native Stage to preTransform of Jellyfin source - - MALOJA_URL=http://192.168.0.100:42010 # maloja receives enhanced scrobble from Jellyfin + - MALOJA_URL=http://192.168.0.100:42010 # [!ref] maloja receives enhanced scrobble from Jellyfin - MALOJA_API_KEY=myApiKey @@ -178,33 +179,36 @@ services: Your [AIO Config](/configuration?configType=aio#configuration-types): -```json5 title="config.json" +:::code-tabs +```json5 label="config" title="config.json" brackets { - // ... + // [!link /AIO Config/ /configuration?configType=aio#configuration-types] + // ...your AIO Config "transformers": [ { "type": "native", "name": "MyNativeTransformer", "defaults": { - // default delimiters when this Stage is used in a hook + // [!ref] default delimiters when this Stage is used in a hook "delimiters": [ "•" ], - // default delimiters when this Stage is used in a hook + // [!ref] default artistsParseFrom when this Stage is used in a hook "artistsParseFrom": ["artists"] } } ] } ``` - -In a [Subsonic](/configuration/sources/subsonic) [File Config](/configuration?configType=file#configuration-types): - -```json5 title="subsonic.json" +```json5 label="subsonic" title="subsonic.json" brackets [ { + // [!link /Subsonic/ /configuration/sources/subsonic] + // [!link /File Config/ /configuration?configType=file#configuration-types] + // ...your Subsonic File Config "name": "MySubsonic", "data": { /* ... */}, + // [!code focus:10] "options": { "playTransform": { "preCompare": [ @@ -218,6 +222,7 @@ In a [Subsonic](/configuration/sources/subsonic) [File Config](/configuration?co } ] ``` +::: ### Don't parse specific artists @@ -228,14 +233,17 @@ In a [Subsonic](/configuration/sources/subsonic) [File Config](/configuration?co Your [AIO Config](/configuration?configType=aio#configuration-types): -```json5 title="config.json" +:::code-tabs +```json5 label="config" title="config.json" brackets { - // ... + // [!link /AIO Config/ /configuration?configType=aio#configuration-types] + // ...your AIO Config "transformers": [ { "type": "native", "name": "MyNativeTransformer", "defaults": { + // [!code highlight:4] "artistsIgnore": [ "Crosby, Stills, Nash & Young", "Polo & Pan" @@ -245,14 +253,15 @@ Your [AIO Config](/configuration?configType=aio#configuration-types): ] } ``` - -In a [Subsonic](/configuration/sources/subsonic) [File Config](/configuration?configType=file#configuration-types): - -```json5 title="subsonic.json" +```json5 label="subsonic" title="subsonic.json" brackets [ { + // [!link /Subsonic/ /configuration/sources/subsonic] + // [!link /File Config/ /configuration?configType=file#configuration-types] + // ...your Subsonic File Config "name": "MySubsonic", "data": { /* ... */}, + // [!code focus:10] "options": { "playTransform": { "preCompare": [ @@ -266,6 +275,7 @@ In a [Subsonic](/configuration/sources/subsonic) [File Config](/configuration?co } ] ``` +::: ### Extract Artists from Title but do not modify Title @@ -278,19 +288,21 @@ In the scenario where you want to extract Artists from Track titles with joiners No additional [stage configuration](#configuration) is required so we can use the [default Stage](#default-stage). -In a [Subsonic](/configuration/sources/subsonic) [File Config](/configuration?configType=file#configuration-types): - -```json5 title="subsonic.json" +```json5 title="subsonic.json" brackets [ { + // [!link /Subsonic/ /configuration/sources/subsonic] + // [!link /File Config/ /configuration?configType=file#configuration-types] + // ...your Subsonic File Config "name": "MySubsonic", "data": { /* ... */}, + // [!code focus:10] "options": { "playTransform": { "preCompare": [ { "type": "native", - "title": false // do not modify title + "title": false // [!code highlight] [!ref] do not modify title } ] } diff --git a/docsite/docs/configuration/transforms/rocksky.mdx b/docsite/docs/configuration/transforms/rocksky.mdx index 9a751934..9b210535 100644 --- a/docsite/docs/configuration/transforms/rocksky.mdx +++ b/docsite/docs/configuration/transforms/rocksky.mdx @@ -51,9 +51,10 @@ Use `url` to define the base URL of the Rocksky server to use. If `url` is not d Example of multiple servers: -```json5 title="config.json" +```json5 title="config.json" brackets { - // ... + // [!link /AIO Config/ /configuration?configType=aio#configuration-types] + // ...your AIO Config "transformers": [ { "type": "rocksky", @@ -61,13 +62,12 @@ Example of multiple servers: "data": { "apis": [ { - "enable": true - // uses default Rocksky server https://api.rocksky.app + "enable": true // [!ref] uses default Rocksky server https://api.rocksky.app }, - // additional server + // [!ref] additional server { "enable": true, - "url": "https://my.rs.mirror.domain.com" + "url": "https://my.rs.mirror.domain.com" // [!code highlight] } ] }, @@ -89,9 +89,10 @@ These constraints apply: Example of configuring rate limit: -```json5 title="config.json" +```json5 title="config.json" brackets { - // ... + // [!link /AIO Config/ /configuration?configType=aio#configuration-types] + // ...your AIO Config "transformers": [ { "type": "rocksky", @@ -99,18 +100,16 @@ Example of configuring rate limit: "data": { "apis": [ { - "enable": true - // uses default Rocksky server https://api.rocksky.app - // hard-limited to 1000 req / 30s + "enable": true // [!ref] uses default Rocksky server https://api.rocksky.app, hard-limited to 1000 req / 30s }, - // additional server + // [!ref] additional server { "enable": true, "url": "https://my.rs.mirror.domain.com", - "rate": { - // IE 6 req/s - "requests": 6, // maximum of 6 requests - "perTime": 1 // can be made within 1 second + // [!code highlight:4] + "rate": { // [!ref] IE 6 req/s + "requests": 6, // [!ref] maximum of 6 requests + "perTime": 1 // [!ref] can be made within 1 second } } ] @@ -128,15 +127,17 @@ All of the properties found in [**Matching with Rocksky**](#matching-with-rocksk Example: -```json5 title="config.json" +```json5 title="config.json" brackets { - // ... + // [!link /AIO Config/ /configuration?configType=aio#configuration-types] + // ...your AIO Config "transformers": [ { "type": "rocksky", "name": "MyRocksky", + // [!code highlight:4] "defaults": { - "searchWhenMissing": ['duration','mbid'] + "searchWhenMissing": ['duration','mbid'], "allowNoMatch": false } } @@ -148,12 +149,16 @@ Example: [Add your Stage](/configuration/transforms/#stage) to a Source or Client by specifying it in a [Hook](/configuration/transforms/#hook): -```json5 title="rocksky.json" +```json5 title="rocksky.json" brackets [ { + // [!link /Rocksky/ /configuration/clients/rocksky] + // [!link /File Config/ /configuration?configType=file#configuration-types] + // ...your Rocksky File Config "name": "Rocky", "configureAs": "client", "data": { /* ... */}, + // [!code focus:10] "options": { "playTransform": { "preCompare": [ @@ -174,25 +179,30 @@ Each [**Stage Rule**](/configuration/transforms#stage-rules) should be either a Example -```json5 title="rocksky.json" +```json5 title="rocksky.json" brackets [ { + // [!link /Rocksky/ /configuration/clients/rocksky] + // [!link /File Config/ /configuration?configType=file#configuration-types] + // ...your Rocksky File Config "name": "Rocky", "configureAs": "client", "data": { /* ... */}, + // [!code focus:17] "options": { "playTransform": { "preCompare": [ { "type": "rocksky", - "name": "MSDefault" + "name": "MSDefault", // ... - "title": false, // will not apply any changes to Play title + "title": false, // [!ref] will not apply any changes to Play title "album": { - "when": {/* ... */}, // will only apply changes to Play artists if "when" is satisfied + // [!link /when/ /configuration/transforms#conditional-modification] + "when": {/* ... */}, // [!ref] will only apply changes to Play album if `"when"` is satisfied /* ... */ }, - "meta": true // adds MusicBrainz MBIDs to scrobble data + "meta": true // [!ref] adds MusicBrainz MBIDs to scrobble data } ] } @@ -211,19 +221,23 @@ The `defaults` you set in [Stage Configuration](#stage-configuration) [can be ov Example -```json5 title="rocksky.json" +```json5 title="rocksky.json" brackets [ { + // [!link /Rocksky/ /configuration/clients/rocksky] + // [!link /File Config/ /configuration?configType=file#configuration-types] + // ...your Rocksky File Config "name": "Rocky", "configureAs": "client", "data": { /* ... */}, + // [!code focus:11] "options": { "playTransform": { "preCompare": [ { "type": "rocksky", "name": "MSDefault", - "searchWhenMissing": ['duration','mbid'], // override from defaults + "searchWhenMissing": ['duration','mbid'], // [!code highlight] [!ref] overrides `"searchWhenMissing"` from `"defaults"` } ] } @@ -259,15 +273,13 @@ The `*` stands for the prefix used for each Source/Client's ENV keys. Refer to t Using [Jellyfin](/quickstart#create-docker-compose-file) example from Quickstart with Rocksky and the Rocksky Stage: -```yaml +```yaml title="docker-compose.yml" services: multi-scrobbler: image: foxxmd/multi-scrobbler container_name: multi-scrobbler environment: - # [!code highlight:3] - # searches with the searchOrder - # isrc => basic + # [!code highlight] [!ref] searches with the searchOrder `isrc` => `basic` - RS_PRESETS=default - JELLYFIN_URL=192.168.0.110:8096 - JELLYFIN_APIKEY=c9fae8756fbf481ebd9c5bb56b @@ -275,8 +287,7 @@ services: - ROCKSKY_ID=Rocky - ROCKSKY_HANDLE=alice.bsky.social - ROCKSKY_APP_PW=1234567 - # [!code highlight:2] - # applies Rocksky Stage to preTransform of Rocksky Client + # [!code highlight] [!ref] applies Rocksky Stage to preTransform of Rocksky Client - ROCKSKY_TRANSFORMS=rocksky @@ -301,15 +312,15 @@ Using [Rules for Play Data](https://docs.multi-scrobbler.app/configuration/trans Example -```json5 -// all properties *not* explicitly set default to true +```json5 brackets +// [!ref] all properties *not* explicitly set default to true { - "type": "musicbrainz", + "type": "rocksky", // ... - "title": false, // will not apply musicbrainz changes to Play title + "title": false, // [!ref] will not apply rocksky changes to Play title //"albumArtists": true, // always applies //"artists": true // always applies - "album": false, // will never apply changes to Play album + "album": false, // [!ref] will never apply changes to Play album //"duration": true, // always applies //"meta": true, // always applies //"art": true, // always applies @@ -366,15 +377,14 @@ Define which data types are required using: Example -[Stage Configuration](/configuration/transforms#configuring-stages) example: - -```json5 -// ... +```json5 brackets +// [!link /Stage Configuration/ /configuration/transforms#configuring-stages] +// ...your Stage Configuration "defaults": { - // only search if duration or recording MBID is missing - "searchWithMissing": ["duration", "mbid"] + // [!ref] only search if duration or recording MBID is missing + "searchWhenMissing": ["duration", "mbid"], - // uncomment to make the stage always search, even if above data is present + // [!ref] uncomment to make the stage always search, even if above data is present //forceSearch: true } ``` @@ -396,10 +406,11 @@ All search methods will be run in the order they were defined until one method r To set search methods and their order use the `searchOrder` option in your [Stage Configuration](/configuration/transforms#configuring-stages): -```json5 -// ... +```json5 brackets +// [!link /Stage Configuration/ /configuration/transforms#configuring-stages] +// ...your Stage Configuration "defaults": { - // only searches using these methods, in the order they are listed here + // [!ref] only searches using these methods, in the order they are listed here "searchOrder": ["isrc", "basic", "artist"] } ``` @@ -418,12 +429,11 @@ If `searchOrder` is undefined then Multi-scrobbler will default to using `isrc` If your Scrobble data contains an [ISRC](https://musicbrainz.org/doc/ISRC) (usually from Spotify) then Multi-scrobbler can search using this ID, title, and artist only. -[Stage Configuration](/configuration/transforms#configuring-stages) example: - -```json5 -// ... +```json5 brackets +// [!link /Stage Configuration/ /configuration/transforms#configuring-stages] +// ...your Stage Configuration "defaults": { - // will only search using isrc + // [!ref] will only search using isrc "searchOrder": ["isrc"] } ``` @@ -436,13 +446,12 @@ If your Scrobble data contains an [ISRC](https://musicbrainz.org/doc/ISRC) (usua If your Scrobble data contains a [Recording MBID](https://musicbrainz.org/doc/Recording) then Multi-scrobbler can search using this MBID, title, and artist only. -[Stage Configuration](/configuration/transforms#configuring-stages) example: - -```json5 -// ... +```json5 brackets +// [!link /Stage Configuration/ /configuration/transforms#configuring-stages] +// ...your Stage Configuration "defaults": { - // will only search using recording mbid - "searchOrder": ["mbidrecording"] + // [!ref] will only search using recording mbid + "searchOrder": ["mbid"] } ``` @@ -454,12 +463,11 @@ If your Scrobble data contains a [Recording MBID](https://musicbrainz.org/doc/Re Will search Musicbrainz using any/all available text fields from your scrobble: Album, Artists(s), and Title. Does not leverage any existing MBIDs. -[Stage Configuration](/configuration/transforms#configuring-stages) example: - -```json5 -// ... +```json5 brackets +// [!link /Stage Configuration/ /configuration/transforms#configuring-stages] +// ...your Stage Configuration "defaults": { - // will only search using album, artist(s), and title + // [!ref] will only search using album, artist(s), and title "searchOrder": ["basic"] } ``` @@ -472,13 +480,12 @@ Will search Musicbrainz using any/all available text fields from your scrobble: This is the same as the above **Album, Artist, and Title Fields** search except that if the scrobble data contains a recording MBID it will use those as well as the plain text field. -[Stage Configuration](/configuration/transforms#configuring-stages) example: - -```json5 -// ... +```json5 brackets +// [!link /Stage Configuration/ /configuration/transforms#configuring-stages] +// ...your Stage Configuration "defaults": { - // will only search using album, artist(s), and title - "searchOrder": ["basicOrIds"] + // [!ref] will only search using album, artist(s), and title + "searchOrder": ["basicorids"] } ``` @@ -492,12 +499,11 @@ If your scrobble data contains only **one** artist string then Multi-scrobbler c Use `artist` with `searchOrder` and optionally specify the extraction mode with `searchArtistMethod`: -[Stage Configuration](/configuration/transforms#configuring-stages) example: - -```json5 -// ... +```json5 brackets +// [!link /Stage Configuration/ /configuration/transforms#configuring-stages] +// ...your Stage Configuration "defaults": { - // will only search using title/album + extracted artists + // [!ref] will only search using title/album + extracted artists "searchOrder": ["artist"], //searchArtistMethod: 'native' // optional } @@ -543,12 +549,11 @@ Use `artist` with `searchOrder` and optionally specify the extraction mode with - [Stage Configuration](/configuration/transforms#configuring-stages) example: - - ```json5 - // ... + ```json5 brackets + // [!link /Stage Configuration/ /configuration/transforms#configuring-stages] + // ...your Stage Configuration "defaults": { - // will only search using title/album + native extracted artists + // [!ref] will only search using title/album + native extracted artists "searchOrder": ["artist"], "searchArtistMethod": "native" } @@ -582,12 +587,11 @@ Use `artist` with `searchOrder` and optionally specify the extraction mode with ``` - [Stage Configuration](/configuration/transforms#configuring-stages) example: - - ```json5 - // ... + ```json5 brackets + // [!link /Stage Configuration/ /configuration/transforms#configuring-stages] + // ...your Stage Configuration "defaults": { - // will only search using title/album + naive extracted artists + // [!ref] will only search using title/album + naive extracted artists "searchOrder": ["artist"], "searchArtistMethod": "naive" } @@ -613,7 +617,7 @@ Each match returned by Rocksky can contain a list of matched external provider e ```json5 { // ... - "score": 90 // external provider entries must score 90 or higher to be considered + "score": 90 // [!ref] external provider entries must score 90 or higher to be considered } ``` @@ -626,7 +630,7 @@ You can configure whether a match is still considered valid when no entries are ```json5 { // ... - "allowNoMatch": false // consider a match invalid if not associated with an external provider + "allowNoMatch": false // [!ref] consider a match invalid if not associated with an external provider } ``` @@ -670,18 +674,23 @@ IDs are added to the title, album, and artists your Play already has. See [how ` Example -```json5 title="rocksky.json" +```json5 title="rocksky.json" brackets [ { + // [!link /Rocksky/ /configuration/clients/rocksky] + // [!link /File Config/ /configuration?configType=file#configuration-types] + // ...your Rocksky File Config "name": "Rocky", "configureAs": "client", "data": { /* ... */}, + // [!code focus:15] "options": { "playTransform": { "preCompare": [ { "type": "rocksky", "name": "MSDefault", + // [!code highlight:5] "title": false, "artists": false, "album": false, @@ -705,14 +714,16 @@ IDs are added to the title, album, and artists your Play already has. See [how ` Example -In a [Rocksky Client](/configuration/clients/rocksky) [File Config](/configuration?configType=file#configuration-types): - -```json5 title="rocksky.json" +```json5 title="rocksky.json" brackets [ { + // [!link /Rocksky/ /configuration/clients/rocksky] + // [!link /File Config/ /configuration?configType=file#configuration-types] + // ...your Rocksky File Config "name": "Rocky", "configureAs": "client", "data": { /* ... */}, + // [!code focus:10] "options": { "playTransform": { "preCompare": [ @@ -729,22 +740,19 @@ In a [Rocksky Client](/configuration/clients/rocksky) [File Config](/configurati Or using a [Rocksky Client](/configuration/clients/rocksky) with [ENV Config](/configuration?configType=env#configuration-types): -```yaml +```yaml title="docker-compose.yml" services: multi-scrobbler: image: foxxmd/multi-scrobbler environment: - # [!code highlight:3] - # searches with the searchOrder - # isrc => basic + # [!code highlight] [!ref] searches with the searchOrder `isrc` => `basic` - RS_PRESETS=default # ... your source ENVs go here # - ROCKSKY_ID=Rocky - ROCKSKY_HANDLE=alice.bsky.social - ROCKSKY_APP_PW=1234567 - # [!code highlight:2] - # applies Rocksky Stage to preTransform of Rocksky Client + # [!code highlight] [!ref] applies Rocksky Stage to preTransform of Rocksky Client - ROCKSKY_TRANSFORMS=rocksky @@ -765,24 +773,27 @@ If you do not want your scrobble data to be modified (artists/album/title) but s Example -In a [Jellyfin](/configuration/sources/jellyfin) [File Config](/configuration?configType=file#configuration-types): - -```json5 title="jellyfin.json" +```json5 title="jellyfin.json" brackets [ { + // [!link /Jellyfin/ /configuration/sources/jellyfin] + // [!link /File Config/ /configuration?configType=file#configuration-types] + // ...your Jellyfin File Config "name": "MyJellyfin", "data": { /* ... */}, + // [!code focus:15] "options": { "playTransform": { "preCompare": [ { "type": "rocksky", "name": "MSDefault", + // [!code highlight:4] "title": false, "artists": false, "album": false, "albumArtists": false, - "meta": true // only update scrobble metadata with MBIDs + "meta": true // [!code highlight] [!ref] only update scrobble metadata with MBIDs } ] } @@ -805,21 +816,25 @@ Use [**Per Component Overrides**](#rules-and-hooks) to specify different search Example -```json5 title="lastfm.json" +:::code-tabs +```json5 label="lastfm" title="lastfm.json" brackets [ { + // [!link /Last.fm/ /configuration/sources/lastfm-source] + // [!link /File Config/ /configuration?configType=file#configuration-types] + // ...your Last.fm File Config "name": "MyLFM", - "configureAs": "source" + "configureAs": "source", "data": { /* ... */}, + // [!code focus:12] "options": { "playTransform": { "preCompare": [ { "type": "rocksky", "name": "MSDefault", - "searchArtistMethod": "native" - // does not use any ID searches - "searchOrder": ["basic", "artist"] + "searchArtistMethod": "native", + "searchOrder": ["basic", "artist"] // [!code highlight] [!ref] does not use any ID searches } ] } @@ -827,20 +842,22 @@ Use [**Per Component Overrides**](#rules-and-hooks) to specify different search } ] ``` - -```json5 title="spotify.json" +```json5 label="spotify" title="spotify.json" brackets [ { + // [!link /Spotify/ /configuration/sources/spotify] + // [!link /File Config/ /configuration?configType=file#configuration-types] + // ...your Spotify File Config "name": "MySpotify", "data": { /* ... */}, + // [!code focus:11] "options": { "playTransform": { "preCompare": [ { "type": "rocksky", "name": "MSDefault", - // use ISRC as first search, then tries searching with title/artist/album/mbid - "searchOrder": ["isrc", "basicorids"] + "searchOrder": ["isrc", "basicorids"] // [!code highlight] [!ref] use ISRC as first search, then tries searching with title/artist/album/mbid } ] } @@ -848,6 +865,7 @@ Use [**Per Component Overrides**](#rules-and-hooks) to specify different search } ] ``` +::: @@ -869,13 +887,15 @@ The [Discord](/configuration/clients/discord) Client can fetch album art images ``` - In your [Discord](/configuration/clients/discord) [File Config](/configuration?configType=file#configuration-types): - - ```json5 title="discord.json" + ```json5 title="discord.json" brackets [ { + // [!link /Discord/ /configuration/clients/discord] + // [!link /File Config/ /configuration?configType=file#configuration-types] + // ...your Discord File Config "name": "MS", "data": { /* ... */}, + // [!code focus:10] "options": { "playTransform": { "preCompare": [ @@ -893,10 +913,11 @@ The [Discord](/configuration/clients/discord) Client can fetch album art images Your [AIO Config](/configuration?configType=aio#configuration-types): - ```json5 title="config.json" + ```json5 title="config.json" brackets { - // ... - "sources": [ + // [!link /AIO Config/ /configuration?configType=aio#configuration-types] + // ...your AIO Config + "clients": [ { "name": "MS", "type": "discord", diff --git a/docsite/docs/configuration/transforms/spotify.mdx b/docsite/docs/configuration/transforms/spotify.mdx index df86105f..1b854660 100644 --- a/docsite/docs/configuration/transforms/spotify.mdx +++ b/docsite/docs/configuration/transforms/spotify.mdx @@ -35,13 +35,15 @@ Set up [Valkey Caching](/configuration?cachedThings=metadata#caching) to cache S This Stage uses Spotify's API to search/lookup the Spotify catalog. This does **not** require a user to log in -- only an app `clientId`/`clientSecret` is needed. [Create a Spotify application](https://developer.spotify.com/documentation/web-api/concepts/apps) if you don't already have one (the same application used for the [Spotify Source](/configuration/sources/spotify) can be reused here). -```json5 title="config.json" +```json5 title="config.json" brackets { - // ... + // [!link /AIO Config/ /configuration?configType=aio#configuration-types] + // ...your AIO Config "transformers": [ { "type": "spotify", "name": "MySpotify", + // [!code highlight:4] "data": { "clientId": "787c921a2a2ab42320831aba0c8f2fc2", "clientSecret": "ec42e09d5ae0ee0f0816ca151008412a" @@ -57,7 +59,7 @@ This Stage uses Spotify's API to search/lookup the Spotify catalog. This does ** `rate` can be used to configure how many requests are made to the Spotify API, defined by **max number of requests** within **timespan of N seconds** (default `10 req/1s`): -```json5 +```json5 brackets { "type": "spotify", "name": "MySpotify", @@ -81,9 +83,10 @@ All of the properties found in [**Matching with Spotify**](#matching-with-spotif These are all also **optional** so you can use the Spotify Transform stage without configuring any of this. -```json5 title="config.json" +```json5 title="config.json" brackets { - // ... + // [!link /AIO Config/ /configuration?configType=aio#configuration-types] + // ...your AIO Config "transformers": [ { "type": "spotify", @@ -106,11 +109,15 @@ These are all also **optional** so you can use the Spotify Transform stage witho [Add your Stage](/configuration/transforms/#stage) to a Source or Client by specifying it in a [Hook](/configuration/transforms/#hook): -```json5 title="subsonic.json" +```json5 title="subsonic.json" brackets [ { + // [!link /Subsonic/ /configuration/sources/subsonic] + // [!link /File Config/ /configuration?configType=file#configuration-types] + // ...your Subsonic File Config "name": "MySubsonic", "data": { /* ... */}, + // [!code focus:10] "options": { "playTransform": { "preCompare": [ @@ -165,10 +172,8 @@ services: # ... - SPOTIFY_CLIENT_ID=787c921a2a2ab42320831aba0c8f2fc2 - SPOTIFY_CLIENT_SECRET=ec42e09d5ae0ee0f0816ca151008412a - # [!code highlight:3] - # only runs transform if album, artists, title, duration, or art are missing in data - # and only searches by isrc - - SPOTIFY_TRANSFORMS_PRESETS=missingArt,missingFields,searchIsrc + # [!code highlight] [!ref] only runs transform if album, artists, title, duration, or art are missing in data and only searches by isrc + - SPOTIFY_TRANSFORM_PRESETS=missingArt,missingFields,searchIsrc # ... ``` @@ -185,26 +190,23 @@ The `*` stands for the prefix used for each Source/Client's ENV keys. Refer to t Example Full Docker Deploy with ENV Configuration -```yaml +```yaml title="docker-compose.yml" services: multi-scrobbler: image: foxxmd/multi-scrobbler container_name: multi-scrobbler environment: - # [!code highlight:5] - # only runs transform if art or fields are missing in data - # and only searches by isrc - - SPOTIFY_TRANSFORMS_PRESETS=missingArt,missingFields,searchIsrc + # [!code highlight:3] [!ref] only runs transform if art or fields are missing in data and only searches by isrc + - SPOTIFY_TRANSFORM_PRESETS=missingArt,missingFields,searchIsrc - SPOTIFY_CLIENT_ID=787c921a2a2ab42320831aba0c8f2fc2 - SPOTIFY_CLIENT_SECRET=ec42e09d5ae0ee0f0816ca151008412a - JELLYFIN_URL=192.168.0.110:8096 - JELLYFIN_APIKEY=c9fae8756fbf481ebd9c5bb56b - JELLYFIN_USER=MyUser - # [!code highlight:2] - # applies spotify Stage to preCompare of Jellyfin source + # [!code highlight] [!ref] applies spotify Stage to preCompare of Jellyfin source - JELLYFIN_TRANSFORMS=spotify - # maloja receives enhanced scrobble from Jellyfin + # [!ref] maloja receives enhanced scrobble from Jellyfin - MALOJA_URL=http://192.168.0.100:42010 - MALOJA_API_KEY=myApiKey @@ -254,8 +256,9 @@ Define which data types are required using: Multi-scrobbler searches for a Spotify match using, in order, up to two methods. Use `searchOrder` to control which methods run and in what order: -```json5 -// ... +```json5 brackets +// [!link /Stage Configuration/ /configuration/transforms#configuring-stages] +// ...your Stage Configuration "defaults": { "searchOrder": ["isrc", "basic"] } @@ -318,7 +321,7 @@ A failed Stage never modifies your play data unless you eplicitly configure it t "data": {/* */}, "defaults": { // ... - "market": "US" + "market": "US" // [!code highlight] } } ``` @@ -338,7 +341,7 @@ A failed Stage never modifies your play data unless you eplicitly configure it t "data": {/* */}, "defaults": { // ... - "locale": "en_US" + "locale": "en_US" // [!code highlight] } } @@ -380,14 +383,15 @@ Each candidate is scored between `0` and `1` based on how similar its title/arti ```json5 { // ... - "score": 0.6 // matches must score 0.6 or higher to be considered + "score": 0.6 // [!ref] matches must score 0.6 or higher to be considered } ``` You can bias which of title/artist/album contributes most to a candidate's score with `titleWeight`, `artistWeight`, and `albumWeight` (defaults are `0.4`/`0.3`/`0.3`, respectively): -```json5 -// ... +```json5 brackets +// [!link /Stage Configuration/ /configuration/transforms#configuring-stages] +// ...your Stage Configuration "defaults": { "titleWeight": 0.4, "artistWeight": 0.3, @@ -399,8 +403,9 @@ You can bias which of title/artist/album contributes most to a candidate's score Spotify catalogs many recordings across multiple compilation albums (Greatest Hits, movie soundtracks that reuse a song, etc...) in addition to their "proper" studio album. If you'd prefer matches to avoid compilation albums when a better alternative exists, enable `deprioritizeCompilations`: -```json5 -// ... +```json5 brackets +// [!link /Stage Configuration/ /configuration/transforms#configuring-stages] +// ...your Stage Configuration "defaults": { "deprioritizeCompilations": true } @@ -428,17 +433,22 @@ With these Rules IDs are added to the title, album, and artists your Play alread Example -```json5 title="subsonic.json" +```json5 title="subsonic.json" brackets [ { + // [!link /Subsonic/ /configuration/sources/subsonic] + // [!link /File Config/ /configuration?configType=file#configuration-types] + // ...your Subsonic File Config "name": "MySubsonic", "data": { /* ... */}, + // [!code focus:15] "options": { "playTransform": { "preCompare": [ { "type": "spotify", "name": "MySpotify", + // [!code highlight:5] "title": false, "artists": false, "album": false, diff --git a/docsite/docs/configuration/transforms/user.mdx b/docsite/docs/configuration/transforms/user.mdx index 390bb4ec..0ee0f492 100644 --- a/docsite/docs/configuration/transforms/user.mdx +++ b/docsite/docs/configuration/transforms/user.mdx @@ -21,9 +21,10 @@ All [Stage Configuration](/configuration/transforms#configuring-stages) is done Example -```json5 title="config.json" +```json5 title="config.json" brackets { - // ... + // [!link /AIO Config/ /configuration?configType=aio#configuration-types] + // ...your AIO Config "transformers": [ { "type": "user", @@ -53,21 +54,22 @@ This can be used by omitting the `name` in your [modification Stage](/configurat Example -In a [Subsonic](/configuration/sources/subsonic) [File Config](/configuration?configType=file#configuration-types): - -```json5 title="subsonic.json" +```json5 title="subsonic.json" brackets [ { + // [!link /Subsonic/ /configuration/sources/subsonic] + // [!link /File Config/ /configuration?configType=file#configuration-types] + // ...your Subsonic File Config "name": "MySubsonic", "data": { /* ... */}, + // [!code focus:12] "options": { "playTransform": { "preCompare": [ { "type": "user", "title": [ - // removes badterm from track title - "badterm" + "badterm" // [!ref] removes badterm from track title ] } ] @@ -100,8 +102,8 @@ Or it may be an object that specifies what to match (using either plain string o ```json5 { - "search": "anotherBadTerm", // match all instances of 'anotherBadTerm' - "replace": "goodTerm" // replace with the string 'goodTerm' + "search": "anotherBadTerm", // [!ref] match all instances of 'anotherBadTerm' + "replace": "goodTerm" // [!ref] replace with the string 'goodTerm' } ``` @@ -111,8 +113,8 @@ Or it may be an object that specifies what to match (using either plain string o ```json5 { - "search": "/^\(\w+.com)/i", // matches any string that starts with EX '(YourMusic.com)' - "replace": "[MySite.com]" // replace with the string '[MySite.com]' + "search": "/^\(\w+.com)/i", // [!ref] matches any string that starts with EX '(YourMusic.com)' + "replace": "[MySite.com]" // [!ref] replace with the string '[MySite.com]' } ``` @@ -126,24 +128,27 @@ The `replace` property uses javascript's [`replace()` function and so can use an Each [Rule](/configuration/transforms#stage-rules) must be an array of [search-and-replace expressions](#search-and-replace-expression): -```json5 title="lastfm.json" +```json5 title="lastfm.json" brackets [ { + // [!link /Last.fm/ /configuration/sources/lastfm-source] + // [!link /File Config/ /configuration?configType=file#configuration-types] + // ...your Last.fm File Config "name": "myLastFm", "configureAs": "source", "data": { // ... }, + // [!code focus:16] "options": { "playTransform": { "preCompare": [ { "type": "user", "title": [ - // removes "badTerm" from title - "badTerm", + "badTerm", // [!ref] removes "badTerm" from title + // [!ref] removes "fooTerm" from title and replaces it with "barTerm" { - // removes "fooTerm" from title and replaces it with "barTerm" "search": "fooTerm", "replace": "barTerm" } @@ -172,24 +177,21 @@ Using `when` for [Conditional Modification](/configuration/transforms#conditiona Example -```json5 -// On search-replace in title... -// IF artist matches "Elephant Gym" -// THEN Run regex search-replace ELSE skip this rule -// -// Run live|remastered regex remove on title +```json5 brackets { "type": "user", "title": [ { "search": "/\\s\\-\\s滾石40\\s滾石撞樂隊\\s40團拚經典(.+)$/i", "replace": "", + // [!callout /when/] Only run this search-replace if artist matches `Elephant Gym`, else skip it "when": [ { "artist": "/Elephant Gym/" } ] }, + // [!callout] Always run live|remastered regex remove on title "/(\\s\\-\\s|\\s)(feat\\.(.+)|live|remastered(.+))$/i" ], } @@ -305,7 +307,7 @@ Removes the phrase `(Album Version)` from the Title of a Play "type": "user", "artists": [ { - "search": "(.*?)(?\\s*\\/\\s*)(.*$)", + "search": "(.*?)(?\\s*\\/\\s*)(.*$)", // [!callout /delim/] replace the contents of this group with the delimiter for your use case "replace": "$1" } ] -- 2.54.0