fix behavior of switching players while paused

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