@playdrop/playdrop-cli 0.16.2 → 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
|
@@ -27,14 +27,14 @@ const dev_1 = require("./dev");
|
|
|
27
27
|
const devRuntimeAssets_1 = require("./devRuntimeAssets");
|
|
28
28
|
const devServer_1 = require("./devServer");
|
|
29
29
|
const devShared_1 = require("./devShared");
|
|
30
|
-
exports.MARKETING_SURFACES = [
|
|
31
|
-
exports.MARKETING_AUDIO_POLICIES = [
|
|
32
|
-
const MOBILE_USER_AGENT =
|
|
30
|
+
exports.MARKETING_SURFACES = ["desktop", "mobile-landscape", "mobile-portrait"];
|
|
31
|
+
exports.MARKETING_AUDIO_POLICIES = ["music-and-sfx", "sfx-only", "silent"];
|
|
32
|
+
const 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";
|
|
33
33
|
const SURFACE_PRESETS = {
|
|
34
34
|
desktop: {
|
|
35
35
|
width: 1920,
|
|
36
36
|
height: 1080,
|
|
37
|
-
sceneId:
|
|
37
|
+
sceneId: "listing-landscape",
|
|
38
38
|
viewport: {
|
|
39
39
|
width: 1280,
|
|
40
40
|
height: 720,
|
|
@@ -43,10 +43,10 @@ const SURFACE_PRESETS = {
|
|
|
43
43
|
hasTouch: false,
|
|
44
44
|
},
|
|
45
45
|
},
|
|
46
|
-
|
|
46
|
+
"mobile-landscape": {
|
|
47
47
|
width: 1920,
|
|
48
48
|
height: 1080,
|
|
49
|
-
sceneId:
|
|
49
|
+
sceneId: "listing-landscape",
|
|
50
50
|
viewport: {
|
|
51
51
|
width: 896,
|
|
52
52
|
height: 414,
|
|
@@ -56,10 +56,10 @@ const SURFACE_PRESETS = {
|
|
|
56
56
|
userAgent: MOBILE_USER_AGENT,
|
|
57
57
|
},
|
|
58
58
|
},
|
|
59
|
-
|
|
59
|
+
"mobile-portrait": {
|
|
60
60
|
width: 1080,
|
|
61
61
|
height: 1920,
|
|
62
|
-
sceneId:
|
|
62
|
+
sceneId: "listing-portrait",
|
|
63
63
|
viewport: {
|
|
64
64
|
width: 414,
|
|
65
65
|
height: 896,
|
|
@@ -73,7 +73,7 @@ const SURFACE_PRESETS = {
|
|
|
73
73
|
const CAPTURE_FRAME_SELECTOR = 'iframe[title="Game"]';
|
|
74
74
|
const DEFAULT_DURATION_SECONDS = 12;
|
|
75
75
|
const DEFAULT_FPS = 60;
|
|
76
|
-
const DEFAULT_AUDIO_POLICY =
|
|
76
|
+
const DEFAULT_AUDIO_POLICY = "music-and-sfx";
|
|
77
77
|
const MIN_DURATION_SECONDS = 1;
|
|
78
78
|
const MAX_DURATION_SECONDS = 300;
|
|
79
79
|
const MIN_FPS = 1;
|
|
@@ -85,7 +85,7 @@ function parsePositiveNumber(raw, defaultValue, fieldName, minimum, maximum) {
|
|
|
85
85
|
if (raw === undefined) {
|
|
86
86
|
return defaultValue;
|
|
87
87
|
}
|
|
88
|
-
const parsed = typeof raw ===
|
|
88
|
+
const parsed = typeof raw === "number" ? raw : Number.parseFloat(String(raw));
|
|
89
89
|
if (!Number.isFinite(parsed)) {
|
|
90
90
|
throw new Error(`${fieldName}_invalid`);
|
|
91
91
|
}
|
|
@@ -95,7 +95,7 @@ function parsePositiveNumber(raw, defaultValue, fieldName, minimum, maximum) {
|
|
|
95
95
|
return parsed;
|
|
96
96
|
}
|
|
97
97
|
function normalizeSurface(raw) {
|
|
98
|
-
const normalized = raw.trim().toLowerCase().replace(/[_\s]/g,
|
|
98
|
+
const normalized = raw.trim().toLowerCase().replace(/[_\s]/g, "-");
|
|
99
99
|
if (exports.MARKETING_SURFACES.includes(normalized)) {
|
|
100
100
|
return normalized;
|
|
101
101
|
}
|
|
@@ -105,7 +105,7 @@ function parseSurfaces(raw) {
|
|
|
105
105
|
if (!raw?.trim()) {
|
|
106
106
|
return [...exports.MARKETING_SURFACES];
|
|
107
107
|
}
|
|
108
|
-
const surfaces = raw.split(
|
|
108
|
+
const surfaces = raw.split(",").map(normalizeSurface);
|
|
109
109
|
const seen = new Set();
|
|
110
110
|
const unique = [];
|
|
111
111
|
for (const surface of surfaces) {
|
|
@@ -121,11 +121,11 @@ function parseAudioPolicy(raw) {
|
|
|
121
121
|
if (exports.MARKETING_AUDIO_POLICIES.includes(normalized)) {
|
|
122
122
|
return normalized;
|
|
123
123
|
}
|
|
124
|
-
throw new Error(
|
|
124
|
+
throw new Error("audio_policy_invalid");
|
|
125
125
|
}
|
|
126
126
|
function parseMarketingCaptureOptions(targetArg, options = {}) {
|
|
127
|
-
const durationSeconds = parsePositiveNumber(options.duration, DEFAULT_DURATION_SECONDS,
|
|
128
|
-
const fps = parsePositiveNumber(options.fps, DEFAULT_FPS,
|
|
127
|
+
const durationSeconds = parsePositiveNumber(options.duration, DEFAULT_DURATION_SECONDS, "duration", MIN_DURATION_SECONDS, MAX_DURATION_SECONDS);
|
|
128
|
+
const fps = parsePositiveNumber(options.fps, DEFAULT_FPS, "fps", MIN_FPS, MAX_FPS);
|
|
129
129
|
const outputDir = options.outputDir?.trim() ? (0, node_path_1.resolve)(process.cwd(), options.outputDir.trim()) : null;
|
|
130
130
|
const seed = options.seed?.trim() || `marketing-${new Date().toISOString().slice(0, 10)}`;
|
|
131
131
|
return {
|
|
@@ -142,40 +142,40 @@ function parseMarketingCaptureOptions(targetArg, options = {}) {
|
|
|
142
142
|
};
|
|
143
143
|
}
|
|
144
144
|
function assertSupportedMarketingCapturePlatform(platform = process.platform) {
|
|
145
|
-
if (platform !==
|
|
145
|
+
if (platform !== "darwin" && platform !== "win32") {
|
|
146
146
|
throw new Error(`unsupported_marketing_capture_platform:${platform}`);
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
149
|
function resolveMarketingOutputPaths(projectRoot, _appName, explicitOutputDir) {
|
|
150
|
-
const rootDir = explicitOutputDir ?? (0, node_path_1.join)(projectRoot,
|
|
151
|
-
const marketingRoot = (0, node_path_1.join)(projectRoot,
|
|
150
|
+
const rootDir = explicitOutputDir ?? (0, node_path_1.join)(projectRoot, "assets", "marketing");
|
|
151
|
+
const marketingRoot = (0, node_path_1.join)(projectRoot, "assets", "marketing");
|
|
152
152
|
const rootRelativeToMarketing = (0, node_path_1.relative)(marketingRoot, rootDir);
|
|
153
|
-
if (rootRelativeToMarketing ===
|
|
153
|
+
if (rootRelativeToMarketing === ".." ||
|
|
154
|
+
rootRelativeToMarketing.startsWith(`..${node_path_1.sep}`) ||
|
|
155
|
+
(0, node_path_1.isAbsolute)(rootRelativeToMarketing)) {
|
|
154
156
|
throw new Error(`marketing_output_outside_assets:${rootDir}`);
|
|
155
157
|
}
|
|
156
158
|
return {
|
|
157
159
|
rootDir,
|
|
158
|
-
capturesDir: (0, node_path_1.join)(rootDir,
|
|
159
|
-
playdropDir: (0, node_path_1.join)(rootDir,
|
|
160
|
-
socialDir: (0, node_path_1.join)(rootDir,
|
|
161
|
-
thumbnailsDir: (0, node_path_1.join)(rootDir,
|
|
162
|
-
screenshotsDir: (0, node_path_1.join)(rootDir,
|
|
163
|
-
manifestPath: (0, node_path_1.join)(rootDir,
|
|
164
|
-
reportPath: (0, node_path_1.join)(rootDir,
|
|
160
|
+
capturesDir: (0, node_path_1.join)(rootDir, "captures"),
|
|
161
|
+
playdropDir: (0, node_path_1.join)(rootDir, "playdrop"),
|
|
162
|
+
socialDir: (0, node_path_1.join)(rootDir, "social"),
|
|
163
|
+
thumbnailsDir: (0, node_path_1.join)(rootDir, "thumbnails"),
|
|
164
|
+
screenshotsDir: (0, node_path_1.join)(rootDir, "screenshots"),
|
|
165
|
+
manifestPath: (0, node_path_1.join)(rootDir, "capture-manifest.json"),
|
|
166
|
+
reportPath: (0, node_path_1.join)(rootDir, "marketing-report.json"),
|
|
165
167
|
};
|
|
166
168
|
}
|
|
167
169
|
function validateMarketingPreviewConfig(rawApp) {
|
|
168
170
|
const errors = [];
|
|
169
|
-
const record = rawApp && typeof rawApp ===
|
|
171
|
+
const record = rawApp && typeof rawApp === "object" ? rawApp : {};
|
|
170
172
|
if (record.previewable !== true) {
|
|
171
|
-
errors.push(
|
|
173
|
+
errors.push("catalogue_previewable_missing");
|
|
172
174
|
}
|
|
173
|
-
const preview = record.preview && typeof record.preview ===
|
|
174
|
-
|
|
175
|
-
: null;
|
|
176
|
-
const audioPolicy = typeof preview?.audioPolicy === 'string' ? preview.audioPolicy.trim() : '';
|
|
175
|
+
const preview = record.preview && typeof record.preview === "object" ? record.preview : null;
|
|
176
|
+
const audioPolicy = typeof preview?.audioPolicy === "string" ? preview.audioPolicy.trim() : "";
|
|
177
177
|
if (!audioPolicy) {
|
|
178
|
-
errors.push(
|
|
178
|
+
errors.push("catalogue_preview_audio_policy_missing");
|
|
179
179
|
}
|
|
180
180
|
else if (!exports.MARKETING_AUDIO_POLICIES.includes(audioPolicy)) {
|
|
181
181
|
errors.push(`catalogue_preview_audio_policy_invalid:${audioPolicy}`);
|
|
@@ -187,68 +187,68 @@ function buildMarketingFfmpegArgs(input) {
|
|
|
187
187
|
`crop=${input.crop.width}:${input.crop.height}:${input.crop.x}:${input.crop.y}`,
|
|
188
188
|
`scale=${input.output.width}:${input.output.height}:flags=lanczos`,
|
|
189
189
|
`fps=${input.fps}`,
|
|
190
|
-
].join(
|
|
190
|
+
].join(",");
|
|
191
191
|
const commonTail = [
|
|
192
|
-
|
|
192
|
+
"-t",
|
|
193
193
|
String(input.durationSeconds),
|
|
194
|
-
|
|
194
|
+
"-vf",
|
|
195
195
|
videoFilter,
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
196
|
+
"-c:v",
|
|
197
|
+
"libx264",
|
|
198
|
+
"-preset",
|
|
199
|
+
"slow",
|
|
200
|
+
"-crf",
|
|
201
|
+
"16",
|
|
202
|
+
"-pix_fmt",
|
|
203
|
+
"yuv420p",
|
|
204
|
+
"-movflags",
|
|
205
|
+
"+faststart",
|
|
206
206
|
input.output.path,
|
|
207
207
|
];
|
|
208
|
-
if (input.platform ===
|
|
209
|
-
const device = input.inputDevice?.trim() || process.env.PLAYDROP_MARKETING_CAPTURE_AVFOUNDATION_DEVICE?.trim() ||
|
|
208
|
+
if (input.platform === "darwin") {
|
|
209
|
+
const device = input.inputDevice?.trim() || process.env.PLAYDROP_MARKETING_CAPTURE_AVFOUNDATION_DEVICE?.trim() || "1";
|
|
210
210
|
return [
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
211
|
+
"-y",
|
|
212
|
+
"-f",
|
|
213
|
+
"avfoundation",
|
|
214
|
+
"-framerate",
|
|
215
215
|
String(input.fps),
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
216
|
+
"-capture_cursor",
|
|
217
|
+
"0",
|
|
218
|
+
"-i",
|
|
219
219
|
`${device}:none`,
|
|
220
220
|
...commonTail,
|
|
221
221
|
];
|
|
222
222
|
}
|
|
223
|
-
if (input.platform ===
|
|
223
|
+
if (input.platform === "win32") {
|
|
224
224
|
return [
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
225
|
+
"-y",
|
|
226
|
+
"-f",
|
|
227
|
+
"gdigrab",
|
|
228
|
+
"-framerate",
|
|
229
229
|
String(input.fps),
|
|
230
|
-
|
|
230
|
+
"-offset_x",
|
|
231
231
|
String(input.crop.x),
|
|
232
|
-
|
|
232
|
+
"-offset_y",
|
|
233
233
|
String(input.crop.y),
|
|
234
|
-
|
|
234
|
+
"-video_size",
|
|
235
235
|
`${input.crop.width}x${input.crop.height}`,
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
236
|
+
"-i",
|
|
237
|
+
"desktop",
|
|
238
|
+
"-t",
|
|
239
239
|
String(input.durationSeconds),
|
|
240
|
-
|
|
240
|
+
"-vf",
|
|
241
241
|
`scale=${input.output.width}:${input.output.height}:flags=lanczos,fps=${input.fps}`,
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
242
|
+
"-c:v",
|
|
243
|
+
"libx264",
|
|
244
|
+
"-preset",
|
|
245
|
+
"slow",
|
|
246
|
+
"-crf",
|
|
247
|
+
"16",
|
|
248
|
+
"-pix_fmt",
|
|
249
|
+
"yuv420p",
|
|
250
|
+
"-movflags",
|
|
251
|
+
"+faststart",
|
|
252
252
|
input.output.path,
|
|
253
253
|
];
|
|
254
254
|
}
|
|
@@ -258,7 +258,7 @@ function buildMarketingCaptureManifest(input) {
|
|
|
258
258
|
return {
|
|
259
259
|
appName: input.appName,
|
|
260
260
|
createdAt: input.createdAt,
|
|
261
|
-
captureSource:
|
|
261
|
+
captureSource: "playdrop-cli-local-screen",
|
|
262
262
|
captureEngine: input.captureEngine,
|
|
263
263
|
audioPolicy: input.audioPolicy,
|
|
264
264
|
seed: input.seed,
|
|
@@ -266,53 +266,53 @@ function buildMarketingCaptureManifest(input) {
|
|
|
266
266
|
};
|
|
267
267
|
}
|
|
268
268
|
function getMarketingCaptureEngine(platform) {
|
|
269
|
-
if (platform ===
|
|
270
|
-
return
|
|
269
|
+
if (platform === "darwin") {
|
|
270
|
+
return "avfoundation";
|
|
271
271
|
}
|
|
272
|
-
if (platform ===
|
|
273
|
-
return
|
|
272
|
+
if (platform === "win32") {
|
|
273
|
+
return "gdigrab";
|
|
274
274
|
}
|
|
275
275
|
throw new Error(`unsupported_marketing_capture_platform:${platform}`);
|
|
276
276
|
}
|
|
277
277
|
function commandExists(command) {
|
|
278
|
-
const result = (0, node_child_process_1.spawnSync)(command, [
|
|
279
|
-
encoding:
|
|
278
|
+
const result = (0, node_child_process_1.spawnSync)(command, ["-version"], {
|
|
279
|
+
encoding: "utf8",
|
|
280
280
|
maxBuffer: 4 * 1024 * 1024,
|
|
281
281
|
});
|
|
282
282
|
return result.status === 0;
|
|
283
283
|
}
|
|
284
284
|
function runTool(command, args, failureCode) {
|
|
285
285
|
const result = (0, node_child_process_1.spawnSync)(command, args, {
|
|
286
|
-
encoding:
|
|
286
|
+
encoding: "utf8",
|
|
287
287
|
maxBuffer: 20 * 1024 * 1024,
|
|
288
288
|
});
|
|
289
289
|
if (result.status !== 0) {
|
|
290
|
-
const stdout = result.stdout?.trim() ||
|
|
291
|
-
const stderr = result.stderr?.trim() ||
|
|
292
|
-
const combined = [stdout, stderr].filter(Boolean).join(
|
|
290
|
+
const stdout = result.stdout?.trim() || "";
|
|
291
|
+
const stderr = result.stderr?.trim() || "";
|
|
292
|
+
const combined = [stdout, stderr].filter(Boolean).join("\n");
|
|
293
293
|
throw new Error(`${failureCode}:${combined}`);
|
|
294
294
|
}
|
|
295
295
|
return {
|
|
296
|
-
stdout: result.stdout ||
|
|
297
|
-
stderr: result.stderr ||
|
|
296
|
+
stdout: result.stdout || "",
|
|
297
|
+
stderr: result.stderr || "",
|
|
298
298
|
};
|
|
299
299
|
}
|
|
300
300
|
function readFfmpegCapabilities() {
|
|
301
|
-
const encoders = runTool(
|
|
302
|
-
const formats = runTool(
|
|
301
|
+
const encoders = runTool("ffmpeg", ["-hide_banner", "-encoders"], "ffmpeg_probe_failed");
|
|
302
|
+
const formats = runTool("ffmpeg", ["-hide_banner", "-formats"], "ffmpeg_probe_failed");
|
|
303
303
|
return `${encoders.stdout}\n${encoders.stderr}\n${formats.stdout}\n${formats.stderr}`;
|
|
304
304
|
}
|
|
305
305
|
function validateFfmpegCapabilities(platform) {
|
|
306
306
|
const capabilities = readFfmpegCapabilities();
|
|
307
307
|
const errors = [];
|
|
308
308
|
if (!/\blibx264\b/.test(capabilities)) {
|
|
309
|
-
errors.push(
|
|
309
|
+
errors.push("ffmpeg_libx264_missing");
|
|
310
310
|
}
|
|
311
|
-
if (platform ===
|
|
312
|
-
errors.push(
|
|
311
|
+
if (platform === "darwin" && !/\bavfoundation\b/.test(capabilities)) {
|
|
312
|
+
errors.push("ffmpeg_avfoundation_missing");
|
|
313
313
|
}
|
|
314
|
-
if (platform ===
|
|
315
|
-
errors.push(
|
|
314
|
+
if (platform === "win32" && !/\bgdigrab\b/.test(capabilities)) {
|
|
315
|
+
errors.push("ffmpeg_gdigrab_missing");
|
|
316
316
|
}
|
|
317
317
|
return errors;
|
|
318
318
|
}
|
|
@@ -328,125 +328,125 @@ function loadCatalogueApp(cataloguePath, appName) {
|
|
|
328
328
|
if (!cataloguePath || !(0, node_fs_1.existsSync)(cataloguePath)) {
|
|
329
329
|
return null;
|
|
330
330
|
}
|
|
331
|
-
const raw = JSON.parse((0, node_fs_1.readFileSync)(cataloguePath,
|
|
332
|
-
const apps = raw && typeof raw ===
|
|
331
|
+
const raw = JSON.parse((0, node_fs_1.readFileSync)(cataloguePath, "utf8"));
|
|
332
|
+
const apps = raw && typeof raw === "object" && Array.isArray(raw.apps)
|
|
333
333
|
? raw.apps
|
|
334
334
|
: Array.isArray(raw)
|
|
335
335
|
? raw
|
|
336
336
|
: [];
|
|
337
|
-
return apps.find(entry => entry && typeof entry ===
|
|
337
|
+
return (apps.find((entry) => entry && typeof entry === "object" && entry.name === appName) ?? null);
|
|
338
338
|
}
|
|
339
339
|
function formatMarketingError(error) {
|
|
340
|
-
if (error.message.startsWith(
|
|
341
|
-
const platform = error.message.split(
|
|
340
|
+
if (error.message.startsWith("unsupported_marketing_capture_platform:")) {
|
|
341
|
+
const platform = error.message.split(":")[1] || process.platform;
|
|
342
342
|
return {
|
|
343
343
|
message: `Marketing capture only supports macOS and Windows in v1. Current platform: ${platform}.`,
|
|
344
344
|
suggestions: [],
|
|
345
345
|
};
|
|
346
346
|
}
|
|
347
|
-
if (error.message ===
|
|
347
|
+
if (error.message === "ffmpeg_missing") {
|
|
348
348
|
return {
|
|
349
|
-
message:
|
|
350
|
-
suggestions: [
|
|
349
|
+
message: "ffmpeg is required for local marketing capture.",
|
|
350
|
+
suggestions: ["Install ffmpeg and make sure it is available on PATH."],
|
|
351
351
|
};
|
|
352
352
|
}
|
|
353
|
-
if (error.message ===
|
|
353
|
+
if (error.message === "ffprobe_missing") {
|
|
354
354
|
return {
|
|
355
|
-
message:
|
|
356
|
-
suggestions: [
|
|
355
|
+
message: "ffprobe is required for local marketing capture.",
|
|
356
|
+
suggestions: ["Install ffmpeg and make sure ffprobe is available on PATH."],
|
|
357
357
|
};
|
|
358
358
|
}
|
|
359
|
-
if (error.message ===
|
|
359
|
+
if (error.message === "ffmpeg_libx264_missing") {
|
|
360
360
|
return {
|
|
361
|
-
message:
|
|
362
|
-
suggestions: [
|
|
361
|
+
message: "ffmpeg must support the libx264 encoder for marketing capture.",
|
|
362
|
+
suggestions: ["Install a full ffmpeg build with libx264 enabled."],
|
|
363
363
|
};
|
|
364
364
|
}
|
|
365
|
-
if (error.message ===
|
|
365
|
+
if (error.message === "ffmpeg_avfoundation_missing") {
|
|
366
366
|
return {
|
|
367
|
-
message:
|
|
368
|
-
suggestions: [
|
|
367
|
+
message: "ffmpeg must support avfoundation screen capture on macOS.",
|
|
368
|
+
suggestions: ["Install a macOS ffmpeg build with avfoundation enabled."],
|
|
369
369
|
};
|
|
370
370
|
}
|
|
371
|
-
if (error.message ===
|
|
371
|
+
if (error.message === "ffmpeg_gdigrab_missing") {
|
|
372
372
|
return {
|
|
373
|
-
message:
|
|
374
|
-
suggestions: [
|
|
373
|
+
message: "ffmpeg must support gdigrab screen capture on Windows.",
|
|
374
|
+
suggestions: ["Install a Windows ffmpeg build with gdigrab enabled."],
|
|
375
375
|
};
|
|
376
376
|
}
|
|
377
|
-
if (error.message.startsWith(
|
|
377
|
+
if (error.message.startsWith("marketing_output_outside_assets:")) {
|
|
378
378
|
return {
|
|
379
|
-
message:
|
|
380
|
-
suggestions: [
|
|
379
|
+
message: "Marketing capture output must stay under assets/marketing/.",
|
|
380
|
+
suggestions: ["Remove --output-dir or set it to assets/marketing or a subdirectory inside assets/marketing."],
|
|
381
381
|
};
|
|
382
382
|
}
|
|
383
|
-
if (error.message ===
|
|
383
|
+
if (error.message === "catalogue_previewable_missing") {
|
|
384
384
|
return {
|
|
385
|
-
message:
|
|
386
|
-
suggestions: [
|
|
385
|
+
message: "catalogue.json must mark the app as previewable before marketing capture.",
|
|
386
|
+
suggestions: ["Set previewable: true on the app entry."],
|
|
387
387
|
};
|
|
388
388
|
}
|
|
389
|
-
if (error.message ===
|
|
389
|
+
if (error.message === "catalogue_preview_audio_policy_missing") {
|
|
390
390
|
return {
|
|
391
|
-
message:
|
|
392
|
-
suggestions: [
|
|
391
|
+
message: "catalogue.json must declare preview.audioPolicy before marketing capture.",
|
|
392
|
+
suggestions: ["Set preview.audioPolicy to music-and-sfx, sfx-only, or silent."],
|
|
393
393
|
};
|
|
394
394
|
}
|
|
395
|
-
if (error.message.startsWith(
|
|
395
|
+
if (error.message.startsWith("catalogue_preview_audio_policy_invalid:")) {
|
|
396
396
|
return {
|
|
397
|
-
message:
|
|
397
|
+
message: "catalogue.json preview.audioPolicy must be music-and-sfx, sfx-only, or silent.",
|
|
398
398
|
suggestions: [],
|
|
399
399
|
};
|
|
400
400
|
}
|
|
401
|
-
if (error.message.startsWith(
|
|
401
|
+
if (error.message.startsWith("ffmpeg_failed:") || error.message.startsWith("ffprobe_failed:")) {
|
|
402
402
|
return {
|
|
403
|
-
message: error.message.slice(error.message.indexOf(
|
|
403
|
+
message: error.message.slice(error.message.indexOf(":") + 1).trim() || "ffmpeg failed.",
|
|
404
404
|
suggestions: [],
|
|
405
405
|
};
|
|
406
406
|
}
|
|
407
|
-
if (error.message ===
|
|
407
|
+
if (error.message === "marketing_preview_hook_missing") {
|
|
408
408
|
return {
|
|
409
|
-
message:
|
|
410
|
-
suggestions: [
|
|
409
|
+
message: "The game did not expose window.__listingCapture.prepare for marketing preview capture.",
|
|
410
|
+
suggestions: ["Implement the PlayDrop preview hook before running marketing capture."],
|
|
411
411
|
};
|
|
412
412
|
}
|
|
413
|
-
if (error.message ===
|
|
413
|
+
if (error.message === "marketing_audio_capture_hook_missing") {
|
|
414
414
|
return {
|
|
415
|
-
message:
|
|
416
|
-
suggestions: [
|
|
415
|
+
message: "The game did not expose preview audio capture hooks.",
|
|
416
|
+
suggestions: ["Expose startAudioCapture and stopAudioCapture when preview audioPolicy is not silent."],
|
|
417
417
|
};
|
|
418
418
|
}
|
|
419
|
-
if (error.message.startsWith(
|
|
420
|
-
const [, surface, similarity] = error.message.split(
|
|
419
|
+
if (error.message.startsWith("marketing_capture_wrong_window:")) {
|
|
420
|
+
const [, surface, similarity] = error.message.split(":");
|
|
421
421
|
return {
|
|
422
|
-
message: `Marketing capture for ${surface ||
|
|
422
|
+
message: `Marketing capture for ${surface || "the surface"} does not match the PlayDrop preview frame. Similarity: ${similarity || "unknown"}.`,
|
|
423
423
|
suggestions: [
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
424
|
+
"Make sure the visible Chromium window is on the selected capture display.",
|
|
425
|
+
"On macOS, rerun with the correct --screen-device value for the display that contains the PlayDrop preview.",
|
|
426
|
+
"Do not continue with manual or browser-frame capture output.",
|
|
427
427
|
],
|
|
428
428
|
};
|
|
429
429
|
}
|
|
430
|
-
if (error.message.startsWith(
|
|
431
|
-
const [, surface, integratedLufs, peakDb] = error.message.split(
|
|
430
|
+
if (error.message.startsWith("marketing_capture_audio_too_quiet:")) {
|
|
431
|
+
const [, surface, integratedLufs, peakDb] = error.message.split(":");
|
|
432
432
|
return {
|
|
433
|
-
message: `Marketing capture audio for ${surface ||
|
|
433
|
+
message: `Marketing capture audio for ${surface || "the surface"} is too quiet (${integratedLufs || "?"} LUFS, peak ${peakDb || "?"} dB).`,
|
|
434
434
|
suggestions: [
|
|
435
|
-
|
|
436
|
-
|
|
435
|
+
"Add or raise preview background music and SFX through PlayDrop catalogue audio or PlayDrop AI generation.",
|
|
436
|
+
"Rerun marketing capture after the preview audio is clearly audible.",
|
|
437
437
|
],
|
|
438
438
|
};
|
|
439
439
|
}
|
|
440
|
-
if (error.message ===
|
|
440
|
+
if (error.message === "preview_game_frame_missing") {
|
|
441
441
|
return {
|
|
442
|
-
message:
|
|
443
|
-
suggestions: [
|
|
442
|
+
message: "The local dev preview did not expose a playable game iframe for capture.",
|
|
443
|
+
suggestions: ["Run playdrop project validate . and confirm the app opens on /dev-preview."],
|
|
444
444
|
};
|
|
445
445
|
}
|
|
446
|
-
if (error.message ===
|
|
446
|
+
if (error.message === "preview_game_frame_hidden") {
|
|
447
447
|
return {
|
|
448
|
-
message:
|
|
449
|
-
suggestions: [
|
|
448
|
+
message: "The local dev preview game frame is hidden or has no measurable size.",
|
|
449
|
+
suggestions: ["Make sure the preview scene renders the game immediately without a menu or loading screen."],
|
|
450
450
|
};
|
|
451
451
|
}
|
|
452
452
|
return {
|
|
@@ -456,11 +456,11 @@ function formatMarketingError(error) {
|
|
|
456
456
|
}
|
|
457
457
|
function ensureToolingAvailable(platform) {
|
|
458
458
|
assertSupportedMarketingCapturePlatform(platform);
|
|
459
|
-
if (!commandExists(
|
|
460
|
-
throw new Error(
|
|
459
|
+
if (!commandExists("ffmpeg")) {
|
|
460
|
+
throw new Error("ffmpeg_missing");
|
|
461
461
|
}
|
|
462
|
-
if (!commandExists(
|
|
463
|
-
throw new Error(
|
|
462
|
+
if (!commandExists("ffprobe")) {
|
|
463
|
+
throw new Error("ffprobe_missing");
|
|
464
464
|
}
|
|
465
465
|
const capabilityErrors = validateFfmpegCapabilities(platform);
|
|
466
466
|
if (capabilityErrors.length > 0) {
|
|
@@ -476,7 +476,7 @@ function resolveProjectRoot(resolvedTarget) {
|
|
|
476
476
|
}
|
|
477
477
|
async function waitForPreviewGameFrame(page, timeoutMs) {
|
|
478
478
|
const frameHandle = await page.waitForSelector(CAPTURE_FRAME_SELECTOR, {
|
|
479
|
-
state:
|
|
479
|
+
state: "attached",
|
|
480
480
|
timeout: timeoutMs,
|
|
481
481
|
});
|
|
482
482
|
const startedAt = Date.now();
|
|
@@ -487,20 +487,20 @@ async function waitForPreviewGameFrame(page, timeoutMs) {
|
|
|
487
487
|
}
|
|
488
488
|
await page.waitForTimeout(200);
|
|
489
489
|
}
|
|
490
|
-
throw new Error(
|
|
490
|
+
throw new Error("preview_game_frame_missing");
|
|
491
491
|
}
|
|
492
492
|
async function prepareMarketingPreview(page, surface, audioPolicy, seed) {
|
|
493
493
|
const frame = await waitForPreviewGameFrame(page, 60000);
|
|
494
494
|
const preset = SURFACE_PRESETS[surface];
|
|
495
495
|
await frame.waitForFunction(() => {
|
|
496
496
|
const captureWindow = window;
|
|
497
|
-
return typeof captureWindow.__listingCapture?.prepare ===
|
|
497
|
+
return typeof captureWindow.__listingCapture?.prepare === "function";
|
|
498
498
|
}, undefined, { timeout: 60000 });
|
|
499
499
|
await frame.evaluate(async (payload) => {
|
|
500
500
|
const captureWindow = window;
|
|
501
501
|
const hook = captureWindow.__listingCapture;
|
|
502
|
-
if (!hook || typeof hook.prepare !==
|
|
503
|
-
throw new Error(
|
|
502
|
+
if (!hook || typeof hook.prepare !== "function") {
|
|
503
|
+
throw new Error("marketing_preview_hook_missing");
|
|
504
504
|
}
|
|
505
505
|
await hook.prepare(payload);
|
|
506
506
|
}, {
|
|
@@ -516,18 +516,18 @@ async function startMarketingAudioCapture(page) {
|
|
|
516
516
|
await frame.evaluate(async () => {
|
|
517
517
|
const captureWindow = window;
|
|
518
518
|
const hook = captureWindow.__listingCapture;
|
|
519
|
-
if (!hook || typeof hook.startAudioCapture !==
|
|
520
|
-
throw new Error(
|
|
519
|
+
if (!hook || typeof hook.startAudioCapture !== "function") {
|
|
520
|
+
throw new Error("marketing_audio_capture_hook_missing");
|
|
521
521
|
}
|
|
522
522
|
await hook.startAudioCapture();
|
|
523
523
|
});
|
|
524
524
|
}
|
|
525
525
|
function resolveExportedAudioFileName(mimeType) {
|
|
526
|
-
if (mimeType.includes(
|
|
527
|
-
return
|
|
526
|
+
if (mimeType.includes("webm")) {
|
|
527
|
+
return "preview-audio.webm";
|
|
528
528
|
}
|
|
529
|
-
if (mimeType.includes(
|
|
530
|
-
return
|
|
529
|
+
if (mimeType.includes("mp4") || mimeType.includes("aac") || mimeType.includes("m4a")) {
|
|
530
|
+
return "preview-audio.m4a";
|
|
531
531
|
}
|
|
532
532
|
throw new Error(`unsupported_marketing_audio_mime_type:${mimeType}`);
|
|
533
533
|
}
|
|
@@ -536,27 +536,27 @@ async function stopMarketingAudioCapture(page, outputDir) {
|
|
|
536
536
|
const exportedAudio = await frame.evaluate(async () => {
|
|
537
537
|
const captureWindow = window;
|
|
538
538
|
const hook = captureWindow.__listingCapture;
|
|
539
|
-
if (!hook || typeof hook.stopAudioCapture !==
|
|
540
|
-
throw new Error(
|
|
539
|
+
if (!hook || typeof hook.stopAudioCapture !== "function") {
|
|
540
|
+
throw new Error("marketing_audio_capture_hook_missing");
|
|
541
541
|
}
|
|
542
542
|
return hook.stopAudioCapture();
|
|
543
543
|
});
|
|
544
544
|
const filePath = (0, node_path_1.join)(outputDir, resolveExportedAudioFileName(exportedAudio.mimeType));
|
|
545
|
-
await (0, promises_1.writeFile)(filePath, Buffer.from(exportedAudio.base64,
|
|
545
|
+
await (0, promises_1.writeFile)(filePath, Buffer.from(exportedAudio.base64, "base64"));
|
|
546
546
|
return filePath;
|
|
547
547
|
}
|
|
548
548
|
async function measurePreviewGameFrame(page) {
|
|
549
549
|
const cdpSession = await page.context().newCDPSession(page);
|
|
550
|
-
const windowState = await cdpSession.send(
|
|
550
|
+
const windowState = await cdpSession.send("Browser.getWindowForTarget");
|
|
551
551
|
const bounds = windowState.bounds ?? {};
|
|
552
552
|
return await page.evaluate((input) => {
|
|
553
553
|
const frame = document.querySelector(input.selector);
|
|
554
554
|
if (!(frame instanceof HTMLIFrameElement)) {
|
|
555
|
-
throw new Error(
|
|
555
|
+
throw new Error("preview_game_frame_missing");
|
|
556
556
|
}
|
|
557
557
|
const rect = frame.getBoundingClientRect();
|
|
558
558
|
if (rect.width <= 0 || rect.height <= 0) {
|
|
559
|
-
throw new Error(
|
|
559
|
+
throw new Error("preview_game_frame_hidden");
|
|
560
560
|
}
|
|
561
561
|
return {
|
|
562
562
|
outerWidth: window.outerWidth,
|
|
@@ -593,10 +593,10 @@ async function fitWindowToSurface(page, surface) {
|
|
|
593
593
|
if (Math.abs(deltaWidth) <= 1 && Math.abs(deltaHeight) <= 1) {
|
|
594
594
|
return measurement;
|
|
595
595
|
}
|
|
596
|
-
const windowState = await cdpSession.send(
|
|
597
|
-
const currentWidth = typeof windowState.bounds.width ===
|
|
598
|
-
const currentHeight = typeof windowState.bounds.height ===
|
|
599
|
-
await cdpSession.send(
|
|
596
|
+
const windowState = await cdpSession.send("Browser.getWindowForTarget");
|
|
597
|
+
const currentWidth = typeof windowState.bounds.width === "number" ? windowState.bounds.width : Math.round(measurement.outerWidth);
|
|
598
|
+
const currentHeight = typeof windowState.bounds.height === "number" ? windowState.bounds.height : Math.round(measurement.outerHeight);
|
|
599
|
+
await cdpSession.send("Browser.setWindowBounds", {
|
|
600
600
|
windowId: windowState.windowId,
|
|
601
601
|
bounds: {
|
|
602
602
|
width: Math.max(400, currentWidth + deltaWidth),
|
|
@@ -609,131 +609,82 @@ async function fitWindowToSurface(page, surface) {
|
|
|
609
609
|
return measurement;
|
|
610
610
|
}
|
|
611
611
|
function computeScreenCrop(measurement, platform) {
|
|
612
|
-
const scale = platform ===
|
|
612
|
+
const scale = platform === "darwin" ? measurement.devicePixelRatio || 1 : 1;
|
|
613
613
|
const x = Math.round((measurement.windowX + measurement.iframeRect.x) * scale);
|
|
614
614
|
const y = Math.round((measurement.windowY + measurement.iframeRect.y) * scale);
|
|
615
615
|
const width = Math.round(measurement.iframeRect.width * scale);
|
|
616
616
|
const height = Math.round(measurement.iframeRect.height * scale);
|
|
617
617
|
if (width <= 0 || height <= 0) {
|
|
618
|
-
throw new Error(
|
|
618
|
+
throw new Error("invalid_marketing_capture_crop");
|
|
619
619
|
}
|
|
620
620
|
return { x, y, width, height };
|
|
621
621
|
}
|
|
622
622
|
function probeMediaFile(filePath) {
|
|
623
|
-
const { stdout } = runTool(
|
|
624
|
-
'-v',
|
|
625
|
-
'error',
|
|
626
|
-
'-show_streams',
|
|
627
|
-
'-show_format',
|
|
628
|
-
'-of',
|
|
629
|
-
'json',
|
|
630
|
-
filePath,
|
|
631
|
-
], 'ffprobe_failed');
|
|
623
|
+
const { stdout } = runTool("ffprobe", ["-v", "error", "-show_streams", "-show_format", "-of", "json", filePath], "ffprobe_failed");
|
|
632
624
|
const probe = JSON.parse(stdout);
|
|
633
|
-
const video = probe.streams?.find(stream => stream.codec_type ===
|
|
634
|
-
if (!video || typeof video.width !==
|
|
635
|
-
throw new Error(
|
|
625
|
+
const video = probe.streams?.find((stream) => stream.codec_type === "video");
|
|
626
|
+
if (!video || typeof video.width !== "number" || typeof video.height !== "number") {
|
|
627
|
+
throw new Error("marketing_video_stream_missing");
|
|
636
628
|
}
|
|
637
629
|
const durationSeconds = probe.format?.duration ? Number.parseFloat(probe.format.duration) : Number.NaN;
|
|
638
630
|
if (!Number.isFinite(durationSeconds)) {
|
|
639
|
-
throw new Error(
|
|
631
|
+
throw new Error("marketing_duration_missing");
|
|
640
632
|
}
|
|
641
633
|
return {
|
|
642
634
|
width: video.width,
|
|
643
635
|
height: video.height,
|
|
644
636
|
durationSeconds,
|
|
645
|
-
audioTracks: probe.streams?.filter(stream => stream.codec_type ===
|
|
637
|
+
audioTracks: probe.streams?.filter((stream) => stream.codec_type === "audio").length ?? 0,
|
|
646
638
|
};
|
|
647
639
|
}
|
|
648
640
|
function muxMarketingAudio(videoPath, audioPath) {
|
|
649
641
|
const muxedPath = `${videoPath}.muxed.mp4`;
|
|
650
|
-
runTool(
|
|
651
|
-
'-y',
|
|
652
|
-
'-i',
|
|
653
|
-
videoPath,
|
|
654
|
-
'-i',
|
|
655
|
-
audioPath,
|
|
656
|
-
'-c:v',
|
|
657
|
-
'copy',
|
|
658
|
-
'-c:a',
|
|
659
|
-
'aac',
|
|
660
|
-
'-shortest',
|
|
661
|
-
muxedPath,
|
|
662
|
-
], 'ffmpeg_failed');
|
|
642
|
+
runTool("ffmpeg", ["-y", "-i", videoPath, "-i", audioPath, "-c:v", "copy", "-c:a", "aac", "-shortest", muxedPath], "ffmpeg_failed");
|
|
663
643
|
(0, node_fs_1.renameSync)(muxedPath, videoPath);
|
|
664
644
|
}
|
|
665
645
|
function extractPoster(videoPath, posterPath) {
|
|
666
|
-
runTool(
|
|
667
|
-
'-y',
|
|
668
|
-
'-ss',
|
|
669
|
-
'2',
|
|
670
|
-
'-i',
|
|
671
|
-
videoPath,
|
|
672
|
-
'-frames:v',
|
|
673
|
-
'1',
|
|
674
|
-
posterPath,
|
|
675
|
-
], 'ffmpeg_failed');
|
|
646
|
+
runTool("ffmpeg", ["-y", "-ss", "2", "-i", videoPath, "-frames:v", "1", posterPath], "ffmpeg_failed");
|
|
676
647
|
}
|
|
677
648
|
async function capturePreviewReferenceFrame(page, outputPath) {
|
|
678
649
|
await page.locator(CAPTURE_FRAME_SELECTOR).screenshot({ path: outputPath });
|
|
679
650
|
}
|
|
680
651
|
function calculatePreviewFrameSimilarity(referencePath, posterPath) {
|
|
681
|
-
const { stderr } = runTool(
|
|
682
|
-
|
|
683
|
-
|
|
652
|
+
const { stderr } = runTool("ffmpeg", [
|
|
653
|
+
"-hide_banner",
|
|
654
|
+
"-i",
|
|
684
655
|
referencePath,
|
|
685
|
-
|
|
656
|
+
"-i",
|
|
686
657
|
posterPath,
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
],
|
|
658
|
+
"-filter_complex",
|
|
659
|
+
"[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",
|
|
660
|
+
"-f",
|
|
661
|
+
"null",
|
|
662
|
+
"-",
|
|
663
|
+
], "ffmpeg_failed");
|
|
693
664
|
const match = stderr.match(/All:([0-9.]+)/);
|
|
694
665
|
if (!match) {
|
|
695
|
-
throw new Error(
|
|
666
|
+
throw new Error("marketing_capture_similarity_missing");
|
|
696
667
|
}
|
|
697
668
|
return Number.parseFloat(match[1]);
|
|
698
669
|
}
|
|
699
670
|
function validatePreviewFrameSimilarity(surface, similarity) {
|
|
700
671
|
if (!Number.isFinite(similarity) || similarity < MIN_MARKETING_PREVIEW_FRAME_SIMILARITY) {
|
|
701
|
-
throw new Error(`marketing_capture_wrong_window:${surface}:${Number.isFinite(similarity) ? similarity.toFixed(3) :
|
|
672
|
+
throw new Error(`marketing_capture_wrong_window:${surface}:${Number.isFinite(similarity) ? similarity.toFixed(3) : "unknown"}`);
|
|
702
673
|
}
|
|
703
674
|
}
|
|
704
675
|
function measureMarketingAudio(videoPath) {
|
|
705
|
-
const ebur = runTool(
|
|
706
|
-
'-hide_banner',
|
|
707
|
-
'-nostats',
|
|
708
|
-
'-i',
|
|
709
|
-
videoPath,
|
|
710
|
-
'-filter_complex',
|
|
711
|
-
'ebur128',
|
|
712
|
-
'-f',
|
|
713
|
-
'null',
|
|
714
|
-
'-',
|
|
715
|
-
], 'ffmpeg_failed');
|
|
676
|
+
const ebur = runTool("ffmpeg", ["-hide_banner", "-nostats", "-i", videoPath, "-filter_complex", "ebur128", "-f", "null", "-"], "ffmpeg_failed");
|
|
716
677
|
const eburText = `${ebur.stdout}\n${ebur.stderr}`;
|
|
717
678
|
const integratedMatches = [...eburText.matchAll(/I:\s*(-?\d+(?:\.\d+)?) LUFS/g)];
|
|
718
679
|
const integratedLufs = integratedMatches.length
|
|
719
|
-
? Number.parseFloat(integratedMatches[integratedMatches.length - 1]?.[1] ??
|
|
680
|
+
? Number.parseFloat(integratedMatches[integratedMatches.length - 1]?.[1] ?? "")
|
|
720
681
|
: Number.NaN;
|
|
721
|
-
const volume = runTool(
|
|
722
|
-
'-hide_banner',
|
|
723
|
-
'-nostats',
|
|
724
|
-
'-i',
|
|
725
|
-
videoPath,
|
|
726
|
-
'-af',
|
|
727
|
-
'volumedetect',
|
|
728
|
-
'-f',
|
|
729
|
-
'null',
|
|
730
|
-
'-',
|
|
731
|
-
], 'ffmpeg_failed');
|
|
682
|
+
const volume = runTool("ffmpeg", ["-hide_banner", "-nostats", "-i", videoPath, "-af", "volumedetect", "-f", "null", "-"], "ffmpeg_failed");
|
|
732
683
|
const volumeText = `${volume.stdout}\n${volume.stderr}`;
|
|
733
684
|
const peakMatch = volumeText.match(/max_volume:\s*(-?\d+(?:\.\d+)?) dB/);
|
|
734
|
-
const peakDb = peakMatch ? Number.parseFloat(peakMatch[1] ??
|
|
685
|
+
const peakDb = peakMatch ? Number.parseFloat(peakMatch[1] ?? "") : Number.NaN;
|
|
735
686
|
if (!Number.isFinite(integratedLufs) || !Number.isFinite(peakDb)) {
|
|
736
|
-
throw new Error(
|
|
687
|
+
throw new Error("marketing_audio_metrics_missing");
|
|
737
688
|
}
|
|
738
689
|
return { integratedLufs, peakDb };
|
|
739
690
|
}
|
|
@@ -743,20 +694,20 @@ function validateMarketingAudio(surface, metrics) {
|
|
|
743
694
|
}
|
|
744
695
|
}
|
|
745
696
|
function createMarketingReport(input) {
|
|
746
|
-
const warnings = [
|
|
697
|
+
const warnings = ["Exciting moment validation requires visual review from the marketing-pack workflow."];
|
|
747
698
|
if (!input.audioValidated) {
|
|
748
|
-
warnings.push(
|
|
699
|
+
warnings.push("Required audio was not validated.");
|
|
749
700
|
}
|
|
750
701
|
return {
|
|
751
702
|
appName: input.appName,
|
|
752
703
|
createdAt: input.createdAt,
|
|
753
|
-
status:
|
|
704
|
+
status: "passed",
|
|
754
705
|
durationSeconds: Math.round((Date.now() - input.startedAt) / 1000),
|
|
755
706
|
gates: [
|
|
756
707
|
{
|
|
757
|
-
id:
|
|
758
|
-
status:
|
|
759
|
-
summary:
|
|
708
|
+
id: "marketing-capture",
|
|
709
|
+
status: "passed",
|
|
710
|
+
summary: "Local marketing capture completed.",
|
|
760
711
|
},
|
|
761
712
|
],
|
|
762
713
|
assetCounts: {
|
|
@@ -784,9 +735,7 @@ async function resolveMarketingTarget(parsedOptions) {
|
|
|
784
735
|
return (0, devShared_1.resolveDevTarget)(parsedOptions.targetArg, parsedOptions.appName);
|
|
785
736
|
}
|
|
786
737
|
catch (error) {
|
|
787
|
-
(0, messages_1.printErrorWithHelp)(error?.message ||
|
|
788
|
-
'Run the command inside a PlayDrop workspace or pass an app target.',
|
|
789
|
-
], { command: 'project marketing capture' });
|
|
738
|
+
(0, messages_1.printErrorWithHelp)(error?.message || "Unable to resolve marketing capture target.", ["Run the command inside a PlayDrop workspace or pass an app target."], { command: "project marketing capture" });
|
|
790
739
|
process.exitCode = 1;
|
|
791
740
|
return null;
|
|
792
741
|
}
|
|
@@ -799,7 +748,7 @@ async function marketingDoctor(targetArg, options = {}) {
|
|
|
799
748
|
}
|
|
800
749
|
catch (error) {
|
|
801
750
|
const detail = formatMarketingError(error instanceof Error ? error : new Error(String(error)));
|
|
802
|
-
(0, messages_1.printErrorWithHelp)(detail.message, detail.suggestions, { command:
|
|
751
|
+
(0, messages_1.printErrorWithHelp)(detail.message, detail.suggestions, { command: "project marketing doctor" });
|
|
803
752
|
process.exitCode = 1;
|
|
804
753
|
return;
|
|
805
754
|
}
|
|
@@ -816,18 +765,18 @@ async function marketingDoctor(targetArg, options = {}) {
|
|
|
816
765
|
if (previewErrors.length > 0) {
|
|
817
766
|
throw new Error(previewErrors[0]);
|
|
818
767
|
}
|
|
819
|
-
const userDataDir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(),
|
|
768
|
+
const userDataDir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "playdrop-marketing-doctor-"));
|
|
820
769
|
const context = await (0, playwright_1.launchPersistentChromiumContext)(userDataDir, { viewport: SURFACE_PRESETS.desktop.viewport });
|
|
821
770
|
await context.close();
|
|
822
771
|
await (0, promises_1.rm)(userDataDir, { recursive: true, force: true });
|
|
823
772
|
}
|
|
824
773
|
catch (error) {
|
|
825
774
|
const detail = formatMarketingError(error instanceof Error ? error : new Error(String(error)));
|
|
826
|
-
(0, messages_1.printErrorWithHelp)(detail.message, detail.suggestions, { command:
|
|
775
|
+
(0, messages_1.printErrorWithHelp)(detail.message, detail.suggestions, { command: "project marketing doctor" });
|
|
827
776
|
process.exitCode = 1;
|
|
828
777
|
return;
|
|
829
778
|
}
|
|
830
|
-
console.log(
|
|
779
|
+
console.log("[marketing] Doctor passed.");
|
|
831
780
|
}
|
|
832
781
|
async function marketingCapture(targetArg, options = {}) {
|
|
833
782
|
const startedAt = Date.now();
|
|
@@ -838,7 +787,7 @@ async function marketingCapture(targetArg, options = {}) {
|
|
|
838
787
|
}
|
|
839
788
|
catch (error) {
|
|
840
789
|
const detail = formatMarketingError(error instanceof Error ? error : new Error(String(error)));
|
|
841
|
-
(0, messages_1.printErrorWithHelp)(detail.message, detail.suggestions, { command:
|
|
790
|
+
(0, messages_1.printErrorWithHelp)(detail.message, detail.suggestions, { command: "project marketing capture" });
|
|
842
791
|
process.exitCode = 1;
|
|
843
792
|
return;
|
|
844
793
|
}
|
|
@@ -859,9 +808,9 @@ async function marketingCapture(targetArg, options = {}) {
|
|
|
859
808
|
const projectRoot = resolveProjectRoot(resolvedTarget);
|
|
860
809
|
const outputPaths = resolveMarketingOutputPaths(projectRoot, appName, parsedOptions.outputDir);
|
|
861
810
|
const projectInfo = (0, devShared_1.findProjectInfo)(resolvedTarget.htmlPath);
|
|
862
|
-
const devScriptAvailable = Boolean(projectInfo.projectDir && projectInfo.packageJson && typeof projectInfo.packageJson.scripts?.dev ===
|
|
863
|
-
await (0, commandContext_1.withEnvironment)(
|
|
864
|
-
let currentUsername =
|
|
811
|
+
const devScriptAvailable = Boolean(projectInfo.projectDir && projectInfo.packageJson && typeof projectInfo.packageJson.scripts?.dev === "string");
|
|
812
|
+
await (0, commandContext_1.withEnvironment)("project marketing capture", "Capturing marketing media", async ({ client, env: _env, envConfig }) => {
|
|
813
|
+
let currentUsername = "";
|
|
865
814
|
try {
|
|
866
815
|
const currentUser = await (0, devShared_1.fetchDevUser)(client);
|
|
867
816
|
currentUsername = currentUser.username.trim();
|
|
@@ -871,7 +820,7 @@ async function marketingCapture(targetArg, options = {}) {
|
|
|
871
820
|
return;
|
|
872
821
|
}
|
|
873
822
|
if (error instanceof types_1.UnsupportedClientError) {
|
|
874
|
-
(0, http_1.handleUnsupportedError)(error,
|
|
823
|
+
(0, http_1.handleUnsupportedError)(error, "Authentication");
|
|
875
824
|
process.exitCode = 1;
|
|
876
825
|
return;
|
|
877
826
|
}
|
|
@@ -879,12 +828,12 @@ async function marketingCapture(targetArg, options = {}) {
|
|
|
879
828
|
(0, messages_1.printErrorWithHelp)(`Could not fetch your account (status ${error.status}).`, [
|
|
880
829
|
'Run "playdrop auth login" to refresh your session.',
|
|
881
830
|
'Use "playdrop auth whoami" afterwards to verify the current account.',
|
|
882
|
-
], { command:
|
|
831
|
+
], { command: "project marketing capture" });
|
|
883
832
|
process.exitCode = 1;
|
|
884
833
|
return;
|
|
885
834
|
}
|
|
886
835
|
if ((0, devShared_1.isNetworkError)(error)) {
|
|
887
|
-
(0, messages_1.printNetworkIssue)(
|
|
836
|
+
(0, messages_1.printNetworkIssue)("Could not reach the Playdrop API to resolve your account.", "project marketing capture");
|
|
888
837
|
process.exitCode = 1;
|
|
889
838
|
return;
|
|
890
839
|
}
|
|
@@ -892,8 +841,8 @@ async function marketingCapture(targetArg, options = {}) {
|
|
|
892
841
|
}
|
|
893
842
|
const taskLookup = (0, catalogue_1.findAppTaskByFile)(resolvedTarget.htmlPath);
|
|
894
843
|
if (taskLookup.errors.length > 0) {
|
|
895
|
-
(0, messages_1.printErrorWithHelp)(taskLookup.errors[0] ||
|
|
896
|
-
command:
|
|
844
|
+
(0, messages_1.printErrorWithHelp)(taskLookup.errors[0] || "Failed to resolve the app task from catalogue.json.", taskLookup.errors.slice(1), {
|
|
845
|
+
command: "project marketing capture",
|
|
897
846
|
});
|
|
898
847
|
process.exitCode = 1;
|
|
899
848
|
return;
|
|
@@ -906,7 +855,7 @@ async function marketingCapture(targetArg, options = {}) {
|
|
|
906
855
|
apiBase: envConfig.apiBase,
|
|
907
856
|
task: taskLookup.task,
|
|
908
857
|
creatorUsername: currentUsername,
|
|
909
|
-
appBaseUrl: new URL(
|
|
858
|
+
appBaseUrl: new URL(".", (0, devServer_1.buildLocalDevAppUrl)({
|
|
910
859
|
creatorUsername: currentUsername,
|
|
911
860
|
appType: appTypeSlug,
|
|
912
861
|
appName,
|
|
@@ -916,7 +865,7 @@ async function marketingCapture(targetArg, options = {}) {
|
|
|
916
865
|
}
|
|
917
866
|
catch (error) {
|
|
918
867
|
const formatted = (0, dev_1.formatDevRuntimeAssetManifestFailure)(error);
|
|
919
|
-
(0, messages_1.printErrorWithHelp)(formatted.message, formatted.suggestions, { command:
|
|
868
|
+
(0, messages_1.printErrorWithHelp)(formatted.message, formatted.suggestions, { command: "project marketing capture" });
|
|
920
869
|
process.exitCode = 1;
|
|
921
870
|
return;
|
|
922
871
|
}
|
|
@@ -925,8 +874,8 @@ async function marketingCapture(targetArg, options = {}) {
|
|
|
925
874
|
const app = loadCatalogueApp(resolvedTarget.cataloguePath, appName);
|
|
926
875
|
const previewErrors = validateMarketingPreviewConfig(app);
|
|
927
876
|
if (previewErrors.length > 0) {
|
|
928
|
-
const detail = formatMarketingError(new Error(previewErrors[0] ||
|
|
929
|
-
(0, messages_1.printErrorWithHelp)(detail.message, detail.suggestions, { command:
|
|
877
|
+
const detail = formatMarketingError(new Error(previewErrors[0] || "Marketing preview configuration is invalid."));
|
|
878
|
+
(0, messages_1.printErrorWithHelp)(detail.message, detail.suggestions, { command: "project marketing capture" });
|
|
930
879
|
process.exitCode = 1;
|
|
931
880
|
return;
|
|
932
881
|
}
|
|
@@ -977,15 +926,15 @@ async function marketingCapture(targetArg, options = {}) {
|
|
|
977
926
|
}
|
|
978
927
|
}
|
|
979
928
|
if (!serverAlreadyRunning) {
|
|
980
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
929
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
981
930
|
}
|
|
982
|
-
const webBase = envConfig.webBase ??
|
|
931
|
+
const webBase = envConfig.webBase ?? "https://www.playdrop.ai";
|
|
983
932
|
const frameUrlObject = new URL(`${(0, appUrls_1.normalizePlaydropWebBase)(webBase)}/creators/${encodeURIComponent(currentUsername)}/apps/${appTypeSlug}/${encodeURIComponent(appName)}/dev-preview`);
|
|
984
|
-
frameUrlObject.searchParams.set(
|
|
933
|
+
frameUrlObject.searchParams.set("launchCheck", "1");
|
|
985
934
|
const frameUrl = frameUrlObject.toString();
|
|
986
|
-
userDataDir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(),
|
|
935
|
+
userDataDir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "playdrop-marketing-capture-"));
|
|
987
936
|
const manifestCaptures = [];
|
|
988
|
-
let audioValidated = parsedOptions.audioPolicy ===
|
|
937
|
+
let audioValidated = parsedOptions.audioPolicy === "silent";
|
|
989
938
|
for (const surface of parsedOptions.surfaces) {
|
|
990
939
|
const preset = SURFACE_PRESETS[surface];
|
|
991
940
|
context = await (0, playwright_1.launchPersistentChromiumContext)(userDataDir, {
|
|
@@ -996,8 +945,8 @@ async function marketingCapture(targetArg, options = {}) {
|
|
|
996
945
|
userAgent: preset.viewport.userAgent,
|
|
997
946
|
automationOrigin: frameUrlObject.origin,
|
|
998
947
|
});
|
|
999
|
-
const page = context.pages()[0] ?? await context.newPage();
|
|
1000
|
-
await page.goto(frameUrl, { waitUntil:
|
|
948
|
+
const page = context.pages()[0] ?? (await context.newPage());
|
|
949
|
+
await page.goto(frameUrl, { waitUntil: "domcontentloaded" });
|
|
1001
950
|
await prepareMarketingPreview(page, surface, parsedOptions.audioPolicy, parsedOptions.seed);
|
|
1002
951
|
await page.waitForTimeout(1000);
|
|
1003
952
|
const measurement = await fitWindowToSurface(page, surface);
|
|
@@ -1021,11 +970,11 @@ async function marketingCapture(targetArg, options = {}) {
|
|
|
1021
970
|
},
|
|
1022
971
|
});
|
|
1023
972
|
let audioMetrics = null;
|
|
1024
|
-
if (parsedOptions.audioPolicy !==
|
|
973
|
+
if (parsedOptions.audioPolicy !== "silent") {
|
|
1025
974
|
await startMarketingAudioCapture(page);
|
|
1026
975
|
}
|
|
1027
|
-
runTool(
|
|
1028
|
-
if (parsedOptions.audioPolicy !==
|
|
976
|
+
runTool("ffmpeg", args, "ffmpeg_failed");
|
|
977
|
+
if (parsedOptions.audioPolicy !== "silent") {
|
|
1029
978
|
const audioPath = await stopMarketingAudioCapture(page, outputPaths.capturesDir);
|
|
1030
979
|
muxMarketingAudio(videoPath, audioPath);
|
|
1031
980
|
audioMetrics = measureMarketingAudio(videoPath);
|
|
@@ -1042,7 +991,7 @@ async function marketingCapture(targetArg, options = {}) {
|
|
|
1042
991
|
if (probe.width !== preset.width || probe.height !== preset.height) {
|
|
1043
992
|
throw new Error(`marketing_capture_dimension_mismatch:${surface}:${probe.width}x${probe.height}`);
|
|
1044
993
|
}
|
|
1045
|
-
if (parsedOptions.audioPolicy !==
|
|
994
|
+
if (parsedOptions.audioPolicy !== "silent" && probe.audioTracks === 0) {
|
|
1046
995
|
throw new Error(`marketing_capture_audio_missing:${surface}`);
|
|
1047
996
|
}
|
|
1048
997
|
manifestCaptures.push({
|
|
@@ -1052,11 +1001,11 @@ async function marketingCapture(targetArg, options = {}) {
|
|
|
1052
1001
|
height: preset.height,
|
|
1053
1002
|
fps: parsedOptions.fps,
|
|
1054
1003
|
durationSeconds: probe.durationSeconds,
|
|
1055
|
-
hasAudio: parsedOptions.audioPolicy !==
|
|
1004
|
+
hasAudio: parsedOptions.audioPolicy !== "silent",
|
|
1056
1005
|
audio: {
|
|
1057
1006
|
policy: parsedOptions.audioPolicy,
|
|
1058
|
-
backgroundMusic: parsedOptions.audioPolicy ===
|
|
1059
|
-
sfx: parsedOptions.audioPolicy !==
|
|
1007
|
+
backgroundMusic: parsedOptions.audioPolicy === "music-and-sfx",
|
|
1008
|
+
sfx: parsedOptions.audioPolicy !== "silent",
|
|
1060
1009
|
peakDb: audioMetrics?.peakDb ?? null,
|
|
1061
1010
|
integratedLufs: audioMetrics?.integratedLufs ?? null,
|
|
1062
1011
|
},
|
|
@@ -1084,14 +1033,14 @@ async function marketingCapture(targetArg, options = {}) {
|
|
|
1084
1033
|
fps: parsedOptions.fps,
|
|
1085
1034
|
audioValidated,
|
|
1086
1035
|
});
|
|
1087
|
-
await (0, promises_1.writeFile)(outputPaths.manifestPath, `${JSON.stringify(manifest, null, 2)}\n`,
|
|
1088
|
-
await (0, promises_1.writeFile)(outputPaths.reportPath, `${JSON.stringify(report, null, 2)}\n`,
|
|
1036
|
+
await (0, promises_1.writeFile)(outputPaths.manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
|
|
1037
|
+
await (0, promises_1.writeFile)(outputPaths.reportPath, `${JSON.stringify(report, null, 2)}\n`, "utf8");
|
|
1089
1038
|
console.log(`[marketing] Saved capture manifest to ${(0, node_path_1.relative)(process.cwd(), outputPaths.manifestPath) || outputPaths.manifestPath}`);
|
|
1090
1039
|
console.log(`[marketing] Saved report to ${(0, node_path_1.relative)(process.cwd(), outputPaths.reportPath) || outputPaths.reportPath}`);
|
|
1091
1040
|
}
|
|
1092
1041
|
catch (error) {
|
|
1093
1042
|
const detail = formatMarketingError(error instanceof Error ? error : new Error(String(error)));
|
|
1094
|
-
(0, messages_1.printErrorWithHelp)(detail.message, detail.suggestions, { command:
|
|
1043
|
+
(0, messages_1.printErrorWithHelp)(detail.message, detail.suggestions, { command: "project marketing capture" });
|
|
1095
1044
|
process.exitCode = 1;
|
|
1096
1045
|
}
|
|
1097
1046
|
finally {
|