{
	"$schema": "https://json-schema.org/draft/2020-12/schema",
	"$id": "https://vidia.kr/schemas/vidia-blueprint-1.schema.json",
	"title": "vidia-blueprint@1",
	"description": "비디아 영상 설계도. 영상 한 편을 똑같이 다시 만들 수 있는 대본 프롬프트·장면·키프레임·요소 시트·모델·음성·자막 설정과 제작 노하우를 담는다. 최대 4MB.",
	"type": "object",
	"additionalProperties": false,
	"patternProperties": {
		"^x-": {}
	},
	"required": [
		"schemaVersion",
		"meta",
		"video",
		"models",
		"speakers",
		"scenes"
	],
	"properties": {
		"schemaVersion": {
			"const": "vidia-blueprint@1"
		},
		"meta": {
			"type": "object",
			"properties": {
				"title": {
					"type": "string",
					"minLength": 1,
					"maxLength": 120
				},
				"summary": {
					"type": "string",
					"maxLength": 1000
				},
				"category": {
					"type": "string",
					"maxLength": 40,
					"description": "예: shorts-engineering"
				},
				"tags": {
					"type": "array",
					"items": {
						"type": "string",
						"minLength": 1,
						"maxLength": 30
					},
					"maxItems": 20
				},
				"language": {
					"type": "string",
					"maxLength": 20,
					"pattern": "^[a-z]{2,3}(-[A-Z]{2})?$",
					"default": "ko-KR"
				},
				"format": {
					"type": "string",
					"enum": [
						"shorts",
						"longform"
					]
				},
				"knowhow": {
					"type": "string",
					"maxLength": 20000,
					"description": "제작 노하우(markdown)"
				},
				"license": {
					"type": "string",
					"maxLength": 40,
					"default": "vidia-open-1"
				},
				"parent": {
					"type": [
						"object",
						"null"
					],
					"properties": {
						"blueprintId": {
							"type": "integer",
							"minimum": 1,
							"maximum": 9007199254740991
						},
						"versionId": {
							"type": "integer",
							"minimum": 1,
							"maximum": 9007199254740991
						}
					},
					"required": [
						"blueprintId",
						"versionId"
					],
					"patternProperties": {
						"^x-": {}
					},
					"additionalProperties": false,
					"description": "서버가 채운다(리믹스 원본).",
					"default": null
				},
				"source": {
					"type": [
						"object",
						"null"
					],
					"properties": {
						"format": {
							"type": "string",
							"minLength": 1,
							"maxLength": 60
						},
						"importedAt": {
							"type": "string",
							"minLength": 1,
							"maxLength": 40
						}
					},
					"required": [
						"format",
						"importedAt"
					],
					"patternProperties": {
						"^x-": {}
					},
					"additionalProperties": false,
					"description": "서버가 채운다(가져오기 원본 형식).",
					"default": null
				}
			},
			"required": [
				"title",
				"format"
			],
			"patternProperties": {
				"^x-": {}
			},
			"additionalProperties": false
		},
		"video": {
			"type": "object",
			"properties": {
				"aspectRatio": {
					"type": "string",
					"enum": [
						"9:16",
						"16:9",
						"1:1"
					]
				},
				"fps": {
					"const": 30,
					"default": 30
				},
				"resolution": {
					"type": "string",
					"enum": [
						"720p",
						"1080p"
					],
					"default": "720p"
				},
				"visualStyle": {
					"type": "string",
					"maxLength": 2000,
					"description": "모든 이미지 프롬프트 앞에 붙는 공통 스타일"
				},
				"negativeStyle": {
					"type": "string",
					"maxLength": 1000
				},
				"targetDurationSec": {
					"type": "object",
					"properties": {
						"min": {
							"type": "number",
							"minimum": 1,
							"maximum": 7200
						},
						"max": {
							"type": "number",
							"minimum": 1,
							"maximum": 7200
						}
					},
					"required": [
						"min",
						"max"
					],
					"patternProperties": {
						"^x-": {}
					},
					"additionalProperties": false
				},
				"screenTitle": {
					"type": "object",
					"properties": {
						"enabled": {
							"type": "boolean"
						},
						"text": {
							"type": "string",
							"maxLength": 72
						}
					},
					"required": [
						"enabled"
					],
					"patternProperties": {
						"^x-": {}
					},
					"additionalProperties": false
				}
			},
			"required": [
				"aspectRatio"
			],
			"patternProperties": {
				"^x-": {}
			},
			"additionalProperties": false
		},
		"models": {
			"type": "object",
			"properties": {
				"llm": {
					"type": "string",
					"minLength": 1,
					"maxLength": 120
				},
				"image": {
					"type": "object",
					"properties": {
						"model": {
							"type": "string",
							"enum": [
								"gpt-image-2"
							],
							"default": "gpt-image-2"
						},
						"size": {
							"type": "string",
							"enum": [
								"1024x1536",
								"1536x1024",
								"1024x1024"
							],
							"description": "없으면 화면 비율에서 정한다"
						}
					},
					"required": [],
					"patternProperties": {
						"^x-": {}
					},
					"additionalProperties": false
				},
				"video": {
					"type": "object",
					"properties": {
						"provider": {
							"type": "string",
							"enum": [
								"seedance",
								"veo",
								"kling"
							]
						},
						"version": {
							"type": "string",
							"maxLength": 20
						},
						"tier": {
							"type": "string",
							"maxLength": 20
						},
						"resolution": {
							"type": "string",
							"enum": [
								"480p",
								"720p",
								"1080p"
							]
						},
						"audio": {
							"type": "boolean"
						}
					},
					"required": [
						"provider"
					],
					"patternProperties": {
						"^x-": {}
					},
					"additionalProperties": false,
					"default": {
						"provider": "seedance",
						"version": "2.0",
						"tier": "mini",
						"resolution": "720p"
					}
				},
				"tts": {
					"type": "object",
					"properties": {
						"defaultVoiceId": {
							"type": "string",
							"enum": [
								"v2_ann_m_30s_01",
								"v2_ann_m_30s_02",
								"v2_ann_m_30s_04",
								"v2_ann_m_30s_05",
								"v2_ann_f_30s_01",
								"v2_ann_f_30s_02",
								"v2_ann_f_30s_03",
								"v2_ann_f_30s_04",
								"v2_ann_f_30s_05",
								"v2_m_teen_01",
								"v2_m_young_01",
								"v2_m_mid_01",
								"v2_m_senior_01",
								"v2_f_teen_01",
								"v2_f_young_01",
								"v2_f_senior_01"
							],
							"description": "없으면 첫 화자의 목소리"
						},
						"tempo": {
							"type": "number",
							"minimum": 0.5,
							"maximum": 2,
							"default": 1
						}
					},
					"required": [],
					"patternProperties": {
						"^x-": {}
					},
					"additionalProperties": false
				},
				"judge": {
					"type": "string",
					"minLength": 1,
					"maxLength": 120,
					"description": "자동 검수용 멀티모달 LLM"
				}
			},
			"required": [],
			"patternProperties": {
				"^x-": {}
			},
			"additionalProperties": false
		},
		"variables": {
			"type": "array",
			"items": {
				"type": "object",
				"properties": {
					"key": {
						"type": "string",
						"pattern": "^[a-z][a-z0-9_]{0,31}$"
					},
					"label": {
						"type": "string",
						"minLength": 1,
						"maxLength": 40
					},
					"type": {
						"type": "string",
						"enum": [
							"text",
							"longtext",
							"select"
						]
					},
					"options": {
						"type": "array",
						"items": {
							"type": "string",
							"minLength": 1,
							"maxLength": 100
						},
						"maxItems": 50,
						"minItems": 1,
						"description": "select 전용"
					},
					"default": {
						"type": "string",
						"maxLength": 4000
					},
					"help": {
						"type": "string",
						"maxLength": 200
					},
					"required": {
						"type": "boolean"
					}
				},
				"required": [
					"key",
					"label",
					"type"
				],
				"patternProperties": {
					"^x-": {}
				},
				"additionalProperties": false
			},
			"maxItems": 20,
			"default": [
				{
					"key": "topic",
					"label": "주제",
					"type": "longtext"
				}
			],
			"$comment": "상한 20 은 SPEC 미기재(서버 보호용)"
		},
		"authoring": {
			"type": "object",
			"properties": {
				"scriptPrompt": {
					"type": "object",
					"properties": {
						"system": {
							"type": "string",
							"maxLength": 20000
						},
						"user": {
							"type": "string",
							"maxLength": 20000
						}
					},
					"required": [],
					"patternProperties": {
						"^x-": {}
					},
					"additionalProperties": false,
					"description": "{{변수}} 를 입력 칸 값으로 치환한다"
				},
				"evaluationPack": {
					"type": "string",
					"enum": [
						"longform-eval@4",
						"shorts-score@1",
						"engineering@1",
						"incident@1",
						"history@1",
						"crime@1",
						"case@1",
						"yt-shorts@1",
						"yt-longform@1",
						"explainer@1",
						"tutorial@1",
						"product-review@1",
						"ad-promo@1",
						"story@1",
						"news-brief@1",
						"finance@1",
						"health@1",
						"kids-edu@1",
						"ranking-list@1",
						"mystery-horror@1",
						"motivation@1",
						"travel-place@1",
						"dialogue@1",
						"lecture@1",
						"comedy@1",
						"life-drama@1"
					]
				},
				"rewritePolicy": {
					"type": "object",
					"properties": {
						"maxRounds": {
							"type": "integer",
							"minimum": 0,
							"maximum": 5,
							"default": 3
						},
						"minGain": {
							"type": "number",
							"minimum": 0,
							"maximum": 10,
							"default": 0.5
						}
					},
					"required": [],
					"patternProperties": {
						"^x-": {}
					},
					"additionalProperties": false
				},
				"notes": {
					"type": "string",
					"maxLength": 5000
				}
			},
			"required": [],
			"patternProperties": {
				"^x-": {}
			},
			"additionalProperties": false
		},
		"elements": {
			"type": "array",
			"items": {
				"type": "object",
				"properties": {
					"id": {
						"type": "string",
						"pattern": "^[A-Za-z0-9_-]{1,64}$"
					},
					"name": {
						"type": "string",
						"minLength": 1,
						"maxLength": 60
					},
					"kind": {
						"type": "string",
						"enum": [
							"person",
							"object",
							"tool",
							"building",
							"place",
							"cutaway",
							"other"
						]
					},
					"description": {
						"type": "string",
						"maxLength": 1000
					},
					"identityRules": {
						"type": "array",
						"items": {
							"type": "string",
							"minLength": 1,
							"maxLength": 300
						},
						"maxItems": 12
					},
					"views": {
						"type": "array",
						"items": {
							"type": "object",
							"properties": {
								"id": {
									"type": "string",
									"pattern": "^[A-Za-z0-9_-]{1,64}$"
								},
								"angle": {
									"type": "string",
									"enum": [
										"front",
										"three_quarter",
										"side",
										"back",
										"top",
										"bottom",
										"closeup",
										"cutaway",
										"custom"
									]
								},
								"detail": {
									"type": "string",
									"maxLength": 200
								},
								"imagePrompt": {
									"type": "string",
									"minLength": 1,
									"maxLength": 4000
								}
							},
							"required": [
								"id",
								"angle",
								"imagePrompt"
							],
							"patternProperties": {
								"^x-": {}
							},
							"additionalProperties": false
						},
						"maxItems": 12,
						"minItems": 1,
						"$comment": "상한 12 는 SPEC 미기재"
					}
				},
				"required": [
					"id",
					"name",
					"kind",
					"views"
				],
				"patternProperties": {
					"^x-": {}
				},
				"additionalProperties": false
			},
			"maxItems": 50,
			"default": [],
			"$comment": "상한 50 은 SPEC 미기재"
		},
		"speakers": {
			"type": "array",
			"items": {
				"type": "object",
				"properties": {
					"id": {
						"type": "string",
						"pattern": "^[A-Za-z0-9_-]{1,64}$"
					},
					"name": {
						"type": "string",
						"minLength": 1,
						"maxLength": 40
					},
					"voiceId": {
						"type": "string",
						"enum": [
							"v2_ann_m_30s_01",
							"v2_ann_m_30s_02",
							"v2_ann_m_30s_04",
							"v2_ann_m_30s_05",
							"v2_ann_f_30s_01",
							"v2_ann_f_30s_02",
							"v2_ann_f_30s_03",
							"v2_ann_f_30s_04",
							"v2_ann_f_30s_05",
							"v2_m_teen_01",
							"v2_m_young_01",
							"v2_m_mid_01",
							"v2_m_senior_01",
							"v2_f_teen_01",
							"v2_f_young_01",
							"v2_f_senior_01"
						]
					},
					"tempo": {
						"type": "number",
						"minimum": 0.5,
						"maximum": 2
					},
					"elementId": {
						"type": [
							"string",
							"null"
						],
						"pattern": "^[A-Za-z0-9_-]{1,64}$"
					}
				},
				"required": [
					"id",
					"name",
					"voiceId"
				],
				"patternProperties": {
					"^x-": {}
				},
				"additionalProperties": false
			},
			"maxItems": 20,
			"minItems": 1
		},
		"subtitles": {
			"type": "object",
			"properties": {
				"enabled": {
					"type": "boolean",
					"default": true
				},
				"style": {
					"type": "object",
					"properties": {
						"font": {
							"type": "string",
							"minLength": 1,
							"maxLength": 60,
							"default": "Noto Sans KR"
						},
						"sizeAt1080": {
							"type": "integer",
							"minimum": 20,
							"maximum": 140,
							"default": 60
						},
						"bold": {
							"type": "boolean",
							"default": true
						},
						"color": {
							"default": "#FFFFFF",
							"type": "string",
							"pattern": "^#[0-9A-Fa-f]{6}$"
						},
						"outlineColor": {
							"default": "#000000",
							"type": "string",
							"pattern": "^#[0-9A-Fa-f]{6}$"
						},
						"outline": {
							"type": "number",
							"minimum": 0,
							"maximum": 12,
							"default": 3
						},
						"shadow": {
							"type": "number",
							"minimum": 0,
							"maximum": 8,
							"default": 0
						},
						"position": {
							"type": "string",
							"enum": [
								"bottom",
								"middle",
								"top"
							],
							"default": "bottom"
						},
						"marginV": {
							"type": "integer",
							"minimum": 0,
							"maximum": 600,
							"default": 160,
							"description": "1080 기준"
						},
						"maxLines": {
							"type": "integer",
							"minimum": 1,
							"maximum": 3,
							"default": 2
						},
						"maxCharsPerLine": {
							"type": "integer",
							"minimum": 8,
							"maximum": 40
						},
						"fadeMs": {
							"type": "integer",
							"minimum": 0,
							"maximum": 500,
							"default": 120
						},
						"background": {
							"type": "string",
							"enum": [
								"none",
								"box"
							]
						}
					},
					"required": [],
					"patternProperties": {
						"^x-": {}
					},
					"additionalProperties": false
				}
			},
			"required": [],
			"patternProperties": {
				"^x-": {}
			},
			"additionalProperties": false
		},
		"audio": {
			"type": "object",
			"properties": {
				"bgm": {
					"type": [
						"object",
						"null"
					],
					"properties": {
						"assetId": {
							"type": [
								"string",
								"null"
							],
							"maxLength": 64
						},
						"url": {
							"type": "null",
							"description": "외부 URL 은 받지 않는다(업로드 자산만)"
						},
						"volumeDb": {
							"type": "number",
							"minimum": -40,
							"maximum": 0,
							"default": -18
						},
						"duckDb": {
							"type": "number",
							"minimum": -30,
							"maximum": 0,
							"default": -10
						}
					},
					"required": [],
					"patternProperties": {
						"^x-": {}
					},
					"additionalProperties": false
				},
				"narrationLufs": {
					"type": "number",
					"minimum": -24,
					"maximum": -10,
					"default": -16
				},
				"sceneGapMs": {
					"type": "integer",
					"minimum": 0,
					"maximum": 2000,
					"default": 200
				}
			},
			"required": [],
			"patternProperties": {
				"^x-": {}
			},
			"additionalProperties": false
		},
		"scenes": {
			"type": "array",
			"items": {
				"type": "object",
				"properties": {
					"id": {
						"type": "string",
						"pattern": "^[A-Za-z0-9_-]{1,64}$"
					},
					"title": {
						"type": "string",
						"maxLength": 80
					},
					"purpose": {
						"type": "string",
						"maxLength": 500,
						"description": "이 장면이 하는 일(연출 의도)"
					},
					"durationSec": {
						"type": "number",
						"minimum": 1,
						"maximum": 60,
						"description": "없으면 내레이션 길이 + tailSec"
					},
					"tailSec": {
						"type": "number",
						"minimum": 0,
						"maximum": 3,
						"default": 0.5
					},
					"utterances": {
						"type": "array",
						"items": {
							"type": "object",
							"properties": {
								"id": {
									"type": "string",
									"pattern": "^[A-Za-z0-9_-]{1,64}$"
								},
								"speakerId": {
									"type": "string",
									"pattern": "^[A-Za-z0-9_-]{1,64}$",
									"description": "없으면 첫 화자"
								},
								"ttsText": {
									"type": "string",
									"minLength": 1,
									"maxLength": 800,
									"description": "읽을 글"
								},
								"subtitleText": {
									"type": "string",
									"maxLength": 200,
									"description": "화면 글. 없으면 ttsText"
								},
								"pauseAfter": {
									"type": "string",
									"enum": [
										"none",
										"micro",
										"normal",
										"strong"
									]
								}
							},
							"required": [
								"id",
								"ttsText"
							],
							"patternProperties": {
								"^x-": {}
							},
							"additionalProperties": false
						},
						"maxItems": 100,
						"default": [],
						"$comment": "상한 100 은 SPEC 미기재"
					},
					"media": {
						"type": "object",
						"properties": {
							"type": {
								"type": "string",
								"enum": [
									"ai-image",
									"ai-video",
									"stock-video",
									"photo",
									"upload"
								]
							},
							"motion": {
								"type": "string",
								"enum": [
									"kenburns-in",
									"kenburns-out",
									"pan-left",
									"pan-right",
									"none"
								],
								"description": "정지 이미지일 때"
							},
							"video": {
								"type": "object",
								"properties": {
									"provider": {
										"type": "string",
										"enum": [
											"seedance",
											"veo",
											"kling"
										]
									},
									"version": {
										"type": "string",
										"maxLength": 20
									},
									"tier": {
										"type": "string",
										"maxLength": 20
									},
									"resolution": {
										"type": "string",
										"enum": [
											"480p",
											"720p",
											"1080p"
										]
									},
									"mode": {
										"type": "string",
										"enum": [
											"image",
											"text",
											"reference"
										]
									},
									"audio": {
										"type": "boolean"
									}
								},
								"required": [],
								"patternProperties": {
									"^x-": {}
								},
								"additionalProperties": false,
								"description": "ai-video 전용. models.video 를 덮어쓴다"
							},
							"stock": {
								"type": "object",
								"properties": {
									"provider": {
										"type": "string",
										"enum": [
											"pexels",
											"pixabay",
											"wikimedia",
											"archive",
											"nasa",
											"upload"
										]
									},
									"id": {
										"type": "string",
										"minLength": 1,
										"maxLength": 128
									},
									"url": {
										"type": "string",
										"maxLength": 2048,
										"pattern": "^https://"
									},
									"startSec": {
										"type": "number",
										"minimum": 0,
										"maximum": 86400
									},
									"durationSec": {
										"type": "number",
										"exclusiveMinimum": 0,
										"maximum": 86400
									},
									"license": {
										"type": "string",
										"maxLength": 200
									},
									"attribution": {
										"type": "string",
										"maxLength": 500
									}
								},
								"required": [
									"provider"
								],
								"patternProperties": {
									"^x-": {}
								},
								"additionalProperties": false,
								"description": "stock-video 전용"
							},
							"photo": {
								"type": "object",
								"properties": {
									"url": {
										"type": "string",
										"maxLength": 2048,
										"pattern": "^https://"
									},
									"assetId": {
										"type": "string",
										"minLength": 1,
										"maxLength": 64
									},
									"license": {
										"type": "string",
										"maxLength": 200
									},
									"attribution": {
										"type": "string",
										"maxLength": 500
									},
									"rightsConfirmed": {
										"type": "boolean"
									}
								},
								"required": [],
								"patternProperties": {
									"^x-": {}
								},
								"additionalProperties": false,
								"description": "photo 전용"
							},
							"assetId": {
								"type": "string",
								"minLength": 1,
								"maxLength": 64,
								"description": "upload 전용: 사용자 업로드 자산 id"
							}
						},
						"required": [
							"type"
						],
						"patternProperties": {
							"^x-": {}
						},
						"additionalProperties": false
					},
					"keyframes": {
						"type": "array",
						"items": {
							"type": "object",
							"properties": {
								"id": {
									"type": "string",
									"pattern": "^[A-Za-z0-9_-]{1,64}$"
								},
								"imagePrompt": {
									"type": "string",
									"minLength": 1,
									"maxLength": 4000
								},
								"role": {
									"type": "string",
									"enum": [
										"start",
										"change",
										"end"
									]
								},
								"atSec": {
									"type": "number",
									"minimum": 0,
									"maximum": 60
								},
								"references": {
									"type": "array",
									"items": {
										"type": "object",
										"properties": {
											"elementId": {
												"type": "string",
												"pattern": "^[A-Za-z0-9_-]{1,64}$"
											},
											"viewId": {
												"type": "string",
												"pattern": "^[A-Za-z0-9_-]{1,64}$"
											}
										},
										"required": [
											"elementId",
											"viewId"
										],
										"patternProperties": {
											"^x-": {}
										},
										"additionalProperties": false
									},
									"maxItems": 3
								},
								"referenceKeyframeIds": {
									"type": "array",
									"items": {
										"type": "string",
										"pattern": "^[A-Za-z0-9_-]{1,64}$"
									},
									"maxItems": 2
								}
							},
							"required": [
								"id",
								"imagePrompt"
							],
							"patternProperties": {
								"^x-": {}
							},
							"additionalProperties": false
						},
						"maxItems": 32,
						"default": [],
						"$comment": "상한 32 는 SPEC 미기재"
					},
					"videoPrompt": {
						"type": "string",
						"maxLength": 2000
					},
					"transition": {
						"type": "string",
						"enum": [
							"cut",
							"fade",
							"white-flash"
						],
						"default": "cut"
					},
					"overlayText": {
						"type": "string",
						"maxLength": 60
					}
				},
				"required": [
					"id",
					"media"
				],
				"patternProperties": {
					"^x-": {}
				},
				"additionalProperties": false
			},
			"maxItems": 120,
			"minItems": 1
		}
	}
}
