mirror of
https://github.com/yangjian102621/geekai.git
synced 2026-08-21 06:57:12 +00:00
9ccff4efbc
- Sync backend and frontend from GeekAI Plus v4.3.0 - Remove commercial License flows and update open-source deployment defaults - Preserve Docker Compose deployment and bump image tags to v4.3.0 BREAKING CHANGE: commercial License configuration and related endpoints are removed
198 lines
5.4 KiB
Go
198 lines
5.4 KiB
Go
package adapters
|
|
|
|
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
// * Copyright 2023 The Geek-AI Authors. All rights reserved.
|
|
// * Use of this source code is governed by a Apache-2.0 license
|
|
// * that can be found in the LICENSE file.
|
|
// * @Author yangjian102621@163.com
|
|
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"geekai/core/types"
|
|
"io"
|
|
"time"
|
|
|
|
"github.com/imroc/req/v3"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// LumaAdapter Luma 视频生成适配器
|
|
type LumaAdapter struct {
|
|
db *gorm.DB
|
|
httpClient *req.Client
|
|
}
|
|
|
|
// NewLumaAdapter 创建 Luma 适配器
|
|
func NewLumaAdapter(db *gorm.DB) *LumaAdapter {
|
|
return &LumaAdapter{
|
|
db: db,
|
|
httpClient: req.C().SetTimeout(time.Minute * 3),
|
|
}
|
|
}
|
|
|
|
// GetProvider 获取服务提供商名称
|
|
func (a *LumaAdapter) GetProvider() string {
|
|
return "luma"
|
|
}
|
|
|
|
// LumaCreateRequest Luma 创建任务请求
|
|
type LumaCreateRequest struct {
|
|
ModelName string `json:"model_name"`
|
|
UserPrompt string `json:"user_prompt"`
|
|
ExpandPrompt bool `json:"expand_prompt,omitempty"`
|
|
Loop bool `json:"loop,omitempty"`
|
|
ImageURL string `json:"image_url,omitempty"` // 图生视频
|
|
ImageEndURL string `json:"image_end_url,omitempty"` // 图生视频
|
|
Duration string `json:"duration,omitempty"` // 视频时长
|
|
Resolution string `json:"resolution,omitempty"` // 视频分辨率
|
|
}
|
|
|
|
// LumaCreateResponse Luma 创建任务响应
|
|
type LumaCreateResponse struct {
|
|
Id string `json:"id"`
|
|
Prompt string `json:"prompt"`
|
|
State string `json:"state"`
|
|
CreatedAt string `json:"created_at"`
|
|
Channel string `json:"channel,omitempty"`
|
|
}
|
|
|
|
// LumaQueryResponse Luma 查询任务响应
|
|
type LumaQueryResponse struct {
|
|
Id string `json:"id"`
|
|
State string `json:"state"`
|
|
Video struct {
|
|
URL string `json:"url"`
|
|
Width int `json:"width"`
|
|
Height int `json:"height"`
|
|
Thumbnail string `json:"thumbnail"`
|
|
DownloadURL string `json:"download_url"`
|
|
} `json:"video"`
|
|
Prompt string `json:"prompt"`
|
|
Thumbnail struct {
|
|
URL string `json:"url"`
|
|
Width int `json:"width"`
|
|
Height int `json:"height"`
|
|
} `json:"thumbnail"`
|
|
}
|
|
|
|
// CreateTask 创建视频生成任务
|
|
func (a *LumaAdapter) CreateTask(task types.VideoTask, videoConfig *types.VideoConfig) (CreateTaskResponse, error) {
|
|
// 解析任务参数
|
|
paramsMap, ok := task.Params.(map[string]any)
|
|
if !ok {
|
|
return CreateTaskResponse{}, errors.New("invalid params type for Luma video task")
|
|
}
|
|
|
|
// 构建请求参数
|
|
reqBody := LumaCreateRequest{
|
|
UserPrompt: task.Prompt,
|
|
}
|
|
|
|
// 从 params 中提取参数
|
|
if expandPrompt, ok := paramsMap["expand_prompt"].(bool); ok {
|
|
reqBody.ExpandPrompt = expandPrompt
|
|
}
|
|
if model, ok := paramsMap["model"].(string); ok {
|
|
reqBody.ModelName = model
|
|
}
|
|
if loop, ok := paramsMap["loop"].(bool); ok {
|
|
reqBody.Loop = loop
|
|
}
|
|
if imageURL, ok := paramsMap["image_url"].(string); ok {
|
|
reqBody.ImageURL = imageURL
|
|
}
|
|
if imageEndURL, ok := paramsMap["image_end_url"].(string); ok {
|
|
reqBody.ImageEndURL = imageEndURL
|
|
}
|
|
if duration, ok := paramsMap["duration"].(string); ok {
|
|
reqBody.Duration = duration
|
|
}
|
|
if resolution, ok := paramsMap["resolution"].(string); ok {
|
|
reqBody.Resolution = resolution
|
|
}
|
|
// 发送请求
|
|
apiURL := fmt.Sprintf("%s/luma/generations", videoConfig.ApiURL)
|
|
r, err := a.httpClient.R().
|
|
SetHeader("Authorization", "Bearer "+videoConfig.ApiKey).
|
|
SetBody(reqBody).
|
|
Post(apiURL)
|
|
|
|
if err != nil {
|
|
return CreateTaskResponse{}, fmt.Errorf("请求 API 出错:%v", err)
|
|
}
|
|
|
|
if r.StatusCode != 200 && r.StatusCode != 201 {
|
|
body, _ := io.ReadAll(r.Body)
|
|
return CreateTaskResponse{}, fmt.Errorf("请求 API 出错:%d, %s", r.StatusCode, string(body))
|
|
}
|
|
|
|
body, _ := io.ReadAll(r.Body)
|
|
var res LumaCreateResponse
|
|
err = json.Unmarshal(body, &res)
|
|
if err != nil {
|
|
return CreateTaskResponse{}, fmt.Errorf("解析API数据失败:%v, %s", err, string(body))
|
|
}
|
|
|
|
return CreateTaskResponse{
|
|
TaskId: res.Id,
|
|
Channel: videoConfig.ApiURL,
|
|
Prompt: res.Prompt,
|
|
State: types.VideoStatusPending,
|
|
CreatedAt: res.CreatedAt,
|
|
}, nil
|
|
}
|
|
|
|
// QueryTask 查询任务状态
|
|
func (a *LumaAdapter) QueryTask(taskId string, channel string, videoConfig *types.VideoConfig) (QueryTaskResponse, error) {
|
|
apiURL := fmt.Sprintf("%s/luma/generations/%s", videoConfig.ApiURL, taskId)
|
|
r, err := a.httpClient.R().
|
|
SetHeader("Authorization", "Bearer "+videoConfig.ApiKey).
|
|
Get(apiURL)
|
|
|
|
if err != nil {
|
|
return QueryTaskResponse{}, fmt.Errorf("请求 API 失败:%v", err)
|
|
}
|
|
defer r.Body.Close()
|
|
|
|
if r.StatusCode != 200 {
|
|
body, _ := io.ReadAll(r.Body)
|
|
return QueryTaskResponse{}, fmt.Errorf("API 返回失败:%d, %s", r.StatusCode, string(body))
|
|
}
|
|
|
|
body, _ := io.ReadAll(r.Body)
|
|
var res LumaQueryResponse
|
|
err = json.Unmarshal(body, &res)
|
|
if err != nil {
|
|
return QueryTaskResponse{}, fmt.Errorf("解析API数据失败:%v, %s", err, string(body))
|
|
}
|
|
|
|
switch res.State {
|
|
case "completed", "succeed", "success":
|
|
res.State = types.VideoStatusSuccess
|
|
case "in_progress", "running":
|
|
res.State = types.VideoStatusInProgress
|
|
case "failed":
|
|
res.State = types.VideoStatusFailed
|
|
default:
|
|
res.State = types.VideoStatusPending
|
|
}
|
|
|
|
// 构建响应
|
|
response := QueryTaskResponse{
|
|
TaskId: res.Id,
|
|
Status: res.State,
|
|
VideoURL: res.Video.DownloadURL,
|
|
Prompt: res.Prompt,
|
|
}
|
|
|
|
// 如果有原始数据,转换为 JSON 字符串
|
|
if len(body) > 0 {
|
|
response.Output = string(body)
|
|
}
|
|
|
|
return response, nil
|
|
}
|