Skip to main content

Terminal Radio

English · 繁體中文

A terminal screen beside the RADIO word mark, in gradient ASCII art

python Textual FastAPI player stations themes i18n platform license

A terminal player for Taiwanese radio stations. The Textual interface and FastAPI control API share the same service layer.

Prerequisites

Radio requires Python 3.12 or later, uv and mpv. Install mpv for your system first:

# macOS (Homebrew)
brew install mpv

# Ubuntu / Debian
sudo apt update
sudo apt install mpv

# Arch Linux
sudo pacman -S mpv

For other systems, see the mpv installation guide. After installation, run mpv --version to confirm that the command is available in your terminal.

macOS and Linux only. The player reaches mpv over a unix socket. Windows is not supported and is not tested. It runs under WSL.

Install

curl -LsSf https://raw.githubusercontent.com/yueswater/terminal-radio/main/install.sh | sh

That installs uv if it is missing, then the radio command, and tells you how to install mpv if you do not have it. Nothing needs cloning.

If you would rather do it yourself:

uv tool install radiotui-tw
# or
pipx install radiotui-tw

To remove it, run uv tool uninstall radiotui-tw.

Working on the project

Clone the repository and install it in editable form, so changes take effect without reinstalling:

make link      # uv tool install --editable . --force
make unlink    # remove it again

Run

radio                     # terminal interface
radio ui --no-autoplay    # do not resume the last station at startup
radio api                 # HTTP API; docs at http://127.0.0.1:8000/docs
radio stations --band AM
radio --help

Without installing, use make run, make api or uv run radio ....

Terminal interface

The tabs include Home, FM, AM, Favorites, History, Statistics, Themes, Settings and About. Every launch starts on Home, even when the app resumes the last station. The bottom bar shows the playback state, frequency, station, program title, elapsed time, audio output, sleep timer and volume. Click the playback state at the bottom left to pause or resume. The output device name is limited to fifteen characters. By default, the last station resumes at startup.

Key Action
Move to the previous or next tab
j k Move the cursor
enter Use the selected item: play or resume a station, apply a theme or change a setting
space Pause or resume
s Stop playback
f Add or remove a favorite
+ = Raise the volume
- _ Lower the volume
m Mute or unmute
t Switch to the next theme
e Export settings
i Import settings
w Switch between English and Traditional Chinese
/ Search all built-in and custom stations
? Open the keyboard shortcut guide
q Quit

Scrolling

When columns are wider than the window, use a mouse or trackpad to scroll horizontally. The horizontal scrollbar is hidden so it does not look like a volume bar. The left and right arrow keys still only switch tabs.

If all rows fit on screen but the columns are too wide, scrolling down moves right and scrolling up moves left. When more rows are available below, the wheel keeps its normal vertical movement. The FM, AM, Favorites, History and Settings tables stay centred with the same space above and below. Their pages remain fixed while only the table rows scroll.

Favorites, volume, mute, autoplay, reconnect, station checks, language, the last station and the active theme are stored in <state>/state.json.

Configuration files

