From 8e815c644a6df1e903487659f7013cce2b9fd21c Mon Sep 17 00:00:00 2001 From: Drew Weymouth Date: Wed, 12 Jun 2024 09:07:15 -0700 Subject: [PATCH] reactivate works through ipc now, at least on Mac --- backend/app.go | 101 ++++++++++-------------------------- backend/ipc/client.go | 7 +-- backend/ipc/conn_other.go | 9 +++- backend/ipc/conn_windows.go | 5 ++ backend/ipc/server.go | 33 ++++++++---- go.mod | 2 +- 6 files changed, 67 insertions(+), 90 deletions(-) diff --git a/backend/app.go b/backend/app.go index cd915ab..eecc85f 100644 --- a/backend/app.go +++ b/backend/app.go @@ -17,7 +17,6 @@ import ( "github.com/dweymouth/supersonic/backend/player" "github.com/dweymouth/supersonic/backend/player/mpv" "github.com/dweymouth/supersonic/backend/util" - "github.com/fsnotify/fsnotify" "github.com/google/uuid" "github.com/20after4/configdir" @@ -25,13 +24,10 @@ import ( ) const ( - configFile = "config.toml" - portableDir = "supersonic_portable" - sessionDir = "session" - sessionLockFile = ".lock" - sessionActivateFile = ".activate" - savedQueueFile = "saved_queue.json" - themesDir = "themes" + configFile = "config.toml" + portableDir = "supersonic_portable" + savedQueueFile = "saved_queue.json" + themesDir = "themes" ) var ( @@ -47,6 +43,7 @@ type App struct { LocalPlayer *mpv.Player UpdateChecker UpdateChecker MPRISHandler *MPRISHandler + ipcServer ipc.IPCServer // UI callbacks to be set in main OnReactivate func() @@ -91,24 +88,6 @@ func StartupApp(appName, displayAppName, appVersionTag, latestReleaseURL string) return nil, ErrAnotherInstance } - /* - sessionPath := path.Join(confDir, sessionDir) - if _, err := os.Stat(path.Join(sessionPath, sessionLockFile)); err == nil { - log.Println("Another instance is running. Reactivating it...") - reactivateFile := path.Join(sessionPath, sessionActivateFile) - if f, err := os.Create(reactivateFile); err == nil { - f.Close() - } - time.Sleep(750 * time.Millisecond) - if _, err := os.Stat(reactivateFile); err == nil { - log.Println("No other instance responded. Starting as normal...") - os.RemoveAll(sessionPath) - } else { - return nil, ErrAnotherInstance - } - } - */ - log.Printf("Starting %s...", appName) log.Printf("Using config dir: %s", confDir) log.Printf("Using cache dir: %s", cacheDir) @@ -124,19 +103,6 @@ func StartupApp(appName, displayAppName, appVersionTag, latestReleaseURL string) a.readConfig() a.startConfigWriter(a.bgrndCtx) - /* - if !a.Config.Application.AllowMultiInstance { - log.Println("Creating session lock file") - os.MkdirAll(sessionPath, 0770) - if f, err := os.Create(path.Join(sessionPath, sessionLockFile)); err == nil { - f.Close() - } else { - log.Printf("error creating session file: %s", err.Error()) - } - a.startSessionWatcher(sessionPath) - } - */ - a.UpdateChecker = NewUpdateChecker(appVersionTag, latestReleaseURL, &a.Config.Application.LastCheckedVersion) a.UpdateChecker.Start(a.bgrndCtx, 24*time.Hour) @@ -155,9 +121,14 @@ func StartupApp(appName, displayAppName, appVersionTag, latestReleaseURL string) a.ServerManager.SetPrefetchAlbumCoverCallback(func(coverID string) { _, _ = a.ImageManager.GetCoverThumbnail(coverID) }) - listener, _ := ipc.Listen() - server := ipc.NewServer(a.PlaybackManager, nil) - go server.Serve(listener) + + // Start IPC server + listener, err := ipc.Listen() + if err == nil { + a.ipcServer = ipc.NewServer(a.PlaybackManager, a.callOnReactivate, + func() { _ = a.callOnExit() }) + go a.ipcServer.Serve(listener) + } // OS media center integrations a.setupMPRIS(displayAppName) @@ -211,26 +182,6 @@ func (a *App) readConfig() { a.Config = cfg } -func (a *App) startSessionWatcher(sessionPath string) { - if sessionWatch, err := fsnotify.NewWatcher(); err == nil { - sessionWatch.Add(sessionPath) - go func() { - for { - select { - case <-a.bgrndCtx.Done(): - return - case <-sessionWatch.Events: - activatePath := path.Join(sessionPath, sessionActivateFile) - if _, err := os.Stat(activatePath); err == nil { - os.Remove(path.Join(sessionPath, sessionActivateFile)) - a.callOnReactivate() - } - } - } - }() - } -} - // periodically save config file so abnormal exit won't lose settings func (a *App) startConfigWriter(ctx context.Context) { tick := time.NewTicker(2 * time.Minute) @@ -254,6 +205,17 @@ func (a *App) callOnReactivate() { } } +func (a *App) callOnExit() error { + if a.OnExit == nil { + return errors.New("no quit handler registered") + } + go func() { + time.Sleep(10 * time.Millisecond) + a.OnExit() + }() + return nil +} + func (a *App) initMPV() error { p := mpv.NewWithClientName(a.appName) c := a.Config.LocalPlayback @@ -329,16 +291,7 @@ func (a *App) setupMPRIS(mprisAppName string) { return a.ImageManager.GetCoverArtUrl(id) } 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.OnQuit = a.callOnExit a.MPRISHandler.Start() } @@ -361,6 +314,9 @@ func (a *App) DeleteServerCacheDir(serverID uuid.UUID) error { } func (a *App) Shutdown() { + if a.ipcServer != nil { + a.ipcServer.Shutdown(a.bgrndCtx) + } a.MPRISHandler.Shutdown() a.PlaybackManager.DisableCallbacks() if a.Config.Application.SavePlayQueue { @@ -377,7 +333,6 @@ func (a *App) Shutdown() { a.cancel() a.LocalPlayer.Destroy() a.Config.WriteConfigFile(a.configFilePath()) - os.RemoveAll(path.Join(a.configDir, sessionDir)) } func (a *App) LoadSavedPlayQueue() error { diff --git a/backend/ipc/client.go b/backend/ipc/client.go index d03eeb6..34276bb 100644 --- a/backend/ipc/client.go +++ b/backend/ipc/client.go @@ -17,15 +17,10 @@ type Client struct { // Connect attempts to connect to the IPC socket as client. func Connect() (*Client, error) { - conn, err := Dial() - if err != nil { - log.Println("dial error") - return nil, err - } client := &Client{httpC: http.Client{ Transport: &http.Transport{ DialContext: func(_ context.Context, _, _ string) (net.Conn, error) { - return conn, nil + return Dial() }, }, }} diff --git a/backend/ipc/conn_other.go b/backend/ipc/conn_other.go index 62fdc9e..0dd4e90 100644 --- a/backend/ipc/conn_other.go +++ b/backend/ipc/conn_other.go @@ -2,7 +2,10 @@ package ipc -import "net" +import ( + "net" + "os" +) func Dial() (net.Conn, error) { // TODO - use XDG runtime dir, also handle portable mode @@ -12,3 +15,7 @@ func Dial() (net.Conn, error) { func Listen() (net.Listener, error) { return net.Listen("unix", "/tmp/supersonic.sock") } + +func DestroyConn() error { + return os.Remove("/tmp/supersonic.sock") +} diff --git a/backend/ipc/conn_windows.go b/backend/ipc/conn_windows.go index 59c0fad..8ddf9c1 100644 --- a/backend/ipc/conn_windows.go +++ b/backend/ipc/conn_windows.go @@ -17,3 +17,8 @@ func Dial() (net.Conn, error) { func Listen() (net.Listener, error) { return winio.ListenPipe("supersonic", nil) } + +func DestroyConn() error { + // Windows named pipes automatically clean up + return nil +} diff --git a/backend/ipc/server.go b/backend/ipc/server.go index 3242e9d..960c20e 100644 --- a/backend/ipc/server.go +++ b/backend/ipc/server.go @@ -1,7 +1,9 @@ package ipc import ( + "context" "encoding/json" + "net" "net/http" ) @@ -17,21 +19,34 @@ type PlaybackHandler interface { SetVolume(int) error } -type WindowHandler interface { - Show() - Quit() +type IPCServer interface { + Serve(net.Listener) error + Shutdown(context.Context) error } type serverImpl struct { + server *http.Server pbHandler PlaybackHandler - wdHandler WindowHandler + showFn func() + quitFn func() } -func NewServer(pbHandler PlaybackHandler, wdHandler WindowHandler) *http.Server { - s := serverImpl{pbHandler: pbHandler, wdHandler: wdHandler} - return &http.Server{ +func NewServer(pbHandler PlaybackHandler, showFn, quitFn func()) IPCServer { + s := &serverImpl{pbHandler: pbHandler, showFn: showFn, quitFn: quitFn} + s.server = &http.Server{ Handler: s.createHandler(), } + return s +} + +func (s *serverImpl) Serve(listener net.Listener) error { + return s.server.Serve(listener) +} + +func (s *serverImpl) Shutdown(ctx context.Context) error { + err := s.server.Shutdown(ctx) + DestroyConn() + return err } func (s *serverImpl) createHandler() http.Handler { @@ -42,11 +57,11 @@ func (s *serverImpl) createHandler() http.Handler { }) m.HandleFunc(PingPath, s.makeSimpleEndpointHandler(func() error { return nil })) m.HandleFunc(ShowPath, s.makeSimpleEndpointHandler(func() error { - s.wdHandler.Show() + s.showFn() return nil })) m.HandleFunc(QuitPath, s.makeSimpleEndpointHandler(func() error { - go s.wdHandler.Quit() + s.quitFn() return nil })) m.HandleFunc(PlayPath, s.makeSimpleEndpointHandler(s.pbHandler.Continue)) diff --git a/go.mod b/go.mod index 40e90e4..dc95f66 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,6 @@ require ( github.com/dweymouth/go-jellyfin v0.0.0-20240517151952-5ceca61cb645 github.com/dweymouth/go-mpv v0.0.0-20230406003141-7f1858e503ee github.com/dweymouth/go-subsonic v0.0.0-20240603150834-605046e7c78a - github.com/fsnotify/fsnotify v1.7.0 github.com/godbus/dbus/v5 v5.1.0 github.com/google/uuid v1.3.0 github.com/pelletier/go-toml/v2 v2.0.8 @@ -30,6 +29,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/disintegration/imaging v1.6.2 // indirect github.com/fredbi/uri v1.1.0 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe // indirect github.com/fyne-io/glfw-js v0.0.0-20240101223322-6e1efdc71b7a // indirect github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2 // indirect