Skip to content

Releases: yeqiqiye/music-plugin

v3.0.2

Choose a tag to compare

@yeqiqiye yeqiqiye released this 03 Nov 16:00

QQ 音乐修好了

绿钻曲之前全失败,根因是 FC 里那把 QQ_COOKIE 已过期 —— 用过期 Cookie 打 QQ 官方 vkey 接口返回 result: 104003,而且带不带 Cookie 结果完全一样,说明服务端压根没认到登录态。免费曲不需要登录,所以「QQ 还能搜、点周杰伦这类全失败」。

换新 Cookie 后(18 首绿钻曲取样,含周杰伦 / 薛之谦 / 林俊杰 / 陈奕迅 / 邓紫棋 / 李荣浩 / 毛不易):

场景 之前 现在
绿钻曲 0/18 能放 18/18 能放
免费曲 5/5 能放 5/5 能放
《晴天》vkey 104003 result: 0

QQ 音质跟上了

vkey 靠 filename 前缀区分档位,现在跟面板的「音质」开关走(和网易云共用一个设置):

面板档位 请求 实测体积
标准 C400 m4a 3.3 MB / 4:29
较高 · 极高 M800 mp3 320k 10.8 MB / 4:29
无损 · Hi-Res · 母带 F000 flac 55.4 MB / 4:29

取不到就「无损 → 320k → 128k」逐级降档;非标准档会先 HEAD 探活,文件实际 404 也退下一档,所以免费曲和低等级账号都不会因此放不出来。

其它

  • 设置里的「网易云音质」改名 音质(网易云与 QQ 共用)
  • 中转 /health 的 features 增加 qq-level
  • 新增 dev/qq-level.mjs 离线单测(假上游 + 每例换 songmid 规避 5 分钟缓存),已进 npm test

安装

https://fastly.jsdelivr.net/gh/yeqiqiye/music-plugin@v3.0.2/iirose/music-iirose.js
https://fastly.jsdelivr.net/gh/yeqiqiye/music-plugin@v3.0.2/iirose/music-iirose-hd.js
https://fastly.jsdelivr.net/gh/yeqiqiye/music-plugin@v3.0.2/iirose/sticker.js

升级时把旧那行删掉再粘新的,两行同时留着会跑两个实例。

v3.0.1 · 修自定义背景下的布局空洞

Choose a tag to compare

@yeqiqiye yeqiqiye released this 22 Oct 16:14

v3.0.1 · 修「设了自定义背景时中间空一块」

上一个版本你说「搜索框位置不对 / 搜索出来后中间空了一块」,查到了,是个 CSS 级联的锅。

根因

为了在自定义背景上把内容压到壁纸之上,有一条规定:

.mk-root[data-bg="1"] .mk-card>*{position:relative;z-index:1}

它给所有直接子元素设了 position:relative。但「正在播放封面」那层装饰是 position:absolute 的
(.mk-hero) —— 优先级 3 > 1,被盖成了 relative,于是它带着 220px 的高度回到文档流:

状态 搜索框 → 平台 tab 列表高度
没设背景 0px 301px
设了背景(v3.0.0,坏) 220px 81px
设了背景(v3.0.1,修好) 0px 301px

所以「搜索框位置不对」和「中间空了一块」是同一个 bug:平台 tab / 范围 tab / 列表被整体往下推了 220px。

修法

给封面层补一条更高优先级的规则(4 > 3):

.mk-root[data-bg="1"] .mk-card>.mk-hero{position:absolute;z-index:0;inset:-12% -6% auto -6%}

为什么以前测不出来

jsdom 不算布局,单元测试里所有元素的高度都是 0 —— 这类「装饰层被 CSS 拖回文档流」的问题只能量真实坐标。
所以新增 dev/layout-check.mjs:用本机 Chrome/Edge 的 headless 打开 docs/demo,
量「头 → 搜索 → 平台 tab → 范围 tab → 列表」四段空隙和列表高度,无背景 / 有背景各跑一遍,
任一段 > 60px 或列表 < 200px 就失败:

node dev/layout-check.mjs      # 已接进 npm test;没有浏览器的 CI 会自动跳过

