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.ChannelTypeOpenAI, constant.ChannelTypeSora: videoURL = fmt.Sprintf("%s/v1/videos/%s/content", baseURL, task.TaskID) req.Header.Set("Authorization", "Bearer "+channel.Key) default: // Video URL is directly in task.FailReason videoURL = task.FailReason } 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") // Cache for 24 hours 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())) } }