| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138 |
- package controller
- import (
- "context"
- "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/QuantumNous/new-api/service"
- "github.com/gin-gonic/gin"
- )
- // videoProxyError returns a standardized OpenAI-style error response.
- func videoProxyError(c *gin.Context, status int, errType, message string) {
- c.JSON(status, gin.H{
- "error": gin.H{
- "message": message,
- "type": errType,
- },
- })
- }
- func VideoProxy(c *gin.Context) {
- taskID := c.Param("task_id")
- if taskID == "" {
- videoProxyError(c, http.StatusBadRequest, "invalid_request_error", "task_id is required")
- 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()))
- videoProxyError(c, http.StatusInternalServerError, "server_error", "Failed to query task")
- return
- }
- if !exists || task == nil {
- videoProxyError(c, http.StatusNotFound, "invalid_request_error", "Task not found")
- return
- }
- if task.Status != model.TaskStatusSuccess {
- videoProxyError(c, http.StatusBadRequest, "invalid_request_error",
- fmt.Sprintf("Task is not completed yet, current status: %s", task.Status))
- return
- }
- channel, err := model.CacheGetChannel(task.ChannelId)
- if err != nil {
- logger.LogError(c.Request.Context(), fmt.Sprintf("Failed to get channel for task %s: %s", taskID, err.Error()))
- videoProxyError(c, http.StatusInternalServerError, "server_error", "Failed to retrieve channel information")
- return
- }
- baseURL := channel.GetBaseURL()
- if baseURL == "" {
- baseURL = "https://api.openai.com"
- }
- var videoURL string
- proxy := channel.GetSetting().Proxy
- client, err := service.GetHttpClientWithProxy(proxy)
- if err != nil {
- logger.LogError(c.Request.Context(), fmt.Sprintf("Failed to create proxy client for task %s: %s", taskID, err.Error()))
- videoProxyError(c, http.StatusInternalServerError, "server_error", "Failed to create proxy client")
- return
- }
- ctx, cancel := context.WithTimeout(c.Request.Context(), 60*time.Second)
- defer cancel()
- req, err := http.NewRequestWithContext(ctx, http.MethodGet, "", nil)
- if err != nil {
- logger.LogError(c.Request.Context(), fmt.Sprintf("Failed to create request: %s", err.Error()))
- videoProxyError(c, http.StatusInternalServerError, "server_error", "Failed to create proxy request")
- 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))
- videoProxyError(c, http.StatusInternalServerError, "server_error", "API key not stored for task")
- 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()))
- videoProxyError(c, http.StatusBadGateway, "server_error", "Failed to resolve Gemini video URL")
- return
- }
- req.Header.Set("x-goog-api-key", apiKey)
- case constant.ChannelTypeOpenAI, constant.ChannelTypeSora:
- videoURL = fmt.Sprintf("%s/v1/videos/%s/content", baseURL, task.GetUpstreamTaskID())
- req.Header.Set("Authorization", "Bearer "+channel.Key)
- default:
- // Video URL is stored in PrivateData.ResultURL (fallback to FailReason for old data)
- videoURL = task.GetResultURL()
- }
- 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()))
- videoProxyError(c, http.StatusInternalServerError, "server_error", "Failed to create proxy request")
- 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()))
- videoProxyError(c, http.StatusBadGateway, "server_error", "Failed to fetch video content")
- 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))
- videoProxyError(c, http.StatusBadGateway, "server_error",
- fmt.Sprintf("Upstream service returned status %d", resp.StatusCode))
- 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)
- if _, err = io.Copy(c.Writer, resp.Body); err != nil {
- logger.LogError(c.Request.Context(), fmt.Sprintf("Failed to stream video content: %s", err.Error()))
- }
- }
|