# mf-plugin-gomusicdl

`mf-plugin-gomusicdl` 是 Music Free 的远程搜索/下载插件集成。
插件基于 Go + Extism Go PDK 实现，并复用了 [`guohuiyuan/go-music-dl`](https://github.com/guohuiyuan/go-music-dl) 的能力。

## 0) 开发速查（10 行版）

```bash
cd plugin/mf-plugin-gomusicdl
go mod tidy && go mod vendor
bash scripts/update-music-lib.sh <version-or-commit>   # 例如 v1.0.9
# 如有冲突，手动修复 vendor/github.com/guohuiyuan/music-lib 后继续
git diff -- vendor/github.com/guohuiyuan/music-lib > patches/music-lib-extism-http.patch
./build.sh
cp plugin.wasm ../../backend/data/plugins/mf-plugin-gomusicdl/plugin.wasm
# 重启 backend，验证 /admin/plugin 与 /admin/music
```

## 1) 目录文件说明

- `manifest.json`：后端插件运行时读取的插件清单
- `main.go`：插件源码
- `go.mod`：模块依赖定义
- `build.sh`：构建脚本
- `plugin.wasm`：构建产物（二进制插件）

## 2) 构建

```bash
cd plugin/mf-plugin-gomusicdl
chmod +x build.sh
./build.sh
```

构建完成后，将 wasm 放到后端插件目录：

```bash
cp plugin.wasm ../../backend/data/plugins/mf-plugin-gomusicdl/plugin.wasm
```

## 2.1) 开发构建指南

本节用于本地迭代插件代码时的开发流程。

### 前置条件

- 已安装 Go 1.25+，并可在 `PATH` 中访问
- 可执行 `bash` 脚本的 shell 环境
- （可选）安装 `jq` 便于调试 JSON 输出

### 开发依赖安装（首次或依赖变更后）

在插件目录执行以下命令，拉取并整理依赖：

```bash
cd plugin/mf-plugin-gomusicdl
go mod tidy
go mod vendor
```

说明：
- `go mod tidy`：补齐/清理 `go.mod`、`go.sum`
- `go mod vendor`：将依赖同步到 `vendor/`，构建时使用 `-mod=vendor`

### 本地开发常用循环

1. 修改 `main.go`（以及相关源码文件）。
2. 重新构建 wasm：

   ```bash
   cd plugin/mf-plugin-gomusicdl
   ./build.sh
   ```

3. 复制 wasm 到后端插件运行目录：

   ```bash
   cp plugin.wasm ../../backend/data/plugins/mf-plugin-gomusicdl/plugin.wasm
   ```

4. 重启后端，并在管理页面验证：
   - `/admin/plugin`
   - `/admin/music`

### 更新 go-music-dl 依赖（按需）

当上游 `go-music-dl` / `music-lib` 发生变化、需要同步本地集成时，可执行：

```bash
cd plugin/mf-plugin-gomusicdl
bash scripts/update-music-lib.sh <version-or-commit>
./build.sh
```

示例：

```bash
bash scripts/update-music-lib.sh v1.0.9
# 或
bash scripts/update-music-lib.sh d96d237a8ad7
```

该脚本会自动执行：
- `go get github.com/guohuiyuan/music-lib@<version-or-commit>`
- `go mod tidy`
- `go mod vendor`
- `git apply patches/music-lib-extism-http.patch`

### 如何打 patch 补丁（手动流程）

如果你修改了 `vendor/github.com/guohuiyuan/music-lib` 下的代码，需要更新补丁文件：

```bash
cd plugin/mf-plugin-gomusicdl
git diff -- vendor/github.com/guohuiyuan/music-lib > patches/music-lib-extism-http.patch
```

验证补丁可应用：

```bash
git apply --check patches/music-lib-extism-http.patch
git apply patches/music-lib-extism-http.patch
```

如果 `git apply` 失败，通常是上游版本变更导致上下文不匹配，需要先手动调整 `vendor/` 中冲突内容，再重新生成补丁文件。

### 推荐工作流（团队协作）

当你需要“升级上游依赖 + 维护本地补丁”时，建议按下面顺序执行：

1. 创建开发分支并进入插件目录。
2. 执行依赖升级（指定版本或 commit）：

   ```bash
   cd plugin/mf-plugin-gomusicdl
   bash scripts/update-music-lib.sh <version-or-commit>
   ```

3. 若脚本提示 patch 冲突，手动修改 `vendor/github.com/guohuiyuan/music-lib` 直到行为符合预期。
4. 重新生成补丁文件：

   ```bash
   git diff -- vendor/github.com/guohuiyuan/music-lib > patches/music-lib-extism-http.patch
   ```

5. 构建并安装插件产物：

   ```bash
   ./build.sh
   cp plugin.wasm ../../backend/data/plugins/mf-plugin-gomusicdl/plugin.wasm
   ```

6. 重启后端，按本文“安装 / 运行检查清单”与“手工 UAT 快速步骤”做回归验证。
7. 提交变更时，至少包含以下文件（按实际变更）：
   - `go.mod`
   - `go.sum`
   - `vendor/...`（如果有）
   - `patches/music-lib-extism-http.patch`
   - `README.md`（如果流程或参数有调整）

`build.sh` 会先执行 `go mod tidy`，再使用以下参数构建：
- `GOOS=wasip1`
- `GOARCH=wasm`
- `-buildmode=c-shared`

输出文件固定为当前目录下的 `plugin.wasm`。

## 3) 必需导出函数

插件必须导出：

- `RemoteSearch`
- `RemoteDownload`

可选导出（元数据刮削）：

- `ScraperSong`：按歌曲名/艺术家在配置的音源中搜索匹配，或解析歌曲链接（`Parse`），返回标准化元数据
- `GetCover`：从 music-lib 搜索/解析结果中的 `Song.Cover`（或平台 Extra 封面字段）下载封面到 `/coverArt`；若同一次刮削中已执行 `ScraperSong`，会复用其匹配结果
- `FetchPlaylist`：通过 [music-lib](https://github.com/guohuiyuan/music-lib) 的 `ParsePlaylist` 解析歌单链接（支持网易云、QQ、酷狗、酷我、汽水、5sing、B 站合集等）
- `GetLyrics`：调用 music-lib 的 `GetLyrics` 拉取歌词；若同一次刮削中已执行 `ScraperSong`，会复用其匹配结果
- `GetAlbumInfo`：按专辑名/艺术家在配置的音源中搜索专辑（`SearchAlbum`），或解析专辑链接（`ParseAlbum`），返回封面、发行日期、简介等

当前后端通过 `manifest.entry.exportedFunctions` 发现并绑定这些函数。

## 4) 安装 / 运行检查清单

1. 构建或获取 `plugin.wasm`，并放置到：
   - `plugin/mf-plugin-gomusicdl/plugin.wasm`
2. 启动后端并启用插件运行时（`plugins.root` 指向你的插件目录）。
3. 打开管理页面：
   - `/admin/plugin`：确认插件可见且已启用
   - `/admin/music`：使用远程搜索/下载界面
4. （可选）配置插件参数：
   - 在插件配置中设置 `download_dir`，覆盖默认下载目录
   - 设置 `sources`（逗号分隔，例如 `qq,netease,kugou,kuwo,soda,bilibili`）
   - 设置 `limit`（每个来源的搜索数量上限，默认 10）
   - 若未设置，后端将回退到默认本地媒体库路径

### 酷狗 / QQ / 网易云下载 Cookie

远程**搜索**通常不需要登录；**下载**酷狗、QQ、网易云等音源时，平台会校验登录态，必须在插件配置里填写对应 Cookie：

| 配置键 | 说明 |
|--------|------|
| `kugou_cookie` | **下载酷狗必填**。浏览器登录 [酷狗音乐](https://www.kugou.com) 后，在开发者工具 → Network 任意请求 → 复制完整 `Cookie` 请求头 |
| `qq_cookie` | 下载 QQ 音乐时可能需要 |
| `netease_cookie` | 下载网易云时可能需要 |

在管理后台 **插件管理** → 启用 **GoMusicDL** → 编辑配置 JSON，例如：

```json
{
  "sources": "kugou,qq,netease",
  "kugou_cookie": "KgMid=...; KugooID=...; t=...; ..."
}
```

未配置 `kugou_cookie` 时也会先尝试下载；仅当获取链接失败时，错误信息会提示配置 Cookie。

## 5) 远程 API 行为（后端实现）

- `POST /rest/api/v1/remote-search`
  - 请求参数：`keyword`，可选 `pluginNames`
  - 响应：NDJSON 流，事件类型包括：
    - `start`
    - `plugin_result`
    - `plugin_error`
    - `complete`

- `POST /rest/api/v1/remote-download`
  - 请求参数：`pluginName`、`record`
  - 行为：插件下载到缓存 -> 元数据补全/校验 -> 移动到目标目录 -> 触发媒体源扫描

## 6) 手工 UAT 快速步骤

1. 在 `/admin/music` 输入关键词搜索
2. 确认结果以流式返回，并包含：
   - 标题、专辑、码率、格式、音质、歌手、平台、时长
3. 在某条结果上点击下载
4. 确认成功提示出现，并触发扫描
5. 扫描后确认歌曲已出现在媒体库中

如有需要，我可以补充 `RemoteSearch` / `RemoteDownload` 的示例输出 JSON。
