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

151
apps/modules/tg4/catalog.go Normal file
View file

@ -0,0 +1,151 @@
package tg4
import (
"encoding/json"
"fmt"
"net/http"
"strings"
appreg "drmdecryption/app"
"drmdecryption/brightcove"
"drmdecryption/session"
)
// playbackConfig is the app's remote config blob. It is decoded generically: the
// field names a channel uses come from module.yaml (token_field / stream_id_field),
// so a renamed or added field needs no code change.
type playbackConfig map[string]any
func fetchPlaybackConfig(url string) (playbackConfig, error) {
if url == "" {
return nil, fmt.Errorf("playback_config_url required (set it in module.yaml or --%s.playback-config-url)", Name)
}
resp, err := http.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return nil, fmt.Errorf("playback config HTTP %d", resp.StatusCode)
}
var pc playbackConfig
if err := json.NewDecoder(resp.Body).Decode(&pc); err != nil {
return nil, err
}
return pc, nil
}
// str reads a string field, tolerating numbers.
func (pc playbackConfig) str(field string) string {
if field == "" {
return ""
}
switch v := pc[field].(type) {
case string:
return v
case float64:
return fmt.Sprintf("%.0f", v)
case json.Number:
return v.String()
default:
return ""
}
}
// Flags reports the boolean switches in the playback config, for `--list`.
func (pc playbackConfig) Flags() map[string]bool {
out := map[string]bool{}
for k, v := range pc {
if b, ok := v.(bool); ok {
out[k] = b
}
}
return out
}
// resolveChannel returns the channel's label, Brightcove video id and playback
// token, consulting the remote playback config for anything not pinned locally.
func (a *App) resolveChannel(pc playbackConfig, id string) (label, videoID, token string, err error) {
ch, ok := a.cfg.Channels[id]
if !ok {
return "", "", "", fmt.Errorf("unknown channel %q", id)
}
label = a.Label(id)
videoID = ch.VideoID
if videoID == "" {
videoID = pc.str(ch.StreamIDField)
}
token = pc.str(ch.TokenField)
if videoID == "" {
if ch.PhoneOnly {
return "", "", "", fmt.Errorf("channel %q has no catalog entry — capture it from the phone: drm capture --app %s --channel %s", id, Name, id)
}
return "", "", "", fmt.Errorf("no video id for channel %q (set video_id or stream_id_field in %s)", id, a.ConfigPath())
}
if token == "" && ch.TokenField != "" {
return label, videoID, "", fmt.Errorf("no playback token for channel %q (field %s)", id, ch.TokenField)
}
return label, videoID, token, nil
}
// Resolve implements app.Catalog: build streamd-ready credentials for a channel
// without touching a phone.
func (a *App) Resolve(channel string) (session.Stream, error) {
id, err := a.Base.Resolve(channel)
if err != nil {
return session.Stream{}, err
}
pc, err := fetchPlaybackConfig(a.cfg.PlaybackConfigURL)
if err != nil {
return session.Stream{}, err
}
label, videoID, token, err := a.resolveChannel(pc, id)
if err != nil {
return session.Stream{}, err
}
vid, err := brightcove.FetchPlayback(a.cfg.AccountID, videoID, token, a.cfg.PolicyKey)
if err != nil {
return session.Stream{}, err
}
hls, license := brightcove.PickHLSAndLicense(vid)
if hls == "" {
return session.Stream{}, fmt.Errorf("no HLS source in playback response for %s", videoID)
}
title := label
if vid.Name != "" {
title = vid.Name
}
return session.Stream{
Name: a.cfg.StreamNamePrefix + "-" + strings.ToLower(id),
Title: title,
App: Name,
Channel: strings.ToLower(id),
MPD: hls,
HeadersJSON: "{}",
Rewriter: "none",
LicenseURL: license,
AccountID: a.cfg.AccountID,
VideoID: videoID,
PlaybackURL: brightcove.PlaybackURL(a.cfg.AccountID, videoID),
ManifestType: "hls",
}, nil
}
// ListChannels resolves each configured channel's video id for `catalog --list`.
func (a *App) ListChannels() ([]appreg.CatalogRow, map[string]bool, error) {
pc, err := fetchPlaybackConfig(a.cfg.PlaybackConfigURL)
if err != nil {
return nil, nil, err
}
rows := make([]appreg.CatalogRow, 0, len(a.cfg.Channels))
for _, id := range a.ChannelIDs() {
ch := a.cfg.Channels[id]
vid := ch.VideoID
if vid == "" {
vid = pc.str(ch.StreamIDField)
}
rows = append(rows, appreg.CatalogRow{ID: id, CatalogID: vid, Label: a.Label(id)})
}
return rows, pc.Flags(), nil
}