start of JukeboxPlayer backend
This commit is contained in:
@@ -148,6 +148,25 @@ type SupportsRating interface {
|
|||||||
SetRating(params RatingFavoriteParameters, rating int) error
|
SetRating(params RatingFavoriteParameters, rating int) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type JukeboxProvider interface {
|
||||||
|
JukeboxStart() error
|
||||||
|
JukeboxStop() error
|
||||||
|
JukeboxSeek(idx, seconds int) error
|
||||||
|
JukeboxClear() error
|
||||||
|
JukeboxSet(trackID string) error
|
||||||
|
JukeboxAdd(trackID string) error
|
||||||
|
JukeboxRemove(idx int) error
|
||||||
|
JukeboxSetVolume(vol int) error
|
||||||
|
JukeboxGetStatus() (*JukeboxStatus, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type JukeboxStatus struct {
|
||||||
|
Volume int
|
||||||
|
CurrentTrack int
|
||||||
|
Playing bool
|
||||||
|
PositionSeconds float64
|
||||||
|
}
|
||||||
|
|
||||||
func genresMatch(filterGenres, albumGenres []string) bool {
|
func genresMatch(filterGenres, albumGenres []string) bool {
|
||||||
for _, g1 := range filterGenres {
|
for _, g1 := range filterGenres {
|
||||||
for _, g2 := range albumGenres {
|
for _, g2 := range albumGenres {
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package subsonic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/dweymouth/supersonic/backend/mediaprovider"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ mediaprovider.JukeboxProvider = (*subsonicMediaProvider)(nil)
|
||||||
|
|
||||||
|
func (s *subsonicMediaProvider) JukeboxStart() error {
|
||||||
|
_, err := s.client.JukeboxControl("start", nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *subsonicMediaProvider) JukeboxStop() error {
|
||||||
|
_, err := s.client.JukeboxControl("stop", nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *subsonicMediaProvider) JukeboxClear() error {
|
||||||
|
_, err := s.client.JukeboxControl("clear", nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *subsonicMediaProvider) JukeboxSetVolume(vol int) error {
|
||||||
|
_, err := s.client.JukeboxControl("setGain",
|
||||||
|
map[string]string{"gain": strconv.Itoa(vol)})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *subsonicMediaProvider) JukeboxSeek(idx, seconds int) error {
|
||||||
|
_, err := s.client.JukeboxControl("skip",
|
||||||
|
map[string]string{"index": strconv.Itoa(idx), "offset": strconv.Itoa(seconds)})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *subsonicMediaProvider) JukeboxRemove(idx int) error {
|
||||||
|
_, err := s.client.JukeboxControl("remove",
|
||||||
|
map[string]string{"index": strconv.Itoa(idx)})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *subsonicMediaProvider) JukeboxSet(trackID string) error {
|
||||||
|
_, err := s.client.JukeboxControl("set",
|
||||||
|
map[string]string{"id": trackID})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *subsonicMediaProvider) JukeboxAdd(trackID string) error {
|
||||||
|
_, err := s.client.JukeboxControl("add",
|
||||||
|
map[string]string{"id": trackID})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *subsonicMediaProvider) JukeboxGetStatus() (*mediaprovider.JukeboxStatus, error) {
|
||||||
|
stat, err := s.client.JukeboxControl("status", nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &mediaprovider.JukeboxStatus{
|
||||||
|
Volume: int(stat.Gain * 100),
|
||||||
|
CurrentTrack: stat.CurrentIndex,
|
||||||
|
Playing: stat.Playing,
|
||||||
|
PositionSeconds: float64(stat.Position),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
@@ -229,8 +229,8 @@ func (p *PlaybackManager) LoadTracks(tracks []*mediaprovider.Track, appendToQueu
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
urlP.AppendFile(url)
|
urlP.AppendFile(url)
|
||||||
} else if trP, ok := p.player.(player.TrackIDPlayer); ok {
|
} else if trP, ok := p.player.(player.TrackPlayer); ok {
|
||||||
trP.AppendTrack(tracks[i].ID)
|
trP.AppendTrack(tracks[i])
|
||||||
} else {
|
} else {
|
||||||
panic("unsupported player type")
|
panic("unsupported player type")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ require (
|
|||||||
github.com/deluan/sanitize v0.0.0-20230310221930-6e18967d9fc1
|
github.com/deluan/sanitize v0.0.0-20230310221930-6e18967d9fc1
|
||||||
github.com/dweymouth/go-jellyfin v0.0.0-20231116161116-e800860bdacc
|
github.com/dweymouth/go-jellyfin v0.0.0-20231116161116-e800860bdacc
|
||||||
github.com/dweymouth/go-mpv v0.0.0-20230406003141-7f1858e503ee
|
github.com/dweymouth/go-mpv v0.0.0-20230406003141-7f1858e503ee
|
||||||
github.com/dweymouth/go-subsonic v0.0.0-20231216234924-3f62122dc53a
|
github.com/dweymouth/go-subsonic v0.0.0-20231217175944-9b48c9ffc002
|
||||||
github.com/fsnotify/fsnotify v1.6.0
|
github.com/fsnotify/fsnotify v1.6.0
|
||||||
github.com/godbus/dbus/v5 v5.1.0
|
github.com/godbus/dbus/v5 v5.1.0
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
|
|||||||
@@ -75,8 +75,8 @@ github.com/dweymouth/go-jellyfin v0.0.0-20231116161116-e800860bdacc h1:wJy4U12Ys
|
|||||||
github.com/dweymouth/go-jellyfin v0.0.0-20231116161116-e800860bdacc/go.mod h1:BMwS4vdjEYf1gmjPGSKCzWP/I6YlI6fkefJ9nsjBjaU=
|
github.com/dweymouth/go-jellyfin v0.0.0-20231116161116-e800860bdacc/go.mod h1:BMwS4vdjEYf1gmjPGSKCzWP/I6YlI6fkefJ9nsjBjaU=
|
||||||
github.com/dweymouth/go-mpv v0.0.0-20230406003141-7f1858e503ee h1:ZGyJ6wp7CAfT31BugypcF/TPKEy2RrGR9JFq1JOjOpY=
|
github.com/dweymouth/go-mpv v0.0.0-20230406003141-7f1858e503ee h1:ZGyJ6wp7CAfT31BugypcF/TPKEy2RrGR9JFq1JOjOpY=
|
||||||
github.com/dweymouth/go-mpv v0.0.0-20230406003141-7f1858e503ee/go.mod h1:Ov0ieN90M7i+0k3OxhA/g1dozGs+UcPHDsMKqPgRDk0=
|
github.com/dweymouth/go-mpv v0.0.0-20230406003141-7f1858e503ee/go.mod h1:Ov0ieN90M7i+0k3OxhA/g1dozGs+UcPHDsMKqPgRDk0=
|
||||||
github.com/dweymouth/go-subsonic v0.0.0-20231216234924-3f62122dc53a h1:UJcwloSIzRpUOIwRE207JF2SHoTmOg7vJWzfQWpztSk=
|
github.com/dweymouth/go-subsonic v0.0.0-20231217175944-9b48c9ffc002 h1:DhWQZJObkCUSFmHu/eEzHRqy0R33DcR266nPAvZJE34=
|
||||||
github.com/dweymouth/go-subsonic v0.0.0-20231216234924-3f62122dc53a/go.mod h1:OWtcumdQsan8uM6wmx6PqKhldaCthH10CQ+vb+94kzo=
|
github.com/dweymouth/go-subsonic v0.0.0-20231217175944-9b48c9ffc002/go.mod h1:OWtcumdQsan8uM6wmx6PqKhldaCthH10CQ+vb+94kzo=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
|
|||||||
@@ -0,0 +1,132 @@
|
|||||||
|
package jukebox
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/dweymouth/supersonic/backend/mediaprovider"
|
||||||
|
"github.com/dweymouth/supersonic/player"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
stopped = 0
|
||||||
|
playing = 1
|
||||||
|
paused = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
type JukeboxPlayer struct {
|
||||||
|
provider mediaprovider.JukeboxProvider
|
||||||
|
|
||||||
|
state int // stopped, playing, paused
|
||||||
|
volume int
|
||||||
|
seeking bool
|
||||||
|
numTracks int
|
||||||
|
|
||||||
|
curTrack int
|
||||||
|
curTrackDuration float64
|
||||||
|
startTrackTime float64
|
||||||
|
startedAtUnixSecs float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *JukeboxPlayer) SetVolume(vol int) error {
|
||||||
|
go func() {
|
||||||
|
if err := j.provider.JukeboxSetVolume(vol); err == nil {
|
||||||
|
j.volume = vol
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *JukeboxPlayer) GetVolume() int {
|
||||||
|
return j.volume
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *JukeboxPlayer) PlayTrackAt(idx int) error {
|
||||||
|
go func() {
|
||||||
|
if err := j.provider.JukeboxSeek(idx, 0); err == nil {
|
||||||
|
j.curTrack = idx
|
||||||
|
j.Continue()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *JukeboxPlayer) Continue() error {
|
||||||
|
if j.state == playing {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
if err := j.provider.JukeboxStart(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
j.state = playing
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *JukeboxPlayer) Pause() error {
|
||||||
|
if j.state != playing {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
if err := j.provider.JukeboxStop(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
j.state = paused
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *JukeboxPlayer) Stop() error {
|
||||||
|
if j.state == stopped {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
if err := j.provider.JukeboxStop(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
j.state = stopped
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *JukeboxPlayer) SeekPrevious() error {
|
||||||
|
track := j.curTrack
|
||||||
|
if track > 0 {
|
||||||
|
track = j.curTrack - 1
|
||||||
|
}
|
||||||
|
return j.PlayTrackAt(track)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *JukeboxPlayer) SeekNext() error {
|
||||||
|
track := j.curTrack
|
||||||
|
if track >= j.numTracks {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return j.PlayTrackAt(track + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *JukeboxPlayer) SeekSeconds(secs float64) error {
|
||||||
|
j.seeking = true
|
||||||
|
go func() {
|
||||||
|
j.provider.JukeboxSeek(j.curTrack, int(secs))
|
||||||
|
j.seeking = false
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *JukeboxPlayer) IsSeeking() bool {
|
||||||
|
return j.seeking
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *JukeboxPlayer) GetStatus() player.Status {
|
||||||
|
state := player.Stopped
|
||||||
|
if j.state == playing {
|
||||||
|
state = player.Playing
|
||||||
|
} else if j.state == paused {
|
||||||
|
state = player.Paused
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO - the rest
|
||||||
|
|
||||||
|
return player.Status{
|
||||||
|
State: state,
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
-4
@@ -1,15 +1,15 @@
|
|||||||
package player
|
package player
|
||||||
|
|
||||||
|
import "github.com/dweymouth/supersonic/backend/mediaprovider"
|
||||||
|
|
||||||
type URLPlayer interface {
|
type URLPlayer interface {
|
||||||
BasePlayer
|
BasePlayer
|
||||||
AppendFile(url string) error
|
AppendFile(url string) error
|
||||||
PlayFile(url string) error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type TrackIDPlayer interface {
|
type TrackPlayer interface {
|
||||||
BasePlayer
|
BasePlayer
|
||||||
AppendTrack(trackID string) error
|
AppendTrack(track *mediaprovider.Track) error
|
||||||
PlayTrack(trackID string) error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type BasePlayer interface {
|
type BasePlayer interface {
|
||||||
|
|||||||
Reference in New Issue
Block a user