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;AAelB,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;IAiH3C,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"}
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
- await this.withTransientRetry("files.download", () => client.files.download({
406
- file: video,
407
- downloadPath: localPath,
408
- }));
409
- await (0, helpers_1.waitForFile)(localPath, 20000, 500);
410
- const readStream = (0, fs_1.createReadStream)(localPath);
411
- const writeStream = file.createWriteStream({ contentType: "video/mp4" });
412
- await (0, promises_2.pipeline)(readStream, writeStream);
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",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vidspotai-shared",
3
- "version": "1.0.93",
3
+ "version": "1.0.94",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "exports": {