mirror of
https://github.com/AynaLivePlayer/AynaLivePlayer.git
synced 2025-12-10 20:28:13 +08:00
add web output backend
This commit is contained in:
33
plugin/webinfo/info.go
Normal file
33
plugin/webinfo/info.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package webinfo
|
||||
|
||||
import "AynaLivePlayer/player"
|
||||
|
||||
type MediaInfo struct {
|
||||
Index int
|
||||
Title string
|
||||
Artist string
|
||||
Album string
|
||||
Username string
|
||||
Cover player.Picture
|
||||
}
|
||||
|
||||
type OutInfo struct {
|
||||
Current MediaInfo
|
||||
CurrentTime int
|
||||
TotalTime int
|
||||
Lyric string
|
||||
Playlist []MediaInfo
|
||||
}
|
||||
|
||||
const (
|
||||
OutInfoC = "Current"
|
||||
OutInfoCT = "CurrentTime"
|
||||
OutInfoTT = "TotalTime"
|
||||
OutInfoL = "Lyric"
|
||||
OutInfoPL = "Playlist"
|
||||
)
|
||||
|
||||
type WebsocketData struct {
|
||||
Update string
|
||||
Data OutInfo
|
||||
}
|
||||
144
plugin/webinfo/server.go
Normal file
144
plugin/webinfo/server.go
Normal file
@@ -0,0 +1,144 @@
|
||||
package webinfo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gorilla/websocket"
|
||||
"net/http"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var upgrader = websocket.Upgrader{
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
return true
|
||||
},
|
||||
}
|
||||
|
||||
type WebInfoServer struct {
|
||||
Info OutInfo
|
||||
Server *http.Server
|
||||
Clients map[*Client]int
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
conn *websocket.Conn
|
||||
Data chan []byte
|
||||
Close chan byte
|
||||
}
|
||||
|
||||
func NewWebInfoServer(port int) *WebInfoServer {
|
||||
server := &WebInfoServer{
|
||||
Clients: map[*Client]int{},
|
||||
}
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("/", http.FileServer(http.Dir("./assets/webinfo")))
|
||||
mux.HandleFunc("/ws/info", server.handleInfo)
|
||||
mux.HandleFunc("/api/info", server.getInfo)
|
||||
server.Server = &http.Server{
|
||||
Addr: fmt.Sprintf(":%d", port),
|
||||
Handler: mux,
|
||||
}
|
||||
return server
|
||||
}
|
||||
|
||||
func (s *WebInfoServer) getInfo(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
d, _ := json.Marshal(s.Info)
|
||||
_, err := w.Write(d)
|
||||
if err != nil {
|
||||
lg.Warnf("api get info error: %s", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (s *WebInfoServer) handleInfo(w http.ResponseWriter, r *http.Request) {
|
||||
lg.Debug("connection start")
|
||||
conn, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
lg.Warnf("upgrade error: %s", err)
|
||||
return
|
||||
}
|
||||
client := &Client{
|
||||
conn: conn,
|
||||
Data: make(chan []byte, 16),
|
||||
Close: make(chan byte, 1),
|
||||
}
|
||||
s.addClient(client)
|
||||
defer s.removeClient(client)
|
||||
go func() {
|
||||
for {
|
||||
_, _, err := client.conn.ReadMessage()
|
||||
if err != nil {
|
||||
client.Close <- 1
|
||||
}
|
||||
}
|
||||
}()
|
||||
for {
|
||||
lg.Trace("waiting for message")
|
||||
select {
|
||||
case data := <-client.Data:
|
||||
writer, err := client.conn.NextWriter(websocket.TextMessage)
|
||||
if err != nil {
|
||||
lg.Warn("get writer error", err)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = writer.Write(data); err != nil {
|
||||
lg.Warn("send error:", err)
|
||||
return
|
||||
}
|
||||
if err = writer.Close(); err != nil {
|
||||
lg.Warnf("can't close writer: %s", err)
|
||||
return
|
||||
}
|
||||
case _ = <-client.Close:
|
||||
lg.Debug("client close")
|
||||
if err := client.conn.Close(); err != nil {
|
||||
lg.Warnf("close connection encouter an error: %s", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *WebInfoServer) SendInfo(update string, info OutInfo) {
|
||||
for client := range s.Clients {
|
||||
d, _ := json.Marshal(WebsocketData{Update: update, Data: info})
|
||||
client.Data <- d
|
||||
}
|
||||
}
|
||||
|
||||
func (s *WebInfoServer) addClient(c *Client) {
|
||||
s.lock.Lock()
|
||||
s.Clients[c] = 1
|
||||
s.lock.Unlock()
|
||||
}
|
||||
|
||||
func (s *WebInfoServer) removeClient(c *Client) {
|
||||
s.lock.Lock()
|
||||
close(c.Data)
|
||||
delete(s.Clients, c)
|
||||
s.lock.Unlock()
|
||||
}
|
||||
|
||||
func (s *WebInfoServer) Start() {
|
||||
go func() {
|
||||
err := s.Server.ListenAndServe()
|
||||
if err == http.ErrServerClosed {
|
||||
lg.Info("server closed")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
lg.Warnf("Failed to start webinfo server: %s", err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (s *WebInfoServer) Stop() error {
|
||||
return s.Server.Shutdown(context.Background())
|
||||
}
|
||||
140
plugin/webinfo/webinfo.go
Normal file
140
plugin/webinfo/webinfo.go
Normal file
@@ -0,0 +1,140 @@
|
||||
package webinfo
|
||||
|
||||
import (
|
||||
"AynaLivePlayer/config"
|
||||
"AynaLivePlayer/controller"
|
||||
"AynaLivePlayer/event"
|
||||
"AynaLivePlayer/gui"
|
||||
"AynaLivePlayer/i18n"
|
||||
"AynaLivePlayer/logger"
|
||||
"AynaLivePlayer/player"
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
"github.com/aynakeya/go-mpv"
|
||||
)
|
||||
|
||||
const MODULE_PLGUIN_WEBINFO = "plugin.webinfo"
|
||||
|
||||
var lg = logger.Logger.WithField("Module", MODULE_PLGUIN_WEBINFO)
|
||||
|
||||
type WebInfo struct {
|
||||
Port int
|
||||
server *WebInfoServer
|
||||
}
|
||||
|
||||
func NewWebInfo() *WebInfo {
|
||||
return &WebInfo{
|
||||
Port: 4000,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WebInfo) Name() string {
|
||||
return "WebInfo"
|
||||
}
|
||||
|
||||
func (w *WebInfo) Title() string {
|
||||
return i18n.T("plugin.webinfo.title")
|
||||
}
|
||||
|
||||
func (w *WebInfo) Description() string {
|
||||
return i18n.T("plugin.webinfo.description")
|
||||
}
|
||||
|
||||
func (w *WebInfo) Enable() error {
|
||||
config.LoadConfig(w)
|
||||
w.server = NewWebInfoServer(w.Port)
|
||||
lg.Info("starting web backend server")
|
||||
w.server.Start()
|
||||
w.registerHandlers()
|
||||
gui.AddConfigLayout(w)
|
||||
lg.Info("webinfo loaded")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *WebInfo) Disable() error {
|
||||
lg.Info("closing webinfo backend server")
|
||||
if err := w.server.Stop(); err != nil {
|
||||
lg.Warnf("stop webinfo server encouter an error: %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *WebInfo) registerHandlers() {
|
||||
controller.MainPlayer.EventHandler.RegisterA(player.EventPlay, "plugin.webinfo.current", func(event *event.Event) {
|
||||
t.server.Info.Current = MediaInfo{
|
||||
Index: 0,
|
||||
Title: event.Data.(player.PlayEvent).Media.Title,
|
||||
Artist: event.Data.(player.PlayEvent).Media.Artist,
|
||||
Album: event.Data.(player.PlayEvent).Media.Album,
|
||||
Cover: event.Data.(player.PlayEvent).Media.Cover,
|
||||
Username: event.Data.(player.PlayEvent).Media.ToUser().Name,
|
||||
}
|
||||
t.server.SendInfo(
|
||||
OutInfoC,
|
||||
OutInfo{Current: t.server.Info.Current},
|
||||
)
|
||||
})
|
||||
if controller.MainPlayer.ObserveProperty("time-pos", func(property *mpv.EventProperty) {
|
||||
if property.Data == nil {
|
||||
t.server.Info.CurrentTime = 0
|
||||
return
|
||||
}
|
||||
ct := int(property.Data.(mpv.Node).Value.(float64))
|
||||
if ct == t.server.Info.CurrentTime {
|
||||
return
|
||||
}
|
||||
t.server.Info.CurrentTime = ct
|
||||
t.server.SendInfo(
|
||||
OutInfoCT,
|
||||
OutInfo{CurrentTime: t.server.Info.CurrentTime},
|
||||
)
|
||||
}) != nil {
|
||||
lg.Error("register time-pos handler failed")
|
||||
}
|
||||
if controller.MainPlayer.ObserveProperty("duration", func(property *mpv.EventProperty) {
|
||||
if property.Data == nil {
|
||||
t.server.Info.TotalTime = 0
|
||||
return
|
||||
}
|
||||
t.server.Info.TotalTime = int(property.Data.(mpv.Node).Value.(float64))
|
||||
t.server.SendInfo(
|
||||
OutInfoTT,
|
||||
OutInfo{TotalTime: t.server.Info.TotalTime},
|
||||
)
|
||||
}) != nil {
|
||||
lg.Error("fail to register handler for total time with property duration")
|
||||
}
|
||||
controller.UserPlaylist.Handler.RegisterA(player.EventPlaylistUpdate, "plugin.webinfo.playlist", func(event *event.Event) {
|
||||
pl := make([]MediaInfo, 0)
|
||||
e := event.Data.(player.PlaylistUpdateEvent)
|
||||
e.Playlist.Lock.RLock()
|
||||
for index, m := range e.Playlist.Playlist {
|
||||
pl = append(pl, MediaInfo{
|
||||
Index: index,
|
||||
Title: m.Title,
|
||||
Artist: m.Artist,
|
||||
Album: m.Album,
|
||||
Username: m.ToUser().Name,
|
||||
})
|
||||
}
|
||||
e.Playlist.Lock.RUnlock()
|
||||
t.server.Info.Playlist = pl
|
||||
t.server.SendInfo(
|
||||
OutInfoPL,
|
||||
OutInfo{Playlist: t.server.Info.Playlist},
|
||||
)
|
||||
})
|
||||
controller.CurrentLyric.Handler.RegisterA(player.EventLyricUpdate, "plugin.webinfo.lyric", func(event *event.Event) {
|
||||
lrcLine := event.Data.(player.LyricUpdateEvent).Lyric
|
||||
t.server.Info.Lyric = lrcLine.Lyric
|
||||
t.server.SendInfo(
|
||||
OutInfoL,
|
||||
OutInfo{Lyric: t.server.Info.Lyric},
|
||||
)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func (w *WebInfo) CreatePanel() fyne.CanvasObject {
|
||||
return widget.NewLabel("adf")
|
||||
}
|
||||
Reference in New Issue
Block a user