Commit Graph

136 Commits

Author SHA1 Message Date
Test User f783e8152b Replace decommissioned Tidal client id
Tidal retired the built-in client id (internal cid 3235): device login
still issues tokens for it, so browsing kept working, but token refresh
returns "Client id 3235 not found" and playbackinfopostpaywall 401s, so
no track ever resolves stream urls. This is a Tidal-side retirement, not
a regression -- the pre-fable code shipped the identical credentials and
fails the same way.

Swap in a currently-live client id + secret, verified end to end against
a real account: refresh succeeds and playbackinfopostpaywall returns a
playable BTS manifest. The XOR "hidden from fulltext search" scheme is
kept, but the obfuscated payloads are now explicit byte arrays rather
than raw control-character string literals, so the change is reviewable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 15:29:44 +02:00
AI User 9706e3b5d2 Survive a permanently dead refresh token while the access token works
A stale config can hold a refresh token issued by a Tidal client that
no longer exists ("Client id ... not found") next to an access token
that is still perfectly valid. The proactive refresh in
ensure_fresh_token treated its own failure as fatal, so every request
died on the unusable refresh token without ever trying the working
access token.

Classify refresh failures: a 4xx from the token endpoint is permanent,
so warn once, stop proactive refreshing, and keep serving with the
current access token; transport errors and 5xx are transient and keep
the retry metadata. The 401-retry in make_request stays as the backstop
for an access token that has actually expired, and a restart heals the
state via the device-login fallback.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 11:48:06 +02:00
AI User 86ce63ae61 Document the as-built architecture
Records the current design in architecture/overview.md: crate topology,
the three concurrency domains and the channels between them, the
filesystem-like path addressing scheme, the span-carrying message
envelope used for tracing, and the Tidal device-flow auth.

Captures the trade-offs behind the load-bearing choices (paths vs opaque
ids, blocking engine thread, lossy broadcast) and the known gaps, so
later changes can see what was traded away.

Adds d2 to devenv for rendering the embedded diagrams.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 10:34:45 +02:00
AI User 634b89b9e8 Fix device login stalling silently until code expiry
Tidal edge WAF rejects HTTP Basic-Auth on /oauth2/token with a 403
HTML page, which the poll loop treated as an ordinary "not yet
authorized" response and retried with no logging, making a broken
login indistinguishable from a slow one until the code expired.

Send the client secret in the request body instead (also fixed for
token refresh), and classify poll outcomes explicitly so pending polls
are logged and real failures surface immediately with the actual
Tidal error.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-20 09:26:48 +02:00
AI User 21e1b10c0b Drop redundant log-to-tracing bridge init
tracing-subscriber .init() already installs the LogTracer via its
default tracing-log feature; the explicit init only produced a startup
warning. Remove it and the direct tracing-log dependency.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 22:01:22 +02:00
AI User d504ebc85f Fix intermittent playback stops and harden the queue
Root causes found and fixed:

- QueueManager could panic and kill the playback task permanently:
  is_last_track() underflowed on an empty queue, remove_tracks accepted
  pos == len (Vec::remove panic) and corrupted positions when removing
  multiple tracks (indices shifted mid-loop), shuffle_behind indexed
  out of range on an empty play order, insert_tracks shifted play-order
  entries by the queue length instead of the inserted count and then
  assert!()ed on the resulting inconsistency, and clear() left
  play_order stale. All mutation methods are now guarded, multi-remove
  works highest-position-first, and an inconsistent play order is
  rebuilt instead of panicking. Regression tests cover these cases.

- The tidal access token was only obtained at startup and never
  refreshed, so long-running sessions ended with every track fetch
  failing (playback just stopped at the next track boundary). Login
  state now lives behind a lock; tokens are refreshed proactively
  before expiry (5 min margin) and once reactively on a 401, and all
  API responses are status-checked (new ClientError::ApiError) instead
  of being fed to the JSON decoder blind. The http client also got a
  30s timeout so a hung connection cannot wedge the provider loop.

