crabidy/quality/audiobookshelf-provider.md

4.6 KiB

Quality gates — audiobookshelf provider

Criteria an implementation of absdy (architecture/audiobookshelf-provider.md) must satisfy. Each is pass/fail by reading/reasoning; automated coverage lives in absdy/src/tests.rs (unit, no network) and absdy/tests/live.rs (ignored, real server).

Secrets (hard rule — highest priority)

  • G1 — api_key never in Debug. Settings and Client Debug output redact the key. (test: settings_debug_redacts_the_api_key.)
  • G2 — The token never reaches a log or error. The ?token= stream URL is built only in Abs::stream_url and returned; it is never passed to debug!/warn!/error!, and never to a reqwest call inside absdy (so it cannot appear in a reqwest error). Browse URLs (logged at debug) carry auth in the bearer header, not the URL.
  • G3 — AbsApi::Debug redacts the token (manual impl, not derived).

Errors and robustness (hard rule: no panics on input/network)

  • G4 — No panics on bad input or network failures. All backend failures map to a typed ProviderError (FetchError for fetches, MalformedPath for bad paths, InvalidInput for empty titles, Config for missing creds). No unwrap/expect/panic! on request, path, or response data. (tests: backend_failures_are_typed_never_panics, foreign_and_malformed_paths_are_rejected, track_metadata_picks_the_file_by_ino.)
  • G5 — Defensive decoding. Wire DTOs use #[serde(default)]; missing fields degrade (empty string / None / dropped entry), never error the whole call. Items/files without an id/ino are dropped, not panicked on.
  • G6 — Timeouts on every external call. AbsApi sets a per-call reqwest timeout from call_timeout_secs (default 30).

Tree and path contract

  • G7 — Path parsing is total and reserved-word safe. Every /abs/... shape maps to a variant or MalformedPath; the literal search segment routes to the search branch and never collides with a (UUID) item id. Empty segments (/abs//x) are MalformedPath.
  • G8 — Root lists only book libraries. Podcast libraries are filtered (D6). (test: root_lists_only_book_libraries.)
  • G9 — Queueability reflects audio. A book child is queueable iff num_audio_files > 0; an ebook is shown but not queueable/downloadable; an audio-less book node is empty and non-queueable. (tests: a_library_lists_search_then_books_with_audio_gating_queueability, an_audio_less_book_is_empty_and_not_queueable.)
  • G10 — Download blessing matches the other providers. A node is downloadable iff it is queueable or holds tracks; a child is downloadable iff it is queueable. The search child is neither.
  • G11 — Search terms mirror tidal/youtube/fyyd and are per-library. search is creatable; terms are editable + deletable, deduped, recreated implicitly on stale paths, isolated per library id, and rename/delete are idempotent. (tests: search_terms_list_books_and_are_per_library, search_terms_rename_and_delete.)

Playback

  • G12 — Stream URL is derivable with no API call. get_urls_for_track builds the URL from the path (item id + ino) plus the token, making no network request; the direct and search branches yield the same URL. (test: track_stream_url_embeds_the_token_and_path_ids_without_a_call.)
  • G13 — Track metadata. artist = author, album = book title, title = file title, duration = seconds (None when absent), provider_item_id = "<item>:<ino>". (test: a_book_lists_its_files_as_tracks.)

Wiring and lifecycle

  • G14 — Non-fatal init. Missing/incomplete abs.toml (no base_url/api_key) disables /abs with a warning; it never aborts startup. (test: init_requires_base_url_and_api_key; orchestrator build block mirrors fyyd.)
  • G15 — Orchestrator parity. abs is wired at every dispatch point (is_track_path, get_urls_for_track, get_metadata_for_track, get_lib_node, create/rename/delete, resolve_tracks_into, root child) and in settings (ALL_PROVIDERS, ProviderToggles, all, provider_toggles).
  • G16 — Bounded listings. items_per_library and search_results cap every listing; truncation to the cap is log-ged, not silent.

Live validation (drift gate)

  • G17 — DTOs decode the real server. absdy/tests/live.rs (#[ignore]) exercises libraries → items → search → detail against a real server and confirms the shapes. Verified during implementation against the provided test server.