https://github.com/dsy4567/neteasecloudmusicapi-hook
让 NeteaseCloudMusicApi 用根正苗红的浏览器请求 weapi 接口
https://github.com/dsy4567/neteasecloudmusicapi-hook
api cloudmusic hooks netease-cloud-music neteasecloud neteasecloudmusicapi nodejs
Last synced: about 1 month ago
JSON representation
让 NeteaseCloudMusicApi 用根正苗红的浏览器请求 weapi 接口
- Host: GitHub
- URL: https://github.com/dsy4567/neteasecloudmusicapi-hook
- Owner: dsy4567
- License: mit
- Created: 2025-01-29T11:26:17.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2025-02-05T12:34:31.000Z (over 1 year ago)
- Last Synced: 2025-12-31T16:53:47.528Z (5 months ago)
- Topics: api, cloudmusic, hooks, netease-cloud-music, neteasecloud, neteasecloudmusicapi, nodejs
- Language: JavaScript
- Homepage:
- Size: 45.9 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# NeteaseCloudMusicApi-hook
让 [NeteaseCloudMusicApi](https://gitlab.com/Binaryify/neteasecloudmusicapi) 用根正苗红的浏览器请求网易云音乐的 weapi 接口。
本项目基于 NeteaseCloudMusicApi 4.25.0 编写。
## 原理&优缺点
本项目通过使用类似于临时替换 [util/request.js](https://gitlab.com/Binaryify/neteasecloudmusicapi/-/blob/main/util/request.js) 的方式来拦截请求,
并**将请求转交给**事先要求用户**在浏览器内运行的**,特别编写的**JS**。发起请求的一般大致流程如下:
```
hook 前
你的项目
|^
||
v|
NeteaseCloudMusicApi
|^
||https
v|
music.163.com
```
```
hook 后
(你已引导用户在浏览器上登录网易云,并在控制台执行特定代码)
你的项目
|^
||
v|
NeteaseCloudMusicApi
|^
||
v|
NeteaseCloudMusicApi-hook
|^
||
v|
local server
|^
||WebSocket
v|
browser
|^
||https
v|
music.163.com
```
优点:
- 请求在浏览器发出,可以**绕过多数风控手段**(过环境检测、允许用户手动解决人机验证等);
- 不必费尽心思模拟浏览器的行为。
缺点:
- ~~肯定有人认为本项目在画蛇添足~~;
- 根据项目自身设计,迁移至本项目存在不同大小的难度;
- 部分返回结果**有异于**原 NeteaseCloudMusicApi(如返回结果里的 `cookie[]` 数组始终为空);
- 每次运行都需要引导用户在控制台执行特定代码,**麻烦且增加了**现有应用的**使用门槛**(可以指定固定的 `connectionToken` + 油猴脚本/折腾无头浏览器来解决);
- **只能本地使用**,不建议(也很难)在线部署(可以在线应用调访客设备上部署的接口)。
## 安装&快速上手
```bash
git clone https://gitlab.com/Binaryify/neteasecloudmusicapi NeteaseCloudMusicApi
git clone https://github.com/dsy4567/NeteaseCloudMusicApi-hook NeteaseCloudMusicApi-hook
cd NeteaseCloudMusicApi
npm i
cd ../NeteaseCloudMusicApi-hook
npm i
```
一般用法请参见示例代码 [example.js](./example.js)。
更多详细用法请见 [types/index.d.ts](types/index.d.ts)。
## 迁移
> **定义**:如无特别说明,
>
> `ncmApiHook` 即 `require("./path/to/NeteaseCloudMusicApi-hook")`;
>
> `ncmApi` 即 `require('NeteaseCloudMusicApi')` 或 `ncmApiHook.getExports()`。
对于一些项目,只需要改变登录逻辑即可,甚至只需要加几行代码就行;对于另一些项目,则可能需要不同程度的大改。
尽管本项目尽力减少与 NeteaseCloudMusicApi 的行为差异,迁移到 NeteaseCloudMusicApi-hook 仍需要注意以下几点:
- 本项目设计之初是供**本地应用**(而不是在线应用)使用,你的项目**不能**连同本项目**在线部署**,也**不要**尝试将本项目的服务**公开至公网**;
- 注意默认情况下,本项目会**自行决定请求去向**,详见“常见问题 > 如何判断请求走浏览器还是 NeteaseCloudMusicApi”;
- 你需要**自行引导用户**在浏览器上登录网易云,并在控制台**执行特定代码**;
- 本项目**不支持**所有**非 weapi 接口**,直接调用它们时,将自动转到原 NeteaseCloudMusicApi(见 常见问题 );
- 未连接浏览器时,调用任何 api 亦会走原 NeteaseCloudMusicApi(除非在 `ncmApiHook.init()` 指定 `{ forceConnection: true }`,未连接浏览器将拒绝所有请求 );
- 如果请求将要走浏览器(尤其浏览器端已登录时),调用 weapi 不必带上 cookie(即使带上也会忽略)。
## 常见问题
### 如何在线部署?
- ~~在**访客**的设备上部署用到本项目的简易服务,然后让你的在线应用访问**访客设备上的服务**~~;
- 放弃这个想法。
### 如何登录?
请求走浏览器,需提前引导用户在浏览器打开 [music.163.com](https://music.163.com/) 并正常完成登录流程,然后在控制台执行以下代码;无需在 `ncmApi.foo_bar()` 指定 `{ cookie: "MUSIC_U=xxx;" }`(即使指定也会忽略)。
```js
// 详见 example.js
(() => {
let s = document.createElement("script");
s.dataset.connectionToken = "REPLACE_THIS"
s.src = "REPLACE_THIS";
document.head.append(s);
})();
```
请求走 NeteaseCloudMusicApi,在 `ncmApi.foo_bar()` 指定 `{ cookie: "MUSIC_U=xxx;" }` 即可;
如需获取浏览器内部分 cookies,使用 `ncmApiHook.loginStatus.get()` 即可。
### 我从 `ncmApiHook.loginStatus.get()` 获得的 `MUSIC_U` 为空字符串怎么办?
在浏览器中的网易云音乐网页版,cookie `MUSIC_U` 是“HttpOnly”的。手动在开发者工具 > 应用 > Cookie 中取消勾选对应复选框即可。
### 如何强制使用 weapi 加密接口?
以下方法任选其一:
- 在 `ncmApiHook.init()` 指定 `{ forceWeapi: true }` 来强制使用 weapi 接口;
```js
ncmApiHook.init({ forceWeapi: true });
```
- 调用 api 时,像原来那样指定 `{ crypto: "weapi" }` );
```js
ncmApi.like({ id: 114514, like: true, crypto: "weapi" });
```
### 如何判断请求走浏览器还是 NeteaseCloudMusicApi?
**实际情况请以在 `ncmApiHook.init()` 指定 `{ debug: true }` 后的日志为准。**
> 你可以结合阅读 [src/hooks/request.js](src/hooks/request.js) (不到 100 行)来理解下面的说明。
满足和以下条件之一,任何请求必定**走浏览器或被拒绝**:
- 在 `ncmApiHook.init()` 指定了 `{ forceWeapi: true, forceConnection: true }`;
- 在表达式 `!!ncmApiHook.server.getServerStatus()._wsConnection === true` (即:已连接浏览器)和以下条件之一成立时。
- 在 `ncmApiHook.init()` 指定了 `{ forceWeapi: true }`;
- 在 `ncmApi.foo_bar()` 指定了 `{ crypto: "weapi" }`;
- 其他最终会请求 weapi 的情况。
满足以下条件之一,任何请求必定**走 NeteaseCloudMusicApi 或被拒绝**:
- 调用了 `ncmApiHook.unhook()`,随后从未调用 `ncmApiHook.hook()`;
- 在表达式 `!!ncmApiHook.server.getServerStatus()._wsConnection === false` (即:未连接浏览器)和以下条件之一成立时;
- 在 `ncmApiHook.init()` 指定了 `{ forceConnection: false }`(默认值)。
- 表达式 `!!ncmApiHook.server.getServerStatus()._wsConnection === true` (即:已连接浏览器)成立,且在 `ncmApiHook.init()` 指定了 `{ forceWeapi: false }`(默认值),且以下条件之一成立时。
- 在 `ncmApi.foo_bar()` 指定了 `{ crypto: "eapi" | "api" | "linuxapi" }`(即:使用非 weapi 加密);
- 其他最终会请求**非** weapi 的情况。
### 尝试将服务公开到内网以供其他设备上的浏览器使用,浏览器连接时报错怎么办?
如果报错信息与 HTTPS 相关,可能由于网易云网站设置的浏览器安全策略,使发往本项目服务(非 `127.0.0.1` / `localhost` 等本机环回地址)的 HTTP 请求自动强制升级为 HTTPS,可通过本机运行反向代理等方法解决,例如,可在浏览器端的设备上执行以下操作:
1. 安装 [mitmproxy](https://mitmproxy.org/);
2. 执行以下命令:
```bash
mitmdump --mode reverse:http://<运行本项目服务设备的 IP 地址>:<端口(默认 16333)> --set listen_port=16333
```
如果是其他问题,请见“常见问题 > 浏览器与 NeteaseCloudMusicApi-hook 的服务器意外断连怎么办?”或提交 issue。
### 浏览器与 NeteaseCloudMusicApi-hook 的服务器意外断连怎么办?
在浏览器端打开开发者工具,并筛选 `[NeteaseCloudMusicApi-hook]`。
如果找到“非正常断连,x 秒后重试”的日志,可等待重连;
如果找到“重连次数过多”的日志,请尝试重启你的应用或检查网络连接(局域网);
如果以上操作没有效果,请准备好你应用的日志、浏览器控制台的日志,以及浏览器开发者工具 > “网络”选项卡 > 与本项目相关的 WebSocket 连接的**所有脱敏消息**,然后提交 issue。
### 本项目和 NeteaseCloudMusicApi 有哪些返回结果上的差异?
已知明显差异如下:
- 执行 `ncmApi.foo_bar()` 后返回的结果中,`cookie[]` 数组始终为空;
- 返回结果中的状态码,少数情况下可能不同于 NeteaseCloudMusicApi(如你认为有必要,可提交 issue 讨论)。
### 每次都要要求用户在控制台执行代码太麻烦,有没有更方便的方法?
1. 在 `ncmApiHook.init()` 指定 `{ connectionToken: "<固定值>" }`;
2. 适当修改 [src/browser/inject.js](./src/browser/inject.js),并将其以油猴脚本/浏览器扩展的形式提供给用户。
或者,折腾无头浏览器。
### 是否支持多用户?
请求走浏览器时,除非不断更换连接的浏览器(同一 NeteaseCloudMusicApi-hook 实例同一时间只能接受一个连接),或者合理使用 `ncmApiHook.unhook()`,否则一个实例**只支持一名用户**;
请求走 NeteaseCloudMusicApi 时,取决于你的应用是否支持多用户。