Appearance
云曲助手 SDK 通讯规范
云曲助手(fsMusicUpload 桌面端)通过「登录 → 上传配置 → 上传文件 → 上传回调 → 资源入库」链路与目标站点通讯。 目标站点服务端需按本规范实现一套 云曲助手 SDK 接口,返回的
apiMap决定客户端后续调用哪些 URL。 客户端统一约定:所有业务接口均为 POST,认证使用请求头x-api-key。
1. 总体流程
[云曲助手客户端] [站点服务端 SDK]
│ 1. POST login(携带 apikey) │
│ ───────────────────────────────▶ │ 校验 key → 返回站点配置(apiMap / bitrate / savekey 等)
│ 2. POST storeCheck(可选) │ 重复发布检测(标题/文件名)
│ 3. POST uploadConfig │ 返回上传策略(存储类型 + 预签名地址)
│ 4. PUT/分片 上传文件 │ 直接 PUT 到预签名 URL(不经业务接口)
│ 5. POST uploadNotify │ 上传完成回调(合并分片 / 转码 / 入库返回 URL)
│ 6. POST storeData │ 资源入库(歌曲元数据 + playlist/downlist 地址)- 渲染进程不直接发 HTTP;全部经
src/utils/ClientRequest.js → IPC → electron/requestHttp.js(net.request),但 SDK 通讯的目标 URL 由服务端apiMap动态给出,客户端直接用完整 URL 请求(requestHttp.get/post对http(s)://开头的地址直连)。 - 客户端主进程调用入口:
electron/fileServer.js(发布流水线)、electron/upload/index.js(上传调度)、src/api/fastMusic.js(登录/检测)。
2. 认证与公共约定
| 项 | 约定 |
|---|---|
| 认证头 | x-api-key: <apikey>(除 login 外所有接口必须携带) |
| 内容类型 | application/json(login / storeCheck / storeData / uploadConfig / uploadNotify) |
| 成功判定 | 客户端兼容两种成功码:code == 1 或 code == 200 |
| 返回结构 | { code, msg/message, data } |
参考实现:Node 端
Backend.js定义noNeedLogin = ["*"],在signVerify()里用x-api-key查music_author.apikey校验,status !== 1拒绝。 PHP 端BackendController.php在_initialize()里排除login/uploadNotify后校验x-api-key == $up_apikey。
3. 平台类型(客户端侧)
客户端添加平台时选择类型(src/pages/web-platform.vue):
| type 值 | login 路径 | 说明 |
|---|---|---|
djaod(默认) | {url}/api/v1/backend/login | 云曲工坊官方系统(fastify 后端,多类型存储) |
sdk(自定义) | {url}/music/backend/login | 第三方自建站点(PHP 等,php 自建存储) |
客户端 src/api/fastMusic.js 中 adminLoginApi(url, { apikey, type }) 按 type 映射 login 地址。 其余接口路径均由服务端 apiMap 返回,客户端不再硬编码(PHP 端示例均形如 {url}/music/backend/xxx)。
4. 接口详解
4.1 登录 POST login
请求体:{ apikey: string }(type 由客户端决定访问哪个 login 路径)
成功返回 data 字段(客户端会整体 Object.assign 到站点对象并落库 IndexedDB):
| 字段 | 类型 | 说明 |
|---|---|---|
title | string | 站点名称 |
author_id | string/number | 默认音乐人 ID(发布时兜底) |
isSystem | bool | 是否为平台自有音乐人账号(1) |
channel | array | 分类树 [{ label, value, source_type?, children? }] |
sourceTypeOptions | array | 来源类型选项 [{ label, value }],如 set(串烧)/single(单曲) |
bitrate | array | 音质配置 [first, last]:{ bitrate, suffix, title, type },type 为 play(播放)/down(下载) |
isAmix | bool | 是否开启水印混音 |
amixOffset | number | 水印偏移(秒) |
AmixURL | string | 水印音频下载地址 |
savekey | string | 上传路径模板(含占位符),见 §7 |
upload_type | string | 存储类型(PHP 端在 login 直接返回,如 local) |
uploadconfig | object | 云存储配置(isSystem 且 djaod 时返回,含 xxx_config 的 savekey 等) |
apiMap | object | 各接口 URL 映射(见下表) |
viplist | array | VIP 等级下拉 [{ label, value }](isSystem 才返回) |
apiMap 结构(客户端后续全部按此 URL 请求):
| key | 用途 |
|---|---|
storeCheck | 发布前重复检测 |
storeData | 资源入库 |
uploadConfig | 获取上传配置 |
uploadNotify | 上传完成回调 |
authors | 音乐人下拉分页 |
albums | 专辑下拉分页 |
Node 端
Backend.js:apiMap基于site_url拼/api/v1/backend/*;PHP 端BackendController.php基于$site_url拼/music/backend/*。
4.2 发布前检测 POST storeCheck
请求体(二选一,均可带):
{ title: string }→ 检测该标题是否已发布{ fileNames: string[] }→ 批量文件名重复检测
返回:
- Node 端:
title检测:{ code: 200, msg: "标题检测通过" };已存在返回{ code: 400, msg: "已发布过《X》,或者没有彻底删除" }fileNames检测:{ code: 200, data: string[] }→data必须是已存在的文件名数组
- PHP 端:
check()目前恒返回{ code: 200, msg: "...检测通过", data: [] }
客户端 fileServer.js 用返回的已存在文件名数组过滤待发布列表(已存在的跳过并提示不重复发布)。
4.3 获取上传配置 POST uploadConfig
请求体:{ mime, filename, size, hls, path }(hls 为 m3u8 关联 ts 数组,path 本地路径)
返回 data 通用字段:
| 字段 | 说明 |
|---|---|
upload_type | 存储类型:local / oss / cos / qiniu / bitiful / minio / r2 / s3 |
isChunk | 是否分片 |
key | 存储 Key(对象路径) |
putUrl | 单文件预签名上传地址(PUT) |
uploadId | 分片上传 ID(合并用) |
chunkUrls | 分片预签名 URL 数组(按 PartNumber 顺序) |
chunkSize | 分片大小(字节) |
chunkCount | 分片数 |
notifyUrl | 分片合并回调地址(local 分片用) |
存储类型说明:
- local(php 自建存储):由上传服务器(如 go-uploads
http://60.174.238.119:9000)接收。putUrl/chunkUrls为带签名参数的地址:{up_apiurl}/upload?signature=...&options=...;uploadId = md5(json(key, contenttype));分片大小固定2 * 1024 * 1024。客户端electron/upload/localUpload.js用net.request直接 PUT,分片传完POST notifyUrl(body{key, uploadId})让服务端合并。 - oss:返回 STS 临时凭证 + region/bucket;客户端用
ali-ossSDKmultipartUpload(electron/upload/ossUpload.js)。 - qiniu:返回
uploadUrl+uploadToken;客户端 V2 分片 PUT,每片返回etag记入contexts(electron/upload/qiniuUpload.js)。 - cos / bitiful / minio / r2 / s3:返回
putUrl/chunkUrls预签名地址(S3 兼容,electron/upload/S3Upload.js)。
Node 端
UploadService.js:init(storage)按request.body.mime/filename/size/hls计算;local()返回{ uploadUrl: "/v1/attachment/upload", mimetype: "..." }。 PHP 端uploadConfig():$size > 2MB则分片,否则单文件预签名。
4.4 上传完成回调 POST uploadNotify
请求体(_options = uploadConfig 返回 + 客户端补充):
json
{
"mime": "audio/mp3", "filename": "xxx.mp3", "size": 123456,
"md5": "…", "key": "uploads/…/xxx.mp3", "savekey": "…",
"transcode": false,
"upload_type": "local", "isChunk": false,
"uploadId": "…", "contexts": [ { "PartNumber": 1, "ETag": "…" } ]
}返回 data:{ url, fullurl, cover, md5 }(url 客户端直接作为该产物的访问地址;分片合并时由服务端回填最终 key/url)。
服务端职责:
- 分片 → 合并(本地存储由 go-uploads
/upload/complete合并;云存储走各自mkfile)。 transcode与bitrate存在且mime为音视频 → 触发服务端转码,返回转码后play/down各码率地址。- 图片类 → 按 md5 去重入库附件表。
说明:
uploadNotify与storeData是两个不同环节——uploadNotify负责「文件已上传、拿 URL」,storeData负责「把歌曲元数据+URL 写入曲库」。
4.5 资源入库 POST storeData
请求体(Node 端 Backend.js 的完整字段):
| 字段 | 类型 | 说明 |
|---|---|---|
name | string | 文件名(服务端自动去扩展名作 title) |
source_type | string | 来源类型(set/single/…) |
type_ids | array | 分类 ID(Node 端为 ObjectId 数组;PHP 端取数组最后一个当 genre_id) |
author_id | string | 音乐人 ID |
image | string | 封面地址(客户端上传前为 {uploadType}::remote,回调后为实际 URL) |
waveform | string | 波形图地址 |
price / score | number | 售价 / 积分 |
playlist | array | 播放音频列表(客户端转码产物) |
downlist | array | 下载音频列表 |
disklist | array | 网盘列表 [{ type, link, password }] |
extension | string | 文件格式 |
size | number | 字节 |
duration | number | 秒 |
bitrate | number | 码率 |
samplerate | number | 采样率 |
key | string | 音调 |
bpm | number | 节拍 |
channels | number | 声道 |
md5 | string | 转码唯一标识 → 服务端存 file_md5 |
vip / album_ids / designated_upload_date / createtime | - | 仅 isSystem(平台音乐人)时发送 |
playlist / downlist 元素结构(客户端 transcodeRowToUploads 生成):
json
{ "type": "local", "name": "128k", "title": "普通", "url": "uploads/play/128k/…/xxx.mp3" }(type 为存储类型;url 为产物 key)
Node 端入库逻辑:
- 标题重复 →
{ code: 400, msg: "您已经发布过该作品" } system_author_id == author_id→ 直接status=1,可带 vip/定时发布- 否则按
is_music_audit配置走审核(1→status=2 待审,2→敏感词检测) - 入库后触发
post_music_settledhook
PHP 端入库逻辑(BackendController.php storeData()):
- 同名存在 → 覆盖更新(songs + songs_extend + 专辑关联),否则新建
source_type == 'single'→car_id = 0vip等级 →down_rule.group收集所有id >= vip的用户组playlist[0]→listen_url,downlist[0]→down_url,disklist[0]→disk_url/disk_passbitrate单位 bps →/1000取整为 kbps
5. 下拉分页接口
5.1 音乐人 POST/GET authors
请求参数(query):pageNum、pageSize、keyword、_id(置顶项)
返回 data:{ list: [{ label, value }], total, pageNum }
- Node 端:
list元素含label(=title) /value(=ObjectId),_id命中时插到第一页首位。 - PHP 端:
list元素{ label: name, value: id },_id命中时同样置顶。
5.2 专辑 POST/GET albums
同上,模型换为 music_album / album。
6. 客户端调用链(代码索引)
| 环节 | 客户端代码 |
|---|---|
| 平台选择 / login | src/pages/web-platform.vue、src/api/fastMusic.js:adminLoginApi |
| 站点配置合并 | src/utils/siteWeb.js:prepareSiteWeb(login 结果 + site_configs 缓存) |
| 发布流水线 | electron/fileServer.js:corePublish → storeCheck → 转码 → Upload.batchUpload → publishWork(storeData) |
| 转码/产物规划 | electron/fileServer.js:transcodeRowToUploads(savekey + bitrate + playlist/downlist) |
| 上传调度 | electron/upload/index.js:Upload.start(按 upload_type 选实现) |
| 各存储实现 | electron/upload/{localUpload,S3Upload,ossUpload,qiniuUpload,cosUpload}.js |
| HTTP 层 | electron/requestHttp.js(net.request,对完整 URL 直连) |
| 登录 API 映射 | src/api/fastMusic.js(djaod→/api/v1/backend/login,sdk→/music/backend/login) |
7. savekey 路径模板
savekey 由服务端在 login 返回,客户端 electron/Utils.js:getSavekey 渲染,支持占位符:
| 占位符 | 含义 |
|---|---|
{year} {mon} {day} {hour} {min} {sec} | 当前时间 |
{mime} / {mimeType} | 主类型(如 audio) |
{filename} | 原始文件名(截断 100 字符) |
{fileprefix} | 去掉后缀的文件名 |
{suffix} / {.suffix} | 后缀(无点/带点) |
{filemd5} | 文件 MD5 |
{random} / {random32} | 16/32 位随机串 |
{category} | 分类(默认 all) |
{index} | index(m3u8 用) |
默认模板(Node 端):uploads/{year}{mon}{day}/{filemd5}{.suffix} PHP 端示例:/uploads/{year}-{mon}-{day}/{filename}{.suffix}
转码产物(playlist/downlist)的远程路径由客户端按
uploadsDirectory/type/bitrate/dateDirectory/文件名拼装(见fileServer.js:transcodeRowToUploads)。
8. 两套服务端实现对照
| 维度 | djaod(Node/fastify)Backend.js | php 自建 BackendController.php |
|---|---|---|
| login 路径 | /api/v1/backend/login | /music/backend/login |
| 存储能力 | 多类型(local/qiniu/oss/cos/bitiful/minio/r2),upload_config 存 savekey | 固定 local(go-uploads 服务器承接) |
| 认证 | x-api-key 查 music_author(key 有效性 + status) | _initialize 比较 $up_apikey,排除 login/uploadNotify |
| 上传签名 | 云存储 SDK 内部签名 | hash_hmac('sha256', json, key) + md5(json) 生成 uploadId |
| 重复策略 | storeData 直接拒绝重复标题 | storeData 同名覆盖更新 |
| 音频处理 | 服务端可转码(transcode/bitrate) | 客户端本地转码,上传产物 |
| 分类 | system_type 树(channel) | Genre 树 |
| VIP | vip_level 集合 → viplist | get_user_groups() → viplist |
| 审核 | is_music_audit 配置 | 固定 status=1 直接上架 |
9. 接入清单(第三方站点需实现)
login(返回apiMap、savekey、bitrate、sourceTypeOptions、channel、author_id、isSystem)uploadConfig(返回upload_type+ 预签名上传地址)uploadNotify(返回最终url,必要时合并分片/转码)storeData(歌曲元数据入库,按客户端字段)- (可选)
storeCheck、authors、albums
若第三方站点无法返回完整
apiMap,可在客户端 fallback:src/api/fastMusic.js中checkloginApi/storeDataApi等已按{url}/music/backend/*拼接(sdk 类型默认路径)。