@ciphore/radiocli 0.2.4 → 0.2.6
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 +103 -2
- package/CONTRIBUTING.md +28 -0
- package/README.md +108 -253
- package/SECURITY.md +23 -1
- package/dist/agent/alarm-service.js +10 -7
- package/dist/agent/cli.js +16 -1
- package/dist/agent/command-schema.js +47 -0
- package/dist/agent/headless-host.js +226 -81
- package/dist/agent/launcher.js +2 -1
- package/dist/agent/mcp-server.js +10 -5
- package/dist/agent/service.js +75 -40
- package/dist/agent/session.js +214 -24
- package/dist/agent/tui-session.js +213 -0
- package/dist/alarms/cli.js +19 -3
- package/dist/alarms/occurrence-cache.js +25 -0
- package/dist/alarms/runner.js +87 -23
- package/dist/alarms/schedule.js +29 -1
- package/dist/alarms/scheduler.js +11 -3
- package/dist/alarms/system-volume-linux.js +160 -0
- package/dist/alarms/system-volume-ownership.js +76 -8
- package/dist/alarms/system-volume.js +44 -23
- package/dist/cli/arguments.js +212 -0
- package/dist/cli/completion.js +44 -0
- package/dist/cli/doctor.js +156 -0
- package/dist/cli/interactive.js +79 -0
- package/dist/cli/listener.js +227 -0
- package/dist/cli/output.js +121 -0
- package/dist/cli/render-tui.js +34 -0
- package/dist/cli.js +182 -199
- package/dist/platform/capabilities.js +8 -1
- package/dist/platform/desktop-media.js +61 -0
- package/dist/platform/diagnostics.js +273 -0
- package/dist/platform/json-worker.js +111 -0
- package/dist/platform/launch-command.js +9 -6
- package/dist/platform/mpris.js +356 -0
- package/dist/platform/network.js +69 -1
- package/dist/platform/omarchy.js +147 -0
- package/dist/platform/packages.js +7 -1
- package/dist/platform/runtime.js +1 -1
- package/dist/platform/terminals.js +24 -4
- package/dist/player/airplay-discovery.js +144 -61
- package/dist/player/airplay-policy.js +15 -0
- package/dist/player/airplay-process.js +63 -0
- package/dist/player/airplay-readiness.js +42 -0
- package/dist/player/airplay-sender-health.js +49 -49
- package/dist/player/airplay-sender-package.js +5 -0
- package/dist/player/airplay-sender-patch.js +102 -4
- package/dist/player/airplay-setup.js +51 -0
- package/dist/player/airplay-worker.js +97 -41
- package/dist/player/backend-install.js +9 -6
- package/dist/player/ffmpeg-input.js +37 -0
- package/dist/player/mpv-desktop-media.js +194 -0
- package/dist/player/mpv-ipc-client.js +51 -2
- package/dist/player/player-controller.js +390 -73
- package/dist/player/radio-engine.js +282 -0
- package/dist/playlists/playlist.js +18 -16
- package/dist/providers/cache.js +291 -75
- package/dist/providers/discovery-session.js +111 -0
- package/dist/providers/provider-manager.js +106 -28
- package/dist/providers/radio-browser.js +272 -41
- package/dist/providers/radio-garden.js +4 -2
- package/dist/setup-playback.js +152 -0
- package/dist/setup.js +88 -21
- package/dist/storage/file-lock.js +130 -0
- package/dist/storage/read-settings.js +33 -0
- package/dist/storage/replace-file.js +20 -0
- package/dist/storage/repository.js +247 -0
- package/dist/storage/station-identity.js +21 -0
- package/dist/storage/station-schema.js +27 -0
- package/dist/storage/store.js +314 -76
- package/dist/types.js +22 -1
- package/dist/ui/AdaptiveContent.js +21 -24
- package/dist/ui/App.js +708 -454
- package/dist/ui/AppContent.js +5 -5
- package/dist/ui/airplay-settings.js +12 -5
- package/dist/ui/bidi-ranges.js +45 -0
- package/dist/ui/components/AdaptiveMarquee.js +16 -9
- package/dist/ui/components/ReceiverVisualRow.js +39 -0
- package/dist/ui/format.js +37 -4
- package/dist/ui/help-content.js +5 -2
- package/dist/ui/rtl-output.js +137 -0
- package/dist/ui/screen-items.js +10 -4
- package/dist/ui/screens/AirPlaySettingsScreen.js +3 -3
- package/dist/ui/screens/AlarmsScreen.js +5 -2
- package/dist/ui/screens/HelpScreen.js +3 -3
- package/dist/ui/screens/NowPlayingScreen.js +2 -21
- package/dist/ui/screens/SettingsScreen.js +16 -16
- package/dist/ui/screens/StatsScreen.js +1 -1
- package/dist/ui/text-direction.js +9 -0
- package/dist/ui/theme.js +46 -0
- package/dist/ui/use-alarm-tui.js +24 -24
- package/dist/ui/use-app-input.js +42 -8
- package/dist/ui/use-command-executor.js +21 -14
- package/dist/ui/visualizers/boat-lake-kernel.js +325 -0
- package/dist/ui/visualizers/boat-lake.js +161 -0
- package/dist/ui/visualizers/gallop.js +2 -2
- package/dist/ui/visualizers/receiver-style-registry.js +3 -1
- package/dist/ui/visualizers/receiver-visualizers.js +6 -108
- package/dist/ui/visualizers/sumi-ocean.js +183 -0
- package/dist/ui/visualizers/terminal-cell.js +45 -0
- package/dist/update-check.js +2 -1
- package/docs/THIRD_PARTY_NOTICES.md +6 -4
- package/docs/boat-lake.md +88 -0
- package/docs/sumi-ocean.md +80 -0
- package/package.json +36 -12
- package/scripts/build-airplay-native.mjs +22 -0
- package/vendor/node-airtunes2/LICENSE +208 -0
- package/vendor/node-airtunes2/RADIOCLI_PATCH.md +29 -0
- package/vendor/node-airtunes2/README.md +225 -0
- package/vendor/node-airtunes2/UPSTREAM.json +50 -0
- package/vendor/node-airtunes2/binding.gyp +41 -0
- package/vendor/node-airtunes2/lib/ap2_test.js +4 -0
- package/vendor/node-airtunes2/lib/atv.js +307 -0
- package/vendor/node-airtunes2/lib/atvAuthenticator.js +136 -0
- package/vendor/node-airtunes2/lib/audio_out.js +69 -0
- package/vendor/node-airtunes2/lib/circular_buffer.js +133 -0
- package/vendor/node-airtunes2/lib/config.js +26 -0
- package/vendor/node-airtunes2/lib/device_airtunes.js +532 -0
- package/vendor/node-airtunes2/lib/devices.js +266 -0
- package/vendor/node-airtunes2/lib/ed25519.js +26 -0
- package/vendor/node-airtunes2/lib/homekit/credentials.js +86 -0
- package/vendor/node-airtunes2/lib/homekit/encryption.js +69 -0
- package/vendor/node-airtunes2/lib/homekit/number.js +44 -0
- package/vendor/node-airtunes2/lib/homekit/tlv.js +95 -0
- package/vendor/node-airtunes2/lib/http.js +189 -0
- package/vendor/node-airtunes2/lib/index.d.ts +141 -0
- package/vendor/node-airtunes2/lib/index.js +127 -0
- package/vendor/node-airtunes2/lib/ntp.js +37 -0
- package/vendor/node-airtunes2/lib/num_util.js +21 -0
- package/vendor/node-airtunes2/lib/packet_pool.js +43 -0
- package/vendor/node-airtunes2/lib/rtsp.js +1807 -0
- package/vendor/node-airtunes2/lib/srp.js +158 -0
- package/vendor/node-airtunes2/lib/udp_servers.js +214 -0
- package/vendor/node-airtunes2/lib/util.js +12 -0
- package/vendor/node-airtunes2/package.json +31 -0
- package/vendor/node-airtunes2/src/CADebugMacros.h +257 -0
- package/vendor/node-airtunes2/src/CAHostTimeBase.cpp +105 -0
- package/vendor/node-airtunes2/src/CAHostTimeBase.h +224 -0
- package/vendor/node-airtunes2/src/aes_utils.c +864 -0
- package/vendor/node-airtunes2/src/aes_utils.h +24 -0
- package/vendor/node-airtunes2/src/base64.c +136 -0
- package/vendor/node-airtunes2/src/base64.h +42 -0
- package/vendor/node-airtunes2/src/bindings.cc +29 -0
- package/vendor/node-airtunes2/src/codec.cc +170 -0
- package/vendor/node-airtunes2/src/coreaudio.cc +332 -0
|
@@ -5,9 +5,9 @@ import { computeListeningStats } from '../activity/stats.js';
|
|
|
5
5
|
import { playbackBackendLabel } from '../player/backend-install.js';
|
|
6
6
|
import { visibleWindow } from './list-window.js';
|
|
7
7
|
import { displayWidth, padDisplayEnd, stationLocation, stationTags, stationTech, truncate } from './format.js';
|
|
8
|
-
import { homeItems, settingsGroup,
|
|
8
|
+
import { homeItems, settingsGroup, settingsGroupsForAirPlay, settingsItemsForPage } from './screen-items.js';
|
|
9
9
|
import { screenTitle } from './screen-meta.js';
|
|
10
|
-
import { keyHelpSections,
|
|
10
|
+
import { keyHelpSections, commandHelpForAirPlay } from './help-content.js';
|
|
11
11
|
import { settingLabel, settingValue } from './screens/SettingsScreen.js';
|
|
12
12
|
import { exploreMapLand, mapMarker, panelBorder, textDim, textMuted, themeAccent } from './theme.js';
|
|
13
13
|
import { panelBorderStyle, useDisplay } from './display-context.js';
|
|
@@ -19,6 +19,7 @@ import { formatExploreCursor } from './app-state.js';
|
|
|
19
19
|
import { buildCosmoWorldMap } from './cosmo-world-map.js';
|
|
20
20
|
import { adaptiveExploreFrameMetrics, computeAdaptiveExploreLayout } from './adaptive-explore-layout.js';
|
|
21
21
|
import { AdaptiveMarquee } from './components/AdaptiveMarquee.js';
|
|
22
|
+
import { ReceiverVisualRow } from './components/ReceiverVisualRow.js';
|
|
22
23
|
export function AdaptiveContent(props) {
|
|
23
24
|
return _jsx(AdaptiveContentBody, { ...props });
|
|
24
25
|
}
|
|
@@ -55,9 +56,11 @@ function AdaptiveContentBody(props) {
|
|
|
55
56
|
width,
|
|
56
57
|
mode,
|
|
57
58
|
ascii,
|
|
58
|
-
airPlaySupported
|
|
59
|
+
airPlaySupported,
|
|
60
|
+
platform: props.platform
|
|
59
61
|
});
|
|
60
62
|
const empty = adaptiveEmptyState({
|
|
63
|
+
airPlaySetupRequired: props.platform === 'linux' && !backends.includes('airplay'),
|
|
61
64
|
screen,
|
|
62
65
|
searchQuery,
|
|
63
66
|
editingSearch,
|
|
@@ -105,9 +108,11 @@ function AdaptiveContentBody(props) {
|
|
|
105
108
|
width,
|
|
106
109
|
mode,
|
|
107
110
|
ascii,
|
|
108
|
-
airPlaySupported
|
|
111
|
+
airPlaySupported,
|
|
112
|
+
platform: props.platform
|
|
109
113
|
});
|
|
110
114
|
const empty = adaptiveEmptyState({
|
|
115
|
+
airPlaySetupRequired: props.platform === 'linux' && !backends.includes('airplay'),
|
|
111
116
|
screen,
|
|
112
117
|
searchQuery,
|
|
113
118
|
editingSearch,
|
|
@@ -221,7 +226,7 @@ function StaticRows({ rows, width, theme }) {
|
|
|
221
226
|
}
|
|
222
227
|
function AdaptiveNowPlaying({ mode, station, playback, metadata, width, height, ascii, theme, receiverStyle, reduceMotion }) {
|
|
223
228
|
const pulse = useReceiverPulse();
|
|
224
|
-
const { screenReader } = useDisplay();
|
|
229
|
+
const { screenReader, app: appBackground } = useDisplay();
|
|
225
230
|
const accent = themeAccent(theme);
|
|
226
231
|
const stationName = station?.name ?? 'No station tuned';
|
|
227
232
|
const showMetadata = Boolean(metadata?.title) && (screenReader || mode === 'compact' && height >= 9);
|
|
@@ -237,19 +242,7 @@ function AdaptiveNowPlaying({ mode, station, playback, metadata, width, height,
|
|
|
237
242
|
const status = `${stationName} ${playback.state}${playback.muted ? ' · muted' : ` · vol ${playback.volume}`}`;
|
|
238
243
|
return (_jsxs(Box, { flexDirection: "column", height: height, width: width, overflow: "hidden", children: [_jsx(Text, { color: accent, bold: true, "aria-label": mode === 'micro' ? `Now playing. Station: ${stationName}. Playback: ${playback.state}, ${playback.muted ? 'muted' : `volume ${playback.volume}`}` : undefined, children: mode === 'micro'
|
|
239
244
|
? _jsx(AdaptiveMarquee, { text: header, width: width, active: true, reduceMotion: reduceMotion })
|
|
240
|
-
: truncate(header, width) }), mode === 'compact' ? _jsx(Text, { color: textMuted, "aria-label": `Station: ${stationName}. Playback: ${playback.state}, ${playback.muted ? 'muted' : `volume ${playback.volume}`}`, children: ascii ? toAsciiSafe(truncate(status, width)) : truncate(status, width) }) : null, gapRows ? _jsx(Box, { height: gapRows, flexShrink: 0 }) : null, !screenReader ? _jsx(Box, { flexDirection: "column", height: availableVisualRows, overflow: "hidden", "aria-hidden": true, children: visualRows.map((row, index) => (_jsx(
|
|
241
|
-
? renderAdaptiveSegments(row.segments, ascii)
|
|
242
|
-
: ascii
|
|
243
|
-
? toAsciiSafe(row.text)
|
|
244
|
-
: row.text }, index))) }) : null, showMetadata ? (_jsx(Text, { color: accent, "aria-label": `Track: ${metadata?.title ?? ''}`, children: _jsx(AdaptiveMarquee, { text: metadata?.title ?? '', width: width, active: true, reduceMotion: reduceMotion }) })) : null, gapRows ? _jsx(Box, { height: gapRows, flexShrink: 0 }) : null] }));
|
|
245
|
-
}
|
|
246
|
-
function renderAdaptiveSegments(segments, ascii) {
|
|
247
|
-
let offset = 0;
|
|
248
|
-
return segments.map(segment => {
|
|
249
|
-
const key = offset;
|
|
250
|
-
offset += segment.text.length;
|
|
251
|
-
return (_jsx(Text, { color: segment.color, backgroundColor: segment.backgroundColor, bold: segment.bold, children: ascii ? toAsciiSafe(segment.text) : segment.text }, key));
|
|
252
|
-
});
|
|
245
|
+
: truncate(header, width) }), mode === 'compact' ? _jsx(Text, { color: textMuted, "aria-label": `Station: ${stationName}. Playback: ${playback.state}, ${playback.muted ? 'muted' : `volume ${playback.volume}`}`, children: ascii ? toAsciiSafe(truncate(status, width)) : truncate(status, width) }) : null, gapRows ? _jsx(Box, { height: gapRows, flexShrink: 0 }) : null, !screenReader ? _jsx(Box, { flexDirection: "column", height: availableVisualRows, overflow: "hidden", "aria-hidden": true, children: visualRows.map((row, index) => (_jsx(ReceiverVisualRow, { row: row, width: width, ascii: ascii, backgroundColor: appBackground }, index))) }) : null, showMetadata ? (_jsx(Text, { color: accent, "aria-label": `Track: ${metadata?.title ?? ''}`, children: _jsx(AdaptiveMarquee, { text: metadata?.title ?? '', width: width, active: true, reduceMotion: reduceMotion }) })) : null, gapRows ? _jsx(Box, { height: gapRows, flexShrink: 0 }) : null] }));
|
|
253
246
|
}
|
|
254
247
|
function AdaptiveSearch({ mode, query, editing, loading, rows, empty, selected, height, width, theme, ascii, reduceMotion }) {
|
|
255
248
|
const accent = themeAccent(theme);
|
|
@@ -273,15 +266,15 @@ function adaptiveRows(input) {
|
|
|
273
266
|
}));
|
|
274
267
|
}
|
|
275
268
|
if (screen === 'settings') {
|
|
276
|
-
const pageItems = settingsItemsForPage(input.settingsPage);
|
|
269
|
+
const pageItems = settingsItemsForPage(input.settingsPage, input.airPlaySupported);
|
|
277
270
|
const labels = pageItems.map(item => settingLabel(item, updateCheck, input.appVersion, input.airPlaySupported));
|
|
278
271
|
const labelWidth = pairedColumnWidth(labels, width, mode);
|
|
279
272
|
return pageItems.map((item, index) => ({
|
|
280
273
|
key: item,
|
|
281
274
|
label: padDisplayEnd(truncate(settingLabel(item, updateCheck, input.appVersion, input.airPlaySupported), labelWidth), labelWidth),
|
|
282
275
|
detail: input.settingsPage === 'root'
|
|
283
|
-
? adaptiveSettingsRootValue(item)
|
|
284
|
-
: settingValue(item, library.settings, diagnostics, backends, airPlayDevices, updateCheck, input.appVersion),
|
|
276
|
+
? adaptiveSettingsRootValue(item, input.airPlaySupported)
|
|
277
|
+
: settingValue(item, library.settings, diagnostics, backends, airPlayDevices, updateCheck, input.appVersion, input.platform),
|
|
285
278
|
separator: ' ',
|
|
286
279
|
index
|
|
287
280
|
}));
|
|
@@ -312,7 +305,7 @@ function adaptiveRows(input) {
|
|
|
312
305
|
}
|
|
313
306
|
}
|
|
314
307
|
rows.push({ key: 'section-commands', label: 'Commands', heading: true });
|
|
315
|
-
for (const command of
|
|
308
|
+
for (const command of commandHelpForAirPlay(input.airPlaySupported)) {
|
|
316
309
|
rows.push({ key: `command-${command.name}`, label: `:${command.name}${command.args ? ` ${command.args}` : ''}`, detail: command.description, index });
|
|
317
310
|
index += 1;
|
|
318
311
|
}
|
|
@@ -320,8 +313,8 @@ function adaptiveRows(input) {
|
|
|
320
313
|
}
|
|
321
314
|
return stationAdaptiveRows(stations, favoriteKeys, selected, width, ascii);
|
|
322
315
|
}
|
|
323
|
-
function adaptiveSettingsRootValue(item) {
|
|
324
|
-
const group =
|
|
316
|
+
function adaptiveSettingsRootValue(item, airPlayVisible) {
|
|
317
|
+
const group = settingsGroupsForAirPlay(airPlayVisible).find(candidate => candidate.label === item);
|
|
325
318
|
return group ? `${group.items.length} settings ›` : undefined;
|
|
326
319
|
}
|
|
327
320
|
function stationAdaptiveRows(stations, favoriteKeys, selected, width, ascii) {
|
|
@@ -378,6 +371,8 @@ function adaptiveEmptyState(input) {
|
|
|
378
371
|
return [{ key: 'error', label: stationError }, { key: 'hint', label: 'Try again later or reopen this view.' }];
|
|
379
372
|
}
|
|
380
373
|
if (screen === 'airplay-settings') {
|
|
374
|
+
if (input.airPlaySetupRequired)
|
|
375
|
+
return [{ key: 'empty', label: 'Setup required' }, { key: 'hint', label: 'radiocli setup' }, { key: 'option', label: '--airplay' }];
|
|
381
376
|
return [{ key: 'empty', label: 'No AirPlay receivers found.' }, { key: 'hint', label: 'Press r to scan again.' }];
|
|
382
377
|
}
|
|
383
378
|
return [{ key: 'empty', label: `No stations in ${stationTitle.toLowerCase()}.` }, { key: 'hint', label: 'Try another view or clear filters.' }];
|
|
@@ -415,6 +410,8 @@ function adaptiveStatus(props) {
|
|
|
415
410
|
return playingStation ? playingStation.name : 'Receiver authentication';
|
|
416
411
|
}
|
|
417
412
|
if (screen === 'airplay-settings') {
|
|
413
|
+
if (props.platform === 'linux' && !props.backends.includes('airplay'))
|
|
414
|
+
return 'Setup required';
|
|
418
415
|
return `${props.airPlayDevices.length} receivers · ${playbackBackendLabel(playback.backend)}`;
|
|
419
416
|
}
|
|
420
417
|
return `${stations.length.toLocaleString()} stations${playingStation ? ` · playing ${playingStation.name}` : ''}`;
|