fix: enforce Blender preview and continuity limits
This commit is contained in:
@@ -1866,6 +1866,27 @@
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"properties": {
|
||||
"kind": {
|
||||
"const": "video"
|
||||
}
|
||||
}
|
||||
},
|
||||
"then": {
|
||||
"properties": {
|
||||
"duration_ms": {
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"minimum": 1,
|
||||
"maximum": 5000
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user