diff --git a/application/application.go b/application/application.go index e859cae..68adbed 100644 --- a/application/application.go +++ b/application/application.go @@ -20,7 +20,6 @@ import ( "github.com/buger/jsonparser" "github.com/pkg/errors" - "github.com/vishen/go-chromecast/cast" pb "github.com/vishen/go-chromecast/cast/proto" "github.com/vishen/go-chromecast/playlists" @@ -66,6 +65,7 @@ type App interface { Close(stopMedia bool) error LoadApp(appID, contentID string) error Status() (*cast.Application, *cast.Media, *cast.Volume) + Info() (*cast.DeviceInfo, error) Update() error Pause() error Unpause() error @@ -437,6 +437,15 @@ func (a *Application) Status() (*cast.Application, *cast.Media, *cast.Volume) { return a.application, a.media, a.volumeReceiver } +func (a *Application) Info() (*cast.DeviceInfo, error) { + addr, err := a.conn.RemoteAddr() + + if err != nil { + return nil, err + } + return GetInfo(addr) +} + func (a *Application) Pause() error { if a.media == nil { return ErrNoMediaPause @@ -1121,6 +1130,10 @@ func (a *Application) getLocalIP() (string, error) { return "", fmt.Errorf("Failed to get local ip address") } +//func (a *Application) Info() cmd.{ +// +//} + func (a *Application) startStreamingServer() error { if a.httpServer != nil { return nil diff --git a/application/info.go b/application/info.go new file mode 100644 index 0000000..6463237 --- /dev/null +++ b/application/info.go @@ -0,0 +1,34 @@ +package application + +import ( + "encoding/json" + "fmt" + "io" + "log" + "net/http" + + "github.com/vishen/go-chromecast/cast" +) + +// getInfo uses the http://:8008/setup/eureka_endpoint to obtain more +// information about the cast-device. +// OBS: The 8008 seems to be pure http, whereas 8009 is typically the port +// to use for protobuf-communication, +func GetInfo(ip string) (info *cast.DeviceInfo, err error) { + url := fmt.Sprintf("http://%v:8008/setup/eureka_info", ip) + log.Printf("Fetching: %s", url) + resp, err := http.Get(url) + if err != nil { + return nil, err + } + defer resp.Body.Close() + data, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + info = new(cast.DeviceInfo) + if err := json.Unmarshal(data, info); err != nil { + return nil, err + } + return info, nil +} diff --git a/cast/connection.go b/cast/connection.go index 258b871..9f06f8a 100644 --- a/cast/connection.go +++ b/cast/connection.go @@ -31,6 +31,7 @@ type Conn interface { Close() error SetDebug(debug bool) LocalAddr() (addr string, err error) + RemoteAddr() (addr string, err error) Send(requestID int, payload Payload, sourceID, destinationID, namespace string) error } @@ -89,6 +90,11 @@ func (c *Connection) LocalAddr() (addr string, err error) { return host, err } +func (c *Connection) RemoteAddr() (addr string, err error) { + host, _, err := net.SplitHostPort(c.conn.RemoteAddr().String()) + return host, err +} + func (c *Connection) log(message string, args ...interface{}) { if c.debug { log.WithField("package", "cast").Debugf(message, args...) diff --git a/cast/payload.go b/cast/payload.go index ca02446..76e9591 100644 --- a/cast/payload.go +++ b/cast/payload.go @@ -156,3 +156,13 @@ type SetVolume struct { PayloadHeader Volume Volume `json:"volume"` } + +type DeviceInfo struct { + Name string `json:"name"` + IpAddress string `json:"ip_address"` + Locale string `json:"locale"` + MacAddress string `json:"mac_address"` + Ssid string `json:"ssid"` + Timezone string `json:"timezone"` + UptimeSec float64 `json:"uptime"` +} diff --git a/cmd/scan.go b/cmd/scan.go index b3bf4d8..160ce08 100644 --- a/cmd/scan.go +++ b/cmd/scan.go @@ -20,38 +20,11 @@ import ( "sync" "time" - "encoding/json" "github.com/seancfoley/ipaddress-go/ipaddr" "github.com/spf13/cobra" - "io" - "net/http" + "github.com/vishen/go-chromecast/application" ) -type deviceInfo struct { - Name string -} - -// getInfo uses the http://:8008/setup/eureka_endpoint to obtain more -// information about the cast-device. -// OBS: The 8008 seems to be pure http, whereas 8009 is typically the port -// to use for protobuf-communication -func getInfo(ip *ipaddr.IPAddress) (info *deviceInfo, err error) { - resp, err := http.Get(fmt.Sprintf("http://%v:8008/setup/eureka_info", ip)) - if err != nil { - return nil, err - } - defer resp.Body.Close() - data, err := io.ReadAll(resp.Body) - if err != nil { - return nil, err - } - info = new(deviceInfo) - if err := json.Unmarshal(data, info); err != nil { - return nil, err - } - return info, nil -} - // scanCmd triggers a scan var scanCmd = &cobra.Command{ Use: "scan", @@ -98,7 +71,7 @@ var scanCmd = &cobra.Command{ continue } conn.Close() - if info, err := getInfo(ip); err != nil { + if info, err := application.GetInfo(ip.String()); err != nil { outputInfo(" - Device at %v:%d errored during discovery: %v", ip, port, err) } else { outputInfo(" - '%v' at %v:%d\n", info.Name, ip, port) diff --git a/http/handlers.go b/http/handlers.go index 9c132d1..95ec2ba 100644 --- a/http/handlers.go +++ b/http/handlers.go @@ -10,6 +10,8 @@ import ( "sync" "time" + "golang.org/x/sync/errgroup" + log "github.com/sirupsen/logrus" "github.com/vishen/go-chromecast/application" "github.com/vishen/go-chromecast/dns" @@ -348,7 +350,15 @@ func (h *Handler) status(w http.ResponseWriter, r *http.Request) { h.log("status for device") castApplication, castMedia, castVolume := app.Status() + info, err := app.Info() + if err != nil { + werr := fmt.Errorf("error getting device info: %v", err) + h.log("%v", werr) + httpError(w, werr) + return + } statusResponse := fromApplicationStatus( + info, castApplication, castMedia, castVolume, @@ -366,20 +376,26 @@ func (h *Handler) statuses(w http.ResponseWriter, r *http.Request) { h.log("statuses for devices") uuids := h.ConnectedDeviceUUIDs() mapUUID2Ch := map[string]chan statusResponse{} - + g := new(errgroup.Group) for _, deviceUUID := range uuids { app, ok := h.app(deviceUUID) if ok { ch := make(chan statusResponse, 1) mapUUID2Ch[deviceUUID] = ch - go func() { + g.Go(func() error { castApplication, castMedia, castVolume := app.Status() + info, err := app.Info() + if err != nil { + return fmt.Errorf("error getting device info: %v", err) + } ch <- fromApplicationStatus( + info, castApplication, castMedia, castVolume, ) - }() + return nil + }) } } diff --git a/http/types.go b/http/types.go index 3a68b7e..234dde5 100644 --- a/http/types.go +++ b/http/types.go @@ -12,6 +12,8 @@ type volumeResponse struct { } type statusResponse struct { + Info *cast.DeviceInfo `json:"info,omitempty"` + AppID string `json:"app_id"` DisplayName string `json:"display_name"` IsIdleScreen bool `json:"is_idle_screen"` @@ -44,9 +46,13 @@ type statusResponse struct { PlayerStateId int `json:"player_state_id"` } -func fromApplicationStatus(app *cast.Application, media *cast.Media, volume *cast.Volume) statusResponse { +func fromApplicationStatus(info *cast.DeviceInfo, app *cast.Application, media *cast.Media, volume *cast.Volume) statusResponse { status := statusResponse{} + if info != nil { + status.Info = info + } + if app != nil { status.AppID = app.AppId status.DisplayName = app.DisplayName