74 lines
3.6 KiB
Markdown
74 lines
3.6 KiB
Markdown
# Task plan — jamendo provider
|
||
|
||
Ordered, independently verifiable tasks for `/jamendo`
|
||
(architecture/jamendo-provider.md, quality/jamendo.md). Each cites the gate/test
|
||
it satisfies.
|
||
|
||
## Crate scaffold
|
||
|
||
- [x] **P1** — Add `jamendody` to `Cargo.toml` workspace `members`; create
|
||
`jamendody/Cargo.toml` mirroring `soundclouddy` (deps: `async-trait`,
|
||
`crabidy-core`, `reqwest`, `serde`, `thiserror`, `tokio`, `toml`, `tracing`;
|
||
dev-dep `tokio` full). *Verify:* `cargo metadata` lists the crate.
|
||
|
||
## Network seam (api.rs)
|
||
|
||
- [x] **P2** — Domain types `JamTrack` (id, title, artist, album,
|
||
duration_secs) and `JamAlbum` (id, title, artist, tracks); `FetchError`
|
||
(`Http`, `Decode`, `NotFound`, `Unauthorized`, `NotStreamable`). *Verify:*
|
||
compiles. (G2)
|
||
- [x] **P3** — `Jam` trait: `search_tracks`, `search_albums`, `album_tracks`,
|
||
`track_detail`, `track_stream`. Doc each with error behavior. *Verify:*
|
||
compiles. (G2/G10)
|
||
- [x] **P4** — `JamApi` (reqwest): `new(client_id, audioformat, timeout)` with a
|
||
timeout-bearing client and redacting `Debug`; `get_json` helper adding
|
||
`client_id` + `format=json`, decoding the `{headers, results}` envelope,
|
||
mapping non-success/404/401 to `FetchError`. `limit.min(200)` cap. *Verify:*
|
||
`cargo build`. (G3/G4/G5/G9)
|
||
- [x] **P5** — Defensive DTOs (`#[serde(default)]`) for track/album/envelope and
|
||
`into_track`/`into_album`. *Verify:* build + a decode unit test on a JSON
|
||
fixture. (G9)
|
||
|
||
## Provider logic (lib.rs)
|
||
|
||
- [x] **P6** — `PROVIDER_ROOT`, segment consts, `JamPath` enum + `parse_path`.
|
||
*Verify:* T2, `foreign_and_malformed_paths_reject`. (D3)
|
||
- [x] **P7** — `Settings` (`client_id: Option<String>` + bounds) with redacting
|
||
`Debug`. *Verify:* T10 `settings_debug_redacts_client_id`. (G3)
|
||
- [x] **P8** — `Client` + `with_api`, search-term store helpers
|
||
(`store_add/remove/snapshot`), `search_limit`/`album_limit`. *Verify:*
|
||
compiles. (D3)
|
||
- [x] **P9** — Node builders: `root_node` (search only), `store_node`,
|
||
`search_term_node` (tracks + albums), `album_node`, `list_node`, `jam_track`.
|
||
*Verify:* T1, T4, T5, T7, T11. (D3/D4)
|
||
- [x] **P10** — `ProviderClient` impl: `init` (require non-empty `client_id`,
|
||
else `Config`), `settings`, `is_track_path`, `get_urls_for_track` (→
|
||
`track_stream`), `get_metadata_for_track`, `get_lib_root`, `get_lib_node`
|
||
(download blessing), `create/rename/delete_lib_node`. *Verify:* T3, T6, T8,
|
||
T9. (D4/G1/G2/G6)
|
||
|
||
## Tests (tests.rs)
|
||
|
||
- [x] **P11** — `FakeJam` + `FailingJam` fixtures and all T1–T11. *Verify:*
|
||
`cargo test -p jamendody`. (all tests)
|
||
|
||
## Orchestrator + settings wiring (crabidy-server)
|
||
|
||
- [x] **P12** — `settings.rs`: `jamendo` in `ALL_PROVIDERS` (8→9),
|
||
`ProviderToggles.jamendo`, defaults, `provider_toggles()`. *Verify:* build +
|
||
existing settings tests. (G6)
|
||
- [x] **P13** — `provider.rs`: `jamendo_client` field, `jamendo_owns`,
|
||
`jamendo_provider`, non-fatal `build()` block (reads `jamendo.toml`),
|
||
`is_track_path` arm, `get_lib_root` child (gated on `is_some`), `get_lib_node`
|
||
arm, `get_urls_for_track`/`get_metadata_for_track` arms, create/rename/delete
|
||
arms. *Verify:* `cargo build -p crabidy-server`. (D1/G6/G8)
|
||
|
||
## Verification gate
|
||
|
||
- [x] **P14** — `cargo fmt --check`, `cargo clippy -- -D warnings`,
|
||
`cargo test` green across the workspace. Re-read G1–G10 against the diff.
|
||
- [ ] **P15 (live gate, R1)** — with a real `client_id` in `jamendo.toml`, browse
|
||
`/jamendo/search/<term>`, queue and **play** a track: confirm audio plays via
|
||
the existing windowed-HTTP path, seek bar correct (duration from metadata),
|
||
clean EOS, no panic. Deferred to a run with network + a key.
|