feat: read, remove and clear listening history and switch recording over HTTP

This commit is contained in:
DiheChen committed 2026-11-10 12:24:22 +08:00
1 parent c6f53953b4
commit fe05ac5486
14 files changed
+722 -18

No files matched your search

+29
View File
@@ -187,6 +187,14 @@ func DecodeAdminResetAccountPasswordRequest(body []byte) (AdminResetAccountPassw
return decodeAdminResetAccountPasswordRequest(body, "")
}
func DecodeRemoveMyListenRequest(body []byte) (RemoveMyListenRequest, error) {
return decodeRemoveMyListenRequest(body, "")
}
func DecodeEnableListenRecordingRequest(body []byte) (EnableListenRecordingRequest, error) {
return decodeEnableListenRecordingRequest(body, "")
}
func decodeAddMyPlaylistItemRequest(raw json.RawMessage, path string) (AddMyPlaylistItemRequest, error) {
var v AddMyPlaylistItemRequest
m, err := decodeObject(raw, path, "track")
@@ -570,6 +578,12 @@ func decodeCreateTicketRequest(raw json.RawMessage, path string) (CreateTicketRe
return v, nil
}
func decodeEnableListenRecordingRequest(raw json.RawMessage, path string) (EnableListenRecordingRequest, error) {
var v EnableListenRecordingRequest
_, err := decodeObject(raw, path)
return v, err
}
func decodeHelloRequest(raw json.RawMessage, path string) (HelloRequest, error) {
var v HelloRequest
m, err := decodeObject(raw, path, "protocolVersion", "roomId", "ticket")
@@ -967,6 +981,21 @@ func decodeRegisterRequest(raw json.RawMessage, path string) (RegisterRequest, e
return v, nil
}
func decodeRemoveMyListenRequest(raw json.RawMessage, path string) (RemoveMyListenRequest, error) {
var v RemoveMyListenRequest
m, err := decodeObject(raw, path, "track")
if err != nil {
return v, err
}
if err = requireFields(m, path, "track"); err != nil {
return v, err
}
if v.Track, err = decodeTrack(m["track"], field(path, "track")); err != nil {
return v, err
}
return v, nil
}
func decodeRemoveMyPlaylistItemsRequest(raw json.RawMessage, path string) (RemoveMyPlaylistItemsRequest, error) {
var v RemoveMyPlaylistItemsRequest
m, err := decodeObject(raw, path, "tracks")
+73 -2
View File
@@ -352,6 +352,16 @@
"message": {"type": "string", "description": "平台给出的说明,可能为空"}
},
"required": ["status", "message"]
},
"MyListen": {
"type": "object",
"additionalProperties": false,
"properties": {
"track": {"$ref": "#/$defs/Track"},
"meta": {"$ref": "#/$defs/TrackMeta", "description": "听到时的元数据"},
"heardAtMs": {"type": "integer", "description": "最近一次听到这首歌时它的开播时间"}
},
"required": ["track", "meta", "heardAtMs"]
}
},
"x-ws": {
@@ -741,9 +751,10 @@
"additionalProperties": false,
"properties": {
"member": {"$ref": "#/$defs/Member"},
"account": {"$ref": "#/$defs/Account"}
"account": {"$ref": "#/$defs/Account"},
"recordListens": {"type": "boolean", "description": "是否把在房间里听到的歌记进播放历史"}
},
"required": ["member"]
"required": ["member", "recordListens"]
}
},
{
@@ -1553,6 +1564,66 @@
},
"required": ["password"]
}
},
{
"name": "listMyListens",
"description": "我的播放历史:在房间里听到过的歌,每首只留最近一次,最近听到的在前,最多 500 首;同一账号的所有设备看到同一份",
"method": "GET",
"path": "/api/me/listens",
"auth": "member",
"response": {
"type": "object",
"additionalProperties": false,
"properties": {
"items": {"type": "array", "items": {"$ref": "#/$defs/MyListen"}}
},
"required": ["items"]
}
},
{
"name": "removeMyListen",
"description": "从播放历史中删除一首歌;不在历史里的忽略",
"method": "POST",
"path": "/api/me/listens/removals",
"auth": "member",
"status": 204,
"request": {
"type": "object",
"additionalProperties": false,
"properties": {
"track": {"$ref": "#/$defs/Track"}
},
"required": ["track"]
}
},
{
"name": "clearMyListens",
"description": "清空播放历史",
"method": "DELETE",
"path": "/api/me/listens",
"auth": "member",
"status": 204
},
{
"name": "enableListenRecording",
"description": "开始把在房间里听到的歌记进播放历史",
"method": "PUT",
"path": "/api/me/listen-recording",
"auth": "member",
"status": 204,
"request": {
"type": "object",
"additionalProperties": false,
"properties": {}
}
},
{
"name": "disableListenRecording",
"description": "不再记录播放历史,已有的历史保留",
"method": "DELETE",
"path": "/api/me/listen-recording",
"auth": "member",
"status": 204
}
]
}
+213 -2
View File
@@ -570,6 +570,10 @@
"additionalProperties": false,
"type": "object"
},
"EnableListenRecordingRequest": {
"additionalProperties": false,
"type": "object"
},
"Entry": {
"additionalProperties": false,
"properties": {
@@ -690,10 +694,15 @@
},
"member": {
"$ref": "#/components/schemas/Member"
},
"recordListens": {
"description": "是否把在房间里听到的歌记进播放历史",
"type": "boolean"
}
},
"required": [
"member"
"member",
"recordListens"
],
"type": "object"
},
@@ -829,6 +838,21 @@
],
"type": "object"
},
"ListMyListensResponse": {
"additionalProperties": false,
"properties": {
"items": {
"items": {
"$ref": "#/components/schemas/MyListen"
},
"type": "array"
}
},
"required": [
"items"
],
"type": "object"
},
"ListMyPlaylistItemsResponse": {
"additionalProperties": false,
"properties": {
@@ -1014,6 +1038,28 @@
],
"type": "object"
},
"MyListen": {
"additionalProperties": false,
"properties": {
"heardAtMs": {
"description": "最近一次听到这首歌时它的开播时间",
"type": "integer"
},
"meta": {
"$ref": "#/components/schemas/TrackMeta",
"description": "听到时的元数据"
},
"track": {
"$ref": "#/components/schemas/Track"
}
},
"required": [
"track",
"meta",
"heardAtMs"
],
"type": "object"
},
"MyPlaylist": {
"additionalProperties": false,
"properties": {
@@ -1386,6 +1432,18 @@
],
"type": "object"
},
"RemoveMyListenRequest": {
"additionalProperties": false,
"properties": {
"track": {
"$ref": "#/components/schemas/Track"
}
},
"required": [
"track"
],
"type": "object"
},
"RemoveMyPlaylistItemsRequest": {
"additionalProperties": false,
"properties": {
@@ -1898,7 +1956,7 @@
},
"info": {
"title": "Alisten",
"version": "ed36759d403f9894"
"version": "ae9a632d648f3840"
},
"openapi": "3.1.0",
"paths": {
@@ -3035,6 +3093,159 @@
"summary": "修改昵称和头像;email 用来计算头像哈希,已注册的账号同时保存它,去掉首尾空白后应形如 name@example.com,空字符串表示清除"
}
},
"/api/me/listen-recording": {
"delete": {
"operationId": "disableListenRecording",
"responses": {
"204": {
"description": "成功"
},
"default": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
},
"description": "失败"
}
},
"security": [
{
"memberToken": []
}
],
"summary": "不再记录播放历史,已有的历史保留"
},
"put": {
"operationId": "enableListenRecording",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/EnableListenRecordingRequest"
}
}
},
"required": true
},
"responses": {
"204": {
"description": "成功"
},
"default": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
},
"description": "失败"
}
},
"security": [
{
"memberToken": []
}
],
"summary": "开始把在房间里听到的歌记进播放历史"
}
},
"/api/me/listens": {
"delete": {
"operationId": "clearMyListens",
"responses": {
"204": {
"description": "成功"
},
"default": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
},
"description": "失败"
}
},
"security": [
{
"memberToken": []
}
],
"summary": "清空播放历史"
},
"get": {
"operationId": "listMyListens",
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ListMyListensResponse"
}
}
},
"description": "成功"
},
"default": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
},
"description": "失败"
}
},
"security": [
{
"memberToken": []
}
],
"summary": "我的播放历史:在房间里听到过的歌,每首只留最近一次,最近听到的在前,最多 500 首;同一账号的所有设备看到同一份"
}
},
"/api/me/listens/removals": {
"post": {
"operationId": "removeMyListen",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RemoveMyListenRequest"
}
}
},
"required": true
},
"responses": {
"204": {
"description": "成功"
},
"default": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
},
"description": "失败"
}
},
"security": [
{
"memberToken": []
}
],
"summary": "从播放历史中删除一首歌;不在历史里的忽略"
}
},
"/api/me/playlist-links": {
"post": {
"operationId": "resolveMyPlaylistLink",
+33 -3
View File
@@ -4,7 +4,7 @@ package protocol
import "encoding/json"
const Version = "ed36759d403f9894"
const Version = "ae9a632d648f3840"
type APIKey struct {
Key string `json:"key"`
@@ -212,6 +212,8 @@ type CreateTicketResponse struct {
type Empty struct{}
type EnableListenRecordingRequest struct{}
type Entry struct {
Auto bool `json:"auto"`
By *Member `json:"by"`
@@ -253,8 +255,9 @@ type ErrorInfo struct {
}
type GetMeResponse struct {
Account *Account `json:"account,omitempty"`
Member Member `json:"member"`
Account *Account `json:"account,omitempty"`
Member Member `json:"member"`
RecordListens bool `json:"recordListens"`
}
type GetSiteResponse struct {
@@ -313,6 +316,18 @@ type ImportedTrack struct {
Track Track `json:"track"`
}
type ListMyListensResponse struct {
Items []MyListen `json:"items"`
}
func (v ListMyListensResponse) MarshalJSON() ([]byte, error) {
type plain ListMyListensResponse
if v.Items == nil {
v.Items = []MyListen{}
}
return json.Marshal(plain(v))
}
type ListMyPlaylistItemsResponse struct {
Items []MyPlaylistItem `json:"items"`
}
@@ -410,6 +425,12 @@ func (v MoveMyPlaylistItemsRequest) MarshalJSON() ([]byte, error) {
return json.Marshal(plain(v))
}
type MyListen struct {
HeardAtMs int64 `json:"heardAtMs"`
Meta TrackMeta `json:"meta"`
Track Track `json:"track"`
}
type MyPlaylist struct {
CoverURL *string `json:"coverUrl"`
CreatedAtMs int64 `json:"createdAtMs"`
@@ -581,6 +602,10 @@ type RegisterResponse struct {
Account Account `json:"account"`
}
type RemoveMyListenRequest struct {
Track Track `json:"track"`
}
type RemoveMyPlaylistItemsRequest struct {
Tracks []Track `json:"tracks"`
}
@@ -865,6 +890,11 @@ var (
RouteAdminCloseRegistration = Route{Pattern: "DELETE /api/admin/registration", Auth: AuthAdmin, Status: 204}
RouteAdminGetAccount = Route{Pattern: "GET /api/admin/accounts/{username}", Auth: AuthAdmin, Status: 200}
RouteAdminResetAccountPassword = Route{Pattern: "POST /api/admin/accounts/{username}/password", Auth: AuthAdmin, Status: 200}
RouteListMyListens = Route{Pattern: "GET /api/me/listens", Auth: AuthMember, Status: 200}
RouteRemoveMyListen = Route{Pattern: "POST /api/me/listens/removals", Auth: AuthMember, Status: 204}
RouteClearMyListens = Route{Pattern: "DELETE /api/me/listens", Auth: AuthMember, Status: 204}
RouteEnableListenRecording = Route{Pattern: "PUT /api/me/listen-recording", Auth: AuthMember, Status: 204}
RouteDisableListenRecording = Route{Pattern: "DELETE /api/me/listen-recording", Auth: AuthMember, Status: 204}
)
var RoomRoutes = []RoomRoute{