package session import ( "encoding/json" "fmt" "os" "path/filepath" "strings" "time" ) // Session is the on-disk capture result (shared with restream tooling). type Session struct { App string `json:"app"` Channel string `json:"channel,omitempty"` CapturedAt string `json:"captured_at"` PSSH string `json:"pssh,omitempty"` Auth string `json:"auth,omitempty"` PID string `json:"pid,omitempty"` Key string `json:"key,omitempty"` KID string `json:"kid,omitempty"` KeyHex string `json:"key_hex,omitempty"` MPD string `json:"mpd,omitempty"` LicenseURL string `json:"license_url,omitempty"` } // Write creates outputs///session.json (+ field txt files + latest). func Write(root, app string, s Session) (string, error) { stamp := time.Now().Format("20060102-150405") dir := filepath.Join(root, "outputs", app, stamp) if err := os.MkdirAll(dir, 0o755); err != nil { return "", err } s.App = app if s.CapturedAt == "" { s.CapturedAt = time.Now().Format(time.RFC3339) } if s.Key != "" && s.KID == "" { parts := strings.SplitN(s.Key, ":", 2) if len(parts) == 2 { s.KID = parts[0] s.KeyHex = parts[1] } } raw, err := json.MarshalIndent(s, "", " ") if err != nil { return "", err } path := filepath.Join(dir, "session.json") if err := os.WriteFile(path, append(raw, '\n'), 0o644); err != nil { return "", err } fields := map[string]string{ "pssh": s.PSSH, "auth": s.Auth, "pid": s.PID, "key": s.Key, "mpd": s.MPD, } for name, val := range fields { if val == "" { continue } _ = os.WriteFile(filepath.Join(dir, name+".txt"), []byte(val+"\n"), 0o644) } latest := filepath.Join(root, "outputs", app, "latest") _ = os.RemoveAll(latest) // Best-effort directory copy for Windows (symlinks often need admin). if err := copyDir(dir, latest); err != nil { fmt.Fprintf(os.Stderr, "[!] latest link/copy: %v\n", err) } return path, nil } func copyDir(src, dst string) error { if err := os.MkdirAll(dst, 0o755); err != nil { return err } entries, err := os.ReadDir(src) if err != nil { return err } for _, e := range entries { in := filepath.Join(src, e.Name()) out := filepath.Join(dst, e.Name()) if e.IsDir() { if err := copyDir(in, out); err != nil { return err } continue } b, err := os.ReadFile(in) if err != nil { return err } if err := os.WriteFile(out, b, 0o644); err != nil { return err } } return nil }