Bước 14 — Render từng part bằng exact part audio + exact part subtitle trên worker Mục tiêu: - Render từng part video từ danh sách scene/images đã chia ở bước 13. - Mỗi part dùng đúng part_XX.wav và đúng part_XX.srt riêng của nó. - Tạo các file video part sync-safe để sang bước 15 concat. Service: - Story render clean full-frame worker: http://192.168.1.104:8015 - Endpoint: /render-story-video Nguyên tắc bắt buộc: - Render từng part, không render full video một lần cho video dài. - audio_path = parts/audio/part_XX.wav - subtitle_path = parts/subtitles/part_XX.srt - Subtitle part phải đã rebase về 00:00. - Không dùng master audio full-file + offset slicing làm mặc định. - Không dùng news template / Breaking News / Finzone overlay. - Render clean full-frame 16:9, mặc định 1920x1080 hoặc 2560x1440 nếu project yêu cầu. - Chuẩn story video: motion_preset=storybook_smooth, transition=crossfade, transition_duration=0.5, ken_burns=true, fade_in=0.0, fade_out=0.0. - Timing hình phải bám voice/subtitle: scene ảnh tiếp theo phải xuất hiện trước hoặc đúng lúc TTS/sub của scene đó bắt đầu; heading scene không được giữ hình trong lúc voice/sub của scene sau đã chạy. - Khi dùng crossfade, phải cộng overlap budget vào scene hold durations trước khi render, vì crossfade làm tổng video ngắn đi theo số transition. Input: - parts/part_ranges.json - parts/audio/part_XX.wav - parts/subtitles/part_XX.srt - scene images tương ứng từng part - scene timing / hold duration từ timeline - render config của project Cách làm: 1. Đọc part_ranges.json. 2. Với từng part, gom đúng scene/images thuộc part đó. 3. Gọi worker 8015 /render-story-video cho từng part. 4. Truyền scenes, audio_path, subtitle_path, output_path và render preset clean full-frame. 5. Nếu dùng smooth/crossfade/storybook_smooth: render pass 1, probe duration, tính lệch, rerender compensation nếu cần, chỉ dùng compensated parts làm output chính. 6. Validate từng part sau render: mp4 tồn tại, duration khớp part trong ngưỡng, có audio, subtitle burn-in dự kiến có hiện, không black frame bất thường, scene đầu/cuối đúng range. Output bắt buộc: - render_parts/part_01.mp4, part_02.mp4, ... - render_parts/render_validation.json - logs/step14_render_parts_manifest.json Nếu có compensation: - render_parts/pass1/part_XX.mp4 - render_parts/compensated/part_XX.mp4 - render_parts/compensation_report.json Quy tắc quan trọng: - Không lấy first-pass smooth render làm final nếu crossfade làm hụt duration. - Nếu smooth/crossfade gây lệch sync mà worker không có path compensation an toàn, phải STOP và báo blocker. - Không fallback render local bằng ffmpeg trên host Erza. - Không render tiếp bước 15 nếu còn part lỗi, thiếu audio/subtitle, hoặc duration lệch quá ngưỡng. Bước này phục vụ trực tiếp: - Bước 15: concat các part mp4 sync-safe trên worker - Bước 16: QA early/mid/late main file Checklist: 14. Render each sync-safe part on worker 192.168.1.104:8015/render-story-video using exact part_XX.wav plus rebased part_XX.srt; validate part MP4s before concat.