| 1 | import { createPaginationVo, createZodDto, FileUtil, zodI18nString } from '@yikart/common' |
| 2 | import { z } from 'zod' |
| 3 | import { AiLogChannel } from '../enums' |
| 4 | |
| 5 | // 通用视频生成响应 |
| 6 | const videoGenerationResponseSchema = z.object({ |
| 7 | id: z.string().describe('任务 ID'), |
| 8 | status: z.string().describe('任务状态'), |
| 9 | }) |
| 10 | |
| 11 | export class VideoGenerationResponseVo extends createZodDto(videoGenerationResponseSchema) {} |
| 12 | |
| 13 | // 视频任务输入参数 |
| 14 | const videoTaskInputSchema = z.object({ |
| 15 | prompt: z.string().describe('提示词'), |
| 16 | groupId: z.string().optional().describe('素材组 ID'), |
| 17 | image: z.string().or(z.string().array()).optional().describe('图片 URL'), |
| 18 | images: z.array(z.string()).optional().describe('参考图片 URL 列表'), |
| 19 | audios: z.array(z.string()).optional().describe('参考音频 URL 列表'), |
| 20 | duration: z.number().optional().describe('时长(秒)'), |
| 21 | aspectRatio: z.string().optional().describe('宽高比'), |
| 22 | resolution: z.string().optional().describe('分辨率'), |
| 23 | videoUrl: z.string().optional().describe('视频 URL(视频编辑模式)'), |
| 24 | videos: z.array(z.string()).optional().describe('参考视频 URL 列表'), |
| 25 | watermark: z.boolean().optional().describe('是否带水印'), |
| 26 | }) |
| 27 | |
| 28 | export type VideoTaskInput = z.infer<typeof videoTaskInputSchema> |
| 29 | |
| 30 | // 通用视频任务状态响应 |
| 31 | const videoTaskStatusResponseSchema = z.object({ |
| 32 | id: z.string().describe('任务 ID'), |
| 33 | model: z.string().describe('模型名称'), |
| 34 | status: z.string().describe('任务状态'), |
| 35 | input: videoTaskInputSchema.describe('输入参数'), |
| 36 | videoUrl: FileUtil.zodBuildUrl().nullable().optional().describe('生成的视频 URL'), |
| 37 | coverUrl: FileUtil.zodBuildUrl().nullable().optional().describe('生成视频的封面 URL'), |
| 38 | mediaId: z.string().optional().describe('保存后的素材 ID'), |
| 39 | groupId: z.string().optional().describe('保存到的素材组 ID'), |
| 40 | error: z.object({ |
| 41 | message: z.string().describe('错误信息'), |
| 42 | }).optional().describe('错误信息'), |
| 43 | submittedAt: z.coerce.date().describe('提交时间'), |
| 44 | startedAt: z.coerce.date().describe('开始时间'), |
| 45 | finishedAt: z.coerce.date().optional().describe('完成时间'), |
| 46 | }) |
| 47 | |
| 48 | export class VideoTaskStatusResponseVo extends createZodDto(videoTaskStatusResponseSchema) {} |
| 49 | |
| 50 | export class ListVideoTasksResponseVo extends createPaginationVo(videoTaskStatusResponseSchema) {} |
| 51 | |
| 52 | const videoModelInputConstraintSchema = z.object({ |
| 53 | maxCount: z.number().optional().describe('最大输入数量'), |
| 54 | formats: z.array(z.string()).optional().describe('支持的文件格式'), |
| 55 | minDuration: z.number().optional().describe('最小时长(秒)'), |
| 56 | maxDuration: z.number().optional().describe('单个文件最大时长(秒)'), |
| 57 | maxTotalDuration: z.number().optional().describe('总最大时长(秒)'), |
| 58 | maxSizeMb: z.number().optional().describe('单个文件最大大小(MB)'), |
| 59 | minAspectRatio: z.number().optional().describe('最小宽高比'), |
| 60 | maxAspectRatio: z.number().optional().describe('最大宽高比'), |
| 61 | minWidth: z.number().optional().describe('最小宽度'), |
| 62 | maxWidth: z.number().optional().describe('最大宽度'), |
| 63 | minPixels: z.number().optional().describe('最小像素数'), |
| 64 | maxPixels: z.number().optional().describe('最大像素数'), |
| 65 | minFps: z.number().optional().describe('最小帧率'), |
| 66 | maxFps: z.number().optional().describe('最大帧率'), |
| 67 | }) |
| 68 | |
| 69 | const videoModelInputConstraintsSchema = z.object({ |
| 70 | images: videoModelInputConstraintSchema.optional().describe('图片输入约束'), |
| 71 | videos: videoModelInputConstraintSchema.optional().describe('视频输入约束'), |
| 72 | audios: videoModelInputConstraintSchema.optional().describe('音频输入约束'), |
| 73 | }).optional().describe('多模态输入约束') |
| 74 | |
| 75 | // 视频生成模型参数 VO |
| 76 | export const videoGenerationModelSchema = z.object({ |
| 77 | name: z.string().describe('模型名称'), |
| 78 | description: z.string().describe('模型描述'), |
| 79 | summary: z.string().optional(), |
| 80 | logo: z.string().optional(), |
| 81 | tags: z.array(zodI18nString()).default([]), |
| 82 | mainTag: z.string().optional(), |
| 83 | channel: z.enum(AiLogChannel).describe('渠道'), |
| 84 | modes: z.array(z.enum(['text2video', 'image2video', 'flf2video', 'lf2video', 'multi-image2video', 'multi-ref', 'video2video'])).describe('支持的模式'), |
| 85 | resolutions: z.array(z.string()).describe('支持的尺寸'), |
| 86 | durations: z.array(z.number()).describe('支持的时长'), |
| 87 | maxInputImages: z.number().describe('最大输入图片数'), |
| 88 | inputConstraints: videoModelInputConstraintsSchema, |
| 89 | aspectRatios: z.array(z.string()).describe('支持的宽高比列表'), |
| 90 | defaults: z.object({ |
| 91 | resolution: z.string().optional(), |
| 92 | aspectRatio: z.string().optional(), |
| 93 | duration: z.number().optional(), |
| 94 | }).describe('默认值'), |
| 95 | }) |
| 96 | |
| 97 | export class VideoGenerationModelParamsVo extends createZodDto(videoGenerationModelSchema) {} |
| 98 |