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 }