A Python CLI for RaiPlaySound program discovery, season and episode inspection, incremental downloads, Rich-based transfer progress, metadata caching, RSS feed generation, and playlist generation.
Disclaimer: RaiPlaySound CLI is an independent, community-developed project and is not affiliated with, endorsed by, or in any way officially connected with RAI, RaiPlaySound, or the official RaiPlaySound application. "RAI", "RaiPlaySound", radio station names, program names, show titles, and related marks remain the property of their respective owners. This tool references publicly accessible RaiPlaySound web resources only to help users inspect programs and download episodes they already have access to. It is provided as a free, open-source convenience utility for users who prefer a terminal-based workflow.
Requirements:
- Python 3.10+
yt-dlpffmpeg
System dependency example on macOS:
brew install python yt-dlp ffmpegStandalone user install:
git clone https://github.com/marcomc/raiplaysound-cli.git
cd raiplaysound-cli
make installThis installs the package into:
~/.local/share/raiplaysound-cli/venv~/.local/share/raiplaysound-cli/bin/raiplaysound-cli~/.local/share/raiplaysound-cli/bin/raiplaysound-cli-daily-sync
and creates the user-facing command at:
~/.local/bin/raiplaysound-cli~/.local/bin/raiplaysound-cli-daily-sync
The installed command is a small Python launcher that loads the package from the local install tree. It does not rely on the exact interpreter path baked into a pip-generated console script, so it keeps working if the underlying Python version changes or the venv is recreated.
Editable development install:
git clone https://github.com/marcomc/raiplaysound-cli.git
cd raiplaysound-cli
make install-devmake install-dev keeps the command in ~/.local/bin/raiplaysound-cli, but
points it at the project-local launcher so source edits take effect immediately.
Run the CLI from the user install:
~/.local/bin/raiplaysound-cli --versionInstall and load the optional daily favourites LaunchAgent:
make launchagent-installThis also installs ~/.local/bin/raiplaysound-cli-daily-sync, then schedules
it as a user LaunchAgent at 08:00 every day. The companion runs
raiplaysound-cli download --favourites, compares the downloaded audio files
before and after the run, and sends a plain-text summary email through msmtp
when email settings are configured.
It refreshes only the daily sync companion and LaunchAgent; it does not rewrite
the user-facing ~/.local/bin/raiplaysound-cli command.
The daily sync install and run path follows the Makefile, LaunchAgent plist, and companion command:
flowchart LR
accTitle: Daily favourites sync
accDescr: Shows how the optional LaunchAgent is installed, scheduled, tested, and run.
install["make launchagent-install"] --> companion["Install daily sync companion"]
companion --> plist["Write user LaunchAgent plist"]
plist --> load["Load com.raiplaysound-cli.daily-sync"]
load --> schedule["Run daily at 08:00"]
schedule --> sync["Download configured favourites"]
sync --> report["Send or dry-run email summary"]
Remove the scheduled job:
make launchagent-uninstallThis unloads and removes the LaunchAgent plist. It leaves installed command
files in place; use make uninstall when you also want to remove the CLI and
daily sync companion.
Test the daily sync manually without sending email:
~/.local/bin/raiplaysound-cli-daily-sync --dry-run-emailRun the same sync for real, including email delivery when configured:
~/.local/bin/raiplaysound-cli-daily-syncTrigger the loaded LaunchAgent immediately:
launchctl kickstart -k gui/$(id -u)/com.raiplaysound-cli.daily-syncFollow the daily sync log while it runs:
tail -f ~/Library/Logs/raiplaysound-cli-daily-sync.logCheck the scheduler state:
launchctl print gui/$(id -u)/com.raiplaysound-cli.daily-syncPrint the focused top-level command overview:
~/.local/bin/raiplaysound-cliUse command-specific help for detailed options:
~/.local/bin/raiplaysound-cli list --help
~/.local/bin/raiplaysound-cli search --help
~/.local/bin/raiplaysound-cli download --helpThe subcommand help screens are organized by topic and include short examples, so they are the best place to discover the supported flags in practice.
Or run it directly from the project venv:
.venv/bin/python -m raiplaysound_cli --versionAlternative installs from Git:
pip install "git+https://github.com/marcomc/raiplaysound-cli.git"
pip install --user "git+https://github.com/marcomc/raiplaysound-cli.git"
pipx install "git+https://github.com/marcomc/raiplaysound-cli.git"Uninstalling:
| Install method | Uninstall command |
|---|---|
make install |
make uninstall |
make install-dev |
make uninstall-dev |
pip install ... |
pip uninstall raiplaysound-cli |
pipx install ... |
pipx uninstall raiplaysound-cli |
- Accepts either a RaiPlaySound
program_slugor fullprogram_url - Supports
list,search,download,repair, andoutputscommands - Lists stations, programs, seasons, and episodes
- Searches stations, programs, locally cached seasons/groupings, and locally cached episode metadata
- Downloads episodes into
~/Music/RaiPlaySound/<slug>/ - Repairs existing local filenames from the per-show metadata cache without deleting or re-downloading audio
- Uses
yt-dlp --download-archivefor idempotent repeat runs - Uses RaiPlaySound episode
date_trackingmetadata for episode dates, so filenames and generated feeds follow the editorial episode date rather than a later technical publication date when Rai exposes both. - Supports audio formats
mp3,m4a,aac,ogg,opus,flac, andwav - Supports season filtering, episode ID filtering, and episode URL filtering
- Supports automatic re-download of archive-marked but missing local files
- Generates Rich-based live per-episode download progress with megabyte size tracking and aggregate transfer speed
- Caches program catalog metadata and per-show episode metadata
- Generates optional
feed.xmlRSS output andplaylist.m3uplaylist output - Downloads each program cover image into the program folder and references it from generated RSS feeds
- Provides an optional macOS LaunchAgent companion that runs the configured
favourites download once a day and emails a compact
msmtpsummary - Keeps a
RaiPlayPodcastindex.htmllanding page in the target root with program artwork, descriptions, local episode counts, latest downloaded episode dates, RSS links, and Apple Podcasts app links only for programs that currently have afeed.xml - Preserves the existing
KEY=VALUEdot-config format at~/.raiplaysound-cli.conf
The CLI reads optional user defaults from:
~/.raiplaysound-cli.conf
Use raiplaysound-cli --config /path/to/config <command> for a one-off run
with a different defaults file.
Install the example config:
cp ./.raiplaysound-cli.conf.example ~/.raiplaysound-cli.confExample values:
TARGET_BASE="$HOME/Music/RaiPlaySound"
AUDIO_FORMAT="mp3"
JOBS=5
FAVORITES="musicalbox,profili"
GROUP_BY="auto"
STATION_FILTER="radio2"
CATALOG_MAX_AGE_HOURS=2160
EMAIL_TO="you@example.test"
EMAIL_FROM="you+raiplaysound-cli@example.test"
EMAIL_FROM_NAME="raiplaysound-cli"
EMAIL_SUBJECT_PREFIX="[raiplaysound-cli]"
EMAIL_CONFIG="$HOME/.config/msmtp/config"
MSMTP_BIN="msmtp"Cache-age defaults are intentionally different:
METADATA_MAX_AGE_HOURSdefaults to24for per-show episode metadataCATALOG_MAX_AGE_HOURSdefaults to2160(90 days) for the full program catalog used bylist programs
If you want fresher program listings by default, lower
CATALOG_MAX_AGE_HOURS in your config, for example:
CATALOG_MAX_AGE_HOURS=24Supported config keys:
| Config key | CLI option | Scope |
|---|---|---|
AUDIO_FORMAT |
--format |
download |
JOBS |
--jobs |
download |
SEASONS_ARG |
--season |
download, list episodes |
GROUPS_ARG |
--group |
download, list episodes |
EPISODES_ARG |
--episode-ids |
download |
EPISODE_URLS_ARG |
--episode-urls |
download |
AUTO_REDOWNLOAD_MISSING |
--missing |
download |
ENABLE_LOG |
--log |
download |
DEBUG_PIDS |
--debug-pids |
download |
LOG_PATH_ARG |
--log[=PATH] |
download |
TARGET_BASE |
--target-base |
outputs |
RSS_FEED |
--rss / --no-rss |
download |
RSS_BASE_URL |
--rss-base-url |
download, repair, outputs |
APPLE_PODCASTS |
--no-apple-podcasts |
outputs |
PLAYLIST |
--playlist / --no-playlist |
download |
FORCE_REFRESH_METADATA |
--refresh-metadata |
download |
CLEAR_METADATA_CACHE |
--clear-metadata-cache |
download |
METADATA_MAX_AGE_HOURS |
--metadata-max-age-hours |
download |
FAVORITES |
--favourites |
download |
HTTP_TIMEOUT_SECONDS |
n/a | network requests |
HTTP_RETRIES |
n/a | network requests |
HTTP_BACKOFF_SECONDS |
n/a | network requests |
FAVORITES_PROGRAM_TIMEOUT_SECONDS |
n/a | download --favourites |
FAVORITES_MAX_SECONDS |
n/a | download --favourites |
EMAIL_TO |
n/a | daily sync email |
EMAIL_FROM |
n/a | daily sync email |
EMAIL_FROM_NAME |
n/a | daily sync email |
EMAIL_SUBJECT_PREFIX |
n/a | daily sync email |
EMAIL_CONFIG |
n/a | daily sync email |
MSMTP_BIN |
n/a | daily sync email |
DAILY_SYNC_LOG |
n/a | daily sync |
DAILY_SYNC_MAX_SECONDS |
n/a | daily sync |
DAILY_SYNC_SCAN_TIMEOUT_SECONDS |
n/a | daily sync |
GROUP_BY |
--group-by |
list programs |
PODCASTS_SORTED |
--sorted |
list programs |
STATION_FILTER |
--filter |
list programs |
FORCE_REFRESH_CATALOG |
--refresh-catalog |
list programs |
CATALOG_MAX_AGE_HOURS |
--catalog-max-age-hours |
list programs |
STATIONS_DETAILED |
--detailed |
list stations |
SHOW_URLS |
--show-urls |
list episodes, search |
PAGER |
--pager |
list, search |
INPUT |
<program_slug|program_url> |
download, list seasons, list episodes |
FAVORITES accepts a comma-separated list of RaiPlaySound program slugs or
full program URLs. Running raiplaysound-cli download --favourites iterates
that list in separate child runs, so one failed or timed-out program does not
stop the remaining favourites. If you do not pass --season, --group,
--episode-ids, or --episode-urls, each favourite downloads only the latest
season by default.
HTTP_TIMEOUT_SECONDS, HTTP_RETRIES, and HTTP_BACKOFF_SECONDS apply to
RaiPlaySound HTTP requests across CLI commands. HTTP_RETRIES retries transient
network failures, HTTP 429, and HTTP 5xx responses with exponential backoff;
HTTP 404 is not retried. FAVORITES_PROGRAM_TIMEOUT_SECONDS limits each
favourite child run, and FAVORITES_MAX_SECONDS limits the whole favourites
run. Set a favourites or daily-sync watchdog timeout to 0 to disable that
specific limit.
The daily sync companion uses the same FAVORITES and download defaults. It
does not configure msmtp; it only uses the existing EMAIL_CONFIG file. If
EMAIL_TO, msmtp, or the msmtp config is missing, the download still runs
and the email step is skipped with a note in
~/Library/Logs/raiplaysound-cli-daily-sync.log.
Use raiplaysound-cli-daily-sync --config /path/to/config to run the sync from
an alternate config file; the companion passes that same config to the child
download command. DAILY_SYNC_SCAN_TIMEOUT_SECONDS bounds the before/after file
snapshots used to detect new downloads. DAILY_SYNC_MAX_SECONDS is an outer
watchdog for the scheduled wrapper; if either guard fires, the companion still
sends a failed summary email when email is configured.
FORCE_REFRESH_CATALOG and CATALOG_MAX_AGE_HOURS affect only list programs.
They do not change the per-show metadata cache used by download and
list episodes.
RSS_BASE_URL must be a direct file-serving base URL. The CLI builds enclosure
URLs as:
<RSS_BASE_URL>/<program_slug>/<filename>
Public enclosure filenames are URL-encoded while preserving the filename form stored on disk, so episode titles with accents remain fetchable by podcast clients on servers that expose those files directly.
That means ordinary browser share-page URLs with query strings are not valid RSS enclosure bases.
raiplaysound-cli list stations
raiplaysound-cli list programs
raiplaysound-cli search lucio dalla
raiplaysound-cli list episodes america7
raiplaysound-cli download america7
raiplaysound-cli download --favourites
raiplaysound-cli repair filenames musicalbox
raiplaysound-cli outputs --indexraiplaysound-cli list stations
raiplaysound-cli list programs
raiplaysound-cli list programs --filter radio2
raiplaysound-cli list stations --detailedExample output:
Available RaiPlaySound radio stations (7):
┏━━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━┓
┃ Name ┃ Programs ┃ Slug ┃ Page ┃
┡━━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━┩
│ Rai Radio 1 │ 123 │ radio1 │ open │
│ Rai Radio 2 │ 107 │ radio2 │ open │
│ Rai Radio 3 │ 198 │ radio3 │ open │
└───────────────┴──────────┴──────────────┴──────┘
Next:
programs for one station: raiplaysound-cli list programs --filter radio2
Programs grouped alphabetically (107):
┏━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━┓
┃ Name ┃ Slug ┃ Station ┃ Years ┃ Groupings ┃ Description ┃ Page ┃
┡━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━┩
│ 1 M Next │ 1mnext │ radio2 │ 2025 │ — │ — │ open │
│ 100 Volte Alberto │ 100voltealbe… │ radio2 │ 2020-2025 │ — │ — │ open │
│ Sordi │ │ │ │ │ │ │
│ A qualcuno piace │ aqualcunopia… │ radio2 │ 2025-2026 │ 2 │ Un programma del mattino... │ open │
│ Radio2 │ │ │ │ │ │ │
└──────────────────────┴───────────────┴─────────┴───────────┴───────────┴──────────────────────────────┴──────┘
Next:
one station: raiplaysound-cli list programs --filter radio2
one program: raiplaysound-cli list episodes 1mnext
download one: raiplaysound-cli download 1mnext
list programs intentionally prints the full table by default so the output is
pipe-friendly and easy to capture. For large catalogs, the most practical ways
to narrow or browse the output are --filter, --sorted, or the opt-in
--pager flag rather than auto-paging by default.
raiplaysound-cli search lucio
raiplaysound-cli search burnt sugar --json
raiplaysound-cli search radio2 --show-urlssearch always queries live stations and the cached-or-refreshed global
program catalog. Season/grouping and episode matches come from local caches
that were already populated by earlier list or download runs.
If RaiPlaySound is temporarily unavailable, search still returns any local
season/grouping and episode matches it can find from existing caches instead
of failing outright. Live station and program sections are left empty in that
case, and the cache summary notes the live lookup failure.
That means:
- station and program matches are available immediately
- season/grouping matches depend on cached
list seasonspayloads - episode matches depend on local
.metadata-cache.tsvfiles and, when available, cachedlist episodespayloads
When episode metadata is refreshed through downloads, the per-show
.metadata-cache.tsv now also stores a searchable text field derived from the
episode JSON payload. That lets search match titles, authors, descriptions,
and other metadata strings that Rai exposes for the cached episode.
raiplaysound-cli list seasons america7
raiplaysound-cli list episodes america7
raiplaysound-cli list episodes america7 --show-urls
raiplaysound-cli list seasons america7 --json
raiplaysound-cli list episodes battiti --group specialiSeason listing uses a lightweight discovery path, so list seasons avoids
download-side metadata refreshes and is typically faster than episode
inspection or download preparation.
Repeated list seasons <program> runs now also use a dedicated list-summary
cache under the state directory, separate from download-side metadata, so
grouped programs do not need to re-enumerate every grouping on every call.
For programs that use non-season groupings on RaiPlaySound, list seasons
also reports those groupings instead of incorrectly collapsing everything into a
flat episode list. For example, programs may expose specials, named thematic
collections, or year and period buckets instead of numbered seasons.
Grouping discovery is now filter-first: when RaiPlaySound exposes selector
filters in the program JSON, the CLI treats those filters as the authoritative
group source even if the site uses custom route names such as cicli,
clip, or other product-specific paths.
list seasons is also the long-term grouping inspector name for the CLI, even
when the discovered program structure uses non-season collections.
When a program exposes real seasons, list seasons <program> --season <n> narrows the
output to the requested season. For non-season groupings or flat programs,
--season is rejected instead of being silently ignored.
Season selectors can be ordinary season numbers or year-style season keys such
as 2025 when RaiPlaySound models seasons that way.
list episodes <program> also aggregates episodes across discovered
groupings for grouped programs, instead of only listing the currently selected
subpage.
Episode listing now uses a read-only path: it can reuse an existing
.metadata-cache.tsv to improve titles and dates, but it does not refresh or
rewrite that cache during list episodes.
Repeated list episodes runs also use a list-only cache keyed by the resolved
source scope, so repeated calls for the same program or grouping can reuse the
previous listing without touching download artifacts.
Those list-only caches are versioned, and stale or incompatible cached payloads are rebuilt automatically instead of being reused indefinitely.
When a program uses non-season groupings, list episodes <program> --group <key>
narrows the output to one or more discovered grouping keys or labels. For
example:
raiplaysound-cli list episodes battiti --group speciali
raiplaysound-cli list episodes profili --group speciale-lucio-dalla--group cannot be combined with --season.
list seasons <program> now renders seasons and non-season groupings in a
table with:
ProgramTypeNameEpisodesSelectorPublished
For grouped programs, the footer now shows compact generic download --group
examples instead of repeating one command per discovered grouping.
For flat programs that do not expose real seasons or other groupings,
list episodes does not invent a fake S1 column. Those programs are shown
as a plain episode list, and JSON output reports the season as null.
download <program> now follows the same grouping-aware discovery path as
list episodes, so grouped programs can be downloaded across their
discovered collections instead of only the root subpage.
Download startup is also narrower now: episode discovery still happens across
the selected program or grouping, but metadata refresh is deferred until after
episode filtering. When you use selectors like --group, --episode-ids, or
--episode-urls, the CLI refreshes metadata only for the episodes that will
actually be downloaded.
During that preparation phase, the CLI now prints explicit startup steps such as source discovery, episode enumeration, metadata refresh, and archive-file checks so long grouped downloads are not silent before transfer starts.
If you prefer a batch workflow, set FAVORITES in
~/.raiplaysound-cli.conf and run:
raiplaysound-cli download --favouritesThat iterates each configured favourite program. By default, seasonal
programs download only their latest season in this mode, matching the normal
single-program default. You can still override the selection for every
favourite in the batch with options such as --season all or --group speciali.
Once transfers begin, each per-episode progress row now shows downloaded size
in megabytes, for example 5.0/10.0 MB, while the overall row shows aggregate
transfer speed.
Download workers are now split into two stages:
yt-dlpworkers fetch source audio plus sidecar metadata and thumbnails into a hidden per-run workspace- a separate
ffmpegqueue converts those completed downloads into the final requested audio format and appends the archive entry only after conversion succeeds
That means long local conversion or thumbnail-embedding work no longer blocks the next network download from starting as soon as a download worker becomes free.
The staged conversion path also preserves richer embedded tags for seasonal shows by restoring episode titles plus season and episode numbering from the downloaded sidecar metadata.
When RaiPlaySound exposes both an editorial episode date and a later technical
publication date, the CLI uses the editorial date_tracking value from the
episode JSON. That date is used for the final audio filename, RSS pubDate,
RSS item matching, playlist titles, and missing-file detection.
When --missing is not enabled, the CLI now skips the archive/file existence
scan entirely instead of paying that startup cost on every download.
Use repair filenames when local audio files were downloaded with a technical
publication date but the metadata cache has the corrected RaiPlaySound
date_tracking date.
Preview changes without touching files:
raiplaysound-cli repair filenames musicalbox
raiplaysound-cli repair filenames --favouritesApply the planned renames:
raiplaysound-cli repair filenames musicalbox --apply
raiplaysound-cli repair filenames --favourites --applyThe repair command matches local audio files to .metadata-cache.tsv by
normalized title. If the cache is partial, it can also use an unambiguous
DD/MM/YYYY date embedded in the local title, such as
Musical Box del 12/04/2026. It changes only the first date segment in the
filename and skips ambiguous titles or destination conflicts. When RSS_FEED
or PLAYLIST is enabled in configuration, --apply also regenerates
feed.xml, playlist.m3u, and the root index.html.
If the metadata cache itself needs to be refreshed first, run:
raiplaysound-cli download PROGRAM_SLUG --clear-metadata-cache --refresh-metadata --rss --playlistThen run the filename repair in dry-run mode before applying it.
Example output:
Available seasons for america7 (https://www.raiplaysound.it/programmi/america7):
- Season 1: 71 episodes (published: 2023-2025)
- Season 2: 17 episodes (published: 2025-2026)
Episodes for america7 (https://www.raiplaysound.it/programmi/america7):
┏━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┓
┃ Season ┃ Date ┃ Episode ┃ ID ┃ URL ┃
┡━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━┩
│ S2 │ 2026-03-13 │ America7 S2E17 │ 692674e0-ceb7-4… │ https://www.rai… │
│ │ │ La prima guerra │ │ │
│ │ │ A.I. │ │ │
│ S2 │ 2026-03-06 │ America7 S2E16 │ 7e2a8652-b220-4… │ https://www.rai… │
raiplaysound-cli download [OPTIONS] <program_slug|program_url>
raiplaysound-cli download musicalbox
raiplaysound-cli download --format mp3 --jobs 5 musicalbox
raiplaysound-cli download --season 1,2 america7
raiplaysound-cli download battiti --group speciali
raiplaysound-cli download --missing america7Example completion summary:
Completed: done=1, skipped=0, errors=0
raiplaysound-cli download --episode-ids <id1,id2> america7
raiplaysound-cli download --episode-url <episode-url> america7raiplaysound-cli download --rss --playlist musicalboxThe RSS feed is written as feed.xml. RSS is an XML format, and .xml is the
most compatible extension for podcast clients and static file hosting.
Every download run also refreshes program-level assets in the show's folder:
the program details cache, the local cover image, and the root
~/Music/RaiPlaySound/index.html page. The index page is titled
RaiPlayPodcast and lists each program folder with artwork, title, author,
description, local episode count, latest local episode date, and an RSS link
only when that program folder currently contains feed.xml. When
RSS_BASE_URL is configured, each RSS link is paired with an Apple Podcasts
link using the pcast:// URL scheme. iOS support for third-party podcast URL
schemes is best-effort, so the normal RSS link remains available as a fallback.
When cached episode metadata can be matched to a local file, the latest date
follows Rai's editorial episode date rather than the file's technical
publication date. The generated index uses paths relative to itself, such as
program-slug/cover.jpg, for local folder and artwork links. RSS links are
shown only for program folders that currently contain feed.xml; when
RSS_BASE_URL is configured, those RSS links point to
<RSS_BASE_URL>/<program_slug>/feed.xml. The index generator also stores
apple-touch-icon.png beside index.html, displays it in the page heading,
and references it with Apple touch icon metadata, so iPhone Home Screen
bookmarks can use the bundled RaiPlayPodcast-style icon. When the target root
already contains older program folders without program metadata or cover art,
index generation refreshes those missing assets for each folder.
Regenerate local output files from the files already present on disk, without
downloading episodes again. Use --target-base, --rss-base-url, and
--no-apple-podcasts to try alternate folders, link hosts, or index link
behavior without changing ~/.raiplaysound-cli.conf. Apple Podcasts links are
included by default when the index can use absolute RSS URLs; set
APPLE_PODCASTS=false in config or pass --no-apple-podcasts to omit them.
raiplaysound-cli outputs --index
raiplaysound-cli outputs --rss --index --rss-base-url http://podcasts.example.test/audio
raiplaysound-cli outputs --all --no-apple-podcasts
raiplaysound-cli outputs --target-base /tmp/RaiPlaySound --all --rss-base-url http://podcasts.example.test/audioCommand forms:
raiplaysound-cli download [OPTIONS] <program_slug|program_url>
raiplaysound-cli list <stations|programs> [OPTIONS]
raiplaysound-cli list <seasons|episodes> [OPTIONS] <program_slug|program_url>
raiplaysound-cli outputs [OPTIONS]Output folder contents:
| File | Producer | Purpose |
|---|---|---|
*.m4a / *.mp3 / ... |
yt-dlp |
Downloaded audio episodes |
cover.* |
CLI | Local program artwork |
.program-info.json |
CLI | Per-show program title, author, description, and artwork metadata |
.download-archive.txt |
yt-dlp |
Idempotency archive |
.metadata-cache.tsv |
CLI | Per-show metadata cache |
feed.xml |
CLI | Optional RSS 2.0 podcast feed |
playlist.m3u |
CLI | Optional local playlist |
*.log |
CLI | Optional run/debug log |
Target root contents:
| File | Producer | Purpose |
|---|---|---|
index.html |
CLI | Static browsable program index for the target root |
apple-touch-icon.png |
CLI | Home Screen icon for iPhone bookmarks |
Common commands:
make install
make install-dev
make launchagent-install
make launchagent-uninstall
make uninstall
make run
make test
make lint
make lint-docsValidation currently includes:
ruff check src testsmypy src testsblack --check src testspython -m py_compile src/raiplaysound_cli/*.pypytestmarkdownlint
See docs/TESTING.md
for the full validation and regression-test guide for users and AI agents.
RaiPlaySound CLI is an independent, community-developed project and is not affiliated with, endorsed by, or officially connected with RAI, RaiPlaySound, or the official RaiPlaySound application. "RAI", "RaiPlaySound", radio station names, program names, and related marks remain the property of their respective owners.