rollberry 0.1.8 → 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/CHANGELOG.md +61 -0
- package/README.md +189 -7
- package/dist/capture/actions.d.ts +11 -0
- package/dist/capture/actions.js +81 -0
- package/dist/capture/browser-install.d.ts +3 -0
- package/dist/capture/browser-install.js +1 -1
- package/dist/capture/browser.d.ts +13 -0
- package/dist/capture/browser.js +16 -10
- package/dist/capture/capture.d.ts +8 -0
- package/dist/capture/capture.js +566 -55
- package/dist/capture/constants.d.ts +15 -0
- package/dist/capture/constants.js +7 -0
- package/dist/capture/ffmpeg.d.ts +41 -0
- package/dist/capture/ffmpeg.js +457 -36
- package/dist/capture/logger.d.ts +15 -0
- package/dist/capture/preflight.d.ts +4 -0
- package/dist/capture/preflight.js +8 -2
- package/dist/capture/progress.d.ts +7 -0
- package/dist/capture/progress.js +50 -0
- package/dist/capture/scroll-plan.d.ts +9 -0
- package/dist/capture/scroll-plan.js +7 -1
- package/dist/capture/stabilize.d.ts +7 -0
- package/dist/capture/stabilize.js +11 -5
- package/dist/capture/types.d.ts +216 -0
- package/dist/capture/utils.d.ts +14 -0
- package/dist/capture/utils.js +30 -3
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +86 -5
- package/dist/index.d.ts +7 -0
- package/dist/index.js +6 -0
- package/dist/options.d.ts +42 -0
- package/dist/options.js +229 -115
- package/dist/project.d.ts +27 -0
- package/dist/project.js +722 -0
- package/dist/render-plan.d.ts +103 -0
- package/dist/render-plan.js +144 -0
- package/dist/run-capture.d.ts +3 -0
- package/dist/run-capture.js +20 -10
- package/dist/run-render.d.ts +17 -0
- package/dist/run-render.js +434 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +1 -0
- package/package.json +10 -3
- package/rollberry.project.sample.json +92 -0
- package/rollberry.project.schema.json +474 -0
package/dist/options.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { resolve } from 'node:path';
|
|
2
2
|
import { parseArgs } from 'node:util';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
3
|
+
import { MAX_FPS } from './capture/constants.js';
|
|
4
|
+
import { parseCaptureUrl, validateHideSelector } from './capture/utils.js';
|
|
5
|
+
import { VERSION } from './version.js';
|
|
6
|
+
export const DEFAULT_OUT_FILE = 'rollberry.mp4';
|
|
7
|
+
export const DEFAULT_VIEWPORT = '1440x900';
|
|
8
|
+
export const DEFAULT_FPS = 60;
|
|
9
|
+
export const DEFAULT_DURATION = 'auto';
|
|
10
|
+
export const DEFAULT_MOTION = 'ease-in-out-sine';
|
|
11
|
+
export const DEFAULT_TIMEOUT_MS = 30_000;
|
|
10
12
|
export class CliError extends Error {
|
|
11
13
|
showUsage;
|
|
12
14
|
constructor(message, showUsage = false) {
|
|
@@ -15,119 +17,79 @@ export class CliError extends Error {
|
|
|
15
17
|
this.name = 'CliError';
|
|
16
18
|
}
|
|
17
19
|
}
|
|
18
|
-
export
|
|
20
|
+
export class HelpRequest extends Error {
|
|
21
|
+
constructor(message) {
|
|
22
|
+
super(message);
|
|
23
|
+
this.name = 'HelpRequest';
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export class VersionRequest extends Error {
|
|
27
|
+
version = VERSION;
|
|
28
|
+
constructor() {
|
|
29
|
+
super(VERSION);
|
|
30
|
+
this.name = 'VersionRequest';
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export function parseCommandArgs(argv = process.argv.slice(2)) {
|
|
19
34
|
const [command, ...rest] = argv;
|
|
20
|
-
if (
|
|
21
|
-
throw new
|
|
35
|
+
if (command === '--help' || command === '-h') {
|
|
36
|
+
throw new HelpRequest(formatUsage());
|
|
22
37
|
}
|
|
23
|
-
if (command
|
|
24
|
-
throw new
|
|
38
|
+
if (command === '--version' || command === '-V') {
|
|
39
|
+
throw new VersionRequest();
|
|
25
40
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
allowPositionals: true,
|
|
29
|
-
options: {
|
|
30
|
-
out: {
|
|
31
|
-
type: 'string',
|
|
32
|
-
},
|
|
33
|
-
viewport: {
|
|
34
|
-
type: 'string',
|
|
35
|
-
},
|
|
36
|
-
fps: {
|
|
37
|
-
type: 'string',
|
|
38
|
-
},
|
|
39
|
-
duration: {
|
|
40
|
-
type: 'string',
|
|
41
|
-
},
|
|
42
|
-
motion: {
|
|
43
|
-
type: 'string',
|
|
44
|
-
},
|
|
45
|
-
timeout: {
|
|
46
|
-
type: 'string',
|
|
47
|
-
},
|
|
48
|
-
'wait-for': {
|
|
49
|
-
type: 'string',
|
|
50
|
-
},
|
|
51
|
-
'hide-selector': {
|
|
52
|
-
type: 'string',
|
|
53
|
-
multiple: true,
|
|
54
|
-
},
|
|
55
|
-
'debug-frames-dir': {
|
|
56
|
-
type: 'string',
|
|
57
|
-
},
|
|
58
|
-
'page-gap': {
|
|
59
|
-
type: 'string',
|
|
60
|
-
},
|
|
61
|
-
manifest: {
|
|
62
|
-
type: 'string',
|
|
63
|
-
},
|
|
64
|
-
'log-file': {
|
|
65
|
-
type: 'string',
|
|
66
|
-
},
|
|
67
|
-
},
|
|
68
|
-
strict: true,
|
|
69
|
-
});
|
|
70
|
-
if (parsed.positionals.length === 0) {
|
|
71
|
-
throw new CliError('capture にはURLが必要です。', true);
|
|
41
|
+
if (!command) {
|
|
42
|
+
throw createMissingSubcommandError();
|
|
72
43
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
44
|
+
switch (command) {
|
|
45
|
+
case 'capture':
|
|
46
|
+
return { kind: 'capture', options: parseCaptureArgs(rest) };
|
|
47
|
+
case 'render':
|
|
48
|
+
return { kind: 'render', options: parseRenderArgs(rest) };
|
|
49
|
+
default:
|
|
50
|
+
throw createUnknownSubcommandError(command);
|
|
77
51
|
}
|
|
78
|
-
const outPath = resolveOutPath(parsed.values.out ?? DEFAULT_OUT_FILE);
|
|
79
|
-
const pageGapSeconds = parseWithCliError(parsed.values['page-gap'] ?? '0', parseNonNegativeNumber);
|
|
80
|
-
return {
|
|
81
|
-
urls: toNonEmptyArray(urls),
|
|
82
|
-
outPath,
|
|
83
|
-
manifestPath: parsed.values.manifest
|
|
84
|
-
? resolveOutPath(parsed.values.manifest)
|
|
85
|
-
: deriveSidecarPath(outPath, '.manifest.json'),
|
|
86
|
-
logFilePath: parsed.values['log-file']
|
|
87
|
-
? resolveOutPath(parsed.values['log-file'])
|
|
88
|
-
: deriveSidecarPath(outPath, '.log.jsonl'),
|
|
89
|
-
viewport: parseWithCliError(parsed.values.viewport ?? DEFAULT_VIEWPORT, parseViewport),
|
|
90
|
-
fps: parseWithCliError(parsed.values.fps ?? String(DEFAULT_FPS), parsePositiveInt),
|
|
91
|
-
duration: durationOption === 'auto'
|
|
92
|
-
? 'auto'
|
|
93
|
-
: parseWithCliError(durationOption, parsePositiveNumber),
|
|
94
|
-
motion: parseWithCliError(parsed.values.motion ?? DEFAULT_MOTION, parseMotion),
|
|
95
|
-
timeoutMs: parseWithCliError(parsed.values.timeout ?? String(DEFAULT_TIMEOUT_MS), parsePositiveInt),
|
|
96
|
-
waitFor: parseWithCliError(parsed.values['wait-for'] ?? 'load', parseWaitFor),
|
|
97
|
-
hideSelectors: parsed.values['hide-selector'] ?? [],
|
|
98
|
-
pageGapSeconds,
|
|
99
|
-
debugFramesDir: parsed.values['debug-frames-dir']
|
|
100
|
-
? resolveOutPath(parsed.values['debug-frames-dir'])
|
|
101
|
-
: undefined,
|
|
102
|
-
};
|
|
103
52
|
}
|
|
104
|
-
function
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
return items;
|
|
53
|
+
export function parseCliArgs(argv = process.argv.slice(2)) {
|
|
54
|
+
const args = argv[0] === 'capture' ? argv.slice(1) : argv;
|
|
55
|
+
return parseCaptureArgs(args);
|
|
109
56
|
}
|
|
110
57
|
export function formatUsage() {
|
|
111
58
|
return [
|
|
59
|
+
`rollberry v${VERSION} — Capture and render web pages into video`,
|
|
60
|
+
'',
|
|
112
61
|
'Usage:',
|
|
113
62
|
' rollberry capture <url...> [options]',
|
|
63
|
+
' rollberry render <project.json> [options]',
|
|
64
|
+
' rollberry --help | -h',
|
|
65
|
+
' rollberry --version | -V',
|
|
114
66
|
'',
|
|
115
|
-
'Options:',
|
|
67
|
+
'Capture Options:',
|
|
116
68
|
' --out <file> Output MP4 path (default: ./rollberry.mp4)',
|
|
117
69
|
' --viewport <WxH> Viewport size (default: 1440x900)',
|
|
118
|
-
|
|
70
|
+
` --fps <n> Frames per second (default: 60, max: ${MAX_FPS})`,
|
|
119
71
|
' --duration <seconds|auto> Capture duration (default: auto)',
|
|
120
|
-
' --motion <curve> ease-in-out-sine | linear',
|
|
72
|
+
' --motion <curve> ease-in-out-sine | linear (default: ease-in-out-sine)',
|
|
121
73
|
' --timeout <ms> Navigation timeout (default: 30000)',
|
|
122
|
-
' --wait-for <mode> load | selector:<css> | ms:<n>',
|
|
123
|
-
' --hide-selector <css> Hide CSS selector before capture',
|
|
74
|
+
' --wait-for <mode> load | selector:<css> | ms:<n> (default: load)',
|
|
75
|
+
' --hide-selector <css> Hide CSS selector before capture (repeatable)',
|
|
76
|
+
' --force Overwrite output file if it already exists',
|
|
124
77
|
' --debug-frames-dir <dir> Save raw PNG frames for debugging',
|
|
125
78
|
' --page-gap <seconds> Pause between pages (default: 0)',
|
|
126
79
|
' --manifest <file> Manifest JSON path (default: <out>.manifest.json)',
|
|
127
80
|
' --log-file <file> Log JSONL path (default: <out>.log.jsonl)',
|
|
81
|
+
'',
|
|
82
|
+
'Render Options:',
|
|
83
|
+
' --output <name> Render only the named output (repeatable)',
|
|
84
|
+
' --force Overwrite configured output files',
|
|
85
|
+
'',
|
|
86
|
+
'Examples:',
|
|
87
|
+
' rollberry capture http://localhost:3000',
|
|
88
|
+
' rollberry capture https://example.com --out demo.mp4 --viewport 1920x1080',
|
|
89
|
+
' rollberry render ./rollberry.project.json --output mobile',
|
|
128
90
|
].join('\n');
|
|
129
91
|
}
|
|
130
|
-
function parseWithCliError(rawValue, parser) {
|
|
92
|
+
export function parseWithCliError(rawValue, parser) {
|
|
131
93
|
try {
|
|
132
94
|
return parser(rawValue);
|
|
133
95
|
}
|
|
@@ -135,66 +97,66 @@ function parseWithCliError(rawValue, parser) {
|
|
|
135
97
|
if (error instanceof CliError) {
|
|
136
98
|
throw error;
|
|
137
99
|
}
|
|
138
|
-
throw new CliError(error instanceof Error ? error.message : '
|
|
100
|
+
throw new CliError(error instanceof Error ? error.message : 'Failed to parse arguments.');
|
|
139
101
|
}
|
|
140
102
|
}
|
|
141
|
-
function resolveOutPath(path) {
|
|
103
|
+
export function resolveOutPath(path) {
|
|
142
104
|
return resolve(process.cwd(), path);
|
|
143
105
|
}
|
|
144
|
-
function deriveSidecarPath(path, suffix) {
|
|
106
|
+
export function deriveSidecarPath(path, suffix) {
|
|
145
107
|
const extension = /\.([^.]+)$/u.exec(path);
|
|
146
108
|
if (!extension) {
|
|
147
109
|
return `${path}${suffix}`;
|
|
148
110
|
}
|
|
149
111
|
return path.slice(0, -extension[0].length) + suffix;
|
|
150
112
|
}
|
|
151
|
-
function parseViewport(rawViewport) {
|
|
113
|
+
export function parseViewport(rawViewport) {
|
|
152
114
|
const match = /^(?<width>\d+)x(?<height>\d+)$/u.exec(rawViewport);
|
|
153
115
|
if (!match?.groups) {
|
|
154
|
-
throw new Error(`--viewport
|
|
116
|
+
throw new Error(`--viewport must be in "WxH" format (e.g. "1440x900"): ${rawViewport}`);
|
|
155
117
|
}
|
|
156
118
|
const width = Number(match.groups.width);
|
|
157
119
|
const height = Number(match.groups.height);
|
|
158
120
|
if (width <= 0 || height <= 0) {
|
|
159
|
-
throw new Error(`--viewport
|
|
121
|
+
throw new Error(`--viewport dimensions must be positive (e.g. "1440x900"): ${rawViewport}`);
|
|
160
122
|
}
|
|
161
123
|
return { width, height };
|
|
162
124
|
}
|
|
163
|
-
function parsePositiveInt(rawValue) {
|
|
125
|
+
export function parsePositiveInt(rawValue) {
|
|
164
126
|
const value = Number(rawValue);
|
|
165
127
|
if (!Number.isInteger(value) || value <= 0) {
|
|
166
|
-
throw new Error(
|
|
128
|
+
throw new Error(`Expected a positive integer (e.g. "60"): ${rawValue}`);
|
|
167
129
|
}
|
|
168
130
|
return value;
|
|
169
131
|
}
|
|
170
|
-
function parsePositiveNumber(rawValue) {
|
|
132
|
+
export function parsePositiveNumber(rawValue) {
|
|
171
133
|
const value = Number(rawValue);
|
|
172
134
|
if (!Number.isFinite(value) || value <= 0) {
|
|
173
|
-
throw new Error(
|
|
135
|
+
throw new Error(`Expected a positive number (e.g. "5.0"): ${rawValue}`);
|
|
174
136
|
}
|
|
175
137
|
return value;
|
|
176
138
|
}
|
|
177
|
-
function parseNonNegativeNumber(rawValue) {
|
|
139
|
+
export function parseNonNegativeNumber(rawValue) {
|
|
178
140
|
const value = Number(rawValue);
|
|
179
141
|
if (!Number.isFinite(value) || value < 0) {
|
|
180
|
-
throw new Error(`0
|
|
142
|
+
throw new Error(`Expected a non-negative number (e.g. "0" or "1.5"): ${rawValue}`);
|
|
181
143
|
}
|
|
182
144
|
return value;
|
|
183
145
|
}
|
|
184
|
-
function parseMotion(rawMotion) {
|
|
146
|
+
export function parseMotion(rawMotion) {
|
|
185
147
|
if (rawMotion === 'ease-in-out-sine' || rawMotion === 'linear') {
|
|
186
148
|
return rawMotion;
|
|
187
149
|
}
|
|
188
|
-
throw new Error(`--motion
|
|
150
|
+
throw new Error(`--motion must be "ease-in-out-sine" or "linear": ${rawMotion}`);
|
|
189
151
|
}
|
|
190
|
-
function parseWaitFor(rawWaitFor) {
|
|
152
|
+
export function parseWaitFor(rawWaitFor) {
|
|
191
153
|
if (rawWaitFor === 'load') {
|
|
192
154
|
return { kind: 'load' };
|
|
193
155
|
}
|
|
194
156
|
if (rawWaitFor.startsWith('selector:')) {
|
|
195
157
|
const selector = rawWaitFor.slice('selector:'.length).trim();
|
|
196
158
|
if (!selector) {
|
|
197
|
-
throw new Error('--wait-for selector:<css>
|
|
159
|
+
throw new Error('--wait-for selector:<css> requires a non-empty CSS selector.');
|
|
198
160
|
}
|
|
199
161
|
return {
|
|
200
162
|
kind: 'selector',
|
|
@@ -207,5 +169,157 @@ function parseWaitFor(rawWaitFor) {
|
|
|
207
169
|
ms: parsePositiveInt(rawWaitFor.slice('ms:'.length)),
|
|
208
170
|
};
|
|
209
171
|
}
|
|
210
|
-
throw new Error(`--wait-for
|
|
172
|
+
throw new Error(`--wait-for must be "load", "selector:<css>", or "ms:<n>": ${rawWaitFor}`);
|
|
173
|
+
}
|
|
174
|
+
function parseCaptureArgs(args) {
|
|
175
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
176
|
+
throw new HelpRequest(formatUsage());
|
|
177
|
+
}
|
|
178
|
+
const parsed = parseArgs({
|
|
179
|
+
args,
|
|
180
|
+
allowPositionals: true,
|
|
181
|
+
options: {
|
|
182
|
+
out: {
|
|
183
|
+
type: 'string',
|
|
184
|
+
},
|
|
185
|
+
viewport: {
|
|
186
|
+
type: 'string',
|
|
187
|
+
},
|
|
188
|
+
fps: {
|
|
189
|
+
type: 'string',
|
|
190
|
+
},
|
|
191
|
+
duration: {
|
|
192
|
+
type: 'string',
|
|
193
|
+
},
|
|
194
|
+
motion: {
|
|
195
|
+
type: 'string',
|
|
196
|
+
},
|
|
197
|
+
timeout: {
|
|
198
|
+
type: 'string',
|
|
199
|
+
},
|
|
200
|
+
'wait-for': {
|
|
201
|
+
type: 'string',
|
|
202
|
+
},
|
|
203
|
+
'hide-selector': {
|
|
204
|
+
type: 'string',
|
|
205
|
+
multiple: true,
|
|
206
|
+
},
|
|
207
|
+
'debug-frames-dir': {
|
|
208
|
+
type: 'string',
|
|
209
|
+
},
|
|
210
|
+
'page-gap': {
|
|
211
|
+
type: 'string',
|
|
212
|
+
},
|
|
213
|
+
manifest: {
|
|
214
|
+
type: 'string',
|
|
215
|
+
},
|
|
216
|
+
'log-file': {
|
|
217
|
+
type: 'string',
|
|
218
|
+
},
|
|
219
|
+
force: {
|
|
220
|
+
type: 'boolean',
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
strict: true,
|
|
224
|
+
});
|
|
225
|
+
if (parsed.positionals.length === 0) {
|
|
226
|
+
throw new CliError('capture requires at least one URL.', true);
|
|
227
|
+
}
|
|
228
|
+
const urls = parsed.positionals.map((raw) => parseWithCliError(raw, parseCaptureUrl));
|
|
229
|
+
const durationOption = parsed.values.duration ?? DEFAULT_DURATION;
|
|
230
|
+
if (durationOption !== 'auto' && Number.isNaN(Number(durationOption))) {
|
|
231
|
+
throw new CliError(`--duration must be "auto" or a positive number (e.g. --duration 5): ${durationOption}`);
|
|
232
|
+
}
|
|
233
|
+
const outPath = resolveOutPath(parsed.values.out ?? DEFAULT_OUT_FILE);
|
|
234
|
+
const pageGapSeconds = parseWithCliError(parsed.values['page-gap'] ?? '0', parseNonNegativeNumber);
|
|
235
|
+
const hideSelectors = parsed.values['hide-selector'] ?? [];
|
|
236
|
+
for (const selector of hideSelectors) {
|
|
237
|
+
parseWithCliError(selector, validateHideSelector);
|
|
238
|
+
}
|
|
239
|
+
const fps = parseWithCliError(parsed.values.fps ?? String(DEFAULT_FPS), parsePositiveInt);
|
|
240
|
+
if (fps > MAX_FPS) {
|
|
241
|
+
throw new CliError(`--fps must be at most ${MAX_FPS}: ${fps}`);
|
|
242
|
+
}
|
|
243
|
+
return {
|
|
244
|
+
urls: toNonEmptyArray(urls),
|
|
245
|
+
outPath,
|
|
246
|
+
manifestPath: parsed.values.manifest
|
|
247
|
+
? resolveOutPath(parsed.values.manifest)
|
|
248
|
+
: deriveSidecarPath(outPath, '.manifest.json'),
|
|
249
|
+
logFilePath: parsed.values['log-file']
|
|
250
|
+
? resolveOutPath(parsed.values['log-file'])
|
|
251
|
+
: deriveSidecarPath(outPath, '.log.jsonl'),
|
|
252
|
+
viewport: parseWithCliError(parsed.values.viewport ?? DEFAULT_VIEWPORT, parseViewport),
|
|
253
|
+
fps,
|
|
254
|
+
duration: durationOption === 'auto'
|
|
255
|
+
? 'auto'
|
|
256
|
+
: parseWithCliError(durationOption, parsePositiveNumber),
|
|
257
|
+
motion: parseWithCliError(parsed.values.motion ?? DEFAULT_MOTION, parseMotion),
|
|
258
|
+
timeoutMs: parseWithCliError(parsed.values.timeout ?? String(DEFAULT_TIMEOUT_MS), parsePositiveInt),
|
|
259
|
+
waitFor: parseWithCliError(parsed.values['wait-for'] ?? 'load', parseWaitFor),
|
|
260
|
+
hideSelectors,
|
|
261
|
+
pageGapSeconds,
|
|
262
|
+
debugFramesDir: parsed.values['debug-frames-dir']
|
|
263
|
+
? resolveOutPath(parsed.values['debug-frames-dir'])
|
|
264
|
+
: undefined,
|
|
265
|
+
force: parsed.values.force === true,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
function parseRenderArgs(args) {
|
|
269
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
270
|
+
throw new HelpRequest(formatUsage());
|
|
271
|
+
}
|
|
272
|
+
const parsed = parseArgs({
|
|
273
|
+
args,
|
|
274
|
+
allowPositionals: true,
|
|
275
|
+
options: {
|
|
276
|
+
output: {
|
|
277
|
+
type: 'string',
|
|
278
|
+
multiple: true,
|
|
279
|
+
},
|
|
280
|
+
force: {
|
|
281
|
+
type: 'boolean',
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
strict: true,
|
|
285
|
+
});
|
|
286
|
+
if (parsed.positionals.length === 0) {
|
|
287
|
+
throw new CliError('render requires a project JSON path.', true);
|
|
288
|
+
}
|
|
289
|
+
if (parsed.positionals.length > 1) {
|
|
290
|
+
throw new CliError(`render accepts exactly one project JSON path: ${parsed.positionals.slice(1).join(', ')}`, true);
|
|
291
|
+
}
|
|
292
|
+
return {
|
|
293
|
+
projectPath: resolveOutPath(parsed.positionals[0]),
|
|
294
|
+
outputNames: parsed.values.output ?? [],
|
|
295
|
+
force: parsed.values.force === true,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
function toNonEmptyArray(items) {
|
|
299
|
+
if (items.length === 0) {
|
|
300
|
+
throw new CliError('capture requires at least one URL.', true);
|
|
301
|
+
}
|
|
302
|
+
return items;
|
|
303
|
+
}
|
|
304
|
+
function createMissingSubcommandError() {
|
|
305
|
+
return new CliError([
|
|
306
|
+
'A subcommand is required.',
|
|
307
|
+
'',
|
|
308
|
+
'Available commands:',
|
|
309
|
+
' capture Capture a scroll video from one or more URLs',
|
|
310
|
+
' render Render a project JSON file into one or more videos',
|
|
311
|
+
'',
|
|
312
|
+
'Run rollberry --help for more information.',
|
|
313
|
+
].join('\n'));
|
|
314
|
+
}
|
|
315
|
+
function createUnknownSubcommandError(command) {
|
|
316
|
+
return new CliError([
|
|
317
|
+
`Unknown subcommand: ${command}`,
|
|
318
|
+
'',
|
|
319
|
+
'Available commands:',
|
|
320
|
+
' capture Capture a scroll video from one or more URLs',
|
|
321
|
+
' render Render a project JSON file into one or more videos',
|
|
322
|
+
'',
|
|
323
|
+
'Run rollberry --help for more information.',
|
|
324
|
+
].join('\n'));
|
|
211
325
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { CaptureAudioTrack, CaptureScene, CaptureSubtitleTrack, CaptureTransition, FinalVideoEncodingSettings, IntermediateArtifactProfile, NonEmptyArray, OutputFormat, Viewport } from './capture/types.js';
|
|
2
|
+
import { type RenderCliOptions } from './options.js';
|
|
3
|
+
export interface ResolvedRenderOutput {
|
|
4
|
+
name: string;
|
|
5
|
+
outPath: string;
|
|
6
|
+
format: OutputFormat;
|
|
7
|
+
manifestPath: string;
|
|
8
|
+
logFilePath: string;
|
|
9
|
+
viewport: Viewport;
|
|
10
|
+
fps: number;
|
|
11
|
+
force: boolean;
|
|
12
|
+
audio?: CaptureAudioTrack;
|
|
13
|
+
subtitles?: CaptureSubtitleTrack;
|
|
14
|
+
transition?: CaptureTransition;
|
|
15
|
+
intermediateArtifact: IntermediateArtifactProfile;
|
|
16
|
+
finalVideo: FinalVideoEncodingSettings;
|
|
17
|
+
debugFramesDir?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface ResolvedRenderProject {
|
|
20
|
+
projectPath: string;
|
|
21
|
+
projectName: string;
|
|
22
|
+
summaryManifestPath: string;
|
|
23
|
+
timeoutMs: number;
|
|
24
|
+
scenes: NonEmptyArray<CaptureScene>;
|
|
25
|
+
outputs: NonEmptyArray<ResolvedRenderOutput>;
|
|
26
|
+
}
|
|
27
|
+
export declare function loadRenderProject(options: RenderCliOptions): Promise<ResolvedRenderProject>;
|