142 lines
4.6 KiB
Markdown
142 lines
4.6 KiB
Markdown
# Netease-sync 需求文档
|
||
|
||
## 项目概述
|
||
在当前目录下新建一个简单的 Node.js 项目,实现和 Navidrome 的联动,将网易云音乐歌单同步到 Navidrome。
|
||
|
||
## 核心功能
|
||
|
||
### 1. 歌单输入
|
||
- 前台页面支持输入网易云音乐的歌单 ID 或分享链接
|
||
- 支持从歌单 URL 中提取 ID(如 `https://music.163.com/#/playlist?id=123456` 提取 `123456`)
|
||
- 只支持网易云音乐平台
|
||
|
||
### 2. 歌单读取
|
||
- 调用 TuneHub API 获取歌单详情
|
||
- API: `GET /api/?source=netease&id={playlistId}&type=playlist`
|
||
- 获取歌单名称、封面、描述、歌曲列表等元数据
|
||
|
||
### 3. 歌曲下载
|
||
- 调用现有的 sync-server 下载逻辑
|
||
- 包括元数据写入(title, artist, album, cover)
|
||
- **不创建歌单缓存 JSON 文件**,避免和主 app 混淆
|
||
- 下载到 sync-server 的 music 目录(Navidrome 也读取此目录)
|
||
- 如果下载失败,尝试换源(根据 api.md 支持的平台:netease, kuwo, qq)
|
||
- **不重试**,失败则跳过
|
||
|
||
### 4. Navidrome 歌单创建
|
||
- 使用 Navidrome 的 Subsonic API
|
||
- 在 Navidrome 中创建同名歌单
|
||
- 将下载的歌曲加入歌单
|
||
- 同步歌单的所有元数据(封面、描述等)
|
||
|
||
### 5. 同步策略
|
||
- **增量同步**:只新增歌曲,不删除已有歌曲
|
||
- 通过网易云音乐歌曲 ID 判断,在本地维护一个映射表(netease_song_id -> navidrome_song_id)
|
||
- **定时同步**:每 300 秒自动同步一次(可配置)
|
||
- 支持手动立即同步
|
||
|
||
### 6. 前端界面
|
||
- 显示已添加的歌单列表
|
||
- 显示每个歌单的同步状态(同步中/成功/失败)
|
||
- 显示上次同步时间
|
||
- 提供立即同步按钮
|
||
- 支持添加/删除歌单
|
||
|
||
## 配置项
|
||
|
||
### Docker 环境变量
|
||
- `NAVIDROME_URL`: Navidrome 服务器地址(如 `http://navidrome:4533`)
|
||
- `NAVIDROME_USERNAME`: Navidrome 用户名
|
||
- `NAVIDROME_PASSWORD`: Navidrome 密码
|
||
- `SYNC_INTERVAL`: 同步间隔(秒),默认 300
|
||
- `SYNC_SERVER_URL`: sync-server 地址(如 `http://sync-service:3001`)
|
||
- `SYNC_SERVER_TOKEN`: sync-server 的 token(用于 KV API)
|
||
- `TUNEHUB_API_URL`: TuneHub API 地址(如 `https://music-dl.sayqz.com`)
|
||
|
||
## 技术架构
|
||
|
||
### 后端
|
||
- Node.js + Express
|
||
- 调用 sync-server 的 KV API 传递歌曲列表
|
||
- 调用 Navidrome Subsonic API 创建歌单
|
||
- 定时任务:每 N 秒检查并同步歌单
|
||
- 数据持久化:JSON 文件存储歌单列表和映射表
|
||
|
||
### 前端
|
||
- 简单的 HTML + JavaScript 页面
|
||
- 实时显示同步状态(WebSocket 或轮询)
|
||
- 添加/删除歌单功能
|
||
|
||
## Subsonic API 集成
|
||
|
||
### 认证
|
||
所有 API 请求需要包含以下参数:
|
||
- `u`: 用户名
|
||
- `p`: 密码(明文或 hex 格式)
|
||
- `v`: API 版本(如 `1.16.0`)
|
||
- `c`: 客户端名称(如 `netease-sync`)
|
||
|
||
### 主要端点
|
||
1. **创建歌单**: `createPlaylist`
|
||
- 参数:`name`(歌单名称)、`songId`(歌曲 ID 列表,逗号分隔)
|
||
- 返回:创建的歌单 ID
|
||
|
||
2. **更新歌单**: `updatePlaylist`
|
||
- 参数:`playlistId`(歌单 ID)、`songIdToAdd`(要添加的歌曲 ID)
|
||
|
||
3. **获取歌单列表**: `getPlaylists`
|
||
|
||
4. **搜索歌曲**: `search3`
|
||
- 参数:`query`(搜索关键词)
|
||
- 用于根据文件名查找 Navidrome 中的歌曲 ID
|
||
|
||
## 歌曲匹配策略
|
||
|
||
参考 sync-server 的去重逻辑,文件名格式为:`Artist - Name [source_id].ext`
|
||
|
||
匹配步骤:
|
||
1. 下载完成后,Navidrome 会自动扫描新歌曲
|
||
2. 通过 `search3` 搜索文件名(如 `Artist - Name [netease_id]`)来获取 Navidrome 的歌曲 ID
|
||
3. 将网易云音乐歌曲 ID 映射到 Navidrome 歌曲 ID
|
||
|
||
## 数据结构
|
||
|
||
### 歌单列表 (playlists.json)
|
||
```json
|
||
{
|
||
"playlists": [
|
||
{
|
||
"id": "netease_123456",
|
||
"neteaseId": "123456",
|
||
"name": "歌单名称",
|
||
"cover": "封面URL",
|
||
"description": "歌单描述",
|
||
"navidromePlaylistId": "navidrome_playlist_id",
|
||
"lastSyncTime": "2025-01-12T10:00:00Z",
|
||
"syncStatus": "success",
|
||
"songMapping": {
|
||
"netease_song_id_1": "navidrome_song_id_1",
|
||
"netease_song_id_2": "navidrome_song_id_2"
|
||
}
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
## 换源逻辑
|
||
|
||
如果网易云音乐下载失败,尝试换源:
|
||
- 优先级:kuwo -> qq
|
||
- 每个平台只尝试一次,不重试
|
||
|
||
## 错误处理
|
||
|
||
- 某首歌下载失败,跳过继续下载其他歌曲
|
||
- 不重试
|
||
- 错误日志输出到 Docker 控制台,不存储
|
||
|
||
## Navidrome 扫描延迟
|
||
|
||
- Navidrome 扫描新歌曲很快,不处理延迟
|
||
- 在添加到歌单时直接搜索歌曲 ID
|