diff --git a/app/main.go b/app/main.go index a9aa112..0eec2fe 100644 --- a/app/main.go +++ b/app/main.go @@ -58,9 +58,12 @@ func main() { global.EventManager.Start() }() gui.MainWindow.ShowAndRun() + global.Logger.Info("closing internal server") internal.Stop() + global.Logger.Infof("closing event manager") global.EventManager.Stop() if *dev { + global.Logger.Infof("saving translation") i18n.SaveTranslation() } _ = config.SaveToConfigFile(config.ConfigPath) diff --git a/assets/icon2.png b/assets/icon2.png new file mode 100644 index 0000000..88ccf96 Binary files /dev/null and b/assets/icon2.png differ diff --git a/assets/translation.json b/assets/translation.json index 75716f0..e5b1248 100644 --- a/assets/translation.json +++ b/assets/translation.json @@ -52,6 +52,14 @@ "en": "Basic", "zh-CN": "基础设置" }, + "gui.config.basic.use_system_playlist": { + "en": "Play system playlist when no music", + "zh-CN": "是否播放闲置歌单(实验性)" + }, + "gui.config.basic.use_system_playlist.prompt": { + "en": "Yes", + "zh-CN": "是" + }, "gui.history.artist": { "en": "Artist", "zh-CN": "歌手" @@ -543,6 +551,10 @@ "plugin.wshub.title": { "en": "Websocket Hub", "zh-CN": "Websocket服务器" + }, + "plugin.wshub.webinfo_text": { + "en": "Obs browser output", + "zh-CN": "OBS网页输出: " } } } diff --git a/go.mod b/go.mod index 5772918..ce937c7 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ replace ( ) require ( - fyne.io/fyne/v2 v2.5.0 + fyne.io/fyne/v2 v2.5.1 github.com/AynaLivePlayer/liveroom-sdk v0.1.0 github.com/AynaLivePlayer/miaosic v0.1.5 github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b @@ -52,7 +52,7 @@ require ( github.com/fyne-io/glfw-js v0.0.0-20240101223322-6e1efdc71b7a // indirect github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2 // indirect github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 // indirect - github.com/go-text/render v0.1.0 // indirect + github.com/go-text/render v0.1.1-0.20240418202334-dd62631dae9b // indirect github.com/go-text/typesetting v0.1.0 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/google/uuid v1.5.0 // indirect @@ -63,7 +63,7 @@ require ( github.com/mattn/go-isatty v0.0.19 // indirect github.com/nicksnyder/go-i18n/v2 v2.4.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/rymdport/portal v0.2.2 // indirect + github.com/rymdport/portal v0.2.6 // indirect github.com/sahilm/fuzzy v0.1.0 // indirect github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d // indirect github.com/spf13/cast v1.5.1 // indirect diff --git a/gui/config_basic.go b/gui/config_basic.go index 70abf30..def8a64 100644 --- a/gui/config_basic.go +++ b/gui/config_basic.go @@ -124,6 +124,13 @@ func (b *bascicConfig) CreatePanel() fyne.CanvasObject { checkUpdateBtn := widget.NewButton(i18n.T("gui.config.basic.check_update"), func() { global.EventManager.CallA(events.CheckUpdateCmd, events.CheckUpdateCmdEvent{}) }) - b.panel = container.NewVBox(randomPlaylist, skipWhenErr, outputDevice, checkUpdateBox, checkUpdateBtn) + useSysPlaylistBtn := container.NewHBox( + widget.NewLabel(i18n.T("gui.config.basic.use_system_playlist")), + component.NewCheckOneWayBinding( + i18n.T("gui.config.basic.use_system_playlist.prompt"), + &config.General.UseSystemPlaylist, + config.General.UseSystemPlaylist), + ) + b.panel = container.NewVBox(randomPlaylist, useSysPlaylistBtn, skipWhenErr, outputDevice, checkUpdateBox, checkUpdateBtn) return b.panel } diff --git a/gui/gui.go b/gui/gui.go index 7e7e568..13b32ef 100644 --- a/gui/gui.go +++ b/gui/gui.go @@ -79,15 +79,20 @@ func Initialize() { }) checkUpdate() - MainWindow.SetFixedSize(true) + MainWindow.SetFixedSize(config.General.FixedSize) if config.General.ShowSystemTray { setupSysTray() } else { MainWindow.SetCloseIntercept( func() { - // save twice i don;t care + // todo: save twice i don;t care _ = config.SaveToConfigFile(config.ConfigPath) MainWindow.Close() }) } + MainWindow.SetOnClosed(func() { + if playerWindow != nil { + playerWindow.Close() + } + }) } diff --git a/gui/player_videoplayer.go b/gui/player_videoplayer.go index 8ee6b2d..96bcb13 100644 --- a/gui/player_videoplayer.go +++ b/gui/player_videoplayer.go @@ -13,9 +13,6 @@ func setupPlayerWindow() { playerWindow.SetCloseIntercept(func() { playerWindow.Hide() }) - MainWindow.SetOnClosed(func() { - playerWindow.Close() - }) playerWindow.Hide() } diff --git a/gui/xfyne/patch.go b/gui/xfyne/patch.go index 5e11c48..798a83d 100644 --- a/gui/xfyne/patch.go +++ b/gui/xfyne/patch.go @@ -1,14 +1,14 @@ package xfyne import ( - "fyne.io/fyne/v2" "fyne.io/fyne/v2/widget" - "reflect" ) func EntryDisableUndoRedo(entry *widget.Entry) *widget.Entry { - val := reflect.ValueOf(entry).Elem().FieldByName("shortcut").Addr().UnsafePointer() - (*fyne.ShortcutHandler)(val).RemoveShortcut(&fyne.ShortcutRedo{}) - (*fyne.ShortcutHandler)(val).RemoveShortcut(&fyne.ShortcutUndo{}) + // do nothing because the bug has been fixed in fyne@v2.5.1 return entry + //val := reflect.ValueOf(entry).Elem().FieldByName("shortcut").Addr().UnsafePointer() + //(*fyne.ShortcutHandler)(val).RemoveShortcut(&fyne.ShortcutRedo{}) + //(*fyne.ShortcutHandler)(val).RemoveShortcut(&fyne.ShortcutUndo{}) + //return entry } diff --git a/internal/controller/controller.go b/internal/controller/controller.go index 579bb38..334c7b8 100644 --- a/internal/controller/controller.go +++ b/internal/controller/controller.go @@ -65,13 +65,17 @@ func handlePlayNext() { events.PlaylistNextCmdEvent{ Remove: true, }) - } else { - log.Infof("Try to play next media in system playlist") - global.EventManager.CallA(events.PlaylistNextCmd(model.PlaylistIDSystem), - events.PlaylistNextCmdEvent{ - Remove: false, - }) + return } + if !config.General.UseSystemPlaylist { + // do not play system playlist + return + } + log.Infof("Try to play next media in system playlist") + global.EventManager.CallA(events.PlaylistNextCmd(model.PlaylistIDSystem), + events.PlaylistNextCmdEvent{ + Remove: false, + }) }) global.EventManager.RegisterA( diff --git a/internal/internal.go b/internal/internal.go index bcce0be..87c72b6 100644 --- a/internal/internal.go +++ b/internal/internal.go @@ -37,6 +37,6 @@ func Stop() { //sysmediacontrol.Destroy() liveroom.StopAndSave() playlist.Close() - player.StopMpvPlayer() plugins.ClosePlugins() + player.StopMpvPlayer() } diff --git a/internal/player/mpv/mpv.go b/internal/player/mpv/mpv.go index a125c44..e0b0db0 100644 --- a/internal/player/mpv/mpv.go +++ b/internal/player/mpv/mpv.go @@ -13,6 +13,7 @@ import ( "github.com/aynakeya/go-mpv" "github.com/tidwall/gjson" "math" + "time" ) var running bool = false @@ -60,7 +61,8 @@ func SetupPlayer() { } if e.EventId == mpv.EVENT_SHUTDOWN { log.Info("[MPV Player] libmpv shutdown") - StopPlayer() + // should not call, otherwise StopPlayer gonna be call twice and cause panic + // StopPlayer() } } }() @@ -68,11 +70,27 @@ func SetupPlayer() { func StopPlayer() { cfg.AudioDevice = libmpv.GetPropertyString("audio-device") + log.Debugf("successfully get audio-device and set config %s", cfg.AudioDevice) log.Info("stopping mpv player") running = false - // stop player async, should be closed in the end anyway - go libmpv.TerminateDestroy() - log.Info("mpv player stopped") + done := make(chan struct{}) + + // Stop player async but wait for at most 1 second + go func() { + // todo: when call TerminateDestroy after wid has been set, a c code panic will arise. + // maybe because the window mpv attach to has been closed. so handle was closed twice + // for now. just don't destroy it. because it also might fix configuration + // not properly saved issue + libmpv.TerminateDestroy() + close(done) + }() + + select { + case <-done: + log.Info("mpv player stopped") + case <-time.After(2 * time.Second): + log.Error("mpv player stop timed out (2s) ") + } } var prevPercentPos float64 = 0 diff --git a/internal/playlist/model.go b/internal/playlist/model.go index 1eff1be..6efe403 100644 --- a/internal/playlist/model.go +++ b/internal/playlist/model.go @@ -151,7 +151,7 @@ func (p *playlist) Next(delete bool) { p.Lock.Lock() if p.Size() == 0 { // no media in the playlist - // do not issue any event + // do not dispatch any event p.Lock.Unlock() return } diff --git a/pkg/config/config_general.go b/pkg/config/config_general.go index c4b9eb3..64afbca 100644 --- a/pkg/config/config_general.go +++ b/pkg/config/config_general.go @@ -2,13 +2,15 @@ package config type _GeneralConfig struct { BaseConfig - Width float32 - Height float32 - Language string - InfoApiServer string - AutoCheckUpdate bool - ShowSystemTray bool - PlayNextOnFail bool + Width float32 + Height float32 + Language string + InfoApiServer string + AutoCheckUpdate bool + ShowSystemTray bool + PlayNextOnFail bool + UseSystemPlaylist bool + FixedSize bool } func (c *_GeneralConfig) Name() string { @@ -16,11 +18,13 @@ func (c *_GeneralConfig) Name() string { } var General = &_GeneralConfig{ - Language: "zh-CN", - ShowSystemTray: false, - InfoApiServer: "http://localhost:9090", - AutoCheckUpdate: true, - Width: 960, - Height: 480, - PlayNextOnFail: false, + Language: "zh-CN", + ShowSystemTray: false, + InfoApiServer: "http://localhost:9090", + AutoCheckUpdate: true, + Width: 960, + Height: 480, + PlayNextOnFail: false, + UseSystemPlaylist: true, + FixedSize: true, } diff --git a/plugin/wshub/server.go b/plugin/wshub/server.go index 3697acd..9ec3f08 100644 --- a/plugin/wshub/server.go +++ b/plugin/wshub/server.go @@ -48,7 +48,9 @@ func (c *wsClient) start() { global.Logger.Warn("unmarshal event data failed", err) return } - global.EventManager.CallA(data.EventID, actualEventData) + if globalEnableWsHubControl { + global.EventManager.CallA(data.EventID, actualEventData) + } } } diff --git a/plugin/wshub/wshub.go b/plugin/wshub/wshub.go index 889ff9f..cada6d9 100644 --- a/plugin/wshub/wshub.go +++ b/plugin/wshub/wshub.go @@ -16,29 +16,36 @@ import ( "fyne.io/fyne/v2/data/binding" "fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/widget" + "net/url" ) type WsHub struct { config.BaseConfig - Enabled bool - Port int - LocalHostOnly bool - panel fyne.CanvasObject - server *wsServer - log logger.ILogger + Enabled bool + Port int + LocalHostOnly bool + EnableWsHubControl bool + panel fyne.CanvasObject + server *wsServer + log logger.ILogger } func NewWsHub() *WsHub { return &WsHub{ - Enabled: false, - Port: 29629, - LocalHostOnly: true, - log: global.Logger.WithPrefix("plugin.wshub"), + Enabled: false, + Port: 29629, + LocalHostOnly: true, + EnableWsHubControl: false, + log: global.Logger.WithPrefix("plugin.wshub"), } } +var globalEnableWsHubControl = false + func (w *WsHub) Enable() error { config.LoadConfig(w) + // todo: should pass EnableWsHubControl to client instead of using global variable + globalEnableWsHubControl = w.EnableWsHubControl w.server = newWsServer(&w.Port, &w.LocalHostOnly) gui.AddConfigLayout(w) w.registerEvents() @@ -158,7 +165,9 @@ func (w *WsHub) CreatePanel() fyne.CanvasObject { widget.NewLabel(i18n.T("plugin.wshub.server_control")), startBtn, stopBtn, restartBtn, ) - w.panel = container.NewVBox(serverStatus, autoStart, localHostOnly, serverPreview, serverPort, ctrlBtns) + uri, _ := url.Parse("http://obsinfo.biliaudiobot.com/") + infos := container.NewHBox(widget.NewLabel(i18n.T("plugin.wshub.webinfo_text")), widget.NewHyperlink("http://obsinfo.biliaudiobot.com", uri)) + w.panel = container.NewVBox(serverStatus, autoStart, localHostOnly, serverPreview, serverPort, ctrlBtns, infos) return nil } diff --git a/todo.txt b/todo.txt index d9ef63c..9d71d77 100644 --- a/todo.txt +++ b/todo.txt @@ -18,6 +18,8 @@ ---- Finished +- 2024.08.25 : 添加是否播放闲置歌单选项,修复退出时panic的导致配置无法正确保存的问题 +- 2024.08.14 : 网页输出模版beta上线 - 2024.08.06 : 修复使用身份码连接的时候房管无法切歌的问题 - 2024.07.25 : 或许修复配置无法正确保存的问题 - 2024.07.20 : fyne升级,字体修改为自动加载系统字体, 设置中可设置mpv是否现实歌曲封面