mirror of
https://github.com/AynaLivePlayer/AynaLivePlayer.git
synced 2025-12-06 10:22:50 +08:00
use state machine to manage player state
This commit is contained in:
@@ -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{},
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
5
go.mod
5
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
|
||||
)
|
||||
|
||||
13
go.sum
13
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=
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
}()
|
||||
}
|
||||
}))
|
||||
|
||||
@@ -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
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
@@ -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{},
|
||||
@@ -300,8 +300,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,
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user