Capture, decrypt, and restream toolkit with compiled-in app modules (RTE, TG4, BBC), on-device MITM proxy, streamd control plane, and www. BBC module.yaml is published (clear streams); other module values stay local.
172 lines
6.1 KiB
Go
172 lines
6.1 KiB
Go
package tg4
|
|
|
|
import (
|
|
"time"
|
|
|
|
"drmdecryption/modcfg"
|
|
"drmdecryption/modules/provider"
|
|
"drmdecryption/uiflow"
|
|
)
|
|
|
|
// Name is the module id (--app tg4).
|
|
const Name = "tg4"
|
|
|
|
// Channel is one live channel. VideoID may be empty when the id must be read from
|
|
// the remote playback config instead.
|
|
type Channel struct {
|
|
Label string `yaml:"label"`
|
|
// VideoID is the Brightcove video id for this live.
|
|
VideoID string `yaml:"video_id"`
|
|
// TokenField names the playback-config field holding this live's playback token.
|
|
TokenField string `yaml:"token_field"`
|
|
// StreamIDField names the playback-config field holding this live's video id,
|
|
// used when VideoID is empty.
|
|
StreamIDField string `yaml:"stream_id_field"`
|
|
// LiveCardIndex is this channel's position in the app's Live card list.
|
|
LiveCardIndex int `yaml:"live_card_index"`
|
|
// PhoneOnly marks a channel with no catalog entry — capture via the phone UI.
|
|
PhoneOnly bool `yaml:"phone_only"`
|
|
}
|
|
|
|
// Config is apps/modules/tg4/module.yaml.
|
|
type Config struct {
|
|
provider.Common `yaml:",inline"`
|
|
|
|
// Brightcove account credentials. Secrets: never defaulted in code.
|
|
AccountID string `yaml:"account_id"`
|
|
PolicyKey string `yaml:"policy_key"`
|
|
PlaybackConfigURL string `yaml:"playback_config_url"`
|
|
|
|
Channels map[string]Channel `yaml:"channels"`
|
|
|
|
// StreamNamePrefix prefixes generated stream names (default: module name).
|
|
StreamNamePrefix string `yaml:"stream_name_prefix"`
|
|
|
|
Cards uiflow.CardOpts `yaml:"cards"`
|
|
UI UI `yaml:"ui"`
|
|
Timeouts Timeouts `yaml:"timeouts"`
|
|
}
|
|
|
|
// UI holds the markers and fallbacks the Live-page navigation uses.
|
|
type UI struct {
|
|
// LaunchDescContains: any of these content-descriptions means the app is up.
|
|
LaunchDescContains []string `yaml:"launch_desc_contains"`
|
|
// LaunchResourceContains: any of these view ids means the app is up. More
|
|
// stable than descriptions, which are localized.
|
|
LaunchResourceContains []string `yaml:"launch_resource_contains"`
|
|
// LiveLabel is the menu/title text identifying the Live page.
|
|
LiveLabel string `yaml:"live_label"`
|
|
// LiveTitleResource marks the Live page title node.
|
|
LiveTitleResource string `yaml:"live_title_resource"`
|
|
// DrawerDesc are the content-descriptions of the drawer (hamburger) button.
|
|
DrawerDesc []string `yaml:"drawer_desc"`
|
|
// DrawerFallbackX/Y is tapped when the drawer button cannot be identified.
|
|
DrawerFallbackX int `yaml:"drawer_fallback_x"`
|
|
DrawerFallbackY int `yaml:"drawer_fallback_y"`
|
|
// MenuMaxX / MenuMinY / MenuMaxY bound where a drawer menu entry can sit.
|
|
MenuMaxX int `yaml:"menu_max_x"`
|
|
MenuMinY int `yaml:"menu_min_y"`
|
|
MenuMaxY int `yaml:"menu_max_y"`
|
|
// TitleMaxY bounds where the Live page title can sit.
|
|
TitleMaxY int `yaml:"title_max_y"`
|
|
// PlaybackDescContains / PlaybackResourceContains are player-up signals.
|
|
PlaybackDescContains []string `yaml:"playback_desc_contains"`
|
|
PlaybackResourceContains []string `yaml:"playback_resource_contains"`
|
|
}
|
|
|
|
// Timeouts for the launch / autoplay sequence. Written as "25s" in module.yaml.
|
|
type Timeouts struct {
|
|
LaunchWaitUI provider.Duration `yaml:"launch_wait_ui"`
|
|
LaunchSettle provider.Duration `yaml:"launch_settle"`
|
|
LiveNav provider.Duration `yaml:"live_nav"`
|
|
Card provider.Duration `yaml:"card"`
|
|
AfterCard provider.Duration `yaml:"after_card"`
|
|
Playback provider.Duration `yaml:"playback"`
|
|
AfterLiveTap provider.Duration `yaml:"after_live_tap"`
|
|
AfterDrawer provider.Duration `yaml:"after_drawer"`
|
|
}
|
|
|
|
func (c Config) withDefaults() Config {
|
|
if c.StreamNamePrefix == "" {
|
|
c.StreamNamePrefix = Name
|
|
}
|
|
c.Cards = c.Cards.WithDefaults()
|
|
if len(c.Cards.DescDeny) == 0 {
|
|
c.Cards.DescDeny = []string{"Search", "Profile", "Cast", "Open", "Closed"}
|
|
}
|
|
if len(c.Cards.TextDeny) == 0 {
|
|
c.Cards.TextDeny = []string{"catch-up"}
|
|
}
|
|
u := &c.UI
|
|
if len(u.LaunchDescContains) == 0 && len(u.LaunchResourceContains) == 0 {
|
|
u.LaunchDescContains = []string{"live", "home"}
|
|
}
|
|
if u.LiveLabel == "" {
|
|
u.LiveLabel = "live"
|
|
}
|
|
if u.LiveTitleResource == "" {
|
|
u.LiveTitleResource = "live_fragment_text_view"
|
|
}
|
|
if len(u.DrawerDesc) == 0 {
|
|
u.DrawerDesc = []string{"Open", "Closed"}
|
|
}
|
|
if u.DrawerFallbackX == 0 {
|
|
u.DrawerFallbackX = 68
|
|
}
|
|
if u.DrawerFallbackY == 0 {
|
|
u.DrawerFallbackY = 183
|
|
}
|
|
if u.MenuMaxX == 0 {
|
|
u.MenuMaxX = 700
|
|
}
|
|
if u.MenuMinY == 0 {
|
|
u.MenuMinY = 250
|
|
}
|
|
if u.MenuMaxY == 0 {
|
|
u.MenuMaxY = 700
|
|
}
|
|
if u.TitleMaxY == 0 {
|
|
u.TitleMaxY = 400
|
|
}
|
|
if len(u.PlaybackDescContains) == 0 {
|
|
u.PlaybackDescContains = []string{"video player"}
|
|
}
|
|
if len(u.PlaybackResourceContains) == 0 {
|
|
u.PlaybackResourceContains = []string{"brightcove_video_view"}
|
|
}
|
|
t := &c.Timeouts
|
|
t.LaunchWaitUI = provider.Duration(t.LaunchWaitUI.D(30 * time.Second))
|
|
t.LaunchSettle = provider.Duration(t.LaunchSettle.D(5 * time.Second))
|
|
t.LiveNav = provider.Duration(t.LiveNav.D(25 * time.Second))
|
|
t.Card = provider.Duration(t.Card.D(25 * time.Second))
|
|
t.AfterCard = provider.Duration(t.AfterCard.D(3 * time.Second))
|
|
t.Playback = provider.Duration(t.Playback.D(60 * time.Second))
|
|
t.AfterLiveTap = provider.Duration(t.AfterLiveTap.D(3 * time.Second))
|
|
t.AfterDrawer = provider.Duration(t.AfterDrawer.D(1200 * time.Millisecond))
|
|
return c
|
|
}
|
|
|
|
// flag overrides, bound before any app is constructed.
|
|
var flags struct {
|
|
packageName string
|
|
accountID string
|
|
policyKey string
|
|
playbackConfigURL string
|
|
caHash string
|
|
proxyBin string
|
|
}
|
|
|
|
func loadConfig() (Config, modcfg.Result, error) {
|
|
var cfg Config
|
|
res, err := modcfg.Load(Name, &cfg)
|
|
if err != nil {
|
|
return cfg, res, err
|
|
}
|
|
cfg.Package = modcfg.Override(Name, "package", flags.packageName, cfg.Package)
|
|
cfg.CAHash = modcfg.Override(Name, "ca_hash", flags.caHash, cfg.CAHash)
|
|
cfg.ProxyBin = modcfg.Override(Name, "proxy_bin", flags.proxyBin, cfg.ProxyBin)
|
|
cfg.AccountID = modcfg.Override(Name, "account_id", flags.accountID, cfg.AccountID)
|
|
cfg.PolicyKey = modcfg.Override(Name, "policy_key", flags.policyKey, cfg.PolicyKey)
|
|
cfg.PlaybackConfigURL = modcfg.Override(Name, "playback_config_url", flags.playbackConfigURL, cfg.PlaybackConfigURL)
|
|
return cfg.withDefaults(), res, nil
|
|
}
|