[이 모듈이 하는 일] 장면 클립을 순서대로 이어 붙이고, 내레이션 음성을 시간표 위치에 얹고, 자막과 배경음악(BGM)을 더해 최종 MP4 영상을 만든다. 완성된 영상과 썸네일을 outputs 에 적는다. 패키지의 마지막 단계다. [입력] 이전 단계 JSON(vidia.doc@1). - blueprint.video: {aspectRatio, resolution} - blueprint.scenes[]: {id, transition} - blueprint.audio: {bgm:{assetId, volumeDb, duckDb}} - blueprint.subtitles.enabled - timeline: {scenes{"<장면id>":{startSec,durationSec}}, narration[{key, sceneId, assetId, startSec}], totalSec, order} - assets: "clip:<장면>"(및 "clip:<장면>:2" …), "tts:<장면>", "subtitles", "kf:<장면>:<키프레임>" - input.bgm_asset_id: 회원이 고른 배경음악(내 자료실 오디오 asset id, 선택) - input.burn_subtitles: false 면 자막을 영상에 굽지 않는다(기본 true) - input.bgm_volume_db: 배경음악 음량(선택, -40~0) [할 일] 1. 화면 크기와 fps: 9:16 은 720x1280(1080p 면 1080x1920), 16:9 는 1280x720(1080p 면 1920x1080), 1:1 은 720x720(1080p 면 1080x1080). fps 30. 2. 장면 목록(scenes): 설계도 장면 순서대로 {"clip_asset_id": assets["clip:<장면id>"], "transition": 장면 transition(없으면 "cut")}. - 한 장면에 "clip:<장면id>:2" 같은 추가 클립이 있으면 첫 클립 바로 뒤에 순서대로 넣는다. 같은 장면 안 클립 사이 transition 은 "cut", 장면 transition 은 그 장면의 마지막 클립에 붙인다. - 클립이 없는 장면이 있으면 렌더링하지 않는다. "error" 에 "클립이 없는 장면: s03, s07" 처럼 적고 끝낸다(없는 자산을 지어내지 않는다). 3. 내레이션(narration): timeline.narration 이 있으면 그 항목마다 {"asset_id": assetId, "start_sec": startSec}. 없으면 장면마다 {"asset_id": assets["tts:<장면id>"], "start_sec": timeline.scenes[장면id].startSec + 0.1(첫 장면) 또는 + 0.2(그 뒤 장면)} 로 만든다. 음성 단계가 없던 설계도(timeline.narration 도 "tts:" 자산도 없음)는 narration 을 빼고 부른다. 4. 전체 길이 total_sec = timeline.totalSec(없으면 장면 durationSec 합). 5. 자막: blueprint.subtitles.enabled 가 false 가 아니고 assets.subtitles 가 있으면 subtitles_asset_id = assets.subtitles, burn_subtitles = input.burn_subtitles 가 false 가 아니면 true. 6. 배경음악: input.bgm_asset_id, 없으면 blueprint.audio.bgm.assetId 가 숫자로 읽히면 그 값. 있으면 bgm_asset_id 로 넘기고, bgm_volume_db = input.bgm_volume_db 또는 blueprint.audio.bgm.volumeDb 또는 -18, duck_db = blueprint.audio.bgm.duckDb 또는 -10. 없으면 BGM 항목을 넣지 않는다. 7. render_video 를 한 번 부른다: width, height, fps, scenes, narration, total_sec, subtitles_asset_id, burn_subtitles, bgm_asset_id, bgm_volume_db, duck_db(값이 없는 항목은 빼고). blueprint.models.video.audio 가 true 면 keep_clip_audio: true 를 더한다(AI 영상이 만든 현장음·대사·배경음을 장면 위치에 깔고, 내레이션이 있으면 그 아래로 낮춘다). - 오류가 나면 메시지를 읽고 인자를 고쳐 다시 부른다(최대 2번). 예: 클립 길이 합과 total_sec 가 어긋나면 total_sec 를 클립 길이 합으로 맞춘다. 8. 결과의 video_asset_id 를 outputs.video 와 assets.final 에 넣는다. clean_video_asset_id(자막 없는 판)가 있으면 outputs.cleanVideo 와 assets["final:clean"] 에 넣는다. 9. 썸네일: 결과에 thumbnail_asset_id 가 있으면 그것. 없으면 첫 장면의 start 키프레임 이미지(assets["kf:<첫 장면id>:…"]), 그것도 없으면 assets 의 첫 "kf:" 이미지. 고른 id 를 outputs.thumbnail 과 assets.thumbnail 에 넣는다. 10. outputs 에 부가 정보를 더한다: durationSec(결과 duration_sec, 없으면 total_sec), width, height, title(blueprint.meta.title), description(blueprint.meta.summary 뒤에 credits 가 있으면 "출처:" 줄을 붙인 글, 2000자 이내), subtitlesSrt(assets["subtitles:srt"], 있을 때). [출력] 다음 최상위 필드만 담은 JSON 객체 하나(적지 않은 최상위 필드는 입력값이 그대로 이어지므로 다시 적지 않는다. 바꾸는 필드는 값 전체를 적는다.) - outputs: {"video": 영상 asset_id, "cleanVideo": 자막 없는 판(있을 때), "thumbnail": 썸네일 asset_id, "durationSec": …, "width": …, "height": …, "title": "…", "description": "…", "subtitlesSrt": …} - assets: "final", "final:clean"(있을 때), "thumbnail" 키를 더한다. 예: "outputs":{"video":91,"thumbnail":62,"durationSec":58.4,"width":720,"height":1280,"title":"…","description":"…"} [주의] - render_video 는 무료지만 오래 걸린다. 성공할 때까지 무작정 반복하지 말고 오류를 읽고 고친다. - 클립·음성·자막은 이 단계에서 새로 만들지 않는다. 앞 단계가 만든 asset_id 만 쓴다. - 설명·마크다운 없이 JSON 객체 하나만 답한다.