Commit Graph

7 Commits

Author SHA1 Message Date
Test User bb084dd62b queue, tui: dedup by title, and keep foreign writes off the screen
Four findings from a day's cbd.log and a 121-entry queue.

**Dedup by title.** The provider-item identity removed nothing from that
queue — 121 entries, 121 distinct ids — while three "Sink Into The Hips"
sat in it: two remixes and the album version, 171/189/228s. Those are
different recordings and merging them by default would silently discard a
version the user chose, so the default stands and `DedupQueue` gains a
`by_title` flag: lowercased (artist, title), survivor = the playing entry
else the *longest* take. Own key everywhere (`U`, `queue dedup --titles`)
because it throws recordings away. Duration-tolerant matching was the
third option and is not worth it: every same-title group in that queue
differed by tens of seconds, so a safe tolerance caught nothing.

**stderr no longer points at the terminal.** tracing goes to a file
because the TUI owns the screen, but fd 2 did not — and in the bundled
`cbd` the ALSA C library shares the process, so its "underrun occurred"
printed straight onto the interface, scrolled the terminal a line and
left the layout looking shifted (the queue appearing to bleed into the
now-playing pane; ratatui repaints only changed cells, so it persisted).
The message was lost too. One dup2 before the alternate screen sends fd 2
to `<log dir>/cbd.stderr.log`, so those diagnostics are kept instead.

**The spectrum flapped ~1/s during playback**, 3664 times in one log.
tokio's default MissedTickBehavior::Burst keeps the absolute schedule, so
once the per-tick FFT lateness reaches a whole period two ticks fire back
to back and the second necessarily sees no new frames — read as silence,
which zeroed the bars. Now `Delay`, plus a FlowDetector that wants two
consecutive empty ticks before declaring idle.

**The one ERROR in the log was a shutdown race**, mislabelled: "request
to server failed: sending on a closed channel" was the orchestrator's
send to the UI channel after the UI thread exited. It now reports the UI
closing at info and stops the loop instead of spinning on a stream nobody
reads.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 22:42:04 +02:00
Test User a03e3de84e build: put every provider, opus, and the spectrum behind cargo features
All on by default, so a plain build is unchanged (verified: the default
dependency set for crabidy-server is byte-identical to before). Tailor a
smaller binary with --no-default-features --features …
(architecture/build-features.md).

Compile-time features draw dependency boundaries; the existing
crabidy-server.toml providers list keeps doing per-mount runtime
toggling. The compile-time set bounds the runtime one: a provider built
out cannot be enabled from the config, and naming it earns one startup
warning rather than silence.

- crabidy-server: tidal · youtube · fyyd · abs · soundcloud · jamendo ·
  fs · opus · spectrum · web-ui, plus the all-providers group.
- fs is local files *and* persistent state (D5): the /fs mount, the
  content store behind /crabidy and /orphans, bookmarks/captures, queue
  persistence, and scan. Without it Capture/SaveQueue answer
  Unimplemented and scan says which feature is missing — never a panic.
- opus drops symphonia + symphonia-adapter-libopus, and with them the
  bundled libopus C build (no more cmake requirement). It also decides
  whether scan indexes .opus at all, so scan never indexes what this
  build cannot play. An Ogg-Opus file in an opus-less build reports the
  missing feature and is skipped like any undecodable file.
- spectrum drops realfft and the FFT task; clients just never receive a
  frame. cbd-tui gains notifications (notify-rust, a D-Bus stack).
- crabidy-server/cbd features print the compiled set, and startup logs
  it, so a tailored binary is self-describing.

Not gated, deliberately: [auth]/argon2 (a build ignoring configured
hashes would run open — fail-open security hole), and hls.rs /
spectrum_tap.rs / windowed_http.rs (no dependency of their own, so
gating them buys cfg noise and nothing else).

devenv gains check-features: the curated matrix (defaults, nothing, each
provider alone, each axis dropped, both worked examples, the client
crates) all clippy-clean under -D warnings.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 03:15:32 +02:00
Test User 28a4c155d0 Audio: let `audio-devices <device>` write the config
The audio-devices command only listed. Give it an optional positional
argument: with none it lists as before; with a device name (or
case-insensitive fragment) it writes that into [audio] device in
crabidy-server.toml and then lists, so the same command both configures
and confirms (the chosen device is marked with *). If the fragment
matches no current output device it still writes but warns, mirroring
the server's startup fallback -- so a typo is caught here, not as silent
output.

Plumbing: AudioDevices carries an AudioDevicesArgs { device: Option }
on both `crabidy-server` and `cbd`; cli::audio_devices takes the option
and, when set, loads/updates/stores the settings via the existing
ServerSettings writer. README shows the set form.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 22:44:46 +02:00
Test User 4c0c5f1401 Audio: select the output device (fixes silent output on Raspberry Pi)
The player always opened the system default output device. On a
Raspberry Pi that default is often HDMI, so playback ran but nothing
came out of the headphone jack or a USB/DAC -- "it plays but I hear no
sound".

Add an [audio] device option to crabidy-server.toml: a case-insensitive
substring of the output device name (a memorable fragment is enough).
The player engine opens the first matching device and falls back to the
system default with a warning if none matches. Absent config keeps the
system default, so existing setups are unchanged.

To discover the names, a new `crabidy-server audio-devices` subcommand
(also on `cbd`) lists the output devices and marks the one the current
config selects, using the same match the server applies at startup.

Plumbing: audio_player::output_device_names() enumerates via cpal;
Player::new(Option<String>) replaces the device-less construction
(Default = new(None)); Playback::new takes the device and serve() reads
it from settings. cpal's name() is deprecated in favor of description(),
but name() returns the ALSA-stable string users see in `aplay -l` and
match against, so it is kept behind a documented #[allow(deprecated)].

README documents the [audio] device option under the Pi/config section.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 20:13:23 +02:00
Test User 0a0d50f748 Implement the comprehensive CLI (stage 5)
Every binary is now a clap-derive CLI; no subcommand keeps the current
default (run server / TUI / both).

- cbd-cli: run_remote executes library/queue/global against a running
  server (mirrors RpcClient; direct Stop; connect+request timeouts;
  concise errors; human-readable listings).
- crabidy-server: guard (hash + write [auth], stdin fallback, --no-config),
  scan (walk + write .cbd-track.toml; --capture/--move via new
  CrabidyStore::ingest_file), ServerSettings::store; replaces hash-password.
- cbd-tui: auth writes the client config; config load+override keeps the
  first-run-defaults / flag-overrides-file behavior.
- cbd: union of server + client subcommands.
- build.rs in each binary generates shell completions + man pages
  (OUT_DIR, and CBD_ASSET_DIR when set); devenv gen-cli-assets → dist/.
- README CLI section; tests for parse, config writers, scan/ingest, guard.

Deviations (plan/summary.md): ClapSerde kept; connection flags top-level
(not clap-global, to avoid colliding with auth --address); Box<dyn Error>
CLI reports per existing convention.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 12:13:51 +02:00
Test User 21fc4dc15a Give cbd its own config file, separate from cbd-tui
cbd (server + TUI in one process) and cbd-tui (standalone client) both
read cbd-tui.toml, so pointing that file at a remote server for cbd-tui
also dragged cbds local TUI to the remote while its in-process server
ran unused. cbd now reads its own cbd.toml (same options, same localhost
default that matches its embedded server), so a self-contained cbd and a
remote-pointed cbd-tui coexist on one machine without their address
settings colliding.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 22:57:43 +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