Merge pull request #1677 from kengwang/feat/api/listen-together

[feat]<api/listentogether>: 一起听 API 相关
This commit is contained in:
binaryify 2023-01-18 16:31:24 +08:00 committed by GitHub
commit 848f899afe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 413 additions and 4 deletions

View File

@ -3574,13 +3574,17 @@ type='1009' 获取其 id, 如`/search?keywords= 代码时间 &type=1009`
**调用例子 :** `/artist/new/mv?limit=1` `/artist/new/mv?limit=1&before=1602777625000`
### 一起听状态
### 一起听相关
说明 :登录后调用此接口可获取一起听状态
一起听相关参见此 Issue: [#1676](https://github.com/Binaryify/NeteaseCloudMusicApi/issues/1676)
**接口地址 :** `/listen/together/status`
主机模式:
**调用例子 :** `/listen/together/status`
代码可参考: https://github.com/Binaryify/NeteaseCloudMusicApi/blob/master/public/listen_together_host.html
访问地址: http://localhost:3000/listen_together_host.html
从机模式: 待整理
### batch 批量请求接口

View File

@ -0,0 +1,20 @@
// 一起听 结束房间
module.exports = (query, request) => {
const data = {
roomId: query.roomId
}
return request(
'POST',
`http://interface.music.163.com/eapi/listen/together/end/v2`,
data,
{
crypto: 'eapi',
cookie: query.cookie,
proxy: query.proxy,
realIP: query.realIP,
url: '/api/listen/together/end/v2'
},
)
}

View File

@ -0,0 +1,23 @@
// 一起听 发送心跳
module.exports = (query, request) => {
const data = {
roomId: query.roomId,
songId: query.songId,
playStatus: query.playStatus,
progress: query.progress
}
return request(
'POST',
`http://interface.music.163.com/eapi/listen/together/heartbeat`,
data,
{
crypto: 'eapi',
cookie: query.cookie,
proxy: query.proxy,
realIP: query.realIP,
url: '/api/listen/together/heartbeat'
},
)
}

View File

@ -0,0 +1,27 @@
// 一起听 发送播放状态
module.exports = (query, request) => {
const data = {
roomId: query.roomId,
commandInfo: JSON.stringify({
commandType: query.commandType,
progress: query.progress || 0,
playStatus: query.playStatus,
formerSongId: query.formerSongId,
targetSongId: query.targetSongId,
clientSeq: query.clientSeq,
}),
}
return request(
'POST',
`http://interface.music.163.com/eapi/listen/together/play/command/report`,
data,
{
crypto: 'eapi',
cookie: query.cookie,
proxy: query.proxy,
realIP: query.realIP,
url: '/api/listen/together/play/command/report',
},
)
}

View File

@ -0,0 +1,20 @@
// 一起听 房间情况
module.exports = (query, request) => {
const data = {
roomId: query.roomId
}
return request(
'POST',
`http://interface.music.163.com/eapi/listen/together/room/check`,
data,
{
crypto: 'eapi',
cookie: query.cookie,
proxy: query.proxy,
realIP: query.realIP,
url: '/api/listen/together/room/check'
},
)
}

View File

@ -0,0 +1,20 @@
// 一起听创建房间
module.exports = (query, request) => {
const data = {
refer: 'songplay_more'
}
return request(
'POST',
`http://interface.music.163.com/eapi/listen/together/room/create`,
data,
{
crypto: 'eapi',
cookie: query.cookie,
proxy: query.proxy,
realIP: query.realIP,
url: '/api/listen/together/room/create'
},
)
}

View File

@ -0,0 +1,33 @@
// 一起听 更新播放列表
module.exports = (query, request) => {
const data = {
roomId: query.roomId,
playlistParam: JSON.stringify({
commandType: query.commandType,
version: [
{
userId: query.userId,
version: query.version
}
],
anchorSongId: '',
anchorPosition: -1,
randomList: query.randomList.split(','),
displayList: query.displayList.split(',')
})
}
return request(
'POST',
`http://interface.music.163.com/eapi/listen/together/sync/list/command/report`,
data,
{
crypto: 'eapi',
cookie: query.cookie,
proxy: query.proxy,
realIP: query.realIP,
url: '/api/listen/together/sync/list/command/report'
},
)
}

View File

@ -0,0 +1,20 @@
// 一起听 当前列表获取
module.exports = (query, request) => {
const data = {
roomId: query.roomId
}
return request(
'POST',
`http://interface.music.163.com/eapi/listen/together/sync/playlist/get`,
data,
{
crypto: 'eapi',
cookie: query.cookie,
proxy: query.proxy,
realIP: query.realIP,
url: '/api/listen/together/sync/playlist/get'
},
)
}

View File

@ -0,0 +1,242 @@
<!-- eslint-disable prettier/prettier -->
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>一起听 - 主机模式</title>
<script src="https://unpkg.com/petite-vue"></script>
<script src="https://cdn.jsdelivr.net/npm/axios@0.26.1/dist/axios.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/mdui@1.0.2/dist/css/mdui.min.css" />
<script src="https://unpkg.com/mdui@1.0.2/dist/js/mdui.min.js"></script>
</head>
<body class="mdui-container">
<h1>一起听 - 主机模式</h1>
<div>消息: {{message}}</div>
<audio
id="player"
autoplay
controls>
</audio>
<br />
<br />
<button v-if="!account.login" @click="login">获取登录状态</button>
<div>您的当前登录账号为: {{account.nickname}}</div>
<br />
<div v-if="account.login">
<button v-if="!roomInfo.roomId" @click="createRoom">创建房间</button>
<div v-if="roomInfo.roomId">
<div>分享链接为:
https://st.music.163.com/listen-together/share/?songId=1372188635&roomId={{roomInfo.roomId}}&inviterId={{account.userId}}
</div>
<br />
<button @click="refreshRoom">刷新房间状态</button>
<div>在线用户: </div>
<ul class="mdui-list">
<li v-for="user in roomInfo.roomUsers" class="mdui-list-item mdui-ripple">
<div class="mdui-list-item-avatar">
<img :src="user.avatarUrl" />
</div>
<div class="mdui-list-item-content">{{user.nickname}}</div>
</li>
</ul>
<button v-if="roomInfo.roomId" @click="closeRoom">关闭房间</button>
</div>
</div>
<button @click="playTrack">播放</button>
<button @click="pauseTrack">暂停</button>
<button @click="seekTrack">同步进度</button>
<details>
<summary>播放列表</summary>
<br />
<div><span>歌单ID: </span><input v-model="playlistInfo.playlistId" /></div>
<button @click="loadPlaylist">加载歌单到播放列表</button>
<span>{{playlistInfo.playlistName}}</span>
<br />
<br />
<div>歌单内容: </div>
<ul class="mdui-list">
<li @click="gotoTrack(track.id)" v-for="track in playlistInfo.playlistTracks"
class="mdui-list-item mdui-ripple">
<div class="mdui-list-item-avatar">
<img :src="track.al.picUrl" />
</div>
<div class="mdui-list-item-content">{{track.name}}</div>
</li>
</ul>
</details>
</body>
<script>
PetiteVue.createApp({
message: '请点击获取登录状态',
account: {
login: false,
userId: 0,
nickname: '未登录',
},
roomInfo: {
roomId: null,
roomUsers: [],
},
playlistInfo: {
playlistId: 0,
playlistName: '未获取',
playlistTrackIds: [],
playlistTracks: [],
},
playingInfo: {
trackId: 0,
status: 'PLAY',
progress: 1,
},
clientSeq: 1,
login: async function () {
const res = await axios({
url: `/login/status`,
method: 'get',
})
if (res.data.data.code != 200) {
alert('请先使用登录 API 登录到网易云音乐')
} else {
this.account.userId = res.data.data.profile.userId
this.account.nickname = res.data.data.profile.nickname
this.account.login = true
this.message = '成功登录, 请创建房间'
}
},
createRoom: async function () {
const res = await axios({
url: 'listentogether/room/create',
method: 'get',
})
console.log(res)
if (res.data.code != 200) {
this.message = '创建房间出现问题: ' + res.data.message
} else {
this.message = '创建房间成功: ' + res.data.data.roomInfo.roomId
this.roomInfo.roomId = res.data.data.roomInfo.roomId
res = await axios({
url: 'listentogether/room/check',
method: 'post',
data: {
roomId: this.roomInfo.roomId,
},
})
console.log(res)
}
},
refreshRoom: async function () {
const res = await axios({
url: '/listentogether/status',
})
console.log(res)
if (res.data.code != 200 || !res.data.data.inRoom) {
this.message = '房间状态获取失败, 可能退出了房间'
} else {
this.roomInfo.roomUsers = res.data.data.roomInfo.roomUsers
}
},
closeRoom: async function () {
const res = await axios({
url: '/listentogether/end',
method: 'post',
data: {
roomId: this.roomInfo.roomId,
},
})
console.log(res)
if (res.data.code != 200 || !res.data.data.success) {
this.message = '房间关闭失败'
} else {
this.message = '房间关闭成功'
this.roomInfo.roomId = null
}
},
loadPlaylist: async function () {
const res = await axios({
url: '/playlist/detail',
method: 'post',
data: {
id: this.playlistInfo.playlistId,
},
})
console.log(res)
this.playlistInfo.playlistName = res.data.playlist.name
this.playlistInfo.playlistTrackIds = res.data.playlist.trackIds
.map((track) => track.id)
.join(',')
const resa = await axios({
url: '/song/detail',
method: 'post',
data: {
ids: this.playlistInfo.playlistTrackIds,
},
})
console.log(resa)
this.playlistInfo.playlistTracks = resa.data.songs
if (this.roomInfo.roomId) {
const resb = await axios({
url: 'listentogether/sync/list/command',
method: 'post',
data: {
roomId: this.roomInfo.roomId,
commandType: 'REPLACE',
userId: this.account.userId,
version: this.clientSeq++,
playMode: 'ORDER_LOOP',
displayList: this.playlistInfo.playlistTrackIds,
randomList: this.playlistInfo.playlistTrackIds,
},
})
console.log(resb)
}
},
gotoTrack: async function (trackId) {
this.playingInfo.trackId = trackId
if (this.roomInfo.roomId) {
await this.playCommand('GOTO')
}
document.getElementById('player').src =
'https://music.163.com/song/media/outer/url?id=' + trackId + '.mp3'
},
playTrack: async function () {
this.playingInfo.status = 'PLAY'
await this.playCommand('PLAY')
document.getElementById('player').play()
},
pauseTrack: async function () {
this.playingInfo.status = 'PAUSE'
await this.playCommand('PAUSE')
document.getElementById('player').pause()
},
seekTrack: async function () {
this.playingInfo.status = 'PLAY'
await this.playCommand('seek')
document.getElementById('player').play()
},
playCommand: async function (action) {
const res = await axios({
url: 'listentogether/play/command',
method: 'post',
data: {
roomId: this.roomInfo.roomId,
progress: Math.floor(
document.getElementById('player').currentTime * 1000,
),
commandType: action,
formerSongId: '-1',
targetSongId: this.playingInfo.trackId,
clientSeq: this.clientSeq++,
playStatus: this.playingInfo.status,
},
})
console.log(res)
},
}).mount()
</script>
</html>