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.
This commit is contained in:
404errordeveloper 2026-10-06 00:25:35 +02:00
commit 2fa8f2435f
121 changed files with 17802 additions and 0 deletions

View file

@ -0,0 +1,130 @@
//go:build live
// Live tests hit the real origin and need this module's local module.yaml.
// They are excluded from the normal test run; enable with:
//
// go -C apps/modules test -tags live ./rte/ -v
package rte
import (
"regexp"
"strconv"
"strings"
"testing"
"drmdecryption/mpd"
)
// The rewriter must publish a usable manifest from the KID alone — this is the
// path taken when the ad-stitched upstream session has expired (HTTP 410) and all
// that is left is the key from an earlier capture.
func TestLiveRewriteFromKIDOnly(t *testing.T) {
cfg, res, err := loadConfig()
if err != nil {
t.Fatal(err)
}
if !res.Loaded {
t.Skipf("no local values at %s", res.Path)
}
if len(cfg.KIDs) == 0 {
t.Skip("no kids configured")
}
for channel, kid := range cfg.KIDs {
t.Run(channel, func(t *testing.T) {
resetCaches(channel)
r := NewRewriterWith(cfg)
// Empty upstream = expired session; only the KID hint is available.
out, err := r.Rewrite(nil, kid)
if err != nil {
t.Fatalf("rewrite from KID: %v", err)
}
got := string(out)
origin := cfg.Origins[channel]
if origin == "" {
t.Fatalf("no origin configured for %s", channel)
}
for _, want := range []string{
`type="dynamic"`,
origin + cfg.Synthetic.SegmentPathSuffix,
channel + "-" + cfg.Synthetic.VideoName,
channel + "-" + cfg.Synthetic.AudioName,
} {
if !strings.Contains(got, want) {
t.Errorf("manifest missing %q", want)
}
}
u, _ := mpd.KIDToUUID(kid)
if !strings.Contains(got, `cenc:default_KID="`+u+`"`) {
t.Errorf("manifest does not carry KID %s", u)
}
// The timeline must be non-empty and on the encoder grid, or the
// downloader asks the origin for segments that do not exist.
re := regexp.MustCompile(`<S t="(\d+)" d="(\d+)" r="(\d+)"/>`)
ms := re.FindAllStringSubmatch(got, -1)
if len(ms) != 2 {
t.Fatalf("want 2 timelines, got %d", len(ms))
}
grid, err := mpd.LearnGrid(channel, origin, cfg.Synthetic.GridSpec())
if err != nil {
t.Fatalf("learn grid: %v", err)
}
for i, m := range ms {
start, _ := strconv.ParseInt(m[1], 10, 64)
dur, _ := strconv.ParseInt(m[2], 10, 64)
rep, _ := strconv.Atoi(m[3])
wantDur, mod := grid.VDur, grid.VMod
if i == 1 {
wantDur, mod = grid.ADur, grid.AMod
}
if dur != wantDur {
t.Errorf("timeline %d: d=%d, want %d (from the live origin)", i, dur, wantDur)
}
if int64(rep+1) != cfg.Synthetic.WindowSegments {
t.Errorf("timeline %d: %d segments, want %d", i, rep+1, cfg.Synthetic.WindowSegments)
}
if off := ((start-mod)%wantDur + wantDur) % wantDur; off != 0 {
t.Errorf("timeline %d: start %d is off the encoder grid by %d", i, start, off)
}
if start <= 0 {
t.Errorf("timeline %d: non-positive start %d", i, start)
}
}
t.Logf("%s: %d bytes, vdur=%d adur=%d", channel, len(out), grid.VDur, grid.ADur)
})
}
}
// The KID→channel→origin lookup is what makes the expired-session path work.
func TestLiveKIDResolvesToConfiguredOrigin(t *testing.T) {
cfg, res, err := loadConfig()
if err != nil {
t.Fatal(err)
}
if !res.Loaded {
t.Skipf("no local values at %s", res.Path)
}
r := NewRewriterWith(cfg)
for channel, kid := range cfg.KIDs {
gotCh, gotBase, ok := r.ResolveKID(mpd.KIDHex(kid))
if !ok {
t.Errorf("%s: KID %s did not resolve", channel, kid)
continue
}
if gotCh != channel {
t.Errorf("KID %s resolved to %q, want %q", kid, gotCh, channel)
}
if gotBase != cfg.Origins[channel] {
t.Errorf("%s: origin %q, want %q", channel, gotBase, cfg.Origins[channel])
}
}
}
func resetCaches(channel string) {
// The grid/anchor caches live in pkg/mpd and are keyed by channel; a fresh
// process per test run is enough, so nothing to do here beyond documenting it.
_ = channel
}