修之前跑出来就是 搜索 → 平台 tab 220px,修完两个状态都是 0px —— 这条测试是真能抓到它的。

这次的地址

点歌 · 标准版(B 站固定 720P)
https://fastly.jsdelivr.net/gh/yeqiqiye/music-plugin@v3.0.1/iirose/music-iirose.js

点歌 · HD 版(B 站可选 1080P)
https://fastly.jsdelivr.net/gh/yeqiqiye/music-plugin@v3.0.1/iirose/music-iirose-hd.js

表情包(独立,15KB)
https://fastly.jsdelivr.net/gh/yeqiqiye/music-plugin@v3.0.1/iirose/sticker.js

换版本号之前请先删掉旧脚本行再加新行,别两行同时留着 —— 两个实例会打架(双悬浮球、两次切歌)。

v3.0.0 · 歌单导出/导入 + 文档截图

Choose a tag to compare

@yeqiqiye yeqiqiye released this 22 Oct 16:00

v3.0.0 · 歌单能搬家了 + 文档补齐

这版没有新玩法,收三件拖了很久的事:歌单能带走、不用装脚本也能看面板、文档终于有图了。


1. 歌单导出 / 导入

「歌单」页多了两个按钮:

按钮 干什么
导出 存一份 music-playlists-YYYY-MM-DD.json,含全部歌单和每首的点歌字段(平台 / id / 歌名 / 歌手 / 时长 / 封面);浏览器不让下载时自动把 JSON 复制到剪贴板
导入 选一份导出的 JSON,合并进本机;同名且曲目完全一样的自动跳过,所以重复导入不会变两份

以前歌单只活在 localStorage.mk_playlists 里 —— 清缓存、换浏览器、换电脑全没了。现在能搬。

2. 不装脚本也能看面板:docs/demo/index.html

一个能直接双击打开的预览页:加载的是构建产物本身(iirose/music-iirose.js),
只把「房间 socket」「中转接口」换成了假数据 —— 搜出来的是占位歌,封面是按色相现生成的渐变图,
点播不会发到任何房间。打开就自动演一遍:开面板 → 搜「周杰伦」→ 点第一首。

对看代码的人是活文档,对写文档的人是截图机:node dev/shots.mjs 用它出图。

3. 文档截图(dev/shots.mjs)

不想为截图往仓库里塞 playwright(几百 MB),直接调本机 Chrome / Edge 的 headless:

node dev/shots.mjs          # 重拍 docs/img/panel.png、playlists.png、lyric.png
node dev/shots.mjs lyric    # 只重拍一张
  • 自带静态服务(默认 3099),SHOT_PORT 可改;找不到浏览器时用 CHROME_PATH 指定;
  • 2x 缩放 + --virtual-time-budget,等异步(搜索、点播、歌词滚动)跑完再拍;
  • 预览页支持 ?lyric=0(不要歌词窗)、?view=lyric(关面板只留歌词)、?view=playlists(我的歌单)。

顺手把文档里陈旧的安装地址(还写着 @v0.2.0)换成了当前版本 —— 之前照着文档装的人会拿到几年前的脚本。


地址(三条)

点歌 · 标准版(B 站固定 720P)
https://fastly.jsdelivr.net/gh/yeqiqiye/music-plugin@v3.0.0/iirose/music-iirose.js

点歌 · HD 版(B 站可选 1080P)
https://fastly.jsdelivr.net/gh/yeqiqiye/music-plugin@v3.0.0/iirose/music-iirose-hd.js

表情包(独立,15KB)
https://fastly.jsdelivr.net/gh/yeqiqiye/music-plugin@v3.0.0/iirose/sticker.js

测试

dev/iirose-smoke.mjs 新增一组 13b:导入一份歌单(added=1 / songs=2)→ 再导一次算重复
(added=0 / skipped=1)→ 坏 JSON 被拒({ok:false, why:'json'})→ 导出的 JSON 可解析且只留点歌字段
→ 导出再导入不重复 → 歌单页有「导出」「导入」两个按钮和隐藏的文件选择框。
npm test 七套全绿。

已知没做

表情包那套(iirose/sticker.js)还没写单独的使用说明 —— 等想清楚怎么讲再做。

