@bitmagic/cli 0.1.61-dev.0 → 0.1.61-dev.2
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 +148 -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 +331 -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,396 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `bitmagic trailer` — from a gameplay recording to a published trailer, in the creator's project.
|
|
3
|
+
*
|
|
4
|
+
* The one-shot is `make`: analyze the newest recording, optionally lay a music bed under it, write
|
|
5
|
+
* a first cut to `trailer/shots.json`, render an MP4, and (with `--publish`) put it on the CDN and
|
|
6
|
+
* the game page. Every step is also its own leaf (`analyze`, `beats`, `cut`, `render`, `merge`),
|
|
7
|
+
* because the tuning loop an agent runs is "edit the shots, cut, render, look" and each of those
|
|
8
|
+
* should be one command with one job. `record` (record-session.ts) is the capture side.
|
|
9
|
+
*
|
|
10
|
+
* All of it is the compositor that used to live in game-play-agent, so a recording from the web
|
|
11
|
+
* lane (`~/Downloads/gameplay_recording_*`) cuts exactly like one from `bitmagic dev`: pass it as
|
|
12
|
+
* `--recording` / the positional dir.
|
|
13
|
+
*/
|
|
14
|
+
import * as fs from 'fs';
|
|
15
|
+
import * as path from 'path';
|
|
16
|
+
import { defineCommand } from 'citty';
|
|
17
|
+
import { CliError } from '../errors.js';
|
|
18
|
+
import { createOutput } from '../output.js';
|
|
19
|
+
import { loadProjectContext } from '../project/context.js';
|
|
20
|
+
import { readLiveDevHandle } from '../project/dev-handle.js';
|
|
21
|
+
import { parseNumberFlag } from './number-flag.js';
|
|
22
|
+
import { runAnalyze } from '../trailer/analyze.js';
|
|
23
|
+
import { cutShots, describeCut, loadShots } from '../trailer/beat-cut.js';
|
|
24
|
+
import { ensureFfmpeg } from '../trailer/ffmpeg.js';
|
|
25
|
+
import { bindTrailerLog } from '../trailer/io.js';
|
|
26
|
+
import { makeTrailer, DEFAULT_TRAILER_SECONDS } from '../trailer/make.js';
|
|
27
|
+
import { runMerge } from '../trailer/merge.js';
|
|
28
|
+
import { runBeats } from '../trailer/music-beats.js';
|
|
29
|
+
import { isRecordingDir, resolveRecording, workDir } from '../trailer/paths.js';
|
|
30
|
+
import { runRender } from '../trailer/render.js';
|
|
31
|
+
import { loadTimeline } from '../trailer/timeline-types.js';
|
|
32
|
+
import { runTrailerPublish } from '../trailer/publish.js';
|
|
33
|
+
import { loadFlythrough, NO_DEV_MESSAGE, recordGameplay } from '../trailer/record-session.js';
|
|
34
|
+
import { RECORDING_RESOLUTIONS } from '../editor/recording-panel.js';
|
|
35
|
+
import { generateMusicFile } from '../generate/music.js';
|
|
36
|
+
import { resolveEnvironmentForCommand, resolveAuth0ClientId } from '../config/environments.js';
|
|
37
|
+
import { getAccessToken } from '../auth/session.js';
|
|
38
|
+
import { trailerPublishCommand } from './trailer-publish.js';
|
|
39
|
+
const JSON_FLAG = {
|
|
40
|
+
type: 'boolean',
|
|
41
|
+
description: 'Print the result as JSON on stdout; all progress goes to stderr.',
|
|
42
|
+
};
|
|
43
|
+
const RECORDING_FLAG = {
|
|
44
|
+
type: 'string',
|
|
45
|
+
description: 'A recording directory (holds timeline.json). Default: the newest under .bitmagic/trailer/recordings/.',
|
|
46
|
+
};
|
|
47
|
+
/** Every leaf opens the same way: find the project, bind the compositor's narration to --json. */
|
|
48
|
+
function open(json) {
|
|
49
|
+
const { root, metadata } = loadProjectContext();
|
|
50
|
+
const output = createOutput(json === true);
|
|
51
|
+
bindTrailerLog({ info: output.info, warn: output.info });
|
|
52
|
+
return { root, gameId: metadata.gameId, output };
|
|
53
|
+
}
|
|
54
|
+
export function harnessUrlFor(root) {
|
|
55
|
+
const handle = readLiveDevHandle(root);
|
|
56
|
+
return handle === null ? undefined : `http://localhost:${handle.editorPort}/hud-replay.html`;
|
|
57
|
+
}
|
|
58
|
+
export const NO_DEV_FOR_UI = '--ui redraws the HUD through the running `bitmagic dev` (its /hud-replay.html), and none is running. ' +
|
|
59
|
+
'Start `bitmagic dev` or drop --ui.';
|
|
60
|
+
function requireHarness(root, ui) {
|
|
61
|
+
if (!ui)
|
|
62
|
+
return undefined;
|
|
63
|
+
const url = harnessUrlFor(root);
|
|
64
|
+
if (url === undefined)
|
|
65
|
+
throw new CliError(NO_DEV_FOR_UI);
|
|
66
|
+
return url;
|
|
67
|
+
}
|
|
68
|
+
const recordCommand = defineCommand({
|
|
69
|
+
meta: {
|
|
70
|
+
name: 'record',
|
|
71
|
+
description: 'Record gameplay for a trailer: opens Chrome on the running `bitmagic dev`, starts the recorder once you play, stops on time or on your F9.',
|
|
72
|
+
},
|
|
73
|
+
args: {
|
|
74
|
+
seconds: { type: 'string', description: 'Stop after this many seconds. Default: record until you press F9 in the game.' },
|
|
75
|
+
resolution: { type: 'string', description: `Frame size, WxH. One of ${RECORDING_RESOLUTIONS.join(', ')}. Default 1920x1080.` },
|
|
76
|
+
flythrough: { type: 'string', description: 'A camera path JSON (trailer/flythrough.json): the camera flies it with nobody playing and the recording stops at its end.' },
|
|
77
|
+
json: JSON_FLAG,
|
|
78
|
+
},
|
|
79
|
+
async run({ args }) {
|
|
80
|
+
const { root, output } = open(args.json);
|
|
81
|
+
const handle = readLiveDevHandle(root);
|
|
82
|
+
if (handle === null)
|
|
83
|
+
throw new CliError(NO_DEV_MESSAGE);
|
|
84
|
+
const resolution = args.resolution ?? '1920x1080';
|
|
85
|
+
if (!RECORDING_RESOLUTIONS.includes(resolution)) {
|
|
86
|
+
throw new CliError(`--resolution must be one of ${RECORDING_RESOLUTIONS.join(', ')} (got ${resolution}).`);
|
|
87
|
+
}
|
|
88
|
+
const seconds = parseNumberFlag('seconds', args.seconds, { min: 3, max: 600 });
|
|
89
|
+
const flythrough = args.flythrough !== undefined ? loadFlythrough(args.flythrough) : undefined;
|
|
90
|
+
const result = await recordGameplay({
|
|
91
|
+
root,
|
|
92
|
+
handle,
|
|
93
|
+
resolution,
|
|
94
|
+
log: output.info,
|
|
95
|
+
...(seconds !== undefined ? { seconds } : {}),
|
|
96
|
+
...(flythrough !== undefined ? { flythrough } : {}),
|
|
97
|
+
});
|
|
98
|
+
output.info('');
|
|
99
|
+
output.info(`Recorded ${result.frameCount} frames (${result.durationSec.toFixed(1)}s, ${result.width}x${result.height}) → ${path.relative(root, result.dir)}/`);
|
|
100
|
+
output.info('Next: bitmagic trailer make');
|
|
101
|
+
output.result({ ok: true, kind: 'trailer-recording', ...result });
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
const analyzeCommand = defineCommand({
|
|
105
|
+
meta: {
|
|
106
|
+
name: 'analyze',
|
|
107
|
+
description: 'Score a recording into highlight windows with contact sheets (analysis.json + sheets/).',
|
|
108
|
+
},
|
|
109
|
+
args: {
|
|
110
|
+
recording: { type: 'positional', description: 'Recording directory. Default: the newest one.', required: false },
|
|
111
|
+
'window-sec': { type: 'string', description: 'Highlight window length in seconds. Default 3.' },
|
|
112
|
+
top: { type: 'string', description: 'How many windows to keep. Default 12.' },
|
|
113
|
+
out: { type: 'string', description: 'Where to write analysis.json and sheets/. Default: the recording\'s work dir.' },
|
|
114
|
+
json: JSON_FLAG,
|
|
115
|
+
},
|
|
116
|
+
async run({ args }) {
|
|
117
|
+
const { root, output } = open(args.json);
|
|
118
|
+
ensureFfmpeg(['ffmpeg']);
|
|
119
|
+
const recording = resolveRecording(root, args.recording);
|
|
120
|
+
const windowSec = parseNumberFlag('window-sec', args['window-sec'], { min: 0.5, max: 30 }) ?? 3;
|
|
121
|
+
const top = parseNumberFlag('top', args.top, { min: 1, max: 100, integer: true }) ?? 12;
|
|
122
|
+
const outDir = args.out !== undefined ? path.resolve(args.out) : workDir(root, path.basename(recording));
|
|
123
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
124
|
+
const analysis = await runAnalyze(recording, windowSec, top, outDir);
|
|
125
|
+
output.result({
|
|
126
|
+
ok: true,
|
|
127
|
+
kind: 'trailer-analysis',
|
|
128
|
+
recording,
|
|
129
|
+
analysis: path.join(outDir, 'analysis.json'),
|
|
130
|
+
sheets: path.join(outDir, 'sheets'),
|
|
131
|
+
windows: analysis.windows.length,
|
|
132
|
+
});
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
const beatsCommand = defineCommand({
|
|
136
|
+
meta: { name: 'beats', description: 'Detect the beat grid of a music track (beats.json), to cut on.' },
|
|
137
|
+
args: {
|
|
138
|
+
music: { type: 'positional', description: 'Audio file (mp3, wav, opus, …).', required: true },
|
|
139
|
+
bpm: { type: 'string', description: 'Force the tempo instead of detecting it.' },
|
|
140
|
+
click: { type: 'string', description: 'Also write the music with a click on every beat (wav), to check the grid by ear.' },
|
|
141
|
+
out: { type: 'string', description: 'Where to write beats.json. Default: beside the music file.' },
|
|
142
|
+
json: JSON_FLAG,
|
|
143
|
+
},
|
|
144
|
+
async run({ args }) {
|
|
145
|
+
const { output } = open(args.json);
|
|
146
|
+
ensureFfmpeg(['ffmpeg', 'ffprobe']);
|
|
147
|
+
const music = path.resolve(args.music);
|
|
148
|
+
const jsonPath = args.out !== undefined ? path.resolve(args.out) : path.join(path.dirname(music), 'beats.json');
|
|
149
|
+
const bpm = parseNumberFlag('bpm', args.bpm, { min: 30, max: 300 });
|
|
150
|
+
await runBeats(music, {
|
|
151
|
+
jsonPath,
|
|
152
|
+
...(bpm !== undefined ? { bpm } : {}),
|
|
153
|
+
...(args.click !== undefined ? { clickPath: path.resolve(args.click) } : {}),
|
|
154
|
+
});
|
|
155
|
+
output.result({ ok: true, kind: 'trailer-beats', music, beats: jsonPath });
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
function readBeats(filePath) {
|
|
159
|
+
if (!fs.existsSync(filePath))
|
|
160
|
+
throw new CliError(`Beats file not found: ${filePath} — run \`bitmagic trailer beats <music>\` first.`);
|
|
161
|
+
const parsed = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
162
|
+
if (typeof parsed !== 'object' || parsed === null || !Array.isArray(parsed.beatTimes)) {
|
|
163
|
+
throw new CliError(`${filePath} is not a beats.json (no beatTimes) — run \`bitmagic trailer beats <music>\` to write one.`);
|
|
164
|
+
}
|
|
165
|
+
return parsed;
|
|
166
|
+
}
|
|
167
|
+
const cutCommand = defineCommand({
|
|
168
|
+
meta: { name: 'cut', description: 'Turn a shots file into a frame-exact edl.json, snapped to a beat grid when given one.' },
|
|
169
|
+
args: {
|
|
170
|
+
shots: { type: 'positional', description: 'Shots file. Default: trailer/shots.json.', required: false },
|
|
171
|
+
beats: { type: 'string', description: 'beats.json from `bitmagic trailer beats` — cuts land on this grid.' },
|
|
172
|
+
recording: RECORDING_FLAG,
|
|
173
|
+
out: { type: 'string', description: 'Where to write edl.json. Default: the recording\'s work dir.' },
|
|
174
|
+
json: JSON_FLAG,
|
|
175
|
+
},
|
|
176
|
+
async run({ args }) {
|
|
177
|
+
const { root, output } = open(args.json);
|
|
178
|
+
const recording = resolveRecording(root, args.recording);
|
|
179
|
+
const timeline = loadTimeline(recording);
|
|
180
|
+
const shotsFile = args.shots !== undefined ? path.resolve(args.shots) : path.join(root, 'trailer', 'shots.json');
|
|
181
|
+
const shots = loadShots(shotsFile);
|
|
182
|
+
const beats = args.beats !== undefined ? readBeats(path.resolve(args.beats)) : null;
|
|
183
|
+
const result = cutShots(shots, beats, timeline.recording.fps, { frameCount: timeline.recording.frameCount });
|
|
184
|
+
const edlPath = args.out !== undefined ? path.resolve(args.out) : path.join(workDir(root, path.basename(recording)), 'edl.json');
|
|
185
|
+
fs.mkdirSync(path.dirname(edlPath), { recursive: true });
|
|
186
|
+
fs.writeFileSync(edlPath, `${JSON.stringify(result.edl, null, 2)}\n`);
|
|
187
|
+
output.info(describeCut(result, beats));
|
|
188
|
+
if (result.truncatedAt !== undefined)
|
|
189
|
+
output.info(`ran out of beats at "${result.truncatedAt}" — the shots after it were dropped`);
|
|
190
|
+
output.info(`wrote ${edlPath}`);
|
|
191
|
+
output.result({
|
|
192
|
+
ok: true,
|
|
193
|
+
kind: 'trailer-cut',
|
|
194
|
+
edl: edlPath,
|
|
195
|
+
clips: result.edl.clips.length,
|
|
196
|
+
totalSec: result.totalSec,
|
|
197
|
+
beatsUsed: result.beatsUsed,
|
|
198
|
+
worstCutErrorMs: result.worstCutErrorMs,
|
|
199
|
+
...(result.truncatedAt !== undefined ? { truncatedAt: result.truncatedAt } : {}),
|
|
200
|
+
});
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
const LOGO_CORNERS = ['br', 'bl', 'tr', 'tl'];
|
|
204
|
+
const renderCommand = defineCommand({
|
|
205
|
+
meta: { name: 'render', description: 'Cut a recording into a trailer MP4 from an edl.json, with reconstructed sound.' },
|
|
206
|
+
args: {
|
|
207
|
+
recording: { type: 'positional', description: 'Recording directory. Default: the newest one.', required: false },
|
|
208
|
+
edl: { type: 'string', description: 'The EDL to render. Default: edl.json in the recording\'s work dir.' },
|
|
209
|
+
out: { type: 'string', description: 'Output MP4. Default: <recording>-trailer.mp4 in the work dir. Existing names get -rN.' },
|
|
210
|
+
ui: { type: 'boolean', description: 'Redraw the recorded HUD over the footage (needs `bitmagic dev` running).' },
|
|
211
|
+
music: { type: 'string', description: 'Music bed file.' },
|
|
212
|
+
'music-gain': { type: 'string', description: 'Music level in dB. Default -6.' },
|
|
213
|
+
'music-start': { type: 'string', description: 'Seconds into the track to come in. Default 0.' },
|
|
214
|
+
'music-fade': { type: 'string', description: 'Fade-out over the last N seconds. Default 2.' },
|
|
215
|
+
'sfx-gain': { type: 'string', description: 'Reconstructed-SFX level in dB. Default 0.' },
|
|
216
|
+
logo: { type: 'string', description: 'Watermark PNG (with alpha).' },
|
|
217
|
+
'logo-opacity': { type: 'string', description: '0..1. Default 0.7.' },
|
|
218
|
+
'logo-height': { type: 'string', description: 'Logo height as a fraction of video height. Default 0.055.' },
|
|
219
|
+
'logo-margin': { type: 'string', description: 'Edge margin as a fraction of video width. Default 0.025.' },
|
|
220
|
+
'logo-corner': { type: 'string', description: 'br | bl | tr | tl. Default br.' },
|
|
221
|
+
'title-font': { type: 'string', description: 'TTF for burned-in titles. Default: the bundled NotoSans-Bold.' },
|
|
222
|
+
overwrite: { type: 'boolean', description: 'Replace the output file instead of writing a new -rN revision.' },
|
|
223
|
+
json: JSON_FLAG,
|
|
224
|
+
},
|
|
225
|
+
async run({ args }) {
|
|
226
|
+
const { root, output } = open(args.json);
|
|
227
|
+
ensureFfmpeg(['ffmpeg', 'ffprobe']);
|
|
228
|
+
const recording = resolveRecording(root, args.recording);
|
|
229
|
+
const work = workDir(root, path.basename(recording));
|
|
230
|
+
fs.mkdirSync(work, { recursive: true });
|
|
231
|
+
const options = {
|
|
232
|
+
edlPath: args.edl !== undefined ? path.resolve(args.edl) : path.join(work, 'edl.json'),
|
|
233
|
+
outPath: args.out !== undefined ? path.resolve(args.out) : path.join(work, `${path.basename(recording)}-trailer.mp4`),
|
|
234
|
+
};
|
|
235
|
+
if (!fs.existsSync(options.edlPath)) {
|
|
236
|
+
throw new CliError(`No EDL at ${options.edlPath} — run \`bitmagic trailer cut\` (or \`make\`) first, or pass --edl.`);
|
|
237
|
+
}
|
|
238
|
+
if (args.ui) {
|
|
239
|
+
options.ui = true;
|
|
240
|
+
options.harnessUrl = requireHarness(root, true);
|
|
241
|
+
}
|
|
242
|
+
const musicGain = parseNumberFlag('music-gain', args['music-gain']);
|
|
243
|
+
const musicStart = parseNumberFlag('music-start', args['music-start'], { min: 0 });
|
|
244
|
+
const musicFade = parseNumberFlag('music-fade', args['music-fade'], { min: 0 });
|
|
245
|
+
if (args.music === undefined && (musicGain ?? musicStart ?? musicFade) !== undefined) {
|
|
246
|
+
throw new CliError('--music-gain/--music-start/--music-fade need --music.');
|
|
247
|
+
}
|
|
248
|
+
if (args.music !== undefined)
|
|
249
|
+
options.musicPath = path.resolve(args.music);
|
|
250
|
+
if (musicGain !== undefined)
|
|
251
|
+
options.musicGainDb = musicGain;
|
|
252
|
+
if (musicStart !== undefined)
|
|
253
|
+
options.musicStartSec = musicStart;
|
|
254
|
+
if (musicFade !== undefined)
|
|
255
|
+
options.musicFadeSec = musicFade;
|
|
256
|
+
const sfxGain = parseNumberFlag('sfx-gain', args['sfx-gain']);
|
|
257
|
+
if (sfxGain !== undefined)
|
|
258
|
+
options.sfxGainDb = sfxGain;
|
|
259
|
+
const logoOpacity = parseNumberFlag('logo-opacity', args['logo-opacity']);
|
|
260
|
+
const logoHeight = parseNumberFlag('logo-height', args['logo-height']);
|
|
261
|
+
const logoMargin = parseNumberFlag('logo-margin', args['logo-margin']);
|
|
262
|
+
if (args.logo === undefined && (logoOpacity ?? logoHeight ?? logoMargin ?? args['logo-corner']) !== undefined) {
|
|
263
|
+
throw new CliError('--logo-opacity/--logo-height/--logo-margin/--logo-corner need --logo.');
|
|
264
|
+
}
|
|
265
|
+
if (args.logo !== undefined)
|
|
266
|
+
options.logoPath = path.resolve(args.logo);
|
|
267
|
+
if (logoOpacity !== undefined)
|
|
268
|
+
options.logoOpacity = logoOpacity;
|
|
269
|
+
if (logoHeight !== undefined)
|
|
270
|
+
options.logoHeightFrac = logoHeight;
|
|
271
|
+
if (logoMargin !== undefined)
|
|
272
|
+
options.logoMarginFrac = logoMargin;
|
|
273
|
+
if (args['logo-corner'] !== undefined) {
|
|
274
|
+
const corner = args['logo-corner'];
|
|
275
|
+
if (!LOGO_CORNERS.includes(corner))
|
|
276
|
+
throw new CliError(`--logo-corner must be br, bl, tr or tl (got ${corner}).`);
|
|
277
|
+
options.logoCorner = corner;
|
|
278
|
+
}
|
|
279
|
+
if (args['title-font'] !== undefined)
|
|
280
|
+
options.titleFontPath = path.resolve(args['title-font']);
|
|
281
|
+
if (args.overwrite)
|
|
282
|
+
options.overwrite = true;
|
|
283
|
+
const result = await runRender(recording, options);
|
|
284
|
+
output.result({ ok: true, kind: 'trailer-render', recording, mp4: result.outPath, durationSec: result.durationSec, clips: result.clipCount });
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
const mergeCommand = defineCommand({
|
|
288
|
+
meta: { name: 'merge', description: 'Join recordings from separate sittings into one cuttable recording.' },
|
|
289
|
+
args: {
|
|
290
|
+
out: { type: 'string', description: 'Directory to write the merged recording into.' },
|
|
291
|
+
'allow-mixed-postfx': { type: 'boolean', description: 'Allow footage recorded before and after the post-processing fix in one session.' },
|
|
292
|
+
json: JSON_FLAG,
|
|
293
|
+
},
|
|
294
|
+
async run({ args }) {
|
|
295
|
+
const { output } = open(args.json);
|
|
296
|
+
const dirs = args._.map((d) => path.resolve(String(d)));
|
|
297
|
+
if (dirs.length < 2)
|
|
298
|
+
throw new CliError('merge expects two or more recording directories: bitmagic trailer merge <dirA> <dirB> [...]');
|
|
299
|
+
for (const dir of dirs) {
|
|
300
|
+
if (!isRecordingDir(dir))
|
|
301
|
+
throw new CliError(`${dir} has no timeline.json — not a finished recording.`);
|
|
302
|
+
}
|
|
303
|
+
runMerge(dirs, {
|
|
304
|
+
...(args.out !== undefined ? { outDir: path.resolve(args.out) } : {}),
|
|
305
|
+
...(args['allow-mixed-postfx'] ? { allowMixedPostFx: true } : {}),
|
|
306
|
+
});
|
|
307
|
+
output.result({ ok: true, kind: 'trailer-merge', inputs: dirs, out: args.out !== undefined ? path.resolve(args.out) : undefined });
|
|
308
|
+
},
|
|
309
|
+
});
|
|
310
|
+
const makeCommand = defineCommand({
|
|
311
|
+
meta: {
|
|
312
|
+
name: 'make',
|
|
313
|
+
description: 'Analyze the recording, write a first cut to trailer/shots.json (or cut the one you edited), and render the trailer.',
|
|
314
|
+
},
|
|
315
|
+
args: {
|
|
316
|
+
recording: RECORDING_FLAG,
|
|
317
|
+
music: { type: 'string', description: 'Music bed file. Cuts snap to its beats.' },
|
|
318
|
+
'music-prompt': { type: 'string', description: 'Generate the music bed from this prompt (`bitmagic generate music`, spends sparks).' },
|
|
319
|
+
seconds: { type: 'string', description: `Target length including the end card. Default ${DEFAULT_TRAILER_SECONDS}.` },
|
|
320
|
+
title: { type: 'string', description: 'End-card title. Default: the game\'s name.' },
|
|
321
|
+
sub: { type: 'string', description: 'End-card second line, e.g. "by Bitmagic".' },
|
|
322
|
+
ui: { type: 'boolean', description: 'Redraw the recorded HUD over the footage (needs `bitmagic dev` running).' },
|
|
323
|
+
publish: { type: 'boolean', description: 'Upload the render to the CDN and attach it to the game page (`bitmagic trailer publish`).' },
|
|
324
|
+
'reset-shots': { type: 'boolean', description: 'Throw away trailer/shots.json and write a fresh first cut.' },
|
|
325
|
+
json: JSON_FLAG,
|
|
326
|
+
},
|
|
327
|
+
async run({ args }) {
|
|
328
|
+
const { root, gameId, output } = open(args.json);
|
|
329
|
+
ensureFfmpeg(['ffmpeg', 'ffprobe']);
|
|
330
|
+
if (args.music !== undefined && args['music-prompt'] !== undefined) {
|
|
331
|
+
throw new CliError('Pass either --music <file> or --music-prompt "...", not both.');
|
|
332
|
+
}
|
|
333
|
+
const recording = resolveRecording(root, args.recording);
|
|
334
|
+
const seconds = parseNumberFlag('seconds', args.seconds, { min: 8, max: 120 });
|
|
335
|
+
const result = await makeTrailer({
|
|
336
|
+
root,
|
|
337
|
+
recordingDir: recording,
|
|
338
|
+
...(args.music !== undefined ? { music: args.music } : {}),
|
|
339
|
+
...(args['music-prompt'] !== undefined ? { musicPrompt: args['music-prompt'] } : {}),
|
|
340
|
+
...(seconds !== undefined ? { seconds } : {}),
|
|
341
|
+
...(args.title !== undefined ? { title: args.title } : {}),
|
|
342
|
+
...(args.sub !== undefined ? { sub: args.sub } : {}),
|
|
343
|
+
...(args.ui ? { ui: true, harnessUrl: requireHarness(root, true) } : {}),
|
|
344
|
+
...(args['reset-shots'] ? { resetShots: true } : {}),
|
|
345
|
+
...trailerLanes(root, gameId, args.publish === true, output),
|
|
346
|
+
});
|
|
347
|
+
output.info('');
|
|
348
|
+
output.info(`Trailer: ${result.mp4} (${result.durationSec.toFixed(1)}s, ${result.clipCount} clips)`);
|
|
349
|
+
output.info(`Shots: ${path.relative(root, result.shotsPath)}${result.shotsWritten ? ' (first cut — edit it and run make again to tune)' : ''}`);
|
|
350
|
+
output.info(`Sheets: ${path.join(path.dirname(result.analysisPath), 'sheets')}`);
|
|
351
|
+
if (result.published !== undefined)
|
|
352
|
+
output.info(`Published: ${result.published.publicUrl}`);
|
|
353
|
+
output.result({ ok: true, kind: 'trailer', ...result });
|
|
354
|
+
},
|
|
355
|
+
});
|
|
356
|
+
/**
|
|
357
|
+
* The two lanes `make` reaches into other commands for. Kept in one place so the seam between
|
|
358
|
+
* "compose locally" and "spend sparks / talk to api-server" is visible.
|
|
359
|
+
*/
|
|
360
|
+
function trailerLanes(root, gameId, publish, output) {
|
|
361
|
+
const lanes = {};
|
|
362
|
+
lanes.generateMusic = async (prompt, seconds, outPath) => {
|
|
363
|
+
const environment = resolveEnvironmentForCommand({ cwd: root });
|
|
364
|
+
const deps = { fetch: globalThis.fetch, sleep: (ms) => new Promise((r) => setTimeout(r, ms)) };
|
|
365
|
+
const token = await getAccessToken(environment, resolveAuth0ClientId(environment), deps);
|
|
366
|
+
await generateMusicFile(environment, token, { gameId, prompt, durationSeconds: Math.min(180, Math.max(10, Math.round(seconds))), instrumental: true }, outPath, { fetch: globalThis.fetch, log: output.info });
|
|
367
|
+
};
|
|
368
|
+
if (publish) {
|
|
369
|
+
lanes.publish = async (mp4) => {
|
|
370
|
+
const result = await runTrailerPublish({ root, file: mp4, attach: true, output });
|
|
371
|
+
return {
|
|
372
|
+
publicUrl: result.publicUrl,
|
|
373
|
+
attached: result.attached,
|
|
374
|
+
...(result.pageUrl !== undefined ? { pageUrl: result.pageUrl } : {}),
|
|
375
|
+
};
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
return lanes;
|
|
379
|
+
}
|
|
380
|
+
export const trailerCommand = defineCommand({
|
|
381
|
+
meta: {
|
|
382
|
+
name: 'trailer',
|
|
383
|
+
description: 'Make a trailer for this game: record gameplay, cut the highlights to music, render, publish.',
|
|
384
|
+
},
|
|
385
|
+
subCommands: {
|
|
386
|
+
record: recordCommand,
|
|
387
|
+
analyze: analyzeCommand,
|
|
388
|
+
beats: beatsCommand,
|
|
389
|
+
cut: cutCommand,
|
|
390
|
+
render: renderCommand,
|
|
391
|
+
merge: mergeCommand,
|
|
392
|
+
make: makeCommand,
|
|
393
|
+
publish: trailerPublishCommand,
|
|
394
|
+
},
|
|
395
|
+
});
|
|
396
|
+
//# sourceMappingURL=trailer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trailer.js","sourceRoot":"","sources":["../../src/commands/trailer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface HudReplayPageOptions {
|
|
2
|
+
/** Port vite serves the project (and its `/dist/engine/...`) on. */
|
|
3
|
+
gamePort: number;
|
|
4
|
+
}
|
|
5
|
+
/** The import map for the harness: CDN entries as-is, local aliases absolute on the game origin. */
|
|
6
|
+
export declare function hudReplayImportMap(gamePort: number): Record<string, string>;
|
|
7
|
+
export declare function renderHudReplayHtml(options: HudReplayPageOptions): string;
|