feat(storyboard): 故事板改分镜制(单场独立重跑/历史)+ 视频过审闸 + 审核前端如实化
三块改动在 services/views/pipeline/App/api 等文件按行交织、且过审闸后端依赖故事板新 模型,无法拆成各自独立可编译的提交,故合并为一次: · 故事板分镜制:新增 StoryboardShot/StoryboardShotVersion(对标 VideoSegment/Version) + 数据迁移(采用版每帧转一 shot+已采用版本)。每镜一个 shot,各自历史版本,可单场 重跑、逐场切换/采用,新增「开始生成故事板/单场重跑/采用某版」端点。重跑非阻塞(后台 静默轮询出图,不锁其它场按钮);历史「查看」与「采用」分离(切版本不丢在制重跑)。 视频取分镜图/删分镜清理/过审闸全部改读 storyboard_shots。 · 视频生成前过审闸:点生成校验含真人脸的人物立绘/故事板分镜是否过审,未过审弹窗列出 哪一镜/哪个,可一键送审,挡住不生成(collect_video_review_blockers + precheck 端点)。 · 审核前端如实化:不再 `|| "processing"` 伪造审核中;送审失败经 onNotify 如实提示; 审核轮询扩到视频阶段,送审后能看着盾变绿。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+134
@@ -0,0 +1,134 @@
|
||||
# Generated by Django 5.1.15 on 2026-06-24 08:01
|
||||
|
||||
import django.db.models.deletion
|
||||
import uuid
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("ai", "0016_person_triview_16_9"),
|
||||
("assets", "0006_alter_asset_category_model"),
|
||||
("projects", "0004_project_projects_pr_team_id_e667f8_idx"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="StoryboardShot",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.UUIDField(
|
||||
default=uuid.uuid4,
|
||||
editable=False,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
),
|
||||
),
|
||||
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||
("updated_at", models.DateTimeField(auto_now=True)),
|
||||
("sort_order", models.PositiveIntegerField(default=0)),
|
||||
(
|
||||
"status",
|
||||
models.CharField(
|
||||
choices=[
|
||||
("not_started", "Not Started"),
|
||||
("queued", "Queued"),
|
||||
("running", "Running"),
|
||||
("succeeded", "Succeeded"),
|
||||
("failed", "Failed"),
|
||||
],
|
||||
default="not_started",
|
||||
max_length=32,
|
||||
),
|
||||
),
|
||||
("prompt", models.TextField(blank=True)),
|
||||
("error_message", models.TextField(blank=True)),
|
||||
(
|
||||
"project",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="storyboard_shots",
|
||||
to="projects.project",
|
||||
),
|
||||
),
|
||||
(
|
||||
"script_segment",
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="storyboard_shots",
|
||||
to="projects.scriptsegment",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"ordering": ["sort_order", "created_at"],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="StoryboardShotVersion",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.UUIDField(
|
||||
default=uuid.uuid4,
|
||||
editable=False,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
),
|
||||
),
|
||||
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||
("updated_at", models.DateTimeField(auto_now=True)),
|
||||
("prompt", models.TextField(blank=True)),
|
||||
("is_adopted", models.BooleanField(default=False)),
|
||||
("metadata", models.JSONField(blank=True, default=dict)),
|
||||
(
|
||||
"asset",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="storyboard_shot_versions",
|
||||
to="assets.asset",
|
||||
),
|
||||
),
|
||||
(
|
||||
"shot",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="versions",
|
||||
to="projects.storyboardshot",
|
||||
),
|
||||
),
|
||||
(
|
||||
"task",
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="storyboard_shot_versions",
|
||||
to="ai.aitask",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"ordering": ["-created_at"],
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="storyboardshot",
|
||||
name="adopted_version",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="adopted_by_shots",
|
||||
to="projects.storyboardshotversion",
|
||||
),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name="storyboardshot",
|
||||
unique_together={("project", "sort_order")},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,50 @@
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def forwards(apps, schema_editor):
|
||||
"""把旧「整版制」故事板迁到新「分镜制」:每个项目只取采用版(无采用版则取最新版)的每一帧,
|
||||
建一个 StoryboardShot + 一条已采用 StoryboardShotVersion(沿用同一张图)。旧的其它整版历史丢弃。"""
|
||||
Project = apps.get_model("projects", "Project")
|
||||
StoryboardVersion = apps.get_model("projects", "StoryboardVersion")
|
||||
StoryboardShot = apps.get_model("projects", "StoryboardShot")
|
||||
StoryboardShotVersion = apps.get_model("projects", "StoryboardShotVersion")
|
||||
|
||||
for project in Project.objects.all().iterator():
|
||||
adopted = (
|
||||
StoryboardVersion.objects.filter(project=project, is_adopted=True).order_by("-created_at").first()
|
||||
or StoryboardVersion.objects.filter(project=project).order_by("-created_at").first()
|
||||
)
|
||||
if adopted is None:
|
||||
continue
|
||||
seen_orders = set()
|
||||
for frame in adopted.frames.order_by("sort_order", "created_at"):
|
||||
if frame.sort_order in seen_orders: # 同序帧只保第一张,守 (project, sort_order) 唯一约束
|
||||
continue
|
||||
seen_orders.add(frame.sort_order)
|
||||
shot = StoryboardShot.objects.create(
|
||||
project=project,
|
||||
script_segment=frame.script_segment,
|
||||
sort_order=frame.sort_order,
|
||||
status="succeeded",
|
||||
prompt=frame.prompt or "",
|
||||
)
|
||||
version = StoryboardShotVersion.objects.create(
|
||||
shot=shot, asset=frame.asset, prompt=frame.prompt or "", is_adopted=True,
|
||||
)
|
||||
shot.adopted_version = version
|
||||
shot.save(update_fields=["adopted_version"])
|
||||
|
||||
|
||||
def backwards(apps, schema_editor):
|
||||
StoryboardShot = apps.get_model("projects", "StoryboardShot")
|
||||
StoryboardShot.objects.all().delete() # version 随 shot 级联删
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("projects", "0005_storyboardshot_storyboardshotversion_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(forwards, backwards),
|
||||
]
|
||||
Reference in New Issue
Block a user