# 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.