Skip to content

Commit

Permalink
feat: Add config to hardcode device list
Browse files Browse the repository at this point in the history
This is mainly useful for environments where discovery does not work.
  • Loading branch information
gabe565 committed Dec 31, 2023
1 parent 6e3956c commit 2acc483
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 27 deletions.
17 changes: 9 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,14 +156,15 @@ CastSponsorSkip can be configured with envs, command-line flags, or a config fil
To use an env that is not listed here, capitalize all characters, replace `-` with `_`, and prefix with `CSS_`. For example, `--paused-interval=1m` would become `CSS_PAUSED_INTERVAL=1m`.

### Notable Envs
| Env | Description | Default |
|-------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------|
| `CSS_DISCOVER_INTERVAL` | Interval to restart the DNS discovery client. | `5m` |
| `CSS_PAUSED_INTERVAL` | Time to wait between each poll of the Cast device status when paused. | `1m` |
| `CSS_PLAYING_INTERVAL` | Time to wait between each poll of the Cast device status when playing. | `500ms` |
| `CSS_CATEGORIES` | Comma-separated list of SponsorBlock categories to skip, see [category list](https://wiki.sponsor.ajay.app/w/Types#Category). | `sponsor` |
| `CSS_YOUTUBE_API_KEY` | [YouTube API key](https://developers.google.com/youtube/registering_an_application) for fallback video identification (required on some Chromecast devices). | ` ` |
| `CSS_MUTE_ADS` | Mutes the device while an ad is playing. | `true` |
| Env | Description | Default |
|-------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------|
| `CSS_DISCOVER_INTERVAL` | Interval to restart the DNS discovery client. | `5m` |
| `CSS_PAUSED_INTERVAL` | Time to wait between each poll of the Cast device status when paused. | `1m` |
| `CSS_PLAYING_INTERVAL` | Time to wait between each poll of the Cast device status when playing. | `500ms` |
| `CSS_CATEGORIES` | Comma-separated list of SponsorBlock categories to skip, see [category list](https://wiki.sponsor.ajay.app/w/Types#Category). | `sponsor` |
| `CSS_YOUTUBE_API_KEY` | [YouTube API key](https://developers.google.com/youtube/registering_an_application) for fallback video identification (required on some Chromecast devices). | ` ` |
| `CSS_MUTE_ADS` | Mutes the device while an ad is playing. | `true` |
| `CSS_DEVICES` | Comma-separated list of device addresses. This will disable discovery and is not recommended unless discovery fails. | `[]` |

> **Note**
> [sponsorblockcast envs](https://github.com/nichobi/sponsorblockcast#configuration) are also supported to simplify the migration to CastSponsorSkip. When used, a deprecation warning will be logged with an updated env key and value. There are currently no plans to remove these envs.
Expand Down
1 change: 1 addition & 0 deletions docs/castsponsorskip.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ castsponsorskip [flags]
--action-types strings SponsorBlock action types to handle. Shorter segments that overlap with content can be muted instead of skipped. (default [skip,mute])
-c, --categories strings Comma-separated list of SponsorBlock categories to skip (default [sponsor])
--completion string Output command-line completion code for the specified shell. Can be 'bash', 'zsh', 'fish', or 'powershell'.
--devices strings Comma-separated list of device addresses. This will disable discovery and is not recommended unless discovery fails
--discover-interval duration Interval to restart the DNS discovery client (default 5m0s)
-h, --help help for castsponsorskip
--log-level string Log level (debug, info, warn, error) (default "info")
Expand Down
47 changes: 43 additions & 4 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ package config
import (
"fmt"
"net"
"net/url"
"strconv"
"strings"
"time"

"github.com/spf13/cobra"
"github.com/spf13/viper"
castdns "github.com/vishen/go-chromecast/dns"
)

var Default Config
Expand Down Expand Up @@ -41,10 +44,12 @@ type Config struct {

LogLevel string `mapstructure:"log-level"`

DiscoverInterval time.Duration `mapstructure:"discover-interval"`
PausedInterval time.Duration `mapstructure:"paused-interval"`
PlayingInterval time.Duration `mapstructure:"playing-interval"`
SkipDelay time.Duration `mapstructure:"skip-delay"`
DeviceAddrStrs []string `mapstructure:"devices"`
DeviceAddrs []castdns.CastEntry `mapstructure:"-"`
DiscoverInterval time.Duration `mapstructure:"discover-interval"`
PausedInterval time.Duration `mapstructure:"paused-interval"`
PlayingInterval time.Duration `mapstructure:"playing-interval"`
SkipDelay time.Duration `mapstructure:"skip-delay"`

NetworkInterfaceName string `mapstructure:"network-interface"`
NetworkInterface *net.Interface
Expand All @@ -59,6 +64,7 @@ type Config struct {

func (c *Config) RegisterFlags(cmd *cobra.Command) {
c.viper = viper.New()
c.RegisterDevices(cmd)
c.RegisterLogLevel(cmd)
c.RegisterNetworkInterface(cmd)
c.RegisterDiscoverInterval(cmd)
Expand Down Expand Up @@ -111,5 +117,38 @@ func (c *Config) Load() error {
c.ActionTypes[i] = strings.TrimSpace(actionType)
}

if len(c.DeviceAddrStrs) != 0 {
c.DeviceAddrs = make([]castdns.CastEntry, 0, len(c.DeviceAddrStrs))
for _, device := range c.DeviceAddrStrs {
u := url.URL{Host: device}

castEntry := castdns.CastEntry{
DeviceName: device,
UUID: device,
}

if port := u.Port(); port == "" {
castEntry.Port = 8009
} else {
port, err := strconv.ParseUint(port, 10, 16)
if err != nil {
return err
}

castEntry.Port = int(port)
}

if ip := net.ParseIP(u.Hostname()); ip == nil {
return fmt.Errorf("failed to parse IP %q", device)
} else if ip.To4() != nil {
castEntry.AddrV4 = ip
} else {
castEntry.AddrV6 = ip
}

c.DeviceAddrs = append(c.DeviceAddrs, castEntry)
}
}

return nil
}
13 changes: 13 additions & 0 deletions internal/config/device.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package config

import (
"github.com/spf13/cobra"
)

func (c *Config) RegisterDevices(cmd *cobra.Command) {
key := "devices"
cmd.PersistentFlags().StringSlice(key, Default.DeviceAddrStrs, "Comma-separated list of device addresses. This will disable discovery and is not recommended unless discovery fails")
if err := c.viper.BindPFlag(key, cmd.PersistentFlags().Lookup(key)); err != nil {
panic(err)
}
}
54 changes: 39 additions & 15 deletions internal/device/dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,26 +55,50 @@ func DiscoverCastDNSEntries(ctx context.Context, iface *net.Interface, ch chan c
}

func BeginDiscover(ctx context.Context) (<-chan castdns.CastEntry, error) {
if config.Default.NetworkInterface != nil {
slog.Info("Searching for devices...", "interface", config.Default.NetworkInterfaceName)
} else {
slog.Info("Searching for devices...")
}

ch := make(chan castdns.CastEntry)
ctx, cancel := context.WithCancel(ctx)

go func() {
defer func() {
close(ch)
}()
defer close(ch)
defer cancel()

for {
if ctx.Err() != nil {
return
if len(config.Default.DeviceAddrs) == 0 {
if config.Default.NetworkInterface != nil {
slog.Info("Searching for devices...", "interface", config.Default.NetworkInterfaceName)
} else {
slog.Info("Searching for devices...")
}

for {
select {
case <-ctx.Done():
return
default:
if err := DiscoverCastDNSEntries(ctx, config.Default.NetworkInterface, ch); err != nil {
slog.Error("Failed to discover devices.", "error", err.Error())
continue
}
}
}
} else {
if config.Default.NetworkInterface != nil {
slog.Info("Connecting to configured devices...", "interface", config.Default.NetworkInterfaceName)
} else {
slog.Info("Connecting to configured devices...")
}

if err := DiscoverCastDNSEntries(ctx, config.Default.NetworkInterface, ch); err != nil {
slog.Error("Failed to discover devices.", "error", err.Error())
continue
timer := time.NewTimer(0)
defer timer.Stop()
for {
select {
case <-ctx.Done():
return
case <-timer.C:
for _, castEntry := range config.Default.DeviceAddrs {
ch <- castEntry
}
timer.Reset(config.Default.DiscoverInterval)
}
}
}
}()
Expand Down

0 comments on commit 2acc483

Please sign in to comment.