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.

342 lines
9.0 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
var ErrPlayerNotExists = errors.New("code not exists")
var ErrParamsInvalid = errors.New("params invalid")
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{})
s.Join("clients")
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)
})
c.events.AddListener("bindUser", c.OnBindUser)
c.events.AddListener("playMusic", c.OnPlayMusic)
c.events.AddListener("playSoundEffect", c.OnPlaySoundEffect)
c.events.AddListener("playVoice", c.OnPlayVoice)
c.events.AddListener("stopPlay", c.OnStopPlay)
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.Token != "" {
if ctx.PairCode != "" {
c.storeModel.RemoveCodeInfo(ctx.PairCode)
}
playerInfo := c.storeModel.GetPlayerInfo(ctx.Token)
if playerInfo != nil {
playerInfo.PairCode = ""
playerInfo.IsOnline = false
c.storeModel.SetPlayerInfo(ctx.Token, playerInfo)
}
}
}
// 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.BoundUser != "" {
ctx.User = playerInfo.BoundUser
userName := playerInfo.BoundUser
userInfo := c.storeModel.GetUserInfo(playerInfo.BoundUser)
if userInfo != nil {
userName = userInfo.DisplayName
}
// 通知客户端
socket.Emit("user:update", ctx.User, userName)
// 加入房间
c.SwitchRoom(socket, "user", playerInfo.BoundUser)
}
if playerInfo.PairCode != "" { // 删除存储中的code
c.storeModel.RemoveCodeInfo(playerInfo.PairCode)
playerInfo.PairCode = ""
c.storeModel.SetPlayerInfo(ctx.Token, playerInfo)
}
playerInfo.IsOnline = true
c.storeModel.SetPlayerInfo(ctx.Token, playerInfo)
socket.Emit("token:update", token)
}
}
// 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)
playerInfo.Name = utils.GetPlayerNameFromUserAgent(socket.RemoteHeader().Get("User-Agent"))
} 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)
}
// 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) (string, error) {
// code为空则不绑定
if code == "" {
return "", ErrParamsInvalid
}
token := ""
c.sockets.ForEach("/", "clients", func(socket socketio.Conn) {
ctx := GetContext[ClientSocketContext](socket)
if ctx.PairCode == code { // 找到需要绑定的客户端
token = ctx.Token
c.events.EmitEvent("bindUser", &events.BindUserEvent{
Token: token,
User: user,
})
}
})
if token != "" {
return token, nil
} else {
return "", ErrPlayerNotExists
}
}
func (c *ClientSocketController) OnBindUser(event events.BindUserEvent) {
userName := event.User
userInfo := c.storeModel.GetUserInfo(event.User)
if userInfo == nil {
log.Println("Cannot find user info for user name '" + event.User + "' while bind player")
return
}
userName = userInfo.DisplayName
playerInfo := c.storeModel.GetPlayerInfo(event.Token)
if playerInfo == nil {
log.Println("Cannot find player info for token '" + event.Token + "' while bind player")
return
}
// 存储信息
userInfo.BoundPlayer = append(userInfo.BoundPlayer, event.Token)
playerInfo.BoundUser = event.User
c.storeModel.SetUserInfo(event.User, userInfo)
c.storeModel.SetPlayerInfo(event.Token, playerInfo)
// 通知客户端
c.sockets.ForEach("/", "", func(socket socketio.Conn) {
ctx := GetContext[ClientSocketContext](socket)
if ctx.Token == event.Token {
socket.Emit("user:update", event.User, userName)
// 加入房间
c.SwitchRoom(socket, "user", event.User)
}
})
}
// 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)
}
// OnPlaySoundEffect 用户触发播放音效
func (c *ClientSocketController) OnPlaySoundEffect(eventObj ...interface{}) {
}
// OnPlayVoice 用户触发播放语音
func (c *ClientSocketController) OnPlayVoice(eventObj ...interface{}) {
}
// OnStopPlay 用户触发停止播放
func (c *ClientSocketController) OnStopPlay(eventObj ...interface{}) {
}