diff --git a/backend/ipc/api.go b/backend/ipc/api.go index 32681f7..d75e5ae 100644 --- a/backend/ipc/api.go +++ b/backend/ipc/api.go @@ -1,19 +1,37 @@ package ipc const ( - PlayPath = "/transport/play" + // GET + PingPath = "/ping" + // POST + PlayPath = "/transport/play" + // POST PlayPausePath = "/transport/playpause" - PausePath = "/transport/pause" - StopPath = "/transport/stop" - PreviousPath = "/transport/previous" - NextPath = "/transport/next" - TimePosPath = "/transport/timepos" - + // POST + PausePath = "/transport/pause" + // POST + StopPath = "/transport/stop" + // POST + PreviousPath = "/transport/previous" + // POST + NextPath = "/transport/next" + // POST(TimePos) + TimePosPath = "/transport/timepos" + // POST to seek PlayTrackPath = "/queue/playtrack" - + // GET -> Volume + // POST(Volume) VolumePath = "/volume" ) +type TimePos struct { + Seconds float64 `json:"seconds"` +} + +type Volume struct { + Volume int `json:"volume"` +} + type Response struct { Error string `json:"error"` } diff --git a/backend/ipc/client.go b/backend/ipc/client.go new file mode 100644 index 0000000..134ed4f --- /dev/null +++ b/backend/ipc/client.go @@ -0,0 +1,83 @@ +package ipc + +import ( + "context" + "encoding/json" + "errors" + "net" + "net/http" +) + +var ErrPingFail = errors.New("ping failed") + +type Client struct { + httpC http.Client +} + +// Connect attempts to connect to the IPC socket as client. +func Connect() (*Client, error) { + conn, err := Dial() + if err != nil { + return nil, err + } + client := &Client{httpC: http.Client{ + Transport: &http.Transport{ + DialContext: func(_ context.Context, _, _ string) (net.Conn, error) { + return conn, nil + }, + }, + }} + if err := client.Ping(); err != nil { + return nil, err + } + return client, nil +} + +func (c *Client) Ping() error { + if c.makeSimpleRequest(http.MethodGet, PingPath) != nil { + return ErrPingFail + } + return nil +} + +func (c *Client) Play() error { + return c.makeSimpleRequest(http.MethodPost, PlayPath) +} + +func (c *Client) Pause() error { + return c.makeSimpleRequest(http.MethodPost, PausePath) +} + +func (c *Client) PlayPause() error { + return c.makeSimpleRequest(http.MethodPost, PlayPausePath) +} + +func (c *Client) SeekNext() error { + return c.makeSimpleRequest(http.MethodPost, NextPath) +} + +func (c *Client) SeekBackOrPrevious() error { + return c.makeSimpleRequest(http.MethodPost, NextPath) +} + +func (c *Client) makeSimpleRequest(method string, path string) error { + var resp *http.Response + var err error + switch method { + case http.MethodGet: + resp, err = c.httpC.Get(path) + case http.MethodPost: + resp, err = c.httpC.Post(path, "application/json", nil) + } + + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + var r Response + json.NewDecoder(resp.Body).Decode(&r) + return errors.New(r.Error) + } + return nil +} diff --git a/backend/ipc/server.go b/backend/ipc/server.go index 00e4ddd..40a38cd 100644 --- a/backend/ipc/server.go +++ b/backend/ipc/server.go @@ -34,22 +34,48 @@ func (s *serverImpl) createHandler() http.Handler { w.WriteHeader(http.StatusNotFound) w.Write([]byte("The given path is not valid")) }) + m.HandleFunc(PingPath, s.makeSimpleEndpointHandler(func() error { return nil })) 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)) + m.HandleFunc(TimePosPath, func(w http.ResponseWriter, r *http.Request) { + var t TimePos + if err := json.NewDecoder(r.Response.Body).Decode(&t); err != nil { + s.writeErr(w, err) + return + } + s.writeSimpleResponse(w, s.handler.SeekSeconds(t.Seconds)) + }) + m.HandleFunc(VolumePath, func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodGet { + msg, _ := json.Marshal(Volume{Volume: s.handler.Volume()}) + w.Write(msg) + return + } + var v Volume + if err := json.NewDecoder(r.Response.Body).Decode(&v); err != nil { + s.writeErr(w, err) + return + } + s.writeSimpleResponse(w, s.handler.SetVolume(v.Volume)) + }) 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) - } + s.writeSimpleResponse(w, f()) + } +} + +func (s *serverImpl) writeSimpleResponse(w http.ResponseWriter, err error) { + if err == nil { + s.writeOK(w) + } else { + s.writeErr(w, err) } }