contributing
plyr.fm is open source. development happens on GitHub (mirrored to tangled.org). contributions welcome — fork the repo and open a PR.
using a coding assistant?
Section titled “using a coding assistant?”give your assistant the project’s llms.txt. Preview it here, copy the full text with one click, then tell your assistant what you want to work on.
# plyr.fm
> An open-source audio streaming app on ATProto. Discover music and other audio,
> inspect tracks and playlists, and publish with the creator's authorization.
> Public records live on the user's PDS; private playlists stay in plyr.fm.
## Start here
- [Agent guide](https://docs.plyr.fm/developers/agents/): choose a surface, discover capabilities, search, inspect, and verify results.
- [Agent guide as Markdown](https://docs.plyr.fm/agents.md): the same guide without the documentation UI.
- [Developer quickstart](https://docs.plyr.fm/developers/quickstart/): working Python SDK and HTTP examples.
- [OpenAPI JSON](https://api.plyr.fm/openapi.json): current HTTP paths, parameters, response schemas, and authentication requirements.
- [Interactive API reference](https://api.plyr.fm/docs): browse the live schema.
## Choose a surface
- HTTP: `https://api.plyr.fm`. Choose it for other languages, exact response schemas, or API-only features. Public search and track metadata need no token.
- Python SDK: `uv add plyrfm` for typed, composable sync/async workflows. Operations use namespaces: `client.discover.search(...)`, `client.tracks.get(...)`, `client.playlists.list()`.
- CLI: `uvx plyrfm --help` for terminal reads and authorized uploads, edits, and library changes. Use SDK objects or HTTP JSON for programmatic output.
- Hosted MCP: `https://plyrfm.fastmcp.app/mcp`. Discover its tools at connection time. It exposes read-only catalog and library operations; it does not play audio in the user's browser or mutate their library.
- Local MCP: `uvx --prerelease=allow plyrfm-mcp`. Optional `PLYR_TOKEN` enables the user's private library reads; the hosted server accepts `x-plyr-token`.
- App: [plyr.fm](https://plyr.fm) for listening, publishing, and account settings.
## Search, inspect, then act
1. `GET https://api.plyr.fm/search/?q=ambient&type=tracks&limit=5`
2. Read `results` and its `type` discriminator. `counts` describes this response, not the full catalog. Search takes a 2–100 character query and a limit of 1–50 per type; narrow the query rather than inventing an offset.
3. `GET https://api.plyr.fm/tracks/{id}` for a chosen track. Inspect its title, artist, tags, visibility, gating, and labels. Search hits are summaries, not full track records.
4. Share `https://plyr.fm/track/{id}`. HTTP track detail returns its audio URL in `r2_url`; the Python SDK calls it `audio_url`. Use that returned URL, preserving any access requirements. There is no `/tracks/{id}/stream` endpoint.
A metadata response is not proof that audio played. A successful audio fetch is
not proof that the listener heard it. Unknown duration or missing metadata remains
unknown; don't invent a value or describe the sound from its title alone.
## Account actions and visibility
- [Authentication](https://docs.plyr.fm/developers/auth/): developer tokens come from [settings](https://plyr.fm/settings#developer). Browser sessions use HttpOnly cookies. Keep tokens out of URLs, logs, and public files.
- Uploads, likes, comments, playlist edits, and deletions can change the user's library or PDS. Use the SDK, CLI, or HTTP API only with authorization for the action; read the affected resource afterward to verify it.
- Public playlists publish ATProto records; private playlists remain in plyr.fm's database. Experimental private tracks use permissioned data on a compatible PDS. Supporter gating is a separate access model.
- [Moderation](https://docs.plyr.fm/moderation/): discovery filtering differs from direct access. Labels are assertions; a successful metadata lookup does not waive access controls or authorize downloading.
## Contributing to plyr.fm
- [Contributor guide](https://docs.plyr.fm/contributing/): local setup and the contribution workflow.
- [Project status](https://github.com/zzstoatzz/plyr.fm/blob/main/STATUS.md): read first for active work and known issues.
- [Repository instructions](https://github.com/zzstoatzz/plyr.fm/blob/main/AGENTS.md): project conventions, commands, and deployment boundaries.
- [Contribution skill](https://github.com/zzstoatzz/plyr.fm/blob/main/.agents/skills/contribute/SKILL.md): fork, implement, validate, and prepare a pull request. Read the checked-out repository's instructions before editing; use its justfiles for commands.
## Guides and source
- [Listeners](https://docs.plyr.fm/listeners/): player, queue, playlists, downloads, and shortcuts.
- [Creators](https://docs.plyr.fm/artists/): uploads, artwork, embeds, visibility, and export.
- [Sensitive content](https://docs.plyr.fm/sensitive-content/): artwork and audio preferences.
- [Lexicons](https://docs.plyr.fm/lexicons/overview/): environment-aware ATProto records; production uses `fm.plyr`, staging `fm.plyr.stg`, development `fm.plyr.dev`. NSIDs are identifiers, not URLs.
- [Troubleshooting](https://docs.plyr.fm/troubleshooting/): authentication, playback, and indexing.
- [App source](https://github.com/zzstoatzz/plyr.fm)
- [SDK, CLI, and MCP source](https://github.com/zzstoatzz/plyr-python-client)
## publishing access
Portal defaults apply to future uploads; track and album overrides save explicit policies. Listening, original downloads, discovery, and rights metadata are separate. Protected managed audio uses private storage; native Spaces retain their authority boundary. Downloads off does not prevent recording playback or revoke previous copies. See https://docs.plyr.fm/developers/publishing/ for the current contract and client migration.
For repository work, use the repository’s contribute skill for richer agent context. Shared skills live in .agents/skills, which Codex discovers automatically; Claude Code reads the same files through .claude/skills symlinks.
prerequisites
Section titled “prerequisites”quickstart
Section titled “quickstart”# fork on github, then clone your forkgh repo fork zzstoatzz/plyr.fm --clonecd plyr.fm
# install dependenciesuv sync --directory backendcd frontend && bun install && cd ..
# configure environmentcp backend/.env.example backend/.envcp frontend/.env.example frontend/.env# edit backend/.env — see the setup guide linked below for details# frontend/.env defaults to the local backend on :8001 and works as-issetup db (if running a local Postgres instance)
Section titled “setup db (if running a local Postgres instance)”just backend db-initjust backend migrate-uprunning the stack
Section titled “running the stack”# start redis (required for background tasks)just dev-services
# terminal 1 — backend (port 8001, hot reloads)just backend run
# terminal 2 — frontend (port 5173, hot reloads)just frontend runthe backend needs a Postgres connection. you can use the Neon dev instance or a local Postgres — set DATABASE_URL in your .env. see backend/.env.example for all configuration options and the local development setup guide for detailed walkthrough.
workflow
Section titled “workflow”- check
STATUS.mdfor active tasks - open an issue describing the change
- fork the repo, branch from
main, make your changes - open a PR from your fork
useful commands
Section titled “useful commands”just backend run # start backendjust frontend run # start frontendjust dev-services # start redisjust backend test # run tests (spins up isolated postgres + redis)just backend lint # type check + ruffjust frontend check # svelte type checkjust backend db-init # setup local postgres databasejust backend migrate-up # apply database migrationsconventions
Section titled “conventions”- type hints required everywhere (Python and TypeScript)
- async everywhere — never block the event loop
- writing: concise, conversational, and usually lowercase; preserve proper names and technical identifiers
- SvelteKit with Svelte 5 Runes (
$state,$derived,$effect) - use
uvfor Python (neverpip) - add regression tests when fixing bugs
detailed internal documentation (environment setup, deployment, architecture) is in docs/internal/.