diff --git a/src/backend/common/infrastructure/config/aioConfig.ts b/src/backend/common/infrastructure/config/aioConfig.ts index 36175c03..630272a6 100644 --- a/src/backend/common/infrastructure/config/aioConfig.ts +++ b/src/backend/common/infrastructure/config/aioConfig.ts @@ -4,7 +4,6 @@ import {commonClientOptionsSchema} from "./client/index.ts"; import {requestRetryOptionsSchema} from "./common.ts"; import {webhookConfigSchema} from "./health/webhooks.ts"; import {commonSourceOptionsSchema, fileLogOptionsSchema, logLevelSchema, sourceRetryOptionsSchema} from "./source/index.ts"; -import {sourceAIOConfigSchema} from "./source/sources.ts"; import {cacheConfigUserSchema} from "../Atomic.ts"; import {retentionConfigDurationValueSchema} from "./database.ts"; @@ -29,6 +28,8 @@ const transformOptionsSchema = z.object({ ttl: z.string().optional(), }); +const looseComponent = z.looseObject({ type: z.string(), configureAs: z.string().optional() }); + const transformerCommonConfigSchema = z.object({ defaults: z.record(z.string(), z.any()).optional(), data: z.record(z.string(), z.any()).optional(), @@ -40,8 +41,8 @@ const transformerCommonConfigSchema = z.object({ export const aioConfigSchema = z.object({ sourceDefaults: sourceDefaultsSchema.optional(), clientDefaults: clientDefaultsSchema.optional(), - sources: z.array(sourceAIOConfigSchema).optional(), - clients: z.array(clientAIOConfigSchema).optional(), + sources: z.array(looseComponent).optional(), + clients: z.array(looseComponent).optional(), webhooks: z.array(webhookConfigSchema).optional(), @@ -134,13 +135,6 @@ export const aioClientRelaxedConfigSchema = z.object({ export type AIOClientRelaxedConfig = z.infer; -export const aioSourceConfigSchema = z.object({ - sourceDefaults: sourceRetryOptionsSchema.optional(), - sources: z.array(sourceAIOConfigSchema).optional(), -}); - -export type AIOSourceConfig = z.infer; - export const aioSourceRelaxedConfigSchema = z.object({ sourceDefaults: sourceRetryOptionsSchema.optional(), sources: z.array(z.looseObject({ type: z.string(), configureAs: z.string().optional() })).optional(), diff --git a/src/backend/common/infrastructure/config/source/sources.ts b/src/backend/common/infrastructure/config/source/sources.ts index 43081a23..3572203b 100644 --- a/src/backend/common/infrastructure/config/source/sources.ts +++ b/src/backend/common/infrastructure/config/source/sources.ts @@ -1,37 +1,34 @@ import * as z from "zod"; -import {azuracastSourceAIOConfigSchema, azuracastSourceConfigSchema, envSchemas as azuracastEnvSchemas, type AzuracastSourceAIOConfig, type AzuracastSourceConfig} from "./azuracast.ts"; -import {chromecastSourceAIOConfigSchema, chromecastSourceConfigSchema, envSchemas as chromecastEnvSchemas, type ChromecastSourceAIOConfig, type ChromecastSourceConfig} from "./chromecast.ts"; -import {listenbrainzEndpointSourceAIOConfigSchema, listenbrainzEndpointSourceConfigSchema, envSchemas as endpointlzEnvSchemas, type ListenbrainzEndpointSourceAIOConfig, type ListenbrainzEndpointSourceConfig} from "./endpointlz.ts"; -import {lastFmEndpointSourceAIOConfigSchema, lastFmEndpointSourceConfigSchema, envSchemas as endpointlfmEnvSchemas, type LastFMEndpointSourceAIOConfig, type LastFMEndpointSourceConfig} from "./endpointlfm.ts"; -import {deezerInternalSourceConfigSchema, deezerInternalAIOConfigSchema, envSchemas as deezerEnvSchemas, type DeezerInternalSourceConfig, type DeezerInternalAIOConfig} from "./deezer.ts"; -import {jellyApiSourceAIOConfigSchema, jellyApiSourceConfigSchema, envSchemas as jellyfinEnvSchemas, type JellyApiSourceAIOConfig, type JellyApiSourceConfig} from "./jellyfin.ts"; -import {jRiverSourceAIOConfigSchema, jRiverSourceConfigSchema, envSchemas as jriverEnvSchemas, type JRiverSourceAIOConfig, type JRiverSourceConfig} from "./jriver.ts"; -import {kodiSourceAIOConfigSchema, kodiSourceConfigSchema, envSchemas as kodiEnvSchemas, type KodiSourceAIOConfig, type KodiSourceConfig} from "./kodi.ts"; -import {lastFmSouceAIOConfigSchema, lastfmSourceConfigSchema, envSchemas as lastfmEnvSchemas, type LastFmSouceAIOConfig, type LastfmSourceConfig} from "./lastfm.ts"; -import {listenBrainzSourceAIOConfigSchema, listenBrainzSourceConfigSchema, envSchemas as listenBrainzEnvSchemas, type ListenBrainzSourceAIOConfig, type ListenBrainzSourceConfig} from "./listenbrainz.ts"; -import {mopidySourceAIOConfigSchema, mopidySourceConfigSchema, envSchemas as mopidyEnvSchemas, type MopidySourceAIOConfig, type MopidySourceConfig} from "./mopidy.ts"; -import {mpdSourceAIOConfigSchema, mpdSourceConfigSchema, envSchemas as mpdEnvSchemas, type MPDSourceAIOConfig, type MPDSourceConfig} from "./mpd.ts"; -import {mprisSourceAIOConfigSchema, mprisSourceConfigSchema, envSchemas as mprisEnvSchemas, type MPRISSourceAIOConfig, type MPRISSourceConfig} from "./mpris.ts"; -import {musikcubeSourceAIOConfigSchema, musikcubeSourceConfigSchema, envSchemas as musikcubeEnvSchemas, type MusikcubeSourceAIOConfig, type MusikcubeSourceConfig} from "./musikcube.ts"; -import {musicCastSourceConfigSchema, musicCastSourceAIOConfigSchema, envSchemas as musicCastEnvSchemas, type MusicCastSourceConfig, type MusicCastSourceAIOConfig} from "./musiccast.ts"; -import {plexApiSourceConfigSchema, plexApiSourceAIOConfigSchema, envSchemas as plexEnvSchemas, type PlexApiSourceConfig, type PlexApiSourceAIOConfig} from "./plex.ts"; -import {spotifySourceAIOConfigSchema, spotifySourceConfigSchema, envSchemas as spotifyEnvSchemas, type SpotifySourceAIOConfig, type SpotifySourceConfig} from "./spotify.ts"; -import {subsonicSourceAIOConfigSchema, subSonicSourceConfigSchema, envSchemas as subsonicEnvSchemas, type SubsonicSourceAIOConfig, type SubSonicSourceConfig} from "./subsonic.ts"; -import {vlcSourceAIOConfigSchema, vlcSourceConfigSchema, envSchemas as vlcEnvSchemas, type VLCSourceAIOConfig, type VLCSourceConfig} from "./vlc.ts"; -import {webScrobblerSourceAIOConfigSchema, webScrobblerSourceConfigSchema, envSchemas as webScrobblerEnvSchemas, type WebScrobblerSourceAIOConfig, type WebScrobblerSourceConfig} from "./webscrobbler.ts"; -import {ytMusicSourceAIOConfigSchema, ytMusicSourceConfigSchema, envSchemas as ytMusicEnvSchemas, type YTMusicSourceAIOConfig, type YTMusicSourceConfig} from "./ytmusic.ts"; -import {yandexMusicBridgeSourceAIOConfigSchema, yandexMusicBridgeSourceConfigSchema, envSchemas as ymbridgeEnvSchemas, type YandexMusicBridgeSourceAIOConfig, type YandexMusicBridgeSourceConfig} from "./ymbridge.ts"; -import {icecastSourceAIOConfigSchema, icecastSourceConfigSchema, envSchemas as icecastEnvSchemas, type IcecastSourceAIOConfig, type IcecastSourceConfig} from "./icecast.ts"; -import {koitoSourceAIOConfigSchema, koitoSourceConfigSchema, envSchemas as koitoEnvSchemas, type KoitoSourceAIOConfig, type KoitoSourceConfig} from "./koito.ts"; -import {malojaSourceAIOConfigSchema, malojaSourceConfigSchema, envSchemas as malojaEnvSchemas, type MalojaSourceAIOConfig, type MalojaSourceConfig} from "./maloja.ts"; -import {tealSourceAIOConfigSchema, tealSourceConfigSchema, envSchemas as tealEnvSchemas, type TealSourceAIOConfig, type TealSourceConfig} from "./tealfm.ts"; -import {rockskySourceAIOConfigSchema, rockskySourceConfigSchema, envSchemas as rockskyEnvSchemas, type RockskySourceAIOConfig, type RockskySourceConfig} from "./rocksky.ts"; -import {librefmSouceAIOConfigSchema, librefmSourceConfigSchema, envSchemas as librefmEnvSchemas, type LibrefmSouceAIOConfig, type LibrefmSourceConfig} from "./librefm.ts"; -import {sonosSourceAIOConfigSchema, sonosSourceConfigSchema, envSchemas as sonosEnvSchemas, type SonosSourceAIOConfig, type SonosSourceConfig} from "./sonos.ts"; -import {appleMusicSourceAIOConfigSchema, appleMusicSourceConfigSchema, envSchemas as appleMusicEnvSchemas, type AppleMusicSourceAIOConfig, type AppleMusicSourceConfig} from "./applemusic.ts"; -import type { SourceType } from "../../../../../core/Atomic.ts"; -import type { CommonSourceConfig, EnvSourceSchema } from "./index.ts"; -import { SimpleError } from "../../../errors/MSErrors.ts"; +import {azuracastSourceAIOConfigSchema, azuracastSourceConfigSchema} from "./azuracast.ts"; +import {chromecastSourceAIOConfigSchema, chromecastSourceConfigSchema} from "./chromecast.ts"; +import {listenbrainzEndpointSourceAIOConfigSchema, listenbrainzEndpointSourceConfigSchema} from "./endpointlz.ts"; +import {lastFmEndpointSourceAIOConfigSchema, lastFmEndpointSourceConfigSchema} from "./endpointlfm.ts"; +import {deezerInternalSourceConfigSchema, deezerInternalAIOConfigSchema} from "./deezer.ts"; +import {jellyApiSourceAIOConfigSchema, jellyApiSourceConfigSchema} from "./jellyfin.ts"; +import {jRiverSourceAIOConfigSchema, jRiverSourceConfigSchema} from "./jriver.ts"; +import {kodiSourceAIOConfigSchema, kodiSourceConfigSchema} from "./kodi.ts"; +import {lastFmSouceAIOConfigSchema, lastfmSourceConfigSchema} from "./lastfm.ts"; +import {listenBrainzSourceAIOConfigSchema, listenBrainzSourceConfigSchema} from "./listenbrainz.ts"; +import {mopidySourceAIOConfigSchema, mopidySourceConfigSchema} from "./mopidy.ts"; +import {mpdSourceAIOConfigSchema, mpdSourceConfigSchema} from "./mpd.ts"; +import {mprisSourceAIOConfigSchema, mprisSourceConfigSchema} from "./mpris.ts"; +import {musikcubeSourceAIOConfigSchema, musikcubeSourceConfigSchema} from "./musikcube.ts"; +import {musicCastSourceConfigSchema, musicCastSourceAIOConfigSchema} from "./musiccast.ts"; +import {plexApiSourceConfigSchema, plexApiSourceAIOConfigSchema} from "./plex.ts"; +import {spotifySourceAIOConfigSchema, spotifySourceConfigSchema} from "./spotify.ts"; +import {subsonicSourceAIOConfigSchema, subSonicSourceConfigSchema} from "./subsonic.ts"; +import {vlcSourceAIOConfigSchema, vlcSourceConfigSchema} from "./vlc.ts"; +import {webScrobblerSourceAIOConfigSchema, webScrobblerSourceConfigSchema} from "./webscrobbler.ts"; +import {ytMusicSourceAIOConfigSchema, ytMusicSourceConfigSchema} from "./ytmusic.ts"; +import {yandexMusicBridgeSourceAIOConfigSchema, yandexMusicBridgeSourceConfigSchema} from "./ymbridge.ts"; +import {icecastSourceAIOConfigSchema, icecastSourceConfigSchema} from "./icecast.ts"; +import {koitoSourceAIOConfigSchema, koitoSourceConfigSchema} from "./koito.ts"; +import {malojaSourceAIOConfigSchema, malojaSourceConfigSchema} from "./maloja.ts"; +import {tealSourceAIOConfigSchema, tealSourceConfigSchema} from "./tealfm.ts"; +import {rockskySourceAIOConfigSchema, rockskySourceConfigSchema} from "./rocksky.ts"; +import {librefmSouceAIOConfigSchema, librefmSourceConfigSchema} from "./librefm.ts"; +import {sonosSourceAIOConfigSchema, sonosSourceConfigSchema} from "./sonos.ts"; +import {appleMusicSourceAIOConfigSchema, appleMusicSourceConfigSchema} from "./applemusic.ts"; export const sourceConfigSchema = z.union([ spotifySourceConfigSchema, @@ -101,83 +98,4 @@ export const sourceAIOConfigSchema = z.union([ appleMusicSourceAIOConfigSchema, ]); -export type SourceAIOConfig = z.infer; - -export interface SourceTypeConfigMap extends Record>]> { - spotify: [SpotifySourceConfig, SpotifySourceAIOConfig, Partial>], - plex: [PlexApiSourceConfig, PlexApiSourceAIOConfig, Partial>], - deezer: [DeezerInternalSourceConfig, DeezerInternalAIOConfig, Partial>], - endpointlz: [ListenbrainzEndpointSourceConfig, ListenbrainzEndpointSourceAIOConfig, Partial>], - endpointlfm: [LastFMEndpointSourceConfig, LastFMEndpointSourceAIOConfig, Partial>], - icecast: [IcecastSourceConfig, IcecastSourceAIOConfig, Partial>], - subsonic: [SubSonicSourceConfig, SubsonicSourceAIOConfig, Partial>], - jellyfin: [JellyApiSourceConfig, JellyApiSourceAIOConfig, Partial>], - lastfm: [LastfmSourceConfig, LastFmSouceAIOConfig, Partial>], - librefm: [LibrefmSourceConfig, LibrefmSouceAIOConfig, Partial>], - ytmusic: [YTMusicSourceConfig, YTMusicSourceAIOConfig, Partial>], - ymbridge: [YandexMusicBridgeSourceConfig, YandexMusicBridgeSourceAIOConfig, Partial>], - maloja: [MalojaSourceConfig, MalojaSourceAIOConfig, Partial>], - mpris: [MPRISSourceConfig, MPRISSourceAIOConfig, Partial>], - mopidy: [MopidySourceConfig, MopidySourceAIOConfig, Partial>], - listenbrainz: [ListenBrainzSourceConfig, ListenBrainzSourceAIOConfig, Partial>], - jriver: [JRiverSourceConfig, JRiverSourceAIOConfig, Partial>], - kodi: [KodiSourceConfig, KodiSourceAIOConfig, Partial>], - chromecast: [ChromecastSourceConfig, ChromecastSourceAIOConfig, Partial>], - webscrobbler: [WebScrobblerSourceConfig, WebScrobblerSourceAIOConfig, Partial>], - musikcube: [MusikcubeSourceConfig, MusikcubeSourceAIOConfig, Partial>], - musiccast: [MusicCastSourceConfig, MusicCastSourceAIOConfig, Partial>], - mpd: [MPDSourceConfig, MPDSourceAIOConfig, Partial>], - vlc: [VLCSourceConfig, VLCSourceAIOConfig, Partial>], - azuracast: [AzuracastSourceConfig, AzuracastSourceAIOConfig, Partial>], - koito: [KoitoSourceConfig, KoitoSourceAIOConfig, Partial>], - tealfm: [TealSourceConfig, TealSourceAIOConfig, Partial>], - rocksky: [RockskySourceConfig, RockskySourceAIOConfig, Partial>], - sonos: [SonosSourceConfig, SonosSourceAIOConfig, Partial>], - applemusic: [AppleMusicSourceConfig, AppleMusicSourceAIOConfig, Partial>] -} - -export const sourceConfigSchemaMap: { [K in keyof SourceTypeConfigMap]: [z.ZodType, z.ZodType, EnvSourceSchema] } = { - spotify: [spotifySourceConfigSchema, spotifySourceAIOConfigSchema, spotifyEnvSchemas], - plex: [plexApiSourceConfigSchema, plexApiSourceAIOConfigSchema, plexEnvSchemas], - deezer: [deezerInternalSourceConfigSchema, deezerInternalAIOConfigSchema, deezerEnvSchemas], - endpointlz: [listenbrainzEndpointSourceConfigSchema, listenbrainzEndpointSourceAIOConfigSchema, endpointlzEnvSchemas], - endpointlfm: [lastFmEndpointSourceConfigSchema, lastFmEndpointSourceAIOConfigSchema, endpointlfmEnvSchemas], - icecast: [icecastSourceConfigSchema, icecastSourceAIOConfigSchema, icecastEnvSchemas], - subsonic: [subSonicSourceConfigSchema, subsonicSourceAIOConfigSchema, subsonicEnvSchemas], - jellyfin: [jellyApiSourceConfigSchema, jellyApiSourceAIOConfigSchema, jellyfinEnvSchemas], - lastfm: [lastfmSourceConfigSchema, lastFmSouceAIOConfigSchema, lastfmEnvSchemas], - librefm: [librefmSourceConfigSchema, librefmSouceAIOConfigSchema, librefmEnvSchemas], - ytmusic: [ytMusicSourceConfigSchema, ytMusicSourceAIOConfigSchema, ytMusicEnvSchemas], - ymbridge: [yandexMusicBridgeSourceConfigSchema, yandexMusicBridgeSourceAIOConfigSchema, ymbridgeEnvSchemas], - maloja: [malojaSourceConfigSchema, malojaSourceAIOConfigSchema, malojaEnvSchemas], - mpris: [mprisSourceConfigSchema, mprisSourceAIOConfigSchema, mprisEnvSchemas], - mopidy: [mopidySourceConfigSchema, mopidySourceAIOConfigSchema, mopidyEnvSchemas], - listenbrainz: [listenBrainzSourceConfigSchema, listenBrainzSourceAIOConfigSchema, listenBrainzEnvSchemas], - jriver: [jRiverSourceConfigSchema, jRiverSourceAIOConfigSchema, jriverEnvSchemas], - kodi: [kodiSourceConfigSchema, kodiSourceAIOConfigSchema, kodiEnvSchemas], - chromecast: [chromecastSourceConfigSchema, chromecastSourceAIOConfigSchema, chromecastEnvSchemas], - webscrobbler: [webScrobblerSourceConfigSchema, webScrobblerSourceAIOConfigSchema, webScrobblerEnvSchemas], - musikcube: [musikcubeSourceConfigSchema, musikcubeSourceAIOConfigSchema, musikcubeEnvSchemas], - musiccast: [musicCastSourceConfigSchema, musicCastSourceAIOConfigSchema, musicCastEnvSchemas], - mpd: [mpdSourceConfigSchema, mpdSourceAIOConfigSchema, mpdEnvSchemas], - vlc: [vlcSourceConfigSchema, vlcSourceAIOConfigSchema, vlcEnvSchemas], - azuracast: [azuracastSourceConfigSchema, azuracastSourceAIOConfigSchema, azuracastEnvSchemas], - koito: [koitoSourceConfigSchema, koitoSourceAIOConfigSchema, koitoEnvSchemas], - tealfm: [tealSourceConfigSchema, tealSourceAIOConfigSchema, tealEnvSchemas], - rocksky: [rockskySourceConfigSchema, rockskySourceAIOConfigSchema, rockskyEnvSchemas], - sonos: [sonosSourceConfigSchema, sonosSourceAIOConfigSchema, sonosEnvSchemas], - applemusic: [appleMusicSourceConfigSchema, appleMusicSourceAIOConfigSchema, appleMusicEnvSchemas] -}; - -export const validateSourceJson = (sourceType: T, json: object): SourceTypeConfigMap[T][0] => { - if(sourceConfigSchemaMap[sourceType] === undefined) { - throw new SimpleError(`No Source has a 'type' of '${sourceType}'`); - } - return sourceConfigSchemaMap[sourceType][0].parse(json) -}; -export const validateSourceAIOJson = (sourceType: T, json: object): SourceTypeConfigMap[T][1] => { - if(sourceConfigSchemaMap[sourceType] === undefined) { - throw new SimpleError(`No Source has a 'type' of '${sourceType}'`); - } - return sourceConfigSchemaMap[sourceType][1].parse(json) -}; \ No newline at end of file +export type SourceAIOConfig = z.infer; \ No newline at end of file diff --git a/src/backend/common/infrastructure/config/source/sourcesMap.ts b/src/backend/common/infrastructure/config/source/sourcesMap.ts new file mode 100644 index 00000000..1a7aeb11 --- /dev/null +++ b/src/backend/common/infrastructure/config/source/sourcesMap.ts @@ -0,0 +1,212 @@ +import type { EnvSourceSchema } from "./index.ts"; +import type {ZodType, ZodObject} from 'zod'; +import type { AzuracastSourceAIOConfig, AzuracastSourceConfig} from "./azuracast.ts"; +import type { ChromecastSourceAIOConfig, ChromecastSourceConfig} from "./chromecast.ts"; +import type { ListenbrainzEndpointSourceAIOConfig, ListenbrainzEndpointSourceConfig} from "./endpointlz.ts"; +import type { LastFMEndpointSourceAIOConfig, LastFMEndpointSourceConfig} from "./endpointlfm.ts"; +import type { DeezerInternalSourceConfig, DeezerInternalAIOConfig} from "./deezer.ts"; +import type { JellyApiSourceAIOConfig, JellyApiSourceConfig} from "./jellyfin.ts"; +import type { JRiverSourceAIOConfig, JRiverSourceConfig} from "./jriver.ts"; +import type { KodiSourceAIOConfig, KodiSourceConfig} from "./kodi.ts"; +import type { LastFmSouceAIOConfig, LastfmSourceConfig} from "./lastfm.ts"; +import type { ListenBrainzSourceAIOConfig, ListenBrainzSourceConfig} from "./listenbrainz.ts"; +import type { MopidySourceAIOConfig, MopidySourceConfig} from "./mopidy.ts"; +import type { MPDSourceAIOConfig, MPDSourceConfig} from "./mpd.ts"; +import type { MPRISSourceAIOConfig, MPRISSourceConfig} from "./mpris.ts"; +import type { MusikcubeSourceAIOConfig, MusikcubeSourceConfig} from "./musikcube.ts"; +import type { MusicCastSourceConfig, MusicCastSourceAIOConfig} from "./musiccast.ts"; +import type { PlexApiSourceConfig, PlexApiSourceAIOConfig} from "./plex.ts"; +import type { SpotifySourceAIOConfig, SpotifySourceConfig} from "./spotify.ts"; +import type { SubsonicSourceAIOConfig, SubSonicSourceConfig} from "./subsonic.ts"; +import type { VLCSourceAIOConfig, VLCSourceConfig} from "./vlc.ts"; +import type { WebScrobblerSourceAIOConfig, WebScrobblerSourceConfig} from "./webscrobbler.ts"; +import type { YTMusicSourceAIOConfig, YTMusicSourceConfig} from "./ytmusic.ts"; +import type { YandexMusicBridgeSourceAIOConfig, YandexMusicBridgeSourceConfig} from "./ymbridge.ts"; +import type { IcecastSourceAIOConfig, IcecastSourceConfig} from "./icecast.ts"; +import type { KoitoSourceAIOConfig, KoitoSourceConfig} from "./koito.ts"; +import type { MalojaSourceAIOConfig, MalojaSourceConfig} from "./maloja.ts"; +import type { TealSourceAIOConfig, TealSourceConfig} from "./tealfm.ts"; +import type { RockskySourceAIOConfig, RockskySourceConfig} from "./rocksky.ts"; +import type { LibrefmSouceAIOConfig, LibrefmSourceConfig} from "./librefm.ts"; +import type { SonosSourceAIOConfig, SonosSourceConfig} from "./sonos.ts"; +import type { AppleMusicSourceAIOConfig, AppleMusicSourceConfig} from "./applemusic.ts"; +import type { SourceType } from "../../../../../core/Atomic.ts"; +import type { CommonSourceConfig } from "./index.ts"; +import type { SourceAIOConfig } from "./sources.ts"; +import { SimpleError } from "../../../errors/MSErrors.ts"; + +export interface SourceTypeConfigMap extends Record>]> { + spotify: [SpotifySourceConfig, SpotifySourceAIOConfig, Partial>]; + plex: [PlexApiSourceConfig, PlexApiSourceAIOConfig, Partial>]; + deezer: [DeezerInternalSourceConfig, DeezerInternalAIOConfig, Partial>]; + endpointlz: [ListenbrainzEndpointSourceConfig, ListenbrainzEndpointSourceAIOConfig, Partial>]; + endpointlfm: [LastFMEndpointSourceConfig, LastFMEndpointSourceAIOConfig, Partial>]; + icecast: [IcecastSourceConfig, IcecastSourceAIOConfig, Partial>]; + subsonic: [SubSonicSourceConfig, SubsonicSourceAIOConfig, Partial>]; + jellyfin: [JellyApiSourceConfig, JellyApiSourceAIOConfig, Partial>]; + lastfm: [LastfmSourceConfig, LastFmSouceAIOConfig, Partial>]; + librefm: [LibrefmSourceConfig, LibrefmSouceAIOConfig, Partial>]; + ytmusic: [YTMusicSourceConfig, YTMusicSourceAIOConfig, Partial>]; + ymbridge: [YandexMusicBridgeSourceConfig, YandexMusicBridgeSourceAIOConfig, Partial>]; + maloja: [MalojaSourceConfig, MalojaSourceAIOConfig, Partial>]; + mpris: [MPRISSourceConfig, MPRISSourceAIOConfig, Partial>]; + mopidy: [MopidySourceConfig, MopidySourceAIOConfig, Partial>]; + listenbrainz: [ListenBrainzSourceConfig, ListenBrainzSourceAIOConfig, Partial>]; + jriver: [JRiverSourceConfig, JRiverSourceAIOConfig, Partial>]; + kodi: [KodiSourceConfig, KodiSourceAIOConfig, Partial>]; + chromecast: [ChromecastSourceConfig, ChromecastSourceAIOConfig, Partial>]; + webscrobbler: [WebScrobblerSourceConfig, WebScrobblerSourceAIOConfig, Partial>]; + musikcube: [MusikcubeSourceConfig, MusikcubeSourceAIOConfig, Partial>]; + musiccast: [MusicCastSourceConfig, MusicCastSourceAIOConfig, Partial>]; + mpd: [MPDSourceConfig, MPDSourceAIOConfig, Partial>]; + vlc: [VLCSourceConfig, VLCSourceAIOConfig, Partial>]; + azuracast: [AzuracastSourceConfig, AzuracastSourceAIOConfig, Partial>]; + koito: [KoitoSourceConfig, KoitoSourceAIOConfig, Partial>]; + tealfm: [TealSourceConfig, TealSourceAIOConfig, Partial>]; + rocksky: [RockskySourceConfig, RockskySourceAIOConfig, Partial>]; + sonos: [SonosSourceConfig, SonosSourceAIOConfig, Partial>]; + applemusic: [AppleMusicSourceConfig, AppleMusicSourceAIOConfig, Partial>]; +} + +export const sourceConfigSchemaMapAsync: { [K in keyof SourceTypeConfigMap]: () => Promise<[ZodType, ZodType, EnvSourceSchema]> } = { + spotify: async () => { + const {spotifySourceConfigSchema, spotifySourceAIOConfigSchema, envSchemas } = (await import('./spotify.ts')); + return [spotifySourceConfigSchema, spotifySourceAIOConfigSchema, envSchemas] + }, + plex: async () => { + const {plexApiSourceConfigSchema, plexApiSourceAIOConfigSchema, envSchemas } = (await import('./plex.ts')); + return [plexApiSourceConfigSchema, plexApiSourceAIOConfigSchema, envSchemas] + }, + deezer: async () => { + const {deezerInternalSourceConfigSchema, deezerInternalAIOConfigSchema, envSchemas } = (await import('./deezer.ts')); + return [deezerInternalSourceConfigSchema, deezerInternalAIOConfigSchema, envSchemas] + }, + endpointlz: async () => { + const {listenbrainzEndpointSourceConfigSchema, listenbrainzEndpointSourceAIOConfigSchema, envSchemas } = (await import('./endpointlz.ts')); + return [listenbrainzEndpointSourceConfigSchema, listenbrainzEndpointSourceAIOConfigSchema, envSchemas] + }, + endpointlfm: async () => { + const {lastFmEndpointSourceConfigSchema, lastFmEndpointSourceAIOConfigSchema, envSchemas } = (await import('./endpointlfm.ts')); + return [lastFmEndpointSourceConfigSchema, lastFmEndpointSourceAIOConfigSchema, envSchemas] + }, + icecast: async () => { + const {icecastSourceConfigSchema, icecastSourceAIOConfigSchema, envSchemas } = (await import('./icecast.ts')); + return [icecastSourceConfigSchema, icecastSourceAIOConfigSchema, envSchemas] + }, + subsonic: async () => { + const {subSonicSourceConfigSchema, subsonicSourceAIOConfigSchema, envSchemas } = (await import('./subsonic.ts')); + return [subSonicSourceConfigSchema, subsonicSourceAIOConfigSchema, envSchemas] + }, + jellyfin: async () => { + const {jellyApiSourceConfigSchema, jellyApiSourceAIOConfigSchema, envSchemas } = (await import('./jellyfin.ts')); + return [jellyApiSourceConfigSchema, jellyApiSourceAIOConfigSchema, envSchemas] + }, + lastfm: async () => { + const {lastfmSourceConfigSchema, lastFmSouceAIOConfigSchema, envSchemas } = (await import('./lastfm.ts')); + return [lastfmSourceConfigSchema, lastFmSouceAIOConfigSchema, envSchemas] + }, + librefm: async () => { + const {librefmSourceConfigSchema, librefmSouceAIOConfigSchema, envSchemas } = (await import('./librefm.ts')); + return [librefmSourceConfigSchema, librefmSouceAIOConfigSchema, envSchemas] + }, + ytmusic: async () => { + const {ytMusicSourceConfigSchema, ytMusicSourceAIOConfigSchema, envSchemas } = (await import('./ytmusic.ts')); + return [ytMusicSourceConfigSchema, ytMusicSourceAIOConfigSchema, envSchemas] + }, + ymbridge: async () => { + const {yandexMusicBridgeSourceConfigSchema, yandexMusicBridgeSourceAIOConfigSchema, envSchemas } = (await import('./ymbridge.ts')); + return [yandexMusicBridgeSourceConfigSchema, yandexMusicBridgeSourceAIOConfigSchema, envSchemas] + }, + maloja: async () => { + const {malojaSourceConfigSchema, malojaSourceAIOConfigSchema, envSchemas } = (await import('./maloja.ts')); + return [malojaSourceConfigSchema, malojaSourceAIOConfigSchema, envSchemas] + }, + mpris: async () => { + const {mprisSourceConfigSchema, mprisSourceAIOConfigSchema, envSchemas } = (await import('./mpris.ts')); + return [mprisSourceConfigSchema, mprisSourceAIOConfigSchema, envSchemas] + }, + mopidy: async () => { + const {mopidySourceConfigSchema, mopidySourceAIOConfigSchema, envSchemas } = (await import('./mopidy.ts')); + return [mopidySourceConfigSchema, mopidySourceAIOConfigSchema, envSchemas] + }, + listenbrainz: async () => { + const {listenBrainzSourceConfigSchema, listenBrainzSourceAIOConfigSchema, envSchemas } = (await import('./listenbrainz.ts')); + return [listenBrainzSourceConfigSchema, listenBrainzSourceAIOConfigSchema, envSchemas] + }, + jriver: async () => { + const {jRiverSourceConfigSchema, jRiverSourceAIOConfigSchema, envSchemas } = (await import('./jriver.ts')); + return [jRiverSourceConfigSchema, jRiverSourceAIOConfigSchema, envSchemas] + }, + kodi: async () => { + const {kodiSourceConfigSchema, kodiSourceAIOConfigSchema, envSchemas } = (await import('./kodi.ts')); + return [kodiSourceConfigSchema, kodiSourceAIOConfigSchema, envSchemas] + }, + chromecast: async () => { + const {chromecastSourceConfigSchema, chromecastSourceAIOConfigSchema, envSchemas } = (await import('./chromecast.ts')); + return [chromecastSourceConfigSchema, chromecastSourceAIOConfigSchema, envSchemas] + }, + webscrobbler: async () => { + const {webScrobblerSourceConfigSchema, webScrobblerSourceAIOConfigSchema, envSchemas } = (await import('./webscrobbler.ts')); + return [webScrobblerSourceConfigSchema, webScrobblerSourceAIOConfigSchema, envSchemas] + }, + musikcube: async () => { + const {musikcubeSourceConfigSchema, musikcubeSourceAIOConfigSchema, envSchemas } = (await import('./musikcube.ts')); + return [musikcubeSourceConfigSchema, musikcubeSourceAIOConfigSchema, envSchemas] + }, + musiccast: async () => { + const {musicCastSourceConfigSchema, musicCastSourceAIOConfigSchema, envSchemas } = (await import('./musiccast.ts')); + return [musicCastSourceConfigSchema, musicCastSourceAIOConfigSchema, envSchemas] + }, + mpd: async () => { + const {mpdSourceConfigSchema, mpdSourceAIOConfigSchema, envSchemas } = (await import('./mpd.ts')); + return [mpdSourceConfigSchema, mpdSourceAIOConfigSchema, envSchemas] + }, + vlc: async () => { + const {vlcSourceConfigSchema, vlcSourceAIOConfigSchema, envSchemas } = (await import('./vlc.ts')); + return [vlcSourceConfigSchema, vlcSourceAIOConfigSchema, envSchemas] + }, + azuracast: async () => { + const {azuracastSourceConfigSchema, azuracastSourceAIOConfigSchema, envSchemas } = (await import('./azuracast.ts')); + return [azuracastSourceConfigSchema, azuracastSourceAIOConfigSchema, envSchemas] + }, + koito: async () => { + const {koitoSourceConfigSchema, koitoSourceAIOConfigSchema, envSchemas } = (await import('./koito.ts')); + return [koitoSourceConfigSchema, koitoSourceAIOConfigSchema, envSchemas] + }, + tealfm: async () => { + const {tealSourceConfigSchema, tealSourceAIOConfigSchema, envSchemas } = (await import('./tealfm.ts')); + return [tealSourceConfigSchema, tealSourceAIOConfigSchema, envSchemas] + }, + rocksky: async () => { + const {rockskySourceConfigSchema, rockskySourceAIOConfigSchema, envSchemas } = (await import('./rocksky.ts')); + return [rockskySourceConfigSchema, rockskySourceAIOConfigSchema, envSchemas] + }, + sonos: async () => { + const {sonosSourceConfigSchema, sonosSourceAIOConfigSchema, envSchemas } = (await import('./sonos.ts')); + return [sonosSourceConfigSchema, sonosSourceAIOConfigSchema, envSchemas] + }, + applemusic: async () => { + const {appleMusicSourceConfigSchema, appleMusicSourceAIOConfigSchema, envSchemas } = (await import('./applemusic.ts')); + return [appleMusicSourceConfigSchema, appleMusicSourceAIOConfigSchema, envSchemas] + }, +}; + +export const validateSourceJson = async (sourceType: T, json: object): Promise => { + if(sourceConfigSchemaMapAsync[sourceType] === undefined) { + throw new SimpleError(`No Source has a 'type' of '${sourceType}'`); + } + return (await sourceConfigSchemaMapAsync[sourceType]())[0].parse(json); +}; +export const validateSourceAIOJson = async (sourceType: T, json: object): Promise => { + if(sourceConfigSchemaMapAsync[sourceType] === undefined) { + throw new SimpleError(`No Source has a 'type' of '${sourceType}'`); + } + return (await sourceConfigSchemaMapAsync[sourceType]())[1].parse(json) +}; + +export const getSourceEnvSchema = async (sourceType: T): Promise> => { + if(sourceConfigSchemaMapAsync[sourceType] === undefined) { + throw new SimpleError(`No Source has a 'type' of '${sourceType}'`); + } + return (await sourceConfigSchemaMapAsync[sourceType]())[2]; +}; \ No newline at end of file diff --git a/src/backend/index.ts b/src/backend/index.ts index 2ce4c471..e4a207e2 100644 --- a/src/backend/index.ts +++ b/src/backend/index.ts @@ -194,11 +194,11 @@ const dataDir = getDataDir(); /* * setup clients * */ - await scrobbleClients.buildClientsFromConfig(notifiers); + await scrobbleClients.buildClientsFromConfig(); /* * setup sources * */ - await scrobbleSources.buildSourcesFromConfig([]); + await scrobbleSources.buildSourcesFromConfig(); // check ambiguous client/source types like this for now const lastfmSources = scrobbleSources.getByType('lastfm'); diff --git a/src/backend/sources/ScrobbleSources.ts b/src/backend/sources/ScrobbleSources.ts index a81d53cd..837c567a 100644 --- a/src/backend/sources/ScrobbleSources.ts +++ b/src/backend/sources/ScrobbleSources.ts @@ -6,7 +6,6 @@ import { isSourceType } from "../../core/Atomic.ts"; import { sourceTypes } from "../../core/Atomic.ts"; import type {SourceType} from "../../core/Atomic.ts"; import {aioSourceRelaxedConfigSchema, type AIOSourceRelaxedConfig, type SourceDefaults} from "../common/infrastructure/config/aioConfig.ts"; -import {sourceConfigSchemaMap, validateSourceAIOJson, validateSourceJson, type SourceTypeConfigMap} from "../common/infrastructure/config/source/sources.ts"; import type { WildcardEmitter } from "../common/WildcardEmitter.ts"; import { pick } from '../../core/DataUtils.ts'; import { readJson } from '../utils/DataUtils.ts'; @@ -16,6 +15,8 @@ import type {CommonSourceConfig, CommonSourceOptions} from '../common/infrastruc import type {ExternalMetadataTerm, PlayTransformHooks} from '../../core/Transform.ts'; import { prettifyError, ZodError } from 'zod'; import { commonComponentEnvConfigToConfigPrimitives, generateCommonComponentEnvConfigSchema, type CommonConfigPrimitives } from '../common/infrastructure/config/common.ts'; +import { getSourceEnvSchema, validateSourceAIOJson, validateSourceJson } from '../common/infrastructure/config/source/sourcesMap.ts'; +import type { SourceTypeConfigMap } from "../common/infrastructure/config/source/sourcesMap.ts"; type UnparsedConfig = {config: object, type: SourceType, source?: 'file' | 'aio' | 'env', pos: string}; @@ -217,7 +218,7 @@ export default class ScrobbleSources { const sourceStr = `${entry.source} ${entry.pos}`; switch (entry.source) { case 'env': { - const envSchema = sourceConfigSchemaMap[configType][2]; + const envSchema = await getSourceEnvSchema(configType); // sourceConfigSchemaMap[configType][2]; const primitiveSchema = generateCommonComponentEnvConfigSchema(envSchema.prefix.toUpperCase()); const parsed = primitiveSchema.parse(entry.config); const primitives: CommonConfigPrimitives = commonComponentEnvConfigToConfigPrimitives(envSchema.prefix.toUpperCase(), parsed); @@ -241,7 +242,7 @@ export default class ScrobbleSources { this.logger.debug(`Skipping ${configType} Config ${entry.source} ${entry.pos} because it is configured as a Source`); continue; } - const parsed = entry.source === 'file' ? validateSourceJson(entry.type, entry.config) : validateSourceAIOJson(entry.type, entry.config); + const parsed = entry.source === 'file' ? (await validateSourceJson(entry.type, entry.config)) : (await validateSourceAIOJson(entry.type, entry.config)); parsedConfig = { ...parsed, source: sourceStr @@ -277,7 +278,7 @@ export default class ScrobbleSources { ) => { for (const s of strongConfigs) { try { - const config = sourceConfigSchemaMap[sourceType][0].parse(s); + const config = await validateSourceJson(sourceType, s); // sourceConfigSchemaMap[sourceType][0].parse(s); const compositeOptions = { ...defaults, ...config.options }; const newComponent = new Ctor(config.name, { ...config, options: compositeOptions }, this.internalConfig, this.emitter); newComponent.logger.info(`Source added from ${s.source}`); diff --git a/src/backend/tests/config/config.test.ts b/src/backend/tests/config/config.test.ts index dabc7163..b23d6b57 100644 --- a/src/backend/tests/config/config.test.ts +++ b/src/backend/tests/config/config.test.ts @@ -12,7 +12,7 @@ import { clientTypes } from "../../../core/Atomic.ts"; import { projectRootDir } from "../../common/infrastructure/Atomic.ts"; import { sourceTypes } from "../../../core/Atomic.ts"; import { difference } from '../../utils.ts'; -import { validateSourceJson } from '../../common/infrastructure/config/source/sources.ts'; +import { validateSourceJson } from '../../common/infrastructure/config/source/sourcesMap.ts'; import { readJson } from '../../utils/DataUtils.ts'; import { prettifyError, ZodError } from 'zod'; import { validateClientJson } from '../../common/infrastructure/config/client/clients.ts'; @@ -68,7 +68,7 @@ describe('Sample Configs', function () { fileContents = fileContents.filter(x => x.configureAs === undefined || x.configureAs === 'source'); for (const [i,rawConf] of fileContents.entries()) { try { - validateSourceJson(componentType, rawConf); + await validateSourceJson(componentType, rawConf); } catch (e) { if(e instanceof ZodError) { expect.fail(`Validation failed for config entry ${i}:\n${prettifyError(e)}`); diff --git a/src/backend/utils/SchemaDocsStaticUtils.ts b/src/backend/utils/SchemaDocsStaticUtils.ts index ad1efcfa..616a98c5 100644 --- a/src/backend/utils/SchemaDocsStaticUtils.ts +++ b/src/backend/utils/SchemaDocsStaticUtils.ts @@ -2,12 +2,13 @@ import { writeFileSync, mkdirSync } from "node:fs"; import { resolve } from "path"; import * as z from 'zod'; import { clientConfigSchemaMap } from '../common/infrastructure/config/client/clients.ts'; -import { sourceConfigSchemaMap } from '../common/infrastructure/config/source/sources.ts'; import { projectRootDir } from "../common/infrastructure/Atomic.ts"; import { aioConfigSchema } from "../common/infrastructure/config/aioConfig.ts"; import { generateCommonComponentEnvConfigSchema } from "../common/infrastructure/config/common.ts"; import { zodObjectToTableColumns, type TableColumn } from "./ZodUtils.ts"; import {markdownTable} from 'markdown-table' +import { sourceAIOConfigSchema } from "../common/infrastructure/config/source/sources.ts"; +import { sourceConfigSchemaMapAsync } from "../common/infrastructure/config/source/sourcesMap.ts"; mkdirSync(resolve(projectRootDir, 'docsite/static/schemas'), {recursive: true}); @@ -67,13 +68,14 @@ for(const [k,v] of clientEntries) { writeFileSync(resolve(projectRootDir, `docsite/docs/configuration/clients/_env_configs/_${k}.md`), tableContent); } -const sourcesEntries = Object.entries(sourceConfigSchemaMap); +const sourcesEntries = Object.entries(sourceConfigSchemaMapAsync); for(const [k,v] of sourcesEntries) { - writeFileSync(resolve(projectRootDir, `docsite/static/schemas/${k}-source.json`), JSON.stringify(generateSchema(z.array(v[0])))); + const [fileSchema, aioSchema, envSchemas] = await v(); + writeFileSync(resolve(projectRootDir, `docsite/static/schemas/${k}-source.json`), JSON.stringify(generateSchema(z.array(fileSchema)))); - const envSchema = v[2]; + const envSchema = envSchemas; const common = generateCommonComponentEnvConfigSchema(envSchema.prefix.toUpperCase()); - const col = zodObjectToTableColumns(z.object({...common.shape,...v[2].env.shape}), 'out'); + const col = zodObjectToTableColumns(z.object({...common.shape,...envSchema.env.shape}), 'out'); const tableContent = markdownTable([ ['Environmental Variable', 'Type', 'Default', 'Description'], ...mdCols(col) @@ -81,4 +83,8 @@ for(const [k,v] of sourcesEntries) { writeFileSync(resolve(projectRootDir, `docsite/docs/configuration/sources/_env_configs/_${k}.md`), tableContent); } -writeFileSync(resolve(projectRootDir, 'docsite/static/schemas/aio.json'), JSON.stringify(generateSchema(aioConfigSchema, 'ref'))); +const aioStrongConfigSchema = aioConfigSchema.extend({ + sources: z.array(sourceAIOConfigSchema).optional() +}) + +writeFileSync(resolve(projectRootDir, 'docsite/static/schemas/aio.json'), JSON.stringify(generateSchema(aioStrongConfigSchema, 'ref')));