Beginning of reworked PlaybackManager - PARTIAL
This commit is contained in:
+71
-46
@@ -64,25 +64,26 @@ func NewPlaybackManager(
|
|||||||
// clamp to 99% to avoid any possible rounding issues
|
// clamp to 99% to avoid any possible rounding issues
|
||||||
scrobbleCfg.ThresholdPercent = clamp(scrobbleCfg.ThresholdPercent, 0, 99)
|
scrobbleCfg.ThresholdPercent = clamp(scrobbleCfg.ThresholdPercent, 0, 99)
|
||||||
pm := &PlaybackManager{
|
pm := &PlaybackManager{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
sm: s,
|
sm: s,
|
||||||
player: p,
|
player: p,
|
||||||
scrobbleCfg: scrobbleCfg,
|
scrobbleCfg: scrobbleCfg,
|
||||||
transcodeCfg: transcodeCfg,
|
transcodeCfg: transcodeCfg,
|
||||||
|
nowPlayingIdx: -1,
|
||||||
}
|
}
|
||||||
p.OnTrackChange(func(tracknum int) {
|
p.OnTrackChange(func() {
|
||||||
if tracknum >= len(pm.playQueue) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
pm.checkScrobble() // scrobble the previous song if needed
|
pm.checkScrobble() // scrobble the previous song if needed
|
||||||
if pm.player.GetStatus().State == player.Playing {
|
if pm.player.GetStatus().State == player.Playing {
|
||||||
pm.playTimeStopwatch.Start()
|
pm.playTimeStopwatch.Start()
|
||||||
}
|
}
|
||||||
pm.nowPlayingIdx = tracknum
|
pm.nowPlayingIdx++
|
||||||
pm.curTrackTime = float64(pm.playQueue[pm.nowPlayingIdx].Duration)
|
pm.curTrackTime = float64(pm.playQueue[pm.nowPlayingIdx].Duration)
|
||||||
pm.sendNowPlayingScrobble() // Must come before invokeOnChangeCallbacks b/c track may immediately be scrobbled
|
pm.sendNowPlayingScrobble() // Must come before invokeOnChangeCallbacks b/c track may immediately be scrobbled
|
||||||
pm.invokeOnSongChangeCallbacks()
|
pm.invokeOnSongChangeCallbacks()
|
||||||
pm.doUpdateTimePos()
|
pm.doUpdateTimePos()
|
||||||
|
if pm.nowPlayingIdx < len(pm.playQueue)-1 {
|
||||||
|
pm.setTrack(pm.nowPlayingIdx+1, true)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
p.OnSeek(func() {
|
p.OnSeek(func() {
|
||||||
pm.doUpdateTimePos()
|
pm.doUpdateTimePos()
|
||||||
@@ -95,6 +96,7 @@ func NewPlaybackManager(
|
|||||||
pm.doUpdateTimePos()
|
pm.doUpdateTimePos()
|
||||||
pm.invokeOnSongChangeCallbacks()
|
pm.invokeOnSongChangeCallbacks()
|
||||||
pm.invokeNoArgCallbacks(pm.onStopped)
|
pm.invokeNoArgCallbacks(pm.onStopped)
|
||||||
|
pm.nowPlayingIdx = -1
|
||||||
})
|
})
|
||||||
p.OnPaused(func() {
|
p.OnPaused(func() {
|
||||||
pm.playTimeStopwatch.Stop()
|
pm.playTimeStopwatch.Stop()
|
||||||
@@ -205,32 +207,24 @@ func (p *PlaybackManager) LoadPlaylist(playlistID string, appendToQueue bool, sh
|
|||||||
func (p *PlaybackManager) LoadTracks(tracks []*mediaprovider.Track, appendToQueue, shuffle bool) error {
|
func (p *PlaybackManager) LoadTracks(tracks []*mediaprovider.Track, appendToQueue, shuffle bool) error {
|
||||||
if !appendToQueue {
|
if !appendToQueue {
|
||||||
p.player.Stop()
|
p.player.Stop()
|
||||||
p.nowPlayingIdx = 0
|
p.nowPlayingIdx = -1
|
||||||
p.playQueue = nil
|
p.playQueue = nil
|
||||||
}
|
}
|
||||||
nums := util.Range(len(tracks))
|
nums := util.Range(len(tracks))
|
||||||
if shuffle {
|
if shuffle {
|
||||||
util.ShuffleSlice(nums)
|
util.ShuffleSlice(nums)
|
||||||
}
|
}
|
||||||
|
needToSetNext := appendToQueue && len(tracks) > 0 && p.nowPlayingIdx == len(p.playQueue)-1
|
||||||
for _, i := range nums {
|
for _, i := range nums {
|
||||||
|
|
||||||
if urlP, ok := p.player.(player.URLPlayer); ok {
|
|
||||||
url, err := p.sm.Server.GetStreamURL(tracks[i].ID, p.transcodeCfg.ForceRawFile)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
urlP.AppendFile(url)
|
|
||||||
} else if trP, ok := p.player.(player.TrackPlayer); ok {
|
|
||||||
trP.AppendTrack(tracks[i])
|
|
||||||
} else {
|
|
||||||
panic("unsupported player type")
|
|
||||||
}
|
|
||||||
// ensure a deep copy of the track info so that we can maintain our own state
|
// ensure a deep copy of the track info so that we can maintain our own state
|
||||||
// (tracking play count increases, favorite, and rating) without messing up
|
// (tracking play count increases, favorite, and rating) without messing up
|
||||||
// other views' track models
|
// other views' track models
|
||||||
tr := *tracks[i]
|
tr := *tracks[i]
|
||||||
p.playQueue = append(p.playQueue, &tr)
|
p.playQueue = append(p.playQueue, &tr)
|
||||||
}
|
}
|
||||||
|
if needToSetNext {
|
||||||
|
p.setTrack(p.nowPlayingIdx+1, true)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,7 +235,7 @@ func (p *PlaybackManager) PlayAlbum(albumID string, firstTrack int, shuffle bool
|
|||||||
if p.replayGainCfg.Mode == ReplayGainAuto {
|
if p.replayGainCfg.Mode == ReplayGainAuto {
|
||||||
p.SetReplayGainMode(player.ReplayGainAlbum)
|
p.SetReplayGainMode(player.ReplayGainAlbum)
|
||||||
}
|
}
|
||||||
return p.player.PlayTrackAt(firstTrack)
|
return p.PlayTrackAt(firstTrack)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PlaybackManager) PlayPlaylist(playlistID string, firstTrack int, shuffle bool) error {
|
func (p *PlaybackManager) PlayPlaylist(playlistID string, firstTrack int, shuffle bool) error {
|
||||||
@@ -251,7 +245,7 @@ func (p *PlaybackManager) PlayPlaylist(playlistID string, firstTrack int, shuffl
|
|||||||
if p.replayGainCfg.Mode == ReplayGainAuto {
|
if p.replayGainCfg.Mode == ReplayGainAuto {
|
||||||
p.SetReplayGainMode(player.ReplayGainTrack)
|
p.SetReplayGainMode(player.ReplayGainTrack)
|
||||||
}
|
}
|
||||||
return p.player.PlayTrackAt(firstTrack)
|
return p.PlayTrackAt(firstTrack)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PlaybackManager) PlayTrack(trackID string) error {
|
func (p *PlaybackManager) PlayTrack(trackID string) error {
|
||||||
@@ -267,11 +261,15 @@ func (p *PlaybackManager) PlayTrack(trackID string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *PlaybackManager) PlayFromBeginning() error {
|
func (p *PlaybackManager) PlayFromBeginning() error {
|
||||||
return p.player.PlayTrackAt(0)
|
return p.PlayTrackAt(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PlaybackManager) PlayTrackAt(idx int) error {
|
func (p *PlaybackManager) PlayTrackAt(idx int) error {
|
||||||
return p.player.PlayTrackAt(idx)
|
if idx < 0 || idx >= len(p.playQueue) {
|
||||||
|
return errors.New("track index out of range")
|
||||||
|
}
|
||||||
|
p.nowPlayingIdx = idx - 1
|
||||||
|
return p.setTrack(idx, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PlaybackManager) PlayRandomSongs(genreName string) {
|
func (p *PlaybackManager) PlayRandomSongs(genreName string) {
|
||||||
@@ -325,7 +323,7 @@ func (p *PlaybackManager) OnTrackRatingChanged(id string, rating int) {
|
|||||||
|
|
||||||
func (p *PlaybackManager) RemoveTracksFromQueue(trackIDs []string) {
|
func (p *PlaybackManager) RemoveTracksFromQueue(trackIDs []string) {
|
||||||
newQueue := make([]*mediaprovider.Track, 0, len(p.playQueue)-len(trackIDs))
|
newQueue := make([]*mediaprovider.Track, 0, len(p.playQueue)-len(trackIDs))
|
||||||
rmCount := 0
|
//rmCount := 0
|
||||||
idSet := sharedutil.ToSet(trackIDs)
|
idSet := sharedutil.ToSet(trackIDs)
|
||||||
isPlayingTrackRemoved := false
|
isPlayingTrackRemoved := false
|
||||||
for i, tr := range p.playQueue {
|
for i, tr := range p.playQueue {
|
||||||
@@ -336,13 +334,14 @@ func (p *PlaybackManager) RemoveTracksFromQueue(trackIDs []string) {
|
|||||||
// If we are removing the currently playing track, we need to scrobble it
|
// If we are removing the currently playing track, we need to scrobble it
|
||||||
p.checkScrobble()
|
p.checkScrobble()
|
||||||
}
|
}
|
||||||
if err := p.player.RemoveTrackAt(i - rmCount); err == nil {
|
// if err := p.player.RemoveTrackAt(i - rmCount); err == nil {
|
||||||
rmCount++
|
// rmCount++
|
||||||
} else {
|
//} else {
|
||||||
log.Printf("error removing track: %v", err.Error())
|
var err error
|
||||||
// did not remove this track
|
log.Printf("error removing track: %v", err.Error())
|
||||||
newQueue = append(newQueue, tr)
|
// did not remove this track
|
||||||
}
|
newQueue = append(newQueue, tr)
|
||||||
|
//}
|
||||||
} else {
|
} else {
|
||||||
// not removing this track
|
// not removing this track
|
||||||
newQueue = append(newQueue, tr)
|
newQueue = append(newQueue, tr)
|
||||||
@@ -359,9 +358,9 @@ func (p *PlaybackManager) RemoveTracksFromQueue(trackIDs []string) {
|
|||||||
// Stop playback and clear the play queue.
|
// Stop playback and clear the play queue.
|
||||||
func (p *PlaybackManager) StopAndClearPlayQueue() {
|
func (p *PlaybackManager) StopAndClearPlayQueue() {
|
||||||
p.player.Stop()
|
p.player.Stop()
|
||||||
p.player.ClearPlayQueue()
|
|
||||||
p.doUpdateTimePos()
|
p.doUpdateTimePos()
|
||||||
p.playQueue = nil
|
p.playQueue = nil
|
||||||
|
p.nowPlayingIdx = -1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PlaybackManager) SetReplayGainOptions(config ReplayGainConfig) {
|
func (p *PlaybackManager) SetReplayGainOptions(config ReplayGainConfig) {
|
||||||
@@ -423,17 +422,17 @@ func (p *PlaybackManager) SetNextLoopMode() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, cb := range p.onLoopModeChange {
|
//for _, cb := range p.onLoopModeChange {
|
||||||
cb(p.player.GetLoopMode())
|
// cb(p.player.GetLoopMode())
|
||||||
}
|
//}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PlaybackManager) SetLoopMode(loopMode player.LoopMode) error {
|
func (p *PlaybackManager) SetLoopMode(loopMode player.LoopMode) error {
|
||||||
if err := p.player.SetLoopMode(player.LoopMode(loopMode)); err != nil {
|
//if err := p.player.SetLoopMode(player.LoopMode(loopMode)); err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
//}
|
||||||
|
|
||||||
for _, cb := range p.onLoopModeChange {
|
for _, cb := range p.onLoopModeChange {
|
||||||
cb(loopMode)
|
cb(loopMode)
|
||||||
@@ -443,7 +442,8 @@ func (p *PlaybackManager) SetLoopMode(loopMode player.LoopMode) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *PlaybackManager) GetLoopMode() player.LoopMode {
|
func (p *PlaybackManager) GetLoopMode() player.LoopMode {
|
||||||
return p.player.GetLoopMode()
|
return 0
|
||||||
|
//return p.player.GetLoopMode()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PlaybackManager) PlayerStatus() player.Status {
|
func (p *PlaybackManager) PlayerStatus() player.Status {
|
||||||
@@ -466,14 +466,17 @@ func (p *PlaybackManager) Volume() int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *PlaybackManager) SeekNext() error {
|
func (p *PlaybackManager) SeekNext() error {
|
||||||
return p.player.SeekNext()
|
if p.CurrentPlayer().GetStatus().State == player.Stopped {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return p.PlayTrackAt(p.nowPlayingIdx + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PlaybackManager) SeekBackOrPrevious() error {
|
func (p *PlaybackManager) SeekBackOrPrevious() error {
|
||||||
if p.player.GetStatus().TimePos > 3 {
|
if p.nowPlayingIdx == 0 || p.player.GetStatus().TimePos > 3 {
|
||||||
return p.player.SeekSeconds(0)
|
return p.player.SeekSeconds(0)
|
||||||
}
|
}
|
||||||
return p.player.SeekPrevious()
|
return p.PlayTrackAt(p.nowPlayingIdx - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Seek to given absolute position in the current track by seconds.
|
// Seek to given absolute position in the current track by seconds.
|
||||||
@@ -501,6 +504,9 @@ func (p *PlaybackManager) Pause() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *PlaybackManager) Continue() error {
|
func (p *PlaybackManager) Continue() error {
|
||||||
|
if p.player.GetStatus().State == player.Stopped {
|
||||||
|
return p.PlayFromBeginning()
|
||||||
|
}
|
||||||
return p.player.Continue()
|
return p.player.Continue()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -516,6 +522,25 @@ func (p *PlaybackManager) PlayPause() error {
|
|||||||
return errors.New("unreached - invalid player state")
|
return errors.New("unreached - invalid player state")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *PlaybackManager) setTrack(idx int, next bool) error {
|
||||||
|
if urlP, ok := p.player.(player.URLPlayer); ok {
|
||||||
|
url, err := p.sm.Server.GetStreamURL(p.playQueue[idx].ID, p.transcodeCfg.ForceRawFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if next {
|
||||||
|
return urlP.SetNextFile(url)
|
||||||
|
}
|
||||||
|
return urlP.PlayFile(url)
|
||||||
|
} else if trP, ok := p.player.(player.TrackPlayer); ok {
|
||||||
|
if next {
|
||||||
|
return trP.SetNextTrack(p.playQueue[idx])
|
||||||
|
}
|
||||||
|
return trP.PlayTrack(p.playQueue[idx])
|
||||||
|
}
|
||||||
|
panic("Unsupported player type")
|
||||||
|
}
|
||||||
|
|
||||||
// call BEFORE updating p.nowPlayingIdx
|
// call BEFORE updating p.nowPlayingIdx
|
||||||
func (p *PlaybackManager) checkScrobble() {
|
func (p *PlaybackManager) checkScrobble() {
|
||||||
if !p.scrobbleCfg.Enabled || len(p.playQueue) == 0 || p.nowPlayingIdx < 0 {
|
if !p.scrobbleCfg.Enabled || len(p.playQueue) == 0 || p.nowPlayingIdx < 0 {
|
||||||
|
|||||||
+18
-129
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"math"
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
@@ -58,9 +57,9 @@ type Player struct {
|
|||||||
haveRGainOpts bool
|
haveRGainOpts bool
|
||||||
audioExclusive bool
|
audioExclusive bool
|
||||||
status player.Status
|
status player.Status
|
||||||
loopMode player.LoopMode
|
|
||||||
seeking bool
|
seeking bool
|
||||||
curPlaylistPos int64
|
curPlaylistPos int64
|
||||||
|
lenPlaylist int64
|
||||||
prePausedState player.State
|
prePausedState player.State
|
||||||
clientName string
|
clientName string
|
||||||
equalizer Equalizer
|
equalizer Equalizer
|
||||||
@@ -72,7 +71,7 @@ type Player struct {
|
|||||||
onStopped []func()
|
onStopped []func()
|
||||||
onPlaying []func()
|
onPlaying []func()
|
||||||
onSeek []func()
|
onSeek []func()
|
||||||
onTrackChange []func(int)
|
onTrackChange []func()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a new player.
|
// Returns a new player.
|
||||||
@@ -133,38 +132,19 @@ func (p *Player) Init(maxCacheMB int) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Appends the given file to the play queue.
|
|
||||||
// Note that the Player API does not provide methods to read
|
|
||||||
// the play queue. Clients are expected to maintain their own play queue model.
|
|
||||||
func (p *Player) AppendFile(url string) error {
|
|
||||||
log.Printf("Adding playback URL: %s", url)
|
|
||||||
if !p.initialized {
|
|
||||||
return ErrUnitialized
|
|
||||||
}
|
|
||||||
return p.mpv.Command([]string{"loadfile", url, "append"})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Plays the specified file, clearing the previous play queue, if any.
|
// Plays the specified file, clearing the previous play queue, if any.
|
||||||
func (p *Player) PlayFile(url string) error {
|
func (p *Player) PlayFile(url string) error {
|
||||||
log.Printf("Adding playback URL: %s", url)
|
|
||||||
if !p.initialized {
|
if !p.initialized {
|
||||||
return ErrUnitialized
|
return ErrUnitialized
|
||||||
}
|
}
|
||||||
err := p.mpv.Command([]string{"loadfile", url, "replace"})
|
err := p.mpv.Command([]string{"loadfile", url, "replace"})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
p.setState(player.Playing)
|
p.setState(player.Playing)
|
||||||
|
p.lenPlaylist = 1
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Removes the item at the given index from the internal playqueue.
|
|
||||||
func (p *Player) RemoveTrackAt(idx int) error {
|
|
||||||
if !p.initialized {
|
|
||||||
return ErrUnitialized
|
|
||||||
}
|
|
||||||
return p.mpv.Command([]string{"playlist-remove", strconv.Itoa(idx)})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stops playback and clears the play queue.
|
// Stops playback and clears the play queue.
|
||||||
func (p *Player) Stop() error {
|
func (p *Player) Stop() error {
|
||||||
if !p.initialized {
|
if !p.initialized {
|
||||||
@@ -180,30 +160,24 @@ func (p *Player) Stop() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
p.lenPlaylist = 0
|
||||||
p.setState(player.Stopped)
|
p.setState(player.Stopped)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clears the play queue, except for the currently playing file.
|
func (p *Player) SetNextFile(url string) error {
|
||||||
func (p *Player) ClearPlayQueue() error {
|
if p.lenPlaylist > p.curPlaylistPos+1 {
|
||||||
if !p.initialized {
|
if err := p.mpv.Command([]string{"playlist-remove", strconv.Itoa(int(p.curPlaylistPos) + 1)}); err != nil {
|
||||||
return ErrUnitialized
|
return err
|
||||||
|
}
|
||||||
|
p.lenPlaylist--
|
||||||
}
|
}
|
||||||
return p.mpv.Command([]string{"playlist-clear"})
|
err := p.mpv.Command([]string{"loadfile", url, "append"})
|
||||||
}
|
if err == nil {
|
||||||
|
p.lenPlaylist++
|
||||||
func (p *Player) SetFile(url string) error {
|
|
||||||
if !p.initialized {
|
|
||||||
return ErrUnitialized
|
|
||||||
}
|
}
|
||||||
if err := p.mpv.Command([]string{"stop"}); err != nil {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := p.mpv.Command([]string{"playlist-clear"}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return p.mpv.Command([]string{"loadfile", url, "append"})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Seeks within the currently playing track.
|
// Seeks within the currently playing track.
|
||||||
@@ -218,27 +192,6 @@ func (p *Player) SeekSeconds(secs float64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Seeks to the beginning of the previous track,
|
|
||||||
// or if no previous track, seeks to the beginning of the current track.
|
|
||||||
func (p *Player) SeekPrevious() error {
|
|
||||||
if !p.initialized {
|
|
||||||
return ErrUnitialized
|
|
||||||
}
|
|
||||||
|
|
||||||
if pos, err := p.getInt64Property("playlist-pos"); err == nil && pos == 0 {
|
|
||||||
return p.SeekSeconds(0)
|
|
||||||
}
|
|
||||||
return p.mpv.Command([]string{"playlist-prev"})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Seeks to the next track in the play queue, if any.
|
|
||||||
func (p *Player) SeekNext() error {
|
|
||||||
if !p.initialized {
|
|
||||||
return ErrUnitialized
|
|
||||||
}
|
|
||||||
return p.mpv.Command([]string{"playlist-next"})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sets the volume of the player (0-100).
|
// Sets the volume of the player (0-100).
|
||||||
// Unlike most Player functions, SetVolume can be called before Init,
|
// Unlike most Player functions, SetVolume can be called before Init,
|
||||||
// to set the initial volume of the player on startup.
|
// to set the initial volume of the player on startup.
|
||||||
@@ -325,22 +278,6 @@ func (p *Player) setPaused(paused bool) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start playback from the specified track index in the play queue.
|
|
||||||
func (p *Player) PlayTrackAt(idx int) error {
|
|
||||||
// check if we have anything to play
|
|
||||||
if c, err := p.getInt64Property("playlist-count"); err == nil && c <= int64(idx) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
err := p.mpv.Command([]string{"playlist-play-index", strconv.Itoa(idx)})
|
|
||||||
if p.GetStatus().State == player.Paused {
|
|
||||||
err = p.setPaused(false)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
p.setState(player.Playing)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pause playback and update the player state
|
// Pause playback and update the player state
|
||||||
func (p *Player) Pause() error {
|
func (p *Player) Pause() error {
|
||||||
if p.status.State != player.Playing {
|
if p.status.State != player.Playing {
|
||||||
@@ -362,57 +299,11 @@ func (p *Player) Continue() error {
|
|||||||
p.setState(p.prePausedState)
|
p.setState(p.prePausedState)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
} else if p.status.State == player.Stopped {
|
|
||||||
return p.PlayTrackAt(0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the loop mode of the player.
|
|
||||||
func (p *Player) GetLoopMode() player.LoopMode {
|
|
||||||
return p.loopMode
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the loop mode of the player.
|
|
||||||
func (p *Player) SetLoopMode(mode player.LoopMode) error {
|
|
||||||
if !p.initialized {
|
|
||||||
return ErrUnitialized
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return early if player is already in specified mode
|
|
||||||
if mode == p.loopMode {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
switch mode {
|
|
||||||
case player.LoopNone:
|
|
||||||
if err := p.mpv.SetOptionString("loop-playlist", "no"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := p.mpv.SetOptionString("loop-file", "no"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case player.LoopAll:
|
|
||||||
if err := p.mpv.SetOptionString("loop-playlist", "inf"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := p.mpv.SetOptionString("loop-file", "no"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case player.LoopOne:
|
|
||||||
if err := p.mpv.SetOptionString("loop-playlist", "no"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := p.mpv.SetOptionString("loop-file", "inf"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p.loopMode = mode
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the current status of the player.
|
// Get the current status of the player.
|
||||||
func (p *Player) GetStatus() player.Status {
|
func (p *Player) GetStatus() player.Status {
|
||||||
if !p.initialized {
|
if !p.initialized {
|
||||||
@@ -539,7 +430,7 @@ func (p *Player) OnSeek(cb func()) {
|
|||||||
// Registers a callback which is invoked when the currently playing track changes,
|
// Registers a callback which is invoked when the currently playing track changes,
|
||||||
// or when playback begins at any time from the Stopped state.
|
// or when playback begins at any time from the Stopped state.
|
||||||
// Callback is invoked with the index of the currently playing track (zero-based).
|
// Callback is invoked with the index of the currently playing track (zero-based).
|
||||||
func (p *Player) OnTrackChange(cb func(int)) {
|
func (p *Player) OnTrackChange(cb func()) {
|
||||||
p.onTrackChange = append(p.onTrackChange, cb)
|
p.onTrackChange = append(p.onTrackChange, cb)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -600,6 +491,7 @@ func (p *Player) eventHandler(ctx context.Context) {
|
|||||||
cb()
|
cb()
|
||||||
}
|
}
|
||||||
case mpv.EVENT_FILE_LOADED:
|
case mpv.EVENT_FILE_LOADED:
|
||||||
|
p.curPlaylistPos, _ = p.getInt64Property("playlist-pos")
|
||||||
if p.status.State == player.Paused {
|
if p.status.State == player.Paused {
|
||||||
// seek while paused switches to a new file
|
// seek while paused switches to a new file
|
||||||
// mpv does not fire seek event in this case
|
// mpv does not fire seek event in this case
|
||||||
@@ -607,11 +499,8 @@ func (p *Player) eventHandler(ctx context.Context) {
|
|||||||
cb()
|
cb()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if pos, err := p.getInt64Property("playlist-pos"); err == nil {
|
for _, cb := range p.onTrackChange {
|
||||||
p.curPlaylistPos = pos
|
cb()
|
||||||
for _, cb := range p.onTrackChange {
|
|
||||||
cb(int(pos))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case mpv.EVENT_IDLE:
|
case mpv.EVENT_IDLE:
|
||||||
p.status.Duration = 0
|
p.status.Duration = 0
|
||||||
|
|||||||
+4
-44
@@ -4,57 +4,17 @@ import "github.com/dweymouth/supersonic/backend/mediaprovider"
|
|||||||
|
|
||||||
type URLPlayer interface {
|
type URLPlayer interface {
|
||||||
BasePlayer
|
BasePlayer
|
||||||
AppendFile(url string) error
|
PlayFile(url string) error
|
||||||
}
|
SetNextFile(url string) error
|
||||||
|
|
||||||
type URLPlayerNew interface {
|
|
||||||
BasePlayer
|
|
||||||
SetFile(url string)
|
|
||||||
SetNextFile(url string)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type TrackPlayer interface {
|
type TrackPlayer interface {
|
||||||
BasePlayer
|
BasePlayer
|
||||||
AppendTrack(track *mediaprovider.Track) error
|
PlayTrack(track *mediaprovider.Track) error
|
||||||
}
|
|
||||||
|
|
||||||
type TrackPlayerNew interface {
|
|
||||||
BasePlayer
|
|
||||||
SetTrack(track *mediaprovider.Track) error
|
|
||||||
SetNextTrack(track *mediaprovider.Track) error
|
SetNextTrack(track *mediaprovider.Track) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type BasePlayer interface {
|
type BasePlayer interface {
|
||||||
// Transport
|
|
||||||
PlayTrackAt(idx int) error
|
|
||||||
Continue() error
|
|
||||||
Pause() error
|
|
||||||
Stop() error
|
|
||||||
SeekPrevious() error
|
|
||||||
SeekNext() error
|
|
||||||
SeekSeconds(secs float64) error
|
|
||||||
IsSeeking() bool
|
|
||||||
|
|
||||||
SetVolume(int) error
|
|
||||||
GetVolume() int
|
|
||||||
|
|
||||||
GetStatus() Status
|
|
||||||
|
|
||||||
ClearPlayQueue() error
|
|
||||||
RemoveTrackAt(idx int) error
|
|
||||||
|
|
||||||
SetLoopMode(LoopMode) error
|
|
||||||
GetLoopMode() LoopMode
|
|
||||||
|
|
||||||
// Event API
|
|
||||||
OnPaused(func())
|
|
||||||
OnStopped(func())
|
|
||||||
OnPlaying(func())
|
|
||||||
OnSeek(func())
|
|
||||||
OnTrackChange(func(int))
|
|
||||||
}
|
|
||||||
|
|
||||||
type BasePlayerNew interface {
|
|
||||||
Continue() error
|
Continue() error
|
||||||
Pause() error
|
Pause() error
|
||||||
Stop() error
|
Stop() error
|
||||||
@@ -72,7 +32,7 @@ type BasePlayerNew interface {
|
|||||||
OnStopped(func())
|
OnStopped(func())
|
||||||
OnPlaying(func())
|
OnPlaying(func())
|
||||||
OnSeek(func())
|
OnSeek(func())
|
||||||
OnTrackChange(func(int))
|
OnTrackChange(func())
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReplayGainPlayer interface {
|
type ReplayGainPlayer interface {
|
||||||
|
|||||||
Reference in New Issue
Block a user