@ciphore/radiocli 0.1.9 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +65 -0
- package/README.md +107 -411
- package/SECURITY.md +6 -3
- package/dist/activity/stats.js +14 -2
- package/dist/cli.js +53 -3
- package/dist/player/airplay-discovery.js +16 -4
- package/dist/player/airplay-worker-protocol.js +1 -0
- package/dist/player/airplay-worker.js +4 -1
- package/dist/player/command.js +37 -14
- package/dist/player/mpv-ipc-client.js +194 -0
- package/dist/player/player-controller.js +179 -98
- package/dist/providers/cache.js +77 -13
- package/dist/providers/provider-manager.js +28 -8
- package/dist/providers/radio-browser.js +141 -57
- package/dist/safety.js +44 -0
- package/dist/storage/store.js +264 -149
- package/dist/types.js +2 -53
- package/dist/ui/AdaptiveContent.js +332 -0
- package/dist/ui/App.js +489 -54
- package/dist/ui/AppContent.js +11 -12
- package/dist/ui/app-state.js +26 -16
- package/dist/ui/ascii.js +42 -1
- package/dist/ui/components/Logo.js +6 -1
- package/dist/ui/components/ScreenHeader.js +2 -2
- package/dist/ui/components/StationList.js +8 -6
- package/dist/ui/components/TopTabs.js +8 -7
- package/dist/ui/cosmo-land-data.js +1 -1
- package/dist/ui/exit-confirmation.js +7 -0
- package/dist/ui/explore-map-layout.js +3 -1
- package/dist/ui/format.js +56 -2
- package/dist/ui/help-content.js +10 -2
- package/dist/ui/layout.js +26 -9
- package/dist/ui/page-footer.js +36 -13
- package/dist/ui/playback-footer.js +15 -2
- package/dist/ui/screen-items.js +60 -21
- package/dist/ui/screen-meta.js +35 -0
- package/dist/ui/screens/AirPlaySettingsScreen.js +4 -2
- package/dist/ui/screens/CountriesScreen.js +8 -5
- package/dist/ui/screens/ExploreScreen.js +1 -1
- package/dist/ui/screens/HelpScreen.js +17 -3
- package/dist/ui/screens/HomeScreen.js +2 -3
- package/dist/ui/screens/MapScreen.js +2 -2
- package/dist/ui/screens/NowPlayingScreen.js +31 -51
- package/dist/ui/screens/SearchScreen.js +1 -1
- package/dist/ui/screens/SettingsScreen.js +109 -10
- package/dist/ui/screens/StationScreen.js +14 -1
- package/dist/ui/screens/StatsScreen.js +35 -44
- package/dist/ui/system-actions.js +10 -3
- package/dist/ui/terminal-mouse.js +44 -0
- package/dist/ui/theme.js +0 -1
- package/dist/ui/use-app-input.js +37 -6
- package/dist/ui/use-command-executor.js +34 -1
- package/dist/ui/visualizers/receiver-style-registry.js +101 -0
- package/dist/ui/visualizers/receiver-visualizers.js +2856 -745
- package/dist/update-check.js +122 -0
- package/docs/THIRD_PARTY_NOTICES.md +7 -0
- package/package.json +2 -2
- package/dist/ui/screens/screen-render.test.js +0 -214
package/README.md
CHANGED
|
@@ -6,160 +6,32 @@
|
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
[](package.json)
|
|
8
8
|
|
|
9
|
-
RadioCLI
|
|
9
|
+
RadioCLI brings live radio to your terminal. Browse stations around the world,
|
|
10
|
+
save favorites, and listen through `mpv`—without an account or cloud library.
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
Built with [Ink](https://github.com/vadimdemedes/ink),
|
|
13
|
+
[React](https://react.dev/), and the
|
|
14
|
+
[Radio Browser](https://api.radio-browser.info/) directory.
|
|
12
15
|
|
|
13
|
-
##
|
|
16
|
+
## Highlights
|
|
14
17
|
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
- Move previous/next through the exact station list you tuned from, even after navigating to another screen.
|
|
22
|
-
- Browse dense station lists with inline location/codec metadata and yellow favorite stars next to station names.
|
|
23
|
-
- Search by station name, place, language, tag, codec, or minimum bitrate.
|
|
24
|
-
- Keep local recents, favorites, imported stations, listening activity, playback settings, learned media keys, and provider cache.
|
|
25
|
-
- Review listening stats with a GitHub-style local-calendar contribution graph, favorite station, thresholded stations listened, sessions, streaks, active days, and total hours listened from persisted sessions.
|
|
26
|
-
- Import `.m3u`, `.pls`, and `.xspf` playlists, including nested local playlists.
|
|
27
|
-
- Export favorites and imports as `.m3u`.
|
|
28
|
-
- Survive ordinary internet-radio failure modes with provider mirror fallback, stale cache fallback, corrupt-file backups, tune timeouts, and skip-broken-stream behavior.
|
|
29
|
-
- Resize with the terminal. The app listens for terminal resize events and recomputes list row counts, map density, receiver width, and compact-mode fallback from the current dimensions.
|
|
18
|
+
- Discover stations through Explore, Search, Countries, or approximate-location Nearby.
|
|
19
|
+
- Listen with a receiver-style Now Playing screen, live metadata, sleep timer, diagnostics, and selectable visualizers.
|
|
20
|
+
- Keep favorites, recents, imports, track history, and listening stats on your machine.
|
|
21
|
+
- Move through the exact station list you tuned from with previous and next controls.
|
|
22
|
+
- Use `mpv` for complete playback control, with `ffplay` and VLC as limited fallbacks.
|
|
23
|
+
- Resize freely: full, compact, and micro layouts preserve navigation and essential controls.
|
|
30
24
|
|
|
31
|
-
##
|
|
25
|
+
## Quick start
|
|
32
26
|
|
|
33
|
-
|
|
34
|
-
The top GIF cycles multiple Now Playing receiver styles and display colors while
|
|
35
|
-
a real stream is playing through `mpv`.
|
|
36
|
-
|
|
37
|
-
### Library: Favorites And Recents
|
|
38
|
-
|
|
39
|
-

|
|
40
|
-
|
|
41
|
-
### Explore: World Map Discovery
|
|
42
|
-
|
|
43
|
-

|
|
44
|
-
|
|
45
|
-
### Search: Query Public Station Directories
|
|
46
|
-
|
|
47
|
-

|
|
48
|
-
|
|
49
|
-
### Nearby: Opt-In Local Stations
|
|
50
|
-
|
|
51
|
-

|
|
52
|
-
|
|
53
|
-
### Stats: Listening History And Display Colors
|
|
54
|
-
|
|
55
|
-

|
|
56
|
-
|
|
57
|
-
## Demo
|
|
58
|
-
|
|
59
|
-
The interactive TUI opens directly into the product, not a marketing screen:
|
|
60
|
-
|
|
61
|
-
```text
|
|
62
|
-
┌ RADIOCLI ──────────────────────────────────────────────────────────┐
|
|
63
|
-
│ Overview │ Playing │ Library │ Explore │ Search │ Countries │ … │ idle │
|
|
64
|
-
└──────────────────────────────────────────────────────────────────────┘
|
|
65
|
-
RADIOCLI ██████████████████
|
|
66
|
-
Live public radio from around the world
|
|
67
|
-
|
|
68
|
-
> 1 Playing · Receiver display and controls
|
|
69
|
-
2 Library · Favorites, recent stations, imported streams
|
|
70
|
-
3 Explore · Move a map cursor through geotagged stations
|
|
71
|
-
4 Search · Find stations by name, genre, language, place
|
|
72
|
-
5 Countries · Browse by country list with a world-map toggle
|
|
73
|
-
6 Nearby · Opt-in approximate location for local stations
|
|
74
|
-
7 Stats · Listening graph, sessions, streaks, hours
|
|
75
|
-
8 Settings · Audio output, colors, providers
|
|
76
|
-
|
|
77
|
-
3 recent · 2 favorites · 1 imported
|
|
78
|
-
|
|
79
|
-
↑/↓ move · Enter open · number jump · l location · : command
|
|
80
|
-
←/→ tabs · F7/F9 or ,/. station · F8 pause · t/v display · +/- volume · ? help · q quit
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
The Now Playing screen is a framed receiver panel with **50 selectable receiver styles**. The sample below shows the default pulse-grid display; press `v` to cycle through the catalog, which spans several families:
|
|
84
|
-
|
|
85
|
-
- **Classic receiver** — ultracode ripple, LEDs, and goniometer.
|
|
86
|
-
- **High-resolution braille** — smooth waveform, radial EQ, spectrogram, nebula, silk, ripple tank, phyllotaxis, harmonograph, bloom bars, moiré, galaxy, caustics.
|
|
87
|
-
- **Generative & motion** — matrix, hologram, spinning ASCII cube, generated fire, fireworks, plasma, spinning donut, starfield, Lorenz attractor, Barnsley fern, Chladni plate, rotating tesseract, torus knot, fractal tree, Julia sets, and lava-lamp motion.
|
|
88
|
-
|
|
89
|
-
Visualizers animate only while playback is actually playing and backend-ready; paused, stopped, idle, loading, and error states render a flat zero-signal display instead of a frozen waveform:
|
|
90
|
-
|
|
91
|
-
```text
|
|
92
|
-
Now playing ──────────────────────────────────────────────── mpv · playing
|
|
93
|
-
╭────────────────────────────────────────────────────────────────────────╮
|
|
94
|
-
│ FM 128.M RADIOCLI PLAYING │
|
|
95
|
-
│ KEXP 90.3 FM │
|
|
96
|
-
│ UNITED STATES · WASHINGTON │
|
|
97
|
-
│ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ │
|
|
98
|
-
│ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ │
|
|
99
|
-
│ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ │
|
|
100
|
-
│ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ │
|
|
101
|
-
│ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ │
|
|
102
|
-
│ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ ▌ │
|
|
103
|
-
│ MP3 · 128 kbps · english │
|
|
104
|
-
│ alternative, indie, seattle │
|
|
105
|
-
│ Waiting for ICY track metadata │
|
|
106
|
-
│ Backend mpv · Vol 70 ☆ Add favorite (f) Sleep off │
|
|
107
|
-
╰────────────────────────────────────────────────────────────────────────╯
|
|
108
|
-
|
|
109
|
-
Playing: KEXP 90.3 FM · Now: Artist - Track · Seattle, Washington, United States · MP3 / 128 kbps / english · mpv · playing · vol 70 · Nearby 4/90
|
|
110
|
-
space/F8 pause · f favorite · m mute · s sleep · d diagnostics · b home
|
|
111
|
-
←/→ tabs · F7/F9 or ,/. station · F8 pause · t/v display · +/- volume · ? help · q quit
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
For the exact non-interactive demo transcript:
|
|
115
|
-
|
|
116
|
-
```bash
|
|
117
|
-
npm run demo:script
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
Recording instructions live in [the demo guide](apps/docs/content/docs/demo.mdx).
|
|
121
|
-
|
|
122
|
-
## Documentation Website
|
|
123
|
-
|
|
124
|
-
The public website and the manual now live together in `apps/docs`, a Fumadocs-powered Next app. It renders the homepage at `/`, the documentation tree at `/docs`, docs search at `/api/search`, generated page images, and LLM-readable text routes.
|
|
125
|
-
|
|
126
|
-
Docs content is written as MDX in `apps/docs/content/docs`, with the navigation tree defined by the local `meta.json` files.
|
|
127
|
-
|
|
128
|
-
Useful commands from the repo root:
|
|
129
|
-
|
|
130
|
-
```bash
|
|
131
|
-
npm run docs:dev
|
|
132
|
-
npm run docs:check
|
|
133
|
-
npm run docs:build
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
Set `NEXT_PUBLIC_SITE_URL` for the canonical public docs URL. Preview builds also read Vercel's `VERCEL_URL` and Cloudflare Pages' `CF_PAGES_URL`.
|
|
137
|
-
|
|
138
|
-
## Install
|
|
139
|
-
|
|
140
|
-
Requirements:
|
|
141
|
-
|
|
142
|
-
- Homebrew on macOS: installs RadioCLI, Node.js, `mpv`, and FFmpeg
|
|
143
|
-
- npm on macOS, Linux, and Windows: Node.js 22 or newer
|
|
144
|
-
- `mpv` for playback, pause, mute, volume, media keys, metadata, and readiness checks
|
|
145
|
-
- FFmpeg on macOS for experimental AirPlay and optional `ffplay` fallback
|
|
146
|
-
- `ffplay` from FFmpeg as an optional playback-only fallback
|
|
147
|
-
|
|
148
|
-
Recommended macOS install:
|
|
27
|
+
macOS with Homebrew:
|
|
149
28
|
|
|
150
29
|
```bash
|
|
151
30
|
brew install ciphore/tap/radiocli
|
|
152
31
|
radiocli
|
|
153
32
|
```
|
|
154
33
|
|
|
155
|
-
|
|
156
|
-
and AirPlay prerequisites come from the native package manager.
|
|
157
|
-
|
|
158
|
-
AirPlay playback on macOS is experimental, but the sender bridge is bundled with
|
|
159
|
-
RadioCLI. The app discovers AirPlay/RAOP receivers with Bonjour and decodes
|
|
160
|
-
streams with FFmpeg; passcode-protected receivers prompt in the TUI.
|
|
161
|
-
|
|
162
|
-
Universal npm install:
|
|
34
|
+
macOS, Linux, or Windows with npm:
|
|
163
35
|
|
|
164
36
|
```bash
|
|
165
37
|
npm install -g @ciphore/radiocli
|
|
@@ -167,324 +39,148 @@ radiocli doctor
|
|
|
167
39
|
radiocli
|
|
168
40
|
```
|
|
169
41
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
42
|
+
RadioCLI requires Node.js 22 or newer. The Homebrew formula installs `mpv` and
|
|
43
|
+
FFmpeg; npm installs JavaScript dependencies only, so install `mpv` with your
|
|
44
|
+
system package manager for pause, volume, mute, media keys, and metadata.
|
|
173
45
|
|
|
174
|
-
|
|
46
|
+
See the [installation guide](apps/docs/content/docs/getting-started/install.mdx)
|
|
47
|
+
for Windows, Linux distributions, AirPlay prerequisites, and fallback players.
|
|
175
48
|
|
|
176
|
-
|
|
177
|
-
sudo apt install mpv
|
|
178
|
-
npm install -g @ciphore/radiocli
|
|
179
|
-
radiocli
|
|
180
|
-
```
|
|
49
|
+
## Visual tour
|
|
181
50
|
|
|
182
|
-
|
|
183
|
-
|
|
51
|
+
These recordings come from the built TUI. Generate them locally with
|
|
52
|
+
`npm run demo:assets`.
|
|
184
53
|
|
|
185
|
-
|
|
54
|
+
### Library
|
|
186
55
|
|
|
187
|
-
|
|
188
|
-
winget install --id OpenJS.NodeJS.LTS -e
|
|
189
|
-
winget install --id shinchiro.mpv -e
|
|
190
|
-
npm install -g @ciphore/radiocli
|
|
191
|
-
radiocli doctor
|
|
192
|
-
radiocli
|
|
193
|
-
```
|
|
56
|
+

|
|
194
57
|
|
|
195
|
-
|
|
196
|
-
of the `winget` mpv command. WSL remains supported through the Linux path.
|
|
58
|
+
### Explore
|
|
197
59
|
|
|
198
|
-
|
|
60
|
+

|
|
199
61
|
|
|
200
|
-
|
|
62
|
+
### Search
|
|
201
63
|
|
|
202
|
-
|
|
203
|
-
brew install ffmpeg # macOS
|
|
204
|
-
sudo apt install ffmpeg # Debian/Ubuntu
|
|
205
|
-
```
|
|
64
|
+

|
|
206
65
|
|
|
207
|
-
|
|
66
|
+
### Nearby
|
|
208
67
|
|
|
209
|
-
|
|
210
|
-
winget install --id Gyan.FFmpeg -e
|
|
211
|
-
```
|
|
68
|
+

|
|
212
69
|
|
|
213
|
-
|
|
214
|
-
provide reliable pause, mute, volume, media-key, or metadata control. In that
|
|
215
|
-
mode RadioCLI labels the backend as `ffplay fallback`, shows limited-control
|
|
216
|
-
footer text, and `radiocli doctor` reports `controls=limited`.
|
|
70
|
+
### Stats
|
|
217
71
|
|
|
218
|
-
|
|
219
|
-
install smoke checks on Ubuntu, macOS, and Windows.
|
|
72
|
+

|
|
220
73
|
|
|
221
|
-
|
|
74
|
+
## Essential controls
|
|
222
75
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
76
|
+
| Key | Action |
|
|
77
|
+
| --- | --- |
|
|
78
|
+
| `←` / `→` or `Tab` / `Shift+Tab` | Switch screens |
|
|
79
|
+
| `↑` / `↓` or `n` / `p` | Move the selection |
|
|
80
|
+
| `Enter` | Open or tune the selection |
|
|
81
|
+
| `space` or `F8` | Pause or resume with `mpv` |
|
|
82
|
+
| `,` / `.` or `F7` / `F9` | Previous or next station |
|
|
83
|
+
| `+` / `-` | Change volume |
|
|
84
|
+
| `f` | Save or remove a favorite |
|
|
85
|
+
| `?` | Open all shortcuts and commands |
|
|
86
|
+
| `q` or `Ctrl+C` twice | Quit cleanly |
|
|
87
|
+
|
|
88
|
+
The footer always shows controls for the current screen. Press `:` to open the
|
|
89
|
+
command palette. The [TUI controls guide](apps/docs/content/docs/getting-started/tui-controls.mdx)
|
|
90
|
+
covers filters, playback, media-key learning, AirPlay, and every command.
|
|
91
|
+
|
|
92
|
+
## CLI
|
|
239
93
|
|
|
240
94
|
```bash
|
|
241
95
|
radiocli # Start the TUI
|
|
242
|
-
radiocli check #
|
|
243
|
-
radiocli doctor
|
|
244
|
-
radiocli countries # Print top countries by station count
|
|
96
|
+
radiocli check # Check providers, playback tools, and the local store
|
|
97
|
+
radiocli doctor --json # Create a redacted support report
|
|
245
98
|
radiocli search "japan hits"
|
|
99
|
+
radiocli countries
|
|
246
100
|
radiocli import stations.m3u
|
|
247
101
|
radiocli export favorites.m3u
|
|
248
102
|
radiocli add-url <stream-url> [station name]
|
|
249
103
|
```
|
|
250
104
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
After a local build, the same commands can be run with:
|
|
254
|
-
|
|
255
|
-
```bash
|
|
256
|
-
node dist/cli.js check
|
|
257
|
-
node dist/cli.js search "lagos talk"
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
## TUI Controls
|
|
261
|
-
|
|
262
|
-
RadioCLI keeps shortcuts at the bottom of the terminal. When playback is active, a compact live row sits above the shortcuts with station, cleaned track metadata, volume or mute state, and an active sleep timer. The page shortcut row changes with the current screen, and the global transport row stays global:
|
|
263
|
-
|
|
264
|
-
- `←` / `→` or `Tab` / `Shift+Tab`: move across the top screen tabs.
|
|
265
|
-
- `F7` / `F9`, `,` / `.`, or `Shift+←` / `Shift+→`: tune previous or next station from the source list, wherever you are in the TUI.
|
|
266
|
-
- `space` / `F8`: pause or resume.
|
|
267
|
-
- `t`: cycle display color.
|
|
268
|
-
- `v`: cycle receiver style.
|
|
269
|
-
- `+` / `-`: volume.
|
|
270
|
-
- `q` or `Ctrl+C`: quit cleanly.
|
|
271
|
-
|
|
272
|
-
Pause, mute, volume, and play/pause media-key control require `mpv`. When only
|
|
273
|
-
`ffplay` is active, RadioCLI keeps playback and station skipping available but
|
|
274
|
-
labels interactive playback controls as limited and shows an `Install mpv`
|
|
275
|
-
warning instead of pretending the control worked.
|
|
276
|
-
|
|
277
|
-
Page-specific footer controls:
|
|
278
|
-
|
|
279
|
-
| Screen | Controls |
|
|
280
|
-
| --- | --- |
|
|
281
|
-
| Home | `↑` / `↓` move, `Enter` open, number jump, `:` command |
|
|
282
|
-
| Search input | type query, `Backspace` edit, `Up` / `Down` move results, `Ctrl+Up` / `Ctrl+Down` recall search history, `Enter` search or tune, `Esc` finish |
|
|
283
|
-
| Search results | `/` edit query, `↑` / `↓` or `n` / `p` move, `Enter` tune, `f` favorite, `b` home |
|
|
284
|
-
| Explore | click map, `WASD` fine move, `Shift+WASD` jump, `↑` / `↓` station, `Enter` tune, `f` favorite, `[` / `]` page, `b` home |
|
|
285
|
-
| Countries | `/` filter, `↑` / `↓` move, `Enter` open stations, `w` map, `b` home |
|
|
286
|
-
| World map | `/` filter, `↑` / `↓` move, `Enter` open country, `w` list, `b` home |
|
|
287
|
-
| Station lists | `↑` / `↓` or `n` / `p` move, `Enter` tune, `f` favorite, `[` / `]` page, `b` home |
|
|
288
|
-
| Now Playing | `space` / `F8` pause, `f` favorite, `m` mute, `s` sleep, `d` diagnostics, `b` home |
|
|
289
|
-
| Settings | `Enter` change selected, `g` Radio Garden, `l` location, `x` skip broken streams, `o` audio output, `a` AirPlay, `r` health, `b` home |
|
|
290
|
-
| AirPlay | `↑` / `↓` choose, `Enter` select receiver, `c` code entry, `r` refresh, `b` settings |
|
|
291
|
-
| AirPlay Code | type receiver code, `Backspace` edit, `Enter` submit, `Esc` AirPlay |
|
|
292
|
-
| Stats | `b` home |
|
|
293
|
-
|
|
294
|
-
Other active shortcuts:
|
|
295
|
-
|
|
296
|
-
- `Enter`: open the selected item or tune the selected station without leaving the current list.
|
|
297
|
-
- `:`: command palette.
|
|
298
|
-
- `/`: edit search or country filter on screens that support it.
|
|
299
|
-
- `[` / `]`: page through long station and country lists.
|
|
300
|
-
- `m`: mute.
|
|
301
|
-
- `o`: cycle audio output. If a station is playing, RadioCLI retunes it on the new output immediately.
|
|
302
|
-
- `g`: toggle the experimental Radio Garden adapter.
|
|
303
|
-
- `l`: toggle nearby location lookup.
|
|
304
|
-
- `x`: toggle skip-broken-stream behavior.
|
|
305
|
-
- `r`: refresh provider health.
|
|
306
|
-
- `f`: favorite the current or selected station.
|
|
307
|
-
- `n` / `p`: move selection; on Now Playing, tune next or previous station from the source list.
|
|
308
|
-
- `s`: cycle the sleep timer on Now Playing through off, 15 minutes, 30 minutes, 60 minutes, then off again.
|
|
309
|
-
- `d`: stream diagnostics on Now Playing.
|
|
310
|
-
- `b`: back home.
|
|
311
|
-
|
|
312
|
-
When you tune a station from Library, Explore, Search, Countries, or Nearby, that list becomes the playback queue. Previous/next keeps moving through that source list from any screen until you tune from another list.
|
|
313
|
-
|
|
314
|
-
Hardware media keys depend on the OS and terminal. RadioCLI maximizes compatibility by enabling enhanced keyboard reporting where supported, recognizing common F7/F8/F9 sequences, Kitty consumer/media-key codes, modified-arrow sequences, comma/dot transport fallback, and learned custom bindings. Previous/next media actions stay app-level; play/pause needs the `mpv` backend. Learn keys from Settings or with `:learn previous`, `:learn play`, and `:learn next`; clear them with `:keys reset`.
|
|
315
|
-
|
|
316
|
-
Explore mouse clicks use terminal mouse reporting while the Explore tab is active. If your terminal or tmux setup does not pass those events through, the WASD cursor controls stay fully available.
|
|
317
|
-
|
|
318
|
-
Useful command palette entries:
|
|
319
|
-
|
|
320
|
-
```text
|
|
321
|
-
:search lagos jazz
|
|
322
|
-
:country japan
|
|
323
|
-
:codec MP3
|
|
324
|
-
:language spanish
|
|
325
|
-
:bitrate 128
|
|
326
|
-
:clear
|
|
327
|
-
:volume 60
|
|
328
|
-
:mute
|
|
329
|
-
:favorite
|
|
330
|
-
:sleep 15
|
|
331
|
-
:sleep off
|
|
332
|
-
:timeout 15
|
|
333
|
-
:skip off
|
|
334
|
-
:location on
|
|
335
|
-
:learn previous
|
|
336
|
-
:learn play
|
|
337
|
-
:learn next
|
|
338
|
-
:keys reset
|
|
339
|
-
:airplay
|
|
340
|
-
:airplay-code 1234
|
|
341
|
-
:map
|
|
342
|
-
:library
|
|
343
|
-
:stats
|
|
344
|
-
:settings
|
|
345
|
-
:stop
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
Settings persist display colors and receiver styles without editing config files. The fourteen display colors are green, amber, blue, ruby, ice, teal, violet, copper, cyan, lime, coral, rose, slate, and mono, cycled with `t`. The 50 receiver styles span classic receiver displays, high-resolution braille visuals, and generative motion scenes; cycle them with `v` (see the [Demo](#demo) for the full family breakdown). The stats graph and legend follow the selected display color, and the selected Now Playing style is restored on the next launch.
|
|
105
|
+
RadioCLI imports `.m3u`, `.pls`, and `.xspf` playlists. It exports favorites
|
|
106
|
+
and imported streams as `.m3u`.
|
|
349
107
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
`:airplay`, and choose a visible receiver. If a station is already playing,
|
|
353
|
-
RadioCLI moves it to the selected receiver immediately; switch Audio output back
|
|
354
|
-
to `This device (mpv)` to leave AirPlay. RadioCLI does not auto-pick a receiver.
|
|
355
|
-
AirPlay is a current-session output: restarting RadioCLI returns to automatic
|
|
356
|
-
local playback while remembering the last receiver for the next manual switch.
|
|
357
|
-
If the receiver asks for a code, RadioCLI opens the AirPlay Code screen; you can
|
|
358
|
-
also press `c` from AirPlay settings or use `:airplay-code 1234`.
|
|
108
|
+
See the [CLI guide](apps/docs/content/docs/getting-started/cli.mdx) for all
|
|
109
|
+
arguments and examples.
|
|
359
110
|
|
|
360
|
-
##
|
|
111
|
+
## Playback and reliability
|
|
361
112
|
|
|
362
|
-
RadioCLI
|
|
113
|
+
RadioCLI prefers `mpv` because it handles redirects, HLS, real-world codecs,
|
|
114
|
+
ICY metadata, and interactive controls reliably. `ffplay` and VLC can keep a
|
|
115
|
+
stream playing when `mpv` is unavailable, but the UI labels their controls as
|
|
116
|
+
limited.
|
|
363
117
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
118
|
+
Station providers use mirror fallback, bounded caches, and stale-cache recovery.
|
|
119
|
+
Playback waits for backend readiness, applies tune timeouts, and can skip broken
|
|
120
|
+
streams. Corrupt library and cache files are backed up instead of silently
|
|
121
|
+
overwritten.
|
|
368
122
|
|
|
369
|
-
|
|
123
|
+
Read the [reliability notes](apps/docs/content/docs/reliability.mdx) for failure
|
|
124
|
+
handling and troubleshooting.
|
|
370
125
|
|
|
371
|
-
|
|
372
|
-
a station, `ProviderManager.resolve()` follows the provider-specific path, then
|
|
373
|
-
`PlayerController` starts `mpv` with `--no-video`, `--force-window=no`, a local
|
|
374
|
-
JSON IPC endpoint, and the configured volume. The endpoint is a Unix socket on
|
|
375
|
-
macOS/Linux and a named pipe on native Windows. `ffplay` is available as a
|
|
376
|
-
playback-only fallback, but `mpv` is the intended backend because it handles
|
|
377
|
-
redirects, HLS, codecs, metadata, pause, mute, volume, media keys, and readiness
|
|
378
|
-
checks more reliably than a JavaScript stream client.
|
|
379
|
-
|
|
380
|
-
RadioCLI waits for the backend to become ready before marking playback as
|
|
381
|
-
`playing`. With `mpv`, it polls playback state every 500ms and ICY metadata every
|
|
382
|
-
2.5s, then cleans the metadata before showing it in the receiver and live footer.
|
|
383
|
-
|
|
384
|
-
### FFT Processing
|
|
385
|
-
|
|
386
|
-
RadioCLI does not currently tap decoded PCM audio or run a real FFT in the Node
|
|
387
|
-
process. The receiver visuals are procedural signal displays driven by playback
|
|
388
|
-
truth, the selected style, terminal dimensions, theme, and a small `pulse`
|
|
389
|
-
counter. That keeps the TUI lightweight and avoids duplicating audio decoding
|
|
390
|
-
work already handled by `mpv`. The spectrum-like modes are generated from
|
|
391
|
-
deterministic samples, so they should be read as receiver visualizers rather than
|
|
392
|
-
measurement-grade audio analysis.
|
|
393
|
-
|
|
394
|
-
### Terminal Rendering And Refresh Rate
|
|
395
|
-
|
|
396
|
-
Ink renders React components into ANSI terminal frames. The app recomputes a
|
|
397
|
-
terminal layout from the current row/column size, then each screen gets stable
|
|
398
|
-
row budgets for station lists, the map, receiver panels, and the adaptive
|
|
399
|
-
footer. Visualizers return text rows or colored text segments; the Now Playing
|
|
400
|
-
screen frames those rows inside the receiver panel.
|
|
401
|
-
|
|
402
|
-
Live receiver animation advances every 80ms, about 12.5 frames per second, only
|
|
403
|
-
on the Now Playing screen while playback is `playing` and backend-ready.
|
|
404
|
-
Ambient/idle-style animation uses a slower 140ms interval, and the loading
|
|
405
|
-
spinner uses 120ms. Inactive playback states render zero-signal frames instead
|
|
406
|
-
of animating.
|
|
407
|
-
|
|
408
|
-
### CPU Usage
|
|
409
|
-
|
|
410
|
-
CPU cost is intentionally bounded: audio decode stays in the native backend,
|
|
411
|
-
there is no JavaScript FFT worker, metadata polling is infrequent, and the pulse
|
|
412
|
-
timer does not run outside live Now Playing. In practice the terminal renderer
|
|
413
|
-
does string and color-segment generation for the current screen only; `mpv` does
|
|
414
|
-
the stream work, and idle/library/search/map screens do not pay the visualizer
|
|
415
|
-
animation cost.
|
|
416
|
-
|
|
417
|
-
Radio Browser is the primary provider. Its own docs recommend using a speaking user agent, resolving station clicks through `/json/url`, and retrying with other servers when one fails; RadioCLI follows that shape with mirror fallback and durable cache. Explore and Nearby use a cached geotagged Radio Browser atlas, then compute local distance in the app so map movement is not biased toward the most-clicked stations worldwide. Radio Garden support is experimental because the useful endpoints are publicly discoverable but unofficial, and they can be blocked or changed independently of this project.
|
|
418
|
-
|
|
419
|
-
Playback prefers `mpv` because it handles real-world streams, redirects, HLS, codecs, and metadata better than a hand-rolled stream client. RadioCLI controls `mpv` through JSON IPC for readiness, pause, mute, volume, and metadata polling, using Unix sockets on macOS/Linux and named pipes on Windows. `ffplay` remains a playback-only fallback and is intentionally labeled with limited controls in the UI and doctor output. Experimental AirPlay output is available on macOS when Bonjour discovery and FFmpeg are available.
|
|
420
|
-
|
|
421
|
-
The npm package is `@ciphore/radiocli`, and the installed executable is `radiocli`. Current installs store data under RadioCLI paths such as `radiocli.json` and `radiocli-cache.json`. Existing Radio Atlas data is still discovered when a new RadioCLI store does not exist, and legacy `RADIO_ATLAS_HOME` / animation environment variables remain supported as migration fallbacks. New automation should use `RADIOCLI_HOME` and `RADIOCLI_DISABLE_ANIMATION`.
|
|
422
|
-
|
|
423
|
-
Read more:
|
|
126
|
+
## Privacy
|
|
424
127
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
- [Roadmap](apps/docs/content/docs/roadmap.mdx)
|
|
429
|
-
- [Release packaging](apps/docs/content/docs/release-packaging.mdx)
|
|
128
|
+
RadioCLI does not require an account, proxy audio, or upload your listening
|
|
129
|
+
history. Favorites, recents, imports, settings, track history, and activity stay
|
|
130
|
+
in a local JSON library.
|
|
430
131
|
|
|
431
|
-
|
|
132
|
+
Nearby contacts `ipapi.co` only when you open that screen, then uses an
|
|
133
|
+
approximate location to sort the local station atlas. Disable the lookup with
|
|
134
|
+
`l`, Settings, or `:location off`.
|
|
432
135
|
|
|
433
|
-
|
|
136
|
+
Favoriting a Radio Browser station sends a best-effort public directory vote by
|
|
137
|
+
default. Turn off **Share favorite votes with Radio Browser** in Settings to
|
|
138
|
+
keep favorites local-only.
|
|
434
139
|
|
|
435
|
-
|
|
140
|
+
See [Privacy and security](apps/docs/content/docs/privacy-security.mdx) for the
|
|
141
|
+
complete data-flow description.
|
|
436
142
|
|
|
437
|
-
|
|
438
|
-
- cached geotagged station atlas for true distance-first Explore and Nearby results
|
|
439
|
-
- Zod schemas at public API and persistence boundaries
|
|
440
|
-
- stale-cache fallback for directory outages
|
|
441
|
-
- corrupt store/cache backup instead of silent overwrite
|
|
442
|
-
- `mpv` readiness checks before reporting playback as active
|
|
443
|
-
- tune timeout and skip-broken-stream behavior
|
|
444
|
-
- cleaned ICY metadata, including key/value payloads such as `title="..." artist="..."` and station-specific `text="..."` fields
|
|
445
|
-
- source-list playback queues for previous/next transport
|
|
446
|
-
- enhanced terminal keyboard parsing with learned media-key bindings
|
|
447
|
-
- local-calendar activity bucketing for late-night listening sessions
|
|
448
|
-
- local-first privacy posture for history, favorites, imports, and settings
|
|
449
|
-
- responsive terminal layout utility with an adaptive footer and focused tests
|
|
450
|
-
- smoke tests that exercise live provider data and real playback
|
|
451
|
-
- package smoke test that packs the npm artifact, installs it into a fresh temp project, and runs the installed binary
|
|
143
|
+
## Project structure
|
|
452
144
|
|
|
453
|
-
|
|
145
|
+
- `src/ui` — screens, input, layout, and terminal rendering
|
|
146
|
+
- `src/providers` — station directories, resolution, and caches
|
|
147
|
+
- `src/player` — playback backends, metadata, and AirPlay
|
|
148
|
+
- `src/storage` — local library persistence and migration
|
|
149
|
+
- `apps/docs` — documentation website and manual
|
|
454
150
|
|
|
455
|
-
|
|
151
|
+
More detail lives in the [architecture guide](apps/docs/content/docs/architecture.mdx)
|
|
152
|
+
and [design notes](apps/docs/content/docs/design.mdx).
|
|
456
153
|
|
|
457
154
|
## Development
|
|
458
155
|
|
|
459
156
|
```bash
|
|
157
|
+
git clone https://github.com/Ciphore/RadioCLI.git
|
|
158
|
+
cd RadioCLI
|
|
460
159
|
npm ci
|
|
461
|
-
npm run check
|
|
462
|
-
npm run lint
|
|
463
|
-
npm run test
|
|
464
|
-
npm run build
|
|
465
|
-
npm run docs:check
|
|
466
|
-
npm run docs:build
|
|
467
|
-
npm run smoke:data
|
|
468
160
|
npm run verify
|
|
469
|
-
npm run
|
|
470
|
-
npm run pack:check
|
|
471
|
-
npm run fresh:check
|
|
472
|
-
npm run verify:release
|
|
161
|
+
npm run dev
|
|
473
162
|
```
|
|
474
163
|
|
|
475
|
-
`npm run
|
|
164
|
+
`npm run verify` checks types, lint, tests, the production build, and package
|
|
165
|
+
contents. Playback and live-data smoke tests are available separately because
|
|
166
|
+
they contact public services or start a local player.
|
|
476
167
|
|
|
477
168
|
## Contributing
|
|
478
169
|
|
|
479
|
-
Contributions are welcome
|
|
170
|
+
Contributions are welcome. Start with [CONTRIBUTING.md](CONTRIBUTING.md), and
|
|
171
|
+
include `radiocli check` output with playback reports.
|
|
172
|
+
|
|
173
|
+
## Documentation
|
|
480
174
|
|
|
481
|
-
|
|
175
|
+
- [Getting started](apps/docs/content/docs/index.mdx)
|
|
176
|
+
- [Installation](apps/docs/content/docs/getting-started/install.mdx)
|
|
177
|
+
- [Controls](apps/docs/content/docs/getting-started/tui-controls.mdx)
|
|
178
|
+
- [Architecture](apps/docs/content/docs/architecture.mdx)
|
|
179
|
+
- [Roadmap](apps/docs/content/docs/roadmap.mdx)
|
|
180
|
+
- [Release packaging](apps/docs/content/docs/release-packaging.mdx)
|
|
482
181
|
|
|
483
|
-
|
|
484
|
-
- [Radio Browser API docs](https://api.radio-browser.info/)
|
|
485
|
-
- [mpv JSON IPC manual](https://mpv.io/manual/stable/#json-ipc)
|
|
486
|
-
- [Unofficial Radio Garden OpenAPI notes](https://github.com/jonasrmichel/radio-garden-openapi)
|
|
182
|
+
Run the documentation site locally with `npm run docs:dev`.
|
|
487
183
|
|
|
488
184
|
## License
|
|
489
185
|
|
|
490
|
-
MIT
|
|
186
|
+
[MIT](LICENSE)
|
package/SECURITY.md
CHANGED
|
@@ -17,13 +17,16 @@ information, or unexpected command execution.
|
|
|
17
17
|
|
|
18
18
|
## Privacy Notes
|
|
19
19
|
|
|
20
|
-
- Nearby
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
- Nearby location is enabled by default for new libraries, but no location
|
|
21
|
+
request is made until the Nearby screen is opened.
|
|
22
|
+
- Location lookup uses approximate IP-based location from `ipapi.co`; disable it
|
|
23
|
+
with `l`, Settings, or `:location off`.
|
|
23
24
|
- The app stores recents, favorites, settings, imports, and provider cache data
|
|
24
25
|
locally under the user data directory.
|
|
25
26
|
- RadioCLI does not proxy audio. It resolves public stream URLs and hands
|
|
26
27
|
playback to `mpv` or `ffplay`.
|
|
28
|
+
- Favoriting a Radio Browser station sends a best-effort vote to that public
|
|
29
|
+
directory by default; the Settings screen can disable vote sharing.
|
|
27
30
|
|
|
28
31
|
## Supported Versions
|
|
29
32
|
|
package/dist/activity/stats.js
CHANGED
|
@@ -48,7 +48,13 @@ export function computeListeningStats(sessions, now = new Date()) {
|
|
|
48
48
|
}
|
|
49
49
|
function sessionSeconds(session, now = new Date()) {
|
|
50
50
|
const started = Date.parse(session.startedAt);
|
|
51
|
-
const ended = session.endedAt
|
|
51
|
+
const ended = session.endedAt
|
|
52
|
+
? Date.parse(session.endedAt)
|
|
53
|
+
: session.lastActiveAt
|
|
54
|
+
? Date.parse(session.lastActiveAt)
|
|
55
|
+
: Number.isFinite(started)
|
|
56
|
+
? started + Math.max(0, session.listenedSeconds) * 1000
|
|
57
|
+
: now.getTime();
|
|
52
58
|
if (!Number.isFinite(started) || !Number.isFinite(ended) || ended <= started) {
|
|
53
59
|
return Math.min(maxContinuousListeningSeconds, Math.max(0, Math.round(session.listenedSeconds)));
|
|
54
60
|
}
|
|
@@ -65,7 +71,13 @@ function splitSessionByDay(session, seconds, firstDay, lastDayEnd, now) {
|
|
|
65
71
|
if (!Number.isFinite(started)) {
|
|
66
72
|
return [];
|
|
67
73
|
}
|
|
68
|
-
const recordedEnd = session.endedAt
|
|
74
|
+
const recordedEnd = session.endedAt
|
|
75
|
+
? Date.parse(session.endedAt)
|
|
76
|
+
: session.lastActiveAt
|
|
77
|
+
? Date.parse(session.lastActiveAt)
|
|
78
|
+
: Number.isFinite(started)
|
|
79
|
+
? started + seconds * 1000
|
|
80
|
+
: now.getTime();
|
|
69
81
|
const rawEnd = Number.isFinite(recordedEnd) && recordedEnd > started
|
|
70
82
|
? recordedEnd
|
|
71
83
|
: started + seconds * 1000;
|