hyperframes 0.8.175 → 0.8.176
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{add-S4AOKSNH.js → add-YZGUHLDP.js} +5 -5
- package/dist/{animatedGifPrep-QRDM2NMV.js → animatedGifPrep-6SPYXSHE.js} +2 -2
- package/dist/api.js +10 -10
- package/dist/{asset-DQ45BUBL.js → asset-YPC34MTV.js} +2 -2
- package/dist/{assetCataloger-NPDCMAYE.js → assetCataloger-5GGNQC3Y.js} +2 -2
- package/dist/{audio-OOAXEELO.js → audio-33JHU37L.js} +7 -7
- package/dist/{auth-33IY64LT.js → auth-UUIHOS2D.js} +1 -1
- package/dist/{autoUpdate-KGLJUGNZ.js → autoUpdate-F747BE3R.js} +2 -2
- package/dist/{backgroundChecks-DQPHYLVN.js → backgroundChecks-3YLO2CSC.js} +3 -3
- package/dist/backgroundChecksWorker.js +3 -3
- package/dist/{beats-WDYSCACS.js → beats-DQDV2ZFS.js} +4 -4
- package/dist/{benchmark-WOAULMYW.js → benchmark-ZPPZEIHZ.js} +6 -6
- package/dist/{browser-2HX46R74.js → browser-ZFLEP3E6.js} +3 -3
- package/dist/{bundleWithLocalizedFonts-32N7TVM6.js → bundleWithLocalizedFonts-PMRGOQWD.js} +1 -1
- package/dist/{capture-7LR5UVM2.js → capture-5OW6VNXU.js} +2 -2
- package/dist/{capture-5ASPHTMF.js → capture-6XCCIPT4.js} +5 -5
- package/dist/{catalog-LVW3RT4U.js → catalog-3A6KKCWF.js} +5 -5
- package/dist/{catch-up-W6Z73GXL.js → catch-up-KPGCLN4C.js} +4 -4
- package/dist/{check-C23ZWACZ.js → check-I3XLXS77.js} +17 -17
- package/dist/{checkBrowser-WS6FSLBJ.js → checkBrowser-KX7LNAFA.js} +15 -15
- package/dist/{chunk-3SWMUZ4Y.js → chunk-44XBAKKT.js} +2 -2
- package/dist/{chunk-WDFLYDKN.js → chunk-5EA4C6RA.js} +1 -1
- package/dist/{chunk-YMU42UU2.js → chunk-5TGVURKQ.js} +1 -1
- package/dist/{chunk-ZGSLXWH2.js → chunk-5ZSLJXQX.js} +1 -1
- package/dist/{chunk-NZRAS3FB.js → chunk-7LU4ZHYV.js} +1 -1
- package/dist/{chunk-CEYY3SOX.js → chunk-7XX4QGQV.js} +3 -3
- package/dist/{chunk-7GUAZGSW.js → chunk-B7DJQFAC.js} +3 -3
- package/dist/{chunk-KZHYAGC3.js → chunk-BTCHRQNV.js} +3 -3
- package/dist/{chunk-4XM6VXLN.js → chunk-BVXZYFAL.js} +2 -2
- package/dist/{chunk-NELZN7SV.js → chunk-EEVCXPUA.js} +2 -2
- package/dist/{chunk-PBKOURSI.js → chunk-GOOB64LO.js} +1 -1
- package/dist/{chunk-S354WIFQ.js → chunk-GVIH4ZQY.js} +1 -1
- package/dist/{chunk-QMQZXNGU.js → chunk-HTC3VGU3.js} +5 -5
- package/dist/{chunk-3X3CELOP.js → chunk-HVMHVW5G.js} +1 -1
- package/dist/{chunk-O6FWDZYJ.js → chunk-K535ASAM.js} +1 -1
- package/dist/{chunk-BHMWVRNS.js → chunk-KRKSY2AE.js} +1 -1
- package/dist/{chunk-KHR4IJAW.js → chunk-KUOUUG33.js} +5 -5
- package/dist/{chunk-V7T7WA2K.js → chunk-L3LJVYXS.js} +2 -2
- package/dist/{chunk-XRRDVRCY.js → chunk-L6XT6GSV.js} +1 -1
- package/dist/{chunk-KZQGUXG5.js → chunk-LEILKM2N.js} +903 -813
- package/dist/{chunk-G7I3WZK4.js → chunk-MDTMWUJI.js} +1 -1
- package/dist/{chunk-EKDTCSCL.js → chunk-MG66UO4N.js} +1 -1
- package/dist/{chunk-NSWIF4VH.js → chunk-MLRCTTSD.js} +1 -1
- package/dist/{chunk-ZXHEOTYW.js → chunk-NGRNAF5X.js} +1 -1
- package/dist/{chunk-ZDOWJ3NS.js → chunk-OMUKSCWE.js} +8 -8
- package/dist/{chunk-UWUKJDTA.js → chunk-PL4WM5XE.js} +1 -1
- package/dist/{chunk-MJZQ44NV.js → chunk-R2TVJ4DW.js} +1 -1
- package/dist/{chunk-JT7PV3GL.js → chunk-RBOVQRMR.js} +2 -2
- package/dist/{chunk-XDK2FUAC.js → chunk-SSE6NWZI.js} +7 -7
- package/dist/{chunk-7E4YFL4U.js → chunk-SURF34CK.js} +1 -1
- package/dist/{chunk-ZD7QKZSS.js → chunk-T6NA6TR3.js} +2 -2
- package/dist/{chunk-Z2R7CFWH.js → chunk-USEUH5VV.js} +1 -1
- package/dist/{chunk-YCSCDQDS.js → chunk-VDUTZXIJ.js} +5 -5
- package/dist/{chunk-IUOPF3DL.js → chunk-VER46LXI.js} +1 -1
- package/dist/{chunk-RAZVWSU5.js → chunk-X4PK5ZXP.js} +1 -1
- package/dist/{chunk-FRNKM4MG.js → chunk-X6HYBA4A.js} +1 -1
- package/dist/{chunk-W5USVP6Z.js → chunk-X6ZRZWPZ.js} +1 -1
- package/dist/{chunk-RERXOG54.js → chunk-YWSXFFO3.js} +3 -1
- package/dist/{clean-KBNXV6DP.js → clean-6REGQY3I.js} +7 -7
- package/dist/cli.js +50 -50
- package/dist/{cloud-BWWKDRR6.js → cloud-RAQZPMNP.js} +4 -4
- package/dist/{compare-UWJRKF5K.js → compare-XYFSHHMQ.js} +9 -9
- package/dist/{component-SWOGSIO2.js → component-CRAO6Q2E.js} +3 -3
- package/dist/{compositions-PIPVEXE3.js → compositions-4SHILZZX.js} +6 -6
- package/dist/{delete-OXIDK7N3.js → delete-ZS4AHMTY.js} +2 -2
- package/dist/{doctor-KLQLFPCF.js → doctor-OL4FYABY.js} +6 -6
- package/dist/{events-EVTTGISX.js → events-LIJBGTTX.js} +2 -2
- package/dist/{events-EGCYD45B.js → events-Z3XZCRFZ.js} +3 -3
- package/dist/{feedback-G4WZLOVD.js → feedback-OE6SB2VE.js} +4 -4
- package/dist/{ffmpeg-QIYSAPHO.js → ffmpeg-CSJLJAQ3.js} +2 -2
- package/dist/{figma-Q4CKDE7M.js → figma-DMUCMO67.js} +3 -3
- package/dist/fontLocalizeCli.js +4 -4
- package/dist/{get-NPRQH64W.js → get-NQOQVUI5.js} +2 -2
- package/dist/{gpuPolicy-L72BQRFR.js → gpuPolicy-C6JWEX7F.js} +2 -2
- package/dist/{grade-compare-H4NJEL4P.js → grade-compare-HLTHHQGH.js} +10 -10
- package/dist/{help-2S3XXDEA.js → help-ZAXWHAYF.js} +1 -1
- package/dist/{history-X4CGCVEQ.js → history-CMY7ZEEW.js} +6 -6
- package/dist/hyperframes-player.global.js +1 -1
- package/dist/{info-YYUISGL7.js → info-STR7EYI3.js} +5 -5
- package/dist/{init-BAAI4ZMR.js → init-YSMKK5VG.js} +18 -18
- package/dist/{inspect-LXJ3XJUZ.js → inspect-QV6DC6GO.js} +11 -11
- package/dist/{keyframes-NXXJSQWG.js → keyframes-HN3DW5FG.js} +6 -6
- package/dist/{layout-CBFO36IE.js → layout-O4QAR2XY.js} +11 -11
- package/dist/{lint-Y6NWQTKF.js → lint-AY55R4UR.js} +6 -6
- package/dist/{list-VY6KIYTX.js → list-TH2KCVNN.js} +2 -2
- package/dist/{login-426Q3H3P.js → login-TRCMRV4B.js} +4 -4
- package/dist/{manager-3RRUFZ4H.js → manager-2J4XGDZ3.js} +3 -3
- package/dist/{media-treatment-ZVIXMJZJ.js → media-treatment-ET5QQSWP.js} +7 -7
- package/dist/{models-KC2QBPVK.js → models-RFUUSCG6.js} +6 -6
- package/dist/{motionShot-3ZSLQJ4D.js → motionShot-2ET565ZV.js} +6 -6
- package/dist/{normalize-audio-DRU5LFYU.js → normalize-audio-LUOJSD3W.js} +4 -4
- package/dist/{open-LPBZQ4DC.js → open-MVEOYQRQ.js} +4 -4
- package/dist/{parakeetRunner-IFY6NK5G.js → parakeetRunner-3TG7EHWA.js} +7 -7
- package/dist/{pipeline-AGMCDT5A.js → pipeline-JWGTH2BJ.js} +3 -3
- package/dist/{play-WVWXNQED.js → play-I3WJPYXK.js} +4 -4
- package/dist/{preflight-I5JGCCKM.js → preflight-4WTT4D5X.js} +3 -3
- package/dist/{present-IPTWJUKY.js → present-DUZBKUYH.js} +4 -4
- package/dist/{preview-JVLRQVD7.js → preview-7J5TBTRP.js} +8 -8
- package/dist/{producer-HKGHSAON.js → producer-UZZQX6IJ.js} +1 -1
- package/dist/{remove-background-WY7KA77M.js → remove-background-OX5WZTXY.js} +4 -4
- package/dist/{render-BEDATKWV.js → render-IYTGM6DE.js} +5 -5
- package/dist/{render-P3PFNCBA.js → render-JUS2ROIN.js} +14 -14
- package/dist/{sherpa-SQGEFX3S.js → sherpa-PRI3JLUP.js} +6 -6
- package/dist/{skills-D2FIQ2JN.js → skills-CZ6VW5SZ.js} +5 -5
- package/dist/{skillsUpdateCheck-2OLTHCBX.js → skillsUpdateCheck-K4UKUAOC.js} +3 -3
- package/dist/{snapshot-R7U73M3N.js → snapshot-MPE4GIMJ.js} +20 -16
- package/dist/{src-4COYCAFY.js → src-JGPFLYBL.js} +1 -1
- package/dist/{src-WOD4AEI3.js → src-SGPA7DM7.js} +3 -3
- package/dist/studio/assets/{hyperframes-player-BByRREAU.js → hyperframes-player-DBHpVw8L.js} +1 -1
- package/dist/studio/assets/{index-Wz-WEqHg.js → index-BkDF4Z-5.js} +4 -4
- package/dist/studio/assets/{index-CULLUqJo.js → index-DHRXpmtI.js} +1 -1
- package/dist/studio/index.html +1 -1
- package/dist/{studioServer-LC5T4OXQ.js → studioServer-FWACRD3Y.js} +22 -22
- package/dist/{telemetry-BLOE3TWY.js → telemetry-CHJA5JFQ.js} +3 -3
- package/dist/{timeline-QQPPMADL.js → timeline-TZDMQBSM.js} +7 -7
- package/dist/{tokens-E3SHBFDU.js → tokens-B5KI7FBA.js} +2 -2
- package/dist/{transcribe-O2AQ5E2L.js → transcribe-M7NO6XBP.js} +5 -5
- package/dist/{transcribe-D5N7SBDO.js → transcribe-VHUDXZET.js} +11 -11
- package/dist/{updateCheck-ELOZ2CID.js → updateCheck-NMD6AFFG.js} +2 -2
- package/dist/{upgrade-JDFZEPZY.js → upgrade-WGFMGJ5O.js} +2 -2
- package/dist/{urlDownloader-32OGC3EW.js → urlDownloader-FZ5ISJES.js} +1 -1
- package/dist/{validate-LQQEABER.js → validate-NM3GLUWX.js} +11 -11
- package/dist/{video-HXIM27TO.js → video-7CDAIWYC.js} +2 -2
- package/package.json +1 -1
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
EncoderInterruptedError,
|
|
23
23
|
encoderFailureError,
|
|
24
24
|
prepareAnimatedGifInputs
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-PL4WM5XE.js";
|
|
26
26
|
import {
|
|
27
27
|
BROWSER_GPU_NOT_SOFTWARE,
|
|
28
28
|
CaptureFailure,
|
|
@@ -151,7 +151,7 @@ import {
|
|
|
151
151
|
validateEngineConfigSnapshot,
|
|
152
152
|
verifyDiskDrawElementSamples,
|
|
153
153
|
writeCapturedFrame
|
|
154
|
-
} from "./chunk-
|
|
154
|
+
} from "./chunk-YWSXFFO3.js";
|
|
155
155
|
import {
|
|
156
156
|
assignBundledRuntimeCompositionIds,
|
|
157
157
|
buildVariablesByCompScript,
|
|
@@ -13151,477 +13151,113 @@ async function executeRenderPipeline(input) {
|
|
|
13151
13151
|
import {
|
|
13152
13152
|
copyFileSync as copyFileSync4,
|
|
13153
13153
|
cpSync as cpSync2,
|
|
13154
|
-
existsSync as
|
|
13154
|
+
existsSync as existsSync19,
|
|
13155
13155
|
linkSync,
|
|
13156
13156
|
mkdirSync as mkdirSync10,
|
|
13157
13157
|
renameSync as renameSync4,
|
|
13158
13158
|
rmSync as rmSync9,
|
|
13159
13159
|
writeFileSync as writeFileSync7
|
|
13160
13160
|
} from "fs";
|
|
13161
|
-
import { extname as
|
|
13161
|
+
import { extname as extname6, isAbsolute as isAbsolute2, join as join31, relative as relative5, sep as sep2 } from "path";
|
|
13162
13162
|
|
|
13163
|
-
// ../producer/src/services/
|
|
13164
|
-
import {
|
|
13165
|
-
import { join as join26, relative as relative3, resolve as resolve7 } from "path";
|
|
13163
|
+
// ../producer/src/services/distributed/planFrameCost.ts
|
|
13164
|
+
import { join as join28 } from "path";
|
|
13166
13165
|
|
|
13167
|
-
// ../producer/src/services/distributed/
|
|
13168
|
-
|
|
13169
|
-
|
|
13170
|
-
|
|
13171
|
-
|
|
13172
|
-
|
|
13173
|
-
|
|
13174
|
-
|
|
13175
|
-
|
|
13176
|
-
|
|
13177
|
-
|
|
13178
|
-
|
|
13179
|
-
|
|
13180
|
-
|
|
13181
|
-
|
|
13182
|
-
schemaVersion: PLAN_V2_SCHEMA_VERSION,
|
|
13183
|
-
artifactLayout: PLAN_V2_ARTIFACT_LAYOUT,
|
|
13184
|
-
hashSchema: PLAN_V2_HASH_SCHEMA
|
|
13185
|
-
});
|
|
13186
|
-
var DISTRIBUTED_RENDER_CAPABILITIES = Object.freeze({
|
|
13187
|
-
roles: Object.freeze({
|
|
13188
|
-
planner: Object.freeze({
|
|
13189
|
-
produces: Object.freeze([PLAN_PROTOCOL_V1, PLAN_PROTOCOL_V2])
|
|
13190
|
-
}),
|
|
13191
|
-
chunk: Object.freeze({
|
|
13192
|
-
accepts: Object.freeze([PLAN_PROTOCOL_V1, PLAN_PROTOCOL_V2]),
|
|
13193
|
-
acceptsLegacyV1WithoutDescriptor: true
|
|
13194
|
-
}),
|
|
13195
|
-
assembler: Object.freeze({
|
|
13196
|
-
accepts: Object.freeze([PLAN_PROTOCOL_V1, PLAN_PROTOCOL_V2]),
|
|
13197
|
-
acceptsLegacyV1WithoutDescriptor: true
|
|
13198
|
-
})
|
|
13199
|
-
})
|
|
13200
|
-
});
|
|
13201
|
-
function getDistributedRenderCapabilities() {
|
|
13202
|
-
return DISTRIBUTED_RENDER_CAPABILITIES;
|
|
13166
|
+
// ../producer/src/services/distributed/chunkFrames.ts
|
|
13167
|
+
import { existsSync as existsSync17, readdirSync as readdirSync6 } from "fs";
|
|
13168
|
+
import { extname as extname4, join as join27 } from "path";
|
|
13169
|
+
|
|
13170
|
+
// ../producer/src/services/distributed/shared.ts
|
|
13171
|
+
import { execFile as execFileCallback } from "child_process";
|
|
13172
|
+
import { dirname as dirname10, join as join26 } from "path";
|
|
13173
|
+
import { existsSync as existsSync16, readFileSync as readFileSync8 } from "fs";
|
|
13174
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
13175
|
+
import { promisify as promisify2 } from "util";
|
|
13176
|
+
var PLAN_VIDEOS_META_RELATIVE_PATH = "meta/videos.json";
|
|
13177
|
+
var PLAN_AUDIO_RELATIVE_PATH = MIXED_AUDIO_FILENAME;
|
|
13178
|
+
var PLAN_AUDIO_LEGACY_RELATIVE_PATH = "audio.aac";
|
|
13179
|
+
function isPlanAudioArtifactPath(path) {
|
|
13180
|
+
return path === PLAN_AUDIO_RELATIVE_PATH || path === PLAN_AUDIO_LEGACY_RELATIVE_PATH;
|
|
13203
13181
|
}
|
|
13204
|
-
|
|
13205
|
-
|
|
13182
|
+
function resolvePlanAudioPath(planDir) {
|
|
13183
|
+
for (const name of [PLAN_AUDIO_RELATIVE_PATH, PLAN_AUDIO_LEGACY_RELATIVE_PATH]) {
|
|
13184
|
+
const candidate = join26(planDir, name);
|
|
13185
|
+
if (existsSync16(candidate)) return candidate;
|
|
13186
|
+
}
|
|
13187
|
+
return null;
|
|
13188
|
+
}
|
|
13189
|
+
var INVALID_VIDEO_METADATA = "INVALID_VIDEO_METADATA";
|
|
13190
|
+
var PlanVideosMetadataError = class extends Error {
|
|
13191
|
+
// Read by cloud adapters across the package boundary to classify retries.
|
|
13206
13192
|
// fallow-ignore-next-line unused-class-member
|
|
13207
|
-
code =
|
|
13208
|
-
constructor(
|
|
13209
|
-
super(
|
|
13210
|
-
this.name = "
|
|
13193
|
+
code = INVALID_VIDEO_METADATA;
|
|
13194
|
+
constructor(message) {
|
|
13195
|
+
super(message);
|
|
13196
|
+
this.name = "PlanVideosMetadataError";
|
|
13211
13197
|
}
|
|
13212
13198
|
};
|
|
13213
|
-
function
|
|
13214
|
-
|
|
13215
|
-
}
|
|
13216
|
-
function protocolMatches(descriptor, expected) {
|
|
13217
|
-
return descriptor.schemaVersion === expected.schemaVersion && descriptor.artifactLayout === expected.artifactLayout && descriptor.hashSchema === expected.hashSchema;
|
|
13218
|
-
}
|
|
13219
|
-
function capabilitiesAccept(capabilities, protocol) {
|
|
13220
|
-
return capabilities.accepts.some(
|
|
13221
|
-
(accepted) => accepted.schemaVersion === protocol.schemaVersion && accepted.artifactLayout === protocol.artifactLayout && accepted.hashSchema === protocol.hashSchema
|
|
13222
|
-
);
|
|
13199
|
+
function metadataError(field, expectation) {
|
|
13200
|
+
throw new PlanVideosMetadataError(`${field} ${expectation}`);
|
|
13223
13201
|
}
|
|
13224
|
-
function
|
|
13225
|
-
if (
|
|
13226
|
-
|
|
13227
|
-
}
|
|
13228
|
-
if (!Object.prototype.hasOwnProperty.call(planJson, "protocol")) {
|
|
13229
|
-
if (!capabilities.acceptsLegacyV1WithoutDescriptor || !capabilitiesAccept(capabilities, PLAN_PROTOCOL_V1)) {
|
|
13230
|
-
throw new PlanProtocolUnsupportedError(
|
|
13231
|
-
"legacy v1 plan without a protocol descriptor is not accepted by this worker"
|
|
13232
|
-
);
|
|
13233
|
-
}
|
|
13234
|
-
return PLAN_PROTOCOL_V1;
|
|
13235
|
-
}
|
|
13236
|
-
const descriptor = planJson.protocol;
|
|
13237
|
-
if (!isRecord(descriptor)) {
|
|
13238
|
-
throw new PlanProtocolUnsupportedError("plan.json protocol descriptor must be an object");
|
|
13239
|
-
}
|
|
13240
|
-
for (const field of ["schemaVersion", "artifactLayout", "hashSchema"]) {
|
|
13241
|
-
if (!Object.prototype.hasOwnProperty.call(descriptor, field)) {
|
|
13242
|
-
throw new PlanProtocolUnsupportedError(`plan.json protocol descriptor is missing ${field}`);
|
|
13243
|
-
}
|
|
13244
|
-
}
|
|
13245
|
-
const protocol = protocolMatches(descriptor, PLAN_PROTOCOL_V1) ? PLAN_PROTOCOL_V1 : protocolMatches(descriptor, PLAN_PROTOCOL_V2) ? PLAN_PROTOCOL_V2 : null;
|
|
13246
|
-
if (protocol === null || !capabilitiesAccept(capabilities, protocol)) {
|
|
13247
|
-
throw new PlanProtocolUnsupportedError("unsupported plan.json protocol descriptor");
|
|
13202
|
+
function readRecord(value, field) {
|
|
13203
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
13204
|
+
metadataError(field, "must be an object");
|
|
13248
13205
|
}
|
|
13249
|
-
return
|
|
13206
|
+
return value;
|
|
13250
13207
|
}
|
|
13251
|
-
function
|
|
13252
|
-
|
|
13253
|
-
|
|
13254
|
-
throw new PlanProtocolUnsupportedError(
|
|
13255
|
-
"content-addressed v2 plan must be materialized before v1 layout access"
|
|
13256
|
-
);
|
|
13208
|
+
function readNonEmptyString(value, field) {
|
|
13209
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
13210
|
+
metadataError(field, "must be a non-empty string");
|
|
13257
13211
|
}
|
|
13258
|
-
return
|
|
13212
|
+
return value;
|
|
13259
13213
|
}
|
|
13260
|
-
|
|
13261
|
-
|
|
13262
|
-
|
|
13263
|
-
import { closeSync as closeSync5, createReadStream as createReadStream3, openSync as openSync4, readSync as readSync3 } from "fs";
|
|
13264
|
-
var PLAN_HASH_SCHEMA_PREFIX = "hyperframes-plan-hash-v1\0";
|
|
13265
|
-
var FIELD_DELIMITER = Buffer.from([0]);
|
|
13266
|
-
function computePlanHash(input) {
|
|
13267
|
-
const hash = createHash5("sha256");
|
|
13268
|
-
hash.update(PLAN_HASH_SCHEMA_PREFIX, "utf8");
|
|
13269
|
-
hash.update(input.compositionHtml);
|
|
13270
|
-
hash.update(FIELD_DELIMITER);
|
|
13271
|
-
const sortedAssets = [...input.assets].sort(
|
|
13272
|
-
(a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0
|
|
13273
|
-
);
|
|
13274
|
-
for (const asset of sortedAssets) {
|
|
13275
|
-
hash.update(asset.path, "utf8");
|
|
13276
|
-
hash.update(FIELD_DELIMITER);
|
|
13277
|
-
hash.update(asset.sha256, "utf8");
|
|
13278
|
-
hash.update(FIELD_DELIMITER);
|
|
13214
|
+
function readString(value, field) {
|
|
13215
|
+
if (typeof value !== "string") {
|
|
13216
|
+
metadataError(field, "must be a string");
|
|
13279
13217
|
}
|
|
13280
|
-
|
|
13281
|
-
hash.update(FIELD_DELIMITER);
|
|
13282
|
-
hash.update(input.encoderConfigCanonicalJson, "utf8");
|
|
13283
|
-
hash.update(FIELD_DELIMITER);
|
|
13284
|
-
hash.update(input.producerVersion, "utf8");
|
|
13285
|
-
hash.update(FIELD_DELIMITER);
|
|
13286
|
-
hash.update(input.ffmpegVersion, "utf8");
|
|
13287
|
-
hash.update(FIELD_DELIMITER);
|
|
13288
|
-
const d = input.dimensions;
|
|
13289
|
-
hash.update(`${d.fpsNum}/${d.fpsDen}x${d.width}x${d.height}x${d.format}`, "utf8");
|
|
13290
|
-
return hash.digest("hex");
|
|
13218
|
+
return value;
|
|
13291
13219
|
}
|
|
13292
|
-
function
|
|
13293
|
-
if (value
|
|
13294
|
-
|
|
13295
|
-
if (typeof value === "number") {
|
|
13296
|
-
if (!Number.isFinite(value)) {
|
|
13297
|
-
throw new TypeError(`canonicalJsonStringify: non-finite number ${value}`);
|
|
13298
|
-
}
|
|
13299
|
-
return JSON.stringify(value);
|
|
13300
|
-
}
|
|
13301
|
-
if (typeof value === "string") return JSON.stringify(value);
|
|
13302
|
-
if (Array.isArray(value)) {
|
|
13303
|
-
return `[${value.map(canonicalJsonStringify).join(",")}]`;
|
|
13304
|
-
}
|
|
13305
|
-
if (typeof value === "object") {
|
|
13306
|
-
const obj = value;
|
|
13307
|
-
const keys = Object.keys(obj).sort();
|
|
13308
|
-
const parts = keys.map((k) => `${JSON.stringify(k)}:${canonicalJsonStringify(obj[k])}`);
|
|
13309
|
-
return `{${parts.join(",")}}`;
|
|
13220
|
+
function readFiniteNumber(value, field) {
|
|
13221
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
13222
|
+
metadataError(field, "must be a finite number");
|
|
13310
13223
|
}
|
|
13311
|
-
|
|
13312
|
-
}
|
|
13313
|
-
function sha256Hex(bytes) {
|
|
13314
|
-
const h = createHash5("sha256");
|
|
13315
|
-
h.update(bytes);
|
|
13316
|
-
return h.digest("hex");
|
|
13224
|
+
return value;
|
|
13317
13225
|
}
|
|
13318
|
-
function
|
|
13319
|
-
|
|
13320
|
-
|
|
13321
|
-
const fd = openSync4(path, "r");
|
|
13322
|
-
try {
|
|
13323
|
-
let bytesRead = 0;
|
|
13324
|
-
do {
|
|
13325
|
-
bytesRead = readSync3(fd, buffer, 0, buffer.byteLength, null);
|
|
13326
|
-
if (bytesRead > 0) hash.update(buffer.subarray(0, bytesRead));
|
|
13327
|
-
} while (bytesRead > 0);
|
|
13328
|
-
} finally {
|
|
13329
|
-
closeSync5(fd);
|
|
13226
|
+
function readBoolean(value, field) {
|
|
13227
|
+
if (typeof value !== "boolean") {
|
|
13228
|
+
metadataError(field, "must be boolean");
|
|
13330
13229
|
}
|
|
13331
|
-
return
|
|
13230
|
+
return value;
|
|
13332
13231
|
}
|
|
13333
|
-
|
|
13334
|
-
|
|
13335
|
-
|
|
13336
|
-
hash.update(chunk);
|
|
13232
|
+
function readPositiveInteger(value, field) {
|
|
13233
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
|
|
13234
|
+
metadataError(field, "must be a positive integer");
|
|
13337
13235
|
}
|
|
13338
|
-
return
|
|
13236
|
+
return value;
|
|
13339
13237
|
}
|
|
13340
|
-
|
|
13341
|
-
|
|
13342
|
-
|
|
13343
|
-
var HASH_EXCLUDED_PLAN_FILES = /* @__PURE__ */ new Set([
|
|
13344
|
-
"plan.json",
|
|
13345
|
-
"meta/encoder.json",
|
|
13346
|
-
COMPILED_INDEX_RELATIVE_PATH
|
|
13347
|
-
]);
|
|
13348
|
-
function planFileSha256(absolutePath, digests) {
|
|
13349
|
-
let sha256 = digests?.get(absolutePath);
|
|
13350
|
-
if (sha256 === void 0) {
|
|
13351
|
-
sha256 = sha256File(absolutePath);
|
|
13352
|
-
digests?.set(absolutePath, sha256);
|
|
13238
|
+
function readNonNegativeInteger(value, field) {
|
|
13239
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
|
|
13240
|
+
metadataError(field, "must be a non-negative integer");
|
|
13353
13241
|
}
|
|
13354
|
-
return
|
|
13242
|
+
return value;
|
|
13355
13243
|
}
|
|
13356
|
-
|
|
13357
|
-
|
|
13358
|
-
|
|
13359
|
-
|
|
13244
|
+
function readDeferredFrameRange(value, field) {
|
|
13245
|
+
const range = readRecord(value, field);
|
|
13246
|
+
const sourcePath = readNonEmptyString(range.sourcePath, `${field}.sourcePath`);
|
|
13247
|
+
if (sourcePath.startsWith("/") || sourcePath.split(/[\\/]/).some((part) => part === "..")) {
|
|
13248
|
+
metadataError(`${field}.sourcePath`, "must stay inside the plan directory");
|
|
13360
13249
|
}
|
|
13361
|
-
}
|
|
13362
|
-
|
|
13363
|
-
if (
|
|
13364
|
-
|
|
13365
|
-
const obj = value;
|
|
13366
|
-
const out = {};
|
|
13367
|
-
for (const key of Object.keys(obj)) {
|
|
13368
|
-
const v = obj[key];
|
|
13369
|
-
if (v === void 0) continue;
|
|
13370
|
-
out[key] = stripUndefined(v);
|
|
13371
|
-
}
|
|
13372
|
-
return out;
|
|
13250
|
+
const durationSeconds = readFiniteNumber(range.durationSeconds, `${field}.durationSeconds`);
|
|
13251
|
+
if (durationSeconds <= 0) metadataError(`${field}.durationSeconds`, "must be positive");
|
|
13252
|
+
if (range.format !== "jpg" && range.format !== "png") {
|
|
13253
|
+
metadataError(`${field}.format`, 'must be "jpg" or "png"');
|
|
13373
13254
|
}
|
|
13374
|
-
return
|
|
13375
|
-
|
|
13376
|
-
|
|
13377
|
-
|
|
13378
|
-
|
|
13379
|
-
|
|
13380
|
-
const entries = readdirSync6(dir, { withFileTypes: true });
|
|
13381
|
-
for (const entry of entries) {
|
|
13382
|
-
const full = join26(dir, entry.name);
|
|
13383
|
-
if (entry.isDirectory()) {
|
|
13384
|
-
walk(full);
|
|
13385
|
-
} else if (entry.isFile()) {
|
|
13386
|
-
results.push({
|
|
13387
|
-
planRelativePath: relative3(rootResolved, full).split(/[\\/]+/).join("/"),
|
|
13388
|
-
absolutePath: full
|
|
13389
|
-
});
|
|
13390
|
-
}
|
|
13391
|
-
}
|
|
13392
|
-
}
|
|
13393
|
-
walk(rootResolved);
|
|
13394
|
-
results.sort((a, b) => a.planRelativePath < b.planRelativePath ? -1 : 1);
|
|
13395
|
-
return results;
|
|
13396
|
-
}
|
|
13397
|
-
function collectPlanAssetShas(planDir, digests) {
|
|
13398
|
-
const files = listPlanFiles(planDir);
|
|
13399
|
-
let compositionHtml = null;
|
|
13400
|
-
const assets = [];
|
|
13401
|
-
for (const file of files) {
|
|
13402
|
-
if (file.planRelativePath === COMPILED_INDEX_RELATIVE_PATH) {
|
|
13403
|
-
compositionHtml = readFileSync8(file.absolutePath);
|
|
13404
|
-
continue;
|
|
13405
|
-
}
|
|
13406
|
-
if (HASH_EXCLUDED_PLAN_FILES.has(file.planRelativePath)) continue;
|
|
13407
|
-
assets.push({
|
|
13408
|
-
path: file.planRelativePath,
|
|
13409
|
-
sha256: planFileSha256(file.absolutePath, digests)
|
|
13410
|
-
});
|
|
13411
|
-
}
|
|
13412
|
-
if (compositionHtml === null) {
|
|
13413
|
-
throw new Error(
|
|
13414
|
-
`[freezePlan] compiled HTML missing at ${COMPILED_INDEX_RELATIVE_PATH} (upstream compile stage did not materialize the expected file).`
|
|
13415
|
-
);
|
|
13416
|
-
}
|
|
13417
|
-
return { compositionHtml, assets };
|
|
13418
|
-
}
|
|
13419
|
-
function recomputePlanHashFromPlanDir(planDir, digests) {
|
|
13420
|
-
const planJsonPath = join26(planDir, "plan.json");
|
|
13421
|
-
const encoderJsonPath = join26(planDir, "meta", "encoder.json");
|
|
13422
|
-
if (!existsSync16(planJsonPath)) {
|
|
13423
|
-
throw new Error(`[freezePlan] plan.json missing: ${planJsonPath}`);
|
|
13424
|
-
}
|
|
13425
|
-
if (!existsSync16(encoderJsonPath)) {
|
|
13426
|
-
throw new Error(`[freezePlan] meta/encoder.json missing: ${encoderJsonPath}`);
|
|
13427
|
-
}
|
|
13428
|
-
const planJson = JSON.parse(readFileSync8(planJsonPath, "utf-8"));
|
|
13429
|
-
const encoderConfigCanonicalJson = readFileSync8(encoderJsonPath, "utf-8");
|
|
13430
|
-
const { compositionHtml, assets } = collectPlanAssetShas(planDir, digests);
|
|
13431
|
-
return computePlanHash({
|
|
13432
|
-
compositionHtml,
|
|
13433
|
-
assets,
|
|
13434
|
-
fontSnapshotSha: planJson.fontSnapshotSha,
|
|
13435
|
-
encoderConfigCanonicalJson,
|
|
13436
|
-
producerVersion: planJson.producerVersion,
|
|
13437
|
-
ffmpegVersion: planJson.ffmpegVersion,
|
|
13438
|
-
dimensions: planJson.dimensions
|
|
13439
|
-
});
|
|
13440
|
-
}
|
|
13441
|
-
async function freezePlan(input) {
|
|
13442
|
-
const {
|
|
13443
|
-
planDir,
|
|
13444
|
-
composition,
|
|
13445
|
-
encoder,
|
|
13446
|
-
chunks,
|
|
13447
|
-
dimensions,
|
|
13448
|
-
producerVersion,
|
|
13449
|
-
fontSnapshotSha,
|
|
13450
|
-
durationSeconds,
|
|
13451
|
-
totalFrames,
|
|
13452
|
-
hasAudio,
|
|
13453
|
-
digests
|
|
13454
|
-
} = input;
|
|
13455
|
-
if (!existsSync16(planDir)) {
|
|
13456
|
-
throw new Error(`[freezePlan] planDir does not exist: ${planDir}`);
|
|
13457
|
-
}
|
|
13458
|
-
const metaDir = join26(planDir, "meta");
|
|
13459
|
-
if (!existsSync16(metaDir)) mkdirSync9(metaDir, { recursive: true });
|
|
13460
|
-
writeFileSync6(
|
|
13461
|
-
join26(metaDir, "composition.json"),
|
|
13462
|
-
`${JSON.stringify(composition, null, 2)}
|
|
13463
|
-
`,
|
|
13464
|
-
"utf-8"
|
|
13465
|
-
);
|
|
13466
|
-
const encoderForCanonical = stripUndefined(encoder);
|
|
13467
|
-
const encoderConfigCanonicalJson = canonicalJsonStringify(encoderForCanonical);
|
|
13468
|
-
writeFileSync6(join26(metaDir, "encoder.json"), encoderConfigCanonicalJson, "utf-8");
|
|
13469
|
-
writeFileSync6(join26(metaDir, "chunks.json"), `${JSON.stringify(chunks, null, 2)}
|
|
13470
|
-
`, "utf-8");
|
|
13471
|
-
const { compositionHtml, assets } = collectPlanAssetShas(planDir, digests);
|
|
13472
|
-
const planHash = computePlanHash({
|
|
13473
|
-
compositionHtml,
|
|
13474
|
-
assets,
|
|
13475
|
-
fontSnapshotSha,
|
|
13476
|
-
encoderConfigCanonicalJson,
|
|
13477
|
-
producerVersion,
|
|
13478
|
-
ffmpegVersion: encoder.ffmpegVersion,
|
|
13479
|
-
dimensions
|
|
13480
|
-
});
|
|
13481
|
-
const planJson = {
|
|
13482
|
-
protocol: PLAN_PROTOCOL_V1,
|
|
13483
|
-
planHash,
|
|
13484
|
-
producerVersion,
|
|
13485
|
-
ffmpegVersion: encoder.ffmpegVersion,
|
|
13486
|
-
fontSnapshotSha,
|
|
13487
|
-
dimensions,
|
|
13488
|
-
chunkCount: chunks.length,
|
|
13489
|
-
totalFrames,
|
|
13490
|
-
duration: durationSeconds,
|
|
13491
|
-
hasAudio
|
|
13492
|
-
};
|
|
13493
|
-
const planJsonPath = join26(planDir, "plan.json");
|
|
13494
|
-
writeFileSync6(planJsonPath, `${JSON.stringify(planJson, null, 2)}
|
|
13495
|
-
`, "utf-8");
|
|
13496
|
-
return { planJsonPath, planHash };
|
|
13497
|
-
}
|
|
13498
|
-
|
|
13499
|
-
// ../producer/src/services/render/runtimeEnvSnapshot.ts
|
|
13500
|
-
var RUNTIME_ENV_PREFIXES = [
|
|
13501
|
-
"PRODUCER_RUNTIME_",
|
|
13502
|
-
"PRODUCER_RENDER_"
|
|
13503
|
-
];
|
|
13504
|
-
function snapshotRuntimeEnv(env = process.env) {
|
|
13505
|
-
const snapshot = {};
|
|
13506
|
-
for (const key of Object.keys(env)) {
|
|
13507
|
-
const matches = RUNTIME_ENV_PREFIXES.some((prefix) => key.startsWith(prefix));
|
|
13508
|
-
if (!matches) continue;
|
|
13509
|
-
const value = env[key];
|
|
13510
|
-
if (typeof value !== "string") continue;
|
|
13511
|
-
snapshot[key] = value;
|
|
13512
|
-
}
|
|
13513
|
-
return snapshot;
|
|
13514
|
-
}
|
|
13515
|
-
function applyRuntimeEnvSnapshot(snapshot, env = process.env) {
|
|
13516
|
-
const previous = {};
|
|
13517
|
-
for (const [key, value] of Object.entries(snapshot)) {
|
|
13518
|
-
if (typeof value !== "string") continue;
|
|
13519
|
-
const matches = RUNTIME_ENV_PREFIXES.some((prefix) => key.startsWith(prefix));
|
|
13520
|
-
if (!matches) continue;
|
|
13521
|
-
previous[key] = env[key];
|
|
13522
|
-
env[key] = value;
|
|
13523
|
-
}
|
|
13524
|
-
return {
|
|
13525
|
-
restore: () => {
|
|
13526
|
-
for (const [key, value] of Object.entries(previous)) {
|
|
13527
|
-
if (value === void 0) delete env[key];
|
|
13528
|
-
else env[key] = value;
|
|
13529
|
-
}
|
|
13530
|
-
}
|
|
13531
|
-
};
|
|
13532
|
-
}
|
|
13533
|
-
|
|
13534
|
-
// ../producer/src/services/distributed/shared.ts
|
|
13535
|
-
import { execFile as execFileCallback } from "child_process";
|
|
13536
|
-
import { dirname as dirname10, join as join27 } from "path";
|
|
13537
|
-
import { existsSync as existsSync17, readFileSync as readFileSync9 } from "fs";
|
|
13538
|
-
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
13539
|
-
import { promisify as promisify2 } from "util";
|
|
13540
|
-
var PLAN_VIDEOS_META_RELATIVE_PATH = "meta/videos.json";
|
|
13541
|
-
var PLAN_AUDIO_RELATIVE_PATH = MIXED_AUDIO_FILENAME;
|
|
13542
|
-
var PLAN_AUDIO_LEGACY_RELATIVE_PATH = "audio.aac";
|
|
13543
|
-
function isPlanAudioArtifactPath(path) {
|
|
13544
|
-
return path === PLAN_AUDIO_RELATIVE_PATH || path === PLAN_AUDIO_LEGACY_RELATIVE_PATH;
|
|
13545
|
-
}
|
|
13546
|
-
function resolvePlanAudioPath(planDir) {
|
|
13547
|
-
for (const name of [PLAN_AUDIO_RELATIVE_PATH, PLAN_AUDIO_LEGACY_RELATIVE_PATH]) {
|
|
13548
|
-
const candidate = join27(planDir, name);
|
|
13549
|
-
if (existsSync17(candidate)) return candidate;
|
|
13550
|
-
}
|
|
13551
|
-
return null;
|
|
13552
|
-
}
|
|
13553
|
-
var INVALID_VIDEO_METADATA = "INVALID_VIDEO_METADATA";
|
|
13554
|
-
var PlanVideosMetadataError = class extends Error {
|
|
13555
|
-
// Read by cloud adapters across the package boundary to classify retries.
|
|
13556
|
-
// fallow-ignore-next-line unused-class-member
|
|
13557
|
-
code = INVALID_VIDEO_METADATA;
|
|
13558
|
-
constructor(message) {
|
|
13559
|
-
super(message);
|
|
13560
|
-
this.name = "PlanVideosMetadataError";
|
|
13561
|
-
}
|
|
13562
|
-
};
|
|
13563
|
-
function metadataError(field, expectation) {
|
|
13564
|
-
throw new PlanVideosMetadataError(`${field} ${expectation}`);
|
|
13565
|
-
}
|
|
13566
|
-
function readRecord(value, field) {
|
|
13567
|
-
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
13568
|
-
metadataError(field, "must be an object");
|
|
13569
|
-
}
|
|
13570
|
-
return value;
|
|
13571
|
-
}
|
|
13572
|
-
function readNonEmptyString(value, field) {
|
|
13573
|
-
if (typeof value !== "string" || value.length === 0) {
|
|
13574
|
-
metadataError(field, "must be a non-empty string");
|
|
13575
|
-
}
|
|
13576
|
-
return value;
|
|
13577
|
-
}
|
|
13578
|
-
function readString(value, field) {
|
|
13579
|
-
if (typeof value !== "string") {
|
|
13580
|
-
metadataError(field, "must be a string");
|
|
13581
|
-
}
|
|
13582
|
-
return value;
|
|
13583
|
-
}
|
|
13584
|
-
function readFiniteNumber(value, field) {
|
|
13585
|
-
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
13586
|
-
metadataError(field, "must be a finite number");
|
|
13587
|
-
}
|
|
13588
|
-
return value;
|
|
13589
|
-
}
|
|
13590
|
-
function readBoolean(value, field) {
|
|
13591
|
-
if (typeof value !== "boolean") {
|
|
13592
|
-
metadataError(field, "must be boolean");
|
|
13593
|
-
}
|
|
13594
|
-
return value;
|
|
13595
|
-
}
|
|
13596
|
-
function readPositiveInteger(value, field) {
|
|
13597
|
-
if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
|
|
13598
|
-
metadataError(field, "must be a positive integer");
|
|
13599
|
-
}
|
|
13600
|
-
return value;
|
|
13601
|
-
}
|
|
13602
|
-
function readNonNegativeInteger(value, field) {
|
|
13603
|
-
if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
|
|
13604
|
-
metadataError(field, "must be a non-negative integer");
|
|
13605
|
-
}
|
|
13606
|
-
return value;
|
|
13607
|
-
}
|
|
13608
|
-
function readDeferredFrameRange(value, field) {
|
|
13609
|
-
const range = readRecord(value, field);
|
|
13610
|
-
const sourcePath = readNonEmptyString(range.sourcePath, `${field}.sourcePath`);
|
|
13611
|
-
if (sourcePath.startsWith("/") || sourcePath.split(/[\\/]/).some((part) => part === "..")) {
|
|
13612
|
-
metadataError(`${field}.sourcePath`, "must stay inside the plan directory");
|
|
13613
|
-
}
|
|
13614
|
-
const durationSeconds = readFiniteNumber(range.durationSeconds, `${field}.durationSeconds`);
|
|
13615
|
-
if (durationSeconds <= 0) metadataError(`${field}.durationSeconds`, "must be positive");
|
|
13616
|
-
if (range.format !== "jpg" && range.format !== "png") {
|
|
13617
|
-
metadataError(`${field}.format`, 'must be "jpg" or "png"');
|
|
13618
|
-
}
|
|
13619
|
-
return {
|
|
13620
|
-
sourcePath,
|
|
13621
|
-
startTime: readFiniteNumber(range.startTime, `${field}.startTime`),
|
|
13622
|
-
durationSeconds,
|
|
13623
|
-
format: range.format
|
|
13624
|
-
};
|
|
13255
|
+
return {
|
|
13256
|
+
sourcePath,
|
|
13257
|
+
startTime: readFiniteNumber(range.startTime, `${field}.startTime`),
|
|
13258
|
+
durationSeconds,
|
|
13259
|
+
format: range.format
|
|
13260
|
+
};
|
|
13625
13261
|
}
|
|
13626
13262
|
function readRateSpec(value, field) {
|
|
13627
13263
|
if (value === void 0) return void 0;
|
|
@@ -13792,37 +13428,612 @@ function buildSyntheticRenderJob(input) {
|
|
|
13792
13428
|
producerConfig: input.producerConfig,
|
|
13793
13429
|
variables: input.variables
|
|
13794
13430
|
};
|
|
13795
|
-
return createRenderJob(renderConfig);
|
|
13431
|
+
return createRenderJob(renderConfig);
|
|
13432
|
+
}
|
|
13433
|
+
var cachedProducerVersion = null;
|
|
13434
|
+
function readProducerVersion() {
|
|
13435
|
+
if (cachedProducerVersion !== null) return cachedProducerVersion;
|
|
13436
|
+
const startDir = dirname10(fileURLToPath5(import.meta.url));
|
|
13437
|
+
let current = startDir;
|
|
13438
|
+
for (let i = 0; i < 10; i++) {
|
|
13439
|
+
const candidate = join26(current, "package.json");
|
|
13440
|
+
if (existsSync16(candidate)) {
|
|
13441
|
+
try {
|
|
13442
|
+
const pkg = JSON.parse(readFileSync8(candidate, "utf-8"));
|
|
13443
|
+
if (pkg.name === "@hyperframes/producer" && typeof pkg.version === "string") {
|
|
13444
|
+
cachedProducerVersion = pkg.version;
|
|
13445
|
+
return pkg.version;
|
|
13446
|
+
}
|
|
13447
|
+
} catch {
|
|
13448
|
+
}
|
|
13449
|
+
}
|
|
13450
|
+
const parent = dirname10(current);
|
|
13451
|
+
if (parent === current) break;
|
|
13452
|
+
current = parent;
|
|
13453
|
+
}
|
|
13454
|
+
cachedProducerVersion = "0.0.0-unknown";
|
|
13455
|
+
return cachedProducerVersion;
|
|
13456
|
+
}
|
|
13457
|
+
|
|
13458
|
+
// ../producer/src/services/distributed/chunkFrames.ts
|
|
13459
|
+
var FFMPEG_VERSION_MISMATCH = "FFMPEG_VERSION_MISMATCH";
|
|
13460
|
+
var PLAN_HASH_MISMATCH = "PLAN_HASH_MISMATCH";
|
|
13461
|
+
var MISSING_PLAN_ARTIFACT = "MISSING_PLAN_ARTIFACT";
|
|
13462
|
+
var CHUNK_INDEX_OUT_OF_RANGE = "CHUNK_INDEX_OUT_OF_RANGE";
|
|
13463
|
+
var MISSING_RUNTIME_ENV_SNAPSHOT = "MISSING_RUNTIME_ENV_SNAPSHOT";
|
|
13464
|
+
var RenderChunkValidationError = class extends Error {
|
|
13465
|
+
code;
|
|
13466
|
+
constructor(code, message) {
|
|
13467
|
+
super(message);
|
|
13468
|
+
this.name = "RenderChunkValidationError";
|
|
13469
|
+
this.code = code;
|
|
13470
|
+
}
|
|
13471
|
+
};
|
|
13472
|
+
function createChunkVideoFrameInjectorFactory(frameLookup) {
|
|
13473
|
+
return () => createVideoFrameInjector(frameLookup);
|
|
13474
|
+
}
|
|
13475
|
+
function frameNumberFromFileName(name) {
|
|
13476
|
+
const match = /(\d+)(?=\.[^.]+$)/.exec(name);
|
|
13477
|
+
if (!match) return null;
|
|
13478
|
+
const frameNumber = Number(match[1]);
|
|
13479
|
+
return Number.isSafeInteger(frameNumber) ? frameNumber : null;
|
|
13480
|
+
}
|
|
13481
|
+
function rebuildExtractedFramesFromPlanDir(planDir, videos, indexMode = "dense-v1") {
|
|
13482
|
+
const result = [];
|
|
13483
|
+
for (const v of videos) {
|
|
13484
|
+
const outputDir = join27(planDir, "video-frames", v.videoId);
|
|
13485
|
+
if (!existsSync17(outputDir)) {
|
|
13486
|
+
throw new Error(
|
|
13487
|
+
`[renderChunk] planDir missing extracted video frames for ${JSON.stringify(v.videoId)}: ${outputDir} not present. plan() should have written frames here; the planDir is malformed.`
|
|
13488
|
+
);
|
|
13489
|
+
}
|
|
13490
|
+
const ext = (extname4(v.framePattern) || ".jpg").toLowerCase();
|
|
13491
|
+
const frames = readdirSync6(outputDir).filter((name) => name.toLowerCase().endsWith(ext)).sort((left, right) => {
|
|
13492
|
+
const leftNumber = frameNumberFromFileName(left);
|
|
13493
|
+
const rightNumber = frameNumberFromFileName(right);
|
|
13494
|
+
if (leftNumber === null || rightNumber === null) return left.localeCompare(right);
|
|
13495
|
+
return leftNumber - rightNumber || left.localeCompare(right);
|
|
13496
|
+
});
|
|
13497
|
+
const framePaths = /* @__PURE__ */ new Map();
|
|
13498
|
+
for (let i = 0; i < frames.length; i++) {
|
|
13499
|
+
const frameName = frames[i];
|
|
13500
|
+
if (!frameName) continue;
|
|
13501
|
+
const frameNumber = indexMode === "sparse-v2" ? frameNumberFromFileName(frameName) : null;
|
|
13502
|
+
const frameIndex = frameNumber === null ? i : frameNumber - 1;
|
|
13503
|
+
framePaths.set(frameIndex, join27(outputDir, frameName));
|
|
13504
|
+
}
|
|
13505
|
+
result.push({
|
|
13506
|
+
videoId: v.videoId,
|
|
13507
|
+
srcPath: v.srcPath,
|
|
13508
|
+
outputDir,
|
|
13509
|
+
framePattern: v.framePattern,
|
|
13510
|
+
fps: v.fps,
|
|
13511
|
+
totalFrames: v.totalFrames,
|
|
13512
|
+
metadata: v.metadata,
|
|
13513
|
+
framePaths,
|
|
13514
|
+
// The planDir is shared read-only across workers; cleanup must not delete its frames.
|
|
13515
|
+
ownedByLookup: false
|
|
13516
|
+
});
|
|
13517
|
+
}
|
|
13518
|
+
return result;
|
|
13519
|
+
}
|
|
13520
|
+
function contiguousRuns(sortedIndexes) {
|
|
13521
|
+
const runs2 = [];
|
|
13522
|
+
for (const index of sortedIndexes) {
|
|
13523
|
+
const last = runs2[runs2.length - 1];
|
|
13524
|
+
if (last && last.firstFrame + last.frames === index) last.frames++;
|
|
13525
|
+
else runs2.push({ firstFrame: index, frames: 1 });
|
|
13526
|
+
}
|
|
13527
|
+
return runs2;
|
|
13528
|
+
}
|
|
13529
|
+
function buildChunkCaptureOptions(input) {
|
|
13530
|
+
const { dimensions, encoder } = input;
|
|
13531
|
+
const videoCaptureBeyondViewport = resolveVideoCaptureBeyondViewport(input.videoCount);
|
|
13532
|
+
return {
|
|
13533
|
+
width: dimensions.width,
|
|
13534
|
+
height: dimensions.height,
|
|
13535
|
+
fps: { num: dimensions.fpsNum, den: dimensions.fpsDen },
|
|
13536
|
+
format: dimensions.format === "mp4" ? "jpeg" : "png",
|
|
13537
|
+
quality: dimensions.format === "mp4" ? resolveCaptureJpegQuality(encoder.quality) : void 0,
|
|
13538
|
+
deviceScaleFactor: encoder.deviceScaleFactor,
|
|
13539
|
+
// The controller's snapshot, so the first capture sees the in-process renderer's `__hfVariables`.
|
|
13540
|
+
variables: encoder.variables,
|
|
13541
|
+
subTimelineWaitMemo: {},
|
|
13542
|
+
...videoCaptureBeyondViewport !== void 0 ? { captureBeyondViewport: videoCaptureBeyondViewport } : {},
|
|
13543
|
+
// A fixed warmup count keeps `beginFrameTimeTicks` host-independent.
|
|
13544
|
+
lockWarmupTicks: true,
|
|
13545
|
+
requiresWebGpu: compositionRequiresWebGpu(input.html)
|
|
13546
|
+
};
|
|
13547
|
+
}
|
|
13548
|
+
async function extractDeferredVideoFramesForChunk(input) {
|
|
13549
|
+
const deferred = input.planVideos.extracted.filter((video) => video.deferredRange);
|
|
13550
|
+
if (deferred.length === 0) return [];
|
|
13551
|
+
const table = createFrameLookupTable(
|
|
13552
|
+
input.planVideos.videos,
|
|
13553
|
+
deferred.map((video) => ({
|
|
13554
|
+
...video,
|
|
13555
|
+
outputDir: input.outputRoot,
|
|
13556
|
+
framePaths: new Map(Array.from({ length: video.totalFrames }, (_, i) => [i, String(i)]))
|
|
13557
|
+
})),
|
|
13558
|
+
resolveRenderFpsConfig(input.fps).value
|
|
13559
|
+
);
|
|
13560
|
+
const shown = /* @__PURE__ */ new Map();
|
|
13561
|
+
for (const frame of input.frames) {
|
|
13562
|
+
const globalTime = frame * input.fps.den / input.fps.num;
|
|
13563
|
+
for (const [videoId, payload] of table.getActiveFramePayloads(globalTime)) {
|
|
13564
|
+
const indexes = shown.get(videoId) ?? /* @__PURE__ */ new Set();
|
|
13565
|
+
indexes.add(payload.frameIndex);
|
|
13566
|
+
shown.set(videoId, indexes);
|
|
13567
|
+
}
|
|
13568
|
+
}
|
|
13569
|
+
return Promise.all(
|
|
13570
|
+
deferred.map(async (video) => {
|
|
13571
|
+
const outputDir = join27(input.outputRoot, video.videoId);
|
|
13572
|
+
const indexes = [...shown.get(video.videoId) ?? []].sort((a, b) => a - b);
|
|
13573
|
+
if (indexes.length === 0) {
|
|
13574
|
+
return { ...video, outputDir, framePaths: /* @__PURE__ */ new Map(), ownedByLookup: false };
|
|
13575
|
+
}
|
|
13576
|
+
const range = video.deferredRange;
|
|
13577
|
+
const sourcePath = join27(input.planDir, ...range.sourcePath.split("/"));
|
|
13578
|
+
if (!existsSync17(sourcePath)) {
|
|
13579
|
+
throw new RenderChunkValidationError(
|
|
13580
|
+
MISSING_PLAN_ARTIFACT,
|
|
13581
|
+
`[renderChunk] planDir is missing the source for video ${JSON.stringify(video.videoId)}: ${range.sourcePath}`
|
|
13582
|
+
);
|
|
13583
|
+
}
|
|
13584
|
+
const extracted = await extractVideoFramesRange(
|
|
13585
|
+
sourcePath,
|
|
13586
|
+
video.videoId,
|
|
13587
|
+
range.startTime,
|
|
13588
|
+
range.durationSeconds,
|
|
13589
|
+
{
|
|
13590
|
+
fps: input.fps,
|
|
13591
|
+
outputDir: input.outputRoot,
|
|
13592
|
+
format: range.format,
|
|
13593
|
+
frameRanges: contiguousRuns(indexes)
|
|
13594
|
+
},
|
|
13595
|
+
void 0,
|
|
13596
|
+
input.cfg
|
|
13597
|
+
);
|
|
13598
|
+
const missing = indexes.find((index) => !extracted.framePaths.has(index));
|
|
13599
|
+
if (missing !== void 0) {
|
|
13600
|
+
throw new RenderChunkValidationError(
|
|
13601
|
+
INVALID_VIDEO_METADATA,
|
|
13602
|
+
`[renderChunk] video ${JSON.stringify(video.videoId)} has no frame ${missing}; the plan expected ${video.totalFrames} frames from ${range.sourcePath}.`
|
|
13603
|
+
);
|
|
13604
|
+
}
|
|
13605
|
+
return { ...video, outputDir, framePaths: extracted.framePaths, ownedByLookup: false };
|
|
13606
|
+
})
|
|
13607
|
+
);
|
|
13608
|
+
}
|
|
13609
|
+
|
|
13610
|
+
// ../producer/src/services/distributed/planFrameCost.ts
|
|
13611
|
+
async function measurePlanSecondsPerFrame(input) {
|
|
13612
|
+
const { planDir, workDir, planVideos, fps, log } = input;
|
|
13613
|
+
const fpsRatio = { num: fps, den: 1 };
|
|
13614
|
+
const sampled = selectCaptureCalibrationFrames(input.totalFrames);
|
|
13615
|
+
const extracted = [
|
|
13616
|
+
...rebuildExtractedFramesFromPlanDir(
|
|
13617
|
+
planDir,
|
|
13618
|
+
planVideos.extracted.filter((video) => !video.deferredRange)
|
|
13619
|
+
),
|
|
13620
|
+
...await extractDeferredVideoFramesForChunk({
|
|
13621
|
+
planDir,
|
|
13622
|
+
planVideos,
|
|
13623
|
+
frames: sampled,
|
|
13624
|
+
fps: fpsRatio,
|
|
13625
|
+
outputRoot: join28(workDir, "frame-cost-video-frames"),
|
|
13626
|
+
cfg: input.cfg
|
|
13627
|
+
})
|
|
13628
|
+
];
|
|
13629
|
+
const lookup = extracted.length > 0 ? createFrameLookupTable(planVideos.videos, extracted, resolveRenderFpsConfig(fpsRatio).value) : null;
|
|
13630
|
+
const compiledDir = join28(planDir, "compiled");
|
|
13631
|
+
const fileServer = await createFileServer({
|
|
13632
|
+
projectDir: compiledDir,
|
|
13633
|
+
compiledDir,
|
|
13634
|
+
port: 0,
|
|
13635
|
+
preHeadScripts: [buildVirtualTimeShim({ seedRandomFromFrame: true })],
|
|
13636
|
+
fps: fpsRatio
|
|
13637
|
+
});
|
|
13638
|
+
let session = null;
|
|
13639
|
+
try {
|
|
13640
|
+
session = await createCaptureSession(
|
|
13641
|
+
fileServer.url,
|
|
13642
|
+
join28(workDir, "frame-cost"),
|
|
13643
|
+
buildChunkCaptureOptions({
|
|
13644
|
+
dimensions: { ...input, fpsNum: fps, fpsDen: 1 },
|
|
13645
|
+
encoder: input,
|
|
13646
|
+
videoCount: planVideos.videos.length,
|
|
13647
|
+
html: input.html
|
|
13648
|
+
}),
|
|
13649
|
+
createChunkVideoFrameInjectorFactory(lookup)(),
|
|
13650
|
+
createCaptureCalibrationConfig(input.cfg)
|
|
13651
|
+
);
|
|
13652
|
+
await initializeSession(session);
|
|
13653
|
+
const { estimate } = await measureCaptureCostFromSession(session, input.totalFrames, fps, log);
|
|
13654
|
+
return (estimate.p95Ms ?? 0) / 1e3;
|
|
13655
|
+
} finally {
|
|
13656
|
+
if (session) await closeCaptureSession(session).catch(() => {
|
|
13657
|
+
});
|
|
13658
|
+
closeFileServerSafely(fileServer, "plan frame cost", log);
|
|
13659
|
+
}
|
|
13660
|
+
}
|
|
13661
|
+
|
|
13662
|
+
// ../producer/src/services/render/stages/freezePlan.ts
|
|
13663
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync9, readFileSync as readFileSync9, readdirSync as readdirSync7, writeFileSync as writeFileSync6 } from "fs";
|
|
13664
|
+
import { join as join29, relative as relative3, resolve as resolve7 } from "path";
|
|
13665
|
+
|
|
13666
|
+
// ../producer/src/services/distributed/planProtocol.ts
|
|
13667
|
+
var PLAN_SCHEMA_VERSION = 1;
|
|
13668
|
+
var PLAN_ARTIFACT_LAYOUT = "plan-dir-v1";
|
|
13669
|
+
var PLAN_HASH_SCHEMA = "hyperframes-plan-hash-v1";
|
|
13670
|
+
var PLAN_V2_SCHEMA_VERSION = 2;
|
|
13671
|
+
var PLAN_V2_ARTIFACT_LAYOUT = "content-addressed-plan-v2";
|
|
13672
|
+
var PLAN_V2_HASH_SCHEMA = "hyperframes-plan-manifest-hash-v2";
|
|
13673
|
+
var PLAN_PROTOCOL_UNSUPPORTED = "PLAN_PROTOCOL_UNSUPPORTED";
|
|
13674
|
+
var PLAN_PROTOCOL_V1 = Object.freeze({
|
|
13675
|
+
schemaVersion: PLAN_SCHEMA_VERSION,
|
|
13676
|
+
artifactLayout: PLAN_ARTIFACT_LAYOUT,
|
|
13677
|
+
hashSchema: PLAN_HASH_SCHEMA
|
|
13678
|
+
});
|
|
13679
|
+
var CURRENT_PLAN_PROTOCOL = PLAN_PROTOCOL_V1;
|
|
13680
|
+
var PLAN_PROTOCOL_V2 = Object.freeze({
|
|
13681
|
+
schemaVersion: PLAN_V2_SCHEMA_VERSION,
|
|
13682
|
+
artifactLayout: PLAN_V2_ARTIFACT_LAYOUT,
|
|
13683
|
+
hashSchema: PLAN_V2_HASH_SCHEMA
|
|
13684
|
+
});
|
|
13685
|
+
var DISTRIBUTED_RENDER_CAPABILITIES = Object.freeze({
|
|
13686
|
+
roles: Object.freeze({
|
|
13687
|
+
planner: Object.freeze({
|
|
13688
|
+
produces: Object.freeze([PLAN_PROTOCOL_V1, PLAN_PROTOCOL_V2])
|
|
13689
|
+
}),
|
|
13690
|
+
chunk: Object.freeze({
|
|
13691
|
+
accepts: Object.freeze([PLAN_PROTOCOL_V1, PLAN_PROTOCOL_V2]),
|
|
13692
|
+
acceptsLegacyV1WithoutDescriptor: true
|
|
13693
|
+
}),
|
|
13694
|
+
assembler: Object.freeze({
|
|
13695
|
+
accepts: Object.freeze([PLAN_PROTOCOL_V1, PLAN_PROTOCOL_V2]),
|
|
13696
|
+
acceptsLegacyV1WithoutDescriptor: true
|
|
13697
|
+
})
|
|
13698
|
+
})
|
|
13699
|
+
});
|
|
13700
|
+
function getDistributedRenderCapabilities() {
|
|
13701
|
+
return DISTRIBUTED_RENDER_CAPABILITIES;
|
|
13702
|
+
}
|
|
13703
|
+
var PlanProtocolUnsupportedError = class extends Error {
|
|
13704
|
+
// Public adapters inspect this typed code even though OSS producer does not.
|
|
13705
|
+
// fallow-ignore-next-line unused-class-member
|
|
13706
|
+
code = PLAN_PROTOCOL_UNSUPPORTED;
|
|
13707
|
+
constructor(reason) {
|
|
13708
|
+
super(`[planProtocol] ${reason} (${PLAN_PROTOCOL_UNSUPPORTED})`);
|
|
13709
|
+
this.name = "PlanProtocolUnsupportedError";
|
|
13710
|
+
}
|
|
13711
|
+
};
|
|
13712
|
+
function isRecord(value) {
|
|
13713
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
13714
|
+
}
|
|
13715
|
+
function protocolMatches(descriptor, expected) {
|
|
13716
|
+
return descriptor.schemaVersion === expected.schemaVersion && descriptor.artifactLayout === expected.artifactLayout && descriptor.hashSchema === expected.hashSchema;
|
|
13717
|
+
}
|
|
13718
|
+
function capabilitiesAccept(capabilities, protocol) {
|
|
13719
|
+
return capabilities.accepts.some(
|
|
13720
|
+
(accepted) => accepted.schemaVersion === protocol.schemaVersion && accepted.artifactLayout === protocol.artifactLayout && accepted.hashSchema === protocol.hashSchema
|
|
13721
|
+
);
|
|
13722
|
+
}
|
|
13723
|
+
function readPlanProtocol(planJson, capabilities = DISTRIBUTED_RENDER_CAPABILITIES.roles.chunk) {
|
|
13724
|
+
if (!isRecord(planJson)) {
|
|
13725
|
+
throw new PlanProtocolUnsupportedError("plan.json must contain a JSON object");
|
|
13726
|
+
}
|
|
13727
|
+
if (!Object.prototype.hasOwnProperty.call(planJson, "protocol")) {
|
|
13728
|
+
if (!capabilities.acceptsLegacyV1WithoutDescriptor || !capabilitiesAccept(capabilities, PLAN_PROTOCOL_V1)) {
|
|
13729
|
+
throw new PlanProtocolUnsupportedError(
|
|
13730
|
+
"legacy v1 plan without a protocol descriptor is not accepted by this worker"
|
|
13731
|
+
);
|
|
13732
|
+
}
|
|
13733
|
+
return PLAN_PROTOCOL_V1;
|
|
13734
|
+
}
|
|
13735
|
+
const descriptor = planJson.protocol;
|
|
13736
|
+
if (!isRecord(descriptor)) {
|
|
13737
|
+
throw new PlanProtocolUnsupportedError("plan.json protocol descriptor must be an object");
|
|
13738
|
+
}
|
|
13739
|
+
for (const field of ["schemaVersion", "artifactLayout", "hashSchema"]) {
|
|
13740
|
+
if (!Object.prototype.hasOwnProperty.call(descriptor, field)) {
|
|
13741
|
+
throw new PlanProtocolUnsupportedError(`plan.json protocol descriptor is missing ${field}`);
|
|
13742
|
+
}
|
|
13743
|
+
}
|
|
13744
|
+
const protocol = protocolMatches(descriptor, PLAN_PROTOCOL_V1) ? PLAN_PROTOCOL_V1 : protocolMatches(descriptor, PLAN_PROTOCOL_V2) ? PLAN_PROTOCOL_V2 : null;
|
|
13745
|
+
if (protocol === null || !capabilitiesAccept(capabilities, protocol)) {
|
|
13746
|
+
throw new PlanProtocolUnsupportedError("unsupported plan.json protocol descriptor");
|
|
13747
|
+
}
|
|
13748
|
+
return protocol;
|
|
13749
|
+
}
|
|
13750
|
+
function readPlanProtocolV1(planJson, capabilities = DISTRIBUTED_RENDER_CAPABILITIES.roles.chunk) {
|
|
13751
|
+
const protocol = readPlanProtocol(planJson, capabilities);
|
|
13752
|
+
if (protocol !== PLAN_PROTOCOL_V1) {
|
|
13753
|
+
throw new PlanProtocolUnsupportedError(
|
|
13754
|
+
"content-addressed v2 plan must be materialized before v1 layout access"
|
|
13755
|
+
);
|
|
13756
|
+
}
|
|
13757
|
+
return PLAN_PROTOCOL_V1;
|
|
13758
|
+
}
|
|
13759
|
+
|
|
13760
|
+
// ../producer/src/services/render/stages/planHash.ts
|
|
13761
|
+
import { createHash as createHash5 } from "crypto";
|
|
13762
|
+
import { closeSync as closeSync5, createReadStream as createReadStream3, openSync as openSync4, readSync as readSync3 } from "fs";
|
|
13763
|
+
var PLAN_HASH_SCHEMA_PREFIX = "hyperframes-plan-hash-v1\0";
|
|
13764
|
+
var FIELD_DELIMITER = Buffer.from([0]);
|
|
13765
|
+
function computePlanHash(input) {
|
|
13766
|
+
const hash = createHash5("sha256");
|
|
13767
|
+
hash.update(PLAN_HASH_SCHEMA_PREFIX, "utf8");
|
|
13768
|
+
hash.update(input.compositionHtml);
|
|
13769
|
+
hash.update(FIELD_DELIMITER);
|
|
13770
|
+
const sortedAssets = [...input.assets].sort(
|
|
13771
|
+
(a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0
|
|
13772
|
+
);
|
|
13773
|
+
for (const asset of sortedAssets) {
|
|
13774
|
+
hash.update(asset.path, "utf8");
|
|
13775
|
+
hash.update(FIELD_DELIMITER);
|
|
13776
|
+
hash.update(asset.sha256, "utf8");
|
|
13777
|
+
hash.update(FIELD_DELIMITER);
|
|
13778
|
+
}
|
|
13779
|
+
hash.update(input.fontSnapshotSha, "utf8");
|
|
13780
|
+
hash.update(FIELD_DELIMITER);
|
|
13781
|
+
hash.update(input.encoderConfigCanonicalJson, "utf8");
|
|
13782
|
+
hash.update(FIELD_DELIMITER);
|
|
13783
|
+
hash.update(input.producerVersion, "utf8");
|
|
13784
|
+
hash.update(FIELD_DELIMITER);
|
|
13785
|
+
hash.update(input.ffmpegVersion, "utf8");
|
|
13786
|
+
hash.update(FIELD_DELIMITER);
|
|
13787
|
+
const d = input.dimensions;
|
|
13788
|
+
hash.update(`${d.fpsNum}/${d.fpsDen}x${d.width}x${d.height}x${d.format}`, "utf8");
|
|
13789
|
+
return hash.digest("hex");
|
|
13790
|
+
}
|
|
13791
|
+
function canonicalJsonStringify(value) {
|
|
13792
|
+
if (value === null) return "null";
|
|
13793
|
+
if (typeof value === "boolean") return value ? "true" : "false";
|
|
13794
|
+
if (typeof value === "number") {
|
|
13795
|
+
if (!Number.isFinite(value)) {
|
|
13796
|
+
throw new TypeError(`canonicalJsonStringify: non-finite number ${value}`);
|
|
13797
|
+
}
|
|
13798
|
+
return JSON.stringify(value);
|
|
13799
|
+
}
|
|
13800
|
+
if (typeof value === "string") return JSON.stringify(value);
|
|
13801
|
+
if (Array.isArray(value)) {
|
|
13802
|
+
return `[${value.map(canonicalJsonStringify).join(",")}]`;
|
|
13803
|
+
}
|
|
13804
|
+
if (typeof value === "object") {
|
|
13805
|
+
const obj = value;
|
|
13806
|
+
const keys = Object.keys(obj).sort();
|
|
13807
|
+
const parts = keys.map((k) => `${JSON.stringify(k)}:${canonicalJsonStringify(obj[k])}`);
|
|
13808
|
+
return `{${parts.join(",")}}`;
|
|
13809
|
+
}
|
|
13810
|
+
throw new TypeError(`canonicalJsonStringify: unsupported value type ${typeof value}`);
|
|
13811
|
+
}
|
|
13812
|
+
function sha256Hex(bytes) {
|
|
13813
|
+
const h = createHash5("sha256");
|
|
13814
|
+
h.update(bytes);
|
|
13815
|
+
return h.digest("hex");
|
|
13816
|
+
}
|
|
13817
|
+
function sha256File(path) {
|
|
13818
|
+
const hash = createHash5("sha256");
|
|
13819
|
+
const buffer = Buffer.allocUnsafe(1024 * 1024);
|
|
13820
|
+
const fd = openSync4(path, "r");
|
|
13821
|
+
try {
|
|
13822
|
+
let bytesRead = 0;
|
|
13823
|
+
do {
|
|
13824
|
+
bytesRead = readSync3(fd, buffer, 0, buffer.byteLength, null);
|
|
13825
|
+
if (bytesRead > 0) hash.update(buffer.subarray(0, bytesRead));
|
|
13826
|
+
} while (bytesRead > 0);
|
|
13827
|
+
} finally {
|
|
13828
|
+
closeSync5(fd);
|
|
13829
|
+
}
|
|
13830
|
+
return hash.digest("hex");
|
|
13831
|
+
}
|
|
13832
|
+
async function sha256FileAsync(path) {
|
|
13833
|
+
const hash = createHash5("sha256");
|
|
13834
|
+
for await (const chunk of createReadStream3(path, { highWaterMark: 1024 * 1024 })) {
|
|
13835
|
+
hash.update(chunk);
|
|
13836
|
+
}
|
|
13837
|
+
return hash.digest("hex");
|
|
13838
|
+
}
|
|
13839
|
+
|
|
13840
|
+
// ../producer/src/services/render/stages/freezePlan.ts
|
|
13841
|
+
var COMPILED_INDEX_RELATIVE_PATH = "compiled/index.html";
|
|
13842
|
+
var HASH_EXCLUDED_PLAN_FILES = /* @__PURE__ */ new Set([
|
|
13843
|
+
"plan.json",
|
|
13844
|
+
"meta/encoder.json",
|
|
13845
|
+
COMPILED_INDEX_RELATIVE_PATH
|
|
13846
|
+
]);
|
|
13847
|
+
function planFileSha256(absolutePath, digests) {
|
|
13848
|
+
let sha256 = digests?.get(absolutePath);
|
|
13849
|
+
if (sha256 === void 0) {
|
|
13850
|
+
sha256 = sha256File(absolutePath);
|
|
13851
|
+
digests?.set(absolutePath, sha256);
|
|
13852
|
+
}
|
|
13853
|
+
return sha256;
|
|
13854
|
+
}
|
|
13855
|
+
async function prehashPlanFiles(dir, digests) {
|
|
13856
|
+
if (!existsSync18(dir)) return;
|
|
13857
|
+
for (const { absolutePath } of listPlanFiles(dir)) {
|
|
13858
|
+
if (!digests.has(absolutePath)) digests.set(absolutePath, await sha256FileAsync(absolutePath));
|
|
13859
|
+
}
|
|
13860
|
+
}
|
|
13861
|
+
function stripUndefined(value) {
|
|
13862
|
+
if (Array.isArray(value)) return value.map(stripUndefined);
|
|
13863
|
+
if (value !== null && typeof value === "object") {
|
|
13864
|
+
const obj = value;
|
|
13865
|
+
const out = {};
|
|
13866
|
+
for (const key of Object.keys(obj)) {
|
|
13867
|
+
const v = obj[key];
|
|
13868
|
+
if (v === void 0) continue;
|
|
13869
|
+
out[key] = stripUndefined(v);
|
|
13870
|
+
}
|
|
13871
|
+
return out;
|
|
13872
|
+
}
|
|
13873
|
+
return value;
|
|
13874
|
+
}
|
|
13875
|
+
function listPlanFiles(planDir) {
|
|
13876
|
+
const results = [];
|
|
13877
|
+
const rootResolved = resolve7(planDir);
|
|
13878
|
+
function walk(dir) {
|
|
13879
|
+
const entries = readdirSync7(dir, { withFileTypes: true });
|
|
13880
|
+
for (const entry of entries) {
|
|
13881
|
+
const full = join29(dir, entry.name);
|
|
13882
|
+
if (entry.isDirectory()) {
|
|
13883
|
+
walk(full);
|
|
13884
|
+
} else if (entry.isFile()) {
|
|
13885
|
+
results.push({
|
|
13886
|
+
planRelativePath: relative3(rootResolved, full).split(/[\\/]+/).join("/"),
|
|
13887
|
+
absolutePath: full
|
|
13888
|
+
});
|
|
13889
|
+
}
|
|
13890
|
+
}
|
|
13891
|
+
}
|
|
13892
|
+
walk(rootResolved);
|
|
13893
|
+
results.sort((a, b) => a.planRelativePath < b.planRelativePath ? -1 : 1);
|
|
13894
|
+
return results;
|
|
13895
|
+
}
|
|
13896
|
+
function collectPlanAssetShas(planDir, digests) {
|
|
13897
|
+
const files = listPlanFiles(planDir);
|
|
13898
|
+
let compositionHtml = null;
|
|
13899
|
+
const assets = [];
|
|
13900
|
+
for (const file of files) {
|
|
13901
|
+
if (file.planRelativePath === COMPILED_INDEX_RELATIVE_PATH) {
|
|
13902
|
+
compositionHtml = readFileSync9(file.absolutePath);
|
|
13903
|
+
continue;
|
|
13904
|
+
}
|
|
13905
|
+
if (HASH_EXCLUDED_PLAN_FILES.has(file.planRelativePath)) continue;
|
|
13906
|
+
assets.push({
|
|
13907
|
+
path: file.planRelativePath,
|
|
13908
|
+
sha256: planFileSha256(file.absolutePath, digests)
|
|
13909
|
+
});
|
|
13910
|
+
}
|
|
13911
|
+
if (compositionHtml === null) {
|
|
13912
|
+
throw new Error(
|
|
13913
|
+
`[freezePlan] compiled HTML missing at ${COMPILED_INDEX_RELATIVE_PATH} (upstream compile stage did not materialize the expected file).`
|
|
13914
|
+
);
|
|
13915
|
+
}
|
|
13916
|
+
return { compositionHtml, assets };
|
|
13917
|
+
}
|
|
13918
|
+
function recomputePlanHashFromPlanDir(planDir, digests) {
|
|
13919
|
+
const planJsonPath = join29(planDir, "plan.json");
|
|
13920
|
+
const encoderJsonPath = join29(planDir, "meta", "encoder.json");
|
|
13921
|
+
if (!existsSync18(planJsonPath)) {
|
|
13922
|
+
throw new Error(`[freezePlan] plan.json missing: ${planJsonPath}`);
|
|
13923
|
+
}
|
|
13924
|
+
if (!existsSync18(encoderJsonPath)) {
|
|
13925
|
+
throw new Error(`[freezePlan] meta/encoder.json missing: ${encoderJsonPath}`);
|
|
13926
|
+
}
|
|
13927
|
+
const planJson = JSON.parse(readFileSync9(planJsonPath, "utf-8"));
|
|
13928
|
+
const encoderConfigCanonicalJson = readFileSync9(encoderJsonPath, "utf-8");
|
|
13929
|
+
const { compositionHtml, assets } = collectPlanAssetShas(planDir, digests);
|
|
13930
|
+
return computePlanHash({
|
|
13931
|
+
compositionHtml,
|
|
13932
|
+
assets,
|
|
13933
|
+
fontSnapshotSha: planJson.fontSnapshotSha,
|
|
13934
|
+
encoderConfigCanonicalJson,
|
|
13935
|
+
producerVersion: planJson.producerVersion,
|
|
13936
|
+
ffmpegVersion: planJson.ffmpegVersion,
|
|
13937
|
+
dimensions: planJson.dimensions
|
|
13938
|
+
});
|
|
13939
|
+
}
|
|
13940
|
+
async function freezePlan(input) {
|
|
13941
|
+
const {
|
|
13942
|
+
planDir,
|
|
13943
|
+
composition,
|
|
13944
|
+
encoder,
|
|
13945
|
+
chunks,
|
|
13946
|
+
dimensions,
|
|
13947
|
+
producerVersion,
|
|
13948
|
+
fontSnapshotSha,
|
|
13949
|
+
durationSeconds,
|
|
13950
|
+
totalFrames,
|
|
13951
|
+
hasAudio,
|
|
13952
|
+
digests
|
|
13953
|
+
} = input;
|
|
13954
|
+
if (!existsSync18(planDir)) {
|
|
13955
|
+
throw new Error(`[freezePlan] planDir does not exist: ${planDir}`);
|
|
13956
|
+
}
|
|
13957
|
+
const metaDir = join29(planDir, "meta");
|
|
13958
|
+
if (!existsSync18(metaDir)) mkdirSync9(metaDir, { recursive: true });
|
|
13959
|
+
writeFileSync6(
|
|
13960
|
+
join29(metaDir, "composition.json"),
|
|
13961
|
+
`${JSON.stringify(composition, null, 2)}
|
|
13962
|
+
`,
|
|
13963
|
+
"utf-8"
|
|
13964
|
+
);
|
|
13965
|
+
const encoderForCanonical = stripUndefined(encoder);
|
|
13966
|
+
const encoderConfigCanonicalJson = canonicalJsonStringify(encoderForCanonical);
|
|
13967
|
+
writeFileSync6(join29(metaDir, "encoder.json"), encoderConfigCanonicalJson, "utf-8");
|
|
13968
|
+
writeFileSync6(join29(metaDir, "chunks.json"), `${JSON.stringify(chunks, null, 2)}
|
|
13969
|
+
`, "utf-8");
|
|
13970
|
+
const { compositionHtml, assets } = collectPlanAssetShas(planDir, digests);
|
|
13971
|
+
const planHash = computePlanHash({
|
|
13972
|
+
compositionHtml,
|
|
13973
|
+
assets,
|
|
13974
|
+
fontSnapshotSha,
|
|
13975
|
+
encoderConfigCanonicalJson,
|
|
13976
|
+
producerVersion,
|
|
13977
|
+
ffmpegVersion: encoder.ffmpegVersion,
|
|
13978
|
+
dimensions
|
|
13979
|
+
});
|
|
13980
|
+
const planJson = {
|
|
13981
|
+
protocol: PLAN_PROTOCOL_V1,
|
|
13982
|
+
planHash,
|
|
13983
|
+
producerVersion,
|
|
13984
|
+
ffmpegVersion: encoder.ffmpegVersion,
|
|
13985
|
+
fontSnapshotSha,
|
|
13986
|
+
dimensions,
|
|
13987
|
+
chunkCount: chunks.length,
|
|
13988
|
+
totalFrames,
|
|
13989
|
+
duration: durationSeconds,
|
|
13990
|
+
hasAudio
|
|
13991
|
+
};
|
|
13992
|
+
const planJsonPath = join29(planDir, "plan.json");
|
|
13993
|
+
writeFileSync6(planJsonPath, `${JSON.stringify(planJson, null, 2)}
|
|
13994
|
+
`, "utf-8");
|
|
13995
|
+
return { planJsonPath, planHash };
|
|
13796
13996
|
}
|
|
13797
|
-
|
|
13798
|
-
|
|
13799
|
-
|
|
13800
|
-
|
|
13801
|
-
|
|
13802
|
-
|
|
13803
|
-
|
|
13804
|
-
|
|
13805
|
-
|
|
13806
|
-
|
|
13807
|
-
|
|
13808
|
-
|
|
13809
|
-
|
|
13810
|
-
|
|
13811
|
-
|
|
13997
|
+
|
|
13998
|
+
// ../producer/src/services/render/runtimeEnvSnapshot.ts
|
|
13999
|
+
var RUNTIME_ENV_PREFIXES = [
|
|
14000
|
+
"PRODUCER_RUNTIME_",
|
|
14001
|
+
"PRODUCER_RENDER_"
|
|
14002
|
+
];
|
|
14003
|
+
function snapshotRuntimeEnv(env = process.env) {
|
|
14004
|
+
const snapshot = {};
|
|
14005
|
+
for (const key of Object.keys(env)) {
|
|
14006
|
+
const matches = RUNTIME_ENV_PREFIXES.some((prefix) => key.startsWith(prefix));
|
|
14007
|
+
if (!matches) continue;
|
|
14008
|
+
const value = env[key];
|
|
14009
|
+
if (typeof value !== "string") continue;
|
|
14010
|
+
snapshot[key] = value;
|
|
14011
|
+
}
|
|
14012
|
+
return snapshot;
|
|
14013
|
+
}
|
|
14014
|
+
function applyRuntimeEnvSnapshot(snapshot, env = process.env) {
|
|
14015
|
+
const previous = {};
|
|
14016
|
+
for (const [key, value] of Object.entries(snapshot)) {
|
|
14017
|
+
if (typeof value !== "string") continue;
|
|
14018
|
+
const matches = RUNTIME_ENV_PREFIXES.some((prefix) => key.startsWith(prefix));
|
|
14019
|
+
if (!matches) continue;
|
|
14020
|
+
previous[key] = env[key];
|
|
14021
|
+
env[key] = value;
|
|
14022
|
+
}
|
|
14023
|
+
return {
|
|
14024
|
+
restore: () => {
|
|
14025
|
+
for (const [key, value] of Object.entries(previous)) {
|
|
14026
|
+
if (value === void 0) delete env[key];
|
|
14027
|
+
else env[key] = value;
|
|
13812
14028
|
}
|
|
13813
14029
|
}
|
|
13814
|
-
|
|
13815
|
-
if (parent === current) break;
|
|
13816
|
-
current = parent;
|
|
13817
|
-
}
|
|
13818
|
-
cachedProducerVersion = "0.0.0-unknown";
|
|
13819
|
-
return cachedProducerVersion;
|
|
14030
|
+
};
|
|
13820
14031
|
}
|
|
13821
14032
|
|
|
13822
14033
|
// ../producer/src/services/distributed/planSize.ts
|
|
13823
14034
|
import { createHash as createHash6 } from "crypto";
|
|
13824
|
-
import { lstatSync as lstatSync2, readdirSync as
|
|
13825
|
-
import { extname as
|
|
14035
|
+
import { lstatSync as lstatSync2, readdirSync as readdirSync8 } from "fs";
|
|
14036
|
+
import { extname as extname5, join as join30, relative as relative4 } from "path";
|
|
13826
14037
|
var MAX_TOP_COMPONENTS = 10;
|
|
13827
14038
|
var SOURCE_MEDIA_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
13828
14039
|
".aac",
|
|
@@ -13882,7 +14093,7 @@ function emptyBreakdown() {
|
|
|
13882
14093
|
function addCategoryBytes(result, category, filePath, sizeBytes) {
|
|
13883
14094
|
if (category === "compiled") {
|
|
13884
14095
|
result.compiledBytes += sizeBytes;
|
|
13885
|
-
if (SOURCE_MEDIA_EXTENSIONS.has(
|
|
14096
|
+
if (SOURCE_MEDIA_EXTENSIONS.has(extname5(filePath).toLowerCase())) {
|
|
13886
14097
|
result.sourceMediaBytes += sizeBytes;
|
|
13887
14098
|
}
|
|
13888
14099
|
return;
|
|
@@ -13929,12 +14140,12 @@ function recordFile(result, components, rootDir, rootKind, filePath) {
|
|
|
13929
14140
|
function walkRegularFiles(dir, visit) {
|
|
13930
14141
|
let entries;
|
|
13931
14142
|
try {
|
|
13932
|
-
entries =
|
|
14143
|
+
entries = readdirSync8(dir, { withFileTypes: true });
|
|
13933
14144
|
} catch {
|
|
13934
14145
|
return;
|
|
13935
14146
|
}
|
|
13936
14147
|
for (const entry of entries) {
|
|
13937
|
-
const full =
|
|
14148
|
+
const full = join30(dir, entry.name);
|
|
13938
14149
|
if (entry.isDirectory()) {
|
|
13939
14150
|
walkRegularFiles(full, visit);
|
|
13940
14151
|
} else if (entry.isFile()) {
|
|
@@ -13995,8 +14206,17 @@ var MAX_PARALLEL_CHUNKS_CEILING = 256;
|
|
|
13995
14206
|
var ESTIMATED_SECONDS_PER_MEGAPIXEL_FRAME = 0.11;
|
|
13996
14207
|
var ESTIMATED_CHUNK_OVERHEAD_SECONDS = 60;
|
|
13997
14208
|
var WORKER_TIMEOUT_MARGIN = 0.75;
|
|
13998
|
-
function
|
|
13999
|
-
return
|
|
14209
|
+
function estimateFrameSeconds(megapixels, measuredSecondsPerFrame = 0) {
|
|
14210
|
+
return Math.max(ESTIMATED_SECONDS_PER_MEGAPIXEL_FRAME * megapixels, measuredSecondsPerFrame);
|
|
14211
|
+
}
|
|
14212
|
+
function estimateChunkSeconds(frames, megapixels, measuredSecondsPerFrame) {
|
|
14213
|
+
return ESTIMATED_CHUNK_OVERHEAD_SECONDS + frames * estimateFrameSeconds(megapixels, measuredSecondsPerFrame);
|
|
14214
|
+
}
|
|
14215
|
+
function planNeedsFrameCostMeasurement(html) {
|
|
14216
|
+
return new RegExp(`\\b${HF_COLOR_GRADING_ATTR}=`, "i").test(html);
|
|
14217
|
+
}
|
|
14218
|
+
function chunkSizingUsesFrameCost(config) {
|
|
14219
|
+
return config.maxParallelChunks === void 0 && config.chunkSize === void 0 && config.targetChunkFrames === void 0 && (config.workerTimeoutSeconds !== void 0 || config.workerConcurrency !== void 0);
|
|
14000
14220
|
}
|
|
14001
14221
|
var CHUNK_EXCEEDS_WORKER_TIMEOUT = "CHUNK_EXCEEDS_WORKER_TIMEOUT";
|
|
14002
14222
|
var ChunkExceedsWorkerTimeoutError = class extends Error {
|
|
@@ -14009,18 +14229,15 @@ var ChunkExceedsWorkerTimeoutError = class extends Error {
|
|
|
14009
14229
|
this.name = "ChunkExceedsWorkerTimeoutError";
|
|
14010
14230
|
}
|
|
14011
14231
|
};
|
|
14012
|
-
function resolveMaxParallelChunks(config, totalFrames, megapixels, log = defaultLogger) {
|
|
14232
|
+
function resolveMaxParallelChunks(config, totalFrames, megapixels, log = defaultLogger, measuredSecondsPerFrame) {
|
|
14013
14233
|
if (config.maxParallelChunks !== void 0) return config.maxParallelChunks;
|
|
14014
14234
|
const { workerTimeoutSeconds, workerConcurrency } = config;
|
|
14015
14235
|
const wave = Math.min(
|
|
14016
14236
|
workerConcurrency ?? DEFAULT_MAX_PARALLEL_CHUNKS,
|
|
14017
14237
|
MAX_PARALLEL_CHUNKS_CEILING
|
|
14018
14238
|
);
|
|
14019
|
-
|
|
14020
|
-
|
|
14021
|
-
return wave;
|
|
14022
|
-
}
|
|
14023
|
-
const secondsPerFrame = ESTIMATED_SECONDS_PER_MEGAPIXEL_FRAME * megapixels;
|
|
14239
|
+
if (!chunkSizingUsesFrameCost(config)) return wave;
|
|
14240
|
+
const secondsPerFrame = estimateFrameSeconds(megapixels, measuredSecondsPerFrame);
|
|
14024
14241
|
const minFramesPerChunk = Math.ceil(ESTIMATED_CHUNK_OVERHEAD_SECONDS / secondsPerFrame);
|
|
14025
14242
|
const wide = Math.max(1, Math.min(wave, Math.floor(totalFrames / minFramesPerChunk)));
|
|
14026
14243
|
if (workerTimeoutSeconds === void 0) return wide;
|
|
@@ -14031,7 +14248,7 @@ function resolveMaxParallelChunks(config, totalFrames, megapixels, log = default
|
|
|
14031
14248
|
if (needed > MAX_PARALLEL_CHUNKS_CEILING) {
|
|
14032
14249
|
const framesAtCeiling = Math.ceil(totalFrames / MAX_PARALLEL_CHUNKS_CEILING);
|
|
14033
14250
|
throw new ChunkExceedsWorkerTimeoutError(
|
|
14034
|
-
Math.ceil(estimateChunkSeconds(framesAtCeiling, megapixels)),
|
|
14251
|
+
Math.ceil(estimateChunkSeconds(framesAtCeiling, megapixels, measuredSecondsPerFrame)),
|
|
14035
14252
|
workerTimeoutSeconds
|
|
14036
14253
|
);
|
|
14037
14254
|
}
|
|
@@ -14138,13 +14355,13 @@ function assertPositiveInteger2(name, value) {
|
|
|
14138
14355
|
}
|
|
14139
14356
|
var FREEZE_OWNED_PLAN_FILES = [
|
|
14140
14357
|
"plan.json",
|
|
14141
|
-
|
|
14142
|
-
|
|
14143
|
-
|
|
14358
|
+
join31("meta", "composition.json"),
|
|
14359
|
+
join31("meta", "encoder.json"),
|
|
14360
|
+
join31("meta", "chunks.json")
|
|
14144
14361
|
];
|
|
14145
14362
|
function removeFreezeOwnedPlanFiles(planDir) {
|
|
14146
14363
|
for (const relativePath of FREEZE_OWNED_PLAN_FILES) {
|
|
14147
|
-
rmSync9(
|
|
14364
|
+
rmSync9(join31(planDir, relativePath), { force: true });
|
|
14148
14365
|
}
|
|
14149
14366
|
}
|
|
14150
14367
|
function buildChunkSlices(totalFrames, chunkCount, effectiveChunkSize) {
|
|
@@ -14275,19 +14492,19 @@ function createVideoSourceShipper(input) {
|
|
|
14275
14492
|
const known = shipped.get(srcPath);
|
|
14276
14493
|
if (known) return known;
|
|
14277
14494
|
const stagedRel = pathInside(input.workVideoFramesDir, srcPath);
|
|
14278
|
-
const current = stagedRel === null ? srcPath :
|
|
14495
|
+
const current = stagedRel === null ? srcPath : join31(input.planDir, "video-frames", stagedRel);
|
|
14279
14496
|
let result = null;
|
|
14280
14497
|
for (const root of stagedRel === null ? [input.workCompiledDir, input.projectDir] : []) {
|
|
14281
14498
|
const rel = pathInside(root, srcPath);
|
|
14282
|
-
if (rel !== null &&
|
|
14499
|
+
if (rel !== null && existsSync19(join31(input.planDir, "compiled", rel))) {
|
|
14283
14500
|
result = ["compiled", ...rel.split(sep2)].join("/");
|
|
14284
14501
|
break;
|
|
14285
14502
|
}
|
|
14286
14503
|
}
|
|
14287
14504
|
if (result === null) {
|
|
14288
|
-
result = `video-sources/${shipped.size}${
|
|
14289
|
-
const destination =
|
|
14290
|
-
mkdirSync10(
|
|
14505
|
+
result = `video-sources/${shipped.size}${extname6(current)}`;
|
|
14506
|
+
const destination = join31(input.planDir, ...result.split("/"));
|
|
14507
|
+
mkdirSync10(join31(input.planDir, "video-sources"), { recursive: true });
|
|
14291
14508
|
try {
|
|
14292
14509
|
linkSync(current, destination);
|
|
14293
14510
|
} catch {
|
|
@@ -14305,7 +14522,7 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
14305
14522
|
useGpu: false,
|
|
14306
14523
|
browserGpuMode: "software"
|
|
14307
14524
|
});
|
|
14308
|
-
if (!
|
|
14525
|
+
if (!existsSync19(planDir)) mkdirSync10(planDir, { recursive: true });
|
|
14309
14526
|
const log = config.logger ?? defaultLogger;
|
|
14310
14527
|
const sizeLimitBytes = options.executionPlanSizeLimitBytes ?? config.planDirSizeLimitBytes ?? PLAN_DIR_SIZE_LIMIT_BYTES;
|
|
14311
14528
|
const abortSignal = config.abortSignal;
|
|
@@ -14334,14 +14551,14 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
14334
14551
|
variables: config.variables
|
|
14335
14552
|
});
|
|
14336
14553
|
const entryFile = config.entryFile ?? "index.html";
|
|
14337
|
-
const htmlPath =
|
|
14338
|
-
if (!
|
|
14554
|
+
const htmlPath = join31(projectDir, entryFile);
|
|
14555
|
+
if (!existsSync19(htmlPath)) {
|
|
14339
14556
|
throw new Error(`[plan] entry file not found: ${htmlPath}`);
|
|
14340
14557
|
}
|
|
14341
|
-
const workDir =
|
|
14558
|
+
const workDir = join31(planDir, ".plan-work");
|
|
14342
14559
|
rmSync9(workDir, { recursive: true, force: true });
|
|
14343
14560
|
mkdirSync10(workDir, { recursive: true });
|
|
14344
|
-
const compiledDir =
|
|
14561
|
+
const compiledDir = join31(workDir, "compiled");
|
|
14345
14562
|
mkdirSync10(compiledDir, { recursive: true });
|
|
14346
14563
|
let stageStarted = Date.now();
|
|
14347
14564
|
cpSync2(projectDir, compiledDir, {
|
|
@@ -14355,7 +14572,7 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
14355
14572
|
}
|
|
14356
14573
|
});
|
|
14357
14574
|
logPlanStage(log, "copy", stageStarted);
|
|
14358
|
-
const finalCompiledDir =
|
|
14575
|
+
const finalCompiledDir = join31(planDir, "compiled");
|
|
14359
14576
|
const needsAlpha = outputNeedsAlpha(config.format);
|
|
14360
14577
|
stageStarted = Date.now();
|
|
14361
14578
|
const compileResult = await runCompileStage({
|
|
@@ -14418,7 +14635,7 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
14418
14635
|
if (probeResult.fileServer) closeFileServerSafely(probeResult.fileServer, "plan", log);
|
|
14419
14636
|
if (probeResult.probeSession) {
|
|
14420
14637
|
try {
|
|
14421
|
-
const { closeCaptureSession: closeCaptureSession2 } = await import("./src-
|
|
14638
|
+
const { closeCaptureSession: closeCaptureSession2 } = await import("./src-JGPFLYBL.js");
|
|
14422
14639
|
await closeCaptureSession2(probeResult.probeSession);
|
|
14423
14640
|
} catch (err) {
|
|
14424
14641
|
log.warn("[plan] probe session close failed", {
|
|
@@ -14426,19 +14643,9 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
14426
14643
|
});
|
|
14427
14644
|
}
|
|
14428
14645
|
}
|
|
14429
|
-
const
|
|
14430
|
-
|
|
14431
|
-
|
|
14432
|
-
compileResult.outputWidth * compileResult.outputHeight / 1e6,
|
|
14433
|
-
log
|
|
14434
|
-
);
|
|
14435
|
-
const { chunkCount, effectiveChunkSize } = resolveChunkPlan(
|
|
14436
|
-
totalFrames,
|
|
14437
|
-
config.chunkSize,
|
|
14438
|
-
maxParallel,
|
|
14439
|
-
config.targetChunkFrames
|
|
14440
|
-
);
|
|
14441
|
-
const chunks = buildChunkSlices(totalFrames, chunkCount, effectiveChunkSize);
|
|
14646
|
+
const megapixels = compileResult.outputWidth * compileResult.outputHeight / 1e6;
|
|
14647
|
+
let maxParallel = resolveMaxParallelChunks(config, totalFrames, megapixels, log);
|
|
14648
|
+
const measureFrameCost = chunkSizingUsesFrameCost(config) && planNeedsFrameCostMeasurement(compiled.html);
|
|
14442
14649
|
assertPlanSizeWithinLimit({
|
|
14443
14650
|
rootDir: compiledDir,
|
|
14444
14651
|
rootKind: "compiled",
|
|
@@ -14463,17 +14670,17 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
14463
14670
|
if (extractResult.extractionResult) {
|
|
14464
14671
|
assertVideoExtractionSucceeded(extractResult.extractionResult);
|
|
14465
14672
|
}
|
|
14466
|
-
const stagedVideoFrames =
|
|
14467
|
-
const videoFramesDst =
|
|
14468
|
-
if (
|
|
14469
|
-
const videoSourcesDir =
|
|
14673
|
+
const stagedVideoFrames = join31(compiledDir, "__hyperframes_video_frames");
|
|
14674
|
+
const videoFramesDst = join31(planDir, "video-frames");
|
|
14675
|
+
if (existsSync19(videoFramesDst)) rmSync9(videoFramesDst, { recursive: true, force: true });
|
|
14676
|
+
const videoSourcesDir = join31(planDir, "video-sources");
|
|
14470
14677
|
rmSync9(videoSourcesDir, { recursive: true, force: true });
|
|
14471
|
-
if (
|
|
14678
|
+
if (existsSync19(stagedVideoFrames)) {
|
|
14472
14679
|
renameSync4(stagedVideoFrames, videoFramesDst);
|
|
14473
14680
|
} else {
|
|
14474
14681
|
mkdirSync10(videoFramesDst, { recursive: true });
|
|
14475
14682
|
}
|
|
14476
|
-
if (
|
|
14683
|
+
if (existsSync19(finalCompiledDir)) rmSync9(finalCompiledDir, { recursive: true, force: true });
|
|
14477
14684
|
renameSync4(compiledDir, finalCompiledDir);
|
|
14478
14685
|
const shipVideoSource = createVideoSourceShipper({
|
|
14479
14686
|
planDir,
|
|
@@ -14499,9 +14706,9 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
14499
14706
|
...ext.deferredRange ? { deferredRange: { ...ext.deferredRange, sourcePath: shipVideoSource(ext.srcPath) } } : {}
|
|
14500
14707
|
}))
|
|
14501
14708
|
});
|
|
14502
|
-
mkdirSync10(
|
|
14709
|
+
mkdirSync10(join31(planDir, "meta"), { recursive: true });
|
|
14503
14710
|
writeFileSync7(
|
|
14504
|
-
|
|
14711
|
+
join31(planDir, PLAN_VIDEOS_META_RELATIVE_PATH),
|
|
14505
14712
|
JSON.stringify(planVideosJson, null, 2),
|
|
14506
14713
|
"utf-8"
|
|
14507
14714
|
);
|
|
@@ -14509,7 +14716,7 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
14509
14716
|
const chunksCarryAudio2 = carriesChunkAudio(config, options);
|
|
14510
14717
|
const audioSeconds = chunksCarryAudio2 ? totalFrames / config.fps : probeResult.duration;
|
|
14511
14718
|
stageStarted = Date.now();
|
|
14512
|
-
const [audioOutcome, overlapOutcome] = await Promise.allSettled([
|
|
14719
|
+
const [audioOutcome, overlapOutcome, frameCostOutcome] = await Promise.allSettled([
|
|
14513
14720
|
runAudioStage({
|
|
14514
14721
|
projectDir,
|
|
14515
14722
|
workDir,
|
|
@@ -14532,19 +14739,53 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
14532
14739
|
await options.whileAudioMixes();
|
|
14533
14740
|
logPlanStage(log, "prepublish", prepublishStarted);
|
|
14534
14741
|
}
|
|
14535
|
-
})()
|
|
14742
|
+
})(),
|
|
14743
|
+
measureFrameCost ? measurePlanSecondsPerFrame({
|
|
14744
|
+
planDir,
|
|
14745
|
+
workDir,
|
|
14746
|
+
html: compiled.html,
|
|
14747
|
+
planVideos: planVideosJson,
|
|
14748
|
+
totalFrames,
|
|
14749
|
+
fps: config.fps,
|
|
14750
|
+
width,
|
|
14751
|
+
height,
|
|
14752
|
+
format: config.format,
|
|
14753
|
+
quality: config.quality ?? "standard",
|
|
14754
|
+
deviceScaleFactor,
|
|
14755
|
+
variables: config.variables,
|
|
14756
|
+
cfg: { ...cfg, forceScreenshot, forceScreenshotExplicitlyOptedOut: !forceScreenshot },
|
|
14757
|
+
log
|
|
14758
|
+
}).finally(() => logPlanStage(log, "frame-cost", stageStarted)) : void 0
|
|
14536
14759
|
]);
|
|
14537
14760
|
if (audioOutcome.status === "rejected") throw audioOutcome.reason;
|
|
14538
14761
|
if (overlapOutcome.status === "rejected") throw overlapOutcome.reason;
|
|
14762
|
+
if (frameCostOutcome.status === "rejected") throw frameCostOutcome.reason;
|
|
14763
|
+
if (frameCostOutcome.value !== void 0) {
|
|
14764
|
+
log.info("[plan] measured frame cost", { secondsPerFrame: frameCostOutcome.value });
|
|
14765
|
+
maxParallel = resolveMaxParallelChunks(
|
|
14766
|
+
config,
|
|
14767
|
+
totalFrames,
|
|
14768
|
+
megapixels,
|
|
14769
|
+
log,
|
|
14770
|
+
frameCostOutcome.value
|
|
14771
|
+
);
|
|
14772
|
+
}
|
|
14773
|
+
const { chunkCount, effectiveChunkSize } = resolveChunkPlan(
|
|
14774
|
+
totalFrames,
|
|
14775
|
+
config.chunkSize,
|
|
14776
|
+
maxParallel,
|
|
14777
|
+
config.targetChunkFrames
|
|
14778
|
+
);
|
|
14779
|
+
const chunks = buildChunkSlices(totalFrames, chunkCount, effectiveChunkSize);
|
|
14539
14780
|
const audioResult = audioOutcome.value;
|
|
14540
14781
|
if (audioResult.audioError) {
|
|
14541
14782
|
applyDistributedAudioWarningPolicy(job, audioResult.audioError, audioResult.audioFailures, log);
|
|
14542
14783
|
}
|
|
14543
|
-
const planAudioPath =
|
|
14544
|
-
if (audioResult.hasAudio &&
|
|
14784
|
+
const planAudioPath = join31(planDir, PLAN_AUDIO_RELATIVE_PATH);
|
|
14785
|
+
if (audioResult.hasAudio && existsSync19(audioResult.audioOutputPath)) {
|
|
14545
14786
|
renameSync4(audioResult.audioOutputPath, planAudioPath);
|
|
14546
14787
|
}
|
|
14547
|
-
if (chunksCarryAudio2 &&
|
|
14788
|
+
if (chunksCarryAudio2 && existsSync19(planAudioPath)) {
|
|
14548
14789
|
stageStarted = Date.now();
|
|
14549
14790
|
const peak = await constrainAacTruePeak({
|
|
14550
14791
|
audioPath: planAudioPath,
|
|
@@ -15129,7 +15370,7 @@ function renderRequestFromDistributedConfig(input) {
|
|
|
15129
15370
|
|
|
15130
15371
|
// ../producer/src/server.ts
|
|
15131
15372
|
import {
|
|
15132
|
-
existsSync as
|
|
15373
|
+
existsSync as existsSync22,
|
|
15133
15374
|
mkdirSync as mkdirSync11,
|
|
15134
15375
|
statSync as statSync7,
|
|
15135
15376
|
mkdtempSync as mkdtempSync4,
|
|
@@ -15137,7 +15378,7 @@ import {
|
|
|
15137
15378
|
rmSync as rmSync10,
|
|
15138
15379
|
createReadStream as createReadStream4
|
|
15139
15380
|
} from "fs";
|
|
15140
|
-
import { resolve as resolve9, dirname as dirname12, join as
|
|
15381
|
+
import { resolve as resolve9, dirname as dirname12, join as join34 } from "path";
|
|
15141
15382
|
import { tmpdir as tmpdir3 } from "os";
|
|
15142
15383
|
import { parseArgs } from "util";
|
|
15143
15384
|
import crypto from "crypto";
|
|
@@ -15147,7 +15388,7 @@ import { serve as serve2 } from "@hono/node-server";
|
|
|
15147
15388
|
|
|
15148
15389
|
// ../producer/src/services/hyperframeLint.ts
|
|
15149
15390
|
import {
|
|
15150
|
-
existsSync as
|
|
15391
|
+
existsSync as existsSync20,
|
|
15151
15392
|
readFileSync as readFileSync10,
|
|
15152
15393
|
statSync as statSync6,
|
|
15153
15394
|
openSync as openSync5,
|
|
@@ -15155,7 +15396,7 @@ import {
|
|
|
15155
15396
|
closeSync as closeSync6,
|
|
15156
15397
|
constants as constants3
|
|
15157
15398
|
} from "fs";
|
|
15158
|
-
import { resolve as resolve8, join as
|
|
15399
|
+
import { resolve as resolve8, join as join32, relative as relative6, isAbsolute as isAbsolute3, sep as sep3 } from "path";
|
|
15159
15400
|
function isStringRecord(value) {
|
|
15160
15401
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
15161
15402
|
return false;
|
|
@@ -15200,7 +15441,7 @@ function readEntryHtml(filePath) {
|
|
|
15200
15441
|
}
|
|
15201
15442
|
function readProjectEntryFile(projectDir, preferredEntryFile) {
|
|
15202
15443
|
const absProjectDir = resolve8(projectDir);
|
|
15203
|
-
if (!
|
|
15444
|
+
if (!existsSync20(absProjectDir) || !statSync6(absProjectDir).isDirectory()) {
|
|
15204
15445
|
return { error: `Project directory not found: ${absProjectDir}` };
|
|
15205
15446
|
}
|
|
15206
15447
|
const entryCandidates = [preferredEntryFile, "index.html", "src/index.html"].filter(
|
|
@@ -15222,7 +15463,7 @@ function readProjectEntryFile(projectDir, preferredEntryFile) {
|
|
|
15222
15463
|
}
|
|
15223
15464
|
}
|
|
15224
15465
|
return {
|
|
15225
|
-
error: `No HTML entry file found in project directory: ${
|
|
15466
|
+
error: `No HTML entry file found in project directory: ${join32(absProjectDir, preferredEntryFile || "index.html")}`
|
|
15226
15467
|
};
|
|
15227
15468
|
}
|
|
15228
15469
|
function prepareHyperframeLintBody(body) {
|
|
@@ -15268,8 +15509,8 @@ async function runHyperframeLint(prepared) {
|
|
|
15268
15509
|
// ../producer/src/services/healthWorker.ts
|
|
15269
15510
|
import { Worker as Worker2 } from "worker_threads";
|
|
15270
15511
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
15271
|
-
import { dirname as dirname11, join as
|
|
15272
|
-
import { existsSync as
|
|
15512
|
+
import { dirname as dirname11, join as join33 } from "path";
|
|
15513
|
+
import { existsSync as existsSync21 } from "fs";
|
|
15273
15514
|
var DEFAULT_HEALTH_PORT = 9848;
|
|
15274
15515
|
async function startHealthWorker(options = {}) {
|
|
15275
15516
|
const log = options.logger ?? defaultLogger2();
|
|
@@ -15344,9 +15585,9 @@ function defaultLogger2() {
|
|
|
15344
15585
|
}
|
|
15345
15586
|
function resolveWorkerEntry2() {
|
|
15346
15587
|
const here = dirname11(fileURLToPath6(import.meta.url));
|
|
15347
|
-
const candidates = [
|
|
15588
|
+
const candidates = [join33(here, "healthWorkerThread.js"), join33(here, "healthWorkerThread.ts")];
|
|
15348
15589
|
for (const candidate of candidates) {
|
|
15349
|
-
if (
|
|
15590
|
+
if (existsSync21(candidate)) return candidate;
|
|
15350
15591
|
}
|
|
15351
15592
|
return void 0;
|
|
15352
15593
|
}
|
|
@@ -15485,7 +15726,7 @@ function resolvePreparedRenderOutput(prepared, rendersDir, log) {
|
|
|
15485
15726
|
const { input, cleanupProjectDir } = prepared;
|
|
15486
15727
|
const absoluteOutputPath = resolveOutputPath(input.projectDir, input.outputPath, rendersDir, log);
|
|
15487
15728
|
const outputDir = dirname12(absoluteOutputPath);
|
|
15488
|
-
if (!
|
|
15729
|
+
if (!existsSync22(outputDir)) mkdirSync11(outputDir, { recursive: true });
|
|
15489
15730
|
return { input, cleanupProjectDir, absoluteOutputPath };
|
|
15490
15731
|
}
|
|
15491
15732
|
function validateRenderOverrides(body) {
|
|
@@ -15525,11 +15766,11 @@ function prepareProjectDirectory(projectDir, options) {
|
|
|
15525
15766
|
const candidate = nonEmptyString(projectDir);
|
|
15526
15767
|
if (!candidate) return null;
|
|
15527
15768
|
const absProjectDir = resolve9(candidate);
|
|
15528
|
-
if (!
|
|
15769
|
+
if (!existsSync22(absProjectDir) || !statSync7(absProjectDir).isDirectory()) {
|
|
15529
15770
|
return { error: `Project directory not found: ${absProjectDir}` };
|
|
15530
15771
|
}
|
|
15531
15772
|
const entry = options.entryFile || "index.html";
|
|
15532
|
-
if (!
|
|
15773
|
+
if (!existsSync22(resolve9(absProjectDir, entry))) {
|
|
15533
15774
|
return { error: `Entry file "${entry}" not found in project directory: ${absProjectDir}` };
|
|
15534
15775
|
}
|
|
15535
15776
|
return { prepared: { input: { projectDir: absProjectDir, ...options } } };
|
|
@@ -15554,8 +15795,8 @@ async function resolveInlineRenderHtml(body) {
|
|
|
15554
15795
|
}
|
|
15555
15796
|
function materializeInlineProject(html, options) {
|
|
15556
15797
|
const tempRoot = process.env.PRODUCER_TMP_PROJECT_DIR || tmpdir3();
|
|
15557
|
-
const tempProjectDir = mkdtempSync4(
|
|
15558
|
-
writeFileSync8(
|
|
15798
|
+
const tempProjectDir = mkdtempSync4(join34(tempRoot, "producer-project-"));
|
|
15799
|
+
writeFileSync8(join34(tempProjectDir, "index.html"), html, "utf-8");
|
|
15559
15800
|
return {
|
|
15560
15801
|
prepared: {
|
|
15561
15802
|
input: { projectDir: tempProjectDir, ...options },
|
|
@@ -15621,7 +15862,7 @@ function cleanupTempDir(dir, log) {
|
|
|
15621
15862
|
}
|
|
15622
15863
|
}
|
|
15623
15864
|
function outputFileSize(path) {
|
|
15624
|
-
return
|
|
15865
|
+
return existsSync22(path) ? statSync7(path).size : 0;
|
|
15625
15866
|
}
|
|
15626
15867
|
function createBlockingProgressReporter(log, requestId) {
|
|
15627
15868
|
let lastLoggedPct = -10;
|
|
@@ -15917,7 +16158,7 @@ function createRenderHandlers(options = {}) {
|
|
|
15917
16158
|
if (!artifact) {
|
|
15918
16159
|
return c.json({ success: false, error: "Output artifact not found or expired" }, 404);
|
|
15919
16160
|
}
|
|
15920
|
-
if (!
|
|
16161
|
+
if (!existsSync22(artifact.path)) {
|
|
15921
16162
|
store.delete(token);
|
|
15922
16163
|
return c.json({ success: false, error: "Output artifact file missing" }, 404);
|
|
15923
16164
|
}
|
|
@@ -15964,7 +16205,7 @@ function startServer(options = {}) {
|
|
|
15964
16205
|
});
|
|
15965
16206
|
async function shutdown(signal) {
|
|
15966
16207
|
log.info(`Received ${signal}, shutting down`);
|
|
15967
|
-
const { drainBrowserPool } = await import("./src-
|
|
16208
|
+
const { drainBrowserPool } = await import("./src-JGPFLYBL.js");
|
|
15968
16209
|
await drainBrowserPool().catch(() => {
|
|
15969
16210
|
});
|
|
15970
16211
|
const handle = await Promise.race([
|
|
@@ -16002,24 +16243,24 @@ if (isPublicServerEntry) {
|
|
|
16002
16243
|
// ../producer/src/services/distributed/planV2.ts
|
|
16003
16244
|
import {
|
|
16004
16245
|
copyFileSync as copyFileSync6,
|
|
16005
|
-
existsSync as
|
|
16246
|
+
existsSync as existsSync24,
|
|
16006
16247
|
lstatSync as lstatSync3,
|
|
16007
16248
|
mkdirSync as mkdirSync13,
|
|
16008
16249
|
mkdtempSync as mkdtempSync6,
|
|
16009
16250
|
readFileSync as readFileSync11,
|
|
16010
|
-
readdirSync as
|
|
16251
|
+
readdirSync as readdirSync9,
|
|
16011
16252
|
renameSync as renameSync6,
|
|
16012
16253
|
rmSync as rmSync12,
|
|
16013
16254
|
statSync as statSync9,
|
|
16014
16255
|
writeFileSync as writeFileSync10
|
|
16015
16256
|
} from "fs";
|
|
16016
16257
|
import { tmpdir as tmpdir4 } from "os";
|
|
16017
|
-
import { dirname as dirname14, isAbsolute as isAbsolute4, join as
|
|
16258
|
+
import { dirname as dirname14, isAbsolute as isAbsolute4, join as join37, relative as relative7, resolve as resolve10, sep as sep4 } from "path";
|
|
16018
16259
|
|
|
16019
16260
|
// ../producer/src/services/distributed/planV2Publisher.ts
|
|
16020
16261
|
import {
|
|
16021
16262
|
copyFileSync as copyFileSync5,
|
|
16022
|
-
existsSync as
|
|
16263
|
+
existsSync as existsSync23,
|
|
16023
16264
|
linkSync as linkSync2,
|
|
16024
16265
|
mkdirSync as mkdirSync12,
|
|
16025
16266
|
mkdtempSync as mkdtempSync5,
|
|
@@ -16028,7 +16269,7 @@ import {
|
|
|
16028
16269
|
statSync as statSync8,
|
|
16029
16270
|
writeFileSync as writeFileSync9
|
|
16030
16271
|
} from "fs";
|
|
16031
|
-
import { dirname as dirname13, join as
|
|
16272
|
+
import { dirname as dirname13, join as join36 } from "path";
|
|
16032
16273
|
|
|
16033
16274
|
// ../producer/src/services/distributed/planV2Errors.ts
|
|
16034
16275
|
var PLAN_V2_INTEGRITY_UNRECOVERABLE = "PLAN_V2_INTEGRITY_UNRECOVERABLE";
|
|
@@ -16043,7 +16284,7 @@ var PlanV2IntegrityError = class extends Error {
|
|
|
16043
16284
|
};
|
|
16044
16285
|
|
|
16045
16286
|
// ../producer/src/services/distributed/planV2Layout.ts
|
|
16046
|
-
import { join as
|
|
16287
|
+
import { join as join35 } from "path";
|
|
16047
16288
|
var SHA256_PATTERN = /^[0-9a-f]{64}$/;
|
|
16048
16289
|
function assertPlanV2Sha256(value, field) {
|
|
16049
16290
|
if (typeof value !== "string" || !SHA256_PATTERN.test(value)) {
|
|
@@ -16053,7 +16294,7 @@ function assertPlanV2Sha256(value, field) {
|
|
|
16053
16294
|
}
|
|
16054
16295
|
function planV2BlobPath(root, sha256) {
|
|
16055
16296
|
const digest = assertPlanV2Sha256(sha256, "artifact sha256");
|
|
16056
|
-
return
|
|
16297
|
+
return join35(root, "artifacts", "sha256", digest.slice(0, 2), digest);
|
|
16057
16298
|
}
|
|
16058
16299
|
|
|
16059
16300
|
// ../producer/src/services/distributed/planV2Publisher.ts
|
|
@@ -16088,13 +16329,13 @@ var LocalPlanV2ArtifactPublisher = class {
|
|
|
16088
16329
|
#linkFile;
|
|
16089
16330
|
#committed = false;
|
|
16090
16331
|
constructor(destinationDir, options = {}) {
|
|
16091
|
-
if (
|
|
16332
|
+
if (existsSync23(destinationDir)) {
|
|
16092
16333
|
throw new PlanV2IntegrityError(`output directory already exists: ${destinationDir}`);
|
|
16093
16334
|
}
|
|
16094
16335
|
this.destinationDir = destinationDir;
|
|
16095
16336
|
this.#linkFile = options.linkFile ?? linkSync2;
|
|
16096
16337
|
mkdirSync12(dirname13(destinationDir), { recursive: true });
|
|
16097
|
-
this.temporaryDir = mkdtempSync5(
|
|
16338
|
+
this.temporaryDir = mkdtempSync5(join36(dirname13(destinationDir), ".plan-v2-publish-"));
|
|
16098
16339
|
}
|
|
16099
16340
|
async putBlob(blob) {
|
|
16100
16341
|
const digest = assertPlanV2Sha256(blob.sha256, "published blob sha256");
|
|
@@ -16105,10 +16346,10 @@ var LocalPlanV2ArtifactPublisher = class {
|
|
|
16105
16346
|
);
|
|
16106
16347
|
}
|
|
16107
16348
|
const destinationPath = planV2BlobPath(this.temporaryDir, digest);
|
|
16108
|
-
if (
|
|
16349
|
+
if (existsSync23(destinationPath)) return;
|
|
16109
16350
|
mkdirSync12(dirname13(destinationPath), { recursive: true });
|
|
16110
|
-
const stagingDir = mkdtempSync5(
|
|
16111
|
-
const temporaryPath =
|
|
16351
|
+
const stagingDir = mkdtempSync5(join36(dirname13(destinationPath), ".plan-v2-blob-"));
|
|
16352
|
+
const temporaryPath = join36(stagingDir, "blob");
|
|
16112
16353
|
try {
|
|
16113
16354
|
try {
|
|
16114
16355
|
this.#linkFile(blob.sourcePath, temporaryPath);
|
|
@@ -16123,13 +16364,13 @@ var LocalPlanV2ArtifactPublisher = class {
|
|
|
16123
16364
|
}
|
|
16124
16365
|
async commitManifest(manifestBytes) {
|
|
16125
16366
|
for (const digest of new Set(manifestDigests(manifestBytes))) {
|
|
16126
|
-
if (!
|
|
16367
|
+
if (!existsSync23(planV2BlobPath(this.temporaryDir, digest))) {
|
|
16127
16368
|
throw new PlanV2IntegrityError(
|
|
16128
16369
|
`cannot commit manifest before referenced blob is durable: ${digest}`
|
|
16129
16370
|
);
|
|
16130
16371
|
}
|
|
16131
16372
|
}
|
|
16132
|
-
writeFileSync9(
|
|
16373
|
+
writeFileSync9(join36(this.temporaryDir, "plan.json"), manifestBytes, "utf-8");
|
|
16133
16374
|
renameSync5(this.temporaryDir, this.destinationDir);
|
|
16134
16375
|
this.#committed = true;
|
|
16135
16376
|
}
|
|
@@ -16173,8 +16414,8 @@ function listFiles(root) {
|
|
|
16173
16414
|
const files = [];
|
|
16174
16415
|
const rootResolved = resolve10(root);
|
|
16175
16416
|
function walk(dir) {
|
|
16176
|
-
for (const entry of
|
|
16177
|
-
const absolutePath =
|
|
16417
|
+
for (const entry of readdirSync9(dir, { withFileTypes: true })) {
|
|
16418
|
+
const absolutePath = join37(dir, entry.name);
|
|
16178
16419
|
if (entry.isDirectory()) {
|
|
16179
16420
|
walk(absolutePath);
|
|
16180
16421
|
} else if (entry.isFile()) {
|
|
@@ -16201,13 +16442,13 @@ function assertValidExtractionCacheCompleteSentinel(path) {
|
|
|
16201
16442
|
}
|
|
16202
16443
|
}
|
|
16203
16444
|
function validateExtractionCacheCompleteSentinels(executionPlanDir) {
|
|
16204
|
-
const videoRoot =
|
|
16205
|
-
if (!
|
|
16206
|
-
for (const videoEntry of
|
|
16445
|
+
const videoRoot = join37(executionPlanDir, "video-frames");
|
|
16446
|
+
if (!existsSync24(videoRoot)) return;
|
|
16447
|
+
for (const videoEntry of readdirSync9(videoRoot, { withFileTypes: true })) {
|
|
16207
16448
|
if (!videoEntry.isDirectory()) continue;
|
|
16208
|
-
const videoDir =
|
|
16209
|
-
if (
|
|
16210
|
-
const sentinelPath =
|
|
16449
|
+
const videoDir = join37(videoRoot, videoEntry.name);
|
|
16450
|
+
if (readdirSync9(videoDir).includes(EXTRACTION_CACHE_COMPLETE_SENTINEL)) {
|
|
16451
|
+
const sentinelPath = join37(videoDir, EXTRACTION_CACHE_COMPLETE_SENTINEL);
|
|
16211
16452
|
assertValidExtractionCacheCompleteSentinel(sentinelPath);
|
|
16212
16453
|
}
|
|
16213
16454
|
}
|
|
@@ -16248,15 +16489,15 @@ function listVideoFramePaths(executionPlanDir, videos) {
|
|
|
16248
16489
|
if (video.deferredRange) {
|
|
16249
16490
|
const framePaths2 = /* @__PURE__ */ new Map();
|
|
16250
16491
|
for (let index = 0; index < video.totalFrames; index++) {
|
|
16251
|
-
framePaths2.set(index,
|
|
16492
|
+
framePaths2.set(index, join37(outputDir, String(index)));
|
|
16252
16493
|
}
|
|
16253
16494
|
return { ...video, outputDir, framePaths: framePaths2, ownedByLookup: false };
|
|
16254
16495
|
}
|
|
16255
|
-
const frameNames =
|
|
16496
|
+
const frameNames = readdirSync9(outputDir).sort();
|
|
16256
16497
|
const framePaths = /* @__PURE__ */ new Map();
|
|
16257
16498
|
for (const frameName of frameNames) {
|
|
16258
16499
|
if (frameName === EXTRACTION_CACHE_COMPLETE_SENTINEL) {
|
|
16259
|
-
assertValidExtractionCacheCompleteSentinel(
|
|
16500
|
+
assertValidExtractionCacheCompleteSentinel(join37(outputDir, frameName));
|
|
16260
16501
|
continue;
|
|
16261
16502
|
}
|
|
16262
16503
|
const match = /(\d+)(?=\.[^.]+$)/.exec(frameName);
|
|
@@ -16275,7 +16516,7 @@ function listVideoFramePaths(executionPlanDir, videos) {
|
|
|
16275
16516
|
`duplicate extracted frame index ${frameIndex} in ${outputDir}`
|
|
16276
16517
|
);
|
|
16277
16518
|
}
|
|
16278
|
-
framePaths.set(frameIndex,
|
|
16519
|
+
framePaths.set(frameIndex, join37(outputDir, frameName));
|
|
16279
16520
|
}
|
|
16280
16521
|
return {
|
|
16281
16522
|
...video,
|
|
@@ -16296,8 +16537,8 @@ function parsePlanVideosJson2(value) {
|
|
|
16296
16537
|
}
|
|
16297
16538
|
}
|
|
16298
16539
|
function materializeExtractedVideoDirectories(planDir) {
|
|
16299
|
-
const videosPath =
|
|
16300
|
-
if (!
|
|
16540
|
+
const videosPath = join37(planDir, PLAN_VIDEOS_META_RELATIVE_PATH);
|
|
16541
|
+
if (!existsSync24(videosPath)) return;
|
|
16301
16542
|
const videos = parsePlanVideosJson2(readJsonFile(videosPath, PLAN_VIDEOS_META_RELATIVE_PATH));
|
|
16302
16543
|
for (const video of videos.extracted) {
|
|
16303
16544
|
mkdirSync13(resolveExtractedVideoOutputDir(planDir, video.videoId), { recursive: true });
|
|
@@ -16325,13 +16566,13 @@ function parseChunkSlices(value) {
|
|
|
16325
16566
|
});
|
|
16326
16567
|
}
|
|
16327
16568
|
function buildVideoChunkDependencies(executionPlanDir, dimensions) {
|
|
16328
|
-
const videoRoot =
|
|
16329
|
-
const hasExtractedFrames =
|
|
16330
|
-
const videosPath =
|
|
16331
|
-
if (!
|
|
16569
|
+
const videoRoot = join37(executionPlanDir, "video-frames");
|
|
16570
|
+
const hasExtractedFrames = existsSync24(videoRoot) && listFiles(videoRoot).length > 0;
|
|
16571
|
+
const videosPath = join37(executionPlanDir, PLAN_VIDEOS_META_RELATIVE_PATH);
|
|
16572
|
+
if (!existsSync24(videosPath)) {
|
|
16332
16573
|
return hasExtractedFrames ? { mode: "full-source-pack", dependencies: null } : { mode: "exact-rendered-frames", dependencies: /* @__PURE__ */ new Map() };
|
|
16333
16574
|
}
|
|
16334
|
-
const chunksPath =
|
|
16575
|
+
const chunksPath = join37(executionPlanDir, "meta", "chunks.json");
|
|
16335
16576
|
const videos = readJsonFile(videosPath, PLAN_VIDEOS_META_RELATIVE_PATH);
|
|
16336
16577
|
const chunks = readJsonFile(chunksPath, "meta/chunks.json");
|
|
16337
16578
|
const parsedVideos = parsePlanVideosJson2(videos);
|
|
@@ -16385,10 +16626,10 @@ function computeManifestHash(manifest) {
|
|
|
16385
16626
|
return sha256Hex(`${PLAN_V2_HASH_PREFIX}${canonicalJsonStringify(manifestPayload(manifest))}`);
|
|
16386
16627
|
}
|
|
16387
16628
|
function writeBlob(sourcePath, destinationPath) {
|
|
16388
|
-
if (
|
|
16629
|
+
if (existsSync24(destinationPath)) return;
|
|
16389
16630
|
mkdirSync13(dirname14(destinationPath), { recursive: true });
|
|
16390
|
-
const temporaryDir = mkdtempSync6(
|
|
16391
|
-
const temporaryPath =
|
|
16631
|
+
const temporaryDir = mkdtempSync6(join37(dirname14(destinationPath), ".plan-v2-blob-"));
|
|
16632
|
+
const temporaryPath = join37(temporaryDir, "blob");
|
|
16392
16633
|
try {
|
|
16393
16634
|
copyFileSync6(sourcePath, temporaryPath);
|
|
16394
16635
|
renameSync6(temporaryPath, destinationPath);
|
|
@@ -16421,8 +16662,8 @@ async function putBlobs(publisher, blobs) {
|
|
|
16421
16662
|
if (failure) throw failure.reason;
|
|
16422
16663
|
}
|
|
16423
16664
|
function buildPlanV2Publication(executionPlanDir, digests) {
|
|
16424
|
-
const executionPlanPath =
|
|
16425
|
-
if (!
|
|
16665
|
+
const executionPlanPath = join37(executionPlanDir, "plan.json");
|
|
16666
|
+
if (!existsSync24(executionPlanPath)) {
|
|
16426
16667
|
throw new PlanV2IntegrityError(`execution plan is missing plan.json: ${executionPlanPath}`);
|
|
16427
16668
|
}
|
|
16428
16669
|
const executionPlanValue = readJsonFile(executionPlanPath, "execution plan.json");
|
|
@@ -16449,7 +16690,7 @@ function buildPlanV2Publication(executionPlanDir, digests) {
|
|
|
16449
16690
|
}
|
|
16450
16691
|
validateExtractionCacheCompleteSentinels(executionPlanDir);
|
|
16451
16692
|
const videoDependencyPlan = buildVideoChunkDependencies(executionPlanDir, dimensions);
|
|
16452
|
-
const encoder = readJsonFile(
|
|
16693
|
+
const encoder = readJsonFile(join37(executionPlanDir, "meta", "encoder.json"), "meta/encoder.json");
|
|
16453
16694
|
const chunksCarryAudio2 = isRecord3(encoder) && encoder.chunksCarryAudio === true;
|
|
16454
16695
|
for (const file of listFiles(executionPlanDir)) {
|
|
16455
16696
|
if (isExtractionCacheCompleteSentinelPath(file.path)) continue;
|
|
@@ -16491,18 +16732,18 @@ function buildPlanV2Publication(executionPlanDir, digests) {
|
|
|
16491
16732
|
};
|
|
16492
16733
|
}
|
|
16493
16734
|
function createPlanV2FromExecutionPlan(executionPlanDir, planV2Dir) {
|
|
16494
|
-
if (
|
|
16735
|
+
if (existsSync24(planV2Dir)) {
|
|
16495
16736
|
throw new PlanV2IntegrityError(`output directory already exists: ${planV2Dir}`);
|
|
16496
16737
|
}
|
|
16497
16738
|
const publication = buildPlanV2Publication(executionPlanDir);
|
|
16498
16739
|
mkdirSync13(dirname14(planV2Dir), { recursive: true });
|
|
16499
|
-
const tempDir = mkdtempSync6(
|
|
16740
|
+
const tempDir = mkdtempSync6(join37(dirname14(planV2Dir), ".plan-v2-build-"));
|
|
16500
16741
|
try {
|
|
16501
16742
|
for (const blob of publication.blobs) {
|
|
16502
16743
|
writeBlob(blob.sourcePath, planV2BlobPath(tempDir, blob.sha256));
|
|
16503
16744
|
}
|
|
16504
16745
|
writeFileSync10(
|
|
16505
|
-
|
|
16746
|
+
join37(tempDir, "plan.json"),
|
|
16506
16747
|
canonicalJsonStringify(publication.manifest),
|
|
16507
16748
|
"utf-8"
|
|
16508
16749
|
);
|
|
@@ -16543,7 +16784,7 @@ async function publishPlanV2FromV1(executionPlanDir, publisher) {
|
|
|
16543
16784
|
return publishPlanV2FromExecutionPlan(executionPlanDir, publisher);
|
|
16544
16785
|
}
|
|
16545
16786
|
async function planV2WithPublisher(projectDir, config, publisher, options = {}) {
|
|
16546
|
-
const stagingRoot = mkdtempSync6(
|
|
16787
|
+
const stagingRoot = mkdtempSync6(join37(options.stagingParentDir ?? tmpdir4(), ".execution-plan-"));
|
|
16547
16788
|
const digests = /* @__PURE__ */ new Map();
|
|
16548
16789
|
try {
|
|
16549
16790
|
await buildLocalExecutionPlan(projectDir, config, stagingRoot, {
|
|
@@ -16553,7 +16794,7 @@ async function planV2WithPublisher(projectDir, config, publisher, options = {})
|
|
|
16553
16794
|
// artifactTargets ships every compiled/ file to all chunks, so each is in the manifest.
|
|
16554
16795
|
whileAudioMixes: () => putBlobs(
|
|
16555
16796
|
publisher,
|
|
16556
|
-
listFiles(
|
|
16797
|
+
listFiles(join37(stagingRoot, "compiled")).map(
|
|
16557
16798
|
(file) => publishBlobFor(file.absolutePath, digests)
|
|
16558
16799
|
)
|
|
16559
16800
|
),
|
|
@@ -16571,7 +16812,7 @@ async function planV2WithPublisher(projectDir, config, publisher, options = {})
|
|
|
16571
16812
|
}
|
|
16572
16813
|
}
|
|
16573
16814
|
async function planV2(projectDir, config, planV2Dir) {
|
|
16574
|
-
if (
|
|
16815
|
+
if (existsSync24(planV2Dir)) {
|
|
16575
16816
|
throw new PlanV2IntegrityError(`output directory already exists: ${planV2Dir}`);
|
|
16576
16817
|
}
|
|
16577
16818
|
const publisher = new LocalPlanV2ArtifactPublisher(planV2Dir);
|
|
@@ -16583,7 +16824,7 @@ async function planV2(projectDir, config, planV2Dir) {
|
|
|
16583
16824
|
function resultFromManifest(planV2Dir, manifest) {
|
|
16584
16825
|
return {
|
|
16585
16826
|
planDir: planV2Dir,
|
|
16586
|
-
manifestPath:
|
|
16827
|
+
manifestPath: join37(planV2Dir, "plan.json"),
|
|
16587
16828
|
planProtocol: PLAN_PROTOCOL_V2,
|
|
16588
16829
|
planHash: manifest.planHash,
|
|
16589
16830
|
sourcePlanV1Hash: getPlanV2ExecutionPlanHash(manifest),
|
|
@@ -16719,8 +16960,8 @@ function parsePlanV2Manifest(value) {
|
|
|
16719
16960
|
return parsed;
|
|
16720
16961
|
}
|
|
16721
16962
|
function readPlanV2Manifest(planV2Dir) {
|
|
16722
|
-
const manifestPath =
|
|
16723
|
-
if (!
|
|
16963
|
+
const manifestPath = join37(planV2Dir, "plan.json");
|
|
16964
|
+
if (!existsSync24(manifestPath)) {
|
|
16724
16965
|
throw new PlanV2IntegrityError(`missing v2 manifest: ${manifestPath}`);
|
|
16725
16966
|
}
|
|
16726
16967
|
const value = readJsonFile(manifestPath, "v2 manifest");
|
|
@@ -16739,7 +16980,7 @@ function listPlanV2ArtifactsForTarget(manifest, target) {
|
|
|
16739
16980
|
}
|
|
16740
16981
|
function verifyBlob(planV2Dir, artifact) {
|
|
16741
16982
|
const sourcePath = planV2BlobPath(planV2Dir, artifact.sha256);
|
|
16742
|
-
if (!
|
|
16983
|
+
if (!existsSync24(sourcePath)) {
|
|
16743
16984
|
throw new PlanV2IntegrityError(`missing content-addressed artifact ${artifact.sha256}`);
|
|
16744
16985
|
}
|
|
16745
16986
|
const stats = statSync9(sourcePath);
|
|
@@ -16752,7 +16993,7 @@ function verifyBlob(planV2Dir, artifact) {
|
|
|
16752
16993
|
return sourcePath;
|
|
16753
16994
|
}
|
|
16754
16995
|
function materializePlanV2Target(planV2Dir, target, destinationDir) {
|
|
16755
|
-
if (
|
|
16996
|
+
if (existsSync24(destinationDir)) {
|
|
16756
16997
|
throw new PlanV2IntegrityError(`materialization destination already exists: ${destinationDir}`);
|
|
16757
16998
|
}
|
|
16758
16999
|
const manifest = readPlanV2Manifest(planV2Dir);
|
|
@@ -16763,16 +17004,16 @@ function materializePlanV2Target(planV2Dir, target, destinationDir) {
|
|
|
16763
17004
|
sourcePath: verifyBlob(planV2Dir, artifact)
|
|
16764
17005
|
}));
|
|
16765
17006
|
mkdirSync13(dirname14(destinationDir), { recursive: true });
|
|
16766
|
-
const tempDir = mkdtempSync6(
|
|
17007
|
+
const tempDir = mkdtempSync6(join37(dirname14(destinationDir), ".plan-v2-materialize-"));
|
|
16767
17008
|
try {
|
|
16768
17009
|
for (const { artifact, sourcePath } of verified) {
|
|
16769
|
-
const destinationPath =
|
|
17010
|
+
const destinationPath = join37(tempDir, ...artifact.path.split("/"));
|
|
16770
17011
|
mkdirSync13(dirname14(destinationPath), { recursive: true });
|
|
16771
17012
|
copyFileSync6(sourcePath, destinationPath);
|
|
16772
17013
|
}
|
|
16773
17014
|
if (target.role === "chunk") materializeExtractedVideoDirectories(tempDir);
|
|
16774
17015
|
writeFileSync10(
|
|
16775
|
-
|
|
17016
|
+
join37(tempDir, PLAN_V2_MATERIALIZATION_MARKER),
|
|
16776
17017
|
canonicalJsonStringify({ manifest, target }),
|
|
16777
17018
|
"utf-8"
|
|
16778
17019
|
);
|
|
@@ -16791,12 +17032,12 @@ function materializePlanV2Target(planV2Dir, target, destinationDir) {
|
|
|
16791
17032
|
// Join the artifact's OWN name, not the current constant: a plan written
|
|
16792
17033
|
// before the container change carries the legacy name, and materializing it
|
|
16793
17034
|
// under the new one would point at a file that was never written.
|
|
16794
|
-
audioPath: target.role === "assembler" ? audioArtifact ?
|
|
17035
|
+
audioPath: target.role === "assembler" ? audioArtifact ? join37(destinationDir, audioArtifact.path) : null : null
|
|
16795
17036
|
};
|
|
16796
17037
|
}
|
|
16797
17038
|
function validatePlanV2MaterializedTarget(planDir, expectedTarget) {
|
|
16798
|
-
const markerPath =
|
|
16799
|
-
if (!
|
|
17039
|
+
const markerPath = join37(planDir, PLAN_V2_MATERIALIZATION_MARKER);
|
|
17040
|
+
if (!existsSync24(markerPath)) return null;
|
|
16800
17041
|
const marker = readJsonFile(markerPath, "materialization marker");
|
|
16801
17042
|
if (!isRecord3(marker) || !isRecord3(marker.manifest) || !isRecord3(marker.target)) {
|
|
16802
17043
|
throw new PlanV2IntegrityError("malformed materialization marker");
|
|
@@ -16810,8 +17051,8 @@ function validatePlanV2MaterializedTarget(planDir, expectedTarget) {
|
|
|
16810
17051
|
const manifest = parsePlanV2Manifest(marker.manifest);
|
|
16811
17052
|
const required = listPlanV2ArtifactsForTarget(manifest, expectedTarget);
|
|
16812
17053
|
for (const artifact of required) {
|
|
16813
|
-
const path =
|
|
16814
|
-
if (!
|
|
17054
|
+
const path = join37(planDir, ...artifact.path.split("/"));
|
|
17055
|
+
if (!existsSync24(path) || statSync9(path).size !== artifact.sizeBytes) {
|
|
16815
17056
|
throw new PlanV2IntegrityError(
|
|
16816
17057
|
`materialized artifact missing or truncated: ${artifact.path}`
|
|
16817
17058
|
);
|
|
@@ -16826,22 +17067,22 @@ function validatePlanV2MaterializedTarget(planDir, expectedTarget) {
|
|
|
16826
17067
|
// ../producer/src/services/distributed/planV2Execution.ts
|
|
16827
17068
|
import { mkdtempSync as mkdtempSync9, rmSync as rmSync16 } from "fs";
|
|
16828
17069
|
import { tmpdir as tmpdir5 } from "os";
|
|
16829
|
-
import { join as
|
|
17070
|
+
import { join as join41 } from "path";
|
|
16830
17071
|
|
|
16831
17072
|
// ../producer/src/services/distributed/assemble.ts
|
|
16832
17073
|
import {
|
|
16833
17074
|
cpSync as cpSync3,
|
|
16834
|
-
existsSync as
|
|
17075
|
+
existsSync as existsSync25,
|
|
16835
17076
|
mkdirSync as mkdirSync15,
|
|
16836
17077
|
mkdtempSync as mkdtempSync8,
|
|
16837
17078
|
readFileSync as readFileSync12,
|
|
16838
|
-
readdirSync as
|
|
17079
|
+
readdirSync as readdirSync10,
|
|
16839
17080
|
renameSync as renameSync8,
|
|
16840
17081
|
rmSync as rmSync14,
|
|
16841
17082
|
statSync as statSync10,
|
|
16842
17083
|
writeFileSync as writeFileSync11
|
|
16843
17084
|
} from "fs";
|
|
16844
|
-
import { dirname as dirname16, join as
|
|
17085
|
+
import { dirname as dirname16, join as join39 } from "path";
|
|
16845
17086
|
|
|
16846
17087
|
// ../producer/src/services/distributed/mp4Stitch.ts
|
|
16847
17088
|
import {
|
|
@@ -16853,7 +17094,7 @@ import {
|
|
|
16853
17094
|
rmSync as rmSync13
|
|
16854
17095
|
} from "fs";
|
|
16855
17096
|
import { open } from "fs/promises";
|
|
16856
|
-
import { dirname as dirname15, join as
|
|
17097
|
+
import { dirname as dirname15, join as join38 } from "path";
|
|
16857
17098
|
import { finished, pipeline } from "stream/promises";
|
|
16858
17099
|
function boxAt(src, off, at, limit) {
|
|
16859
17100
|
let size = src.readUInt32BE(off);
|
|
@@ -17175,13 +17416,13 @@ async function writeStitchedMp4(chunkPaths, outputPath) {
|
|
|
17175
17416
|
const header = buildStitchedHeader(layouts);
|
|
17176
17417
|
mkdirSync14(dirname15(outputPath), { recursive: true });
|
|
17177
17418
|
const workDir = mkdtempSync7(`${outputPath}.stitch-`);
|
|
17178
|
-
const out = createWriteStream(
|
|
17419
|
+
const out = createWriteStream(join38(workDir, "out.mp4"));
|
|
17179
17420
|
try {
|
|
17180
17421
|
out.write(header);
|
|
17181
17422
|
for (const [i, path] of chunkPaths.entries()) await copyPayload(path, layouts[i], i, out);
|
|
17182
17423
|
out.end();
|
|
17183
17424
|
await finished(out);
|
|
17184
|
-
renameSync7(
|
|
17425
|
+
renameSync7(join38(workDir, "out.mp4"), outputPath);
|
|
17185
17426
|
} finally {
|
|
17186
17427
|
out.destroy();
|
|
17187
17428
|
rmSync13(workDir, { recursive: true, force: true });
|
|
@@ -17215,7 +17456,7 @@ async function assemble(planDir, chunkPaths, audioPath, outputPath, options) {
|
|
|
17215
17456
|
}
|
|
17216
17457
|
const format = plan2.dimensions.format;
|
|
17217
17458
|
const cfrEncoder = cfr ? readCfrEncoder(planDir, format) : null;
|
|
17218
|
-
if (!
|
|
17459
|
+
if (!existsSync25(dirname16(outputPath))) {
|
|
17219
17460
|
mkdirSync15(dirname16(outputPath), { recursive: true });
|
|
17220
17461
|
}
|
|
17221
17462
|
const workDir = mkdtempSync8(`${outputPath}.assemble-work-`);
|
|
@@ -17238,8 +17479,8 @@ async function assemble(planDir, chunkPaths, audioPath, outputPath, options) {
|
|
|
17238
17479
|
};
|
|
17239
17480
|
try {
|
|
17240
17481
|
const videoSources = cfrEncoder ? [await reencodeCfr(ctx, chunkPaths, cfrEncoder)] : chunkPaths;
|
|
17241
|
-
const finalPath =
|
|
17242
|
-
const audio = audioPath !== null &&
|
|
17482
|
+
const finalPath = join39(workDir, `output.${format}`);
|
|
17483
|
+
const audio = audioPath !== null && existsSync25(audioPath) ? audioPath : null;
|
|
17243
17484
|
await writeJoined(ctx, videoSources, audio, finalPath);
|
|
17244
17485
|
renameSync8(finalPath, outputPath);
|
|
17245
17486
|
log.info("[assemble] stage timings", {
|
|
@@ -17257,7 +17498,7 @@ async function assemble(planDir, chunkPaths, audioPath, outputPath, options) {
|
|
|
17257
17498
|
});
|
|
17258
17499
|
}
|
|
17259
17500
|
}
|
|
17260
|
-
const fileSize =
|
|
17501
|
+
const fileSize = existsSync25(outputPath) ? statSync10(outputPath).size : 0;
|
|
17261
17502
|
return {
|
|
17262
17503
|
outputPath,
|
|
17263
17504
|
durationMs: Date.now() - start,
|
|
@@ -17266,15 +17507,15 @@ async function assemble(planDir, chunkPaths, audioPath, outputPath, options) {
|
|
|
17266
17507
|
};
|
|
17267
17508
|
}
|
|
17268
17509
|
function readAssemblePlan(planDir, chunkPaths) {
|
|
17269
|
-
const planJsonPath =
|
|
17270
|
-
const chunksJsonPath =
|
|
17271
|
-
if (!
|
|
17510
|
+
const planJsonPath = join39(planDir, "plan.json");
|
|
17511
|
+
const chunksJsonPath = join39(planDir, "meta", "chunks.json");
|
|
17512
|
+
if (!existsSync25(planJsonPath)) {
|
|
17272
17513
|
throw new Error(`[assemble] planDir missing plan.json: ${planJsonPath}`);
|
|
17273
17514
|
}
|
|
17274
17515
|
const plan2 = JSON.parse(readFileSync12(planJsonPath, "utf-8"));
|
|
17275
17516
|
readPlanProtocolV1(plan2, DISTRIBUTED_RENDER_CAPABILITIES.roles.assembler);
|
|
17276
17517
|
validatePlanV2MaterializedTarget(planDir, { role: "assembler" });
|
|
17277
|
-
if (!
|
|
17518
|
+
if (!existsSync25(chunksJsonPath)) {
|
|
17278
17519
|
throw new Error(`[assemble] planDir missing meta/chunks.json: ${chunksJsonPath}`);
|
|
17279
17520
|
}
|
|
17280
17521
|
const chunks = JSON.parse(readFileSync12(chunksJsonPath, "utf-8"));
|
|
@@ -17284,7 +17525,7 @@ function readAssemblePlan(planDir, chunkPaths) {
|
|
|
17284
17525
|
);
|
|
17285
17526
|
}
|
|
17286
17527
|
for (const path of chunkPaths) {
|
|
17287
|
-
if (!
|
|
17528
|
+
if (!existsSync25(path)) {
|
|
17288
17529
|
throw new Error(`[assemble] chunk path does not exist: ${path}`);
|
|
17289
17530
|
}
|
|
17290
17531
|
}
|
|
@@ -17294,7 +17535,7 @@ function videoInputArgs(ctx, videoSources) {
|
|
|
17294
17535
|
return videoSources.length === 1 ? ["-i", videoSources[0]] : concatDemuxerInput(videoSources, ctx.workDir, ctx.fpsArg);
|
|
17295
17536
|
}
|
|
17296
17537
|
async function reencodeCfr(ctx, chunkPaths, encoder) {
|
|
17297
|
-
const cfrOutputPath =
|
|
17538
|
+
const cfrOutputPath = join39(ctx.workDir, `cfr.${ctx.format}`);
|
|
17298
17539
|
const cfrArgs = [
|
|
17299
17540
|
...videoInputArgs(ctx, chunkPaths),
|
|
17300
17541
|
"-c:v",
|
|
@@ -17332,7 +17573,7 @@ async function writeJoined(ctx, videoSources, audioPath, finalPath) {
|
|
|
17332
17573
|
);
|
|
17333
17574
|
return;
|
|
17334
17575
|
}
|
|
17335
|
-
const paddedAudioPath =
|
|
17576
|
+
const paddedAudioPath = join39(ctx.workDir, "audio-padded.m4a");
|
|
17336
17577
|
const padTrimResult = await ctx.timed(
|
|
17337
17578
|
"audioMs",
|
|
17338
17579
|
() => padOrTrimAudioToVideoFrameCount({
|
|
@@ -17372,7 +17613,7 @@ async function writeJoined(ctx, videoSources, audioPath, finalPath) {
|
|
|
17372
17613
|
}
|
|
17373
17614
|
}
|
|
17374
17615
|
function concatDemuxerInput(chunkPaths, workDir, fpsArg) {
|
|
17375
|
-
const listPath =
|
|
17616
|
+
const listPath = join39(workDir, "concat-list.txt");
|
|
17376
17617
|
const body = chunkPaths.map((path) => `file '${path.replace(/'/g, "'\\''")}'`).join("\n");
|
|
17377
17618
|
writeFileSync11(listPath, `${body}
|
|
17378
17619
|
`, "utf-8");
|
|
@@ -17405,8 +17646,8 @@ function chunksAreStitchedMp4(planDir, cfr) {
|
|
|
17405
17646
|
return true;
|
|
17406
17647
|
}
|
|
17407
17648
|
function chunksCarryAudio(planDir) {
|
|
17408
|
-
const encoderJsonPath =
|
|
17409
|
-
if (!
|
|
17649
|
+
const encoderJsonPath = join39(planDir, "meta", "encoder.json");
|
|
17650
|
+
if (!existsSync25(encoderJsonPath)) return false;
|
|
17410
17651
|
return JSON.parse(readFileSync12(encoderJsonPath, "utf-8")).chunksCarryAudio === true;
|
|
17411
17652
|
}
|
|
17412
17653
|
function readCfrEncoder(planDir, format) {
|
|
@@ -17415,8 +17656,8 @@ function readCfrEncoder(planDir, format) {
|
|
|
17415
17656
|
`[assemble] cfr=true is only supported for format="mp4" (got "${format}"). Stream-copy paths for webm and mov already produce exact avg_frame_rate; cfr re-encode is not needed.`
|
|
17416
17657
|
);
|
|
17417
17658
|
}
|
|
17418
|
-
const encoderJsonPath =
|
|
17419
|
-
if (!
|
|
17659
|
+
const encoderJsonPath = join39(planDir, "meta", "encoder.json");
|
|
17660
|
+
if (!existsSync25(encoderJsonPath)) {
|
|
17420
17661
|
throw new Error(`[assemble] planDir missing meta/encoder.json: ${encoderJsonPath}`);
|
|
17421
17662
|
}
|
|
17422
17663
|
const encoderJson = JSON.parse(readFileSync12(encoderJsonPath, "utf-8"));
|
|
@@ -17428,7 +17669,7 @@ function readCfrEncoder(planDir, format) {
|
|
|
17428
17669
|
return encoderJson;
|
|
17429
17670
|
}
|
|
17430
17671
|
function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, startTimeMs) {
|
|
17431
|
-
if (
|
|
17672
|
+
if (existsSync25(outputPath)) rmSync14(outputPath, { recursive: true, force: true });
|
|
17432
17673
|
mkdirSync15(outputPath, { recursive: true });
|
|
17433
17674
|
let globalIdx = 0;
|
|
17434
17675
|
for (const chunkDir of chunkPaths) {
|
|
@@ -17437,13 +17678,13 @@ function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, start
|
|
|
17437
17678
|
`[assemble] png-sequence chunk must be a directory: ${chunkDir} (got a file)`
|
|
17438
17679
|
);
|
|
17439
17680
|
}
|
|
17440
|
-
const frames =
|
|
17681
|
+
const frames = readdirSync10(chunkDir).filter((name) => name.endsWith(".png")).sort();
|
|
17441
17682
|
if (frames.length === 0) {
|
|
17442
17683
|
throw new Error(`[assemble] png-sequence chunk has no frames: ${chunkDir}`);
|
|
17443
17684
|
}
|
|
17444
17685
|
for (const frame of frames) {
|
|
17445
|
-
const dst =
|
|
17446
|
-
cpSync3(
|
|
17686
|
+
const dst = join39(outputPath, formatExportFrameName(globalIdx, "png"));
|
|
17687
|
+
cpSync3(join39(chunkDir, frame), dst);
|
|
17447
17688
|
globalIdx += 1;
|
|
17448
17689
|
}
|
|
17449
17690
|
}
|
|
@@ -17452,14 +17693,14 @@ function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, start
|
|
|
17452
17693
|
`[assemble] png-sequence frame count mismatch: merged ${globalIdx} frames vs plan.totalFrames=${totalFrames}. Using on-disk count.`
|
|
17453
17694
|
);
|
|
17454
17695
|
}
|
|
17455
|
-
if (audioPath !== null &&
|
|
17456
|
-
const sidecar =
|
|
17696
|
+
if (audioPath !== null && existsSync25(audioPath)) {
|
|
17697
|
+
const sidecar = join39(outputPath, MIXED_AUDIO_FILENAME);
|
|
17457
17698
|
cpSync3(audioPath, sidecar);
|
|
17458
17699
|
}
|
|
17459
17700
|
let fileSize = 0;
|
|
17460
|
-
for (const name of
|
|
17701
|
+
for (const name of readdirSync10(outputPath)) {
|
|
17461
17702
|
try {
|
|
17462
|
-
fileSize += statSync10(
|
|
17703
|
+
fileSize += statSync10(join39(outputPath, name)).size;
|
|
17463
17704
|
} catch {
|
|
17464
17705
|
}
|
|
17465
17706
|
}
|
|
@@ -17473,8 +17714,8 @@ function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, start
|
|
|
17473
17714
|
|
|
17474
17715
|
// ../producer/src/services/distributed/renderChunk.ts
|
|
17475
17716
|
import { randomBytes } from "crypto";
|
|
17476
|
-
import { existsSync as
|
|
17477
|
-
import {
|
|
17717
|
+
import { existsSync as existsSync26, mkdirSync as mkdirSync16, readFileSync as readFileSync13, readdirSync as readdirSync11, rmSync as rmSync15, writeFileSync as writeFileSync12 } from "fs";
|
|
17718
|
+
import { join as join40 } from "path";
|
|
17478
17719
|
|
|
17479
17720
|
// ../producer/src/services/distributed/chunkAudio.ts
|
|
17480
17721
|
function audioCutForChunk(grid, chunks, index, fps) {
|
|
@@ -17572,20 +17813,7 @@ async function muxChunkWithAudio(input) {
|
|
|
17572
17813
|
}
|
|
17573
17814
|
|
|
17574
17815
|
// ../producer/src/services/distributed/renderChunk.ts
|
|
17575
|
-
var FFMPEG_VERSION_MISMATCH = "FFMPEG_VERSION_MISMATCH";
|
|
17576
|
-
var PLAN_HASH_MISMATCH = "PLAN_HASH_MISMATCH";
|
|
17577
|
-
var MISSING_PLAN_ARTIFACT = "MISSING_PLAN_ARTIFACT";
|
|
17578
|
-
var CHUNK_INDEX_OUT_OF_RANGE = "CHUNK_INDEX_OUT_OF_RANGE";
|
|
17579
|
-
var MISSING_RUNTIME_ENV_SNAPSHOT = "MISSING_RUNTIME_ENV_SNAPSHOT";
|
|
17580
17816
|
var LEGACY_DISTRIBUTED_VP9_CPU_USED = 2;
|
|
17581
|
-
var RenderChunkValidationError = class extends Error {
|
|
17582
|
-
code;
|
|
17583
|
-
constructor(code, message) {
|
|
17584
|
-
super(message);
|
|
17585
|
-
this.name = "RenderChunkValidationError";
|
|
17586
|
-
this.code = code;
|
|
17587
|
-
}
|
|
17588
|
-
};
|
|
17589
17817
|
function validatePlanVideosForChunk(value) {
|
|
17590
17818
|
try {
|
|
17591
17819
|
return parsePlanVideosJson(value);
|
|
@@ -17621,9 +17849,6 @@ async function createVerifiedDistributedCaptureSession(serverUrl, framesDir, cap
|
|
|
17621
17849
|
throw error;
|
|
17622
17850
|
}
|
|
17623
17851
|
}
|
|
17624
|
-
function createChunkVideoFrameInjectorFactory(frameLookup) {
|
|
17625
|
-
return () => createVideoFrameInjector(frameLookup);
|
|
17626
|
-
}
|
|
17627
17852
|
function isCaptureMode(value) {
|
|
17628
17853
|
return value === "beginframe" || value === "screenshot" || value === "drawelement";
|
|
17629
17854
|
}
|
|
@@ -17651,130 +17876,11 @@ async function beginFrameSessionNeedsScreenshotFallback(session, probe = probeBe
|
|
|
17651
17876
|
);
|
|
17652
17877
|
return !await probe(session.page, timeoutMs, probeTick, session.beginFrameIntervalMs);
|
|
17653
17878
|
}
|
|
17654
|
-
function frameNumberFromFileName(name) {
|
|
17655
|
-
const match = /(\d+)(?=\.[^.]+$)/.exec(name);
|
|
17656
|
-
if (!match) return null;
|
|
17657
|
-
const frameNumber = Number(match[1]);
|
|
17658
|
-
return Number.isSafeInteger(frameNumber) ? frameNumber : null;
|
|
17659
|
-
}
|
|
17660
|
-
function rebuildExtractedFramesFromPlanDir(planDir, videos, indexMode = "dense-v1") {
|
|
17661
|
-
const result = [];
|
|
17662
|
-
for (const v of videos) {
|
|
17663
|
-
const outputDir = join38(planDir, "video-frames", v.videoId);
|
|
17664
|
-
if (!existsSync25(outputDir)) {
|
|
17665
|
-
throw new Error(
|
|
17666
|
-
`[renderChunk] planDir missing extracted video frames for ${JSON.stringify(v.videoId)}: ${outputDir} not present. plan() should have written frames here; the planDir is malformed.`
|
|
17667
|
-
);
|
|
17668
|
-
}
|
|
17669
|
-
const ext = (extname6(v.framePattern) || ".jpg").toLowerCase();
|
|
17670
|
-
const frames = readdirSync10(outputDir).filter((name) => name.toLowerCase().endsWith(ext)).sort((left, right) => {
|
|
17671
|
-
const leftNumber = frameNumberFromFileName(left);
|
|
17672
|
-
const rightNumber = frameNumberFromFileName(right);
|
|
17673
|
-
if (leftNumber === null || rightNumber === null) return left.localeCompare(right);
|
|
17674
|
-
return leftNumber - rightNumber || left.localeCompare(right);
|
|
17675
|
-
});
|
|
17676
|
-
const framePaths = /* @__PURE__ */ new Map();
|
|
17677
|
-
for (let i = 0; i < frames.length; i++) {
|
|
17678
|
-
const frameName = frames[i];
|
|
17679
|
-
if (!frameName) continue;
|
|
17680
|
-
const frameNumber = indexMode === "sparse-v2" ? frameNumberFromFileName(frameName) : null;
|
|
17681
|
-
const frameIndex = frameNumber === null ? i : frameNumber - 1;
|
|
17682
|
-
framePaths.set(frameIndex, join38(outputDir, frameName));
|
|
17683
|
-
}
|
|
17684
|
-
result.push({
|
|
17685
|
-
videoId: v.videoId,
|
|
17686
|
-
srcPath: v.srcPath,
|
|
17687
|
-
outputDir,
|
|
17688
|
-
framePattern: v.framePattern,
|
|
17689
|
-
fps: v.fps,
|
|
17690
|
-
totalFrames: v.totalFrames,
|
|
17691
|
-
metadata: v.metadata,
|
|
17692
|
-
framePaths,
|
|
17693
|
-
// The chunk worker doesn't own the planDir's video-frames/ directory
|
|
17694
|
-
// (the controller does — adapters that fan out chunks across machines
|
|
17695
|
-
// share the planDir as read-only). Mark ownership as false so the
|
|
17696
|
-
// injector's eventual cleanup doesn't rm bytes another worker may
|
|
17697
|
-
// still be reading.
|
|
17698
|
-
ownedByLookup: false
|
|
17699
|
-
});
|
|
17700
|
-
}
|
|
17701
|
-
return result;
|
|
17702
|
-
}
|
|
17703
|
-
function contiguousRuns(sortedIndexes) {
|
|
17704
|
-
const runs2 = [];
|
|
17705
|
-
for (const index of sortedIndexes) {
|
|
17706
|
-
const last = runs2[runs2.length - 1];
|
|
17707
|
-
if (last && last.firstFrame + last.frames === index) last.frames++;
|
|
17708
|
-
else runs2.push({ firstFrame: index, frames: 1 });
|
|
17709
|
-
}
|
|
17710
|
-
return runs2;
|
|
17711
|
-
}
|
|
17712
|
-
async function extractDeferredVideoFramesForChunk(input) {
|
|
17713
|
-
const deferred = input.planVideos.extracted.filter((video) => video.deferredRange);
|
|
17714
|
-
if (deferred.length === 0) return [];
|
|
17715
|
-
const table = createFrameLookupTable(
|
|
17716
|
-
input.planVideos.videos,
|
|
17717
|
-
deferred.map((video) => ({
|
|
17718
|
-
...video,
|
|
17719
|
-
outputDir: input.outputRoot,
|
|
17720
|
-
framePaths: new Map(Array.from({ length: video.totalFrames }, (_, i) => [i, String(i)]))
|
|
17721
|
-
})),
|
|
17722
|
-
resolveRenderFpsConfig(input.fps).value
|
|
17723
|
-
);
|
|
17724
|
-
const shown = /* @__PURE__ */ new Map();
|
|
17725
|
-
for (let frame = input.slice.startFrame; frame < input.slice.endFrame; frame++) {
|
|
17726
|
-
const globalTime = frame * input.fps.den / input.fps.num;
|
|
17727
|
-
for (const [videoId, payload] of table.getActiveFramePayloads(globalTime)) {
|
|
17728
|
-
const indexes = shown.get(videoId) ?? /* @__PURE__ */ new Set();
|
|
17729
|
-
indexes.add(payload.frameIndex);
|
|
17730
|
-
shown.set(videoId, indexes);
|
|
17731
|
-
}
|
|
17732
|
-
}
|
|
17733
|
-
return Promise.all(
|
|
17734
|
-
deferred.map(async (video) => {
|
|
17735
|
-
const outputDir = join38(input.outputRoot, video.videoId);
|
|
17736
|
-
const indexes = [...shown.get(video.videoId) ?? []].sort((a, b) => a - b);
|
|
17737
|
-
if (indexes.length === 0) {
|
|
17738
|
-
return { ...video, outputDir, framePaths: /* @__PURE__ */ new Map(), ownedByLookup: false };
|
|
17739
|
-
}
|
|
17740
|
-
const range = video.deferredRange;
|
|
17741
|
-
const sourcePath = join38(input.planDir, ...range.sourcePath.split("/"));
|
|
17742
|
-
if (!existsSync25(sourcePath)) {
|
|
17743
|
-
throw new RenderChunkValidationError(
|
|
17744
|
-
MISSING_PLAN_ARTIFACT,
|
|
17745
|
-
`[renderChunk] planDir is missing the source for video ${JSON.stringify(video.videoId)}: ${range.sourcePath}`
|
|
17746
|
-
);
|
|
17747
|
-
}
|
|
17748
|
-
const extracted = await extractVideoFramesRange(
|
|
17749
|
-
sourcePath,
|
|
17750
|
-
video.videoId,
|
|
17751
|
-
range.startTime,
|
|
17752
|
-
range.durationSeconds,
|
|
17753
|
-
{
|
|
17754
|
-
fps: input.fps,
|
|
17755
|
-
outputDir: input.outputRoot,
|
|
17756
|
-
format: range.format,
|
|
17757
|
-
frameRanges: contiguousRuns(indexes)
|
|
17758
|
-
},
|
|
17759
|
-
void 0,
|
|
17760
|
-
input.cfg
|
|
17761
|
-
);
|
|
17762
|
-
const missing = indexes.find((index) => !extracted.framePaths.has(index));
|
|
17763
|
-
if (missing !== void 0) {
|
|
17764
|
-
throw new RenderChunkValidationError(
|
|
17765
|
-
INVALID_VIDEO_METADATA,
|
|
17766
|
-
`[renderChunk] video ${JSON.stringify(video.videoId)} has no frame ${missing}; the plan expected ${video.totalFrames} frames from ${range.sourcePath}.`
|
|
17767
|
-
);
|
|
17768
|
-
}
|
|
17769
|
-
return { ...video, outputDir, framePaths: extracted.framePaths, ownedByLookup: false };
|
|
17770
|
-
})
|
|
17771
|
-
);
|
|
17772
|
-
}
|
|
17773
17879
|
function hashChunkOutput(outputPath, kind) {
|
|
17774
17880
|
if (kind === "file") return sha256Hex(readFileSync13(outputPath));
|
|
17775
|
-
const entries =
|
|
17881
|
+
const entries = readdirSync11(outputPath).filter((name) => /\.(png|jpg|jpeg)$/i.test(name)).sort();
|
|
17776
17882
|
const lines = entries.map(
|
|
17777
|
-
(name) => `${name}\0${sha256Hex(readFileSync13(
|
|
17883
|
+
(name) => `${name}\0${sha256Hex(readFileSync13(join40(outputPath, name)))}`
|
|
17778
17884
|
);
|
|
17779
17885
|
return sha256Hex(lines.join("\0"));
|
|
17780
17886
|
}
|
|
@@ -17791,10 +17897,10 @@ function resolveLockedVp9CpuUsed(lockedEncoder) {
|
|
|
17791
17897
|
async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
17792
17898
|
const start = Date.now();
|
|
17793
17899
|
const log = defaultLogger;
|
|
17794
|
-
const planJsonPath =
|
|
17795
|
-
const encoderJsonPath =
|
|
17796
|
-
const chunksJsonPath =
|
|
17797
|
-
if (!
|
|
17900
|
+
const planJsonPath = join40(planDir, "plan.json");
|
|
17901
|
+
const encoderJsonPath = join40(planDir, "meta", "encoder.json");
|
|
17902
|
+
const chunksJsonPath = join40(planDir, "meta", "chunks.json");
|
|
17903
|
+
if (!existsSync26(planJsonPath)) {
|
|
17798
17904
|
throw new RenderChunkValidationError(
|
|
17799
17905
|
MISSING_PLAN_ARTIFACT,
|
|
17800
17906
|
`[renderChunk] planDir is missing required artifact: ${planJsonPath}`
|
|
@@ -17808,7 +17914,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
17808
17914
|
chunkIndex
|
|
17809
17915
|
});
|
|
17810
17916
|
for (const required of [encoderJsonPath, chunksJsonPath]) {
|
|
17811
|
-
if (!
|
|
17917
|
+
if (!existsSync26(required)) {
|
|
17812
17918
|
throw new RenderChunkValidationError(
|
|
17813
17919
|
MISSING_PLAN_ARTIFACT,
|
|
17814
17920
|
`[renderChunk] planDir is missing required artifact: ${required}`
|
|
@@ -17817,9 +17923,9 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
17817
17923
|
}
|
|
17818
17924
|
const encoder = JSON.parse(readFileSync13(encoderJsonPath, "utf-8"));
|
|
17819
17925
|
const chunks = JSON.parse(readFileSync13(chunksJsonPath, "utf-8"));
|
|
17820
|
-
const videosJsonPath =
|
|
17926
|
+
const videosJsonPath = join40(planDir, PLAN_VIDEOS_META_RELATIVE_PATH);
|
|
17821
17927
|
let planVideos = null;
|
|
17822
|
-
if (
|
|
17928
|
+
if (existsSync26(videosJsonPath)) {
|
|
17823
17929
|
try {
|
|
17824
17930
|
planVideos = validatePlanVideosForChunk(JSON.parse(readFileSync13(videosJsonPath, "utf-8")));
|
|
17825
17931
|
} catch (err) {
|
|
@@ -17847,8 +17953,8 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
17847
17953
|
`[renderChunk] chunk ${chunkIndex} has non-positive frame count: ${framesInChunk}`
|
|
17848
17954
|
);
|
|
17849
17955
|
}
|
|
17850
|
-
const compiledDir =
|
|
17851
|
-
if (!
|
|
17956
|
+
const compiledDir = join40(planDir, "compiled");
|
|
17957
|
+
if (!existsSync26(compiledDir)) {
|
|
17852
17958
|
throw new RenderChunkValidationError(
|
|
17853
17959
|
MISSING_PLAN_ARTIFACT,
|
|
17854
17960
|
`[renderChunk] planDir missing compiled/ directory: ${compiledDir}`
|
|
@@ -17905,7 +18011,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
17905
18011
|
};
|
|
17906
18012
|
const workDir = `${outputChunkPath}.work.${process.pid}.${randomBytes(4).toString("hex")}`;
|
|
17907
18013
|
mkdirSync16(workDir, { recursive: true });
|
|
17908
|
-
const framesDir =
|
|
18014
|
+
const framesDir = join40(workDir, "captured-frames");
|
|
17909
18015
|
mkdirSync16(framesDir, { recursive: true });
|
|
17910
18016
|
const videoFramesDir = `${workDir}.video-frames`;
|
|
17911
18017
|
const videoFrameLookup = planVideos && planVideos.extracted.length > 0 ? createFrameLookupTable(
|
|
@@ -17919,7 +18025,10 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
17919
18025
|
...await extractDeferredVideoFramesForChunk({
|
|
17920
18026
|
planDir,
|
|
17921
18027
|
planVideos,
|
|
17922
|
-
|
|
18028
|
+
frames: Array.from(
|
|
18029
|
+
{ length: slice.endFrame - slice.startFrame },
|
|
18030
|
+
(_, i) => slice.startFrame + i
|
|
18031
|
+
),
|
|
17923
18032
|
fps: { num: plan2.dimensions.fpsNum, den: plan2.dimensions.fpsDen },
|
|
17924
18033
|
outputRoot: videoFramesDir,
|
|
17925
18034
|
cfg
|
|
@@ -17928,9 +18037,6 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
17928
18037
|
resolveRenderFpsConfig(job.config.fps).value
|
|
17929
18038
|
) : null;
|
|
17930
18039
|
const createChunkVideoFrameInjector = createChunkVideoFrameInjectorFactory(videoFrameLookup);
|
|
17931
|
-
const videoCaptureBeyondViewport = resolveVideoCaptureBeyondViewport(
|
|
17932
|
-
planVideos?.videos.length ?? 0
|
|
17933
|
-
);
|
|
17934
18040
|
const fileServer = await createFileServer({
|
|
17935
18041
|
projectDir: compiledDir,
|
|
17936
18042
|
compiledDir,
|
|
@@ -17940,28 +18046,12 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
17940
18046
|
// chunk runtime seek quantization stays on the same fps grid as capture.
|
|
17941
18047
|
fps: { num: plan2.dimensions.fpsNum, den: plan2.dimensions.fpsDen }
|
|
17942
18048
|
});
|
|
17943
|
-
const captureOptions = {
|
|
17944
|
-
|
|
17945
|
-
|
|
17946
|
-
|
|
17947
|
-
|
|
17948
|
-
|
|
17949
|
-
deviceScaleFactor: encoder.deviceScaleFactor,
|
|
17950
|
-
// Re-inject the controller's snapshotted variables so the chunk's
|
|
17951
|
-
// first capture sees the same `window.__hfVariables` the in-process
|
|
17952
|
-
// renderer would have seen. Optional — compositions that don't
|
|
17953
|
-
// declare `data-composition-variables` leave this undefined and the
|
|
17954
|
-
// engine skips the `evaluateOnNewDocument` injection.
|
|
17955
|
-
variables: encoder.variables,
|
|
17956
|
-
subTimelineWaitMemo: {},
|
|
17957
|
-
...videoCaptureBeyondViewport !== void 0 ? { captureBeyondViewport: videoCaptureBeyondViewport } : {},
|
|
17958
|
-
// lock the BeginFrame warmup loop to a fixed iteration count so
|
|
17959
|
-
// `beginFrameTimeTicks` is host-independent. Only chunks ever set this.
|
|
17960
|
-
lockWarmupTicks: true,
|
|
17961
|
-
requiresWebGpu: compositionRequiresWebGpu(
|
|
17962
|
-
readFileSync13(join38(compiledDir, "index.html"), "utf-8")
|
|
17963
|
-
)
|
|
17964
|
-
};
|
|
18049
|
+
const captureOptions = buildChunkCaptureOptions({
|
|
18050
|
+
dimensions: plan2.dimensions,
|
|
18051
|
+
encoder,
|
|
18052
|
+
videoCount: planVideos?.videos.length ?? 0,
|
|
18053
|
+
html: readFileSync13(join40(compiledDir, "index.html"), "utf-8")
|
|
18054
|
+
});
|
|
17965
18055
|
const chunkWorkerCount = calculateOptimalWorkers(framesInChunk, void 0, cfg);
|
|
17966
18056
|
let session = null;
|
|
17967
18057
|
let outputKind;
|
|
@@ -18122,10 +18212,10 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
18122
18212
|
const effectiveBitrate = encoder.crf != null ? void 0 : encoder.bitrate;
|
|
18123
18213
|
const videoOnlyPath = encoder.chunksCarryAudio ? `${outputChunkPath}.video.mp4` : outputChunkPath;
|
|
18124
18214
|
if (isPngSequence) {
|
|
18125
|
-
if (!
|
|
18215
|
+
if (!existsSync26(outputChunkPath)) mkdirSync16(outputChunkPath, { recursive: true });
|
|
18126
18216
|
} else {
|
|
18127
|
-
const outDir =
|
|
18128
|
-
if (!
|
|
18217
|
+
const outDir = join40(outputChunkPath, "..");
|
|
18218
|
+
if (!existsSync26(outDir)) mkdirSync16(outDir, { recursive: true });
|
|
18129
18219
|
}
|
|
18130
18220
|
const encodeStarted = Date.now();
|
|
18131
18221
|
await runEncodeStage({
|
|
@@ -18248,8 +18338,8 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
18248
18338
|
|
|
18249
18339
|
// ../producer/src/services/distributed/planV2Execution.ts
|
|
18250
18340
|
async function renderChunkV2(planV2Dir, chunkIndex, outputChunkPath) {
|
|
18251
|
-
const workRoot = mkdtempSync9(
|
|
18252
|
-
const materializedPlanDir =
|
|
18341
|
+
const workRoot = mkdtempSync9(join41(tmpdir5(), "hf-plan-v2-chunk-"));
|
|
18342
|
+
const materializedPlanDir = join41(workRoot, "plan");
|
|
18253
18343
|
try {
|
|
18254
18344
|
materializePlanV2Target(planV2Dir, { role: "chunk", chunkIndex }, materializedPlanDir);
|
|
18255
18345
|
return await renderChunk(materializedPlanDir, chunkIndex, outputChunkPath);
|
|
@@ -18258,8 +18348,8 @@ async function renderChunkV2(planV2Dir, chunkIndex, outputChunkPath) {
|
|
|
18258
18348
|
}
|
|
18259
18349
|
}
|
|
18260
18350
|
async function assembleV2(planV2Dir, chunkPaths, outputPath, options) {
|
|
18261
|
-
const workRoot = mkdtempSync9(
|
|
18262
|
-
const materializedPlanDir =
|
|
18351
|
+
const workRoot = mkdtempSync9(join41(tmpdir5(), "hf-plan-v2-assembler-"));
|
|
18352
|
+
const materializedPlanDir = join41(workRoot, "plan");
|
|
18263
18353
|
try {
|
|
18264
18354
|
const materialized = materializePlanV2Target(
|
|
18265
18355
|
planV2Dir,
|
|
@@ -18279,9 +18369,9 @@ async function assembleV2(planV2Dir, chunkPaths, outputPath, options) {
|
|
|
18279
18369
|
}
|
|
18280
18370
|
|
|
18281
18371
|
// ../producer/src/services/distributed/projectHash.ts
|
|
18282
|
-
import { readdirSync as
|
|
18372
|
+
import { readdirSync as readdirSync12, readFileSync as readFileSync14 } from "fs";
|
|
18283
18373
|
import { createHash as createHash7 } from "crypto";
|
|
18284
|
-
import { join as
|
|
18374
|
+
import { join as join42, relative as relative8 } from "path";
|
|
18285
18375
|
|
|
18286
18376
|
export {
|
|
18287
18377
|
createConsoleLogger,
|