@playdrop/playdrop-cli 0.11.8 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/client-meta.json +2 -2
- package/dist/apps/build.d.ts +1 -1
- package/dist/apps/build.js +4 -6
- package/dist/apps/index.d.ts +4 -5
- package/dist/apps/index.js +14 -16
- package/dist/apps/loadCheck.d.ts +76 -0
- package/dist/apps/loadCheck.js +874 -0
- package/dist/apps/upload.d.ts +3 -2
- package/dist/apps/upload.js +112 -9
- package/dist/assetSpecs.js +2 -5
- package/dist/catalogue-utils.d.ts +1 -0
- package/dist/catalogue-utils.js +1 -0
- package/dist/catalogue.d.ts +3 -1
- package/dist/catalogue.js +19 -3
- package/dist/commands/browse.js +2 -2
- package/dist/commands/build.js +4 -1
- package/dist/commands/captureListing.d.ts +30 -1
- package/dist/commands/captureListing.js +308 -125
- package/dist/commands/check.d.ts +8 -0
- package/dist/commands/check.js +188 -0
- package/dist/commands/create.js +17 -15
- package/dist/commands/createRemixContent.js +2 -2
- package/dist/commands/detail.js +2 -2
- package/dist/commands/dev.js +9 -12
- package/dist/commands/devServer.d.ts +2 -2
- package/dist/commands/devServer.js +3 -4
- package/dist/commands/marketing.js +10 -11
- package/dist/commands/play.js +2 -2
- package/dist/commands/review.d.ts +20 -0
- package/dist/commands/review.js +112 -0
- package/dist/commands/upload-content.d.ts +1 -1
- package/dist/commands/upload-content.js +1 -1
- package/dist/commands/upload.d.ts +2 -1
- package/dist/commands/upload.js +9 -52
- package/dist/commands/validate.js +4 -161
- package/dist/commands/worker/runtime.d.ts +1 -0
- package/dist/commands/worker/runtime.js +48 -5
- package/dist/commands/worker.d.ts +5 -6
- package/dist/commands/worker.js +42 -92
- package/dist/config.js +1 -2
- package/dist/index.js +33 -49
- package/dist/playwright.d.ts +5 -0
- package/dist/playwright.js +60 -1
- package/node_modules/@playdrop/api-client/dist/client.d.ts +5 -3
- package/node_modules/@playdrop/api-client/dist/client.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/client.js +10 -27
- package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts +2 -1
- package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/apps.js +11 -0
- package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/auth.d.ts +3 -1
- package/node_modules/@playdrop/api-client/dist/domains/auth.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/auth.js +22 -0
- package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/index.d.ts +7 -6
- package/node_modules/@playdrop/api-client/dist/index.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/index.js +11 -6
- package/node_modules/@playdrop/boxel-core/dist/src/entity-cleaner.js +0 -7
- package/node_modules/@playdrop/boxel-core/dist/src/entity-cleaner.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/textured-builder.js +2 -46
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/textured-builder.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/voxel-builder.js +2 -22
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/voxel-builder.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/palette_tools.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/serialization.js +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/serialization.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/infer-face.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/layer-mode.js +2 -2
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/layer-mode.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/slice.js +0 -11
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/slice.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/upscale.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/validation.d.ts +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/validation.js +0 -20
- package/node_modules/@playdrop/boxel-core/dist/src/validation.js.map +1 -1
- package/node_modules/@playdrop/boxel-three/dist/src/exporters/glb.d.ts +2 -2
- package/node_modules/@playdrop/boxel-three/dist/src/exporters/glb.js +2 -12
- package/node_modules/@playdrop/boxel-three/dist/src/voxels/faces.js +1 -125
- package/node_modules/@playdrop/config/client-meta.json +2 -2
- package/node_modules/@playdrop/config/dist/src/client-meta.d.ts +8 -0
- package/node_modules/@playdrop/config/dist/src/client-meta.d.ts.map +1 -0
- package/node_modules/@playdrop/config/dist/src/client-meta.js +51 -0
- package/node_modules/@playdrop/config/dist/src/deployment.d.ts.map +1 -1
- package/node_modules/@playdrop/config/dist/src/deployment.js +2 -32
- package/node_modules/@playdrop/config/dist/src/index.d.ts +2 -2
- package/node_modules/@playdrop/config/dist/src/index.d.ts.map +1 -1
- package/node_modules/@playdrop/config/dist/src/index.js +3 -29
- package/node_modules/@playdrop/config/dist/src/runtime-service-origins.d.ts +0 -1
- package/node_modules/@playdrop/config/dist/src/runtime-service-origins.d.ts.map +1 -1
- package/node_modules/@playdrop/config/dist/src/runtime-service-origins.js +21 -5
- package/node_modules/@playdrop/config/dist/src/server/fastify.d.ts +2 -2
- package/node_modules/@playdrop/config/dist/src/server/fastify.d.ts.map +1 -1
- package/node_modules/@playdrop/config/dist/src/server/fastify.js +0 -1
- package/node_modules/@playdrop/config/dist/test/runtime-service-origins.test.js +8 -0
- package/node_modules/@playdrop/config/dist/tsconfig.tsbuildinfo +1 -1
- package/node_modules/@playdrop/types/dist/api.d.ts +25 -24
- package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/api.js +5 -10
- package/node_modules/@playdrop/types/dist/version.d.ts +14 -0
- package/node_modules/@playdrop/types/dist/version.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/apps/launchCheck.d.ts +0 -39
- package/dist/apps/launchCheck.js +0 -414
- package/dist/captureRuntime.d.ts +0 -73
- package/dist/captureRuntime.js +0 -1021
- package/dist/commands/capture.d.ts +0 -14
- package/dist/commands/capture.js +0 -550
- package/dist/commands/captureRemote.d.ts +0 -17
- package/dist/commands/captureRemote.js +0 -394
- package/node_modules/@playdrop/types/dist/entity.d.ts +0 -31
- package/node_modules/@playdrop/types/dist/entity.d.ts.map +0 -1
- package/node_modules/@playdrop/types/dist/entity.js +0 -67
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/* eslint-disable max-lines */
|
|
3
2
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
3
|
if (k2 === undefined) k2 = k;
|
|
5
4
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
@@ -35,24 +34,34 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
34
|
})();
|
|
36
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
36
|
exports.resolveListingCaptureRouteSegment = resolveListingCaptureRouteSegment;
|
|
37
|
+
exports.resolveCaptureListingDevRouterPort = resolveCaptureListingDevRouterPort;
|
|
38
|
+
exports.resolveListingCaptureLocalDevPort = resolveListingCaptureLocalDevPort;
|
|
39
|
+
exports.buildListingCaptureFrameUrl = buildListingCaptureFrameUrl;
|
|
38
40
|
exports.resolveListingCaptureSceneId = resolveListingCaptureSceneId;
|
|
39
41
|
exports.parseCaptureListingOptions = parseCaptureListingOptions;
|
|
40
42
|
exports.assertSupportedListingEnvironment = assertSupportedListingEnvironment;
|
|
41
43
|
exports.resolveListingRecorderPath = resolveListingRecorderPath;
|
|
44
|
+
exports.buildSurfaceOutputPaths = buildSurfaceOutputPaths;
|
|
45
|
+
exports.resolveCaptureDimensions = resolveCaptureDimensions;
|
|
46
|
+
exports.resolveListingCaptureBrowserContextOptions = resolveListingCaptureBrowserContextOptions;
|
|
42
47
|
exports.computeRecordedCrop = computeRecordedCrop;
|
|
43
48
|
exports.captureListing = captureListing;
|
|
49
|
+
const types_1 = require("@playdrop/types");
|
|
44
50
|
const node_child_process_1 = require("node:child_process");
|
|
51
|
+
const node_crypto_1 = require("node:crypto");
|
|
45
52
|
const promises_1 = require("node:fs/promises");
|
|
46
53
|
const node_path_1 = require("node:path");
|
|
47
|
-
const
|
|
54
|
+
const appUrls_1 = require("../appUrls");
|
|
55
|
+
const catalogue_1 = require("../catalogue");
|
|
48
56
|
const catalogue_utils_1 = require("../catalogue-utils");
|
|
49
57
|
const commandContext_1 = require("../commandContext");
|
|
50
58
|
const http_1 = require("../http");
|
|
51
59
|
const messages_1 = require("../messages");
|
|
52
60
|
const playwright_1 = require("../playwright");
|
|
53
|
-
const
|
|
61
|
+
const dev_1 = require("./dev");
|
|
62
|
+
const devRuntimeAssets_1 = require("./devRuntimeAssets");
|
|
54
63
|
const devServer_1 = require("./devServer");
|
|
55
|
-
const
|
|
64
|
+
const devShared_1 = require("./devShared");
|
|
56
65
|
const CAPTURE_FRAME_SELECTOR = 'iframe[title="Game"]';
|
|
57
66
|
const DEFAULT_DURATION_SECONDS = 8;
|
|
58
67
|
const DEFAULT_WIDTH = 1280;
|
|
@@ -64,10 +73,43 @@ const MIN_CAPTURE_SECONDS = 1;
|
|
|
64
73
|
const MIN_DIMENSION = 16;
|
|
65
74
|
const MAX_DIMENSION = 8192;
|
|
66
75
|
const MAX_FPS = 120;
|
|
67
|
-
const
|
|
76
|
+
const MIN_SUPPORTED_MACOS_MAJOR = 26;
|
|
77
|
+
const MIN_SUPPORTED_MACOS_MINOR = 4;
|
|
68
78
|
const LISTING_CAPTURE_HOOK_TIMEOUT_MS = 60000;
|
|
79
|
+
const SHA256_HEX_REGEX = /^[a-f0-9]{64}$/i;
|
|
80
|
+
const LISTING_CAPTURE_MOBILE_USER_AGENT = 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1';
|
|
81
|
+
const SURFACE_CAPTURE_DIMENSIONS = {
|
|
82
|
+
DESKTOP: { width: 1280, height: 720 },
|
|
83
|
+
MOBILE_LANDSCAPE: { width: 844, height: 390 },
|
|
84
|
+
MOBILE_PORTRAIT: { width: 390, height: 844 },
|
|
85
|
+
};
|
|
86
|
+
const SURFACE_OUTPUT_SLUG = {
|
|
87
|
+
DESKTOP: 'desktop',
|
|
88
|
+
MOBILE_LANDSCAPE: 'mobile-landscape',
|
|
89
|
+
MOBILE_PORTRAIT: 'mobile-portrait',
|
|
90
|
+
};
|
|
69
91
|
function resolveListingCaptureRouteSegment(previewable) {
|
|
70
|
-
|
|
92
|
+
if (!previewable) {
|
|
93
|
+
throw new Error('listing_preview_support_missing');
|
|
94
|
+
}
|
|
95
|
+
return 'dev-preview';
|
|
96
|
+
}
|
|
97
|
+
function resolveCaptureListingDevRouterPort(env = process.env) {
|
|
98
|
+
return (0, dev_1.resolveCliDevRouterPort)(env);
|
|
99
|
+
}
|
|
100
|
+
function resolveListingCaptureLocalDevPort(devRouterPort) {
|
|
101
|
+
return devRouterPort === devServer_1.DEV_ROUTER_PORT ? null : devRouterPort;
|
|
102
|
+
}
|
|
103
|
+
function buildListingCaptureFrameUrl({ webBase, currentUsername, appTypeSlug, appName, routeSegment, localDevPort, }) {
|
|
104
|
+
const frameUrlObject = new URL(`${(0, appUrls_1.normalizePlaydropWebBase)(webBase)}/creators/${encodeURIComponent(currentUsername)}/apps/${appTypeSlug}/${encodeURIComponent(appName)}/${routeSegment}`);
|
|
105
|
+
frameUrlObject.searchParams.set('devAuth', 'anonymous');
|
|
106
|
+
frameUrlObject.searchParams.set('launchCheck', '1');
|
|
107
|
+
frameUrlObject.searchParams.set('captureShell', 'listing');
|
|
108
|
+
frameUrlObject.searchParams.set('mobileFrameHost', 'admin');
|
|
109
|
+
if (localDevPort !== null) {
|
|
110
|
+
frameUrlObject.searchParams.set('localDevPort', String(localDevPort));
|
|
111
|
+
}
|
|
112
|
+
return frameUrlObject.toString();
|
|
71
113
|
}
|
|
72
114
|
function resolveListingCaptureSceneId(width, height) {
|
|
73
115
|
return height > width ? 'listing-portrait' : 'listing-landscape';
|
|
@@ -93,6 +135,7 @@ function parseCaptureListingOptions(targetArg, options = {}) {
|
|
|
93
135
|
minimum: MIN_CAPTURE_SECONDS,
|
|
94
136
|
maximum: MAX_CAPTURE_SECONDS,
|
|
95
137
|
});
|
|
138
|
+
const explicitDimensions = options.width !== undefined || options.height !== undefined;
|
|
96
139
|
const width = parsePositiveNumber(options.width, DEFAULT_WIDTH, 'width', {
|
|
97
140
|
minimum: MIN_DIMENSION,
|
|
98
141
|
maximum: MAX_DIMENSION,
|
|
@@ -113,6 +156,12 @@ function parseCaptureListingOptions(targetArg, options = {}) {
|
|
|
113
156
|
maximum: durationSeconds,
|
|
114
157
|
});
|
|
115
158
|
const outputDir = options.outputDir?.trim() ? (0, node_path_1.resolve)(process.cwd(), options.outputDir.trim()) : null;
|
|
159
|
+
const contentHash = typeof options.contentHash === 'string' && options.contentHash.trim().length > 0
|
|
160
|
+
? options.contentHash.trim().toLowerCase()
|
|
161
|
+
: null;
|
|
162
|
+
if (contentHash && !SHA256_HEX_REGEX.test(contentHash)) {
|
|
163
|
+
throw new Error('content_hash_invalid');
|
|
164
|
+
}
|
|
116
165
|
return {
|
|
117
166
|
targetArg,
|
|
118
167
|
appName: options.app?.trim() || undefined,
|
|
@@ -124,6 +173,8 @@ function parseCaptureListingOptions(targetArg, options = {}) {
|
|
|
124
173
|
audio: Boolean(options.audio),
|
|
125
174
|
outputDir,
|
|
126
175
|
keepRaw: Boolean(options.keepRaw),
|
|
176
|
+
contentHash,
|
|
177
|
+
explicitDimensions,
|
|
127
178
|
};
|
|
128
179
|
}
|
|
129
180
|
function readMacOsVersion() {
|
|
@@ -139,7 +190,13 @@ function assertSupportedListingEnvironment(platform = process.platform, macosVer
|
|
|
139
190
|
if (platform !== 'darwin') {
|
|
140
191
|
throw new Error('unsupported_platform');
|
|
141
192
|
}
|
|
142
|
-
|
|
193
|
+
const [majorRaw, minorRaw] = macosVersion.split('.');
|
|
194
|
+
const major = Number.parseInt(majorRaw ?? '', 10);
|
|
195
|
+
const minor = Number.parseInt(minorRaw ?? '', 10);
|
|
196
|
+
if (!Number.isInteger(major)
|
|
197
|
+
|| !Number.isInteger(minor)
|
|
198
|
+
|| major !== MIN_SUPPORTED_MACOS_MAJOR
|
|
199
|
+
|| minor < MIN_SUPPORTED_MACOS_MINOR) {
|
|
143
200
|
throw new Error(`unsupported_macos_version:${macosVersion}`);
|
|
144
201
|
}
|
|
145
202
|
}
|
|
@@ -166,11 +223,40 @@ async function ensureOutputPaths(appName, explicitOutputDir) {
|
|
|
166
223
|
await (0, promises_1.mkdir)(outputDir, { recursive: true });
|
|
167
224
|
return {
|
|
168
225
|
outputDir,
|
|
169
|
-
rawVideoPath: (0, node_path_1.join)(outputDir, 'listing-raw.mp4'),
|
|
170
|
-
metadataPath: (0, node_path_1.join)(outputDir, 'listing-raw.json'),
|
|
171
226
|
reportPath: (0, node_path_1.join)(outputDir, 'capture-report.json'),
|
|
172
|
-
|
|
173
|
-
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
function buildSurfaceOutputPaths(outputDir, surface, useSurfacePrefix) {
|
|
230
|
+
const prefix = useSurfacePrefix ? `${SURFACE_OUTPUT_SLUG[surface]}-` : '';
|
|
231
|
+
return {
|
|
232
|
+
rawVideoPath: (0, node_path_1.join)(outputDir, `${prefix}listing-raw.mp4`),
|
|
233
|
+
metadataPath: (0, node_path_1.join)(outputDir, `${prefix}listing-raw.json`),
|
|
234
|
+
posterPath: (0, node_path_1.join)(outputDir, `${prefix}poster.png`),
|
|
235
|
+
finalVideoPath: (0, node_path_1.join)(outputDir, `${prefix}listing.mp4`),
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
function resolveCaptureDimensions(surface, parsedOptions) {
|
|
239
|
+
if (parsedOptions.explicitDimensions) {
|
|
240
|
+
return {
|
|
241
|
+
width: parsedOptions.width,
|
|
242
|
+
height: parsedOptions.height,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
return SURFACE_CAPTURE_DIMENSIONS[surface];
|
|
246
|
+
}
|
|
247
|
+
function resolveListingCaptureBrowserContextOptions(surface, dimensions) {
|
|
248
|
+
if (surface === 'DESKTOP') {
|
|
249
|
+
return { viewport: null };
|
|
250
|
+
}
|
|
251
|
+
return {
|
|
252
|
+
viewport: {
|
|
253
|
+
width: dimensions.width,
|
|
254
|
+
height: dimensions.height,
|
|
255
|
+
},
|
|
256
|
+
deviceScaleFactor: 2,
|
|
257
|
+
isMobile: true,
|
|
258
|
+
hasTouch: true,
|
|
259
|
+
userAgent: LISTING_CAPTURE_MOBILE_USER_AGENT,
|
|
174
260
|
};
|
|
175
261
|
}
|
|
176
262
|
async function grantListingBrowserLocalNetworkAccess(context, origin) {
|
|
@@ -180,7 +266,7 @@ async function grantListingBrowserLocalNetworkAccess(context, origin) {
|
|
|
180
266
|
}
|
|
181
267
|
await grantPermissions.call(context, ['local-network-access'], { origin });
|
|
182
268
|
}
|
|
183
|
-
async function launchListingBrowser(initialViewport, automationOrigin) {
|
|
269
|
+
async function launchListingBrowser(initialViewport, automationOrigin, contextOptions) {
|
|
184
270
|
const playwright = await Promise.resolve().then(() => __importStar(require('playwright-core')));
|
|
185
271
|
const args = [
|
|
186
272
|
'--no-sandbox',
|
|
@@ -207,7 +293,7 @@ async function launchListingBrowser(initialViewport, automationOrigin) {
|
|
|
207
293
|
throw new Error('browser_process_missing');
|
|
208
294
|
}
|
|
209
295
|
const browser = await playwright.chromium.connect(server.wsEndpoint());
|
|
210
|
-
const context = await browser.newContext(
|
|
296
|
+
const context = await browser.newContext(contextOptions);
|
|
211
297
|
await grantListingBrowserLocalNetworkAccess(context, automationOrigin);
|
|
212
298
|
const page = await context.newPage();
|
|
213
299
|
return {
|
|
@@ -471,6 +557,9 @@ async function probeMediaFile(filePath) {
|
|
|
471
557
|
], 'ffprobe_failed');
|
|
472
558
|
return JSON.parse(stdout);
|
|
473
559
|
}
|
|
560
|
+
async function computeFileSha256(filePath) {
|
|
561
|
+
return (0, node_crypto_1.createHash)('sha256').update(await (0, promises_1.readFile)(filePath)).digest('hex');
|
|
562
|
+
}
|
|
474
563
|
function parseFrameRate(raw) {
|
|
475
564
|
if (!raw) {
|
|
476
565
|
return null;
|
|
@@ -533,14 +622,14 @@ async function runListingRecorder(recorderPath, pid, durationSeconds, rawOutputP
|
|
|
533
622
|
function formatCommandError(error) {
|
|
534
623
|
if (error.message === 'unsupported_platform') {
|
|
535
624
|
return {
|
|
536
|
-
message: '
|
|
625
|
+
message: 'Game media capture only runs on macOS.',
|
|
537
626
|
suggestions: [],
|
|
538
627
|
};
|
|
539
628
|
}
|
|
540
629
|
if (error.message.startsWith('unsupported_macos_version:')) {
|
|
541
630
|
const version = error.message.split(':')[1] || 'unknown';
|
|
542
631
|
return {
|
|
543
|
-
message: `
|
|
632
|
+
message: `Game media capture requires macOS ${MIN_SUPPORTED_MACOS_MAJOR}.${MIN_SUPPORTED_MACOS_MINOR} or newer on this machine. Current version: ${version}.`,
|
|
544
633
|
suggestions: [],
|
|
545
634
|
};
|
|
546
635
|
}
|
|
@@ -550,6 +639,18 @@ function formatCommandError(error) {
|
|
|
550
639
|
suggestions: [],
|
|
551
640
|
};
|
|
552
641
|
}
|
|
642
|
+
if (error.message === 'content_hash_invalid') {
|
|
643
|
+
return {
|
|
644
|
+
message: 'The --content-hash value must be a 64-character SHA-256 hex string.',
|
|
645
|
+
suggestions: [],
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
if (error.message === 'listing_preview_support_missing') {
|
|
649
|
+
return {
|
|
650
|
+
message: 'This game does not declare preview support, so listing capture cannot run.',
|
|
651
|
+
suggestions: ['Set previewable: true in catalogue.json, implement the preview scene and __listingCapture hooks, then rerun "playdrop project capture".'],
|
|
652
|
+
};
|
|
653
|
+
}
|
|
553
654
|
if (error.message.startsWith('listing_recorder_failed:')) {
|
|
554
655
|
return {
|
|
555
656
|
message: error.message.slice('listing_recorder_failed:'.length),
|
|
@@ -629,7 +730,7 @@ function ensureCaptureListingToolAvailable(command) {
|
|
|
629
730
|
return true;
|
|
630
731
|
}
|
|
631
732
|
(0, messages_1.printErrorWithHelp)(`${command} is required for listing capture.`, [`Install ${command} and make sure it is available on PATH.`], {
|
|
632
|
-
command: 'project capture
|
|
733
|
+
command: 'project capture',
|
|
633
734
|
});
|
|
634
735
|
process.exitCode = 1;
|
|
635
736
|
return false;
|
|
@@ -640,7 +741,7 @@ async function ensureListingRecorderAvailable(recorderPath) {
|
|
|
640
741
|
return true;
|
|
641
742
|
}
|
|
642
743
|
catch {
|
|
643
|
-
(0, messages_1.printErrorWithHelp)(`Native recorder binary was not found at ${recorderPath}.`, ['Build it with: (cd clients/apple/playdrop-listing-recorder && swift build -c release)'], { command: 'project capture
|
|
744
|
+
(0, messages_1.printErrorWithHelp)(`Native recorder binary was not found at ${recorderPath}.`, ['Build it with: (cd clients/apple/playdrop-listing-recorder && swift build -c release)'], { command: 'project capture' });
|
|
644
745
|
process.exitCode = 1;
|
|
645
746
|
return false;
|
|
646
747
|
}
|
|
@@ -657,20 +758,22 @@ function resolveCaptureListingTarget(parsedOptions) {
|
|
|
657
758
|
: code === 'catalogue_missing'
|
|
658
759
|
? ['Run the command inside a Playdrop workspace or pass the direct HTML file path.']
|
|
659
760
|
: ['Provide a valid app target or HTML file path.'];
|
|
660
|
-
(0, messages_1.printErrorWithHelp)(message, suggestions, { command: 'project capture
|
|
761
|
+
(0, messages_1.printErrorWithHelp)(message, suggestions, { command: 'project capture' });
|
|
661
762
|
process.exitCode = 1;
|
|
662
763
|
return null;
|
|
663
764
|
}
|
|
664
765
|
}
|
|
665
766
|
async function captureListing(targetArg, options = {}) {
|
|
666
767
|
let parsedOptions;
|
|
768
|
+
let devRouterPort;
|
|
667
769
|
try {
|
|
668
770
|
parsedOptions = parseCaptureListingOptions(targetArg, options);
|
|
771
|
+
devRouterPort = resolveCaptureListingDevRouterPort();
|
|
669
772
|
assertSupportedListingEnvironment();
|
|
670
773
|
}
|
|
671
774
|
catch (error) {
|
|
672
775
|
const detail = formatCommandError(error instanceof Error ? error : new Error(String(error)));
|
|
673
|
-
(0, messages_1.printErrorWithHelp)(detail.message, detail.suggestions, { command: 'project capture
|
|
776
|
+
(0, messages_1.printErrorWithHelp)(detail.message, detail.suggestions, { command: 'project capture' });
|
|
674
777
|
process.exitCode = 1;
|
|
675
778
|
return;
|
|
676
779
|
}
|
|
@@ -687,21 +790,30 @@ async function captureListing(targetArg, options = {}) {
|
|
|
687
790
|
}
|
|
688
791
|
let appName = resolvedTarget.appName;
|
|
689
792
|
let appTypeSlug = (0, appUrls_1.getAppTypeSlug)(null);
|
|
793
|
+
let declaresPreviewSupport = false;
|
|
690
794
|
try {
|
|
691
|
-
const match = (0, catalogue_utils_1.
|
|
795
|
+
const match = (0, catalogue_utils_1.findAppCatalogueInfo)(resolvedTarget.htmlPath);
|
|
692
796
|
appName = match.name;
|
|
693
797
|
appTypeSlug = (0, appUrls_1.getAppTypeSlug)(match.type);
|
|
798
|
+
declaresPreviewSupport = match.previewable;
|
|
694
799
|
}
|
|
695
800
|
catch {
|
|
696
|
-
|
|
801
|
+
declaresPreviewSupport = false;
|
|
802
|
+
}
|
|
803
|
+
try {
|
|
804
|
+
resolveListingCaptureRouteSegment(declaresPreviewSupport);
|
|
805
|
+
}
|
|
806
|
+
catch (error) {
|
|
807
|
+
const detail = formatCommandError(error instanceof Error ? error : new Error(String(error)));
|
|
808
|
+
(0, messages_1.printErrorWithHelp)(detail.message, detail.suggestions, { command: 'project capture' });
|
|
809
|
+
process.exitCode = 1;
|
|
810
|
+
return;
|
|
697
811
|
}
|
|
698
812
|
const outputPaths = await ensureOutputPaths(appName, parsedOptions.outputDir);
|
|
699
813
|
const projectInfo = (0, devShared_1.findProjectInfo)(resolvedTarget.htmlPath);
|
|
700
814
|
const devScriptAvailable = Boolean(projectInfo.projectDir && projectInfo.packageJson && typeof projectInfo.packageJson.scripts?.dev === 'string');
|
|
701
|
-
|
|
702
|
-
await (0, commandContext_1.withEnvironment)('project capture listing', 'Capturing listing media', async ({ client, env, envConfig }) => {
|
|
815
|
+
await (0, commandContext_1.withEnvironment)('project capture', 'Capturing game media', async ({ client, env, envConfig }) => {
|
|
703
816
|
let currentUsername = '';
|
|
704
|
-
let registeredApp = null;
|
|
705
817
|
try {
|
|
706
818
|
const currentUser = await (0, devShared_1.fetchDevUser)(client);
|
|
707
819
|
currentUsername = currentUser.username.trim();
|
|
@@ -719,36 +831,36 @@ async function captureListing(targetArg, options = {}) {
|
|
|
719
831
|
(0, messages_1.printErrorWithHelp)(`Could not fetch your account (status ${error.status}).`, [
|
|
720
832
|
'Run "playdrop auth login" to refresh your session.',
|
|
721
833
|
'Use "playdrop auth whoami" afterwards to verify the current account.',
|
|
722
|
-
], { command: 'project capture
|
|
834
|
+
], { command: 'project capture' });
|
|
723
835
|
process.exitCode = 1;
|
|
724
836
|
return;
|
|
725
837
|
}
|
|
726
838
|
if ((0, devShared_1.isNetworkError)(error)) {
|
|
727
|
-
(0, messages_1.printNetworkIssue)('Could not reach the Playdrop API to resolve your account.', 'project capture
|
|
839
|
+
(0, messages_1.printNetworkIssue)('Could not reach the Playdrop API to resolve your account.', 'project capture');
|
|
728
840
|
process.exitCode = 1;
|
|
729
841
|
return;
|
|
730
842
|
}
|
|
731
843
|
throw error;
|
|
732
844
|
}
|
|
733
845
|
try {
|
|
734
|
-
|
|
846
|
+
await (0, devShared_1.assertAppRegistered)(client, currentUsername, appName);
|
|
735
847
|
}
|
|
736
848
|
catch (error) {
|
|
737
849
|
if (error instanceof http_1.CLIUnsupportedClientError) {
|
|
738
850
|
return;
|
|
739
851
|
}
|
|
740
852
|
if (error instanceof types_1.UnsupportedClientError) {
|
|
741
|
-
(0, http_1.handleUnsupportedError)(error, '
|
|
853
|
+
(0, http_1.handleUnsupportedError)(error, 'Game media capture');
|
|
742
854
|
process.exitCode = 1;
|
|
743
855
|
return;
|
|
744
856
|
}
|
|
745
857
|
if (error instanceof types_1.ApiError && error.status === 404) {
|
|
746
|
-
(0, messages_1.printErrorWithHelp)(`App ${currentUsername}/${appName} is not registered on ${env}.`, [`Run "playdrop project create app ${appName}" before using listing capture.`], { command: 'project capture
|
|
858
|
+
(0, messages_1.printErrorWithHelp)(`App ${currentUsername}/${appName} is not registered on ${env}.`, [`Run "playdrop project create app ${appName}" before using listing capture.`], { command: 'project capture' });
|
|
747
859
|
process.exitCode = 1;
|
|
748
860
|
return;
|
|
749
861
|
}
|
|
750
862
|
if ((0, devShared_1.isNetworkError)(error)) {
|
|
751
|
-
(0, messages_1.printNetworkIssue)('Could not verify the app registration.', 'project capture
|
|
863
|
+
(0, messages_1.printNetworkIssue)('Could not verify the app registration.', 'project capture');
|
|
752
864
|
process.exitCode = 1;
|
|
753
865
|
return;
|
|
754
866
|
}
|
|
@@ -756,11 +868,41 @@ async function captureListing(targetArg, options = {}) {
|
|
|
756
868
|
}
|
|
757
869
|
const entryLabel = (0, node_path_1.relative)(process.cwd(), resolvedTarget.htmlPath) || resolvedTarget.htmlPath;
|
|
758
870
|
console.log(`[listing] Preparing ${entryLabel} for ${env} (${appTypeSlug}).`);
|
|
871
|
+
const taskLookup = (0, catalogue_1.findAppTaskByFile)(resolvedTarget.htmlPath);
|
|
872
|
+
if (taskLookup.errors.length > 0) {
|
|
873
|
+
(0, messages_1.printErrorWithHelp)(taskLookup.errors[0] || 'Failed to resolve the app task from catalogue.json.', taskLookup.errors.slice(1), { command: 'project capture' });
|
|
874
|
+
process.exitCode = 1;
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
877
|
+
const localDevAppUrl = (0, devServer_1.buildLocalDevAppUrl)({
|
|
878
|
+
creatorUsername: currentUsername,
|
|
879
|
+
appType: appTypeSlug,
|
|
880
|
+
appName,
|
|
881
|
+
port: devRouterPort,
|
|
882
|
+
});
|
|
883
|
+
let runtimeAssetManifest = (0, devRuntimeAssets_1.createEmptyDevRuntimeAssetManifest)();
|
|
884
|
+
if (taskLookup.task) {
|
|
885
|
+
try {
|
|
886
|
+
runtimeAssetManifest = await (0, devRuntimeAssets_1.buildDevRuntimeAssetManifest)({
|
|
887
|
+
client,
|
|
888
|
+
apiBase: envConfig.apiBase,
|
|
889
|
+
task: taskLookup.task,
|
|
890
|
+
creatorUsername: currentUsername,
|
|
891
|
+
appBaseUrl: new URL('.', localDevAppUrl).toString(),
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
catch (error) {
|
|
895
|
+
const formatted = (0, dev_1.formatDevRuntimeAssetManifestFailure)(error);
|
|
896
|
+
(0, messages_1.printErrorWithHelp)(formatted.message, formatted.suggestions, { command: 'project capture' });
|
|
897
|
+
process.exitCode = 1;
|
|
898
|
+
return;
|
|
899
|
+
}
|
|
900
|
+
}
|
|
759
901
|
const serverAlreadyRunning = await (0, devServer_1.isDevServerAvailable)({
|
|
760
902
|
creatorUsername: currentUsername,
|
|
761
903
|
appType: appTypeSlug,
|
|
762
904
|
appName,
|
|
763
|
-
port:
|
|
905
|
+
port: devRouterPort,
|
|
764
906
|
}, 750);
|
|
765
907
|
const devServerStartedByCapture = !serverAlreadyRunning;
|
|
766
908
|
let serverHandle = null;
|
|
@@ -780,12 +922,13 @@ async function captureListing(targetArg, options = {}) {
|
|
|
780
922
|
};
|
|
781
923
|
try {
|
|
782
924
|
if (serverAlreadyRunning) {
|
|
783
|
-
|
|
925
|
+
await (0, devServer_1.updateMountedDevRuntimeAssetManifest)({
|
|
784
926
|
creatorUsername: currentUsername,
|
|
785
|
-
appType: appTypeSlug,
|
|
786
927
|
appName,
|
|
787
|
-
|
|
788
|
-
|
|
928
|
+
runtimeAssetManifest,
|
|
929
|
+
port: devRouterPort,
|
|
930
|
+
});
|
|
931
|
+
console.log(`[listing] Reusing dev server at ${localDevAppUrl}`);
|
|
789
932
|
}
|
|
790
933
|
else {
|
|
791
934
|
serverHandle = await (0, devServer_1.startDevServer)({
|
|
@@ -793,8 +936,9 @@ async function captureListing(targetArg, options = {}) {
|
|
|
793
936
|
appType: appTypeSlug,
|
|
794
937
|
creatorUsername: currentUsername,
|
|
795
938
|
htmlPath: resolvedTarget.htmlPath,
|
|
796
|
-
port:
|
|
939
|
+
port: devRouterPort,
|
|
797
940
|
projectInfo,
|
|
941
|
+
runtimeAssetManifest,
|
|
798
942
|
});
|
|
799
943
|
}
|
|
800
944
|
if (!serverAlreadyRunning && projectInfo.projectDir && !devScriptAvailable && projectInfo.packageJsonPath) {
|
|
@@ -807,114 +951,153 @@ async function captureListing(targetArg, options = {}) {
|
|
|
807
951
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
808
952
|
}
|
|
809
953
|
const webBase = envConfig.webBase ?? 'https://www.playdrop.ai';
|
|
810
|
-
const routeSegment = resolveListingCaptureRouteSegment(
|
|
811
|
-
const
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
954
|
+
const routeSegment = resolveListingCaptureRouteSegment(declaresPreviewSupport);
|
|
955
|
+
const localDevPort = resolveListingCaptureLocalDevPort(devRouterPort);
|
|
956
|
+
const frameUrl = buildListingCaptureFrameUrl({
|
|
957
|
+
webBase,
|
|
958
|
+
currentUsername,
|
|
959
|
+
appTypeSlug,
|
|
960
|
+
appName,
|
|
961
|
+
routeSegment,
|
|
962
|
+
localDevPort,
|
|
963
|
+
});
|
|
964
|
+
const frameUrlObject = new URL(frameUrl);
|
|
965
|
+
console.log(`[listing] Opening ${frameUrl}`);
|
|
815
966
|
const shouldExportPreviewAudio = parsedOptions.audio && routeSegment === 'dev-preview';
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
967
|
+
const surfaces = resolvedTarget.surfaceTargets.list;
|
|
968
|
+
const useSurfacePrefix = surfaces.length > 1;
|
|
969
|
+
const captures = [];
|
|
970
|
+
const artifactHashes = {};
|
|
971
|
+
const capturedSurfaces = [];
|
|
972
|
+
const allWarnings = [];
|
|
973
|
+
for (const surface of surfaces) {
|
|
974
|
+
const dimensions = resolveCaptureDimensions(surface, parsedOptions);
|
|
975
|
+
const captureSceneId = resolveListingCaptureSceneId(dimensions.width, dimensions.height);
|
|
976
|
+
const surfaceOutputPaths = buildSurfaceOutputPaths(outputPaths.outputDir, surface, useSurfacePrefix);
|
|
977
|
+
console.log(`[listing] Capturing ${surface} at ${dimensions.width}x${dimensions.height}.`);
|
|
978
|
+
const contextOptions = resolveListingCaptureBrowserContextOptions(surface, dimensions);
|
|
979
|
+
browserHandle = await launchListingBrowser(dimensions, frameUrlObject.origin, contextOptions);
|
|
980
|
+
try {
|
|
981
|
+
await browserHandle.page.goto(frameUrl, { waitUntil: 'domcontentloaded' });
|
|
982
|
+
await browserHandle.page.waitForTimeout(1000);
|
|
983
|
+
if (routeSegment === 'dev-preview') {
|
|
984
|
+
console.log(`[listing] Preparing preview scene ${captureSceneId}.`);
|
|
985
|
+
await prepareHostedListingScene(browserHandle.page, captureSceneId);
|
|
986
|
+
await browserHandle.page.waitForTimeout(1000);
|
|
987
|
+
}
|
|
988
|
+
if (shouldExportPreviewAudio) {
|
|
989
|
+
console.log('[listing] Starting in-app preview audio capture.');
|
|
990
|
+
await startHostedListingAudioCapture(browserHandle.page);
|
|
991
|
+
}
|
|
992
|
+
const measurement = await fitWindowToRequestedGameplay(browserHandle.page, dimensions.width, dimensions.height);
|
|
993
|
+
console.log(`[listing] Gameplay frame ${Math.round(measurement.iframeRect.width)}x${Math.round(measurement.iframeRect.height)} in window ${Math.round(measurement.outerWidth)}x${Math.round(measurement.outerHeight)}.`);
|
|
994
|
+
await browserHandle.page.waitForTimeout(750);
|
|
995
|
+
const recorderMetadata = await runListingRecorder(recorderPath, browserHandle.processId, parsedOptions.durationSeconds, surfaceOutputPaths.rawVideoPath, surfaceOutputPaths.metadataPath, shouldExportPreviewAudio ? false : parsedOptions.audio);
|
|
996
|
+
const exportedAudioPath = shouldExportPreviewAudio
|
|
997
|
+
? await stopHostedListingAudioCapture(browserHandle.page, outputPaths.outputDir)
|
|
998
|
+
: null;
|
|
999
|
+
const rawProbe = await probeMediaFile(surfaceOutputPaths.rawVideoPath);
|
|
1000
|
+
const rawVideoStream = extractVideoStream(rawProbe);
|
|
1001
|
+
const rawDurationSeconds = readDurationSeconds(rawProbe);
|
|
1002
|
+
const rawAudioTrackCount = countAudioStreams(rawProbe);
|
|
1003
|
+
const crop = computeRecordedCrop(measurement, rawVideoStream.width, rawVideoStream.height);
|
|
1004
|
+
console.log(`[listing] Cropping raw capture to gameplay at ${crop.width}x${crop.height}+${crop.x}+${crop.y}.`);
|
|
1005
|
+
await encodeListingVideo(surfaceOutputPaths.rawVideoPath, surfaceOutputPaths.finalVideoPath, surfaceOutputPaths.posterPath, crop, dimensions.width, dimensions.height, parsedOptions.fps, parsedOptions.posterAtSeconds);
|
|
1006
|
+
if (exportedAudioPath) {
|
|
1007
|
+
await muxListingAudio(surfaceOutputPaths.finalVideoPath, exportedAudioPath);
|
|
1008
|
+
}
|
|
1009
|
+
const finalProbe = await probeMediaFile(surfaceOutputPaths.finalVideoPath);
|
|
1010
|
+
const finalVideoStream = extractVideoStream(finalProbe);
|
|
1011
|
+
const finalDurationSeconds = readDurationSeconds(finalProbe);
|
|
1012
|
+
const finalAudioTrackCount = countAudioStreams(finalProbe);
|
|
1013
|
+
const finalFps = parseFrameRate(finalVideoStream.avg_frame_rate);
|
|
1014
|
+
ensureTolerance(finalVideoStream.width, dimensions.width, 0, 'final_width_mismatch');
|
|
1015
|
+
ensureTolerance(finalVideoStream.height, dimensions.height, 0, 'final_height_mismatch');
|
|
1016
|
+
ensureTolerance(finalDurationSeconds, parsedOptions.durationSeconds, 1.5, 'final_duration_mismatch');
|
|
1017
|
+
if (parsedOptions.audio && finalAudioTrackCount === 0) {
|
|
1018
|
+
throw new Error('audio_track_missing');
|
|
1019
|
+
}
|
|
1020
|
+
const warnings = createWarnings(finalProbe, parsedOptions.audio);
|
|
1021
|
+
for (const warning of warnings) {
|
|
1022
|
+
allWarnings.push(`${surface}: ${warning}`);
|
|
1023
|
+
}
|
|
1024
|
+
artifactHashes[(0, node_path_1.relative)(outputPaths.outputDir, surfaceOutputPaths.finalVideoPath) || 'listing.mp4'] = await computeFileSha256(surfaceOutputPaths.finalVideoPath);
|
|
1025
|
+
artifactHashes[(0, node_path_1.relative)(outputPaths.outputDir, surfaceOutputPaths.posterPath) || 'poster.png'] = await computeFileSha256(surfaceOutputPaths.posterPath);
|
|
1026
|
+
capturedSurfaces.push(surface);
|
|
1027
|
+
captures.push({
|
|
1028
|
+
surface,
|
|
1029
|
+
targetUrl: frameUrl,
|
|
1030
|
+
command: {
|
|
1031
|
+
durationSeconds: parsedOptions.durationSeconds,
|
|
1032
|
+
width: dimensions.width,
|
|
1033
|
+
height: dimensions.height,
|
|
1034
|
+
fps: parsedOptions.fps,
|
|
1035
|
+
posterAtSeconds: parsedOptions.posterAtSeconds,
|
|
1036
|
+
audio: parsedOptions.audio,
|
|
1037
|
+
keepRaw: parsedOptions.keepRaw,
|
|
1038
|
+
},
|
|
1039
|
+
measurement,
|
|
1040
|
+
recorder: recorderMetadata,
|
|
1041
|
+
crop,
|
|
1042
|
+
rawVideo: {
|
|
1043
|
+
path: parsedOptions.keepRaw ? surfaceOutputPaths.rawVideoPath : null,
|
|
1044
|
+
width: rawVideoStream.width,
|
|
1045
|
+
height: rawVideoStream.height,
|
|
1046
|
+
durationSeconds: rawDurationSeconds,
|
|
1047
|
+
audioTrackCount: rawAudioTrackCount,
|
|
1048
|
+
},
|
|
1049
|
+
finalVideo: {
|
|
1050
|
+
path: surfaceOutputPaths.finalVideoPath,
|
|
1051
|
+
width: finalVideoStream.width,
|
|
1052
|
+
height: finalVideoStream.height,
|
|
1053
|
+
durationSeconds: finalDurationSeconds,
|
|
1054
|
+
audioTrackCount: finalAudioTrackCount,
|
|
1055
|
+
fps: finalFps,
|
|
1056
|
+
},
|
|
1057
|
+
posterPath: surfaceOutputPaths.posterPath,
|
|
1058
|
+
warnings,
|
|
1059
|
+
});
|
|
1060
|
+
if (!parsedOptions.keepRaw) {
|
|
1061
|
+
await (0, promises_1.rm)(surfaceOutputPaths.rawVideoPath, { force: true });
|
|
1062
|
+
}
|
|
1063
|
+
console.log(`[listing] Saved ${surface} video to ${(0, node_path_1.relative)(process.cwd(), surfaceOutputPaths.finalVideoPath) || surfaceOutputPaths.finalVideoPath}`);
|
|
1064
|
+
console.log(`[listing] Saved ${surface} poster to ${(0, node_path_1.relative)(process.cwd(), surfaceOutputPaths.posterPath) || surfaceOutputPaths.posterPath}`);
|
|
1065
|
+
if (warnings.length > 0) {
|
|
1066
|
+
for (const warning of warnings) {
|
|
1067
|
+
console.warn(`[listing][warn][${surface}] ${warning}`);
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
finally {
|
|
1072
|
+
await browserHandle.close();
|
|
1073
|
+
browserHandle = null;
|
|
1074
|
+
}
|
|
858
1075
|
}
|
|
859
|
-
const warnings = createWarnings(finalProbe, parsedOptions.audio);
|
|
860
1076
|
const report = {
|
|
861
1077
|
appName,
|
|
862
|
-
targetUrl: frameUrl,
|
|
863
1078
|
outputDir: outputPaths.outputDir,
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
audio: parsedOptions.audio,
|
|
871
|
-
keepRaw: parsedOptions.keepRaw,
|
|
872
|
-
},
|
|
873
|
-
measurement,
|
|
874
|
-
recorder: recorderMetadata,
|
|
875
|
-
crop,
|
|
876
|
-
rawVideo: {
|
|
877
|
-
path: parsedOptions.keepRaw ? outputPaths.rawVideoPath : null,
|
|
878
|
-
width: rawVideoStream.width,
|
|
879
|
-
height: rawVideoStream.height,
|
|
880
|
-
durationSeconds: rawDurationSeconds,
|
|
881
|
-
audioTrackCount: rawAudioTrackCount,
|
|
1079
|
+
captures,
|
|
1080
|
+
warnings: allWarnings,
|
|
1081
|
+
binding: {
|
|
1082
|
+
contentHash: parsedOptions.contentHash,
|
|
1083
|
+
artifactHashes,
|
|
1084
|
+
capturedSurfaces,
|
|
882
1085
|
},
|
|
883
|
-
finalVideo: {
|
|
884
|
-
path: outputPaths.finalVideoPath,
|
|
885
|
-
width: finalVideoStream.width,
|
|
886
|
-
height: finalVideoStream.height,
|
|
887
|
-
durationSeconds: finalDurationSeconds,
|
|
888
|
-
audioTrackCount: finalAudioTrackCount,
|
|
889
|
-
fps: finalFps,
|
|
890
|
-
},
|
|
891
|
-
posterPath: outputPaths.posterPath,
|
|
892
|
-
warnings,
|
|
893
1086
|
};
|
|
894
1087
|
await (0, promises_1.writeFile)(outputPaths.reportPath, `${JSON.stringify(report, null, 2)}\n`, 'utf8');
|
|
895
|
-
if (!parsedOptions.keepRaw) {
|
|
896
|
-
await (0, promises_1.rm)(outputPaths.rawVideoPath, { force: true });
|
|
897
|
-
}
|
|
898
|
-
console.log(`[listing] Saved video to ${(0, node_path_1.relative)(process.cwd(), outputPaths.finalVideoPath) || outputPaths.finalVideoPath}`);
|
|
899
|
-
console.log(`[listing] Saved poster to ${(0, node_path_1.relative)(process.cwd(), outputPaths.posterPath) || outputPaths.posterPath}`);
|
|
900
1088
|
console.log(`[listing] Saved report to ${(0, node_path_1.relative)(process.cwd(), outputPaths.reportPath) || outputPaths.reportPath}`);
|
|
901
|
-
if (warnings.length > 0) {
|
|
902
|
-
for (const warning of warnings) {
|
|
903
|
-
console.warn(`[listing][warn] ${warning}`);
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
1089
|
}
|
|
907
1090
|
catch (error) {
|
|
908
1091
|
if (error instanceof http_1.CLIUnsupportedClientError) {
|
|
909
1092
|
return;
|
|
910
1093
|
}
|
|
911
1094
|
if (error instanceof types_1.UnsupportedClientError) {
|
|
912
|
-
(0, http_1.handleUnsupportedError)(error, '
|
|
1095
|
+
(0, http_1.handleUnsupportedError)(error, 'Game media capture');
|
|
913
1096
|
process.exitCode = 1;
|
|
914
1097
|
return;
|
|
915
1098
|
}
|
|
916
1099
|
const detail = formatCommandError(error instanceof Error ? error : new Error(String(error)));
|
|
917
|
-
(0, messages_1.printErrorWithHelp)(detail.message, detail.suggestions, { command: 'project capture
|
|
1100
|
+
(0, messages_1.printErrorWithHelp)(detail.message, detail.suggestions, { command: 'project capture' });
|
|
918
1101
|
process.exitCode = 1;
|
|
919
1102
|
}
|
|
920
1103
|
finally {
|