- (from the rodio rewrite, same bug class) end of stream used to be
  detected by string-comparing an io::Error message; any other decode
  or network error ended the stream silently without an EndOfStream
  message, so playback never advanced. EOS is now a guaranteed
  callback with a generation counter.

Plus workspace-wide clippy cleanup (zero warnings), cargo-machete
cleanup, and fmt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 22:00:22 +02:00
AI User 56bc0b0d04 Refactor provider addressing to filesystem-like paths
Identifiers like node:tidal / node:playlist:<id> / track:<id> are
replaced by absolute, hierarchical paths that encode the position in
the library tree:

  /                                  global root
  /tidal                             provider root
  /tidal/playlists/<id>              playlist (tracks inside)
  /tidal/playlists/<id>/<track>      track
  /tidal/artists/<id>/<album>        album
  /tidal/artists/<id>/<album>/<t>    track

- proto: uuid -> path, uuids -> paths (same field tags, wire
  compatible); crabidy-core gains ROOT_PATH, parent_path, join_path,
  path_segments helpers with unit tests
- ProviderClient gains is_track_path; the orchestrator routes by path
  prefix and exposes a single ResolveTracks command (track path ->
  that track, node path -> flattened subtree), replacing the
  track:-prefix sniffing in the playback loop
- tidaldy parses paths into a typed TidalPath enum; node parents are
  derived from the request path, which removes the album.artist
  unwrap() panic; the network-dependent scratch test is #[ignore]d
- TUI navigates by paths

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 21:53:12 +02:00
AI User 6d8bc7f166 Overhaul tracing: fix span misattribution, broaden coverage
The old pattern passed a Span in every channel message and entered it
with a guard that was held across await points, which misattributed
events from interleaved tasks. Messages are now {span, command} pairs:
the span is captured automatically at send time (Span::current) and the
consumer instruments the whole handler future with a child span
(playback_command/provider_command with a command name field), so events
are attributed correctly across the queue boundary and all the manual
in_current_span() plumbing is gone.

Also:
- server: EnvFilter with RUST_LOG support (default: own crates at
  debug, rest at info), log-crate bridge for symphonia/cpal
- cbd-tui: logs to a file under the state dir (the terminal belongs to
  the TUI), EnvFilter, no more println into the alternate screen
- tidaldy: fix misused levels (error->debug), structured fields,
  payload dumps moved to trace, login flow at info/warn
- no panic on missing notification daemon in the TUI
- no panic on backwards clock steps in QueueManager
- provider init errors propagate instead of expect()

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 21:48:00 +02:00
AI User 6eb5a87b15 Update all dependencies to current versions
- tonic 0.9 -> 0.14 (tonic-prost/tonic-prost-build split), prost 0.14
- ratatui 0.20 -> 0.30 (Frame no longer generic, Line instead of Spans),
  crossterm 0.29
- rodio 0.17 -> 0.22: replace the custom symphonia decoder with rodio's
  built-in decoder, seeking (try_seek) and position tracking (get_pos);
  end-of-stream is now signalled via an EmptyCallback source with a
  generation counter so a replaced track can never emit a stale EOS
- replace the vendored stream-download crate with the published
  stream-download 0.24 (rustls), with a 30s open timeout
- reqwest 0.12->0.13 (rustls/webpki-roots/query features), base64 0.22
  Engine API, rand 0.10, flume 0.12, thiserror 2, dirs 6, toml 1
- unify everything under [workspace.dependencies]; drop unused deps
  (once_cell, serde_json in server; confique, secrecy in tidaldy)
