radieo/README.md
Pierre-Olivier Mercier 29ab0be7cb Milestone 1: Liquidsoap broadcasting skeleton
Liquidsoap (v2.4.5) container that plays the /cache directory in random
order and broadcasts it over HTTP at :8000/radio.mp3 (MP3 192 kbps).
mksafe guarantees a continuous stream (silence when the cache is empty).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 16:14:14 +08:00

73 lines
2.8 KiB
Markdown

# radieo
A personal music radio: an always-on HTTP audio stream, automatically fed from
several sources and broadcast with [Liquidsoap](https://www.liquidsoap.info/).
The goal is a hassle-free stream that always has something playing, where the
next track is picked automatically. It is meant for personal use (a couple of
simultaneous listeners), not for public broadcasting.
## How it works
radieo is built as two layers, each running in its own Docker container and
sharing a cache volume:
- **`ingest`** (Python) — the brain. It decides what to play next, resolves and
downloads tracks into a local cache, keeps a pre-filled queue, and exposes the
next track over HTTP. *(planned — see roadmap below)*
- **`stream`** (Liquidsoap) — deliberately dumb. It broadcasts the audio over
HTTP and never goes silent thanks to a local fallback.
Playback sources (planned): a [Navidrome](https://www.navidrome.org/) library
via the OpenSubsonic API, arbitrary tracks fetched with
[yt-dlp](https://github.com/yt-dlp/yt-dlp) (Bandcamp, SoundCloud, YouTube…), and
listening suggestions from a ListenBrainz RSS feed.
## Usage
Requirements: Docker with Compose v2.
```sh
# Drop some .mp3 files into the cache directory
cp /path/to/music/*.mp3 cache/
# Build and start the stream
docker compose up -d
# Listen (VLC, a browser, any audio player)
# http://localhost:8000/radio.mp3
```
Stop it with `docker compose down`.
The stream is MP3 at 192 kbps. Multiple clients can listen at the same time.
New files dropped into `cache/` are picked up automatically (the playlist is
reloaded when the directory changes).
## Current status
**Milestone 1 — broadcasting skeleton: done.**
- Liquidsoap (v2.4.5) container plays the `cache/` directory in random order.
- HTTP stream served at `http://localhost:8000/radio.mp3` (MP3, 192 kbps).
- Continuous output guaranteed: silence rather than a crash when the cache is
empty (`mksafe`).
- Multiple simultaneous listeners supported.
At this stage the playlist is filled manually; the automatic ingestion layer is
not implemented yet.
## Roadmap
1.**Broadcasting skeleton** — Liquidsoap serving the cache directory.
2. **Ingestion daemon** — Python daemon exposing `GET /next`; Liquidsoap
switches to a `request.dynamic` source with the cache as fallback.
3. **Navidrome provider** — play from an OpenSubsonic playlist, with caching,
LRU retention and play history.
4. **yt-dlp provider** — fetch tracks from a maintained URL/artist list; weighted
mixing between sources.
5. **Canonicalizer** — ListenBrainz MBID lookup for source-agnostic
de-duplication.
6. **ListenBrainz provider** — parse the RSS suggestions feed and resolve each
one to Navidrome or yt-dlp.
7. **Polish** — crossfade, robustness, optional web player, config file.