plyr.fm#
audio streaming app
check the plyr.fm artist page for the latest auto-generated development podcast!
tech stack
backend#
- framework: FastAPI
- database: Neon PostgreSQL
- storage: Cloudflare R2
- background tasks: docket (Redis-backed)
- hosting: Fly.io
- observability: Pydantic Logfire
- auth: atproto OAuth 2.1
frontend#
- framework: SvelteKit with Svelte 5 runes
- runtime: Bun
- hosting: Cloudflare Pages
- styling: vanilla CSS (lowercase aesthetic)
services#
- transcoder: Rust audio conversion service (ffmpeg, Fly.io)
- moderation: Rust ATProto labeler for copyright/sensitive content (Fly.io)
- mood search: CLAP audio embeddings (Modal)
- genre classification: effnet-discogs ML tagging (Replicate)
- vector search: turbopuffer for semantic audio queries
local development
prerequisites#
quick start#
# install dependencies
uv sync
cd frontend && bun install && cd ..
# start dev services (redis for background tasks)
just dev-services
# run backend (hot reloads at http://localhost:8001)
just backend run
# run frontend (hot reloads at http://localhost:5173)
just frontend run
useful commands#
# run tests
just backend test
# run linting
just backend lint
just frontend check
# database migrations
just backend migrate "migration message"
just backend migrate-up
# stop dev services
just dev-services-down
features
listening#
- audio playback with persistent queue across tabs
- like tracks, add to playlists
- browse by artist, album, tag, or playlist
- share tracks and albums with embeddable players and link previews
- mood search - describe a vibe, get matching tracks (CLAP embeddings)
- unified search with Cmd/Ctrl+K (fuzzy match across tracks, artists, albums, tags, playlists)
- genre browsing and tag filtering
- platform media controls (Media Session API)
- teal.fm scrobbling and now-playing reporting
creating#
- OAuth authentication via ATProto (bluesky accounts), multi-account support
- upload tracks with title, artwork, tags, and featured artists
- lossless audio support (AIFF/FLAC) with automatic MP3 transcoding for universal playback
- auto-tagging via ML genre classification
- organize tracks into albums and playlists with drag-and-drop reordering
- timed comments with clickable timestamps
- artist support links and supporter-gated content
- copyright scanning via audio fingerprinting
- content reporting and automated sensitive content filtering
data ownership#
- tracks, likes, playlists synced to your PDS as ATProto records
- bulk media export (download all your tracks)
- portable identity - your data travels with you
- public by default - any client can read your music records
some features may be paywalled in the future for the financial viability of the project. if you have thoughts on what should or shouldn't be gated, open a discussion on GitHub or tangled.
project structure
plyr.fm/
├── backend/ # FastAPI app & Python tooling
│ ├── src/backend/ # application code
│ ├── tests/ # pytest suite
│ └── alembic/ # database migrations
├── frontend/ # SvelteKit app
│ ├── src/lib/ # components & state
│ └── src/routes/ # pages
├── services/
│ ├── transcoder/ # Rust audio transcoding (Fly.io)
│ ├── moderation/ # Rust content moderation (Fly.io)
│ └── clap/ # ML embeddings (Python, Modal)
├── infrastructure/
│ └── redis/ # self-hosted Redis (Fly.io)
├── docs/ # documentation
└── justfile # task runner
costs
~$25/month:
- fly.io (backend + transcoder + redis + moderation): ~$14/month
- neon postgres: $5/month
- cloudflare (pages + r2): ~$1/month
- audd audio fingerprinting: $5-10/month (usage-based)
- modal (CLAP embeddings): free tier / scales to zero
- replicate (genre classification): <$1/month
live dashboard: https://plyr.fm/costs
links#
- docs: https://docs.plyr.fm
- production: https://plyr.fm
- staging: https://stg.plyr.fm
- API docs: https://api.plyr.fm/docs
- python SDK / MCP server: plyrfm (PyPI)
- status: STATUS.md