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.
128 lines
3 KiB
Go
128 lines
3 KiB
Go
package wvkey
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
// Options for the Python wvkey.py helper.
|
|
type Options struct {
|
|
Python string // e.g. .venv/Scripts/python.exe
|
|
Script string // path to wvkey.py
|
|
WVD string
|
|
PSSH string
|
|
Auth string
|
|
PID string
|
|
LicenseURL string
|
|
UserAgent string
|
|
}
|
|
|
|
// Fetch runs wvkey.py --quiet (ModularDrm) and returns the first KID:KEY line.
|
|
func Fetch(opt Options) (string, error) {
|
|
lines, err := run(opt, false, false)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if len(lines) == 0 {
|
|
return "", fmt.Errorf("wvkey produced no KID:KEY")
|
|
}
|
|
return lines[0], nil
|
|
}
|
|
|
|
// FetchRaw runs wvkey.py --mode raw (Brightcove / octet-stream license).
|
|
func FetchRaw(opt Options) (string, error) {
|
|
lines, err := run(opt, true, false)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if len(lines) == 0 {
|
|
return "", fmt.Errorf("wvkey produced no KID:KEY")
|
|
}
|
|
return lines[0], nil
|
|
}
|
|
|
|
// FetchRawAll returns every CONTENT key from a raw license response.
|
|
func FetchRawAll(opt Options) ([]string, error) {
|
|
return run(opt, true, true)
|
|
}
|
|
|
|
func run(opt Options, raw, all bool) ([]string, error) {
|
|
py := opt.Python
|
|
if py == "" {
|
|
py = findPython()
|
|
}
|
|
script := opt.Script
|
|
if script == "" {
|
|
return nil, fmt.Errorf("wvkey script path required")
|
|
}
|
|
if strings.TrimSpace(opt.WVD) == "" {
|
|
return nil, fmt.Errorf("wvkey --wvd path required")
|
|
}
|
|
if strings.TrimSpace(opt.PSSH) == "" {
|
|
return nil, fmt.Errorf("wvkey --pssh required")
|
|
}
|
|
if strings.TrimSpace(opt.LicenseURL) == "" {
|
|
return nil, fmt.Errorf("wvkey --license-url required")
|
|
}
|
|
args := []string{
|
|
script,
|
|
"--wvd", opt.WVD,
|
|
"--pssh", opt.PSSH,
|
|
"--license-url", opt.LicenseURL,
|
|
"--quiet",
|
|
}
|
|
if raw {
|
|
args = append(args, "--mode", "raw")
|
|
} else {
|
|
args = append(args, "--mode", "modulardrm", "--auth", opt.Auth, "--pid", opt.PID)
|
|
}
|
|
if all {
|
|
args = append(args, "--all")
|
|
}
|
|
if opt.UserAgent != "" {
|
|
args = append(args, "--user-agent", opt.UserAgent)
|
|
}
|
|
cmd := exec.Command(py, args...)
|
|
var stdout, stderr bytes.Buffer
|
|
cmd.Stdout = &stdout
|
|
cmd.Stderr = &stderr
|
|
if err := cmd.Run(); err != nil {
|
|
return nil, fmt.Errorf("wvkey failed: %w\n%s", err, strings.TrimSpace(stderr.String()))
|
|
}
|
|
var out []string
|
|
for _, line := range strings.Split(strings.TrimSpace(stdout.String()), "\n") {
|
|
line = strings.TrimSpace(line)
|
|
if strings.Count(line, ":") == 1 && len(line) > 40 {
|
|
out = append(out, line)
|
|
}
|
|
}
|
|
if len(out) == 0 {
|
|
return nil, fmt.Errorf("wvkey produced no KID:KEY (stderr=%s)", strings.TrimSpace(stderr.String()))
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func findPython() string {
|
|
candidates := []string{
|
|
filepath.Join(".venv", "Scripts", "python.exe"),
|
|
filepath.Join(".venv", "bin", "python"),
|
|
"python3",
|
|
"python",
|
|
}
|
|
for _, c := range candidates {
|
|
if filepath.IsAbs(c) || strings.Contains(c, string(os.PathSeparator)) {
|
|
if st, err := os.Stat(c); err == nil && !st.IsDir() {
|
|
return c
|
|
}
|
|
continue
|
|
}
|
|
if p, err := exec.LookPath(c); err == nil {
|
|
return p
|
|
}
|
|
}
|
|
return "python"
|
|
}
|