mirror of
https://github.com/NeteaseCloudMusicApiEnhanced/api-enhanced.git
synced 2026-10-20 14:41:23 +08:00
Merge branch 'feat-lottery' of https://github.com/ZxwyWebSite/api-enhanced into pr/215
This commit is contained in:
+220
-4
@@ -283,7 +283,9 @@ AI 生成的图,仅供娱乐()
|
||||
|
||||
`md5_password`: md5 加密后的密码,传入后 `password` 参数将失效
|
||||
|
||||
`captcha`: 验证码,使用 [`/captcha/sent`](#发送验证码)接口传入手机号获取验证码,调用此接口传入验证码,可使用验证码登录,传入后 `password` 参数将失效
|
||||
`captcha`: 验证码,使用 `/captcha/sent` 或 `/captcha/sent/v1`接口传入手机号获取验证码,调用此接口传入验证码,可使用验证码登录,传入后 `password` 参数将失效
|
||||
|
||||
`sca`: 网易易盾滑块验证token, 获取方式未知
|
||||
|
||||
**接口地址 :** `/login/cellphone`
|
||||
|
||||
@@ -397,6 +399,19 @@ body {
|
||||
|
||||
**调用例子 :** `/captcha/sent?phone=13xxx`
|
||||
|
||||
### 新版发送验证码
|
||||
|
||||
说明 : 调用此接口 ,传入手机号码, 可发送验证码
|
||||
|
||||
**必选参数 :** `phone`: 手机号码
|
||||
|
||||
**可选参数 :**
|
||||
`ctcode`: 国家区号,默认 86 即中国
|
||||
|
||||
**接口地址 :** `/captcha/sent/v1`
|
||||
|
||||
**调用例子 :** `/captcha/sent/v1?phone=13xxx`
|
||||
|
||||
### 验证验证码
|
||||
|
||||
说明 : 调用此接口 ,传入手机号码和验证码, 可校验验证码是否正确
|
||||
@@ -1259,9 +1274,11 @@ tags: 歌单标签
|
||||
`lossless`=>`无损`, `hires`=>`Hi-Res`, `jyeffect` => `高清环绕声`, `sky` => `沉浸环绕声`, `dolby` => `杜比全景声`, `jymaster` => `超清母带`
|
||||
`unblock`: 是否使用使用歌曲解锁, 分为`true`和`false`
|
||||
|
||||
**可选参数 :** `immerseType`: 沉浸声环绕声类型, 分为 `c51` => `c51类型`, `ste` => `环绕立体声类型`, `aac` => `aac类型`, 仅在 `level=sky` 时生效, 默认为 `c51`
|
||||
|
||||
**接口地址 :** `/song/url/v1`
|
||||
|
||||
**调用例子 :** `/song/url/v1?id=1969519579&level=exhigh` `/song/url/v1?id=1969519579,33894312&level=lossless`
|
||||
**调用例子 :** `/song/url/v1?id=1969519579&level=exhigh` `/song/url/v1?id=1969519579,33894312&level=lossless` `/song/url/v1?id=1969519579&level=sky&immerseType=ste`
|
||||
|
||||
说明:`杜比全景声`音质需要设备支持,不同的设备可能会返回不同码率的 url。cookie 需要传入`os=pc`保证返回正常码率的 url。
|
||||
|
||||
@@ -5493,6 +5510,14 @@ let data = encodeURIComponent(
|
||||
|
||||
**调用例子 :** `/ad/listening/rights/gain`
|
||||
|
||||
### 获取免费听时长状态
|
||||
|
||||
说明 : 登录后调用此接口, 获取免费听剩余时长
|
||||
|
||||
**接口地址 :** `/ad/listening/rights`
|
||||
|
||||
**调用例子 :** `/ad/listening/rights`
|
||||
|
||||
### 云小编 - 获取用户详情
|
||||
|
||||
说明: 登录后调用此接口, 获取云小编用户详情
|
||||
@@ -5603,12 +5628,203 @@ let data = encodeURIComponent(
|
||||
|
||||
`drawCount`: 未知, 默认 `1`
|
||||
|
||||
`checkToken`: 易盾反作弊 Token, 默认自动获取
|
||||
|
||||
**接口地址:** `/middle/play/do/lottery`
|
||||
|
||||
**调用例子:** `/middle/play/do/lottery?activityId=6501202&drawCount=1`
|
||||
|
||||
### 云小编 - 考试状态
|
||||
|
||||
说明: 登录后调用此接口, 查询云小编入站考试状态, 进度 (`data.process`): no: 未开始, process: 考试中, whole_exam_end: 已结束, 此时可检查 `data.hasPassExamination` 确认是否通过
|
||||
|
||||
**必选参数:**
|
||||
|
||||
`examType`: 考试类型, musicalStyleEnter: 歌曲曲风审核, languageEnter: 歌曲语种审核, oriSingerEnter: 歌曲原唱审核, emotionEnter: 情绪标签审核
|
||||
|
||||
**接口地址:** `/rep/ugc/exam/info/get`
|
||||
|
||||
**调用例子:** `/rep/ugc/exam/info/get?examType=emotionEnter`
|
||||
|
||||
### 云小编 - 考试开始
|
||||
|
||||
说明: 未考试或考试失败时调用此接口, 开始新一轮考试
|
||||
|
||||
**必选参数:**
|
||||
|
||||
`examType`: 考试类型, musicalStyleEnter: 歌曲曲风审核, languageEnter: 歌曲语种审核, oriSingerEnter: 歌曲原唱审核, emotionEnter: 情绪标签审核
|
||||
|
||||
**接口地址:** `/rep/ugc/exam/start`
|
||||
|
||||
**调用例子:** `/rep/ugc/exam/start?examType=emotionEnter`
|
||||
|
||||
### 云小编 - 考试取题
|
||||
|
||||
说明: 考试开始后调用此接口, 获取当前试题
|
||||
|
||||
**必选参数:**
|
||||
|
||||
`examType`: 考试类型, musicalStyleEnter: 歌曲曲风审核, languageEnter: 歌曲语种审核, oriSingerEnter: 歌曲原唱审核, emotionEnter: 情绪标签审核
|
||||
|
||||
`taskId`: 任务 ID, 首次调用 `/rep/ugc/exam/start` 获取,之后调用 `/rep/ugc/exam/info/get` 获取
|
||||
|
||||
**接口地址:** `/rep/ugc/exam/question/single/get`
|
||||
|
||||
**调用例子:** `/rep/ugc/exam/question/single/get?examType=emotionEnter&taskId=123456`
|
||||
|
||||
### 云小编 - 考试提交
|
||||
|
||||
说明: 取题后调用此接口, 提交作答结果
|
||||
|
||||
**必选参数:**
|
||||
|
||||
`examType`: 考试类型, musicalStyleEnter: 歌曲曲风审核, languageEnter: 歌曲语种审核, oriSingerEnter: 歌曲原唱审核, emotionEnter: 情绪标签审核
|
||||
|
||||
`taskId`: 任务 ID, 首次调用 `/rep/ugc/exam/start` 获取,之后调用 `/rep/ugc/exam/info/get` 获取
|
||||
|
||||
`questionId`: 试题 ID, 调用 `/rep/ugc/exam/question/single/get` 获取
|
||||
|
||||
`answer`: 判断结果, A: 对, B: 错
|
||||
|
||||
**接口地址:** `/rep/ugc/exam/submit`
|
||||
|
||||
**调用例子:** `/rep/ugc/exam/submit?examType=emotionEnter&taskId=123456&questionId=123456&answer=A`
|
||||
|
||||
### 云小编 - 考试结果
|
||||
|
||||
说明: 考试结束后调用此接口, 获取考试正确数与正确率
|
||||
|
||||
**必选参数:**
|
||||
|
||||
`examType`: 考试类型, musicalStyleEnter: 歌曲曲风审核, languageEnter: 歌曲语种审核, oriSingerEnter: 歌曲原唱审核, emotionEnter: 情绪标签审核
|
||||
|
||||
`taskId`: 任务 ID, 首次调用 `/rep/ugc/exam/start` 获取,之后调用 `/rep/ugc/exam/info/get` 获取
|
||||
|
||||
**接口地址:** `/rep/ugc/exam/result/get`
|
||||
|
||||
**调用例子:** `/rep/ugc/exam/result/get?examType=emotionEnter&taskId=123456`
|
||||
|
||||
### 发送/删除评论
|
||||
|
||||
说明 : 调用此接口,可发送评论或者删除评论
|
||||
|
||||
1. 发送评论
|
||||
|
||||
**必选参数**
|
||||
|
||||
`type`: 数字,资源类型,对应歌曲,mv,专辑,歌单,电台,视频对应以下类型
|
||||
|
||||
```
|
||||
0: 歌曲
|
||||
|
||||
1: mv
|
||||
|
||||
2: 歌单
|
||||
|
||||
3: 专辑
|
||||
|
||||
4: 电台
|
||||
|
||||
5: 视频
|
||||
|
||||
6: 动态
|
||||
```
|
||||
|
||||
`id`: 对应资源 id
|
||||
|
||||
`content`: 要发送的内容
|
||||
|
||||
**调用例子** : `/comment/add?type=1&id=5436712&content=test` (往广岛之恋 mv 发送评论: test)
|
||||
|
||||
2. 回复评论
|
||||
|
||||
**必选参数**
|
||||
|
||||
`type`: 数字,资源类型,对应歌曲,mv,专辑,歌单,电台,视频对应以下类型
|
||||
|
||||
```
|
||||
0: 歌曲
|
||||
|
||||
1: mv
|
||||
|
||||
2: 歌单
|
||||
|
||||
3: 专辑
|
||||
|
||||
4: 电台
|
||||
|
||||
5: 视频
|
||||
|
||||
6: 动态
|
||||
```
|
||||
|
||||
`id`: 对应资源 id
|
||||
|
||||
`cid`: 评论 id
|
||||
|
||||
`content`: 要发送的内容
|
||||
|
||||
**调用例子** : `/comment/add?type=1&id=5436712&cid=1535550516319&content=test` (往广岛之恋 mv 回复test评论: test)
|
||||
|
||||
3. 删除评论
|
||||
|
||||
**必选参数**
|
||||
|
||||
`type`: 数字,资源类型,对应歌曲,mv,专辑,歌单,电台,视频对应以下类型
|
||||
|
||||
```
|
||||
0: 歌曲
|
||||
|
||||
1: mv
|
||||
|
||||
2: 歌单
|
||||
|
||||
3: 专辑
|
||||
|
||||
4: 电台节目
|
||||
|
||||
5: 视频
|
||||
|
||||
6: 动态
|
||||
|
||||
7: 电台
|
||||
|
||||
```
|
||||
|
||||
`id`: 对应资源 id
|
||||
|
||||
`cid`: 评论 id
|
||||
|
||||
**调用例子** : `/comment?type=1&id=5436712&cid=1535550516319` (在广岛之恋 mv 删除评论)
|
||||
|
||||
### 获取在线设备列表
|
||||
|
||||
说明: 登录后调用此接口, 获取在线设备列表
|
||||
|
||||
**接口地址:** `/device/list`
|
||||
|
||||
**调用例子:** `/device/list`
|
||||
|
||||
### 发送安全验证码
|
||||
|
||||
说明: 登录后调用此接口, 传入手机号, 可发送安全验证码
|
||||
|
||||
**必选参数 :** `phone`: 手机号
|
||||
|
||||
**接口地址 :** `/captcha/safe/sent`
|
||||
|
||||
**调用例子 :** `/captcha/safe/sent?phone=13XXXXXXXXX`
|
||||
|
||||
### 强制下线设备
|
||||
|
||||
说明: 登录后调用此接口, 传入设备 id, 可强制下线设备的登录会话
|
||||
|
||||
**必选参数 :** `key`: 设备的 `deviceKey`, 可通过 `/device/list` 获取
|
||||
|
||||
`captcha`: 安全验证码, 可通过 `/captcha/safe/sent` 获取
|
||||
|
||||
**接口地址 :** `/device/kickoff`
|
||||
|
||||
**调用例子 :** `/device/kickoff?key=00ALDFGEXXXXXXXXXXXXXXXXX&captcha=1234`
|
||||
|
||||
## 离线访问此文档
|
||||
|
||||
此文档同时也是 Progressive Web Apps(PWA), 加入了 serviceWorker, 可离线访问
|
||||
|
||||
+175
-10
@@ -6,6 +6,8 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="referrer" content="no-referrer" />
|
||||
<link rel="icon" href="docs/netease.png" />
|
||||
<meta name="referrer" content="no-referrer" />
|
||||
<link rel="icon" href="docs/netease.png" />
|
||||
<title>云小编任务中心 - 网易云音乐 API Enhanced</title>
|
||||
<style>
|
||||
* {
|
||||
@@ -327,6 +329,10 @@
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
#userPoint {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* ---- Song Review ---- */
|
||||
.song-review {
|
||||
margin-top: 16px;
|
||||
@@ -379,7 +385,7 @@
|
||||
border-radius: 6px;
|
||||
border: 1px solid #e5e7eb;
|
||||
white-space: pre-wrap;
|
||||
max-height: 120px;
|
||||
max-height: 160px;
|
||||
overflow-y: auto;
|
||||
font-family: monospace;
|
||||
line-height: 1.6;
|
||||
@@ -419,6 +425,7 @@
|
||||
<div class="info-card" id="user">
|
||||
<img src="docs/netease.png" alt="头像" class="avatar" id="userAvatar" />
|
||||
<span class="info-text" id="userName">未登录</span>
|
||||
<span id="userPoint">0</span>
|
||||
<button class="btn-sm green" id="userSign" disabled>签到</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -440,7 +447,7 @@
|
||||
<button class="btn-sm" id="taskGet">获取任务详情</button>
|
||||
</div>
|
||||
<div class="info-card" id="task">
|
||||
<span class="info-text" id="taskInfo">[<span id="taskCount">0</span>/<span id="taskGoal">0</span>] 审核歌曲赚贡献分 (+66)</span>
|
||||
<span class="info-text" id="taskInfo">[<span id="taskCount">0</span>/<span id="taskGoal">0</span>] 审核歌曲赚贡献分 (+<span id="taskPoint">0</span>)</span>
|
||||
<button class="btn-sm" id="taskStart" disabled>去完成</button>
|
||||
</div>
|
||||
|
||||
@@ -556,6 +563,7 @@
|
||||
/// 用户信息 ///
|
||||
var userAvatar = document.getElementById('userAvatar');
|
||||
var userName = document.getElementById('userName');
|
||||
var userPoint = document.getElementById('userPoint');
|
||||
var userSign = document.getElementById('userSign');
|
||||
var userGet = document.getElementById('userGet');
|
||||
|
||||
@@ -564,6 +572,7 @@
|
||||
var d = res.data;
|
||||
if (d.imgUrl) userAvatar.src = d.imgUrl + '?param=50y50';
|
||||
userName.textContent = d.name || '游客' + d.id;
|
||||
userPoint.textContent = d.availablePoints;
|
||||
userSign.disabled = d.signed;
|
||||
showResult('用户信息加载成功', 'success');
|
||||
});
|
||||
@@ -576,6 +585,7 @@
|
||||
var d = res.data;
|
||||
userSign.disabled = d;
|
||||
showResult('签到' + (d ? '成功 ✓' : '失败 ✗'), d ? 'success' : 'error');
|
||||
if (d) userGet.click();
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -608,21 +618,36 @@
|
||||
/// 审核任务 ///
|
||||
var taskCount = document.getElementById('taskCount');
|
||||
var taskGoal = document.getElementById('taskGoal');
|
||||
var taskPoint = document.getElementById('taskPoint');
|
||||
var taskStartBtn = document.getElementById('taskStart');
|
||||
var taskGetBtn = document.getElementById('taskGet');
|
||||
|
||||
taskGetBtn.onclick = function () {
|
||||
request('/rep/ugc/activity/get', {}, function (res) {
|
||||
request('rep/ugc/activity/get', {}, function (res) {
|
||||
var d = res.data;
|
||||
taskCount.textContent = d.count;
|
||||
taskGoal.textContent = d.goalCount;
|
||||
taskStartBtn.disabled = d.activityId !== 5001;
|
||||
taskPoint.textContent = d.points;
|
||||
// 注:超过 100 可能有 bug,导致 thinktank/audit/resource/detail 获取的任务 ID 一直是 5002,目标数量也不对,暂时限制前两个任务
|
||||
taskStartBtn.disabled = d.activityId !== 5001 && d.activityId !== 5002; // 5001: 1-50, 5002: 51-100
|
||||
showResult('任务详情加载成功', 'success');
|
||||
// 任务完成,领取积分
|
||||
if (d.count >= d.goalCount) {
|
||||
request('rep/ugc/activity/collect', { activityId: d.activityId }, function (res) {
|
||||
var d = res.data;
|
||||
showResult('积分领取' + (d ? '成功,可以兑换会员了!' : '失败'), d ? 'success' : 'error');
|
||||
// 刷新任务状态
|
||||
if (d) loadAll();
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
taskStartBtn.onclick = function () {
|
||||
if (!taskStartBtn.disabled) taskStart();
|
||||
if (!taskStartBtn.disabled) {
|
||||
songAudio.pause();
|
||||
examInit();
|
||||
}
|
||||
};
|
||||
|
||||
/// 歌曲审核区域 ///
|
||||
@@ -636,12 +661,93 @@
|
||||
var songSkip = document.getElementById('songSkip');
|
||||
var songLyric = document.getElementById('songLyric');
|
||||
|
||||
var examType = 'emotionEnter';
|
||||
// 考试初始化、检查状态
|
||||
function examInit() {
|
||||
request('rep/ugc/exam/info/get', { examType: examType }, function (res) {
|
||||
var d = res.data;
|
||||
switch (d.process) {
|
||||
case 'whole_exam_end':
|
||||
// 通过考试
|
||||
if (d.hasPassExamination) {
|
||||
taskStart();
|
||||
return;
|
||||
}
|
||||
request('rep/ugc/exam/result/get', { examType: examType, taskId: d.taskId }, function (res) {
|
||||
var d = res.data;
|
||||
showResult('考试未通过:正确数:' + d.wrightNum + ',正确率:' + d.wrightRate, 'error');
|
||||
});
|
||||
case 'no':
|
||||
// 未通过或未开始
|
||||
request('rep/ugc/exam/start', { examType: examType }, function (res) {
|
||||
examStart(res.data.taskId);
|
||||
});
|
||||
break;
|
||||
case 'process':
|
||||
// 正在考试
|
||||
examStart(d.taskId);
|
||||
break;
|
||||
default:
|
||||
showResult('未知考试进度:' + d.process, 'error');
|
||||
return;
|
||||
}
|
||||
// 在上个信息不为异常时显示考试中提示
|
||||
if (d.process !== 'whole_exam_end' && !resultDiv.className.endsWith('error')) showResult('正在考试,请认真审题!', 'info');
|
||||
});
|
||||
}
|
||||
// 考试取题开始
|
||||
function examStart(taskId) {
|
||||
request('rep/ugc/exam/question/single/get', { examType: examType, taskId: taskId }, function (res) {
|
||||
var d = res.data;
|
||||
|
||||
songCover.src = d.coverUrl + '?param=100y100';
|
||||
songTitle.textContent = '[' + d.resId + '] ' + d.resName + ' - ' + d.artists;
|
||||
songTag.textContent = '[' + (d.auditCount + 1) + '/' + d.auditTaskCount + '] 当前结果: ' + d.questionContent + '?';
|
||||
songLyric.textContent = d.lyric ? d.transLyric ? lrctran(d.lyric, d.transLyric) : d.lyric : '[00:00.00] 暂无歌词';
|
||||
songLyric.scrollTop = 0;
|
||||
// 有时候就是没有播放链接,只能根据歌词硬审
|
||||
if (d.songUrl) {
|
||||
songAudio.src = d.songUrl;
|
||||
songAudio.play().catch(function () { /* 自动播放被浏览器阻止 */ });
|
||||
} else {
|
||||
songAudio.src = ''; // 清理播放器
|
||||
}
|
||||
songApprove.onclick = function () { if (!songApprove.disabled) examSubmit(taskId, d.questionId, 'A') };
|
||||
songReject.onclick = function () { if (!songReject.disabled) examSubmit(taskId, d.questionId, 'B') };
|
||||
songSkip.onclick = null;
|
||||
|
||||
songApprove.disabled = songReject.disabled = songSkip.disabled = true;
|
||||
song.style.display = 'block';
|
||||
|
||||
setTimeout(function () {
|
||||
songApprove.disabled = songReject.disabled = false;
|
||||
}, 3e3);
|
||||
});
|
||||
}
|
||||
// 考试提交
|
||||
function examSubmit(taskId, questionId, answer) {
|
||||
songAudio.pause();
|
||||
songApprove.disabled = songReject.disabled = songSkip.disabled = true;
|
||||
request(
|
||||
'rep/ugc/exam/submit',
|
||||
{ examType: examType, taskId: taskId, questionId: questionId, answer: answer },
|
||||
function (res) {
|
||||
var d = res.data;
|
||||
if (d.result) {
|
||||
showResult('回答正确,加载下一首...', 'success');
|
||||
} else {
|
||||
showResult('回答错误:' + d.analysis, 'error');
|
||||
}
|
||||
examInit();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取任务
|
||||
*/
|
||||
function taskStart() {
|
||||
song.style.display = 'none';
|
||||
request('/thinktank/audit/resource/detail', { type: '4' }, function (res) {
|
||||
request('thinktank/audit/resource/detail', { type: '4' }, function (res) {
|
||||
var d = res.data;
|
||||
|
||||
if (d.auditCount >= 50) {
|
||||
@@ -661,8 +767,8 @@
|
||||
songCover.src = d.coverUrl + '?param=100y100';
|
||||
songTitle.textContent = '[' + d.resId + '] ' + d.resName + ' - ' + d.artists;
|
||||
songTag.textContent = '[' + (d.auditCount + 1) + '/' + d.auditTaskCount + '] 当前结果: ' + d.initResult + '?';
|
||||
songLyric.textContent = (d.lyric || '') + (d.transLyric || '');
|
||||
if (!songLyric.textContent.trim()) songLyric.textContent = '[00:00.00] 暂无歌词';
|
||||
songLyric.textContent = d.lyric ? d.transLyric ? lrctran(d.lyric, d.transLyric) : d.lyric : '[00:00.00] 暂无歌词';
|
||||
songLyric.scrollTop = 0;
|
||||
songAudio.src = d.songUrl;
|
||||
songAudio.play().catch(function () { /* 自动播放被浏览器阻止 */ });
|
||||
|
||||
@@ -685,6 +791,7 @@
|
||||
* @param {'1'|'2'|'3'} judgement
|
||||
*/
|
||||
function taskSubmit(taskId, judgement) {
|
||||
songAudio.pause();
|
||||
songApprove.disabled = songReject.disabled = songSkip.disabled = true;
|
||||
request(
|
||||
'thinktank/audit/resource/update',
|
||||
@@ -692,7 +799,10 @@
|
||||
function (res) {
|
||||
var d = res.data;
|
||||
if (d.result) {
|
||||
showResult('审核提交成功,加载下一首...', 'success');
|
||||
showResult((d.test ? '抽查' : '审核') + '提交成功,加载下一首...', 'success');
|
||||
taskStart();
|
||||
} else if (d.test) {
|
||||
showResult('抽查失败: [' + d.dayErrorNum + '/' + d.dayErrorNumStandard + '] ' + d.errorAnalysis, 'error');
|
||||
taskStart();
|
||||
} else {
|
||||
showResult('未知状态: ' + JSON.stringify(d), 'error');
|
||||
@@ -701,6 +811,61 @@
|
||||
);
|
||||
}
|
||||
|
||||
/**解析歌词时间戳和文本 */
|
||||
function lrctrim(lyrics) {
|
||||
var lines = lyrics.split('\n');
|
||||
var data = [];
|
||||
lines.forEach(function (line, index) {
|
||||
var matches = line.match(/\[(\d{2}):(\d{2}[\.:]?\d*)\]/);
|
||||
if (matches) {
|
||||
var minutes = parseInt(matches[1], 10);
|
||||
var seconds = parseFloat(matches[2].replace('.', ':')) || 0;
|
||||
var timestamp = minutes * 6e4 + seconds * 1e3;
|
||||
var text = line.replace(/\[\d{2}:\d{2}[\.:]?\d*\]/g, '').trim();
|
||||
text = text.replace(/\s\s+/g, ' '); // Replace multiple spaces with a single space
|
||||
data.push([timestamp, index, text]);
|
||||
}
|
||||
});
|
||||
data.sort(function (a, b) {
|
||||
return a[0] - b[0];
|
||||
});
|
||||
return data;
|
||||
}
|
||||
/**合并原文歌词和翻译歌词 */
|
||||
function lrctran(lyric, tlyric) {
|
||||
lyric = lrctrim(lyric);
|
||||
tlyric = lrctrim(tlyric);
|
||||
var len1 = lyric.length;
|
||||
var len2 = tlyric.length;
|
||||
var result = '';
|
||||
for (var i = 0, j = 0; i < len1 && j < len2; i++) {
|
||||
while (lyric[i][0] > tlyric[j][0] && j + 1 < len2) {
|
||||
j++;
|
||||
}
|
||||
if (lyric[i][0] === tlyric[j][0]) {
|
||||
tlyric[j][2] = tlyric[j][2].replace('/', '');
|
||||
if (tlyric[j][2]) {
|
||||
lyric[i][2] += ' (' + tlyric[j][2] + ')';
|
||||
}
|
||||
j++;
|
||||
}
|
||||
}
|
||||
for (var k = 0; k < len1; k++) {
|
||||
var t = lyric[k][0];
|
||||
result += '['.concat(
|
||||
String(Math.floor(t / 6e4)).padStart(2, '0'),
|
||||
':',
|
||||
String(Math.floor((t % 6e4) / 1e3)).padStart(2, '0'),
|
||||
'.',
|
||||
String(t % 1e3).padStart(3, '0'),
|
||||
']',
|
||||
lyric[k][2],
|
||||
'\n',
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// 一键载入 ///
|
||||
window.loadAll = function () {
|
||||
userGet.click();
|
||||
|
||||
+108
-147
@@ -9,20 +9,13 @@
|
||||
<title>云小编每日抽奖 - 网易云音乐 API Enhanced</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg-start: #f3f0ff;
|
||||
--bg-end: #e8f4fd;
|
||||
--primary: #7c6cf0;
|
||||
--primary-light: #ede9fe;
|
||||
--accent: #fd79a8;
|
||||
--gold: #fdcb6e;
|
||||
--text: #2d3436;
|
||||
--text-light: #636e72;
|
||||
--card-bg: #ffffff;
|
||||
--border: #e8e8f0;
|
||||
--shadow: 0 8px 30px rgba(108, 92, 231, 0.08);
|
||||
--radius: 20px;
|
||||
--radius-sm: 12px;
|
||||
--transition: 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
--fg: #333;
|
||||
--muted: #666;
|
||||
--border: #ddd;
|
||||
--bg: #f5f5f5;
|
||||
--panel: #ffffff;
|
||||
}
|
||||
|
||||
* {
|
||||
@@ -33,13 +26,13 @@
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', system-ui, -apple-system, 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||||
background: linear-gradient(135deg, var(--bg-start) 0%, var(--bg-end) 50%, #fce4ec 100%);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
/* align-items: center; */
|
||||
justify-content: center;
|
||||
padding: 1.5rem;
|
||||
color: var(--text);
|
||||
color: var(--fg);
|
||||
background: var(--bg);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
@@ -51,65 +44,77 @@
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
/* 标题编辑卡片 */
|
||||
/* 标题 */
|
||||
.header h1 {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.header p {
|
||||
margin-top: 4px;
|
||||
color: var(--muted);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 卡片 */
|
||||
.card {
|
||||
background: var(--card-bg);
|
||||
border-radius: var(--radius);
|
||||
padding: 1.8rem 2rem;
|
||||
box-shadow: var(--shadow);
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--border);
|
||||
transition: var(--transition);
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
margin-bottom: 16px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.title-section {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.title-input {
|
||||
width: 100%;
|
||||
border: 2px solid transparent;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 1.6rem;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
padding: 0.6rem 1rem;
|
||||
color: var(--text);
|
||||
background: #fafafa;
|
||||
transition: var(--transition);
|
||||
outline: none;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.title-input:hover {
|
||||
background: #f5f3ff;
|
||||
}
|
||||
|
||||
.title-input:focus {
|
||||
border-color: var(--primary-light);
|
||||
background: #fff;
|
||||
box-shadow: 0 0 0 4px rgba(124, 108, 240, 0.06);
|
||||
}
|
||||
|
||||
.title-hint {
|
||||
font-size: 0.8rem;
|
||||
color: #b2bec3;
|
||||
margin-top: 0.4rem;
|
||||
}
|
||||
|
||||
/* 抽奖主卡片 */
|
||||
.lottery-card {
|
||||
background: var(--card-bg);
|
||||
border-radius: var(--radius);
|
||||
padding: 2rem 2rem 2.2rem;
|
||||
box-shadow: var(--shadow);
|
||||
/* 按钮 */
|
||||
button {
|
||||
padding: 8px 18px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--border);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
background: var(--panel);
|
||||
color: var(--fg);
|
||||
transition: all 0.15s;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
button.primary {
|
||||
background: var(--fg);
|
||||
color: var(--panel);
|
||||
border-color: var(--fg);
|
||||
}
|
||||
|
||||
button.primary:hover {
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
/* 页脚 */
|
||||
.footer {
|
||||
padding-top: 12px;
|
||||
border-top: 1px solid var(--border);
|
||||
color: var(--muted);
|
||||
text-align: center;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.footer a {
|
||||
color: var(--fg);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.footer a:hover {
|
||||
border-bottom: 1px dotted var(--fg);
|
||||
}
|
||||
|
||||
.rolling-display {
|
||||
font-size: 2.4rem;
|
||||
font-weight: 800;
|
||||
color: var(--primary);
|
||||
color: #c4b5fd;
|
||||
min-height: 4rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -121,7 +126,6 @@
|
||||
}
|
||||
|
||||
.rolling-display.idle {
|
||||
color: #c4b5fd;
|
||||
font-size: 1.3rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
@@ -134,6 +138,7 @@
|
||||
padding: 1.5rem;
|
||||
margin: 1rem 0 1.5rem;
|
||||
animation: popIn 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.winner-label {
|
||||
@@ -152,6 +157,12 @@
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.winner-img {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
margin: -20px;
|
||||
}
|
||||
|
||||
.confetti-row {
|
||||
margin-top: 0.5rem;
|
||||
display: flex;
|
||||
@@ -198,59 +209,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* 按钮 */
|
||||
.btn {
|
||||
padding: 0.9rem 2.5rem;
|
||||
border: none;
|
||||
border-radius: 50px;
|
||||
font-weight: 700;
|
||||
font-size: 1.05rem;
|
||||
cursor: pointer;
|
||||
transition: var(--transition);
|
||||
letter-spacing: 0.5px;
|
||||
width: 100%;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.btn-lottery {
|
||||
background: linear-gradient(135deg, #fd79a8, #e84393);
|
||||
color: #fff;
|
||||
box-shadow: 0 6px 20px rgba(253, 121, 168, 0.35);
|
||||
}
|
||||
|
||||
.btn-lottery:hover:not(:disabled) {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 10px 28px rgba(253, 121, 168, 0.45);
|
||||
}
|
||||
|
||||
.btn-lottery:active:not(:disabled) {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.btn-lottery:disabled {
|
||||
background: #b2bec3;
|
||||
box-shadow: none;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.btn-reset {
|
||||
background: transparent;
|
||||
border: 2px solid var(--border);
|
||||
color: var(--text-light);
|
||||
margin-top: 0.8rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.btn-reset:hover {
|
||||
border-color: var(--primary-light);
|
||||
color: var(--primary);
|
||||
background: #f8f7ff;
|
||||
}
|
||||
|
||||
/* 状态提示 */
|
||||
.status-bar {
|
||||
display: flex;
|
||||
@@ -288,16 +246,6 @@
|
||||
|
||||
/* 响应式 */
|
||||
@media (max-width: 500px) {
|
||||
|
||||
.card,
|
||||
.lottery-card {
|
||||
padding: 1.4rem 1.2rem;
|
||||
}
|
||||
|
||||
.title-input {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
.rolling-display {
|
||||
font-size: 1.8rem;
|
||||
min-height: 3rem;
|
||||
@@ -312,20 +260,19 @@
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<!-- 标题编辑区 -->
|
||||
<div class="card title-section">
|
||||
<input type="text" class="title-input" id="titleInput" value="🎉 云小编每日抽奖 🎉" autocomplete="off" disabled>
|
||||
<div class="title-hint">每次抽奖消耗 <b>200 个曲库编辑部积分</b>,每日最多可抽奖 3 次</div>
|
||||
<!-- 标题 -->
|
||||
<div class="header">
|
||||
<h1>🎉 云小编每日抽奖</h1>
|
||||
<p>每次抽奖消耗 <b>200 个曲库编辑部积分</b>,每日最多可抽奖 3 次</p>
|
||||
</div>
|
||||
|
||||
<!-- 抽奖主区域 -->
|
||||
<div class="lottery-card">
|
||||
<!-- 抽奖 -->
|
||||
<div class="card">
|
||||
<div class="rolling-display idle" id="rollingDisplay">等待抽奖</div>
|
||||
<div id="resultContainer"></div>
|
||||
<button class="btn btn-lottery" id="lotteryBtn">
|
||||
<button id="lotteryBtn" class="primary">
|
||||
<span>🎲</span> 开始抽奖
|
||||
</button>
|
||||
<button class="btn btn-reset" id="resetBtn">
|
||||
<button id="resetBtn">
|
||||
<span>🔄</span> 编辑令牌
|
||||
</button>
|
||||
<div class="status-bar" id="statusBar">
|
||||
@@ -333,12 +280,15 @@
|
||||
<span id="statusText">就绪,点击按钮开始抽奖</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 页脚 -->
|
||||
<div class="footer">
|
||||
<a href="/">← 返回首页</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
// ============ DOM 元素 ============
|
||||
const titleInput = document.getElementById('titleInput');
|
||||
const rollingDisplay = document.getElementById('rollingDisplay');
|
||||
const resultContainer = document.getElementById('resultContainer');
|
||||
const lotteryBtn = document.getElementById('lotteryBtn');
|
||||
@@ -420,19 +370,21 @@
|
||||
const res = await request('middle/play/do/lottery', '&activityId=6501202&drawCount=1');
|
||||
|
||||
// 根据你的后端返回格式提取中奖者名称
|
||||
const winner = res.data?.prizeDetailInfoMap?.['8186002']?.prizeName;
|
||||
if (!winner) {
|
||||
const prizeMap = res.data?.prizeDetailInfoMap || {};
|
||||
for (var k in prizeMap) break; // 取第一个奖品 ID
|
||||
const prize = k ? prizeMap[k] : null;
|
||||
if (!prize) {
|
||||
throw new Error('无法解析响应数据');
|
||||
}
|
||||
|
||||
// [抽奖结果, 剩余机会]
|
||||
return [winner, res.data.restChance];
|
||||
// [抽奖结果, 剩余机会, 奖品图片]
|
||||
return [prize.prizeName, res.data.restChance, prize.prizeImgList[0] || ''];
|
||||
}
|
||||
|
||||
// ============ 滚动动画 ============
|
||||
function startRollingAnimation(finalWinner) {
|
||||
function startRollingAnimation(finalWinner, img) {
|
||||
const placeholderNames = ['🎰', '🎲', '🎯', '✨', '💫', '🌟', '🎪', '🎭'];
|
||||
const totalDuration = 2800;
|
||||
const totalDuration = 2000;
|
||||
const startInterval = 50;
|
||||
const endInterval = 280;
|
||||
const startTime = Date.now();
|
||||
@@ -454,7 +406,8 @@
|
||||
// 动画结束,显示真实结果
|
||||
clearTimeout(rollingTimer);
|
||||
rollingDisplay.textContent = finalWinner;
|
||||
showWinnerResult(finalWinner);
|
||||
rollingDisplay.style.display = 'none';
|
||||
showWinnerResult(finalWinner, img);
|
||||
finishDrawing();
|
||||
return;
|
||||
}
|
||||
@@ -465,11 +418,12 @@
|
||||
roll();
|
||||
}
|
||||
|
||||
function showWinnerResult(winner) {
|
||||
function showWinnerResult(winner, img) {
|
||||
resultContainer.innerHTML = `
|
||||
<div class="winner-display">
|
||||
<div class="winner-label">🏆 恭喜中奖</div>
|
||||
<div class="winner-name">${escapeHTML(winner)}</div>
|
||||
<img src="${img}" alt="奖品图片" class="winner-img" />
|
||||
<div class="confetti-row">
|
||||
<span class="confetti-emoji">🎉</span>
|
||||
<span class="confetti-emoji">🎊</span>
|
||||
@@ -503,6 +457,7 @@
|
||||
resultContainer.innerHTML = '';
|
||||
rollingDisplay.className = 'rolling-display';
|
||||
rollingDisplay.textContent = '🎰';
|
||||
rollingDisplay.style.display = 'flex';
|
||||
setStatus('info', '正在获取抽奖结果...');
|
||||
|
||||
try {
|
||||
@@ -510,7 +465,7 @@
|
||||
const res = await fetchLotteryResult();
|
||||
setStatus('info', '抽奖成功,剩余次数 ' + res[1]);
|
||||
// 启动滚动动画,最终定格在中奖者
|
||||
startRollingAnimation(res[0]);
|
||||
startRollingAnimation(res[0], res[2]);
|
||||
} catch (error) {
|
||||
// 请求失败处理
|
||||
rollingDisplay.textContent = '抽奖失败';
|
||||
@@ -528,7 +483,7 @@
|
||||
|
||||
// 空格键快捷抽奖
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === ' ' && document.activeElement !== titleInput) {
|
||||
if (e.key === ' ') {
|
||||
e.preventDefault();
|
||||
if (!isDrawing && !lotteryBtn.disabled) {
|
||||
startLottery();
|
||||
@@ -538,6 +493,12 @@
|
||||
|
||||
// ============ 初始化 ============
|
||||
resetResult();
|
||||
|
||||
window.demo = function (name, chance, img) {
|
||||
rollingDisplay.style.display = 'flex';
|
||||
setStatus('info', '抽奖成功,剩余次数 ' + (chance || '0'));
|
||||
startRollingAnimation(name || '演示', img || 'https://p5.music.126.net/obj/worDmcOCw6HCnzzCmsKx/61891591760/8d1c/528a/e977/10cbaa3fb39e8057be5d2560182cc5c7.png');
|
||||
};
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user