| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- 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:
- // Video URL is directly in task.FailReason
- videoURL = task.FailReason
- default:
- // Default (Sora, etc.): Use original logic
- 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") // 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()))
- }
- }
|