music on atproto
plyr.fm
1# local development setup
2
3## prerequisites
4
5- **python**: 3.11+ (managed via `uv`)
6- **node/bun**: for frontend development
7- **postgres**: local database (optional - can use neon dev instance)
8- **ffmpeg**: for transcoder development (optional)
9
10## quick start
11
12```bash
13# clone repository
14gh repo clone zzstoatzz/plyr.fm
15cd plyr.fm
16
17# install python dependencies
18uv sync
19
20# install frontend dependencies
21cd frontend && bun install && cd ..
22
23# copy environment template
24cp .env.example .env
25# edit .env with your credentials
26
27# run backend
28uv run uvicorn backend.main:app --reload --host 0.0.0.0 --port 8001
29
30# run frontend (separate terminal)
31cd frontend && bun run dev
32```
33
34visit http://localhost:5173 to see the app.
35
36## environment configuration
37
38### required environment variables
39
40create a `.env` file in the project root:
41
42```bash
43# database (use neon dev instance or local postgres)
44DATABASE_URL=postgresql+asyncpg://localhost/plyr # local
45# DATABASE_URL=<neon-dev-connection-string> # neon dev
46
47# oauth (uses client metadata discovery - no registration required)
48ATPROTO_CLIENT_ID=http://localhost:8001/oauth-client-metadata.json
49ATPROTO_CLIENT_SECRET=<your-client-secret>
50ATPROTO_REDIRECT_URI=http://localhost:5173/auth/callback
51OAUTH_ENCRYPTION_KEY=<base64-encoded-32-byte-key>
52
53# storage (r2 or filesystem)
54STORAGE_BACKEND=filesystem # or "r2" for cloudflare r2
55R2_BUCKET=audio-dev
56R2_PRIVATE_BUCKET=audio-private-dev # for supporter-gated content
57R2_IMAGE_BUCKET=images-dev
58R2_ENDPOINT_URL=<your-r2-endpoint>
59R2_PUBLIC_BUCKET_URL=<your-r2-public-url>
60R2_PUBLIC_IMAGE_BUCKET_URL=<your-r2-image-public-url>
61AWS_ACCESS_KEY_ID=<your-r2-access-key>
62AWS_SECRET_ACCESS_KEY=<your-r2-secret>
63
64# optional: observability
65LOGFIRE_ENABLED=false # set to true to enable
66LOGFIRE_WRITE_TOKEN=<your-token>
67LOGFIRE_ENVIRONMENT=development
68
69# optional: notifications
70NOTIFY_ENABLED=false
71```
72
73### generating oauth encryption key
74
75```bash
76python -c "import base64, os; print(base64.b64encode(os.urandom(32)).decode())"
77```
78
79## database setup
80
81### option 1: use neon dev instance (recommended)
82
831. get dev database URL from neon console or `.env.example`
842. set `DATABASE_URL` in `.env`
853. run migrations: `uv run alembic upgrade head`
86
87### option 2: local postgres
88
89```bash
90# install postgres
91brew install postgresql@15 # macos
92# or use docker
93
94# create database
95createdb plyr
96
97# run migrations
98DATABASE_URL=postgresql+asyncpg://localhost/plyr uv run alembic upgrade head
99```
100
101## running services
102
103### backend
104
105```bash
106# standard run
107uv run uvicorn backend.main:app --reload
108
109# with custom port
110uv run uvicorn backend.main:app --reload --port 8001
111
112# with host binding (for mobile testing)
113uv run uvicorn backend.main:app --reload --host 0.0.0.0 --port 8001
114```
115
116backend api docs: http://localhost:8001/docs
117
118### frontend
119
120```bash
121cd frontend
122
123# development server
124bun run dev
125
126# custom port
127PORT=5174 bun run dev
128
129# expose to network (for mobile testing)
130bun run dev -- --host
131```
132
133frontend: http://localhost:5173
134
135### transcoder (optional)
136
137```bash
138cd transcoder
139
140# install rust toolchain if needed
141rustup update
142
143# install ffmpeg
144brew install ffmpeg # macos
145
146# run transcoder
147cargo run
148
149# with custom port
150TRANSCODER_PORT=9000 cargo run
151
152# with debug logging
153RUST_LOG=debug cargo run
154```
155
156transcoder: http://localhost:8080
157
158## development workflow
159
160### making backend changes
161
1621. edit code in `src/backend/`
1632. uvicorn auto-reloads on file changes
1643. test endpoints at http://localhost:8001/docs
1654. check logs in terminal
166
167### making frontend changes
168
1691. edit code in `frontend/src/`
1702. vite auto-reloads on file changes
1713. view changes at http://localhost:5173
1724. check console for errors
173
174### creating database migrations
175
176```bash
177# make model changes in src/backend/models/
178
179# generate migration
180uv run alembic revision --autogenerate -m "description"
181
182# review generated migration in alembic/versions/
183
184# apply migration
185uv run alembic upgrade head
186
187# test downgrade
188uv run alembic downgrade -1
189uv run alembic upgrade head
190```
191
192see [database-migrations.md](../deployment/database-migrations.md) for details.
193
194### running tests
195
196```bash
197# all tests
198uv run pytest
199
200# specific test file
201uv run pytest tests/api/test_tracks.py
202
203# with verbose output
204uv run pytest -v
205
206# with coverage
207uv run pytest --cov=backend
208
209# watch mode (re-run on changes)
210uv run pytest-watch
211```
212
213## mobile testing
214
215to test on mobile devices on your local network:
216
217### 1. find your local ip
218
219```bash
220# macos/linux
221ifconfig | grep "inet " | grep -v 127.0.0.1
222
223# windows
224ipconfig
225```
226
227### 2. run backend with host binding
228
229```bash
230uv run uvicorn backend.main:app --reload --host 0.0.0.0 --port 8001
231```
232
233### 3. run frontend with network exposure
234
235```bash
236cd frontend && bun run dev -- --host
237```
238
239### 4. access from mobile
240
241- backend: http://<your-ip>:8001
242- frontend: http://<your-ip>:5173
243
244## troubleshooting
245
246### backend won't start
247
248**symptoms**: `ModuleNotFoundError` or import errors
249
250**solutions**:
251```bash
252# reinstall dependencies
253uv sync
254
255# check python version
256uv run python --version # should be 3.11+
257
258# verify environment
259uv run python -c "from backend.main import app; print('ok')"
260```
261
262### database connection errors
263
264**symptoms**: `could not connect to server` or SSL errors
265
266**solutions**:
267```bash
268# verify DATABASE_URL is set
269echo $DATABASE_URL
270
271# test connection
272uv run python -c "from backend.config import settings; print(settings.database.url)"
273
274# check postgres is running (if local)
275pg_isready
276
277# verify neon credentials (if remote)
278# check neon console for connection string
279```
280
281### frontend build errors
282
283**symptoms**: `module not found` or dependency errors
284
285**solutions**:
286```bash
287# reinstall dependencies
288cd frontend && rm -rf node_modules && bun install
289
290# clear cache
291rm -rf frontend/.svelte-kit
292
293# check node version
294node --version # should be 18+
295bun --version
296```
297
298### oauth redirect errors
299
300**symptoms**: `invalid redirect_uri` or callback errors
301
302**solutions**:
303```bash
304# verify ATPROTO_REDIRECT_URI matches frontend URL
305# should be: http://localhost:5173/auth/callback
306
307# check ATPROTO_CLIENT_ID is accessible (should return client metadata JSON)
308curl http://localhost:8001/oauth-client-metadata.json
309```
310
311### r2 upload failures
312
313**symptoms**: `failed to upload to R2` or storage errors
314
315**solutions**:
316```bash
317# verify credentials
318echo $AWS_ACCESS_KEY_ID
319echo $AWS_SECRET_ACCESS_KEY
320echo $R2_BUCKET
321
322# test r2 connectivity
323uv run python -c "
324from backend.storage import get_storage_backend
325storage = get_storage_backend()
326print(storage.bucket_name)
327"
328
329# or use filesystem backend for local development
330STORAGE_BACKEND=filesystem uv run uvicorn backend.main:app --reload
331```
332
333## useful commands
334
335```bash
336# backend
337uv run uvicorn backend.main:app --reload # start backend
338uv run pytest # run tests
339uv run alembic upgrade head # run migrations
340uv run python -m backend.utilities.cli # admin cli
341
342# frontend
343cd frontend && bun run dev # start frontend
344cd frontend && bun run build # build for production
345cd frontend && bun run preview # preview production build
346cd frontend && bun run check # type check
347
348# transcoder
349cd transcoder && cargo run # start transcoder
350cd transcoder && cargo test # run tests
351cd transcoder && cargo build --release # build for production
352```
353
354## next steps
355
356- read [backend/configuration.md](../backend/configuration.md) for config details
357- read [frontend/state-management.md](../frontend/state-management.md) for frontend patterns
358- read [tools/](../tools/) for development tools (logfire, neon, pdsx)
359- check [deployment/](../deployment/) when ready to deploy