From 39e0e0718d86b6be4dd5d4def2ace122bfcb990d Mon Sep 17 00:00:00 2001 From: Developer Date: Thu, 28 May 2026 13:00:21 +0000 Subject: [PATCH 1/3] feat: add relay_play_state_submit API for song play state tracking - Add new interface /api/relay/play/state/submit - Support sessionId tracking for play sessions - Support playMode parameter (list_loop, single_loop, random, single) - Add parameter validation - Include integration documentation --- docs/RELAY_PLAY_STATE_INTEGRATION.md | 159 +++++++++++++++++++++++++++ module/relay_play_state_submit.js | 32 ++++++ 2 files changed, 191 insertions(+) create mode 100644 docs/RELAY_PLAY_STATE_INTEGRATION.md create mode 100644 module/relay_play_state_submit.js diff --git a/docs/RELAY_PLAY_STATE_INTEGRATION.md b/docs/RELAY_PLAY_STATE_INTEGRATION.md new file mode 100644 index 0000000..7c342a7 --- /dev/null +++ b/docs/RELAY_PLAY_STATE_INTEGRATION.md @@ -0,0 +1,159 @@ +# relay_play_state_submit 集成说明 + +## 修改内容 + +### 1. 新增 API 接口模块 + +**文件**: `module/relay_play_state_submit.js` + +**功能**: 提交歌曲播放状态,支持会话追踪和播放模式记录 + +**接口地址**: `/api/relay/play/state/submit` + +**参数说明**: +- `id` (必填): 歌曲 ID +- `sessionId` (必填): 播放会话 ID(12 位大写字母和数字) +- `progress` (可选): 播放进度(秒),默认 0 +- `playMode` (可选): 播放模式,默认 `list_loop` +- `type` (可选): 资源类型,默认 `song` + +**参数校验**: +```javascript +if (!id || !sessionId) { + return Promise.reject({ + status: 400, + body: { + code: 400, + msg: '缺少必要参数:id, sessionId', + }, + }) +} +``` + +### 2. 更新 API 文档 + +**文件**: `public/docs/home.md` + +在文档末尾添加了接口说明,包括: +- 接口功能说明 +- 参数列表 +- 调用示例 + +### 3. 集成到自动任务脚本 + +**文件**: `auto_tasks_enhanced.js` + +**修改内容**: + +1. **导入接口** (Line 32): +```javascript +const { + // ... 其他接口 + relay_play_state_submit, + // ... 其他接口 +} = require('@neteasecloudmusicapienhanced/api') +``` + +2. **组合使用两个接口** (Lines 447-483): +```javascript +// 2. 上传听歌记录(组合使用两个接口) +console.log(' [2] 上传播放状态...') +try { + // 生成 sessionId(12 位大写字母和数字) + const sessionId = Math.random().toString(36).substring(2, 8).toUpperCase() + + Math.random().toString(36).substring(2, 8).toUpperCase() + + // 2.1 提交播放开始状态(relay_play_state_submit) + const relayResult = await relay_play_state_submit({ + cookie, + id: song.id, + sessionId: sessionId, + progress: 0, + playMode: 'list_loop' + }) + if (relayResult.body.code === 200) { + console.log(' ✓ 播放状态已提交') + } + + await sleep(1000) + + // 2.2 上传完整听歌记录(scrobble) + const playTime = Math.floor(song.dt / 1000) + const scrobbleResult = await scrobble({ + cookie, + id: song.id, + sourceid: playlistId, + time: playTime + }) + if (scrobbleResult.body.code === 200) { + const timeStr = (playTime / 60).toFixed(2) + console.log(` ✓ 听歌记录已上传 (${timeStr}分钟)`) + successCount++ + } +} catch (e) { + console.log(` 上传失败:${e.message}`) +} +``` + +## 工作流程 + +完整的 VIP 音乐任务播放流程: + +``` +1. 收藏歌曲 (song_like) + ↓ +2. 提交播放状态 (relay_play_state_submit) + ↓ (延迟 1 秒) +3. 上传听歌记录 (scrobble) + ↓ (延迟 1 秒) +4. 等待 30-60 秒 + ↓ +5. 取消收藏 (song_like) +``` + +## 优势 + +### 组合使用两个接口的好处: + +1. **relay_play_state_submit**: + - 实时播放状态同步 + - 会话追踪(sessionId) + - 播放模式记录 + - 更符合现代播放器行为 + +2. **scrobble**: + - 完整的听歌记录 + - 播放时长统计 + - 来源歌单记录 + - 用于任务完成判定 + +3. **组合效果**: + - 更真实的播放行为模拟 + - 完整的数据记录 + - 提高任务完成可靠性 + +## Pull Request + +**PR #181**: https://github.com/NeteaseCloudMusicApiEnhanced/api-enhanced/pull/181 + +**提交内容**: +- ✅ 新增 `module/relay_play_state_submit.js` +- ✅ 更新 `public/docs/home.md` +- ✅ 参数校验修复 +- ✅ 使用示例更新 + +## 测试验证 + +接口已通过测试: +``` +✓ relay_play_state_submit 成功 +✓ scrobble 成功 +✓ 组合使用正常 +``` + +## 兼容性 + +- ✅ 保持原有 `scrobble` 接口不变 +- ✅ 新增 `relay_play_state_submit` 可选使用 +- ✅ 向后兼容 +- ✅ 不影响现有功能 diff --git a/module/relay_play_state_submit.js b/module/relay_play_state_submit.js new file mode 100644 index 0000000..fd698e9 --- /dev/null +++ b/module/relay_play_state_submit.js @@ -0,0 +1,32 @@ +// 提交歌曲播放状态 + +const createOption = require('../util/option.js') +module.exports = (query, request) => { + const { id, sessionId, progress = 0, playMode = 'list_loop', type = 'song' } = query + + if (!id || !sessionId) { + return Promise.reject({ + status: 400, + body: { + code: 400, + msg: '缺少必要参数:id, sessionId', + }, + }) + } + + const playStateSubmitReq = JSON.stringify({ + resource: { + id: String(id), + type: type + }, + progress: progress, + sessionId: sessionId, + playMode: playMode + }) + + const data = { + playStateSubmitReq: playStateSubmitReq + } + + return request('/api/relay/play/state/submit', data, createOption(query, 'weapi')) +} From cb5b2e7d0f981a849d73dd6ea19772775750af76 Mon Sep 17 00:00:00 2001 From: MoEFusion Date: Sat, 6 Jun 2026 15:22:25 +0800 Subject: [PATCH 2/3] Apply suggestions from code review Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- docs/RELAY_PLAY_STATE_INTEGRATION.md | 3 +-- module/relay_play_state_submit.js | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/RELAY_PLAY_STATE_INTEGRATION.md b/docs/RELAY_PLAY_STATE_INTEGRATION.md index 7c342a7..fa31d06 100644 --- a/docs/RELAY_PLAY_STATE_INTEGRATION.md +++ b/docs/RELAY_PLAY_STATE_INTEGRATION.md @@ -60,8 +60,7 @@ const { console.log(' [2] 上传播放状态...') try { // 生成 sessionId(12 位大写字母和数字) - const sessionId = Math.random().toString(36).substring(2, 8).toUpperCase() + - Math.random().toString(36).substring(2, 8).toUpperCase() + const sessionId = Array.from({ length: 12 }, () => 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'[Math.floor(Math.random() * 36)]).join('') // 2.1 提交播放开始状态(relay_play_state_submit) const relayResult = await relay_play_state_submit({ diff --git a/module/relay_play_state_submit.js b/module/relay_play_state_submit.js index fd698e9..ac87205 100644 --- a/module/relay_play_state_submit.js +++ b/module/relay_play_state_submit.js @@ -19,7 +19,7 @@ module.exports = (query, request) => { id: String(id), type: type }, - progress: progress, + progress: Number(progress) || 0, sessionId: sessionId, playMode: playMode }) From e7ff0da4ba336aa706fa2c38d06d4ed7a4e4c50b Mon Sep 17 00:00:00 2001 From: MoeFurina Date: Sat, 6 Jun 2026 15:33:15 +0800 Subject: [PATCH 3/3] =?UTF-8?q?refactor:=20=E8=87=AA=E5=8A=A8=E7=94=9F?= =?UTF-8?q?=E6=88=90sessionID?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/RELAY_PLAY_STATE_INTEGRATION.md | 158 --------------------------- module/relay_play_state_submit.js | 35 ++++-- public/docs/home.md | 12 ++ 3 files changed, 38 insertions(+), 167 deletions(-) delete mode 100644 docs/RELAY_PLAY_STATE_INTEGRATION.md diff --git a/docs/RELAY_PLAY_STATE_INTEGRATION.md b/docs/RELAY_PLAY_STATE_INTEGRATION.md deleted file mode 100644 index fa31d06..0000000 --- a/docs/RELAY_PLAY_STATE_INTEGRATION.md +++ /dev/null @@ -1,158 +0,0 @@ -# relay_play_state_submit 集成说明 - -## 修改内容 - -### 1. 新增 API 接口模块 - -**文件**: `module/relay_play_state_submit.js` - -**功能**: 提交歌曲播放状态,支持会话追踪和播放模式记录 - -**接口地址**: `/api/relay/play/state/submit` - -**参数说明**: -- `id` (必填): 歌曲 ID -- `sessionId` (必填): 播放会话 ID(12 位大写字母和数字) -- `progress` (可选): 播放进度(秒),默认 0 -- `playMode` (可选): 播放模式,默认 `list_loop` -- `type` (可选): 资源类型,默认 `song` - -**参数校验**: -```javascript -if (!id || !sessionId) { - return Promise.reject({ - status: 400, - body: { - code: 400, - msg: '缺少必要参数:id, sessionId', - }, - }) -} -``` - -### 2. 更新 API 文档 - -**文件**: `public/docs/home.md` - -在文档末尾添加了接口说明,包括: -- 接口功能说明 -- 参数列表 -- 调用示例 - -### 3. 集成到自动任务脚本 - -**文件**: `auto_tasks_enhanced.js` - -**修改内容**: - -1. **导入接口** (Line 32): -```javascript -const { - // ... 其他接口 - relay_play_state_submit, - // ... 其他接口 -} = require('@neteasecloudmusicapienhanced/api') -``` - -2. **组合使用两个接口** (Lines 447-483): -```javascript -// 2. 上传听歌记录(组合使用两个接口) -console.log(' [2] 上传播放状态...') -try { - // 生成 sessionId(12 位大写字母和数字) - const sessionId = Array.from({ length: 12 }, () => 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'[Math.floor(Math.random() * 36)]).join('') - - // 2.1 提交播放开始状态(relay_play_state_submit) - const relayResult = await relay_play_state_submit({ - cookie, - id: song.id, - sessionId: sessionId, - progress: 0, - playMode: 'list_loop' - }) - if (relayResult.body.code === 200) { - console.log(' ✓ 播放状态已提交') - } - - await sleep(1000) - - // 2.2 上传完整听歌记录(scrobble) - const playTime = Math.floor(song.dt / 1000) - const scrobbleResult = await scrobble({ - cookie, - id: song.id, - sourceid: playlistId, - time: playTime - }) - if (scrobbleResult.body.code === 200) { - const timeStr = (playTime / 60).toFixed(2) - console.log(` ✓ 听歌记录已上传 (${timeStr}分钟)`) - successCount++ - } -} catch (e) { - console.log(` 上传失败:${e.message}`) -} -``` - -## 工作流程 - -完整的 VIP 音乐任务播放流程: - -``` -1. 收藏歌曲 (song_like) - ↓ -2. 提交播放状态 (relay_play_state_submit) - ↓ (延迟 1 秒) -3. 上传听歌记录 (scrobble) - ↓ (延迟 1 秒) -4. 等待 30-60 秒 - ↓ -5. 取消收藏 (song_like) -``` - -## 优势 - -### 组合使用两个接口的好处: - -1. **relay_play_state_submit**: - - 实时播放状态同步 - - 会话追踪(sessionId) - - 播放模式记录 - - 更符合现代播放器行为 - -2. **scrobble**: - - 完整的听歌记录 - - 播放时长统计 - - 来源歌单记录 - - 用于任务完成判定 - -3. **组合效果**: - - 更真实的播放行为模拟 - - 完整的数据记录 - - 提高任务完成可靠性 - -## Pull Request - -**PR #181**: https://github.com/NeteaseCloudMusicApiEnhanced/api-enhanced/pull/181 - -**提交内容**: -- ✅ 新增 `module/relay_play_state_submit.js` -- ✅ 更新 `public/docs/home.md` -- ✅ 参数校验修复 -- ✅ 使用示例更新 - -## 测试验证 - -接口已通过测试: -``` -✓ relay_play_state_submit 成功 -✓ scrobble 成功 -✓ 组合使用正常 -``` - -## 兼容性 - -- ✅ 保持原有 `scrobble` 接口不变 -- ✅ 新增 `relay_play_state_submit` 可选使用 -- ✅ 向后兼容 -- ✅ 不影响现有功能 diff --git a/module/relay_play_state_submit.js b/module/relay_play_state_submit.js index ac87205..3b948e8 100644 --- a/module/relay_play_state_submit.js +++ b/module/relay_play_state_submit.js @@ -1,15 +1,28 @@ // 提交歌曲播放状态 const createOption = require('../util/option.js') -module.exports = (query, request) => { - const { id, sessionId, progress = 0, playMode = 'list_loop', type = 'song' } = query +const generateSessionId = () => + Array.from( + { length: 12 }, + () => + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'[Math.floor(Math.random() * 36)], + ).join('') - if (!id || !sessionId) { +module.exports = (query, request) => { + const { + id, + sessionId, + progress = 0, + playMode = 'list_loop', + type = 'song', + } = query + + if (!id) { return Promise.reject({ status: 400, body: { code: 400, - msg: '缺少必要参数:id, sessionId', + msg: '缺少必要参数:id', }, }) } @@ -17,16 +30,20 @@ module.exports = (query, request) => { const playStateSubmitReq = JSON.stringify({ resource: { id: String(id), - type: type + type: type, }, progress: Number(progress) || 0, - sessionId: sessionId, - playMode: playMode + sessionId: sessionId || generateSessionId(), + playMode: playMode, }) const data = { - playStateSubmitReq: playStateSubmitReq + playStateSubmitReq: playStateSubmitReq, } - return request('/api/relay/play/state/submit', data, createOption(query, 'weapi')) + return request( + '/api/relay/play/state/submit', + data, + createOption(query, 'weapi'), + ) } diff --git a/public/docs/home.md b/public/docs/home.md index dba5c69..8e611d9 100644 --- a/public/docs/home.md +++ b/public/docs/home.md @@ -2492,6 +2492,18 @@ privilege:权限相关信息 **调用例子 :** `/scrobble?id=518066366&sourceid=36780169&time=291` +### 提交歌曲播放状态 + +说明 : 调用此接口可提交歌曲播放状态,支持会话追踪和播放模式记录,未传入 `sessionId` 时后端会自动生成 + +**必选参数 :** `id`: 歌曲 id + +**可选参数 :** `sessionId`: 播放会话 ID(12 位大写字母和数字),不传则自动生成, `progress`: 播放进度(秒),默认 0, `playMode`: 播放模式,默认 `list_loop`, `type`: 资源类型,默认 `song` + +**接口地址 :** `/relay/play/state/submit` + +**调用例子 :** `/relay/play/state/submit?id=518066366&progress=30` + ### 热门歌手 说明 : 调用此接口 , 可获取热门歌手数据