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 · 五款支持实时预览的主题 —— 一切皆动。 NaviTui now playing [![license](https://img.shields.io/badge/license-GPL--3.0--or--later-blue)](LICENSE) [![python](https://img.shields.io/badge/python-3.11%2B-blue)](https://www.python.org/) [![built with textual](https://img.shields.io/badge/built%20with-Textual-5a3fd6)](https://github.com/Textualize/textual) [![design system](https://img.shields.io/badge/design-ricekit-e8a33d)](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 上的媒体*按键*传递功能是新增的,仍需进行针对性测试。) ## 截图
**搜索 —— 一切内容,即时分组呈现** global search **`void` 主题 —— 纯黑 OLED,封面熠熠生辉** void theme **同步滚动的歌词** synced karaoke lyrics
## 功能 ### 歌曲优先的媒体库 - 一个侧边栏:**所有曲目**、**最近添加**、**最近播放**、**最常 播放**、**已加星标**、**随机播放所有** —— 你的**播放列表**就在 其下,还有**流派** (`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, 无后门, 逆向工具, 音乐播放器