| 1 | import { Injectable, Logger } from '@nestjs/common' |
| 2 | import { AssetsService } from '@yikart/assets' |
| 3 | import { AppException, ResponseCode } from '@yikart/common' |
| 4 | import { |
| 5 | AideoAiLogResponse, |
| 6 | AiLog, |
| 7 | AiLogChannel, |
| 8 | AiLogRepository, |
| 9 | AiLogStatus, |
| 10 | AiLogType, |
| 11 | AssetType, |
| 12 | StyleTransferAiLogResponse, |
| 13 | } from '@yikart/mongodb' |
| 14 | import { VolcengineVideoUtils } from '../../agent/mcp/volcengine/volcengine.utils' |
| 15 | import { |
| 16 | AideoTaskStatus, |
| 17 | AITranslationApiResponse, |
| 18 | AITranslationProjectStatus, |
| 19 | ApiResponse, |
| 20 | DramaRecapTaskStatus, |
| 21 | EraseApiResponse, |
| 22 | GetAideoTaskResultResponse, |
| 23 | HighlightApiResponse, |
| 24 | SkillType, |
| 25 | VCreativeApiResponse, |
| 26 | VCreativeStatus, |
| 27 | VideoInput, |
| 28 | VolcengineService, |
| 29 | } from '../libs/volcengine' |
| 30 | import { parseVolcengineError } from '../libs/volcengine/volcengine.utils' |
| 31 | import { |
| 32 | UserGetAideoTaskQueryDto, |
| 33 | UserGetDramaRecapTaskRequest, |
| 34 | UserGetVideoStyleTransferTaskRequest, |
| 35 | UserListAideoTasksQueryDto, |
| 36 | UserSubmitAideoTaskRequest, |
| 37 | UserSubmitDramaRecapTaskRequest, |
| 38 | UserSubmitVideoStyleTransferRequest, |
| 39 | } from './aideo.dto' |
| 40 | import { DramaRecapService } from './drama-recap.service' |
| 41 | import { VideoStyleTransferService } from './video-style-transfer.service' |
| 42 | |
| 43 | /** |
| 44 | * Aideo 服务 |
| 45 | * 负责核心 Aideo 任务的提交和查询 |
| 46 | * 视频风格转换和短剧解说委托给专门的服务 |
| 47 | */ |
| 48 | @Injectable() |
| 49 | export class AideoService { |
| 50 | private readonly logger = new Logger(AideoService.name) |
| 51 | |
| 52 | constructor( |
| 53 | private readonly volcengineService: VolcengineService, |
| 54 | private readonly aiLogRepo: AiLogRepository, |
| 55 | private readonly assetsService: AssetsService, |
| 56 | private readonly videoStyleTransferService: VideoStyleTransferService, |
| 57 | private readonly dramaRecapService: DramaRecapService, |
| 58 | ) { } |
| 59 | |
| 60 | /** |
| 61 | * 提交 Aideo 任务 |
| 62 | */ |
| 63 | async submitAideoTask(request: UserSubmitAideoTaskRequest) { |
| 64 | const { userId, userType, ...params } = request |
| 65 | |
| 66 | const startedAt = new Date() |
| 67 | |
| 68 | const videoInputs: VideoInput[] = params.multiInputs.map((input): VideoInput => { |
| 69 | if (typeof input === 'string') { |
| 70 | return input |
| 71 | } |
| 72 | if (input.type === 'url') { |
| 73 | return this.assetsService.buildUrl(input.url) |
| 74 | } |
| 75 | |
| 76 | return { |
| 77 | type: 'stream', |
| 78 | stream: input.stream, |
| 79 | fileSize: input.fileSize, |
| 80 | fileName: input.fileName, |
| 81 | fileExtension: input.fileExtension, |
| 82 | } |
| 83 | }) |
| 84 | |
| 85 | this.logger.debug({ inputCount: videoInputs.length }, '视频输入处理完成') |
| 86 | |
| 87 | let taskResult |
| 88 | if ('prompt' in params) { |
| 89 | taskResult = await this.volcengineService.submitAideoTaskAsyncWithUpload({ |
| 90 | SpaceName: params.spaceName, |
| 91 | MultiInputs: videoInputs, |
| 92 | Prompt: params.prompt, |
| 93 | }) |
| 94 | } |
| 95 | else { |
| 96 | taskResult = await this.volcengineService.submitAideoTaskAsyncWithUpload({ |
| 97 | SpaceName: params.spaceName, |
| 98 | MultiInputs: videoInputs, |
| 99 | SkillType: params.skillType, |
| 100 | SkillParams: params.skillParams, |
| 101 | }) |
| 102 | } |
| 103 | |
| 104 | this.logger.debug({ |
| 105 | taskId: taskResult.TaskId, |
| 106 | spaceName: params.spaceName, |
| 107 | inputVidsCount: videoInputs.length, |
| 108 | inputVids: videoInputs.map(v => (typeof v === 'string' ? v : v.type === 'stream' ? 'stream' : v.url)).filter(Boolean).slice(0, 5), |
| 109 | }, 'VOLCENGINE Submit summary') |
| 110 | |
| 111 | const aiLog = await this.aiLogRepo.create({ |
| 112 | userId, |
| 113 | userType, |
| 114 | taskId: taskResult.TaskId, |
| 115 | model: 'aideo', |
| 116 | channel: AiLogChannel.Volcengine, |
| 117 | startedAt, |
| 118 | type: AiLogType.Aideo, |
| 119 | request: params, |
| 120 | status: AiLogStatus.Generating, |
| 121 | }) |
| 122 | |
| 123 | return { |
| 124 | taskId: aiLog.id, |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | /** |
| 129 | * 处理 Aideo 任务(从火山接口获取结果) |
| 130 | * 支持多种任务类型: |
| 131 | * - 'aideo': 通用 Aideo 任务(使用 GetAideoTaskResult API) |
| 132 | * - 'video-style-transfer': 视频风格转换任务(委托给 VideoStyleTransferService) |
| 133 | * - 'drama-recap': 短剧解说任务(委托给 DramaRecapService) |
| 134 | */ |
| 135 | async processAideoTask(task: AiLog) { |
| 136 | const taskId = task.taskId |
| 137 | if (!taskId) { |
| 138 | this.logger.warn({ taskId: task.id }, '任务缺少 taskId,跳过处理') |
| 139 | return |
| 140 | } |
| 141 | |
| 142 | if (task.model === 'video-style-transfer') { |
| 143 | return this.videoStyleTransferService.processVCreativeTask(task) |
| 144 | } |
| 145 | |
| 146 | if (task.model === 'drama-recap') { |
| 147 | this.logger.log({ taskId: task.id, volcengineTaskId: taskId }, '[DramaRecap] 定时任务查询状态') |
| 148 | |
| 149 | const result = await this.volcengineService.getDramaRecapTask({ |
| 150 | TaskId: taskId, |
| 151 | SpaceName: this.volcengineService.getSpaceName(), |
| 152 | }) |
| 153 | |
| 154 | this.logger.log({ taskId: task.id, status: result.Status }, '[DramaRecap] 查询结果') |
| 155 | if (result.Status === DramaRecapTaskStatus.Completed || result.Status === DramaRecapTaskStatus.Failed) { |
| 156 | await this.dramaRecapService.processDramaRecapTask(task, result) |
| 157 | } |
| 158 | return |
| 159 | } |
| 160 | |
| 161 | const taskResult = await this.volcengineService.getAideoTaskResult({ |
| 162 | TaskId: taskId, |
| 163 | }) |
| 164 | |
| 165 | const { Status } = taskResult |
| 166 | |
| 167 | if (Status === AideoTaskStatus.Completed) { |
| 168 | // 检查是否有有效的API响应 |
| 169 | if (!taskResult.ApiResponses || taskResult.ApiResponses.length === 0) { |
| 170 | this.logger.error( |
| 171 | { taskId: task.id, taskResult }, |
| 172 | '任务完成但没有API响应,可能是火山引擎处理失败', |
| 173 | ) |
| 174 | await this.aiLogRepo.updateById(task.id, { |
| 175 | status: AiLogStatus.Failed, |
| 176 | response: taskResult, |
| 177 | errorMessage: '任务完成但没有有效的输出结果,可能是火山引擎处理失败', |
| 178 | }) |
| 179 | return |
| 180 | } |
| 181 | |
| 182 | const apiResponse = taskResult.ApiResponses[0] as ApiResponse |
| 183 | |
| 184 | if (apiResponse.Error) { |
| 185 | const errorMessage = apiResponse.Error.Message || '任务执行失败' |
| 186 | |
| 187 | this.logger.error({ |
| 188 | taskId: task.id, |
| 189 | errorCode: apiResponse.Error.Code, |
| 190 | errorMessage, |
| 191 | vodTaskType: apiResponse.VodTaskType, |
| 192 | }, 'API 响应包含错误,任务失败') |
| 193 | |
| 194 | await this.aiLogRepo.updateById(task.id, { |
| 195 | status: AiLogStatus.Failed, |
| 196 | response: taskResult, |
| 197 | errorMessage, |
| 198 | }) |
| 199 | return |
| 200 | } |
| 201 | |
| 202 | // 额外验证:对于 AITranslation,确保 ProjectInfo 状态与输出资产已就绪 |
| 203 | if (apiResponse.VodTaskType === SkillType.AITranslation) { |
| 204 | const translationResponse = apiResponse as AITranslationApiResponse |
| 205 | const projectInfo = translationResponse.AITranslation?.ProjectInfo |
| 206 | const projectStatus = projectInfo?.Status |
| 207 | |
| 208 | const hasAsset = Boolean( |
| 209 | projectInfo?.OutputVideo?.Url || projectInfo?.OutputVideo?.Vid || projectInfo?.OutputVideo?.FileName |
| 210 | || projectInfo?.FacialTranslationVideo?.Url || projectInfo?.FacialTranslationVideo?.Vid || projectInfo?.FacialTranslationVideo?.FileName |
| 211 | || projectInfo?.VoiceTranslationVideo?.Url || projectInfo?.VoiceTranslationVideo?.Vid || projectInfo?.VoiceTranslationVideo?.FileName, |
| 212 | ) |
| 213 | |
| 214 | const exportingOrProcessingStatuses = new Set([ |
| 215 | AITranslationProjectStatus.InProcessing, |
| 216 | AITranslationProjectStatus.ProcessSuspended, |
| 217 | AITranslationProjectStatus.InExporting, |
| 218 | ]) |
| 219 | |
| 220 | if (!hasAsset || (projectStatus && exportingOrProcessingStatuses.has(projectStatus))) { |
| 221 | const elapsed = Date.now() - task.startedAt.getTime() |
| 222 | |
| 223 | this.logger.warn({ taskId: task.id, elapsed, projectStatus, hasAsset }, 'AITranslation 标记为 Completed 但未产出可用资产,延迟处理') |
| 224 | |
| 225 | await this.aiLogRepo.updateById(task.id, { status: AiLogStatus.Generating, response: taskResult }) |
| 226 | return |
| 227 | } |
| 228 | } |
| 229 | |
| 230 | // 额外验证:对于 VCreative,检查内部状态是否成功 |
| 231 | if (apiResponse.VodTaskType === SkillType.VCreative) { |
| 232 | const vCreativeResponse = apiResponse as VCreativeApiResponse |
| 233 | const vCreative = vCreativeResponse.VCreative |
| 234 | |
| 235 | if (vCreative && vCreative.Status !== VCreativeStatus.Success) { |
| 236 | const errorMessage = typeof vCreative.OutputJson === 'string' |
| 237 | ? vCreative.OutputJson |
| 238 | : (vCreativeResponse.Error?.Message || `VCreative 任务失败: ${vCreative.Status}`) |
| 239 | |
| 240 | this.logger.error({ |
| 241 | taskId: task.id, |
| 242 | vCreativeStatus: vCreative.Status, |
| 243 | errorMessage, |
| 244 | }, 'VCreative 任务内部状态失败') |
| 245 | |
| 246 | await this.aiLogRepo.updateById(task.id, { |
| 247 | status: AiLogStatus.Failed, |
| 248 | response: taskResult, |
| 249 | errorMessage, |
| 250 | }) |
| 251 | return |
| 252 | } |
| 253 | } |
| 254 | |
| 255 | await this.updateTaskResult(task, taskResult) |
| 256 | } |
| 257 | else if (Status === AideoTaskStatus.Failed) { |
| 258 | const apiError = taskResult.ApiResponses?.[0]?.Error |
| 259 | const errorMessage = apiError?.Message || '任务失败' |
| 260 | const errorCode = apiError?.Code |
| 261 | |
| 262 | // 解析错误并提供友好提示 |
| 263 | let enhancedErrorMessage = errorMessage |
| 264 | if (errorCode) { |
| 265 | const parsedError = parseVolcengineError(errorCode, errorMessage) |
| 266 | enhancedErrorMessage = `${parsedError.userMessage}\n技术详情: ${parsedError.technicalDetails}\n建议: ${parsedError.suggestions.join('; ')}` |
| 267 | |
| 268 | this.logger.error({ |
| 269 | taskId: task.id, |
| 270 | errorCode, |
| 271 | userMessage: parsedError.userMessage, |
| 272 | suggestions: parsedError.suggestions, |
| 273 | }, '任务失败') |
| 274 | } |
| 275 | |
| 276 | await this.aiLogRepo.updateById(task.id, { |
| 277 | status: AiLogStatus.Failed, |
| 278 | response: taskResult, |
| 279 | errorMessage: enhancedErrorMessage, |
| 280 | }) |
| 281 | } |
| 282 | else if (Status === AideoTaskStatus.Processing) { |
| 283 | await this.aiLogRepo.updateById(task.id, { |
| 284 | status: AiLogStatus.Generating, |
| 285 | response: taskResult, |
| 286 | }) |
| 287 | } |
| 288 | } |
| 289 | |
| 290 | /** |
| 291 | * 更新任务结果 |
| 292 | */ |
| 293 | private async updateTaskResult( |
| 294 | task: AiLog, |
| 295 | taskResult: GetAideoTaskResultResponse, |
| 296 | ) { |
| 297 | if (taskResult.ApiResponses && taskResult.ApiResponses.length > 0) { |
| 298 | const apiResponse = taskResult.ApiResponses[0] as ApiResponse |
| 299 | await this.saveOutputVideos(apiResponse, task) |
| 300 | } |
| 301 | |
| 302 | await this.aiLogRepo.updateById(task.id, { |
| 303 | status: AiLogStatus.Success, |
| 304 | response: taskResult, |
| 305 | duration: Date.now() - task.startedAt.getTime(), |
| 306 | }) |
| 307 | } |
| 308 | |
| 309 | /** |
| 310 | * 从 VID 获取视频并上传 |
| 311 | * @param vid 视频 ID |
| 312 | * @param task 任务日志 |
| 313 | * @param filenamePrefix 文件名前缀 |
| 314 | * @returns URL,如果失败则返回 undefined |
| 315 | */ |
| 316 | private async saveVideoFromVid( |
| 317 | vid: string, |
| 318 | task: AiLog, |
| 319 | filenamePrefix: string, |
| 320 | ): Promise<string | undefined> { |
| 321 | return VolcengineVideoUtils.saveVideoFromVid( |
| 322 | vid, |
| 323 | task.userId, |
| 324 | `${task.id}-${filenamePrefix}`, |
| 325 | task.model || 'aideo', |
| 326 | this.volcengineService, |
| 327 | this.assetsService, |
| 328 | this.logger, |
| 329 | AssetType.AideoOutput, |
| 330 | ) |
| 331 | } |
| 332 | |
| 333 | /** |
| 334 | * 保存输出视频 |
| 335 | */ |
| 336 | private async saveOutputVideos(apiResponse: ApiResponse, task: AiLog) { |
| 337 | if (apiResponse.VodTaskType === SkillType.AITranslation) { |
| 338 | const translationResponse = apiResponse as AITranslationApiResponse |
| 339 | const projectInfo = translationResponse.AITranslation?.ProjectInfo |
| 340 | if (projectInfo?.OutputVideo?.Url) { |
| 341 | const result = await this.assetsService.uploadFromUrl(task.userId, { |
| 342 | url: projectInfo.OutputVideo.Url, |
| 343 | type: AssetType.AideoOutput, |
| 344 | }, task.model || 'aideo') |
| 345 | if (result && projectInfo.OutputVideo) { |
| 346 | projectInfo.OutputVideo.Url = this.assetsService.buildUrl(result.asset.path) |
| 347 | } |
| 348 | } |
| 349 | } |
| 350 | else if (apiResponse.VodTaskType === SkillType.Erase) { |
| 351 | const eraseResponse = apiResponse as EraseApiResponse |
| 352 | const erase = eraseResponse.Erase |
| 353 | const file = erase?.Output?.Task?.Erase?.File |
| 354 | |
| 355 | if (file?.FileName) { |
| 356 | const outputUrl = await VolcengineVideoUtils.saveVideoFromFileName( |
| 357 | file.FileName, |
| 358 | task.userId, |
| 359 | `${task.id}-erase`, |
| 360 | task.model || 'aideo', |
| 361 | this.assetsService, |
| 362 | this.logger, |
| 363 | AssetType.AideoOutput, |
| 364 | ) |
| 365 | if (outputUrl && file) { |
| 366 | file.url = outputUrl |
| 367 | } |
| 368 | } |
| 369 | } |
| 370 | else if (apiResponse.VodTaskType === SkillType.Highlight) { |
| 371 | const highlightResponse = apiResponse as HighlightApiResponse |
| 372 | const highlight = highlightResponse.Highlight |
| 373 | if (highlight?.Edits) { |
| 374 | for (const edit of highlight.Edits) { |
| 375 | if (edit.Vid) { |
| 376 | const outputUrl = await this.saveVideoFromVid(edit.Vid, task, 'highlight') |
| 377 | if (outputUrl) { |
| 378 | edit.url = outputUrl |
| 379 | } |
| 380 | } |
| 381 | } |
| 382 | } |
| 383 | } |
| 384 | else if (apiResponse.VodTaskType === SkillType.VCreative) { |
| 385 | const vCreativeResponse = apiResponse as VCreativeApiResponse |
| 386 | const vCreative = vCreativeResponse.VCreative |
| 387 | if (!vCreative) { |
| 388 | return |
| 389 | } |
| 390 | |
| 391 | if (vCreative.Status === VCreativeStatus.Success) { |
| 392 | const outputJson = vCreative.OutputJson |
| 393 | // 支持新版本和旧版本的数据结构 |
| 394 | const vid = outputJson.Result?.Vid || outputJson.vid |
| 395 | if (vid) { |
| 396 | const outputUrl = await this.saveVideoFromVid(vid, task, 'vcreative') |
| 397 | |
| 398 | if (outputUrl) { |
| 399 | if (outputJson.Result) { |
| 400 | outputJson.Result.url = outputUrl |
| 401 | } |
| 402 | else { |
| 403 | outputJson.url = outputUrl |
| 404 | } |
| 405 | this.logger.debug({ outputUrl }, '[VCreative] 视频已保存') |
| 406 | } |
| 407 | else { |
| 408 | this.logger.error({ vid }, '[VCreative] 视频保存失败') |
| 409 | } |
| 410 | } |
| 411 | else { |
| 412 | this.logger.warn({ outputJson }, '[VCreative] 无法从 outputJson 中提取 VID') |
| 413 | } |
| 414 | } |
| 415 | } |
| 416 | } |
| 417 | |
| 418 | /** |
| 419 | * 查询 Aideo 任务状态 |
| 420 | */ |
| 421 | async getAideoTask(request: UserGetAideoTaskQueryDto) { |
| 422 | const { userId, userType, taskId } = request |
| 423 | |
| 424 | const aiLog = await this.aiLogRepo.getByIdAndUserId(taskId, userId, userType) |
| 425 | |
| 426 | if (!aiLog || aiLog.type !== AiLogType.Aideo || aiLog.channel !== AiLogChannel.Volcengine) { |
| 427 | throw new AppException(ResponseCode.InvalidAiTaskId) |
| 428 | } |
| 429 | |
| 430 | return this.transformToResponseVo(aiLog) |
| 431 | } |
| 432 | |
| 433 | /** |
| 434 | * 列表查询 Aideo 任务 |
| 435 | */ |
| 436 | async listAideoTasks(request: UserListAideoTasksQueryDto) { |
| 437 | const { userId, userType, ...pagination } = request |
| 438 | |
| 439 | const [aiLogs, total] = await this.aiLogRepo.listWithPagination({ |
| 440 | ...pagination, |
| 441 | userId, |
| 442 | userType, |
| 443 | type: AiLogType.Aideo, |
| 444 | channel: AiLogChannel.Volcengine, |
| 445 | }) |
| 446 | |
| 447 | return [ |
| 448 | await Promise.all(aiLogs.map(log => this.transformToResponseVo(log))), |
| 449 | total, |
| 450 | ] as const |
| 451 | } |
| 452 | |
| 453 | /** |
| 454 | * 转换为响应 VO |
| 455 | * 支持两种任务类型: |
| 456 | * 1. 通用 Aideo 任务(model: 'aideo') |
| 457 | * 2. 视频风格转换任务(model: 'video-style-transfer') |
| 458 | */ |
| 459 | private transformToResponseVo(aiLog: AiLog) { |
| 460 | // 处理视频风格转换任务 |
| 461 | if (aiLog.model === 'video-style-transfer') { |
| 462 | const response = aiLog.response as StyleTransferAiLogResponse | undefined |
| 463 | |
| 464 | return { |
| 465 | taskId: aiLog.id, |
| 466 | model: aiLog.model, |
| 467 | status: aiLog.status === AiLogStatus.Success |
| 468 | ? AideoTaskStatus.Completed |
| 469 | : aiLog.status === AiLogStatus.Failed |
| 470 | ? AideoTaskStatus.Failed |
| 471 | : AideoTaskStatus.Processing, |
| 472 | outputVid: response?.outputVid, |
| 473 | outputUrl: response?.outputUrl, |
| 474 | errorMessage: aiLog.errorMessage, |
| 475 | createdAt: aiLog.startedAt, |
| 476 | updatedAt: aiLog.updatedAt || aiLog.startedAt, |
| 477 | } |
| 478 | } |
| 479 | |
| 480 | // 处理通用 Aideo 任务 |
| 481 | const response = aiLog.response as AideoAiLogResponse | undefined |
| 482 | const error = response?.ApiResponses?.[0]?.Error |
| 483 | const status = response?.Status && Object.values(AideoTaskStatus).includes(response.Status as AideoTaskStatus) |
| 484 | ? response.Status as AideoTaskStatus |
| 485 | : aiLog.status === AiLogStatus.Success |
| 486 | ? AideoTaskStatus.Completed |
| 487 | : aiLog.status === AiLogStatus.Failed |
| 488 | ? AideoTaskStatus.Failed |
| 489 | : AideoTaskStatus.Processing |
| 490 | const skillType = response?.SkillType && Object.values(SkillType).includes(response.SkillType as SkillType) |
| 491 | ? response.SkillType as SkillType |
| 492 | : undefined |
| 493 | |
| 494 | return { |
| 495 | taskId: aiLog.id, |
| 496 | model: aiLog.model, |
| 497 | status, |
| 498 | skillType, |
| 499 | skillParams: response?.SkillParams ? JSON.stringify(response.SkillParams) : undefined, |
| 500 | apiResponses: response?.ApiResponses, |
| 501 | error: error ? { code: error.Code, message: error.Message } : undefined, |
| 502 | errorMessage: aiLog.errorMessage, |
| 503 | createdAt: aiLog.startedAt, |
| 504 | updatedAt: aiLog.updatedAt || aiLog.startedAt, |
| 505 | } |
| 506 | } |
| 507 | |
| 508 | // ========== 委托方法(保持向后兼容) ========== |
| 509 | |
| 510 | /** |
| 511 | * 提交视频风格转换任务 |
| 512 | * @deprecated 直接使用 VideoStyleTransferService.submitVideoStyleTransferTask |
| 513 | */ |
| 514 | async submitVideoStyleTransferTask( |
| 515 | request: UserSubmitVideoStyleTransferRequest, |
| 516 | ): Promise<{ taskId: string }> { |
| 517 | return this.videoStyleTransferService.submitVideoStyleTransferTask(request) |
| 518 | } |
| 519 | |
| 520 | /** |
| 521 | * 获取视频风格转换任务结果 |
| 522 | * @deprecated 直接使用 VideoStyleTransferService.getVideoStyleTransferTask |
| 523 | */ |
| 524 | async getVideoStyleTransferTask( |
| 525 | request: UserGetVideoStyleTransferTaskRequest, |
| 526 | ) { |
| 527 | return this.videoStyleTransferService.getVideoStyleTransferTask(request) |
| 528 | } |
| 529 | |
| 530 | /** |
| 531 | * 提交短剧解说任务 |
| 532 | * @deprecated 直接使用 DramaRecapService.submitDramaRecapTask |
| 533 | */ |
| 534 | async submitDramaRecapTask( |
| 535 | request: UserSubmitDramaRecapTaskRequest, |
| 536 | ): Promise<{ taskId: string, dramaScriptTaskId: string }> { |
| 537 | return this.dramaRecapService.submitDramaRecapTask(request) |
| 538 | } |
| 539 | |
| 540 | /** |
| 541 | * 获取短剧解说任务结果 |
| 542 | * @deprecated 直接使用 DramaRecapService.getDramaRecapTask |
| 543 | */ |
| 544 | async getDramaRecapTask( |
| 545 | request: UserGetDramaRecapTaskRequest, |
| 546 | ): Promise<{ |
| 547 | taskId: string |
| 548 | status: DramaRecapTaskStatus |
| 549 | outputVid?: string |
| 550 | outputUrl?: string |
| 551 | errorMessage?: string |
| 552 | }> { |
| 553 | return this.dramaRecapService.getDramaRecapTask(request) |
| 554 | } |
| 555 | } |
| 556 |