Live testing showed SoundCloud only serves the progressive+audio/mpeg
transcoding to anonymous clients: its media exchange returns 200 with a direct,
range-streamable mp3 URL (cf-media.sndcdn.com, 206, audio/mpeg), while the plain
hls+audio/mpeg exchange 404s for every track (streamable or not). The provider
picked HLS, so every queued track failed to resolve a URL and was skipped.
pick_stream_url now prefers progressive, falling back to hls. A progressive URL
is a plain mp3 the player streams on its normal windowed-HTTP path (no .m3u8, so
HlsStream is bypassed); HlsStream stays the fallback for HLS-only tracks. A
genuinely restricted track (Go+/label preview, geo-blocked) still 404s the
exchange and is skipped, not crashed. Note: SoundCloud login does not help here
- public streaming is client_id-only.
Verified live: search a streamable track -> resolve -> 206 range GET returns
audio/mpeg with an mp3 frame-sync header. 19 unit tests pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
New `soundclouddy` crate mounted at `/soundcloud`: search tracks and
playlists, resolve permalink URLs, and — with an optional OAuth token — the
user's likes and playlists (public browse/play needs only a client_id). Ran the
full dev-flow: architecture/soundcloud-provider.md, quality/soundcloud-provider.md,
plan/soundcloud.md, plan/summary.md.
- Provider logic over an `Sc` reqwest seam (faked in tests): creatable
`search`/`resolve` parents, canonical `track/<id>` and `playlist/<id>`
leaves, playlist hydration, download blessing — mirrors abs/fyyd.
- Auth: `client_id` from config or scraped from soundcloud.com (pure parsers,
unit-tested), re-scraped once on 401; scraped id persisted via `settings()`.
- Playback: a new `HlsStream` SourceStream in audio-player streams the m3u8's
mp3 segments in order as one continuous mp3; `open_source` routes `.m3u8` to
it, non-seekable so symphonia never end-seeks a length-less stream.
- Wired into crabidy-server the standard way (settings toggle, sc_owns/
sc_provider, non-fatal build block, root child, dispatch arms).
Verified offline: soundclouddy 19 tests, audio-player 14 (incl. HLS-parser),
crabidy-server 77+4 — all green; fmt/clippy/machete clean. The live client_id
scrape, real JSON shapes, and mp3-HLS play-to-EOS need real SoundCloud access
and are covered by tests/live.rs + #[ignore] gates (quality G7/G8/G14/G19).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>