-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
feat: 支持可灵视频渠道(异步任务) #1244
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: 支持可灵视频渠道(异步任务) #1244
Conversation
WalkthroughThe changes introduce support for a new video generation platform called "Kling" across both backend and frontend components. Backend updates include new constants, DTOs, router endpoints, relay modes, adaptors, and controller logic for handling Kling video tasks. The frontend is updated to display Kling tasks and preview generated video results. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Frontend
participant Backend
participant KlingAPI
User->>Frontend: Submit video generation request
Frontend->>Backend: POST /v1/video/generations
Backend->>KlingAPI: Submit video generation (with JWT)
KlingAPI-->>Backend: Responds with task ID
Backend-->>Frontend: Responds with task ID
User->>Frontend: Check video task status
Frontend->>Backend: GET /v1/video/generations/:task_id
Backend->>KlingAPI: Fetch task status (with JWT)
KlingAPI-->>Backend: Responds with task status/result
Backend-->>Frontend: Returns video status/result
Poem
✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
♻️ Duplicate comments (1)
relay/channel/task/kling/adaptor.go (1)
172-199: Repeated token-fallback issue inFetchTaskSame security concern: on JWT generation failure the secret key string is sent upstream. Remove the fallback as above.
🧹 Nitpick comments (6)
controller/task_video.go (2)
30-41: Bulk-failure branch ignores database errorIf
model.TaskBulkUpdateitself returns an error it is only logged toSysError; the outer function still returns success causing the caller to believe the failure path was handled.Return a wrapped error instead so the caller can retry / alert.
42-45: Hard-coded platform ties controller to one adaptor
relay.GetTaskAdaptor(constant.TaskPlatformKling)locks this controller to Kling only.
If another video platform is added you’ll need an almost identical controller. Pass the platform as a parameter or look it up fromchannel.Typeto keep this generic.relay/channel/task/kling/adaptor.go (4)
213-229:Metadatafield is ignored
SubmitReqexposes ametadatamap butconvertToRequestPayloaddrops it.
Either propagate it or remove the field to avoid misleading API consumers.
149-170: Adaptor writes HTTP response – violates separation of concerns
DoResponseboth returns(taskID, taskData)and callsc.JSON(...). The caller (relay controller) is likely to write its own response, risking duplicate headers/body.Return the data only; let the higher layer decide how to respond.
266-272:createJWTTokenWithKeysilently accepts malformed inputIt just splits on the first comma and returns “invalid format” only when the count ≠ 2.
Validate that both pieces are non-empty to avoid generating tokens with emptyissor secret.
289-312: Nil-safe navigation needed inParseResultUrlMultiple unchecked type assertions can panic when upstream schema changes. Prefer the
maputilpattern or successiveokchecks with early returns to keep the adaptor robust.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (17)
common/constants.go(2 hunks)constant/task.go(1 hunks)controller/relay.go(1 hunks)controller/task.go(1 hunks)controller/task_video.go(1 hunks)dto/video.go(1 hunks)middleware/distributor.go(1 hunks)relay/channel/adapter.go(1 hunks)relay/channel/task/kling/adaptor.go(1 hunks)relay/channel/task/suno/adaptor.go(1 hunks)relay/constant/relay_mode.go(2 hunks)relay/relay_adaptor.go(2 hunks)relay/relay_task.go(4 hunks)router/main.go(1 hunks)router/video-router.go(1 hunks)web/src/components/table/TaskLogsTable.js(6 hunks)web/src/constants/channel.constants.js(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (15)
- router/main.go
- constant/task.go
- relay/channel/task/suno/adaptor.go
- controller/relay.go
- relay/relay_adaptor.go
- web/src/constants/channel.constants.js
- middleware/distributor.go
- controller/task.go
- relay/channel/adapter.go
- router/video-router.go
- common/constants.go
- relay/constant/relay_mode.go
- dto/video.go
- relay/relay_task.go
- web/src/components/table/TaskLogsTable.js
🧰 Additional context used
🧬 Code Graph Analysis (1)
relay/channel/task/kling/adaptor.go (6)
relay/channel/adapter.go (1)
TaskAdaptor(30-49)relay/channel/task/suno/adaptor.go (1)
TaskAdaptor(21-23)relay/common/relay_info.go (1)
TaskRelayInfo(272-278)service/error.go (2)
TaskErrorWrapperLocal(134-138)TaskErrorWrapper(140-156)relay/channel/api_request.go (1)
DoTaskApiRequest(252-274)service/http_client.go (1)
GetHttpClient(32-34)
controller/task_video.go
Outdated
| case "submitted", "queued": | ||
| task.Status = model.TaskStatusSubmitted | ||
| case "processing": | ||
| task.Status = model.TaskStatusInProgress | ||
| case "succeed": | ||
| task.Status = model.TaskStatusSuccess | ||
| task.Progress = "100%" | ||
| if url, err := adaptor.(interface { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Progress never advances for “submitted/queued/processing” states
task.Progress is updated only on success/failure. For long-running video renders the UI will always show whatever stale value was written at submission time (often "").
Consider setting a rough percentage, e.g.
• queued → 0%
• processing → value returned by upstream if available, or 50%/80%.
🤖 Prompt for AI Agents
In controller/task_video.go around lines 100 to 107, the task.Progress field is
only updated for success or failure states, causing the UI to show stale
progress for submitted, queued, and processing states. To fix this, set
task.Progress to "0%" for the queued state, and for processing, update
task.Progress with a value returned by the upstream source if available, or
default to a reasonable estimate like "50%" or "80%". This ensures the UI
reflects ongoing progress more accurately.
| if task.Status == model.TaskStatusFailure { | ||
| common.LogInfo(ctx, fmt.Sprintf("Task %s failed: %s", task.TaskID, task.FailReason)) | ||
| quota := task.Quota | ||
| if quota != 0 { | ||
| if err := model.IncreaseUserQuota(task.UserId, quota, false); err != nil { | ||
| common.LogError(ctx, "Failed to increase user quota: "+err.Error()) | ||
| } | ||
| logContent := fmt.Sprintf("Video async task failed %s, refund %s", task.TaskID, common.LogQuota(quota)) | ||
| model.RecordLog(task.UserId, model.LogTypeSystem, logContent) | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Potential double-refund on repeated polling
When a task is permanently marked FAILURE you refund quota each time this poller runs. Nothing prevents the same failed task from being re-evaluated on the next scheduler tick, issuing multiple refunds.
Persist a “refunded” flag or guard with if task.Status == FAILURE && !task.Refunded.
🤖 Prompt for AI Agents
In controller/task_video.go around lines 124 to 134, the code refunds user quota
every time a task with status FAILURE is processed, causing potential double
refunds on repeated polls. To fix this, add a "refunded" flag to the task model
and update the condition to check that the task is FAILURE and not yet refunded
before issuing a refund. After successfully refunding, set the refunded flag to
true and persist this change to prevent multiple refunds for the same task.
| if status, ok := data["task_status"].(string); ok { | ||
| switch status { | ||
| case "submitted", "queued": | ||
| task.Status = model.TaskStatusSubmitted | ||
| case "processing": | ||
| task.Status = model.TaskStatusInProgress | ||
| case "succeed": | ||
| task.Status = model.TaskStatusSuccess | ||
| task.Progress = "100%" | ||
| if url, err := adaptor.(interface { | ||
| ParseResultUrl(map[string]any) (string, error) | ||
| }).ParseResultUrl(responseItem); err == nil { | ||
| task.FailReason = url | ||
| } else { | ||
| common.LogWarn(ctx, fmt.Sprintf("Failed to get url from body for task %s: %s", task.TaskID, err.Error())) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do not overload FailReason with success information
The successful-task branch stores the result video URL in task.FailReason.
That field semantically represents an error; overloading it with the result makes downstream code and UI very confusing and forces callers to branch on both Status and the meaning of FailReason.
- if url, err := adaptor.(interface {
- ParseResultUrl(map[string]any) (string, error)
- }).ParseResultUrl(responseItem); err == nil {
- task.FailReason = url // <- misuse
- } else {
+ if url, err := adaptor.(interface {
+ ParseResultUrl(map[string]any) (string, error)
+ }).ParseResultUrl(responseItem); err == nil {
+ task.ResultUrl = url // add a dedicated column / json field
+ } else {Please add a dedicated field such as ResultUrl (string) to model.Task and persist that instead.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In controller/task_video.go around lines 98 to 113, the code incorrectly stores
the result video URL in the FailReason field, which should only represent
errors. To fix this, add a new string field named ResultUrl to the model.Task
struct and assign the parsed URL to this new field instead of FailReason in the
success case. This will keep error and success data separate and avoid confusion
in downstream code and UI.
| func (a *TaskAdaptor) ValidateRequestAndSetAction(c *gin.Context, info *relaycommon.TaskRelayInfo) (taskErr *dto.TaskError) { | ||
| // Accept only POST /v1/video/generations as "generate" action. | ||
| action := "generate" | ||
| info.Action = action | ||
|
|
||
| var req SubmitReq | ||
| if err := common.UnmarshalBodyReusable(c, &req); err != nil { | ||
| taskErr = service.TaskErrorWrapperLocal(err, "invalid_request", http.StatusBadRequest) | ||
| return | ||
| } | ||
| if strings.TrimSpace(req.Prompt) == "" { | ||
| taskErr = service.TaskErrorWrapperLocal(fmt.Errorf("prompt is required"), "invalid_request", http.StatusBadRequest) | ||
| return | ||
| } | ||
|
|
||
| // Store into context for later usage | ||
| c.Set("kling_request", req) | ||
| return nil | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
HTTP method/path are not validated
ValidateRequestAndSetAction assumes every request hitting this adaptor is POST /v1/video/generations.
A malformed route (e.g. GET) will slip through and produce upstream 405/404 errors.
Check verb & path and return a TaskError early.
🤖 Prompt for AI Agents
In relay/channel/task/kling/adaptor.go around lines 81 to 99, the function
ValidateRequestAndSetAction does not verify the HTTP method and path, assuming
all requests are POST /v1/video/generations. To fix this, add checks at the
start of the function to confirm the request method is POST and the path matches
/v1/video/generations; if not, return a TaskError with an appropriate error code
and message to prevent malformed requests from proceeding.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (3)
controller/task_video.go (3)
98-106: Progress never advances for “submitted/queued/processing” states
task.Progressis only set on success. For long-running renders the UI will keep whatever stale value was written at submission time (often empty).Please initialise sensible percentages (e.g.
0%,50%, …) or propagate the upstream value for these intermediate states.
107-110: Do not overloadFailReasonwith success dataStoring the result URL in
task.FailReasonblurs the field’s purpose and complicates downstream logic.- task.FailReason = url + task.ResultURL = url // add a dedicated column / json tag
121-132: Potential double-refund on repeated pollingEach scheduler pass refunds quota for any
FAILUREtask. Without a guard, users can receive multiple refunds for the same task.Add a
Refundedflag (or similar) and guard the block:- if task.Status == model.TaskStatusFailure { + if task.Status == model.TaskStatusFailure && !task.Refunded { ... + task.Refunded = true + _ = task.Update() }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
controller/task_video.go(1 hunks)relay/channel/task/kling/adaptor.go(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- relay/channel/task/kling/adaptor.go
| adaptor := relay.GetTaskAdaptor(constant.TaskPlatformKling) | ||
| if adaptor == nil { | ||
| return fmt.Errorf("video adaptor not found") | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Hard-coded adaptor locks the controller to Kling only
relay.GetTaskAdaptor(constant.TaskPlatformKling) forces every channel—regardless of its actual type—to be handled with the Kling adaptor. As soon as another video platform is added, those tasks will be mis-processed or fail outright.
- adaptor := relay.GetTaskAdaptor(constant.TaskPlatformKling)
+ // Select adaptor based on the channel’s platform/type.
+ adaptor := relay.GetTaskAdaptor(channel.Type) // map or switch if names differ🤖 Prompt for AI Agents
In controller/task_video.go around lines 42 to 45, the code currently hard-codes
the adaptor to Kling by calling relay.GetTaskAdaptor with
constant.TaskPlatformKling, which restricts the controller to only handle Kling
platform tasks. To fix this, modify the code to dynamically select the adaptor
based on the actual platform of the task or channel being processed instead of
using a fixed constant. This may involve passing the platform identifier as a
parameter or retrieving it from the task context before calling GetTaskAdaptor.
|
这种非 Chat 接口渠道,应该要在 Chat 那边拦截住禁止 Chat 调用 |
有道理, 参考suno把渠道测试那边栏了 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
controller/channel-test.go (1)
43-45: Consolidate duplicated “unsupported channel” guards & unify namingFour separate
ifbranches now do the exact same thing. Consider:
- Replace the repeated checks with a helper like
isNonChatChannel(channel.Type)or aswitchstatement to reduce copy-paste and future merge conflicts.- The message says “kling” while the constant/PR text alternates between “Kling” and “Keling”. Pick one spelling and use it everywhere to avoid confusing logs and UX.
- (Optional) When returning sentinel errors, wrapping with
%w(or usingerrors.Join/third-party stack helpers) preserves stack traces for easier debugging.Example sketch:
- if channel.Type == common.ChannelTypeKling { - return errors.New("kling channel test is not supported"), nil - } +if isNonChatChannel(channel.Type) { + return fmt.Errorf("%s channel test is not supported", channel.Type.String()), nil +}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
controller/channel-test.go(2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
controller/channel-test.go (1)
common/constants.go (1)
ChannelTypeKling(244-244)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (2)
relay/channel/task/kling/adaptor.go (2)
81-99: HTTP method/path validation still missing.This method still doesn't validate that the request is actually
POST /v1/video/generationsas assumed. Malformed requests (e.g., GET requests) will proceed and cause upstream 405/404 errors.
107-118: Security vulnerability: Dangerous JWT fallback still present.Despite the past review comment indicating this was addressed, the dangerous fallback on line 110 still exists. When JWT creation fails, the raw API key (containing the secret) is used in the Authorization header, which can leak sensitive credentials to logs and proxies.
func (a *TaskAdaptor) BuildRequestHeader(c *gin.Context, req *http.Request, info *relaycommon.TaskRelayInfo) error { token, err := a.createJWTToken() if err != nil { - token = info.ApiKey // fallback + return fmt.Errorf("jwt token creation failed: %w", err) } req.Header.Set("Content-Type", "application/json") req.Header.Set("Accept", "application/json") req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("User-Agent", "kling-sdk/1.0") return nil }
🧹 Nitpick comments (1)
relay/channel/task/kling/adaptor.go (1)
68-78: Consider adding validation for API key format.The Init method assumes the API key will be in "access_key,secret_key" format but doesn't validate this assumption. While JWT creation methods validate later, early validation would provide clearer error messages.
func (a *TaskAdaptor) Init(info *relaycommon.TaskRelayInfo) { a.ChannelType = info.ChannelType a.baseURL = info.BaseUrl // apiKey format: "access_key,secret_key" keyParts := strings.Split(info.ApiKey, ",") - if len(keyParts) == 2 { + if len(keyParts) != 2 { + // Log warning or handle invalid format + return + } a.accessKey = strings.TrimSpace(keyParts[0]) a.secretKey = strings.TrimSpace(keyParts[1]) - }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (17)
common/constants.go(2 hunks)constant/task.go(1 hunks)controller/relay.go(1 hunks)controller/task.go(1 hunks)controller/task_video.go(1 hunks)dto/video.go(1 hunks)middleware/distributor.go(1 hunks)relay/channel/adapter.go(1 hunks)relay/channel/task/kling/adaptor.go(1 hunks)relay/channel/task/suno/adaptor.go(1 hunks)relay/constant/relay_mode.go(2 hunks)relay/relay_adaptor.go(2 hunks)relay/relay_task.go(4 hunks)router/main.go(1 hunks)router/video-router.go(1 hunks)web/src/components/table/TaskLogsTable.js(6 hunks)web/src/constants/channel.constants.js(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- dto/video.go
🚧 Files skipped from review as they are similar to previous changes (15)
- router/main.go
- constant/task.go
- relay/channel/adapter.go
- web/src/constants/channel.constants.js
- relay/relay_adaptor.go
- common/constants.go
- relay/channel/task/suno/adaptor.go
- controller/relay.go
- middleware/distributor.go
- router/video-router.go
- controller/task.go
- relay/constant/relay_mode.go
- web/src/components/table/TaskLogsTable.js
- relay/relay_task.go
- controller/task_video.go
🔇 Additional comments (11)
relay/channel/task/kling/adaptor.go (11)
1-22: Package structure and imports look good.The package declaration and imports are properly organized and include all necessary dependencies for the Kling adapter implementation.
24-55: Request/response structures are well-defined.The struct definitions properly model the API request and response formats with appropriate JSON tags and field types.
102-104: URL construction is correct.The method properly constructs the upstream API URL for video generation.
121-134: Request body construction is well-implemented.The method properly retrieves the validated request from context, converts it to the upstream format, and handles errors appropriately.
137-170: Response handling methods are robust.The DoRequest delegation and DoResponse parsing with fallback mechanisms are well-implemented. The dual parsing approach (Kling-specific then generic) provides good error resilience.
201-207: Metadata methods are correctly implemented.The GetModelList and GetChannelName methods properly return the supported models and channel identifier.
213-229: Request payload conversion is well-implemented.The conversion logic properly transforms the internal request format to the upstream API format with appropriate default values.
231-256: Helper functions are well-designed.The aspect ratio mapping and default value functions are simple, clear, and handle edge cases appropriately.
262-287: JWT token creation is properly implemented.The JWT methods follow security best practices with proper validation, standard claims structure, reasonable expiration times, and secure HS256 signing.
290-312: URL parsing method is robust and safe.The ParseResultUrl method properly handles the complex nested JSON structure with comprehensive type checking and clear error messages for different failure scenarios.
1-313: Overall implementation is solid but has critical security vulnerabilities.The Kling adapter is well-structured and implements the required interface properly. However, the JWT fallback mechanisms in
BuildRequestHeaderandFetchTaskmethods pose serious security risks by potentially exposing API secrets. These must be fixed before merging.Key strengths:
- Clear code organization and separation of concerns
- Robust error handling in most areas
- Proper JWT implementation
- Good request/response handling
Critical issues to address:
- JWT fallback vulnerabilities (lines 110, 187)
- Missing HTTP method/path validation (lines 81-99)
| token, err := a.createJWTTokenWithKey(key) | ||
| if err != nil { | ||
| token = key | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same JWT fallback vulnerability in FetchTask.
This method has the same dangerous fallback pattern where the raw API key is used if JWT token creation fails, potentially exposing secrets.
token, err := a.createJWTTokenWithKey(key)
if err != nil {
- token = key
+ return nil, fmt.Errorf("jwt token creation failed: %w", err)
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| token, err := a.createJWTTokenWithKey(key) | |
| if err != nil { | |
| token = key | |
| } | |
| // … other code … | |
| token, err := a.createJWTTokenWithKey(key) | |
| if err != nil { | |
| return nil, fmt.Errorf("jwt token creation failed: %w", err) | |
| } | |
| // … remaining code … |
🤖 Prompt for AI Agents
In relay/channel/task/kling/adaptor.go around lines 185 to 188, the code falls
back to using the raw API key if JWT token creation fails, which risks exposing
sensitive secrets. Remove the fallback assignment of token to key on error.
Instead, handle the error properly by returning it or logging it without using
the raw key as a token, ensuring no secret leakage occurs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
controller/channel-test.go (1)
43-45: LGTM! Consistent implementation for blocking Kling channel testing.The implementation correctly prevents testing of the Kling channel type, following the established pattern for other non-Chat interface channels like Suno. This aligns with the PR objectives of ensuring proper separation between Chat and non-Chat channels.
Minor observation: Consider standardizing error message formats across all channel types for consistency (e.g., "midjourney plus channel test is not supported!!!" has extra exclamation marks while others don't).
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (18)
common/constants.go(2 hunks)constant/task.go(1 hunks)controller/channel-test.go(1 hunks)controller/relay.go(1 hunks)controller/task.go(1 hunks)controller/task_video.go(1 hunks)dto/video.go(1 hunks)middleware/distributor.go(1 hunks)relay/channel/adapter.go(1 hunks)relay/channel/task/kling/adaptor.go(1 hunks)relay/channel/task/suno/adaptor.go(1 hunks)relay/constant/relay_mode.go(2 hunks)relay/relay_adaptor.go(2 hunks)relay/relay_task.go(4 hunks)router/main.go(1 hunks)router/video-router.go(1 hunks)web/src/components/table/TaskLogsTable.js(6 hunks)web/src/constants/channel.constants.js(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (17)
- controller/task.go
- router/main.go
- constant/task.go
- web/src/constants/channel.constants.js
- controller/relay.go
- relay/channel/task/suno/adaptor.go
- relay/channel/adapter.go
- common/constants.go
- relay/relay_adaptor.go
- middleware/distributor.go
- router/video-router.go
- dto/video.go
- web/src/components/table/TaskLogsTable.js
- relay/constant/relay_mode.go
- relay/relay_task.go
- controller/task_video.go
- relay/channel/task/kling/adaptor.go
增加可灵视频渠道, 已修改为所有逻辑内聚在new-api内, 使用和suno一致的任务提交和查询逻辑, 可灵渠道按任务接口实现为一个适配器, 后期其他视频渠道可按目录实现适配器
点击预览视频Summary by CodeRabbit
New Features
Improvements