Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 
 

148 Zeilen
5.3 KiB

  1. package controller
  2. import (
  3. "context"
  4. "fmt"
  5. "io"
  6. "net/http"
  7. "net/url"
  8. "time"
  9. "github.com/QuantumNous/new-api/common"
  10. "github.com/QuantumNous/new-api/constant"
  11. "github.com/QuantumNous/new-api/logger"
  12. "github.com/QuantumNous/new-api/model"
  13. "github.com/QuantumNous/new-api/service"
  14. "github.com/QuantumNous/new-api/setting/system_setting"
  15. "github.com/gin-gonic/gin"
  16. )
  17. // videoProxyError returns a standardized OpenAI-style error response.
  18. func videoProxyError(c *gin.Context, status int, errType, message string) {
  19. c.JSON(status, gin.H{
  20. "error": gin.H{
  21. "message": message,
  22. "type": errType,
  23. },
  24. })
  25. }
  26. func VideoProxy(c *gin.Context) {
  27. taskID := c.Param("task_id")
  28. if taskID == "" {
  29. videoProxyError(c, http.StatusBadRequest, "invalid_request_error", "task_id is required")
  30. return
  31. }
  32. task, exists, err := model.GetByOnlyTaskId(taskID)
  33. if err != nil {
  34. logger.LogError(c.Request.Context(), fmt.Sprintf("Failed to query task %s: %s", taskID, err.Error()))
  35. videoProxyError(c, http.StatusInternalServerError, "server_error", "Failed to query task")
  36. return
  37. }
  38. if !exists || task == nil {
  39. videoProxyError(c, http.StatusNotFound, "invalid_request_error", "Task not found")
  40. return
  41. }
  42. if task.Status != model.TaskStatusSuccess {
  43. videoProxyError(c, http.StatusBadRequest, "invalid_request_error",
  44. fmt.Sprintf("Task is not completed yet, current status: %s", task.Status))
  45. return
  46. }
  47. channel, err := model.CacheGetChannel(task.ChannelId)
  48. if err != nil {
  49. logger.LogError(c.Request.Context(), fmt.Sprintf("Failed to get channel for task %s: %s", taskID, err.Error()))
  50. videoProxyError(c, http.StatusInternalServerError, "server_error", "Failed to retrieve channel information")
  51. return
  52. }
  53. baseURL := channel.GetBaseURL()
  54. if baseURL == "" {
  55. baseURL = "https://api.openai.com"
  56. }
  57. var videoURL string
  58. proxy := channel.GetSetting().Proxy
  59. client, err := service.GetHttpClientWithProxy(proxy)
  60. if err != nil {
  61. logger.LogError(c.Request.Context(), fmt.Sprintf("Failed to create proxy client for task %s: %s", taskID, err.Error()))
  62. videoProxyError(c, http.StatusInternalServerError, "server_error", "Failed to create proxy client")
  63. return
  64. }
  65. ctx, cancel := context.WithTimeout(c.Request.Context(), 60*time.Second)
  66. defer cancel()
  67. req, err := http.NewRequestWithContext(ctx, http.MethodGet, "", nil)
  68. if err != nil {
  69. logger.LogError(c.Request.Context(), fmt.Sprintf("Failed to create request: %s", err.Error()))
  70. videoProxyError(c, http.StatusInternalServerError, "server_error", "Failed to create proxy request")
  71. return
  72. }
  73. switch channel.Type {
  74. case constant.ChannelTypeGemini:
  75. apiKey := task.PrivateData.Key
  76. if apiKey == "" {
  77. logger.LogError(c.Request.Context(), fmt.Sprintf("Missing stored API key for Gemini task %s", taskID))
  78. videoProxyError(c, http.StatusInternalServerError, "server_error", "API key not stored for task")
  79. return
  80. }
  81. videoURL, err = getGeminiVideoURL(channel, task, apiKey)
  82. if err != nil {
  83. logger.LogError(c.Request.Context(), fmt.Sprintf("Failed to resolve Gemini video URL for task %s: %s", taskID, err.Error()))
  84. videoProxyError(c, http.StatusBadGateway, "server_error", "Failed to resolve Gemini video URL")
  85. return
  86. }
  87. req.Header.Set("x-goog-api-key", apiKey)
  88. case constant.ChannelTypeOpenAI, constant.ChannelTypeSora:
  89. videoURL = fmt.Sprintf("%s/v1/videos/%s/content", baseURL, task.GetUpstreamTaskID())
  90. req.Header.Set("Authorization", "Bearer "+channel.Key)
  91. default:
  92. // Video URL is stored in PrivateData.ResultURL (fallback to FailReason for old data)
  93. videoURL = task.GetResultURL()
  94. }
  95. req.URL, err = url.Parse(videoURL)
  96. if err != nil {
  97. logger.LogError(c.Request.Context(), fmt.Sprintf("Failed to parse URL %s: %s", videoURL, err.Error()))
  98. videoProxyError(c, http.StatusInternalServerError, "server_error", "Failed to create proxy request")
  99. return
  100. }
  101. fetchSetting := system_setting.GetFetchSetting()
  102. if err := common.ValidateURLWithFetchSetting(videoURL, fetchSetting.EnableSSRFProtection, fetchSetting.AllowPrivateIp, fetchSetting.DomainFilterMode, fetchSetting.IpFilterMode, fetchSetting.DomainList, fetchSetting.IpList, fetchSetting.AllowedPorts, fetchSetting.ApplyIPFilterForDomain); err != nil {
  103. logger.LogError(c.Request.Context(), fmt.Sprintf("Video URL blocked for task %s: %v", taskID, err))
  104. videoProxyError(c, http.StatusForbidden, "server_error", fmt.Sprintf("request blocked: %v", err))
  105. return
  106. }
  107. resp, err := client.Do(req)
  108. if err != nil {
  109. logger.LogError(c.Request.Context(), fmt.Sprintf("Failed to fetch video from %s: %s", videoURL, err.Error()))
  110. videoProxyError(c, http.StatusBadGateway, "server_error", "Failed to fetch video content")
  111. return
  112. }
  113. defer resp.Body.Close()
  114. if resp.StatusCode != http.StatusOK {
  115. logger.LogError(c.Request.Context(), fmt.Sprintf("Upstream returned status %d for %s", resp.StatusCode, videoURL))
  116. videoProxyError(c, http.StatusBadGateway, "server_error",
  117. fmt.Sprintf("Upstream service returned status %d", resp.StatusCode))
  118. return
  119. }
  120. for key, values := range resp.Header {
  121. for _, value := range values {
  122. c.Writer.Header().Add(key, value)
  123. }
  124. }
  125. c.Writer.Header().Set("Cache-Control", "public, max-age=86400")
  126. c.Writer.WriteHeader(resp.StatusCode)
  127. if _, err = io.Copy(c.Writer, resp.Body); err != nil {
  128. logger.LogError(c.Request.Context(), fmt.Sprintf("Failed to stream video content: %s", err.Error()))
  129. }
  130. }