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

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
}
}
2 years ago
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{}) {
}