#!/usr/bin/env python3 """Validate the executable cross-document contract of one creator-first episode.""" from __future__ import annotations import argparse import json import re import sys from pathlib import Path, PurePosixPath from collections.abc import Iterable, Mapping from typing import NamedTuple, Optional MINIMUM_PYTHON = (3, 9) if sys.version_info < MINIMUM_PYTHON: raise SystemExit( "creator_markdown_check.py requires Python {}.{}, running {}.{}".format( *MINIMUM_PYTHON, sys.version_info.major, sys.version_info.minor ) ) REQUIRED_DOCUMENTS = ( "剧本.md", "视觉设定.md", "图片提示词.md", "分镜.md", "视频提示词.md", ) SECTION_RE = re.compile(r"^## ((?:SHOT|MOTION|GROUP)-[A-Z0-9-]+)\b", re.MULTILINE) ANY_SECTION_RE = re.compile(r"^## ", re.MULTILINE) IMG_RE = re.compile(r"\b(IMG-[A-Z0-9-]+)《([^》]+)》(控制:([^)]+))") # 用途 is optional in the pattern on purpose: a declaration written before this # field existed still parses, so the creator gets "REF 缺少用途" instead of the # generic syntax error that gives no hint about what to add. REF_RE = re.compile( r"(REF-[A-Z0-9-]+)(顺序:([1-9]\d*))· " r"([^;\n]+?\.(?:png|jpe?g|webp))《([^》\n]+)》" r"((?:用途:([^;)\n]+);)?控制:([^;)]+);不得控制:([^)]+))", re.IGNORECASE, ) # The same slot grammar, but the locator is a document entry instead of a file. # A creator who generates in an external tool never has the file inside the # project, and the copyable body only ever needed 顺序 and 用途 -- the path is a # production input, not a prompt input. PLAN says "this picture does not exist # here; attach it yourself in this order", which is a different claim from # 「已有图片」 and from 「待补」. PLAN_RE = re.compile( r"(PLAN-[A-Z0-9-]+)(顺序:([1-9]\d*))· " r"((?:IMG|SHOT)-[A-Z0-9-]+)《([^》\n]+)》" r"((?:用途:([^;)\n]+);)?控制:([^;)]+);不得控制:([^)]+))", re.IGNORECASE, ) # One picture answers one question. 「同一张图承担两个作用时分开说明」 in # references/reference-roles.md is what makes a slot translatable into a # provider role, so the vocabulary is closed rather than free prose. REF_PURPOSES = ( "身份", "造型状态", "地理", "构图", "尺度", "效果", "起始帧", "结束帧", "风格", ) # A character's voice travels on its own line, `- 参考音频:`, never inside # 输入参考图: the picture slots number `@图片N`/``/`图N` and the audio # slots number `@音频N`/`