fix: enforce Blender preview and continuity limits

This commit is contained in:
NINI
2026-10-09 23:24:06 +09:00
parent d4f1a800aa
commit 93b9f92f91
8 changed files with 77 additions and 10 deletions
+21
View File
@@ -1866,6 +1866,27 @@
]
}
}
},
{
"if": {
"properties": {
"kind": {
"const": "video"
}
}
},
"then": {
"properties": {
"duration_ms": {
"type": [
"integer",
"null"
],
"minimum": 1,
"maximum": 5000
}
}
}
}
]
}
+46
View File
@@ -83,12 +83,17 @@ def semantic_errors(data, context, complete=False):
require(profile["width"] * 16 == profile["height"] * 9, "RenderProfile은 9:16 필요")
cuts = context.get("07-cuts", {}).get("payload", {}).get("cuts", [])
cut_ids = {cut["id"] for cut in cuts}
if stage == "08-scenes" and complete:
require(preview is not None, "대표 품질 검토 자료 필요")
if stage == "08-scenes" and preview:
indexed(preview["samples"], "id")
require(bool(preview["samples"]), "대표 샘플 필요")
for sample in preview["samples"]:
require(set(sample["cut_ids"]) <= cut_ids, "대표 샘플의 알 수 없는 컷")
require(sample["media"]["verified"], "대표 미디어 미검증")
width, height = sample["media"]["width"], sample["media"]["height"]
require(isinstance(width, int) and isinstance(height, int)
and width * 16 == height * 9, "대표 실제 미디어는 9:16 필요")
require(preview["scene_file"]["verified"], "대표 .blend 미검증")
if stage == "10-references":
indexed(request["items"], "id")
@@ -239,6 +244,9 @@ class ContractTests(unittest.TestCase):
self.invalid(bad)
def test_profile_and_duration_constraints(self):
for stage in ("10-references", "11-images", "12-motion"):
profile = read("templates/stages/" + stage + ".json")["payload"]["request"]["render_profile"]
self.assertEqual((profile["samples"], profile["seed"]), (64, 0))
for key, value in [("engine", "UNKNOWN"), ("device", "CPU"), ("fps", 0),
("samples", 0), ("width", 0), ("height", 0), ("seed", -1)]:
bad = samples()["11-images"]
@@ -298,6 +306,44 @@ class ContractTests(unittest.TestCase):
self.assertIn(".blend는 media가 아니다", semantic_errors(data,
{"07-cuts": {"payload": {"cuts": [{"id": "c1"}]}}}))
def test_preview_video_duration_and_actual_aspect(self):
data = samples()["08-scenes"]
data["payload"]["preview"] = dict(scene_file=dict(BLEND, verified=True,
sha256="a" * 64, blender_version="5.2.2"), render_profile=deepcopy(PROFILE),
samples=[dict(id="p1", cut_ids=["c1"], media=dict(IMAGE, kind="video",
path="examples/blender-contract/preview.mp4", verified=True,
duration_ms=5000, width=1080, height=1920))])
context = {"07-cuts": {"payload": {"cuts": [{"id": "c1"}]}}}
for duration in (1, 5000):
with self.subTest(valid_duration=duration):
data["payload"]["preview"]["samples"][0]["media"]["duration_ms"] = duration
self.valid(data)
self.assertEqual(semantic_errors(data, context, complete=True), [])
for duration in (0, 5001, 6000):
with self.subTest(invalid_duration=duration):
bad = deepcopy(data)
bad["payload"]["preview"]["samples"][0]["media"]["duration_ms"] = duration
self.invalid(bad)
for width, height in ((1920, 1080), (1080, 1800)):
with self.subTest(invalid_aspect=(width, height)):
bad = deepcopy(data)
bad["payload"]["preview"]["samples"][0]["media"].update(width=width, height=height)
self.valid(bad)
self.assertTrue(semantic_errors(bad, context, complete=True))
for field in ("duration_ms", "width", "height"):
with self.subTest(verified_missing_metadata=field):
bad = deepcopy(data)
bad["payload"]["preview"]["samples"][0]["media"][field] = None
self.invalid(bad)
bad = deepcopy(data)
bad["payload"]["preview"]["samples"][0]["media"].update(verified=False,
duration_ms=None, width=None, height=None)
self.valid(bad)
self.assertTrue(semantic_errors(bad, context, complete=True))
bad = samples()["08-scenes"]
self.valid(bad)
self.assertTrue(semantic_errors(bad, context, complete=True))
if __name__ == "__main__":
unittest.main(verbosity=2)