feat(ai): 自由创作视频生成全量移植(/free-create)——Seedance 文/图生视频+按时长计费+人物素材库
后端: - free_video.py: 提交/轮询/收藏/软删全链路,复用 AITask(新增 is_deleted/is_favorited, migration 0022) - video_pricing.py: 按时长 token 计费(×1.10 buffer+clamp); video_errors.py 错误归一; media_probe.py 时长探测 - catalog/volcano: Seedance free-video 模型接入+seed(migration 0023) - 素材库: FreeAssetGroup/FreeAsset(火山 Assets API 引用登记, migration 0009)+ 上传/轮询/删除接口 - settings: FREE_VIDEO_MAX_CONCURRENT 团队并发闸(默认3); CELERY_TASK_ALWAYS_EAGER 本地联调开关(生产恒关) - 测试: test_free_video.py 新增; billing/products/projects tests 配套调整 前端: - /free-create 页面+components/free-create/ 全套(输入栏/@mention 素材引用/生成卡/视频详情弹窗/素材库弹窗) - api.ts/types.ts 扩展 free-video 与 free-assets 接口; 路由/侧边栏入口接入 bug/: 测试清单 (11)(12) 与截图归档 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -59,7 +59,11 @@ def _get_service():
|
||||
Credentials(ak, sk, SERVICE, REGION),
|
||||
10, 30,
|
||||
)
|
||||
actions = ["CreateAssetGroup", "CreateAsset", "ListAssetGroups", "ListAssets", "GetAsset", "DeleteAsset"]
|
||||
actions = [
|
||||
"CreateAssetGroup", "CreateAsset", "ListAssetGroups", "ListAssets", "GetAsset", "DeleteAsset",
|
||||
# 自由创作素材库(组/素材全生命周期)
|
||||
"GetAssetGroup", "UpdateAssetGroup", "UpdateAsset", "DeleteAssetGroup",
|
||||
]
|
||||
api_info = {a: ApiInfo("POST", "/", {"Action": a, "Version": API_VERSION}, {}, {}) for a in actions}
|
||||
return Service(service_info, api_info)
|
||||
|
||||
@@ -117,3 +121,49 @@ def list_asset_groups(page: int = 1, page_size: int = 20, name: str | None = Non
|
||||
{"Filter": filter_dict, "PageNumber": page, "PageSize": page_size, "ProjectName": _project()},
|
||||
)
|
||||
return result.get("Items", []), result.get("TotalCount", 0)
|
||||
|
||||
|
||||
def list_assets(group_ids: list | None = None, status: str | None = None,
|
||||
name: str | None = None, page: int = 1, page_size: int = 20) -> tuple:
|
||||
"""列组内素材。返回 (items, total_count)。"""
|
||||
filter_dict: dict = {"GroupType": "AIGC"}
|
||||
if group_ids:
|
||||
filter_dict["GroupIds"] = group_ids
|
||||
if status:
|
||||
filter_dict["Statuses"] = [status]
|
||||
if name:
|
||||
filter_dict["Name"] = name
|
||||
result = _do_request(
|
||||
"ListAssets",
|
||||
{"Filter": filter_dict, "PageNumber": page, "PageSize": page_size, "ProjectName": _project()},
|
||||
)
|
||||
return result.get("Items", []), result.get("TotalCount", 0)
|
||||
|
||||
|
||||
def get_asset_group(group_id: str) -> dict:
|
||||
return _do_request("GetAssetGroup", {"Id": group_id, "ProjectName": _project()})
|
||||
|
||||
|
||||
def update_asset_group(group_id: str, name: str | None = None, description: str | None = None) -> None:
|
||||
body: dict = {"Id": group_id, "ProjectName": _project()}
|
||||
if name is not None:
|
||||
body["Name"] = name
|
||||
if description is not None:
|
||||
body["Description"] = description
|
||||
_do_request("UpdateAssetGroup", body)
|
||||
|
||||
|
||||
def update_asset(asset_id: str, name: str | None = None) -> None:
|
||||
body: dict = {"Id": asset_id, "ProjectName": _project()}
|
||||
if name is not None:
|
||||
body["Name"] = name
|
||||
_do_request("UpdateAsset", body)
|
||||
|
||||
|
||||
def delete_asset(asset_id: str) -> None:
|
||||
_do_request("DeleteAsset", {"Id": asset_id, "ProjectName": _project()})
|
||||
|
||||
|
||||
def delete_asset_group(group_id: str) -> None:
|
||||
"""删组(远程级联删组内素材)。"""
|
||||
_do_request("DeleteAssetGroup", {"Id": group_id, "ProjectName": _project()})
|
||||
|
||||
Reference in New Issue
Block a user