diff --git a/src/backend/common/infrastructure/config/source/koito.ts b/src/backend/common/infrastructure/config/source/koito.ts index 3c714b19..284df112 100644 --- a/src/backend/common/infrastructure/config/source/koito.ts +++ b/src/backend/common/infrastructure/config/source/koito.ts @@ -50,6 +50,8 @@ export type KoitoSourceConfig = z.infer; export const koitoSourceAIOConfigSchema = z.object({ ...koitoSourceConfigSchema.shape, + // can be optional since it always appears in a source array + configureAs: koitoSourceConfigSchema.shape.configureAs.optional(), type: z.literal('koito'), }).meta({title: "Koito"}); diff --git a/src/backend/common/infrastructure/config/source/lastfm.ts b/src/backend/common/infrastructure/config/source/lastfm.ts index b0fe9e38..dc9ad876 100644 --- a/src/backend/common/infrastructure/config/source/lastfm.ts +++ b/src/backend/common/infrastructure/config/source/lastfm.ts @@ -52,6 +52,8 @@ export type LastfmSourceConfig = z.infer; export const lastFmSouceAIOConfigSchema = z.object({ ...lastfmSourceConfigSchema.shape, + // can be optional since it always appears in a source array + configureAs: lastfmSourceConfigSchema.shape.configureAs.optional(), type: z.literal('lastfm'), }).meta({title: "Last.fm"}); diff --git a/src/backend/common/infrastructure/config/source/librefm.ts b/src/backend/common/infrastructure/config/source/librefm.ts index 13f7bef0..d25f0105 100644 --- a/src/backend/common/infrastructure/config/source/librefm.ts +++ b/src/backend/common/infrastructure/config/source/librefm.ts @@ -54,6 +54,8 @@ export type LibrefmSourceConfig = z.infer; export const librefmSouceAIOConfigSchema = z.object({ ...librefmSourceConfigSchema.shape, + // can be optional since it always appears in a source array + configureAs: librefmSourceConfigSchema.shape.configureAs.optional(), type: z.literal('librefm'), }).meta({title: "Libre.fm"}); diff --git a/src/backend/common/infrastructure/config/source/listenbrainz.ts b/src/backend/common/infrastructure/config/source/listenbrainz.ts index efb38517..9a8b008f 100644 --- a/src/backend/common/infrastructure/config/source/listenbrainz.ts +++ b/src/backend/common/infrastructure/config/source/listenbrainz.ts @@ -50,6 +50,8 @@ export type ListenBrainzSourceConfig = z.infer; export const malojaSourceAIOConfigSchema = z.object({ ...malojaSourceConfigSchema.shape, + // can be optional since it always appears in a source array + configureAs: malojaSourceConfigSchema.shape.configureAs.optional(), type: z.literal('maloja'), }).meta({title: 'Maloja'}); diff --git a/src/backend/common/infrastructure/config/source/rocksky.ts b/src/backend/common/infrastructure/config/source/rocksky.ts index bd2800d8..5f391bea 100644 --- a/src/backend/common/infrastructure/config/source/rocksky.ts +++ b/src/backend/common/infrastructure/config/source/rocksky.ts @@ -56,6 +56,8 @@ export type RockskySourceConfig = z.infer; export const rockskySourceAIOConfigSchema = z.object({ ...rockskySourceConfigSchema.shape, + // can be optional since it always appears in a source array + configureAs: rockskySourceConfigSchema.shape.configureAs.optional(), type: z.literal('rocksky'), }).meta({title: 'Rocksky'}); diff --git a/src/backend/common/infrastructure/config/source/tealfm.ts b/src/backend/common/infrastructure/config/source/tealfm.ts index be8cf2fa..8283e5be 100644 --- a/src/backend/common/infrastructure/config/source/tealfm.ts +++ b/src/backend/common/infrastructure/config/source/tealfm.ts @@ -59,6 +59,8 @@ export type TealSourceConfig = z.infer; export const tealSourceAIOConfigSchema = z.object({ ...tealSourceConfigSchema.shape, + // can be optional since it always appears in a source array + configureAs: tealSourceConfigSchema.shape.configureAs.optional(), type: z.literal('tealfm'), }).meta({title: 'teal.fm'}); diff --git a/src/backend/scrobblers/ScrobbleClients.ts b/src/backend/scrobblers/ScrobbleClients.ts index ef721e71..64d7a940 100644 --- a/src/backend/scrobblers/ScrobbleClients.ts +++ b/src/backend/scrobblers/ScrobbleClients.ts @@ -219,13 +219,28 @@ export default class ScrobbleClients { } }; } break; - case 'file': - case 'aio': { + case 'file': { + // only file config has a combined array of both source and client configs + // + // if configureAs is missing (optional) we assume it is a client + // and only skip it if it is explicitly set as a source + // if ('configureAs' in entry.config && entry.config.configureAs === 'source') { - this.logger.debug(`Skipping ${generateConfigLocation('client', entry)} because it is configured as a Source`); + this.logger.debug(`Skipping ${generateConfigLocation('client', entry)} because it is configured as a Source (configureAs set to 'source')`); continue; } - const parsed = entry.source === 'file' ? (await validateClientJson(entry.type, entry.config)) : (await validateClientAIOJson(entry.type, entry.config)); + const parsed = await validateClientJson(entry.type, entry.config); + parsedConfig = { + ...parsed, + name: parsed.name ?? parsed.id, + source: generateConfigLocation('client', entry) + } + } break; + case 'aio': { + // aio entries can also optionally have `configureAs` but it must always be `client` + // and we are only including entries from the `clients` array at this point + // so there's no need to manually check if `configureAs` is present + const parsed = await validateClientAIOJson(entry.type, entry.config) parsedConfig = { ...parsed, name: parsed.name ?? parsed.id, diff --git a/src/backend/sources/ScrobbleSources.ts b/src/backend/sources/ScrobbleSources.ts index e5fbe0c7..4ca47291 100644 --- a/src/backend/sources/ScrobbleSources.ts +++ b/src/backend/sources/ScrobbleSources.ts @@ -238,14 +238,34 @@ export default class ScrobbleSources { }; } break; case 'file': - case 'aio': { - if (('configureAs' in entry.config && entry.config.configureAs === 'client') - // @ts-expect-error could be a client type - || (clientTypes.includes(entry.type) && entry.config.configureAs !== 'source')) { - this.logger.debug(`Skipping ${generateConfigLocation('source', entry)} because it is configured as a Client`); - continue; + { + // only file config has a combined array of both source and client configs + // + // for source configs, it is required that "configureAs": "source" + // + // if `configureAs` is missing OR its value is not `source` then we skip it + // + // @ts-expect-error could be a client type + if(clientTypes.includes(entry.type)) { + const confAs = 'configureAs' in entry.config ? entry.config.configureAs : undefined; + if(confAs === undefined || confAs !== 'source') { + const reason = confAs === undefined ? `configureAs was not defined` : `configureAs was not set to 'source'`; + this.logger.debug(`Skipping ${generateConfigLocation('source', entry)} because it is configured as a Client (${reason})`); + continue; + } } - const parsed = entry.source === 'file' ? (await validateSourceJson(entry.type, entry.config)) : (await validateSourceAIOJson(entry.type, entry.config)); + const parsed = await validateSourceJson(entry.type, entry.config); + parsedConfig = { + ...parsed, + name: parsed.name ?? parsed.id, + source: generateConfigLocation('source', entry) + } + } break; + case 'aio': { + // aio entries can also optionally have `configureAs` but it must always be `source` + // and we are only including entries from the `sources` array at this point + // so there's no need to manually check if `configureAs` is present + const parsed = await validateSourceAIOJson(entry.type, entry.config) parsedConfig = { ...parsed, name: parsed.name ?? parsed.id,