diff --git a/backend/app.go b/backend/app.go index 1fd9e02..37a662f 100644 --- a/backend/app.go +++ b/backend/app.go @@ -38,7 +38,10 @@ type App struct { Player *player.Player UpdateChecker UpdateChecker MPRISHandler *MPRISHandler - OnReactivate func() + + // UI callbacks to be set in main + OnReactivate func() + OnExit func() appName string appVersionTag string @@ -104,8 +107,7 @@ func StartupApp(appName, displayAppName, appVersionTag, configFile, latestReleas _, _ = a.ImageManager.GetCoverThumbnail(coverID) }) - a.MPRISHandler = NewMPRISHandler(displayAppName, a.Player, a.PlaybackManager) - a.MPRISHandler.Start() + a.setupMPRIS(displayAppName) return a, nil } @@ -138,9 +140,7 @@ func (a *App) startSessionWatcher(sessionPath string) { activatePath := path.Join(sessionPath, sessionActivateFile) if _, err := os.Stat(activatePath); err == nil { os.Remove(path.Join(sessionPath, sessionActivateFile)) - if a.OnReactivate != nil { - a.OnReactivate() - } + a.callOnReactivate() } } } @@ -148,6 +148,12 @@ func (a *App) startSessionWatcher(sessionPath string) { } } +func (a *App) callOnReactivate() { + if a.OnReactivate != nil { + a.OnReactivate() + } +} + func (a *App) initMPV() error { p := player.NewWithClientName(a.appName) c := a.Config.LocalPlayback @@ -206,6 +212,23 @@ func (a *App) setupMPV() error { return nil } +func (a *App) setupMPRIS(mprisAppName string) { + a.MPRISHandler = NewMPRISHandler(mprisAppName, a.Player, a.PlaybackManager) + a.MPRISHandler.ArtURLLookup = a.ImageManager.GetCoverArtUrl + a.MPRISHandler.OnRaise = func() error { a.callOnReactivate(); return nil } + a.MPRISHandler.OnQuit = func() error { + if a.OnExit == nil { + return errors.New("no quit handler registered") + } + go func() { + time.Sleep(10 * time.Millisecond) + a.OnExit() + }() + return nil + } + a.MPRISHandler.Start() +} + func (a *App) LoginToDefaultServer(string) error { serverCfg := a.ServerManager.GetDefaultServer() if serverCfg == nil { diff --git a/backend/imagemanager.go b/backend/imagemanager.go index c515f90..b5d4721 100644 --- a/backend/imagemanager.go +++ b/backend/imagemanager.go @@ -88,6 +88,16 @@ func (i *ImageManager) GetFullSizeCoverArt(coverID string) (image.Image, error) return im, nil } +func (i *ImageManager) GetCoverArtUrl(coverID string) (string, error) { + path := i.filePathForCover(coverID) + if _, err := os.Stat(path); err == nil { + // this is probably broken for Windows but it's currently only used + // for MPRIS, so we are OK for now + return fmt.Sprintf("file://%s", path), nil + } + return "", errors.New("cover not found") +} + func (i *ImageManager) GetCachedArtistImage(artistID string) (image.Image, bool) { return i.loadLocalImage(i.filePathForArtistImage(artistID)) } diff --git a/backend/mpris.go b/backend/mpris.go index 4f72af4..e5f1aae 100644 --- a/backend/mpris.go +++ b/backend/mpris.go @@ -31,7 +31,7 @@ type MPRISHandler struct { OnRaise func() error // Function to look up the artwork URL for a given track ID - ArtURLLookup func(trackID string) string + ArtURLLookup func(trackID string) (string, error) playerName string p *player.Player @@ -44,6 +44,10 @@ func NewMPRISHandler(playerName string, p *player.Player, pm *PlaybackManager) * m := &MPRISHandler{playerName: playerName, p: p, pm: pm} m.s = server.NewServer(playerName, m, m) m.evt = events.NewEventHandler(m.s) + m.p.OnSeek(func() { + pos := secondsToMicroseconds(m.p.GetStatus().TimePos) + m.evt.Player.OnSeek(pos) + }) return m } @@ -176,7 +180,9 @@ func (m *MPRISHandler) Metadata() (types.Metadata, error) { } var artURL string if tr.ID != "" && m.ArtURLLookup != nil { - artURL = m.ArtURLLookup(tr.ID) + if u, err := m.ArtURLLookup(tr.ID); err == nil { + artURL = u + } } return types.Metadata{ TrackId: dbus.ObjectPath(trackID), @@ -205,11 +211,11 @@ func (m *MPRISHandler) Position() (int64, error) { } func (m *MPRISHandler) MinimumRate() (float64, error) { - return 0, notImplemented + return 1, nil } func (m *MPRISHandler) MaximumRate() (float64, error) { - return 0, notImplemented + return 1, nil } func (m *MPRISHandler) CanGoNext() (bool, error) { @@ -229,7 +235,7 @@ func (m *MPRISHandler) CanPause() (bool, error) { } func (m *MPRISHandler) CanSeek() (bool, error) { - return false, notImplemented + return true, nil } func (m *MPRISHandler) CanControl() (bool, error) { diff --git a/main.go b/main.go index 6cc4fd2..01e09b1 100644 --- a/main.go +++ b/main.go @@ -41,6 +41,10 @@ func main() { } mainWindow := ui.NewMainWindow(fyneApp, appname, displayName, appVersion, myApp, fyne.NewSize(w, h)) myApp.OnReactivate = mainWindow.Show + myApp.OnExit = func() { + saveWindowPosition(myApp.Config, mainWindow.Window) + fyneApp.Quit() + } go func() { // TODO: There is a race condition with laying out the window before the @@ -61,8 +65,7 @@ func main() { mainWindow.Show() mainWindow.Window.SetCloseIntercept(func() { - myApp.Config.Application.WindowHeight = int(mainWindow.Canvas().Size().Height) - myApp.Config.Application.WindowWidth = int(mainWindow.Canvas().Size().Width) + saveWindowPosition(myApp.Config, mainWindow.Window) if myApp.Config.Application.CloseToSystemTray && mainWindow.HaveSystemTray() { mainWindow.Window.Hide() @@ -75,3 +78,8 @@ func main() { log.Println("Running shutdown tasks...") myApp.Shutdown() } + +func saveWindowPosition(config *backend.Config, window fyne.Window) { + config.Application.WindowHeight = int(window.Canvas().Size().Height) + config.Application.WindowWidth = int(window.Canvas().Size().Width) +}