From a851e0f600e4979e2e87cbc27806ed505a739e4f Mon Sep 17 00:00:00 2001 From: Drew Weymouth Date: Sun, 9 Jun 2024 09:33:50 -0700 Subject: [PATCH] skeleton of IPC mechanism to allow playback control with cmdline flags --- backend/ipc/api.go | 19 ++++++++++ backend/ipc/conn_other.go | 14 +++++++ backend/ipc/conn_windows.go | 19 ++++++++++ backend/ipc/server.go | 73 +++++++++++++++++++++++++++++++++++++ go.mod | 1 + go.sum | 2 + 6 files changed, 128 insertions(+) create mode 100644 backend/ipc/api.go create mode 100644 backend/ipc/conn_other.go create mode 100644 backend/ipc/conn_windows.go create mode 100644 backend/ipc/server.go diff --git a/backend/ipc/api.go b/backend/ipc/api.go new file mode 100644 index 00000000..32681f7a --- /dev/null +++ b/backend/ipc/api.go @@ -0,0 +1,19 @@ +package ipc + +const ( + PlayPath = "/transport/play" + PlayPausePath = "/transport/playpause" + PausePath = "/transport/pause" + StopPath = "/transport/stop" + PreviousPath = "/transport/previous" + NextPath = "/transport/next" + TimePosPath = "/transport/timepos" + + PlayTrackPath = "/queue/playtrack" + + VolumePath = "/volume" +) + +type Response struct { + Error string `json:"error"` +} diff --git a/backend/ipc/conn_other.go b/backend/ipc/conn_other.go new file mode 100644 index 00000000..62fdc9e3 --- /dev/null +++ b/backend/ipc/conn_other.go @@ -0,0 +1,14 @@ +//go:build !windows + +package ipc + +import "net" + +func Dial() (net.Conn, error) { + // TODO - use XDG runtime dir, also handle portable mode + return net.Dial("unix", "/tmp/supersonic.sock") +} + +func Listen() (net.Listener, error) { + return net.Listen("unix", "/tmp/supersonic.sock") +} diff --git a/backend/ipc/conn_windows.go b/backend/ipc/conn_windows.go new file mode 100644 index 00000000..59c0fadb --- /dev/null +++ b/backend/ipc/conn_windows.go @@ -0,0 +1,19 @@ +//go:build windows + +package ipc + +import ( + "net" + "time" + + "github.com/Microsoft/go-winio" +) + +func Dial() (net.Conn, error) { + timeout := 300 * time.Millisecond + return winio.DialPipe("supersonic", &timeout) +} + +func Listen() (net.Listener, error) { + return winio.ListenPipe("supersonic", nil) +} diff --git a/backend/ipc/server.go b/backend/ipc/server.go new file mode 100644 index 00000000..00e4ddda --- /dev/null +++ b/backend/ipc/server.go @@ -0,0 +1,73 @@ +package ipc + +import ( + "encoding/json" + "net/http" +) + +type Handler interface { + PlayPause() error + Stop() error + Pause() error + Continue() error + SeekBackOrPrevious() error + SeekNext() error + SeekSeconds(float64) error + Volume() int + SetVolume(int) error +} + +type serverImpl struct { + handler Handler +} + +func NewServer(handler Handler) *http.Server { + s := serverImpl{handler: handler} + return &http.Server{ + Handler: s.createHandler(), + } +} + +func (s *serverImpl) createHandler() http.Handler { + m := http.NewServeMux() + m.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotFound) + w.Write([]byte("The given path is not valid")) + }) + m.HandleFunc(PlayPath, s.makeSimpleEndpointHandler(s.handler.Continue)) + m.HandleFunc(PausePath, s.makeSimpleEndpointHandler(s.handler.Pause)) + m.HandleFunc(PlayPausePath, s.makeSimpleEndpointHandler(s.handler.PlayPause)) + m.HandleFunc(StopPath, s.makeSimpleEndpointHandler(s.handler.Stop)) + m.HandleFunc(PreviousPath, s.makeSimpleEndpointHandler(s.handler.SeekBackOrPrevious)) + m.HandleFunc(NextPath, s.makeSimpleEndpointHandler(s.handler.SeekNext)) + return m +} + +func (s *serverImpl) makeSimpleEndpointHandler(f func() error) func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + if err := f(); err == nil { + s.writeOK(w) + } else { + s.writeErr(w, err) + } + } +} + +func (s *serverImpl) writeOK(w http.ResponseWriter) (int, error) { + var r Response + b, err := json.Marshal(&r) + if err != nil { + return 0, err + } + return w.Write(b) +} + +func (s *serverImpl) writeErr(w http.ResponseWriter, err error) (int, error) { + r := Response{Error: err.Error()} + b, err := json.Marshal(&r) + if err != nil { + return 0, err + } + w.WriteHeader(http.StatusInternalServerError) + return w.Write(b) +} diff --git a/go.mod b/go.mod index 72b5bb79..40e90e4b 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.21 require ( fyne.io/fyne/v2 v2.5.0 github.com/20after4/configdir v0.1.1 + github.com/Microsoft/go-winio v0.6.2 github.com/cenkalti/dominantcolor v1.0.2 github.com/deluan/sanitize v0.0.0-20230310221930-6e18967d9fc1 github.com/dweymouth/fyne-lyrics v0.0.0-20240528234907-15eee7ce5e64 diff --git a/go.sum b/go.sum index 6a858b24..335b6a50 100644 --- a/go.sum +++ b/go.sum @@ -50,6 +50,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/akavel/rsrc v0.10.2/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0= github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30=