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:
commit
2fa8f2435f
121 changed files with 17802 additions and 0 deletions
147
apps/modules/provider/provider.go
Normal file
147
apps/modules/provider/provider.go
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
// Package provider holds the plumbing every app module shares: loading its local
|
||||
// values file, channel id normalization and aliasing, and the boilerplate half of
|
||||
// app.App. Module packages embed Base and add their own navigation and catalog.
|
||||
package provider
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"drmdecryption/capture"
|
||||
"drmdecryption/modcfg"
|
||||
"drmdecryption/phonecap"
|
||||
"drmdecryption/repo"
|
||||
)
|
||||
|
||||
// Common is the part of a module.yaml that every module has. Modules embed it in
|
||||
// their own config struct with `yaml:",inline"`.
|
||||
type Common struct {
|
||||
Package string `yaml:"package"`
|
||||
LicenseURL string `yaml:"license_url"`
|
||||
ProxyBin string `yaml:"proxy_bin"`
|
||||
CAHash string `yaml:"ca_hash"`
|
||||
Aliases map[string]string `yaml:"aliases"`
|
||||
Score capture.ScoreCfg `yaml:"score"`
|
||||
}
|
||||
|
||||
// Base implements the boilerplate half of app.App.
|
||||
type Base struct {
|
||||
name string
|
||||
common Common
|
||||
labels map[string]string // canonical channel id -> label
|
||||
cacheDir string
|
||||
cfg modcfg.Result
|
||||
}
|
||||
|
||||
// New builds a Base. labels maps canonical channel ids to display labels.
|
||||
func New(name string, common Common, labels map[string]string, cfg modcfg.Result) *Base {
|
||||
root := repo.Root()
|
||||
cacheDir := filepath.Join(root, ".cache", "ui-"+name)
|
||||
_ = os.MkdirAll(cacheDir, 0o755)
|
||||
return &Base{name: name, common: common, labels: labels, cacheDir: cacheDir, cfg: cfg}
|
||||
}
|
||||
|
||||
func (b *Base) Name() string { return b.name }
|
||||
func (b *Base) Package() string { return b.common.Package }
|
||||
func (b *Base) LicenseURL() string { return b.common.LicenseURL }
|
||||
func (b *Base) CacheDir() string { return b.cacheDir }
|
||||
|
||||
// ConfigPath / ConfigLoaded report where this module's values came from.
|
||||
func (b *Base) ConfigPath() string { return b.cfg.Path }
|
||||
func (b *Base) ConfigLoaded() bool { return b.cfg.Loaded }
|
||||
|
||||
// ProxyBin resolves the on-device MITM binary, relative paths against repo root.
|
||||
func (b *Base) ProxyBin() string {
|
||||
if b.common.ProxyBin != "" {
|
||||
return modcfg.Resolve(b.common.ProxyBin)
|
||||
}
|
||||
root := repo.Root()
|
||||
for _, p := range []string{
|
||||
filepath.Join(root, "bin", "proxy-android-arm64"),
|
||||
filepath.Join(root, "apps", "proxy", "proxy-android-arm64"),
|
||||
} {
|
||||
if st, err := os.Stat(p); err == nil && !st.IsDir() {
|
||||
return p
|
||||
}
|
||||
}
|
||||
return filepath.Join(root, "bin", "proxy-android-arm64")
|
||||
}
|
||||
|
||||
func (b *Base) CAHash() string {
|
||||
if b.common.CAHash == "" {
|
||||
return phonecap.DefaultCAHash
|
||||
}
|
||||
return b.common.CAHash
|
||||
}
|
||||
|
||||
// Score is this module's manifest-URL scoring, merged onto the neutral baseline.
|
||||
func (b *Base) Score() capture.ScoreCfg {
|
||||
return capture.DefaultScoreCfg().Merge(b.common.Score)
|
||||
}
|
||||
|
||||
// Hints returns device-layout hints with this module's scoring applied.
|
||||
func (b *Base) Hints(require ...string) capture.Hints {
|
||||
h := capture.DefaultHints()
|
||||
h.Require = require
|
||||
h.Score = b.Score()
|
||||
return h
|
||||
}
|
||||
|
||||
// NormalizeID folds a channel id to its comparison form.
|
||||
func NormalizeID(id string) string {
|
||||
id = strings.ToLower(strings.TrimSpace(id))
|
||||
for _, ch := range []string{"-", "_", " "} {
|
||||
id = strings.ReplaceAll(id, ch, "")
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
// Resolve maps any spelling or alias of a channel to its canonical id.
|
||||
func (b *Base) Resolve(id string) (string, error) {
|
||||
want := NormalizeID(id)
|
||||
for canon := range b.labels {
|
||||
if NormalizeID(canon) == want {
|
||||
return canon, nil
|
||||
}
|
||||
}
|
||||
for alias, canon := range b.common.Aliases {
|
||||
if NormalizeID(alias) == want {
|
||||
if _, ok := b.labels[canon]; ok {
|
||||
return canon, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
known := make([]string, 0, len(b.labels))
|
||||
for k := range b.labels {
|
||||
known = append(known, k)
|
||||
}
|
||||
return "", fmt.Errorf("unknown channel %q for %s (known: %s) — add it to %s",
|
||||
id, b.name, strings.Join(known, "|"), b.cfg.Path)
|
||||
}
|
||||
|
||||
func (b *Base) HasChannel(id string) bool {
|
||||
_, err := b.Resolve(id)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// Label returns a channel's display label, falling back to its id.
|
||||
func (b *Base) Label(canonID string) string {
|
||||
if l := b.labels[canonID]; l != "" {
|
||||
return l
|
||||
}
|
||||
return canonID
|
||||
}
|
||||
|
||||
// ChannelIDs lists canonical channel ids, sorted so listings and API responses
|
||||
// are stable (map iteration order is not).
|
||||
func (b *Base) ChannelIDs() []string {
|
||||
out := make([]string, 0, len(b.labels))
|
||||
for k := range b.labels {
|
||||
out = append(out, k)
|
||||
}
|
||||
sort.Strings(out)
|
||||
return out
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue