diff --git a/backend/app.go b/backend/app.go index bbf421d..7cb225a 100644 --- a/backend/app.go +++ b/backend/app.go @@ -110,8 +110,9 @@ func StartupApp(appName, displayAppName, appVersionTag, configFile, latestReleas _, _ = a.ImageManager.GetCoverThumbnail(coverID) }) + // OS media center integrations a.setupMPRIS(displayAppName) - a.setupMPMedia() + InitMPMediaHandler(a.Player, a.PlaybackManager, a.ImageManager.GetCoverArtUrl) return a, nil } @@ -233,17 +234,6 @@ func (a *App) setupMPRIS(mprisAppName string) { a.MPRISHandler.Start() } -func (a *App) setupMPMedia() { - a.MPMediaHandler = NewMPMediaHandler(a.Player, a.PlaybackManager) - if a.MPMediaHandler != nil { - a.MPMediaHandler.ArtURLLookup = func(coverID string) (string, error) { - // artwork needs to be in cache before playback, so cover is retrieved in advance. - a.ImageManager.GetCoverThumbnail(coverID) - return a.ImageManager.GetCoverArtUrl(coverID) - } - } -} - func (a *App) LoginToDefaultServer(string) error { serverCfg := a.ServerManager.GetDefaultServer() if serverCfg == nil { diff --git a/backend/mpmedia.go b/backend/mpmedia.go deleted file mode 100644 index 385a113..0000000 --- a/backend/mpmedia.go +++ /dev/null @@ -1,12 +0,0 @@ -package backend - -import ( - "github.com/dweymouth/supersonic/player" -) - -// MPMediaHandler is the handler for MacOS media controls and system events. -type MPMediaHandler struct { - player *player.Player - playbackManager *PlaybackManager - ArtURLLookup func(trackID string) (string, error) -} diff --git a/backend/mpmedia_mac.go b/backend/mpmedia_mac.go index 7848856..832b8ac 100644 --- a/backend/mpmedia_mac.go +++ b/backend/mpmedia_mac.go @@ -18,6 +18,7 @@ import ( import ( "fmt" "log" + "strings" "unsafe" "github.com/dweymouth/supersonic/backend/mediaprovider" @@ -48,16 +49,24 @@ func os_remote_command_callback(command C.Command, value C.double) { } } +// MPMediaHandler is the handler for MacOS media controls and system events. +type MPMediaHandler struct { + player *player.Player + playbackManager *PlaybackManager + artURLLookup func(string) (string, error) +} + // global recipient for Object-C callbacks from command center. // This is global so that it can be called from 'os_remote_command_callback' to avoid passing Go pointers into C. var mpMediaEventRecipient *MPMediaHandler // NewMPMediaHandler creates a new MPMediaHandler instances and sets it as the current recipient // for incoming system events. -func NewMPMediaHandler(player *player.Player, playbackManager *PlaybackManager) *MPMediaHandler { +func InitMPMediaHandler(player *player.Player, playbackManager *PlaybackManager, artURLLookup func(trackID string) (string, error)) error { mp := &MPMediaHandler{ player: player, playbackManager: playbackManager, + artURLLookup: artURLLookup, } // register remote commands and set callback target @@ -65,33 +74,31 @@ func NewMPMediaHandler(player *player.Player, playbackManager *PlaybackManager) C.register_os_remote_commands() mp.playbackManager.OnSongChange(func(track, _ *mediaprovider.Track) { + var title, artist, artURL string + var duration int if track != nil && track.ID != "" { - var artURL string - if mp.ArtURLLookup != nil { - var err error - if artURL, err = mp.ArtURLLookup(track.CoverArtID); err != nil { - log.Printf("error fetching art url: %s", err.Error()) - } + var err error + if artURL, err = mp.artURLLookup(track.CoverArtID); err != nil { + log.Printf("error fetching art url: %s", err.Error()) } - - cTitle := C.CString(track.Name) - defer C.free(unsafe.Pointer(cTitle)) - - var artist string - if len(track.ArtistNames) > 0 { - artist = track.ArtistNames[0] - } - - cArtist := C.CString(artist) - defer C.free(unsafe.Pointer(cArtist)) - - cArtURL := C.CString(artURL) - defer C.free(unsafe.Pointer(cArtURL)) - - cTrackDuration := C.double(track.Duration) - - C.set_os_now_playing_info(cTitle, cArtist, cArtURL, cTrackDuration) + title = track.Name + artist = strings.Join(track.ArtistNames, ", ") + duration = track.Duration } + + cTitle := C.CString(title) + defer C.free(unsafe.Pointer(cTitle)) + + cArtist := C.CString(artist) + defer C.free(unsafe.Pointer(cArtist)) + + cArtURL := C.CString(artURL) + defer C.free(unsafe.Pointer(cArtURL)) + + cTrackDuration := C.double(duration) + + C.set_os_now_playing_info(cTitle, cArtist, cArtURL, cTrackDuration) + }) mp.player.OnStopped(func() { @@ -112,7 +119,7 @@ func NewMPMediaHandler(player *player.Player, playbackManager *PlaybackManager) C.update_os_now_playing_info_position(C.double(mp.player.GetStatus().TimePos)) }) - return mp + return nil } /** diff --git a/backend/mpmedia_other.go b/backend/mpmedia_other.go index 1c0c275..0f1ba84 100644 --- a/backend/mpmedia_other.go +++ b/backend/mpmedia_other.go @@ -2,9 +2,13 @@ package backend -import "github.com/dweymouth/supersonic/player" +import ( + "errors" -func NewMPMediaHandler(player *player.Player, playbackManager *PlaybackManager) *MPMediaHandler { + "github.com/dweymouth/supersonic/player" +) + +func InitMPMediaHandler(player *player.Player, playbackManager *PlaybackManager, artURLLookup func(trackID string) (string, error)) error { // MPMediaHandler only supports macOS. - return nil + return errors.New("unsupported platform") } diff --git a/player/player.go b/player/player.go index b33c9c3..e2e3570 100644 --- a/player/player.go +++ b/player/player.go @@ -367,6 +367,10 @@ func (p *Player) PlayFromBeginning() error { // 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 == Paused { err = p.setPaused(false)