本地开发
环境
- Node.js 与
pnpm 11.3.0 - Python 3.11+ 与 uv(QQ provider 开发)
- Rust toolchain(本地检查)
- Docker(构建 SteamOS 兼容的 Rust/QQ provider 发布产物)
- 可通过 SSH 访问的 Steam Deck(真机部署与验收)
安装前端依赖
git clone https://github.com/jinzhongjia/decky-music.git
cd decky-music
pnpm install
pnpm build
常用命令
| 命令 | 用途 |
|---|---|
pnpm watch |
监听并构建前端 |
pnpm build |
构建前端到 dist/ |
pnpm test:ui |
运行前端 Node 测试 |
pnpm lint |
TypeScript 类型检查与 Prettier 校验 |
python3 -m unittest discover -s tests |
运行 bridge/protocol Python 测试 |
cargo test --workspace |
运行 Rust workspace 测试 |
cargo fmt --all && cargo clippy --workspace |
Rust 格式与静态检查 |
构建 SteamOS 二进制
发布和真机部署使用 Docker 内的兼容工具链,避免本机 glibc 版本高于 SteamOS:
bash scripts/build-rust.sh -p player -p provider
产物位于 target/release/player 与 target/release/provider。
构建镜像固定到 digest,构建脚本自动运行 scripts/check-binaries.py:检查 x86-64 ELF、
glibc 需求不超过 2.39,以及两个可执行文件的动态依赖白名单。
此检查只能证明二进制 ABI 边界,不能代替音频、手柄、屏幕缩放和睡眠恢复的真机验收。
自动检查与固定构建工具
PR 和分支 push 会运行 Checks:stdlib bridge 单测、 pnpm 9/11 两套 UI 测试/类型检查/构建,以及 Rust fmt/test/clippy(含 provider 的 QQ / 网易云后端)。 运行时和第三方 Actions 使用固定版本或提交;工作流失败不会被忽略,不自动修改分支保护规则。
插件打包统一使用 bash scripts/decky-build.sh。它校验固定版本 Decky CLI 的 SHA-256,
从固定 digest 准备官方 builder,并使用 Git 可见的当前工作区源码建立临时输入目录:
未提交的新源码仍会纳入,target/ 与被忽略的密钥文件不会被复制进构建输入。
默认使用 Docker + sudo;已有 rootless Podman 时可用
DECKY_BUILD_SUDO=0 DECKY_BUILD_ENGINE=podman bash scripts/decky-build.sh --build-as-root。
部署到开发机
DECK_HOST=<user>@<steamos-ip> bash scripts/deploy.sh
scripts/deploy.sh 会构建前端、打包插件、复制已有二进制并重启 plugin_loader。它不会重新构建
player/provider;修改 player/、provider/ 或 wire/ 后,必须先运行上面的构建命令。
DECK_HOST 必填,SSH 登录用户不必名为 deck。脚本在构建前从远端 plugin_loader 服务配置
发现实际插件目录;无法可靠发现时停止,不猜测 /home/deck。可用
DECK_PLUGIN_PATH=/absolute/path/to/plugins 显式指定,目录仍须通过安全检查。
写权限按服务的 UNPRIVILEGED_USER 配置(或可唯一对应的用户 home)设置,不使用 SSH 登录用户。
目录覆盖不能替代账号解析;使用间接环境文件或无法确定账号时,需要先明确服务的非特权用户配置。
侧载前须备齐两个预构建二进制;脚本会先检查 ABI,再开始打包。
远端 sudo 如需口令,通过环境变量 DECK_PASS 提供,勿写入脚本或仓库。
兼容性验收须分别记录:实际用户名/UID 与安装目录、SteamOS/Steam/Decky 版本、内置与外接音频、 睡眠唤醒、屏幕比例/缩放和控制器操作。现有 Steam Deck 回归不等于非 Deck 设备已验收; 后者目前因缺少设备而待验证。
目录
| 路径 | 职责 |
|---|---|
src/ |
React UI、播放器页面、provider 页面和唯一前端 API 层 |
main.py |
Decky Plugin facade:CALLABLES 白名单 + __getattr__ 转发给 bridge |
py_modules/ |
bridge、播放队列、协议和日志实现,只使用 Python 标准库 |
player/ |
Rust 音频 player,负责流式拉取、解码、播放、控制与 MPRIS |
wire/ |
bridge ↔ 子进程协议 v1 的 Rust 实现,player 与 provider 共用 |
provider/ |
统一 Rust 音源进程:QQMusicApi-rs 的 QQ 音乐后端 + ncm-api-rs 的网易云后端 |
tests/ |
bridge、协议、设置和前端行为测试 |
docs/ |
架构、路线图、队列语义、provider 能力和 UI 规格 |
scripts/ |
SteamOS 兼容构建、真机部署,以及 full / CN 发布打包 |
website/ |
项目官网(Steam 商店页风格,Cloudflare Workers 部署),见 website/README.md |
贡献
提交改动前请先阅读 AGENTS.md 和相关设计文档。关键约束:
- callable/emit 契约变化必须同步修改 bridge 与
src/api.ts。 - bridge ↔ 子进程协议变化必须同步修改四端 protocol 模块和配套测试。
- 所有用户文案同时维护中文和英文;所有可交互 UI 必须支持手柄焦点导航。
- 日志不得包含播放 URL、Cookie、凭证或其他敏感信息。
- 影响 UI 视觉、文案、布局或焦点的改动必须更新对应 provider 的真机截图。
- Commit 使用 Conventional Commits,主题优先使用中文。