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{}) { }