You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

289 lines
7.4 KiB
Go

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package controller
import (
"errors"
"log"
"strings"
socketio "github.com/googollee/go-socket.io"
"github.com/hyperzlib/isekai-remote-playback/context"
"github.com/hyperzlib/isekai-remote-playback/events"
"github.com/hyperzlib/isekai-remote-playback/store"
"github.com/hyperzlib/isekai-remote-playback/utils"
"github.com/pochard/commons/randstr"
)
type ClientSocketContext struct {
Token string
User string
PairCode string
}
type ClientSocketController struct {
SocketController
}
var clientSocketControllerInstance *ClientSocketController
func NewClientSocketController(basePath string, ctx *context.ServerContext) (*ClientSocketController, error) {
instance := &ClientSocketController{
SocketController{
basePath: basePath,
sockets: ctx.Sockets,
events: ctx.EventSource,
context: ctx,
storeModel: store.NewStoreModel(),
},
}
err := instance.Initialize()
if err != nil {
return nil, err
}
clientSocketControllerInstance = instance
return instance, nil
}
func (c *ClientSocketController) Initialize() error {
server := c.sockets
server.OnConnect(c.basePath, func(s socketio.Conn) error {
s.SetContext(&ClientSocketContext{})
return nil
})
server.OnDisconnect(c.basePath, c.OnDisconnect)
server.OnEvent(c.basePath, "token:refresh", c.RefreshToken)
server.OnEvent(c.basePath, "token:set", c.SetToken)
server.OnEvent(c.basePath, "code:get", c.GetCode)
server.OnEvent(c.basePath, "player:name:set", c.SetPlayerName)
server.OnEvent(c.basePath, "player:play", c.SetPlayerName)
server.OnError(c.basePath, func(s socketio.Conn, e error) {
log.Println("meet error:", e)
})
return nil
}
// SwitchRoom 切换room并退出所有同前缀的room
func (c *ClientSocketController) SwitchRoom(socket socketio.Conn, prefix string, room string) {
prefixWithColon := prefix + ":"
for _, roomName := range socket.Rooms() {
if strings.HasPrefix(roomName, prefixWithColon) {
socket.Leave(roomName)
}
}
if room != "" {
socket.Join(prefixWithColon + ":" + room)
}
}
// OnDisconnect 处理连接断开
func (c *ClientSocketController) OnDisconnect(socket socketio.Conn, reason string) {
ctx := GetContext[ClientSocketContext](socket)
// 删除存储中的code
if ctx.PairCode != "" {
c.storeModel.RemoveCodeInfo(ctx.PairCode)
if ctx.Token != "" {
playerInfo := c.storeModel.GetPlayerInfo(ctx.Token)
if playerInfo != nil {
playerInfo.PairCode = ""
c.storeModel.SetPlayerInfo(ctx.Token, playerInfo)
}
}
}
}
// RefreshToken 刷新token
func (c *ClientSocketController) RefreshToken(socket socketio.Conn) {
ctx := GetContext[ClientSocketContext](socket)
oldToken := ctx.Token
var playerInfo *store.PlayerInfo
if oldToken != "" {
playerInfo = c.storeModel.GetPlayerInfo(oldToken)
}
newToken := randstr.RandomAlphanumeric(32)
ctx.Token = newToken
if playerInfo == nil {
playerInfo = new(store.PlayerInfo)
} else {
c.storeModel.RemovePlayerInfo(oldToken)
}
if playerInfo.PairCode != "" { // 删除存储中的code
c.storeModel.RemoveCodeInfo(playerInfo.PairCode)
playerInfo.PairCode = ""
}
c.storeModel.SetPlayerInfo(newToken, playerInfo)
socket.Emit("token:update", newToken)
}
// SetToken 设置token
func (c *ClientSocketController) SetToken(socket socketio.Conn, token string) {
ctx := GetContext[ClientSocketContext](socket)
if token != "" {
ctx.Token = token
playerInfo := c.storeModel.GetPlayerInfo(token)
if playerInfo == nil {
socket.Emit("token:expired")
return
}
if playerInfo.BindingUser != "" {
ctx.User = playerInfo.BindingUser
userName := playerInfo.BindingUser
userInfo := c.storeModel.GetUserInfo(playerInfo.BindingUser)
if userInfo != nil {
userName = userInfo.DisplayName
}
// 通知客户端
socket.Emit("user:update", userName)
// 加入房间
c.SwitchRoom(socket, "user", playerInfo.BindingUser)
}
if playerInfo.PairCode != "" { // 删除存储中的code
c.storeModel.RemoveCodeInfo(playerInfo.PairCode)
playerInfo.PairCode = ""
c.storeModel.SetPlayerInfo(ctx.Token, playerInfo)
}
socket.Emit("token:update", token)
}
}
// GetCode 获取连接码
func (c *ClientSocketController) GetCode(socket socketio.Conn) {
ctx := GetContext[ClientSocketContext](socket)
newCode := randstr.RandomNumeric(6)
socket.Emit("code:update", newCode)
ctx.PairCode = newCode
}
// SetPlayerName 设置播放器设备名
func (c *ClientSocketController) SetPlayerName(socket socketio.Conn, name string) {
ctx := GetContext[ClientSocketContext](socket)
playerInfo := c.storeModel.GetPlayerInfo(ctx.Token)
if playerInfo == nil {
return
}
playerInfo.Name = name
c.storeModel.SetPlayerInfo(ctx.Token, playerInfo)
socket.Emit("player:name:update", name)
}
// PingbackPlay 播放器开始播放
func (c *ClientSocketController) PingbackPlay(socket socketio.Conn, currentTime int64, musicId string, playTime int64) {
ctx := GetContext[ClientSocketContext](socket)
c.events.EmitEvent("playerStartPlay", &events.PlayerStartPlay{
User: ctx.User,
MusicId: musicId,
CurrentTime: currentTime,
PlayTime: playTime,
})
}
// PingbackPause 播放器暂停
func (c *ClientSocketController) PingbackPause(socket socketio.Conn, currentTime int64, musicId string, playTime int64) {
ctx := GetContext[ClientSocketContext](socket)
c.events.EmitEvent("playerPause", &events.PlayerPause{
User: ctx.User,
MusicId: musicId,
CurrentTime: currentTime,
PlayTime: playTime,
})
}
// PingbackStop 播放器停止
func (c *ClientSocketController) PingbackStop(socket socketio.Conn, musicId string, isEnd bool) {
ctx := GetContext[ClientSocketContext](socket)
c.events.EmitEvent("playerStop", &events.PlayerStop{
User: ctx.User,
MusicId: musicId,
})
}
// BindPlayer 用户触发绑定播放器
func (c *ClientSocketController) BindPlayer(user string, code string) (*store.PlayerInfo, error) {
// code为空则不绑定
if code == "" {
return nil, errors.New("code invalid")
}
c.sockets.ForEach("/", "", func(socket socketio.Conn) {
ctx := GetContext[ClientSocketContext](socket)
if ctx.PairCode == code {
// 找到需要绑定的客户端
c.events.EmitEvent("bindUser", &events.BindUserEvent{
Token: ctx.Token,
User: user,
})
userName := user
userInfo := c.storeModel.GetUserInfo(user)
if userInfo != nil {
userName = userInfo.DisplayName
}
// 通知客户端
socket.Emit("user:update", userName)
// 加入房间
c.SwitchRoom(socket, "user", user)
return
}
})
return nil, errors.New("code not exists")
}
// OnPlayMusic 用户触发播放音乐
func (c *ClientSocketController) OnPlayMusic(eventObj ...interface{}) {
event, err := utils.GetEvent[events.PlayMusicEvent](eventObj)
if err != nil {
log.Panicln("Cannot get event in OnPlayMusic: ", err.Error())
}
type PlayMusicResponse struct {
MusicId string `json:"music_id"`
Src map[string]string `json:"src"`
Name string `json:"name"`
LyricUrl string `json:"lyric_url"`
ImageUrl string `json:"image_url"`
Loop bool `json:"loop"`
Pingback bool `json:"pingback"`
}
res := &PlayMusicResponse{
Src: event.Src,
Name: event.Name,
LyricUrl: event.LyricUrl,
ImageUrl: event.ImageUrl,
Loop: event.Loop,
Pingback: event.Pingback,
}
c.sockets.BroadcastToRoom("/", "user"+event.User, "music:play", res)
}
func (c *ClientSocketController) OnPlaySE(eventObj interface{}) {
}