diff --git a/core/events/mapping.go b/core/events/mapping.go index 9903e12..b2eb0ac 100644 --- a/core/events/mapping.go +++ b/core/events/mapping.go @@ -29,7 +29,7 @@ var EventsMapping = map[event.EventId]any{ PlayerPlayingUpdate: PlayerPlayingUpdateEvent{}, PlayerPropertyPauseUpdate: PlayerPropertyPauseUpdateEvent{}, PlayerPropertyPercentPosUpdate: PlayerPropertyPercentPosUpdateEvent{}, - PlayerPropertyIdleActiveUpdate: PlayerPropertyIdleActiveUpdateEvent{}, + PlayerPropertyStateUpdate: PlayerPropertyStateUpdateEvent{}, PlayerPropertyTimePosUpdate: PlayerPropertyTimePosUpdateEvent{}, PlayerPropertyDurationUpdate: PlayerPropertyDurationUpdateEvent{}, PlayerPropertyVolumeUpdate: PlayerPropertyVolumeUpdateEvent{}, diff --git a/core/events/player_property.go b/core/events/player_property.go index 9777872..e08317a 100644 --- a/core/events/player_property.go +++ b/core/events/player_property.go @@ -21,10 +21,10 @@ type PlayerPropertyPercentPosUpdateEvent struct { PercentPos float64 } -const PlayerPropertyIdleActiveUpdate = "update.player.property.idle_active" +const PlayerPropertyStateUpdate = "update.player.property.state" -type PlayerPropertyIdleActiveUpdateEvent struct { - IsIdle bool +type PlayerPropertyStateUpdateEvent struct { + State model.PlayerState } const PlayerPropertyTimePosUpdate = "update.player.property.time_pos" diff --git a/core/model/player.go b/core/model/player.go index 29309b8..d0a3e03 100644 --- a/core/model/player.go +++ b/core/model/player.go @@ -4,3 +4,27 @@ type AudioDevice struct { Name string Description string } + +type PlayerState int + +const ( + PlayerStatePlaying PlayerState = iota + PlayerStateLoading + PlayerStateIdle +) + +func (s PlayerState) NextState(next PlayerState) PlayerState { + if s == PlayerStatePlaying { + return next + } + if s == PlayerStateIdle { + return next + } + if s == PlayerStateLoading { + if next != PlayerStatePlaying { + return PlayerStateLoading + } + return next + } + return next +} diff --git a/go.mod b/go.mod index 8891063..49569e8 100644 --- a/go.mod +++ b/go.mod @@ -43,6 +43,7 @@ require ( github.com/BurntSushi/toml v1.5.0 // indirect github.com/PuerkitoBio/goquery v1.10.3 // indirect github.com/XiaoMengXinX/Music163Api-Go v0.1.30 // indirect + github.com/abadojack/whatlanggo v1.0.1 // indirect github.com/andybalholm/brotli v1.1.0 // indirect github.com/andybalholm/cascadia v1.3.3 // indirect github.com/aynakeya/deepcolor v1.0.3 // indirect @@ -58,7 +59,7 @@ require ( github.com/go-text/render v0.2.0 // indirect github.com/go-text/typesetting v0.2.1 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect - github.com/google/uuid v1.5.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade // indirect github.com/jinzhu/copier v0.4.0 // indirect github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 // indirect @@ -80,7 +81,7 @@ require ( golang.org/x/mobile v0.0.0-20231127183840-76ac6878050a // indirect golang.org/x/net v0.42.0 // indirect golang.org/x/text v0.27.0 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 293f21b..c469730 100644 --- a/go.sum +++ b/go.sum @@ -11,6 +11,8 @@ github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiU github.com/PuerkitoBio/goquery v1.10.3/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y= github.com/XiaoMengXinX/Music163Api-Go v0.1.30 h1:MqRItDFtX1J0JTlFtwN2RwjsYMA7/g/+cTjcOJXy19s= github.com/XiaoMengXinX/Music163Api-Go v0.1.30/go.mod h1:kLU/CkLxKnEJFCge0URvQ0lHt6ImoG1/2aVeNbgV2RQ= +github.com/abadojack/whatlanggo v1.0.1 h1:19N6YogDnf71CTHm3Mp2qhYfkRdyvbgwWdd2EPxJRG4= +github.com/abadojack/whatlanggo v1.0.1/go.mod h1:66WiQbSbJBIlOZMsvbKe5m6pzQovxCH9B/K8tQB2uoc= github.com/adrg/libvlc-go/v3 v3.1.6 h1:Cm22w6xNMDdzYCW8koHgAvjonYm4xbPP5TrlVTtMdl4= github.com/adrg/libvlc-go/v3 v3.1.6/go.mod h1:xJK0YD8cyMDejnrTFQinStE6RYCV1nlfS8KmqTpszSc= github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= @@ -70,8 +72,8 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y= github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= -github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= -github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade h1:FmusiCI1wHw+XQbvL9M+1r/C3SPqKrmBaIOYwVfQoDE= @@ -80,8 +82,8 @@ github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 h1:YLvr1eE6cdCqjOe972w/cYF+FjW34v27+9Vo5106B4M= github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25/go.mod h1:kLgvv7o6UM+0QSf0QjAse3wReFDsb9qbZJdfexWlrQw= -github.com/k0kubun/pp/v3 v3.4.1 h1:1WdFZDRRqe8UsR61N/2RoOZ3ziTEqgTPVqKrHeb779Y= -github.com/k0kubun/pp/v3 v3.4.1/go.mod h1:+SiNiqKnBfw1Nkj82Lh5bIeKQOAkPy6Xw9CAZUZ8npI= +github.com/k0kubun/pp/v3 v3.5.0 h1:iYNlYA5HJAJvkD4ibuf9c8y6SHM0QFhaBuCqm1zHp0w= +github.com/k0kubun/pp/v3 v3.5.0/go.mod h1:5lzno5ZZeEeTV/Ky6vs3g6d1U3WarDrH8k240vMtGro= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -237,8 +239,9 @@ golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58 golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= +golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/gui/player_controller.go b/gui/player_controller.go index 3aca8c3..d102d61 100644 --- a/gui/player_controller.go +++ b/gui/player_controller.go @@ -2,6 +2,7 @@ package gui import ( "AynaLivePlayer/core/events" + "AynaLivePlayer/core/model" "AynaLivePlayer/global" "AynaLivePlayer/gui/component" "AynaLivePlayer/gui/gutil" @@ -84,9 +85,9 @@ func registerPlayControllerHandler() { PlayController.Progress.Refresh() })) - global.EventManager.RegisterA(events.PlayerPropertyIdleActiveUpdate, "gui.player.controller.idle_active", gutil.ThreadSafeHandler(func(event *event.Event) { - isIdle := event.Data.(events.PlayerPropertyIdleActiveUpdateEvent).IsIdle - if isIdle { + global.EventManager.RegisterA(events.PlayerPropertyStateUpdate, "gui.player.controller.idle_active", gutil.ThreadSafeHandler(func(event *event.Event) { + state := event.Data.(events.PlayerPropertyStateUpdateEvent).State + if state == model.PlayerStateIdle || state == model.PlayerStateLoading { PlayController.Progress.Value = 0 PlayController.Progress.Max = 0 //PlayController.Title.SetText("Title") @@ -178,7 +179,6 @@ func registerPlayControllerHandler() { PlayController.Cover.Resource = pic.Resource gutil.RunInFyneThread(PlayController.Cover.Refresh) } - }() } })) diff --git a/internal/controller/controller.go b/internal/controller/controller.go index 9fc9ec5..7a85447 100644 --- a/internal/controller/controller.go +++ b/internal/controller/controller.go @@ -21,25 +21,39 @@ func Stop() { func handlePlayNext() { log := global.Logger.WithPrefix("Controller") - isIdle := false + playerState := model.PlayerStatePlaying global.EventManager.RegisterA( - events.PlayerPropertyIdleActiveUpdate, + events.PlayerPropertyStateUpdate, "internal.controller.playcontrol.idleplaynext", func(event *event.Event) { - data := event.Data.(events.PlayerPropertyIdleActiveUpdateEvent) - isIdle = data.IsIdle - if data.IsIdle { + data := event.Data.(events.PlayerPropertyStateUpdateEvent) + log.Debug("[MPV PlayControl] update player to state", playerState, "->", data.State) + playerState = data.State + if playerState == model.PlayerStateIdle { log.Info("mpv went idle, try play next") global.EventManager.CallA(events.PlayerPlayNextCmd, events.PlayerPlayNextCmdEvent{}) } }) + global.EventManager.RegisterA( + events.PlayerPropertyStateUpdate, + "internal.controller.playcontrol.clear_when_idle", func(event *event.Event) { + data := event.Data.(events.PlayerPropertyStateUpdateEvent) + // if is idle, remove playing media + if data.State == model.PlayerStateIdle { + global.EventManager.CallA(events.PlayerPlayingUpdate, events.PlayerPlayingUpdateEvent{ + Media: model.Media{}, + Removed: true, + }) + } + }) + global.EventManager.RegisterA( events.PlaylistInsertUpdate(model.PlaylistIDPlayer), "internal.controller.playcontrol.playnext_when_insert.player", func(event *event.Event) { - if isIdle && config.General.PlayNextOnFail { + if playerState == model.PlayerStateIdle { global.EventManager.CallA(events.PlayerPlayNextCmd, events.PlayerPlayNextCmdEvent{}) } @@ -49,7 +63,7 @@ func handlePlayNext() { events.PlaylistInsertUpdate(model.PlaylistIDSystem), "internal.controller.playcontrol.playnext_when_insert.system", func(event *event.Event) { - if isIdle && config.General.PlayNextOnFail { + if playerState == model.PlayerStateIdle { global.EventManager.CallA(events.PlayerPlayNextCmd, events.PlayerPlayNextCmdEvent{}) } @@ -82,8 +96,9 @@ func handlePlayNext() { events.PlayerPlayErrorUpdate, "internal.controller.playcontrol.playnext_on_error", func(event *event.Event) { - if isIdle && config.General.PlayNextOnFail { + if config.General.PlayNextOnFail { global.EventManager.CallA(events.PlayerPlayNextCmd, events.PlayerPlayNextCmdEvent{}) + return } }) diff --git a/internal/player/mpv/mpv.go b/internal/player/mpv/mpv.go index 9cbeeda..fb1e258 100644 --- a/internal/player/mpv/mpv.go +++ b/internal/player/mpv/mpv.go @@ -95,6 +95,7 @@ func StopPlayer() { var prevPercentPos float64 = 0 var prevTimePos float64 = 0 +var currentState = model.PlayerStateIdle var mpvPropertyHandler = map[string]func(value any){ "pause": func(value any) { @@ -123,20 +124,20 @@ var mpvPropertyHandler = map[string]func(value any){ }, "idle-active": func(value any) { - var data events.PlayerPropertyIdleActiveUpdateEvent + var data events.PlayerPropertyStateUpdateEvent if value == nil { - data.IsIdle = true + data.State = model.PlayerStateIdle } else { - data.IsIdle = value.(bool) + if value.(bool) { + data.State = model.PlayerStateIdle + } else { + data.State = model.PlayerStatePlaying + } } - // if is idle, remove playing media - if data.IsIdle { - global.EventManager.CallA(events.PlayerPlayingUpdate, events.PlayerPlayingUpdateEvent{ - Media: model.Media{}, - Removed: true, - }) - } - global.EventManager.CallA(events.PlayerPropertyIdleActiveUpdate, data) + log.Debugf("mpv state update %v + %v = %v", currentState, data.State, currentState.NextState(data.State)) + currentState = currentState.NextState(data.State) + data.State = currentState + global.EventManager.CallA(events.PlayerPropertyStateUpdate, data) }, "time-pos": func(value any) { @@ -190,15 +191,28 @@ func registerHandler() { func registerCmdHandler() { global.EventManager.RegisterA(events.PlayerPlayCmd, "player.play", func(evnt *event.Event) { + currentState = currentState.NextState(model.PlayerStateLoading) + global.EventManager.CallA( + events.PlayerPropertyStateUpdate, + events.PlayerPropertyStateUpdateEvent{ + State: currentState, + }) mediaInfo := evnt.Data.(events.PlayerPlayCmdEvent).Media.Info + media := evnt.Data.(events.PlayerPlayCmdEvent).Media + if m, err := miaosic.GetMediaInfo(media.Info.Meta); err == nil { + media.Info = m + } + global.EventManager.CallA(events.PlayerPlayingUpdate, events.PlayerPlayingUpdateEvent{ + Media: media, + Removed: false, + }) log.Infof("[MPV Player] Play media %s", mediaInfo.Title) mediaUrls, err := miaosic.GetMediaUrl(mediaInfo.Meta, miaosic.QualityAny) if err != nil || len(mediaUrls) == 0 { log.Warn("[MPV PlayControl] get media url failed ", mediaInfo.Meta.ID(), err) - global.EventManager.CallA(events.PlayerPlayingUpdate, events.PlayerPlayingUpdateEvent{ - Media: evnt.Data.(events.PlayerPlayCmdEvent).Media, - Removed: false, - }) + if err := libmpv.Command([]string{"stop"}); err != nil { + log.Error("[MPV PlayControl] failed to stop", err) + } global.EventManager.CallA( events.PlayerPlayErrorUpdate, events.PlayerPlayErrorUpdateEvent{ @@ -224,14 +238,6 @@ func registerCmdHandler() { return } } - media := evnt.Data.(events.PlayerPlayCmdEvent).Media - if m, err := miaosic.GetMediaInfo(media.Info.Meta); err == nil { - media.Info = m - } - global.EventManager.CallA(events.PlayerPlayingUpdate, events.PlayerPlayingUpdateEvent{ - Media: media, - Removed: false, - }) log.Debugf("mpv command loadfile %s %s", mediaInfo.Title, mediaUrl.Url) cmd := []string{"loadfile", mediaUrl.Url} if cfg.DisplayMusicCover && media.Info.Cover.Url != "" { @@ -254,6 +260,12 @@ func registerCmdHandler() { }) return } + currentState = currentState.NextState(model.PlayerStatePlaying) + global.EventManager.CallA( + events.PlayerPropertyStateUpdate, + events.PlayerPropertyStateUpdateEvent{ + State: currentState, + }) global.EventManager.CallA(events.PlayerPropertyTimePosUpdate, events.PlayerPropertyTimePosUpdateEvent{ TimePos: 0, }) diff --git a/internal/player/vlc/vlc.go b/internal/player/vlc/vlc.go index 38dc921..4ae1a8b 100644 --- a/internal/player/vlc/vlc.go +++ b/internal/player/vlc/vlc.go @@ -133,8 +133,8 @@ func StopPlayer() { func registerEvents() { // 播放结束事件 _, err := eventManager.Attach(vlc.MediaPlayerEndReached, func(e vlc.Event, userData interface{}) { - global.EventManager.CallA(events.PlayerPropertyIdleActiveUpdate, events.PlayerPropertyIdleActiveUpdateEvent{ - IsIdle: true, + global.EventManager.CallA(events.PlayerPropertyStateUpdate, events.PlayerPropertyStateUpdateEvent{ + State: model.PlayerStateIdle, }) global.EventManager.CallA(events.PlayerPlayingUpdate, events.PlayerPlayingUpdateEvent{ Media: model.Media{}, @@ -223,10 +223,6 @@ func registerCmdHandler() { mediaUrls, err := miaosic.GetMediaUrl(mediaInfo.Meta, miaosic.QualityAny) if err != nil || len(mediaUrls) == 0 { log.Warn("[VLC PlayControl] get media url failed ", mediaInfo.Meta.ID(), err) - global.EventManager.CallA(events.PlayerPlayingUpdate, events.PlayerPlayingUpdateEvent{ - Media: evnt.Data.(events.PlayerPlayCmdEvent).Media, - Removed: false, - }) global.EventManager.CallA( events.PlayerPlayErrorUpdate, events.PlayerPlayErrorUpdateEvent{ @@ -300,8 +296,8 @@ func registerCmdHandler() { global.EventManager.CallA(events.PlayerPropertyPercentPosUpdate, events.PlayerPropertyPercentPosUpdateEvent{ PercentPos: 0, }) - global.EventManager.CallA(events.PlayerPropertyIdleActiveUpdate, events.PlayerPropertyIdleActiveUpdateEvent{ - IsIdle: false, + global.EventManager.CallA(events.PlayerPropertyStateUpdate, events.PlayerPropertyStateUpdateEvent{ + State: model.PlayerStatePlaying, }) }) diff --git a/pkg/config/config.go b/pkg/config/config.go index 35f00bc..4d6baeb 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -10,7 +10,7 @@ import ( const ( ProgramName = "卡西米尔唱片机" - Version uint32 = 0x010201 + Version uint32 = 0x010202 ) const ( diff --git a/pkg/miaosic b/pkg/miaosic index 111797d..dd6ffb0 160000 --- a/pkg/miaosic +++ b/pkg/miaosic @@ -1 +1 @@ -Subproject commit 111797db21a22818de18dd8f42a4dcebae842890 +Subproject commit dd6ffb054612bf4ded1f68bb785eefd662da5d91 diff --git a/todo.txt b/todo.txt index 47ed61c..632c6df 100644 --- a/todo.txt +++ b/todo.txt @@ -16,6 +16,7 @@ ---- Finished +- 2024.08.07 : 修复由于无法获取到连接的歌曲导致的播放的歌曲和展示的信息不匹配的问题/修复qq登陆刷新logic - 2024.07.30 : 由于相关法律法规问题,删除了第三方歌源 - 2024.07.24 : 修复网易云,修复wshub大小写问题 - 2024.07.07 : QQ音乐