add interface to decouple MPV player from PlaybackManager

This commit is contained in:
Drew Weymouth
2023-12-18 20:22:24 -08:00
parent 1665281ad7
commit df6c53a3f5
6 changed files with 135 additions and 107 deletions
+13 -55
View File
@@ -15,16 +15,6 @@ import (
// Error returned by many Player functions if called before the player has not been initialized.
var ErrUnitialized error = errors.New("mpv player uninitialized")
// Argument to Seek function (SeekAbsolute, SeekRelative, SeekAbsolutePercent, SeekRelativePercent).
type SeekMode int
const (
SeekAbsolute SeekMode = iota
SeekRelative
SeekAbsolutePercent
SeekRelativePercent
)
// Information about a specific audio device.
// Returned by ListAudioDevices.
type AudioDevice struct {
@@ -56,6 +46,8 @@ type MediaInfo struct {
Bitrate int
}
var _ player.URLPlayer = (*Player)(nil)
// Player encapsulates the mpv instance and provides functions
// to control it and to check its status.
type Player struct {
@@ -80,7 +72,7 @@ type Player struct {
onStopped []func()
onPlaying []func()
onSeek []func()
onTrackChange []func(int64)
onTrackChange []func(int)
}
// Returns a new player.
@@ -203,30 +195,25 @@ func (p *Player) ClearPlayQueue() error {
// Seeks within the currently playing track.
// See MPV seek command documentation for more details.
func (p *Player) Seek(target string, mode SeekMode) error {
func (p *Player) SeekSeconds(secs float64) error {
if !p.initialized {
return ErrUnitialized
}
target := fmt.Sprintf("%0.1f", secs)
p.seeking = true
err := p.mpv.Command([]string{"seek", target, mode.String()})
err := p.mpv.Command([]string{"seek", target, "absolute"})
return err
}
// Seeks to the beginning of the current track if:
// - The current track is the first track in the play queue, or
// - The current time is more than 3 seconds past the beginning of the track.
//
// Else seeks to the beginning of the previous track.
func (p *Player) SeekBackOrPrevious() error {
// 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("time-pos"); err == nil && pos > 3 {
return p.Seek("0", SeekAbsolutePercent)
}
if pos, err := p.getInt64Property("playlist-pos"); err == nil && pos == 0 {
return p.Seek("0", SeekAbsolutePercent)
return p.SeekSeconds(0)
}
return p.mpv.Command([]string{"playlist-prev"})
}
@@ -445,21 +432,6 @@ func (p *Player) SetLoopMode(mode player.LoopMode) error {
return nil
}
// Change the loop mode of the player to the next one.
// Useful for toggling UI elements, to change modes without knowing the current player mode.
func (p *Player) SetNextLoopMode() error {
switch p.loopMode {
case player.LoopNone:
return p.SetLoopMode(player.LoopAll)
case player.LoopAll:
return p.SetLoopMode(player.LoopOne)
case player.LoopOne:
return p.SetLoopMode(player.LoopNone)
default:
return nil
}
}
// Get the current status of the player.
func (p *Player) GetStatus() player.Status {
if !p.initialized {
@@ -475,7 +447,7 @@ func (p *Player) GetStatus() player.Status {
p.status.Duration = dur.(float64)
}
if playpos, err := p.getInt64Property("playlist-pos"); err == nil {
p.status.PlaylistPos = playpos
p.status.PlaylistPos = int(playpos)
}
return p.status
}
@@ -586,7 +558,7 @@ func (p *Player) OnSeek(cb func()) {
// Registers a callback which is invoked when the currently playing track changes,
// or when playback begins at any time from the Stopped state.
// Callback is invoked with the index of the currently playing track (zero-based).
func (p *Player) OnTrackChange(cb func(int64)) {
func (p *Player) OnTrackChange(cb func(int)) {
p.onTrackChange = append(p.onTrackChange, cb)
}
@@ -657,7 +629,7 @@ func (p *Player) eventHandler(ctx context.Context) {
if pos, err := p.getInt64Property("playlist-pos"); err == nil {
p.curPlaylistPos = pos
for _, cb := range p.onTrackChange {
cb(pos)
cb(int(pos))
}
}
case mpv.EVENT_IDLE:
@@ -668,17 +640,3 @@ func (p *Player) eventHandler(ctx context.Context) {
}
}
}
func (s SeekMode) String() string {
switch s {
case SeekAbsolute:
return "absolute"
case SeekRelative:
return "relative"
case SeekAbsolutePercent:
return "absolute-percent"
case SeekRelativePercent:
return "relative-percent"
}
return "UNKNOWN_SEEK_MODE"
}
+48 -1
View File
@@ -1,5 +1,52 @@
package player
type URLPlayer interface {
BasePlayer
AppendFile(url string) error
PlayFile(url string) error
}
type TrackIDPlayer interface {
BasePlayer
AppendTrack(trackID string) error
PlayTrack(trackID string) error
}
type BasePlayer interface {
// Transport
PlayTrackAt(idx int) error
Continue() error
Pause() error
PlayPause() 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 ReplayGainPlayer interface {
SetReplayGainOptions(ReplayGainOptions) error
}
// The playback state (Stopped, Paused, or Playing).
type State int
@@ -15,7 +62,7 @@ type Status struct {
State State
TimePos float64
Duration float64
PlaylistPos int64
PlaylistPos int
}
// The playback loop mode (LoopNone, LoopAll, LoopOne).