Admin b61ff1ebec
CI/CD / BackEnd (push) Successful in 1m22s
CI/CD / WebPlayer (push) Successful in 38s
CI/CD / Deploy (push) Skipped
CI/CD / BackEnd (pull_request) Successful in 1m21s
CI/CD / WebPlayer (pull_request) Successful in 40s
CI/CD / Deploy (pull_request) Skipped
Make Discover and upload genres dynamic
Discover used a hardcoded list of genre tiles that searched track titles,
and the upload form kept its own copy of the genre list and compatibility
rules. Both now come from the backend.

Backend:
- GET /genres returns genres with at least one track and their counts.
- GET /genres/available returns every assignable genre with the genres
  it cannot be combined with, so the rules live only in
  GenreCompatibility.
- GET /tracks accepts ?genre= (case-insensitive, 400 on unknown values).

web-player:
- Discover tiles are built from /genres; each opens /explore?genre=X, a
  song list filtered by that tag. Unknown genres fall back to a label
  derived from their name.
- The upload form loads /genres/available, disables incompatible genres
  as you pick, and the server validates before reserving upload URLs.
- lib/data/trackGenres.ts is removed; its helpers merge into
  lib/data/genres.ts.
- Regenerate the typed API client.
2026-09-23 20:05:30 +02:00

Musify

A music streaming app. Upload your own tracks, organize them into playlists and albums, and stream them back with a web player built around your listening habits: auto-generated mixes, a home feed, search, likes, and an Explore page for finding something new.

What it does

  • Upload and organize your own music: tracks, albums, cover art, artist credits.
  • Playlists, likes, and a "recently played" history.
  • Mixes, playlists generated automatically from what you actually listen to.
  • Explore, genre browsing and search across tracks, albums, playlists and people.
  • Real streaming, not just file downloads: seek, queue, shuffle, repeat.
  • Accounts via OIDC login, with an optional anonymous listening mode.
  • Light and dark themes, and a layout that works on both desktop and mobile.

How it's built

Backend: .NET 10, split into a REST API, a background worker, and a dedicated streaming gateway.

  • Clean architecture (Domain / Application / Infrastructure) with CQRS (Mediator) and ErrorOr results.
  • PostgreSQL via EF Core for metadata. RabbitMQ and MassTransit handle async processing (audio transcoding, thumbnails) with saga-based rollback.
  • SeaweedFS (S3-compatible) for file storage. Uploads and downloads go straight between the client and storage, never through the API.
  • ffmpeg for audio transcoding, ImageSharp for thumbnails.
  • A YARP reverse proxy that streams the audio and checks a signed, short-lived ticket on every request.
  • Zitadel for login (OIDC/OAuth2).

Web client: SvelteKit 2 on Svelte 5 (runes), TypeScript, Tailwind 4. The session lives on the server, so the backend access token never reaches the browser.

Infrastructure: Musify's own apps run from Docker Compose (dev and production overlays). Postgres, RabbitMQ, SeaweedFS, Zitadel and Jaeger are shared infrastructure that lives in a separate Infrastructure git repository, used by Musify and by other projects that need the same OIDC provider and object storage.

Project layout

backend/      .NET solution: Api, Worker, StreamingGateway, and the shared Domain/Application/Infrastructure
web-player/   the SvelteKit web client
deploy/       Docker Compose stacks for dev and production
docs/         architecture notes, one file per area

Postgres, Zitadel, SeaweedFS, RabbitMQ and Jaeger are not part of this layout. They live in the separate Infrastructure repository instead.

Digging deeper

docs/ covers how each piece works and why: architecture, authentication, storage, streaming, media processing, and the local dev setup. deploy/README.md has the full instructions for running the stack, in development or on a production server.

S
Description
No description provided
Readme
5.1 MiB
Languages
C# 73.1%
Svelte 12.1%
TypeScript 9%
Kotlin 4.1%
CSS 1.1%
Other 0.6%