From 868e979755c2d7efd569ca0a918556878cad65c5 Mon Sep 17 00:00:00 2001 From: mrwnslz Date: Sat, 24 Oct 2026 16:05:59 +0200 Subject: [PATCH] =?UTF-8?q?docs/radio.md:=20Radio,=20Ambient=20und=20Fokus?= =?UTF-8?q?=20=E2=80=93=20Server,=20Web,=20Android,=20Desktop,=20bekannte?= =?UTF-8?q?=20Schwachstellen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- docs/radio.md | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 docs/radio.md diff --git a/docs/radio.md b/docs/radio.md new file mode 100644 index 0000000..6dfeaeb --- /dev/null +++ b/docs/radio.md @@ -0,0 +1,132 @@ +# Radio, Ambient, Fokus + +The oldest part of diora (it began as a radio player): internet radio with saved stations, +history and track titles, the generative Ambient player, and the web page's Fokus timer. +Written down 2026-10-24 from the code; it is the reference for the three apps. + +## Server: the `radio` app + +Models (`radio/models.py`): + +| model | what | +|---|---| +| `SavedStation` | a user's station: name, url (unique per user), bitrate, country, tags, `favicon_url` (the logo), `is_favorite`, notes | +| `StationPlay` | one listening: station, started/ended; hour, weekday, weekend filled on save (for the recommendations) | +| `TrackHistory` | a track title heard (user, or `session_key` signed out), `scrobbled` | +| `FeaturedStation` | stations picked in the admin for everybody (order, active) | +| `FocusSession` | a finished Fokus round | + +Also: `podcasts.PlayerState` with `kind='radio'` (the player sync between devices) and +`accounts.UserProfile.focus_station_url/_name` (the station the Fokus timer plays). + +Endpoints (`radio/urls.py`): + +- `GET /` — the page (`templates/radio/player.html`). The saved stations, the history (50) and + the featured stations come rendered into it (`#saved-tbody`, `#history-tbody`, + `.featured-section`) and as `INITIAL_SAVED` (with `play_count`), `INITIAL_FEATURED`, + `USER_FOCUS_STATION`. The apps take these parts from it (see below). +- `GET /radio/sse/?url=` — server-sent events with the track title, `data: {"track": …}` when it + changes, `{"error": "stream ended"}` at the end. The server opens the stream itself with + `Icy-MetaData: 1` and reads `StreamTitle` (`radio/icy.py`). +- `POST /radio/record/` — a track into the history; `scrobble: true` sends it to Last.fm when the + account has it linked (`radio/lastfm.py`; "Artist - Title", else the station as artist). +- `POST /radio/save/` (by url: create or update), `/radio/remove//`, `/radio/favorite//`, + `/radio/notes//`; `GET /radio/history/` (last 200), `POST /radio/history//delete/`. +- `POST /radio/play/start/` → `play_id`, `/radio/play/stop/`; `GET /radio/recommendations/` — + the five stations most played around this hour on this kind of day, with a German label. +- `POST /radio/import/` — an `.m3u`/`.m3u8` file into the saved stations (`#EXTINF` names) → + `{added, skipped}`. +- `POST /radio/focus/record/`, `GET /radio/focus/stats/`. +- `GET /radio/affiliate/?track=` — artwork/album from iTunes (an Amazon link only with + `AMAZON_AFFILIATE_ENABLED`). +- `GET /radio/creamfresh-stream/`, `POST /radio/creamfresh-feedback/` — the user's own station + (radio.creamfresh.xyz) through the server, which adds its login; 👍/👎 for the DJ. +- `GET /radio/stream-player/` — an old standalone player page; nothing links to it. + +**Station search** is not on the server: the page asks radio-browser.info directly +(`de1.api.radio-browser.info/json/stations/search`, 50, by click count). A pasted `http(s)://` +address is offered to play or save as it is. + +## Web page (`static/js/app/`) + +| file | what | +|---|---| +| `01-core.js` | the globals: `audio` (one `