Files
web/server/pkg/weblive/danmaku.go

97 lines
1.9 KiB
Go

package weblive
import (
"encoding/json"
"strings"
"sync"
"time"
"unicode/utf8"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
)
const maxDanmakuRunes = 120
var (
danmakuClientsMu sync.Mutex
danmakuClients = make(map[*websocket.Conn]struct{})
)
// handleDanmakuWS 弹幕:客户端发 JSON {"text":"..."},服务端立刻向所有连接广播 {"type":"dm","text","ts"},不落库
func handleDanmakuWS(c *gin.Context) {
ws, err := upgrader.Upgrade(c.Writer, c.Request, nil)
if err != nil {
return
}
ws.SetReadLimit(4096)
danmakuClientsMu.Lock()
danmakuClients[ws] = struct{}{}
danmakuClientsMu.Unlock()
defer func() {
danmakuClientsMu.Lock()
delete(danmakuClients, ws)
danmakuClientsMu.Unlock()
_ = ws.Close()
}()
for {
mt, payload, err := ws.ReadMessage()
if err != nil {
return
}
if mt != websocket.TextMessage {
continue
}
text := extractDanmakuText(payload)
if text == "" {
continue
}
out, err := json.Marshal(map[string]interface{}{
"type": "dm",
"text": text,
"ts": time.Now().UnixMilli(),
})
if err != nil {
continue
}
danmakuBroadcast(out)
}
}
func extractDanmakuText(payload []byte) string {
var v struct {
Text string `json:"text"`
}
if err := json.Unmarshal(payload, &v); err != nil {
return ""
}
t := strings.TrimSpace(v.Text)
if t == "" {
return ""
}
if utf8.RuneCountInString(t) <= maxDanmakuRunes {
return t
}
runes := []rune(t)
return string(runes[:maxDanmakuRunes])
}
func danmakuBroadcast(b []byte) {
danmakuClientsMu.Lock()
defer danmakuClientsMu.Unlock()
dead := make([]*websocket.Conn, 0)
for conn := range danmakuClients {
_ = conn.SetWriteDeadline(time.Now().Add(8 * time.Second))
if err := conn.WriteMessage(websocket.TextMessage, b); err != nil {
dead = append(dead, conn)
}
}
for _, conn := range dead {
delete(danmakuClients, conn)
_ = conn.Close()
}
}