Gheat1/NaviTui
GitHub: Gheat1/NaviTui
一款快速、带动画的终端音乐播放器,专为 Navidrome 和 Subsonic 服务器设计,融合了 TUI 的键盘效率与 GUI 级别的视觉体验和功能丰富度。
Stars: 9 | Forks: 2
# ♪ NaviTui
**一款快速、带动画的终端播放器,专为 [Navidrome](https://www.navidrome.org/) 和任何 Subsonic 服务器设计。**
终端内显示真实专辑封面 · 通过 mpv 播放 · 离线下载 ·
卡拉OK歌词 · 无限电台 · 供脚本和 AI agent 使用的控制 API ·
五款支持实时预览的主题 —— 一切皆动。

[](LICENSE)
[](https://www.python.org/)
[](https://github.com/Textualize/textual)
[](https://github.com/Gheat1/ricekit)
## 目录
- [为什么选择 NaviTui](#why-navitui)
- [截图](#screenshots)
- [功能](#features)
- [安装](#install)
- [首次运行](#first-run)
- [快捷键](#keybindings)
- [配置](#configuration)
- [自动化与控制 API](#automation--the-control-api)
- [工作原理](#how-it-works)
- [故障排除](#troubleshooting)
- [开发](#development)
- [套件](#the-suite)
- [许可协议](#license)
- [鸣谢](#acknowledgements)
## 为什么选择 NaviTui
如果你在 **Navidrome**(或任何兼容 Subsonic 的服务器)上自托管音乐,
并且你习惯在终端中工作,你的选择通常要么是简陋的 TUI,要么是完整的
桌面 GUI。NaviTui 旨在寻找最佳平衡点:一款**像 TUI 一样快速且依赖键盘驱动,同时又像 GUI 一样功能丰富且美观**的播放器 ——
真实的专辑封面、流畅的动画、离线下载,以及其他同类软件所不具备的自动化界面。
它的核心设计只为了一个工作流:**只管播放我的歌曲。** 没有需要点击浏览的专辑网格,
也没有“现在去浏览这位艺术家的唱片目录”之类的繁文缛节。只有一个列出歌曲方式的侧边栏,
一个巨大的曲目列表,按下回车即可。专辑和艺术家依然存在——它们存在于搜索功能中,
只有当你特意去寻找它们时才会出现。
**与众不同之处**
- **以真实像素显示封面艺术** —— 在你的终端支持的情况下使用 kitty 图形 / sixel,并在不支持时优雅降级至真彩色半字符单元,然后再降级为 unicode。
- **正确的离线优先** —— 将单曲、播放列表或整个媒体库固定到磁盘;离线模式会完全跳过网络;并且具备离线**变更队列**,让你在飞机上标记的红心和 scrobble 记录在落地后自动同步。
- **控制 API + MCP 服务器** —— 通过 shell 脚本、全局热键或 AI agent 来驱动运行中的播放器。在这个领域中,没有其他软件能做到这一点。
- **生机勃勃的感觉** —— 闪烁的图标、能根据真实播放音量做出反应的可视化效果、会“呼吸”的亚字符单元进度条——所有这些都由单一的 8fps 心跳信号驱动,该信号仅重绘少量字符单元,因此性能开销很低。
跨平台支持:播放功能可在 **Linux、macOS 和 Windows** 上运行(任何 `libmpv` 可以运行的地方)。原生的“正在播放”功能依赖于各个操作系统的集成——Linux 上的 **MPRIS2**,macOS 上的 **MPNowPlayingInfoCenter / 控制中心**,以及 Windows 上的 **系统媒体传输控制 (SMTC)**——这样曲目、封面和进度就会显示在操作系统指定的任何地方。(元数据显示在所有三个平台上都很稳定;macOS/Windows 上的媒体*按键*传递功能是新增的,仍需进行针对性测试。)
## 截图
**搜索 —— 一切内容,即时分组呈现**

**`void` 主题 —— 纯黑 OLED,封面熠熠生辉**

**同步滚动的歌词**
## 功能
### 歌曲优先的媒体库
- 一个侧边栏:**所有曲目**、**最近添加**、**最近播放**、**最常
播放**、**已加星标**、**随机播放所有** —— 你的**播放列表**就在
其下,还有**流派** (`y`)、**播客**和**网络电台**。
- 专辑和艺术家存在于**搜索** (`/`) 中,那才是它们该在的地方;你可以从
任何曲目跳转到其所属专辑 (`e`) 或艺术家 (`E`)。
- **输入即过滤** (`\`) 会在你输入时缩小当前列表的范围 —— 无需网络
往返,瞬间完成。
- **多选** (`v`) —— 标记多行,然后将整个选择内容加入队列、加入播放列表、加星标或一次性
全部下载。
### 播放与队列
- 一个能显示**即将播放**内容的队列 —— 已播放的曲目在上方以暗淡显示;
向上滚动可查看历史记录。添加 (`a`)、下一首播放 (`A`)、移除 (`x`)、清空 (`X`)、
重排 (`ctrl+↑/↓`)。
- **随机播放** 会重新打乱即将播放的内容,并从一首随机
曲目开始全新视图;**循环** 关闭 / 全部循环 / 单曲循环。
- **睡眠定时器** (`<`) 和**播放速度** (`>`) —— 非常适合播客和
有声读物;**交叉淡入淡出** 结合下一曲预取,实现无缝过渡。
- 队列 —— **包括你在当前歌曲中的播放位置** —— 在重启后依然存在。
### 离线
- **下载**单曲 (`d`)、整个视图或播放列表 (`D`),或者你的**整个
媒体库** (`ctrl+d`)。已固定的曲目会标记上 ✓ 并永远在本地播放。
- **离线模式** (`O`) 仅播放已下载的内容并跳过网络。
- 在离线状态下生成的红心 / 评分 / scrobble 记录会在重新连接时进行**排队并刷新**。
- 流媒体**比特率上限** (`Q`),适合拥挤的连接 —— 下载内容始终保留
原始文件。
### 卡拉OK 歌词
- 定时、滚动、高亮的歌词 (`L`) 随节拍跳动 —— 并且在
**禅模式** (`z`) 中拥有相同的视图:一个巨大的、居中的封面与歌词展示。
### 探索
- **无尽电台** —— 从任何曲目或艺术家开始建立一个电台 (`i`),或者让
队列在播完时**自动永远播放相似曲目** (`I`)。
- **书签** (`w` / `W`) 用于保存并跳回到长曲目中的某个位置。
- **收听统计数据** (`ctrl+w`) —— 一个本地的年度听歌报告迷你版:热门曲目与艺术家、
本周播放次数、活跃度走势图、连续播放记录。
### 桌面集成的一部分
- 每个平台上的原生“正在播放”功能:**MPRIS2**(Linux —— 媒体键、waybar、
playerctl),**控制中心 / 锁屏界面**(macOS),**系统媒体传输控制**(Windows)。安装 `navitui[macos]` 或 `navitui[windows]` 即可启用这些功能。
- 切换曲目时的**桌面通知**(封面作为图标显示),并带有**上一首 /
播放暂停 / 下一首**操作按钮。
- 可选的 **Discord rich presence**,带有实时进度条。
- 可选的 **ListenBrainz** scrobbling,与你服务器自身的 scrobble 并行记录。
### 控制与自动化
- 通过 unix socket 暴露的本地**控制 API**,以便其他工具可以驱动正在运行的
播放器。它为以下功能提供支持:
- **`navitui-remote`** —— 一个可编写脚本的 CLI(`navitui-remote status`、`next`、
`play "
"`、……),非常适合配置全局热键。
- **`navitui-mcp`** —— 一个 MCP 服务器,让 Claude 和其他 agent 能够控制
播放、搜索、下载并读取正在播放的内容。
- **点唱机模式** (`J`) —— 驱动**服务器自带**的音频输出,适用于连接到真实扬声器的无头设备。
### 可实际编辑的播放列表
- 创建、添加 (`p`)、移除 (`P`)、重排 (`shift+↑/↓`)、重命名 (`ctrl+r`)、
删除 (`ctrl+x`) —— 或者**将当前队列保存为播放列表** (`ctrl+s`)。
### 生动且美观
- 图标闪烁、**可视化效果随真实播放音量做出反应**、
进度条具有 1/8 字符单元的分辨率并且会“呼吸”,长标题以跑马灯方式滚动、
面板淡入淡出 —— 所有这些都由一个 8fps 心跳信号驱动。
- **专辑封面主题化** 会根据当前封面的颜色对 UI 进行染色。
- **五款主题**,支持实时预览(`t` 循环切换,`T` 选择) —— 包括 `clear`
(透出终端的透明度)和 `system`(使用终端自带的
ANSI 调色板)。
- **缓存优先** —— 每个面板都会瞬间从磁盘渲染,然后在后台静默刷新
(每 3 分钟自动刷新一次)。
### 其他功能
- **Scrobble、红心与评分** —— 加星 (`f`),在数字键行上按 1–5 进行评分。
- **命令面板** (`ctrl+p`) —— 每一个操作都支持模糊搜索。
- 将正在播放的卡片导出为 **SVG** (`C`);复制**分享链接** (`S`)。
- **Vim 重复计数**(`3j` 表示向下移动三次)和完整的**鼠标支持** ——
点击进度条进行跳转,拖动面板分隔线,点击随机播放/循环。
## 安装
你需要 **libmpv** 来进行播放(其他所有依赖都已随包提供):
```
# arch
sudo pacman -S mpv
# debian / ubuntu
sudo apt install libmpv2
# fedora
sudo dnf install mpv-libs
# macos
brew install mpv
# windows — 将 libmpv-2.dll 放到你的 PATH 中:https://mpv.io/installation/
```
然后安装 NaviTui。使用 [`uv`](https://github.com/astral-sh/uv) 是最简单的方式:
```
uv tool install "navitui[integrations] @ git+https://github.com/Gheat1/NaviTui"
navitui
```
或者使用 pip:
```
pip install "navitui[integrations] @ git+https://github.com/Gheat1/NaviTui"
```
**可选附加组件**
| 附加组件 | 拉取依赖 | 用途 |
| --- | --- | --- |
| `integrations` | `dbus-fast`, `pypresence` | MPRIS2 媒体键 (Linux), Discord presence |
| `macos` | `pyobjc-framework-MediaPlayer`, `pyobjc-framework-Cocoa` | 控制中心正在播放 (macOS) |
| `windows` | `winrt-runtime`, `winrt-Windows.*` | 系统媒体传输控制 (Windows) |
| `mcp` | `mcp` | `navitui-mcp` agent 服务器 |
你可以组合它们:`navitui[integrations,mcp]`。如果只想要纯粹的播放器,可以完全不安装附加组件 —— 每一个集成在缺少其依赖项时都会自动降级为一个干净的无操作,因此不会发生任何崩溃报错。
## 首次运行
在首次启动时,NaviTui 会要求你输入**服务器 URL、用户名和密码**,
并实时验证它们,然后存储一个**加盐 token**(绝不存储密码本身)
存放在 `~/.config/navitui/` 目录中,权限为 `chmod 600`。它还会在同目录下生成一个带有注释的
`player.toml` 文件,供你稍后微调。
兼容 **Navidrome** 以及任何兼容 **Subsonic / OpenSubsonic** 的服务器
(Gonic、Airsonic 等)。还没有自己的服务器?可以尝试针对公共演示服务器运行:
```
server: https://demo.navidrome.org
username: demo
password: demo
```
## 快捷键
在应用内按 `?` 查看实时备忘单,或按 `ctrl+p` 根据名称模糊搜索每一个操作。这里的每一个按键都可以重新映射 —— 请参阅 [配置](#configuration)。
列表支持 Vim 风格导航(`j` / `k` / `g` / `G`)并且接受**重复计数**
(`5j`)。
**播放与传输**
| 按键 | 动作 | 按键 | 动作 |
| --- | --- | --- | --- |
| `space` | 播放 / 暂停 | `s` | 随机播放 |
| `n` / `b` | 下一首 / 上一首 | `r` | 循环 (关闭 → 全部 → 单曲) |
| `←` / `→` | 快进/退 5s | `m` | 静音 |
| `shift+←/→` | 快进/退 30s | `>` | 播放速度 |
| `-` / `+` |量减小 / 增大 | `<` | 睡眠定时器 |
**队列**
| 按键 | 动作 | 按键 | 动作 |
| --- | --- | --- | --- |
| `a` | 添加到队列 | `ctrl+↑` / `ctrl+↓` | 上移 / 下移曲目 |
| `A` | 下一首播放 | `X` | 清空队列 |
| `x` | 移除曲目 | `ctrl+s` | 将队列保存为播放列表 |
**媒体库与导航**
| 按键 | 动作 | 按键 | 动作 |
| --- | --- | --- | --- |
| `/` | 搜索所有内容 | `e` / `E` | 前往专辑 / 艺术家 |
| `\` | 过滤当前列表 | `y` | 按流派浏览 |
| `v` | 多选模式 | `R` | 从服务器刷新 |
| `h` / `l` | 上一个 / 下一个面板 | `j`/`k`/`g`/`G` | 在列表中移动 |
**播放列表**
| 按键 | 动作 | 按键 | 动作 |
| --- | --- | --- | --- |
| `p` | 添加到播放列表 | `shift+↑/↓` | 在播放列表中重新排序 |
| `P` | 从播放列表移除 | `ctrl+r` | 重命名播放列表 |
| `ctrl+s` | 将队列保存为播放列表 | `ctrl+x` | 删除播放列表 |
**离线与音质**
| 按键 | 动作 | 按键 | 动作 |
| --- | --- | --- | --- |
| `d` | 下载曲目 | `O` | 离线模式 |
| `D` | 下载视图 / 播放列表 | `Q` | 循环切换流媒体质量 |
| `ctrl+d` | 下载整个媒体库 | `J` | 点唱机(服务器音频)模式 |
**探索与正在播放**
| 按键 | 动作 | 按键 | 动作 |
| --- | --- | --- | --- |
| `i` | 从此处开启电台 | `L` | 歌词 |
| `I` | 无尽自动播放开关 | `z` | 禅模式全屏 |
| `w` / `W` | 设置 / 列出书签 | `ctrl+w` | 收听统计数据 |
| `f` | 加星 / 取消加星 | `1`–`5` | 评分(再次按相同数字取消) |
| `S` | 复制分享链接 | `C` | 导出正在播放卡片 (SVG) |
**应用**
| 按键 | 动作 | 按键 | 动作 |
| --- | --- | --- | --- |
| `t` / `T` | 循环切换 / 选择主题 | `N` | 切换通知 |
| `ctrl+p` | 命令面板 | `?` | 帮助 |
| `R` | 刷新 | `q` | 退出 |
## 配置
NaviTui 会在首次运行时写入一个带有完整注释的 `~/.config/navitui/player.toml` 文件。
编辑它并**重启**以应用更改。所有内容都是可选的且可以安全删除 ——
缺失的配置项会回退到默认值,格式错误的文件会被忽略而不会导致致命错误。
```
# ── 播放 ──────────────────────────────────────────────
replaygain = "album" # "album" | "track" | "no"
gapless = "weak" # "yes" | "weak" (gapless when formats match) | "no"
crossfade = 0.0 # seconds of soft fade on track change (0 = off)
max_bitrate = 0 # streaming cap in kbps (0 = original / unlimited)
stream_format = "" # transcode target: "mp3" | "opus" | "raw" | "" original
# ── 桌面与 scrobbling ─────────────────────────────────
notifications = true # desktop notification on track change
art_theming = true # tint the UI toward the cover's colors
discord_rich_presence = false
discord_app_id = "" # from discord.com/developers/applications
listenbrainz_token = "" # from listenbrainz.org/profile
# ── 控制与服务器音频 ───────────────────────────────
remote_control = true # local control API (unix socket) for the CLI & MCP
remote_token = "" # optional shared secret (required on the TCP fallback)
jukebox = false # play on the SERVER's audio output, not this machine
# ── 重新映射任意键 ────────────────────────────────
[keybinds]
# action_id = "key" — 使用逗号分隔别名,例如 "plus,equals_sign"
next_track = "ctrl+n"
download = "d"
# …`?` 中列出的每个操作都可以在此重新绑定
```
- **`replaygain` / `gapless` / `crossfade`** 会直接传递给 mpv,因此它们的
行为与 mpv 自身的选项完全一致。
- **`max_bitrate` / `stream_format`** 仅适用于网络流 —— **离线
下载始终保留原始文件**。在运行时按 `Q` 可循环切换预设。
- **`remote_control`** 暴露了供 CLI 和 MCP 服务器通信的 socket;它
**仅限 localhost / socket 访问**,永远不会离开你的机器。设置 `remote_token`
可要求提供共享密钥。
## 自动化与控制 API
正在运行的 NaviTui 会通过 unix socket 暴露一个小型的本地控制 API,因此你可以从同一台机器上的任何地方驱动它。
**从 shell 驱动**(非常适合全局热键):
```
navitui-remote status # what's playing, as JSON
navitui-remote play-pause # toggle
navitui-remote next # skip
navitui-remote play "daft punk" # search + play the top hit
navitui-remote volume 80 # set volume
```
**从 AI agent 驱动** —— 将任何 MCP 客户端指向 `navitui-mcp`:
```
// e.g. Claude Code / any MCP host
{
"mcpServers": {
"navitui": { "command": "navitui-mcp" }
}
}
```
然后,agent 就可以搜索你的媒体库、排队和下载曲目、控制
播放,并报告正在播放的内容。传输层使用的是同一个本地 socket ——
不会向你的机器外部暴露任何内容。
## 工作原理
三个原则,按优先级排列:
1. **快速** —— 到处都是缓存优先。每个面板会在大约 50 毫秒内从磁盘渲染出最后已知的状态,然后后台工作进程会获取新的数据行并无缝替换它们。变更操作会立即更新缓存,因此你所看到的永远是你刚刚执行的操作 —— 即使你在刷新完成前退出也没关系。
2. **生动** —— 一个共享的 8fps 心跳信号驱动着*每一个*动画(logo 闪烁、
可视化效果、进度脉动、跑马灯、加载图标)。每一次跳动只重绘少数几个单元格,因此持续的运动几乎不消耗任何资源,也永远不会占用你的 CPU。
3. **美观** —— 圆角边框、严谨的调色板以及 nerd-font
图标,所有这些都源自 [**ricekit**](https://github.com/Gheat1/ricekit),这是 NaviTui 与其套件的其他部分共享的
设计系统。
底层采用:使用 [Textual](https://github.com/Textualize/textual) 构建 UI,
使用 [`python-mpv`](https://github.com/jaseg/python-mpv) (libmpv) 进行播放,
使用 [`textual-image`](https://github.com/lnqs/textual-image) 处理终端原生
封面图,以及异步的 [httpx](https://www.python-httpx.org/) Subsonic 客户端。
身份验证采用加盐 token 方案 —— 密码会被转换为每个会话唯一的
token,绝不写入磁盘。
## 故障排除
**"找不到 libmpv" / 没有声音。** NaviTui 仍然可以运行(你可以浏览、搜索、
排队),但无法播放音频。请为你的操作系统安装 mpv —— 请参阅 [安装](#install)。
在 Windows 上,必须确保 `libmpv-2.dll` 位于你的 `PATH` 中。
**封面图显示为方块而不是真实图像。** 你的终端(或者
像 tmux 这样的复用器)没有公告支持图形协议,因此 NaviTui 退回
使用半字符单元。请使用 `NAVITUI_ART` 环境变量强制指定一种协议:
```
NAVITUI_ART=auto # detect the best available (default)
NAVITUI_ART=tgp # force the kitty graphics protocol
NAVITUI_ART=sixel # force sixel
NAVITUI_ART=halfcell # truecolor half-blocks (works nearly everywhere)
NAVITUI_ART=unicode # last-resort unicode
NAVITUI_ART=off # a tasteful placeholder, no image
```
kitty、WezTerm、Ghostty 和 Konsole 都能渲染真实图像;在 tmux 内部你可能
需要设置 `NAVITUI_ART=tgp`。
**媒体键不起作用。** 在 Linux 上,它们依赖于 MPRIS2 —— 请安装
`integrations` 附加组件(`dbus-fast`)并确保处于会话总线中;当 NaviTui 正在播放时,`playerctl status`
应当报告 NaviTui。在 macOS 上,请安装 `navitui[macos]`
(正在播放会出现在控制中心);在 Windows 上,请安装 `navitui[windows]`
(SMTC 叠加层)。元数据在所有三个平台上都能可靠显示 —— 如果硬件媒体*键*在 macOS/Windows 上仍然无法传达到 NaviTui,那是因为该路径是新增的,目前正在强化中,所以请提交一个包含你的终端和操作系统版本信息的 issue。
**什么都不加载 / 提示处于离线状态。** NaviTui 在无法连接服务器时会显示你上次缓存的媒体库,因此你仍然可以播放已下载的曲目。请检查
`~/.config/navitui/` 中的服务器 URL,或按 `R` 重试。
**某个按键被绑定到了我不想要的功能。** 请在 `player.toml` 的 `[keybinds]` 下重新绑定它,然后重启 —— 请参阅 [配置](#configuration)。
## 开发
```
git clone https://github.com/Gheat1/NaviTui
cd NaviTui
python -m venv .venv && . .venv/bin/activate
pip install -e ".[integrations,mcp]"
navitui
```
测试在无头模式下针对模拟客户端运行,或者针对公共演示服务器以只读方式进行。使用 `HOME=$(mktemp -d)` 隔离状态,并传入 `ao="null"`,这样 mpv 就不需要音频设备了。README 中的截图是生成的,绝不是从真实的媒体库截取的 —— `tools/shots.sh` 在 kitty 中驱动真实应用;`tools/screenshots.py` 是无头环境下的后备方案。请参阅 [`CLAUDE.md`](CLAUDE.md) 了解架构图
和来之不易的坑点表。
欢迎提交 Issue 和 PR。NaviTui 采用 GPL-3.0-or-later 许可协议,因此贡献和分叉
都将保持开源(请参阅 [许可协议](#license))。
## 套件
- [**ricekit**](https://github.com/Gheat1/ricekit) —— NaviTui 所基于的 Textual 设计系统
- [**ltui**](https://github.com/Gheat1/ltui) —— 一款快速、美观的 Linear TUI,ricekit 的诞生地
## 许可协议
[**GPL-3.0-or-later**](LICENSE) —— 由 [@Gheat1](https://github.com/Gheat1) 制作。
**0.3.0** 及之前的版本采用 MIT 许可;从 **0.4.0** 版本开始,NaviTui 采用 GPL 许可,因此衍生作品
将保持开源并保留其声明。(早期 MIT 版本的副本
仍然是 MIT 许可的——这是无法撤回的——但目前的所有内容均采用 Copyleft 许可。)
## 鸣谢
站在巨人的肩膀上:[Textual](https://github.com/Textualize/textual)、
[mpv](https://mpv.io/)、[Navidrome](https://www.navidrome.org/) 和
[Subsonic API](https://www.subsonic.org/pages/api.jsp)、
[textual-image](https://github.com/lnqs/textual-image) 以及
[httpx](https://www.python-httpx.org/) —— 封面图协议得益于
[kitty](https://sw.kovidgoyal.net/kitty/graphics-protocol/) 和 sixel 图形
标准。感谢每一个自托管音乐并保持
生态系统活力的人。
*如果 NaviTui 让你的媒体库重新有了归属感,请给它点个 ⭐。*
标签:mpv, Navidrome, Python, Subsonic, TUI, 无后门, 逆向工具, 音乐播放器