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.
118 lines
2.8 KiB
Go
118 lines
2.8 KiB
Go
package client
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type Client struct {
|
|
BaseURL string
|
|
Token string
|
|
HTTP *http.Client
|
|
UserAgent string
|
|
}
|
|
|
|
func New(baseURL, token string) *Client {
|
|
return &Client{
|
|
BaseURL: strings.TrimRight(baseURL, "/"),
|
|
Token: token,
|
|
HTTP: &http.Client{Timeout: 30 * time.Second},
|
|
UserAgent: "drm-agent/1.0",
|
|
}
|
|
}
|
|
|
|
type Stream struct {
|
|
ID int64 `json:"id"`
|
|
Name string `json:"name"`
|
|
Title string `json:"title"`
|
|
App string `json:"app"`
|
|
Channel string `json:"channel"`
|
|
MPD string `json:"mpd"`
|
|
Key string `json:"key"`
|
|
Enabled bool `json:"enabled"`
|
|
Health string `json:"health"`
|
|
LastError string `json:"last_error"`
|
|
PlaylistAgeS float64 `json:"playlist_age_s"`
|
|
ClaimedBy string `json:"claimed_by"`
|
|
}
|
|
|
|
type Credentials struct {
|
|
MPD string `json:"mpd"`
|
|
Key string `json:"key"`
|
|
PSSH string `json:"pssh,omitempty"`
|
|
Auth string `json:"auth,omitempty"`
|
|
PID string `json:"pid,omitempty"`
|
|
}
|
|
|
|
func (c *Client) Health() error {
|
|
_, err := c.do(http.MethodGet, "/api/health", nil)
|
|
return err
|
|
}
|
|
|
|
func (c *Client) ListStreams() ([]Stream, error) {
|
|
body, err := c.do(http.MethodGet, "/api/streams", nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var wrap struct {
|
|
Streams []Stream `json:"streams"`
|
|
}
|
|
if err := json.Unmarshal(body, &wrap); err != nil {
|
|
return nil, err
|
|
}
|
|
return wrap.Streams, nil
|
|
}
|
|
|
|
func (c *Client) Claim(id int64, agentID string, ttlSec int) error {
|
|
payload := map[string]any{"agent_id": agentID, "ttl_sec": ttlSec}
|
|
_, err := c.do(http.MethodPost, fmt.Sprintf("/api/streams/%d/claim", id), payload)
|
|
return err
|
|
}
|
|
|
|
func (c *Client) ReleaseClaim(id int64, agentID string) error {
|
|
payload := map[string]any{"agent_id": agentID}
|
|
_, err := c.do(http.MethodPost, fmt.Sprintf("/api/streams/%d/claim/release", id), payload)
|
|
return err
|
|
}
|
|
|
|
func (c *Client) PostCredentials(id int64, cred Credentials) error {
|
|
_, err := c.do(http.MethodPost, fmt.Sprintf("/api/streams/%d/credentials", id), cred)
|
|
return err
|
|
}
|
|
|
|
func (c *Client) do(method, path string, payload any) ([]byte, error) {
|
|
var rdr io.Reader
|
|
if payload != nil {
|
|
b, err := json.Marshal(payload)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
rdr = bytes.NewReader(b)
|
|
}
|
|
req, err := http.NewRequest(method, c.BaseURL+path, rdr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if payload != nil {
|
|
req.Header.Set("Content-Type", "application/json")
|
|
}
|
|
req.Header.Set("User-Agent", c.UserAgent)
|
|
if c.Token != "" {
|
|
req.Header.Set("Authorization", "Bearer "+c.Token)
|
|
}
|
|
resp, err := c.HTTP.Do(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
body, _ := io.ReadAll(resp.Body)
|
|
if resp.StatusCode >= 300 {
|
|
return nil, fmt.Errorf("%s %s: %s — %s", method, path, resp.Status, strings.TrimSpace(string(body)))
|
|
}
|
|
return body, nil
|
|
}
|