From b98756a2629b26a9256a693397b37a0fb714afac Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Mon, 10 Aug 2026 14:21:59 +0000 Subject: [PATCH 1/2] fix: Fix incorrect prefix not being used for env key collection #668 --- src/backend/scrobblers/ScrobbleClients.ts | 11 +++++++---- src/backend/sources/ScrobbleSources.ts | 12 ++++++------ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/backend/scrobblers/ScrobbleClients.ts b/src/backend/scrobblers/ScrobbleClients.ts index 7a6f32b0..ef721e71 100644 --- a/src/backend/scrobblers/ScrobbleClients.ts +++ b/src/backend/scrobblers/ScrobbleClients.ts @@ -181,7 +181,10 @@ export default class ScrobbleClients { } } - const clientKeys = envKeys.filter(x => x.includes(clientUpper)); + const envSchema = await getClientEnvSchema(clientType); + const configTypeUpper = envSchema.prefix.toUpperCase(); + + const clientKeys = envKeys.filter(x => x.includes(configTypeUpper)); if (clientKeys.length > 0) { clientUnparsedConfigs.push({ config: pick(process.env, ...clientKeys), @@ -198,12 +201,12 @@ export default class ScrobbleClients { switch (entry.source) { case 'env': { const envSchema = await getClientEnvSchema(clientType); - const primitiveSchema = generateCommonComponentEnvConfigSchema(envSchema.prefix.toUpperCase()); + const primitiveSchema = generateCommonComponentEnvConfigSchema(configTypeUpper); const parsed = primitiveSchema.parse(entry.config); - const primitives: CommonConfigPrimitives = commonComponentEnvConfigToConfigPrimitives(envSchema.prefix.toUpperCase(), parsed); + const primitives: CommonConfigPrimitives = commonComponentEnvConfigToConfigPrimitives(configTypeUpper, parsed); const parsedEnvConfigValues = envSchema.env.parse(entry.config); const { data = {}, options = {}, ...rest } = envSchema.toConfig(parsedEnvConfigValues); - const transformOptions = transformPresetEnv(envSchema.prefix.toUpperCase()); + const transformOptions = transformPresetEnv(configTypeUpper); parsedConfig = { name: `${clientType} - ${entry.source}${entry.pos !== '' ? ` - ${entry.pos}` : ''} `, ...primitives, diff --git a/src/backend/sources/ScrobbleSources.ts b/src/backend/sources/ScrobbleSources.ts index 5999a882..e5fbe0c7 100644 --- a/src/backend/sources/ScrobbleSources.ts +++ b/src/backend/sources/ScrobbleSources.ts @@ -178,8 +178,6 @@ export default class ScrobbleSources { let sourceUnparsedConfigs = unparsedConfigs.filter(x => x.type === configType); - const configTypeUpper = configType.toUpperCase(); - let rawConfigs; try { rawConfigs = await readJson(`${this.internalConfig.configDir}/${configType}.json`, { throwOnNotFound: false, logger: childLogger(this.logger, `${configType} Secrets`) }); @@ -202,6 +200,9 @@ export default class ScrobbleSources { } } + const envSchema = await getSourceEnvSchema(configType); + const configTypeUpper = envSchema.prefix.toUpperCase(); + const configKeys = envKeys.filter(x => x.includes(configTypeUpper)); if (configKeys.length > 0) { sourceUnparsedConfigs.push({ @@ -218,13 +219,12 @@ export default class ScrobbleSources { try { switch (entry.source) { case 'env': { - const envSchema = await getSourceEnvSchema(configType); - const primitiveSchema = generateCommonComponentEnvConfigSchema(envSchema.prefix.toUpperCase()); + const primitiveSchema = generateCommonComponentEnvConfigSchema(configTypeUpper); const parsed = primitiveSchema.parse(entry.config); - const primitives: CommonConfigPrimitives = commonComponentEnvConfigToConfigPrimitives(envSchema.prefix.toUpperCase(), parsed); + const primitives: CommonConfigPrimitives = commonComponentEnvConfigToConfigPrimitives(configTypeUpper, parsed); const parsedEnvConfigValues = envSchema.env.parse(entry.config); const { data = {}, options = {}, ...rest } = envSchema.toConfig(parsedEnvConfigValues); - const transformOptions = transformPresetEnv(envSchema.prefix.toUpperCase()); + const transformOptions = transformPresetEnv(configTypeUpper); parsedConfig = { name: `${configType} - ${entry.source}${entry.pos !== '' ? ` - ${entry.pos}` : ''} `, ...primitives, -- 2.51.2 From fe4e625824049a1c27d5b17ba3262b64225443ca Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Mon, 10 Aug 2026 16:21:39 +0000 Subject: [PATCH 2/2] docs: Improve scenario guidance for lfm/lz endpoints --- .../sources/_env_configs/_endpointlfm.md | 16 +-- .../sources/_env_configs/_endpointlz.md | 16 +-- .../configuration/sources/lastfm-endpoint.mdx | 92 +++++++++++++- .../sources/listenbrainz-endpoint.mdx | 119 ++++++++++++++---- .../config/source/endpointlfm.ts | 2 +- .../config/source/endpointlz.ts | 2 +- 6 files changed, 205 insertions(+), 42 deletions(-) diff --git a/docsite/docs/configuration/sources/_env_configs/_endpointlfm.md b/docsite/docs/configuration/sources/_env_configs/_endpointlfm.md index b885cf29..25014f6b 100644 --- a/docsite/docs/configuration/sources/_env_configs/_endpointlfm.md +++ b/docsite/docs/configuration/sources/_env_configs/_endpointlfm.md @@ -1,8 +1,8 @@ -| Environmental Variable | Type | Default | Description | -| ---------------------- | ------- | ----------------- | ------------------------------------------------------------------------ | -| _**`LFM_ID`**_ | string | | A globally unique ID EX `myComponentId` | -| `LFM_NAME` | string | Value of `LFM_ID` | A vanity name EX `My Cool Component` | -| `LFM_ENABLE` | boolean | true | Should this component be used? | -| `LFM_SLUG` | string | | The URL ending that should be used to identify scrobbles for this source | -| `LFM_USERNAME` | string | | A fake username to differentiate LFM Endpoint Sources | -| `LFM_API_KEY` | string | | A fake api key to differentiate LFM Endpoint Sources | \ No newline at end of file +| Environmental Variable | Type | Default | Description | +| ---------------------- | ------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| _**`LFM_ID`**_ | string | | A globally unique ID EX `myComponentId` | +| `LFM_NAME` | string | Value of `LFM_ID` | A vanity name EX `My Cool Component` | +| `LFM_ENABLE` | boolean | true | Should this component be used? | +| `LFM_SLUG` | string | | When using **multiple sources without apikey/user**, or **not** using the standard base URL, this is the URL base path that should be used to identify scrobbles for this source | +| `LFM_USERNAME` | string | | A fake username to differentiate LFM Endpoint Sources | +| `LFM_API_KEY` | string | | A fake api key to differentiate LFM Endpoint Sources | \ No newline at end of file diff --git a/docsite/docs/configuration/sources/_env_configs/_endpointlz.md b/docsite/docs/configuration/sources/_env_configs/_endpointlz.md index 8cc4681d..4ddca042 100644 --- a/docsite/docs/configuration/sources/_env_configs/_endpointlz.md +++ b/docsite/docs/configuration/sources/_env_configs/_endpointlz.md @@ -1,8 +1,8 @@ -| Environmental Variable | Type | Default | Description | -| ---------------------- | ------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------ | -| _**`LZE_ID`**_ | string | | A globally unique ID EX `myComponentId` | -| `LZE_NAME` | string | Value of `LZE_ID` | A vanity name EX `My Cool Component` | -| `LZE_ENABLE` | boolean | true | Should this component be used? | -| `LZE_SLUG` | string | | The URL ending that should be used to identify scrobbles for this source | -| `LZE_TOKEN` | string | | If an LZ submission request contains this token in the Authorization Header it will be used to match the submission with this Source | -| `LZE_USERNAME` | string | | The listenbrainz "username" to associate with this Source | \ No newline at end of file +| Environmental Variable | Type | Default | Description | +| ---------------------- | ------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| _**`LZE_ID`**_ | string | | A globally unique ID EX `myComponentId` | +| `LZE_NAME` | string | Value of `LZE_ID` | A vanity name EX `My Cool Component` | +| `LZE_ENABLE` | boolean | true | Should this component be used? | +| `LZE_SLUG` | string | | When using **multiple sources without tokens**, or **not** using a standard base URL, this is the URL base path that should be used to identify scrobbles for this source | +| `LZE_TOKEN` | string | | If an LZ submission request contains this token in the Authorization Header it will be used to match the submission with this Source | +| `LZE_USERNAME` | string | | The listenbrainz "username" to associate with this 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 index 867f4c67..7e998e4a 100644 --- a/docsite/docs/configuration/sources/lastfm-endpoint.mdx +++ b/docsite/docs/configuration/sources/lastfm-endpoint.mdx @@ -29,7 +29,67 @@ If your application has the option to scrobble using Listenbrainz then use the [ ## Setup -### URL +:::tip[Scenario Picker] + +Use **one** of the scenarios below based on how on your service(s) (things "scrobbling" to Multi-Scrobbler using this Source) will need to interact with MS. + + + + > You have **one** service, like Panoscrobbler, that will scrobble to Multi-Scrobbler. + + Configure **one** Last.fm Endpoint Source. Use the [standard base URL](#standard-base-url) to configure your service and [apikey/user](#authentication), if necessary. Use the same apikey/user in MS as you do in your service. + + + + > You may have **more than one** service that will scrobble to Multi-Scrobbler, like ArchiveTune and Panoscrobbler. + > + > All of these services will scrobble for the same user (you) and all of the scrobbles should go to the same [Clients](/configuration/clients). + + Configure **one** Lastfm Endpoint Source. Use the [standard base URL](#standard-base-url) to configure your service and configure [apikey/user](#authentication) in MS, if necessary. Use the same apikey/user for all your services as you configure in MS. + + + > You have **more than one** service that will scrobble to Multi-Scrobbler, like ArchiveTune and Panoscrobbler. + > + > Some of these services should scrobble for different users, scrobble to different [Clients](/configuration/clients), or generally be treated differently (such as for transforms). + > + > **All** of these services can accept a [**apikey/user**](#authentication) to authenticate a Last.fm user/account. + + Configure one Last.fm Endpoint Sources **per service that should be treated differently**. That is, if multiple services are all "the same user" then you only need one Source for that entire group of services. + + For each Lastfm Endpoint Source, configure a different [apikey/user](#authentication). Use that same apikey/user when configuring Last.fm for the associated service. + + All Last.fm Endpoint Sources should use the same [standard base URL](#standard-base-url). + + + > You have **more than one** service that will scrobble to Multi-Scrobbler, like ArchiveTune and Panoscrobbler. + > + > Some of these services should scrobble for different users, scrobble to different [Clients](/configuration/clients), or generally be treated differently (such as for transforms). + > + > All, or some, of these services **cannot** accept an [apikey/user](#authentication) to authenticate a Last.fm user/account. + + **This is not a common scenario** but if it fits your usecase then use these guidelines to differentiate your services: + + Configure one Last.fm Endpoint Sources **per service that should be treated differently**. That is, if multiple services are all "the same user" then you only need one Source for that entire group of services. + + * For any groups of services that **can use [apikey/user](#authentication)**, use the instructions for [Many Services, Many Users, Can use LFM Account](./?scenario=multiple-tokens#setup) + * For any groups of services that **cannot accept** an apikey/user... + * You can still configure a Source **without an [apikey/user](#authentication)**. Any services not using an account will use this "default" Source. + * If multiple groups cannot use an apikey/user, or the groups cannot use the [standard base URL](#standard-base-url), then use different [base url **Slugs**](#multiple-sources). + + + > You have **one** service, like PanoScrobbler, that will scrobble to Multi-Scrobbler. + > + > You have already tried the [**One Service**](./?scenario=single#setup) scenario with the [standard base URL](#standard-base-url) but it is not working. + + **This is not a common scenario.** Check the [**Troubleshooting URL**](#troubleshooting-url) section before trying this. + + At this point, you can try to use a different [base url **Slug**](#multiple-sources). Your service must be able to set a "fully custom" URL for Last.fm IE it must allow you to set more than just a domain/host. + + + +::: + +### Standard Base URL When setting up your Last.fm client to communicate with Multi-Scrobbler replace the Last.fm domain with your Multi-Scrobbler domain: @@ -49,12 +109,14 @@ Currently, Multi-Scrobbler only supports the [**Mobile Application**](https://ww -#### Multiple Sources +### Multiple Sources -If you have **more than one** Lastfm Endpoint Source then you should configure username/apiKey for MS, per Source. This enables MS to differentiate scrobbles for each Source. +If you need configure **more than one** Lastfm Endpoint Source then you should configure username/apiKey for MS, per Source. This enables MS to differentiate scrobbles for each Source. Use the same **username** and/or **API Key** you configure with MS when setting up your Last.fm Client. These values can be anything you want, as long as they match between MS and your client. Password can be anything and is not checked. +However, if you cannot configure username/apiKeys for your scenario you may also use a URL **Slug** to tell MS which Source belongs to a specific user. **This is not a common scenario** and should only be used if username/apiKey configuration is not sufficient or not possible. + If you cannot use different username/apiKey per Source (or Last.fm Client) you can still differentiate Sources by using a different **url base (slug)** for Last.fm communication. @@ -72,6 +134,30 @@ The above url base is equivalent to making calls to `http://yourMSDomain/2.0/` +## Troubleshooting URL + +If you think your service should be able to use the [standard base URL](#standard-base-url) but it is not working follow these steps before trying the [the base URL Slug](#multiple-sources). + +#### Verify URL Format + +Some service may require `http/https` in the URL format (`http://myMSDomainOrIp:9078`) while others may not (`myDomainOrIp:9078`). Check the docs for your service to see if they specify this or have an example. Try both versions, if possible, to see if one works. + + + +Some services that can scrobble to a custom Last.fm URL may require the URL to have 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. + + + +#### Is MS is Reachable? + +Verify that MS can be accessed by your service, or from the same host as the service is running. See the [**FAQ**](/FAQ/#ingress-connection) for guidance on troubleshooting ingress connection issues. + +#### Service Logging + +If you service has accessible logging, check to see if it outputs the URL it is trying to reach when authenticating the Last.fm apiKey/user, or trying to make a scrobble request. + +If you decide to report this issue, please include this logging in your issue. + ## Configuration diff --git a/docsite/docs/configuration/sources/listenbrainz-endpoint.mdx b/docsite/docs/configuration/sources/listenbrainz-endpoint.mdx index 1aa54eec..24e31a06 100644 --- a/docsite/docs/configuration/sources/listenbrainz-endpoint.mdx +++ b/docsite/docs/configuration/sources/listenbrainz-endpoint.mdx @@ -23,16 +23,76 @@ If your service/player has a "Scrobble to Listenbrainz" feature and there is an ## Setup -### URL +:::tip[Scenario Picker] -In general, you should use one of the following as the **base URL** when configuring your application to scrobble to multi-scrobbler: +Use **one** of the scenarios below based on how on your service(s) (things "scrobbling" to Multi-Scrobbler using this Source) will need to interact with MS. + + + + > You have **one** service, like Navidrome, that will scrobble to Multi-Scrobbler. + + Configure **one** Listenbrainz Endpoint Source. Use the [standard base URL](#standard-base-url) to configure your service and set a [Token](#token), if necessary. Use the same **Token** in MS as you do in your service. + + + + > You may have **more than one** service that will scrobble to Multi-Scrobbler, like Navidrome and Panoscrobbler. + > + > All of these services will scrobble for the same user (you) and all of the scrobbles should go to the same [Clients](/configuration/clients). + + Configure **one** Listenbrainz Endpoint Source. Use the [standard base URL](#standard-base-url) to configure your service and configure a [Token](#token) in MS, if necessary. Use the same token for all your services as you configure in MS. + + + > You have **more than one** service that will scrobble to Multi-Scrobbler, like Navidrome and Panoscrobbler. + > + > Some of these services should scrobble for different users, scrobble to different [Clients](/configuration/clients), or generally be treated differently (such as for transforms). + > + > **All** of these services can accept a **Token** to authenticate a Listenbrainz user/account. + + Configure one Listenbrainz Endpoint Sources **per service that should be treated differently**. That is, if multiple services are all "the same user" then you only need one Source for that entire group of services. + + For each Listenbrainz Endpoint Source, configure a different [**Token**](#token). Use that same token when configuring Listenbrainz for the associated service. + + All Listenbrainz Endpoint Sources should use the same [standard base URL](#standard-base-url). + + + > You have **more than one** service that will scrobble to Multi-Scrobbler, like Navidrome and Panoscrobbler. + > + > Some of these services should scrobble for different users, scrobble to different [Clients](/configuration/clients), or generally be treated differently (such as for transforms). + > + > All, or some, of these services **cannot** accept a **Token** to authenticate a Listenbrainz user/account. + + **This is not a common scenario** but if it fits your usecase then use these guidelines to differentiate your services: + + Configure one Listenbrainz Endpoint Sources **per service that should be treated differently**. That is, if multiple services are all "the same user" then you only need one Source for that entire group of services. + + * For any groups of services that **can accept** a token, use the instructions for [Many Services, Many Users, Can use Tokens](./?scenario=multiple-tokens#setup) + * For any groups of services that **cannot accept** a token... + * You can still configure a Source **without a [Token](#token)**. Any services not using a token will use this "default" Source. + * If multiple groups cannot use a token, or the groups cannot use the [standard base URL](#standard-base-url), then use different [base url **Slugs**](#multiple-sources). + + + > You have **one** service, like Navidrome, that will scrobble to Multi-Scrobbler. + > + > You have already tried the [**One Service**](./?scenario=single#setup) scenario with both [standard base URL](#standard-base-url) and neither is working. + + **This is not a common scenario.** Check the [**Troubleshooting URL**](#troubleshooting-url) section before trying this. + + At this point, you can try to use a different [base url **Slug**](#multiple-sources). Your service must be able to set a "fully custom" URL for Listenbrainz IE it must allow you to set more than just a domain/host. + + + +::: + +### Standard Base URL + +In most scenarios you should use one of the following as the **base URL** when configuring your service to scrobble to multi-scrobbler: * `http://myMultiScrobblerIP:9078` * `http://myMultiScrobblerIP:9078/1/` -If you are not using a [slug for multiple endpoints](#multiple-endpoints) then multi-scrobbler will accept Listenbrainz scrobbles at +Multi-scrobbler will accept Listenbrainz scrobbles at ``` http://myMultiScrobblerIP:9078/1/submit-listens @@ -42,41 +102,34 @@ which is the **standard** Listenbrainz server path for scrobbling. - - -Some services that can scrobble to a custom Listenbrainz URL may require the URL to have 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. +Most Listenbrainz clients require an Authentication Token to be provided during setup. This value can be anything you want but ensure you use the same value for `token` (ENV `LZE_TOKEN`) in your configuration for this Listenbrainz Endpoint Source. -### Multiple Endpoints +If your service does not require a token, or does not let you set one, then do not configure it for this Source either. -If you are setting up multiple [Listenbrainz (Endpoint) Sources](/configuration/sources/listenbrainz-endpoint) MS can differentiate scrobbles based on [Token](#token) so that you can use the same endpoint for many users. +### Multiple Sources -However, if you cannot configure multiple tokens for your scenario you may also use a URL **Slug** to tell MS which Source belongs to a specific user. +If you need configure **more than one** Listenbrainz Endpoint Source then MS can differentiate scrobbles based on [Token](#token) so that you can use the [**same, standard base URL**](#standard-base-url) for many users. -
+However, if you cannot configure multiple tokens for your scenario you may also use a URL **Slug** to tell MS which Source belongs to a specific user. **This is not a common scenario** and should only be used if **Token** configuration is not sufficient or not possible. -Using a Slug + -When a **slug** is defined in ENV/File config MS will listen for listenbrainz routes using the slug as a the route path. Example: +When a **slug** is defined in ENV/File config MS will listen for listenbrainz `submit-listens` requests using the slug as the base route path. Example: ``` LZE_SLUG=foobar ``` -MS will accept calls to +MS will accept requests for `submit-listens` (scrobble submissions and now playing) at ``` http://myMultiScrobblerIP:9078/api/listenbrainz/foobar +http://myMultiScrobblerIP:9078/api/listenbrainz/foobar/submit-listens ``` -for scrobble submission. - -
+ ## Setup for Popular Apps @@ -100,13 +153,37 @@ Set the [**ListenBrainz.BaseURL**](https://www.navidrome.org/docs/usage/configur * In the ListenBrainz Scrobbler configuration screen: * User Token is the [Token](#token) you configured * Toggle **Show advanced settings** - * Base URL is the [URL](#url) you can configured, or the equivalent of `http://myMultiScrobblerIP:9078` for your MS instance + * Base URL is the [URL](#standard-base-url) you can configured, or the equivalent of `http://myMultiScrobblerIP:9078` for your MS instance * Save the configuration and you are ready to scrobble To troubleshoot any errors, and assuming you are using Home Assistant, view the Logs in the Music Assistant Add-ons. Please include these logs in any reported issue. +## Troubleshooting URL + +If you think your service should be able to use the [standard base URL](#standard-base-url) but it is not working follow these steps before trying the [the base URL Slug](#multiple-sources). + +#### Verify URL Format + +Some service may require `http/https` in the URL format (`http://myMSDomainOrIp:9078`) while others may not (`myDomainOrIp:9078`). Check the docs for your service to see if they specify this or have an example. Try both versions, if possible, to see if one works. + + + +Some services that can scrobble to a custom Listenbrainz URL may require the URL to have 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. + + + +#### Is MS is Reachable? + +Verify that MS can be accessed by your service, or from the same host as the service is running. See the [**FAQ**](/FAQ/#ingress-connection) for guidance on troubleshooting ingress connection issues. + +#### Service Logging + +If you service has accessible logging, check to see if it outputs the URL it is trying to reach when authenticating the Listenbrainz account/user, or trying to make a scrobble request. + +If you decide to report this issue, please include this logging in your issue. + ## Configuration diff --git a/src/backend/common/infrastructure/config/source/endpointlfm.ts b/src/backend/common/infrastructure/config/source/endpointlfm.ts index 84e23fd8..b236e92a 100644 --- a/src/backend/common/infrastructure/config/source/endpointlfm.ts +++ b/src/backend/common/infrastructure/config/source/endpointlfm.ts @@ -16,7 +16,7 @@ export const lastFmEndpointDataSchema = z.object({ * If no slug is found from an extension's incoming webhook event the first Last.fm source without a slug will be used * */ slug: z.string().optional().meta({ - description: "The URL ending that should be used to identify scrobbles for this source" + description: "When using **multiple sources without apikey/user**, or **not** using the standard base URL, this is the URL base path that should be used to identify scrobbles for this source" }), username: z.string().optional().meta({ description: 'A fake username to differentiate LFM Endpoint Sources' diff --git a/src/backend/common/infrastructure/config/source/endpointlz.ts b/src/backend/common/infrastructure/config/source/endpointlz.ts index 58d1dfab..1a1203ce 100644 --- a/src/backend/common/infrastructure/config/source/endpointlz.ts +++ b/src/backend/common/infrastructure/config/source/endpointlz.ts @@ -16,7 +16,7 @@ export const listenbrainzEndpointDataSchema = z.object({ * If no slug is found from an extension's incoming webhook event the first Listenbrainz source without a slug will be used * */ slug: z.string().optional().meta({ - description: "The URL ending that should be used to identify scrobbles for this source" + description: "When using **multiple sources without tokens**, or **not** using a standard base URL, this is the URL base path that should be used to identify scrobbles for this source" }), /** -- 2.51.2