Files
video-gen/video-gen-api/app/schemas/upload_resource.py
T
2026-07-09 09:10:49 +08:00

186 lines
9.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from __future__ import annotations
from typing import Literal
from pydantic import BaseModel, ConfigDict, Field, field_validator
from app.enums.upload_resource import (
UploadResourceBindStatusEnum,
UploadResourceDeletePolicyEnum,
UploadResourceModuleEnum,
UploadResourceTypeEnum,
)
from app.schemas.common import NaiveDatetimeOptional
class UploadResourceMediaReferenceOut(BaseModel):
"""AI 创作可直接复用的上传素材参考结构。"""
name: str | None = Field(None, description="参考素材名称,前端展示用")
type: Literal["image", "video", "audio"] = Field(..., description="参考素材类型:image=图片,video=视频,audio=音频")
url: str = Field(..., description="参考素材 URL,创建 AI 创作任务时传入 media_references[].url")
label: str | None = Field("", description="前端展示标签,默认空,由 AI 创作页重新计算")
duration: float | None = Field(None, ge=0, description="素材时长秒数,type=video/audio 时必须有有效值")
source: Literal["upload_resource"] = Field("upload_resource", description="参考素材来源固定为 upload_resource")
upload_resource_id: str = Field(..., description="UploadResource 本地资源账本 ID")
class UploadResourceHistoryItemOut(BaseModel):
"""上传资源历史素材项。"""
model_config = ConfigDict(
json_schema_extra={
"example": {
"id": "0019e0a448a23114888",
"source_type": "upload_resource",
"history_source": "upload_resource",
"history_source_label": "历史上传素材",
"module": "common",
"module_label": "普通上传",
"resource_type": "video",
"resource_type_label": "视频",
"resource_url": "/uploads/videos/2026/07/08/video_ref_xxx.mp4",
"display_url": "/uploads/videos/2026/07/08/video_ref_xxx.mp4",
"preview_url": "/uploads/videos/2026/07/08/video_ref_xxx.mp4",
"video_url": "/uploads/videos/2026/07/08/video_ref_xxx.mp4",
"duration_seconds": 5.2,
"bind_status": "pending",
"delete_policy": "user_deletable",
"deletable": True,
"media_reference": {
"name": "video_ref_xxx.mp4",
"type": "video",
"url": "/uploads/videos/2026/07/08/video_ref_xxx.mp4",
"duration": 5.2,
"source": "upload_resource",
"upload_resource_id": "0019e0a448a23114888",
},
"created_at": "2026-07-08T13:20:00",
}
}
)
id: str = Field(..., description="UploadResource.id")
source_type: Literal["upload_resource"] = Field("upload_resource", description="前端判定来源固定值")
history_source: Literal["upload_resource"] = Field("upload_resource", description="素材云历史来源固定为 upload_resource")
history_source_label: str = Field("历史上传素材", description="素材云历史来源中文名称")
module: str = Field(..., description="上传资源所属模块:common=普通上传,hot_opening_replicate=爆款开头复刻,shot_replicate=拆镜复刻")
module_label: str = Field(..., description="上传资源所属模块中文名称")
resource_type: Literal["image", "video", "audio"] = Field(..., description="资源类型:image=图片,video=视频,audio=音频")
resource_type_label: str = Field(..., description="资源类型中文名称")
resource_url: str = Field(..., description="原始资源 URL,可传给 AI 创作 media_references[].url")
display_url: str = Field(..., description="前端展示 URL。当前普通上传资源通常与 resource_url 一致")
preview_url: str = Field(..., description="前端预览 URL。当前普通上传资源通常与 display_url 一致")
image_url: str | None = Field(None, description="图片资源 URLresource_type=image 时有值")
video_url: str | None = Field(None, description="视频资源 URLresource_type=video 时有值")
audio_url: str | None = Field(None, description="音频资源 URLresource_type=audio 时有值")
file_name: str | None = Field(None, description="文件名")
file_ext: str | None = Field(None, description="文件扩展名")
mime_type: str | None = Field(None, description="MIME 类型")
file_size_bytes: int = Field(0, description="文件大小,单位字节")
duration_seconds: float | None = Field(None, description="视频/音频时长秒数")
width: int | None = Field(None, description="图片或视频宽度,未识别时为空")
height: int | None = Field(None, description="图片或视频高度,未识别时为空")
bind_status: str = Field(..., description="绑定状态。历史素材接口只返回 pending=未绑定")
delete_policy: str = Field(..., description="删除策略。历史素材接口只返回 user_deletable=用户可删除")
deletable: bool = Field(True, description="是否允许当前用户删除。历史素材接口只返回 true")
media_reference: UploadResourceMediaReferenceOut = Field(..., description="AI 创作可直接复用的参考素材对象")
created_at: NaiveDatetimeOptional = Field(None, description="上传时间")
updated_at: NaiveDatetimeOptional = Field(None, description="更新时间")
class UploadResourceHistoryDayGroupOut(BaseModel):
"""上传资源历史按天分组响应项。"""
generated_date: str = Field(..., description="上传日期,格式 YYYY-MM-DD。字段名沿用生成历史,方便前端复用分组逻辑")
total: int = Field(..., description="当前日期下可展示/可复用/可删除的上传素材总数")
page: int = Field(1, description="当前日期下 items 的页码,分组接口固定为 1")
items: list[UploadResourceHistoryItemOut] = Field(default_factory=list, description="当前日期倒序前若干条上传素材")
class UploadResourceHistoryGroupedOut(BaseModel):
"""上传资源历史日期分组列表响应体。"""
total_days: int = Field(..., description="符合条件的上传素材日期分组总数")
page: int = Field(..., description="日期分组页码,从 1 开始")
page_size: int = Field(..., description="日期分组每页数量,最大 10")
groups: list[UploadResourceHistoryDayGroupOut] = Field(default_factory=list, description="上传日期倒序分组列表")
class UploadResourceHistoryDayItemsOut(BaseModel):
"""指定日期下上传资源历史分页响应体。"""
generated_date: str = Field(..., description="当前查询上传日期,格式 YYYY-MM-DD")
total: int = Field(..., description="当前日期下符合条件的上传素材总数")
page: int = Field(..., description="当前日期下分页页码,从 1 开始")
page_size: int = Field(..., description="当前日期下每页返回数量,最大 100")
items: list[UploadResourceHistoryItemOut] = Field(default_factory=list, description="当前日期下上传素材列表,按上传时间倒序排列")
class UploadResourceHistoryBatchDeleteRequest(BaseModel):
"""批量删除上传历史素材请求体。"""
resource_ids: list[str] = Field(
...,
min_length=1,
max_length=30,
description="UploadResource.id 列表。一次最多 30 条,不允许重复",
examples=[["0019e0a448a23114888", "0019e0a448a23114889"]],
)
@field_validator("resource_ids")
@classmethod
def validate_resource_ids(cls, values: list[str]) -> list[str]:
cleaned = [str(value).strip() for value in values if str(value or "").strip()]
if not cleaned:
raise ValueError("resource_ids 不能为空")
if len(cleaned) > 30:
raise ValueError("单次最多删除 30 条上传素材")
if len(cleaned) != len(set(cleaned)):
raise ValueError("resource_ids 不允许重复")
return cleaned
class UploadResourceCleanupOut(BaseModel):
"""真实文件清理结果。"""
matched: int = Field(0, description="匹配到待清理资源数量")
deleted: int = Field(0, description="真实文件删除成功数量")
missing: int = Field(0, description="真实文件已不存在数量")
failed: int = Field(0, description="真实文件删除失败数量")
legacy_deleted: int = Field(0, description="兼容历史路径删除成功数量")
legacy_missing: int = Field(0, description="兼容历史路径不存在数量")
legacy_failed: int = Field(0, description="兼容历史路径删除失败数量")
class UploadResourceHistoryBatchDeleteOut(BaseModel):
"""批量删除上传历史素材响应体。"""
message: str = Field("删除成功", description="操作结果消息")
requested_count: int = Field(..., description="请求删除数量")
deleted_count: int = Field(..., description="成功软删数量")
requested_ids: list[str] = Field(default_factory=list, description="请求删除的 UploadResource.id 列表")
deleted_ids: list[str] = Field(default_factory=list, description="已软删的 UploadResource.id 列表")
released_size_bytes: int = Field(0, description="已释放的上传容量字节数")
cleanup: UploadResourceCleanupOut = Field(default_factory=UploadResourceCleanupOut, description="commit 成功后的真实文件清理结果")
UPLOAD_RESOURCE_HISTORY_ALLOWED_RESOURCE_TYPES = {
UploadResourceTypeEnum.IMAGE.value,
UploadResourceTypeEnum.VIDEO.value,
UploadResourceTypeEnum.AUDIO.value,
}
UPLOAD_RESOURCE_HISTORY_ALLOWED_BIND_STATUS = UploadResourceBindStatusEnum.PENDING.value
UPLOAD_RESOURCE_HISTORY_ALLOWED_DELETE_POLICY = UploadResourceDeletePolicyEnum.USER_DELETABLE.value
UPLOAD_RESOURCE_HISTORY_ALLOWED_MODULES = {
UploadResourceModuleEnum.COMMON.value,
UploadResourceModuleEnum.HOT_OPENING_REPLICATE.value,
UploadResourceModuleEnum.SHOT_REPLICATE.value,
}