Skip to content

Commit

Permalink
Support info (device name and other meta-information) in the status.
Browse files Browse the repository at this point in the history
Examplar result:
```
% curl "http://0.0.0.0:8011/status?uuid=edfaf976-0411-41c4-9e5a-724c8781f0bc" | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   610  100   610    0     0    288      0  0:00:02  0:00:02 --:--:--   288
{
  "info": {
    "name": "Sisters Room speaker",
    "ip_address": "192.168.0.161",
    "locale": "pl",
    "mac_address": "54:60:09:D0:10:A4",
    "ssid": "Szum",
    "timezone": "Europe/Warsaw",
    "uptime": 45394.987882
  },
  "app_id": "",
  "display_name": "",
  "is_idle_screen": false,
  "status_text": "",
  "player_state": "",
  "current_time": 0,
  "idle_reason": "",
  "current_item_id": 0,
  "loading_item_id": 0,
  "content_id": "",
  "content_type": "",
  "stream_type": "",
  "duration": 0,
  "artist": "",
  "title": "",
  "subtitle": "",
  "volume_level": 0.89,
  "volume_muted": false,
  "media_volume_level": 0,
  "media_volume_muted": false,
  "session_id": "",
  "transport_id": "",
  "media_session_id": 0,
  "player_state_id": 0
}
```
  • Loading branch information
ptabor committed Dec 1, 2024
1 parent 92f518d commit c8fe761
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 34 deletions.
15 changes: 14 additions & 1 deletion application/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
34 changes: 34 additions & 0 deletions application/info.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package application

import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"

"github.com/vishen/go-chromecast/cast"
)

// getInfo uses the http://<ip>: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
}
6 changes: 6 additions & 0 deletions cast/connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down Expand Up @@ -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...)
Expand Down
10 changes: 10 additions & 0 deletions cast/payload.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
}
31 changes: 2 additions & 29 deletions cmd/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -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://<ip>: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",
Expand Down Expand Up @@ -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)
Expand Down
22 changes: 19 additions & 3 deletions http/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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,
Expand All @@ -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
})
}
}

Expand Down
8 changes: 7 additions & 1 deletion http/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit c8fe761

Please sign in to comment.