music on atproto
plyr.fm
1# [plyr.fm](https://plyr.fm)
2
3music on [atproto](https://atproto.com)
4
5check the [plyr.fm artist page](https://plyr.fm/u/plyr.fm) for the latest [auto-generated](.github/workflows/status-maintenance.yml) development podcast!
6
7<details>
8<summary>tech stack</summary>
9
10### backend
11- **framework**: [FastAPI](https://fastapi.tiangolo.com)
12- **database**: [Neon PostgreSQL](https://neon.com)
13- **storage**: [Cloudflare R2](https://developers.cloudflare.com/r2/)
14- **background tasks**: [docket](https://github.com/zzstoatzz/docket) (Redis-backed)
15- **hosting**: [Fly.io](https://fly.io)
16- **observability**: [Pydantic Logfire](https://logfire.pydantic.dev)
17- **auth**: [atproto OAuth 2.1](https://atproto.com/specs/oauth)
18
19### frontend
20- **framework**: [SvelteKit](https://kit.svelte.dev) with Svelte 5 runes
21- **runtime**: [Bun](https://bun.sh)
22- **hosting**: [Cloudflare Pages](https://pages.cloudflare.com)
23- **styling**: vanilla CSS (lowercase aesthetic)
24
25### services
26- **moderation**: Rust ATProto labeler for copyright/sensitive content
27- **transcoder**: Rust audio conversion service (ffmpeg)
28
29</details>
30
31<details>
32<summary>local development</summary>
33
34### prerequisites
35
36- [uv](https://docs.astral.sh/uv/) for Python
37- [bun](https://bun.sh/) for frontend
38- [just](https://github.com/casey/just) for task running
39- [docker](https://www.docker.com/) for dev services (redis)
40
41### quick start
42
43```bash
44# install dependencies
45uv sync
46cd frontend && bun install && cd ..
47
48# start dev services (redis for background tasks)
49just dev-services
50
51# run backend (hot reloads at http://localhost:8001)
52just backend run
53
54# run frontend (hot reloads at http://localhost:5173)
55just frontend dev
56```
57
58### useful commands
59
60```bash
61# run tests
62just backend test
63
64# run linting
65just backend lint
66just frontend check
67
68# database migrations
69just backend migrate "migration message"
70just backend migrate-up
71
72# stop dev services
73just dev-services-down
74```
75
76</details>
77
78<details>
79<summary>features</summary>
80
81### listening
82- audio playback with persistent queue across tabs
83- like tracks, add to playlists
84- browse artist profiles and discographies
85- share tracks, albums, and playlists with link previews
86- unified search with Cmd/Ctrl+K
87- teal.fm scrobbling
88
89### creating
90- OAuth authentication via ATProto (bluesky accounts)
91- upload tracks with title, artwork, tags, and featured artists
92- organize tracks into albums and playlists
93- drag-and-drop reordering
94- timed comments with clickable timestamps
95- artist support links (ko-fi, patreon, etc.)
96
97### data ownership
98- tracks, likes, playlists synced to your PDS as ATProto records
99- portable identity - your data travels with you
100- public by default - any client can read your music records
101
102</details>
103
104<details>
105<summary>project structure</summary>
106
107```
108plyr.fm/
109├── backend/ # FastAPI app
110│ ├── src/backend/ # application code
111│ │ ├── api/ # public endpoints
112│ │ ├── _internal/ # services (auth, atproto, background tasks)
113│ │ ├── models/ # database schemas
114│ │ └── storage/ # R2 adapter
115│ ├── tests/ # pytest suite
116│ └── alembic/ # migrations
117├── frontend/ # SvelteKit app
118│ ├── src/lib/ # components & state
119│ └── src/routes/ # pages
120├── moderation/ # Rust labeler service
121├── transcoder/ # Rust audio service
122├── redis/ # self-hosted Redis config
123├── docs/ # documentation
124└── justfile # task runner
125```
126
127</details>
128
129<details>
130<summary>costs</summary>
131
132~$20/month:
133- fly.io (backend + redis + moderation): ~$14/month
134- neon postgres: $5/month
135- cloudflare (pages + r2): ~$1/month
136- audd audio fingerprinting: $5-10/month (usage-based)
137
138live dashboard: https://plyr.fm/costs
139
140</details>
141
142## links
143
144- **production**: https://plyr.fm
145- **staging**: https://stg.plyr.fm
146- **API docs**: https://api.plyr.fm/docs
147- **python SDK / MCP server**: [plyrfm](https://github.com/zzstoatzz/plyr-python-client) ([PyPI](https://pypi.org/project/plyrfm/))
148- **documentation**: [docs/README.md](docs/README.md)
149- **status**: [STATUS.md](STATUS.md)
150
151### mirrors
152- **github**: https://github.com/zzstoatzz/plyr.fm
153- **tangled**: https://tangled.sh/@zzstoatzz.io/plyr.fm