零依赖 · 跨平台 · 可脚本化的终端互联网电台播放器 / Zero-dependency terminal internet radio
AirWave 是一款运行在终端里的互联网电台播放器:不需要图形界面、不需要任何第三方运行时库,只用 Python 标准库就能收听全球数万个网络电台,实时看到 Now-Playing 曲目信息,并能把直播流原样录制下来。
它解决了这些真实痛点:
- 🥱 图形播放器太重:浏览器标签页 + 网页播放器占用大量内存,而你只想边写代码边听点背景音。
- 🧩 流格式五花八门:电台地址可能是 M3U、PLS、XSPF、ASX,甚至多层嵌套;Shoutcast/Icecast 的元数据还被穿插在音频字节流里,普通播放器经常读不到歌名。
- 🛰️ 网络环境不稳定:电台目录服务偶尔抽风、公网不可达时播放器直接“白板”。
- 🖥️ 服务器/无头环境无解:没有声卡的机器上,绝大多数播放器无法启动,更别说定时录制节目。
| 能力 | 说明 |
|---|---|
| 零三方依赖 | 运行时只使用 Python 3.8+ 标准库,pip install 后无传递依赖、无供应链负担 |
| 双目录容灾 | 在线对接 Radio Browser(约 5 万家电台,多节点故障转移),失败自动回退到内置 27 个精选台,完全离线可用 |
| 全格式解析 | M3U / M3U8 / PLS / XSPF / ASX 全覆盖,支持嵌套播放列表解析、相对路径补全、循环引用保护 |
| ICY 解复用 | 纯手写 icy-metaint 状态机,精确分离音频字节与 StreamTitle 元数据,录制文件不含元数据帧 |
| 可插拔后端 | mpv / ffplay / VLC / MPlayer 自动探测;独创 null 无头后端,让服务器与 CI 也能跑通“解析→录制→统计”全链路 |
| 双界面 | 可脚本化 CLI(支持 JSON 事件流)+ curses 全屏 TUI,同一套播放引擎 |
| 工程完备 | 67 个单元/集成测试、本地模拟 Shoutcast 服务器、TUI 伪终端冒烟、wheel/sdist 打包校验 |
💡 灵感来源:GitHub Trending 上的终端音频播放器热潮(如
bjarnee/cliamp,周增近 4000 Star)证明了“终端收听”的真实需求。AirWave 没有复制任何现有项目的代码,而是独立设计了一条网络电台优先、零依赖、无头可测的技术路线,并在目录容灾、播放列表格式、ICY 解析与可脚本化方向做了差异化增强。
- 📻 海量电台目录:名称 / 标签 / 国家代码多维搜索,热门榜单一键获取,无需 API Key
- 📶 离线精选目录:内置 SomaFM、Radio Paradise、KEXP、FIP、Radio Swiss 等 27 个稳定公开流,断网也能浏览
- 🎶 实时 Now-Playing:自动解复用 ICY 元数据,终端里实时滚动当前曲目
- 📄 五种播放列表格式:
M3U / M3U8 / PLS / XSPF / ASX,自动识别格式、自动嵌套跳转 - ⏺️ 边听边录 / 纯录制:支持定时停止、定量停止、安全文件名,录制结果是纯净音频流(自动剔除 ICY 帧)
- 🔌 后端自动探测:按
mpv → ffplay → mplayer → vlc → null优先级选择,也可--backend手动指定 - ⭐ 收藏与历史:本地 JSON 原子写入,收藏增删查、收听历史、JSON/CSV 导出
- 💻 curses 全屏 TUI:榜单 / 收藏 / 内置目录三个数据源、关键词搜索、一键收藏、一键录制
- 🧑💻 JSON 事件流:
--json输出结构化播放事件,方便接入脚本、状态栏、通知系统 - 🪶 跨平台:Linux / macOS 原生可用;Windows 通过可选的
windows-curses使用 TUI,CLI 全平台一致 - 🔒 隐私友好:无账号、无遥测、无埋点;收藏与历史只保存在本机配置目录
| 项目 | 要求 |
|---|---|
| Python | 3.8 及以上(3.8 / 3.9 / 3.10 / 3.11 / 3.12 均已兼容) |
| 运行时依赖 | 无(仅标准库) |
| 播放后端(可选) | 系统中安装任意一个即可:mpv、ffmpeg 自带的 ffplay、VLC、MPlayer;都没有时可用 null 后端跑录制与解析 |
| 网络 | 在线目录/收听需要联网;内置目录与本地播放列表离线可用 |
# 方式一:pip 安装(推荐)
pip install airwave-radio
# Windows 用户如需 TUI,补装可选依赖:
pip install "airwave-radio[windows]"
# 方式二:pipx 隔离安装(命令行工具首选)
pipx install airwave-radio
# 方式三:源码运行(零安装)
git clone https://github.com/gitstq/airwave.git
cd airwave
PYTHONPATH=src python3 -m airwave --version# 1. 看看本机有哪些可用播放后端
airwave backends
# 2. 浏览热门电台(在线 Radio Browser,自动回退内置目录)
airwave top --limit 15
# 3. 搜索关键词
airwave search "jazz"
airwave search "" --tag ambient --country US
# 4. 直接播放:支持「电台名 / 收藏名 / 直接 URL」三种写法
airwave play "Groove Salad"
airwave play https://ice1.somafm.com/groovesalad-128-mp3
# 5. 全屏终端界面
airwave tui🎧 没有安装任何播放器?先
airwave play "Groove Salad" --backend null验证链路,再按 后端安装指引 装一个 ffplay/mpv。
| 命令 | 作用 |
|---|---|
airwave top |
热门电台榜单 |
airwave search <关键词> |
按名称/标签/国家搜索 |
airwave play <目标> |
播放(目标 = 名称、收藏名或 URL) |
airwave record <目标> |
无界面录制(自动使用 null 后端) |
airwave fav add/list/rm |
收藏管理 |
airwave history |
收听历史 / 导出 / 清空 |
airwave catalog |
浏览内置离线目录 |
airwave backends |
探测播放后端 |
airwave tui |
全屏终端界面 |
# 在线热门榜(默认 30 条)
airwave top --limit 20
# 只看内置离线目录(飞机上/内网环境)
airwave top --offline
airwave catalog --tags # 列出内置目录全部标签
airwave catalog --tag jazz # 按标签筛内置目录
# 多条件搜索
airwave search paradise
airwave search "" --tag classical --country CH --limit 10
# 给脚本用:输出 JSON
airwave search soma --json# 名称会按「收藏 → 在线/离线目录」顺序自动匹配
airwave play "Radio Paradise"
# 直接给流地址或播放列表地址(.m3u/.pls/.xspf/.asx 会自动解析嵌套)
airwave play https://example.com/station.m3u
# 指定后端 / 音量 / 播放 10 分钟后自动退出
airwave play "Drone Zone" --backend ffplay --volume 80 --duration 600
# 结构化事件流(适合写脚本、接入 polybar/tmux 状态栏)
airwave play "Groove Salad" --backend null --json
# 输出示例:
# {"event":"connected","content_type":"audio/mpeg","bitrate":128,...}
# {"event":"metadata","title":"Rena Jones - Driftwood"}
# {"event":"status","elapsed":8.9,"kbps":128.2,...}
# {"event":"ended",...}# 边听边录(录满 30 分钟自动停止)
airwave play "Groove Salad" --record --record-for 1800
# 无界面纯录制(服务器定时任务场景,不依赖声卡)
airwave record "FIP" --duration 3600
airwave record https://ice1.somafm.com/dronezone-128-mp3 --out ~/show.mp3 --duration 1800
# 配合系统定时任务,每天凌晨录一档节目
# crontab 示例:
0 1 * * * /usr/bin/airwave record "FIP" --duration 3600录制文件默认保存在:
- Linux:
~/.local/share/airwave/recordings/ - macOS:
~/Library/Caches/airwave/recordings/ - Windows:
%LOCALAPPDATA%\airwave\recordings\ - 可用环境变量
AIRWAVE_RECORDINGS覆盖目录。
airwave fav add "我的台" https://example.com/stream --tags "jazz,lofi"
airwave fav list
airwave fav rm "我的台"
airwave history # 最近 20 条
airwave history --export h.csv # 导出 CSV(也支持 .json)
airwave history --clear # 清空收藏/历史文件位于配置目录(Linux/macOS:~/.config/airwave/,Windows:%APPDATA%\airwave\),写入采用临时文件 + 原子替换,进程崩溃也不会损坏资料库。
启动:airwave tui(追加 --offline 仅用内置目录,--backend ffplay 指定后端)
| 按键 | 功能 |
|---|---|
↑ ↓ / j k |
移动光标,PgUp/PgDn 翻页 |
Enter |
播放选中电台 |
x |
停止播放 |
r |
边听边录 |
f |
收藏 / 取消收藏 |
/ |
输入关键词搜索(Enter 确认,Esc 取消) |
1 / 2 / 3 |
切换 热门榜 / 收藏 / 内置目录 |
q |
退出 |
🖼️ 演示动图/截图占位:
docs/demo/(欢迎 PR 补充你的运行截图)
| 变量 | 作用 | 默认 |
|---|---|---|
AIRWAVE_BACKEND |
默认播放后端(等价 --backend) |
自动探测 |
AIRWAVE_RECORDINGS |
录制输出目录 | 系统数据目录 |
XDG_CONFIG_HOME / XDG_DATA_HOME |
Linux 配置/数据根目录 | 遵循 XDG 规范 |
APPDATA / LOCALAPPDATA |
Windows 配置/数据目录 | 系统默认 |
| 后端 | 安装(Linux) | 安装(macOS) | 安装(Windows) | 喂流方式 |
|---|---|---|---|---|
| mpv(推荐) | apt install mpv |
brew install mpv |
官网下载加入 PATH | 管道喂流 |
| ffplay | apt install ffmpeg |
brew install ffmpeg |
下载 ffmpeg | 管道喂流 |
| VLC | apt install vlc |
brew install --cask vlc |
官网安装 | 后端直连 URL |
| MPlayer | apt install mplayer |
brew install mplayer |
构建安装 | 管道喂流 |
| null(内置) | 无需安装 | 无需安装 | 无需安装 | 丢弃音频,仅跑链路 |
Q1:提示“未找到任何可用播放后端”?
A:AirWave 自身不发声,需要系统里有 mpv/ffplay/VLC/MPlayer 之一;临时验证可用 --backend null,服务器录制场景也用它。
Q2:TUI 启动报 curses 相关错误?
A:Linux/macOS 的官方 Python 自带 curses;Windows 请执行 pip install windows-curses,或直接使用 CLI 子命令。
Q3:某个电台打不开 / 一直缓冲?
A:直播流会失效或做地区限制。可先 airwave play <url> --json 看连接事件;确认源失效后欢迎到 Issue 标注 stream-broken。
Q4:为什么录制的是 MP3 但文件能直接播?
A:AirWave 录制的是剔除 ICY 元数据帧后的纯净音频字节,扩展名按流类型选择(AAC 流为 .aac),不需要二次封装。
Q5:会上传我的收听记录吗? A:不会。除了向你指定的电台地址与公开的 Radio Browser 发起请求外,没有任何遥测。
┌──────────────┐ ┌──────────────┐
│ CLI / TUI │────▶│ StreamPlayer │ 播放引擎(线程安全、UI 无关)
└──────────────┘ └──────┬───────┘
├─▶ playlists M3U/PLS/XSPF/ASX + 嵌套解析
├─▶ metadata ICY 帧状态机 / StreamTitle
├─▶ directory Radio Browser + 内置离线目录
├─▶ library 收藏 / 历史(原子 JSON)
└─▶ backends mpv/ffplay/vlc/mplayer/null
关键取舍:
- 标准库优先:
urllib+http.server+curses+xml.etree足以覆盖全部能力,换来零供应链风险与极致可移植性。 - 引擎与界面解耦:播放引擎只通过事件回调对外通信,因此 CLI、TUI、自动化脚本与测试共用同一条链路,行为完全一致。
- 永不阻塞在直播流上:播放列表解析只对“看起来像播放列表”的 URL 发起读取,直连流零预读,从设计上杜绝挂死。
- 后端外置:解码交给成熟播放器,AirWave 专注网络协议、元数据与录制,因此不需要绑定任何音频驱动。
- v1.1:电台测速与延迟排序、收藏分组、TUI 内音量调节
- v1.2:HLS(m3u8 分片)内置 demux 录制、节目单(EPG)抓取
- v1.3:TUI 主题/键位配置文件、桌面通知(now-playing)
- v1.4:局域网 Web 遥控模式、Podcast RSS 订阅下载
- 长期:更多目录源、插件机制、可选的 equalizer 后处理
欢迎认领路线图条目,也特别欢迎:新增稳定公开电台到内置目录、补充多语言翻译、完善打包(Homebrew/AUR/Scoop)。
AirWave 属于工具库 / CLI 类项目(纯 Python、跨平台、无需编译原生二进制),因此以 wheel/sdist 形式分发,不发布平台可执行文件。
pip install build
python -m build
# 产物:
# dist/airwave_radio-1.0.0-py3-none-any.whl (跨平台 wheel)
# dist/airwave_radio-1.0.0.tar.gz (源码包)把 wheel 拷贝到目标机器后:
pip install airwave_radio-1.0.0-py3-none-any.whl --no-index --find-links .
airwave --version| 平台 | CLI | TUI | 说明 |
|---|---|---|---|
| Linux | ✅ | ✅ | 主力验证环境(Ubuntu 22.04 / Python 3.10) |
| macOS | ✅ | ✅ | 系统 Python 自带 curses |
| Windows | ✅ | windows-curses |
CLI 与录制能力不受影响 |
| 无头服务器 / 容器 | ✅(null 后端) |
— | 适合定时录制与元数据采集 |
# 1) 安装
pipx install airwave-radio
# 2) 验证无头链路
airwave play "FIP" --backend null --duration 5
# 3) 用 cron/systemd timer 周期执行 `airwave record ...` 即可欢迎 Issue、PR 与电台源推荐!开发前请阅读 CONTRIBUTING.md:测试基于标准库 unittest(PYTHONPATH=src python3 -m unittest discover -s tests),提交信息遵循 feat/fix/docs/refactor/test/chore 的 Angular 规范,且不接受新增运行时第三方依赖。
本项目基于 MIT License 开源,可自由使用、修改与商用,请保留版权声明。内置目录中的电台流版权归各电台所有。