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.
100 lines
2.6 KiB
Go
100 lines
2.6 KiB
Go
package capture
|
|
|
|
import "strings"
|
|
|
|
// ScoreCfg ranks candidate URLs seen by the MITM so a schedule/EPG/analytics URL
|
|
// is never mistaken for a stream manifest. Providers supply their own hosts and
|
|
// noise needles; this package ships only format-level scoring.
|
|
type ScoreCfg struct {
|
|
// Deny: any match scores the URL out entirely.
|
|
Deny []string `yaml:"deny"`
|
|
// Hosts: known-good manifest hosts for this provider.
|
|
Hosts []string `yaml:"hosts"`
|
|
// HostBonus is added when a Hosts entry matches (default 50).
|
|
HostBonus int `yaml:"host_bonus"`
|
|
}
|
|
|
|
// DefaultScoreCfg is the provider-neutral baseline: catalog/analytics shapes that
|
|
// are never a manifest for anyone.
|
|
func DefaultScoreCfg() ScoreCfg {
|
|
return ScoreCfg{
|
|
Deny: []string{
|
|
"schedules", "bylistingtime", "maxlistings", "bycallsign",
|
|
"/feed.", "playback_config", "config.json",
|
|
},
|
|
HostBonus: 50,
|
|
}
|
|
}
|
|
|
|
// Merge overlays a provider's hosts and extra deny needles on the baseline.
|
|
func (s ScoreCfg) Merge(other ScoreCfg) ScoreCfg {
|
|
out := s
|
|
out.Deny = append(append([]string{}, s.Deny...), other.Deny...)
|
|
out.Hosts = append(append([]string{}, s.Hosts...), other.Hosts...)
|
|
if other.HostBonus != 0 {
|
|
out.HostBonus = other.HostBonus
|
|
}
|
|
if out.HostBonus == 0 {
|
|
out.HostBonus = 50
|
|
}
|
|
return out
|
|
}
|
|
|
|
// Score rates a URL as a live manifest. Zero or negative means "not a manifest".
|
|
func (s ScoreCfg) Score(u string) int {
|
|
lu := strings.ToLower(u)
|
|
path := lu
|
|
if i := strings.Index(path, "?"); i >= 0 {
|
|
path = path[:i]
|
|
}
|
|
for _, bad := range s.Deny {
|
|
if bad != "" && strings.Contains(lu, strings.ToLower(bad)) {
|
|
return -100
|
|
}
|
|
}
|
|
score := 0
|
|
bonus := s.HostBonus
|
|
if bonus == 0 {
|
|
bonus = 50
|
|
}
|
|
for _, host := range s.Hosts {
|
|
if host != "" && strings.Contains(lu, strings.ToLower(host)) {
|
|
score += bonus
|
|
break
|
|
}
|
|
}
|
|
// Format-level signals — true for any provider.
|
|
if strings.Contains(path, "playlist-hls") || strings.Contains(path, "playlist.m3u8") {
|
|
score += 40
|
|
}
|
|
if strings.Contains(path, "chunklist") {
|
|
score += 10
|
|
}
|
|
if strings.HasSuffix(path, ".m3u8") {
|
|
score += 5
|
|
}
|
|
if strings.HasSuffix(path, ".mpd") || strings.Contains(path, "manifest.mpd") {
|
|
score += 30
|
|
}
|
|
if strings.Contains(path, ".isml") || strings.Contains(path, "/manifest") {
|
|
score += 20
|
|
}
|
|
return score
|
|
}
|
|
|
|
// ProxyArgs renders this config as flags for the on-device MITM, which runs on
|
|
// the phone and cannot read an app module's values file itself.
|
|
func (s ScoreCfg) ProxyArgs() []string {
|
|
out := []string{}
|
|
for _, h := range s.Hosts {
|
|
if h != "" {
|
|
out = append(out, "-score-host", h)
|
|
}
|
|
}
|
|
for _, d := range s.Deny {
|
|
if d != "" {
|
|
out = append(out, "-score-deny", d)
|
|
}
|
|
}
|
|
return out
|
|
}
|