Compare commits

...

3 Commits
v0.1.1 ... main

Author SHA1 Message Date
Kinneyzhang
4347dc081f release v0.1.4 2026-10-02 09:30:50 +08:00
Kinneyzhang
af78e270d0 release v0.1.3 2026-10-02 07:43:52 +08:00
Kinneyzhang
2bdae6ed7b release v0.1.2 2026-10-02 07:35:44 +08:00
7 changed files with 13 additions and 561 deletions

View File

@ -4,13 +4,13 @@
"summary": "网易云音乐遥控器:一句话对上云里的内容(每日推荐 / 歌单 / 榜单 / 收藏 / FM / 最近 / 播客)并放出来;播放侧管好本机队列现场(快照 / back / 追加)",
"tier": 1,
"status": "stable",
"version": "fb22dd2",
"version": "f7a6763",
"source": {
"path": "skills/netease-music",
"commit": "fb22dd2",
"describe": "fb22dd2",
"commit": "f7a6763",
"describe": "f7a6763",
"dirty": false,
"packed_at": "2026-10-01T23:34:48.413Z"
"packed_at": "2026-10-02T01:30:49.636Z"
},
"platforms": [
"darwin"
@ -316,8 +316,8 @@
"files": [
{
"path": "REFERENCE.md",
"bytes": 32599,
"sha256": "d17e12c8754a504c"
"bytes": 32611,
"sha256": "73aecd57c9653302"
},
{
"path": "SKILL.md",
@ -327,7 +327,7 @@
{
"path": "VERSION",
"bytes": 6,
"sha256": "80447316529fc620"
"sha256": "8a3dad16d348c163"
},
{
"path": "contract.lock.json",
@ -336,8 +336,8 @@
},
{
"path": "interface.json",
"bytes": 9260,
"sha256": "ddd1274a65b4dfd3"
"bytes": 9254,
"sha256": "cb427039f6ad7b04"
},
{
"path": "scripts/install-cli.sh",

View File

@ -2,7 +2,8 @@
网易云音乐遥控器:一句话对上云里的内容(每日推荐 / 歌单 / 榜单 / 收藏 / FM / 最近 / 播客)并放出来;播放侧管好本机队列现场(快照 / back / 追加)
由 Pi 技能 `netease-music` 发布,当前版本 v0.1.1。命令名是 `netease-music`(仓库名带 `-cli`,命令不带)。
由 Pi 技能 `netease-music` 发布(同一个源还有技能包出口),当前版本 v0.1.4。
命令名是 `netease-music`:仓库名带 `-cli`,命令不带。
## 安装

View File

@ -310,7 +310,7 @@ mpv --idle=yes --no-video --force-window=no --audio-display=no --no-terminal \
本技能目录同时是一个可独立使用的 CLI:入口 `scripts/netease-music.sh`,standalone 安装用
`scripts/install-cli.sh`(链接成 `netease-music` 并查 node/mpv/python3),发布用共享的
`skill-publish`(声明在 interface.json 的 `publish`,见 skill-interface 的 REFERENCE §12)。**命名规范**:公开镜像仓叫 `<工具>-cli`(这里是
`skill-release cli|package`(声明在 interface.json 的 `publish`,见 skill-interface 的 REFERENCE §12)。**命名规范**:公开镜像仓叫 `<工具>-cli`(这里是
`netease-music-cli`),装出来的命令仍叫 `<工具>`(`netease-music`)——仓名标交付物类型,
命令名只管好敲。消费者(Emacs 包 etaf-ncm、其他脚本)只依赖本节与 `--json` 输出,
不读实现、不读状态文件。

View File

@ -1,86 +0,0 @@
---
name: netease-music
description: 放网易云音乐并控制播放:直接调接口(搜歌点播、每日推荐、我喜欢的音乐、暂停切歌、音量、歌词、喜欢),不像以前那样模拟鼠标点界面。用户说「放首歌/放每日推荐/换一首/暂停/现在在放什么」时使用。
platforms: [darwin]
---
# 网易云音乐(netease-music)
**一句话**:`pi-skill netease-music daily` 直接开播每日推荐;`play <关键词>` 想听什么说什么;控制都是毫秒级命令,状态是真值。
## 何时用
| 用户说的 | 跑这个 |
|---|---|
| 放每日推荐 / 放点歌 | `pi-skill netease-music daily`(`--limit 20` 少放点,`--shuffle` 随机) |
| 放某首歌 / 某个歌手的歌 | `pi-skill netease-music play "<关键词>"` |
| 放某张歌单 | `pi-skill netease-music play "<歌单链接>"`(或 `play playlist:<id>`;id 用 `myplaylists` 查) |
| 放我喜欢的音乐 | `pi-skill netease-music liked` |
| 放某位歌手的热门 | `pi-skill netease-music artist "陈粒"`(`--limit 20` 少放点;也可给歌手 id) |
| 放某张专辑 | `pi-skill netease-music album "自传"`(名字、id、专辑链接都行) |
| 查某歌手有哪些专辑 | `pi-skill netease-music albums "周杰伦"`(只列不播;搜不到正版专辑时用它拿 id) |
| 放别人做的歌单 | `pi-skill netease-music playlist "夜跑"`(名字直接写;`playlists "夜跑"` 只搜不播) |
| 放排行榜 | `pi-skill netease-music chart 飙升榜`(`charts` 列全部 63 个) |
| 放我收藏的歌单 / 专辑 / 歌手 | `pi-skill netease-music myplaylists collected` / `myalbums` / `myartists` |
| 放点没听过的 | `pi-skill netease-music fm`(私人 FM) |
| 刚才那首再放一遍 | `pi-skill netease-music replay`(`recent` 看最近播放) |
| 放播客(订阅的) | `pi-skill netease-music podcasts` → `podcast 黑水公园 --limit 3` |
| 换队列但别丢现场 | 每次换队列会自动留「上一条」:`back` 切回(含位置);`queue-save 名字` / `queue-load 名字` 自己存取的;`queues` 看有哪些 |
| 往队列里加一首 | `pi-skill netease-music add "幻听"`(不动正在放的那首) |
| 把搜索结果整列播放 / 从某首开始 | `pi-skill netease-music play-ids 1000,1001,1002 --start 2`(单个逗号分隔的 ID 参数;按输入顺序,序号从 1 开始) |
| 看队列 / 跳到第几首 | `pi-skill netease-music queue` / `jump 5` |
| 删除队列第几首 / 清空 | `pi-skill netease-music remove 3` / `clear`(当前项删除后选下一首,末尾选上一首,删空停止;保留暂停状态) |
| 单曲循环 / 整列循环 / 关闭循环 | `pi-skill netease-music repeat single` / `repeat list` / `repeat off`(旧 `on` = `list`;状态字段 `repeat_mode`) |
| 换一首 / 回上一首 | `pi-skill netease-music next` / `prev` |
| 暂停 / 继续 | `pi-skill netease-music pause` / `resume`(或 `toggle`) |
| 声音大点 / 小点 | `pi-skill netease-music volume 45`(不带数字=读当前值) |
| 现在在放什么 | `pi-skill netease-music status` |
| 这首歌歌词 | `pi-skill netease-music lyric`(`--all` 全文) |
| 查歌曲封面 / 下载封面 | `pi-skill netease-music cover "<歌曲关键词或id>"`(始终输出 JSON;加 `--download PATH` 下载,覆盖需 `--force`,最多 10 MB = 10,000,000 字节;不改变队列或登录态) |
| 这首好听,收藏 | `pi-skill netease-music like`(`unlike` 取消) |
| 私人 FM 这首不喜欢 | `pi-skill netease-music dislike`(也可 `dislike 1000` 指定歌曲 ID;提交 `fm/trash`,不自动切歌) |
| 查询哪些歌曲已喜欢 | `pi-skill netease-music liked-ids --json`(`{count,total,ids:[数字,...]}` 全表;只读) |
| 只是查有哪些歌 | `pi-skill netease-music search "<关键词>"`(只列不播) |
| 取歌曲 JSON 给播放器 | `node scripts/netease-api.mjs song "<关键词或id>"`(歌曲字段依次为 `id/name/artist/album/ms/vip/cover`;没有封面时为 `""`;接口提供布尔 `liked` 时追加它) |
| 列下一页 | `pi-skill netease-music myplaylists --limit 3 --offset 3`(跳过前 3 条;`--json` 带 `total`,人读样式不变) |
**分页**:`playlist` / `liked` / `album` / `artist` / `chart` / `search` / `playlists` / `charts` / `myalbums` / `myartists` / `recent` / `podcasts` / `podcast`,以及 `myplaylists` / `albums` / `daily`,都支持 `--limit N --offset N`。`offset` 默认 0,从 0 起跳过 N 条;后端 JSON 的 `count` 是本页条数,`total` 是接口总数(没给总数时用接口返回、尚未本地截取的条数)。翻过末尾返回空列表,具体口径见 `REFERENCE.md`。
**封面**:`cover` 查询返回 `{id,name,artist,cover}`,下载返回 `{id,cover,file,bytes}`(`file` 为绝对路径,`bytes` 为实际字节数)。歌曲或封面缺失时报 `notfound` 错误 JSON;下载路径的父目录须已存在。字段来源与大小检查见 `REFERENCE.md` §2.2。
**批量与队列**:`play-ids <id,id,...> [--start N]` 接收一个无空格、逗号分隔的正整数 ID 参数,保留顺序和重复项;默认从输入列表第 1 首开始。`--start` 指输入列表的 1 基序号;指定歌曲无播放地址或任一歌曲详情缺失时报错并保留旧队列,其他无地址项跳过并计入 `skipped`。它关闭随机,不支持 `--shuffle`。`remove N` 按 `queue` 显示的顺序删除(含随机队列);`clear` 停止、清掉本地队列和地址保活,保留配置及快照。
**喜欢状态**:列表、搜索和详情的歌曲对象仅在接口提供布尔值时追加 `liked: true|false`;`liked` 歌曲列表全部为 `true`。缺字段表示未知,可用只读 `liked-ids --json`(或 `node scripts/netease-api.mjs liked-ids`)取得完整 ID 数组后按 ID 补齐;不自动为每首歌增加请求。`dislike [id]` 是私人 FM 的不喜欢,与 `unlike` 分开;只提交 API,播放位置和队列保持原样。
**循环状态**:`repeat off|list|single` 同时设置 mpv 的两个循环属性;`on` 和无参数继续表示 `list`。`status` 的 `repeat_mode` 报 `off/list/single`,原 `repeat` 布尔字段仍表示是否整列循环。
**首次使用**:`pi-skill netease-music check`(自检依赖与登录态)。没登录就登一次,两条路都行:
`login --sms <手机号>` 收验证码,再 `login --sms <手机号> --sms-code <验证码>`(主力,实测最稳);
或 `login` 出二维码用手机网易云 App 扫(30 秒)。cookie 只存本机。
**与音箱配合**:电脑的声音去哪台设备由系统输出决定,要让家里的 EDIFIER 音箱出声就先 `pi-skill edifier-speaker bt`(它会切音箱档位 + 把系统输出接过去)。
## 失败 → 动作
| 现象 | 动作 |
|---|---|
| `error=auth`(未登录) | `login --sms <手机号>` 两步短信登录;或 `login` 扫码;或 `setup --cookie 'MUSIC_U=…'` 粘浏览器里的 cookie |
| `error=notfound`(搜不到/歌单不存在) | 换个关键词;歌单 id 用 `myplaylists` 查 |
| `error=notfound ... 没有一首能播` | 这首/这张全要 VIP 或没版权:换一个(周杰伦正版目录基本都这样) |
| 歌手/专辑搜到的是翻唱 | 用 `albums <歌手>` 拿正版专辑 id,再 `album <id>`(网易搜索索引会缺正版) |
| `error=deps missing=mpv` | `brew install mpv`(播放器);`qrencode` 只在扫码登录时要 |
| `error=external ... url=null` | 那首要 VIP 或没版权,队列会自己跳过并在 `skipped=` 里报数量 |
| `error=timeout`(扫码超时) | 重新 `login`(二维码 2 分钟过期) |
| 队列有歌但没声音 | 系统输出没指向音箱:`pi-skill edifier-speaker bt`;或 `volume` 太小 |
| 播放卡在旧队列 | `stop` 清空队列再来 |
| `error=notfound 没有上一条可切回` | 还没换过队列;`queues` 看有哪些快照 |
| 换队列后想找回刚才那条 | `back`(回到上一条,含放到哪儿);更早的看 `queues`,或 `queue-load <名字>` |
| `add` 之后没马上听到 | 队列开着随机,新歌在队尾按随机顺序插进来;想顺序放先 `shuffle off` |
| 播客某一期放不出来 | 那期要付费或已下架;换几期:`podcast <播客> --limit 5` |
## 边界
- 只做「播放与控制」,不碰客户端界面;官方客户端开着不受影响,两边互不干扰(各放各的)。
- 音质默认无损(`quality`,改成 `exhigh` 可省流量)。
- 歌单每页默认最多取 500 首,每日推荐 40 首(`--limit` 可调);用 `--offset` 接着取下一页,500 不是歌单总量上限。歌手热门默认 50 首、专辑默认 200 首;后端用 `total` 报分页前数量。
- 能不能播由版权/VIP 决定:拉不到地址的会被跳过(`skipped=` 报数量),整张都拿不到就 `error=notfound`。

View File

@ -1 +1 @@
0.1.1
0.1.4

View File

@ -1,174 +0,0 @@
{
"version": 1,
"skill": "netease-music",
"tier": 1,
"platforms": [
"darwin"
],
"commands": {
"add": {
"destructive": false
},
"album": {
"destructive": false
},
"albums": {
"destructive": false
},
"artist": {
"destructive": false
},
"back": {
"destructive": false
},
"chart": {
"destructive": false
},
"charts": {
"destructive": false
},
"check": {
"destructive": false
},
"clear": {
"destructive": false
},
"cover": {
"destructive": false
},
"daily": {
"destructive": false
},
"dislike": {
"destructive": false
},
"episode": {
"destructive": false
},
"fm": {
"destructive": false
},
"jump": {
"destructive": false
},
"like": {
"destructive": false
},
"liked": {
"destructive": false
},
"liked-ids": {
"destructive": false
},
"login": {
"destructive": false
},
"lyric": {
"destructive": false
},
"myalbums": {
"destructive": false
},
"myartists": {
"destructive": false
},
"myplaylists": {
"destructive": false
},
"next": {
"destructive": false
},
"pause": {
"destructive": false
},
"play": {
"destructive": false
},
"play-ids": {
"destructive": false
},
"playlist": {
"destructive": false
},
"playlists": {
"destructive": false
},
"podcast": {
"destructive": false
},
"podcasts": {
"destructive": false
},
"prev": {
"destructive": false
},
"queue": {
"destructive": false
},
"queue-load": {
"destructive": false
},
"queue-save": {
"destructive": false
},
"queues": {
"destructive": false
},
"recent": {
"destructive": false
},
"remove": {
"destructive": false
},
"repeat": {
"destructive": false
},
"replay": {
"destructive": false
},
"resume": {
"destructive": false
},
"search": {
"destructive": false
},
"seek": {
"destructive": false
},
"setup": {
"destructive": false
},
"shuffle": {
"destructive": false
},
"status": {
"destructive": false
},
"stop": {
"destructive": false
},
"toggle": {
"destructive": false
},
"unlike": {
"destructive": false
},
"volume": {
"destructive": false
}
},
"errors": [
"auth",
"deps",
"external",
"internal",
"notfound",
"timeout",
"usage"
],
"aliases": [
"netease",
"netease-music"
],
"adminAliases": []
}

View File

@ -1,289 +0,0 @@
{
"summary": "网易云音乐遥控器:一句话对上云里的内容(每日推荐 / 歌单 / 榜单 / 收藏 / FM / 最近 / 播客)并放出来;播放侧管好本机队列现场(快照 / back / 追加)",
"useWhen": "用户要放网易云的音乐(每日推荐、指定歌曲/歌手、我喜欢的音乐)、问现在在放什么,或要暂停/切歌/调音量/看歌词/取歌曲封面时",
"tier": 1,
"entry": {
"path": "scripts/netease-music.sh"
},
"aliases": [
"netease",
"netease-music"
],
"commands": [
{
"name": "daily",
"summary": "每日推荐分页入队播放(--limit N / --offset N / --shuffle;JSON 带 total)"
},
{
"name": "play",
"summary": "播一首或一整张:关键词→最匹配一首;歌单 id/链接→整张(--shuffle)"
},
{
"name": "play-ids",
"summary": "play-ids <id,id,...> [--start N]:按输入顺序整列替换,1 基起点默认 1;保留重复项,选中歌不可播则保留旧队列;关闭随机"
},
{
"name": "search",
"summary": "只搜不播:列出 id/歌名/歌手/时长/是否要 VIP(--limit N / --offset N;JSON 带 total)"
},
{
"name": "cover",
"summary": "cover <歌曲关键词|id> [--download PATH] [--force]:只读查询封面,JSON 为 id/name/artist/cover;下载为 id/cover/file/bytes,最多 10 MB(10000000 字节),覆盖需 --force;所有原语歌曲对象含字符串 cover(缺失为空)"
},
{
"name": "artist",
"summary": "歌手热门分页入队播放(名字或 id;--limit N / --offset N;JSON 带 total)"
},
{
"name": "album",
"summary": "专辑按顺序分页入队播放(名字、id 或专辑链接;--limit N / --offset N;JSON 带 total)"
},
{
"name": "albums",
"summary": "只列不播:歌手的专辑(含老专辑;--limit N / --offset N;JSON 带 total)"
},
{
"name": "liked",
"summary": "分页播放「我喜欢的音乐」(--limit N / --offset N;JSON 带 total)"
},
{
"name": "liked-ids",
"summary": "只读喜欢 ID 全表;--json 为 {count,total,ids:[数字,...]};歌曲 JSON 透传可用的布尔 liked,缺字段时用全表按 id 判定"
},
{
"name": "myplaylists",
"summary": "我的歌单(自建 + 收藏);--limit N / --offset N 分页,collected/own 筛本页;JSON 带 total"
},
{
"name": "pause",
"summary": "暂停"
},
{
"name": "resume",
"summary": "继续播放"
},
{
"name": "toggle",
"summary": "暂停/继续切换"
},
{
"name": "next",
"summary": "下一首"
},
{
"name": "prev",
"summary": "上一首"
},
{
"name": "stop",
"summary": "停止并清空队列"
},
{
"name": "clear",
"summary": "停止并清空 mpv 和本地队列,收掉保活;配置和快照保留,可重复调用"
},
{
"name": "volume",
"summary": "读或设音量(0-130)"
},
{
"name": "seek",
"summary": "跳进度(相对秒数,如 +30)"
},
{
"name": "queue",
"summary": "看队列(mpv 真值):每首歌名、时长,标出正在放的那首"
},
{
"name": "jump",
"summary": "跳到队列第 N 首(从 1 开始),并回读状态确认"
},
{
"name": "remove",
"summary": "remove <index>:按 queue 显示的 1 基序号删除;当前项选下一首,末尾选上一首,删空停止;同步本地文件"
},
{
"name": "status",
"summary": "当前曲目/进度/音量/队列位置(mpv 真值);repeat_mode 为 off/list/single"
},
{
"name": "lyric",
"summary": "当前歌曲歌词(默认只显示当前句与下一句,--all 全文)"
},
{
"name": "like",
"summary": "把当前这首歌加入「我喜欢的音乐」"
},
{
"name": "unlike",
"summary": "把当前这首歌移出「我喜欢的音乐」"
},
{
"name": "shuffle",
"summary": "队列随机开/关"
},
{
"name": "repeat",
"summary": "repeat off|list|single:关闭/整列/单曲循环;on 和无参数 = list;状态追加 repeat_mode,保留旧 repeat 布尔含义"
},
{
"name": "login",
"summary": "登录(--sms 手机号+验证码两步为主力;不带参数出二维码扫码),cookie 存本机 config"
},
{
"name": "setup",
"summary": "写配置(--cookie/--quality);不带参数打印登录办法"
},
{
"name": "check",
"summary": "只读自检:依赖、登录态、mpv 可用性、默认音质"
},
{
"name": "playlist",
"summary": "放一张歌单:名字/id/链接都行(--limit N / --offset N;JSON 带 total)"
},
{
"name": "playlists",
"summary": "搜歌单(只列不播):id / 名字 / 曲目数 / 作者 / 是否已收藏;--limit N / --offset N,JSON 带 total"
},
{
"name": "charts",
"summary": "列排行榜(只列不播):飙升榜 / 新歌榜 / …;--limit N / --offset N,JSON 带 total"
},
{
"name": "chart",
"summary": "放某个榜单:名字或 id(--limit N / --offset N;JSON 带 total)"
},
{
"name": "myalbums",
"summary": "我收藏的专辑(只列不播;--limit N / --offset N;JSON 带 total;id 可喂 album)"
},
{
"name": "myartists",
"summary": "我收藏的歌手(只列不播;--limit N / --offset N;JSON 带 total;id 可喂 artist)"
},
{
"name": "fm",
"summary": "私人 FM:没听过的歌,整列入队开播(--limit N)"
},
{
"name": "dislike",
"summary": "dislike [歌曲id]:私人 FM 不喜欢(fm/trash);省略 id 用当前歌曲,只上报,不自动切歌或改队列"
},
{
"name": "recent",
"summary": "最近播放(只列不播,一周内,含播放次数;--limit N / --offset N;JSON 带 total)"
},
{
"name": "replay",
"summary": "再放一次最近第 N 首(默认 1 = 最近那首)"
},
{
"name": "podcasts",
"summary": "我的播客订阅(只列不播;--recommend 看推荐的;--limit N / --offset N;JSON 带 total)"
},
{
"name": "podcast",
"summary": "放某播客的节目(--limit N 默认 3;--offset N;名字或 id;JSON 带 total)"
},
{
"name": "episode",
"summary": "放某一期节目(节目 id 从 podcast 输出里抄)"
},
{
"name": "queues",
"summary": "看本地队列快照(每次换队列会自动留一条「上一条」)"
},
{
"name": "queue-save",
"summary": "把当前队列存成命名快照(只存歌曲列表,地址回放时重取)"
},
{
"name": "queue-load",
"summary": "载入某个命名快照(含当时位置与随机状态)"
},
{
"name": "back",
"summary": "切回上一条队列(含位置);再 back 一次切回来"
},
{
"name": "add",
"summary": "把一首加到当前队列尾部,不动正在放的那首"
}
],
"errors": [
"usage",
"deps",
"auth",
"notfound",
"timeout",
"external",
"internal"
],
"requires": [
{
"kind": "runtime",
"name": "node",
"check": "which node",
"note": "接口客户端与 mpv 控制都是 node 脚本,零 npm 依赖"
},
{
"kind": "runtime",
"name": "python3",
"check": "which python3"
},
{
"kind": "binary",
"name": "mpv",
"install": "brew install mpv",
"check": "which mpv",
"note": "真正的播放器(常驻 + unix socket 控制)"
},
{
"kind": "binary",
"name": "qrencode",
"install": "brew install qrencode",
"check": "which qrencode",
"note": "只在扫码登录时用"
}
],
"config": [
{
"key": "cookie",
"desc": "登录 cookie(MUSIC_U=…),由 login 命令扫码写入",
"type": "string",
"secret": true,
"required": true,
"ask": "扫码登录一次:pi-skill netease-music login",
"note": "只存本机 local/skills/netease-music/config.json,不进共享仓库"
},
{
"key": "quality",
"desc": "音质级别:standard/higher/exhigh/lossless/hires",
"type": "enum",
"values": [
"standard",
"higher",
"exhigh",
"lossless",
"hires"
],
"default": "lossless"
}
],
"helpListsCommands": true,
"probeUnknownCommand": true,
"admin": [
{
"path": "scripts/install-cli.sh",
"runner": "sh",
"summary": "standalone 安装:不需要 Pi,把入口链接成 netease-music 并检查 node/mpv/python3"
}
],
"publish": {
"repo": "https://gitea.vhkd.top/geekinney/netease-music-cli.git",
"mirrors": [
"git@gitea.gklazycat.heiyu.space:geekinney/netease-music-cli.git"
]
}
}