Null-DRM-Official/apps/pkg/capture/score.go
404errordeveloper 2fa8f2435f Initial commit: Null DRM Official
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.
2026-10-06 00:25:35 +02:00

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
}