8.4 KiB
8.4 KiB
📖 网易云音乐工具箱 (v9.x - Web 应用重构版)
本项目是基于原版 Netease_url 项目的深度重构,旨在将其从一个依赖本地 cookie.txt 的工具,升级为一个支持多用户、无状态后端、客户端认证的现代化 Web 应用。
✨ V9 核心功能升级
相较于原版,V9 架构带来了质的飞跃:
- 用户认证系统:
- 扫码登录:移除本地脚本,实现了完整的 API 驱动的扫码登录流程。
- 客户端凭证:用户 Cookie 不再存储于服务器。登录成功后,Cookie 凭证安全地存储在用户自己的浏览器
localStorage中。
- 无状态 (Stateless) 后端:
- 服务器不保存任何用户状态。所有 API 请求均通过
localStorage自动携带 Cookie 凭证进行鉴权。 - 高并发支持:天然支持多用户同时登录和使用,Cookie 互相隔离,绝不串号。
- 服务器不保存任何用户状态。所有 API 请求均通过
- 全局播放器:
- 实现了一个固定在底部的全局 APlayer 播放器。
- 音乐播放在页面内导航、搜索、解析时不会中断,提供了现代音乐站的体验。
- 高级 UI/UX:
- 智能错误提示:当解析高音质失败时,系统会智能判断用户是否未登录,并提示“请登录黑胶会员”。
- 用户设置:增加了“自动播放”开关,并将其偏好存储在
localStorage。 - 安全加固:在 F12 控制台增加了醒目的安全警告,防止用户被“社会工程学”攻击。
🚀 部署模式 (重要)
V9 架构支持两种部署模式,灵活性极高:
1. 🌍 服务器部署模式 (多用户)
这是标准的线上部署模式。
- 清空
cookie.txt:将cookie.txt文件内容设置为空。 - 启动服务:
python main.py。 - 用户流程:
- 访问者必须点击右上角的“登录”按钮。
- 通过 App 扫码登录。
- Cookie 自动保存在他们各自的浏览器中,全程不接触你的服务器。
2. 💻 本地开发模式 (单用户)
这是你自己本地调试时使用的便捷模式。
- 填写
cookie.txt:在cookie.txt中填入你自己的黑胶会员 Cookie。 - 启动服务:
python main.py。 - 开发者流程:
- 你无需点击“登录”按钮。
- 当你进行解析、下载等操作时,前端
index.html会发送一个“空”的 Cookie。 - 后端
main.py的_get_cookies_from_request函数会检测到 Cookie 为空,并自动降级 (Fallback) 去读取cookie.txt中的内容来完成请求。
🔌 API 接口文档 (V9)
原版的 API 文档 已失效。以下是当前 main.py 提供的有效接口。
请求说明:
- 所有需要鉴权的接口(如下载、解析高音质),都必须在 JSON 请求体中包含
cookie字段。 - 前端
index.html的getApiPayload函数会自动完成这一操作。
/login/qr/generate
-
功能:生成一个用于扫码登录的二维码 Key 和 Base64 图像。
-
方法:
POST -
请求 (JSON):
{}(无参数) -
响应 (JSON):
JSON
{ "success": true, "data": { "qr_key": "xxx-xxx-xxx", "qr_img_b64": "data:image/png;base64,..." } }
/login/qr/check
-
功能:轮询检查二维码的扫码状态。
-
方法:
POST -
请求 (JSON):
JSON
{ "qr_key": "xxx-xxx-xxx" } -
响应 (JSON) (code:
801=等待,802=已扫码,803=成功,800=过期):JSON
{ "success": true, "data": { "code": 803, "cookie": "MUSIC_U=...; __csrf=...;", "message": "登录成功" } }
/search
-
功能:搜索歌曲。
-
方法:
POST -
请求 (JSON):
JSON
{ "keyword": "蓝莲花", "cookie": "...(来自 localStorage)" }
/song
-
功能:解析单曲详情和播放链接。
-
方法:
POST -
请求 (JSON):
JSON
{ "url": "歌曲ID或链接", "level": "lossless", "cookie": "...(来自 localStorage)" }
/playlist
-
功能:解析歌单详情。
-
方法:
POST -
请求 (JSON):
JSON
{ "id": "歌单ID或链接", "cookie": "...(来自 localStorage)" }
/album
-
功能:解析专辑详情。
-
方法:
POST -
请求 (JSON):
JSON
{ "id": "专辑ID或链接", "cookie": "...(来自 localStorage)" }
/download_song_zip
-
功能:打包下载单曲(含封面、歌词)。
-
方法:
POST -
请求 (JSON):
JSON
{ "id": "歌曲ID", "quality": "lossless", "cookie": "...(来自 localStorage)" } -
响应:
application/zip文件流。
/download_playlist_zip
-
功能:打包下载整个歌单。
-
方法:
POST -
请求 (JSON):
JSON
{ "id": "歌单ID", "quality": "lossless", "cookie": "...(来自 localStorage)" } -
响应:
application/zip文件流。
/download_album_zip
-
功能:打包下载整个专辑。
-
方法:
POST -
请求 (JSON):
JSON
{ "id": "专辑ID", "quality": "lossless", "cookie": "...(来自 localStorage)" } -
响应:
application/zip文件流。
📝 架构与开发说明
V9 核心架构
本项目成功的关键在于**“责任分离”**:
main.py(服务层):作为“总指挥”。它只负责编排请求 (接收 HTTP、解析参数) 和响应 (返回 JSON 或 Zip)。它不包含任何Cookie管理或API请求的硬编码逻辑。qr_login.py(认证模块):只负责生成和检查二维码。music_api.py(API 模块):只负责调用网易云的 JSON API (如搜索、歌单详情)。它必须是“无状态的”,并接收cookies作为参数。music_downloader.py(下载模块):只负责获取 文件 API (如歌曲 URL、歌词),并处理打包逻辑。它也必须接收cookies参数。cookie_manager.py(工具模块):只负责解析 Cookie 字符串 (parse_cookie_string) 和读取cookie.txt(作为备用)。index.html(客户端):作为“用户界面”和“凭证保险箱”。它全权负责存储userCookie,并在每一次 API 调用时主动提供它。
项目结构 (V9)
Netease_url/
├── main.py # [!! 重构 !!] Flask 主程序, 路由层
├── music_api.py # API 核心模块 (基本不变, 仅被调用)
├── music_downloader.py # [!! 重构 !!] 下载模块 (已解耦, 需传入 cookie)
├── cookie_manager.py # Cookie 管理工具 (现主要用于解析)
├── qr_login.py # [!! 重构 !!] 从脚本变为 QR 登录 API 模块
├── templates/
│ └── index.html # [!! 重构 !!] V9 前端应用
├── static/ # [!! 新增 !!] 静态文件目录
│ └── favicon.ico # [!! 新增 !!] 网站图标
├── requirements.txt
├── cookie.txt # (可选) 仅用于本地开发模式
└── README_v9.md # (本文档)
未来升级的基石 (v10 展望)
基于 V9 的“无状态后端 + 客户端凭证”架构,我们为下一阶段的升级打下了完美的基础:
- 实现“我的音乐”:
- 后端:在
music_api.py中新增get_user_playlists(uid, cookies)函数。 - 后端:在
main.py中新增/my/playlists路由,它调用上述函数。 - 前端:在登录成功后,
fetch('/my/playlists')并在index.html中渲染一个新区域。
- 后端:在
- 实现“真实进度条”:
- 后端:
main.py引入Flask-SocketIO。 - 后端:
_package_tracks_as_zip在for循环中socketio.emit真实进度。 - 前端:
handleDownloadClick不再使用假进度setInterval,而是socket.on('progress', ...)来更新进度条。
- 后端:
- 实现“歌单即时播放”:
- 前端:在
renderCollection中增加“播放全部”按钮。 - 前端:点击后,循环
tracks列表,为每首歌调用/song接口。 - 前端:将所有返回的
data.url组装成一个列表,一次性globalAPlayer.list.add([...])。
- 前端:在