4.3 KiB
4.3 KiB
Quality gates — fyyd provider
LLM-verified gates for architecture/fyyd-provider.md. Automatic tests live
in fyyd/src/lib.rs (provider logic against a fake Fyyd) and
crabidy-server/src/settings.rs (the provider toggle).
Correctness / semantics
- Tree shape matches D3:
/fyyd→search(creatable) +hot;/fyyd/search/<term>and/fyyd/hotlist podcasts as children (not tracks);/fyyd/<branch>/<podcast>lists episodes as tracks;/fyyd/<branch>/<podcast>/<episode>is the track leaf. Verified byroot_lists_search_and_hot,search_terms_list_podcasts_as_children,a_podcast_lists_its_episodes_as_tracks,hot_lists_featured_podcasts. - A podcast listing node is not itself queueable; each podcast child is
queueable and downloadable (queue/capture a whole podcast). An episode
list node is queueable and downloadable.
is_track_pathis true only for episode leaves. Verified by the tests above andtracks_resolve_streams_and_metadata. - Search terms are creatable/renamable/deletable exactly like
tidal/youtube (in-memory, dedup, implicit recreation on stale paths),
and only
/fyyd/searchis creatable. Verified bysearch_terms_list_podcasts_as_children,search_terms_rename_and_delete,foreign_and_malformed_paths_are_rejected. - Playback resolves an episode to its
enclosureURL; a track'sartistis the podcast title andprovider_item_idis the fyyd episode id. Verified bytracks_resolve_streams_and_metadata. - The download blessing is applied centrally (node downloadable when
queueable or track-bearing; children mirror queueability), so
Wcaptures work with no capture-side change.
Robustness (hard rules)
- No panic on any input: malformed/foreign paths, empty create/rename
input, a missing enclosure, and every backend failure return a typed
ProviderError, never a panic. Verified byforeign_and_malformed_paths_are_rejected,a_missing_enclosure_is_an_error_not_a_panic,backend_failures_are_typed_never_panics. - Every external call is bounded by a timeout (
call_timeout_secs, default 30) set on thereqwestclient; every listing is capped (search_results,hot_count,episodes_per_podcast). D5. - fyyd responses decode defensively: the
dataenvelope is unwrapped, DTO fields use#[serde(default)], entries without a usable id are dropped, and non-positive durations degrade toNone. Failures are typedFetchError→ProviderError::FetchError. - No secrets: fyyd's public API uses no credentials, so nothing secret is logged; only public URLs/ids appear in traces.
Integration / operability
fyydis a registered provider: inALL_PROVIDERS,ProviderToggles(all()+provider_toggles()), and the defaultcrabidy-server.toml. Disabling it in theproviderslist drops the/fyydsubtree. Covered by the existing settings tests (which iterateALL_PROVIDERS).- Init is non-fatal: a client that fails to build disables
/fyydwith a warning and leaves every other provider and the server running (D1). A missingfyyd.tomlis normal (no credentials needed). - The orchestrator routes every
ProviderClientmethod for/fyydpaths to the fyyd client (owns-check +fyyd_provider()), andget_lib_rootlistsfyydonly when the client is mounted. No proto change and no newProviderCommandwere needed.
Live validation
- Validated against the real
api.fyyd.deon 2026-07-23. All four endpoints match thefyyd/src/api.rsDTOs exactly: responses are wrapped indata; a podcast carries numericid+title;/podcast/episodesreturns a single object withtitleand anepisodesarray; each episode carriesid(int),title,enclosure(a direct audio URL, e.g. an.mp3),duration(int seconds), andpodcast_id(int);/episode?episode_id=returns one such object. No DTO change was needed. - End-to-end audio playthrough of an enclosure and a
Wcapture to/crabidystill want a manual smoke test on the running server (the resolve path and the capture reqwest client both follow the enclosure, including podtrac/redirect URLs).