@argo-video/cli 0.12.3 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +102 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +108 -7
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +24 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/dashboard.d.ts +22 -0
- package/dist/dashboard.d.ts.map +1 -0
- package/dist/dashboard.js +186 -0
- package/dist/dashboard.js.map +1 -0
- package/dist/export.d.ts +13 -2
- package/dist/export.d.ts.map +1 -1
- package/dist/export.js +136 -16
- package/dist/export.js.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/media.d.ts +2 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/media.js +18 -0
- package/dist/media.js.map +1 -0
- package/dist/pipeline.d.ts +8 -0
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +62 -53
- package/dist/pipeline.js.map +1 -1
- package/dist/preview.d.ts +13 -0
- package/dist/preview.d.ts.map +1 -1
- package/dist/preview.js +27 -10
- package/dist/preview.js.map +1 -1
- package/dist/progress.d.ts +8 -0
- package/dist/progress.d.ts.map +1 -0
- package/dist/progress.js +67 -0
- package/dist/progress.js.map +1 -0
- package/dist/record.d.ts +3 -0
- package/dist/record.d.ts.map +1 -1
- package/dist/record.js +13 -1
- package/dist/record.js.map +1 -1
- package/dist/speed-ramp.d.ts +39 -0
- package/dist/speed-ramp.d.ts.map +1 -0
- package/dist/speed-ramp.js +184 -0
- package/dist/speed-ramp.js.map +1 -0
- package/dist/timeline.d.ts +19 -0
- package/dist/timeline.d.ts.map +1 -0
- package/dist/timeline.js +82 -0
- package/dist/timeline.js.map +1 -0
- package/dist/transitions.d.ts +15 -0
- package/dist/transitions.d.ts.map +1 -0
- package/dist/transitions.js +124 -0
- package/dist/transitions.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../src/dashboard.ts"],"names":[],"mappings":"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"}
|
package/dist/export.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import type { Placement } from './tts/align.js';
|
|
2
|
+
import type { TransitionConfig } from './config.js';
|
|
3
|
+
import { type Segment } from './speed-ramp.js';
|
|
1
4
|
export interface ExportOptions {
|
|
2
5
|
demoName: string;
|
|
3
6
|
argoDir: string;
|
|
@@ -18,8 +21,16 @@ export interface ExportOptions {
|
|
|
18
21
|
thumbnailPath?: string;
|
|
19
22
|
/** Optional path to ffmpeg chapter metadata file for MP4 chapter markers. */
|
|
20
23
|
chapterMetadataPath?: string;
|
|
21
|
-
/** Additional
|
|
22
|
-
formats?: Array<'1:1' | '9:16'>;
|
|
24
|
+
/** Additional formats to export alongside the main 16:9. */
|
|
25
|
+
formats?: Array<'1:1' | '9:16' | 'gif'>;
|
|
26
|
+
/** Scene transition config for inter-scene transitions. */
|
|
27
|
+
transition?: TransitionConfig;
|
|
28
|
+
/** Scene placements — needed for transitions. */
|
|
29
|
+
placements?: Placement[];
|
|
30
|
+
/** Estimated total duration in ms — used for progress bar. */
|
|
31
|
+
totalDurationMs?: number;
|
|
32
|
+
/** Precomputed speed-ramp segments on the post-trim timeline. */
|
|
33
|
+
speedRampSegments?: Segment[];
|
|
23
34
|
}
|
|
24
35
|
/**
|
|
25
36
|
* Check whether ffmpeg is available on the system PATH.
|
package/dist/export.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../src/export.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../src/export.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGpD,OAAO,EAAwB,KAAK,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAErE,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,qFAAqF;IACrF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qFAAqF;IACrF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sFAAsF;IACtF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yFAAyF;IACzF,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,8EAA8E;IAC9E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,6EAA6E;IAC7E,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,4DAA4D;IAC5D,OAAO,CAAC,EAAE,KAAK,CAAC,KAAK,GAAG,MAAM,GAAG,KAAK,CAAC,CAAC;IACxC,2DAA2D;IAC3D,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,iDAAiD;IACjD,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;IACzB,8DAA8D;IAC9D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iEAAiE;IACjE,iBAAiB,CAAC,EAAE,OAAO,EAAE,CAAC;CAC/B;AAMD;;;GAGG;AACH,wBAAgB,WAAW,IAAI,OAAO,CAYrC;AAuDD;;GAEG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAqPzE"}
|
package/dist/export.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { execFileSync, spawnSync } from 'node:child_process';
|
|
2
2
|
import { existsSync, mkdirSync } from 'node:fs';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
|
+
import { buildTransitionFilters } from './transitions.js';
|
|
5
|
+
import { runFfmpegWithProgress } from './progress.js';
|
|
6
|
+
import { buildSpeedRampFilter } from './speed-ramp.js';
|
|
4
7
|
function formatSeconds(ms) {
|
|
5
8
|
return (ms / 1000).toFixed(3).replace(/\.?0+$/, '');
|
|
6
9
|
}
|
|
@@ -20,11 +23,54 @@ export function checkFfmpeg() {
|
|
|
20
23
|
' Windows: choco install ffmpeg');
|
|
21
24
|
}
|
|
22
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Export an MP4 to animated GIF with palette optimization.
|
|
28
|
+
*/
|
|
29
|
+
async function exportGif(mp4Path, gifPath, fps = 10, width = 640) {
|
|
30
|
+
// Two-pass approach: generate palette first, then use it for high-quality GIF
|
|
31
|
+
const palettePath = mp4Path.replace(/\.mp4$/, '.palette.png');
|
|
32
|
+
const paletteArgs = [
|
|
33
|
+
'-i', mp4Path,
|
|
34
|
+
'-vf', `fps=${fps},scale=${width}:-1:flags=lanczos,palettegen=stats_mode=diff`,
|
|
35
|
+
'-y', palettePath,
|
|
36
|
+
];
|
|
37
|
+
const paletteResult = spawnSync('ffmpeg', paletteArgs, { stdio: 'pipe' });
|
|
38
|
+
if (paletteResult.status !== 0) {
|
|
39
|
+
console.warn('Warning: GIF palette generation failed, using single-pass fallback');
|
|
40
|
+
// Single-pass fallback
|
|
41
|
+
const fallbackArgs = [
|
|
42
|
+
'-i', mp4Path,
|
|
43
|
+
'-vf', `fps=${fps},scale=${width}:-1:flags=lanczos`,
|
|
44
|
+
'-y', gifPath,
|
|
45
|
+
];
|
|
46
|
+
const fbResult = spawnSync('ffmpeg', fallbackArgs, { stdio: 'inherit' });
|
|
47
|
+
if (fbResult.status !== 0) {
|
|
48
|
+
console.warn(`Warning: GIF export failed`);
|
|
49
|
+
}
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const gifArgs = [
|
|
53
|
+
'-i', mp4Path,
|
|
54
|
+
'-i', palettePath,
|
|
55
|
+
'-lavfi', `fps=${fps},scale=${width}:-1:flags=lanczos[x];[x][1:v]paletteuse=dither=bayer:bayer_scale=5`,
|
|
56
|
+
'-y', gifPath,
|
|
57
|
+
];
|
|
58
|
+
const gifResult = spawnSync('ffmpeg', gifArgs, { stdio: 'pipe' });
|
|
59
|
+
if (gifResult.status !== 0) {
|
|
60
|
+
console.warn(`Warning: GIF export failed`);
|
|
61
|
+
}
|
|
62
|
+
// Clean up palette
|
|
63
|
+
try {
|
|
64
|
+
const { unlinkSync } = await import('node:fs');
|
|
65
|
+
unlinkSync(palettePath);
|
|
66
|
+
}
|
|
67
|
+
catch { /* ignore */ }
|
|
68
|
+
}
|
|
23
69
|
/**
|
|
24
70
|
* Export a demo to MP4 by combining the screen recording with aligned narration audio.
|
|
25
71
|
*/
|
|
26
72
|
export async function exportVideo(options) {
|
|
27
|
-
const { demoName, argoDir, outputDir, preset = 'slow', crf = 16, fps, tailPadMs, outputWidth, outputHeight, deviceScaleFactor = 1, thumbnailPath, chapterMetadataPath, } = options;
|
|
73
|
+
const { demoName, argoDir, outputDir, preset = 'slow', crf = 16, fps, tailPadMs, outputWidth, outputHeight, deviceScaleFactor = 1, thumbnailPath, chapterMetadataPath, transition, placements, totalDurationMs, speedRampSegments, } = options;
|
|
28
74
|
checkFfmpeg();
|
|
29
75
|
const demoDir = join(argoDir, demoName);
|
|
30
76
|
const videoPath = join(demoDir, 'video.webm');
|
|
@@ -67,6 +113,17 @@ export async function exportVideo(options) {
|
|
|
67
113
|
args.push('-i', thumbnailPath);
|
|
68
114
|
}
|
|
69
115
|
// Build video filter chain
|
|
116
|
+
const filterParts = [];
|
|
117
|
+
let videoSource = '0:v';
|
|
118
|
+
let audioSource = hasAudio ? '1:a' : undefined;
|
|
119
|
+
const speedRampFilter = speedRampSegments && speedRampSegments.length > 0
|
|
120
|
+
? buildSpeedRampFilter(speedRampSegments, { video: '0:v', audio: hasAudio ? '1:a' : undefined })
|
|
121
|
+
: null;
|
|
122
|
+
if (speedRampFilter) {
|
|
123
|
+
filterParts.push(speedRampFilter.filterComplex);
|
|
124
|
+
videoSource = speedRampFilter.outputLabels.video;
|
|
125
|
+
audioSource = speedRampFilter.outputLabels.audio;
|
|
126
|
+
}
|
|
70
127
|
const vFilters = [];
|
|
71
128
|
if (tailPadMs && tailPadMs > 0) {
|
|
72
129
|
vFilters.push(`tpad=stop_mode=clone:stop_duration=${formatSeconds(tailPadMs)}`);
|
|
@@ -74,8 +131,51 @@ export async function exportVideo(options) {
|
|
|
74
131
|
if (deviceScaleFactor > 1 && outputWidth && outputHeight) {
|
|
75
132
|
vFilters.push(`scale=${outputWidth}:${outputHeight}:flags=lanczos`);
|
|
76
133
|
}
|
|
77
|
-
|
|
78
|
-
|
|
134
|
+
// Scene transitions
|
|
135
|
+
let transitionComplex = null;
|
|
136
|
+
if (transition && placements && placements.length > 1) {
|
|
137
|
+
const transitionResult = buildTransitionFilters(placements, transition, hasAudio, fps ?? 30);
|
|
138
|
+
if (Array.isArray(transitionResult)) {
|
|
139
|
+
// Simple -vf filters (wipe)
|
|
140
|
+
vFilters.push(...transitionResult);
|
|
141
|
+
}
|
|
142
|
+
else if (transitionResult.filterComplex) {
|
|
143
|
+
// Complex filter graph (fade/dissolve — split+trim+fade+concat)
|
|
144
|
+
transitionComplex = transitionResult;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (transitionComplex) {
|
|
148
|
+
// Fade transitions use filter_complex with split+concat.
|
|
149
|
+
// Apply any vFilters (scale, tpad) before the transition.
|
|
150
|
+
let fc = transitionComplex.filterComplex;
|
|
151
|
+
if (speedRampFilter) {
|
|
152
|
+
fc = fc.replace('[0:v]', `[${videoSource}]`);
|
|
153
|
+
if (audioSource && transitionComplex.audioOutput) {
|
|
154
|
+
fc = fc.replace('[1:a]', `[${audioSource}]`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (vFilters.length > 0) {
|
|
158
|
+
// Prepend vFilters to the video input before split
|
|
159
|
+
const inputRef = speedRampFilter ? `[${videoSource}]` : '[0:v]';
|
|
160
|
+
fc = fc.replace(inputRef + 'split=', `${inputRef}${vFilters.join(',')},split=`);
|
|
161
|
+
}
|
|
162
|
+
filterParts.push(fc);
|
|
163
|
+
videoSource = transitionComplex.videoOutput.replace(/[\[\]]/g, '');
|
|
164
|
+
if (hasAudio && transitionComplex.audioOutput) {
|
|
165
|
+
audioSource = transitionComplex.audioOutput.replace(/[\[\]]/g, '');
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
else if (vFilters.length > 0) {
|
|
169
|
+
if (speedRampFilter || filterParts.length > 0) {
|
|
170
|
+
filterParts.push(`[${videoSource}]${vFilters.join(',')}[outvfinal]`);
|
|
171
|
+
videoSource = 'outvfinal';
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
args.push('-vf', vFilters.join(','));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (filterParts.length > 0) {
|
|
178
|
+
args.push('-filter_complex', filterParts.join(';\n'));
|
|
79
179
|
}
|
|
80
180
|
args.push('-c:v', 'libx264', '-preset', preset, '-crf', String(crf));
|
|
81
181
|
if (hasAudio) {
|
|
@@ -87,11 +187,19 @@ export async function exportVideo(options) {
|
|
|
87
187
|
if (hasChapters) {
|
|
88
188
|
args.push('-map_metadata', String(chapterInputIdx));
|
|
89
189
|
}
|
|
190
|
+
const usesExplicitMaps = hasThumbnail || filterParts.length > 0;
|
|
191
|
+
const mapRef = (label) => (label.includes(':') ? label : `[${label}]`);
|
|
192
|
+
if (usesExplicitMaps) {
|
|
193
|
+
args.push('-map', mapRef(videoSource));
|
|
194
|
+
if (hasAudio && audioSource)
|
|
195
|
+
args.push('-map', mapRef(audioSource));
|
|
196
|
+
}
|
|
90
197
|
if (hasThumbnail) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
198
|
+
if (!usesExplicitMaps) {
|
|
199
|
+
args.push('-map', '0:v');
|
|
200
|
+
if (hasAudio)
|
|
201
|
+
args.push('-map', '1:a');
|
|
202
|
+
}
|
|
95
203
|
args.push('-map', `${thumbInputIdx}:v`);
|
|
96
204
|
// Encode thumbnail stream as PNG attached picture
|
|
97
205
|
args.push('-c:v:1', 'png', '-disposition:v:1', 'attached_pic');
|
|
@@ -101,19 +209,31 @@ export async function exportVideo(options) {
|
|
|
101
209
|
args.push('-shortest');
|
|
102
210
|
}
|
|
103
211
|
args.push('-y', outputPath);
|
|
104
|
-
|
|
105
|
-
if (
|
|
106
|
-
|
|
212
|
+
// Use progress bar when we know the total duration
|
|
213
|
+
if (totalDurationMs && totalDurationMs > 0) {
|
|
214
|
+
await runFfmpegWithProgress(args, totalDurationMs);
|
|
107
215
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
216
|
+
else {
|
|
217
|
+
const result = spawnSync('ffmpeg', args, { stdio: 'inherit' });
|
|
218
|
+
if (result.error) {
|
|
219
|
+
throw new Error(`Failed to launch ffmpeg: ${result.error.message}`);
|
|
220
|
+
}
|
|
221
|
+
if (result.signal) {
|
|
222
|
+
throw new Error(`ffmpeg was killed by signal ${result.signal}`);
|
|
223
|
+
}
|
|
224
|
+
if (result.status !== 0) {
|
|
225
|
+
throw new Error(`ffmpeg failed with exit code ${result.status}`);
|
|
226
|
+
}
|
|
113
227
|
}
|
|
114
|
-
// Export additional
|
|
228
|
+
// Export additional formats
|
|
115
229
|
const formats = options.formats ?? [];
|
|
116
230
|
for (const format of formats) {
|
|
231
|
+
if (format === 'gif') {
|
|
232
|
+
const gifPath = outputPath.replace(/\.mp4$/, '.gif');
|
|
233
|
+
console.log(` Exporting GIF → ${gifPath}`);
|
|
234
|
+
await exportGif(outputPath, gifPath, 10, outputWidth ?? 640);
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
117
237
|
const suffix = format.replace(':', 'x');
|
|
118
238
|
const formatPath = outputPath.replace(/\.mp4$/, `.${suffix}.mp4`);
|
|
119
239
|
// Compute crop dimensions from 16:9 source
|
package/dist/export.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"export.js","sourceRoot":"","sources":["../src/export.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"export.js","sourceRoot":"","sources":["../src/export.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { defineConfig, loadConfig, demosProject, type ArgoConfig, type UserConfig, type TTSConfig, type TTSEngine, type VideoConfig, type ExportConfig, } from './config.js';
|
|
1
|
+
export { defineConfig, loadConfig, demosProject, type ArgoConfig, type UserConfig, type TTSConfig, type TTSEngine, type VideoConfig, type ExportConfig, type TransitionType, type TransitionConfig, type SpeedRampConfig, } from './config.js';
|
|
2
2
|
export { test, expect, demoType } from './fixtures.js';
|
|
3
3
|
export { NarrationTimeline, type SceneDurationOptions } from './narration.js';
|
|
4
4
|
export { showCaption, hideCaption, withCaption } from './captions.js';
|
|
@@ -13,5 +13,10 @@ export { generateChapterMetadata } from './chapters.js';
|
|
|
13
13
|
export { buildSceneReport, formatSceneReport, type SceneReport } from './report.js';
|
|
14
14
|
export { validateDemo, type ValidateOptions, type ValidateResult } from './validate.js';
|
|
15
15
|
export { runDoctor, formatDoctorResults } from './doctor.js';
|
|
16
|
+
export { runPipeline, runBatchPipeline, discoverDemos, type PipelineOptions } from './pipeline.js';
|
|
17
|
+
export { buildTransitionFilters } from './transitions.js';
|
|
18
|
+
export { computeSegments, applySpeedRamp } from './speed-ramp.js';
|
|
19
|
+
export { runFfmpegWithProgress } from './progress.js';
|
|
20
|
+
export { startDashboardServer } from './dashboard.js';
|
|
16
21
|
export { init } from './init.js';
|
|
17
22
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,eAAe,GACrB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGvD,OAAO,EAAE,iBAAiB,EAAE,KAAK,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAG9E,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAGtE,OAAO,EACL,WAAW,EACX,WAAW,EACX,WAAW,EACX,KAAK,UAAU,EACf,KAAK,oBAAoB,EACzB,KAAK,IAAI,EACT,KAAK,YAAY,EACjB,KAAK,YAAY,GAClB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,YAAY,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAGlE,OAAO,EACL,SAAS,EACT,SAAS,EACT,SAAS,EACT,MAAM,EACN,WAAW,EACX,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,aAAa,GACnB,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,eAAe,EACf,WAAW,EACX,KAAK,sBAAsB,GAC5B,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAGjD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAG1D,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAGxD,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAGpF,OAAO,EAAE,YAAY,EAAE,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAGxF,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAG7D,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,aAAa,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAGnG,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAG1D,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGlE,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAGtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAGtD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -26,6 +26,16 @@ export { buildSceneReport, formatSceneReport } from './report.js';
|
|
|
26
26
|
export { validateDemo } from './validate.js';
|
|
27
27
|
// Doctor
|
|
28
28
|
export { runDoctor, formatDoctorResults } from './doctor.js';
|
|
29
|
+
// Pipeline
|
|
30
|
+
export { runPipeline, runBatchPipeline, discoverDemos } from './pipeline.js';
|
|
31
|
+
// Transitions
|
|
32
|
+
export { buildTransitionFilters } from './transitions.js';
|
|
33
|
+
// Speed Ramp
|
|
34
|
+
export { computeSegments, applySpeedRamp } from './speed-ramp.js';
|
|
35
|
+
// Progress
|
|
36
|
+
export { runFfmpegWithProgress } from './progress.js';
|
|
37
|
+
// Dashboard
|
|
38
|
+
export { startDashboardServer } from './dashboard.js';
|
|
29
39
|
// Init
|
|
30
40
|
export { init } from './init.js';
|
|
31
41
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,qDAAqD;AAErD,SAAS;AACT,OAAO,EACL,YAAY,EACZ,UAAU,EACV,YAAY,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,qDAAqD;AAErD,SAAS;AACT,OAAO,EACL,YAAY,EACZ,UAAU,EACV,YAAY,GAUb,MAAM,aAAa,CAAC;AAErB,WAAW;AACX,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEvD,YAAY;AACZ,OAAO,EAAE,iBAAiB,EAA6B,MAAM,gBAAgB,CAAC;AAE9E,WAAW;AACX,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEtE,WAAW;AACX,OAAO,EACL,WAAW,EACX,WAAW,EACX,WAAW,GAMZ,MAAM,qBAAqB,CAAC;AAE7B,UAAU;AACV,OAAO,EAAE,YAAY,EAAwB,MAAM,cAAc,CAAC;AAElE,SAAS;AACT,OAAO,EACL,SAAS,EACT,SAAS,EACT,SAAS,EACT,MAAM,EACN,WAAW,GAKZ,MAAM,aAAa,CAAC;AAErB,SAAS;AACT,OAAO,EACL,eAAe,EACf,WAAW,GAEZ,MAAM,aAAa,CAAC;AAIrB,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAEjD,YAAY;AACZ,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE1D,WAAW;AACX,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAExD,SAAS;AACT,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAoB,MAAM,aAAa,CAAC;AAEpF,WAAW;AACX,OAAO,EAAE,YAAY,EAA6C,MAAM,eAAe,CAAC;AAExF,SAAS;AACT,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAE7D,WAAW;AACX,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,aAAa,EAAwB,MAAM,eAAe,CAAC;AAEnG,cAAc;AACd,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,aAAa;AACb,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAElE,WAAW;AACX,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAEtD,YAAY;AACZ,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAEtD,OAAO;AACP,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/media.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../src/media.ts"],"names":[],"mappings":"AAEA,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAwB5D"}
|
package/dist/media.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
export function getVideoDurationMs(videoPath) {
|
|
3
|
+
let raw;
|
|
4
|
+
try {
|
|
5
|
+
raw = execFileSync('ffprobe', ['-v', 'error', '-show_entries', 'format=duration', '-of', 'csv=p=0', videoPath], { encoding: 'utf-8' }).trim();
|
|
6
|
+
}
|
|
7
|
+
catch (err) {
|
|
8
|
+
throw new Error(`Failed to get video duration from ${videoPath}. ` +
|
|
9
|
+
`Ensure ffprobe is installed (it usually comes with ffmpeg). ` +
|
|
10
|
+
`Original error: ${err.message}`);
|
|
11
|
+
}
|
|
12
|
+
const durationMs = Math.round(parseFloat(raw) * 1000);
|
|
13
|
+
if (isNaN(durationMs) || durationMs <= 0) {
|
|
14
|
+
throw new Error(`ffprobe returned invalid duration "${raw}" for ${videoPath}. The video file may be corrupt.`);
|
|
15
|
+
}
|
|
16
|
+
return durationMs;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=media.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media.js","sourceRoot":"","sources":["../src/media.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,UAAU,kBAAkB,CAAC,SAAiB;IAClD,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,YAAY,CAChB,SAAS,EACT,CAAC,IAAI,EAAE,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,CAAC,EAChF,EAAE,QAAQ,EAAE,OAAO,EAAE,CACtB,CAAC,IAAI,EAAE,CAAC;IACX,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,qCAAqC,SAAS,IAAI;YAClD,8DAA8D;YAC9D,mBAAoB,GAAa,CAAC,OAAO,EAAE,CAC5C,CAAC;IACJ,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;IACtD,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,UAAU,IAAI,CAAC,EAAE,CAAC;QACzC,MAAM,IAAI,KAAK,CACb,sCAAsC,GAAG,SAAS,SAAS,kCAAkC,CAC9F,CAAC;IACJ,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC"}
|
package/dist/pipeline.d.ts
CHANGED
|
@@ -2,5 +2,13 @@ import type { ArgoConfig } from './config.js';
|
|
|
2
2
|
export interface PipelineOptions {
|
|
3
3
|
headed?: boolean;
|
|
4
4
|
}
|
|
5
|
+
/**
|
|
6
|
+
* Discover all demo names in the demos directory by looking for `.scenes.json` files.
|
|
7
|
+
*/
|
|
8
|
+
export declare function discoverDemos(demosDir: string): string[];
|
|
9
|
+
/**
|
|
10
|
+
* Run the pipeline for all demos in the demosDir.
|
|
11
|
+
*/
|
|
12
|
+
export declare function runBatchPipeline(config: Pick<ArgoConfig, 'baseURL' | 'demosDir' | 'outputDir' | 'tts' | 'video' | 'export' | 'overlays'>, pipelineOpts?: PipelineOptions): Promise<string[]>;
|
|
5
13
|
export declare function runPipeline(demoName: string, config: Pick<ArgoConfig, 'baseURL' | 'demosDir' | 'outputDir' | 'tts' | 'video' | 'export' | 'overlays'>, pipelineOpts?: PipelineOptions): Promise<string>;
|
|
6
14
|
//# sourceMappingURL=pipeline.d.ts.map
|
package/dist/pipeline.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAU9C,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CASxD;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,SAAS,GAAG,UAAU,GAAG,WAAW,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,CAAC,EACxG,YAAY,CAAC,EAAE,eAAe,GAC7B,OAAO,CAAC,MAAM,EAAE,CAAC,CAyBnB;AAED,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,SAAS,GAAG,UAAU,GAAG,WAAW,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,CAAC,EACxG,YAAY,CAAC,EAAE,eAAe,GAC7B,OAAO,CAAC,MAAM,CAAC,CA8MjB"}
|
package/dist/pipeline.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
2
|
-
import {
|
|
3
|
-
import { join } from 'node:path';
|
|
1
|
+
import { readFileSync, writeFileSync, mkdirSync, readdirSync } from 'node:fs';
|
|
2
|
+
import { join, basename } from 'node:path';
|
|
4
3
|
import { generateClips } from './tts/generate.js';
|
|
5
4
|
import { record } from './record.js';
|
|
6
5
|
import { alignClips } from './tts/align.js';
|
|
@@ -9,21 +8,49 @@ import { exportVideo, checkFfmpeg } from './export.js';
|
|
|
9
8
|
import { generateSrt, generateVtt } from './subtitles.js';
|
|
10
9
|
import { generateChapterMetadata } from './chapters.js';
|
|
11
10
|
import { buildSceneReport, formatSceneReport } from './report.js';
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
import { applySpeedRampToTimeline } from './speed-ramp.js';
|
|
12
|
+
import { getVideoDurationMs } from './media.js';
|
|
13
|
+
import { buildPlacementsFromTimingAndDurations, buildSceneTexts, computeHeadTrimMs, readScenesManifest, shiftPlacements, } from './timeline.js';
|
|
14
|
+
/**
|
|
15
|
+
* Discover all demo names in the demos directory by looking for `.scenes.json` files.
|
|
16
|
+
*/
|
|
17
|
+
export function discoverDemos(demosDir) {
|
|
14
18
|
try {
|
|
15
|
-
|
|
19
|
+
return readdirSync(demosDir)
|
|
20
|
+
.filter((f) => f.endsWith('.scenes.json'))
|
|
21
|
+
.map((f) => basename(f).replace(/\.scenes\.json$/, ''))
|
|
22
|
+
.sort();
|
|
16
23
|
}
|
|
17
|
-
catch
|
|
18
|
-
|
|
19
|
-
`Ensure ffprobe is installed (it usually comes with ffmpeg). ` +
|
|
20
|
-
`Original error: ${err.message}`);
|
|
24
|
+
catch {
|
|
25
|
+
return [];
|
|
21
26
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Run the pipeline for all demos in the demosDir.
|
|
30
|
+
*/
|
|
31
|
+
export async function runBatchPipeline(config, pipelineOpts) {
|
|
32
|
+
const demos = discoverDemos(config.demosDir);
|
|
33
|
+
if (demos.length === 0) {
|
|
34
|
+
throw new Error(`No demos found in ${config.demosDir}/ (no .scenes.json files)`);
|
|
25
35
|
}
|
|
26
|
-
|
|
36
|
+
console.log(`Found ${demos.length} demo(s): ${demos.join(', ')}\n`);
|
|
37
|
+
const results = [];
|
|
38
|
+
for (const demo of demos) {
|
|
39
|
+
console.log(`\n${'═'.repeat(60)}`);
|
|
40
|
+
console.log(` Pipeline: ${demo}`);
|
|
41
|
+
console.log(`${'═'.repeat(60)}\n`);
|
|
42
|
+
try {
|
|
43
|
+
const output = await runPipeline(demo, config, pipelineOpts);
|
|
44
|
+
results.push(output);
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
console.error(`\n✗ Pipeline failed for ${demo}: ${err.message}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
console.log(`\n${'═'.repeat(60)}`);
|
|
51
|
+
console.log(` Batch complete: ${results.length}/${demos.length} succeeded`);
|
|
52
|
+
console.log(`${'═'.repeat(60)}\n`);
|
|
53
|
+
return results;
|
|
27
54
|
}
|
|
28
55
|
export async function runPipeline(demoName, config, pipelineOpts) {
|
|
29
56
|
if (!config.baseURL) {
|
|
@@ -34,6 +61,7 @@ export async function runPipeline(demoName, config, pipelineOpts) {
|
|
|
34
61
|
}
|
|
35
62
|
checkFfmpeg();
|
|
36
63
|
const argoDir = join('.argo', demoName);
|
|
64
|
+
mkdirSync(argoDir, { recursive: true });
|
|
37
65
|
// Step 1: Generate TTS clips
|
|
38
66
|
console.log('★ Brewing voiceover clips...');
|
|
39
67
|
const clipResults = await generateClips({
|
|
@@ -59,6 +87,9 @@ export async function runPipeline(demoName, config, pipelineOpts) {
|
|
|
59
87
|
video: { width: config.video.width, height: config.video.height },
|
|
60
88
|
browser: config.video.browser,
|
|
61
89
|
deviceScaleFactor: config.video.deviceScaleFactor,
|
|
90
|
+
isMobile: config.video.isMobile,
|
|
91
|
+
hasTouch: config.video.hasTouch,
|
|
92
|
+
contextOptions: config.video.contextOptions,
|
|
62
93
|
autoBackground: config.overlays.autoBackground,
|
|
63
94
|
defaultPlacement: config.overlays.defaultPlacement,
|
|
64
95
|
headed: pipelineOpts?.headed,
|
|
@@ -80,14 +111,7 @@ export async function runPipeline(demoName, config, pipelineOpts) {
|
|
|
80
111
|
let shiftedPlacements = [];
|
|
81
112
|
let shiftedDurationMs = totalDurationMs;
|
|
82
113
|
// Auto-trim: skip setup before first scene mark (with 200ms lead-in)
|
|
83
|
-
const
|
|
84
|
-
let headTrimMs = 0;
|
|
85
|
-
if (markTimes.length > 0) {
|
|
86
|
-
const firstMarkMs = Math.min(...markTimes);
|
|
87
|
-
headTrimMs = Math.max(0, firstMarkMs - 200);
|
|
88
|
-
if (headTrimMs <= 500)
|
|
89
|
-
headTrimMs = 0;
|
|
90
|
-
}
|
|
114
|
+
const headTrimMs = computeHeadTrimMs(timing);
|
|
91
115
|
if (!isSilent) {
|
|
92
116
|
console.log('★ Mixing the soundtrack...');
|
|
93
117
|
// Load WAV clips into memory
|
|
@@ -110,46 +134,27 @@ export async function runPipeline(demoName, config, pipelineOpts) {
|
|
|
110
134
|
console.warn(`Aligned narration runs ${aligned.overflowMs}ms past the recording. ` +
|
|
111
135
|
`Padding the final video frame to preserve the full audio.`);
|
|
112
136
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const sortedMarks = Object.entries(timing).sort((a, b) => a[1] - b[1]);
|
|
116
|
-
const silentPlacements = sortedMarks
|
|
117
|
-
.filter(([scene]) => !voicedScenes.has(scene))
|
|
118
|
-
.map(([scene, startMs], _i, arr) => {
|
|
119
|
-
const idx = sortedMarks.findIndex(([s]) => s === scene);
|
|
120
|
-
const endMs = idx + 1 < sortedMarks.length ? sortedMarks[idx + 1][1] : totalDurationMs;
|
|
121
|
-
return { scene, startMs, endMs };
|
|
122
|
-
});
|
|
123
|
-
const allPlacements = [...aligned.placements, ...silentPlacements].sort((a, b) => a.startMs - b.startMs);
|
|
124
|
-
shiftedPlacements = headTrimMs > 0
|
|
125
|
-
? allPlacements.map(p => ({ ...p, startMs: p.startMs - headTrimMs, endMs: p.endMs - headTrimMs }))
|
|
126
|
-
: allPlacements;
|
|
137
|
+
const allPlacements = buildPlacementsFromTimingAndDurations(timing, sceneDurations, totalDurationMs);
|
|
138
|
+
shiftedPlacements = shiftPlacements(allPlacements, headTrimMs);
|
|
127
139
|
shiftedDurationMs = Math.max(totalDurationMs, aligned.requiredDurationMs) - headTrimMs;
|
|
128
140
|
}
|
|
129
141
|
else {
|
|
130
142
|
console.log('★ Silent mode — no voiceover clips');
|
|
131
143
|
shiftedDurationMs = totalDurationMs - headTrimMs;
|
|
132
|
-
|
|
133
|
-
const sortedMarks = Object.entries(timing).sort((a, b) => a[1] - b[1]);
|
|
134
|
-
shiftedPlacements = sortedMarks.map(([scene, startMs], i) => {
|
|
135
|
-
const endMs = i + 1 < sortedMarks.length ? sortedMarks[i + 1][1] : totalDurationMs;
|
|
136
|
-
return { scene, startMs: startMs - headTrimMs, endMs: endMs - headTrimMs };
|
|
137
|
-
});
|
|
144
|
+
shiftedPlacements = shiftPlacements(buildPlacementsFromTimingAndDurations(timing, sceneDurations, totalDurationMs), headTrimMs);
|
|
138
145
|
}
|
|
146
|
+
const speedRampPlan = applySpeedRampToTimeline(shiftedPlacements, shiftedDurationMs, config.export.speedRamp);
|
|
147
|
+
const finalPlacements = speedRampPlan.placements;
|
|
148
|
+
const finalDurationMs = speedRampPlan.totalDurationMs;
|
|
139
149
|
// Ensure output directory exists before writing subtitles
|
|
140
150
|
mkdirSync(config.outputDir, { recursive: true });
|
|
141
151
|
// Build scene text map for subtitles
|
|
142
152
|
const manifestPath = `${config.demosDir}/${demoName}.scenes.json`;
|
|
143
|
-
const sceneTexts = {};
|
|
144
153
|
try {
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
// Generate subtitles (shifted if head-trimming)
|
|
151
|
-
const srt = generateSrt(shiftedPlacements, sceneTexts);
|
|
152
|
-
const vtt = generateVtt(shiftedPlacements, sceneTexts);
|
|
154
|
+
const sceneTexts = buildSceneTexts(readScenesManifest(manifestPath));
|
|
155
|
+
// Generate subtitles on the final export timeline.
|
|
156
|
+
const srt = generateSrt(finalPlacements, sceneTexts);
|
|
157
|
+
const vtt = generateVtt(finalPlacements, sceneTexts);
|
|
153
158
|
writeFileSync(join(config.outputDir, `${demoName}.srt`), srt, 'utf-8');
|
|
154
159
|
writeFileSync(join(config.outputDir, `${demoName}.vtt`), vtt, 'utf-8');
|
|
155
160
|
}
|
|
@@ -158,7 +163,7 @@ export async function runPipeline(demoName, config, pipelineOpts) {
|
|
|
158
163
|
}
|
|
159
164
|
// Generate chapter metadata for ffmpeg
|
|
160
165
|
const chapterMetadataPath = join(argoDir, 'chapters.txt');
|
|
161
|
-
const chapterMetadata = generateChapterMetadata(
|
|
166
|
+
const chapterMetadata = generateChapterMetadata(finalPlacements, finalDurationMs);
|
|
162
167
|
writeFileSync(chapterMetadataPath, chapterMetadata, 'utf-8');
|
|
163
168
|
// Step 4: Export final video
|
|
164
169
|
console.log('★ Cutting the final take...');
|
|
@@ -175,6 +180,10 @@ export async function runPipeline(demoName, config, pipelineOpts) {
|
|
|
175
180
|
thumbnailPath: config.export.thumbnailPath,
|
|
176
181
|
chapterMetadataPath,
|
|
177
182
|
formats: config.export.formats,
|
|
183
|
+
transition: config.export.transition,
|
|
184
|
+
placements: finalPlacements,
|
|
185
|
+
totalDurationMs: finalDurationMs,
|
|
186
|
+
speedRampSegments: speedRampPlan.segments,
|
|
178
187
|
};
|
|
179
188
|
if (tailPadMs !== undefined)
|
|
180
189
|
exportOptions.tailPadMs = tailPadMs;
|
|
@@ -182,7 +191,7 @@ export async function runPipeline(demoName, config, pipelineOpts) {
|
|
|
182
191
|
exportOptions.headTrimMs = headTrimMs;
|
|
183
192
|
const outputPath = await exportVideo(exportOptions);
|
|
184
193
|
// Scene report
|
|
185
|
-
const report = buildSceneReport(demoName,
|
|
194
|
+
const report = buildSceneReport(demoName, finalPlacements, overflowMs, finalDurationMs, outputPath);
|
|
186
195
|
writeFileSync(join(argoDir, 'scene-report.json'), JSON.stringify(report, null, 2), 'utf-8');
|
|
187
196
|
console.log(formatSceneReport(report));
|
|
188
197
|
// Pipeline metadata — provenance tracking for voices, settings, resolution
|