|
- package maas_seedance_sdk
-
- import (
- "bytes"
- "encoding/json"
- "fmt"
- "net/http"
- "os"
- "strings"
-
- "maas_seedance_sdk_1.0.0_go/volcengine_aicc_sdk"
- "maas_seedance_sdk_1.0.0_go/volcengine_aicc_sdk/common/log"
- )
-
- const (
- mappingQueryPath = "/mapping/query"
- )
-
- // MaasSeedanceClient is the top-level client for MaaS Seedance video generation.
- type MaasSeedanceClient struct {
- volcClient *volcengine_aicc_sdk.SeedanceClient
- maasModel string // the resolved endpoint returned by /mapping/query, used as model in request body
- serviceVersion string // service version, passed as service-version header
- enableVideoEncrypt bool
- logger *log.Logger
- }
-
- // NewMaasSeedanceClient creates a new MaasSeedanceClient.
- //
- // maasBaseURL: the MaaS gateway base URL
- // maasAPIKey: the API key for authentication
- // maasModel: the virtual model name (resolved via /mapping/query)
- // enableVideoEncrypt: whether to enable video file encryption
- func NewMaasSeedanceClient(maasBaseURL, maasAPIKey, maasModel string, enableVideoEncrypt bool, opts ...MaasSeedanceClientOption) (*MaasSeedanceClient, error) {
- client := &MaasSeedanceClient{
- enableVideoEncrypt: enableVideoEncrypt,
- logger: log.NewLogger(),
- }
-
- for _, opt := range opts {
- opt(client)
- }
-
- // Resolve actual endpoint: the endpoint from /mapping/query is used as the model field in the request body
- resolvedModel := client.resolveActualModel(maasBaseURL, maasAPIKey, maasModel)
- client.maasModel = resolvedModel
-
- seedanceOpts := []volcengine_aicc_sdk.SeedanceClientOption{
- volcengine_aicc_sdk.WithSeedanceBaseURL(maasBaseURL),
- volcengine_aicc_sdk.WithSeedanceAPIKey(maasAPIKey),
- volcengine_aicc_sdk.WithSeedanceEndpoint(resolvedModel),
- volcengine_aicc_sdk.WithSeedanceIsSecure(true),
- volcengine_aicc_sdk.WithSeedanceIsEnableVideoEncrypt(enableVideoEncrypt),
- volcengine_aicc_sdk.WithSeedanceTimeout(120.0),
- volcengine_aicc_sdk.WithSeedanceVideoFileStorageLocation("TOS"),
- }
-
- client.volcClient = volcengine_aicc_sdk.NewSeedanceClient(seedanceOpts...)
-
- client.logger.Info("[MaasSeedanceClient] initialized, baseUrl: %s, originalModel: %s, actualModel: %s, enableVideoEncrypt: %v, serviceVersion: %s",
- maasBaseURL, maasModel, resolvedModel, enableVideoEncrypt, client.serviceVersion)
-
- return client, nil
- }
-
- func (c *MaasSeedanceClient) resolveActualModel(baseURL, apiKey, model string) string {
- cleanBaseURL := strings.TrimRight(baseURL, "/")
- mappingURL := cleanBaseURL + mappingQueryPath
-
- c.logger.Info("[MaasSeedanceClient] Querying model mapping: url=%s, model=%s", mappingURL, model)
-
- requestBody := map[string]interface{}{
- "model": model,
- }
-
- jsonBody, err := json.Marshal(requestBody)
- if err != nil {
- c.logger.Warning("[MaasSeedanceClient] Failed to marshal mapping query request: %v", err)
- return model
- }
-
- httpClient := &http.Client{}
- req, err := http.NewRequest("POST", mappingURL, bytes.NewBuffer(jsonBody))
- if err != nil {
- c.logger.Warning("[MaasSeedanceClient] Failed to create mapping query request: %v", err)
- return model
- }
-
- req.Header.Set("Content-Type", "application/json")
- req.Header.Set("Accept", "application/json")
- req.Header.Set("Authorization", "Bearer "+apiKey)
-
- if c.serviceVersion != "" {
- req.Header.Set("service-version", c.serviceVersion)
- }
-
- resp, err := httpClient.Do(req)
- if err != nil {
- c.logger.Warning("[MaasSeedanceClient] Failed to query model mapping, using original model: %s. Error: %v", model, err)
- return model
- }
- defer resp.Body.Close()
-
- var result map[string]interface{}
- if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
- c.logger.Warning("[MaasSeedanceClient] Failed to decode mapping response, using original model: %s", model)
- return model
- }
-
- c.logger.Info("[MaasSeedanceClient] Mapping query response: status=%d, body=%v", resp.StatusCode, result)
-
- if resp.StatusCode == 200 {
- if endpoint, ok := result["endpoint"].(string); ok && endpoint != "" {
- c.logger.Info("[MaasSeedanceClient] Resolved model mapping: %s -> %s", model, endpoint)
- return endpoint
- }
- c.logger.Warning("[MaasSeedanceClient] No endpoint found in mapping response for model: %s, using original model", model)
- } else {
- c.logger.Warning("[MaasSeedanceClient] Mapping query failed with status: %d, using original model: %s", resp.StatusCode, model)
- }
-
- return model
- }
-
- // SetVideoFileEncryptKey sets or generates RSA key pair for video file encryption.
- func (c *MaasSeedanceClient) SetVideoFileEncryptKey(publicKeyPath, privateKeyPath string) error {
- if !c.enableVideoEncrypt {
- c.logger.Warning("[MaasSeedanceClient] Current mode is plaintext, no need to set video encrypt key")
- return nil
- }
-
- if _, err := os.Stat(publicKeyPath); os.IsNotExist(err) {
- if _, err := os.Stat(privateKeyPath); os.IsNotExist(err) {
- c.logger.Info("[MaasSeedanceClient] Generating RSA key pair...")
- if err := c.volcClient.GenerateVideoFileEncryptKey(publicKeyPath, privateKeyPath); err != nil {
- return fmt.Errorf("failed to generate RSA key pair: %w", err)
- }
- c.logger.Info("[MaasSeedanceClient] RSA key pair generated successfully")
- }
- } else {
- c.logger.Info("[MaasSeedanceClient] Loading existing RSA key pair from: %s", publicKeyPath)
- }
-
- if err := c.volcClient.SetVideoFileEncryptKey(publicKeyPath, privateKeyPath); err != nil {
- return fmt.Errorf("failed to set video file encrypt key: %w", err)
- }
-
- return nil
- }
-
- // CreateVideoGenerationTask creates a video generation task.
- func (c *MaasSeedanceClient) CreateVideoGenerationTask(data map[string]interface{}) (string, error) {
- data["model"] = c.maasModel
- c.logger.Info("[MaasSeedanceClient] Request body after adding model: %v", data)
-
- headers := make(map[string]string)
- if c.hasVideoInput(data) {
- headers["Input-Has-Video"] = "true"
- }
- if c.serviceVersion != "" {
- headers["service-version"] = c.serviceVersion
- }
-
- c.logger.Info("[MaasSeedanceClient] Creating video generation task, Input-Has-Video: %v, service-version: %s",
- headers["Input-Has-Video"] != "", c.serviceVersion)
-
- taskID, err := c.volcClient.CreateVideoGenerationTask(data, headers)
- if err != nil {
- return "", err
- }
-
- c.logger.Info("[MaasSeedanceClient] Task created successfully, taskId: %s", taskID)
- return taskID, nil
- }
-
- // QueryVideoGenerationTask queries the status of a video generation task.
- func (c *MaasSeedanceClient) QueryVideoGenerationTask(taskID string) (map[string]interface{}, error) {
- c.logger.Info("[MaasSeedanceClient] Querying video generation task: %s", taskID)
-
- result, err := c.volcClient.QueryVideoGenerationTask(taskID)
- if err != nil {
- return nil, err
- }
-
- if result != nil {
- if status, ok := result["status"].(string); ok {
- c.logger.Info("[MaasSeedanceClient] Task status: %s", status)
- }
- }
-
- return result, nil
- }
-
- // QueryVideoGenerationTaskList queries the list of video generation tasks.
- func (c *MaasSeedanceClient) QueryVideoGenerationTaskList(pageNum, pageSize int, status string, taskIDs []string, startTime, endTime string) (map[string]interface{}, error) {
- c.logger.Info("[MaasSeedanceClient] Querying video generation task list - pageNum: %d, pageSize: %d, status: %s",
- pageNum, pageSize, status)
-
- opts := volcengine_aicc_sdk.QueryVideoGenerationTaskListOptions{
- PageNum: pageNum,
- PageSize: pageSize,
- Status: status,
- TaskIDs: taskIDs,
- }
-
- return c.volcClient.QueryVideoGenerationTaskList(opts)
- }
-
- // DownloadVideo downloads the video file for a completed task.
- func (c *MaasSeedanceClient) DownloadVideo(taskID, localFilePath string) (bool, error) {
- c.logger.Info("[MaasSeedanceClient] Downloading video: taskId=%s, localPath=%s", taskID, localFilePath)
-
- success, err := c.volcClient.VideoFileDownload(taskID, localFilePath)
- if err != nil {
- return false, err
- }
-
- if success {
- c.logger.Info("[MaasSeedanceClient] Video downloaded successfully to: %s", localFilePath)
- } else {
- c.logger.Warning("[MaasSeedanceClient] Video download failed for taskId: %s", taskID)
- }
-
- return success, nil
- }
-
- // DeleteVideoGenerationTask deletes a video generation task.
- func (c *MaasSeedanceClient) DeleteVideoGenerationTask(taskID string) (bool, error) {
- c.logger.Info("[MaasSeedanceClient] Deleting video generation task: %s", taskID)
-
- result, err := c.volcClient.DeleteVideoGenerationTask(taskID)
- if err != nil {
- return false, err
- }
-
- return result != nil, nil
- }
-
- func (c *MaasSeedanceClient) hasVideoInput(data map[string]interface{}) bool {
- content, ok := data["content"]
- if !ok {
- return false
- }
-
- contentList, ok := content.([]interface{})
- if !ok {
- return false
- }
-
- for _, item := range contentList {
- part, ok := item.(map[string]interface{})
- if !ok {
- continue
- }
-
- if partType, ok := part["type"].(string); ok && partType == "video_url" {
- return true
- }
- }
-
- return false
- }
|