fix(web): keep the Rockbox DSP/EQ in the signal path + match CLI compressor
The EQ/DSP had no audible effect on most stations. Cross-origin radio hosts
rarely send CORS headers, so the decoder worker's direct fetch was blocked, the
engine errored, and playback silently fell back to a bare <audio> element that
is not in the Rockbox DSP graph — bypassing EQ, bass, crossfeed, everything.
The CLI decodes natively (no CORS), so it always applied the full chain.
- proxiedStreamUrl now routes every absolute http(s) stream through the
CORS-enabled /api/stream proxy (except HLS and relative/same-origin URLs), so
the worker can always fetch it and the DSP stays in the path.
- Fix the compressor params to match the CLI: makeup=1, ratio as the 0–3 index
(compRatioIndex), knee=2, release=200ms, attack=5ms — was makeup=0, raw ratio,
and zeroed knee/attack/release. Shared via applyCompressor().
The EQ gain math itself was already correct/identical to the CLI (both
rockbox-dsp versions apply the ×10 fixed-point convention internally).
refactor(web): migrate onto @atradio/sdk
The web now consumes the official TypeScript SDK for all record I/O:
- Add an agentAtom that wraps the OAuth-authenticated atcute client via
AtradioAgent.fromClient(client, did) — the single entry point for reads/writes.
- lib/atproto/records.ts becomes a thin facade delegating to the agent, keeping
its (client, did, …) signatures so no consumer changed; favoriteRkey and
StoredStation are re-exported from the SDK.
- favorites.ts uses agent.favorite/unfavorite directly (the SDK bundles the
idempotent write + stationId reconcile), dropping the local putFavorite +
pruneFavoriteDuplicates.
- Extend @atradio/sdk with the verbs the web needs: reaction, updateComment,
deleteComment, generic deleteRecord, listFavorites/listStations (+ StoredStation),
getAudioSettingsRecord.
The favorite record-key logic now has one source of truth (the SDK) shared by
web, CLI (Rust), and the Go/TS SDKs. Web typecheck + all 29 tests pass.
feat: atradio Connect — cross-device remote control
Spotify-Connect-style remote control across web + CLI. Every logged-in
client opens a WebSocket to a new hub in apps/api, is grouped by account
DID (verified via atproto service-auth JWT), and can list and control the
other devices on the account.
- api: WebSocket Connect hub (/connect) with per-DID device registry,
command routing, presence tracking, Redis cross-instance relay, and a
did:web service document. Service-auth JWTs verified with
@atproto/xrpc-server + @atproto/identity.
- web: device picker (navbar) + bottom "Playing on …" banner, transport
routing to the selected remote, transfer-here/there, and WS-driven
deletion of the actor.status record when no player is online.
- cli: remote module (tokio-tungstenite) making the TUI both controllable
and a controller, a device-picker overlay + text indicator, a headless
`--no-tui` daemon mode, and a settings.toml-configurable device name.
- tests: hub integration tests (ws) + web routing/transfer + device
helper tests; new GitHub Actions workflow runs web + api suites.
- i18n: new `connect` namespace for en/fr/pt.
feat: AT Proto social platform — OAuth, PDS-backed data, Jetstream AppView
Turn atradio.fm into a social, AT-Protocol-native app.
Web (apps/web):
- Browser OAuth login (@atcute/oauth-browser-client), Atmosphere login modal
- Gate favoriting/add/remove behind login; public browse/search/playback
- Favorites + user stations stored as fm.atradio.* records in the user's PDS
(optimistic writes, loaded on login)
- Profile shows real display name/handle/avatar; public /profile/:did|:handle
Lexicons (packages/lexicons):
- fm.atradio.{station,favorite,getFavorites,getStations} authored in Pkl
(pkl eval -> JSON) + TS types/zod/mappers
API (apps/api) — Express + Drizzle + Postgres AppView:
- Jetstream consumer across all 4 official hosts, idempotent upserts, cursor
- XRPC getFavorites/getStations (+ discovery), open CORS
- Production TuneIn + ICY media proxies (moved off Vite dev middleware)
Ops & tooling:
- systemd units (api + jetstream)
- tools/console: Babashka + Clojure + rebel REPL command hub, root ./console
- mise pins java/clojure/babashka; consola logging throughout
refactor(web): migrate onto @atradio/sdk
The web now consumes the official TypeScript SDK for all record I/O:
- Add an agentAtom that wraps the OAuth-authenticated atcute client via
AtradioAgent.fromClient(client, did) — the single entry point for reads/writes.
- lib/atproto/records.ts becomes a thin facade delegating to the agent, keeping
its (client, did, …) signatures so no consumer changed; favoriteRkey and
StoredStation are re-exported from the SDK.
- favorites.ts uses agent.favorite/unfavorite directly (the SDK bundles the
idempotent write + stationId reconcile), dropping the local putFavorite +
pruneFavoriteDuplicates.
- Extend @atradio/sdk with the verbs the web needs: reaction, updateComment,
deleteComment, generic deleteRecord, listFavorites/listStations (+ StoredStation),
getAudioSettingsRecord.
The favorite record-key logic now has one source of truth (the SDK) shared by
web, CLI (Rust), and the Go/TS SDKs. Web typecheck + all 29 tests pass.
feat: atradio Connect — cross-device remote control
Spotify-Connect-style remote control across web + CLI. Every logged-in
client opens a WebSocket to a new hub in apps/api, is grouped by account
DID (verified via atproto service-auth JWT), and can list and control the
other devices on the account.
- api: WebSocket Connect hub (/connect) with per-DID device registry,
command routing, presence tracking, Redis cross-instance relay, and a
did:web service document. Service-auth JWTs verified with
@atproto/xrpc-server + @atproto/identity.
- web: device picker (navbar) + bottom "Playing on …" banner, transport
routing to the selected remote, transfer-here/there, and WS-driven
deletion of the actor.status record when no player is online.
- cli: remote module (tokio-tungstenite) making the TUI both controllable
and a controller, a device-picker overlay + text indicator, a headless
`--no-tui` daemon mode, and a settings.toml-configurable device name.
- tests: hub integration tests (ws) + web routing/transfer + device
helper tests; new GitHub Actions workflow runs web + api suites.
- i18n: new `connect` namespace for en/fr/pt.