Mixes are their own concept, not playlists: they live in their own
Mixes/MixItems tables, cannot be edited and never show up among the
user's playlists. Each item is either a Musify track or a YouTube Music
reference, so YouTube songs stream directly instead of being downloaded.
A daily Hangfire job seeds the mixes from the listening history: one
discovery mix, one daily mix and one per top artist.
The per-user YouTube filler query was already in the working tree and
rides along here because it shares files with this change.
- Register IAmazonS3 as a singleton instead of scoped: the AWS SDK client is
thread-safe and manages its own connection pool, so one instance per DI scope
(per request / per job iteration) was wasteful and risked socket exhaustion
- AudioTranscoderService: start reading stdout/stderr before writing stdin to
remove the latent pipe-buffer deadlock shape
- Dispose the resized-picture MemoryStream in ResizePictureActivity
- Background jobs use PeriodicTimer instead of while + Task.Delay
Verified: Worker boots against RabbitMQ + Postgres in Docker with no captive-
dependency error (dev scope validation) and both jobs start.
Build: 0 warnings, 0 errors.
- Add Musify.Application.AddApplication(services, config) as the layer's DI
entry point: registers Mediator, UploadIntentValidator and all app configs
- Collapse the 5 near-identical AddXConfiguration methods into a single
generic AddValidatedOptions<T>(config, section)
- Move UploadIntentValidator registration out of Infrastructure into the
Application layer (fixes layering leak); Infra config binding now uses the
generic helper
- Api calls AddApplication; Worker uses AddValidatedOptions for its configs
Build: 0 warnings, 0 errors.
- Move all .NET (6 projects, solution, Directory.*.props, nuget.config)
into backend/ so the monorepo root can host non-.NET projects later
- Remove throwaway test artifacts: player.html, pl.json
- Trim dead Flutter rules from .gitignore
Build from backend/Musify.slnx: 0 warnings, 0 errors.