Document the providers and their configuration in READMEs
The root README covers the binaries, quick start, the config directory, and links per-provider READMEs; each provider README explains how the provider works, how it is used from the TUI, and its config file with every option and default (tidaly.toml, fsdy.toml, ytdy.toml, cbd-tui.toml). The fsdy README doubles as the reference for the .cbd-track.toml on-disk format shared by queues, bookmarks, and captures. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
c84dec9ca2
commit
383173046d
100
README.md
100
README.md
|
|
@ -1 +1,101 @@
|
|||
# crabidy
|
||||
|
||||
A client/server music player. A headless gRPC server owns the library,
|
||||
the play queue, and audio output; a terminal UI connects to it over
|
||||
localhost (or the network). Media comes from pluggable **providers**,
|
||||
each mounted as a subtree of one library:
|
||||
|
||||
```text
|
||||
/
|
||||
├── tidal Tidal streaming (see tidaldy/README.md)
|
||||
├── youtube YouTube search & playlists (see ytdy/README.md)
|
||||
├── fs a local music folder (see fsdy/README.md)
|
||||
├── queues saved play queues (managed by the server)
|
||||
├── bookmarks link snapshots of library subtrees (`w`)
|
||||
└── captures downloaded snapshots with local audio (`W`)
|
||||
```
|
||||
|
||||
## Binaries
|
||||
|
||||
- `crabidy-server` — the server: providers, queue, playback, gRPC on
|
||||
`0.0.0.0:50051`.
|
||||
- `cbd-tui` — the terminal client. Press `?` inside for all key
|
||||
bindings.
|
||||
- `cbd` — both in one process: starts the server, waits until it
|
||||
accepts connections, then runs the TUI. Adopts an already-running
|
||||
server instead of failing on an occupied port.
|
||||
|
||||
## Quick start
|
||||
|
||||
The toolchain is managed by [devenv](https://devenv.sh):
|
||||
|
||||
```sh
|
||||
devenv shell # provides rust, yt-dlp, and friends
|
||||
cargo run -p cbd # server + TUI in one process
|
||||
```
|
||||
|
||||
Or run the halves separately: `cargo run -p crabidy-server` and, in
|
||||
another terminal, `cargo run -p cbd-tui`.
|
||||
|
||||
## Configuration
|
||||
|
||||
All configuration lives in `~/.config/crabidy/` (the platform config
|
||||
directory). Every file is optional; missing providers simply do not
|
||||
mount. Files are created/rewritten on first start with their defaults
|
||||
filled in.
|
||||
|
||||
| File | Component | Documentation |
|
||||
| -------------- | --------- | -------------------------------------- |
|
||||
| `tidaly.toml` | Tidal | [tidaldy/README.md](tidaldy/README.md) |
|
||||
| `ytdy.toml` | YouTube | [ytdy/README.md](ytdy/README.md) |
|
||||
| `fsdy.toml` | local fs | [fsdy/README.md](fsdy/README.md) |
|
||||
| `cbd-tui.toml` | TUI / cbd | below |
|
||||
|
||||
The server-managed folders (`queues/`, `bookmarks/`, `captures/`) also
|
||||
live in `~/.config/crabidy/`; they need no configuration and hold plain
|
||||
folders of track files in the format documented in
|
||||
[fsdy/README.md](fsdy/README.md).
|
||||
|
||||
### `cbd-tui.toml`
|
||||
|
||||
Configuration of the TUI (and the TUI half of `cbd`):
|
||||
|
||||
```toml
|
||||
# Where to find the server. Default shown.
|
||||
address = "http://127.0.0.1:50051"
|
||||
```
|
||||
|
||||
Every option is also available as a command-line flag
|
||||
(`cbd-tui --address ...`).
|
||||
|
||||
## Using the library
|
||||
|
||||
Navigation is vim-style: `j`/`k` select, `l` enters the selected
|
||||
folder, `h` goes to the parent, `Tab` switches between library and
|
||||
queue, `Enter` replaces the queue with the selection. `%` creates a
|
||||
node where the pane title shows `% to add` (e.g. a search term), `e`
|
||||
renames, `d` deletes.
|
||||
|
||||
- `w` saves the selection as a **bookmark** (links; needs the source
|
||||
provider to replay) or, in the queue pane, saves the queue.
|
||||
- `W` **captures** the selection: the subtree is mirrored under
|
||||
`/captures/<name>` with every track's audio downloaded next to its
|
||||
metadata — fully local playback afterwards. Captures are incremental:
|
||||
re-capturing the same name resumes and completes it; tracks whose
|
||||
source cannot be captured are recorded as *skipped* (red in the UI,
|
||||
skipped by playback). Download captures can take long; progress is
|
||||
shown in the library pane.
|
||||
|
||||
Press `?` for the full binding table.
|
||||
|
||||
## Logs
|
||||
|
||||
`cbd` and `cbd-tui` log to `~/.local/state/crabidy/` (daily files);
|
||||
`crabidy-server` logs to stderr. Stream URLs and credentials are
|
||||
redacted from logs by design.
|
||||
|
||||
## Development
|
||||
|
||||
Design documents live in `architecture/`, per-feature quality gates in
|
||||
`quality/`, and implementation plans in `plan/`. See `CLAUDE.md` /
|
||||
`AGENTS.md` for the development workflow and coding rules.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
# fsdy — the filesystem provider
|
||||
|
||||
Mounts a local directory at **`/fs`** in the crabidy library. The same
|
||||
engine also powers the server-managed mounts `/queues`, `/bookmarks`,
|
||||
and `/captures` — everything below applies to their on-disk format too.
|
||||
|
||||
## How it works
|
||||
|
||||
The provider walks one configured root directory. Every **directory**
|
||||
becomes a queueable library node; every file ending in
|
||||
**`.cbd-track.toml`** becomes a track; everything else (audio files,
|
||||
covers, hidden entries) is invisible to the library. Listing order is
|
||||
case-insensitive by file name — prefix files with numbers to fix an
|
||||
order (the capture/bookmark writers use `0001`-style prefixes for
|
||||
exactly this reason).
|
||||
|
||||
A track file carries the track's metadata plus a reference to the
|
||||
playable thing:
|
||||
|
||||
```toml
|
||||
title = "We Will Rock You" # required
|
||||
artist = "Queen" # optional
|
||||
duration = 122 # optional, seconds
|
||||
|
||||
[album] # optional
|
||||
title = "News of the World"
|
||||
release_date = "1977-10-28"
|
||||
|
||||
# Exactly ONE of the following four:
|
||||
[playable]
|
||||
file = "we-will-rock-you.flac" # local audio; absolute, or relative to
|
||||
# this file's directory (relocatable)
|
||||
# url = "https://example.org/radio.mp3" # http(s) stream
|
||||
# link = "/tidal/artists/1/2/3" # another provider's track
|
||||
# skipped = true # no audio: a capture recorded its
|
||||
# source as uncapturable; shown red,
|
||||
# skipped by playback
|
||||
```
|
||||
|
||||
Malformed track files are skipped with a warning; they never break the
|
||||
listing. `link` playables resolve exactly one hop (a link to a link
|
||||
fails at play time), which keeps cycles impossible.
|
||||
|
||||
## Configuration — `~/.config/crabidy/fsdy.toml`
|
||||
|
||||
```toml
|
||||
# Absolute path of the directory to expose under /fs.
|
||||
# Default: the platform music directory (e.g. ~/Music). When neither is
|
||||
# available the /fs mount is disabled — the rest of the server runs on.
|
||||
# root = "/home/me/music"
|
||||
```
|
||||
|
||||
## The server-managed instances
|
||||
|
||||
`/queues`, `/bookmarks`, and `/captures` are fsdy instances over
|
||||
`~/.config/crabidy/{queues,bookmarks,captures}/`, written by the server
|
||||
(queue persistence, `w`, `W`) and readable/editable with any file
|
||||
manager — they are just folders of the track files described above.
|
||||
Their top-level folders can be renamed (`e`) and deleted (`d`) from the
|
||||
TUI; `/queues/current` is the continuously persisted play queue and is
|
||||
protected.
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
# tidaldy — the Tidal provider
|
||||
|
||||
Mounts your Tidal account at **`/tidal`** in the crabidy library, using
|
||||
Tidal's web API.
|
||||
|
||||
## How it works
|
||||
|
||||
On first start the provider runs Tidal's OAuth **device login**: the
|
||||
server prints a `link.tidal.com` verification URL to its stdout (for
|
||||
`cbd`, check the log file) — open it in a browser, authorize, and the
|
||||
provider finishes logging in by itself. The obtained tokens are written
|
||||
back into the config file and refreshed automatically from then on; you
|
||||
should not need to log in again.
|
||||
|
||||
The tree offers your playlists, favorite artists (with their albums and
|
||||
tracks), and a **search** node: press `%` under `/tidal/search` to
|
||||
create a search term; the term becomes a persistent child node holding
|
||||
its results (tracks, artists, albums). Terms are renamable (`e`,
|
||||
re-runs the search) and deletable (`d`).
|
||||
|
||||
Everything queueable is also capturable: `w` bookmarks a subtree as
|
||||
links, `W` downloads a subtree's audio into `/captures/<name>`.
|
||||
|
||||
## Configuration — `~/.config/crabidy/tidaly.toml`
|
||||
|
||||
The file is rewritten on every server start with the current values
|
||||
(including refreshed tokens), so **it contains your credentials — keep
|
||||
it private**. All options with their defaults:
|
||||
|
||||
```toml
|
||||
# Tidal API endpoints. Change only if you know why.
|
||||
base_url = "https://api.tidal.com/v1"
|
||||
hifi_url = "https://api.tidalhifi.com/v1"
|
||||
|
||||
# Stream quality: "Low" | "High" | "Lossless" | "HiRes".
|
||||
audio_quality = "Lossless"
|
||||
|
||||
# Managed by the provider: filled in by the device login and refreshed
|
||||
# automatically. Delete this whole section to force a fresh login.
|
||||
[login]
|
||||
# device_code = ...
|
||||
# user_id = ...
|
||||
# country_code = ...
|
||||
# access_token = ...
|
||||
# refresh_token = ...
|
||||
# expires_after = ...
|
||||
|
||||
# OAuth client identity used for the device flow. Working defaults are
|
||||
# built in; override only to use your own client registration.
|
||||
[oauth]
|
||||
# client_id = ...
|
||||
# client_secret = ...
|
||||
base_url = "https://auth.tidal.com/v1/oauth2"
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- A missing or empty file is fine: defaults are used and the device
|
||||
login runs on the next start.
|
||||
- If Tidal ever invalidates the session (long offline periods), delete
|
||||
the `[login]` section and restart.
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
# ytdy — the YouTube provider
|
||||
|
||||
Mounts YouTube at **`/youtube`** in the crabidy library.
|
||||
|
||||
## How it works
|
||||
|
||||
Metadata comes from the pure-Rust [rustypipe] Innertube client:
|
||||
|
||||
- **`/youtube/search`** works without any login. Press `%` to create a
|
||||
search term; the term becomes a persistent child node listing the top
|
||||
video results as tracks. Terms are renamable (`e`, re-runs the
|
||||
search) and deletable (`d`). Terms live in memory — a server restart
|
||||
forgets them, but navigating to an old term path recreates it.
|
||||
- **`/youtube/playlists`** appears when a cookie login is configured
|
||||
and accepted: it lists the account's saved playlists.
|
||||
|
||||
**Stream URLs** are resolved through a minimal `yt-dlp` sidecar when
|
||||
the binary is available (one bounded subprocess call per played or
|
||||
captured track — no other functionality uses it). This is a deliberate
|
||||
concession: YouTube currently caps tokenless stream URLs at their
|
||||
leading ~1 MiB, and `yt-dlp` is the only maintained solver for the
|
||||
cipher challenges that lift the cap; its URLs stream whole files at a
|
||||
throttled ~32 KB/s (still ~2× audio bitrate). Without the binary the
|
||||
provider still works, but playback of a track stops after roughly a
|
||||
minute. Streams prefer `audio/mp4` (AAC), the format the built-in
|
||||
player decodes; captures therefore store `.m4a` files. See
|
||||
`architecture/youtube-rustypipe.md` for the full analysis.
|
||||
|
||||
Search results and playlists are downloadable: `W` captures them with
|
||||
audio into `/captures/<name>` (slow under the throttle, but captures
|
||||
are resumable — re-capture the same name to continue).
|
||||
|
||||
## Configuration — `~/.config/crabidy/ytdy.toml`
|
||||
|
||||
All options with their defaults:
|
||||
|
||||
```toml
|
||||
# Netscape cookies.txt export for the logged-in features (saved
|
||||
# playlists). Export with a browser extension like "Get cookies.txt
|
||||
# LOCALLY", ideally from an Incognito session you close afterwards.
|
||||
# The provider caches the rotated session cookie under
|
||||
# ~/.config/crabidy/rustypipe/, so the export only needs to be valid
|
||||
# once. Default: unset (logged out; search still works).
|
||||
# cookies = "/home/me/youtube-cookies.txt"
|
||||
|
||||
# Results per search term. Default: 20.
|
||||
# search_results = 20
|
||||
|
||||
# Per-request timeout in seconds (Innertube calls; the yt-dlp sidecar
|
||||
# gets at least 60 s). Default: 30.
|
||||
# call_timeout_secs = 30
|
||||
|
||||
# The yt-dlp binary used ONLY for stream URLs. A bare name resolves
|
||||
# via PATH. Default: "yt-dlp". Keep it fresh — a stale yt-dlp is the
|
||||
# first suspect when YouTube playback breaks.
|
||||
# binary = "yt-dlp"
|
||||
|
||||
# Optional rustypipe-botguard binary for PO-token attestation (also
|
||||
# auto-detected on PATH). Currently ineffective upstream, but once
|
||||
# rustypipe's web-client deciphering is fixed this makes streams work
|
||||
# without yt-dlp. Default: unset.
|
||||
# botguard_bin = "/home/me/.cargo/bin/rustypipe-botguard"
|
||||
```
|
||||
|
||||
Secrets: the cookies file and the rustypipe cache grant access to your
|
||||
YouTube session — keep both private. The provider never logs their
|
||||
contents (paths only), and stream URLs are redacted from logs.
|
||||
|
||||
## Failure behavior
|
||||
|
||||
- No network / broken client at startup: only `/youtube` is disabled,
|
||||
the server runs on.
|
||||
- Rejected or missing cookies: degrades to logged-out with a warning.
|
||||
- Missing `yt-dlp`: warning at startup; streams stop after ~1 MiB.
|
||||
- `403 Forbidden` on streams in the log: YouTube's per-IP/per-video
|
||||
enforcement fluctuates; usually transient. If it persists, update
|
||||
`yt-dlp`.
|
||||
|
||||
[rustypipe]: https://crates.io/crates/rustypipe
|
||||
Loading…
Reference in New Issue