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