@playdrop/playdrop-cli 0.16.5 → 0.16.11
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/bin/playdrop +1 -2
- package/config/client-meta.json +4 -4
- package/dist/apiClient.d.ts +2 -2
- package/dist/apiClient.js +16 -20
- package/dist/appUrls.d.ts +2 -2
- package/dist/appUrls.js +31 -33
- package/dist/apps/build.d.ts +2 -2
- package/dist/apps/build.js +83 -83
- package/dist/apps/index.d.ts +7 -7
- package/dist/apps/index.js +8 -8
- package/dist/apps/loadCheck.d.ts +28 -16
- package/dist/apps/loadCheck.js +411 -279
- package/dist/apps/registration.d.ts +3 -3
- package/dist/apps/registration.js +1 -1
- package/dist/apps/staticHtml.d.ts +3 -3
- package/dist/apps/staticHtml.js +90 -91
- package/dist/apps/surfaceProfiles.d.ts +2 -2
- package/dist/apps/surfaceProfiles.js +6 -6
- package/dist/apps/upload.d.ts +5 -5
- package/dist/apps/upload.js +75 -81
- package/dist/apps/validate.d.ts +2 -2
- package/dist/apps/validate.js +18 -22
- package/dist/assetSpecs.d.ts +2 -2
- package/dist/assetSpecs.js +35 -35
- package/dist/assets/model-artifacts.d.ts +1 -1
- package/dist/assets/model-artifacts.js +81 -77
- package/dist/assets/model-worker.d.ts +1 -1
- package/dist/assets/model-worker.js +6 -6
- package/dist/browser.js +9 -9
- package/dist/catalogue-utils.d.ts +1 -1
- package/dist/catalogue-utils.js +26 -26
- package/dist/catalogue.d.ts +21 -16
- package/dist/catalogue.js +509 -413
- package/dist/clientInfo.d.ts +1 -1
- package/dist/clientInfo.js +28 -28
- package/dist/commandContext.d.ts +5 -5
- package/dist/commandContext.js +19 -23
- package/dist/commands/accounts.js +6 -6
- package/dist/commands/ads.js +16 -16
- package/dist/commands/agents.d.ts +2 -2
- package/dist/commands/agents.js +131 -127
- package/dist/commands/boosts.js +41 -31
- package/dist/commands/browse.d.ts +2 -2
- package/dist/commands/browse.js +168 -128
- package/dist/commands/build.js +9 -9
- package/dist/commands/captureListing.d.ts +8 -8
- package/dist/commands/captureListing.js +292 -332
- package/dist/commands/changelog.js +1 -1
- package/dist/commands/chat.js +51 -53
- package/dist/commands/check.d.ts +1 -1
- package/dist/commands/check.js +44 -44
- package/dist/commands/clients.d.ts +3 -3
- package/dist/commands/clients.js +67 -64
- package/dist/commands/comments.js +36 -29
- package/dist/commands/create.js +305 -312
- package/dist/commands/createRemixContent.d.ts +1 -1
- package/dist/commands/createRemixContent.js +65 -60
- package/dist/commands/creations.js +205 -135
- package/dist/commands/credits.js +37 -27
- package/dist/commands/detail.js +40 -34
- package/dist/commands/dev.js +103 -126
- package/dist/commands/devBrowser.js +74 -65
- package/dist/commands/devRuntimeAssets.d.ts +4 -4
- package/dist/commands/devRuntimeAssets.js +38 -40
- package/dist/commands/devServer.d.ts +4 -4
- package/dist/commands/devServer.js +183 -174
- package/dist/commands/devShared.d.ts +3 -3
- package/dist/commands/devShared.js +31 -31
- package/dist/commands/doctor.d.ts +5 -5
- package/dist/commands/doctor.js +63 -57
- package/dist/commands/documentation.js +42 -32
- package/dist/commands/feedback.js +59 -42
- package/dist/commands/format.js +9 -9
- package/dist/commands/generation.d.ts +2 -2
- package/dist/commands/generation.js +344 -326
- package/dist/commands/init.d.ts +4 -4
- package/dist/commands/init.js +64 -61
- package/dist/commands/login.d.ts +2 -2
- package/dist/commands/login.js +42 -62
- package/dist/commands/logout.js +4 -4
- package/dist/commands/marketing.d.ts +8 -8
- package/dist/commands/marketing.js +246 -297
- package/dist/commands/notes.js +19 -19
- package/dist/commands/notifications.js +34 -22
- package/dist/commands/npmProcess.js +3 -3
- package/dist/commands/ownedRuntimeImageValidation.d.ts +1 -1
- package/dist/commands/ownedRuntimeImageValidation.js +19 -17
- package/dist/commands/play.js +12 -21
- package/dist/commands/publicPages.js +8 -4
- package/dist/commands/review.js +123 -110
- package/dist/commands/search.js +107 -88
- package/dist/commands/tags.js +10 -12
- package/dist/commands/taskCaptureSession.d.ts +3 -3
- package/dist/commands/taskCaptureSession.js +3 -3
- package/dist/commands/tweaks.js +25 -23
- package/dist/commands/upgrade.js +40 -31
- package/dist/commands/upload-content.d.ts +5 -5
- package/dist/commands/upload-content.js +130 -122
- package/dist/commands/upload-graph.d.ts +1 -1
- package/dist/commands/upload-graph.js +7 -11
- package/dist/commands/upload.d.ts +7 -7
- package/dist/commands/upload.js +196 -217
- package/dist/commands/validate.js +33 -32
- package/dist/commands/versionsBrowse.js +61 -51
- package/dist/commands/whoami.js +11 -14
- package/dist/commands/worker/archive-staging.d.ts +9 -0
- package/dist/commands/worker/archive-staging.js +257 -0
- package/dist/commands/worker/instrument-evidence.d.ts +1 -1
- package/dist/commands/worker/instrument-evidence.js +41 -43
- package/dist/commands/worker/provider-telemetry.d.ts +2 -1
- package/dist/commands/worker/provider-telemetry.js +181 -64
- package/dist/commands/worker/runtime.d.ts +18 -3
- package/dist/commands/worker/runtime.js +291 -239
- package/dist/commands/worker.d.ts +83 -22
- package/dist/commands/worker.js +1980 -1411
- package/dist/commands/workspaces.d.ts +2 -2
- package/dist/commands/workspaces.js +57 -57
- package/dist/config.js +23 -20
- package/dist/devAuth.d.ts +3 -3
- package/dist/devAuth.js +20 -17
- package/dist/environment.d.ts +1 -1
- package/dist/environment.js +34 -34
- package/dist/errors.d.ts +1 -1
- package/dist/errors.js +7 -7
- package/dist/externalAssetPackValidation.d.ts +1 -1
- package/dist/externalAssetPackValidation.js +9 -9
- package/dist/http.d.ts +1 -1
- package/dist/http.js +9 -9
- package/dist/index.js +657 -664
- package/dist/listingCaptureLease.js +23 -23
- package/dist/listingPreflight.d.ts +2 -2
- package/dist/listingPreflight.js +33 -33
- package/dist/messages.js +6 -14
- package/dist/output.js +9 -9
- package/dist/playwright.d.ts +1 -1
- package/dist/playwright.js +35 -40
- package/dist/proxyFetch.js +30 -27
- package/dist/recorderRelease.js +8 -8
- package/dist/refs.d.ts +5 -5
- package/dist/refs.js +40 -37
- package/dist/sessionCookie.js +15 -21
- package/dist/shellProbe.js +5 -5
- package/dist/taskSelection.d.ts +3 -3
- package/dist/taskSelection.js +67 -62
- package/dist/taskUtils.d.ts +1 -1
- package/dist/taskUtils.js +4 -4
- package/dist/tweakStaleness.d.ts +3 -3
- package/dist/tweakStaleness.js +8 -8
- package/dist/uploadLog.d.ts +3 -3
- package/dist/uploadLog.js +51 -51
- package/dist/versionCompare.js +3 -3
- package/dist/workerAppProject.d.ts +1 -1
- package/dist/workerAppProject.js +10 -17
- package/dist/workspaceAuth.d.ts +1 -1
- package/dist/workspaceAuth.js +28 -22
- package/node_modules/@playdrop/ai-client/dist/index.d.ts +35 -35
- package/node_modules/@playdrop/ai-client/dist/index.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/client.d.ts +34 -33
- package/node_modules/@playdrop/api-client/dist/client.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/core/errors.d.ts +1 -1
- package/node_modules/@playdrop/api-client/dist/core/errors.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/core/request.d.ts +2 -2
- package/node_modules/@playdrop/api-client/dist/core/request.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts +2 -2
- package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/admin.js +4 -1
- package/node_modules/@playdrop/api-client/dist/domains/agent-tasks.d.ts +4 -3
- package/node_modules/@playdrop/api-client/dist/domains/agent-tasks.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/agent-tasks.js +12 -0
- package/node_modules/@playdrop/api-client/dist/domains/ai.d.ts +2 -2
- package/node_modules/@playdrop/api-client/dist/domains/ai.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts +4 -4
- package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/asset-packs.d.ts +4 -4
- package/node_modules/@playdrop/api-client/dist/domains/asset-packs.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts +4 -4
- 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 +2 -2
- package/node_modules/@playdrop/api-client/dist/domains/auth.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/chat.d.ts +2 -2
- package/node_modules/@playdrop/api-client/dist/domains/chat.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/comments.d.ts +3 -3
- package/node_modules/@playdrop/api-client/dist/domains/comments.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/free-credits.d.ts +2 -2
- package/node_modules/@playdrop/api-client/dist/domains/free-credits.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/me.d.ts +2 -2
- package/node_modules/@playdrop/api-client/dist/domains/me.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts +2 -2
- package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/player-meta.d.ts +2 -2
- package/node_modules/@playdrop/api-client/dist/domains/player-meta.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/playtrade.d.ts +3 -3
- package/node_modules/@playdrop/api-client/dist/domains/playtrade.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/playtrade.js +2 -2
- package/node_modules/@playdrop/api-client/dist/domains/search.d.ts +2 -2
- package/node_modules/@playdrop/api-client/dist/domains/search.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/tags.d.ts +2 -2
- package/node_modules/@playdrop/api-client/dist/domains/tags.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/index.d.ts +19 -18
- package/node_modules/@playdrop/api-client/dist/index.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/index.js +7 -2
- package/node_modules/@playdrop/boxel-core/dist/src/entity-cleaner.d.ts +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/entity-cleaner.js +47 -47
- package/node_modules/@playdrop/boxel-core/dist/src/entity-cleaner.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/entity-utils.d.ts +3 -3
- package/node_modules/@playdrop/boxel-core/dist/src/entity-utils.js +32 -47
- package/node_modules/@playdrop/boxel-core/dist/src/entity-utils.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/format-utils.js +16 -18
- package/node_modules/@playdrop/boxel-core/dist/src/format-utils.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/geometry.d.ts +2 -2
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/index.d.ts +4 -4
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/common.d.ts +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/index.d.ts +3 -3
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/textured-builder.d.ts +2 -2
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/textured-builder.js +44 -45
- 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/types.d.ts +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/voxel-builder.d.ts +2 -2
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/voxel-builder.js +40 -44
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/voxel-builder.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/builder.d.ts +2 -2
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/builder.js +30 -34
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/builder.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/index.d.ts +4 -4
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/index.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/scanner.d.ts +3 -3
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/scanner.js +23 -20
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/scanner.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/textures/artifacts.d.ts +3 -3
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/textures/artifacts.js +3 -3
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/textures/face-map.d.ts +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/textures/face-map.js +21 -21
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/textures/face-map.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/templates/index.d.ts +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/templates/index.js +2 -2
- package/node_modules/@playdrop/boxel-core/dist/src/index.d.ts +19 -19
- package/node_modules/@playdrop/boxel-core/dist/src/materials.d.ts +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/materials.js +12 -16
- package/node_modules/@playdrop/boxel-core/dist/src/materials.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/palette_tools.d.ts +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/palette_tools.js +38 -41
- package/node_modules/@playdrop/boxel-core/dist/src/palette_tools.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/serialization.d.ts +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/serialization.js +54 -56
- package/node_modules/@playdrop/boxel-core/dist/src/serialization.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/textures.d.ts +3 -3
- package/node_modules/@playdrop/boxel-core/dist/src/textures.js +39 -45
- package/node_modules/@playdrop/boxel-core/dist/src/textures.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/infer-face.d.ts +2 -2
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/infer-face.js +39 -43
- 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.d.ts +3 -3
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/layer-mode.js +35 -35
- 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/merge-overlay.d.ts +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/merge-overlay.js +18 -18
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/merge-overlay.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/slice.d.ts +3 -3
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/slice.js +50 -48
- 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.d.ts +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/upscale.js +22 -26
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/upscale.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/voxels/slice.d.ts +2 -2
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/voxels/slice.js +8 -8
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/voxels/slice.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/voxels/textured-to-voxel.d.ts +2 -2
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/voxels/textured-to-voxel.js +23 -28
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/voxels/textured-to-voxel.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/types.d.ts +8 -8
- package/node_modules/@playdrop/boxel-core/dist/src/validation.d.ts +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/validation.js +93 -95
- package/node_modules/@playdrop/boxel-core/dist/src/validation.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/voxels/greedy-mesher.d.ts +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/voxels/greedy-mesher.js +2 -2
- package/node_modules/@playdrop/boxel-core/dist/src/voxels/greedy-mesher.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/voxels.d.ts +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/voxels.js +2 -7
- package/node_modules/@playdrop/boxel-core/dist/src/voxels.js.map +1 -1
- package/node_modules/@playdrop/boxel-three/dist/src/animations.d.ts +3 -3
- package/node_modules/@playdrop/boxel-three/dist/src/builders.d.ts +4 -4
- package/node_modules/@playdrop/boxel-three/dist/src/context.d.ts +3 -3
- package/node_modules/@playdrop/boxel-three/dist/src/exporters/glb.d.ts +6 -6
- package/node_modules/@playdrop/boxel-three/dist/src/exporters/glb.js +9 -1
- package/node_modules/@playdrop/boxel-three/dist/src/exporters/image.d.ts +3 -3
- package/node_modules/@playdrop/boxel-three/dist/src/index.d.ts +17 -17
- package/node_modules/@playdrop/boxel-three/dist/src/instantiate.d.ts +9 -9
- package/node_modules/@playdrop/boxel-three/dist/src/nodes.d.ts +3 -3
- package/node_modules/@playdrop/boxel-three/dist/src/nodes.js +1 -1
- package/node_modules/@playdrop/boxel-three/dist/src/overlays.d.ts +2 -2
- package/node_modules/@playdrop/boxel-three/dist/src/primitives.d.ts +1 -1
- package/node_modules/@playdrop/boxel-three/dist/src/scene.d.ts +1 -1
- package/node_modules/@playdrop/boxel-three/dist/src/skinned-mesh.d.ts +2 -2
- package/node_modules/@playdrop/boxel-three/dist/src/texture-atlas.d.ts +1 -1
- package/node_modules/@playdrop/boxel-three/dist/src/textures.d.ts +3 -3
- package/node_modules/@playdrop/boxel-three/dist/src/types.d.ts +4 -4
- package/node_modules/@playdrop/boxel-three/dist/src/voxels/faces.d.ts +2 -2
- package/node_modules/@playdrop/boxel-three/dist/src/voxels/mesher.d.ts +1 -1
- package/node_modules/@playdrop/boxel-three/dist/test/fixtures/render-worker.d.ts +1 -1
- package/node_modules/@playdrop/config/client-meta.json +4 -4
- package/node_modules/@playdrop/config/dist/src/client-meta.d.ts +1 -1
- package/node_modules/@playdrop/config/dist/src/client-meta.d.ts.map +1 -1
- package/node_modules/@playdrop/config/dist/src/client-meta.js +10 -10
- package/node_modules/@playdrop/config/dist/src/constants.js +10 -10
- package/node_modules/@playdrop/config/dist/src/creator-plans.d.ts +1 -1
- package/node_modules/@playdrop/config/dist/src/creator-plans.d.ts.map +1 -1
- package/node_modules/@playdrop/config/dist/src/creator-plans.js +27 -28
- package/node_modules/@playdrop/config/dist/src/deployment.d.ts +4 -4
- package/node_modules/@playdrop/config/dist/src/deployment.d.ts.map +1 -1
- package/node_modules/@playdrop/config/dist/src/deployment.js +42 -41
- package/node_modules/@playdrop/config/dist/src/game-arena.js +12 -12
- package/node_modules/@playdrop/config/dist/src/index.d.ts +9 -9
- package/node_modules/@playdrop/config/dist/src/index.d.ts.map +1 -1
- package/node_modules/@playdrop/config/dist/src/index.js +24 -25
- package/node_modules/@playdrop/config/dist/src/public-deployment.d.ts +4 -4
- package/node_modules/@playdrop/config/dist/src/public-deployment.d.ts.map +1 -1
- package/node_modules/@playdrop/config/dist/src/public-deployment.js +41 -39
- package/node_modules/@playdrop/config/dist/src/runtime-client-meta.js +1 -3
- 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 +23 -25
- package/node_modules/@playdrop/config/dist/src/server/fastify.d.ts +5 -5
- package/node_modules/@playdrop/config/dist/src/server/fastify.d.ts.map +1 -1
- package/node_modules/@playdrop/config/dist/src/server/fastify.js +44 -46
- package/node_modules/@playdrop/config/dist/src/server/logging.d.ts +1 -1
- package/node_modules/@playdrop/config/dist/src/server/logging.d.ts.map +1 -1
- package/node_modules/@playdrop/config/dist/src/server/logging.js +6 -8
- package/node_modules/@playdrop/config/dist/src/types.d.ts +2 -2
- package/node_modules/@playdrop/config/dist/test/creator-plans.test.js +12 -12
- package/node_modules/@playdrop/config/dist/test/deployment.test.js +59 -59
- package/node_modules/@playdrop/config/dist/test/fastify.test.js +4 -4
- package/node_modules/@playdrop/config/dist/test/game-arena.test.js +8 -8
- package/node_modules/@playdrop/config/dist/test/logging.test.js +6 -6
- package/node_modules/@playdrop/config/dist/test/validateClientEnvironment.test.js +57 -57
- package/node_modules/@playdrop/config/dist/tsconfig.tsbuildinfo +1 -1
- package/node_modules/@playdrop/types/dist/agent-task-playtest.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/agent-task-terminal.d.ts +2 -2
- package/node_modules/@playdrop/types/dist/agent-task-terminal.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/api.d.ts +314 -250
- package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/api.js +40 -59
- package/node_modules/@playdrop/types/dist/app-capability-filters.d.ts +2 -2
- package/node_modules/@playdrop/types/dist/app-playtest-tape.d.ts +6 -6
- package/node_modules/@playdrop/types/dist/app-playtest-tape.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/app-review.d.ts +2 -2
- package/node_modules/@playdrop/types/dist/app-review.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/app-review.js +1 -6
- package/node_modules/@playdrop/types/dist/app.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/asset-pack.d.ts +5 -5
- package/node_modules/@playdrop/types/dist/asset-spec.d.ts +4 -4
- package/node_modules/@playdrop/types/dist/asset.d.ts +11 -11
- package/node_modules/@playdrop/types/dist/attachment-policy.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/chat.d.ts +3 -3
- package/node_modules/@playdrop/types/dist/creator-public-image.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/graph.d.ts +4 -4
- package/node_modules/@playdrop/types/dist/index.d.ts +25 -25
- package/node_modules/@playdrop/types/dist/instrument-evidence.d.ts +2 -2
- package/node_modules/@playdrop/types/dist/instrument-evidence.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/instrument-evidence.js +2 -11
- package/node_modules/@playdrop/types/dist/locale.d.ts +2 -2
- package/node_modules/@playdrop/types/dist/locale.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/locale.js +2 -17
- package/node_modules/@playdrop/types/dist/owned-assets.d.ts +2 -2
- package/node_modules/@playdrop/types/dist/owned-assets.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/player-meta.d.ts +3 -3
- package/node_modules/@playdrop/types/dist/player-meta.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/playtest-notes.d.ts +11 -11
- package/node_modules/@playdrop/types/dist/playtest-notes.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/playtrade.d.ts +8 -5
- package/node_modules/@playdrop/types/dist/playtrade.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/realtime.d.ts +1 -1
- package/node_modules/@playdrop/types/dist/tweaks.d.ts +4 -4
- package/node_modules/@playdrop/types/dist/tweaks.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/tweaks.js +12 -2
- package/node_modules/@playdrop/types/dist/version.d.ts +14 -14
- package/node_modules/@playdrop/types/dist/version.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/version.js +4 -1
- package/node_modules/@playdrop/vox-three/dist/src/index.d.ts +1 -1
- package/node_modules/@playdrop/vox-three/dist/src/vox.d.ts +2 -2
- package/node_modules/@playdrop/vox-three/dist/src/vox.js +20 -18
- package/node_modules/@playdrop/vox-three/dist/src/vox.js.map +1 -1
- package/node_modules/@playdrop/vox-three/dist/test/vox.test.js +11 -14
- package/node_modules/@playdrop/vox-three/dist/test/vox.test.js.map +1 -1
- package/package.json +1 -1
|
@@ -95,7 +95,7 @@ const MAX_FPS = 120;
|
|
|
95
95
|
const MIN_SUPPORTED_MACOS_MAJOR = 26;
|
|
96
96
|
const MIN_SUPPORTED_MACOS_MINOR = 4;
|
|
97
97
|
const LISTING_CAPTURE_HOOK_TIMEOUT_MS = 60000;
|
|
98
|
-
const LISTING_CAPTURE_MOBILE_USER_AGENT =
|
|
98
|
+
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";
|
|
99
99
|
const SURFACE_CAPTURE_DIMENSIONS = {
|
|
100
100
|
DESKTOP: { width: 1280, height: 720 },
|
|
101
101
|
MOBILE_LANDSCAPE: { width: 844, height: 390 },
|
|
@@ -107,15 +107,15 @@ const MIN_LISTING_VISIBLE_PIXEL_RATIO = 0.005;
|
|
|
107
107
|
const MIN_LISTING_MOTION_MEAN_DELTA = 0.002;
|
|
108
108
|
const MIN_LISTING_MOTION_CHANGED_PIXEL_RATIO = 0.01;
|
|
109
109
|
const SURFACE_OUTPUT_SLUG = {
|
|
110
|
-
DESKTOP:
|
|
111
|
-
MOBILE_LANDSCAPE:
|
|
112
|
-
MOBILE_PORTRAIT:
|
|
110
|
+
DESKTOP: "desktop",
|
|
111
|
+
MOBILE_LANDSCAPE: "mobile-landscape",
|
|
112
|
+
MOBILE_PORTRAIT: "mobile-portrait",
|
|
113
113
|
};
|
|
114
114
|
function resolveListingCaptureRouteSegment(previewable) {
|
|
115
115
|
if (!previewable) {
|
|
116
|
-
throw new Error(
|
|
116
|
+
throw new Error("listing_preview_support_missing");
|
|
117
117
|
}
|
|
118
|
-
return
|
|
118
|
+
return "dev-preview";
|
|
119
119
|
}
|
|
120
120
|
function resolveCaptureListingDevRouterPort(env = process.env) {
|
|
121
121
|
return (0, dev_1.resolveCliDevRouterPort)(env);
|
|
@@ -125,23 +125,23 @@ function resolveListingCaptureLocalDevPort(devRouterPort) {
|
|
|
125
125
|
}
|
|
126
126
|
function buildListingCaptureFrameUrl({ webBase, currentUsername, appTypeSlug, appName, routeSegment, localDevPort, captureSession, }) {
|
|
127
127
|
const frameUrlObject = new URL(`${(0, appUrls_1.normalizePlaydropWebBase)(webBase)}/creators/${encodeURIComponent(currentUsername)}/apps/${appTypeSlug}/${encodeURIComponent(appName)}/${routeSegment}`);
|
|
128
|
-
frameUrlObject.searchParams.set(
|
|
129
|
-
frameUrlObject.searchParams.set(
|
|
130
|
-
frameUrlObject.searchParams.set(
|
|
128
|
+
frameUrlObject.searchParams.set("launchCheck", "1");
|
|
129
|
+
frameUrlObject.searchParams.set("captureShell", "listing");
|
|
130
|
+
frameUrlObject.searchParams.set("mobileFrameHost", "admin");
|
|
131
131
|
if (localDevPort !== null) {
|
|
132
|
-
frameUrlObject.searchParams.set(
|
|
132
|
+
frameUrlObject.searchParams.set("localDevPort", String(localDevPort));
|
|
133
133
|
}
|
|
134
134
|
(0, appUrls_1.appendTaskCaptureSession)(frameUrlObject, captureSession);
|
|
135
135
|
return frameUrlObject.toString();
|
|
136
136
|
}
|
|
137
137
|
function resolveListingCaptureSceneId(width, height) {
|
|
138
|
-
return height > width ?
|
|
138
|
+
return height > width ? "listing-portrait" : "listing-landscape";
|
|
139
139
|
}
|
|
140
140
|
function parsePositiveNumber(raw, fallback, fieldName, { minimum = 0, maximum = Number.POSITIVE_INFINITY, integer = false, } = {}) {
|
|
141
141
|
if (raw === undefined) {
|
|
142
142
|
return fallback;
|
|
143
143
|
}
|
|
144
|
-
const parsed = typeof raw ===
|
|
144
|
+
const parsed = typeof raw === "number" ? raw : Number.parseFloat(String(raw));
|
|
145
145
|
if (!Number.isFinite(parsed)) {
|
|
146
146
|
throw new Error(`${fieldName}_invalid`);
|
|
147
147
|
}
|
|
@@ -154,27 +154,27 @@ function parsePositiveNumber(raw, fallback, fieldName, { minimum = 0, maximum =
|
|
|
154
154
|
return parsed;
|
|
155
155
|
}
|
|
156
156
|
function parseCaptureListingOptions(targetArg, options = {}) {
|
|
157
|
-
const durationSeconds = parsePositiveNumber(options.duration, DEFAULT_DURATION_SECONDS,
|
|
157
|
+
const durationSeconds = parsePositiveNumber(options.duration, DEFAULT_DURATION_SECONDS, "duration", {
|
|
158
158
|
minimum: MIN_CAPTURE_SECONDS,
|
|
159
159
|
maximum: MAX_CAPTURE_SECONDS,
|
|
160
160
|
});
|
|
161
161
|
const explicitDimensions = options.width !== undefined || options.height !== undefined;
|
|
162
|
-
const width = parsePositiveNumber(options.width, DEFAULT_WIDTH,
|
|
162
|
+
const width = parsePositiveNumber(options.width, DEFAULT_WIDTH, "width", {
|
|
163
163
|
minimum: MIN_DIMENSION,
|
|
164
164
|
maximum: MAX_DIMENSION,
|
|
165
165
|
integer: true,
|
|
166
166
|
});
|
|
167
|
-
const height = parsePositiveNumber(options.height, DEFAULT_HEIGHT,
|
|
167
|
+
const height = parsePositiveNumber(options.height, DEFAULT_HEIGHT, "height", {
|
|
168
168
|
minimum: MIN_DIMENSION,
|
|
169
169
|
maximum: MAX_DIMENSION,
|
|
170
170
|
integer: true,
|
|
171
171
|
});
|
|
172
|
-
const fps = parsePositiveNumber(options.fps, DEFAULT_FPS,
|
|
172
|
+
const fps = parsePositiveNumber(options.fps, DEFAULT_FPS, "fps", {
|
|
173
173
|
minimum: 1,
|
|
174
174
|
maximum: MAX_FPS,
|
|
175
175
|
integer: true,
|
|
176
176
|
});
|
|
177
|
-
const posterAtSeconds = parsePositiveNumber(options.posterAt, DEFAULT_POSTER_AT_SECONDS,
|
|
177
|
+
const posterAtSeconds = parsePositiveNumber(options.posterAt, DEFAULT_POSTER_AT_SECONDS, "poster_at", {
|
|
178
178
|
minimum: 0,
|
|
179
179
|
maximum: durationSeconds,
|
|
180
180
|
});
|
|
@@ -198,36 +198,34 @@ function parseCaptureListingOptions(targetArg, options = {}) {
|
|
|
198
198
|
function resolveCaptureListingExplicitOutputDir(outputDirInput, cwdResolvedOutputDir, projectDir) {
|
|
199
199
|
if (!outputDirInput || !cwdResolvedOutputDir)
|
|
200
200
|
return null;
|
|
201
|
-
return (0, node_path_1.isAbsolute)(outputDirInput)
|
|
202
|
-
? cwdResolvedOutputDir
|
|
203
|
-
: (0, node_path_1.resolve)(projectDir, outputDirInput);
|
|
201
|
+
return (0, node_path_1.isAbsolute)(outputDirInput) ? cwdResolvedOutputDir : (0, node_path_1.resolve)(projectDir, outputDirInput);
|
|
204
202
|
}
|
|
205
203
|
function readMacOsVersion() {
|
|
206
|
-
const result = (0, node_child_process_1.spawnSync)(
|
|
207
|
-
encoding:
|
|
204
|
+
const result = (0, node_child_process_1.spawnSync)("sw_vers", ["-productVersion"], {
|
|
205
|
+
encoding: "utf8",
|
|
208
206
|
});
|
|
209
207
|
if (result.status !== 0) {
|
|
210
|
-
throw new Error(
|
|
208
|
+
throw new Error("macos_version_probe_failed");
|
|
211
209
|
}
|
|
212
210
|
return result.stdout.trim();
|
|
213
211
|
}
|
|
214
|
-
function assertSupportedListingEnvironment(platform = process.platform, macosVersion = platform ===
|
|
215
|
-
if (platform !==
|
|
216
|
-
throw new Error(
|
|
217
|
-
}
|
|
218
|
-
const [majorRaw, minorRaw] = macosVersion.split(
|
|
219
|
-
const major = Number.parseInt(majorRaw ??
|
|
220
|
-
const minor = Number.parseInt(minorRaw ??
|
|
221
|
-
if (!Number.isInteger(major)
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
212
|
+
function assertSupportedListingEnvironment(platform = process.platform, macosVersion = platform === "darwin" ? readMacOsVersion() : "") {
|
|
213
|
+
if (platform !== "darwin") {
|
|
214
|
+
throw new Error("unsupported_platform");
|
|
215
|
+
}
|
|
216
|
+
const [majorRaw, minorRaw] = macosVersion.split(".");
|
|
217
|
+
const major = Number.parseInt(majorRaw ?? "", 10);
|
|
218
|
+
const minor = Number.parseInt(minorRaw ?? "", 10);
|
|
219
|
+
if (!Number.isInteger(major) ||
|
|
220
|
+
!Number.isInteger(minor) ||
|
|
221
|
+
major !== MIN_SUPPORTED_MACOS_MAJOR ||
|
|
222
|
+
minor < MIN_SUPPORTED_MACOS_MINOR) {
|
|
225
223
|
throw new Error(`unsupported_macos_version:${macosVersion}`);
|
|
226
224
|
}
|
|
227
225
|
}
|
|
228
226
|
function commandExists(command) {
|
|
229
|
-
const result = (0, node_child_process_1.spawnSync)(
|
|
230
|
-
encoding:
|
|
227
|
+
const result = (0, node_child_process_1.spawnSync)("which", [command], {
|
|
228
|
+
encoding: "utf8",
|
|
231
229
|
});
|
|
232
230
|
return result.status === 0 && result.stdout.trim().length > 0;
|
|
233
231
|
}
|
|
@@ -239,20 +237,20 @@ function resolveListingRecorderPath(workerHome) {
|
|
|
239
237
|
}
|
|
240
238
|
function buildTimestampFolder(date = new Date()) {
|
|
241
239
|
const iso = date.toISOString();
|
|
242
|
-
return iso.replace(/[:]/g,
|
|
240
|
+
return iso.replace(/[:]/g, "-").replace(/\.\d{3}Z$/, "Z");
|
|
243
241
|
}
|
|
244
242
|
async function ensureOutputPaths(appName, explicitOutputDir) {
|
|
245
243
|
const outputDir = explicitOutputDir
|
|
246
244
|
? explicitOutputDir
|
|
247
|
-
: (0, node_path_1.resolve)(process.cwd(),
|
|
245
|
+
: (0, node_path_1.resolve)(process.cwd(), "output", "listing-capture", appName, buildTimestampFolder());
|
|
248
246
|
await (0, promises_1.mkdir)(outputDir, { recursive: true });
|
|
249
247
|
return {
|
|
250
248
|
outputDir,
|
|
251
|
-
reportPath: (0, node_path_1.join)(outputDir,
|
|
249
|
+
reportPath: (0, node_path_1.join)(outputDir, "capture-report.json"),
|
|
252
250
|
};
|
|
253
251
|
}
|
|
254
252
|
function buildSurfaceOutputPaths(outputDir, surface, useSurfacePrefix) {
|
|
255
|
-
const prefix = useSurfacePrefix ? `${SURFACE_OUTPUT_SLUG[surface]}-` :
|
|
253
|
+
const prefix = useSurfacePrefix ? `${SURFACE_OUTPUT_SLUG[surface]}-` : "";
|
|
256
254
|
return {
|
|
257
255
|
rawVideoPath: (0, node_path_1.join)(outputDir, `${prefix}listing-raw.mp4`),
|
|
258
256
|
metadataPath: (0, node_path_1.join)(outputDir, `${prefix}listing-raw.json`),
|
|
@@ -261,9 +259,7 @@ function buildSurfaceOutputPaths(outputDir, surface, useSurfacePrefix) {
|
|
|
261
259
|
};
|
|
262
260
|
}
|
|
263
261
|
function resolveWorkerCaptureOutputDir(projectDir, isWorkerGameTask) {
|
|
264
|
-
return isWorkerGameTask
|
|
265
|
-
? (0, node_path_1.join)(projectDir, 'assets', 'marketing', 'playdrop', 'capture')
|
|
266
|
-
: null;
|
|
262
|
+
return isWorkerGameTask ? (0, node_path_1.join)(projectDir, "assets", "marketing", "playdrop", "capture") : null;
|
|
267
263
|
}
|
|
268
264
|
function resolveCaptureDimensions(surface, parsedOptions) {
|
|
269
265
|
if (parsedOptions.explicitDimensions) {
|
|
@@ -275,7 +271,7 @@ function resolveCaptureDimensions(surface, parsedOptions) {
|
|
|
275
271
|
return SURFACE_CAPTURE_DIMENSIONS[surface];
|
|
276
272
|
}
|
|
277
273
|
function resolveListingCaptureBrowserContextOptions(surface, dimensions) {
|
|
278
|
-
if (surface ===
|
|
274
|
+
if (surface === "DESKTOP") {
|
|
279
275
|
return { viewport: null };
|
|
280
276
|
}
|
|
281
277
|
return {
|
|
@@ -291,26 +287,26 @@ function resolveListingCaptureBrowserContextOptions(surface, dimensions) {
|
|
|
291
287
|
}
|
|
292
288
|
async function grantListingBrowserLocalNetworkAccess(context, origin) {
|
|
293
289
|
const grantPermissions = context.grantPermissions;
|
|
294
|
-
if (typeof grantPermissions !==
|
|
290
|
+
if (typeof grantPermissions !== "function") {
|
|
295
291
|
return;
|
|
296
292
|
}
|
|
297
|
-
await grantPermissions.call(context, [
|
|
293
|
+
await grantPermissions.call(context, ["local-network-access"], { origin });
|
|
298
294
|
}
|
|
299
295
|
async function launchListingBrowser(initialViewport, automationOrigin, contextOptions) {
|
|
300
|
-
const playwright = await Promise.resolve().then(() => __importStar(require(
|
|
296
|
+
const playwright = await Promise.resolve().then(() => __importStar(require("playwright-core")));
|
|
301
297
|
const args = [
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
298
|
+
"--no-sandbox",
|
|
299
|
+
"--disable-web-security",
|
|
300
|
+
"--app=about:blank",
|
|
305
301
|
`--window-size=${initialViewport.width},${initialViewport.height}`,
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
302
|
+
"--autoplay-policy=no-user-gesture-required",
|
|
303
|
+
"--disable-extensions",
|
|
304
|
+
"--disable-background-networking",
|
|
305
|
+
"--disable-component-update",
|
|
306
|
+
"--disable-sync",
|
|
307
|
+
"--metrics-recording-only",
|
|
308
|
+
"--no-first-run",
|
|
309
|
+
"--no-default-browser-check",
|
|
314
310
|
];
|
|
315
311
|
try {
|
|
316
312
|
const server = await playwright.chromium.launchServer({
|
|
@@ -320,7 +316,7 @@ async function launchListingBrowser(initialViewport, automationOrigin, contextOp
|
|
|
320
316
|
const processId = server.process()?.pid ?? 0;
|
|
321
317
|
if (processId <= 0) {
|
|
322
318
|
await server.close();
|
|
323
|
-
throw new Error(
|
|
319
|
+
throw new Error("browser_process_missing");
|
|
324
320
|
}
|
|
325
321
|
const browser = await playwright.chromium.connect(server.wsEndpoint());
|
|
326
322
|
const context = await browser.newContext(contextOptions);
|
|
@@ -355,23 +351,23 @@ async function launchListingBrowser(initialViewport, automationOrigin, contextOp
|
|
|
355
351
|
};
|
|
356
352
|
}
|
|
357
353
|
catch (error) {
|
|
358
|
-
throw (0, playwright_1.createPlaywrightLaunchError)(
|
|
354
|
+
throw (0, playwright_1.createPlaywrightLaunchError)("playdrop", error);
|
|
359
355
|
}
|
|
360
356
|
}
|
|
361
357
|
async function waitForHostedGameMeasurement(page, timeoutMs) {
|
|
362
358
|
const startedAt = Date.now();
|
|
363
|
-
let lastSignature =
|
|
359
|
+
let lastSignature = "";
|
|
364
360
|
let stableHits = 0;
|
|
365
361
|
while (Date.now() - startedAt < timeoutMs) {
|
|
366
362
|
try {
|
|
367
363
|
const measurement = await page.evaluate((selector) => {
|
|
368
364
|
const frame = document.querySelector(selector);
|
|
369
365
|
if (!(frame instanceof HTMLIFrameElement)) {
|
|
370
|
-
throw new Error(
|
|
366
|
+
throw new Error("hosted_game_frame_missing");
|
|
371
367
|
}
|
|
372
368
|
const rect = frame.getBoundingClientRect();
|
|
373
369
|
if (rect.width <= 0 || rect.height <= 0) {
|
|
374
|
-
throw new Error(
|
|
370
|
+
throw new Error("hosted_game_frame_hidden");
|
|
375
371
|
}
|
|
376
372
|
return {
|
|
377
373
|
outerWidth: window.outerWidth,
|
|
@@ -413,17 +409,19 @@ async function waitForHostedGameMeasurement(page, timeoutMs) {
|
|
|
413
409
|
}
|
|
414
410
|
}
|
|
415
411
|
catch (error) {
|
|
416
|
-
if (error instanceof Error &&
|
|
412
|
+
if (error instanceof Error &&
|
|
413
|
+
error.message !== "hosted_game_frame_missing" &&
|
|
414
|
+
error.message !== "hosted_game_frame_hidden") {
|
|
417
415
|
throw error;
|
|
418
416
|
}
|
|
419
417
|
}
|
|
420
418
|
await page.waitForTimeout(250);
|
|
421
419
|
}
|
|
422
|
-
throw new Error(
|
|
420
|
+
throw new Error("hosted_game_measurement_timeout");
|
|
423
421
|
}
|
|
424
422
|
async function waitForHostedGameFrame(page, timeoutMs) {
|
|
425
423
|
const frameHandle = await page.waitForSelector(CAPTURE_FRAME_SELECTOR, {
|
|
426
|
-
state:
|
|
424
|
+
state: "attached",
|
|
427
425
|
timeout: timeoutMs,
|
|
428
426
|
});
|
|
429
427
|
const startedAt = Date.now();
|
|
@@ -434,19 +432,19 @@ async function waitForHostedGameFrame(page, timeoutMs) {
|
|
|
434
432
|
}
|
|
435
433
|
await page.waitForTimeout(200);
|
|
436
434
|
}
|
|
437
|
-
throw new Error(
|
|
435
|
+
throw new Error("hosted_game_frame_missing");
|
|
438
436
|
}
|
|
439
437
|
async function prepareHostedListingScene(page, sceneId) {
|
|
440
438
|
const frame = await waitForHostedGameFrame(page, LISTING_CAPTURE_HOOK_TIMEOUT_MS);
|
|
441
439
|
await frame.waitForFunction(() => {
|
|
442
440
|
const captureWindow = window;
|
|
443
|
-
return typeof captureWindow.__listingCapture?.prepare ===
|
|
441
|
+
return typeof captureWindow.__listingCapture?.prepare === "function";
|
|
444
442
|
}, undefined, { timeout: LISTING_CAPTURE_HOOK_TIMEOUT_MS });
|
|
445
443
|
await frame.evaluate(async (requestedSceneId) => {
|
|
446
444
|
const captureWindow = window;
|
|
447
445
|
const hook = captureWindow.__listingCapture;
|
|
448
|
-
if (!hook || typeof hook.prepare !==
|
|
449
|
-
throw new Error(
|
|
446
|
+
if (!hook || typeof hook.prepare !== "function") {
|
|
447
|
+
throw new Error("listing_capture_hook_missing");
|
|
450
448
|
}
|
|
451
449
|
await hook.prepare(requestedSceneId);
|
|
452
450
|
}, sceneId);
|
|
@@ -455,38 +453,34 @@ async function startHostedListingAudioCapture(page) {
|
|
|
455
453
|
const frame = await waitForHostedGameFrame(page, 20000);
|
|
456
454
|
await frame.waitForFunction(() => {
|
|
457
455
|
const captureWindow = window;
|
|
458
|
-
return typeof captureWindow.__listingCapture?.startAudioCapture ===
|
|
456
|
+
return typeof captureWindow.__listingCapture?.startAudioCapture === "function";
|
|
459
457
|
}, undefined, { timeout: 20000 });
|
|
460
458
|
await frame.evaluate(async () => {
|
|
461
459
|
const captureWindow = window;
|
|
462
460
|
const hook = captureWindow.__listingCapture;
|
|
463
|
-
if (!hook || typeof hook.startAudioCapture !==
|
|
464
|
-
throw new Error(
|
|
461
|
+
if (!hook || typeof hook.startAudioCapture !== "function") {
|
|
462
|
+
throw new Error("listing_audio_capture_hook_missing");
|
|
465
463
|
}
|
|
466
464
|
await hook.startAudioCapture();
|
|
467
465
|
});
|
|
468
466
|
}
|
|
469
467
|
function assertExportedListingAudio(value) {
|
|
470
|
-
if (!value || typeof value !==
|
|
471
|
-
throw new Error(
|
|
472
|
-
}
|
|
473
|
-
const mimeType = typeof value.mimeType ===
|
|
474
|
-
|
|
475
|
-
: '';
|
|
476
|
-
const base64 = typeof value.base64 === 'string'
|
|
477
|
-
? value.base64.trim()
|
|
478
|
-
: '';
|
|
468
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
469
|
+
throw new Error("listing_audio_capture_export_missing");
|
|
470
|
+
}
|
|
471
|
+
const mimeType = typeof value.mimeType === "string" ? value.mimeType.trim() : "";
|
|
472
|
+
const base64 = typeof value.base64 === "string" ? value.base64.trim() : "";
|
|
479
473
|
if (!mimeType || !base64) {
|
|
480
|
-
throw new Error(
|
|
474
|
+
throw new Error("listing_audio_capture_export_missing");
|
|
481
475
|
}
|
|
482
476
|
return { mimeType, base64 };
|
|
483
477
|
}
|
|
484
478
|
function resolveExportedAudioFileName(mimeType) {
|
|
485
|
-
if (mimeType.includes(
|
|
486
|
-
return
|
|
479
|
+
if (mimeType.includes("webm")) {
|
|
480
|
+
return "listing-audio.webm";
|
|
487
481
|
}
|
|
488
|
-
if (mimeType.includes(
|
|
489
|
-
return
|
|
482
|
+
if (mimeType.includes("mp4") || mimeType.includes("aac") || mimeType.includes("m4a")) {
|
|
483
|
+
return "listing-audio.m4a";
|
|
490
484
|
}
|
|
491
485
|
throw new Error(`unsupported_listing_audio_mime_type:${mimeType}`);
|
|
492
486
|
}
|
|
@@ -495,13 +489,13 @@ async function stopHostedListingAudioCapture(page, outputDir) {
|
|
|
495
489
|
const exportedAudio = assertExportedListingAudio(await frame.evaluate(async () => {
|
|
496
490
|
const captureWindow = window;
|
|
497
491
|
const hook = captureWindow.__listingCapture;
|
|
498
|
-
if (!hook || typeof hook.stopAudioCapture !==
|
|
499
|
-
throw new Error(
|
|
492
|
+
if (!hook || typeof hook.stopAudioCapture !== "function") {
|
|
493
|
+
throw new Error("listing_audio_capture_hook_missing");
|
|
500
494
|
}
|
|
501
495
|
return hook.stopAudioCapture();
|
|
502
496
|
}));
|
|
503
497
|
const filePath = (0, node_path_1.join)(outputDir, resolveExportedAudioFileName(exportedAudio.mimeType));
|
|
504
|
-
await (0, promises_1.writeFile)(filePath, Buffer.from(exportedAudio.base64,
|
|
498
|
+
await (0, promises_1.writeFile)(filePath, Buffer.from(exportedAudio.base64, "base64"));
|
|
505
499
|
return filePath;
|
|
506
500
|
}
|
|
507
501
|
async function assertHostedListingAudioCaptureContract(page, outputDir) {
|
|
@@ -513,13 +507,9 @@ async function fitWindowToRequestedGameplay(page, requestedWidth, requestedHeigh
|
|
|
513
507
|
const cdpSession = await page.context().newCDPSession(page);
|
|
514
508
|
let measurement = await waitForHostedGameMeasurement(page, 15000);
|
|
515
509
|
for (let attempt = 0; attempt < 5; attempt += 1) {
|
|
516
|
-
const windowState = await cdpSession.send(
|
|
517
|
-
const currentWidth = typeof windowState.bounds.width ===
|
|
518
|
-
|
|
519
|
-
: Math.round(measurement.outerWidth);
|
|
520
|
-
const currentHeight = typeof windowState.bounds.height === 'number'
|
|
521
|
-
? windowState.bounds.height
|
|
522
|
-
: Math.round(measurement.outerHeight);
|
|
510
|
+
const windowState = await cdpSession.send("Browser.getWindowForTarget");
|
|
511
|
+
const currentWidth = typeof windowState.bounds.width === "number" ? windowState.bounds.width : Math.round(measurement.outerWidth);
|
|
512
|
+
const currentHeight = typeof windowState.bounds.height === "number" ? windowState.bounds.height : Math.round(measurement.outerHeight);
|
|
523
513
|
const deltaWidth = requestedWidth - Math.round(measurement.iframeRect.width);
|
|
524
514
|
const deltaHeight = requestedHeight - Math.round(measurement.iframeRect.height);
|
|
525
515
|
if (Math.abs(deltaWidth) <= 1 && Math.abs(deltaHeight) <= 1) {
|
|
@@ -531,7 +521,7 @@ async function fitWindowToRequestedGameplay(page, requestedWidth, requestedHeigh
|
|
|
531
521
|
}
|
|
532
522
|
const nextWidth = Math.max(400, currentWidth + deltaWidth);
|
|
533
523
|
const nextHeight = Math.max(300, currentHeight + deltaHeight);
|
|
534
|
-
await cdpSession.send(
|
|
524
|
+
await cdpSession.send("Browser.setWindowBounds", {
|
|
535
525
|
windowId: windowState.windowId,
|
|
536
526
|
bounds: {
|
|
537
527
|
width: nextWidth,
|
|
@@ -541,7 +531,7 @@ async function fitWindowToRequestedGameplay(page, requestedWidth, requestedHeigh
|
|
|
541
531
|
await page.waitForTimeout(800);
|
|
542
532
|
measurement = await waitForHostedGameMeasurement(page, 8000);
|
|
543
533
|
}
|
|
544
|
-
throw new Error(
|
|
534
|
+
throw new Error("hosted_game_resize_failed");
|
|
545
535
|
}
|
|
546
536
|
function assertListingCaptureWindowCanContainViewport(measurement, windowBounds) {
|
|
547
537
|
const requiredWidth = Math.ceil(measurement.innerWidth);
|
|
@@ -557,33 +547,33 @@ function roundCropValue(value) {
|
|
|
557
547
|
}
|
|
558
548
|
function computeRecordedCrop(measurement, recorder, rawWidth, rawHeight) {
|
|
559
549
|
if (measurement.outerWidth <= 0 || measurement.outerHeight <= 0) {
|
|
560
|
-
throw new Error(
|
|
550
|
+
throw new Error("invalid_window_geometry");
|
|
561
551
|
}
|
|
562
552
|
if (measurement.innerWidth <= 0 || measurement.innerHeight <= 0) {
|
|
563
|
-
throw new Error(
|
|
553
|
+
throw new Error("invalid_viewport_geometry");
|
|
564
554
|
}
|
|
565
555
|
if (rawWidth <= 0 || rawHeight <= 0) {
|
|
566
|
-
throw new Error(
|
|
556
|
+
throw new Error("invalid_raw_dimensions");
|
|
567
557
|
}
|
|
568
558
|
const nativeOuterWidth = recorder.windowFramePoints.width;
|
|
569
559
|
const nativeOuterHeight = recorder.windowFramePoints.height;
|
|
570
560
|
if (nativeOuterWidth <= 0 || nativeOuterHeight <= 0) {
|
|
571
|
-
throw new Error(
|
|
561
|
+
throw new Error("invalid_native_window_geometry");
|
|
572
562
|
}
|
|
573
563
|
const horizontalChrome = nativeOuterWidth - measurement.innerWidth;
|
|
574
564
|
const verticalChrome = nativeOuterHeight - measurement.innerHeight;
|
|
575
565
|
if (horizontalChrome < 0 || verticalChrome < 0) {
|
|
576
|
-
throw new Error(
|
|
566
|
+
throw new Error("invalid_chrome_geometry");
|
|
577
567
|
}
|
|
578
|
-
const usesSyntheticMobileWindow = Math.abs(measurement.outerWidth - measurement.innerWidth) <= 1
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
568
|
+
const usesSyntheticMobileWindow = Math.abs(measurement.outerWidth - measurement.innerWidth) <= 1 &&
|
|
569
|
+
Math.abs(measurement.outerHeight - measurement.innerHeight) <= 1 &&
|
|
570
|
+
(Math.abs(nativeOuterWidth - measurement.outerWidth) > 1 ||
|
|
571
|
+
Math.abs(nativeOuterHeight - measurement.outerHeight) > 1);
|
|
582
572
|
const leftChrome = usesSyntheticMobileWindow ? 0 : horizontalChrome / 2;
|
|
583
573
|
const bottomChrome = usesSyntheticMobileWindow ? 0 : leftChrome;
|
|
584
574
|
const topChrome = verticalChrome - bottomChrome;
|
|
585
575
|
if (topChrome < 0) {
|
|
586
|
-
throw new Error(
|
|
576
|
+
throw new Error("invalid_vertical_chrome_geometry");
|
|
587
577
|
}
|
|
588
578
|
const cropLeftCss = leftChrome + measurement.iframeRect.left;
|
|
589
579
|
const cropTopCss = topChrome + measurement.iframeRect.top;
|
|
@@ -594,60 +584,50 @@ function computeRecordedCrop(measurement, recorder, rawWidth, rawHeight) {
|
|
|
594
584
|
const width = roundCropValue(measurement.iframeRect.width * scaleX);
|
|
595
585
|
const height = roundCropValue(measurement.iframeRect.height * scaleY);
|
|
596
586
|
if (x < 0 || y < 0 || width <= 0 || height <= 0) {
|
|
597
|
-
throw new Error(
|
|
587
|
+
throw new Error("invalid_crop_geometry");
|
|
598
588
|
}
|
|
599
589
|
if (x + width > rawWidth || y + height > rawHeight) {
|
|
600
|
-
throw new Error(
|
|
590
|
+
throw new Error("crop_out_of_bounds");
|
|
601
591
|
}
|
|
602
592
|
return { x, y, width, height };
|
|
603
593
|
}
|
|
604
594
|
function runTool(command, args, failureCode, deadlineAt) {
|
|
605
595
|
const result = (0, node_child_process_1.spawnSync)(command, args, {
|
|
606
|
-
encoding:
|
|
596
|
+
encoding: "utf8",
|
|
607
597
|
maxBuffer: 20 * 1024 * 1024,
|
|
608
598
|
timeout: (0, listingCaptureLease_1.remainingListingCaptureTime)(deadlineAt),
|
|
609
|
-
killSignal:
|
|
599
|
+
killSignal: "SIGKILL",
|
|
610
600
|
});
|
|
611
|
-
if (result.error?.code ===
|
|
612
|
-
throw new Error(
|
|
601
|
+
if (result.error?.code === "ETIMEDOUT") {
|
|
602
|
+
throw new Error("listing_capture_timeout");
|
|
613
603
|
}
|
|
614
604
|
if (result.status !== 0) {
|
|
615
|
-
const stderr = result.stderr?.trim() ||
|
|
616
|
-
const stdout = result.stdout?.trim() ||
|
|
617
|
-
const combined = [stdout, stderr].filter(Boolean).join(
|
|
605
|
+
const stderr = result.stderr?.trim() || "";
|
|
606
|
+
const stdout = result.stdout?.trim() || "";
|
|
607
|
+
const combined = [stdout, stderr].filter(Boolean).join("\n");
|
|
618
608
|
throw new Error(`${failureCode}:${combined}`);
|
|
619
609
|
}
|
|
620
610
|
return {
|
|
621
|
-
stdout: result.stdout ||
|
|
622
|
-
stderr: result.stderr ||
|
|
611
|
+
stdout: result.stdout || "",
|
|
612
|
+
stderr: result.stderr || "",
|
|
623
613
|
};
|
|
624
614
|
}
|
|
625
615
|
async function probeMediaFile(filePath, deadlineAt) {
|
|
626
|
-
const { stdout } = runTool(
|
|
627
|
-
'-v',
|
|
628
|
-
'error',
|
|
629
|
-
'-show_streams',
|
|
630
|
-
'-show_format',
|
|
631
|
-
'-of',
|
|
632
|
-
'json',
|
|
633
|
-
filePath,
|
|
634
|
-
], 'ffprobe_failed', deadlineAt);
|
|
616
|
+
const { stdout } = runTool("ffprobe", ["-v", "error", "-show_streams", "-show_format", "-of", "json", filePath], "ffprobe_failed", deadlineAt);
|
|
635
617
|
return JSON.parse(stdout);
|
|
636
618
|
}
|
|
637
619
|
async function computeFileSha256(filePath) {
|
|
638
|
-
return (0, node_crypto_1.createHash)(
|
|
620
|
+
return (0, node_crypto_1.createHash)("sha256")
|
|
621
|
+
.update(await (0, promises_1.readFile)(filePath))
|
|
622
|
+
.digest("hex");
|
|
639
623
|
}
|
|
640
624
|
async function readListingValidationPixels(filePath) {
|
|
641
|
-
return (0, sharp_1.default)(filePath)
|
|
642
|
-
.resize(64, 64, { fit: 'fill' })
|
|
643
|
-
.greyscale()
|
|
644
|
-
.raw()
|
|
645
|
-
.toBuffer();
|
|
625
|
+
return (0, sharp_1.default)(filePath).resize(64, 64, { fit: "fill" }).greyscale().raw().toBuffer();
|
|
646
626
|
}
|
|
647
627
|
async function measureListingFrameVisibility(filePath) {
|
|
648
628
|
const pixels = await readListingValidationPixels(filePath);
|
|
649
629
|
if (pixels.length === 0) {
|
|
650
|
-
throw new Error(
|
|
630
|
+
throw new Error("listing_capture_frame_pixels_missing");
|
|
651
631
|
}
|
|
652
632
|
let total = 0;
|
|
653
633
|
let visiblePixels = 0;
|
|
@@ -663,8 +643,8 @@ async function measureListingFrameVisibility(filePath) {
|
|
|
663
643
|
}
|
|
664
644
|
async function assertListingCaptureFrameVisible(surface, label, filePath) {
|
|
665
645
|
const visibility = await measureListingFrameVisibility(filePath);
|
|
666
|
-
if (visibility.meanLuma < MIN_LISTING_FRAME_MEAN_LUMA
|
|
667
|
-
|
|
646
|
+
if (visibility.meanLuma < MIN_LISTING_FRAME_MEAN_LUMA &&
|
|
647
|
+
visibility.visiblePixelRatio < MIN_LISTING_VISIBLE_PIXEL_RATIO) {
|
|
668
648
|
throw new Error(`listing_capture_black_frame:${surface}:${label}`);
|
|
669
649
|
}
|
|
670
650
|
}
|
|
@@ -674,7 +654,7 @@ async function measureListingFrameMotion(firstPath, lastPath) {
|
|
|
674
654
|
readListingValidationPixels(lastPath),
|
|
675
655
|
]);
|
|
676
656
|
if (first.length !== last.length || first.length === 0) {
|
|
677
|
-
throw new Error(
|
|
657
|
+
throw new Error("listing_capture_frame_shape_mismatch");
|
|
678
658
|
}
|
|
679
659
|
let deltaTotal = 0;
|
|
680
660
|
let changedPixels = 0;
|
|
@@ -690,39 +670,39 @@ async function measureListingFrameMotion(firstPath, lastPath) {
|
|
|
690
670
|
};
|
|
691
671
|
}
|
|
692
672
|
function calculateListingFrameSimilarity(referencePath, capturedPath, deadlineAt) {
|
|
693
|
-
const { stderr } = runTool(
|
|
694
|
-
|
|
695
|
-
|
|
673
|
+
const { stderr } = runTool("ffmpeg", [
|
|
674
|
+
"-hide_banner",
|
|
675
|
+
"-i",
|
|
696
676
|
referencePath,
|
|
697
|
-
|
|
677
|
+
"-i",
|
|
698
678
|
capturedPath,
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
],
|
|
679
|
+
"-filter_complex",
|
|
680
|
+
"[0:v]scale=320:320:force_original_aspect_ratio=decrease,pad=320:320:(ow-iw)/2:(oh-ih)/2,format=yuv420p[ref];[1:v]scale=320:320:force_original_aspect_ratio=decrease,pad=320:320:(ow-iw)/2:(oh-ih)/2,format=yuv420p[cap];[ref][cap]ssim",
|
|
681
|
+
"-f",
|
|
682
|
+
"null",
|
|
683
|
+
"-",
|
|
684
|
+
], "ffmpeg_failed", deadlineAt);
|
|
705
685
|
const match = stderr.match(/All:([0-9.]+)/);
|
|
706
686
|
if (!match) {
|
|
707
|
-
throw new Error(
|
|
687
|
+
throw new Error("listing_capture_similarity_missing");
|
|
708
688
|
}
|
|
709
689
|
return Number.parseFloat(match[1]);
|
|
710
690
|
}
|
|
711
691
|
function assertListingCapturePixelMetrics(surface, metrics) {
|
|
712
|
-
if (!Number.isFinite(metrics.referenceFrameSimilarity)
|
|
713
|
-
|
|
714
|
-
throw new Error(`listing_capture_wrong_game:${surface}:${Number.isFinite(metrics.referenceFrameSimilarity) ? metrics.referenceFrameSimilarity.toFixed(3) :
|
|
692
|
+
if (!Number.isFinite(metrics.referenceFrameSimilarity) ||
|
|
693
|
+
metrics.referenceFrameSimilarity < MIN_LISTING_REFERENCE_FRAME_SIMILARITY) {
|
|
694
|
+
throw new Error(`listing_capture_wrong_game:${surface}:${Number.isFinite(metrics.referenceFrameSimilarity) ? metrics.referenceFrameSimilarity.toFixed(3) : "unknown"}`);
|
|
715
695
|
}
|
|
716
|
-
if (metrics.firstFrameMeanLuma < MIN_LISTING_FRAME_MEAN_LUMA
|
|
717
|
-
|
|
696
|
+
if (metrics.firstFrameMeanLuma < MIN_LISTING_FRAME_MEAN_LUMA &&
|
|
697
|
+
metrics.firstFrameVisiblePixelRatio < MIN_LISTING_VISIBLE_PIXEL_RATIO) {
|
|
718
698
|
throw new Error(`listing_capture_black_frame:${surface}:first`);
|
|
719
699
|
}
|
|
720
|
-
if (metrics.posterFrameMeanLuma < MIN_LISTING_FRAME_MEAN_LUMA
|
|
721
|
-
|
|
700
|
+
if (metrics.posterFrameMeanLuma < MIN_LISTING_FRAME_MEAN_LUMA &&
|
|
701
|
+
metrics.posterFrameVisiblePixelRatio < MIN_LISTING_VISIBLE_PIXEL_RATIO) {
|
|
722
702
|
throw new Error(`listing_capture_black_frame:${surface}:poster`);
|
|
723
703
|
}
|
|
724
|
-
if (metrics.motionMeanDelta < MIN_LISTING_MOTION_MEAN_DELTA
|
|
725
|
-
|
|
704
|
+
if (metrics.motionMeanDelta < MIN_LISTING_MOTION_MEAN_DELTA &&
|
|
705
|
+
metrics.motionChangedPixelRatio < MIN_LISTING_MOTION_CHANGED_PIXEL_RATIO) {
|
|
726
706
|
throw new Error(`listing_capture_stale_video:${surface}`);
|
|
727
707
|
}
|
|
728
708
|
}
|
|
@@ -742,25 +722,16 @@ async function validateListingCapturePixels(input) {
|
|
|
742
722
|
motionChangedPixelRatio: motion.changedPixelRatio,
|
|
743
723
|
};
|
|
744
724
|
assertListingCapturePixelMetrics(input.surface, metrics);
|
|
745
|
-
return { status:
|
|
725
|
+
return { status: "passed", ...metrics };
|
|
746
726
|
}
|
|
747
727
|
function extractListingValidationFrame(videoPath, atSeconds, outputPath, deadlineAt) {
|
|
748
|
-
runTool(
|
|
749
|
-
'-y',
|
|
750
|
-
'-ss',
|
|
751
|
-
String(atSeconds),
|
|
752
|
-
'-i',
|
|
753
|
-
videoPath,
|
|
754
|
-
'-frames:v',
|
|
755
|
-
'1',
|
|
756
|
-
outputPath,
|
|
757
|
-
], 'ffmpeg_failed', deadlineAt);
|
|
728
|
+
runTool("ffmpeg", ["-y", "-ss", String(atSeconds), "-i", videoPath, "-frames:v", "1", outputPath], "ffmpeg_failed", deadlineAt);
|
|
758
729
|
}
|
|
759
730
|
function parseFrameRate(raw) {
|
|
760
731
|
if (!raw) {
|
|
761
732
|
return null;
|
|
762
733
|
}
|
|
763
|
-
const parts = raw.split(
|
|
734
|
+
const parts = raw.split("/");
|
|
764
735
|
if (parts.length !== 2) {
|
|
765
736
|
const parsed = Number.parseFloat(raw);
|
|
766
737
|
return Number.isFinite(parsed) ? parsed : null;
|
|
@@ -773,144 +744,152 @@ function parseFrameRate(raw) {
|
|
|
773
744
|
return numerator / denominator;
|
|
774
745
|
}
|
|
775
746
|
function extractVideoStream(probe) {
|
|
776
|
-
const videoStream = probe.streams?.find(stream => stream.codec_type ===
|
|
777
|
-
if (!videoStream || typeof videoStream.width !==
|
|
778
|
-
throw new Error(
|
|
747
|
+
const videoStream = probe.streams?.find((stream) => stream.codec_type === "video");
|
|
748
|
+
if (!videoStream || typeof videoStream.width !== "number" || typeof videoStream.height !== "number") {
|
|
749
|
+
throw new Error("video_stream_missing");
|
|
779
750
|
}
|
|
780
751
|
return videoStream;
|
|
781
752
|
}
|
|
782
753
|
function countAudioStreams(probe) {
|
|
783
|
-
return probe.streams?.filter(stream => stream.codec_type ===
|
|
754
|
+
return probe.streams?.filter((stream) => stream.codec_type === "audio").length ?? 0;
|
|
784
755
|
}
|
|
785
756
|
function readDurationSeconds(probe) {
|
|
786
757
|
const raw = probe.format?.duration;
|
|
787
758
|
const parsed = raw ? Number.parseFloat(raw) : Number.NaN;
|
|
788
759
|
if (!Number.isFinite(parsed)) {
|
|
789
|
-
throw new Error(
|
|
760
|
+
throw new Error("duration_missing");
|
|
790
761
|
}
|
|
791
762
|
return parsed;
|
|
792
763
|
}
|
|
793
764
|
async function runListingRecorder(recorderPath, pid, durationSeconds, rawOutputPath, metadataPath, audio, deadlineAt) {
|
|
794
765
|
const args = [
|
|
795
|
-
|
|
766
|
+
"--pid",
|
|
796
767
|
String(pid),
|
|
797
|
-
|
|
768
|
+
"--duration",
|
|
798
769
|
String(durationSeconds),
|
|
799
|
-
|
|
770
|
+
"--output",
|
|
800
771
|
rawOutputPath,
|
|
801
|
-
|
|
772
|
+
"--metadata",
|
|
802
773
|
metadataPath,
|
|
803
774
|
];
|
|
804
775
|
if (audio) {
|
|
805
|
-
args.push(
|
|
776
|
+
args.push("--audio");
|
|
806
777
|
}
|
|
807
778
|
const result = (0, node_child_process_1.spawnSync)(recorderPath, args, {
|
|
808
|
-
encoding:
|
|
779
|
+
encoding: "utf8",
|
|
809
780
|
maxBuffer: 10 * 1024 * 1024,
|
|
810
781
|
timeout: (0, listingCaptureLease_1.remainingListingCaptureTime)(deadlineAt),
|
|
811
|
-
killSignal:
|
|
782
|
+
killSignal: "SIGKILL",
|
|
812
783
|
});
|
|
813
|
-
if (result.error?.code ===
|
|
814
|
-
throw new Error(
|
|
784
|
+
if (result.error?.code === "ETIMEDOUT") {
|
|
785
|
+
throw new Error("listing_capture_timeout");
|
|
815
786
|
}
|
|
816
787
|
if (result.status !== 0) {
|
|
817
|
-
const message = result.stderr?.trim() || result.stdout?.trim() ||
|
|
788
|
+
const message = result.stderr?.trim() || result.stdout?.trim() || "Native recorder failed.";
|
|
818
789
|
throw new Error(`listing_recorder_failed:${message}`);
|
|
819
790
|
}
|
|
820
|
-
const metadata = JSON.parse(await (0, promises_1.readFile)(metadataPath,
|
|
791
|
+
const metadata = JSON.parse(await (0, promises_1.readFile)(metadataPath, "utf8"));
|
|
821
792
|
return metadata;
|
|
822
793
|
}
|
|
823
794
|
function formatCommandError(error) {
|
|
824
|
-
if (error.message ===
|
|
795
|
+
if (error.message === "unsupported_platform") {
|
|
825
796
|
return {
|
|
826
|
-
message:
|
|
797
|
+
message: "Game media capture only runs on macOS.",
|
|
827
798
|
suggestions: [],
|
|
828
799
|
};
|
|
829
800
|
}
|
|
830
|
-
if (error.message.startsWith(
|
|
831
|
-
const version = error.message.split(
|
|
801
|
+
if (error.message.startsWith("unsupported_macos_version:")) {
|
|
802
|
+
const version = error.message.split(":")[1] || "unknown";
|
|
832
803
|
return {
|
|
833
804
|
message: `Game media capture requires macOS ${MIN_SUPPORTED_MACOS_MAJOR}.${MIN_SUPPORTED_MACOS_MINOR} or newer on this machine. Current version: ${version}.`,
|
|
834
805
|
suggestions: [],
|
|
835
806
|
};
|
|
836
807
|
}
|
|
837
|
-
if (error.message ===
|
|
808
|
+
if (error.message === "macos_version_probe_failed") {
|
|
838
809
|
return {
|
|
839
|
-
message:
|
|
810
|
+
message: "Could not determine the local macOS version.",
|
|
840
811
|
suggestions: [],
|
|
841
812
|
};
|
|
842
813
|
}
|
|
843
|
-
if (error.message ===
|
|
814
|
+
if (error.message === "listing_capture_timeout" || error.message.startsWith("listing_capture_lease_timeout:")) {
|
|
844
815
|
return {
|
|
845
|
-
message:
|
|
816
|
+
message: "Listing capture did not finish within 30 minutes.",
|
|
846
817
|
suggestions: [],
|
|
847
818
|
};
|
|
848
819
|
}
|
|
849
|
-
if (error.message ===
|
|
820
|
+
if (error.message === "listing_preview_support_missing") {
|
|
850
821
|
return {
|
|
851
|
-
message:
|
|
852
|
-
suggestions: [
|
|
822
|
+
message: "This game does not declare preview support, so listing capture cannot run.",
|
|
823
|
+
suggestions: [
|
|
824
|
+
'Set previewable: true in catalogue.json, implement the preview scene and __listingCapture hooks, then rerun "playdrop project capture".',
|
|
825
|
+
],
|
|
853
826
|
};
|
|
854
827
|
}
|
|
855
|
-
if (error.message ===
|
|
828
|
+
if (error.message === "listing_audio_capture_export_missing") {
|
|
856
829
|
return {
|
|
857
|
-
message:
|
|
858
|
-
suggestions: [
|
|
830
|
+
message: "The game preview audio hook did not export recorded audio.",
|
|
831
|
+
suggestions: [
|
|
832
|
+
"Make window.__listingCapture.stopAudioCapture() resolve { mimeType, base64 }; toggling game audio on or off is not an export.",
|
|
833
|
+
],
|
|
859
834
|
};
|
|
860
835
|
}
|
|
861
|
-
if (error.message ===
|
|
836
|
+
if (error.message === "listing_capture_runtime_already_recorded") {
|
|
862
837
|
return {
|
|
863
|
-
message:
|
|
864
|
-
suggestions: [
|
|
838
|
+
message: "This exact game runtime already has a canonical recording with different capture settings.",
|
|
839
|
+
suggestions: [
|
|
840
|
+
"Use the existing video, or change and revalidate the preview implementation before recording the changed runtime once.",
|
|
841
|
+
],
|
|
865
842
|
};
|
|
866
843
|
}
|
|
867
|
-
if (error.message ===
|
|
844
|
+
if (error.message === "listing_capture_current_report_incomplete") {
|
|
868
845
|
return {
|
|
869
|
-
message:
|
|
870
|
-
suggestions: [
|
|
846
|
+
message: "The canonical capture report is incomplete for this runtime.",
|
|
847
|
+
suggestions: ["Fix or remove the incomplete canonical capture directory, then record the runtime once."],
|
|
871
848
|
};
|
|
872
849
|
}
|
|
873
|
-
if (error.message ===
|
|
874
|
-
|
|
850
|
+
if (error.message === "listing_capture_report_pixels_unvalidated" ||
|
|
851
|
+
error.message.startsWith("listing_capture_artifact_changed:")) {
|
|
875
852
|
return {
|
|
876
|
-
message:
|
|
853
|
+
message: "The existing canonical recording is unvalidated or its files changed after capture.",
|
|
877
854
|
suggestions: ['Remove the invalid capture directory and rerun "playdrop project capture" once.'],
|
|
878
855
|
};
|
|
879
856
|
}
|
|
880
|
-
if (error.message.startsWith(
|
|
881
|
-
const [, requested =
|
|
857
|
+
if (error.message.startsWith("listing_capture_dimensions_exceed_display:")) {
|
|
858
|
+
const [, requested = "unknown", available = "unknown"] = error.message.split(":");
|
|
882
859
|
return {
|
|
883
860
|
message: `Requested capture size ${requested} cannot fit in this worker's ${available} browser window.`,
|
|
884
|
-
suggestions: [
|
|
861
|
+
suggestions: [
|
|
862
|
+
"Omit --width and --height to use the safe surface defaults, or request dimensions that fit this display.",
|
|
863
|
+
],
|
|
885
864
|
};
|
|
886
865
|
}
|
|
887
|
-
if (error.message.startsWith(
|
|
866
|
+
if (error.message.startsWith("listing_recorder_failed:")) {
|
|
888
867
|
return {
|
|
889
|
-
message: error.message.slice(
|
|
868
|
+
message: error.message.slice("listing_recorder_failed:".length),
|
|
890
869
|
suggestions: [],
|
|
891
870
|
};
|
|
892
871
|
}
|
|
893
|
-
if (error.message.startsWith(
|
|
872
|
+
if (error.message.startsWith("ffprobe_failed:") || error.message.startsWith("ffmpeg_failed:")) {
|
|
894
873
|
return {
|
|
895
|
-
message: error.message.slice(error.message.indexOf(
|
|
874
|
+
message: error.message.slice(error.message.indexOf(":") + 1).trim() || "ffmpeg validation failed.",
|
|
896
875
|
suggestions: [],
|
|
897
876
|
};
|
|
898
877
|
}
|
|
899
|
-
if (error.message.startsWith(
|
|
878
|
+
if (error.message.startsWith("listing_capture_wrong_game:")) {
|
|
900
879
|
return {
|
|
901
|
-
message:
|
|
880
|
+
message: "The recorded pixels do not match the selected game preview.",
|
|
902
881
|
suggestions: ['Verify preview mode shows the selected game, then rerun "playdrop project capture".'],
|
|
903
882
|
};
|
|
904
883
|
}
|
|
905
|
-
if (error.message.startsWith(
|
|
884
|
+
if (error.message.startsWith("listing_capture_black_frame:")) {
|
|
906
885
|
return {
|
|
907
|
-
message:
|
|
886
|
+
message: "The gameplay recording contains a black first frame or poster.",
|
|
908
887
|
suggestions: ['Make preview mode visibly ready before capture starts, then rerun "playdrop project capture".'],
|
|
909
888
|
};
|
|
910
889
|
}
|
|
911
|
-
if (error.message.startsWith(
|
|
890
|
+
if (error.message.startsWith("listing_capture_stale_video:")) {
|
|
912
891
|
return {
|
|
913
|
-
message:
|
|
892
|
+
message: "The gameplay recording is visually static.",
|
|
914
893
|
suggestions: ['Make preview mode show real gameplay motion, then rerun "playdrop project capture".'],
|
|
915
894
|
};
|
|
916
895
|
}
|
|
@@ -921,62 +900,53 @@ function formatCommandError(error) {
|
|
|
921
900
|
}
|
|
922
901
|
async function encodeListingVideo(rawVideoPath, finalVideoPath, posterPath, crop, width, height, fps, posterAtSeconds, deadlineAt) {
|
|
923
902
|
const filter = `crop=${crop.width}:${crop.height}:${crop.x}:${crop.y},scale=${width}:${height}:flags=lanczos,fps=${fps}`;
|
|
924
|
-
runTool(
|
|
925
|
-
|
|
926
|
-
|
|
903
|
+
runTool("ffmpeg", [
|
|
904
|
+
"-y",
|
|
905
|
+
"-i",
|
|
927
906
|
rawVideoPath,
|
|
928
|
-
|
|
907
|
+
"-vf",
|
|
929
908
|
filter,
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
finalVideoPath,
|
|
937
|
-
], 'ffmpeg_failed', deadlineAt);
|
|
938
|
-
runTool('ffmpeg', [
|
|
939
|
-
'-y',
|
|
940
|
-
'-ss',
|
|
941
|
-
String(posterAtSeconds),
|
|
942
|
-
'-i',
|
|
909
|
+
"-c:v",
|
|
910
|
+
"h264_videotoolbox",
|
|
911
|
+
"-pix_fmt",
|
|
912
|
+
"yuv420p",
|
|
913
|
+
"-c:a",
|
|
914
|
+
"aac",
|
|
943
915
|
finalVideoPath,
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
posterPath,
|
|
947
|
-
], 'ffmpeg_failed', deadlineAt);
|
|
916
|
+
], "ffmpeg_failed", deadlineAt);
|
|
917
|
+
runTool("ffmpeg", ["-y", "-ss", String(posterAtSeconds), "-i", finalVideoPath, "-frames:v", "1", posterPath], "ffmpeg_failed", deadlineAt);
|
|
948
918
|
}
|
|
949
919
|
function captureCommandMatchesExceptPoster(capture, surface, parsedOptions) {
|
|
950
920
|
const dimensions = resolveCaptureDimensions(surface, parsedOptions);
|
|
951
|
-
return capture.command.durationSeconds === parsedOptions.durationSeconds
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
921
|
+
return (capture.command.durationSeconds === parsedOptions.durationSeconds &&
|
|
922
|
+
capture.command.width === dimensions.width &&
|
|
923
|
+
capture.command.height === dimensions.height &&
|
|
924
|
+
capture.command.fps === parsedOptions.fps &&
|
|
925
|
+
capture.command.audio === parsedOptions.audio &&
|
|
926
|
+
capture.command.keepRaw === parsedOptions.keepRaw);
|
|
957
927
|
}
|
|
958
928
|
function resolveCurrentCaptureReuseAction(captures, surfaces, parsedOptions) {
|
|
959
929
|
if (captures.length !== surfaces.length) {
|
|
960
|
-
throw new Error(
|
|
930
|
+
throw new Error("listing_capture_current_report_incomplete");
|
|
961
931
|
}
|
|
962
932
|
const orderedCaptures = surfaces.map((surface) => captures.find((candidate) => candidate.surface === surface));
|
|
963
933
|
if (orderedCaptures.some((capture) => !capture)) {
|
|
964
|
-
throw new Error(
|
|
934
|
+
throw new Error("listing_capture_current_report_incomplete");
|
|
965
935
|
}
|
|
966
936
|
if (orderedCaptures.some((capture, index) => !captureCommandMatchesExceptPoster(capture, surfaces[index], parsedOptions))) {
|
|
967
|
-
throw new Error(
|
|
937
|
+
throw new Error("listing_capture_runtime_already_recorded");
|
|
968
938
|
}
|
|
969
939
|
return orderedCaptures.some((capture) => capture.command.posterAtSeconds !== parsedOptions.posterAtSeconds)
|
|
970
|
-
?
|
|
971
|
-
:
|
|
940
|
+
? "refresh_poster"
|
|
941
|
+
: "reuse";
|
|
972
942
|
}
|
|
973
943
|
async function reuseCurrentListingCapture(input) {
|
|
974
944
|
let report;
|
|
975
945
|
try {
|
|
976
|
-
report = JSON.parse(await (0, promises_1.readFile)(input.outputPaths.reportPath,
|
|
946
|
+
report = JSON.parse(await (0, promises_1.readFile)(input.outputPaths.reportPath, "utf8"));
|
|
977
947
|
}
|
|
978
948
|
catch (error) {
|
|
979
|
-
if (error?.code ===
|
|
949
|
+
if (error?.code === "ENOENT") {
|
|
980
950
|
return false;
|
|
981
951
|
}
|
|
982
952
|
throw new Error(`listing_capture_report_invalid:${input.outputPaths.reportPath}`);
|
|
@@ -984,8 +954,8 @@ async function reuseCurrentListingCapture(input) {
|
|
|
984
954
|
if (report.appName !== input.appName) {
|
|
985
955
|
throw new Error(`listing_capture_report_app_mismatch:${report.appName}:${input.appName}`);
|
|
986
956
|
}
|
|
987
|
-
if (report.binding?.pixelValidation !==
|
|
988
|
-
throw new Error(
|
|
957
|
+
if (report.binding?.pixelValidation !== "passed") {
|
|
958
|
+
throw new Error("listing_capture_report_pixels_unvalidated");
|
|
989
959
|
}
|
|
990
960
|
if (!input.runtimeBundleHash || report.binding?.runtimeBundleHash !== input.runtimeBundleHash) {
|
|
991
961
|
return false;
|
|
@@ -1004,47 +974,35 @@ async function reuseCurrentListingCapture(input) {
|
|
|
1004
974
|
}
|
|
1005
975
|
}
|
|
1006
976
|
}
|
|
1007
|
-
if (action ===
|
|
1008
|
-
console.log(
|
|
977
|
+
if (action === "reuse") {
|
|
978
|
+
console.log("[listing] The current runtime already has a matching canonical capture; reusing it.");
|
|
1009
979
|
console.log(`[listing] Existing report: ${(0, node_path_1.relative)(process.cwd(), input.outputPaths.reportPath) || input.outputPaths.reportPath}`);
|
|
1010
980
|
return true;
|
|
1011
981
|
}
|
|
1012
982
|
for (const capture of captures) {
|
|
1013
|
-
runTool(
|
|
1014
|
-
|
|
1015
|
-
|
|
983
|
+
runTool("ffmpeg", [
|
|
984
|
+
"-y",
|
|
985
|
+
"-ss",
|
|
1016
986
|
String(input.parsedOptions.posterAtSeconds),
|
|
1017
|
-
|
|
987
|
+
"-i",
|
|
1018
988
|
capture.finalVideo.path,
|
|
1019
|
-
|
|
1020
|
-
|
|
989
|
+
"-frames:v",
|
|
990
|
+
"1",
|
|
1021
991
|
capture.posterPath,
|
|
1022
|
-
],
|
|
992
|
+
], "ffmpeg_failed", input.deadlineAt);
|
|
1023
993
|
capture.command.posterAtSeconds = input.parsedOptions.posterAtSeconds;
|
|
1024
|
-
const artifactKey = (0, node_path_1.relative)(input.outputPaths.outputDir, capture.posterPath) ||
|
|
994
|
+
const artifactKey = (0, node_path_1.relative)(input.outputPaths.outputDir, capture.posterPath) || "poster.png";
|
|
1025
995
|
report.binding.artifactHashes[artifactKey] = await computeFileSha256(capture.posterPath);
|
|
1026
|
-
await assertListingCaptureFrameVisible(capture.surface,
|
|
996
|
+
await assertListingCaptureFrameVisible(capture.surface, "poster", capture.posterPath);
|
|
1027
997
|
}
|
|
1028
|
-
await (0, promises_1.writeFile)(input.outputPaths.reportPath, `${JSON.stringify(report, null, 2)}\n`,
|
|
998
|
+
await (0, promises_1.writeFile)(input.outputPaths.reportPath, `${JSON.stringify(report, null, 2)}\n`, "utf8");
|
|
1029
999
|
console.log(`[listing] Refreshed the canonical poster at ${input.parsedOptions.posterAtSeconds}s without relaunching the game.`);
|
|
1030
1000
|
console.log(`[listing] Updated report: ${(0, node_path_1.relative)(process.cwd(), input.outputPaths.reportPath) || input.outputPaths.reportPath}`);
|
|
1031
1001
|
return true;
|
|
1032
1002
|
}
|
|
1033
1003
|
async function muxListingAudio(finalVideoPath, audioPath, deadlineAt) {
|
|
1034
1004
|
const muxedVideoPath = `${finalVideoPath}.muxed.mp4`;
|
|
1035
|
-
runTool(
|
|
1036
|
-
'-y',
|
|
1037
|
-
'-i',
|
|
1038
|
-
finalVideoPath,
|
|
1039
|
-
'-i',
|
|
1040
|
-
audioPath,
|
|
1041
|
-
'-c:v',
|
|
1042
|
-
'copy',
|
|
1043
|
-
'-c:a',
|
|
1044
|
-
'aac',
|
|
1045
|
-
'-shortest',
|
|
1046
|
-
muxedVideoPath,
|
|
1047
|
-
], 'ffmpeg_failed', deadlineAt);
|
|
1005
|
+
runTool("ffmpeg", ["-y", "-i", finalVideoPath, "-i", audioPath, "-c:v", "copy", "-c:a", "aac", "-shortest", muxedVideoPath], "ffmpeg_failed", deadlineAt);
|
|
1048
1006
|
await (0, promises_1.rename)(muxedVideoPath, finalVideoPath);
|
|
1049
1007
|
}
|
|
1050
1008
|
function ensureTolerance(actual, expected, tolerance, failureCode) {
|
|
@@ -1056,7 +1014,7 @@ function createWarnings(finalProbe, requestedAudio) {
|
|
|
1056
1014
|
const warnings = [];
|
|
1057
1015
|
const audioTrackCount = countAudioStreams(finalProbe);
|
|
1058
1016
|
if (!requestedAudio && audioTrackCount > 0) {
|
|
1059
|
-
warnings.push(
|
|
1017
|
+
warnings.push("final output contains audio even though --audio was not requested");
|
|
1060
1018
|
}
|
|
1061
1019
|
return warnings;
|
|
1062
1020
|
}
|
|
@@ -1065,7 +1023,7 @@ function ensureCaptureListingToolAvailable(command) {
|
|
|
1065
1023
|
return true;
|
|
1066
1024
|
}
|
|
1067
1025
|
(0, messages_1.printErrorWithHelp)(`${command} is required for listing capture.`, [`Install ${command} and make sure it is available on PATH.`], {
|
|
1068
|
-
command:
|
|
1026
|
+
command: "project capture",
|
|
1069
1027
|
});
|
|
1070
1028
|
process.exitCode = 1;
|
|
1071
1029
|
return false;
|
|
@@ -1076,7 +1034,7 @@ async function ensureListingRecorderAvailable(recorderPath) {
|
|
|
1076
1034
|
return true;
|
|
1077
1035
|
}
|
|
1078
1036
|
catch {
|
|
1079
|
-
(0, messages_1.printErrorWithHelp)(`Native recorder binary was not found at ${recorderPath}.`, [
|
|
1037
|
+
(0, messages_1.printErrorWithHelp)(`Native recorder binary was not found at ${recorderPath}.`, ["Start the PlayDrop worker once on this Mac to install the recorder from the PlayDrop CDN."], { command: "project capture" });
|
|
1080
1038
|
process.exitCode = 1;
|
|
1081
1039
|
return false;
|
|
1082
1040
|
}
|
|
@@ -1087,13 +1045,13 @@ function resolveCaptureListingTarget(parsedOptions) {
|
|
|
1087
1045
|
}
|
|
1088
1046
|
catch (error) {
|
|
1089
1047
|
const code = error?.code;
|
|
1090
|
-
const message = error?.message ||
|
|
1091
|
-
const suggestions = code ===
|
|
1048
|
+
const message = error?.message || "Unable to resolve capture target.";
|
|
1049
|
+
const suggestions = code === "app_required"
|
|
1092
1050
|
? ['Use "--app <name>" to choose which app to capture.']
|
|
1093
|
-
: code ===
|
|
1094
|
-
? [
|
|
1095
|
-
: [
|
|
1096
|
-
(0, messages_1.printErrorWithHelp)(message, suggestions, { command:
|
|
1051
|
+
: code === "catalogue_missing"
|
|
1052
|
+
? ["Run the command inside a Playdrop workspace or pass the direct HTML file path."]
|
|
1053
|
+
: ["Provide a valid app target or HTML file path."];
|
|
1054
|
+
(0, messages_1.printErrorWithHelp)(message, suggestions, { command: "project capture" });
|
|
1097
1055
|
process.exitCode = 1;
|
|
1098
1056
|
return null;
|
|
1099
1057
|
}
|
|
@@ -1108,15 +1066,15 @@ async function captureListing(targetArg, options = {}) {
|
|
|
1108
1066
|
}
|
|
1109
1067
|
catch (error) {
|
|
1110
1068
|
const detail = formatCommandError(error instanceof Error ? error : new Error(String(error)));
|
|
1111
|
-
(0, messages_1.printErrorWithHelp)(detail.message, detail.suggestions, { command:
|
|
1069
|
+
(0, messages_1.printErrorWithHelp)(detail.message, detail.suggestions, { command: "project capture" });
|
|
1112
1070
|
process.exitCode = 1;
|
|
1113
1071
|
return;
|
|
1114
1072
|
}
|
|
1115
|
-
if (!ensureCaptureListingToolAvailable(
|
|
1073
|
+
if (!ensureCaptureListingToolAvailable("ffmpeg") || !ensureCaptureListingToolAvailable("ffprobe")) {
|
|
1116
1074
|
return;
|
|
1117
1075
|
}
|
|
1118
1076
|
const recorderPath = resolveListingRecorderPath();
|
|
1119
|
-
if (!await ensureListingRecorderAvailable(recorderPath)) {
|
|
1077
|
+
if (!(await ensureListingRecorderAvailable(recorderPath))) {
|
|
1120
1078
|
return;
|
|
1121
1079
|
}
|
|
1122
1080
|
const resolvedTarget = resolveCaptureListingTarget(parsedOptions);
|
|
@@ -1140,7 +1098,7 @@ async function captureListing(targetArg, options = {}) {
|
|
|
1140
1098
|
}
|
|
1141
1099
|
catch (error) {
|
|
1142
1100
|
const detail = formatCommandError(error instanceof Error ? error : new Error(String(error)));
|
|
1143
|
-
(0, messages_1.printErrorWithHelp)(detail.message, detail.suggestions, { command:
|
|
1101
|
+
(0, messages_1.printErrorWithHelp)(detail.message, detail.suggestions, { command: "project capture" });
|
|
1144
1102
|
process.exitCode = 1;
|
|
1145
1103
|
return;
|
|
1146
1104
|
}
|
|
@@ -1152,15 +1110,15 @@ async function captureListing(targetArg, options = {}) {
|
|
|
1152
1110
|
}
|
|
1153
1111
|
catch (error) {
|
|
1154
1112
|
const detail = formatCommandError(error instanceof Error ? error : new Error(String(error)));
|
|
1155
|
-
(0, messages_1.printErrorWithHelp)(detail.message, detail.suggestions, { command:
|
|
1113
|
+
(0, messages_1.printErrorWithHelp)(detail.message, detail.suggestions, { command: "project capture" });
|
|
1156
1114
|
process.exitCode = 1;
|
|
1157
1115
|
return;
|
|
1158
1116
|
}
|
|
1159
1117
|
const explicitOutputDir = resolveCaptureListingExplicitOutputDir(parsedOptions.outputDirInput, parsedOptions.outputDir, projectDir);
|
|
1160
1118
|
const outputPaths = await ensureOutputPaths(appName, explicitOutputDir ?? resolveWorkerCaptureOutputDir(projectDir, isWorkerGameTask));
|
|
1161
|
-
const devScriptAvailable = Boolean(projectInfo.projectDir && projectInfo.packageJson && typeof projectInfo.packageJson.scripts?.dev ===
|
|
1162
|
-
await (0, commandContext_1.withEnvironment)(
|
|
1163
|
-
let currentUsername =
|
|
1119
|
+
const devScriptAvailable = Boolean(projectInfo.projectDir && projectInfo.packageJson && typeof projectInfo.packageJson.scripts?.dev === "string");
|
|
1120
|
+
await (0, commandContext_1.withEnvironment)("project capture", "Capturing game media", async ({ client, env, envConfig, account, workspaceAuth }) => {
|
|
1121
|
+
let currentUsername = "";
|
|
1164
1122
|
try {
|
|
1165
1123
|
currentUsername = await (0, devShared_1.resolveDevCreatorUsername)({
|
|
1166
1124
|
client,
|
|
@@ -1173,7 +1131,7 @@ async function captureListing(targetArg, options = {}) {
|
|
|
1173
1131
|
return;
|
|
1174
1132
|
}
|
|
1175
1133
|
if (error instanceof types_1.UnsupportedClientError) {
|
|
1176
|
-
(0, http_1.handleUnsupportedError)(error,
|
|
1134
|
+
(0, http_1.handleUnsupportedError)(error, "Authentication");
|
|
1177
1135
|
process.exitCode = 1;
|
|
1178
1136
|
return;
|
|
1179
1137
|
}
|
|
@@ -1181,12 +1139,12 @@ async function captureListing(targetArg, options = {}) {
|
|
|
1181
1139
|
(0, messages_1.printErrorWithHelp)(`Could not fetch your account (status ${error.status}).`, [
|
|
1182
1140
|
'Run "playdrop auth login" to refresh your session.',
|
|
1183
1141
|
'Use "playdrop auth whoami" afterwards to verify the current account.',
|
|
1184
|
-
], { command:
|
|
1142
|
+
], { command: "project capture" });
|
|
1185
1143
|
process.exitCode = 1;
|
|
1186
1144
|
return;
|
|
1187
1145
|
}
|
|
1188
1146
|
if ((0, devShared_1.isNetworkError)(error)) {
|
|
1189
|
-
(0, messages_1.printNetworkIssue)(
|
|
1147
|
+
(0, messages_1.printNetworkIssue)("Could not reach the Playdrop API to resolve your account.", "project capture");
|
|
1190
1148
|
process.exitCode = 1;
|
|
1191
1149
|
return;
|
|
1192
1150
|
}
|
|
@@ -1201,17 +1159,17 @@ async function captureListing(targetArg, options = {}) {
|
|
|
1201
1159
|
return;
|
|
1202
1160
|
}
|
|
1203
1161
|
if (error instanceof types_1.UnsupportedClientError) {
|
|
1204
|
-
(0, http_1.handleUnsupportedError)(error,
|
|
1162
|
+
(0, http_1.handleUnsupportedError)(error, "Game media capture");
|
|
1205
1163
|
process.exitCode = 1;
|
|
1206
1164
|
return;
|
|
1207
1165
|
}
|
|
1208
1166
|
if (error instanceof types_1.ApiError && error.status === 404) {
|
|
1209
|
-
(0, messages_1.printErrorWithHelp)(`App ${currentUsername}/${appName} is not registered on ${env}.`, [`Run "playdrop project create app ${appName}" before using listing capture.`], { command:
|
|
1167
|
+
(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" });
|
|
1210
1168
|
process.exitCode = 1;
|
|
1211
1169
|
return;
|
|
1212
1170
|
}
|
|
1213
1171
|
if ((0, devShared_1.isNetworkError)(error)) {
|
|
1214
|
-
(0, messages_1.printNetworkIssue)(
|
|
1172
|
+
(0, messages_1.printNetworkIssue)("Could not verify the app registration.", "project capture");
|
|
1215
1173
|
process.exitCode = 1;
|
|
1216
1174
|
return;
|
|
1217
1175
|
}
|
|
@@ -1222,7 +1180,7 @@ async function captureListing(targetArg, options = {}) {
|
|
|
1222
1180
|
captureSession = (0, taskCaptureSession_1.resolveTaskCaptureSession)(workspaceAuth, registeredApp);
|
|
1223
1181
|
}
|
|
1224
1182
|
catch (error) {
|
|
1225
|
-
(0, messages_1.printErrorWithHelp)(error?.message ||
|
|
1183
|
+
(0, messages_1.printErrorWithHelp)(error?.message || "Worker capture session is invalid.", ["Run this command from the active worker task workspace."], { command: "project capture" });
|
|
1226
1184
|
process.exitCode = 1;
|
|
1227
1185
|
return;
|
|
1228
1186
|
}
|
|
@@ -1230,7 +1188,7 @@ async function captureListing(targetArg, options = {}) {
|
|
|
1230
1188
|
console.log(`[listing] Preparing ${entryLabel} for ${env} (${appTypeSlug}).`);
|
|
1231
1189
|
const taskLookup = (0, catalogue_1.findAppTaskByFile)(resolvedTarget.htmlPath);
|
|
1232
1190
|
if (taskLookup.errors.length > 0) {
|
|
1233
|
-
(0, messages_1.printErrorWithHelp)(taskLookup.errors[0] ||
|
|
1191
|
+
(0, messages_1.printErrorWithHelp)(taskLookup.errors[0] || "Failed to resolve the app task from catalogue.json.", taskLookup.errors.slice(1), { command: "project capture" });
|
|
1234
1192
|
process.exitCode = 1;
|
|
1235
1193
|
return;
|
|
1236
1194
|
}
|
|
@@ -1243,7 +1201,7 @@ async function captureListing(targetArg, options = {}) {
|
|
|
1243
1201
|
runtimeBundleHash = (await (0, build_1.buildApp)(captureTask)).bundleHash;
|
|
1244
1202
|
}
|
|
1245
1203
|
catch (error) {
|
|
1246
|
-
(0, messages_1.printErrorWithHelp)(error instanceof Error ? error.message : String(error), [
|
|
1204
|
+
(0, messages_1.printErrorWithHelp)(error instanceof Error ? error.message : String(error), ["Fix the game build before recording final gameplay."], { command: "project capture" });
|
|
1247
1205
|
process.exitCode = 1;
|
|
1248
1206
|
return;
|
|
1249
1207
|
}
|
|
@@ -1264,7 +1222,7 @@ async function captureListing(targetArg, options = {}) {
|
|
|
1264
1222
|
}
|
|
1265
1223
|
catch (error) {
|
|
1266
1224
|
const detail = formatCommandError(error instanceof Error ? error : new Error(String(error)));
|
|
1267
|
-
(0, messages_1.printErrorWithHelp)(detail.message, detail.suggestions, { command:
|
|
1225
|
+
(0, messages_1.printErrorWithHelp)(detail.message, detail.suggestions, { command: "project capture" });
|
|
1268
1226
|
process.exitCode = 1;
|
|
1269
1227
|
return;
|
|
1270
1228
|
}
|
|
@@ -1282,12 +1240,12 @@ async function captureListing(targetArg, options = {}) {
|
|
|
1282
1240
|
apiBase: envConfig.apiBase,
|
|
1283
1241
|
task: taskLookup.task,
|
|
1284
1242
|
creatorUsername: currentUsername,
|
|
1285
|
-
appBaseUrl: new URL(
|
|
1243
|
+
appBaseUrl: new URL(".", localDevAppUrl).toString(),
|
|
1286
1244
|
});
|
|
1287
1245
|
}
|
|
1288
1246
|
catch (error) {
|
|
1289
1247
|
const formatted = (0, dev_1.formatDevRuntimeAssetManifestFailure)(error);
|
|
1290
|
-
(0, messages_1.printErrorWithHelp)(formatted.message, formatted.suggestions, { command:
|
|
1248
|
+
(0, messages_1.printErrorWithHelp)(formatted.message, formatted.suggestions, { command: "project capture" });
|
|
1291
1249
|
process.exitCode = 1;
|
|
1292
1250
|
return;
|
|
1293
1251
|
}
|
|
@@ -1345,9 +1303,9 @@ async function captureListing(targetArg, options = {}) {
|
|
|
1345
1303
|
}
|
|
1346
1304
|
}
|
|
1347
1305
|
if (devServerStartedByCapture) {
|
|
1348
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
1306
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1349
1307
|
}
|
|
1350
|
-
const webBase = envConfig.webBase ??
|
|
1308
|
+
const webBase = envConfig.webBase ?? "https://www.playdrop.ai";
|
|
1351
1309
|
const routeSegment = resolveListingCaptureRouteSegment(declaresPreviewSupport);
|
|
1352
1310
|
const localDevPort = resolveListingCaptureLocalDevPort(devRouterPort);
|
|
1353
1311
|
const frameUrl = buildListingCaptureFrameUrl({
|
|
@@ -1362,7 +1320,7 @@ async function captureListing(targetArg, options = {}) {
|
|
|
1362
1320
|
const frameUrlObject = new URL(frameUrl);
|
|
1363
1321
|
const reportFrameUrl = (0, appUrls_1.redactTaskCaptureSession)(frameUrl);
|
|
1364
1322
|
console.log(`[listing] Opening ${reportFrameUrl}`);
|
|
1365
|
-
const shouldExportPreviewAudio = parsedOptions.audio && routeSegment ===
|
|
1323
|
+
const shouldExportPreviewAudio = parsedOptions.audio && routeSegment === "dev-preview";
|
|
1366
1324
|
const surfaces = resolvedTarget.surfaceTargets.list;
|
|
1367
1325
|
const useSurfacePrefix = surfaces.length > 1;
|
|
1368
1326
|
const captures = [];
|
|
@@ -1373,7 +1331,7 @@ async function captureListing(targetArg, options = {}) {
|
|
|
1373
1331
|
const dimensions = resolveCaptureDimensions(surface, parsedOptions);
|
|
1374
1332
|
const captureSceneId = resolveListingCaptureSceneId(dimensions.width, dimensions.height);
|
|
1375
1333
|
const surfaceOutputPaths = buildSurfaceOutputPaths(outputPaths.outputDir, surface, useSurfacePrefix);
|
|
1376
|
-
const validationPrefix = useSurfacePrefix ? `${surface.toLowerCase().replace(/_/g,
|
|
1334
|
+
const validationPrefix = useSurfacePrefix ? `${surface.toLowerCase().replace(/_/g, "-")}-` : "";
|
|
1377
1335
|
const validationPaths = {
|
|
1378
1336
|
reference: (0, node_path_1.join)(outputPaths.outputDir, `.${validationPrefix}capture-reference.png`),
|
|
1379
1337
|
first: (0, node_path_1.join)(outputPaths.outputDir, `.${validationPrefix}capture-first.png`),
|
|
@@ -1383,9 +1341,9 @@ async function captureListing(targetArg, options = {}) {
|
|
|
1383
1341
|
const contextOptions = resolveListingCaptureBrowserContextOptions(surface, dimensions);
|
|
1384
1342
|
browserHandle = await launchListingBrowser(dimensions, frameUrlObject.origin, contextOptions);
|
|
1385
1343
|
try {
|
|
1386
|
-
await browserHandle.page.goto(frameUrl, { waitUntil:
|
|
1344
|
+
await browserHandle.page.goto(frameUrl, { waitUntil: "domcontentloaded" });
|
|
1387
1345
|
await browserHandle.page.waitForTimeout(1000);
|
|
1388
|
-
if (routeSegment ===
|
|
1346
|
+
if (routeSegment === "dev-preview") {
|
|
1389
1347
|
console.log(`[listing] Preparing preview scene ${captureSceneId}.`);
|
|
1390
1348
|
await prepareHostedListingScene(browserHandle.page, captureSceneId);
|
|
1391
1349
|
await browserHandle.page.waitForTimeout(1000);
|
|
@@ -1393,13 +1351,13 @@ async function captureListing(targetArg, options = {}) {
|
|
|
1393
1351
|
const measurement = await fitWindowToRequestedGameplay(browserHandle.page, dimensions.width, dimensions.height);
|
|
1394
1352
|
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)}.`);
|
|
1395
1353
|
if (shouldExportPreviewAudio) {
|
|
1396
|
-
console.log(
|
|
1354
|
+
console.log("[listing] Checking the in-app preview audio export contract.");
|
|
1397
1355
|
await assertHostedListingAudioCaptureContract(browserHandle.page, outputPaths.outputDir);
|
|
1398
|
-
console.log(
|
|
1356
|
+
console.log("[listing] Starting in-app preview audio capture.");
|
|
1399
1357
|
await startHostedListingAudioCapture(browserHandle.page);
|
|
1400
1358
|
}
|
|
1401
1359
|
await browserHandle.page.waitForTimeout(750);
|
|
1402
|
-
console.log(
|
|
1360
|
+
console.log("[listing] Waiting for the machine capture lease.");
|
|
1403
1361
|
const captureLease = await (0, listingCaptureLease_1.acquireListingCaptureLease)({
|
|
1404
1362
|
deadlineAt: captureDeadlineAt,
|
|
1405
1363
|
workspaceDir: (0, node_path_1.dirname)(resolvedTarget.htmlPath),
|
|
@@ -1430,11 +1388,11 @@ async function captureListing(targetArg, options = {}) {
|
|
|
1430
1388
|
const finalDurationSeconds = readDurationSeconds(finalProbe);
|
|
1431
1389
|
const finalAudioTrackCount = countAudioStreams(finalProbe);
|
|
1432
1390
|
const finalFps = parseFrameRate(finalVideoStream.avg_frame_rate);
|
|
1433
|
-
ensureTolerance(finalVideoStream.width, dimensions.width, 0,
|
|
1434
|
-
ensureTolerance(finalVideoStream.height, dimensions.height, 0,
|
|
1435
|
-
ensureTolerance(finalDurationSeconds, parsedOptions.durationSeconds, 1.5,
|
|
1391
|
+
ensureTolerance(finalVideoStream.width, dimensions.width, 0, "final_width_mismatch");
|
|
1392
|
+
ensureTolerance(finalVideoStream.height, dimensions.height, 0, "final_height_mismatch");
|
|
1393
|
+
ensureTolerance(finalDurationSeconds, parsedOptions.durationSeconds, 1.5, "final_duration_mismatch");
|
|
1436
1394
|
if (parsedOptions.audio && finalAudioTrackCount === 0) {
|
|
1437
|
-
throw new Error(
|
|
1395
|
+
throw new Error("audio_track_missing");
|
|
1438
1396
|
}
|
|
1439
1397
|
extractListingValidationFrame(surfaceOutputPaths.finalVideoPath, Math.min(0.1, finalDurationSeconds / 4), validationPaths.first, captureDeadlineAt);
|
|
1440
1398
|
extractListingValidationFrame(surfaceOutputPaths.finalVideoPath, Math.max(0, finalDurationSeconds - 0.1), validationPaths.last, captureDeadlineAt);
|
|
@@ -1450,8 +1408,10 @@ async function captureListing(targetArg, options = {}) {
|
|
|
1450
1408
|
for (const warning of warnings) {
|
|
1451
1409
|
allWarnings.push(`${surface}: ${warning}`);
|
|
1452
1410
|
}
|
|
1453
|
-
artifactHashes[(0, node_path_1.relative)(outputPaths.outputDir, surfaceOutputPaths.finalVideoPath) ||
|
|
1454
|
-
|
|
1411
|
+
artifactHashes[(0, node_path_1.relative)(outputPaths.outputDir, surfaceOutputPaths.finalVideoPath) || "listing.mp4"] =
|
|
1412
|
+
await computeFileSha256(surfaceOutputPaths.finalVideoPath);
|
|
1413
|
+
artifactHashes[(0, node_path_1.relative)(outputPaths.outputDir, surfaceOutputPaths.posterPath) || "poster.png"] =
|
|
1414
|
+
await computeFileSha256(surfaceOutputPaths.posterPath);
|
|
1455
1415
|
capturedSurfaces.push(surface);
|
|
1456
1416
|
captures.push({
|
|
1457
1417
|
surface,
|
|
@@ -1512,11 +1472,11 @@ async function captureListing(targetArg, options = {}) {
|
|
|
1512
1472
|
binding: {
|
|
1513
1473
|
artifactHashes,
|
|
1514
1474
|
capturedSurfaces,
|
|
1515
|
-
pixelValidation:
|
|
1475
|
+
pixelValidation: "passed",
|
|
1516
1476
|
runtimeBundleHash,
|
|
1517
1477
|
},
|
|
1518
1478
|
};
|
|
1519
|
-
await (0, promises_1.writeFile)(outputPaths.reportPath, `${JSON.stringify(report, null, 2)}\n`,
|
|
1479
|
+
await (0, promises_1.writeFile)(outputPaths.reportPath, `${JSON.stringify(report, null, 2)}\n`, "utf8");
|
|
1520
1480
|
console.log(`[listing] Saved report to ${(0, node_path_1.relative)(process.cwd(), outputPaths.reportPath) || outputPaths.reportPath}`);
|
|
1521
1481
|
}
|
|
1522
1482
|
catch (error) {
|
|
@@ -1524,12 +1484,12 @@ async function captureListing(targetArg, options = {}) {
|
|
|
1524
1484
|
return;
|
|
1525
1485
|
}
|
|
1526
1486
|
if (error instanceof types_1.UnsupportedClientError) {
|
|
1527
|
-
(0, http_1.handleUnsupportedError)(error,
|
|
1487
|
+
(0, http_1.handleUnsupportedError)(error, "Game media capture");
|
|
1528
1488
|
process.exitCode = 1;
|
|
1529
1489
|
return;
|
|
1530
1490
|
}
|
|
1531
1491
|
const detail = formatCommandError(error instanceof Error ? error : new Error(String(error)));
|
|
1532
|
-
(0, messages_1.printErrorWithHelp)(detail.message, detail.suggestions, { command:
|
|
1492
|
+
(0, messages_1.printErrorWithHelp)(detail.message, detail.suggestions, { command: "project capture" });
|
|
1533
1493
|
process.exitCode = 1;
|
|
1534
1494
|
}
|
|
1535
1495
|
finally {
|