From ba871fd44c2a240eee5f401b2612d8aba1ab9bfb Mon Sep 17 00:00:00 2001 From: Jonathan Pentecost Date: Sat, 9 May 2020 22:04:05 -0400 Subject: [PATCH] all: add ApplicationOption's and a way to get cast dns by name Added ApplicationOption's which can be used to configure the application. Changed to use a variable for 'connectionRetries' instead of the hardcoded 5. Can also be configured with the ApplicationOption. New method in 'dns' to quickly and easily get a cast dns entry by name. --- application/application.go | 65 +++++++++++++++++++++++++++++--------- cast/connection.go | 3 +- cmd/utils.go | 8 ++++- dns/dns.go | 16 ++++++++++ 4 files changed, 74 insertions(+), 18 deletions(-) diff --git a/application/application.go b/application/application.go index b31ea4c..5873ec2 100644 --- a/application/application.go +++ b/application/application.go @@ -90,25 +90,56 @@ type Application struct { playedItems map[string]PlayedItem cacheDisabled bool cache *storage.Storage + + // Number of connection retries to try before returning + // and error. + connectionRetries int +} + +type ApplicationOption func(*Application) + +func WithIface(iface *net.Interface) ApplicationOption { + return func(a *Application) { + a.iface = iface + } } -func NewApplication(iface *net.Interface, debug, cacheDisabled bool) *Application { - // TODO(vishen): make cast.Connection an interface, most likely will just need - // the Send method - // Channel to receive messages from the cast connecttion. 5 is a randomly - // chosen number. +func WithDebug(debug bool) ApplicationOption { + return func(a *Application) { + a.debug = debug + a.conn.SetDebug(debug) + } +} + +func WithCacheDisabled(cacheDisabled bool) ApplicationOption { + return func(a *Application) { + a.cacheDisabled = cacheDisabled + } +} + +func WithConnectionRetries(connectionRetries int) ApplicationOption { + return func(a *Application) { + a.connectionRetries = connectionRetries + } +} + +func NewApplication(opts ...ApplicationOption) *Application { recvMsgChan := make(chan *pb.CastMessage, 5) a := &Application{ - recvMsgChan: recvMsgChan, - resultChanMap: map[int]chan *pb.CastMessage{}, - messageChan: make(chan *pb.CastMessage), - conn: cast.NewConnection(recvMsgChan, debug), - debug: debug, - cacheDisabled: cacheDisabled, - playedItems: map[string]PlayedItem{}, - cache: storage.NewStorage(), - iface: iface, + recvMsgChan: recvMsgChan, + resultChanMap: map[int]chan *pb.CastMessage{}, + messageChan: make(chan *pb.CastMessage), + conn: cast.NewConnection(recvMsgChan), + playedItems: map[string]PlayedItem{}, + cache: storage.NewStorage(), + connectionRetries: 5, + } + + // Apply options + for _, o := range opts { + o(a) } + // Kick off the listener for asynchronous messages received from the // cast connection. go a.recvMessages() @@ -119,6 +150,7 @@ func NewApplication(iface *net.Interface, debug, cacheDisabled bool) *Applicatio func (a *Application) Application() *cast.Application { return a.application } func (a *Application) Media() *cast.Media { return a.media } +func (a *Application) Volume() *cast.Volume { return a.volumeReceiver } func (a *Application) AddMessageFunc(f CastMessageFunc) { a.messageMu.Lock() @@ -236,7 +268,10 @@ func (a *Application) Update() error { var err error // Simple retry. We need this for when the device isn't currently // available, but it is likely that it will come up soon. - for i := 0; i < 5; i++ { + // TODO: This seems to happen when changing media on the cast device, + // not sure how to fix but there might be some way of knowing from the + // payload? + for i := 0; i < a.connectionRetries; i++ { recvStatus, err = a.getReceiverStatus() if err == nil { break diff --git a/cast/connection.go b/cast/connection.go index d3b289f..2e32b74 100644 --- a/cast/connection.go +++ b/cast/connection.go @@ -32,10 +32,9 @@ type Connection struct { connected bool } -func NewConnection(recvMsgChan chan *pb.CastMessage, debug bool) *Connection { +func NewConnection(recvMsgChan chan *pb.CastMessage) *Connection { c := &Connection{ recvMsgChan: recvMsgChan, - debug: debug, connected: false, } return c diff --git a/cmd/utils.go b/cmd/utils.go index a988d54..cfa48b2 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -65,6 +65,11 @@ func castApplication(cmd *cobra.Command, args []string) (*application.Applicatio dnsTimeoutSeconds, _ := cmd.Flags().GetInt("dns-timeout") useFirstDevice, _ := cmd.Flags().GetBool("first") + applicationOptions := []application.ApplicationOption{ + application.WithDebug(debug), + application.WithCacheDisabled(disableCache), + } + // If we need to look on a specific network interface for mdns or // for finding a network ip to host from, ensure that the network // interface exists. @@ -74,6 +79,7 @@ func castApplication(cmd *cobra.Command, args []string) (*application.Applicatio if iface, err = net.InterfaceByName(ifaceName); err != nil { return nil, errors.Wrap(err, fmt.Sprintf("unable to find interface %q", ifaceName)) } + applicationOptions = append(applicationOptions, application.WithIface(iface)) } var entry castdns.CastDNSEntry @@ -116,7 +122,7 @@ func castApplication(cmd *cobra.Command, args []string) (*application.Applicatio Port: p, } } - app := application.NewApplication(iface, debug, disableCache) + app := application.NewApplication(applicationOptions...) if err := app.Start(entry); err != nil { // NOTE: currently we delete the dns cache every time we get // an error, this is to make sure that if the device gets a new diff --git a/dns/dns.go b/dns/dns.go index ca70af5..9788c7b 100644 --- a/dns/dns.go +++ b/dns/dns.go @@ -54,6 +54,22 @@ func (e CastEntry) GetPort() int { return e.Port } +// DiscoverCastDNSEntryByName returns the first cast dns device +// found that matches the name. +func DiscoverCastDNSEntryByName(ctx context.Context, iface *net.Interface, name string) (CastEntry, error) { + castEntryChan, err := DiscoverCastDNSEntries(ctx, iface) + if err != nil { + return CastEntry{}, err + } + + for d := range castEntryChan { + if d.DeviceName == name { + return d, nil + } + } + return CastEntry{}, fmt.Errorf("No cast device found with name %q", name) +} + // DiscoverCastDNSEntries will return a channel with any cast dns entries // found. func DiscoverCastDNSEntries(ctx context.Context, iface *net.Interface) (<-chan CastEntry, error) {