seek to correct position when switching players
This commit is contained in:
+11
-10
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user