seek to correct position when switching players

This commit is contained in:
Drew Weymouth
2025-03-29 15:57:34 -07:00
parent 084aa6cdba
commit f9c9f290bd
5 changed files with 63 additions and 30 deletions
+11 -10
View File
@@ -171,10 +171,7 @@ func (p *playbackEngine) SetPlayer(pl player.BasePlayer) error {
p.registerPlayerCallbacks(pl)
if needToUnpause {
p.PlayTrackAt(p.nowPlayingIdx)
// TODO - find a better way to wait until the players are ready
time.Sleep(200 * time.Millisecond)
p.SeekSeconds(p.pendingPlayerChangeTimePos)
p.playTrackAt(p.nowPlayingIdx, p.pendingPlayerChangeTimePos)
p.pendingPlayerChange = false
}
p.invokeNoArgCallbacks(p.onPlayerChange)
@@ -188,6 +185,10 @@ func (p *playbackEngine) SetPlayer(pl player.BasePlayer) error {
}
func (p *playbackEngine) PlayTrackAt(idx int) error {
return p.playTrackAt(idx, 0)
}
func (p *playbackEngine) playTrackAt(idx int, startTime float64) error {
if idx < 0 || idx >= len(p.playQueue) {
return errors.New("track index out of range")
}
@@ -195,7 +196,7 @@ func (p *playbackEngine) PlayTrackAt(idx int) error {
p.checkScrobble()
p.alreadyScrobbled = true
p.pendingTrackChangeNum = idx
err := p.setTrack(idx, false)
err := p.setTrack(idx, false, startTime)
return err
}
@@ -475,7 +476,7 @@ func (p *playbackEngine) RemoveTracksFromQueue(idxs []int) {
p.Stop()
} else {
p.nowPlayingIdx -= 1 // will be incremented in newtrack callback from player
p.setTrack(newNowPlaying, false)
p.setTrack(newNowPlaying, false, 0)
}
// setNextTrack and onSongChange callbacks will be handled
// when we receive new track event from player
@@ -617,7 +618,7 @@ func (p *playbackEngine) setNextTrackAfterQueueUpdate() {
}
}
func (p *playbackEngine) setTrack(idx int, next bool) error {
func (p *playbackEngine) setTrack(idx int, next bool, startTime float64) error {
if urlP, ok := p.player.(player.URLPlayer); ok {
url := ""
var meta mediaprovider.MediaItemMetadata
@@ -637,7 +638,7 @@ func (p *playbackEngine) setTrack(idx int, next bool) error {
if next {
return urlP.SetNextFile(url, meta)
}
return urlP.PlayFile(url, meta)
return urlP.PlayFile(url, meta, startTime)
} else if trP, ok := p.player.(player.TrackPlayer); ok {
var track *mediaprovider.Track
if idx >= 0 {
@@ -649,13 +650,13 @@ func (p *playbackEngine) setTrack(idx int, next bool) error {
if next {
return trP.SetNextTrack(track)
}
return trP.PlayTrack(track)
return trP.PlayTrack(track, startTime)
}
panic("Unsupported player type")
}
func (p *playbackEngine) setNextTrack(idx int) error {
return p.setTrack(idx, true)
return p.setTrack(idx, true, 0)
}
// call BEFORE updating p.nowPlayingIdx
+24 -7
View File
@@ -123,7 +123,7 @@ func (d *DLNAPlayer) GetVolume() int {
return vol
}
func (d *DLNAPlayer) PlayFile(urlstr string, meta mediaprovider.MediaItemMetadata) error {
func (d *DLNAPlayer) PlayFile(urlstr string, meta mediaprovider.MediaItemMetadata, startTime float64) error {
d.ensureSetupProxy()
d.metaLock.Lock()
@@ -139,13 +139,23 @@ func (d *DLNAPlayer) PlayFile(urlstr string, meta mediaprovider.MediaItemMetadat
if err := d.playAVTransportMedia(&media); err != nil {
return err
}
if startTime > 0 {
// TODO: do something better than this!!
time.Sleep(2 * time.Second)
d.sendSeekCmd(startTime)
}
d.state = playing
d.setTrackChangeTimer(time.Duration(meta.Duration) * time.Second)
remainingDur := meta.Duration - int(startTime)
d.setTrackChangeTimer(time.Duration(remainingDur) * time.Second)
d.stopwatch.Reset()
d.stopwatch.Start()
d.lastStartTime = 0
d.lastStartTime = int(startTime)
d.InvokeOnPlaying()
d.InvokeOnTrackChange()
if startTime > 0 {
d.InvokeOnSeek()
}
return nil
}
@@ -251,12 +261,9 @@ func (d *DLNAPlayer) SeekSeconds(secs float64) error {
d.pendingSeek = true
d.pendingSeekSecs = secs
} else {
d.seeking = true
if err := d.avTransport.Seek(context.Background(), int(secs)); err != nil {
d.seeking = false
if err := d.sendSeekCmd(secs); err != nil {
return err
}
d.seeking = false
}
d.lastStartTime = int(secs)
@@ -274,6 +281,16 @@ func (d *DLNAPlayer) SeekSeconds(secs float64) error {
return nil
}
func (d *DLNAPlayer) sendSeekCmd(secs float64) error {
d.seeking = true
if err := d.avTransport.Seek(context.Background(), int(secs)); err != nil {
d.seeking = false
return err
}
d.seeking = false
return nil
}
func (d *DLNAPlayer) IsSeeking() bool {
return d.seeking
}
+1 -1
View File
@@ -80,7 +80,7 @@ func (j *JukeboxPlayer) Stop() error {
return nil
}
func (j *JukeboxPlayer) PlayTrack(track *mediaprovider.Track) error {
func (j *JukeboxPlayer) PlayTrack(track *mediaprovider.Track, _ float64) error {
if err := j.provider.JukeboxSet(track.ID); err != nil {
return err
}
+22 -7
View File
@@ -6,6 +6,7 @@ import (
"fmt"
"math"
"strconv"
"sync"
"github.com/dweymouth/supersonic/backend/mediaprovider"
"github.com/dweymouth/supersonic/backend/player"
@@ -68,6 +69,9 @@ type Player struct {
equalizer Equalizer
peaksEnabled bool
fileLoadedLock sync.Mutex
fileLoadedSig *sync.Cond
bgCancel context.CancelFunc
}
@@ -80,10 +84,12 @@ func New() *Player {
// Same as New, but sets the application name that mpv
// reports to the system audio API.
func NewWithClientName(c string) *Player {
return &Player{
p := &Player{
vol: -1, // use 100 in Init
clientName: c,
}
p.fileLoadedSig = sync.NewCond(&p.fileLoadedLock)
return p
}
// Initializes the Player and makes it ready for playback.
@@ -134,18 +140,27 @@ func (p *Player) Init(maxCacheMB int) error {
}
// Plays the specified file, clearing the previous play queue, if any.
func (p *Player) PlayFile(url string, _ mediaprovider.MediaItemMetadata) error {
func (p *Player) PlayFile(url string, _ mediaprovider.MediaItemMetadata, startTime float64) error {
if !p.initialized {
return ErrUnitialized
}
err := p.mpv.Command([]string{"loadfile", url, "replace"})
if err == nil {
if err != nil {
return err
}
p.lenPlaylist = 1
if p.status.State == player.Paused {
return p.Continue()
}
err = p.Continue()
} else {
p.setState(player.Playing)
}
if startTime > 0 {
p.fileLoadedLock.Lock()
p.fileLoadedSig.Wait()
p.fileLoadedLock.Unlock()
p.SeekSeconds(startTime)
}
return err
}
@@ -481,10 +496,9 @@ func (p *Player) eventHandler(ctx context.Context) {
}
switch e.Event_Id {
case mpv.EVENT_PLAYBACK_RESTART:
if p.seeking {
p.seeking = false
}
case mpv.EVENT_SEEK:
p.seeking = false
p.InvokeOnSeek()
case mpv.EVENT_FILE_LOADED:
p.curPlaylistPos, _ = p.getInt64Property("playlist-pos")
@@ -494,6 +508,7 @@ func (p *Player) eventHandler(ctx context.Context) {
p.InvokeOnSeek()
}
p.InvokeOnTrackChange()
p.fileLoadedSig.Signal()
case mpv.EVENT_IDLE:
p.status.Duration = 0
p.status.TimePos = 0
+2 -2
View File
@@ -4,13 +4,13 @@ import "github.com/dweymouth/supersonic/backend/mediaprovider"
type URLPlayer interface {
BasePlayer
PlayFile(url string, metadata mediaprovider.MediaItemMetadata) error
PlayFile(url string, metadata mediaprovider.MediaItemMetadata, startTime float64) error
SetNextFile(url string, metadata mediaprovider.MediaItemMetadata) error
}
type TrackPlayer interface {
BasePlayer
PlayTrack(track *mediaprovider.Track) error
PlayTrack(track *mediaprovider.Track, startTime float64) error
SetNextTrack(track *mediaprovider.Track) error
}