| | package controller |
| |
|
| | import ( |
| | "fmt" |
| | "io" |
| | "net/http" |
| | "net/url" |
| | "time" |
| |
|
| | "github.com/QuantumNous/new-api/constant" |
| | "github.com/QuantumNous/new-api/logger" |
| | "github.com/QuantumNous/new-api/model" |
| |
|
| | "github.com/gin-gonic/gin" |
| | ) |
| |
|
| | func VideoProxy(c *gin.Context) { |
| | taskID := c.Param("task_id") |
| | if taskID == "" { |
| | c.JSON(http.StatusBadRequest, gin.H{ |
| | "error": gin.H{ |
| | "message": "task_id is required", |
| | "type": "invalid_request_error", |
| | }, |
| | }) |
| | return |
| | } |
| |
|
| | task, exists, err := model.GetByOnlyTaskId(taskID) |
| | if err != nil { |
| | logger.LogError(c.Request.Context(), fmt.Sprintf("Failed to query task %s: %s", taskID, err.Error())) |
| | c.JSON(http.StatusInternalServerError, gin.H{ |
| | "error": gin.H{ |
| | "message": "Failed to query task", |
| | "type": "server_error", |
| | }, |
| | }) |
| | return |
| | } |
| | if !exists || task == nil { |
| | logger.LogError(c.Request.Context(), fmt.Sprintf("Failed to get task %s: %v", taskID, err)) |
| | c.JSON(http.StatusNotFound, gin.H{ |
| | "error": gin.H{ |
| | "message": "Task not found", |
| | "type": "invalid_request_error", |
| | }, |
| | }) |
| | return |
| | } |
| |
|
| | if task.Status != model.TaskStatusSuccess { |
| | c.JSON(http.StatusBadRequest, gin.H{ |
| | "error": gin.H{ |
| | "message": fmt.Sprintf("Task is not completed yet, current status: %s", task.Status), |
| | "type": "invalid_request_error", |
| | }, |
| | }) |
| | return |
| | } |
| |
|
| | channel, err := model.CacheGetChannel(task.ChannelId) |
| | if err != nil { |
| | logger.LogError(c.Request.Context(), fmt.Sprintf("Failed to get task %s: not found", taskID)) |
| | c.JSON(http.StatusInternalServerError, gin.H{ |
| | "error": gin.H{ |
| | "message": "Failed to retrieve channel information", |
| | "type": "server_error", |
| | }, |
| | }) |
| | return |
| | } |
| | baseURL := channel.GetBaseURL() |
| | if baseURL == "" { |
| | baseURL = "https://api.openai.com" |
| | } |
| |
|
| | var videoURL string |
| | client := &http.Client{ |
| | Timeout: 60 * time.Second, |
| | } |
| |
|
| | req, err := http.NewRequestWithContext(c.Request.Context(), http.MethodGet, "", nil) |
| | if err != nil { |
| | logger.LogError(c.Request.Context(), fmt.Sprintf("Failed to create request: %s", err.Error())) |
| | c.JSON(http.StatusInternalServerError, gin.H{ |
| | "error": gin.H{ |
| | "message": "Failed to create proxy request", |
| | "type": "server_error", |
| | }, |
| | }) |
| | return |
| | } |
| |
|
| | switch channel.Type { |
| | case constant.ChannelTypeGemini: |
| | apiKey := task.PrivateData.Key |
| | if apiKey == "" { |
| | logger.LogError(c.Request.Context(), fmt.Sprintf("Missing stored API key for Gemini task %s", taskID)) |
| | c.JSON(http.StatusInternalServerError, gin.H{ |
| | "error": gin.H{ |
| | "message": "API key not stored for task", |
| | "type": "server_error", |
| | }, |
| | }) |
| | return |
| | } |
| |
|
| | videoURL, err = getGeminiVideoURL(channel, task, apiKey) |
| | if err != nil { |
| | logger.LogError(c.Request.Context(), fmt.Sprintf("Failed to resolve Gemini video URL for task %s: %s", taskID, err.Error())) |
| | c.JSON(http.StatusBadGateway, gin.H{ |
| | "error": gin.H{ |
| | "message": "Failed to resolve Gemini video URL", |
| | "type": "server_error", |
| | }, |
| | }) |
| | return |
| | } |
| | req.Header.Set("x-goog-api-key", apiKey) |
| | case constant.ChannelTypeAli: |
| | |
| | videoURL = task.FailReason |
| | default: |
| | |
| | videoURL = fmt.Sprintf("%s/v1/videos/%s/content", baseURL, task.TaskID) |
| | req.Header.Set("Authorization", "Bearer "+channel.Key) |
| | } |
| |
|
| | req.URL, err = url.Parse(videoURL) |
| | if err != nil { |
| | logger.LogError(c.Request.Context(), fmt.Sprintf("Failed to parse URL %s: %s", videoURL, err.Error())) |
| | c.JSON(http.StatusInternalServerError, gin.H{ |
| | "error": gin.H{ |
| | "message": "Failed to create proxy request", |
| | "type": "server_error", |
| | }, |
| | }) |
| | return |
| | } |
| |
|
| | resp, err := client.Do(req) |
| | if err != nil { |
| | logger.LogError(c.Request.Context(), fmt.Sprintf("Failed to fetch video from %s: %s", videoURL, err.Error())) |
| | c.JSON(http.StatusBadGateway, gin.H{ |
| | "error": gin.H{ |
| | "message": "Failed to fetch video content", |
| | "type": "server_error", |
| | }, |
| | }) |
| | return |
| | } |
| | defer resp.Body.Close() |
| |
|
| | if resp.StatusCode != http.StatusOK { |
| | logger.LogError(c.Request.Context(), fmt.Sprintf("Upstream returned status %d for %s", resp.StatusCode, videoURL)) |
| | c.JSON(http.StatusBadGateway, gin.H{ |
| | "error": gin.H{ |
| | "message": fmt.Sprintf("Upstream service returned status %d", resp.StatusCode), |
| | "type": "server_error", |
| | }, |
| | }) |
| | return |
| | } |
| |
|
| | for key, values := range resp.Header { |
| | for _, value := range values { |
| | c.Writer.Header().Add(key, value) |
| | } |
| | } |
| |
|
| | c.Writer.Header().Set("Cache-Control", "public, max-age=86400") |
| | c.Writer.WriteHeader(resp.StatusCode) |
| | _, err = io.Copy(c.Writer, resp.Body) |
| | if err != nil { |
| | logger.LogError(c.Request.Context(), fmt.Sprintf("Failed to stream video content: %s", err.Error())) |
| | } |
| | } |
| |
|