vidspotai-shared 1.0.93 → 1.0.94
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"google.service.d.ts","sourceRoot":"","sources":["../../../../../src/services/aiGen/providers/google/google.service.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,EACrB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"google.service.d.ts","sourceRoot":"","sources":["../../../../../src/services/aiGen/providers/google/google.service.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,EACrB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,UAAU,CAAC;AAkElB,qBAAa,aAAc,SAAQ,wBAAwB;IAKzD,OAAO,CAAC,EAAE,CAAc;IACxB,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAK;;IAQ/C;;;;;;;;;OASG;IACG,YAAY,CAChB,MAAM,EAAE,oBAAoB,GAC3B,OAAO,CAAC,oBAAoB,CAAC;IAoChC;;;;;OAKG;IACH,OAAO,CAAC,eAAe;IAoCvB;;;;OAIG;YACW,kBAAkB;IAsC1B,aAAa,CACjB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;IA0O3B,gBAAgB,CAAC,EACrB,IAAI,EACJ,cAAc,EACd,cAAyB,GAC1B,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IA8J3C,aAAa,CACjB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;YAiBnB,cAAc;IAwG5B;;;;;;OAMG;IACH;;;;OAIG;IACG,aAAa,CACjB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;IAIjC,aAAa,CAAC,EAAE,QAAQ,EAAE,QAAY,EAAE,UAAmB,EAAE,SAAiB,EAAE,SAAa,EAAE,SAAS,EAAE,WAAW,EAAE,EAAE,iBAAiB,GAAG,MAAM;CA8BpJ"}
|
|
@@ -22,6 +22,52 @@ const googleFetch_1 = require("./googleFetch");
|
|
|
22
22
|
const googleMusic_1 = require("./googleMusic");
|
|
23
23
|
const googleApiKeys_1 = require("./googleApiKeys");
|
|
24
24
|
const googleKeyPool_1 = require("./googleKeyPool");
|
|
25
|
+
/**
|
|
26
|
+
* Wait until a file exists AND its on-disk size has stopped growing, returning
|
|
27
|
+
* the final stable byte size. The Veo Files API downloader can resolve / create
|
|
28
|
+
* the file before all bytes are flushed; reading it immediately then streams a
|
|
29
|
+
* partial file — truncated at a chunk boundary (commonly exactly 4 MiB) — which
|
|
30
|
+
* drops the trailing `moov` atom and yields a corrupt MP4. Requiring the size to
|
|
31
|
+
* hold steady across consecutive polls closes that flush race.
|
|
32
|
+
*/
|
|
33
|
+
async function waitForStableFile(path, timeoutMs = 30000, intervalMs = 500) {
|
|
34
|
+
const start = Date.now();
|
|
35
|
+
let lastSize = -1;
|
|
36
|
+
let stablePolls = 0;
|
|
37
|
+
while (Date.now() - start < timeoutMs) {
|
|
38
|
+
let size = -1;
|
|
39
|
+
try {
|
|
40
|
+
size = (await promises_1.default.stat(path)).size;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// not created yet — keep polling
|
|
44
|
+
}
|
|
45
|
+
if (size > 0 && size === lastSize) {
|
|
46
|
+
// Two consecutive equal, non-zero reads ⇒ the writer has finished.
|
|
47
|
+
if (++stablePolls >= 2)
|
|
48
|
+
return size;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
stablePolls = 0;
|
|
52
|
+
}
|
|
53
|
+
lastSize = size;
|
|
54
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
55
|
+
}
|
|
56
|
+
throw new Error(`Timeout waiting for a stable (fully-written) file: ${path}`);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Cheap structural check that a local MP4 is COMPLETE: a well-formed Veo clip
|
|
60
|
+
* carries both an `ftyp` box (header) and a `moov` box (movie metadata, written
|
|
61
|
+
* at the END for non-faststart output). A truncated download is missing `moov`
|
|
62
|
+
* — exactly what ffprobe reports downstream as "moov atom not found". Scans the
|
|
63
|
+
* file bytes (clips are a few MB) so we never need an ffprobe binary in this
|
|
64
|
+
* shared module.
|
|
65
|
+
*/
|
|
66
|
+
async function mp4IsComplete(path) {
|
|
67
|
+
const buf = await promises_1.default.readFile(path);
|
|
68
|
+
return (buf.includes(Buffer.from("ftyp", "ascii")) &&
|
|
69
|
+
buf.includes(Buffer.from("moov", "ascii")));
|
|
70
|
+
}
|
|
25
71
|
const VEO_3_1_MODELS = new Set([
|
|
26
72
|
aiModels_1.EVideoGenModels.GOOGLE_VEO_3_1,
|
|
27
73
|
aiModels_1.EVideoGenModels.GOOGLE_VEO_3_1_FAST,
|
|
@@ -402,14 +448,52 @@ class GoogleService extends baseAiGenProvider_service_1.BaseAiGenProviderService
|
|
|
402
448
|
// separate project, so downloading its videos via `this.ai` would 403/404.
|
|
403
449
|
// Use the re-selected `client` (falls back to `this.ai` for un-tagged
|
|
404
450
|
// legacy tasks, which were submitted on GOOGLE_API_KEY anyway).
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
451
|
+
// Download → validate-complete → upload, retried as a unit. The Files API
|
|
452
|
+
// downloader intermittently resolves before the clip is fully flushed to
|
|
453
|
+
// disk; uploading then truncates the scene at a chunk boundary (commonly
|
|
454
|
+
// exactly 4 MiB), dropping the trailing `moov` atom and yielding a corrupt
|
|
455
|
+
// MP4 that ffprobe rejects downstream (poster/watermark "moov atom not
|
|
456
|
+
// found", and a broken deliverable). Guard with (1) a stable-size wait so
|
|
457
|
+
// we only read a fully-written file, (2) a structural ftyp+moov check, and
|
|
458
|
+
// (3) a stored-vs-local size match after upload. Any failure re-runs the
|
|
459
|
+
// whole download instead of shipping corruption.
|
|
460
|
+
const MAX_FETCH_ATTEMPTS = 3;
|
|
461
|
+
let uploaded = false;
|
|
462
|
+
for (let attempt = 1; attempt <= MAX_FETCH_ATTEMPTS && !uploaded; attempt++) {
|
|
463
|
+
await this.withTransientRetry("files.download", () => client.files.download({ file: video, downloadPath: localPath }));
|
|
464
|
+
const localSize = await waitForStableFile(localPath, 30000, 500);
|
|
465
|
+
if (await mp4IsComplete(localPath)) {
|
|
466
|
+
await (0, promises_2.pipeline)((0, fs_1.createReadStream)(localPath), file.createWriteStream({ contentType: "video/mp4" }));
|
|
467
|
+
const [meta] = await file.getMetadata();
|
|
468
|
+
const storedSize = Number(meta.size);
|
|
469
|
+
if (Number.isFinite(storedSize) && storedSize === localSize) {
|
|
470
|
+
uploaded = true;
|
|
471
|
+
break; // faithful, complete upload
|
|
472
|
+
}
|
|
473
|
+
logger_1.logger.warn("google: scene upload size mismatch — retrying", {
|
|
474
|
+
filePath,
|
|
475
|
+
attempt,
|
|
476
|
+
localSize,
|
|
477
|
+
storedSize,
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
else {
|
|
481
|
+
logger_1.logger.warn("google: downloaded scene missing moov atom — retrying", {
|
|
482
|
+
filePath,
|
|
483
|
+
attempt,
|
|
484
|
+
localSize,
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
// Failed validation — discard the partial file before the next attempt.
|
|
488
|
+
await promises_1.default.unlink(localPath).catch(() => { });
|
|
489
|
+
}
|
|
490
|
+
if (!uploaded) {
|
|
491
|
+
logger_1.logger.error("google: scene download/upload failed integrity validation — giving up", { filePath });
|
|
492
|
+
return {
|
|
493
|
+
status: types_1.EVideoSceneStatus.FAILED,
|
|
494
|
+
errorMessage: "Generated video failed integrity validation (incomplete/truncated download).",
|
|
495
|
+
};
|
|
496
|
+
}
|
|
413
497
|
const [signedUrl] = await file.getSignedUrl({
|
|
414
498
|
action: "read",
|
|
415
499
|
expires: "03-09-2491",
|