|
|
@@ -4,6 +4,7 @@ import { join } from "node:path";
|
|
|
import type {
|
|
|
PipelineInput,
|
|
|
PipelineJob,
|
|
|
+ ExportFile,
|
|
|
} from "@pipeline/shared";
|
|
|
import { PLATFORM_PRESETS } from "@pipeline/shared";
|
|
|
import { parseText } from "./stages/parse.js";
|
|
|
@@ -60,6 +61,7 @@ export async function runPipeline(
|
|
|
const workDir = join(config.output.dir, "tmp", jobId);
|
|
|
await mkdir(workDir, { recursive: true });
|
|
|
|
|
|
+ const platforms = input.platforms;
|
|
|
const job: PipelineJob = {
|
|
|
id: jobId,
|
|
|
input,
|
|
|
@@ -69,7 +71,7 @@ export async function runPipeline(
|
|
|
};
|
|
|
|
|
|
try {
|
|
|
- // Stage 1: Parse
|
|
|
+ // Stage 1: Parse (shared across all platforms)
|
|
|
callbacks?.onStageStart?.("parse");
|
|
|
const parsed = await parseText(input.text, input.template, {
|
|
|
llm: config.llm,
|
|
|
@@ -78,7 +80,7 @@ export async function runPipeline(
|
|
|
job.parsed = parsed;
|
|
|
callbacks?.onStageComplete?.("parse");
|
|
|
|
|
|
- // Stage 2: TTS (can be skipped with --no-tts)
|
|
|
+ // Stage 2: TTS (shared across all platforms)
|
|
|
callbacks?.onStageStart?.("tts");
|
|
|
const tts = await generateTTS(parsed, workDir, {
|
|
|
provider: config.tts.provider,
|
|
|
@@ -92,37 +94,44 @@ export async function runPipeline(
|
|
|
job.tts = tts;
|
|
|
callbacks?.onStageComplete?.("tts");
|
|
|
|
|
|
- // Stage 3: Assets
|
|
|
- callbacks?.onStageStart?.("assets");
|
|
|
- const assets = await resolveAssets(
|
|
|
- parsed, workDir, config.assets.root, config.assets.inputDir,
|
|
|
- { template: input.template, aspect: PLATFORM_PRESETS[input.platform].aspect }
|
|
|
- );
|
|
|
- callbacks?.onStageComplete?.("assets");
|
|
|
+ // Stages 3-6: Per-platform loop
|
|
|
+ const allExportFiles: ExportFile[] = [];
|
|
|
+ job.composed = {};
|
|
|
|
|
|
- // Stage 4: Compose
|
|
|
- callbacks?.onStageStart?.("compose");
|
|
|
- const composed = composeProject(parsed, tts, assets, input.platform, input.template);
|
|
|
- job.composed = composed;
|
|
|
- callbacks?.onStageComplete?.("compose");
|
|
|
+ for (const platform of platforms) {
|
|
|
+ // Stage 3: Assets
|
|
|
+ callbacks?.onStageStart?.(`assets:${platform}`);
|
|
|
+ const assets = await resolveAssets(
|
|
|
+ parsed, workDir, config.assets.root, config.assets.inputDir,
|
|
|
+ { template: input.template, aspect: PLATFORM_PRESETS[platform].aspect }
|
|
|
+ );
|
|
|
+ callbacks?.onStageComplete?.(`assets:${platform}`);
|
|
|
|
|
|
- // Stage 5: Render
|
|
|
- callbacks?.onStageStart?.("render");
|
|
|
- const renderOutput = join(workDir, "render.mp4");
|
|
|
- await renderVideo(composed, renderOutput, config.templates.entryPoint);
|
|
|
- callbacks?.onStageComplete?.("render");
|
|
|
+ // Stage 4: Compose
|
|
|
+ callbacks?.onStageStart?.(`compose:${platform}`);
|
|
|
+ const composed = composeProject(parsed, tts, assets, platform, input.template);
|
|
|
+ job.composed[platform] = composed;
|
|
|
+ callbacks?.onStageComplete?.(`compose:${platform}`);
|
|
|
|
|
|
- // Stage 6: Export
|
|
|
- callbacks?.onStageStart?.("export");
|
|
|
- const exported = await exportVideo(
|
|
|
- renderOutput,
|
|
|
- composed,
|
|
|
- config.output.dir,
|
|
|
- jobId
|
|
|
- );
|
|
|
- job.exported = exported;
|
|
|
- callbacks?.onStageComplete?.("export");
|
|
|
+ // Stage 5: Render
|
|
|
+ callbacks?.onStageStart?.(`render:${platform}`);
|
|
|
+ const renderOutput = join(workDir, `render-${platform}.mp4`);
|
|
|
+ await renderVideo(composed, renderOutput, config.templates.entryPoint);
|
|
|
+ callbacks?.onStageComplete?.(`render:${platform}`);
|
|
|
|
|
|
+ // Stage 6: Export
|
|
|
+ callbacks?.onStageStart?.(`export:${platform}`);
|
|
|
+ const exported = await exportVideo(
|
|
|
+ renderOutput,
|
|
|
+ composed,
|
|
|
+ config.output.dir,
|
|
|
+ jobId
|
|
|
+ );
|
|
|
+ allExportFiles.push(...exported.files);
|
|
|
+ callbacks?.onStageComplete?.(`export:${platform}`);
|
|
|
+ }
|
|
|
+
|
|
|
+ job.exported = { jobId, createdAt: Date.now(), files: allExportFiles };
|
|
|
job.status = "completed";
|
|
|
} catch (err) {
|
|
|
job.status = "failed";
|