aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Fischer <florian.fischer@muhq.space>2023-07-01 18:53:37 +0200
committerFlorian Fischer <florian.fischer@muhq.space>2023-07-01 18:53:37 +0200
commited9682c977fb0cc5d94f4ac653e3fbb514bd8ecc (patch)
treefc588216fc8419ff86482dcb48a446f6058af34a
parentdb21d2ec6980e31a2e9a0db60eb2fdad83b33671 (diff)
downloadWRMS-ed9682c977fb0cc5d94f4ac653e3fbb514bd8ecc.tar.gz
WRMS-ed9682c977fb0cc5d94f4ac653e3fbb514bd8ecc.zip
introduce Player interface to allow multiple player implementations
-rw-r--r--backend.go6
-rw-r--r--localBackend.go2
-rw-r--r--player.go53
-rw-r--r--spotify.go2
-rw-r--r--upload.go2
-rw-r--r--wrms.go14
-rw-r--r--youtube.go2
7 files changed, 48 insertions, 33 deletions
diff --git a/backend.go b/backend.go
index 0d82e7f..9f778b2 100644
--- a/backend.go
+++ b/backend.go
@@ -6,15 +6,15 @@ import (
)
type Backend interface {
- Play(song *Song, player *Player)
+ Play(song *Song, player Player)
Search(keyword string) []Song
OnSongFinished(song *Song)
}
type DummyBackend struct{}
-func (dummy *DummyBackend) Play(song *Song, player *Player) {}
-func (dummy *DummyBackend) OnSongFinished(song *Song) {}
+func (dummy *DummyBackend) Play(song *Song, player Player) {}
+func (dummy *DummyBackend) OnSongFinished(song *Song) {}
func (dummy *DummyBackend) Search(keyword string) []Song {
s := NewDummySong("Dummy Mc Crashtest", "exactly "+keyword)
return []Song{s}
diff --git a/localBackend.go b/localBackend.go
index ead1dc9..19be1b4 100644
--- a/localBackend.go
+++ b/localBackend.go
@@ -121,7 +121,7 @@ func (b *LocalBackend) findSongs() {
b.insert(songs)
}
-func (b *LocalBackend) Play(song *Song, player *Player) {
+func (b *LocalBackend) Play(song *Song, player Player) {
player.PlayUri("file://" + song.Uri)
}
diff --git a/player.go b/player.go
index 9a3723f..490fb0a 100644
--- a/player.go
+++ b/player.go
@@ -11,6 +11,17 @@ import (
"muhq.space/go/wrms/llog"
)
+type Player interface {
+ Play(*Song)
+ Playing() bool
+ Search(string) chan []Song
+ PlayUri(string)
+ PlayData(io.Reader)
+ Pause()
+ Continue()
+ Stop()
+}
+
// command struct used to serialize player commands
type cmd struct {
cmd string
@@ -18,14 +29,14 @@ type cmd struct {
uri string
}
-type Player struct {
+type MpvPlayer struct {
Backends map[string]Backend
wrms *Wrms
mpv atomic.Pointer[exec.Cmd]
cmdQueue chan cmd
}
-func NewPlayer(wrms *Wrms, backends []string) *Player {
+func NewMpvPlayer(wrms *Wrms, backends []string) *MpvPlayer {
availableBackends := map[string]Backend{}
var b Backend
@@ -53,7 +64,7 @@ func NewPlayer(wrms *Wrms, backends []string) *Player {
}
}
- p := &Player{
+ p := &MpvPlayer{
Backends: availableBackends,
wrms: wrms,
cmdQueue: make(chan cmd)}
@@ -72,7 +83,7 @@ func mpvArgv(uri string) []string {
return cmd
}
-func (player *Player) startMpv(uri string) *exec.Cmd {
+func (player *MpvPlayer) startMpv(uri string) *exec.Cmd {
if player.mpv.Load() != nil {
llog.Fatal("Player has already an mpv subprocess")
}
@@ -88,7 +99,7 @@ func (player *Player) startMpv(uri string) *exec.Cmd {
return mpv
}
-func (player *Player) runMpv() {
+func (player *MpvPlayer) runMpv() {
// Remember the song we are playing
// TODO: This is potentially racy
currentSong := wrms.CurrentSong.Load()
@@ -113,7 +124,7 @@ func (player *Player) runMpv() {
}
}
-func (p *Player) serveCmds() {
+func (p *MpvPlayer) serveCmds() {
for cmd := range p.cmdQueue {
switch cmd.cmd {
case "playData":
@@ -131,27 +142,31 @@ func (p *Player) serveCmds() {
}
// Play arbitrary media using mpv
-func (p *Player) PlayUri(uri string) { p.cmdQueue <- cmd{cmd: "playUri", uri: uri} }
-func (p *Player) PlayData(data io.Reader) { p.cmdQueue <- cmd{cmd: "playData", data: data} }
+func (p *MpvPlayer) PlayUri(uri string) { p.cmdQueue <- cmd{cmd: "playUri", uri: uri} }
+func (p *MpvPlayer) PlayData(data io.Reader) { p.cmdQueue <- cmd{cmd: "playData", data: data} }
// Controls
-func (p *Player) Pause() { p.cmdQueue <- cmd{cmd: "pause"} }
-func (p *Player) Continue() { p.cmdQueue <- cmd{cmd: "continue"} }
-func (p *Player) Stop() { p.cmdQueue <- cmd{cmd: "stop"} }
-func (p *Player) Close() { close(p.cmdQueue) }
+func (p *MpvPlayer) Pause() { p.cmdQueue <- cmd{cmd: "pause"} }
+func (p *MpvPlayer) Continue() { p.cmdQueue <- cmd{cmd: "continue"} }
+func (p *MpvPlayer) Stop() { p.cmdQueue <- cmd{cmd: "stop"} }
+func (p *MpvPlayer) Close() { close(p.cmdQueue) }
// Double dispatch play entry point
-func (p *Player) Play(song *Song) {
+func (p *MpvPlayer) Play(song *Song) {
llog.Info("Start playing %v", song)
p.Backends[song.Source].Play(song, p)
}
-func (player *Player) _playUri(uri string) {
+func (p *MpvPlayer) Playing() bool {
+ return p.mpv.Load() != nil
+}
+
+func (player *MpvPlayer) _playUri(uri string) {
player.startMpv(uri)
go player.runMpv()
}
-func (player *Player) _playData(data io.Reader) {
+func (player *MpvPlayer) _playData(data io.Reader) {
mpv := player.startMpv("-")
stdin, err := mpv.StdinPipe()
@@ -170,7 +185,7 @@ func (player *Player) _playData(data io.Reader) {
go player.runMpv()
}
-func (player *Player) _pause() {
+func (player *MpvPlayer) _pause() {
mpv := player.mpv.Load()
if mpv == nil {
llog.Warning("No mpv process to pause")
@@ -184,7 +199,7 @@ func (player *Player) _pause() {
}
}
-func (player *Player) _continue() {
+func (player *MpvPlayer) _continue() {
mpv := player.mpv.Load()
if mpv == nil {
llog.Warning("Continue Play but there is no running mpv process to continue")
@@ -198,7 +213,7 @@ func (player *Player) _continue() {
}
}
-func (player *Player) _stop() {
+func (player *MpvPlayer) _stop() {
mpv := player.mpv.Load()
if mpv == nil {
// Wrms.Next() may race with Player.runMpv() therefore this must not be a hard error
@@ -217,7 +232,7 @@ func (player *Player) _stop() {
player.mpv.Store(nil)
}
-func (player *Player) Search(pattern string) chan []Song {
+func (player *MpvPlayer) Search(pattern string) chan []Song {
var wg sync.WaitGroup
wg.Add(len(player.Backends))
diff --git a/spotify.go b/spotify.go
index e0fcf65..da7f6da 100644
--- a/spotify.go
+++ b/spotify.go
@@ -64,7 +64,7 @@ func NewSpotify(config *SpotifyConfig) (*SpotifyBackend, error) {
func (_ *SpotifyBackend) OnSongFinished(*Song) {}
-func (spotify *SpotifyBackend) Play(song *Song, player *Player) {
+func (spotify *SpotifyBackend) Play(song *Song, player Player) {
trackID := song.Uri
session := spotify.session
llog.Debug("Loading track for play: %v", trackID)
diff --git a/upload.go b/upload.go
index 15568c9..2d62f1c 100644
--- a/upload.go
+++ b/upload.go
@@ -91,7 +91,7 @@ func (b *UploadBackend) OnSongFinished(song *Song) {
os.Remove(songPath)
}
-func (b *UploadBackend) Play(song *Song, player *Player) {
+func (b *UploadBackend) Play(song *Song, player Player) {
player.PlayUri("file://" + path.Join(b.uploadDir, song.Uri))
}
diff --git a/wrms.go b/wrms.go
index 96bb43d..9d94325 100644
--- a/wrms.go
+++ b/wrms.go
@@ -164,7 +164,7 @@ type Wrms struct {
Songs []Song
queue Playlist
CurrentSong atomic.Pointer[Song]
- Player *Player
+ Player Player
Playing bool
Config Config
eventId atomic.Uint64
@@ -173,7 +173,7 @@ type Wrms struct {
func NewWrms(config Config) *Wrms {
wrms := Wrms{}
wrms.Config = config
- wrms.Player = NewPlayer(&wrms, config.Backends)
+ wrms.Player = NewMpvPlayer(&wrms, config.Backends)
return &wrms
}
@@ -300,8 +300,8 @@ func (wrms *Wrms) DeleteSong(songUri string) {
func (wrms *Wrms) Next() {
wrms.rwlock.Lock()
- // Terminate the mpv process playing the current song
- if wrms.Player.mpv.Load() != nil {
+ // Terminate the Player if it is currently playing
+ if wrms.Player.Playing() {
wrms.Player.Stop()
}
@@ -372,10 +372,10 @@ func (wrms *Wrms) PlayPause() {
return
}
- // Wrms has an active mpv process -> continue playing
- if wrms.Player.mpv.Load() != nil {
+ // The player is playing -> continue playing
+ if wrms.Player.Playing() {
wrms.Player.Continue()
- // Wrms has a current song but no mpv process playing it -> start a new one
+ // The player is stopped -> start it
} else {
wrms.Player.Play(currentSong)
}
diff --git a/youtube.go b/youtube.go
index 3b9efbe..8b29043 100644
--- a/youtube.go
+++ b/youtube.go
@@ -18,7 +18,7 @@ func NewYoutubeBackend() *YoutubeBackend {
func (_ *YoutubeBackend) OnSongFinished(*Song) {}
-func (_ *YoutubeBackend) Play(song *Song, player *Player) {
+func (_ *YoutubeBackend) Play(song *Song, player Player) {
player.PlayUri("https://youtube.com/watch?v=" + song.Uri)
}