This repository has been archived on 2026-02-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
breakpilot-pwa/admin-v2/ai-compliance-sdk/internal/academy/heygen_client.go
Benjamin Admin 70f2b0ae64 refactor: Consolidate standalone services into admin-v2, add new SDK modules
Remove standalone services (ai-compliance-sdk root, developer-portal,
dsms-gateway, dsms-node, night-scheduler) and legacy compliance/dsgvo pages.
Add new SDK pipeline modules (academy, document-crawler, dsb-portal,
incidents, whistleblower, reporting, sso, multi-tenant, industry-templates).
Add drafting engine, legal corpus files (AT/CH/DE), pitch-deck,
blog and Förderantrag pages.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-15 09:05:18 +01:00

185 lines
4.4 KiB
Go

package academy
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"time"
)
// HeyGenClient handles avatar video generation via the HeyGen API
type HeyGenClient struct {
apiKey string
avatarID string
client *http.Client
}
// NewHeyGenClient creates a new HeyGen client
func NewHeyGenClient() *HeyGenClient {
apiKey := os.Getenv("HEYGEN_API_KEY")
avatarID := os.Getenv("HEYGEN_AVATAR_ID")
if avatarID == "" {
avatarID = "josh_lite3_20230714" // Default avatar
}
return &HeyGenClient{
apiKey: apiKey,
avatarID: avatarID,
client: &http.Client{
Timeout: 300 * time.Second, // Video generation can take time
},
}
}
// IsConfigured returns true if API key is set
func (c *HeyGenClient) IsConfigured() bool {
return c.apiKey != ""
}
// CreateVideoRequest represents the HeyGen API request
type CreateVideoRequest struct {
VideoInputs []VideoInput `json:"video_inputs"`
Dimension Dimension `json:"dimension"`
}
// VideoInput represents a single video segment
type VideoInput struct {
Character Character `json:"character"`
Voice VideoVoice `json:"voice"`
}
// Character represents the avatar
type Character struct {
Type string `json:"type"`
AvatarID string `json:"avatar_id"`
}
// VideoVoice represents the voice/audio source
type VideoVoice struct {
Type string `json:"type"` // "audio" for pre-generated audio
AudioURL string `json:"audio_url,omitempty"`
InputText string `json:"input_text,omitempty"`
}
// Dimension represents video dimensions
type Dimension struct {
Width int `json:"width"`
Height int `json:"height"`
}
// CreateVideoResponse represents the HeyGen API response
type CreateVideoResponse struct {
Data struct {
VideoID string `json:"video_id"`
} `json:"data"`
Error interface{} `json:"error"`
}
// HeyGenVideoStatus represents video status from HeyGen
type HeyGenVideoStatus struct {
Data struct {
Status string `json:"status"` // processing, completed, failed
VideoURL string `json:"video_url"`
} `json:"data"`
}
// CreateVideo creates a video with the avatar and audio
func (c *HeyGenClient) CreateVideo(audioURL string) (*CreateVideoResponse, error) {
if !c.IsConfigured() {
return nil, fmt.Errorf("HeyGen API key not configured")
}
url := "https://api.heygen.com/v2/video/generate"
reqBody := CreateVideoRequest{
VideoInputs: []VideoInput{
{
Character: Character{
Type: "avatar",
AvatarID: c.avatarID,
},
Voice: VideoVoice{
Type: "audio",
AudioURL: audioURL,
},
},
},
Dimension: Dimension{
Width: 1920,
Height: 1080,
},
}
jsonBody, err := json.Marshal(reqBody)
if err != nil {
return nil, fmt.Errorf("failed to marshal request: %w", err)
}
req, err := http.NewRequest("POST", url, bytes.NewReader(jsonBody))
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Api-Key", c.apiKey)
resp, err := c.client.Do(req)
if err != nil {
return nil, fmt.Errorf("HeyGen API request failed: %w", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response: %w", err)
}
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
return nil, fmt.Errorf("HeyGen API error %d: %s", resp.StatusCode, string(body))
}
var result CreateVideoResponse
if err := json.Unmarshal(body, &result); err != nil {
return nil, fmt.Errorf("failed to parse response: %w", err)
}
return &result, nil
}
// GetVideoStatus checks the status of a video generation job
func (c *HeyGenClient) GetVideoStatus(videoID string) (*HeyGenVideoStatus, error) {
if !c.IsConfigured() {
return nil, fmt.Errorf("HeyGen API key not configured")
}
url := fmt.Sprintf("https://api.heygen.com/v1/video_status.get?video_id=%s", videoID)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
req.Header.Set("X-Api-Key", c.apiKey)
resp, err := c.client.Do(req)
if err != nil {
return nil, fmt.Errorf("HeyGen API request failed: %w", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response: %w", err)
}
var status HeyGenVideoStatus
if err := json.Unmarshal(body, &status); err != nil {
return nil, fmt.Errorf("failed to parse response: %w", err)
}
return &status, nil
}