diff --git a/backend/app.go b/backend/app.go index 55ca333..f76a58b 100644 --- a/backend/app.go +++ b/backend/app.go @@ -404,7 +404,7 @@ func (a *App) SetupWindowsSMTC(hwnd uintptr) { }) a.PlaybackManager.OnSeek(func() { dur := a.PlaybackManager.NowPlaying().Metadata().Duration - smtc.UpdatePosition(int(a.PlaybackManager.PlayerStatus().TimePos*1000), dur*1000) + smtc.UpdatePosition(int(a.PlaybackManager.PlaybackStatus().TimePos*1000), dur*1000) }) a.PlaybackManager.OnPlaying(func() { smtc.SetEnabled(true) diff --git a/backend/mpmedia_mac.go b/backend/mpmedia_mac.go index dff1fcc..e7616ee 100644 --- a/backend/mpmedia_mac.go +++ b/backend/mpmedia_mac.go @@ -84,17 +84,17 @@ func InitMPMediaHandler(playbackManager *PlaybackManager, artURLLookup func(trac }) mp.playbackManager.OnSeek(func() { - C.update_os_now_playing_info_position(C.double(mp.playbackManager.PlayerStatus().TimePos)) + C.update_os_now_playing_info_position(C.double(mp.playbackManager.PlaybackStatus().TimePos)) }) mp.playbackManager.OnPlaying(func() { C.set_os_playback_state_playing() - C.update_os_now_playing_info_position(C.double(mp.playbackManager.PlayerStatus().TimePos)) + C.update_os_now_playing_info_position(C.double(mp.playbackManager.PlaybackStatus().TimePos)) }) mp.playbackManager.OnPaused(func() { C.set_os_playback_state_paused() - C.update_os_now_playing_info_position(C.double(mp.playbackManager.PlayerStatus().TimePos)) + C.update_os_now_playing_info_position(C.double(mp.playbackManager.PlaybackStatus().TimePos)) }) return nil diff --git a/backend/mpris.go b/backend/mpris.go index 9d90da4..f2921a4 100644 --- a/backend/mpris.go +++ b/backend/mpris.go @@ -54,7 +54,7 @@ func NewMPRISHandler(playerName string, pm *PlaybackManager) *MPRISHandler { pm.OnSeek(func() { if m.connErr == nil { - pos := secondsToMicroseconds(pm.PlayerStatus().TimePos) + pos := secondsToMicroseconds(pm.PlaybackStatus().TimePos) m.evt.Player.OnSeek(pos) } }) @@ -160,7 +160,7 @@ func (m *MPRISHandler) Previous() error { } func (m *MPRISHandler) Pause() error { - if m.pm.PlayerStatus().State == player.Playing { + if m.pm.PlaybackStatus().State == player.Playing { m.pm.PlayPause() } return nil @@ -177,7 +177,7 @@ func (m *MPRISHandler) Stop() error { } func (m *MPRISHandler) Play() error { - switch m.pm.PlayerStatus().State { + switch m.pm.PlaybackStatus().State { case player.Paused: m.pm.PlayPause() case player.Stopped: @@ -204,7 +204,7 @@ func (m *MPRISHandler) OpenUri(uri string) error { } func (m *MPRISHandler) PlaybackStatus() (types.PlaybackStatus, error) { - switch m.pm.PlayerStatus().State { + switch m.pm.PlaybackStatus().State { case player.Playing: return types.PlaybackStatusPlaying, nil case player.Paused: @@ -254,7 +254,7 @@ func (m *MPRISHandler) Metadata() (types.Metadata, error) { if m.curTrackPath != "" { trackObjPath = m.curTrackPath } - status := m.pm.PlayerStatus() + status := m.pm.PlaybackStatus() var meta mediaprovider.MediaItemMetadata // metadata that can come only from tracks @@ -307,7 +307,7 @@ func (m *MPRISHandler) SetVolume(v float64) error { } func (m *MPRISHandler) Position() (int64, error) { - return int64(secondsToMicroseconds(m.pm.PlayerStatus().TimePos)), nil + return int64(secondsToMicroseconds(m.pm.PlaybackStatus().TimePos)), nil } func (m *MPRISHandler) MinimumRate() (float64, error) { diff --git a/backend/playbackengine.go b/backend/playbackengine.go index 8ed8654..09e7192 100644 --- a/backend/playbackengine.go +++ b/backend/playbackengine.go @@ -40,6 +40,14 @@ const ( LoopOne ) +type PlaybackState = player.State + +type PlaybackStatus struct { + State PlaybackState + TimePos float64 + Duration float64 +} + type playbackEngine struct { ctx context.Context cancelPollPos context.CancelFunc @@ -65,8 +73,8 @@ type playbackEngine struct { // and reset this to -1 pendingTrackChangeNum int - pendingPlayerChange bool - pendingPlayerChangeTimePos float64 + pendingPlayerChange bool + pendingPlayerChangeStatus player.Status // to pass to onSongChange listeners; clear once listeners have been called lastScrobbled *mediaprovider.Track @@ -155,7 +163,7 @@ func (p *playbackEngine) SetPlayer(pl player.BasePlayer) error { needToUnpause := false - stat := p.PlayerStatus() + stat := p.CurrentPlayer().GetStatus() if p.pendingPlayerChange { stat.State = player.Paused } @@ -165,9 +173,10 @@ func (p *playbackEngine) SetPlayer(pl player.BasePlayer) error { // nothing case player.Playing: needToUnpause = true + p.stopPollTimePos() fallthrough case player.Paused: - p.pendingPlayerChangeTimePos = stat.TimePos + p.pendingPlayerChangeStatus = stat p.pendingPlayerChange = true } p.unregisterPlayerCallbacks(p.player) @@ -183,7 +192,7 @@ func (p *playbackEngine) SetPlayer(pl player.BasePlayer) error { p.registerPlayerCallbacks(pl) if needToUnpause { - p.playTrackAt(p.nowPlayingIdx, p.pendingPlayerChangeTimePos) + p.playTrackAt(p.nowPlayingIdx, p.pendingPlayerChangeStatus.TimePos) p.pendingPlayerChange = false } p.invokeNoArgCallbacks(p.onPlayerChange) @@ -239,8 +248,16 @@ func (p *playbackEngine) GetLoopMode() LoopMode { return p.loopMode } -func (p *playbackEngine) PlayerStatus() player.Status { - return p.player.GetStatus() +func (p *playbackEngine) PlaybackStatus() PlaybackStatus { + stat := p.pendingPlayerChangeStatus + if !p.pendingPlayerChange { + stat = p.CurrentPlayer().GetStatus() + } + return PlaybackStatus{ + State: stat.State, + TimePos: stat.TimePos, + Duration: stat.Duration, + } } func (p *playbackEngine) SetVolume(vol int) error { @@ -259,14 +276,14 @@ func (p *playbackEngine) CurrentPlayer() player.BasePlayer { } func (p *playbackEngine) SeekNext() error { - if p.PlayerStatus().State == player.Stopped { + if p.PlaybackStatus().State == player.Stopped { return nil } return p.PlayTrackAt(p.nowPlayingIdx + 1) } func (p *playbackEngine) SeekBackOrPrevious() error { - if p.nowPlayingIdx == 0 || p.PlayerStatus().TimePos > 3 { + if p.nowPlayingIdx == 0 || p.PlaybackStatus().TimePos > 3 { return p.player.SeekSeconds(0) } return p.PlayTrackAt(p.nowPlayingIdx - 1) @@ -274,7 +291,7 @@ func (p *playbackEngine) SeekBackOrPrevious() error { func (p *playbackEngine) SeekFwdBackN(n int) error { idx := p.nowPlayingIdx - if n < 0 && p.PlayerStatus().TimePos > 3 { + if n < 0 && p.PlaybackStatus().TimePos > 3 { n += 1 // first seek back is just seek to beginning of current } if n == 0 || (idx == 0 && n < 0) { @@ -310,15 +327,11 @@ func (p *playbackEngine) Pause() error { func (p *playbackEngine) Continue() error { if p.pendingPlayerChange { - err := p.PlayTrackAt(p.nowPlayingIdx) - if p.pendingPlayerChangeTimePos != 0 { - p.SeekSeconds(p.pendingPlayerChangeTimePos) - } p.pendingPlayerChange = false - return err + return p.playTrackAt(p.nowPlayingIdx, p.pendingPlayerChangeStatus.TimePos) } - if p.PlayerStatus().State == player.Stopped { + if p.PlaybackStatus().State == player.Stopped { return p.PlayTrackAt(0) } return p.player.Continue() @@ -548,7 +561,7 @@ func (p *playbackEngine) handleOnTrackChange() { p.checkScrobble() } - if p.PlayerStatus().State == player.Playing { + if p.PlaybackStatus().State == player.Playing { p.playTimeStopwatch.Start() } if p.pendingTrackChangeNum < 0 && (p.wasStopped || p.loopMode != LoopOne) { @@ -785,7 +798,7 @@ func (p *playbackEngine) doUpdateTimePos(seeked bool) { if p.callbacksDisabled { return } - s := p.PlayerStatus() + s := p.PlaybackStatus() if s.TimePos > p.latestTrackPosition { p.latestTrackPosition = s.TimePos } diff --git a/backend/playbackmanager.go b/backend/playbackmanager.go index a6eb122..153760f 100644 --- a/backend/playbackmanager.go +++ b/backend/playbackmanager.go @@ -85,7 +85,7 @@ func (p *PlaybackManager) addOnTrackChangeHook() { return } // workaround for https://github.com/dweymouth/supersonic/issues/483 (see above comment) - if p.NowPlayingIndex() != len(p.engine.playQueue) && p.PlayerStatus().State == player.Playing { + if p.NowPlayingIndex() != len(p.engine.playQueue) && p.PlaybackStatus().State == player.Playing { p.lastPlayTime = 0 go func() { time.Sleep(300 * time.Millisecond) @@ -476,8 +476,8 @@ func (p *PlaybackManager) IsAutoplay() bool { return p.autoplay } -func (p *PlaybackManager) PlayerStatus() player.Status { - return p.engine.PlayerStatus() +func (p *PlaybackManager) PlaybackStatus() PlaybackStatus { + return p.engine.PlaybackStatus() } func (p *PlaybackManager) SetVolume(vol int) { @@ -510,7 +510,7 @@ func (p *PlaybackManager) SeekSeconds(sec float64) { // Seek by given relative position in the current track by seconds. func (p *PlaybackManager) SeekBySeconds(sec float64) { - status := p.engine.PlayerStatus() + status := p.engine.PlaybackStatus() target := status.TimePos + sec if target < 0 { target = 0 @@ -548,7 +548,7 @@ func (p *PlaybackManager) Continue() { } func (p *PlaybackManager) PlayPause() { - switch p.engine.PlayerStatus().State { + switch p.engine.PlaybackStatus().State { case player.Playing: p.Pause() case player.Paused: diff --git a/backend/savedplayqueue.go b/backend/savedplayqueue.go index fd6e025..5efc0d8 100644 --- a/backend/savedplayqueue.go +++ b/backend/savedplayqueue.go @@ -26,7 +26,7 @@ type serializedSavedPlayQueue struct { // If the provided CanSavePlayQueue server is non-nil, it will also save to the server. func SavePlayQueue(serverID string, pm *PlaybackManager, filepath string, server mediaprovider.CanSavePlayQueue) error { queue := pm.GetPlayQueue() - stats := pm.PlayerStatus() + stats := pm.PlaybackStatus() trackIdx := pm.NowPlayingIndex() trackIDs := make([]string, 0, len(queue)) diff --git a/ui/browsing/nowplayingpage.go b/ui/browsing/nowplayingpage.go index ae539e5..bbb0a7f 100644 --- a/ui/browsing/nowplayingpage.go +++ b/ui/browsing/nowplayingpage.go @@ -443,7 +443,7 @@ func (a *NowPlayingPage) Reload() { } switch a.tabs.SelectedIndex() { case 1: /*lyrics*/ - a.lastPlayPos = a.pm.PlayerStatus().TimePos + a.lastPlayPos = a.pm.PlaybackStatus().TimePos a.updateLyrics() case 2: /*related*/ a.updateRelatedList() @@ -527,7 +527,7 @@ func (a *NowPlayingPage) saveSelectedTab(tabNum int) { func (a *NowPlayingPage) formatStatusLine() { curPlayer := a.pm.CurrentPlayer() - playerStats := a.pm.PlayerStatus() + playerStats := a.pm.PlaybackStatus() lastStatus := a.statusLabel.Text stopped := lang.L("Stopped") state := stopped diff --git a/ui/controller/controller.go b/ui/controller/controller.go index 67dde41..91bd54a 100644 --- a/ui/controller/controller.go +++ b/ui/controller/controller.go @@ -155,6 +155,9 @@ func (m *Controller) ShowCastMenu(onPendingPlayerChange func()) { rp := m.App.PlaybackManager.CurrentRemotePlayer() devices := m.App.PlaybackManager.RemotePlayers() local := fyne.NewMenuItem(lang.L("This computer"), func() { + if rp == nil { + return // no-op. already not casting + } onPendingPlayerChange() go func() { if err := m.App.PlaybackManager.SetRemotePlayer(nil); err != nil { @@ -168,7 +171,11 @@ func (m *Controller) ShowCastMenu(onPendingPlayerChange func()) { menu := fyne.NewMenu("", local) for _, d := range devices { _d := d + isCurrent := rp != nil && _d.URL == rp.URL item := fyne.NewMenuItem(d.Name, func() { + if isCurrent { + return // no-op. + } onPendingPlayerChange() go func() { if err := m.App.PlaybackManager.SetRemotePlayer(&_d); err != nil { @@ -177,7 +184,7 @@ func (m *Controller) ShowCastMenu(onPendingPlayerChange func()) { }() }) item.Icon = myTheme.CastIcon - item.Checked = rp != nil && _d.URL == rp.URL + item.Checked = isCurrent menu.Items = append(menu.Items, item) } pop := widget.NewPopUpMenu(menu, m.MainWindow.Canvas())