完成网络音乐、工具页面与安装器体验升级

This commit is contained in:
2026-08-18 13:54:33 +08:00
parent 337390f53e
commit 149c28082d
1082 changed files with 26442 additions and 162838 deletions
+13
View File
@@ -0,0 +1,13 @@
/**
!/module
!/plugins
!/public
!/static
!/util
!/app.js
!/server.js
!/package.json
!/package-lock.json
!/pnpm-lock.yaml
!/index.js
!/main.js
+21
View File
@@ -0,0 +1,21 @@
## 配置设备平台
### lite: 酷狗概念版, 默认为手机版
platform=''
## 服务端口 (可选)
PORT=''
## HTTP 代理地址 (可选)
KUGOU_API_PROXY=''
## 设备id(可选,建议固定 )
## KUGOU_API_GUID 建议为 uuidv4
## KUGOU_API_DEV 建议长度为 10 位的字符串
## MAC地址
KUGOU_API_GUID=''
KUGOU_API_DEV=''
KUGOU_API_MAC=''
## webglhash 指纹,如:9655615838832711846
## 如果想使用正式的,可以运行 public/sid_edt_generator.html 来获取固定
KUGOU_API_WEBGL=''
+148
View File
@@ -0,0 +1,148 @@
# 发行版本部署
## 多端部署
name: Build Release
## 权限配置
permissions:
contents: write
on:
push:
tags:
- v*
jobs:
# window 端打包
build-windows:
name: Build for Windows
runs-on: windows-latest
timeout-minutes: 30
steps:
# 检出 Git 仓库
- name: Check out Git repository
uses: actions/checkout@v4
# 安装 Node.js
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: '18.x'
# 安装项目依赖
- name: Install Dependencies
run: npm install
# 构建
- name: Build App for Windows
run: npm run pkgwin
shell: bash
# 上传构建产物
- name: Upload Windows artifact
uses: actions/upload-artifact@v4
with:
name: app-win
if-no-files-found: ignore
path: |
bin/*.exe
#创建 GitHub Release
- name: Create Release
uses: softprops/action-gh-release@v2
if: startsWith(github.ref, 'refs/tags/v')
with:
token: ${{ secrets.GITHUB_TOKEN}}
draft: false
prerelease: false
files: bin/*.exe
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN}}
# mac 端打包
build-masos:
name: Build for MacOS
runs-on: macos-latest
timeout-minutes: 30
steps:
# 检出 Git 仓库
- name: Check out Git repository
uses: actions/checkout@v4
# 安装 Node.js
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: '18.x'
# 安装项目依赖
- name: Install Dependencies
run: npm install
# 构建
- name: Build App for Macos
run: npm run pkgmacos
shell: bash
# 上传构建产物
- name: Upload Macos artifact
uses: actions/upload-artifact@v4
with:
name: app-macos
if-no-files-found: ignore
path: |
bin/*
#创建 GitHub Release
- name: Create Release
uses: softprops/action-gh-release@v2
if: startsWith(github.ref, 'refs/tags/v')
with:
token: ${{ secrets.GITHUB_TOKEN}}
draft: false
prerelease: false
files: bin/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN}}
# Linux 端打包
build-linux:
name: Build for Linux
runs-on: ubuntu-22.04
timeout-minutes: 30
steps:
# 检出 Git 仓库
- name: Check out Git repository
uses: actions/checkout@v4
# 安装 Node.js
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: '18.x'
# 更新 Ubuntu 软件源
- name: Ubuntu Update with sudo
run: sudo apt-get update
# 安装 RPM 和 Pacman 插件
- name: Install RPM & Pacman (on Ubuntu)
run: |
sudo apt-get install --no-install-recommends -y rpm &&
sudo apt-get install --no-install-recommends -y libarchive-tools &&
sudo apt-get install --no-install-recommends -y libopenjp2-tools
# 安装项目依赖
- name: Install Dependencies
run: npm install
# 构建
- name: Build App for Linux
run: npm run pkglinux
shell: bash
# 上传构建产物
- name: Upload Linux artifact
uses: actions/upload-artifact@v4
with:
name: app-linux
if-no-files-found: ignore
path: |
bin/*
#创建 GitHub Release
- name: Create Release
uses: softprops/action-gh-release@v2
if: startsWith(github.ref, 'refs/tags/v')
with:
token: ${{ secrets.GITHUB_TOKEN}}
draft: false
prerelease: false
files: bin/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN}}
+27
View File
@@ -0,0 +1,27 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
test
bin/*
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
.env
+19
View File
@@ -0,0 +1,19 @@
{
"arrowParens": "always",
"bracketSameLine": false,
"bracketSpacing": true,
"embeddedLanguageFormatting": "auto",
"htmlWhitespaceSensitivity": "css",
"insertPragma": false,
"jsxSingleQuote": false,
"printWidth": 150,
"proseWrap": "always",
"quoteProps": "preserve",
"requirePragma": false,
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"useTabs": false,
"vueIndentScriptAndStyle": false
}
+184
View File
@@ -0,0 +1,184 @@
# CLAUDE.md
> KuGouMusicApi 项目指南 — 酷狗音乐 NodeJS 版 API
## 项目概述
本项目是酷狗音乐的非官方 NodeJS API 服务,通过伪造请求头和调用官方 API 实现酷狗音乐的全部功能。支持两种运行模式:HTTP 服务模式(Express)和编程式调用模式(作为库引入)。
- **作者**: Lines(基于 MakcRe 的项目)
- **版本**: 1.5.1
- **Node 要求**: >= 12
- **License**: MIT
## 技术栈
- **运行时**: Node.js
- **HTTP 框架**: Express 4.x
- **HTTP 客户端**: Axios
- **加密库**: crypto-jsAES/MD5)、node-forgeRSA
- **压缩库**: pakozlib 解压,用于 KRC 歌词解码)
- **大整数库**: big-integer(用于 MID 计算)
- **开发工具**: nodemon、prettier、esbuild、pkg
## 项目结构
```
KuGouMusicApi/
├── app.js # CLI 入口(shebang),调用 server.startService()
├── index.js # 开发入口,require('./app')
├── main.js # 编程式 API 入口(作为库使用时)
├── server.js # Express 服务器核心(CORS、Cookie、路由注册)
├── module/ # API 模块目录(约 160 个接口)
│ ├── search.js # 搜索
│ ├── login.js # 登录
│ ├── login_cellphone.js # 手机登录
│ ├── song_url.js # 歌曲 URL
│ ├── playlist_detail.js # 歌单详情
│ └── ... # 其他接口
├── util/ # 工具库
│ ├── index.js # 统一导出入口
│ ├── config.json # 平台配置(appid、clientver 等)
│ ├── crypto.js # 加密函数(AES、RSA、MD5、SHA1
│ ├── helper.js # 签名函数(signature、sign、signKey
│ ├── request.js # HTTP 请求封装(签名、代理、响应处理)
│ ├── util.js # 通用工具(随机数、Cookie、歌词解码、MID、WebGL 指纹)
│ ├── runtime.js # 运行时配置(CLI 参数、代理解析)
│ ├── generate_simulate.js # 行为指纹模拟生成(AES+RSA 加密)
│ ├── apicache.js # API 响应缓存中间件
│ └── memory-cache.js # 内存缓存实现
├── public/ # 前端页面
│ ├── index.html # 接口文档首页
│ ├── login_captcha.html # 登录页(依赖 WASM
│ ├── login_captcha_simulate.html # 登录页(纯 JS 模拟,不依赖 WASM)
│ ├── sid_edt_generator.html # SID/EDT 生成工具页
│ └── verify-pkg/ # WASM 验证码包
├── docs/ # 接口文档
├── .env.example # 环境变量配置示例
├── Dockerfile # Docker 部署配置
├── vercel.json # Vercel 部署配置
└── package.json
```
## 核心架构
### 请求流程
```
客户端请求 → Express 中间件(CORS → Cookie 解析 → 平台标识注入 → 缓存)
→ 路由处理器(参数合并 → Cookie 格式化 → Authorization 头解析)
→ API 模块函数(构建请求参数 → 生成签名 → 调用 createRequest
→ createRequest(注入设备标识 → 签名 → 代理 → 发送请求 → 响应处理)
→ 酷狗服务器
```
### 签名机制
所有 API 请求都需要签名验证,支持三种签名类型:
- **Android 签名**(默认): `MD5(盐值 + 排序参数 + 请求体 + 盐值)`
- **Web 签名**: `MD5(盐值 + 排序参数 + 盐值)`
- **Register 签名**: `MD5("1014" + 排序值 + "1014")`
区分标准版和概念版(lite),使用不同的盐值和 appid。
### 行为指纹(SID/EDT
酷狗服务端会检测请求的行为指纹,需要生成模拟数据:
- **SID**: RSA-OAEP 加密的 AES 密钥(Base64
- **EDT**: AES-128-CBC 加密的行为数据(Base64
- 行为数据包括:鼠标轨迹(贝塞尔曲线)、滚动事件、窗口事件、WebGL 指纹
### 双平台支持
通过 `.env` 中的 `platform` 环境变量切换:
- **标准版**(默认): `appid=1005`, `clientver=20489`
- **概念版 lite**: `appid=3116`, `clientver=11440`
## 常用命令
```bash
# 安装依赖
npm install
# 开发模式(热重载)
npm run dev
# 生产模式
npm start
# 自定义端口
PORT=4000 npm run dev
# 使用代理
KUGOU_API_PROXY=http://127.0.0.1:7890 npm run dev
# CLI 参数方式设置代理
node app.js --proxy=http://127.0.0.1:7890 --platform=lite
```
## 环境变量
| 变量 | 说明 | 默认值 |
|------|------|--------|
| `platform` | 平台类型:`lite`(概念版)或空(标准版) | 空 |
| `PORT` | 服务端口 | 3000 |
| `HOST` | 监听地址 | 空(所有地址) |
| `KUGOU_API_PROXY` | HTTP 代理地址 | 空 |
| `KUGOU_API_GUID` | 设备 GUID(建议 UUID v4 | 自动生成 |
| `KUGOU_API_DEV` | 开发设备标识(10位大写字符串) | 自动生成 |
| `KUGOU_API_MAC` | 设备 MAC 地址 | 02:00:00:00:00:00 |
| `KUGOU_API_WEBGL` | WebGL 指纹哈希 | 自动生成随机值 |
## 开发规范
### 添加新接口
1.`module/` 目录下创建新的 `.js` 文件
2. 文件名即为路由路径(`_` 替换为 `/`),如 `user_detail.js``/user/detail`
3. 模块导出一个函数:`(params, useAxios) => Promise`
- `params`: 合并后的请求参数(含 cookie、query、body
- `useAxios`: 请求工厂函数,调用 `createRequest` 发送请求
```javascript
// module/example.js
module.exports = (params, useAxios) => {
return new Promise((resolve, reject) => {
useAxios({
baseURL: 'https://gateway.kugou.com',
url: '/v1/example',
method: 'GET',
params: { keyword: params.keyword },
cookie: params.cookie,
}).then(resolve).catch(reject);
});
};
```
### 签名选择
- 大多数接口使用 `android` 签名(默认)
- Web 相关接口使用 `web` 签名
- 设备注册接口使用 `register` 签名
### Cookie 传递
Cookie 通过以下方式传递:
- URL 查询参数: `?cookie=token%3Dxxx%3Buserid%3Dxxx`
- 请求体: `{ cookie: "token=xxx;userid=xxx" }`
- Authorization 头: `Authorization: token=xxx;userid=xxx`
服务端会自动将 Cookie 字符串转换为 JSON 对象并合并。
## 代码风格
- 使用 Prettier 格式化(配置见 `.prettierrc.json`
- 2 空格缩进
- 单引号
- 无分号(Prettier 默认)
## 部署
- **Docker**: 使用项目根目录的 `Dockerfile`
- **Vercel**: 使用 `vercel.json` 配置
- **pkg 打包**: `npm run pkgwin` / `npm run pkglinux` / `npm run pkgmacos`
- **esbuild 打包**: `npm run pkgjs`
+28
View File
@@ -0,0 +1,28 @@
FROM node:lts-alpine
RUN apk add --no-cache tini
RUN corepack enable
RUN npm config set registry https://registry.npmmirror.com && npm install -g pnpm --force
ENV NODE_ENV=production
WORKDIR /app
RUN chown node:node /app
COPY --chown=node:node package.json pnpm-lock.yaml ./
USER node
#RUN pnpm install --prod --frozen-lockfile --ignore-scripts false
RUN npm install --production --ignore-scripts=false
COPY --chown=node:node . ./
EXPOSE 3000
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["node", "app.js"]
+14
View File
@@ -0,0 +1,14 @@
MIT License
Copyright (c) 2023 MakcRe
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
+259
View File
@@ -0,0 +1,259 @@
# KuGouMusic API
酷狗音乐 NodeJS 版 API
[![](https://img.shields.io/badge/Author-MakcRe-blueviolet?style=for-the-badge '作者')](https://github.com/MakcRe)
![](https://img.shields.io/badge/dynamic/json?label=GitHub%20Followers&style=for-the-badge&query=%24.data.totalSubs&url=https%3A%2F%2Fapi.spencerwoo.com%2Fsubstats%2F%3Fsource%3Dgithub%26queryKey%3DMakcRe&labelColor=282c34&color=181717&logo=github&longCache=true '关注数量')
![](https://img.shields.io/github/stars/MakcRe/KuGouMusicApi.svg?style=for-the-badge&label=Star 'Star数量')
![](https://img.shields.io/github/forks/MakcRe/KuGouMusicApi.svg?style=for-the-badge&label=Fork 'Fork数量')
![](https://img.shields.io/github/issues/MakcRe/KuGouMusicApi.svg?style=for-the-badge&label=Issues 'Issues数量')
![](https://img.shields.io/github/contributors/MakcRe/KuGouMusicApi?style=for-the-badge '贡献者')
![](https://img.shields.io/github/repo-size/MakcRe/KuGouMusicApi?style=for-the-badge&label=files&color=cf8ef4&labelColor=373e4dl '文件大小')
![](https://img.shields.io/github/languages/code-size/MakcRe/KuGouMusicApi?color=blueviolet&style=for-the-badge '代码大小')
[//]: # '<br>'
![](https://img.shields.io/github/package-json/v/MakcRe/KuGouMusicApi?longCache=true&style=for-the-badge)
![](https://img.shields.io/badge/Node-12+-green.svg?longCache=true&style=for-the-badge)
![](https://img.shields.io/badge/License-mit-blue.svg?longCache=true&style=for-the-badge)
## 灵感来自
[Binaryify/NeteaseCloudMusicApi](https://github.com/Binaryify/NeteaseCloudMusicApi)
## 环境要求
需要 NodeJS 12+ 环境
## 工作原理
跨站请求伪造 (CSRF), 伪造请求头 , 调用官方 API
## 免责声明
> 1. 本项目仅供学习使用,请尊重版权,请勿利用此项目从事商业行为及非法用途!
> 2. 使用本项目的过程中可能会产生版权数据。对于这些版权数据,本项目不拥有它们的所有权。为了避免侵权,使用者务必在 24 小时内清除使用本项目的过程中所产
> 生的版权数据。
> 3. 由于使用本项目产生的包括由于本协议或由于使用或无法使用本项目而引起的任何性质的任何直接、间接、特殊、偶然或结果性损害(包括但不限于因商誉损失、停
> 工、计算机故障或故障引起的损害赔偿,或任何及所有其他商业损害或损失)由使用者负责。
> 4. **禁止在违反当地法律法规的情况下使用本项目。** 对于使用者在明知或不知当地法律法规不允许的情况下使用本项目所造成的任何违法违规行为由使用者承担,本
> 项目不承担由此造成的任何直接、间接、特殊、偶然或结果性责任。
> 5. 音乐平台不易,请尊重版权,支持正版。
> 6. 本项目仅用于对技术可行性的探索及研究,不接受任何商业(包括但不限于广告等)合作及捐赠。
> 7. 如果官方音乐平台觉得本项目不妥,可联系本项目更改或移除。
### 安装
```shell
$ git clone git@github.com:MakcRe/KuGouMusicApi.git
$ cd KuGouMusicApi
$ npm install
```
### 使用接口为概念版
```
$ 复制 .env.example 为 .env,并且把里面的 `platform=''` 改为 `platform=lite`
$ 注意不同版本的平台的 token 是不通用的。
```
### 运行
```shell
$ npm run dev
```
服务器启动默认端口为 3000, 若不想使用 3000 端口 , 可使用以下命令 : Mac/Linux
```shell
$ PORT=4000 npm run dev
```
windows 下使用 git-bash 或者 cmder 等终端执行以下命令 :
```shell
$ set PORT=4000 && npm run dev
```
windows 下使用 PowerShell 等终端执行以下命令 :
```shell
$ $Env:PORT=4000; npm run dev
```
服务器启动默认 host 为 localhost,如果需要更改, 可使用以下命令 : Mac/Linux
```shell
$ HOST=127.0.0.1 npm run dev
```
windows 下使用 git-bash 或者 cmder 等终端执行以下命令 :
```shell
$ set HOST=127.0.0.1 && npm run dev
```
windows 下使用 PowerShell 等终端执行以下命令 :
```shell
$ $Env:HOST=127.0.0.1; npm run dev
```
### 代理配置
如需通过 HTTP 代理请求酷狗接口,可在启动前设置 `KUGOU_API_PROXY` 环境变量,例如:
```shell
$ $Env:KUGOU_API_PROXY='http://127.0.0.1:7890'; npm run dev
```
也可以在启动命令后追加代理参数:
```shell
$ node app.js --proxy=http://127.0.0.1:7890
```
## Vercel 部署
### 操作方法
1. fork 此项目
2. 在 Vercel 官网点击 `New Project`
3. 点击 `Import Git Repository` 并选择你 fork 的此项目并点击 `import`
4. 点击 `PERSONAL ACCOUNT``select`
5. 直接点 `Continue`
6. 若需要部署版本为概念版(不需要该步骤可以跳过),在 `Environment Variables` 添加 `key``platform``Value (Will Be Encrypted)``lite` 然后点击
`Add`
7. `PROJECT NAME`自己填,`FRAMEWORK PRESET``Other` 然后直接点 `Deploy` 接着等部署完成即可
## 功能特性
1. 登录
2. 刷新登录
3. 发送验证码
4. dfid 获取
5. 获取用户额外信息
6. 获取用户 vip 信息
7. 获取用户歌单
8. 获取用户关注歌手
9. 获取用户听歌历史排行
10. 获取用户最近听歌历史
11. 获取继续播放信息(对应手机版首页显示继续播放入口)
12. 收藏歌单/新建歌单
13. 取消收藏歌单/删除歌单
14. 对歌单添加歌曲
15. 对歌单删除歌曲
16. 新碟上架
17. 专辑信息
18. 专辑详情
19. 专辑音乐列表
20. 获取音乐 URL
21. 获取歌曲高潮部分
22. 搜索
23. 默认搜索关键词
24. 综合搜索
25. 热搜列表
26. 搜索建议
27. 歌词搜索
28. 获取歌词
29. 歌单分类
30. 歌单
31. 主题歌单
32. 音效歌单
33. 获取歌单详情
34. 获取歌单所有歌曲
35. 获取歌单所有歌曲(新版)
36. 相似歌单
37. 获取主题歌单所有歌曲
38. 获取主题音乐
39. 获取主题音乐详情
40. 歌曲推荐
41. 获取歌手和专辑图片
42. 获取歌手图片
43. 获取音乐相关信息
44. 获取更多音乐版本
45. 获取音乐伴奏信息
46. 获取音乐 k 歌数量
47. 获取音乐详情
48. 获取音乐专辑/歌手信息
49. 私人 FM(对应手机和 pc 端的猜你喜欢)
50. banner
51. 乐库 banner
52. 乐库电台
53. 乐库
54. 电台 - 推荐
55. 电台
56. 电台 - 图片
57. 电台 - 音乐列表
58. 编辑精选
59. 编辑精选数据
60. 编辑精选歌单
61. 编辑精选专区
62. 编辑精选专区详情
63. 领取 VIP(需要登陆,该接口为测试接口)
64. 获取歌手列表
65. 获取歌手详情
66. 获取歌手专辑
67. 获取歌手单曲
68. 获取歌手 MV
69. 关注歌手
70. 取消关注歌手
71. 获取关注歌手新歌
72. 获取视频 url
73. 获取歌曲 MV
74. 获取视频相关信息
75. 获取视频详情
76. 新歌速递
77. 场景音乐列表
78. 场景音乐详情
79. 获取场景音乐讨论区
80. 获取场景音乐模块 Tag
81. 获取场景音乐歌单列表
82. 获取场景音乐视频列表
83. 获取场景音乐音乐列表
84. 每日推荐
85. 历史推荐
86. 风格推荐
87. 排行列表
88. 排行榜推荐列表
89. 排行榜往期列表
90. 排行榜信息
91. 排行榜歌曲列表
92. 歌曲评论
93. 歌曲评论-根据分类返回
94. 歌曲评论-根据热词返回
95. 楼层评论
96. 歌单评论
97. 专辑评论
98. 歌曲曲谱
99. 曲谱详情
100. 推荐曲谱
101. 曲谱合集
102. 曲谱合集详情
103. 提交听歌历史
104. 获取服务器时间
105. 刷刷
106. AI 推荐
107. 频道 - 获取用户所有频道
108. 频道 - 详情
109. 频道 - 频道安利
110. 频道 - 相似频道
111. 频道 - 订阅
112. 频道 - 音乐故事
113. 频道 - 音乐故事详情
114. 动态 - 最常访问
115. 获取用户公开的音乐
116. 听书 - 每日推荐
117. 听书 - 排行榜推荐
118. 听书 - VIP 推荐
119. 听书 - 每周推荐
120. 听书 - 专辑详情
121. 听书 - 专辑音乐列表
122. 歌曲详情 - 歌曲成绩单
123. 歌曲详情 - 歌曲成绩单详情
124. 获取已购单曲
125. 获取已购专辑
## License
[The MIT License (MIT)](https://github.com/MakcRe/KuGouMusicApi/blob/main/LICENSE)
+8
View File
@@ -0,0 +1,8 @@
#!/usr/bin/env node
async function start() {
require('./util/runtime').applyCliOverrides();
await require('./server').startService();
}
start().catch(console.error);
View File
File diff suppressed because it is too large Load Diff
+12
View File
@@ -0,0 +1,12 @@
# 酷狗音乐 API
> 酷狗音乐 NodeJS 版 API
- 全部接口已升级到最新
- 具备登录接口
- 更完善的文档
[GitHub](https://github.com/MakcRe/KuGouMusicApi)
[Get Started](#kugoumusic-api)
![color](#fff)
+23
View File
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>酷狗音乐 NodeJS 版 API</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="description" content="Description" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@4/lib/themes/vue.css" />
</head>
<body>
<div id="app"></div>
<script>
window.$docsify = {
name: '酷狗音乐 API',
repo: 'https://github.com/MakcRe/KuGouMusicApi',
coverpage: true,
};
</script>
<!-- Docsify v4 -->
<script src="//cdn.jsdelivr.net/npm/docsify@4"></script>
</body>
</html>
+1
View File
@@ -0,0 +1 @@
require('./app');
File diff suppressed because it is too large Load Diff
+126
View File
@@ -0,0 +1,126 @@
/**
* @fileoverview KuGouMusicApi 程序化 API 入口模块
*
* 本模块是项目的编程式调用入口(区别于 index.js 的 HTTP 服务入口),
* 负责:
* 1. 扫描 `module/` 目录下所有 API 模块文件并动态加载
* 2. 为每个模块创建统一的包装函数,自动处理 Cookie 格式转换
* 3. 将所有 API 函数与服务器工具、请求工具合并后统一导出
*
* 导出结构为扁平对象,模块文件名(去 .js 后缀)即为 API 函数名。
* 例如:`module/search.js` → `api.search(params)`
*
* @module app
* @requires node:fs
* @requires path
* @requires ./util - 工具函数(cookieToJson 等)
* @requires ./server - 服务器管理(startService、getModulesDefinitions
* @requires ./util/request - 底层 HTTP 请求工具(createRequest
*
* @example
* // 作为库使用(编程式调用,不启动 HTTP 服务)
* const api = require('./app');
*
* // 手机登录
* const loginRes = await api.login_cellphone({
* mobile: '13800138000',
* code: '123456'
* });
*
* // 搜索音乐(携带认证 Cookie)
* const searchRes = await api.search({
* keywords: '海阔天空',
* cookie: `token=${loginRes.body.token};userid=${loginRes.body.userid}`
* });
*/
const fs = require('node:fs');
const path = require('path');
const { cookieToJson } = require('./util');
/**
* 动态注册的 API 函数集合
*
* 键为模块文件名(去 .js 后缀),值为对应的包装函数。
* 例如:`{ search: [Function], login_cellphone: [Function], ... }`
*
* @type {Record<string, (data?: Record<string, any>) => Promise<any>>}
*/
let obj = {};
/**
* ============================================================
* 动态扫描并加载 module/ 目录下的所有 API 模块
* ============================================================
*
* 扫描流程:
* 1. 使用 `fs.readdirSync` 同步读取 `module/` 目录下所有文件
* 2. 对文件列表进行倒序排列(与 server.js 中的路由注册顺序保持一致)
* 3. 过滤出以 `.js` 结尾的文件
* 4. 对每个模块文件:
* a. 通过 `require` 加载模块,获取模块处理函数
* b. 从文件名提取 API 函数名(去掉 `.js` 后缀,如 `search.js` → `search`
* c. 创建包装函数并注册到 `obj` 对象中
*
* 每个包装函数的作用:
* - 将调用者传入的 cookie 字符串自动转换为 JSON 对象
* - 合并调用参数与默认 cookie
* - 调用原始模块处理函数,同时注入请求工厂函数(lazy 加载 createRequest
*/
fs.readdirSync(path.join(__dirname, 'module'))
.reverse()
.forEach((file) => {
// 跳过非 .js 文件(如 .json、.map、目录等)
if (!file.endsWith('.js')) return;
// 加载模块,获取其导出的处理函数
let fileModule = require(path.join(__dirname, 'module', file));
// 提取 API 函数名:取文件名中第一个 `.` 之前的部分
// 例如:`search.js` → `search``login_cellphone.js` → `login_cellphone`
let fn = file.split('.').shift() || '';
/**
* 为当前模块创建包装函数
*
* @param {Record<string, any>} [data={}] - 调用者传入的请求参数
* @returns {Promise<any>} 模块处理函数的返回结果
*/
obj[fn] = (data = {}) => {
// 自动将 cookie 字符串转换为 JSON 对象
// 调用者可以传入 cookie 字符串(如 "token=xxx;userid=xxx")或对象格式
if (typeof data.cookie === 'string') data.cookie = cookieToJson(data.cookie);
// 调用原始模块处理函数,传入两个参数:
// 1. 合并后的参数对象(确保 cookie 始终为对象格式,默认为空对象)
// 2. 请求工厂函数:延迟加载 createRequest,避免循环依赖
return fileModule({ ...data, cookie: data.cookie ? data.cookie : {} }, (...args) => {
// 延迟加载:仅在实际需要发起请求时才 require createRequest
// 这样可以避免模块加载阶段的循环依赖问题
const { createRequest } = require('./util/request');
return createRequest(...args);
});
};
});
/**
* ============================================================
* 统一导出
* ============================================================
*
* 最终导出一个扁平对象,包含三部分内容:
*
* 1. `...require('./server')` — 服务器管理工具
* - `startService()`: 启动 HTTP 服务
* - `getModulesDefinitions()`: 动态扫描模块定义
*
* 2. `...require('./util/request')` — 底层请求工具
* - `createRequest()`: 创建 HTTP 请求
*
* 3. `...obj` — 所有 API 模块函数
* - 每个 module/*.js 文件对应一个同名函数
* - 如 `search`、`login_cellphone`、`song_url` 等
*
* @type {Record<string, any> & import("./server")}
*/
module.exports = { ...require('./server'), ...require('./util/request'), ...obj };
+34
View File
@@ -0,0 +1,34 @@
const { appid, clientver, signParamsKey, cryptoMd5 } = require('../util');
module.exports = (params, useAxios) => {
const userid = params?.userid || params?.cookie?.userid || 0;
const mid = params?.cookie?.KUGOU_API_MID; // 可以自定义
const clienttime = Date.now();
const recommend_source = (params?.album_audio_id || '').split(',').map((s) => ({ ID: Number(s) }));
const dataMap = {
platform: 'ios',
clientver,
clienttime: clienttime,
userid,
client_playlist: [],
source_type: 2,
playlist_ver: 2,
area_code: 1,
appid,
key: signParamsKey(clienttime.toString()),
mid,
recommend_source,
};
return useAxios({
url: '/recommend',
data: dataMap,
method: 'post',
encryptType: 'android',
cookie: params?.cookie || {},
headers: { 'x-router': 'songlistairec.kugou.com' },
clearDefaultParams: true,
});
};
+34
View File
@@ -0,0 +1,34 @@
const { clientver, appid, cryptoMd5, signParamsKey } = require('../util');
module.exports = (params, useAxios) => {
const dateTime = Date.now();
const data = (params?.album_id || '').split(',').map((s) => ({ album_id: s, album_name: '', author_name: '' }));
const dfid = params?.cookie?.dfid || params?.dfid || '-';
const userid = params?.cookie?.userid || params?.userid || 0;
const token = params?.cookie?.token || params?.token || 0;
const dataMap = {
appid,
clienttime: dateTime,
clientver,
data,
dfid,
fields: params?.fields || '',
key: signParamsKey(dateTime),
mid: params?.cookie?.KUGOU_API_MID,
};
if (token) dataMap['token'] = token;
if (userid) dataMap['userid'] = userid;
return useAxios({
baseURL: 'http://kmr.service.kugou.com',
url: '/v1/album',
method: 'POST',
data: dataMap,
encryptType: 'android',
cookie: params?.cookie || {},
headers: { 'x-router': 'kmr.service.kugou.com', 'Content-Type': 'application/json' },
});
};
+16
View File
@@ -0,0 +1,16 @@
// 专辑详情
module.exports = (params, useAxios) => {
const data = {
data: [{ album_id: params.id }],
is_buy: params?.is_buy || 0,
fields: 'album_id,album_name,publish_date,sizable_cover,intro,language,is_publish,heat,type,quality,authors,exclusive,author_name,trans_param',
};
return useAxios({
url: '/kmr/v2/albums',
method: 'POST',
data,
encryptType: 'android',
cookie: params?.cookie || {},
headers: { 'x-router': 'openapi.kugou.com', 'kg-tid': '255' },
});
};
+9
View File
@@ -0,0 +1,9 @@
// 唱片店
module.exports = (params, useAxios) => {
return useAxios({
url: '/zhuanjidata/v3/album_shop_v2/get_classify_data',
method: 'GET',
encryptType: 'android',
cookie: params?.cookie || {},
});
};
+18
View File
@@ -0,0 +1,18 @@
// 专辑音乐列表
module.exports = (params, useAxios) => {
const dataMap = {
album_id: params.id,
is_buy: params?.is_buy || '',
page: params?.page || 1,
pagesize: params?.pagesize || 30,
};
return useAxios({
url: '/v1/album_audio/lite',
method: 'POST',
data: dataMap,
encryptType: 'android',
cookie: params?.cookie || {},
headers: { 'x-router': 'openapi.kugou.com', 'kg-tid': '255' },
});
}
+21
View File
@@ -0,0 +1,21 @@
// 获取歌手专辑
module.exports = (params, useAxios) => {
const dataMap = {
author_id: params.id,
pagesize: params?.pagesize || 30,
page: params?.page || 1,
sort: params?.sort === 'hot' ? 3 : 1, // 3:最热,1:最新
category: 1,
area_code: 'all',
};
return useAxios({
url: '/kmr/v1/author/albums',
method: 'POST',
data: dataMap,
encryptType: 'android',
cookie: params?.cookie || {},
headers: { 'x-router': 'openapi.kugou.com', 'kg-tid': 36 },
});
};
+29
View File
@@ -0,0 +1,29 @@
const { cryptoMd5, signParamsKey, clientver, appid } = require('../util');
// 获取歌手单曲
module.exports = (params, useAxios) => {
const clienttime = Math.floor(new Date().getTime() / 1000);
const mid = params?.cookie?.KUGOU_API_MID;
const dataMap = {
appid,
clientver,
mid,
clienttime,
key: signParamsKey(clienttime),
author_id: params.id,
pagesize: params?.pagesize || 30,
page: params?.page || 1,
sort: params?.sort === 'hot' ? 1 : 2, // 1:最热,2:最新
area_code: 'all',
};
return useAxios({
baseURL: 'https://openapi.kugou.com',
url: '/kmr/v1/audio_group/author',
method: 'POST',
data: dataMap,
encryptType: 'android',
cookie: params?.cookie || {},
headers: { 'x-router': 'openapi.kugou.com', 'kg-tid': 220 },
});
};
+11
View File
@@ -0,0 +1,11 @@
// 歌手详情
module.exports = (params, useAxios) => {
return useAxios({
url: '/kmr/v3/author',
method: 'POST',
data: { author_id: params.id },
encryptType: 'android',
cookie: params?.cookie || {},
headers: { 'x-router': 'openapi.kugou.com', 'kg-tid': 36 },
});
};
+31
View File
@@ -0,0 +1,31 @@
const { cryptoAesEncrypt, rsaEncrypt2 } = require('../util');
// 关注歌手
module.exports = (params, useAxios) => {
const singerid = Number(params.id);
const token = params?.token || params.cookie?.token || '';
const userid = Number(params?.userid || params?.cookie?.userid || 0);
const clienttime = Math.floor(Date.now() / 1000);
const encrypt = cryptoAesEncrypt({ singerid, token });
const paramsMap = {
plat: 0,
userid,
singerid,
source: 7,
p: rsaEncrypt2({ clienttime, key: encrypt.key }),
params: encrypt.str
}
return useAxios({
url: '/followservice/v3/follow_singer',
method: 'post',
data: paramsMap,
params: {clienttime},
encryptType: 'android',
cookie: params?.cookie || {},
});
};
@@ -0,0 +1,21 @@
// 获取关注歌手新歌
module.exports = (params, useAxios) => {
const last_album_id = params.last_album_id || 0;
const page_size = params.pagesize || 30;
const opt_sort = params.opt_sort === 2 ? 2 : 1;
const paramsMap = {
last_album_id,
page_size,
opt_sort,
};
return useAxios({
url: '/feed/v1/follow/newsong_album_list',
method: 'post',
data: { last_album_id },
params: paramsMap,
encryptType: 'android',
cookie: params?.cookie || {},
});
};
+11
View File
@@ -0,0 +1,11 @@
// 歌手荣誉详情
module.exports = (params, useAxios) => {
return useAxios({
baseURL: 'http://h5activity.kugou.com',
url: '/v1/query_singer_honour_detail',
method: 'POST',
params: { singer_id: params.id, pagesize: params?.pagesize || 30, page: params?.page || 1 },
encryptType: 'android',
cookie: params?.cookie || {},
});
};
+18
View File
@@ -0,0 +1,18 @@
// 歌手列表
module.exports = (params, useAxios) => {
const paramsMap = {
musician: Number(params.musician || 0),
sextype: params.sextypes || 0,
showtype: 2,
type: params.type || 0,
hotsize: Number(params.hotsize || 30)
};
return useAxios({
url: '/ocean/v6/singer/list',
method: 'GET',
params: paramsMap,
encryptType: 'android',
cookie: params?.cookie || {},
});
};
+28
View File
@@ -0,0 +1,28 @@
const { cryptoAesEncrypt, rsaEncrypt2 } = require('../util');
// 取消关注歌手
module.exports = (params, useAxios) => {
const singerid = params.id;
const token = params?.token || params.cookie?.token || '';
const userid = params?.userid || params?.cookie?.userid || 0;
const clienttime = Math.floor(Date.now() / 1000);
const encrypt = cryptoAesEncrypt({ singerid, token });
const paramsMap = {
plat: 0,
userid,
singerid,
source: 7,
p: rsaEncrypt2({ clienttime, key: encrypt.key }),
params: encrypt.str
}
return useAxios({
url: '/followservice/v3/unfollow_singer',
method: 'post',
data: paramsMap,
encryptType: 'android',
cookie: params?.cookie || {},
});
};
+22
View File
@@ -0,0 +1,22 @@
// 获取歌手mv
const tag_idx = { official: 18, live: 20, fan: 23, artist: 42419, all: '' };
module.exports = (params, useAxios) => {
const paramsMap = {
author_id: params.id,
is_fanmade: '',
tag_idx: tag_idx[params?.tag || 'all'] || '', // 18:官方版本,20:现场版本,23:饭制版本,42419:歌手发布
pagesize: params.pagesize || 30,
page: params.page || 1,
};
return useAxios({
baseURL: 'https://openapicdn.kugou.com',
url: '/kmr/v1/author/videos',
method: 'GET',
params: paramsMap,
encryptType: 'android',
cookie: params?.cookie || {},
});
};
+32
View File
@@ -0,0 +1,32 @@
const { signParamsKey, appid, clientver } = require('../util');
module.exports = (params, useAxios) => {
const dateTime = Date.now();
const data = (params?.hash || '').split(',').map((s) => ({ hash: s, audio_id: 0 }));
const dfid = params?.cookie?.dfid || params?.dfid || '-';
const userid = params?.cookie?.userid || params?.userid || 0;
const token = params?.cookie?.token || params?.token || 0;
const dataMap = {
appid,
clienttime: dateTime,
clientver,
data,
dfid,
key: signParamsKey(dateTime),
mid: params?.cookie?.KUGOU_API_MID,
};
if (token) dataMap['token'] = token;
if (userid) dataMap['userid'] = userid;
return useAxios({
baseURL: 'http://kmr.service.kugou.com',
url: '/v1/audio/audio',
method: 'POST',
data: dataMap,
encryptType: 'android',
cookie: params?.cookie || {},
headers: { 'x-router': 'kmr.service.kugou.com', 'Content-Type': 'application/json' },
});
};
@@ -0,0 +1,34 @@
const { cryptoMd5, appid } = require('../util');
module.exports = (params, useAxios) => {
const dataMap = {
isteen: 0,
mixId: Number(params.mixId) || 0,
usemkv: 1,
platform: 2,
fileName: params.fileName || '',
hash: params.hash,
version: 12375,
appid,
}
const str = '*s&iN#G70*';
const paramsString = Object.keys(dataMap)
.sort()
.map((key) => `${key}=${typeof dataMap[key] === 'object' ? JSON.stringify(dataMap[key]) : dataMap[key]}`)
.join('&');
dataMap['sign'] = cryptoMd5(`${paramsString}${str}`).substring(8, 24);
return useAxios({
baseURL: 'https://nsongacsing.kugou.com',
url: '/sing7/accompanywan/json/v2/cdn/optimal_matching_accompany_2_listen.do',
params: dataMap,
method: 'get',
encryptType: 'android',
cookie: params?.cookie || {},
clearDefaultParams: true,
notSignature: true,
});
};
+34
View File
@@ -0,0 +1,34 @@
const { cryptoMd5, appid } = require('../util');
module.exports = (params, useAxios) => {
const dataMap = {
isteen: 0,
songId: Number(params.songId),
usemkv: 1,
platform: 2,
singerName: params.singerName,
songHash: params.songHash,
version: 12375,
appid,
}
const str = '*s&iN#G70*';
const paramsString = Object.keys(dataMap)
.sort()
.map((key) => `${key}=${typeof dataMap[key] === 'object' ? JSON.stringify(dataMap[key]) : dataMap[key]}`)
.join('&');
dataMap['sign'] = cryptoMd5(`${paramsString}${str}`).substring(8, 24);
return useAxios({
baseURL: 'https://acsing.service.kugou.com',
url: '/sing7/listenguide/json/v2/cdn/listenguide/get_total_opus_num_v02.do',
params: dataMap,
method: 'get',
encryptType: 'android',
cookie: params?.cookie || {},
clearDefaultParams: true,
notSignature: true,
});
};
+20
View File
@@ -0,0 +1,20 @@
// 听歌识曲
module.exports = (params, useAxios) => {
const paramsMap = {
fpid: Date.now(),
area_code: 1,
include_unpublish: 1,
useid: params?.userid || params?.cookie?.userid || 0,
multi_result: 1,
};
return useAxios({
url: '/fingerprint.service/v1/music_trackid_mulit',
encryptType: 'android',
method: 'POST',
data: params.data,
params: paramsMap,
cookie: params?.cookie || {},
headers: { 'content-type': 'application/octet-stream', 'user-agent': 'KuGou/11490 (Android)' },
});
};
+46
View File
@@ -0,0 +1,46 @@
const { cryptoMd5 } = require('../util');
const sortType = { all: 1, hot: 2, new: 3 };
// https://listkmrp3cdnretry.kugou.com/v3/album_audio/related
module.exports = (params, useAxios) => {
const show_detail = Number(params.show_detail) === 0;
let dataMap = {
album_audio_id: Number(params.album_audio_id),
appid: 1005,
area_code: 1,
clientver: 12329,
};
if (!show_detail) {
dataMap = {
...dataMap,
page: params.page || 1,
pagesize: params.pagesize || 30,
show_input: 1,
show_type: params.show_type || 0,
sort: sortType[params.sort] || 1,
type: params.type || 0,
};
}
dataMap['version'] = 1;
const str = 'OIlwieks28dk2k092lksi2UIkp';
const paramsString = Object.keys(dataMap)
.sort()
.map((key) => `${key}=${typeof dataMap[key] === 'object' ? JSON.stringify(dataMap[key]) : dataMap[key]}`)
.join('');
dataMap['signature'] = cryptoMd5(`${str}${paramsString}${str}`);
return useAxios({
baseURL: 'https://listkmrp3cdnretry.kugou.com',
url: !show_detail ? '/v3/album_audio/related' : '/v2/audio_related/total',
params: dataMap,
method: 'get',
encryptType: 'android',
cookie: params?.cookie || {},
clearDefaultParams: true,
});
};
+49
View File
@@ -0,0 +1,49 @@
const { appid, clientver, signParamsKey } = require('../util');
module.exports = (params, useAxios) => {
const userid = params?.cookie?.userid || params?.userid || 0;
const vip_type = params?.cookie?.vip_type || params?.vipType || 0;
const dateTime = Date.now();
const personal_recommend = {
userid,
appid,
playlist_ver: 2,
clienttime: dateTime,
mid: params?.cookie?.KUGOU_API_MID,
new_sync_point: dateTime,
module_id: 1,
action: 'login',
vip_type,
vip_flags: 3,
recommend_source_locked: 0,
song_pool_id: Number(params?.song_pool_id || 0),
callerid: 0,
m_type: 1,
kguid: userid,
platform: 'ios',
area_code: 1,
fakem: 'ca981cfc583a4c37f28d2d49000013c16a0a',
clientver: 11850,
mode: params?.mode || 'normal',
active_swtich: 'on',
key: signParamsKey(dateTime),
};
const dataMap = {
behaviors: [],
abtest: {
abtest: { shuashua: { commentcard: 2 } },
},
personal_recommend_params: personal_recommend,
};
return useAxios({
url: '/genesisapi/v1/newepoch_song_rec/feed',
data: dataMap,
params: { sort_type: 1, platform: 'ios', page: 1, content_ver: 4, clientver: 11850 },
method: 'post',
encryptType: 'android',
cookie: params?.cookie || {},
});
};
+17
View File
@@ -0,0 +1,17 @@
// 手机验证码发送
module.exports = (params, useAxios) => {
const dataMap = {
businessid: 5,
mobile: `${params?.mobile}`,
plat: 3,
};
return useAxios({
baseURL: 'http://login.user.kugou.com',
url: '/v7/send_mobile_code',
method: 'POST',
data: dataMap,
encryptType: 'android',
cookie: {mid: params?.cookie?.KUGOU_API_MID},
});
};
+21
View File
@@ -0,0 +1,21 @@
// 歌曲评论
module.exports = (params, useAxios) => {
const paramsMap = {
childrenid: params.id,
need_show_image: 1,
p: params.page || 1,
pagesize: params.pagesize || 30,
show_classify: params.show_classify || 1,
show_hotword_list: params.show_hotword_list || 1,
code: '94f1792ced1df89aa68a7939eaf2efca',
}
return useAxios({
url: '/m.comment.service/v1/cmtlist',
encryptType: 'android',
method: 'POST',
params: paramsMap,
cookie: params?.cookie || {},
});
};
+23
View File
@@ -0,0 +1,23 @@
// 歌曲评论数
module.exports = (params, useAxios) => {
const paramsMap = {
r: 'comments/getcommentsnum',
code: 'fc4be23b4e972707f36b8a828a93ba8a',
};
if (params && params.hash) {
paramsMap.hash = params.hash;
} else if (params && params.special_id) {
paramsMap.childrenid = params.special_id;
}
return useAxios({
url: '/index.php',
encryptType: 'web',
method: 'GET',
params: paramsMap,
cookie: params?.cookie || {},
headers: { 'x-router': 'sum.comment.service.kugou.com' },
});
};
+59
View File
@@ -0,0 +1,59 @@
// 楼层评论
const songCode = 'fc4be23b4e972707f36b8a828a93ba8a';
const playlistCode = 'ca53b96fe5a1d9c22d71c8f522ef7c4f';
const albumCode = '94f1792ced1df89aa68a7939eaf2efca';
const isNonEmpty = (value) => {
if (value == null) return false;
const text = `${value}`.trim();
return text !== '' && text !== 'null' && text !== 'undefined';
};
const resolveFloorCommentRequestConfig = (params = {}) => {
const resourceType = `${params.resource_type || params.resourceType || ''}`.toLowerCase();
const code = isNonEmpty(params.code)
? `${params.code}`
: resourceType === 'playlist'
? playlistCode
: resourceType === 'album'
? albumCode
: songCode;
const useServiceEndpoint =
resourceType === 'playlist' ||
resourceType === 'album' ||
code === playlistCode ||
code === albumCode;
const paramsMap = {
childrenid: params.special_id,
need_show_image: 1,
p: params.page || 1,
pagesize: params.pagesize || 30,
show_classify: params.show_classify ?? 1,
show_hotword_list: params.show_hotword_list ?? 1,
code,
tid: params.tid,
};
if (isNonEmpty(params.mixsongid)) {
paramsMap.mixsongid = params.mixsongid;
}
return {
url: useServiceEndpoint
? '/m.comment.service/v1/hot_replylist'
: '/mcomment/v1/hot_replylist',
encryptType: 'android',
method: 'POST',
params: paramsMap,
cookie: params?.cookie || {},
};
};
const commentFloor = (params, useAxios) => {
return useAxios(resolveFloorCommentRequestConfig(params));
};
module.exports = commentFloor;
module.exports.resolveFloorCommentRequestConfig = resolveFloorCommentRequestConfig;
+22
View File
@@ -0,0 +1,22 @@
// 歌曲评论
module.exports = (params, useAxios) => {
const paramsMap = {
mixsongid: params.mixsongid,
need_show_image: 1,
p: params.page || 1,
pagesize: params.pagesize || 30,
show_classify: params.show_classify || 1,
show_hotword_list: params.show_hotword_list || 1,
extdata: '0',
code: 'fc4be23b4e972707f36b8a828a93ba8a'
}
return useAxios({
url: '/mcomment/v1/cmtlist',
encryptType: 'android',
method: 'POST',
params: paramsMap,
cookie: params?.cookie || {},
});
};
@@ -0,0 +1,22 @@
// 歌曲评论-根据分类获取评论
module.exports = (params, useAxios) => {
const paramsMap = {
mixsongid: params.mixsongid,
need_show_image: 1,
page: params.page || 1,
pagesize: params.pagesize || 30,
type_id: params.type_id,
extdata: '0',
code: 'fc4be23b4e972707f36b8a828a93ba8a',
sort_method: Number(params.sort) === 2 ? params.sort : 1
}
return useAxios({
url: '/mcomment/v1/cmt_classify_list',
encryptType: 'android',
method: 'POST',
params: paramsMap,
cookie: params?.cookie || {},
});
};
@@ -0,0 +1,21 @@
// 歌曲评论-更具热词获取评论
module.exports = (params, useAxios) => {
const paramsMap = {
mixsongid: params.mixsongid,
need_show_image: 1,
p: params.page || 1,
pagesize: params.pagesize || 30,
hot_word: params.hot_word,
extdata: '0',
code: 'fc4be23b4e972707f36b8a828a93ba8a',
}
return useAxios({
url: '/mcomment/v1/get_hot_word',
encryptType: 'android',
method: 'POST',
params: paramsMap,
cookie: params?.cookie || {},
});
};
+23
View File
@@ -0,0 +1,23 @@
// 歌曲评论
module.exports = (params, useAxios) => {
const paramsMap = {
childrenid: params.id,
need_show_image: 1,
p: params.page || 1,
pagesize: params.pagesize || 30,
show_classify: params.show_classify || 1,
show_hotword_list: params.show_hotword_list || 1,
code: 'ca53b96fe5a1d9c22d71c8f522ef7c4f',
content_type: 0,
tag: 5
}
return useAxios({
url: '/m.comment.service/v1/cmtlist',
encryptType: 'android',
method: 'POST',
params: paramsMap,
cookie: params?.cookie || {},
});
};
+13
View File
@@ -0,0 +1,13 @@
module.exports = (params, useAxios) => {
return useAxios({
baseURL: 'https://acsing.service.kugou.com',
url: '/sing7/relation/json/v3/friend_rec_by_using_song_list',
encryptType: 'android',
method: 'POST',
data: {list: [{user_id: 853927886, mixsong_ids: [290083753,251724346,571554587,250126644,208831644,40328518,250504076,581706850,318347675,585258401,288481998,407414475,28239430,280584633,291957521,64556644,243149863,488725103,32114153,39951172,29019580,40397606,327507651,32029382,32218359,340353127,276448762,177071956,100031397,249251602]}]},
params: { channel: 130, isteen: 0, platform: 2, usemkv: 1 },
cookie: params?.cookie || {},
headers: { pid: 126556797 },
});
};
+20
View File
@@ -0,0 +1,20 @@
// mode list ,song
module.exports = (params, useAxios) => {
const paramsMap = {
mode: params.mode || 'list',
platform: params.platform || 'ios'
};
if (params.history_name) paramsMap['history_name'] = params.history_name;
if (params.date) paramsMap['date'] = params.date;
return useAxios({
url: '/everyday/api/v1/get_history',
encryptType: 'android',
method: 'POST',
params: paramsMap,
cookie: params?.cookie || {},
headers: { 'x-router': 'everydayrec.service.kugou.com' },
});
};
@@ -0,0 +1,10 @@
module.exports = (params, useAxios) => {
return useAxios({
url: '/everyday_song_recommend',
encryptType: 'android',
method: 'POST',
params: { platform: params.platform || 'ios' },
cookie: params?.cookie || {},
headers: { 'x-router': 'everydayrec.service.kugou.com' },
});
};
@@ -0,0 +1,14 @@
module.exports = (params, useAxios) => {
const dataMap = {
platform: params.platform || 'ios'
};
return useAxios({
url: '/everydayrec.service/everyday_style_recommend',
encryptType: 'android',
method: 'POST',
data: {},
params: { tagids: params.tagids ?? ''},
cookie: params?.cookie || {},
});
};
+9
View File
@@ -0,0 +1,9 @@
module.exports = (params, useAxios) => {
return useAxios({
url: `/count/v1/audio/mget_collect`,
method: 'GET',
encryptType: 'android',
cookie: params?.cookie || {},
params: { mixsongids: params.mixsongids },
});
};
+25
View File
@@ -0,0 +1,25 @@
const { appid, clientver, cryptoMd5, signParamsKey } = require('../util');
module.exports = (params, useAxios) => {
const dateTime = Date.now();
const userid = params?.cookie?.userid || params?.userid || 0;
const dataMap = {
kguid: userid,
clienttime: dateTime,
mid: params?.cookie?.KUGOU_API_MID,
platform: 'android',
clientver,
uid: userid,
get_tracker: 1,
key: signParamsKey(dateTime),
appid,
};
return useAxios({
url: '/v1/class_fm_song',
encryptType: 'android',
method: 'POST',
data: dataMap,
cookie: params?.cookie || {},
headers: { 'x-router': 'fm.service.kugou.com' },
});
};
+30
View File
@@ -0,0 +1,30 @@
const { appid, clientver, signParamsKey } = require('../util');
module.exports = (params, useAxios) => {
const dateTime = Date.now();
const dfid = params?.cookie?.dfid || params?.dfid || '-';
const userid = params?.cookie?.userid || params?.userid;
const token = params?.cookie?.token || params?.token;
const fmData = (params?.fmid || '').split(',').map((s) => ({ fields: 'imgUrl100,imgUrl50', fmid: s, fmtype: 2 }));
const dataMap = {
appid,
clienttime: dateTime,
clientver,
data: fmData,
dfid,
key: signParamsKey(dateTime),
mid: params?.cookie?.KUGOU_API_MID,
};
if (userid) dataMap['userid'] = userid;
if (token) dataMap['token'] = token;
return useAxios({
url: '/v1/fm_info',
encryptType: 'android',
method: 'POST',
data: dataMap,
cookie: params?.cookie || {},
headers: { 'x-router': 'fm.service.kugou.com', 'Content-Type': 'application/json' },
});
};
+26
View File
@@ -0,0 +1,26 @@
const { appid, clientver, signParamsKey } = require('../util');
module.exports = (params, useAxios) => {
const dateTime = Date.now();
const dfid = params?.cookie?.dfid || params?.dfid || '-';
const dataMap = {
appid,
clientver,
clienttime: dateTime,
mid: params?.cookie?.KUGOU_API_MID,
key: signParamsKey(dateTime),
rcmdsongcount: 1,
level: 0,
area_code: 1,
get_tracker: 1,
uid: 0,
};
return useAxios({
url: '/v1/rcmd_list',
encryptType: 'android',
method: 'POST',
data: dataMap,
cookie: params?.cookie || {},
headers: { 'x-router': 'fm.service.kugou.com' },
});
};
+41
View File
@@ -0,0 +1,41 @@
const { appid, clientver, signParamsKey } = require('../util');
module.exports = (params, useAxios) => {
const dateTime = Date.now();
const userid = params?.cookie?.userid || params?.userid;
const fmData = (params?.fmid || '').split(',').map((s) => ({
fmid: s,
fmtype: params?.type || 2,
offset: params?.offset || -1,
size: params?.size || 20,
singername: s.singername || '',
}));
// fmType 生成
(params?.fmtype || '').split(',').forEach((s, l) => (fmData[l].fmtype = s || fmData[l].fmtype));
(params?.fmoffset || '').split(',').forEach((s, l) => (fmData[l].offset = s || fmData[l].offset));
(params?.fmsize || '').split(',').forEach((s, l) => (fmData[l].size = s || fmData[l].size));
const dataMap = {
appid,
area_code: 1,
clienttime: dateTime,
clientver,
data: fmData,
get_tracker: 1,
key: signParamsKey(dateTime),
mid: params?.cookie?.KUGOU_API_MID,
uid: userid,
};
return useAxios({
url: '/v1/app_song_list_offset',
encryptType: 'android',
method: 'POST',
data: dataMap,
cookie: params?.cookie || {},
headers: { 'x-router': 'fm.service.kugou.com', 'Content-Type': 'application/json' },
});
};
+24
View File
@@ -0,0 +1,24 @@
// 获取音效详情
const { signParamsKey } = require('../util');
module.exports = (params, useAxios) => {
const dataMap = {
model_id: Number(params.model_id || 0),
req_src: 'collection',
earphone_vip: 1,
sound_ver: 2,
key: signParamsKey(Date.now()),
page: params.page || 1,
pagesize: params.pagesize || 30,
};
return useAxios({
baseURL: 'http://mobileservice.kugou.com',
url: '/api/v5/earphone/get_model_info',
encryptType: 'android',
method: 'GET',
params: dataMap,
cookie: params?.cookie || {},
});
};
+26
View File
@@ -0,0 +1,26 @@
// 获取社区音效
module.exports = (params, useAxios) => {
const dataMap = {
super_vip: 1,
sound_ver: 2,
page: params.page || 1,
pagesize: params.pagesize || 30,
apiver: 3,
classify: '2,3',
plat: 2,
privilege: 1,
sort: 2
};
return useAxios({
url: '/ocean/v6/sound/list',
encryptType: 'android',
method: 'GET',
params: dataMap,
cookie: params?.cookie || {},
});
};
+22
View File
@@ -0,0 +1,22 @@
// 获取验证码数据
module.exports = (params, useAxios) => {
const userid = Number(params?.userid || params?.cookie?.userid || '0');
const dataMap = {
eventid: params?.eventid,
userid,
platid: params?.platid || 2,
rtype: 1,
wasm: 1,
i: '',
sid: '',
edt: '',
};
return useAxios({
url: '/verifyservice/v3/get_verify_info',
encryptType: 'android',
method: 'POST',
data: dataMap,
cookie: params?.cookie || {},
});
};
+43
View File
@@ -0,0 +1,43 @@
const { signatureAndroidParams, appid, clientver } = require('../util');
module.exports = (params, useAxios) => {
const data = (params?.hash || '').split(',').map((s) => ({ album_id: 0, hash: s, album_audio_id: 0 }));
(params?.album_id || '').split(',').forEach((s, index) => {
if (index <= data.length - 1) {
data[index]['album_id'] = s || 0;
}
});
(params?.album_audio_id || '').split(',').forEach((s, index) => {
if (index <= data.length - 1) {
data[index]['album_audio_id'] = s || 0;
}
});
const paramsMap = {
album_image_type: '-3',
appid,
clientver,
author_image_type: '3,4,5',
count: params?.count || 5,
data,
isCdn: 1,
publish_time: 1,
};
const query = Object.keys(paramsMap)
.sort()
.map((s) => `${s}=${encodeURIComponent(typeof paramsMap[s] === 'object' ? JSON.stringify(paramsMap[s]) : paramsMap[s])}`);
const signature = signatureAndroidParams(paramsMap);
return useAxios({
baseURL: 'https://expendablekmr.kugou.com',
url: `/container/v2/image?${query.join('&')}`,
method: 'GET',
encryptType: 'android',
params: { signature },
cookie: params?.cookie || {},
notSign: true,
clearDefaultParams: true,
});
};
+47
View File
@@ -0,0 +1,47 @@
const { signatureAndroidParams, appid, clientver } = require('../util');
module.exports = (params, useAxios) => {
const data = (params?.hash || '').split(',').map((s) => ({ audio_id: 0, hash: s, album_audio_id: 0, filename: '' }));
(params?.audio_id || '').split(',').forEach((s, index) => {
if (index <= data.length - 1) {
data[index]['audio_id'] = s || 0;
}
});
(params?.album_audio_id || '').split(',').forEach((s, index) => {
if (index <= data.length - 1) {
data[index]['album_audio_id'] = s || 0;
}
});
(params?.filename || '').split(',').forEach((s, index) => {
if (index <= data.length - 1) {
data[index]['filename'] = s;
}
});
const paramsMap = {
appid,
clientver,
count: params?.count || 5,
data,
isCdn: 1,
publish_time: 1,
show_authors: 1,
};
const query = Object.keys(paramsMap)
.sort()
.map((s) => `${s}=${encodeURIComponent(typeof paramsMap[s] === 'object' ? JSON.stringify(paramsMap[s]) : paramsMap[s])}`);
const signature = signatureAndroidParams(paramsMap);
return useAxios({
baseURL: 'https://expendablekmr.kugou.com',
url: `/v2/author_image/audio?${query.join('&')}`,
method: 'GET',
encryptType: 'android',
params: { signature },
cookie: params?.cookie || {},
notSign: true,
clearDefaultParams: true,
});
};
+21
View File
@@ -0,0 +1,21 @@
// 根据 ip id 获取相对应的 歌曲/专辑/视频/歌手
module.exports = (params, useAxios) => {
const dataMap = {
is_publish: 1,
ip_id: params?.id,
sort: 3,
page: params?.page || 1,
pagesize: params?.pagesize || 30,
query: 1,
};
const type = ['audios', 'albums', 'videos', 'author_list'].includes(params?.type) ? params.type : 'audios';
return useAxios({
url: `/openapi/v1/ip/${type}`,
encryptType: 'android',
method: 'POST',
data: dataMap,
cookie: params?.cookie || {},
});
};
+17
View File
@@ -0,0 +1,17 @@
// 获取ip详情
module.exports = (params, useAxios) => {
const data = (params?.id || '').split(',').map((s) => ({ ip_id: s }));
const dataMap = {
data,
is_publish: 1,
};
return useAxios({
url: '/openapi/v1/ip',
encryptType: 'android',
method: 'POST',
data: dataMap,
cookie: params?.cookie || {},
});
};
+16
View File
@@ -0,0 +1,16 @@
// 根据 ip 获取相对于歌单
module.exports = (params, useAxios) => {
const dataMap = {
ip: params?.id,
page: params?.page || 1,
pagesize: params?.pagesize || 30,
};
return useAxios({
url: '/ocean/v6/pubsongs/list_info_for_ip',
encryptType: 'android',
method: 'POST',
params: dataMap,
cookie: params?.cookie || {},
});
};
+31
View File
@@ -0,0 +1,31 @@
// ip 专区
module.exports = (params, useAxios) => {
return new Promise((resolve, reject) => {
useAxios({
url: `/v1/zone/index`,
encryptType: 'android',
method: 'GET',
cookie: params?.cookie || {},
headers: { 'x-router': 'yuekucategory.kugou.com' },
})
.then((resp) => {
if (resp.body.status == 1) {
if (resp.body.data?.list) {
const list = Array.isArray(resp.body.data.list) ? resp.body.data?.list : [];
for (let index = 0; index < list.length; index += 1) {
if (list[index].special_link) {
const urls = new URLSearchParams(list[index].special_link);
if (urls.has('path')) {
const pathUrls = new URLSearchParams(urls.get('path') || '');
list[index]['ip_id'] = Number(pathUrls.get('ip_id') || '');
}
}
}
resp.body.data.list = list;
}
}
resolve(resp);
})
.catch((e) => reject(e));
});
};
+16
View File
@@ -0,0 +1,16 @@
// 获取今日推荐信息,有可能为空
module.exports = (params, useAxios) => {
const dataMap = {
id: params?.id,
share: 0,
};
return useAxios({
url: '/v1/zone/home',
encryptType: 'android',
method: 'GET',
params: dataMap,
cookie: params?.cookie || {},
headers: { 'x-router': 'yuekucategory.kugou.com' },
});
};
+20
View File
@@ -0,0 +1,20 @@
const { appid, clientver } = require('../util');
// 根据 album_audio_id/MixSongID 获取歌曲 相对应的 mv
module.exports = (params, useAxios) => {
const resource = (params?.album_audio_id || '').split(',').map((s) => ({ album_audio_id: s }));
const paramsMap = {
data: resource,
fields: params.fields || '',
};
return useAxios({
url: '/kmr/v1/audio/mv',
method: 'POST',
data: paramsMap,
encryptType: 'android',
cookie: params?.cookie || {},
headers: {'x-router': 'openapi.kugou.com', 'KG-TID': 38},
});
};
+18
View File
@@ -0,0 +1,18 @@
// 根据 album_audio_id/MixSongID 获取歌曲 相对应的 歌手/专辑/歌曲信息
module.exports = (params, useAxios) => {
const resource = (params?.album_audio_id || '').split(',').map((s) => ({ entity_id: Number(s) }));
const paramsMap = {
data: resource,
fields: params.fields || 'base',
};
return useAxios({
url: '/kmr/v2/audio',
method: 'POST',
data: paramsMap,
encryptType: 'android',
cookie: params?.cookie || {},
headers: {'x-router': 'openapi.kugou.com', 'KG-TID': 238},
});
};
@@ -0,0 +1,21 @@
// 获取继续播放信息
module.exports = (params, useAxios) => {
const token = params?.token || params.cookie?.token || '';
const userid = Number(params?.userid || params?.cookie?.userid || 0);
const paramsMap = {
area_code: '1',
sources: ['pc', 'mobile', 'tv', 'car'],
userid,
ret_info: 1,
token,
pagesize: Number(params.pagesize || 30)
}
return useAxios({
url: '/playque/devque/v1/get_latest_songs',
encryptType: 'android',
method: 'POST',
data: paramsMap,
cookie: params?.cookie || {},
});
};
+51
View File
@@ -0,0 +1,51 @@
const { cryptoAesDecrypt, cryptoAesEncrypt, cryptoRSAEncrypt } = require('../util');
module.exports = (params, useAxios) => {
const dateNow = Date.now();
const encrypt = cryptoAesEncrypt({ pwd: params?.password || '', code: params?.code || '', clienttime_ms: dateNow });
const dataMap = {
plat: 1,
support_multi: 1,
clienttime_ms: dateNow,
t1: '562a6f12a6e803453647d16a08f5f0c2ff7eee692cba2ab74cc4c8ab47fc467561a7c6b586ce7dc46a63613b246737c03a1dc8f8d162d8ce1d2c71893d19f1d4b797685a4c6d3d81341cbde65e488c4829a9b4d42ef2df470eb102979fa5adcdd9b4eecfea8b909ff7599abeb49867640f10c3c70fc444effca9d15db44a9a6c907731e2bb0f22cd9b3536380169995693e5f0e2424e3378097d3813186e3fe96bbe7023808a0981b4e2b6135a76faac',
t2: '31c4daf4cf480169ccea1cb7d4a209295865a9d2b788510301694db229b87807469ea0d41b4d4b9173c2151da7294aeebfc9738df154bbdf11a4e117bb5dff6a3af8ce5ce333e681c1f29a44038f27567d58992eb81283e080778ac77db1400fdf49b7cf7e26be2e5af4da7830cc3be4',
t3: 'MCwwLDAsMCwwLDAsMCwwLDA=',
username: params?.username,
params: encrypt.str,
pk: cryptoRSAEncrypt({ 'clienttime_ms': dateNow, key: encrypt.key }).toUpperCase(),
};
return new Promise((resolve, reject) => {
useAxios({
url: '/v9/login_by_pwd',
method: 'POST',
data: dataMap,
encryptType: 'android',
cookie: params?.cookie || {},
headers: { 'x-router': 'login.user.kugou.com' },
})
.then((res) => {
const { body } = res;
if (body?.status && body?.status === 1) {
if (body?.data?.secu_params) {
const getToken = cryptoAesDecrypt(body.data.secu_params, encrypt.key);
if (typeof getToken === 'object') {
res.body.data = { ...body.data, ...getToken };
Object.keys(getToken).forEach((key) => res.cookie.push(`${key}=${getToken[key]}`));
} else {
res.body.data['token'] = getToken;
res.cookie.push(`token=${getToken}`);
}
res.cookie.push(`userid=${res.body.data?.userid || 0}`);
res.cookie.push(`vip_type=${res.body.data?.vip_type || 0}`);
res.cookie.push(`vip_token=${res.body.data?.vip_token || ''}`);
resolve(res);
return;
}
}
resolve(res);
})
.catch((e) => reject(e));
});
};
+76
View File
@@ -0,0 +1,76 @@
// 手机登录
const { cryptoAesDecrypt, cryptoAesEncrypt, cryptoRSAEncrypt, signParamsKey, isLite, randomString } = require('../util');
let liteT2Key = 'fd14b35e3f81af3817a20ae7adae7020';
let liteT2Iv = '17a20ae7adae7020';
let liteT1Key = '5e4ef500e9597fe004bd09a46d8add98';
let liteT1Iv = '04bd09a46d8add98';
module.exports = (params, useAxios) => {
const dateTime = Date.now();
const encrypt = cryptoAesEncrypt({ mobile: params?.mobile || '', code: params?.code || '' });
const mobile = params?.mobile && `${params.mobile.toString().substring(0, 2)}*****${params.mobile.toString().substring(10, 11)}`;
const dfid = params?.cookie?.dfid ?? randomString(24);
const t2 = cryptoAesEncrypt(
`${params.cookie?.KUGOU_API_GUID}|0f607264fc6318a92b9e13c65db7cd3c|${params.cookie?.KUGOU_API_MAC}|${params.cookie?.KUGOU_API_DEV}|${dateTime}`,
{ key: liteT2Key, iv: liteT2Iv }
);
const t1 = cryptoAesEncrypt(`|${dateTime}`, { key: liteT1Key, iv: liteT1Iv });
let dataMap = {
plat: 1,
support_multi: 1,
t1: isLite ? t1 : 0,
t2: isLite ? t2 : 0,
clienttime_ms: dateTime,
mobile,
key: signParamsKey(dateTime),
pk: cryptoRSAEncrypt({ 'clienttime_ms': dateTime, key: encrypt.key }).toUpperCase(),
params: encrypt.str,
};
if (params?.userid) dataMap['userid'] = params.userid;
if (isLite) {
dataMap['dfid'] = dfid;
dataMap['dev'] = params.cookie?.KUGOU_API_DEV;
dataMap['gitversion'] = '5f0b7c4';
} else {
dataMap['t3'] = 'MCwwLDAsMCwwLDAsMCwwLDA=';
}
return new Promise((resolve, reject) => {
useAxios({
baseURL: 'https://loginserviceretry.kugou.com',
url: `/v7/login_by_verifycode`,
method: 'POST',
data: dataMap,
encryptType: 'android',
headers: {
'support-calm': '1',
'User-Agent': 'Android16-1070-11440-130-0-LOGIN-wifi',
},
cookie: params?.cookie || {},
})
.then((res) => {
const { body } = res;
if (body?.status && body?.status === 1) {
if (body?.data?.secu_params) {
const getToken = cryptoAesDecrypt(body.data.secu_params, encrypt.key);
if (typeof getToken === 'object') {
res.body.data = { ...body.data, ...getToken };
Object.keys(getToken).forEach((key) => res.cookie.push(`${key}=${getToken[key]}`));
} else {
res.body.data['token'] = getToken;
}
}
res.cookie.push(`t1=${res.body.data['t1']}`);
res.cookie.push(`token=${res.body.data['token']}`);
res.cookie.push(`userid=${res.body.data?.userid || 0}`);
res.cookie.push(`vip_type=${res.body.data?.vip_type || 0}`);
res.cookie.push(`vip_token=${res.body.data?.vip_token || ''}`);
}
resolve(res);
})
.catch((e) => reject(e));
});
};
+22
View File
@@ -0,0 +1,22 @@
const { cryptoAesEncrypt, cryptoRSAEncrypt } = require('../util');
module.exports = (params, useAxios) => {
const clienttime_ms = parseInt(new Date().getTime());
const encrypt = cryptoAesEncrypt({ token: params.token || params.cookie?.token });
const dataMap = {
plat: 1,
userid: params.userid || params.cookie?.userid || 0,
clienttime_ms,
pk: cryptoRSAEncrypt({ clienttime_ms, key: encrypt.key }).toUpperCase(),
params: encrypt.str,
};
return useAxios({
baseURL: 'https://userinfoservice.kugou.com',
url: '/v2/get_dev',
encryptType: 'android',
method: 'POST',
data: dataMap,
cookie: params?.cookie || {},
});
};
+34
View File
@@ -0,0 +1,34 @@
//登出选定设备
const { cryptoAesEncrypt, cryptoRSAEncrypt, appid, clientver, srcappid,signParamsKey } = require('../util');
module.exports = (params, useAxios) => {
const clienttime_ms = parseInt(new Date().getTime());
const encrypt = cryptoAesEncrypt({ token: params.token || params.cookie?.token });
const mid = calculateMid(params.mid)
const dateTime = Date.now();
const dataMap = {
appid,
clientver,
clienttime: clienttime_ms,
mid: mid,
uuid,
dfid,
plat: 1,
userid,
token: encrypt,
t_mid :guid,
t : dateTime,
t_appid: 3116,
t_clientver: 10597,
srcappid,
signature: signParamsKey(dateTime),
};
return useAxios({
url: '/loginservice/v1/dev_logout',
encryptType: 'android',
method: 'GET',
data: dataMap,
cookie: params?.cookie,
headers: { 'Host':'gateway.kugou.com'}
});
};
+84
View File
@@ -0,0 +1,84 @@
// 开放平台登录
const axios = require('axios');
const { wx_appid, wx_secret, cryptoAesDecrypt, cryptoAesEncrypt, cryptoRSAEncrypt, wx_lite_appid, wx_lite_secret, isLite } = require('../util');
const appid = isLite ? wx_lite_appid : wx_appid;
const secret = isLite ? wx_lite_secret : wx_secret;
let liteT2Key = 'fd14b35e3f81af3817a20ae7adae7020';
let liteT2Iv = '17a20ae7adae7020';
let liteT1Key = '5e4ef500e9597fe004bd09a46d8add98';
let liteT1Iv = '04bd09a46d8add98';
const assetsToken = (code) => {
return axios({
url: 'https://api.weixin.qq.com/sns/oauth2/access_token',
method: 'POST',
params: { secret, appid, code, grant_type: 'authorization_code' },
});
};
module.exports = (params, useAxios) => {
const answer = { status: 500, body: {}, cookie: [] };
return new Promise(async (resolve, reject) => {
try {
const assetsTokenResp = await assetsToken(params?.code || '');
if (assetsTokenResp.data?.access_token && assetsTokenResp.data?.openid) {
const dateNow = Date.now();
const encrypt = cryptoAesEncrypt({ access_token: assetsTokenResp.data.access_token });
const pk = cryptoRSAEncrypt({ 'clienttime_ms': dateNow, key: encrypt.key }).toUpperCase();
const t2 = cryptoAesEncrypt(
`${params.cookie?.KUGOU_API_GUID}|0f607264fc6318a92b9e13c65db7cd3c|${params.cookie?.KUGOU_API_MAC}|${params.cookie?.KUGOU_API_DEV}|${dateNow}`,
{ key: liteT2Key, iv: liteT2Iv }
);
const t1 = cryptoAesEncrypt(`|${dateNow}`, { key: liteT1Key, iv: liteT1Iv });
const dataMap = {
dev: params.cookie?.KUGOU_API_DEV,
force_login: 1,
partnerid: 36,
clienttime_ms: dateNow,
t1: isLite ? t1 : 0,
t2: isLite ? t2 : 0,
t3: 'MCwwLDAsMCwwLDAsMCwwLDA=',
openid: assetsTokenResp.data.openid,
params: encrypt.str,
pk,
};
const response = await useAxios({
url: `/v6/login_by_openplat`,
method: 'POST',
data: dataMap,
cookie: params?.cookie,
encryptType: 'android',
headers: { 'x-router': 'login.user.kugou.com' },
});
if (response.body?.status === 1) {
const getToken = cryptoAesDecrypt(response.body.data?.secu_params, encrypt.key);
if (typeof getToken === 'object') {
response.body.data = { ...response.body.data, ...getToken };
Object.keys(getToken).forEach((key) => response.cookie.push(`${key}=${getToken[key]}`));
} else {
response.body.data['token'] = getToken;
response.cookie.push(`token=${getToken}`);
}
response.cookie.push(`t1=${response.body.data?.t1 ?? ''}`);
response.cookie.push(`userid=${response.body.data?.userid || 0}`);
response.cookie.push(`vip_type=${response.body.data?.vip_type || 0}`);
response.cookie.push(`vip_token=${response.body.data?.vip_token || ''}`);
}
resolve(response);
} else {
answer.status = 502;
answer.body = { status: 0, msg: assetsTokenResp.data };
reject(answer);
}
} catch (error) {
answer.status = 502;
answer.body = { status: 0, msg: error };
reject(answer);
}
});
};
+22
View File
@@ -0,0 +1,22 @@
const { srcappid, appid } = require('../util');
// 酷狗二维码状态检测
// 0 为二维码过期,1 为等待扫码,2 为待确认,4 为授权登录成功(4 状态码下会返回 token)
module.exports = (params, useAxios) => {
return new Promise((resolve, reject) => {
useAxios({
baseURL: 'https://login-user.kugou.com',
url: '/v2/get_userinfo_qrcode',
method: 'GET',
params: { plat: 4, appid, srcappid, qrcode: params?.key },
encryptType: 'web',
cookie: params?.cookie || {},
}).then(resp => {
if (resp.body?.data?.status == 4) {
resp.cookie.push(`token=${resp.body?.data?.token}`);
resp.cookie.push(`userid=${resp.body?.data?.userid}`);
}
resolve(resp);
}).catch(e => reject(e));
});
};
+19
View File
@@ -0,0 +1,19 @@
const qrcode = require('qrcode');
// 酷狗二维码生成
module.exports = (params, useAxios) => {
return new Promise(async (resolve) => {
const url = `https://h5.kugou.com/apps/loginQRCode/html/index.html?qrcode=${params.key}`
return resolve({
code: 200,
status: 200,
body: {
code: 200,
data: {
url: url,
base64: params?.qrimg ? await qrcode.toDataURL(url) : '',
},
},
})
})
}
+19
View File
@@ -0,0 +1,19 @@
const { srcappid, appid } = require('../util');
// 二维码 key 生成接口
module.exports = (params, useAxios) => {
return useAxios({
baseURL: 'https://login-user.kugou.com',
url: '/v2/qrcode',
method: 'GET',
params: {
appid: params?.type === 'web' ? 1014 : 1001,
type: 1,
plat: 4,
qrcode_txt: `https://h5.kugou.com/apps/loginQRCode/html/index.html?appid=${appid}&`,
srcappid,
},
encryptType: 'web',
cookie: params?.cookie || {},
});
};
+79
View File
@@ -0,0 +1,79 @@
// 刷新登录
const { cryptoAesDecrypt, cryptoAesEncrypt, cryptoRSAEncrypt, isLite } = require('../util');
const key = '90b8382a1bb4ccdcf063102053fd75b8';
const iv = 'f063102053fd75b8';
const liteKey = 'c24f74ca2820225badc01946dba4fdf7';
const liteIv = 'adc01946dba4fdf7';
let liteT2Key = 'fd14b35e3f81af3817a20ae7adae7020';
let liteT2Iv = '17a20ae7adae7020';
let liteT1Key = '5e4ef500e9597fe004bd09a46d8add98';
let liteT1Iv = '04bd09a46d8add98';
module.exports = (params, useAxios) => {
const dateNow = Date.now();
const token = params?.token || params?.cookie?.token || '';
const userid = params?.userid || params?.cookie?.userid || '0';
const encrypt = cryptoAesEncrypt({ clienttime: Math.floor(dateNow / 1000), token }, { key: isLite ? liteKey : key, iv: isLite ? liteIv : iv });
const encryptParams = cryptoAesEncrypt({});
const pk = cryptoRSAEncrypt({ clienttime_ms: dateNow, key: encryptParams.key });
const t2 = cryptoAesEncrypt(
`${params.cookie?.KUGOU_API_GUID}|0f607264fc6318a92b9e13c65db7cd3c|${params.cookie?.KUGOU_API_MAC}|${params.cookie?.KUGOU_API_DEV}|${dateNow}`,
{ key: liteT2Key, iv: liteT2Iv }
);
const t1 = cryptoAesEncrypt(params.cookie?.t1 ? `${params.cookie?.t1}|${dateNow}` : `|${dateNow}`, { key: liteT1Key, iv: liteT1Iv });
const dataMap = {
dfid: params?.cookie?.dfid || '-',
p3: encrypt,
plat: 1,
t1: isLite ? t1 : 0,
t2: isLite ? t2 : 0,
t3: 'MCwwLDAsMCwwLDAsMCwwLDA=',
pk,
params: encryptParams.str,
userid,
clienttime_ms: dateNow,
};
if (isLite) {
dataMap['dev'] = params.cookie?.KUGOU_API_DEV;
}
return new Promise((resolve, reject) => {
useAxios({
baseURL: 'http://login.user.kugou.com',
url: `/v5/login_by_token`,
method: 'POST',
data: dataMap,
cookie: params?.cookie,
encryptType: 'android',
})
.then((res) => {
const { body } = res;
if (body?.status && body?.status === 1) {
if (body?.data?.secu_params) {
const getToken = cryptoAesDecrypt(body.data.secu_params, encryptParams.key);
if (typeof getToken === 'object') {
res.body.data = { ...body.data, ...getToken };
Object.keys(getToken).forEach((key) => res.cookie.push(`${key}=${getToken[key]}`));
} else {
res.body.data['token'] = getToken;
}
}
res.cookie.push(`t1=${res.body.data['t1']}`);
res.cookie.push(`token=${res.body.data['token']}`);
res.cookie.push(`userid=${res.body.data?.userid || 0}`);
res.cookie.push(`vip_type=${res.body.data?.vip_type || 0}`);
res.cookie.push(`vip_token=${res.body.data?.vip_token || ''}`);
}
resolve(res);
})
.catch((e) => reject(e));
});
};
+20
View File
@@ -0,0 +1,20 @@
const axios = require('axios');
module.exports = (params, useAxios) => {
const answer = { status: 500, body: {}, cookie: [] };
return new Promise(async (resolve, reject) => {
try {
const resp = await axios({ url: `https://long.open.weixin.qq.com/connect/l/qrconnect?f=json&uuid=${params?.uuid || ''}` });
answer.status = 200;
answer.body = resp.data;
resolve(answer);
} catch (err) {
answer.status = 502;
answer.body = { status: 0, msg: err };
reject(answer);
}
});
}
+61
View File
@@ -0,0 +1,61 @@
const axios = require('axios');
const { wx_appid, wx_secret, cryptoMd5, cryptoSha1, randomString, wx_lite_appid, wx_lite_secret, isLite } = require('../util');
const appid = isLite ? wx_lite_appid : wx_appid;
const secret = isLite ? wx_lite_secret : wx_secret;
const accessToken = () => {
return axios({ url: 'https://api.weixin.qq.com/cgi-bin/token', params: { appid, secret, grant_type: 'client_credential' } });
};
/**
*
* @param {string} accessToken
* @returns
*/
const ticket = (accessToken) => axios({ url: 'https://api.weixin.qq.com/cgi-bin/ticket/getticket', params: { access_token: accessToken, type: 2 } });
module.exports = (params, useAxios) => {
const answer = { status: 500, body: {}, cookie: [] };
return new Promise(async (resolve, reject) => {
try {
const accessTokenResp = await accessToken();
if (accessTokenResp.data?.access_token) {
const ticketResp = await ticket(accessTokenResp.data.access_token);
if (ticketResp.data?.errcode === 0) {
const ticket = ticketResp.data.ticket;
const timestamp = Date.now();
const noncestr = cryptoMd5(randomString());
const signaturePrams = `appid=${appid}&noncestr=${noncestr}&sdk_ticket=${ticket}&timestamp=${timestamp}`;
const signature = cryptoSha1(signaturePrams);
const params = { appid: appid, noncestr, timestamp, scope: 'snsapi_userinfo', signature };
const connect = await axios({ url: 'https://open.weixin.qq.com/connect/sdk/qrconnect', params });
if (connect.data?.errcode === 0) {
answer.status = 200;
connect.data.qrcode['qrcodeurl'] = `https://open.weixin.qq.com/connect/confirm?uuid=${connect.data.uuid}`;
answer.body = connect.data;
resolve(answer);
} else {
answer.status = 502;
answer.body = { status: 0, msg: connect.data };
reject(answer);
}
} else {
answer.status = 502;
answer.body = { status: 0, msg: ticketResp.data };
reject(answer);
}
} else {
answer.status = 502;
answer.body = { status: 0, msg: accessTokenResp.data };
reject(answer);
}
} catch (e) {
answer.status = 502;
answer.body = { status: 0, msg: e };
reject(answer);
}
});
};
@@ -0,0 +1,16 @@
module.exports = (params, useAxios) => {
return useAxios({
url: `/longaudio/v2/album_audios`,
method: 'post',
encryptType: 'android',
data: {
album_id: params.album_id,
area_code: 1,
tagid: 0,
page: params.page || 1,
pagesize: params.pagesize || 30,
},
cookie: params?.cookie || {},
headers: { 'x-router': 'openapi.kugou.com', 'KG-TID': '78' },
});
};
@@ -0,0 +1,15 @@
module.exports = (params, useAxios) => {
const data = (params.album_id || '').split(',').map((s) => ({ album_id: s }));
return useAxios({
url: `/openapi/v2/broadcast`,
method: 'post',
encryptType: 'android',
data: {
data,
show_album_tag: 1,
fields: "album_name,album_id,category,authors,sizable_cover,intro,author_name,trans_param,album_tag,mix_intro,full_intro,is_publish"
},
cookie: params?.cookie || {},
headers: {'KG-TID': '78'}
});
};
@@ -0,0 +1,9 @@
module.exports = (params, useAxios) => {
return useAxios({
url: `/longaudio/v1/home_new/daily_recommend`,
method: 'post',
encryptType: 'android',
params: { module_id: 1, size: params.pagesize || 30, page: params.page || 1 },
cookie: params?.cookie || {},
});
};
@@ -0,0 +1,9 @@
module.exports = (params, useAxios) => {
return useAxios({
url: `/longaudio/v1/home_new/rank_card_recommend`,
method: 'get',
encryptType: 'android',
params: {platform: 'ios'},
cookie: params?.cookie || {},
});
};
@@ -0,0 +1,10 @@
module.exports = (params, useAxios) => {
return useAxios({
url: `/longaudio/v1/home_new/vip_select_recommend`,
method: 'post',
encryptType: 'android',
data: {album_playlist: []},
params: {position: '2', clientver: 12329},
cookie: params?.cookie || {},
});
};
@@ -0,0 +1,10 @@
module.exports = (params, useAxios) => {
return useAxios({
url: `/longaudio/v1/home_new/week_new_albums_recommend`,
method: 'post',
encryptType: 'android',
data: {album_playlist: []},
params: {clientver: 12329},
cookie: params?.cookie || {},
});
};
+35
View File
@@ -0,0 +1,35 @@
// 歌词获取
const { decodeLyrics } = require('../util');
module.exports = (params, useAxios) => {
const dataMap = {
ver: 1,
client: params?.client || 'android',
id: params?.id,
accesskey: params?.accesskey,
fmt: params.fmt || 'krc',
charset: 'utf8',
};
return new Promise((resolve, reject) => {
useAxios({
baseURL: 'https://lyrics.kugou.com',
url: '/download',
method: 'GET',
params: dataMap,
cookie: params?.cookie || {},
encryptType: 'android',
})
.then((res) => {
if (params?.decode) {
if (res.body?.content) {
res.body['decodeContent'] = params?.fmt == 'lrc' || Number(res.body?.contenttype) !== 0 ? Buffer.from(res.body?.content, 'base64').toString() : decodeLyrics(res.body.content);
resolve(res);
return;
}
}
resolve(res);
})
.catch((e) => reject(e));
});
};
+17
View File
@@ -0,0 +1,17 @@
// 电台 banner
module.exports = (params, useAxios) => {
const userid = params?.cookie?.userid || params?.userid || 0;
const dataMap = {
isvip: 0,
userid,
vipType: 0,
};
return useAxios({
baseURL: 'https://adservice.kugou.com',
url: '/v3/pc_diantai',
data: dataMap,
method: 'post',
encryptType: 'android',
cookie: params?.cookie || {},
});
};
+46
View File
@@ -0,0 +1,46 @@
const { signParamsKey, appid, clientver } = require('../util');
module.exports = (params, useAxios) => {
const userid = params?.cookie?.userid || params?.userid || 0;
const token = params?.cookie?.token || params?.token || 0;
const vip_type = params?.cookie?.vip_type || params?.vipType || 0;
const dateTime = Date.now();
const dataMap = {
appid,
clienttime: dateTime,
mid: params?.cookie?.KUGOU_API_MID,
action: params?.action || 'play',
recommend_source_locked: 0,
song_pool_id: Number(params?.song_pool_id || 0),
callerid: 0,
m_type: 1,
platform: params?.platform || 'ios',
area_code: 1,
remain_songcnt: Number(params?.remain_songcnt || 0),
clientver,
is_overplay: params?.is_overplay ? 1 : 0,
mode: params?.mode || 'normal',
fakem: 'ca981cfc583a4c37f28d2d49000013c16a0a',
key: signParamsKey(dateTime),
};
if (userid) {
dataMap['userid'] = userid;
dataMap['kguid'] = userid;
}
if (token) dataMap['token'] = token;
if (vip_type) dataMap['vip_type'] = vip_type;
if (params?.hash) dataMap['hash'] = params.hash;
if (params?.songid) dataMap['songid'] = params.songid;
if (params?.playtime) dataMap['playtime'] = params.playtime;
return useAxios({
url: '/v2/personal_recommend',
data: dataMap,
method: 'post',
encryptType: 'android',
cookie: params?.cookie || {},
headers: { 'x-router': 'persnfm.service.kugou.com' },
});
};
@@ -0,0 +1,17 @@
// 提交听歌历史
module.exports = (params, useAxios) => {
const userid = params?.userid || params?.cookie?.userid || 0;
const token = params?.token || params?.cookie?.token || '';
const songs = [{ mxid: Number(params.mxid), op: 1, ot: Number(params.time || Math.floor(Date.now() / 1000)), pc: Number(params.pc || 1) }];
const dataMap = { songs, token, userid };
return useAxios({
url: '/playhistory/v1/upload_songs',
data: dataMap,
encryptType: 'android',
method: 'POST',
params: { plat: 3 },
cookie: params?.cookie || {},
});
};
+33
View File
@@ -0,0 +1,33 @@
// 收藏歌单
module.exports = (params, useAxios) => {
const userid = params?.userid || params?.cookie?.userid || 0;
const token = params?.token || params.cookie?.token || '';
const clienttime = Math.floor(Date.now() / 1000);
const dataMap = {
userid,
token,
total_ver: 0,
name: params.name,
type: params.type || 0,
source: params.source === 0 ? 0 : params.source || 1,
is_pri: 0,
list_create_userid: params.list_create_userid,
list_create_listid: params.list_create_listid,
list_create_gid: params.list_create_gid || '',
from_shupinmv: 0,
};
if (params.type === 0) {
dataMap['is_pri'] = params.is_pri || 0;
}
return useAxios({
url: '/cloudlist.service/v5/add_list',
data: dataMap,
params: params.type === 0 ? { last_time: clienttime, last_area: 'gztx', userid, token } : {},
method: 'post',
encryptType: 'android',
cookie: params?.cookie || {},
});
};
+52
View File
@@ -0,0 +1,52 @@
const { playlistAesEncrypt, playlistAesDecrypt, rsaEncrypt2, signParamsKey, clientver, appid } = require('../util');
// 取消收藏歌单
module.exports = (params, useAxios) => {
const answer = { status: 500, body: {}, cookie: [] };
return new Promise(async (resolve) => {
try {
const userid = params?.userid || params?.cookie?.userid || 0;
const token = params?.token || params.cookie?.token || '';
const clienttime = Math.floor(Date.now() / 1000);
const dataMap = {
listid: Number(params.listid),
total_ver: 0,
type: 1,
};
const aesEncrypt = playlistAesEncrypt(dataMap);
const p = rsaEncrypt2({ aes: aesEncrypt.key, uid: userid, token }).toUpperCase();
const paramsMap = {
clienttime,
key: signParamsKey(clienttime.toString()),
last_area: 'gztx',
clientver,
appid,
last_time: clienttime,
p,
};
const respone = await useAxios({
url: '/v2/delete_list',
params: paramsMap,
data: aesEncrypt.str,
method: 'post',
encryptType: 'android',
headers: { 'x-router': 'cloudlist.service.kugou.com' },
responseType: 'arraybuffer',
cookie: params?.cookie || {},
});
respone.body = playlistAesDecrypt({ str: respone.body.toString('base64'), key: aesEncrypt.key });
resolve(respone);
} catch (error) {
console.log(error);
answer.body = error;
resolve(answer);
}
});
};
+22
View File
@@ -0,0 +1,22 @@
// 获取歌单详情
module.exports = (params, useAxios) => {
const data = (params?.ids || '').split(',').map(s => ({'global_collection_id': s }));
const dataMap = {
data,
userid: params?.userid || params?.cookie?.userid || 0,
token: params?.token || params?.cookie?.token || ''
};
return useAxios({
url: '/v3/get_list_info',
method: 'POST',
encryptType: 'android',
data: dataMap,
cookie: params?.cookie || {},
headers: {'x-router': 'pubsongs.kugou.com'}
})
}
+16
View File
@@ -0,0 +1,16 @@
// 获取音效歌单
module.exports = (params, useAxios) => {
const dataMap = {
page: params?.page || 1,
pagesize: params?.pagesize || 30,
};
return useAxios({
url: '/pubsongs/v1/get_sound_effect_list',
method: 'POST',
encryptType: 'android',
data: dataMap,
cookie: params?.cookie || {},
});
};
+26
View File
@@ -0,0 +1,26 @@
const { appid, clientver, signParamsKey } = require('../util');
module.exports = (params, useAxios) => {
const data = (params?.ids || '').split(',').map((s) => ({ 'global_collection_id': s }));
const clienttime = Date.now();
const dataMap = {
appid,
clientver,
clienttime,
key: signParamsKey(clienttime),
userid: params?.userid || params?.cookie?.userid || 0,
ugc: 1,
show_list: 1,
need_songs: 1,
data,
};
return useAxios({
url: '/pubsongs/v1/kmr_get_similar_lists',
method: 'POST',
encryptType: 'android',
data: dataMap,
cookie: params?.cookie || {},
});
};
+17
View File
@@ -0,0 +1,17 @@
// 获取歌单分类
module.exports = (params, useAxios) => {
const dataMap = {
tag_type: 'collection',
tag_id: 0,
source: 3,
};
return useAxios({
url: '/pubsongs/v1/get_tags_by_type',
method: 'POST',
encryptType: 'android',
data: dataMap,
cookie: params?.cookie || {},
});
};
@@ -0,0 +1,26 @@
// 获取歌单所有歌曲
module.exports = (params, useAxios) => {
const pagesize = params?.pagesize || 30;
const paramsMap = {
area_code: 1,
begin_idx: (Number(params.page || 1) - 1) * pagesize,
plat: 1,
type: 1,
// module: 'NONE',
mode: 1,
personal_switch: 1,
extend_fields: 'abtags,hot_cmt,popularization',
// page: params?.page || 1,
pagesize ,
global_collection_id: params?.id,
};
return useAxios({
url: '/pubsongs/v2/get_other_list_file_nofilt',
method: 'GET',
encryptType: 'android',
params: paramsMap,
cookie: params?.cookie || {},
// headers: { 'x-router': 'pubsongscdn.kugou.com' },
});
};
@@ -0,0 +1,26 @@
// 获取歌单所有歌曲
module.exports = (params, useAxios) => {
const userid = params?.userid || params?.cookie?.userid || '0';
const token = params?.token || params?.cookie?.token || '0';
const dataMap = {
listid: params.listid,
userid,
area_code: 1,
show_relate_goods: 0,
pagesize: params.pagesize || 30,
allplatform: 1,
show_cover: 1,
type: 0,
token,
page: params.page || 1,
};
return useAxios({
url: '/v4/get_list_all_file',
method: 'post',
encryptType: 'android',
data: dataMap,
cookie: params?.cookie || {},
headers: { 'x-router': 'cloudlist.service.kugou.com' },
});
};
@@ -0,0 +1,42 @@
// 对歌单添加歌曲
// listid, 歌单listid
module.exports = (params, useAxios) => {
const userid = params?.userid || params?.cookie?.userid || 0;
const token = params?.token || params.cookie?.token || '';
const clienttime = Math.floor(Date.now() / 1000);
const resource = (params.data || '').split(',').map((s) => {
const data = s.split('|');
return {
number: 1,
name: data[0] || '',
hash: data[1] || '',
size: 0,
sort: 0,
timelen: 0,
bitrate: 0,
album_id: Number(data[2] || 0),
mixsongid: Number(data[3] || 0),
};
});
const dataMap = {
userid,
token,
listid: params.listid,
list_ver: 0,
type: 0,
slow_upload: 1,
scene: 'false;null',
data: resource,
};
return useAxios({
url: '/cloudlist.service/v6/add_song',
data: dataMap,
params: { last_time: clienttime, last_area: 'gztx', userid, token },
method: 'post',
encryptType: 'android',
cookie: params?.cookie || {},
});
};
@@ -0,0 +1,26 @@
// 对歌单删除歌曲
module.exports = (params, useAxios) => {
const userid = params?.userid || params?.cookie?.userid || 0;
const token = params?.token || params.cookie?.token || '';
const resource = (params.fileids || '').split(',').map((s) => ({ fileid: Number(s) }));
const dataMap = {
listid: params.listid,
userid,
data: resource,
type: 0,
token,
list_ver: 0,
};
return useAxios({
url: '/v4/delete_songs',
data: dataMap,
method: 'post',
encryptType: 'android',
cookie: params?.cookie || {},
headers: { 'x-router': 'cloudlist.service.kugou.com' },
});
};
+28
View File
@@ -0,0 +1,28 @@
// 获取歌曲信息
const { appid, clientver } = require('../util');
module.exports = (params, useAxios) => {
const resource = (params?.hash || '').split(',').map((s) => ({ type: 'audio', page_id: 0, hash: s, album_id: 0 }));
(params?.album_id || '').split(',').forEach((s, l) => (resource[l]['album_id'] = s));
const dataMap = {
appid,
area_code: 1,
behavior: 'play',
clientver,
need_hash_offset: 1,
relate: 1,
support_verify: 1,
resource,
qualities: ['128', '320', 'flac', 'high', 'viper_atmos', 'viper_tape', 'viper_clear', 'super', 'multitrack'],
};
return useAxios({
url: '/v2/get_res_privilege/lite',
data: dataMap,
method: 'post',
encryptType: 'android',
cookie: params?.cookie || {},
headers: { 'x-router': 'media.store.kugou.com', 'Content-Type': 'application/json' },
});
};

Some files were not shown because too many files have changed in this diff Show More