ben-music-mcp 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ben_music_mcp-0.1.1/PKG-INFO +99 -0
- ben_music_mcp-0.1.1/README.md +83 -0
- ben_music_mcp-0.1.1/pyproject.toml +23 -0
- ben_music_mcp-0.1.1/src/ben_music_mcp/__init__.py +204 -0
- ben_music_mcp-0.1.1/src/ben_music_mcp/cookie.txt +1 -0
- ben_music_mcp-0.1.1/src/ben_music_mcp/server.py +5 -0
- ben_music_mcp-0.1.1/src/ben_music_mcp/styleList.json +26 -0
- ben_music_mcp-0.1.1/src/ben_music_mcp/tool.py +491 -0
- ben_music_mcp-0.1.1/src/ben_music_mcp/toplist.json +65 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ben-music-mcp
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary:
|
|
5
|
+
Author: ben
|
|
6
|
+
Author-email: 2014911413@qq.com
|
|
7
|
+
Requires-Python: >=3.12
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
12
|
+
Requires-Dist: fastmcp (>=2.13.0.2)
|
|
13
|
+
Requires-Dist: requests (>=2.32.4)
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# API 工具说明
|
|
17
|
+
|
|
18
|
+
本项目基于 FastMCP,封装了音乐相关常用接口,所有方法均以 JSON 格式返回,适合自动化调用和二次开发。
|
|
19
|
+
|
|
20
|
+
## 方法列表
|
|
21
|
+
|
|
22
|
+
### 1. hello
|
|
23
|
+
测试方法,返回示例字符串。
|
|
24
|
+
|
|
25
|
+
### 2. get_song_info(keywords)
|
|
26
|
+
根据关键词搜索歌曲信息,返回首条歌曲的详细信息(id、name、artists、album、alias、transNames、duration、fee、mvid)。
|
|
27
|
+
|
|
28
|
+
### 3. get_song_id(keywords)
|
|
29
|
+
根据关键词搜索歌曲,返回第一首歌的ID。
|
|
30
|
+
|
|
31
|
+
### 4. lyric(keywords)
|
|
32
|
+
根据歌名关键词搜索歌词(内部先获取歌曲ID),返回歌词文本。
|
|
33
|
+
|
|
34
|
+
### 5. search_artist(keyword)
|
|
35
|
+
根据关键词搜索歌手信息,自动带上 cookie,返回首个歌手的基本信息(artistName、artistId、avatar)。
|
|
36
|
+
|
|
37
|
+
### 6. get_artist_info(keyword)
|
|
38
|
+
根据关键词获取歌手详细信息,先查 id 再查详情,返回完整歌手信息。
|
|
39
|
+
|
|
40
|
+
### 7. get_artist_hot_songs(keyword)
|
|
41
|
+
根据关键词获取歌手最火的 50 首歌曲,先查 id 再查热门歌曲。
|
|
42
|
+
|
|
43
|
+
### 8. get_music_url(keywords, level="standard")
|
|
44
|
+
根据关键词获取音乐播放 url,先查歌曲 id 再查 url,支持音质等级选择。
|
|
45
|
+
|
|
46
|
+
### 9. get_song_comment(keywords)
|
|
47
|
+
根据关键词获取歌曲评论,先查歌曲 id 再查评论,只保留有用字段(nickname、content、likedCount、timeStr)。
|
|
48
|
+
|
|
49
|
+
### 10. get_playlist(id)
|
|
50
|
+
根据歌单 id 获取歌单详情,只返回关键字段(id、name、coverImgUrl、userId、trackCount、playCount、tracks 等)。
|
|
51
|
+
|
|
52
|
+
### 11. get_toplist(name)
|
|
53
|
+
根据排行榜名获取榜单详情(先查 id 再查 get_playlist)。
|
|
54
|
+
|
|
55
|
+
### 12. get_similar_songs(keywords)
|
|
56
|
+
根据关键词获取相似音乐(先查 id 再查 /simi/song),只保留关键信息。
|
|
57
|
+
|
|
58
|
+
### 13. get_style_songs(style_name)
|
|
59
|
+
根据曲风名返回歌曲列表(先查曲风 id,再查 /style/song),只保留关键信息(id、name、artists、album、duration)。
|
|
60
|
+
|
|
61
|
+
### 14. get_uid(nickname)
|
|
62
|
+
根据用户昵称获取其 uid,返回 uid 和昵称。
|
|
63
|
+
|
|
64
|
+
### 15. get_user_playlist(nickname)
|
|
65
|
+
输入用户昵称,获取用户歌单(先查 uid,再查 /user/playlist),只保留必要字段(id、name、coverImgUrl、trackCount、playCount、creator 信息)。
|
|
66
|
+
|
|
67
|
+
### 16. login_anonymous()
|
|
68
|
+
游客登录,获取游客 cookie,并保存到全局变量。
|
|
69
|
+
|
|
70
|
+
### 17. login_refresh()
|
|
71
|
+
刷新登录状态,获取新的 cookie。
|
|
72
|
+
|
|
73
|
+
### 18. start_netease_music(exe_path="C:\\CloudMusic\\cloudmusic.exe")
|
|
74
|
+
启动本地网易云音乐客户端,支持自定义 exe 路径。
|
|
75
|
+
|
|
76
|
+
## 返回格式
|
|
77
|
+
所有方法均返回如下结构:
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
{
|
|
81
|
+
"code": 200, // 状态码,200 表示成功
|
|
82
|
+
"msg": "success", // 状态信息
|
|
83
|
+
"data": {...} // 具体数据内容
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## 使用说明
|
|
88
|
+
1. 推荐通过 MCP 工具接口调用各方法。
|
|
89
|
+
2. 部分方法(如评论、歌单、曲风)已做字段精简,适合前端展示和二次处理。
|
|
90
|
+
3. 启动本地网易云客户端需 Windows 环境,默认路径可自定义。
|
|
91
|
+
|
|
92
|
+
## 依赖
|
|
93
|
+
- Python 3.8+
|
|
94
|
+
- requests
|
|
95
|
+
- fastmcp
|
|
96
|
+
|
|
97
|
+
## 维护
|
|
98
|
+
如需扩展新接口或字段,请参考 tool.py 和 server.py 的实现风格。
|
|
99
|
+
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# API 工具说明
|
|
2
|
+
|
|
3
|
+
本项目基于 FastMCP,封装了音乐相关常用接口,所有方法均以 JSON 格式返回,适合自动化调用和二次开发。
|
|
4
|
+
|
|
5
|
+
## 方法列表
|
|
6
|
+
|
|
7
|
+
### 1. hello
|
|
8
|
+
测试方法,返回示例字符串。
|
|
9
|
+
|
|
10
|
+
### 2. get_song_info(keywords)
|
|
11
|
+
根据关键词搜索歌曲信息,返回首条歌曲的详细信息(id、name、artists、album、alias、transNames、duration、fee、mvid)。
|
|
12
|
+
|
|
13
|
+
### 3. get_song_id(keywords)
|
|
14
|
+
根据关键词搜索歌曲,返回第一首歌的ID。
|
|
15
|
+
|
|
16
|
+
### 4. lyric(keywords)
|
|
17
|
+
根据歌名关键词搜索歌词(内部先获取歌曲ID),返回歌词文本。
|
|
18
|
+
|
|
19
|
+
### 5. search_artist(keyword)
|
|
20
|
+
根据关键词搜索歌手信息,自动带上 cookie,返回首个歌手的基本信息(artistName、artistId、avatar)。
|
|
21
|
+
|
|
22
|
+
### 6. get_artist_info(keyword)
|
|
23
|
+
根据关键词获取歌手详细信息,先查 id 再查详情,返回完整歌手信息。
|
|
24
|
+
|
|
25
|
+
### 7. get_artist_hot_songs(keyword)
|
|
26
|
+
根据关键词获取歌手最火的 50 首歌曲,先查 id 再查热门歌曲。
|
|
27
|
+
|
|
28
|
+
### 8. get_music_url(keywords, level="standard")
|
|
29
|
+
根据关键词获取音乐播放 url,先查歌曲 id 再查 url,支持音质等级选择。
|
|
30
|
+
|
|
31
|
+
### 9. get_song_comment(keywords)
|
|
32
|
+
根据关键词获取歌曲评论,先查歌曲 id 再查评论,只保留有用字段(nickname、content、likedCount、timeStr)。
|
|
33
|
+
|
|
34
|
+
### 10. get_playlist(id)
|
|
35
|
+
根据歌单 id 获取歌单详情,只返回关键字段(id、name、coverImgUrl、userId、trackCount、playCount、tracks 等)。
|
|
36
|
+
|
|
37
|
+
### 11. get_toplist(name)
|
|
38
|
+
根据排行榜名获取榜单详情(先查 id 再查 get_playlist)。
|
|
39
|
+
|
|
40
|
+
### 12. get_similar_songs(keywords)
|
|
41
|
+
根据关键词获取相似音乐(先查 id 再查 /simi/song),只保留关键信息。
|
|
42
|
+
|
|
43
|
+
### 13. get_style_songs(style_name)
|
|
44
|
+
根据曲风名返回歌曲列表(先查曲风 id,再查 /style/song),只保留关键信息(id、name、artists、album、duration)。
|
|
45
|
+
|
|
46
|
+
### 14. get_uid(nickname)
|
|
47
|
+
根据用户昵称获取其 uid,返回 uid 和昵称。
|
|
48
|
+
|
|
49
|
+
### 15. get_user_playlist(nickname)
|
|
50
|
+
输入用户昵称,获取用户歌单(先查 uid,再查 /user/playlist),只保留必要字段(id、name、coverImgUrl、trackCount、playCount、creator 信息)。
|
|
51
|
+
|
|
52
|
+
### 16. login_anonymous()
|
|
53
|
+
游客登录,获取游客 cookie,并保存到全局变量。
|
|
54
|
+
|
|
55
|
+
### 17. login_refresh()
|
|
56
|
+
刷新登录状态,获取新的 cookie。
|
|
57
|
+
|
|
58
|
+
### 18. start_netease_music(exe_path="C:\\CloudMusic\\cloudmusic.exe")
|
|
59
|
+
启动本地网易云音乐客户端,支持自定义 exe 路径。
|
|
60
|
+
|
|
61
|
+
## 返回格式
|
|
62
|
+
所有方法均返回如下结构:
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"code": 200, // 状态码,200 表示成功
|
|
67
|
+
"msg": "success", // 状态信息
|
|
68
|
+
"data": {...} // 具体数据内容
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## 使用说明
|
|
73
|
+
1. 推荐通过 MCP 工具接口调用各方法。
|
|
74
|
+
2. 部分方法(如评论、歌单、曲风)已做字段精简,适合前端展示和二次处理。
|
|
75
|
+
3. 启动本地网易云客户端需 Windows 环境,默认路径可自定义。
|
|
76
|
+
|
|
77
|
+
## 依赖
|
|
78
|
+
- Python 3.8+
|
|
79
|
+
- requests
|
|
80
|
+
- fastmcp
|
|
81
|
+
|
|
82
|
+
## 维护
|
|
83
|
+
如需扩展新接口或字段,请参考 tool.py 和 server.py 的实现风格。
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "ben-music-mcp"
|
|
3
|
+
version = "0.1.1"
|
|
4
|
+
description = ""
|
|
5
|
+
authors = [
|
|
6
|
+
{name = "ben",email = "2014911413@qq.com"}
|
|
7
|
+
]
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
requires-python = ">=3.12"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"requests>=2.32.4",
|
|
12
|
+
"fastmcp>=2.13.0.2"
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[tool.poetry]
|
|
16
|
+
packages = [{include = "ben_music_mcp", from = "src"}]
|
|
17
|
+
|
|
18
|
+
[project.scripts]
|
|
19
|
+
ben-music-mcp = "ben_music_mcp:main"
|
|
20
|
+
|
|
21
|
+
[build-system]
|
|
22
|
+
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
|
23
|
+
build-backend = "poetry.core.masonry.api"
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
from fastmcp import FastMCP
|
|
4
|
+
from .tool import (
|
|
5
|
+
_hello,
|
|
6
|
+
_get_song_info,
|
|
7
|
+
_get_song_id,
|
|
8
|
+
_lyric,
|
|
9
|
+
_search_artist,
|
|
10
|
+
_login_anonymous,
|
|
11
|
+
_login_refresh,
|
|
12
|
+
_get_artist_info,
|
|
13
|
+
_get_artist_hot_songs,
|
|
14
|
+
_get_music_url,
|
|
15
|
+
_get_song_comment,
|
|
16
|
+
_get_playlist,
|
|
17
|
+
_get_toplist,
|
|
18
|
+
_get_similar_songs,
|
|
19
|
+
_get_style_songs,
|
|
20
|
+
_start_netease_music,
|
|
21
|
+
_get_uid,
|
|
22
|
+
_get_user_playlist,
|
|
23
|
+
global_cookie
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
mcp = FastMCP("MCPService")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@mcp.tool
|
|
30
|
+
def hello() -> dict:
|
|
31
|
+
"""方法注释"""
|
|
32
|
+
return _hello()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@mcp.tool
|
|
36
|
+
def get_song_info(keywords: str) -> dict:
|
|
37
|
+
"""
|
|
38
|
+
根据关键词搜索歌曲信息,歌曲id无需向用户透漏
|
|
39
|
+
:param keywords: 歌曲关键词
|
|
40
|
+
:return: 歌曲信息
|
|
41
|
+
"""
|
|
42
|
+
return _get_song_info(keywords)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@mcp.tool
|
|
46
|
+
def get_song_id(keywords: str) -> dict:
|
|
47
|
+
"""
|
|
48
|
+
根据关键词搜索歌曲,返回第一首歌的ID
|
|
49
|
+
:param keywords: 歌曲关键词
|
|
50
|
+
:return: 歌曲ID
|
|
51
|
+
"""
|
|
52
|
+
return _get_song_id(keywords)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@mcp.tool
|
|
56
|
+
def lyric(keywords: str) -> dict:
|
|
57
|
+
"""
|
|
58
|
+
根据歌名关键词搜索歌词(内部先获取歌曲ID)
|
|
59
|
+
:param keywords: 歌曲关键词
|
|
60
|
+
:return: 歌词文本
|
|
61
|
+
"""
|
|
62
|
+
return _lyric(keywords)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@mcp.tool
|
|
66
|
+
def search_artist(keyword: str) -> dict:
|
|
67
|
+
"""
|
|
68
|
+
根据关键词搜索歌手信息,自动带上cookie
|
|
69
|
+
:param keyword: 歌手关键词
|
|
70
|
+
:return: 歌手信息字符串
|
|
71
|
+
"""
|
|
72
|
+
return _search_artist(keyword)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@mcp.tool
|
|
76
|
+
def get_artist_info(keyword: str) -> dict:
|
|
77
|
+
"""
|
|
78
|
+
根据关键词获取歌手详细信息,先查id再查详情,返回json
|
|
79
|
+
:param keyword: 歌手关键词
|
|
80
|
+
:return: 歌手详细信息json
|
|
81
|
+
"""
|
|
82
|
+
return _get_artist_info(keyword)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@mcp.tool
|
|
86
|
+
def get_artist_hot_songs(keyword: str) -> dict:
|
|
87
|
+
"""
|
|
88
|
+
根据关键词获取歌手最火的50首歌曲,先查id再查热门歌曲,返回json
|
|
89
|
+
:param keyword: 歌手关键词
|
|
90
|
+
:return: 热门歌曲json
|
|
91
|
+
"""
|
|
92
|
+
return _get_artist_hot_songs(keyword)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@mcp.tool
|
|
96
|
+
def get_music_url(keywords: str, level: str = "standard") -> dict:
|
|
97
|
+
"""
|
|
98
|
+
根据关键词获取音乐播放url,先查歌曲id再查url,返回json
|
|
99
|
+
:param keywords: 歌曲关键词
|
|
100
|
+
:param level: 音质等级
|
|
101
|
+
:return: 音乐url json
|
|
102
|
+
"""
|
|
103
|
+
return _get_music_url(keywords, level)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@mcp.tool
|
|
107
|
+
def get_song_comment(keywords: str) -> dict:
|
|
108
|
+
"""
|
|
109
|
+
根据关键词获取歌曲评论,先查歌曲id再查评论,返回json
|
|
110
|
+
:param keywords: 歌曲关键词
|
|
111
|
+
:return: 歌曲评论json
|
|
112
|
+
"""
|
|
113
|
+
return _get_song_comment(keywords)
|
|
114
|
+
|
|
115
|
+
@mcp.tool
|
|
116
|
+
def get_playlist(id: int) -> dict:
|
|
117
|
+
"""
|
|
118
|
+
根据歌单id获取歌单详情
|
|
119
|
+
:param id: 歌单id
|
|
120
|
+
:return: 歌单详情json
|
|
121
|
+
"""
|
|
122
|
+
return _get_playlist(id)
|
|
123
|
+
|
|
124
|
+
@mcp.tool
|
|
125
|
+
def get_toplist(name: str) -> dict:
|
|
126
|
+
"""
|
|
127
|
+
根据排行榜名获取榜单详情(先查id再查_get_playlist)
|
|
128
|
+
:param name: 榜单名称
|
|
129
|
+
:return: 榜单详情json
|
|
130
|
+
"""
|
|
131
|
+
return _get_toplist(name)
|
|
132
|
+
|
|
133
|
+
@mcp.tool
|
|
134
|
+
def get_similar_songs(keywords: str) -> dict:
|
|
135
|
+
"""
|
|
136
|
+
根据关键词获取相似音乐(先查id再查/simi/song)
|
|
137
|
+
:param keywords: 歌曲关键词
|
|
138
|
+
:return: 相似音乐json
|
|
139
|
+
"""
|
|
140
|
+
return _get_similar_songs(keywords)
|
|
141
|
+
|
|
142
|
+
@mcp.tool
|
|
143
|
+
def get_style_songs(style_name: str) -> dict:
|
|
144
|
+
"""
|
|
145
|
+
根据曲风名返回歌曲列表。
|
|
146
|
+
:param style_name: 曲风名
|
|
147
|
+
:return: 歌曲列表json
|
|
148
|
+
"""
|
|
149
|
+
return _get_style_songs(style_name)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@mcp.tool()
|
|
153
|
+
def get_uid(nickname: str) -> dict:
|
|
154
|
+
"""
|
|
155
|
+
根据用户昵称获取其uid
|
|
156
|
+
:param nickname: 用户昵称
|
|
157
|
+
:return: 用户uid json
|
|
158
|
+
"""
|
|
159
|
+
return _get_uid(nickname)
|
|
160
|
+
|
|
161
|
+
@mcp.tool()
|
|
162
|
+
def get_user_playlist(nickname: str) -> dict:
|
|
163
|
+
"""
|
|
164
|
+
输入用户昵称,获取用户歌单(只保留必要字段)
|
|
165
|
+
:param nickname: 用户昵称
|
|
166
|
+
:return: 歌单列表json
|
|
167
|
+
"""
|
|
168
|
+
return _get_user_playlist(nickname)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
@mcp.tool
|
|
175
|
+
def login_anonymous() -> dict:
|
|
176
|
+
"""
|
|
177
|
+
游客登录,获取游客 cookie,并保存到全局变量
|
|
178
|
+
:return: 游客 cookie 字符串
|
|
179
|
+
"""
|
|
180
|
+
return _login_anonymous()
|
|
181
|
+
|
|
182
|
+
@mcp.tool
|
|
183
|
+
def login_refresh() -> dict:
|
|
184
|
+
"""
|
|
185
|
+
刷新登录状态,获取新的 cookie
|
|
186
|
+
:return: 新的 cookie 字符串
|
|
187
|
+
"""
|
|
188
|
+
return _login_refresh()
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
@mcp.tool()
|
|
192
|
+
def start_netease_music(exe_path: str) -> dict:
|
|
193
|
+
"""
|
|
194
|
+
启动本地网易云音乐客户端
|
|
195
|
+
:param exe_path: 网易云音乐客户端的 exe 路径,默认 C:\\CloudMusic\\cloudmusic.exe
|
|
196
|
+
:return: 启动结果 json
|
|
197
|
+
"""
|
|
198
|
+
return _start_netease_music(exe_path)
|
|
199
|
+
|
|
200
|
+
def main():
|
|
201
|
+
mcp.run(transport="stdio")
|
|
202
|
+
|
|
203
|
+
if __name__ == "__main__":
|
|
204
|
+
mcp.run(transport="stdio")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"MUSIC_A_T": "1762674143805", "MUSIC_R_T": "0", "MUSIC_A": "00FD2909A8CC993C4FB0FC90128C7E2E3EEC34146238C0F67438D16B51DB026C9AC74700E4270BCF0954B0454EDA13126BBD915DF4CF52EF21ACB5141059BEFA897B59C94F25484D47A8C8B257C322CB9D745805D39E61449D59DF795D936D9F5F628CE6E0D6610792EB91B0D34EBFB837357B9AC0135C97B18B1D6FB1EA248B5532FEFF6A34A5C57B4EDE76C36AACF86A542A7358C44E0F2978A3B7AA852F2A84DA48857A5CAAE3FA2D4968232C727BFD96CD7D7E7479829C726EC940B1880EFD01740C665B517699FAAEF1CA3BDD92E049A36AB793A15AD9AD75E4D691D4D821E363CAE7BF3AC9586A653999810747C974239EC1E43944EB679B3DC92CFF113BDBC9279CCDCD5271C55DD18FE58847F6AF6D5E12D1D8BB8417D99B2AEDF13B4EC9097EE893ABD6BBC56E37187D2386D061DE4288B289AD7F4068E488B691682F61F170D1DDB0E6D84A679E49677A1E529C96FE9540E0B5527B1D23C956FEAB3CB6B3647B749B1CAE39EE70CC6FFC06E5F4AC33DA9470E856C1391FD7006FB487C9537E4D11B2D469A78615B32771CAE5A725D8FD8FA172E865A236F487B665817AE5F57F4AF8781F2663CA0E5780CBBF4474D616D068125C6EAFC1741C5B844F8BB6621037BA47FC0CD9E48FA63499730DF294E62E3A2CF99319D6DD778B573CCB7B99B2AFC4CC9035540E3864862E2707B4898BD831AF07C4F0774ED9AFA3F96B94079C3F48D262C4898BD0A3E39016", "__csrf": "eabbb81b921eb9637c0b389245b3a0dc", "MUSIC_R_U": "00F9CF8A93CB06AEC0A041DB4FC98922D18884F91B648A3F78CC43943902835DB940DC984AC33ED14A019B44DCDB707C2FA8F2ED50EC556FB93B675A320C20F5E39A3EA15488819D77A922244FB3B663B4"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
[{"tagId": 1000, "tagName": "流行"},
|
|
2
|
+
{"tagId": 1017, "tagName": "嘻哈说唱"},
|
|
3
|
+
{"tagId": 1015, "tagName": "电子"},
|
|
4
|
+
{"tagId": 1028, "tagName": "R&B"},
|
|
5
|
+
{"tagId": 1008, "tagName": "摇滚"},
|
|
6
|
+
{"tagId": 1003, "tagName": "民谣"},
|
|
7
|
+
{"tagId": 10119, "tagName": "国风"},
|
|
8
|
+
{"tagId": 1010, "tagName": "原声带"},
|
|
9
|
+
{"tagId": 1026, "tagName": "儿童"},
|
|
10
|
+
{"tagId": 79088, "tagName": "慢摇DJ"},
|
|
11
|
+
{"tagId": 1001, "tagName": "中国传统特色"},
|
|
12
|
+
{"tagId": 1022, "tagName": "古典"},
|
|
13
|
+
{"tagId": 1011, "tagName": "爵士"},
|
|
14
|
+
{"tagId": 1252, "tagName": "二次元"},
|
|
15
|
+
{"tagId": 1006, "tagName": "轻音乐"},
|
|
16
|
+
{"tagId": 1029, "tagName": "放克"},
|
|
17
|
+
{"tagId": 1263, "tagName": "世界乐器体裁"},
|
|
18
|
+
{"tagId": 1077, "tagName": "乡村"},
|
|
19
|
+
{"tagId": 1007, "tagName": "新世纪"},
|
|
20
|
+
{"tagId": 152124, "tagName": "朋克"},
|
|
21
|
+
{"tagId": 1071, "tagName": "拉丁音乐"},
|
|
22
|
+
{"tagId": 1041, "tagName": "金属"},
|
|
23
|
+
{"tagId": 1037, "tagName": "蓝调"},
|
|
24
|
+
{"tagId": 1014, "tagName": "另类/独立"},
|
|
25
|
+
{"tagId": 1043, "tagName": "雷鬼"},
|
|
26
|
+
{"tagId": 1012, "tagName": "世界音乐"}]
|
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
import subprocess
|
|
4
|
+
import requests
|
|
5
|
+
|
|
6
|
+
# 全局变量保存 cookie
|
|
7
|
+
global_cookie = {}
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
import json
|
|
11
|
+
|
|
12
|
+
def _save_cookie_to_file(cookie: dict, filename: str = "cookie.txt"):
|
|
13
|
+
"""将cookie保存到文件"""
|
|
14
|
+
with open(filename, "w", encoding="utf-8") as f:
|
|
15
|
+
f.write(json.dumps(cookie, ensure_ascii=False))
|
|
16
|
+
|
|
17
|
+
def _load_cookie_from_file(filename: str = "cookie.txt") -> dict:
|
|
18
|
+
"""从文件读取cookie"""
|
|
19
|
+
if not os.path.exists(filename):
|
|
20
|
+
return {}
|
|
21
|
+
try:
|
|
22
|
+
with open(filename, "r", encoding="utf-8") as f:
|
|
23
|
+
return json.loads(f.read())
|
|
24
|
+
except Exception:
|
|
25
|
+
return {}
|
|
26
|
+
|
|
27
|
+
def _get_cookie() -> dict:
|
|
28
|
+
"""获取当前cookie(优先文件)"""
|
|
29
|
+
global global_cookie
|
|
30
|
+
if global_cookie:
|
|
31
|
+
return global_cookie
|
|
32
|
+
cookie = _load_cookie_from_file()
|
|
33
|
+
global_cookie = cookie
|
|
34
|
+
return cookie
|
|
35
|
+
|
|
36
|
+
def _hello() -> dict:
|
|
37
|
+
"""方法注释"""
|
|
38
|
+
return {"code": 200, "msg": "success", "data": "我是哈尔滨工业大学的学生,我叫张三,学号是123456。"}
|
|
39
|
+
|
|
40
|
+
def _get_song_info(keywords: str) -> dict:
|
|
41
|
+
"""
|
|
42
|
+
搜索歌曲,返回json格式
|
|
43
|
+
:param keywords: 歌曲关键词
|
|
44
|
+
:return: 歌曲信息json
|
|
45
|
+
"""
|
|
46
|
+
url = f"https://ncm.nekogan.com/search?keywords={keywords}"
|
|
47
|
+
try:
|
|
48
|
+
resp = requests.get(url, timeout=5)
|
|
49
|
+
resp.raise_for_status()
|
|
50
|
+
data = resp.json()
|
|
51
|
+
if "result" in data and "songs" in data["result"]:
|
|
52
|
+
songs = data["result"]["songs"]
|
|
53
|
+
if not songs:
|
|
54
|
+
return {"code": 404, "msg": "未找到相关歌曲", "data": {}}
|
|
55
|
+
song = songs[0]
|
|
56
|
+
return {
|
|
57
|
+
"code": 200,
|
|
58
|
+
"msg": "success",
|
|
59
|
+
"data": {
|
|
60
|
+
"id": song.get("id", "未知"),
|
|
61
|
+
"name": song.get("name", "未知"),
|
|
62
|
+
"artists": [a["name"] for a in song.get("artists", [])],
|
|
63
|
+
"album": song.get("album", {}).get("name", "未知"),
|
|
64
|
+
"alias": song.get("alias", []),
|
|
65
|
+
"transNames": song.get("transNames", []),
|
|
66
|
+
"duration": song.get("duration", 0),
|
|
67
|
+
"fee": song.get("fee", 0),
|
|
68
|
+
"mvid": song.get("mvid", 0)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else:
|
|
72
|
+
return {"code": 404, "msg": "未找到相关歌曲", "data": {}}
|
|
73
|
+
except Exception as e:
|
|
74
|
+
return {"code": 500, "msg": f"请求失败: {e}", "data": {}}
|
|
75
|
+
|
|
76
|
+
def _get_song_id(keywords: str) -> dict:
|
|
77
|
+
"""
|
|
78
|
+
搜索歌曲,返回第一个歌曲ID,json格式
|
|
79
|
+
:param keywords: 歌曲关键词
|
|
80
|
+
:return: 歌曲ID json
|
|
81
|
+
"""
|
|
82
|
+
result = _get_song_info(keywords)
|
|
83
|
+
if result.get("code") == 200 and "id" in result.get("data", {}):
|
|
84
|
+
return {"code": 200, "msg": "success", "id": str(result["data"]["id"])}
|
|
85
|
+
return {"code": 404, "msg": "未找到相关歌曲ID", "id": ""}
|
|
86
|
+
|
|
87
|
+
def _lyric(keywords: str) -> dict:
|
|
88
|
+
"""先获取歌曲id,再查歌词,返回json"""
|
|
89
|
+
song_id_json = _get_song_id(keywords)
|
|
90
|
+
song_id = song_id_json.get("id", "")
|
|
91
|
+
if not song_id or not song_id.isdigit():
|
|
92
|
+
return {"code": 404, "msg": f"未找到歌曲ID,原因:{song_id_json}", "data": ""}
|
|
93
|
+
url = f"https://ncm.nekogan.com/lyric?id={song_id}"
|
|
94
|
+
try:
|
|
95
|
+
resp = requests.get(url, timeout=5)
|
|
96
|
+
resp.raise_for_status()
|
|
97
|
+
data = resp.json()
|
|
98
|
+
lyric_text = data.get("lrc", {}).get("lyric", "")
|
|
99
|
+
if lyric_text:
|
|
100
|
+
return {"code": 200, "msg": "success", "data": lyric_text}
|
|
101
|
+
else:
|
|
102
|
+
return {"code": 404, "msg": "未找到歌词。", "data": ""}
|
|
103
|
+
except Exception as e:
|
|
104
|
+
return {"code": 500, "msg": f"请求失败 {e}", "data": ""}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _search_artist(keyword: str) -> dict:
|
|
108
|
+
"""
|
|
109
|
+
搜索歌手,返回json格式
|
|
110
|
+
:param keyword: 歌手关键词
|
|
111
|
+
:return: 歌手信息json
|
|
112
|
+
"""
|
|
113
|
+
url = f"https://ncm.nekogan.com/ugc/artist/search?keyword={keyword}"
|
|
114
|
+
try:
|
|
115
|
+
cookie = _get_cookie()
|
|
116
|
+
resp = requests.get(url, timeout=5, cookies=cookie)
|
|
117
|
+
resp.raise_for_status()
|
|
118
|
+
data = resp.json()
|
|
119
|
+
data_field = data.get("data")
|
|
120
|
+
if not data_field or "list" not in data_field:
|
|
121
|
+
return {"code": 404, "msg": "未找到相关歌手", "data": {}}
|
|
122
|
+
artists = data_field.get("list", [])
|
|
123
|
+
if not artists:
|
|
124
|
+
return {"code": 404, "msg": "未找到相关歌手", "data": {}}
|
|
125
|
+
artist = artists[0]
|
|
126
|
+
return {
|
|
127
|
+
"code": 200,
|
|
128
|
+
"msg": "success",
|
|
129
|
+
"data": {
|
|
130
|
+
"artistName": artist.get("artistName", "未知"),
|
|
131
|
+
"artistId": artist.get("artistId", "未知"),
|
|
132
|
+
"avatar": artist.get("artistAvatarPicUrl", "")
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
except Exception as e:
|
|
136
|
+
return {"code": 500, "msg": f"请求失败: {e}", "data": {}}
|
|
137
|
+
|
|
138
|
+
def _get_artist_id(keyword: str) -> dict:
|
|
139
|
+
"""
|
|
140
|
+
根据关键词搜索歌手,返回第一个歌手ID,json格式
|
|
141
|
+
:param keyword: 歌手关键词
|
|
142
|
+
:return: 歌手ID json
|
|
143
|
+
"""
|
|
144
|
+
result = _search_artist(keyword)
|
|
145
|
+
if result.get("code") == 200 and "artistId" in result.get("data", {}):
|
|
146
|
+
return {"code": 200, "msg": "success", "id": str(result["data"]["artistId"])}
|
|
147
|
+
return {"code": 404, "msg": "未找到相关歌手ID", "id": ""}
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _get_artist_info(keyword: str) -> dict:
|
|
151
|
+
"""
|
|
152
|
+
根据关键词获取歌手详细信息,先查id再查详情,返回json
|
|
153
|
+
:param keyword: 歌手关键词
|
|
154
|
+
:return: 歌手详细信息json
|
|
155
|
+
"""
|
|
156
|
+
id_json = _get_artist_id(keyword)
|
|
157
|
+
artist_id = id_json.get("id", "")
|
|
158
|
+
if not artist_id or not artist_id.isdigit():
|
|
159
|
+
return {"code": 404, "msg": f"未找到歌手ID,原因:{id_json}", "data": {}}
|
|
160
|
+
url = f"https://ncm.nekogan.com/ugc/artist/get?id={artist_id}"
|
|
161
|
+
try:
|
|
162
|
+
cookie = _get_cookie()
|
|
163
|
+
resp = requests.get(url, timeout=5, cookies=cookie)
|
|
164
|
+
resp.raise_for_status()
|
|
165
|
+
data = resp.json()
|
|
166
|
+
return {"code": 200, "msg": "success", "data": data}
|
|
167
|
+
except Exception as e:
|
|
168
|
+
return {"code": 500, "msg": f"请求失败: {e}", "data": {}}
|
|
169
|
+
|
|
170
|
+
def _get_artist_hot_songs(keyword: str) -> dict:
|
|
171
|
+
"""
|
|
172
|
+
根据关键词获取歌手最火的50首歌曲,先查id再查热门歌曲,返回json
|
|
173
|
+
:param keyword: 歌手关键词
|
|
174
|
+
:return: 热门歌曲json
|
|
175
|
+
"""
|
|
176
|
+
id_json = _get_artist_id(keyword)
|
|
177
|
+
artist_id = id_json.get("id", "")
|
|
178
|
+
if not artist_id or not artist_id.isdigit():
|
|
179
|
+
return {"code": 404, "msg": f"未找到歌手ID,原因:{id_json}", "data": []}
|
|
180
|
+
url = f"https://ncm.nekogan.com/artist/top/song?id={artist_id}"
|
|
181
|
+
try:
|
|
182
|
+
cookie = _get_cookie()
|
|
183
|
+
resp = requests.get(url, timeout=5, cookies=cookie)
|
|
184
|
+
resp.raise_for_status()
|
|
185
|
+
data = resp.json()
|
|
186
|
+
return {"code": 200, "msg": "success", "data": data}
|
|
187
|
+
except Exception as e:
|
|
188
|
+
return {"code": 500, "msg": f"请求失败: {e}", "data": []}
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _get_music_url(keywords: str, level: str = "standard") -> dict:
|
|
192
|
+
"""
|
|
193
|
+
根据关键词获取音乐播放url,先查歌曲id再查url,返回json
|
|
194
|
+
:param keywords: 歌曲关键词
|
|
195
|
+
:param level: 音质等级
|
|
196
|
+
:return: 音乐url json
|
|
197
|
+
"""
|
|
198
|
+
id_json = _get_song_id(keywords)
|
|
199
|
+
song_id = id_json.get("id", "")
|
|
200
|
+
if not song_id or not song_id.isdigit():
|
|
201
|
+
return {"code": 404, "msg": f"未找到歌曲ID,原因:{id_json}", "data": {}}
|
|
202
|
+
url = f"https://ncm.nekogan.com/song/url/v1?id={song_id}&level={level}"
|
|
203
|
+
try:
|
|
204
|
+
resp = requests.get(url, timeout=5)
|
|
205
|
+
resp.raise_for_status()
|
|
206
|
+
data = resp.json()
|
|
207
|
+
return {"code": 200, "msg": "success", "data": data}
|
|
208
|
+
except Exception as e:
|
|
209
|
+
return {"code": 500, "msg": f"请求失败: {e}", "data": {}}
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def _get_song_comment(keywords: str) -> dict:
|
|
213
|
+
"""
|
|
214
|
+
根据关键词获取歌曲评论,只保留有用信息,返回json
|
|
215
|
+
:param keywords: 歌曲关键词
|
|
216
|
+
:return: 歌曲评论json
|
|
217
|
+
"""
|
|
218
|
+
id_json = _get_song_id(keywords)
|
|
219
|
+
song_id = id_json.get("id", "")
|
|
220
|
+
if not song_id or not song_id.isdigit():
|
|
221
|
+
return {"code": 404, "msg": f"未找到歌曲ID,原因:{id_json}", "data": []}
|
|
222
|
+
url = f"https://ncm.nekogan.com/comment/music?id={song_id}"
|
|
223
|
+
try:
|
|
224
|
+
resp = requests.get(url, timeout=5)
|
|
225
|
+
resp.raise_for_status()
|
|
226
|
+
data = resp.json()
|
|
227
|
+
hot_comments = data.get("hotComments", [])
|
|
228
|
+
# 只保留有用字段
|
|
229
|
+
result = []
|
|
230
|
+
for c in hot_comments:
|
|
231
|
+
user = c.get("user", {})
|
|
232
|
+
result.append({
|
|
233
|
+
"nickname": user.get("nickname", "未知"),
|
|
234
|
+
"content": c.get("content", ""),
|
|
235
|
+
"likedCount": c.get("likedCount", 0),
|
|
236
|
+
"timeStr": c.get("timeStr", "")
|
|
237
|
+
})
|
|
238
|
+
return {"code": 200, "msg": "success", "data": result}
|
|
239
|
+
except Exception as e:
|
|
240
|
+
return {"code": 500, "msg": f"请求失败: {e}", "data": []}
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def _get_playlist(id):
|
|
244
|
+
"""
|
|
245
|
+
获取歌单详情,只返回关键字段。
|
|
246
|
+
参数: id (歌单id)
|
|
247
|
+
返回: JSON
|
|
248
|
+
"""
|
|
249
|
+
url = f"https://ncm.nekogan.com/playlist/detail?id={id}"
|
|
250
|
+
try:
|
|
251
|
+
resp = requests.get(url, timeout=10)
|
|
252
|
+
data = resp.json()
|
|
253
|
+
if data.get("code") != 200 or "playlist" not in data:
|
|
254
|
+
return {"code": 500, "msg": "请求失败"}
|
|
255
|
+
pl = data["playlist"]
|
|
256
|
+
result = {
|
|
257
|
+
"id": pl.get("id"),
|
|
258
|
+
"name": pl.get("name"),
|
|
259
|
+
"coverImgUrl": pl.get("coverImgUrl"),
|
|
260
|
+
"userId": pl.get("userId"),
|
|
261
|
+
"createTime": pl.get("createTime"),
|
|
262
|
+
"trackCount": pl.get("trackCount"),
|
|
263
|
+
"playCount": pl.get("playCount"),
|
|
264
|
+
"subscribedCount": pl.get("subscribedCount"),
|
|
265
|
+
"description": pl.get("description"),
|
|
266
|
+
"tags": pl.get("tags", []),
|
|
267
|
+
"tracks": []
|
|
268
|
+
}
|
|
269
|
+
for t in pl.get("tracks", []):
|
|
270
|
+
track = {
|
|
271
|
+
"id": t.get("id"),
|
|
272
|
+
"name": t.get("name"),
|
|
273
|
+
"ar": [a.get("name") for a in t.get("ar", [])],
|
|
274
|
+
"al": t.get("al", {}).get("name"),
|
|
275
|
+
"alia": t.get("alia", [])
|
|
276
|
+
}
|
|
277
|
+
result["tracks"].append(track)
|
|
278
|
+
return result
|
|
279
|
+
except Exception as e:
|
|
280
|
+
return {"code": 500, "msg": str(e)}
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def _get_toplist(name: str) -> dict:
|
|
284
|
+
"""
|
|
285
|
+
根据排行榜名获取榜单详情(先查id再查_get_playlist)
|
|
286
|
+
:param name: 榜单名称
|
|
287
|
+
:return: 榜单详情json
|
|
288
|
+
"""
|
|
289
|
+
try:
|
|
290
|
+
with open("toplist.json", "r", encoding="utf-8") as f:
|
|
291
|
+
data = json.load(f)
|
|
292
|
+
toplists = data.get("toplists", [])
|
|
293
|
+
match = next((item for item in toplists if item.get("name") == name), None)
|
|
294
|
+
if not match:
|
|
295
|
+
return {"code": 404, "msg": f"未找到榜单: {name}"}
|
|
296
|
+
return _get_playlist(match["id"])
|
|
297
|
+
except Exception as e:
|
|
298
|
+
return {"code": 500, "msg": f"读取榜单失败: {e}"}
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def _get_similar_songs(keywords: str) -> dict:
|
|
302
|
+
"""
|
|
303
|
+
根据关键词获取相似音乐(先查id再查/simi/song)
|
|
304
|
+
:param keywords: 歌曲关键词
|
|
305
|
+
:return: 相似音乐json
|
|
306
|
+
"""
|
|
307
|
+
id_json = _get_song_id(keywords)
|
|
308
|
+
song_id = id_json.get("id", "")
|
|
309
|
+
if not song_id or not song_id.isdigit():
|
|
310
|
+
return {"code": 404, "msg": f"未找到歌曲ID,原因:{id_json}", "data": []}
|
|
311
|
+
url = f"https://ncm.nekogan.com/simi/song?id={song_id}"
|
|
312
|
+
try:
|
|
313
|
+
resp = requests.get(url, timeout=5)
|
|
314
|
+
resp.raise_for_status()
|
|
315
|
+
data = resp.json()
|
|
316
|
+
songs = data.get("songs", [])
|
|
317
|
+
result = []
|
|
318
|
+
for s in songs:
|
|
319
|
+
result.append({
|
|
320
|
+
"id": s.get("id"),
|
|
321
|
+
"name": s.get("name"),
|
|
322
|
+
"artists": [a.get("name") for a in s.get("artists", [])],
|
|
323
|
+
"album": s.get("album", {}).get("name"),
|
|
324
|
+
"duration": s.get("duration")
|
|
325
|
+
})
|
|
326
|
+
return {"code": 200, "msg": "success", "data": result}
|
|
327
|
+
except Exception as e:
|
|
328
|
+
return {"code": 500, "msg": f"请求失败: {e}", "data": []}
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def _get_style_songs(style_name: str) -> dict:
|
|
332
|
+
"""
|
|
333
|
+
根据曲风名获取对应曲风id,再查 /style/song?tagId=xxx,返回歌曲列表。
|
|
334
|
+
:param style_name: 曲风名
|
|
335
|
+
:return: 歌曲列表json
|
|
336
|
+
"""
|
|
337
|
+
|
|
338
|
+
# 读取 styleList.json
|
|
339
|
+
try:
|
|
340
|
+
with open("styleList.json", "r", encoding="utf-8") as f:
|
|
341
|
+
style_list = json.load(f)
|
|
342
|
+
except Exception as e:
|
|
343
|
+
return {"code": 500, "msg": f"读取曲风列表失败: {e}", "data": {}}
|
|
344
|
+
|
|
345
|
+
tag_id = None
|
|
346
|
+
for item in style_list:
|
|
347
|
+
if item.get("tagName") == style_name:
|
|
348
|
+
tag_id = item.get("tagId")
|
|
349
|
+
print("tag_id: ",tag_id)
|
|
350
|
+
break
|
|
351
|
+
if not tag_id:
|
|
352
|
+
return {"code": 404, "msg": f"未找到曲风: {style_name}", "data": {}}
|
|
353
|
+
|
|
354
|
+
url = f"https://ncm.nekogan.com/style/song?tagId={tag_id}"
|
|
355
|
+
try:
|
|
356
|
+
cookie = _get_cookie()
|
|
357
|
+
resp = requests.get(url, timeout=10, cookies=cookie)
|
|
358
|
+
resp.raise_for_status()
|
|
359
|
+
data = resp.json()
|
|
360
|
+
print("完整响应:", data)
|
|
361
|
+
page = data.get("page", {})
|
|
362
|
+
songs = data.get("songs")
|
|
363
|
+
# 自动适配嵌套结构
|
|
364
|
+
if songs is None:
|
|
365
|
+
for v in data.values():
|
|
366
|
+
if isinstance(v, dict) and "songs" in v:
|
|
367
|
+
songs = v["songs"]
|
|
368
|
+
break
|
|
369
|
+
elif isinstance(v, list):
|
|
370
|
+
for item in v:
|
|
371
|
+
if isinstance(item, dict) and "songs" in item:
|
|
372
|
+
songs = item["songs"]
|
|
373
|
+
break
|
|
374
|
+
if songs is None:
|
|
375
|
+
songs = []
|
|
376
|
+
# 只保留关键信息
|
|
377
|
+
result = []
|
|
378
|
+
for s in songs:
|
|
379
|
+
if isinstance(s, dict):
|
|
380
|
+
result.append({
|
|
381
|
+
"id": s.get("id"),
|
|
382
|
+
"name": s.get("name"),
|
|
383
|
+
"artists": [a.get("name") for a in s.get("ar", [])],
|
|
384
|
+
"album": s.get("al", {}).get("name"),
|
|
385
|
+
"duration": s.get("dt")
|
|
386
|
+
})
|
|
387
|
+
print("最终精简songs:", result)
|
|
388
|
+
return {"code": 200, "msg": "success", "data": result}
|
|
389
|
+
except Exception as e:
|
|
390
|
+
return {"code": 500, "msg": f"请求失败: {e}", "data": {}}
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
def _get_uid(nickname: str) -> dict:
|
|
395
|
+
"""
|
|
396
|
+
根据用户昵称获取其uid
|
|
397
|
+
:param nickname: 用户昵称
|
|
398
|
+
:return: 用户uid json
|
|
399
|
+
"""
|
|
400
|
+
url = f"https://ncm.nekogan.com/get/userids?nicknames={nickname}"
|
|
401
|
+
try:
|
|
402
|
+
resp = requests.get(url, timeout=5)
|
|
403
|
+
resp.raise_for_status()
|
|
404
|
+
data = resp.json()
|
|
405
|
+
# 适配 nicknames 字段结构
|
|
406
|
+
nick_dict = data.get("nicknames", {})
|
|
407
|
+
if not nick_dict:
|
|
408
|
+
return {"code": 404, "msg": "未找到用户", "data": {}}
|
|
409
|
+
# 取第一个昵称和uid
|
|
410
|
+
for n, uid in nick_dict.items():
|
|
411
|
+
return {"code": 200, "msg": "success", "data": {"uid": uid, "nickname": n}}
|
|
412
|
+
return {"code": 404, "msg": "未找到用户", "data": {}}
|
|
413
|
+
except Exception as e:
|
|
414
|
+
return {"code": 500, "msg": f"请求失败: {e}", "data": {}}
|
|
415
|
+
|
|
416
|
+
def _get_user_playlist(nickname: str) -> dict:
|
|
417
|
+
"""
|
|
418
|
+
输入用户昵称,获取用户歌单(只保留必要字段)
|
|
419
|
+
:param nickname: 用户昵称
|
|
420
|
+
:return: 歌单列表json
|
|
421
|
+
"""
|
|
422
|
+
# 第一步:获取uid
|
|
423
|
+
uid_result = _get_uid(nickname)
|
|
424
|
+
if uid_result.get("code") != 200 or "uid" not in uid_result.get("data", {}):
|
|
425
|
+
return {"code": 404, "msg": f"未找到用户: {nickname}", "data": []}
|
|
426
|
+
uid = uid_result["data"]["uid"]
|
|
427
|
+
# 第二步:获取歌单
|
|
428
|
+
url = f"https://ncm.nekogan.com/user/playlist?uid={uid}"
|
|
429
|
+
try:
|
|
430
|
+
resp = requests.get(url, timeout=5)
|
|
431
|
+
resp.raise_for_status()
|
|
432
|
+
data = resp.json()
|
|
433
|
+
playlists = data.get("playlist", [])
|
|
434
|
+
result = []
|
|
435
|
+
for pl in playlists:
|
|
436
|
+
result.append({
|
|
437
|
+
"id": pl.get("id"),
|
|
438
|
+
"name": pl.get("name"),
|
|
439
|
+
"coverImgUrl": pl.get("coverImgUrl"),
|
|
440
|
+
"trackCount": pl.get("trackCount"),
|
|
441
|
+
"playCount": pl.get("playCount"),
|
|
442
|
+
"creator": {
|
|
443
|
+
"userId": pl.get("creator", {}).get("userId"),
|
|
444
|
+
"nickname": pl.get("creator", {}).get("nickname"),
|
|
445
|
+
"avatarUrl": pl.get("creator", {}).get("avatarUrl")
|
|
446
|
+
}
|
|
447
|
+
})
|
|
448
|
+
return {"code": 200, "msg": "success", "data": result}
|
|
449
|
+
except Exception as e:
|
|
450
|
+
return {"code": 500, "msg": f"请求失败: {e}", "data": []}
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
def _login_anonymous() -> dict:
|
|
456
|
+
url = "https://ncm.nekogan.com/register/anonimous"
|
|
457
|
+
try:
|
|
458
|
+
resp = requests.get(url, timeout=10)
|
|
459
|
+
resp.raise_for_status()
|
|
460
|
+
data = resp.json()
|
|
461
|
+
cookie = resp.cookies.get_dict()
|
|
462
|
+
global global_cookie
|
|
463
|
+
global_cookie = cookie
|
|
464
|
+
_save_cookie_to_file(cookie)
|
|
465
|
+
return {"code": 200, "msg": "游客登录成功", "cookie": cookie, "data": data}
|
|
466
|
+
except Exception as e:
|
|
467
|
+
return {"code": 500, "msg": f"游客登录失败: {e}", "cookie": {}, "data": {}}
|
|
468
|
+
|
|
469
|
+
def _login_refresh() -> dict:
|
|
470
|
+
url = "https://ncm.nekogan.com/login/refresh"
|
|
471
|
+
try:
|
|
472
|
+
resp = requests.get(url, timeout=10)
|
|
473
|
+
resp.raise_for_status()
|
|
474
|
+
data = resp.json()
|
|
475
|
+
cookie = resp.cookies.get_dict()
|
|
476
|
+
return {"code": 200, "msg": "登录状态刷新成功", "cookie": cookie, "data": data}
|
|
477
|
+
except Exception as e:
|
|
478
|
+
return {"code": 500, "msg": f"刷新登录失败: {e}", "cookie": {}, "data": {}}
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
def _start_netease_music(exe_path: str) -> dict:
|
|
482
|
+
"""
|
|
483
|
+
启动本地网易云音乐客户端
|
|
484
|
+
:param exe_path: 网易云音乐客户端的 exe 路径
|
|
485
|
+
:return: 启动结果 json
|
|
486
|
+
"""
|
|
487
|
+
try:
|
|
488
|
+
subprocess.Popen(exe_path)
|
|
489
|
+
return {"code": 200, "msg": "网易云音乐已启动", "exe_path": exe_path}
|
|
490
|
+
except Exception as e:
|
|
491
|
+
return {"code": 500, "msg": f"启动失败: {e}", "exe_path": exe_path}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"toplists": [
|
|
3
|
+
{"name": "飙升榜", "id": 19723756},
|
|
4
|
+
{"name": "新歌榜", "id": 3779629},
|
|
5
|
+
{"name": "原创榜", "id": 2884035},
|
|
6
|
+
{"name": "热歌榜", "id": 3778678},
|
|
7
|
+
{"name": "网易云中文说唱榜", "id": 991319590},
|
|
8
|
+
{"name": "网易云古典榜", "id": 71384707},
|
|
9
|
+
{"name": "网易云电音榜", "id": 1978921795},
|
|
10
|
+
{"name": "网易云全球说唱榜", "id": 14028249541},
|
|
11
|
+
{"name": "潮流风向榜", "id": 13372522766},
|
|
12
|
+
{"name": "音乐合伙人推荐榜", "id": 12911403728},
|
|
13
|
+
{"name": "音乐合伙人热歌榜", "id": 12911589513},
|
|
14
|
+
{"name": "音乐合伙人留名榜", "id": 12911619970},
|
|
15
|
+
{"name": "音乐合伙人高分新歌榜", "id": 12911379734},
|
|
16
|
+
{"name": "音乐合伙人高分榜", "id": 12768855486},
|
|
17
|
+
{"name": "黑胶VIP爱听榜", "id": 5453912201},
|
|
18
|
+
{"name": "网易云ACG榜", "id": 71385702},
|
|
19
|
+
{"name": "网易云韩语榜", "id": 745956260},
|
|
20
|
+
{"name": "UK排行榜周榜", "id": 180106},
|
|
21
|
+
{"name": "美国Billboard榜", "id": 60198},
|
|
22
|
+
{"name": "Beatport全球电子舞曲榜", "id": 3812895},
|
|
23
|
+
{"name": "KTV唛榜", "id": 21845217},
|
|
24
|
+
{"name": "日本Oricon榜", "id": 60131},
|
|
25
|
+
{"name": "网易云欧美热歌榜", "id": 2809513713},
|
|
26
|
+
{"name": "网易云欧美新歌榜", "id": 2809577409},
|
|
27
|
+
{"name": "蛋仔派对听歌榜", "id": 8532443277},
|
|
28
|
+
{"name": "AI歌曲榜", "id": 9651277674},
|
|
29
|
+
{"name": "黑胶VIP新歌榜", "id": 7785123708},
|
|
30
|
+
{"name": "黑胶VIP热歌榜", "id": 7785066739},
|
|
31
|
+
{"name": "黑胶VIP爱搜榜", "id": 7785091694},
|
|
32
|
+
{"name": "实时热度榜", "id": 8246775932},
|
|
33
|
+
{"name": "法国 NRJ Vos Hits 周榜", "id": 27135204},
|
|
34
|
+
{"name": "网易云ACG动画榜", "id": 3001835560},
|
|
35
|
+
{"name": "网易云ACG游戏榜", "id": 3001795926},
|
|
36
|
+
{"name": "网易云ACG VOCALOID榜", "id": 3001890046},
|
|
37
|
+
{"name": "网易云日语榜", "id": 5059644681},
|
|
38
|
+
{"name": "网易云摇滚榜", "id": 5059633707},
|
|
39
|
+
{"name": "网易云国风榜", "id": 5059642708},
|
|
40
|
+
{"name": "潜力爆款榜", "id": 5338990334},
|
|
41
|
+
{"name": "网易云民谣榜", "id": 5059661515},
|
|
42
|
+
{"name": "听歌识曲榜", "id": 6688069460},
|
|
43
|
+
{"name": "网络热歌榜", "id": 6723173524},
|
|
44
|
+
{"name": "俄语榜", "id": 6732051320},
|
|
45
|
+
{"name": "越南语榜", "id": 6732014811},
|
|
46
|
+
{"name": "中文慢摇DJ榜", "id": 6886768100},
|
|
47
|
+
{"name": "俄罗斯top hit流行音乐榜", "id": 6939992364},
|
|
48
|
+
{"name": "泰语榜", "id": 7095271308},
|
|
49
|
+
{"name": "BEAT排行榜", "id": 7356827205},
|
|
50
|
+
{"name": "星云榜VOL.30 Wolf Alice新专来袭,人生应该拥有不一样的颜色", "id": 7325478166},
|
|
51
|
+
{"name": "LOOK直播歌曲榜", "id": 7603212484},
|
|
52
|
+
{"name": "赏音榜", "id": 7775163417},
|
|
53
|
+
{"name": "黑胶VIP限免榜", "id": 12344472377},
|
|
54
|
+
{"name": "吉利车友爱听榜", "id": 12717025277},
|
|
55
|
+
{"name": "昊铂车友爱听榜", "id": 10131772880},
|
|
56
|
+
{"name": "埃安车友爱听榜", "id": 10162841534},
|
|
57
|
+
{"name": "欧美R&B榜", "id": 12225155968},
|
|
58
|
+
{"name": "理想车友爱听榜", "id": 8703052295},
|
|
59
|
+
{"name": "比亚迪车友爱听榜", "id": 8702582160},
|
|
60
|
+
{"name": "蔚来车友爱听榜", "id": 8703220480},
|
|
61
|
+
{"name": "极氪车友爱听榜", "id": 8702982391},
|
|
62
|
+
{"name": "特斯拉车友爱听榜", "id": 8703179781},
|
|
63
|
+
{"name": "乐夏榜", "id": 8661209031}
|
|
64
|
+
]
|
|
65
|
+
}
|