screenwright 0.1.45 → 0.2.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/dist/src/commands/compose.d.ts.map +1 -1
- package/dist/src/commands/compose.js +60 -41
- package/dist/src/commands/compose.js.map +1 -1
- package/dist/src/commands/preview.d.ts.map +1 -1
- package/dist/src/commands/preview.js +9 -12
- package/dist/src/commands/preview.js.map +1 -1
- package/dist/src/composition/DemoVideo.d.ts.map +1 -1
- package/dist/src/composition/DemoVideo.js +24 -78
- package/dist/src/composition/DemoVideo.js.map +1 -1
- package/dist/src/composition/frame-resolve.d.ts +37 -0
- package/dist/src/composition/frame-resolve.d.ts.map +1 -0
- package/dist/src/composition/frame-resolve.js +114 -0
- package/dist/src/composition/frame-resolve.js.map +1 -0
- package/dist/src/composition/remotion-root.d.ts.map +1 -1
- package/dist/src/composition/remotion-root.js +7 -14
- package/dist/src/composition/remotion-root.js.map +1 -1
- package/dist/src/composition/render.d.ts.map +1 -1
- package/dist/src/composition/render.js +2 -6
- package/dist/src/composition/render.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/runtime/action-helpers.d.ts +15 -5
- package/dist/src/runtime/action-helpers.d.ts.map +1 -1
- package/dist/src/runtime/action-helpers.js +218 -60
- package/dist/src/runtime/action-helpers.js.map +1 -1
- package/dist/src/runtime/instrumented-page.d.ts +3 -2
- package/dist/src/runtime/instrumented-page.d.ts.map +1 -1
- package/dist/src/runtime/instrumented-page.js +105 -101
- package/dist/src/runtime/instrumented-page.js.map +1 -1
- package/dist/src/runtime/narration-preprocess.d.ts +30 -0
- package/dist/src/runtime/narration-preprocess.d.ts.map +1 -0
- package/dist/src/runtime/narration-preprocess.js +79 -0
- package/dist/src/runtime/narration-preprocess.js.map +1 -0
- package/dist/src/runtime/timeline-collector.d.ts +1 -7
- package/dist/src/runtime/timeline-collector.d.ts.map +1 -1
- package/dist/src/runtime/timeline-collector.js +2 -17
- package/dist/src/runtime/timeline-collector.js.map +1 -1
- package/dist/src/timeline/schema.d.ts +143 -162
- package/dist/src/timeline/schema.d.ts.map +1 -1
- package/dist/src/timeline/schema.js +12 -18
- package/dist/src/timeline/schema.js.map +1 -1
- package/dist/src/timeline/types.d.ts +15 -18
- package/dist/src/timeline/types.d.ts.map +1 -1
- package/dist/src/timeline/types.js.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.d.ts.map +1 -1
- package/dist/src/version.js +1 -1
- package/dist/src/version.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/skill/SKILL.md +3 -3
- package/dist/src/composition/SceneSlide.d.ts +0 -12
- package/dist/src/composition/SceneSlide.d.ts.map +0 -1
- package/dist/src/composition/SceneSlide.js +0 -71
- package/dist/src/composition/SceneSlide.js.map +0 -1
- package/dist/src/composition/frame-lookup.d.ts +0 -8
- package/dist/src/composition/frame-lookup.d.ts.map +0 -1
- package/dist/src/composition/frame-lookup.js +0 -26
- package/dist/src/composition/frame-lookup.js.map +0 -1
- package/dist/src/composition/time-remap.d.ts +0 -87
- package/dist/src/composition/time-remap.d.ts.map +0 -1
- package/dist/src/composition/time-remap.js +0 -218
- package/dist/src/composition/time-remap.js.map +0 -1
- package/dist/src/voiceover/narration-timing.d.ts +0 -18
- package/dist/src/voiceover/narration-timing.d.ts.map +0 -1
- package/dist/src/voiceover/narration-timing.js +0 -40
- package/dist/src/voiceover/narration-timing.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compose.d.ts","sourceRoot":"","sources":["../../../src/commands/compose.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"compose.d.ts","sourceRoot":"","sources":["../../../src/commands/compose.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAapC,eAAO,MAAM,cAAc,SA+KvB,CAAC"}
|
|
@@ -5,10 +5,11 @@ import { pathToFileURL } from 'node:url';
|
|
|
5
5
|
import ora from 'ora';
|
|
6
6
|
import chalk from 'chalk';
|
|
7
7
|
import { runScenario } from '../runtime/instrumented-page.js';
|
|
8
|
-
import {
|
|
8
|
+
import { extractNarrations, pregenerateNarrations, validateNarrationCount } from '../runtime/narration-preprocess.js';
|
|
9
9
|
import { ensureDependencies } from '../voiceover/voice-models.js';
|
|
10
10
|
import { renderDemoVideo } from '../composition/render.js';
|
|
11
11
|
import { loadConfig } from '../config/load-config.js';
|
|
12
|
+
import { expandedFrameCount } from '../composition/frame-resolve.js';
|
|
12
13
|
export const composeCommand = new Command('compose')
|
|
13
14
|
.description('Record and compose final demo video')
|
|
14
15
|
.argument('<scenario>', 'Path to demo scenario file')
|
|
@@ -61,7 +62,50 @@ export const composeCommand = new Command('compose')
|
|
|
61
62
|
}
|
|
62
63
|
process.exit(1);
|
|
63
64
|
}
|
|
64
|
-
// 2.
|
|
65
|
+
// 2. PREPROCESS: Extract narrations from scenario
|
|
66
|
+
let pregenerated = [];
|
|
67
|
+
if (opts.voiceover !== false) {
|
|
68
|
+
spinner = ora('Extracting narrations').start();
|
|
69
|
+
try {
|
|
70
|
+
const texts = await extractNarrations(scenarioFn);
|
|
71
|
+
spinner.succeed(`Found ${texts.length} narration segments`);
|
|
72
|
+
if (texts.length > 0) {
|
|
73
|
+
// Validate API key before starting TTS
|
|
74
|
+
if (config.ttsProvider === 'openai' && !process.env.OPENAI_API_KEY) {
|
|
75
|
+
console.error(chalk.red('OPENAI_API_KEY is required when ttsProvider is "openai".'));
|
|
76
|
+
console.error(chalk.dim('Set it with: export OPENAI_API_KEY=sk-...'));
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
// 3. TTS: Pre-generate all narration audio in parallel
|
|
80
|
+
const tempNarrationDir = resolve(outputDir, '.narration-temp');
|
|
81
|
+
await mkdir(tempNarrationDir, { recursive: true });
|
|
82
|
+
spinner = ora(`Generating voiceover (${texts.length} segments via ${config.ttsProvider})`).start();
|
|
83
|
+
try {
|
|
84
|
+
const modelPath = config.ttsProvider === 'piper'
|
|
85
|
+
? (await ensureDependencies(config.voice)).modelPath
|
|
86
|
+
: undefined;
|
|
87
|
+
pregenerated = await pregenerateNarrations(texts, {
|
|
88
|
+
tempDir: tempNarrationDir,
|
|
89
|
+
ttsProvider: config.ttsProvider,
|
|
90
|
+
modelPath,
|
|
91
|
+
openaiVoice: config.openaiVoice,
|
|
92
|
+
openaiTtsInstructions: config.openaiTtsInstructions,
|
|
93
|
+
});
|
|
94
|
+
spinner.succeed(`Generated ${texts.length} voiceover segments`);
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
spinner.warn('Voiceover generation failed — continuing without audio');
|
|
98
|
+
console.error(chalk.dim(err.message));
|
|
99
|
+
pregenerated = [];
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
spinner.warn('Narration extraction failed — continuing without voiceover');
|
|
105
|
+
console.error(chalk.dim(err.message));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// 4. RECORD: Run scenario in Playwright with pre-generated narrations
|
|
65
109
|
spinner = ora('Recording scenario').start();
|
|
66
110
|
let timeline, tempDir;
|
|
67
111
|
try {
|
|
@@ -69,10 +113,16 @@ export const composeCommand = new Command('compose')
|
|
|
69
113
|
scenarioFile: scenarioPath,
|
|
70
114
|
testFile: scenarioPath,
|
|
71
115
|
viewport: { width, height },
|
|
116
|
+
pregenerated: pregenerated.length > 0 ? pregenerated : undefined,
|
|
72
117
|
});
|
|
73
118
|
timeline = result.timeline;
|
|
74
119
|
tempDir = result.tempDir;
|
|
75
|
-
|
|
120
|
+
// 5. VALIDATE: Assert narration count matches
|
|
121
|
+
if (pregenerated.length > 0) {
|
|
122
|
+
validateNarrationCount(pregenerated.length, result.narrationCount);
|
|
123
|
+
}
|
|
124
|
+
const frameCount = expandedFrameCount(timeline.metadata.frameManifest);
|
|
125
|
+
spinner.succeed(`Recorded ${timeline.events.length} events, ${frameCount} frames`);
|
|
76
126
|
}
|
|
77
127
|
catch (err) {
|
|
78
128
|
spinner.fail('Recording failed');
|
|
@@ -88,43 +138,11 @@ export const composeCommand = new Command('compose')
|
|
|
88
138
|
}
|
|
89
139
|
process.exit(1);
|
|
90
140
|
}
|
|
91
|
-
//
|
|
92
|
-
let finalTimeline = timeline;
|
|
93
|
-
if (opts.voiceover !== false) {
|
|
94
|
-
const narrationCount = timeline.events.filter(e => e.type === 'narration').length;
|
|
95
|
-
if (narrationCount > 0) {
|
|
96
|
-
// Validate API key before starting TTS loop
|
|
97
|
-
if (config.ttsProvider === 'openai' && !process.env.OPENAI_API_KEY) {
|
|
98
|
-
console.error(chalk.red('OPENAI_API_KEY is required when ttsProvider is "openai".'));
|
|
99
|
-
console.error(chalk.dim('Set it with: export OPENAI_API_KEY=sk-...'));
|
|
100
|
-
process.exit(1);
|
|
101
|
-
}
|
|
102
|
-
spinner = ora(`Generating voiceover (${narrationCount} segments via ${config.ttsProvider})`).start();
|
|
103
|
-
try {
|
|
104
|
-
const modelPath = config.ttsProvider === 'piper'
|
|
105
|
-
? (await ensureDependencies(config.voice)).modelPath
|
|
106
|
-
: undefined;
|
|
107
|
-
finalTimeline = await generateNarration(timeline, {
|
|
108
|
-
tempDir,
|
|
109
|
-
modelPath,
|
|
110
|
-
ttsProvider: config.ttsProvider,
|
|
111
|
-
openaiVoice: config.openaiVoice,
|
|
112
|
-
openaiTtsInstructions: config.openaiTtsInstructions,
|
|
113
|
-
});
|
|
114
|
-
spinner.succeed(`Generated ${narrationCount} voiceover segments`);
|
|
115
|
-
}
|
|
116
|
-
catch (err) {
|
|
117
|
-
spinner.warn('Voiceover generation failed — continuing without audio');
|
|
118
|
-
console.error(chalk.dim(err.message));
|
|
119
|
-
console.error(chalk.dim('Tip: use --no-voiceover to skip, or re-run "screenwright init".'));
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
// 4. Render final video via Remotion
|
|
141
|
+
// 6. COMPOSE: Render final video via Remotion
|
|
124
142
|
spinner = ora('Composing final video').start();
|
|
125
143
|
try {
|
|
126
144
|
await renderDemoVideo({
|
|
127
|
-
timeline
|
|
145
|
+
timeline,
|
|
128
146
|
outputPath,
|
|
129
147
|
publicDir: tempDir,
|
|
130
148
|
branding: config.branding,
|
|
@@ -142,23 +160,24 @@ export const composeCommand = new Command('compose')
|
|
|
142
160
|
}
|
|
143
161
|
process.exit(1);
|
|
144
162
|
}
|
|
145
|
-
//
|
|
163
|
+
// 7. Cleanup
|
|
146
164
|
if (!opts.keepTemp) {
|
|
147
165
|
await rm(tempDir, { recursive: true, force: true });
|
|
148
166
|
}
|
|
149
167
|
else {
|
|
150
168
|
console.log(chalk.dim(`Temp files kept at: ${tempDir}`));
|
|
151
169
|
}
|
|
152
|
-
//
|
|
170
|
+
// 8. Report
|
|
153
171
|
const fileStats = await stat(outputPath);
|
|
154
172
|
const sizeMB = (fileStats.size / (1024 * 1024)).toFixed(1);
|
|
155
|
-
const
|
|
173
|
+
const totalFrames = expandedFrameCount(timeline.metadata.frameManifest);
|
|
174
|
+
const durationSec = (totalFrames / 30).toFixed(0);
|
|
156
175
|
const mins = Math.floor(Number(durationSec) / 60);
|
|
157
176
|
const secs = Number(durationSec) % 60;
|
|
158
177
|
console.log('');
|
|
159
178
|
console.log(chalk.green(` Demo video saved to: ${outputPath}`));
|
|
160
179
|
console.log(chalk.dim(` Duration: ${mins}:${String(secs).padStart(2, '0')}`));
|
|
161
180
|
console.log(chalk.dim(` Size: ${sizeMB} MB`));
|
|
162
|
-
console.log(chalk.dim(` Events: ${
|
|
181
|
+
console.log(chalk.dim(` Events: ${timeline.events.length}`));
|
|
163
182
|
});
|
|
164
183
|
//# sourceMappingURL=compose.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compose.js","sourceRoot":"","sources":["../../../src/commands/compose.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,WAAW,EAAmB,MAAM,iCAAiC,CAAC;AAC/E,OAAO,EAAE,iBAAiB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"compose.js","sourceRoot":"","sources":["../../../src/commands/compose.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../../../src/commands/preview.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,eAAO,MAAM,cAAc,
|
|
1
|
+
{"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../../../src/commands/preview.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,eAAO,MAAM,cAAc,SA2DvB,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import { resolve, basename } from 'node:path';
|
|
3
|
-
import { access, mkdir,
|
|
2
|
+
import { resolve, basename, join } from 'node:path';
|
|
3
|
+
import { access, mkdir, writeFile } from 'node:fs/promises';
|
|
4
4
|
import { pathToFileURL } from 'node:url';
|
|
5
5
|
import ora from 'ora';
|
|
6
6
|
import chalk from 'chalk';
|
|
@@ -8,7 +8,7 @@ import { runScenario } from '../runtime/instrumented-page.js';
|
|
|
8
8
|
export const previewCommand = new Command('preview')
|
|
9
9
|
.description('Quick preview without cursor overlay or voiceover')
|
|
10
10
|
.argument('<scenario>', 'Path to demo scenario file')
|
|
11
|
-
.option('--out <path>', 'Output path for preview
|
|
11
|
+
.option('--out <path>', 'Output path for preview timeline')
|
|
12
12
|
.action(async (scenario, opts) => {
|
|
13
13
|
const scenarioPath = resolve(scenario);
|
|
14
14
|
try {
|
|
@@ -22,7 +22,7 @@ export const previewCommand = new Command('preview')
|
|
|
22
22
|
const outputDir = resolve(opts.out ? resolve(opts.out, '..') : './output');
|
|
23
23
|
const outputPath = opts.out
|
|
24
24
|
? resolve(opts.out)
|
|
25
|
-
: resolve(outputDir, `${basename(scenarioPath, '.ts')}-preview.
|
|
25
|
+
: resolve(outputDir, `${basename(scenarioPath, '.ts')}-preview.json`);
|
|
26
26
|
await mkdir(outputDir, { recursive: true });
|
|
27
27
|
// 1. Load scenario module
|
|
28
28
|
let spinner = ora('Loading scenario').start();
|
|
@@ -42,22 +42,19 @@ export const previewCommand = new Command('preview')
|
|
|
42
42
|
console.error(chalk.red(err.message));
|
|
43
43
|
process.exit(1);
|
|
44
44
|
}
|
|
45
|
-
// 2. Run scenario
|
|
45
|
+
// 2. Run scenario (frame-based recording)
|
|
46
46
|
spinner = ora('Recording preview').start();
|
|
47
47
|
try {
|
|
48
48
|
const result = await runScenario(scenarioFn, {
|
|
49
49
|
scenarioFile: scenarioPath,
|
|
50
50
|
testFile: scenarioPath,
|
|
51
|
-
captureMode: 'video',
|
|
52
51
|
});
|
|
53
52
|
const { timeline } = result;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
process.exit(1);
|
|
57
|
-
}
|
|
58
|
-
await copyFile(result.videoFile, outputPath);
|
|
59
|
-
spinner.succeed(`Preview saved to: ${outputPath}`);
|
|
53
|
+
await writeFile(outputPath, JSON.stringify(timeline, null, 2));
|
|
54
|
+
spinner.succeed(`Preview timeline saved to: ${outputPath}`);
|
|
60
55
|
console.log(chalk.dim(` ${timeline.events.length} events recorded`));
|
|
56
|
+
console.log(chalk.dim(` ${timeline.metadata.frameManifest.length} manifest entries`));
|
|
57
|
+
console.log(chalk.dim(` Frames at: ${join(result.tempDir, 'frames')}`));
|
|
61
58
|
}
|
|
62
59
|
catch (err) {
|
|
63
60
|
spinner.fail('Recording failed');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preview.js","sourceRoot":"","sources":["../../../src/commands/preview.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"preview.js","sourceRoot":"","sources":["../../../src/commands/preview.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,WAAW,EAAmB,MAAM,iCAAiC,CAAC;AAE/E,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,OAAO,CAAC,SAAS,CAAC;KACjD,WAAW,CAAC,mDAAmD,CAAC;KAChE,QAAQ,CAAC,YAAY,EAAE,4BAA4B,CAAC;KACpD,MAAM,CAAC,cAAc,EAAE,kCAAkC,CAAC;KAC1D,MAAM,CAAC,KAAK,EAAE,QAAgB,EAAE,IAAI,EAAE,EAAE;IACvC,MAAM,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAEvC,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,4BAA4B,YAAY,EAAE,CAAC,CAAC,CAAC;QACrE,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,0DAA0D,CAAC,CAAC,CAAC;QACrF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;IAC3E,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG;QACzB,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC;QACnB,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,QAAQ,CAAC,YAAY,EAAE,KAAK,CAAC,eAAe,CAAC,CAAC;IAExE,MAAM,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE5C,0BAA0B;IAC1B,IAAI,OAAO,GAAG,GAAG,CAAC,kBAAkB,CAAC,CAAC,KAAK,EAAE,CAAC;IAC9C,IAAI,UAAsB,CAAC;IAC3B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,CAAC;QAC3D,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC;QACzB,IAAI,OAAO,UAAU,KAAK,UAAU,EAAE,CAAC;YACrC,OAAO,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;YACtC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,gDAAgD,CAAC,CAAC,CAAC;YAC3E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACrC,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,OAAO,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;QACxC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;QACtC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,0CAA0C;IAC1C,OAAO,GAAG,GAAG,CAAC,mBAAmB,CAAC,CAAC,KAAK,EAAE,CAAC;IAC3C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,UAAU,EAAE;YAC3C,YAAY,EAAE,YAAY;YAC1B,QAAQ,EAAE,YAAY;SACvB,CAAC,CAAC;QACH,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC;QAE5B,MAAM,SAAS,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC/D,OAAO,CAAC,OAAO,CAAC,8BAA8B,UAAU,EAAE,CAAC,CAAC;QAC5D,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,QAAQ,CAAC,MAAM,CAAC,MAAM,kBAAkB,CAAC,CAAC,CAAC;QACtE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,QAAQ,CAAC,QAAQ,CAAC,aAAa,CAAC,MAAM,mBAAmB,CAAC,CAAC,CAAC;QACvF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,gBAAgB,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3E,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,OAAO,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;QACjC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;QACtC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DemoVideo.d.ts","sourceRoot":"","sources":["../../../src/composition/DemoVideo.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"DemoVideo.d.ts","sourceRoot":"","sources":["../../../src/composition/DemoVideo.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AASjE,UAAU,KAAK;IACb,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CA0FrC,CAAC"}
|
|
@@ -1,76 +1,28 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Img,
|
|
2
|
+
import { Img, staticFile, useCurrentFrame } from 'remotion';
|
|
3
3
|
import { CursorOverlay } from './CursorOverlay.js';
|
|
4
4
|
import { NarrationTrack } from './NarrationTrack.js';
|
|
5
|
-
import { SceneSlide } from './SceneSlide.js';
|
|
6
5
|
import { precomputeCursorPaths } from './cursor-path.js';
|
|
7
|
-
import { findClosestFrame } from './frame-lookup.js';
|
|
8
6
|
import { getTransitionStyles } from './transition-styles.js';
|
|
9
|
-
import {
|
|
7
|
+
import { resolveOutputFrame, remapEventsForOutput } from './frame-resolve.js';
|
|
10
8
|
const IMG_STYLE = { width: '100%', height: '100%', display: 'block' };
|
|
11
9
|
export const DemoVideo = ({ timeline, branding }) => {
|
|
12
10
|
const fps = 30;
|
|
13
11
|
const frame = useCurrentFrame();
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
: outputTimeMs;
|
|
22
|
-
const eventsToUse = hasInsertions
|
|
23
|
-
? remapEvents(timeline.events, slideScenes, resolvedTransitions)
|
|
24
|
-
: timeline.events;
|
|
25
|
-
const cursorEvents = precomputeCursorPaths(eventsToUse.filter((e) => e.type === 'cursor_target'));
|
|
26
|
-
const clickEvents = eventsToUse.filter((e) => e.type === 'action' && e.action === 'click');
|
|
27
|
-
const narrations = eventsToUse.filter((e) => e.type === 'narration');
|
|
28
|
-
const { frameManifest, videoFile } = timeline.metadata;
|
|
29
|
-
const { slides: slideSegments, transitions: transitionSegments } = computeOutputSegments(scenes, resolvedTransitions, timeline.events);
|
|
30
|
-
function resolveSlideProps(seg) {
|
|
31
|
-
return {
|
|
32
|
-
title: seg.sceneTitle,
|
|
33
|
-
description: seg.sceneDescription,
|
|
34
|
-
brandColor: seg.slideConfig.brandColor ?? branding?.brandColor ?? '#000000',
|
|
35
|
-
textColor: seg.slideConfig.textColor ?? branding?.textColor ?? '#FFFFFF',
|
|
36
|
-
fontFamily: seg.slideConfig.fontFamily ?? branding?.fontFamily,
|
|
37
|
-
titleFontSize: seg.slideConfig.titleFontSize,
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
const activeSlide = slideSegments.find(s => outputTimeMs >= s.slideStartMs && outputTimeMs < s.slideEndMs);
|
|
41
|
-
// Check if current time is inside a transition segment
|
|
42
|
-
const activeTransition = !activeSlide
|
|
43
|
-
? transitionSegments.find(t => outputTimeMs >= t.outputStartMs && outputTimeMs < t.outputEndMs)
|
|
44
|
-
: null;
|
|
12
|
+
const { frameManifest, transitionMarkers, viewport } = timeline.metadata;
|
|
13
|
+
const resolution = resolveOutputFrame(frame, frameManifest, transitionMarkers);
|
|
14
|
+
const remappedEvents = remapEventsForOutput(timeline.events, frameManifest, transitionMarkers);
|
|
15
|
+
const cursorEvents = precomputeCursorPaths(remappedEvents.filter((e) => e.type === 'cursor_target'));
|
|
16
|
+
const clickEvents = remappedEvents.filter((e) => e.type === 'action' && e.action === 'click');
|
|
17
|
+
const narrations = remappedEvents.filter((e) => e.type === 'narration');
|
|
18
|
+
const slideScenes = remappedEvents.filter((e) => e.type === 'scene' && !!e.slide);
|
|
45
19
|
let baseLayer;
|
|
46
|
-
if (
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
else if (activeTransition && frameManifest && frameManifest.length > 0) {
|
|
50
|
-
const progress = (outputTimeMs - activeTransition.outputStartMs) / activeTransition.durationMs;
|
|
51
|
-
const { width: vw } = timeline.metadata.viewport;
|
|
52
|
-
const styles = getTransitionStyles(activeTransition.transition, progress, vw);
|
|
20
|
+
if (resolution.type === 'transition') {
|
|
21
|
+
const styles = getTransitionStyles(resolution.transition, resolution.progress, viewport.width);
|
|
53
22
|
const faceClip = styles.container ? {} : { overflow: 'hidden' };
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
let exitContent;
|
|
58
|
-
if (beforeSlide) {
|
|
59
|
-
exitContent = _jsx(SceneSlide, { ...resolveSlideProps(beforeSlide) });
|
|
60
|
-
}
|
|
61
|
-
else if (activeTransition.beforeSnapshot) {
|
|
62
|
-
exitContent = _jsx(Img, { src: staticFile(activeTransition.beforeSnapshot), style: IMG_STYLE });
|
|
63
|
-
}
|
|
64
|
-
// Resolve entrance content (after the transition)
|
|
65
|
-
const afterSlide = activeTransition.adjacentSlideAfter !== null
|
|
66
|
-
? slideSegments[activeTransition.adjacentSlideAfter] : null;
|
|
67
|
-
let entranceContent;
|
|
68
|
-
if (afterSlide) {
|
|
69
|
-
entranceContent = _jsx(SceneSlide, { ...resolveSlideProps(afterSlide) });
|
|
70
|
-
}
|
|
71
|
-
else if (activeTransition.afterSnapshot) {
|
|
72
|
-
entranceContent = _jsx(Img, { src: staticFile(activeTransition.afterSnapshot), style: IMG_STYLE });
|
|
73
|
-
}
|
|
23
|
+
const backdropColor = styles.backdrop ?? branding?.brandColor ?? '#000000';
|
|
24
|
+
const exitContent = _jsx(Img, { src: staticFile(resolution.beforeFile), style: IMG_STYLE });
|
|
25
|
+
const entranceContent = _jsx(Img, { src: staticFile(resolution.afterFile), style: IMG_STYLE });
|
|
74
26
|
const faces = (_jsxs(_Fragment, { children: [_jsx("div", { style: { position: 'absolute', inset: 0, ...faceClip, ...styles.entrance }, children: entranceContent }), _jsx("div", { style: { position: 'absolute', inset: 0, ...faceClip, ...styles.exit }, children: exitContent }), styles.exit2 && (_jsx("div", { style: { position: 'absolute', inset: 0, ...faceClip, ...styles.exit2 }, children: exitContent }))] }));
|
|
75
27
|
let wrappedFaces = faces;
|
|
76
28
|
if (styles.container) {
|
|
@@ -79,28 +31,22 @@ export const DemoVideo = ({ timeline, branding }) => {
|
|
|
79
31
|
if (styles.perspective) {
|
|
80
32
|
wrappedFaces = _jsx("div", { style: { position: 'absolute', inset: 0, perspective: styles.perspective }, children: wrappedFaces });
|
|
81
33
|
}
|
|
82
|
-
const backdropColor = styles.backdrop ?? branding?.brandColor ?? '#000000';
|
|
83
34
|
baseLayer = (_jsxs(_Fragment, { children: [_jsx("div", { style: { position: 'absolute', inset: 0, backgroundColor: backdropColor } }), wrappedFaces] }));
|
|
84
35
|
}
|
|
85
|
-
else if (frameManifest && frameManifest.length > 0) {
|
|
86
|
-
const entry = findClosestFrame(frameManifest, timeMs);
|
|
87
|
-
baseLayer = (_jsx(Img, { src: staticFile(entry.file), style: IMG_STYLE }));
|
|
88
|
-
}
|
|
89
|
-
else if (videoFile) {
|
|
90
|
-
const transitionEvents = timeline.events.filter((e) => e.type === 'transition');
|
|
91
|
-
if (transitionEvents.length > 0 && frame === 0) {
|
|
92
|
-
console.warn('sw.transition() effects require frame-based capture (captureMode: "frame"). Transitions will be ignored with video-based capture.');
|
|
93
|
-
}
|
|
94
|
-
baseLayer = _jsx(OffthreadVideo, { src: staticFile(videoFile) });
|
|
95
|
-
}
|
|
96
36
|
else {
|
|
97
|
-
|
|
37
|
+
baseLayer = _jsx(Img, { src: staticFile(resolution.file), style: IMG_STYLE });
|
|
98
38
|
}
|
|
39
|
+
const currentTimeMs = (frame / fps) * 1000;
|
|
40
|
+
const duringSlide = slideScenes.some(s => {
|
|
41
|
+
const dur = s.slide.duration ?? 2000;
|
|
42
|
+
return currentTimeMs >= s.timestampMs && currentTimeMs < s.timestampMs + dur;
|
|
43
|
+
});
|
|
44
|
+
const showCursor = resolution.type !== 'transition' && !duringSlide;
|
|
99
45
|
return (_jsxs("div", { style: {
|
|
100
46
|
position: 'relative',
|
|
101
|
-
width:
|
|
102
|
-
height:
|
|
47
|
+
width: viewport.width,
|
|
48
|
+
height: viewport.height,
|
|
103
49
|
overflow: 'hidden',
|
|
104
|
-
}, children: [baseLayer,
|
|
50
|
+
}, children: [baseLayer, showCursor && (_jsx(CursorOverlay, { cursorEvents: cursorEvents, clickEvents: clickEvents, fps: fps })), _jsx(NarrationTrack, { narrations: narrations, fps: fps })] }));
|
|
105
51
|
};
|
|
106
52
|
//# sourceMappingURL=DemoVideo.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DemoVideo.js","sourceRoot":"","sources":["../../../src/composition/DemoVideo.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,
|
|
1
|
+
{"version":3,"file":"DemoVideo.js","sourceRoot":"","sources":["../../../src/composition/DemoVideo.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAI5D,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE9E,MAAM,SAAS,GAAG,EAAE,KAAK,EAAE,MAAe,EAAE,MAAM,EAAE,MAAe,EAAE,OAAO,EAAE,OAAgB,EAAE,CAAC;AAOjG,MAAM,CAAC,MAAM,SAAS,GAAoB,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAE;IACnE,MAAM,GAAG,GAAG,EAAE,CAAC;IACf,MAAM,KAAK,GAAG,eAAe,EAAE,CAAC;IAChC,MAAM,EAAE,aAAa,EAAE,iBAAiB,EAAE,QAAQ,EAAE,GAAG,QAAQ,CAAC,QAAQ,CAAC;IAEzE,MAAM,UAAU,GAAG,kBAAkB,CAAC,KAAK,EAAE,aAAa,EAAE,iBAAiB,CAAC,CAAC;IAE/E,MAAM,cAAc,GAAG,oBAAoB,CAAC,QAAQ,CAAC,MAAM,EAAE,aAAa,EAAE,iBAAiB,CAAC,CAAC;IAE/F,MAAM,YAAY,GAAG,qBAAqB,CACxC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,EAA0B,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,eAAe,CAAC,CACjF,CAAC;IAEF,MAAM,WAAW,GAAG,cAAc,CAAC,MAAM,CACvC,CAAC,CAAC,EAAoB,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,KAAK,OAAO,CACrE,CAAC;IAEF,MAAM,UAAU,GAAG,cAAc,CAAC,MAAM,CACtC,CAAC,CAAC,EAAuB,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CACnD,CAAC;IAEF,MAAM,WAAW,GAAG,cAAc,CAAC,MAAM,CACvC,CAAC,CAAC,EAAmB,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CACxD,CAAC;IAEF,IAAI,SAA0B,CAAC;IAC/B,IAAI,UAAU,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;QACrC,MAAM,MAAM,GAAG,mBAAmB,CAAC,UAAU,CAAC,UAAU,EAAE,UAAU,CAAC,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;QAC/F,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAiB,EAAE,CAAC;QACzE,MAAM,aAAa,GAAG,MAAM,CAAC,QAAQ,IAAI,QAAQ,EAAE,UAAU,IAAI,SAAS,CAAC;QAE3E,MAAM,WAAW,GAAG,KAAC,GAAG,IAAC,GAAG,EAAE,UAAU,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,SAAS,GAAI,CAAC;QACtF,MAAM,eAAe,GAAG,KAAC,GAAG,IAAC,GAAG,EAAE,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,SAAS,GAAI,CAAC;QAEzF,MAAM,KAAK,GAAG,CACZ,8BACE,cAAK,KAAK,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,QAAQ,EAAE,GAAG,MAAM,CAAC,QAAQ,EAAE,YAC5E,eAAe,GACZ,EACN,cAAK,KAAK,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,QAAQ,EAAE,GAAG,MAAM,CAAC,IAAI,EAAE,YACxE,WAAW,GACR,EACL,MAAM,CAAC,KAAK,IAAI,CACf,cAAK,KAAK,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,QAAQ,EAAE,GAAG,MAAM,CAAC,KAAK,EAAE,YACzE,WAAW,GACR,CACP,IACA,CACJ,CAAC;QACF,IAAI,YAAY,GAAoB,KAAK,CAAC;QAC1C,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YACrB,YAAY,GAAG,cAAK,KAAK,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,MAAM,CAAC,SAAS,EAAE,YAAG,KAAK,GAAO,CAAC;QACpG,CAAC;QACD,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;YACvB,YAAY,GAAG,cAAK,KAAK,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,YAAG,YAAY,GAAO,CAAC;QACvH,CAAC;QAED,SAAS,GAAG,CACV,8BACE,cAAK,KAAK,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,EAAE,eAAe,EAAE,aAAa,EAAE,GAAI,EACjF,YAAY,IACZ,CACJ,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,SAAS,GAAG,KAAC,GAAG,IAAC,GAAG,EAAE,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,SAAS,GAAI,CAAC;IAC1E,CAAC;IAED,MAAM,aAAa,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC;IAC3C,MAAM,WAAW,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE;QACvC,MAAM,GAAG,GAAG,CAAC,CAAC,KAAM,CAAC,QAAQ,IAAI,IAAI,CAAC;QACtC,OAAO,aAAa,IAAI,CAAC,CAAC,WAAW,IAAI,aAAa,GAAG,CAAC,CAAC,WAAW,GAAG,GAAG,CAAC;IAC/E,CAAC,CAAC,CAAC;IACH,MAAM,UAAU,GAAG,UAAU,CAAC,IAAI,KAAK,YAAY,IAAI,CAAC,WAAW,CAAC;IAEpE,OAAO,CACL,eACE,KAAK,EAAE;YACL,QAAQ,EAAE,UAAU;YACpB,KAAK,EAAE,QAAQ,CAAC,KAAK;YACrB,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,QAAQ,EAAE,QAAQ;SACnB,aAEA,SAAS,EACT,UAAU,IAAI,CACb,KAAC,aAAa,IAAC,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,GAAG,EAAE,GAAG,GAAI,CAClF,EACD,KAAC,cAAc,IAAC,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,GAAI,IAChD,CACP,CAAC;AACJ,CAAC,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ManifestEntry, TransitionMarker, TransitionType, TimelineEvent } from '../timeline/types.js';
|
|
2
|
+
export interface SourceFrameResolution {
|
|
3
|
+
type: 'source';
|
|
4
|
+
file: string;
|
|
5
|
+
}
|
|
6
|
+
export interface TransitionFrameResolution {
|
|
7
|
+
type: 'transition';
|
|
8
|
+
beforeFile: string;
|
|
9
|
+
afterFile: string;
|
|
10
|
+
progress: number;
|
|
11
|
+
transition: TransitionType;
|
|
12
|
+
}
|
|
13
|
+
export type FrameResolution = SourceFrameResolution | TransitionFrameResolution;
|
|
14
|
+
/** Total source frames (expanding holds). */
|
|
15
|
+
export declare function expandedFrameCount(manifest: ManifestEntry[]): number;
|
|
16
|
+
/** Get image file for a source frame index. */
|
|
17
|
+
export declare function sourceFrameImage(manifest: ManifestEntry[], sourceFrame: number): string;
|
|
18
|
+
/** Total output frames accounting for transition insertions. */
|
|
19
|
+
export declare function totalOutputFrames(manifest: ManifestEntry[], transitions: TransitionMarker[]): number;
|
|
20
|
+
/**
|
|
21
|
+
* Resolve what to render for a given output frame.
|
|
22
|
+
*
|
|
23
|
+
* Transitions are sorted by afterEntryIndex. For each transition at expanded
|
|
24
|
+
* source frame S:
|
|
25
|
+
* - Source frames 0..S map 1:1 to output
|
|
26
|
+
* - Output frames S+1..S+durationFrames are the transition animation
|
|
27
|
+
* - Source frame S+1 (the first expanded frame of the next entry) is consumed
|
|
28
|
+
* - Source frames S+2.. resume 1:1 with accumulated offset
|
|
29
|
+
*/
|
|
30
|
+
export declare function resolveOutputFrame(outputFrame: number, manifest: ManifestEntry[], transitions: TransitionMarker[]): FrameResolution;
|
|
31
|
+
/**
|
|
32
|
+
* Offset event timestamps to account for transition insertions.
|
|
33
|
+
* Each transition at source frame S inserts (durationFrames - 1) extra frames worth of time.
|
|
34
|
+
* Events after each transition get shifted forward.
|
|
35
|
+
*/
|
|
36
|
+
export declare function remapEventsForOutput<T extends TimelineEvent>(events: T[], manifest: ManifestEntry[], transitions: TransitionMarker[]): T[];
|
|
37
|
+
//# sourceMappingURL=frame-resolve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame-resolve.d.ts","sourceRoot":"","sources":["../../../src/composition/frame-resolve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAI3G,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,YAAY,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,cAAc,CAAC;CAC5B;AAED,MAAM,MAAM,eAAe,GAAG,qBAAqB,GAAG,yBAAyB,CAAC;AAEhF,6CAA6C;AAC7C,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,aAAa,EAAE,GAAG,MAAM,CAMpE;AAED,+CAA+C;AAC/C,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,aAAa,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAQvF;AAsBD,gEAAgE;AAChE,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,aAAa,EAAE,EAAE,WAAW,EAAE,gBAAgB,EAAE,GAAG,MAAM,CASpG;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,aAAa,EAAE,EACzB,WAAW,EAAE,gBAAgB,EAAE,GAC9B,eAAe,CAwCjB;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,aAAa,EAC1D,MAAM,EAAE,CAAC,EAAE,EACX,QAAQ,EAAE,aAAa,EAAE,EACzB,WAAW,EAAE,gBAAgB,EAAE,GAC9B,CAAC,EAAE,CAiBL"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
const FRAME_MS = 1000 / 30;
|
|
2
|
+
/** Total source frames (expanding holds). */
|
|
3
|
+
export function expandedFrameCount(manifest) {
|
|
4
|
+
let count = 0;
|
|
5
|
+
for (const e of manifest) {
|
|
6
|
+
count += e.type === 'hold' ? e.count : 1;
|
|
7
|
+
}
|
|
8
|
+
return count;
|
|
9
|
+
}
|
|
10
|
+
/** Get image file for a source frame index. */
|
|
11
|
+
export function sourceFrameImage(manifest, sourceFrame) {
|
|
12
|
+
let accumulated = 0;
|
|
13
|
+
for (const entry of manifest) {
|
|
14
|
+
const count = entry.type === 'hold' ? entry.count : 1;
|
|
15
|
+
if (sourceFrame < accumulated + count)
|
|
16
|
+
return entry.file;
|
|
17
|
+
accumulated += count;
|
|
18
|
+
}
|
|
19
|
+
return manifest[manifest.length - 1].file;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Convert a manifest entry index to an expanded source frame index.
|
|
23
|
+
* Returns the first expanded frame of the given entry.
|
|
24
|
+
*/
|
|
25
|
+
function entryFrameCount(entry) {
|
|
26
|
+
return entry.type === 'hold' ? entry.count : 1;
|
|
27
|
+
}
|
|
28
|
+
function entryToSourceFrame(manifest, entryIndex) {
|
|
29
|
+
let frame = 0;
|
|
30
|
+
for (let i = 0; i < entryIndex && i < manifest.length; i++) {
|
|
31
|
+
frame += entryFrameCount(manifest[i]);
|
|
32
|
+
}
|
|
33
|
+
return frame;
|
|
34
|
+
}
|
|
35
|
+
function lastSourceFrameOfEntry(manifest, entryIndex) {
|
|
36
|
+
return entryToSourceFrame(manifest, entryIndex) + entryFrameCount(manifest[entryIndex]) - 1;
|
|
37
|
+
}
|
|
38
|
+
/** Total output frames accounting for transition insertions. */
|
|
39
|
+
export function totalOutputFrames(manifest, transitions) {
|
|
40
|
+
const source = expandedFrameCount(manifest);
|
|
41
|
+
let inserted = 0;
|
|
42
|
+
let consumed = 0;
|
|
43
|
+
for (const t of transitions) {
|
|
44
|
+
inserted += t.durationFrames;
|
|
45
|
+
consumed += 1; // each transition consumes the first expanded frame of the next entry
|
|
46
|
+
}
|
|
47
|
+
return source + inserted - consumed;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Resolve what to render for a given output frame.
|
|
51
|
+
*
|
|
52
|
+
* Transitions are sorted by afterEntryIndex. For each transition at expanded
|
|
53
|
+
* source frame S:
|
|
54
|
+
* - Source frames 0..S map 1:1 to output
|
|
55
|
+
* - Output frames S+1..S+durationFrames are the transition animation
|
|
56
|
+
* - Source frame S+1 (the first expanded frame of the next entry) is consumed
|
|
57
|
+
* - Source frames S+2.. resume 1:1 with accumulated offset
|
|
58
|
+
*/
|
|
59
|
+
export function resolveOutputFrame(outputFrame, manifest, transitions) {
|
|
60
|
+
const sorted = [...transitions].sort((a, b) => a.afterEntryIndex - b.afterEntryIndex);
|
|
61
|
+
let offset = 0; // accumulated shift: inserted frames minus consumed frames
|
|
62
|
+
for (const t of sorted) {
|
|
63
|
+
const sourceS = lastSourceFrameOfEntry(manifest, t.afterEntryIndex);
|
|
64
|
+
// The output frame where the source frame S lives
|
|
65
|
+
const outputS = sourceS + offset;
|
|
66
|
+
// Transition occupies output frames (outputS + 1) through (outputS + durationFrames)
|
|
67
|
+
const transStart = outputS + 1;
|
|
68
|
+
const transEnd = outputS + t.durationFrames;
|
|
69
|
+
if (outputFrame <= outputS) {
|
|
70
|
+
// Before this transition: resolve as source
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
if (outputFrame >= transStart && outputFrame <= transEnd) {
|
|
74
|
+
const progress = (outputFrame - transStart + 1) / t.durationFrames;
|
|
75
|
+
const beforeFile = sourceFrameImage(manifest, sourceS);
|
|
76
|
+
// "after" is the first frame of the next manifest entry
|
|
77
|
+
const afterEntryIdx = t.afterEntryIndex + 1;
|
|
78
|
+
const afterFile = afterEntryIdx < manifest.length
|
|
79
|
+
? manifest[afterEntryIdx].file
|
|
80
|
+
: manifest[manifest.length - 1].file;
|
|
81
|
+
return { type: 'transition', beforeFile, afterFile, progress, transition: t.transition };
|
|
82
|
+
}
|
|
83
|
+
// Past this transition: update offset
|
|
84
|
+
// We inserted durationFrames and consumed 1 source frame
|
|
85
|
+
offset += t.durationFrames - 1;
|
|
86
|
+
}
|
|
87
|
+
// Map output frame back to source frame
|
|
88
|
+
const sourceFrame = outputFrame - offset;
|
|
89
|
+
const clamped = Math.max(0, Math.min(sourceFrame, expandedFrameCount(manifest) - 1));
|
|
90
|
+
return { type: 'source', file: sourceFrameImage(manifest, clamped) };
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Offset event timestamps to account for transition insertions.
|
|
94
|
+
* Each transition at source frame S inserts (durationFrames - 1) extra frames worth of time.
|
|
95
|
+
* Events after each transition get shifted forward.
|
|
96
|
+
*/
|
|
97
|
+
export function remapEventsForOutput(events, manifest, transitions) {
|
|
98
|
+
const sorted = [...transitions].sort((a, b) => a.afterEntryIndex - b.afterEntryIndex);
|
|
99
|
+
return events.map(event => {
|
|
100
|
+
let offsetMs = 0;
|
|
101
|
+
for (const t of sorted) {
|
|
102
|
+
// Convert transition's afterEntryIndex to a source timestamp
|
|
103
|
+
const sourceS = lastSourceFrameOfEntry(manifest, t.afterEntryIndex);
|
|
104
|
+
const transitionSourceMs = sourceS * FRAME_MS;
|
|
105
|
+
if (event.timestampMs > transitionSourceMs) {
|
|
106
|
+
offsetMs += (t.durationFrames - 1) * FRAME_MS;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (offsetMs === 0)
|
|
110
|
+
return event;
|
|
111
|
+
return { ...event, timestampMs: event.timestampMs + offsetMs };
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=frame-resolve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame-resolve.js","sourceRoot":"","sources":["../../../src/composition/frame-resolve.ts"],"names":[],"mappings":"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"}
|