AlbumApplicationResponse used to carry YouTubeAlbumId/ThumbnailUrl as two always-present-but-usually-null fields for the sake of a single flat shape. Since one of these fields IS genuinely exclusive to external albums, an inheritance split reads better than the flattened shape: AlbumApplicationResponse now holds only the fields shared by every album, and ExternalAlbumApplicationResponse adds ExternalId (renamed from YouTubeAlbumId, matching the source-generalized naming used everywhere else) and ThumbnailUrl. [JsonPolymorphic]/[JsonDerivedType] are required here, not optional: without them System.Text.Json serializes strictly by the declared CLR type at each call site (both the bare return type and nested inside AlbumSearchItemResponse.Album), so an ExternalAlbumApplicationResponse instance would silently lose its extra fields on the wire. This also gives every response a "source" discriminator field for free, consistent with AlbumSearchItemResponse.Source, and the generated OpenAPI schema now models it as a proper oneOf/discriminator instead of nullable fields.
web-player
Musify's web client: SvelteKit 2 (adapter-node), Svelte 5 runes, Tailwind 4,
TypeScript. Authentication is server-side OIDC against Zitadel (openid-client
and jose) with an encrypted session cookie; the backend is called from load
functions and form actions through a typed openapi-fetch client.
Development
npm install
npm run dev # :5173
Configuration lives in .env (template in .env.example). Bring the
infrastructure stack up first (see ../deploy/README.md)
and copy the WEB_CLIENT_ID / WEB_CLIENT_SECRET it writes to deploy/.env
into this .env once, then run the dev server.
npm run check # svelte-check — must be clean before closing a change
npm run lint # prettier --check . && eslint .
npm run format # prettier --write .
npm run build # production build into build/
npm run gen:api # regenerate src/lib/api/schema.d.ts from the running API
Production
Built and run as a container by the production stack — see
../deploy/README.md. There the same variables come from
deploy/.env.prod through compose instead of from .env.
Conventions and the design system are documented in CLAUDE.md and
in the musify-web skill.