File Contents
app/data/stations.toml Station slug, name, band, frequency and stream URL
app/data/themes.yml All color palettes and the default theme
app/data/locales/*.yml English and Traditional Chinese interface text
app/tui/radio.tcss Terminal interface layout
<state>/history.jsonl Listening history, with one JSON event per line
<state>/state.json Favorites, volume, mute, autoplay, animations, language, station and theme
<state>/custom-stations.toml Stations added from the Settings page

<state> is the per-user directory the program writes to, outside the installation, so upgrading or reinstalling never loses a history: ~/Library/Application Support/terminal-radio on macOS and ~/.local/state/terminal-radio on Linux. RADIO_DATA_DIR overrides it.

The bundled catalogue, themes and locales are read-only. To use your own without touching the installation, drop a stations.toml, themes.yml or locales/ into the config directory, ~/Library/Application Support/terminal-radio on macOS and ~/.config/terminal-radio on Linux, and it is read in preference.

Built-in stations live in app/data/stations.toml. You can also open Custom stations from Settings to add, edit or delete a local station without changing the project file. Custom stream URLs must use HTTP or HTTPS. To add a built-in station, append a block to app/data/stations.toml:

[[stations]]
slug = "example"
name = "Example FM"
band = "FM"
frequency = "99.9"
description = "Optional description"
url = "https://example.com/live/playlist.m3u8"

Audio output

The bottom bar shows where the sound is being sent. mpv only reports auto, so macOS runs system_profiler SPAudioDataType in the background every fifteen seconds and caches the result. On other platforms, or when detection fails, the app shows the name of the mpv output driver instead.

Playback tools

Press / to search by frequency, station name, description or band. Results update while you type, and enter plays the highlighted station.

Automatic reconnect is enabled by default. When a stream drops, Radio retries after 1, 2, 4, 8 and 15 seconds. It stops retrying after the fifth failure. You can turn this off in Settings.

The sleep timer can be turned off or set to 15, 30, 60 or a custom number of minutes from 1 to 1440. Its countdown appears in the bottom bar and only lasts for the current run.

Radio can check whether station streams are online, slow or offline. Automatic checks are cached for five minutes, and Check all stations now runs a fresh check. At most four streams are checked at once.

Languages

Radio currently includes only English and Traditional Chinese. Their messages are stored in app/data/locales/en.yml and app/data/locales/zh-Hant.yml, and Traditional Chinese is the default. Press w to switch between them. The Settings page also shows the current language.

All text written by the app is translated. Station names, descriptions and program titles come from the catalog or stream data, so they remain in their original language. When interface text changes, update both locale files. If a translation key is missing, the app first falls back to Traditional Chinese and then displays the key itself.

Themes, settings and about

The Themes page previews every palette in app/data/themes.yml. Each card uses its own background, foreground and color swatches. Press enter to apply the selected theme. When you return to this page, the cursor stays on the active theme.

The Settings page includes autoplay, reconnect, sleep timer, station checks, custom stations, keyboard shortcuts, animations, language, theme and volume. Press enter to change an editable item. Read-only items show their value and the environment variable that can override it. Select Restore defaults and confirm to reset preferences while keeping favorites, custom stations, the last station and listening history.

Animations are off by default.

The About page shows the version, copyright and packages used by the app. The author, year and project URL are defined in app/core/about.py.

Exporting and importing settings

Press e, or select Export settings, to list the available Desktop, Documents, Downloads, home, project and data folders. Press enter to write the file or escape to cancel.

The file name follows the format settings_<timestamp>.radio.config, with time recorded to the millisecond.

{
  "version": "0.1.0",
  "exported_at": "2026-08-30T13:44:24.355+08:00",
  "settings": { "...": "..." },
  "preferences": { "favorites": [], "volume": 100, "...": "..." },
  "custom_stations": []
}

Press i to search the same folders for .radio.config files, listed from newest to oldest. Importing restores custom stations, favorites, volume, mute, theme, language, autoplay, reconnect, station checks and animations. Every page is updated at once. Older exports without custom_stations remain supported.

The app only applies the preferences section. The settings section records the environment at the time of export, so its paths and commands belong to the original device and are not transferred during import. Files with an invalid format or the wrong value types are rejected. Stations that no longer exist are also removed from favorites and the last-played record.

Listening history

Each session start, session end, play, pause and resume is written to <state>/history.jsonl with timing data. A play_ended event records the total elapsed, paused and interrupted time. Listening time excludes both pauses and reconnect interruptions. The table always uses HH:MM:SS.

Select Export CSV to save the complete station summary with a UTF-8 BOM. Column names follow the current interface language. Select Clear listening history and confirm to remove all saved events.

The Statistics page reads the complete valid history and draws terminal charts for total listening time, play count, active days, the ten most-listened stations, a 14-day trend, weekdays, time of day and FM/AM share. Only completed plays are counted.

API endpoints

Method Path Purpose
GET /stations?band=FM&q=police List stations, optionally filtered by band or search text
GET /stations/{slug} Get one station
GET /player Get playback state, program title and timers
POST /player/play Play a station
POST /player/toggle Toggle playback for a station
POST /player/pause Pause playback
POST /player/resume Resume playback
POST /player/stop Stop playback
GET /history Get recent listening events
GET /history/summary Get listening totals for each station
GET /themes List available themes

Contributing and security

Read CONTRIBUTING.md before submitting a pull request. Report security issues privately by following SECURITY.md, and do not open a public issue. All participants must follow the Code of Conduct.

License

Radio uses the MIT License.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

radiotui_tw-0.2.0.tar.gz (87.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

radiotui_tw-0.2.0-py3-none-any.whl (96.8 kB view details)

Uploaded Python 3

File details

Details for the file radiotui_tw-0.2.0.tar.gz.

File metadata

  • Download URL: radiotui_tw-0.2.0.tar.gz
  • Upload date:
  • Size: 87.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for radiotui_tw-0.2.0.tar.gz
Algorithm Hash digest
SHA256 ea4be0dc91df7cb5312c44ebe7d5a0ba316a63f930278c8e02790724d888b0b2
MD5 306ca791594412c1570cc123a4fb8474
BLAKE2b-256 e67942431e40630319beddd5f434b8f8c883887a719b86c6bc6811053762befa

See more details on using hashes here.

Provenance

The following attestation bundles were made for radiotui_tw-0.2.0.tar.gz:

Publisher: publish.yml on yueswater/terminal-radio

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file radiotui_tw-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: radiotui_tw-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 96.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for radiotui_tw-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2d8c509ac35905fe8ca8ff2584e9f1eecc271bb757feaa8569ef8d0e85db7092
MD5 efdbcc7fb5f0b1599c0b02932f4b5591
BLAKE2b-256 7597000f0b2c8f53d7dd12cb99dde2ebb07efe3ac648404171fce31edf913322

See more details on using hashes here.

Provenance

The following attestation bundles were made for radiotui_tw-0.2.0-py3-none-any.whl:

Publisher: publish.yml on yueswater/terminal-radio

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.3.3

2 files

0.3.2

2 files

0.3.1

2 files

0.3.0

2 files

This release

0.2.0 This release

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page