v2.7.0 · 播放器样式升级

Choose a tag to compare

@yeqiqiye yeqiqiye released this 22 Oct 15:49

v2.7.0 · 播放器样式向主流音乐 App 看齐

这版做了什么

1. 正在播放改成信息条(封面 + 歌名 / 歌手 · 平台 · 剩余)

以前只有一行文字,现在左边是 38px 圆角封面,右边两行字:
歌名一行、歌手 · 平台 · 剩 3:21 一行。房间那首也能认出是哪家音源了
(网易云 / QQ / B站),以前只能靠歌名猜。

2. 封面做顶部模糊背景

正在放的那首歌的封面,会在面板顶上做一层 34px 模糊 + 轻微去饱和的背景,
往下渐隐 —— 就是 Apple Music / Spotify「正在播放」页的做法:
换歌的时候整块面板的氛围跟着封面换。

  • 设置 → 外观 → 「封面当背景」 可以关(默认开);
  • 设了自定义背景图片时它自动让位,不跟你的背景打架;
  • 没有封面(别人点的、或者播放器没给)就什么都不显示,不会糊一片脏色。

从音乐 App 搬了哪些、没搬哪些

别人的做法 我们 为什么
正在播放 = 封面 + 标题 + 歌手 ✅ 搬了 一眼知道在放什么,比纯文字强太多
封面取主色染整个 UI ✅ 搬到「模糊背景」 取像素要过 CORS,封面域名不带 CORS 头;而模糊背景只用 <img> + CSS,零成本
上一首 / 播放 / 下一首 三大键 早就有了 —
进度条 + 拖动跳转 ❌ 没搬 播放器不在我们手里(IIROSE 客户端自己的 #radioPlayer),拖了也不会跳,只会骗人
音量滑杆 ❌ 没搬 同上,音量归客户端
B 站 #audio= 直连播放 ❌ 没搬 中转在阿里云出口解析出来的 B 站地址是按 IP 签名的,交给你的浏览器就是 403;所以 1080P 还是中转 FFmpeg 合流

地址(三条)

点歌 · 标准版(B 站固定 720P)
https://fastly.jsdelivr.net/gh/yeqiqiye/music-plugin@v2.7.0/iirose/music-iirose.js

点歌 · HD 版(B 站可选 1080P)
https://fastly.jsdelivr.net/gh/yeqiqiye/music-plugin@v2.7.0/iirose/music-iirose-hd.js

表情包(独立,15KB)
https://fastly.jsdelivr.net/gh/yeqiqiye/music-plugin@v2.7.0/iirose/sticker.js

测试

dev/iirose-smoke.mjs 加了两条:点播后 .mk-now-art img 的 src 是那首歌的封面、
.mk-hero 拿到 .mk-on 且 background-image 是同一张图。
npm test 七套全绿。

v2.6.0 · 表情包独立脚本

Choose a tag to compare

@yeqiqiye yeqiqiye released this 22 Oct 11:31

v2.6.0 · 表情包拆成独立脚本

变了什么

表情从点歌台里搬出来,变成一个 15KB 的独立脚本 iirose/sticker.js:

  • 开关:Alt+E,或右下角 😀(可拖动);面板是独立的小窗口,不占点歌台;
  • 点歌台的「表情」按钮撤掉了 —— 两个脚本互不依赖,可以只加载其中一个;
  • 只共享两样东西:中转地址(mk_iirose_relay)和收藏(mk_stickers),所以收藏是两边通的。

为什么值得拆

理由 说明
包体 点歌台不用为表情变胖(现在 218KB vs 独立 15KB)
风险隔离 表情比点歌更容易踩房间规则,出问题可以只摘这一个
分发 想给谁表情、不想给谁点歌,可以只发那一个链接

微信表情包:做不了「官方直连」

微信没有公开的表情搜索接口 —— 客户端走的是 storeapi.weixin.qq.com 那套带签名的私有接口,
第三方全是抓包 + 模拟签名,随时会被封;做成插件只会「今天能用明天全挂」。

所以仍然是搜狗 / Bing / 斗图啦 / 发表情这几个公开源。想要「微信味」有两条路:

  1. 搜索里直接搜「微信 表情包」(能搜到大量搬运图,现在就支持);
  2. 在微信里把表情存成图片,手发到房间(我们这套不接管上传)。

如果你有具体的镜像站,把地址给我,我加一个适配器 —— 源是可插拔的。

地址(三条)

点歌 · 标准版(B 站固定 720P)
https://fastly.jsdelivr.net/gh/yeqiqiye/music-plugin@v2.6.0/iirose/music-iirose.js

点歌 · HD 版(B 站可选 1080P)
https://fastly.jsdelivr.net/gh/yeqiqiye/music-plugin@v2.6.0/iirose/music-iirose-hd.js

表情包(独立,15KB)
https://fastly.jsdelivr.net/gh/yeqiqiye/music-plugin@v2.6.0/iirose/sticker.js

测试

新增 dev/sticker-smoke.mjs:载入 → 😀 球 → Alt+E 打开 → 搜索(假中转)→ 点图发出
{"m":"…/sticker/img?u=cat.gif&s=deadbeef#e",…} → 2 秒内连点被限速 → 收藏落盘并渲染 → ✕ 关闭。
npm test 现在跑七套,全绿。

v2.5.0 · 表情包面板

Choose a tag to compare

@yeqiqiye yeqiqiye released this 22 Oct 11:19

v2.5.0 · 表情包面板:网搜 → 点一下发到房间

用起来

顶栏新增 「表情」(只有配了中转才出现)→ 输关键词 → 点一张就发出去。发过的自动进本机收藏(最多 60 张),
收藏区点一下再发一次、✕ 删掉。

怎么实现的(也是成本控制的地方)

  • 搜索在中转里做,依次试 搜狗图片 → Bing 图片 → 斗图啦 → 发表情 → Giphy(要 GIPHY_KEY),
    谁先出结果用谁;面板上会写「来自 X」,全挂会说「没搜到」;
  • 发图发的是 中转/sticker/img?u=…&s=<HMAC签名>:
    签名只对搜出来的那几张有效 → 既能补齐 Referer 绕过图床防盗链,又不会把中转变成「谁都能用的开放图片代理」;
    没签名的一律按老的域名白名单挡掉(实测 /sticker/img?u=https://example.com/x.png 返回 403);
  • 限制:2 秒一张(防刷屏)、单张 ≤ 3MB、18+ 脏词直接拒搜、只接受 image/*。

实测(部署在阿里云 FC 上验的)

  • /sticker?kw=开心 → 15 张,来源 fabiaoqing;
  • 前四张经 /sticker/img 取回:200 · image/jpeg · 222944 / 222854 / 8300 / 9157 字节;
  • 斗图啦 从阿里云出口 fetch failed(被挡),所以它排在后面,靠降级不影响使用;
  • /health 的 features 现在含 sticker、netease-level。

成本

每张 100–300KB;10 人每天 10 张 ≈ 450MB/月,在 CDT 免费 20GB 内;FC 调用每月多几毛钱(现有账单 0.21 元级别)。

地址

标准版(B 站固定 720P)
https://fastly.jsdelivr.net/gh/yeqiqiye/music-plugin@v2.5.0/iirose/music-iirose.js

HD 版(B 站可选 1080P)
https://fastly.jsdelivr.net/gh/yeqiqiye/music-plugin@v2.5.0/iirose/music-iirose-hd.js

测试

npm test 六套全绿。新增表情用例:面板有搜索框 → 搜到 1 张 → 点一下发出
{"m":"…/sticker/img?u=cat.gif&s=deadbeef#e",…} → 2 秒内连点被限速(不刷屏)→ 发过的自动进本机收藏。

注意

表情包多是二创/影视截图,公开房间大量发可能被管理提醒;个别房间禁图。抓 HTML 的源改版后会失效,会自动降级到搜狗/Bing。

v2.4.1 · 歌词窗不会拖丢、不会锁死

Choose a tag to compare

@yeqiqiye yeqiqiye released this 22 Oct 07:45

v2.4.1 · 歌词窗不会再「拖丢 / 锁死」

问题

歌词窗能拖到屏幕外(只限制了「至少露一点」),锁定后唯一能点的 🔓 在窗子右上角 —— 窗子大半在屏幕外时,
那个按钮也跟着出界,就解不开了。

现在

  • 夹在可见范围内:拖动时实时夹(整块留在视口里,左右各留 8px);松手再夹一次;
  • 顶部给按钮条留 30px:🔒/🔓 永远在屏幕里;
  • 锁定 / 改字号 / 改行数 / 旋转屏幕 / 改窗口大小 都会重新夹一次;
  • 旧位置(换过更小屏幕、之前拖出界)在挂载时也会被拉回来,并写回本机。

保底:设置 → 外观 →「重置歌词窗」

一键做到:清掉本机记的位置 → 回到默认布局(居中偏下)→ 解锁 → 字号/行数复位 → 确保歌词是开着的。
就算哪天它真的不见了,点这个按钮一定回来。

地址

标准版(B 站固定 720P)
https://fastly.jsdelivr.net/gh/yeqiqiye/music-plugin@v2.4.1/iirose/music-iirose.js

HD 版(B 站可选 1080P)
https://fastly.jsdelivr.net/gh/yeqiqiye/music-plugin@v2.4.1/iirose/music-iirose-hd.js

测试

npm test 六套全绿。新增:本机存的位置在 (9000, 9000) → 挂载后被夹到 (496, 655) 并写回;
设置里「重置歌词窗」存在,点了之后位置清空、回到默认布局、解除锁定、歌词开着。

v2.4.0 · 房间自带歌词同步

Choose a tag to compare

@yeqiqiye yeqiqiye released this 22 Oct 07:05

v2.4.0 · 房间自带的歌词面板能同步了

找到了「音词不同步」的原因

IIROSE 的 &1 媒体帧里有个 l 字段,放的就是整段 LRC。客户端(以及房间里所有人)的歌词面板靠它对齐时间轴。
我们之前一直发的是 l: null —— 所以房间那边只能自己猜,自然对不上。

参考的 IIROSE-MEDIA-WEB 里就是这么干的:

const f = d.tlyric ? mergeLyrics(d.lrc.lyric, d.tlyric.lyric) : d.lrc.lyric;
send(JSON.stringify({ s, d, c, n, r, b, o, l: f }));

现在

点播前先向 中转 /lyric 取整段 LRC,塞进帧里:

  • 纯 LRC:原样发;
  • 有译文:合成 [00:12.34] 原文 | 译文(与 MEDIA-WEB 同款格式,一行搞定);
  • 拿不到:仍然是 null,行为跟以前完全一样。

效果:全房间的人(哪怕没装我们的面板)都能看到跟着时间轴滚的歌词。

关于另外两份脚本

脚本 结论
iirose-edia-web-…/bundle.js 就是你上次发的那份 IIROSE-MEDIA-WEB(我这边拉它返回 400,用的是本地那份副本),l 字段的用法就是从它这儿确认的
qwq0.github.io/iiroseForge/l.js 只是加载器(2.5 KB):判断自己在 / 还是 /messages.html,再往父窗口注入真正的插件。它证实了一件事——IIROSE 的 app 跑在 messages.html 这个 iframe 里,这跟我们 v2.2.2 的 iframe 扫描对上了
shadowsee.icu/upload/iirose-music-by-xcwqw233.js 被打包/混淆过(内容是空格分隔的十进制字节码),没逆,参考价值低

地址

标准版(B 站固定 720P)
https://fastly.jsdelivr.net/gh/yeqiqiye/music-plugin@v2.4.0/iirose/music-iirose.js

HD 版(B 站可选 1080P)
https://fastly.jsdelivr.net/gh/yeqiqiye/music-plugin@v2.4.0/iirose/music-iirose-hd.js

测试

npm test 六套全绿。新增断言:媒体帧的 l 是整段 LRC(以 [00:00.50] 开头、含换行)、有译文时是
[00:00.50] 晴天 歌词 | sunny line 这种合并格式。线上中转 /lyric 实测:晴天返回 1663 字符 LRC。

限制

只有网易云有歌词(中转对 QQ / B 站直接回空)—— 那两类仍然是 null。

v2.3.0 · 切歌更快 + 8 档音质

Choose a tag to compare

@yeqiqiye yeqiqiye released this 22 Oct 06:48

v2.3.0 · 切歌收得更快 + 网易云 8 档音质

1. 延迟从 2.5 秒压到 0.7 秒

上一版只认「元素停了」,所以统一等 2.5 秒。现在既然已经盯上了客户端的 #radioPlayButton,就按「有多确定」分档:

情况 等多久
播放按钮从 mdi-pause 变回 mdi-play(客户端自己都说停了) 0.7 秒
只是元素 paused(可能在缓冲/切歌) 1.2 秒
自然放完(下一首马上接上) 1.5 秒
其他不确定情况 2.5 秒

轮询间隔也从 1.5 秒缩到 0.8 秒。正常切歌不会误杀:新帧一到立刻取消倒计时。

2. 网易云 8 档音质(借 MEDIA-WEB 的思路)

设置 → 点歌 →「网易云音质」:标准 / 较高 / 极高 / 无损 / Hi-Res / 环绕 / 沉浸 / 母带。

  • 中转已同步升级并部署(阿里云 FC),/playurl 现在接受 level 参数,走 song/enhance/player/url/v1;
  • 实测同一首歌(晴天 1901371647):standard → 128 kbps、exhigh → 320 kbps、lossless → 1 676 kbps;
  • 你的 Cookie 是普通黑胶:hires / jymaster 实际会给到 lossless(1 676 kbps)——网易云的限制,不是我们的;
  • 要了高档但拿不到(没会员/没版权)时,中转自动退回 320k 并在 note 里说明,不会放不出来。

关于 B 站 #audio=

MEDIA-WEB 那招是把「视频流 + 音轨流」两个地址拼成 <video>#audio=<audio> 交给客户端,让客户端自己合。
我们用不了:B 站的 DASH 地址是按请求出口 IP 签名的,我们的中转在阿里云解析,签出来的是阿里云的 IP,
你的浏览器直接拉会 403 —— 这也是我们一开始就选「服务器端 FFmpeg 合流」的原因。这条我不硬搬。

地址

标准版(B 站固定 720P)
https://fastly.jsdelivr.net/gh/yeqiqiye/music-plugin@v2.3.0/iirose/music-iirose.js

HD 版(B 站可选 1080P)
https://fastly.jsdelivr.net/gh/yeqiqiye/music-plugin@v2.3.0/iirose/music-iirose-hd.js

测试

npm test 六套全绿(含之前的 iframe / #radioPlayer 用例)。
中转侧用真实请求验证了三档码率(128001 / 320001 / 1676530 bps)。

v2.2.3 · 认准 #radioPlayer

Choose a tag to compare

@yeqiqiye yeqiqiye released this 22 Oct 06:31

v2.2.3 · 直接认客户端自己的播放器

参考了两个现成的 IIROSE 插件(IIROSE_Send_like、Ming's IIROSE-MEDIA-WEB)之后确认:
IIROSE 客户端的房间播放器在 DOM 里是有 id 的 —— MEDIA-WEB 就是靠 #radioPlayer + #radioPlayButton 跟它协同的。

所以这一版加了最可靠的一层:

  • 优先找 #radioPlayer(真正在响的 audio/video),src 是 blob: 之类对不上帧也能认出来;
  • 顺带读 #radioPlayButton 的 class:mdi-pause = 在播,mdi-play = 停了 —— 这是客户端自己的状态,比我们猜准;
  • 原来的三层(iframe 扫描 / 原型补丁 / 切歌指令)全部保留,互为兜底。

地址

标准版(B 站固定 720P)
https://fastly.jsdelivr.net/gh/yeqiqiye/music-plugin@v2.2.3/iirose/music-iirose.js

HD 版(B 站可选 1080P)
https://fastly.jsdelivr.net/gh/yeqiqiye/music-plugin@v2.2.3/iirose/music-iirose-hd.js

测试

npm test 六套全绿。新增:造一个 #radioPlayer(src 是 blob:,与帧里地址完全不同)+ #radioPlayButton.mdi-pause →
面板必须认出来(roomMedia().src 是那个 blob);把元素停掉、按钮 class 改成 mdi-play → 2.9 秒后播放信息回「未在播放」。