Commit Graph

8 Commits

Author SHA1 Message Date
Test User dbd1b955fb Implement the crabidy content store and single /crabidy provider
Replace /queues + /bookmarks + /captures with one /crabidy fs provider
whose track tomls link into a content-addressed store that de-duplicates
audio by provider id and by content hash (architecture/crabidy-store.md).
Green-field: no data migration.

- crabidy_store.rs: CrabidyStore owns the state tree (state_dir/crabidy)
  and the data store (data_dir/crabidy); StoreIndex derived from the
  .cbd-store.toml sidecars. save() enumerates a source into a temp folder
  and swaps it in atomically (conflict refuses); capture_track dedups
  (already-stored -> provider-id -> hash -> new). Queue persistence lives
  here now (persist_current/load_current/save_snapshot/spawn_persister).
- capture.rs: reduced to enumerate + Downloader::download_to + Progress;
  removed the Sink/capture_into/download-to-toml machinery.
- orchestrator: one crabidy_client + crabidy_store, single crabidy_owns
  routing; get_lib_node annotates captured tracks via the store index.
- rpc: capture_error_status helper; save_queue link-saves the live queue
  into /crabidy. playback persists/restores via CrabidyStore.
- tidal/youtube set Track.provider_item_id (track id / video id).
- cbd-tui: /crabidy/current, captured | row marker, delete confirmation
  removed (deletes never touch the store), cache-invalidation + help text.
- delete bookmark_store/capture_store/queue_store; supersede their docs.

See plan/summary.md for deviations (SaveQueue RPC kept; fs id left empty;
shallow folder marking).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 11:34:40 +02:00
Test User 80c4b6e7ed Implement mute (it was a stub)
Toggling mute did nothing: the server logged a FIXME and never touched
the player, and the TUI ignored the Mute stream update. Now the player
engine mutes by zeroing the sink volume and remembering the level to
restore (setting the volume unmutes), ToggleMute drives it and
broadcasts the new state, the TUI shows a Muted marker in the
now-playing pane, and the web client mute button already reflected the
Mute update so it now works too.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 00:20:01 +02:00
Test User 572be04206 Add a server-streamed frequency spectrum visualizer
A row of frequency bars under the track progress, on by default and
toggleable with the client spectrum config option. Because the audio
plays on the server and clients may be remote, the spectrum is produced
server-side, not captured locally: audio-player taps its own output
into a lock-free ring on the audio thread (one store per sample, no
locks), crabidy-server runs a Hann + realfft over 2048 samples at 20fps,
folds it into log-spaced bars, and broadcasts them as a new SpectrumFrame
on the update stream. The task idles when nothing is playing or no
client is listening. The TUI renders block-glyph bars in the now-playing
pane; the web client renders the same bins as CSS bars.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 23:14:03 +02:00
Test User 550237f69a Add a / search filter to the TUI library and queue panes
Pressing / in either pane opens a live case-insensitive substring
filter: typing narrows the visible rows, Enter keeps the filter and
returns to navigation, Esc clears it. A shared Filter helper keeps each
pane full list intact and maps view indices to real ones, so movement
keys work on the filtered view unchanged and the queue maps a filtered
selection back to the real server position before removing or setting
current. The library resets search on node change; the queue preserves
it across stream updates.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 22:54:39 +02:00
Test User 4e59e50943 Gate the gRPC surface behind basic-auth roles
crabidy-server.toml gains an [auth] section with one argon2 PHC hash
per role: owner (everything), queue-owner (queue and playback, no
library writes), queue-appender (browse, search, and Append only).
Enforcement is a single fail-closed tower layer in front of the tonic
service — unknown methods require owner, a malformed config aborts
startup, and a missing one keeps the server open as before. Successful
credentials are cached so argon2 runs once, failures re-verify at full
cost and stay indistinguishable. crabidy-server hash-password turns a
stdin password into the config hash; cbd-tui sends the header from new
user/password options.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 20:47:06 +02:00
Test User 1c83217745 Delete captures from disk at any depth, behind a confirmation
Deletion (d) previously reached only top-level folders of editable
stores. /captures now exposes its whole tree: nested folders delete
recursively, single tracks delete their metadata file plus the
downloaded audio next to it (never audio outside the instance root).
Tracks advertise this through the new LibraryNode.tracks_deletable
flag. Because these deletes destroy slow-to-redo downloads, the TUI
asks delete <title>? [y/N] first; cheap deletables (search terms,
bookmarks, saved queues) stay unconfirmed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 20:34:03 +02:00
Test User 214dece19f Make download captures incremental with skipped tracks and progress
Download captures now write straight into captures/<name>: satisfied
entries are reused, uncapturable tracks are recorded as skipped tomls
(a new fourth playable, marked red in the TUI and skipped by playback
with a bounded pass), and a failed run keeps its progress so capturing
the same name resumes it. The capture RPC replies on acceptance and
streams CaptureProgress over the update stream, rendered as status
lines in the library pane; help and the input overlay warn that
captures are slow. Colored list items switch to a dark foreground
under the focused selection bar so they stay readable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 15:45:04 +02:00
Test User 58f7f9c66b Bundle server and TUI into a single cbd binary
crabidy-server and cbd-tui become libraries with thin mains:
crabidy_server::serve(addr) hosts the whole server stack,
cbd_tui::run(config) the client loops. The new cbd binary logs both
halves to one file, starts the server in-process, waits for the socket
(adopting an already-running standalone server on an occupied port),
and runs the TUI against it over the unchanged localhost gRPC wire.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 14:15:47 +02:00