@bitmagic/cli 0.1.60 → 0.1.61-dev.1
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 +47 -8
- package/assets/fonts/NotoSans-Bold.ttf +0 -0
- package/assets/fonts/OFL.txt +94 -0
- package/dist/chrome-launch.d.ts +18 -0
- package/dist/chrome-launch.js +24 -0
- package/dist/chrome-launch.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/generate.d.ts +44 -0
- package/dist/commands/generate.js +143 -0
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/trailer-publish.d.ts +15 -0
- package/dist/commands/trailer-publish.js +45 -0
- package/dist/commands/trailer-publish.js.map +1 -0
- package/dist/commands/trailer.d.ts +3 -0
- package/dist/commands/trailer.js +396 -0
- package/dist/commands/trailer.js.map +1 -0
- package/dist/editor/hud-replay-page.d.ts +7 -0
- package/dist/editor/hud-replay-page.js +219 -0
- package/dist/editor/hud-replay-page.js.map +1 -0
- package/dist/editor/recording-panel.d.ts +3 -1
- package/dist/editor/recording-panel.js +6 -2
- package/dist/editor/recording-panel.js.map +1 -1
- package/dist/editor/recording-sink.d.ts +3 -20
- package/dist/editor/recording-sink.js +14 -35
- package/dist/editor/recording-sink.js.map +1 -1
- package/dist/editor/server.js +15 -18
- package/dist/editor/server.js.map +1 -1
- package/dist/editor/shell-page.d.ts +0 -8
- package/dist/editor/shell-page.js +23 -19
- package/dist/editor/shell-page.js.map +1 -1
- package/dist/generate/music.d.ts +51 -0
- package/dist/generate/music.js +93 -0
- package/dist/generate/music.js.map +1 -0
- package/dist/generate/stream.d.ts +13 -0
- package/dist/generate/stream.js +17 -1
- package/dist/generate/stream.js.map +1 -1
- package/dist/project/jobs.d.ts +1 -1
- package/dist/scaffold/project-files.d.ts +0 -1
- package/dist/scaffold/project-files.js +6 -33
- package/dist/scaffold/project-files.js.map +1 -1
- package/dist/scaffold/project.js +3 -1
- package/dist/scaffold/project.js.map +1 -1
- package/dist/scaffold/skill-files.d.ts +22 -0
- package/dist/scaffold/skill-files.js +198 -0
- package/dist/scaffold/skill-files.js.map +1 -0
- package/dist/trailer/analyze.d.ts +23 -0
- package/dist/trailer/analyze.js +186 -0
- package/dist/trailer/analyze.js.map +1 -0
- package/dist/trailer/auto-edl.d.ts +46 -0
- package/dist/trailer/auto-edl.js +143 -0
- package/dist/trailer/auto-edl.js.map +1 -0
- package/dist/trailer/beat-cut.d.ts +57 -0
- package/dist/trailer/beat-cut.js +232 -0
- package/dist/trailer/beat-cut.js.map +1 -0
- package/dist/trailer/client.d.ts +52 -0
- package/dist/trailer/client.js +133 -0
- package/dist/trailer/client.js.map +1 -0
- package/dist/trailer/edl-time.d.ts +61 -0
- package/dist/trailer/edl-time.js +105 -0
- package/dist/trailer/edl-time.js.map +1 -0
- package/dist/trailer/ffmpeg.d.ts +8 -0
- package/dist/trailer/ffmpeg.js +55 -0
- package/dist/trailer/ffmpeg.js.map +1 -0
- package/dist/trailer/hud-raster.d.ts +28 -0
- package/dist/trailer/hud-raster.js +114 -0
- package/dist/trailer/hud-raster.js.map +1 -0
- package/dist/trailer/hud-state.d.ts +110 -0
- package/dist/trailer/hud-state.js +410 -0
- package/dist/trailer/hud-state.js.map +1 -0
- package/dist/trailer/io.d.ts +20 -0
- package/dist/trailer/io.js +15 -0
- package/dist/trailer/io.js.map +1 -0
- package/dist/trailer/make.d.ts +54 -0
- package/dist/trailer/make.js +205 -0
- package/dist/trailer/make.js.map +1 -0
- package/dist/trailer/merge.d.ts +7 -0
- package/dist/trailer/merge.js +144 -0
- package/dist/trailer/merge.js.map +1 -0
- package/dist/trailer/music-beats.d.ts +123 -0
- package/dist/trailer/music-beats.js +707 -0
- package/dist/trailer/music-beats.js.map +1 -0
- package/dist/trailer/paths.d.ts +28 -0
- package/dist/trailer/paths.js +101 -0
- package/dist/trailer/paths.js.map +1 -0
- package/dist/trailer/publish-record.d.ts +17 -0
- package/dist/trailer/publish-record.js +59 -0
- package/dist/trailer/publish-record.js.map +1 -0
- package/dist/trailer/publish.d.ts +45 -0
- package/dist/trailer/publish.js +149 -0
- package/dist/trailer/publish.js.map +1 -0
- package/dist/trailer/record-session.d.ts +72 -0
- package/dist/trailer/record-session.js +328 -0
- package/dist/trailer/record-session.js.map +1 -0
- package/dist/trailer/render.d.ts +57 -0
- package/dist/trailer/render.js +475 -0
- package/dist/trailer/render.js.map +1 -0
- package/dist/trailer/revision.d.ts +8 -0
- package/dist/trailer/revision.js +31 -0
- package/dist/trailer/revision.js.map +1 -0
- package/dist/trailer/synth.d.ts +9 -0
- package/dist/trailer/synth.js +120 -0
- package/dist/trailer/synth.js.map +1 -0
- package/dist/trailer/timeline-types.d.ts +176 -0
- package/dist/trailer/timeline-types.js +379 -0
- package/dist/trailer/timeline-types.js.map +1 -0
- package/dist/trailer/title-raster.d.ts +6 -0
- package/dist/trailer/title-raster.js +47 -0
- package/dist/trailer/title-raster.js.map +1 -0
- package/dist/trailer/titles.d.ts +44 -0
- package/dist/trailer/titles.js +119 -0
- package/dist/trailer/titles.js.map +1 -0
- package/dist/trailer/video-chain.d.ts +41 -0
- package/dist/trailer/video-chain.js +78 -0
- package/dist/trailer/video-chain.js.map +1 -0
- package/dist/verify/browser.js +12 -19
- package/dist/verify/browser.js.map +1 -1
- package/package.json +5 -4
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `bitmagic trailer record` — a guided capture session.
|
|
3
|
+
*
|
|
4
|
+
* Recording used to mean: find the F9 key, remember to press it again, then find the folder.
|
|
5
|
+
* This drives the same engine recorder from outside: it opens the creator's Chrome on the running
|
|
6
|
+
* `bitmagic dev` shell, waits until the game is actually being played, starts the recorder, stops
|
|
7
|
+
* it when the time is up (or when the creator presses F9 themselves), and waits for the
|
|
8
|
+
* `timeline.json` that makes the recording cuttable. A `--flythrough` path does the playing.
|
|
9
|
+
*
|
|
10
|
+
* Two ways to reach the recorder, chosen by what the project's vendored engine understands:
|
|
11
|
+
* - `TRAILER_RECORDING` / `TRAILER_FLYTHROUGH` postMessages, plus `TRAILER_RECORDING_STATE`
|
|
12
|
+
* reports back (engines vendored after the trailer lane shipped);
|
|
13
|
+
* - a synthetic F9 keypress into the game frame, with the recording folder appearing on disk as
|
|
14
|
+
* the only signal (older engines — everything still works, minus the fly-through).
|
|
15
|
+
* `recorderCapabilities` reads which one applies straight from the vendored source, the way the
|
|
16
|
+
* dev sidecar already detects `sinkUrl` support.
|
|
17
|
+
*/
|
|
18
|
+
import * as fs from 'fs';
|
|
19
|
+
import * as path from 'path';
|
|
20
|
+
import { CliError } from '../errors.js';
|
|
21
|
+
import { DEFAULT_START_INTERACTION } from '../verify/start.js';
|
|
22
|
+
import { browserGlArgs } from '../browser-gl.js';
|
|
23
|
+
import { launchSystemChrome } from '../chrome-launch.js';
|
|
24
|
+
import { recordingsDir, TIMELINE_FILE } from './paths.js';
|
|
25
|
+
/** Read straight off `engine/debug/ScreenRecorder.ts`, the vendored recorder source. */
|
|
26
|
+
export function recorderCapabilities(root) {
|
|
27
|
+
let source = '';
|
|
28
|
+
try {
|
|
29
|
+
source = fs.readFileSync(path.join(root, 'engine', 'debug', 'ScreenRecorder.ts'), 'utf-8');
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return { sink: false, message: false, flythrough: false };
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
sink: source.includes('sinkUrl'),
|
|
36
|
+
message: source.includes('TRAILER_RECORDING'),
|
|
37
|
+
flythrough: source.includes('TRAILER_FLYTHROUGH'),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export function upgradeMessage(what, marker) {
|
|
41
|
+
return (`This project's engine predates ${what} (engine/debug/ScreenRecorder.ts has no ${marker} handler). ` +
|
|
42
|
+
'Run `bitmagic upgrade`, restart `bitmagic dev`, and try again.');
|
|
43
|
+
}
|
|
44
|
+
export const NO_DEV_MESSAGE = 'No `bitmagic dev` is running for this project. Start it (`bitmagic dev`), wait for the game to load, ' +
|
|
45
|
+
'then run `bitmagic trailer record` again.';
|
|
46
|
+
export const MAX_FLYTHROUGH_SEC = 600;
|
|
47
|
+
function isVec3(value) {
|
|
48
|
+
if (typeof value !== 'object' || value === null)
|
|
49
|
+
return false;
|
|
50
|
+
const v = value;
|
|
51
|
+
return [v.x, v.y, v.z].every((n) => typeof n === 'number' && Number.isFinite(n));
|
|
52
|
+
}
|
|
53
|
+
/** Validate a fly-through document so a typo is reported here, not as a silent no-op in the engine. */
|
|
54
|
+
export function parseFlythrough(raw, source) {
|
|
55
|
+
if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {
|
|
56
|
+
throw new CliError(`${source}: expected an object with a "markers" array`);
|
|
57
|
+
}
|
|
58
|
+
const doc = raw;
|
|
59
|
+
if (doc.version !== undefined && doc.version !== 1) {
|
|
60
|
+
throw new CliError(`${source}: unsupported version ${JSON.stringify(doc.version)} (write version 1)`);
|
|
61
|
+
}
|
|
62
|
+
if (!Array.isArray(doc.markers))
|
|
63
|
+
throw new CliError(`${source}: "markers" must be an array`);
|
|
64
|
+
if (doc.markers.length < 2)
|
|
65
|
+
throw new CliError(`${source}: a fly-through needs at least two markers`);
|
|
66
|
+
const markers = doc.markers.map((entry, index) => {
|
|
67
|
+
const ctx = `${source}: markers[${index}]`;
|
|
68
|
+
if (typeof entry !== 'object' || entry === null)
|
|
69
|
+
throw new CliError(`${ctx}: expected an object`);
|
|
70
|
+
const m = entry;
|
|
71
|
+
if (!isVec3(m.position))
|
|
72
|
+
throw new CliError(`${ctx}.position: expected { x, y, z } numbers`);
|
|
73
|
+
if (typeof m.timestamp !== 'number' || !Number.isFinite(m.timestamp) || m.timestamp < 0) {
|
|
74
|
+
throw new CliError(`${ctx}.timestamp: expected seconds >= 0`);
|
|
75
|
+
}
|
|
76
|
+
const marker = { position: m.position, timestamp: m.timestamp };
|
|
77
|
+
if (m.lookAt !== undefined) {
|
|
78
|
+
if (!isVec3(m.lookAt))
|
|
79
|
+
throw new CliError(`${ctx}.lookAt: expected { x, y, z } numbers`);
|
|
80
|
+
marker.lookAt = m.lookAt;
|
|
81
|
+
}
|
|
82
|
+
if (m.rotation !== undefined) {
|
|
83
|
+
if (!isVec3(m.rotation))
|
|
84
|
+
throw new CliError(`${ctx}.rotation: expected { x, y, z } radians`);
|
|
85
|
+
marker.rotation = m.rotation;
|
|
86
|
+
}
|
|
87
|
+
if (marker.lookAt === undefined && marker.rotation === undefined) {
|
|
88
|
+
throw new CliError(`${ctx}: needs a "lookAt" point or a "rotation"`);
|
|
89
|
+
}
|
|
90
|
+
return marker;
|
|
91
|
+
});
|
|
92
|
+
if (markers[0].timestamp !== 0)
|
|
93
|
+
throw new CliError(`${source}: the first marker's timestamp must be 0`);
|
|
94
|
+
for (let i = 1; i < markers.length; i++) {
|
|
95
|
+
if (markers[i].timestamp <= markers[i - 1].timestamp) {
|
|
96
|
+
throw new CliError(`${source}: markers[${i}].timestamp must be greater than markers[${i - 1}].timestamp`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const total = markers[markers.length - 1].timestamp;
|
|
100
|
+
if (total > MAX_FLYTHROUGH_SEC)
|
|
101
|
+
throw new CliError(`${source}: a fly-through may run at most ${MAX_FLYTHROUGH_SEC} s (this one is ${total} s)`);
|
|
102
|
+
const result = { version: 1, markers };
|
|
103
|
+
if (typeof doc.name === 'string')
|
|
104
|
+
result.name = doc.name;
|
|
105
|
+
return result;
|
|
106
|
+
}
|
|
107
|
+
export function loadFlythrough(file) {
|
|
108
|
+
const resolved = path.resolve(file);
|
|
109
|
+
if (!fs.existsSync(resolved))
|
|
110
|
+
throw new CliError(`Fly-through file not found: ${resolved}`);
|
|
111
|
+
let raw;
|
|
112
|
+
try {
|
|
113
|
+
raw = JSON.parse(fs.readFileSync(resolved, 'utf-8'));
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
throw new CliError(`Invalid JSON in ${resolved}: ${err instanceof Error ? err.message : String(err)}`);
|
|
117
|
+
}
|
|
118
|
+
return parseFlythrough(raw, path.basename(resolved));
|
|
119
|
+
}
|
|
120
|
+
// ---------------------------------------------------------------------------
|
|
121
|
+
// Watching the recordings folder
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
/** A recording directory that was not there before the session started, or null. */
|
|
124
|
+
export function findNewRecording(before, dir) {
|
|
125
|
+
if (!fs.existsSync(dir))
|
|
126
|
+
return null;
|
|
127
|
+
const fresh = fs
|
|
128
|
+
.readdirSync(dir, { withFileTypes: true })
|
|
129
|
+
.filter((entry) => entry.isDirectory() && !before.has(entry.name))
|
|
130
|
+
.map((entry) => entry.name)
|
|
131
|
+
.sort();
|
|
132
|
+
return fresh.length > 0 ? path.join(dir, fresh[fresh.length - 1]) : null;
|
|
133
|
+
}
|
|
134
|
+
export function listRecordingNames(dir) {
|
|
135
|
+
if (!fs.existsSync(dir))
|
|
136
|
+
return new Set();
|
|
137
|
+
return new Set(fs.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name));
|
|
138
|
+
}
|
|
139
|
+
export function countFrames(dir) {
|
|
140
|
+
try {
|
|
141
|
+
return fs.readdirSync(dir).filter((name) => name.endsWith('.png')).length;
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return 0;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const POLL_MS = 250;
|
|
148
|
+
const DEFAULT_STOP_WAIT_MS = 120_000;
|
|
149
|
+
const DEFAULT_PLAY_WAIT_MS = 10 * 60_000;
|
|
150
|
+
const FLYTHROUGH_GRACE_SEC = 15;
|
|
151
|
+
function sleep(ms) {
|
|
152
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
153
|
+
}
|
|
154
|
+
async function waitFor(predicate, timeoutMs) {
|
|
155
|
+
const deadline = Date.now() + timeoutMs;
|
|
156
|
+
while (Date.now() < deadline) {
|
|
157
|
+
if (predicate())
|
|
158
|
+
return true;
|
|
159
|
+
await sleep(POLL_MS);
|
|
160
|
+
}
|
|
161
|
+
return predicate();
|
|
162
|
+
}
|
|
163
|
+
function gameFrame(page, gamePort) {
|
|
164
|
+
return page.frames().find((frame) => frame.url().includes(`localhost:${gamePort}`)) ?? null;
|
|
165
|
+
}
|
|
166
|
+
async function postToGame(frame, message) {
|
|
167
|
+
/* eslint-disable no-undef -- browser-side callback: runs inside the game frame, not in node. */
|
|
168
|
+
await frame.evaluate((msg) => {
|
|
169
|
+
window.postMessage(msg, '*');
|
|
170
|
+
}, message);
|
|
171
|
+
/* eslint-enable no-undef */
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Run one recording session end to end. Throws a CliError when the recording never happened or
|
|
175
|
+
* never finished; returns where it landed otherwise.
|
|
176
|
+
*/
|
|
177
|
+
export async function recordGameplay(options) {
|
|
178
|
+
const { root, handle, log } = options;
|
|
179
|
+
const capabilities = recorderCapabilities(root);
|
|
180
|
+
if (!capabilities.sink)
|
|
181
|
+
throw new CliError(upgradeMessage('recording to the dev sink', 'sinkUrl'));
|
|
182
|
+
if (options.flythrough !== undefined && !capabilities.flythrough) {
|
|
183
|
+
throw new CliError(upgradeMessage('camera fly-throughs', 'TRAILER_FLYTHROUGH'));
|
|
184
|
+
}
|
|
185
|
+
const trigger = capabilities.message ? 'message' : 'f9';
|
|
186
|
+
const recordings = recordingsDir(root);
|
|
187
|
+
const before = listRecordingNames(recordings);
|
|
188
|
+
const states = [];
|
|
189
|
+
const consoleLines = [];
|
|
190
|
+
const browser = await launchSystemChrome({
|
|
191
|
+
headless: false,
|
|
192
|
+
args: [...browserGlArgs(process.env), '--window-size=1600,1000', '--autoplay-policy=no-user-gesture-required'],
|
|
193
|
+
command: 'bitmagic trailer record',
|
|
194
|
+
});
|
|
195
|
+
try {
|
|
196
|
+
const context = await browser.newContext({ viewport: null });
|
|
197
|
+
const page = await context.newPage();
|
|
198
|
+
page.on('console', (message) => consoleLines.push(message.text()));
|
|
199
|
+
await page.exposeFunction('__bmTrailerState', (state) => {
|
|
200
|
+
states.push(state);
|
|
201
|
+
});
|
|
202
|
+
// Runs in the shell (and every frame): forward the recorder's reports to us.
|
|
203
|
+
/* eslint-disable no-undef -- browser-side callback: runs inside the page, not in node. */
|
|
204
|
+
await page.addInitScript(() => {
|
|
205
|
+
window.addEventListener('message', (event) => {
|
|
206
|
+
const data = event.data;
|
|
207
|
+
if (data && data.type === 'TRAILER_RECORDING_STATE') {
|
|
208
|
+
const report = window.__bmTrailerState;
|
|
209
|
+
if (report)
|
|
210
|
+
report(data);
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
/* eslint-enable no-undef */
|
|
215
|
+
const shellUrl = `http://localhost:${handle.editorPort}/?rec=${encodeURIComponent(options.resolution)}`;
|
|
216
|
+
log(`Opening ${shellUrl} in Chrome…`);
|
|
217
|
+
await page.goto(shellUrl, { waitUntil: 'domcontentloaded', timeout: 30_000 });
|
|
218
|
+
const frameReady = await waitFor(() => gameFrame(page, handle.gamePort) !== null, 30_000);
|
|
219
|
+
if (!frameReady)
|
|
220
|
+
throw new CliError('The dev view opened but the game frame never appeared. Is `bitmagic dev` still running?');
|
|
221
|
+
const frame = gameFrame(page, handle.gamePort);
|
|
222
|
+
// Gameplay must be running before the recorder starts, or the first seconds are a menu.
|
|
223
|
+
const started = () => consoleLines.some((line) => line.includes(DEFAULT_START_INTERACTION.startedConsoleMarker));
|
|
224
|
+
if (options.flythrough !== undefined) {
|
|
225
|
+
log('Starting the game for the fly-through…');
|
|
226
|
+
const deadline = Date.now() + DEFAULT_START_INTERACTION.buttonTimeoutMs + DEFAULT_START_INTERACTION.loadingGraceMs;
|
|
227
|
+
let picks = 0;
|
|
228
|
+
while (!started() && Date.now() < deadline) {
|
|
229
|
+
const option = frame.locator(DEFAULT_START_INTERACTION.selectionOptionSelector).first();
|
|
230
|
+
const play = frame.locator(DEFAULT_START_INTERACTION.playButtonSelector).first();
|
|
231
|
+
if (picks < DEFAULT_START_INTERACTION.maxSelectionPicks && (await option.isVisible().catch(() => false))) {
|
|
232
|
+
await option.click().catch(() => undefined);
|
|
233
|
+
picks++;
|
|
234
|
+
}
|
|
235
|
+
else if (await play.isVisible().catch(() => false)) {
|
|
236
|
+
await play.click().catch(() => undefined);
|
|
237
|
+
}
|
|
238
|
+
await sleep(POLL_MS);
|
|
239
|
+
}
|
|
240
|
+
if (!started())
|
|
241
|
+
throw new CliError('The game never reached gameplay (no Play button appeared, or it did not start). Check the browser console via `bitmagic verify`.');
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
log('Press Play in the Chrome window and start playing — the recorder starts as soon as the game does.');
|
|
245
|
+
const ok = await waitFor(started, options.playWaitMs ?? DEFAULT_PLAY_WAIT_MS);
|
|
246
|
+
if (!ok)
|
|
247
|
+
throw new CliError('Gave up waiting for the game to start. Run `bitmagic trailer record` again when you are ready to play.');
|
|
248
|
+
// A moment for the fade-in, and for the creator's hands to reach the keys.
|
|
249
|
+
await sleep(1500);
|
|
250
|
+
}
|
|
251
|
+
// Start.
|
|
252
|
+
const recordingStartedAt = Date.now();
|
|
253
|
+
if (options.flythrough !== undefined) {
|
|
254
|
+
await postToGame(frame, { type: 'TRAILER_FLYTHROUGH', path: options.flythrough });
|
|
255
|
+
}
|
|
256
|
+
else if (trigger === 'message') {
|
|
257
|
+
await postToGame(frame, { type: 'TRAILER_RECORDING', action: 'start', mode: 'frames' });
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
await frame.locator('body').press('F9');
|
|
261
|
+
}
|
|
262
|
+
const recordingDirAppeared = () => states.find((s) => s.state === 'started' && typeof s.name === 'string')?.name !== undefined
|
|
263
|
+
? path.join(recordings, states.find((s) => s.state === 'started').name)
|
|
264
|
+
: findNewRecording(before, recordings);
|
|
265
|
+
const failure = () => states.find((s) => s.state === 'failed');
|
|
266
|
+
const appeared = await waitFor(() => recordingDirAppeared() !== null || failure() !== undefined, 20_000);
|
|
267
|
+
const failed = failure();
|
|
268
|
+
if (failed !== undefined)
|
|
269
|
+
throw new CliError(`The recorder refused to start: ${failed.error ?? 'unknown error'}`);
|
|
270
|
+
if (!appeared) {
|
|
271
|
+
throw new CliError('The recorder did not start (no recording folder appeared under .bitmagic/trailer/recordings/). ' +
|
|
272
|
+
'Click into the game once and try again, or press F9 in the game yourself.');
|
|
273
|
+
}
|
|
274
|
+
const dir = recordingDirAppeared();
|
|
275
|
+
const name = path.basename(dir);
|
|
276
|
+
log(`Recording ${name} at ${options.resolution}…`);
|
|
277
|
+
// Run: until the timer, the creator's F9, or the fly-through's end.
|
|
278
|
+
const stopped = () => states.some((s) => s.state === 'stopped' || s.state === 'saved') ||
|
|
279
|
+
consoleLines.some((line) => line.includes('📹 Recording stopped'));
|
|
280
|
+
const plannedSec = options.flythrough !== undefined
|
|
281
|
+
? options.flythrough.markers[options.flythrough.markers.length - 1].timestamp + FLYTHROUGH_GRACE_SEC
|
|
282
|
+
: options.seconds;
|
|
283
|
+
let lastReport = 0;
|
|
284
|
+
while (!stopped()) {
|
|
285
|
+
const elapsed = (Date.now() - recordingStartedAt) / 1000;
|
|
286
|
+
if (plannedSec !== undefined && elapsed >= plannedSec) {
|
|
287
|
+
if (trigger === 'message')
|
|
288
|
+
await postToGame(frame, { type: 'TRAILER_RECORDING', action: 'stop' });
|
|
289
|
+
else
|
|
290
|
+
await frame.locator('body').press('F9');
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
if (elapsed - lastReport >= 5) {
|
|
294
|
+
lastReport = elapsed;
|
|
295
|
+
log(` ${elapsed.toFixed(0)}s — ${countFrames(dir)} frames on disk${plannedSec !== undefined ? ` (stops at ${plannedSec}s)` : ' (press F9 in the game to stop)'}`);
|
|
296
|
+
}
|
|
297
|
+
await sleep(POLL_MS);
|
|
298
|
+
}
|
|
299
|
+
const durationSec = (Date.now() - recordingStartedAt) / 1000;
|
|
300
|
+
// Wait for the frames to flush and the timeline to land.
|
|
301
|
+
const timelinePath = path.join(dir, TIMELINE_FILE);
|
|
302
|
+
log('Stopping — waiting for the frames to flush and timeline.json to arrive…');
|
|
303
|
+
const saved = await waitFor(() => fs.existsSync(timelinePath) || failure() !== undefined, options.stopWaitMs ?? DEFAULT_STOP_WAIT_MS);
|
|
304
|
+
const late = failure();
|
|
305
|
+
if (late !== undefined && !fs.existsSync(timelinePath)) {
|
|
306
|
+
throw new CliError(`The recording stopped but its timeline could not be saved: ${late.error ?? 'unknown error'}. Frames are in ${dir}.`);
|
|
307
|
+
}
|
|
308
|
+
if (!saved) {
|
|
309
|
+
throw new CliError(`Recording stopped but timeline.json never arrived (waited ${Math.round((options.stopWaitMs ?? DEFAULT_STOP_WAIT_MS) / 1000)} s). ` +
|
|
310
|
+
`Frames are in ${dir}; check the game's browser console for save-recording-timeline errors.`);
|
|
311
|
+
}
|
|
312
|
+
const manifest = JSON.parse(fs.readFileSync(timelinePath, 'utf-8'));
|
|
313
|
+
const [w, h] = options.resolution.split('x').map(Number);
|
|
314
|
+
return {
|
|
315
|
+
name,
|
|
316
|
+
dir,
|
|
317
|
+
frameCount: manifest.recording?.frameCount ?? countFrames(dir),
|
|
318
|
+
width: manifest.recording?.width ?? w,
|
|
319
|
+
height: manifest.recording?.height ?? h,
|
|
320
|
+
durationSec,
|
|
321
|
+
trigger,
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
finally {
|
|
325
|
+
await browser.close().catch(() => undefined);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
//# sourceMappingURL=record-session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"record-session.js","sourceRoot":"","sources":["../../src/trailer/record-session.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export declare const DEFAULT_MUSIC_GAIN_DB = -6;
|
|
2
|
+
export declare const DEFAULT_MUSIC_START_SEC = 0;
|
|
3
|
+
export declare const DEFAULT_MUSIC_FADE_SEC = 2;
|
|
4
|
+
/** Watermark defaults, all as fractions of the video so they hold at any resolution. */
|
|
5
|
+
export declare const DEFAULT_LOGO_OPACITY = 0.7;
|
|
6
|
+
export declare const DEFAULT_LOGO_HEIGHT_FRAC = 0.055;
|
|
7
|
+
export declare const DEFAULT_LOGO_MARGIN_FRAC = 0.025;
|
|
8
|
+
export type LogoCorner = 'br' | 'bl' | 'tr' | 'tl';
|
|
9
|
+
export interface RenderOptions {
|
|
10
|
+
/** Resolved relative to the recording dir. Default "edl.json". */
|
|
11
|
+
edlPath?: string;
|
|
12
|
+
/** Resolved relative to the recording dir. Default "<recordingName>-trailer.mp4". */
|
|
13
|
+
outPath?: string;
|
|
14
|
+
tailPadFrames?: number;
|
|
15
|
+
/** Draw the recorded HUD over every clip that doesn't opt out via "ui": false. */
|
|
16
|
+
ui?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Where the HUD replay harness lives — `/hud-replay.html` on the running `bitmagic dev` sidecar.
|
|
19
|
+
* Without it a `ui` render warns and draws no overlays rather than guessing a port.
|
|
20
|
+
*/
|
|
21
|
+
harnessUrl?: string;
|
|
22
|
+
/** Music bed under the reconstructed SFX. Resolved relative to the cwd. */
|
|
23
|
+
musicPath?: string;
|
|
24
|
+
/** dB applied to the music. Default -6. */
|
|
25
|
+
musicGainDb?: number;
|
|
26
|
+
/** Where in the track to come in. Default 0. */
|
|
27
|
+
musicStartSec?: number;
|
|
28
|
+
/** Fade-out over the last N seconds of the video. Default 2. */
|
|
29
|
+
musicFadeSec?: number;
|
|
30
|
+
/** dB applied to the reconstructed-SFX bus. Default 0. */
|
|
31
|
+
sfxGainDb?: number;
|
|
32
|
+
/** Watermark image (PNG with alpha). Resolved relative to the cwd. */
|
|
33
|
+
logoPath?: string;
|
|
34
|
+
/** 0..1. Default 0.7. */
|
|
35
|
+
logoOpacity?: number;
|
|
36
|
+
/** Logo height as a fraction of video height. Default 0.055. */
|
|
37
|
+
logoHeightFrac?: number;
|
|
38
|
+
/** Margin from the edges as a fraction of video width. Default 0.025. */
|
|
39
|
+
logoMarginFrac?: number;
|
|
40
|
+
/** Which corner. Default bottom-right. */
|
|
41
|
+
logoCorner?: LogoCorner;
|
|
42
|
+
/** TTF used for burned-in titles. Defaults to the bundled NotoSans-Bold. */
|
|
43
|
+
titleFontPath?: string;
|
|
44
|
+
/**
|
|
45
|
+
* Replace the output file instead of writing a new revision. Off by default:
|
|
46
|
+
* re-rendering to the same name loses the previous cut, and players like
|
|
47
|
+
* QuickTime keep showing the stale file until they are closed and reopened.
|
|
48
|
+
*/
|
|
49
|
+
overwrite?: boolean;
|
|
50
|
+
}
|
|
51
|
+
export interface RenderResult {
|
|
52
|
+
/** The file actually written — the requested name, or its next free `-rN` revision. */
|
|
53
|
+
outPath: string;
|
|
54
|
+
durationSec: number;
|
|
55
|
+
clipCount: number;
|
|
56
|
+
}
|
|
57
|
+
export declare function runRender(recordingDir: string, options?: RenderOptions): Promise<RenderResult>;
|