Skip to content

Commit

Permalink
feat: Add experimental mute ad support
Browse files Browse the repository at this point in the history
Ref #7
  • Loading branch information
gabe565 committed Aug 28, 2023
1 parent 05b4563 commit 7661c41
Show file tree
Hide file tree
Showing 6 changed files with 44 additions and 10 deletions.
17 changes: 9 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,14 +140,15 @@ systemctl enable --now castsponsorskip
## Configuration
You can configure the following parameters by setting the appropriate command line flag or environment variable:

| 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. | `1s` |
| `CSS_CATEGORIES` | Comma-separated list of SponsorBlock categories to skip, see [category list](https://github.com/ajayyy/SponsorBlock/blob/master/config.json.example). | `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_NETWORK_INTERFACE` | Optionally configure the network interface to use. | All interfaces |
| 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. | `1s` |
| `CSS_CATEGORIES` | Comma-separated list of SponsorBlock categories to skip, see [category list](https://github.com/ajayyy/SponsorBlock/blob/master/config.json.example). | `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_NETWORK_INTERFACE` | Optionally configure the network interface to use. | All interfaces |
| `CSS_MUTE_ADS` | Enables experimental support for muting unskippable ads. | `false` |

See command-line flag documentation [here](./docs/castsponsorskip.md).

Expand Down
5 changes: 5 additions & 0 deletions cmd/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ func TestFlags(t *testing.T) {
"--playing-interval=" + playingInterval.String(),
"--categories=a,b,c",
"--youtube-api-key=AIzaSyDaGmWKa4JsXZ-HjGw7ISLn_3namBGewQe",
"--mute-ads",
})
cmd.RunE = func(cmd *cobra.Command, args []string) error {
return nil
Expand All @@ -55,6 +56,7 @@ func TestFlags(t *testing.T) {
assert.Equal(t, playingInterval, config.Default.PlayingInterval)
assert.Equal(t, []string{"a", "b", "c"}, config.Default.Categories)
assert.Equal(t, "AIzaSyDaGmWKa4JsXZ-HjGw7ISLn_3namBGewQe", config.Default.YouTubeAPIKey)
assert.Equal(t, true, config.Default.MuteAds)
}

func TestEnvs(t *testing.T) {
Expand All @@ -72,6 +74,7 @@ func TestEnvs(t *testing.T) {
_ = os.Unsetenv("CSS_PLAYING_INTERVAL")
_ = os.Unsetenv("CSS_CATEGORIES")
_ = os.Unsetenv("CSS_YOUTUBE_API_KEY")
_ = os.Unsetenv("CSS_MUTE_ADS")
}()
_ = os.Setenv("CSS_LOG_LEVEL", "warn")
_ = os.Setenv("CSS_NETWORK_INTERFACE", "eno1")
Expand All @@ -80,6 +83,7 @@ func TestEnvs(t *testing.T) {
_ = os.Setenv("CSS_PLAYING_INTERVAL", playingInterval.String())
_ = os.Setenv("CSS_CATEGORIES", "a,b,c")
_ = os.Setenv("CSS_YOUTUBE_API_KEY", "AIzaSyDaGmWKa4JsXZ-HjGw7ISLn_3namBGewQe")
_ = os.Setenv("CSS_MUTE_ADS", "true")

var cmd *cobra.Command
if !assert.NotPanics(t, func() {
Expand All @@ -102,4 +106,5 @@ func TestEnvs(t *testing.T) {
assert.Equal(t, playingInterval, config.Default.PlayingInterval)
assert.Equal(t, []string{"a", "b", "c"}, config.Default.Categories)
assert.Equal(t, "AIzaSyDaGmWKa4JsXZ-HjGw7ISLn_3namBGewQe", config.Default.YouTubeAPIKey)
assert.Equal(t, true, config.Default.MuteAds)
}
1 change: 1 addition & 0 deletions docs/castsponsorskip.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ castsponsorskip [flags]
--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")
--mute-ads Enables experimental support for muting unskippable ads
-i, --network-interface string Network interface to use for multicast dns discovery
--paused-interval duration Interval to scan paused devices (default 1m0s)
--playing-interval duration Interval to scan playing devices (default 1s)
Expand Down
2 changes: 2 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type Config struct {
Categories []string

YouTubeAPIKey string `mapstructure:"youtube-api-key"`
MuteAds bool `mapstructure:"mute-ads"`
}

func (c *Config) RegisterFlags(cmd *cobra.Command) {
Expand All @@ -33,6 +34,7 @@ func (c *Config) RegisterFlags(cmd *cobra.Command) {
c.RegisterPlayingInterval(cmd)
c.RegisterCategories(cmd)
c.RegisterYouTubeAPIKey(cmd)
c.RegisterMuteAds(cmd)
}

func (c *Config) Load() error {
Expand Down
8 changes: 8 additions & 0 deletions internal/config/youtube.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,11 @@ func (c *Config) RegisterYouTubeAPIKey(cmd *cobra.Command) {
viper.SetDefault(key, env)
}
}

func (c *Config) RegisterMuteAds(cmd *cobra.Command) {
key := "mute-ads"
cmd.PersistentFlags().Bool(key, false, "Enables experimental support for muting unskippable ads")
if err := viper.BindPFlag(key, cmd.PersistentFlags().Lookup(key)); err != nil {
panic(err)
}
}
21 changes: 19 additions & 2 deletions internal/device/watch.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ func Watch(ctx context.Context, entry castdns.CastEntry) {
return
}

castApp, castMedia, _ := app.Status()
castApp, castMedia, castVol := app.Status()

if castApp == nil || castApp.DisplayName != "YouTube" || castMedia == nil {
mediaSessionId = 0
Expand Down Expand Up @@ -212,12 +212,29 @@ func Watch(ctx context.Context, entry castdns.CastEntry) {

switch castMedia.CustomData.PlayerState {
case StateAd:
logger.Info("Detected ad. Attempting to skip...")
var shouldUnmute bool
if config.Default.MuteAds && !castVol.Muted {
logger.Info("Detected ad. Muting and attempting to skip...")
if err := app.SetMuted(true); err == nil {
shouldUnmute = true
} else {
logger.Warn("Failed to mute ad.", "error", err.Error())
}
} else {
logger.Info("Detected ad. Attempting to skip...")
}

if err := app.Skipad(); err == nil {
logger.Info("Skipped ad.")
} else if !errors.Is(err, application.ErrNoMediaSkipad) {
logger.Warn("Failed to skip ad.", "error", err.Error())
}

if shouldUnmute {
if err := app.SetMuted(false); err != nil {
logger.Warn("Failed to unmute ad.", "error", err.Error())
}
}
default:
for _, segment := range segments {
if castMedia.CurrentTime > segment.Segment[0] && castMedia.CurrentTime < segment.Segment[1]-1 {
Expand Down

0 comments on commit 7661c41

Please sign in to comment.