@bitmagic/asset-core 0.2.25-dev.0 → 0.2.25-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/dist/trailer/analyze.d.ts +24 -0
- package/dist/trailer/analyze.d.ts.map +1 -0
- package/dist/trailer/analyze.js +186 -0
- package/dist/trailer/analyze.js.map +1 -0
- package/dist/trailer/auto-edl.d.ts +51 -0
- package/dist/trailer/auto-edl.d.ts.map +1 -0
- package/dist/trailer/auto-edl.js +266 -0
- package/dist/trailer/auto-edl.js.map +1 -0
- package/dist/trailer/beat-cut.d.ts +58 -0
- package/dist/trailer/beat-cut.d.ts.map +1 -0
- package/dist/trailer/beat-cut.js +232 -0
- package/dist/trailer/beat-cut.js.map +1 -0
- package/dist/trailer/capture.d.ts +102 -0
- package/dist/trailer/capture.d.ts.map +1 -0
- package/dist/trailer/capture.js +268 -0
- package/dist/trailer/capture.js.map +1 -0
- package/dist/trailer/edl-time.d.ts +62 -0
- package/dist/trailer/edl-time.d.ts.map +1 -0
- package/dist/trailer/edl-time.js +105 -0
- package/dist/trailer/edl-time.js.map +1 -0
- package/dist/trailer/errors.d.ts +15 -0
- package/dist/trailer/errors.d.ts.map +1 -0
- package/dist/trailer/errors.js +22 -0
- package/dist/trailer/errors.js.map +1 -0
- package/dist/trailer/ffmpeg.d.ts +19 -0
- package/dist/trailer/ffmpeg.d.ts.map +1 -0
- package/dist/trailer/ffmpeg.js +100 -0
- package/dist/trailer/ffmpeg.js.map +1 -0
- package/dist/trailer/hud-state.d.ts +182 -0
- package/dist/trailer/hud-state.d.ts.map +1 -0
- package/dist/trailer/hud-state.js +523 -0
- package/dist/trailer/hud-state.js.map +1 -0
- package/dist/trailer/index.d.ts +30 -0
- package/dist/trailer/index.d.ts.map +1 -0
- package/dist/trailer/index.js +30 -0
- package/dist/trailer/index.js.map +1 -0
- package/dist/trailer/io.d.ts +23 -0
- package/dist/trailer/io.d.ts.map +1 -0
- package/dist/trailer/io.js +20 -0
- package/dist/trailer/io.js.map +1 -0
- package/dist/trailer/link.d.ts +2 -0
- package/dist/trailer/link.d.ts.map +1 -0
- package/dist/trailer/link.js +15 -0
- package/dist/trailer/link.js.map +1 -0
- package/dist/trailer/make.d.ts +60 -0
- package/dist/trailer/make.d.ts.map +1 -0
- package/dist/trailer/make.js +196 -0
- package/dist/trailer/make.js.map +1 -0
- package/dist/trailer/merge.d.ts +8 -0
- package/dist/trailer/merge.d.ts.map +1 -0
- package/dist/trailer/merge.js +165 -0
- package/dist/trailer/merge.js.map +1 -0
- package/dist/trailer/music-beats.d.ts +124 -0
- package/dist/trailer/music-beats.d.ts.map +1 -0
- package/dist/trailer/music-beats.js +672 -0
- package/dist/trailer/music-beats.js.map +1 -0
- package/dist/trailer/paths.d.ts +29 -0
- package/dist/trailer/paths.d.ts.map +1 -0
- package/dist/trailer/paths.js +101 -0
- package/dist/trailer/paths.js.map +1 -0
- package/dist/trailer/png.d.ts +10 -0
- package/dist/trailer/png.d.ts.map +1 -0
- package/dist/trailer/png.js +37 -0
- package/dist/trailer/png.js.map +1 -0
- package/dist/trailer/render.d.ts +93 -0
- package/dist/trailer/render.d.ts.map +1 -0
- package/dist/trailer/render.js +527 -0
- package/dist/trailer/render.js.map +1 -0
- package/dist/trailer/revision.d.ts +9 -0
- package/dist/trailer/revision.d.ts.map +1 -0
- package/dist/trailer/revision.js +31 -0
- package/dist/trailer/revision.js.map +1 -0
- package/dist/trailer/synth.d.ts +10 -0
- package/dist/trailer/synth.d.ts.map +1 -0
- package/dist/trailer/synth.js +120 -0
- package/dist/trailer/synth.js.map +1 -0
- package/dist/trailer/timeline-types.d.ts +196 -0
- package/dist/trailer/timeline-types.d.ts.map +1 -0
- package/dist/trailer/timeline-types.js +419 -0
- package/dist/trailer/timeline-types.js.map +1 -0
- package/dist/trailer/titles.d.ts +38 -0
- package/dist/trailer/titles.d.ts.map +1 -0
- package/dist/trailer/titles.js +110 -0
- package/dist/trailer/titles.js.map +1 -0
- package/dist/trailer/video-chain.d.ts +130 -0
- package/dist/trailer/video-chain.d.ts.map +1 -0
- package/dist/trailer/video-chain.js +261 -0
- package/dist/trailer/video-chain.js.map +1 -0
- package/package.json +5 -1
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trailer rendering: EDL -> final MP4 with reconstructed sound.
|
|
3
|
+
*
|
|
4
|
+
* Video: one image2 input per clip, trim + setpts (speed) + fps, chained with
|
|
5
|
+
* concat (cuts) / xfade (crossfades), then — with --ui — the recorded HUD
|
|
6
|
+
* composited over that chain from a single densified alpha PNG sequence built
|
|
7
|
+
* in final output time. Audio: every placed sound instance is its own
|
|
8
|
+
* input, adelay-ed to its output-timeline position and amix-ed, optionally
|
|
9
|
+
* mixed with a music bed, then loudnorm. The whole filtergraph goes through
|
|
10
|
+
* -filter_complex_script to dodge argument-length limits. All time math comes
|
|
11
|
+
* from edl-time.ts.
|
|
12
|
+
*/
|
|
13
|
+
import * as fs from 'fs';
|
|
14
|
+
import * as path from 'path';
|
|
15
|
+
import { frameFilePath, loadEdl, loadTimeline } from './timeline-types.js';
|
|
16
|
+
import { DEFAULT_TAIL_PAD_FRAMES, computeClipTimeline, placeSounds } from './edl-time.js';
|
|
17
|
+
import { collectUiKeyframes, computeRasterGeometry, isBlankHudState } from './hud-state.js';
|
|
18
|
+
import { TrailerError } from './errors.js';
|
|
19
|
+
import { probeAudioDurationSec } from './music-beats.js';
|
|
20
|
+
import { recipeHash, synthesizeWav } from './synth.js';
|
|
21
|
+
import { runFfmpeg } from './ffmpeg.js';
|
|
22
|
+
import { out, warn } from './io.js';
|
|
23
|
+
import { appendUiSequenceChain, buildLogoChain, buildUiSequence, buildVideoChain, fmtNum, foldUiOverlays, } from './video-chain.js';
|
|
24
|
+
import { nextFreeRevision } from './revision.js';
|
|
25
|
+
import { buildTitleOverlayChain, titleInputArgs } from './titles.js';
|
|
26
|
+
export const DEFAULT_MUSIC_GAIN_DB = -6;
|
|
27
|
+
export const DEFAULT_MUSIC_START_SEC = 0;
|
|
28
|
+
export const DEFAULT_MUSIC_FADE_SEC = 2;
|
|
29
|
+
/** Watermark defaults, all as fractions of the video so they hold at any resolution. */
|
|
30
|
+
export const DEFAULT_LOGO_OPACITY = 0.7;
|
|
31
|
+
export const DEFAULT_LOGO_HEIGHT_FRAC = 0.055;
|
|
32
|
+
export const DEFAULT_LOGO_MARGIN_FRAC = 0.025;
|
|
33
|
+
/**
|
|
34
|
+
* Copy the nearest existing earlier frame over each missing frame that any
|
|
35
|
+
* clip needs, so the image2 sequence has no gaps. Additive and idempotent.
|
|
36
|
+
*/
|
|
37
|
+
function fillMissingFrames(recordingDir, timeline, timings) {
|
|
38
|
+
const { name, frameCount } = timeline.recording;
|
|
39
|
+
const missing = new Set(timeline.recording.missingFrames ?? []);
|
|
40
|
+
let filled = 0;
|
|
41
|
+
for (const frame of missing) {
|
|
42
|
+
if (!timings.some((t) => frame >= t.in && frame < t.out))
|
|
43
|
+
continue;
|
|
44
|
+
const target = frameFilePath(recordingDir, name, frame);
|
|
45
|
+
if (fs.existsSync(target))
|
|
46
|
+
continue;
|
|
47
|
+
let source = null;
|
|
48
|
+
for (let f = frame - 1; f >= 0; f--) {
|
|
49
|
+
if (!missing.has(f) && fs.existsSync(frameFilePath(recordingDir, name, f))) {
|
|
50
|
+
source = f;
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (source === null) {
|
|
55
|
+
for (let f = frame + 1; f < frameCount; f++) {
|
|
56
|
+
if (!missing.has(f) && fs.existsSync(frameFilePath(recordingDir, name, f))) {
|
|
57
|
+
source = f;
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (source === null) {
|
|
63
|
+
warn(`missing frame ${frame}: no existing frame available to substitute`);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
fs.copyFileSync(frameFilePath(recordingDir, name, source), target);
|
|
67
|
+
filled++;
|
|
68
|
+
}
|
|
69
|
+
return filled;
|
|
70
|
+
}
|
|
71
|
+
function assetExtension(url) {
|
|
72
|
+
const ext = path.extname(new URL(url).pathname);
|
|
73
|
+
return ext.length > 0 ? ext : '.bin';
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Download each unique asset URL once into `.assets/` (skip if cached).
|
|
77
|
+
* Failures warn and map to null so the sound is skipped, never aborting.
|
|
78
|
+
*/
|
|
79
|
+
async function prepareAssetFiles(assetsDir, sounds) {
|
|
80
|
+
const byUrl = new Map();
|
|
81
|
+
for (const sound of sounds) {
|
|
82
|
+
if (byUrl.has(sound.url))
|
|
83
|
+
continue;
|
|
84
|
+
try {
|
|
85
|
+
const safeId = sound.assetId.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
86
|
+
const filePath = path.join(assetsDir, `${safeId}${assetExtension(sound.url)}`);
|
|
87
|
+
if (!fs.existsSync(filePath)) {
|
|
88
|
+
const response = await fetch(sound.url);
|
|
89
|
+
if (!response.ok)
|
|
90
|
+
throw new Error(`HTTP ${response.status}`);
|
|
91
|
+
fs.writeFileSync(filePath, Buffer.from(await response.arrayBuffer()));
|
|
92
|
+
}
|
|
93
|
+
byUrl.set(sound.url, filePath);
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
warn(`asset download failed, skipping sound "${sound.assetId}" (${sound.url}): ${err instanceof Error ? err.message : String(err)}`);
|
|
97
|
+
byUrl.set(sound.url, null);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return byUrl;
|
|
101
|
+
}
|
|
102
|
+
/** Synthesize (or reuse) the cached WAV for a synth sound's recipe. */
|
|
103
|
+
function prepareSynthFile(assetsDir, sound, cache) {
|
|
104
|
+
const hash = recipeHash(sound.recipe);
|
|
105
|
+
const cached = cache.get(hash);
|
|
106
|
+
if (cached !== undefined)
|
|
107
|
+
return cached;
|
|
108
|
+
const filePath = path.join(assetsDir, `synth-${hash}.wav`);
|
|
109
|
+
if (!fs.existsSync(filePath))
|
|
110
|
+
fs.writeFileSync(filePath, synthesizeWav(sound.recipe));
|
|
111
|
+
cache.set(hash, filePath);
|
|
112
|
+
return filePath;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Filter chain that turns the music input into a bed the length of the video.
|
|
116
|
+
*
|
|
117
|
+
* `[N:a:0]` is deliberate: music files routinely carry embedded cover art as a
|
|
118
|
+
* video stream, and a bare `[N:a]`-style guess is not worth the risk. asetpts
|
|
119
|
+
* re-bases the timestamps after an input seek so the fade lands at the end of
|
|
120
|
+
* the VIDEO, not at some offset inherited from the track.
|
|
121
|
+
*/
|
|
122
|
+
function musicFilterChain(music, inputIndex, totalDuration) {
|
|
123
|
+
const parts = ['aresample=48000', 'aformat=channel_layouts=stereo', 'asetpts=PTS-STARTPTS'];
|
|
124
|
+
if (music.gainDb !== 0)
|
|
125
|
+
parts.push(`volume=${fmtNum(music.gainDb)}dB`);
|
|
126
|
+
if (music.fadeSec > 0) {
|
|
127
|
+
const fadeStart = Math.max(0, totalDuration - music.fadeSec);
|
|
128
|
+
parts.push(`afade=t=out:st=${fmtNum(fadeStart)}:d=${fmtNum(music.fadeSec)}`);
|
|
129
|
+
}
|
|
130
|
+
parts.push(`atrim=end=${fmtNum(totalDuration)}`);
|
|
131
|
+
return `[${inputIndex}:a:0]${parts.join(',')}`;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* The tail every audio path shares: normalize, then pad/trim to exactly the
|
|
135
|
+
* video length.
|
|
136
|
+
*/
|
|
137
|
+
function audioTail(totalDuration) {
|
|
138
|
+
return ('loudnorm=I=-16:TP=-1.5:LRA=11,aresample=48000,aformat=sample_fmts=fltp:channel_layouts=stereo,' +
|
|
139
|
+
`apad,atrim=end=${fmtNum(totalDuration)}[aout]`);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Per-RENDER scratch beside the output, like `<out>.filtergraph.txt`: a
|
|
143
|
+
* recording-scoped `.ui-seq` would be wiped by any other render of the same
|
|
144
|
+
* recording (`make` and `render` overlapping on two music variants) while
|
|
145
|
+
* this one's ffmpeg is still reading it — and its entries hardlink rasters
|
|
146
|
+
* that render rewrites in place. Removed with the filtergraph on success, kept
|
|
147
|
+
* on failure for the same diagnostic reason.
|
|
148
|
+
*/
|
|
149
|
+
function uiSequenceDir(outPath) {
|
|
150
|
+
return `${outPath}.ui-seq`;
|
|
151
|
+
}
|
|
152
|
+
/** Build the full ffmpeg argument list and write the filtergraph script file. */
|
|
153
|
+
function buildFfmpegArgs(build) {
|
|
154
|
+
const { recordingDir, timeline, timings, audioPlacements, overlays, totalDuration, music, logo, titles, titlePngs, outPath } = build;
|
|
155
|
+
const fps = timeline.recording.fps;
|
|
156
|
+
const framePattern = path.join(recordingDir, `${timeline.recording.name}_%06d.png`);
|
|
157
|
+
const args = ['-y'];
|
|
158
|
+
for (const timing of timings) {
|
|
159
|
+
args.push('-framerate', String(fps), '-start_number', String(timing.in), '-i', framePattern);
|
|
160
|
+
}
|
|
161
|
+
const audioBase = timings.length;
|
|
162
|
+
for (const placement of audioPlacements) {
|
|
163
|
+
args.push('-i', placement.filePath);
|
|
164
|
+
}
|
|
165
|
+
// Silent bed so the MP4 always has an audio track — not needed when the
|
|
166
|
+
// music is about to become the audio track by itself.
|
|
167
|
+
const silentBed = audioPlacements.length === 0 && music === undefined;
|
|
168
|
+
if (silentBed) {
|
|
169
|
+
args.push('-f', 'lavfi', '-t', fmtNum(totalDuration), '-i', 'anullsrc=r=48000:cl=stereo');
|
|
170
|
+
}
|
|
171
|
+
const musicIndex = audioBase + audioPlacements.length + (silentBed ? 1 : 0);
|
|
172
|
+
if (music !== undefined) {
|
|
173
|
+
// -stream_loop is an input option, so it goes before this input's -i.
|
|
174
|
+
if (music.loop)
|
|
175
|
+
args.push('-stream_loop', '-1');
|
|
176
|
+
if (music.startSec > 0)
|
|
177
|
+
args.push('-ss', fmtNum(music.startSec));
|
|
178
|
+
args.push('-i', music.filePath);
|
|
179
|
+
}
|
|
180
|
+
// `card` titles dim the footage behind their own scrim, but the scrim is
|
|
181
|
+
// deliberately translucent — a bright HUD element under it punches through
|
|
182
|
+
// and reads as a glitch on the end card. Blank the HUD for the card's whole
|
|
183
|
+
// window, fades included; `title`/`line` styles sit over live footage and
|
|
184
|
+
// keep the HUD.
|
|
185
|
+
const blocked = titles
|
|
186
|
+
.filter((cue) => (cue.style ?? 'line') === 'card')
|
|
187
|
+
// Parsed back from the same 6-decimal strings the card's own `enable` is
|
|
188
|
+
// built from (titles.ts), so both gates compare one number: a raw
|
|
189
|
+
// `at + dur` (0.1 + 0.2 = 0.30000000000000004) kept the HUD blank for a
|
|
190
|
+
// frame after the card had already released.
|
|
191
|
+
.map((cue) => ({ startSec: Number(fmtNum(cue.at)), endSec: Number(fmtNum(cue.at + cue.dur)) }));
|
|
192
|
+
// One image2 input carrying every output frame, rather than one PNG input
|
|
193
|
+
// per keyframe — see buildUiSequence.
|
|
194
|
+
const uiIndex = musicIndex + (music !== undefined ? 1 : 0);
|
|
195
|
+
const sequence = overlays.length > 0
|
|
196
|
+
? buildUiSequence(overlays, fps, totalDuration, uiSequenceDir(outPath), blocked)
|
|
197
|
+
: null;
|
|
198
|
+
if (sequence !== null) {
|
|
199
|
+
// ffmpeg reports a short sequence by silently dropping the HUD and exiting
|
|
200
|
+
// 0, so the count is checked here instead of trusting it.
|
|
201
|
+
const written = fs.readdirSync(sequence.dir).filter((name) => name.startsWith('ui_')).length;
|
|
202
|
+
if (written < sequence.frameCount) {
|
|
203
|
+
throw new TrailerError(`ui: overlay sequence is short (${written}/${sequence.frameCount} frames) — refusing to render`);
|
|
204
|
+
}
|
|
205
|
+
out(`ui: overlay sequence ${sequence.frameCount} frame(s), ` +
|
|
206
|
+
`${sequence.frameCount - sequence.blankFrames} drawn / ${sequence.blankFrames} blank`);
|
|
207
|
+
// A rasterized overlay that owns no output frame cost ~25 ms of Playwright
|
|
208
|
+
// and a PNG for nothing — a window shorter than 1/fps, which a speed ramp
|
|
209
|
+
// or --ui-keyframe-rate 0 can produce. Everything else here speaks when it
|
|
210
|
+
// degrades; so does this.
|
|
211
|
+
if (sequence.unusedOverlays > 0) {
|
|
212
|
+
warn(`ui: ${sequence.unusedOverlays} rasterized overlay(s) were too short to own an output frame ` +
|
|
213
|
+
'and never appear — lower --ui-keyframe-rate to stop paying for them');
|
|
214
|
+
}
|
|
215
|
+
args.push('-framerate', String(fps), '-start_number', '0', '-i', path.join(sequence.dir, 'ui_%06d.png'));
|
|
216
|
+
}
|
|
217
|
+
// The logo goes last so adding it can't shift any existing input index.
|
|
218
|
+
const logoIndex = uiIndex + (sequence !== null ? 1 : 0);
|
|
219
|
+
if (logo !== undefined) {
|
|
220
|
+
args.push('-i', logo.filePath);
|
|
221
|
+
}
|
|
222
|
+
const titleBase = logoIndex + (logo !== undefined ? 1 : 0);
|
|
223
|
+
args.push(...titleInputArgs(titles, titlePngs));
|
|
224
|
+
// Stages applied on top of the cut video, in draw order: titles sit above
|
|
225
|
+
// the HUD overlay (they're the marketing layer, not part of the game), and
|
|
226
|
+
// the watermark sits above everything.
|
|
227
|
+
const { width, height } = timeline.recording;
|
|
228
|
+
const stages = [];
|
|
229
|
+
if (titles.length > 0) {
|
|
230
|
+
stages.push((i, o) => buildTitleOverlayChain(i, o, titles, titleBase));
|
|
231
|
+
}
|
|
232
|
+
if (logo !== undefined) {
|
|
233
|
+
stages.push((i, o) => buildLogoChain(i, o, {
|
|
234
|
+
inputIndex: logoIndex,
|
|
235
|
+
opacity: logo.opacity,
|
|
236
|
+
heightFrac: logo.heightFrac,
|
|
237
|
+
marginFrac: logo.marginFrac,
|
|
238
|
+
corner: logo.corner,
|
|
239
|
+
}, width, height));
|
|
240
|
+
}
|
|
241
|
+
const videoEnd = stages.length > 0 ? 'vstage0' : 'vout';
|
|
242
|
+
const chainEnd = sequence !== null ? 'vbase' : videoEnd;
|
|
243
|
+
const lines = buildVideoChain(timings, fps, chainEnd);
|
|
244
|
+
if (sequence !== null) {
|
|
245
|
+
appendUiSequenceChain(lines, sequence, uiIndex, width, height, videoEnd);
|
|
246
|
+
}
|
|
247
|
+
stages.forEach((stage, i) => {
|
|
248
|
+
const from = `vstage${i}`;
|
|
249
|
+
const to = i === stages.length - 1 ? 'vout' : `vstage${i + 1}`;
|
|
250
|
+
lines.push(...stage(from, to));
|
|
251
|
+
});
|
|
252
|
+
const tail = audioTail(totalDuration);
|
|
253
|
+
if (audioPlacements.length > 0) {
|
|
254
|
+
audioPlacements.forEach((placement, j) => {
|
|
255
|
+
const delayMs = Math.max(0, Math.round(placement.outputTimeSec * 1000));
|
|
256
|
+
lines.push(`[${audioBase + j}:a]aresample=48000,aformat=channel_layouts=stereo,volume=${fmtNum(placement.volume)},adelay=${delayMs}:all=1[snd${j}]`);
|
|
257
|
+
});
|
|
258
|
+
const mixInputs = audioPlacements.map((_, j) => `[snd${j}]`).join('');
|
|
259
|
+
const sfxBus = `${mixInputs}amix=inputs=${audioPlacements.length}:duration=longest:normalize=0` +
|
|
260
|
+
(build.sfxGainDb !== 0 ? `,volume=${fmtNum(build.sfxGainDb)}dB` : '');
|
|
261
|
+
if (music === undefined) {
|
|
262
|
+
lines.push(`${sfxBus},${tail}`);
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
lines.push(`${sfxBus}[sfxbus]`);
|
|
266
|
+
lines.push(`${musicFilterChain(music, musicIndex, totalDuration)}[music]`);
|
|
267
|
+
lines.push(`[sfxbus][music]amix=inputs=2:duration=longest:normalize=0,${tail}`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
else if (music !== undefined) {
|
|
271
|
+
// No SFX to mix with: the music IS the audio track.
|
|
272
|
+
lines.push(`${musicFilterChain(music, musicIndex, totalDuration)},${tail}`);
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
lines.push(`[${audioBase}:a]anull[aout]`);
|
|
276
|
+
}
|
|
277
|
+
fs.writeFileSync(build.scriptPath, `${lines.join(';\n')}\n`);
|
|
278
|
+
args.push('-filter_complex_script', build.scriptPath, '-map', '[vout]', '-map', '[aout]', '-c:v', 'libx264', '-crf', '20', '-pix_fmt', 'yuv420p', '-c:a', 'aac', '-b:a', '192k', '-movflags', '+faststart', build.outPath);
|
|
279
|
+
return args;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Collect, rasterize and place the HUD overlays. Every "can't draw the UI"
|
|
283
|
+
* path returns [] with a spoken reason: --ui must never silently produce a
|
|
284
|
+
* bare video.
|
|
285
|
+
*/
|
|
286
|
+
async function prepareUiOverlays(timeline, timings, options, outPath) {
|
|
287
|
+
const globalUi = options.ui === true;
|
|
288
|
+
const clipEnabled = (index) => timings[index].ui ?? globalUi;
|
|
289
|
+
if (!timings.some((_, i) => clipEnabled(i))) {
|
|
290
|
+
warn('ui: every clip sets "ui": false — rendering without overlays');
|
|
291
|
+
return [];
|
|
292
|
+
}
|
|
293
|
+
if (timeline.hud.length === 0) {
|
|
294
|
+
warn('ui: this recording has no HUD track (timeline.json has no hud[] entries). Either the game builds its ' +
|
|
295
|
+
'UI as custom DOM outside the GameHUD API, or it was captured before HUD recording existed. ' +
|
|
296
|
+
'Rendering without overlays.');
|
|
297
|
+
return [];
|
|
298
|
+
}
|
|
299
|
+
const keyframes = collectUiKeyframes(timeline.hud, timings, {
|
|
300
|
+
fps: timeline.recording.fps,
|
|
301
|
+
clipEnabled,
|
|
302
|
+
...(options.uiKeyframesPerSec !== undefined ? { maxPerSecond: options.uiKeyframesPerSec } : {})
|
|
303
|
+
});
|
|
304
|
+
// Blank states are dropped only AFTER windows are chained, so a hidden HUD
|
|
305
|
+
// leaves a real gap instead of stretching the previous overlay over it.
|
|
306
|
+
const drawable = keyframes.filter((k) => !isBlankHudState(k.state));
|
|
307
|
+
if (drawable.length === 0) {
|
|
308
|
+
warn('ui: the HUD is hidden or empty across every selected clip — rendering without overlays');
|
|
309
|
+
return [];
|
|
310
|
+
}
|
|
311
|
+
// Custom-element markup without its stylesheets replays as bare browser-
|
|
312
|
+
// default markup — the single most common "my UI looks broken in the
|
|
313
|
+
// trailer" cause. Recordings made before the engine captured stylesheets
|
|
314
|
+
// can't be fixed offline, so say it once, precisely.
|
|
315
|
+
const hasCustom = timeline.hud.some((op) => (op.op === 'create' && op.elType === 'custom') || op.op === 'custom-html');
|
|
316
|
+
const hasStylesheets = timeline.hud.some((op) => op.op === 'stylesheet');
|
|
317
|
+
// `recording.window` and stylesheet capture shipped in the same engine
|
|
318
|
+
// change, so a manifest without a window is the one reliable marker that
|
|
319
|
+
// this recording PREDATES capture. Without that check the warning also fires
|
|
320
|
+
// on current recordings of games that style their widgets purely inline —
|
|
321
|
+
// telling the user to re-record when re-recording would change nothing.
|
|
322
|
+
if (hasCustom && !hasStylesheets && timeline.recording.window === undefined) {
|
|
323
|
+
warn('ui: this recording has custom HUD elements but predates stylesheet capture — styled widgets will ' +
|
|
324
|
+
'replay unstyled. Re-record with a current engine to capture the game\'s CSS (in a CLI project the ' +
|
|
325
|
+
'recorder is the vendored engine: run `bitmagic upgrade` first, or re-recording changes nothing).');
|
|
326
|
+
}
|
|
327
|
+
if (options.harnessUrl === undefined) {
|
|
328
|
+
warn('ui: no HUD replay harness (is `bitmagic dev` running?) — rendering without overlays');
|
|
329
|
+
return [];
|
|
330
|
+
}
|
|
331
|
+
const geometry = computeRasterGeometry(timeline.recording.width, timeline.recording.height, timeline.recording.window);
|
|
332
|
+
const rasterizeUi = options.rasterizers?.ui;
|
|
333
|
+
if (rasterizeUi === undefined) {
|
|
334
|
+
warn('ui: this host cannot draw the HUD replay — rendering without overlays');
|
|
335
|
+
return [];
|
|
336
|
+
}
|
|
337
|
+
const paths = await rasterizeUi(drawable, {
|
|
338
|
+
harnessUrl: options.harnessUrl,
|
|
339
|
+
outDir: `${outPath}.ui`,
|
|
340
|
+
width: geometry.viewportWidth,
|
|
341
|
+
height: geometry.viewportHeight,
|
|
342
|
+
deviceScaleFactor: geometry.deviceScaleFactor
|
|
343
|
+
});
|
|
344
|
+
if (paths === null)
|
|
345
|
+
return [];
|
|
346
|
+
// A null slot is a keyframe that failed to rasterize; foldUiOverlays holds
|
|
347
|
+
// the previous overlay across it, but only within the same clip.
|
|
348
|
+
const { overlays, bare } = foldUiOverlays(paths, drawable);
|
|
349
|
+
if (bare > 0) {
|
|
350
|
+
warn(`ui: ${bare} failed keyframe window(s) had no adjacent overlay to hold — left without UI`);
|
|
351
|
+
}
|
|
352
|
+
return overlays;
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Resolve the music bed and decide whether it has to loop. A track that ends
|
|
356
|
+
* before the video does is a real editorial surprise, so it warns rather than
|
|
357
|
+
* quietly dropping to silence half way through.
|
|
358
|
+
*/
|
|
359
|
+
/**
|
|
360
|
+
* Resolve the watermark. Like `--music`, the path is relative to the cwd —
|
|
361
|
+
* brand assets live in the repo, not in the recording folder.
|
|
362
|
+
*/
|
|
363
|
+
function prepareLogo(logoPath, options) {
|
|
364
|
+
const filePath = path.resolve(logoPath);
|
|
365
|
+
if (!fs.existsSync(filePath)) {
|
|
366
|
+
throw new TrailerError(`logo not found: ${filePath}`);
|
|
367
|
+
}
|
|
368
|
+
const opacity = options.logoOpacity ?? DEFAULT_LOGO_OPACITY;
|
|
369
|
+
if (!(opacity > 0 && opacity <= 1)) {
|
|
370
|
+
throw new TrailerError(`--logo-opacity must be in (0, 1], got ${opacity}`);
|
|
371
|
+
}
|
|
372
|
+
const heightFrac = options.logoHeightFrac ?? DEFAULT_LOGO_HEIGHT_FRAC;
|
|
373
|
+
if (!(heightFrac > 0 && heightFrac <= 0.5)) {
|
|
374
|
+
throw new TrailerError(`--logo-height must be a fraction in (0, 0.5], got ${heightFrac}`);
|
|
375
|
+
}
|
|
376
|
+
const marginFrac = options.logoMarginFrac ?? DEFAULT_LOGO_MARGIN_FRAC;
|
|
377
|
+
if (!(marginFrac >= 0 && marginFrac <= 0.4)) {
|
|
378
|
+
throw new TrailerError(`--logo-margin must be a fraction in [0, 0.4], got ${marginFrac}`);
|
|
379
|
+
}
|
|
380
|
+
return { filePath, opacity, heightFrac, marginFrac, corner: options.logoCorner ?? 'br' };
|
|
381
|
+
}
|
|
382
|
+
async function prepareMusicBed(musicPath, options, totalDuration) {
|
|
383
|
+
const filePath = path.resolve(musicPath);
|
|
384
|
+
if (!fs.existsSync(filePath))
|
|
385
|
+
throw new TrailerError(`music file not found: ${filePath}`);
|
|
386
|
+
const startSec = options.musicStartSec ?? DEFAULT_MUSIC_START_SEC;
|
|
387
|
+
const fadeSec = options.musicFadeSec ?? DEFAULT_MUSIC_FADE_SEC;
|
|
388
|
+
if (fadeSec > totalDuration) {
|
|
389
|
+
warn(`music: --music-fade ${fadeSec}s is longer than the ${totalDuration.toFixed(2)}s video — it fades from the first frame`);
|
|
390
|
+
}
|
|
391
|
+
let loop = false;
|
|
392
|
+
try {
|
|
393
|
+
const trackDuration = await probeAudioDurationSec(filePath);
|
|
394
|
+
const usable = trackDuration - startSec;
|
|
395
|
+
if (usable <= 0) {
|
|
396
|
+
throw new TrailerError(`--music-start ${startSec}s is past the end of a ${trackDuration.toFixed(2)}s track`);
|
|
397
|
+
}
|
|
398
|
+
if (usable < totalDuration) {
|
|
399
|
+
warn(`music: ${usable.toFixed(1)}s of track from --music-start ${startSec}s does not cover the ` +
|
|
400
|
+
`${totalDuration.toFixed(1)}s video — looping it`);
|
|
401
|
+
loop = true;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
catch (err) {
|
|
405
|
+
if (err instanceof Error && err.message.startsWith('--music-start'))
|
|
406
|
+
throw err;
|
|
407
|
+
warn(`music: could not read the track duration (${err instanceof Error ? err.message : String(err)}) — looping it to be safe`);
|
|
408
|
+
loop = true;
|
|
409
|
+
}
|
|
410
|
+
return { filePath, gainDb: options.musicGainDb ?? DEFAULT_MUSIC_GAIN_DB, startSec, fadeSec, loop };
|
|
411
|
+
}
|
|
412
|
+
export async function runRender(recordingDir, options = {}) {
|
|
413
|
+
const timeline = loadTimeline(recordingDir);
|
|
414
|
+
const { name, fps, frameCount } = timeline.recording;
|
|
415
|
+
const edlPath = path.resolve(recordingDir, options.edlPath ?? 'edl.json');
|
|
416
|
+
const edl = loadEdl(edlPath);
|
|
417
|
+
const requestedOut = path.resolve(recordingDir, options.outPath ?? `${name}-trailer.mp4`);
|
|
418
|
+
const outPath = options.overwrite === true ? requestedOut : nextFreeRevision(requestedOut);
|
|
419
|
+
const { timings, totalDuration, warnings } = computeClipTimeline(edl.clips, fps);
|
|
420
|
+
warnings.forEach((w) => warn(w));
|
|
421
|
+
for (const timing of timings) {
|
|
422
|
+
if (timing.out > frameCount) {
|
|
423
|
+
throw new TrailerError(`clip ${timing.index}: out=${timing.out} exceeds recording frameCount=${frameCount}`);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
const filled = fillMissingFrames(recordingDir, timeline, timings);
|
|
427
|
+
if (filled > 0)
|
|
428
|
+
out(`filled ${filled} missing frame(s) from nearest neighbours`);
|
|
429
|
+
const placements = placeSounds(timings, timeline.sounds, fps, options.tailPadFrames ?? DEFAULT_TAIL_PAD_FRAMES);
|
|
430
|
+
const assetsDir = path.join(recordingDir, '.assets');
|
|
431
|
+
fs.mkdirSync(assetsDir, { recursive: true });
|
|
432
|
+
const assetSounds = placements
|
|
433
|
+
.map((p) => p.sound)
|
|
434
|
+
.filter((s) => s.kind === 'asset');
|
|
435
|
+
const assetFiles = await prepareAssetFiles(assetsDir, assetSounds);
|
|
436
|
+
const synthCache = new Map();
|
|
437
|
+
const audioPlacements = [];
|
|
438
|
+
let skipped = 0;
|
|
439
|
+
for (const placement of placements) {
|
|
440
|
+
const sound = placement.sound;
|
|
441
|
+
if (sound.kind === 'asset') {
|
|
442
|
+
const filePath = assetFiles.get(sound.url) ?? null;
|
|
443
|
+
if (filePath === null) {
|
|
444
|
+
skipped++;
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
audioPlacements.push({ filePath, outputTimeSec: placement.outputTimeSec, volume: sound.volume ?? 1 });
|
|
448
|
+
}
|
|
449
|
+
else {
|
|
450
|
+
audioPlacements.push({
|
|
451
|
+
filePath: prepareSynthFile(assetsDir, sound, synthCache),
|
|
452
|
+
outputTimeSec: placement.outputTimeSec,
|
|
453
|
+
volume: sound.volume ?? 1
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
const wantUi = options.ui === true || edl.clips.some((clip) => clip.ui === true);
|
|
458
|
+
const overlays = wantUi ? await prepareUiOverlays(timeline, timings, options, outPath) : [];
|
|
459
|
+
const music = options.musicPath !== undefined ? await prepareMusicBed(options.musicPath, options, totalDuration) : undefined;
|
|
460
|
+
const logo = options.logoPath !== undefined ? prepareLogo(options.logoPath, options) : undefined;
|
|
461
|
+
// Titles are timed in output seconds; warn rather than fail if one would
|
|
462
|
+
// land past the end of the cut, since that only means it is never seen.
|
|
463
|
+
const titles = edl.titles ?? [];
|
|
464
|
+
for (const cue of titles) {
|
|
465
|
+
if (cue.at >= totalDuration) {
|
|
466
|
+
warn(`title "${cue.text}" starts at ${cue.at.toFixed(2)}s, past the ${totalDuration.toFixed(2)}s cut — it will not appear`);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
let titlePngs = [];
|
|
470
|
+
if (titles.length > 0) {
|
|
471
|
+
const font = options.titleFontPath !== undefined ? path.resolve(options.titleFontPath) : undefined;
|
|
472
|
+
const rasterizeTitles = options.rasterizers?.titles;
|
|
473
|
+
if (rasterizeTitles === undefined)
|
|
474
|
+
warn('titles: this host cannot draw title cards — rendering without them');
|
|
475
|
+
const rendered = rasterizeTitles === undefined ? null : await rasterizeTitles(titles, timeline.recording.width, timeline.recording.height, path.join(recordingDir, '.titles'), font);
|
|
476
|
+
if (rendered === null)
|
|
477
|
+
titles.length = 0;
|
|
478
|
+
else
|
|
479
|
+
titlePngs = rendered;
|
|
480
|
+
}
|
|
481
|
+
const scriptPath = `${outPath}.filtergraph.txt`;
|
|
482
|
+
const build = {
|
|
483
|
+
recordingDir,
|
|
484
|
+
timeline,
|
|
485
|
+
timings,
|
|
486
|
+
audioPlacements,
|
|
487
|
+
overlays,
|
|
488
|
+
totalDuration,
|
|
489
|
+
scriptPath,
|
|
490
|
+
outPath,
|
|
491
|
+
titles,
|
|
492
|
+
titlePngs,
|
|
493
|
+
sfxGainDb: options.sfxGainDb ?? 0
|
|
494
|
+
};
|
|
495
|
+
if (music !== undefined)
|
|
496
|
+
build.music = music;
|
|
497
|
+
if (logo !== undefined)
|
|
498
|
+
build.logo = logo;
|
|
499
|
+
const args = buildFfmpegArgs(build);
|
|
500
|
+
try {
|
|
501
|
+
await runFfmpeg(args);
|
|
502
|
+
}
|
|
503
|
+
catch (err) {
|
|
504
|
+
warn(`render failed; filtergraph kept at ${scriptPath}`);
|
|
505
|
+
throw err;
|
|
506
|
+
}
|
|
507
|
+
fs.unlinkSync(scriptPath);
|
|
508
|
+
// The UI scratch (rasters + densified sequence) served its purpose; the
|
|
509
|
+
// filtergraph kept on failure is the debugging trail, not these.
|
|
510
|
+
fs.rmSync(uiSequenceDir(outPath), { recursive: true, force: true });
|
|
511
|
+
fs.rmSync(`${outPath}.ui`, { recursive: true, force: true });
|
|
512
|
+
// The EDL is the entire creative decision and is a few KB; keep a copy with
|
|
513
|
+
// the video so any render can be reproduced or re-cut later.
|
|
514
|
+
fs.writeFileSync(`${outPath}.edl.json`, JSON.stringify(edl, null, 2));
|
|
515
|
+
out(`out: ${outPath} | duration: ${totalDuration.toFixed(2)}s | clips: ${timings.length} | ` +
|
|
516
|
+
`sounds: ${audioPlacements.length} placed / ${skipped} skipped` +
|
|
517
|
+
(wantUi ? ` | ui overlays: ${overlays.length}` : '') +
|
|
518
|
+
(music !== undefined
|
|
519
|
+
? ` | music: ${path.basename(music.filePath)} @ ${music.gainDb}dB${music.loop ? ' (looped)' : ''}`
|
|
520
|
+
: '') +
|
|
521
|
+
(logo !== undefined
|
|
522
|
+
? ` | logo: ${path.basename(logo.filePath)} ${logo.corner} @ ${Math.round(logo.opacity * 100)}%`
|
|
523
|
+
: '') +
|
|
524
|
+
(titles.length > 0 ? ` | titles: ${titles.length}` : ''));
|
|
525
|
+
return { outPath, durationSec: totalDuration, clipCount: timings.length };
|
|
526
|
+
}
|
|
527
|
+
//# sourceMappingURL=render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/trailer/render.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* First unused `<name>-rN.<ext>` for a path that already exists.
|
|
3
|
+
*
|
|
4
|
+
* Renders are iterative and the EDL is the whole creative decision, so
|
|
5
|
+
* overwriting silently destroys the previous cut. A fresh filename also means
|
|
6
|
+
* a video player opens a genuinely new file rather than a cached old one.
|
|
7
|
+
*/
|
|
8
|
+
export declare function nextFreeRevision(filePath: string): string;
|
|
9
|
+
//# sourceMappingURL=revision.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"revision.d.ts","sourceRoot":"","sources":["../../src/trailer/revision.ts"],"names":[],"mappings":"AAWA;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAUzD"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Never overwrite a rendered trailer.
|
|
3
|
+
*
|
|
4
|
+
* Renders are iterative and the EDL is the whole creative decision, so
|
|
5
|
+
* replacing the file destroys the only record of that edit. A fresh filename
|
|
6
|
+
* also means a video player opens a genuinely new file instead of showing a
|
|
7
|
+
* cached stale one.
|
|
8
|
+
*/
|
|
9
|
+
import * as fs from 'fs';
|
|
10
|
+
import * as path from 'path';
|
|
11
|
+
/**
|
|
12
|
+
* First unused `<name>-rN.<ext>` for a path that already exists.
|
|
13
|
+
*
|
|
14
|
+
* Renders are iterative and the EDL is the whole creative decision, so
|
|
15
|
+
* overwriting silently destroys the previous cut. A fresh filename also means
|
|
16
|
+
* a video player opens a genuinely new file rather than a cached old one.
|
|
17
|
+
*/
|
|
18
|
+
export function nextFreeRevision(filePath) {
|
|
19
|
+
if (!fs.existsSync(filePath))
|
|
20
|
+
return filePath;
|
|
21
|
+
const dir = path.dirname(filePath);
|
|
22
|
+
const ext = path.extname(filePath);
|
|
23
|
+
const base = path.basename(filePath, ext).replace(/-r\d+$/, '');
|
|
24
|
+
for (let rev = 2; rev < 1000; rev++) {
|
|
25
|
+
const candidate = path.join(dir, `${base}-r${rev}${ext}`);
|
|
26
|
+
if (!fs.existsSync(candidate))
|
|
27
|
+
return candidate;
|
|
28
|
+
}
|
|
29
|
+
throw new Error(`could not find a free revision name for ${filePath}`);
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=revision.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"revision.js","sourceRoot":"","sources":["../../src/trailer/revision.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAgB;IAC/C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC9C,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACnC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACnC,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAChE,KAAK,IAAI,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC;QACpC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,KAAK,GAAG,GAAG,GAAG,EAAE,CAAC,CAAC;QAC1D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC;YAAE,OAAO,SAAS,CAAC;IAClD,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,2CAA2C,QAAQ,EAAE,CAAC,CAAC;AACzE,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { SynthRecipe } from './timeline-types.js';
|
|
2
|
+
export declare const SYNTH_SAMPLE_RATE = 48000;
|
|
3
|
+
/** Render a recipe to mono float samples in [-1, 1], faded at both ends. */
|
|
4
|
+
export declare function synthesizeSamples(recipe: SynthRecipe): Float32Array;
|
|
5
|
+
/** Encode mono float samples as a 48kHz 16-bit PCM WAV file. */
|
|
6
|
+
export declare function samplesToWav(samples: Float32Array): Buffer;
|
|
7
|
+
export declare function synthesizeWav(recipe: SynthRecipe): Buffer;
|
|
8
|
+
/** Stable cache key for a recipe (16 hex chars of sha256 over its JSON). */
|
|
9
|
+
export declare function recipeHash(recipe: SynthRecipe): string;
|
|
10
|
+
//# sourceMappingURL=synth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"synth.d.ts","sourceRoot":"","sources":["../../src/trailer/synth.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAc,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEnE,eAAO,MAAM,iBAAiB,QAAQ,CAAC;AAyDvC,4EAA4E;AAC5E,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,YAAY,CAmBnE;AAED,gEAAgE;AAChE,wBAAgB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,CAoB1D;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAEzD;AAED,4EAA4E;AAC5E,wBAAgB,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAEtD"}
|