- devenv: add protobuf (protoc) for prost-build

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 21:41:10 +02:00
AI User 91716daf84 Add devenv-based dev environment and README
Adds devenv.nix (with alsa-lib and pkg-config for rodio/cpal builds),
rust-toolchain.toml pinning stable, and updated .gitignore.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 21:29:23 +02:00
chmanie a1987869c2 Styling fixes
CI checks / stable / fmt (push) Has been cancelled Details
2023-10-08 23:48:49 +02:00
Hans Mündelein d6406d1e09
Add users artists album support
CI checks / stable / fmt (push) Successful in 4s Details
CI release / stable / cross-${{ matrix.target }} (aarch64-unknown-linux-gnu) (push) Successful in 14m15s Details
CI release / stable / cross-${{ matrix.target }} (x86_64-unknown-linux-gnu) (push) Successful in 20m49s Details
CI release / stable / cross-${{ matrix.target }} (armv7-unknown-linux-gnueabihf) (push) Successful in 22m2s Details
2023-06-17 13:16:19 +02:00
Hans Mündelein 24e968ac08
Start playing when adding to an empty queue
CI checks / stable / fmt (push) Successful in 3s Details
CI release / stable / cross-${{ matrix.target }} (x86_64-unknown-linux-gnu) (push) Successful in 5m52s Details
CI release / stable / cross-${{ matrix.target }} (aarch64-unknown-linux-gnu) (push) Successful in 7m41s Details
CI release / stable / cross-${{ matrix.target }} (armv7-unknown-linux-gnueabihf) (push) Successful in 7m40s Details
2023-06-14 11:49:27 +02:00
Hans Mündelein a4303b9b70
Change tidal track duration to milliseconds
CI checks / stable / fmt (push) Successful in 3s Details
2023-06-14 11:31:15 +02:00
Hans Mündelein efc36e88f8
Don't error on failing broadcast channel send
CI checks / stable / fmt (push) Successful in 3s Details
2023-06-14 11:21:33 +02:00
Hans Mündelein 6db11131c0
Fix incorrect offset when turning off shuffle
CI checks / stable / fmt (push) Successful in 4s Details
2023-06-14 11:15:42 +02:00
Hans Mündelein f8a77ee6ed
Stop playing if last track is removed
CI checks / stable / fmt (push) Successful in 3s Details
2023-06-14 10:59:05 +02:00
Hans Mündelein 97ebb44ca5
Fix panic on next on empty queue
CI checks / stable / fmt (push) Successful in 3s Details
2023-06-14 10:51:29 +02:00
Hans Mündelein dfd2e0af92
Fix incorrect position update on queue and insert
CI checks / stable / fmt (push) Successful in 4s Details
2023-06-14 10:44:26 +02:00
Hans Mündelein 5c50544523
Unnwrap server code
CI checks / stable / fmt (push) Successful in 4s Details
2023-06-14 09:57:26 +02:00
chmanie 73cc79d776 Clean up a bit
CI checks / stable / fmt (push) Successful in 4s Details
2023-06-13 10:12:26 +02:00
chmanie a7c2fe391b Try tag filtering again
CI release / stable / cross-${{ matrix.target }} (x86_64-unknown-linux-gnu) (push) Successful in 4m52s Details
CI release / stable / cross-${{ matrix.target }} (aarch64-unknown-linux-gnu) (push) Successful in 6m7s Details
CI release / stable / cross-${{ matrix.target }} (armv7-unknown-linux-gnueabihf) (push) Successful in 7m5s Details
2023-06-13 10:00:16 +02:00
chmanie 2f89886e5d Show album and release date
stable / fmt Details
stable / cross-${{ matrix.target }} (aarch64-unknown-linux-gnu) Details
stable / cross-${{ matrix.target }} (armv7-unknown-linux-gnueabihf) Details
stable / cross-${{ matrix.target }} (x86_64-unknown-linux-gnu) Details
2023-06-13 00:59:54 +02:00
chmanie 4b34fa7233 Add key command to select currently playing track
stable / fmt Details
stable / cross-${{ matrix.target }} (aarch64-unknown-linux-gnu) Details
stable / cross-${{ matrix.target }} (armv7-unknown-linux-gnueabihf) Details
stable / cross-${{ matrix.target }} (x86_64-unknown-linux-gnu) Details
2023-06-13 00:17:56 +02:00
Hans Mündelein 902c0b903f
Replace some unwraps with more error logging
stable / fmt Details
stable / cross-${{ matrix.target }} (aarch64-unknown-linux-gnu) Details
stable / cross-${{ matrix.target }} (armv7-unknown-linux-gnueabihf) Details
stable / cross-${{ matrix.target }} (x86_64-unknown-linux-gnu) Details
2023-06-12 22:34:39 +02:00
Hans Mündelein 5d1a62c630
Add exclude current clearing for the server
stable / cross-${{ matrix.target }} (aarch64-unknown-linux-gnu) Details
stable / cross-${{ matrix.target }} (armv7-unknown-linux-gnueabihf) Details
stable / cross-${{ matrix.target }} (x86_64-unknown-linux-gnu) Details
stable / fmt Details
2023-06-12 22:18:34 +02:00
chmanie 02f47d682b Implement clear queue in tui
stable / fmt Details
stable / cross-${{ matrix.target }} (aarch64-unknown-linux-gnu) Details
stable / cross-${{ matrix.target }} (armv7-unknown-linux-gnueabihf) Details
stable / cross-${{ matrix.target }} (x86_64-unknown-linux-gnu) Details
2023-06-12 22:07:41 +02:00
chmanie 6ac13a710c Fail release workflow if not in tags
stable / fmt Details
stable / cross-${{ matrix.target }} (x86_64-unknown-linux-gnu) Details
stable / cross-${{ matrix.target }} (armv7-unknown-linux-gnueabihf) Details
stable / cross-${{ matrix.target }} (aarch64-unknown-linux-gnu) Details
2023-06-12 21:48:09 +02:00
Hans Mündelein 18671683ff
Add clear queue to server
stable / fmt Details
stable / cross-${{ matrix.target }} (x86_64-unknown-linux-gnu) Details
stable / cross-${{ matrix.target }} (armv7-unknown-linux-gnueabihf) Details
stable / cross-${{ matrix.target }} (aarch64-unknown-linux-gnu) Details
2023-06-12 20:49:37 +02:00
chmanie e71398d243 Add proto definition for ClearQueue
stable / cross-${{ matrix.target }} (aarch64-unknown-linux-gnu) Details
stable / fmt Details
stable / cross-${{ matrix.target }} (armv7-unknown-linux-gnueabihf) Details
stable / cross-${{ matrix.target }} (x86_64-unknown-linux-gnu) Details
2023-06-12 20:23:22 +02:00
chmanie a8ef5545c1 Use chmanie/release-action
stable / cross-${{ matrix.target }} (x86_64-unknown-linux-gnu) Details
stable / cross-${{ matrix.target }} (aarch64-unknown-linux-gnu) Details
stable / cross-${{ matrix.target }} (armv7-unknown-linux-gnueabihf) Details
2023-06-12 20:08:18 +02:00
chmanie 0723ad4914 Generate x86_64 target
stable / fmt Details
stable / cross-${{ matrix.target }} (aarch64-unknown-linux-gnu) Details
stable / cross-${{ matrix.target }} (armv7-unknown-linux-gnueabihf) Details
stable / cross-${{ matrix.target }} (x86_64-unknown-linux-gnu) Details
2023-06-12 18:21:58 +02:00
chmanie 17a0e8606e Try to add release files
stable / fmt Details
stable / cross-${{ matrix.target }} (armv7-unknown-linux-gnueabihf) Details
stable / cross-${{ matrix.target }} (aarch64-unknown-linux-gnu) Details
2023-06-12 18:06:00 +02:00
chmanie 717c5c54a5 Add rust cache action
stable / cross-${{ matrix.target }} (aarch64-unknown-linux-gnu) Details
stable / cross-${{ matrix.target }} (armv7-unknown-linux-gnueabihf) Details
2023-06-12 15:15:55 +02:00
chmanie fe7fd3c63b Add release workflow
stable / fmt Details
stable / cross-${{ matrix.target }} (armv7-unknown-linux-gnueabihf) Details
stable / cross-${{ matrix.target }} (aarch64-unknown-linux-gnu) Details
2023-06-12 14:47:24 +02:00
chmanie a8bb7018b5 Adjust build image tag
stable / cross-${{ matrix.target }} (aarch64-unknown-linux-gnu) Details
stable / cross-${{ matrix.target }} (armv7-unknown-linux-gnueabihf) Details
stable / fmt Details
2023-06-12 14:13:00 +02:00
chmanie 79ac68479e Remove unused import statements
ubuntu / stable / cross-${{ matrix.target }} (aarch64-unknown-linux-gnu) Details
ubuntu / stable / cross-${{ matrix.target }} (armv7-unknown-linux-gnueabihf) Details
ubuntu / stable / fmt Details
2023-06-12 13:03:24 +02:00
Hans Mündelein 0c66165598
Try fixing docker ci build
ubuntu / stable / cross-${{ matrix.target }} (armv7-unknown-linux-gnueabihf) Details
ubuntu / stable / fmt Details
ubuntu / stable / cross-${{ matrix.target }} (aarch64-unknown-linux-gnu) Details
2023-06-12 10:41:13 +02:00
Hans Mündelein 3cff753275
Add cross compile release flow
ubuntu / stable / cross-${{ matrix.target }} (aarch64-unknown-linux-gnu) Details
ubuntu / stable / cross-${{ matrix.target }} (armv7-unknown-linux-gnueabihf) Details
ubuntu / stable / fmt Details
2023-06-12 09:38:19 +02:00
Hans Mündelein d535476d62
Add forgotten files to git
ubuntu / stable / fmt Details
2023-06-12 09:20:57 +02:00
chmanie 2e167e6714 Add simple gitea runner demo
ubuntu / stable / fmt Details
2023-06-11 20:20:37 +02:00
chmanie 7f48bca5df Split up cbd-tui into components 2023-06-11 20:06:06 +02:00
Hans Mündelein f1db89ca99
Add experimental gitea actions
ubuntu / stable / coverage Details
ubuntu / nightly / doc Details
ubuntu / stable / fmt Details
ubuntu / stable / feature-powerset Details
loom Details
ubuntu / stable / minimal-versions Details
miri Details
ubuntu / ${{ matrix.toolchain }} / clippy (beta) Details
ubuntu / ${{ matrix.toolchain }} / clippy (nightly) Details
ubuntu / stable / msrv Details
ubuntu / ${{ matrix.toolchain }} (beta) Details
ubuntu / ${{ matrix.toolchain }} (stable) Details
sanitizers Details
ubuntu / nightly / unused deps Details
ubuntu / ${{ matrix.toolchain }} / clippy (stable) Details
${{ matrix.os }} / stable (windows-latest) Details
${{ matrix.os }} / stable (macos-latest) Details
2023-06-11 18:30:26 +02:00
Hans Mündelein e3cb2e3ad4
Add cross compiling capabilities 2023-06-11 13:39:05 +02:00
chmanie d970e372af TUI: Add config file and argument parsing w/ clap 2023-06-11 01:02:14 +02:00
chmanie a65ad793dc Implement shuffle, repeat and their indicators 2023-06-10 13:01:29 +02:00
Hans Mündelein ef22a84021
Fix toggle shuffle bug 2023-06-10 11:06:42 +02:00
chmanie 773cb511e0 Implement multi track queue, replace, append, insert 2023-06-09 18:47:27 +02:00
Hans Mündelein 06c02ac75c
Fix resume on delte current and queue on empty 2023-06-09 18:44:42 +02:00
Hans Mündelein 258d45a29e
Add quable flag to library children 2023-06-09 17:13:49 +02:00