@bonginkan/maria 4.3.15 → 4.3.16

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.
@@ -7008,7 +7008,7 @@ async function atomicRename(stage, dest) {
7008
7008
  }
7009
7009
  async function saveArtifacts(ctx, items, manifest) {
7010
7010
  const root = ctx.root;
7011
- const base = ctx.baseDir || (ctx.kind === "image" ? "artifacts/media/images" : "artifacts/media/videos");
7011
+ const base = typeof ctx.baseDir === "string" ? ctx.baseDir : "";
7012
7012
  const trace = ctx.trace || Math.random().toString(36).slice(2, 10).toUpperCase();
7013
7013
  const stage = stageDir(root, trace);
7014
7014
  ensureDirSync(stage);
@@ -7016,7 +7016,7 @@ async function saveArtifacts(ctx, items, manifest) {
7016
7016
  const dateSeg = datePath(/* @__PURE__ */ new Date());
7017
7017
  const reqHash = manifest.request && manifest.request.promptHash || "sha256:unknown";
7018
7018
  const slug = hashPrefix(reqHash, 6);
7019
- const outDirSeg = `${base}/${dateSeg}/${slug}`;
7019
+ const outDirSeg = ctx.flat ? base || "" : `${base ? base + "/" : ""}${dateSeg}/${slug}`;
7020
7020
  const outDir = safeJoin(root, outDirSeg).full;
7021
7021
  ensureDirSync(outDir);
7022
7022
  const saved = [];
@@ -7026,7 +7026,8 @@ async function saveArtifacts(ctx, items, manifest) {
7026
7026
  const ext = it.ext.startsWith(".") ? it.ext : `.${it.ext}`;
7027
7027
  const baseName = it.logicalName ? `${it.logicalName}` : `${contentHash}`;
7028
7028
  const fname = `${baseName}${ext}`;
7029
- const dest = safeJoin(root, `${outDirSeg}/${fname}`);
7029
+ const relPath = outDirSeg ? `${outDirSeg}/${fname}` : `${fname}`;
7030
+ const dest = safeJoin(root, relPath);
7030
7031
  validateWinPathEdge(dest.full);
7031
7032
  if (await hasCaseInsensitiveCollision(path__namespace.dirname(dest.full), path__namespace.basename(dest.full))) {
7032
7033
  throw new Error("case-insensitive filename collision");
@@ -7046,23 +7047,28 @@ async function saveArtifacts(ctx, items, manifest) {
7046
7047
  await atomicRename(stg, dest.full);
7047
7048
  saved.push(dest.rel);
7048
7049
  }
7049
- const manifestObj = {
7050
- manifestVersion: 1,
7051
- ...manifest,
7052
- createdAt: manifest.createdAt || (/* @__PURE__ */ new Date()).toISOString(),
7053
- artifacts: manifest.artifacts && manifest.artifacts.length > 0 ? manifest.artifacts : saved.map((file) => ({ file, hash: `sha256:${path__namespace.basename(file).split(".")[0]}` }))
7054
- };
7055
- const manifestPathRel = `${outDirSeg}/manifest.json`;
7056
- const manifestStage = path__namespace.join(stage, "manifest.json.part");
7057
- const manifestFull = safeJoin(root, manifestPathRel).full;
7058
- await fsp__namespace.writeFile(manifestStage, JSON.stringify(manifestObj, null, 2), "utf8");
7059
- try {
7060
- console.log(`[store] manifest -> ${manifestPathRel}`);
7061
- } catch {
7050
+ if (!ctx.skipManifest) {
7051
+ const manifestObj = {
7052
+ manifestVersion: 1,
7053
+ ...manifest,
7054
+ createdAt: manifest.createdAt || (/* @__PURE__ */ new Date()).toISOString(),
7055
+ artifacts: manifest.artifacts && manifest.artifacts.length > 0 ? manifest.artifacts : saved.map((file) => ({ file, hash: `sha256:${path__namespace.basename(file).split(".")[0]}` }))
7056
+ };
7057
+ const manifestPathRel = `${outDirSeg ? outDirSeg + "/" : ""}manifest.json`;
7058
+ const manifestStage = path__namespace.join(stage, "manifest.json.part");
7059
+ const manifestFull = safeJoin(root, manifestPathRel).full;
7060
+ await fsp__namespace.writeFile(manifestStage, JSON.stringify(manifestObj, null, 2), "utf8");
7061
+ try {
7062
+ console.log(`[store] manifest -> ${manifestPathRel}`);
7063
+ } catch {
7064
+ }
7065
+ await atomicRename(manifestStage, manifestFull);
7066
+ await fsp__namespace.rm(stage, { recursive: true, force: true });
7067
+ return { files: saved, manifestPath: manifestPathRel };
7068
+ } else {
7069
+ await fsp__namespace.rm(stage, { recursive: true, force: true });
7070
+ return { files: saved, manifestPath: "" };
7062
7071
  }
7063
- await atomicRename(manifestStage, manifestFull);
7064
- await fsp__namespace.rm(stage, { recursive: true, force: true });
7065
- return { files: saved, manifestPath: manifestPathRel };
7066
7072
  } catch (e2) {
7067
7073
  await fsp__namespace.rm(stage, { recursive: true, force: true });
7068
7074
  throw e2;
@@ -8532,7 +8538,12 @@ var app = express__default.default();
8532
8538
  var port = process.env.PORT || 8080;
8533
8539
  app.use(helmet__default.default());
8534
8540
  app.use(cors__default.default());
8535
- app.use(compression__default.default());
8541
+ app.use(compression__default.default({
8542
+ filter: (req, res) => {
8543
+ if (/\.(mp4|webm|mov)$/i.test(req.url)) return false;
8544
+ return compression.filter(req, res);
8545
+ }
8546
+ }));
8536
8547
  app.use(express__default.default.json({ limit: "10mb" }));
8537
8548
  app.use(express__default.default.urlencoded({ extended: true }));
8538
8549
  try {
@@ -8760,7 +8771,7 @@ app.post("/api/v1/video", rateLimitMiddleware, async (req, res) => {
8760
8771
  await loadProviderKeys();
8761
8772
  const auth = req.headers.authorization;
8762
8773
  if (!auth || !auth.startsWith("Bearer ")) return res.status(401).json({ error: "unauthorized" });
8763
- const { prompt, duration = 5, fps = 24, res: resStr = "1280x720", format = "mp4", model, seed } = req.body || {};
8774
+ const { prompt, duration = 8, fps = 24, res: resStr = "1280x720", format = "mp4", model, seed } = req.body || {};
8764
8775
  if (!prompt) return res.status(400).json({ error: "bad_request", message: "prompt required" });
8765
8776
  const m2 = /^(\d{2,4})x(\d{2,4})$/.exec(String(resStr));
8766
8777
  if (!m2) return res.status(400).json({ error: "bad_request", message: "res must be WxH" });
@@ -8772,10 +8783,15 @@ app.post("/api/v1/video", rateLimitMiddleware, async (req, res) => {
8772
8783
  const veoModel = model && String(model).trim() || process.env.MARIA_VIDEO_MODEL || "veo-3.0-generate-001";
8773
8784
  const aspectRatio = w >= h2 ? "16:9" : "9:16";
8774
8785
  const startedMs = Date.now();
8786
+ const requestedDuration = Number(duration);
8787
+ const effectiveDuration = Number.isFinite(requestedDuration) && requestedDuration === 8 ? 8 : 8;
8788
+ const requestedFps = Number(fps);
8789
+ const effectiveFps = Number.isFinite(requestedFps) ? Math.min(60, Math.max(1, Math.floor(requestedFps))) : 24;
8775
8790
  let operation = await ai.models.generateVideos({
8776
8791
  model: veoModel,
8777
8792
  prompt: String(prompt),
8778
- config: { aspectRatio }
8793
+ // Pass duration/fps to provider to avoid default ~1s clips
8794
+ config: { aspectRatio, durationSeconds: effectiveDuration, frameRate: effectiveFps }
8779
8795
  });
8780
8796
  const deadline = Date.now() + 6e5;
8781
8797
  while (!operation?.done) {
@@ -8794,22 +8810,46 @@ app.post("/api/v1/video", rateLimitMiddleware, async (req, res) => {
8794
8810
  const tmpDir = path__namespace.default.join(process.cwd(), ".stage", traceId);
8795
8811
  await fsp__namespace.default.mkdir(tmpDir, { recursive: true });
8796
8812
  const tmpOut = path__namespace.default.join(tmpDir, "video.bin");
8797
- await ai.files.download({ file: videoRef, downloadPath: tmpOut });
8798
- {
8799
- const deadline2 = Date.now() + 1e4;
8813
+ const dlApiKey = process.env.GOOGLE_API_KEY || process.env.GEMINI_API_KEY || "";
8814
+ const isLikelyValidVideo = async (buf) => {
8815
+ if (!buf || buf.length < 16) return false;
8816
+ const head = buf.subarray(0, Math.min(256, buf.length));
8817
+ const headStr = head.toString("latin1");
8818
+ const hasFtyp2 = headStr.indexOf("ftyp") >= 0 || head.includes(Buffer.from("ftyp", "ascii"));
8819
+ const isWebm2 = head[0] === 26 && head[1] === 69 && head[2] === 223 && head[3] === 163;
8820
+ const looksHtml = headStr.includes("<!doctype") || headStr.includes("<html") || headStr.includes("<?xml");
8821
+ return (hasFtyp2 || isWebm2) && !looksHtml;
8822
+ };
8823
+ const ensureFileMaterialized = async (p, waitMs = 1e4) => {
8824
+ const deadline2 = Date.now() + waitMs;
8800
8825
  while (true) {
8801
8826
  try {
8802
- const st = await fsp__namespace.default.stat(tmpOut);
8803
- if (st && st.size > 0) break;
8827
+ const st = await fsp__namespace.default.stat(p);
8828
+ if (st && st.size > 0) return;
8804
8829
  } catch {
8805
8830
  }
8806
- if (Date.now() > deadline2) {
8807
- throw new Error(`video file not found after download: ${tmpOut}`);
8808
- }
8831
+ if (Date.now() > deadline2) throw new Error(`video file not found after download: ${p}`);
8809
8832
  await new Promise((r2) => setTimeout(r2, 200));
8810
8833
  }
8834
+ };
8835
+ const trySdkDownloadOnce = async () => {
8836
+ try {
8837
+ await ai.files.download({ file: videoRef, downloadPath: tmpOut });
8838
+ await ensureFileMaterialized(tmpOut, 15e3);
8839
+ const bytes = await fsp__namespace.default.readFile(tmpOut);
8840
+ if (await isLikelyValidVideo(bytes)) return bytes;
8841
+ } catch {
8842
+ }
8843
+ return null;
8844
+ };
8845
+ let videoBytes = null;
8846
+ for (let attempt = 1; attempt <= 3 && !videoBytes; attempt++) {
8847
+ videoBytes = await trySdkDownloadOnce();
8848
+ if (!videoBytes) await new Promise((r2) => setTimeout(r2, 600 * attempt));
8849
+ }
8850
+ if (!videoBytes) {
8851
+ throw new Error("failed to download valid video bytes");
8811
8852
  }
8812
- const videoBytes = await fsp__namespace.default.readFile(tmpOut);
8813
8853
  const header = videoBytes.subarray(0, 256);
8814
8854
  const headerStr = header.toString("latin1");
8815
8855
  let outExt = ".mp4";
@@ -8819,23 +8859,32 @@ app.post("/api/v1/video", rateLimitMiddleware, async (req, res) => {
8819
8859
  if (videoMime && /webm/i.test(videoMime)) {
8820
8860
  outExt = ".webm";
8821
8861
  outMime = "video/webm";
8862
+ } else if (videoMime && /quicktime/i.test(videoMime)) {
8863
+ outExt = ".mov";
8864
+ outMime = "video/quicktime";
8822
8865
  } else if (isWebm) {
8823
8866
  outExt = ".webm";
8824
8867
  outMime = "video/webm";
8825
8868
  } else if (hasFtyp) {
8826
- outExt = ".mp4";
8827
- outMime = "video/mp4";
8869
+ const majorBrand = header.subarray(8, 12).toString("latin1");
8870
+ if ((majorBrand || "").toLowerCase().startsWith("qt")) {
8871
+ outExt = ".mov";
8872
+ outMime = "video/quicktime";
8873
+ } else {
8874
+ outExt = ".mp4";
8875
+ outMime = "video/mp4";
8876
+ }
8828
8877
  }
8829
8878
  await fsp__namespace.default.rm(tmpDir, { recursive: true, force: true });
8830
8879
  const promptHash = hashPrompt(prompt);
8831
8880
  const manifest = {
8832
8881
  kind: "video",
8833
- request: { promptHash, seed, params: { size: [w, h2], fps, duration, format: outExt.replace(".", "") }, model: veoModel, provider: "google" },
8882
+ request: { promptHash, seed, params: { size: [w, h2], fps: effectiveFps, duration: effectiveDuration, format: outExt.replace(".", "") }, model: veoModel, provider: "google" },
8834
8883
  artifacts: [],
8835
8884
  metrics: { durationMs: Date.now() - startedMs, retries: 0, fallbacks: 0 },
8836
8885
  trace: traceId
8837
8886
  };
8838
- const saved = await saveArtifacts({ root: process.cwd(), kind: "video" }, [{ bytes: videoBytes, ext: outExt }], manifest);
8887
+ const saved = await saveArtifacts({ root: process.cwd(), kind: "video", baseDir: "artifacts/media/videos", flat: false, skipManifest: true }, [{ bytes: videoBytes, ext: outExt }], manifest);
8839
8888
  const idemKey = req.headers["idempotency-key"] || void 0;
8840
8889
  const idToken = auth.substring("Bearer ".length).trim();
8841
8890
  const decoded = await decodeFirebaseToken(idToken).catch(() => null);
@@ -8850,7 +8899,7 @@ app.post("/api/v1/video", rateLimitMiddleware, async (req, res) => {
8850
8899
  updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
8851
8900
  uid
8852
8901
  });
8853
- return res.json({ success: true, data: { url: saved.manifestPath, files: saved.files, jobId: manifest.trace } });
8902
+ return res.json({ success: true, data: { url: saved.files[0] ? `/${saved.files[0]}` : void 0, files: saved.files, jobId: manifest.trace, applied: { durationSeconds: effectiveDuration, frameRate: effectiveFps } } });
8854
8903
  } catch (error) {
8855
8904
  console.error("[Video API] Error:", error);
8856
8905
  return res.status(500).json({
@@ -7008,7 +7008,7 @@ async function atomicRename(stage, dest) {
7008
7008
  }
7009
7009
  async function saveArtifacts(ctx, items, manifest) {
7010
7010
  const root = ctx.root;
7011
- const base = ctx.baseDir || (ctx.kind === "image" ? "artifacts/media/images" : "artifacts/media/videos");
7011
+ const base = typeof ctx.baseDir === "string" ? ctx.baseDir : "";
7012
7012
  const trace = ctx.trace || Math.random().toString(36).slice(2, 10).toUpperCase();
7013
7013
  const stage = stageDir(root, trace);
7014
7014
  ensureDirSync(stage);
@@ -7016,7 +7016,7 @@ async function saveArtifacts(ctx, items, manifest) {
7016
7016
  const dateSeg = datePath(/* @__PURE__ */ new Date());
7017
7017
  const reqHash = manifest.request && manifest.request.promptHash || "sha256:unknown";
7018
7018
  const slug = hashPrefix(reqHash, 6);
7019
- const outDirSeg = `${base}/${dateSeg}/${slug}`;
7019
+ const outDirSeg = ctx.flat ? base || "" : `${base ? base + "/" : ""}${dateSeg}/${slug}`;
7020
7020
  const outDir = safeJoin(root, outDirSeg).full;
7021
7021
  ensureDirSync(outDir);
7022
7022
  const saved = [];
@@ -7026,7 +7026,8 @@ async function saveArtifacts(ctx, items, manifest) {
7026
7026
  const ext = it.ext.startsWith(".") ? it.ext : `.${it.ext}`;
7027
7027
  const baseName = it.logicalName ? `${it.logicalName}` : `${contentHash}`;
7028
7028
  const fname = `${baseName}${ext}`;
7029
- const dest = safeJoin(root, `${outDirSeg}/${fname}`);
7029
+ const relPath = outDirSeg ? `${outDirSeg}/${fname}` : `${fname}`;
7030
+ const dest = safeJoin(root, relPath);
7030
7031
  validateWinPathEdge(dest.full);
7031
7032
  if (await hasCaseInsensitiveCollision(path__namespace.dirname(dest.full), path__namespace.basename(dest.full))) {
7032
7033
  throw new Error("case-insensitive filename collision");
@@ -7046,23 +7047,28 @@ async function saveArtifacts(ctx, items, manifest) {
7046
7047
  await atomicRename(stg, dest.full);
7047
7048
  saved.push(dest.rel);
7048
7049
  }
7049
- const manifestObj = {
7050
- manifestVersion: 1,
7051
- ...manifest,
7052
- createdAt: manifest.createdAt || (/* @__PURE__ */ new Date()).toISOString(),
7053
- artifacts: manifest.artifacts && manifest.artifacts.length > 0 ? manifest.artifacts : saved.map((file) => ({ file, hash: `sha256:${path__namespace.basename(file).split(".")[0]}` }))
7054
- };
7055
- const manifestPathRel = `${outDirSeg}/manifest.json`;
7056
- const manifestStage = path__namespace.join(stage, "manifest.json.part");
7057
- const manifestFull = safeJoin(root, manifestPathRel).full;
7058
- await fsp__namespace.writeFile(manifestStage, JSON.stringify(manifestObj, null, 2), "utf8");
7059
- try {
7060
- console.log(`[store] manifest -> ${manifestPathRel}`);
7061
- } catch {
7050
+ if (!ctx.skipManifest) {
7051
+ const manifestObj = {
7052
+ manifestVersion: 1,
7053
+ ...manifest,
7054
+ createdAt: manifest.createdAt || (/* @__PURE__ */ new Date()).toISOString(),
7055
+ artifacts: manifest.artifacts && manifest.artifacts.length > 0 ? manifest.artifacts : saved.map((file) => ({ file, hash: `sha256:${path__namespace.basename(file).split(".")[0]}` }))
7056
+ };
7057
+ const manifestPathRel = `${outDirSeg ? outDirSeg + "/" : ""}manifest.json`;
7058
+ const manifestStage = path__namespace.join(stage, "manifest.json.part");
7059
+ const manifestFull = safeJoin(root, manifestPathRel).full;
7060
+ await fsp__namespace.writeFile(manifestStage, JSON.stringify(manifestObj, null, 2), "utf8");
7061
+ try {
7062
+ console.log(`[store] manifest -> ${manifestPathRel}`);
7063
+ } catch {
7064
+ }
7065
+ await atomicRename(manifestStage, manifestFull);
7066
+ await fsp__namespace.rm(stage, { recursive: true, force: true });
7067
+ return { files: saved, manifestPath: manifestPathRel };
7068
+ } else {
7069
+ await fsp__namespace.rm(stage, { recursive: true, force: true });
7070
+ return { files: saved, manifestPath: "" };
7062
7071
  }
7063
- await atomicRename(manifestStage, manifestFull);
7064
- await fsp__namespace.rm(stage, { recursive: true, force: true });
7065
- return { files: saved, manifestPath: manifestPathRel };
7066
7072
  } catch (e2) {
7067
7073
  await fsp__namespace.rm(stage, { recursive: true, force: true });
7068
7074
  throw e2;
@@ -8532,7 +8538,12 @@ var app = express__default.default();
8532
8538
  var port = process.env.PORT || 8080;
8533
8539
  app.use(helmet__default.default());
8534
8540
  app.use(cors__default.default());
8535
- app.use(compression__default.default());
8541
+ app.use(compression__default.default({
8542
+ filter: (req, res) => {
8543
+ if (/\.(mp4|webm|mov)$/i.test(req.url)) return false;
8544
+ return compression.filter(req, res);
8545
+ }
8546
+ }));
8536
8547
  app.use(express__default.default.json({ limit: "10mb" }));
8537
8548
  app.use(express__default.default.urlencoded({ extended: true }));
8538
8549
  try {
@@ -8760,7 +8771,7 @@ app.post("/api/v1/video", rateLimitMiddleware, async (req, res) => {
8760
8771
  await loadProviderKeys();
8761
8772
  const auth = req.headers.authorization;
8762
8773
  if (!auth || !auth.startsWith("Bearer ")) return res.status(401).json({ error: "unauthorized" });
8763
- const { prompt, duration = 5, fps = 24, res: resStr = "1280x720", format = "mp4", model, seed } = req.body || {};
8774
+ const { prompt, duration = 8, fps = 24, res: resStr = "1280x720", format = "mp4", model, seed } = req.body || {};
8764
8775
  if (!prompt) return res.status(400).json({ error: "bad_request", message: "prompt required" });
8765
8776
  const m2 = /^(\d{2,4})x(\d{2,4})$/.exec(String(resStr));
8766
8777
  if (!m2) return res.status(400).json({ error: "bad_request", message: "res must be WxH" });
@@ -8772,10 +8783,15 @@ app.post("/api/v1/video", rateLimitMiddleware, async (req, res) => {
8772
8783
  const veoModel = model && String(model).trim() || process.env.MARIA_VIDEO_MODEL || "veo-3.0-generate-001";
8773
8784
  const aspectRatio = w >= h2 ? "16:9" : "9:16";
8774
8785
  const startedMs = Date.now();
8786
+ const requestedDuration = Number(duration);
8787
+ const effectiveDuration = Number.isFinite(requestedDuration) && requestedDuration === 8 ? 8 : 8;
8788
+ const requestedFps = Number(fps);
8789
+ const effectiveFps = Number.isFinite(requestedFps) ? Math.min(60, Math.max(1, Math.floor(requestedFps))) : 24;
8775
8790
  let operation = await ai.models.generateVideos({
8776
8791
  model: veoModel,
8777
8792
  prompt: String(prompt),
8778
- config: { aspectRatio }
8793
+ // Pass duration/fps to provider to avoid default ~1s clips
8794
+ config: { aspectRatio, durationSeconds: effectiveDuration, frameRate: effectiveFps }
8779
8795
  });
8780
8796
  const deadline = Date.now() + 6e5;
8781
8797
  while (!operation?.done) {
@@ -8794,22 +8810,46 @@ app.post("/api/v1/video", rateLimitMiddleware, async (req, res) => {
8794
8810
  const tmpDir = path__namespace.default.join(process.cwd(), ".stage", traceId);
8795
8811
  await fsp__namespace.default.mkdir(tmpDir, { recursive: true });
8796
8812
  const tmpOut = path__namespace.default.join(tmpDir, "video.bin");
8797
- await ai.files.download({ file: videoRef, downloadPath: tmpOut });
8798
- {
8799
- const deadline2 = Date.now() + 1e4;
8813
+ const dlApiKey = process.env.GOOGLE_API_KEY || process.env.GEMINI_API_KEY || "";
8814
+ const isLikelyValidVideo = async (buf) => {
8815
+ if (!buf || buf.length < 16) return false;
8816
+ const head = buf.subarray(0, Math.min(256, buf.length));
8817
+ const headStr = head.toString("latin1");
8818
+ const hasFtyp2 = headStr.indexOf("ftyp") >= 0 || head.includes(Buffer.from("ftyp", "ascii"));
8819
+ const isWebm2 = head[0] === 26 && head[1] === 69 && head[2] === 223 && head[3] === 163;
8820
+ const looksHtml = headStr.includes("<!doctype") || headStr.includes("<html") || headStr.includes("<?xml");
8821
+ return (hasFtyp2 || isWebm2) && !looksHtml;
8822
+ };
8823
+ const ensureFileMaterialized = async (p, waitMs = 1e4) => {
8824
+ const deadline2 = Date.now() + waitMs;
8800
8825
  while (true) {
8801
8826
  try {
8802
- const st = await fsp__namespace.default.stat(tmpOut);
8803
- if (st && st.size > 0) break;
8827
+ const st = await fsp__namespace.default.stat(p);
8828
+ if (st && st.size > 0) return;
8804
8829
  } catch {
8805
8830
  }
8806
- if (Date.now() > deadline2) {
8807
- throw new Error(`video file not found after download: ${tmpOut}`);
8808
- }
8831
+ if (Date.now() > deadline2) throw new Error(`video file not found after download: ${p}`);
8809
8832
  await new Promise((r2) => setTimeout(r2, 200));
8810
8833
  }
8834
+ };
8835
+ const trySdkDownloadOnce = async () => {
8836
+ try {
8837
+ await ai.files.download({ file: videoRef, downloadPath: tmpOut });
8838
+ await ensureFileMaterialized(tmpOut, 15e3);
8839
+ const bytes = await fsp__namespace.default.readFile(tmpOut);
8840
+ if (await isLikelyValidVideo(bytes)) return bytes;
8841
+ } catch {
8842
+ }
8843
+ return null;
8844
+ };
8845
+ let videoBytes = null;
8846
+ for (let attempt = 1; attempt <= 3 && !videoBytes; attempt++) {
8847
+ videoBytes = await trySdkDownloadOnce();
8848
+ if (!videoBytes) await new Promise((r2) => setTimeout(r2, 600 * attempt));
8849
+ }
8850
+ if (!videoBytes) {
8851
+ throw new Error("failed to download valid video bytes");
8811
8852
  }
8812
- const videoBytes = await fsp__namespace.default.readFile(tmpOut);
8813
8853
  const header = videoBytes.subarray(0, 256);
8814
8854
  const headerStr = header.toString("latin1");
8815
8855
  let outExt = ".mp4";
@@ -8819,23 +8859,32 @@ app.post("/api/v1/video", rateLimitMiddleware, async (req, res) => {
8819
8859
  if (videoMime && /webm/i.test(videoMime)) {
8820
8860
  outExt = ".webm";
8821
8861
  outMime = "video/webm";
8862
+ } else if (videoMime && /quicktime/i.test(videoMime)) {
8863
+ outExt = ".mov";
8864
+ outMime = "video/quicktime";
8822
8865
  } else if (isWebm) {
8823
8866
  outExt = ".webm";
8824
8867
  outMime = "video/webm";
8825
8868
  } else if (hasFtyp) {
8826
- outExt = ".mp4";
8827
- outMime = "video/mp4";
8869
+ const majorBrand = header.subarray(8, 12).toString("latin1");
8870
+ if ((majorBrand || "").toLowerCase().startsWith("qt")) {
8871
+ outExt = ".mov";
8872
+ outMime = "video/quicktime";
8873
+ } else {
8874
+ outExt = ".mp4";
8875
+ outMime = "video/mp4";
8876
+ }
8828
8877
  }
8829
8878
  await fsp__namespace.default.rm(tmpDir, { recursive: true, force: true });
8830
8879
  const promptHash = hashPrompt(prompt);
8831
8880
  const manifest = {
8832
8881
  kind: "video",
8833
- request: { promptHash, seed, params: { size: [w, h2], fps, duration, format: outExt.replace(".", "") }, model: veoModel, provider: "google" },
8882
+ request: { promptHash, seed, params: { size: [w, h2], fps: effectiveFps, duration: effectiveDuration, format: outExt.replace(".", "") }, model: veoModel, provider: "google" },
8834
8883
  artifacts: [],
8835
8884
  metrics: { durationMs: Date.now() - startedMs, retries: 0, fallbacks: 0 },
8836
8885
  trace: traceId
8837
8886
  };
8838
- const saved = await saveArtifacts({ root: process.cwd(), kind: "video" }, [{ bytes: videoBytes, ext: outExt }], manifest);
8887
+ const saved = await saveArtifacts({ root: process.cwd(), kind: "video", baseDir: "artifacts/media/videos", flat: false, skipManifest: true }, [{ bytes: videoBytes, ext: outExt }], manifest);
8839
8888
  const idemKey = req.headers["idempotency-key"] || void 0;
8840
8889
  const idToken = auth.substring("Bearer ".length).trim();
8841
8890
  const decoded = await decodeFirebaseToken(idToken).catch(() => null);
@@ -8850,7 +8899,7 @@ app.post("/api/v1/video", rateLimitMiddleware, async (req, res) => {
8850
8899
  updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
8851
8900
  uid
8852
8901
  });
8853
- return res.json({ success: true, data: { url: saved.manifestPath, files: saved.files, jobId: manifest.trace } });
8902
+ return res.json({ success: true, data: { url: saved.files[0] ? `/${saved.files[0]}` : void 0, files: saved.files, jobId: manifest.trace, applied: { durationSeconds: effectiveDuration, frameRate: effectiveFps } } });
8854
8903
  } catch (error) {
8855
8904
  console.error("[Video API] Error:", error);
8856
8905
  return res.status(500).json({
@@ -7008,7 +7008,7 @@ async function atomicRename(stage, dest) {
7008
7008
  }
7009
7009
  async function saveArtifacts(ctx, items, manifest) {
7010
7010
  const root = ctx.root;
7011
- const base = ctx.baseDir || (ctx.kind === "image" ? "artifacts/media/images" : "artifacts/media/videos");
7011
+ const base = typeof ctx.baseDir === "string" ? ctx.baseDir : "";
7012
7012
  const trace = ctx.trace || Math.random().toString(36).slice(2, 10).toUpperCase();
7013
7013
  const stage = stageDir(root, trace);
7014
7014
  ensureDirSync(stage);
@@ -7016,7 +7016,7 @@ async function saveArtifacts(ctx, items, manifest) {
7016
7016
  const dateSeg = datePath(/* @__PURE__ */ new Date());
7017
7017
  const reqHash = manifest.request && manifest.request.promptHash || "sha256:unknown";
7018
7018
  const slug = hashPrefix(reqHash, 6);
7019
- const outDirSeg = `${base}/${dateSeg}/${slug}`;
7019
+ const outDirSeg = ctx.flat ? base || "" : `${base ? base + "/" : ""}${dateSeg}/${slug}`;
7020
7020
  const outDir = safeJoin(root, outDirSeg).full;
7021
7021
  ensureDirSync(outDir);
7022
7022
  const saved = [];
@@ -7026,7 +7026,8 @@ async function saveArtifacts(ctx, items, manifest) {
7026
7026
  const ext = it.ext.startsWith(".") ? it.ext : `.${it.ext}`;
7027
7027
  const baseName = it.logicalName ? `${it.logicalName}` : `${contentHash}`;
7028
7028
  const fname = `${baseName}${ext}`;
7029
- const dest = safeJoin(root, `${outDirSeg}/${fname}`);
7029
+ const relPath = outDirSeg ? `${outDirSeg}/${fname}` : `${fname}`;
7030
+ const dest = safeJoin(root, relPath);
7030
7031
  validateWinPathEdge(dest.full);
7031
7032
  if (await hasCaseInsensitiveCollision(path__namespace.dirname(dest.full), path__namespace.basename(dest.full))) {
7032
7033
  throw new Error("case-insensitive filename collision");
@@ -7046,23 +7047,28 @@ async function saveArtifacts(ctx, items, manifest) {
7046
7047
  await atomicRename(stg, dest.full);
7047
7048
  saved.push(dest.rel);
7048
7049
  }
7049
- const manifestObj = {
7050
- manifestVersion: 1,
7051
- ...manifest,
7052
- createdAt: manifest.createdAt || (/* @__PURE__ */ new Date()).toISOString(),
7053
- artifacts: manifest.artifacts && manifest.artifacts.length > 0 ? manifest.artifacts : saved.map((file) => ({ file, hash: `sha256:${path__namespace.basename(file).split(".")[0]}` }))
7054
- };
7055
- const manifestPathRel = `${outDirSeg}/manifest.json`;
7056
- const manifestStage = path__namespace.join(stage, "manifest.json.part");
7057
- const manifestFull = safeJoin(root, manifestPathRel).full;
7058
- await fsp__namespace.writeFile(manifestStage, JSON.stringify(manifestObj, null, 2), "utf8");
7059
- try {
7060
- console.log(`[store] manifest -> ${manifestPathRel}`);
7061
- } catch {
7050
+ if (!ctx.skipManifest) {
7051
+ const manifestObj = {
7052
+ manifestVersion: 1,
7053
+ ...manifest,
7054
+ createdAt: manifest.createdAt || (/* @__PURE__ */ new Date()).toISOString(),
7055
+ artifacts: manifest.artifacts && manifest.artifacts.length > 0 ? manifest.artifacts : saved.map((file) => ({ file, hash: `sha256:${path__namespace.basename(file).split(".")[0]}` }))
7056
+ };
7057
+ const manifestPathRel = `${outDirSeg ? outDirSeg + "/" : ""}manifest.json`;
7058
+ const manifestStage = path__namespace.join(stage, "manifest.json.part");
7059
+ const manifestFull = safeJoin(root, manifestPathRel).full;
7060
+ await fsp__namespace.writeFile(manifestStage, JSON.stringify(manifestObj, null, 2), "utf8");
7061
+ try {
7062
+ console.log(`[store] manifest -> ${manifestPathRel}`);
7063
+ } catch {
7064
+ }
7065
+ await atomicRename(manifestStage, manifestFull);
7066
+ await fsp__namespace.rm(stage, { recursive: true, force: true });
7067
+ return { files: saved, manifestPath: manifestPathRel };
7068
+ } else {
7069
+ await fsp__namespace.rm(stage, { recursive: true, force: true });
7070
+ return { files: saved, manifestPath: "" };
7062
7071
  }
7063
- await atomicRename(manifestStage, manifestFull);
7064
- await fsp__namespace.rm(stage, { recursive: true, force: true });
7065
- return { files: saved, manifestPath: manifestPathRel };
7066
7072
  } catch (e2) {
7067
7073
  await fsp__namespace.rm(stage, { recursive: true, force: true });
7068
7074
  throw e2;
@@ -8532,7 +8538,12 @@ var app = express__default.default();
8532
8538
  var port = process.env.PORT || 8080;
8533
8539
  app.use(helmet__default.default());
8534
8540
  app.use(cors__default.default());
8535
- app.use(compression__default.default());
8541
+ app.use(compression__default.default({
8542
+ filter: (req, res) => {
8543
+ if (/\.(mp4|webm|mov)$/i.test(req.url)) return false;
8544
+ return compression.filter(req, res);
8545
+ }
8546
+ }));
8536
8547
  app.use(express__default.default.json({ limit: "10mb" }));
8537
8548
  app.use(express__default.default.urlencoded({ extended: true }));
8538
8549
  try {
@@ -8760,7 +8771,7 @@ app.post("/api/v1/video", rateLimitMiddleware, async (req, res) => {
8760
8771
  await loadProviderKeys();
8761
8772
  const auth = req.headers.authorization;
8762
8773
  if (!auth || !auth.startsWith("Bearer ")) return res.status(401).json({ error: "unauthorized" });
8763
- const { prompt, duration = 5, fps = 24, res: resStr = "1280x720", format = "mp4", model, seed } = req.body || {};
8774
+ const { prompt, duration = 8, fps = 24, res: resStr = "1280x720", format = "mp4", model, seed } = req.body || {};
8764
8775
  if (!prompt) return res.status(400).json({ error: "bad_request", message: "prompt required" });
8765
8776
  const m2 = /^(\d{2,4})x(\d{2,4})$/.exec(String(resStr));
8766
8777
  if (!m2) return res.status(400).json({ error: "bad_request", message: "res must be WxH" });
@@ -8772,10 +8783,15 @@ app.post("/api/v1/video", rateLimitMiddleware, async (req, res) => {
8772
8783
  const veoModel = model && String(model).trim() || process.env.MARIA_VIDEO_MODEL || "veo-3.0-generate-001";
8773
8784
  const aspectRatio = w >= h2 ? "16:9" : "9:16";
8774
8785
  const startedMs = Date.now();
8786
+ const requestedDuration = Number(duration);
8787
+ const effectiveDuration = Number.isFinite(requestedDuration) && requestedDuration === 8 ? 8 : 8;
8788
+ const requestedFps = Number(fps);
8789
+ const effectiveFps = Number.isFinite(requestedFps) ? Math.min(60, Math.max(1, Math.floor(requestedFps))) : 24;
8775
8790
  let operation = await ai.models.generateVideos({
8776
8791
  model: veoModel,
8777
8792
  prompt: String(prompt),
8778
- config: { aspectRatio }
8793
+ // Pass duration/fps to provider to avoid default ~1s clips
8794
+ config: { aspectRatio, durationSeconds: effectiveDuration, frameRate: effectiveFps }
8779
8795
  });
8780
8796
  const deadline = Date.now() + 6e5;
8781
8797
  while (!operation?.done) {
@@ -8794,22 +8810,46 @@ app.post("/api/v1/video", rateLimitMiddleware, async (req, res) => {
8794
8810
  const tmpDir = path__namespace.default.join(process.cwd(), ".stage", traceId);
8795
8811
  await fsp__namespace.default.mkdir(tmpDir, { recursive: true });
8796
8812
  const tmpOut = path__namespace.default.join(tmpDir, "video.bin");
8797
- await ai.files.download({ file: videoRef, downloadPath: tmpOut });
8798
- {
8799
- const deadline2 = Date.now() + 1e4;
8813
+ const dlApiKey = process.env.GOOGLE_API_KEY || process.env.GEMINI_API_KEY || "";
8814
+ const isLikelyValidVideo = async (buf) => {
8815
+ if (!buf || buf.length < 16) return false;
8816
+ const head = buf.subarray(0, Math.min(256, buf.length));
8817
+ const headStr = head.toString("latin1");
8818
+ const hasFtyp2 = headStr.indexOf("ftyp") >= 0 || head.includes(Buffer.from("ftyp", "ascii"));
8819
+ const isWebm2 = head[0] === 26 && head[1] === 69 && head[2] === 223 && head[3] === 163;
8820
+ const looksHtml = headStr.includes("<!doctype") || headStr.includes("<html") || headStr.includes("<?xml");
8821
+ return (hasFtyp2 || isWebm2) && !looksHtml;
8822
+ };
8823
+ const ensureFileMaterialized = async (p, waitMs = 1e4) => {
8824
+ const deadline2 = Date.now() + waitMs;
8800
8825
  while (true) {
8801
8826
  try {
8802
- const st = await fsp__namespace.default.stat(tmpOut);
8803
- if (st && st.size > 0) break;
8827
+ const st = await fsp__namespace.default.stat(p);
8828
+ if (st && st.size > 0) return;
8804
8829
  } catch {
8805
8830
  }
8806
- if (Date.now() > deadline2) {
8807
- throw new Error(`video file not found after download: ${tmpOut}`);
8808
- }
8831
+ if (Date.now() > deadline2) throw new Error(`video file not found after download: ${p}`);
8809
8832
  await new Promise((r2) => setTimeout(r2, 200));
8810
8833
  }
8834
+ };
8835
+ const trySdkDownloadOnce = async () => {
8836
+ try {
8837
+ await ai.files.download({ file: videoRef, downloadPath: tmpOut });
8838
+ await ensureFileMaterialized(tmpOut, 15e3);
8839
+ const bytes = await fsp__namespace.default.readFile(tmpOut);
8840
+ if (await isLikelyValidVideo(bytes)) return bytes;
8841
+ } catch {
8842
+ }
8843
+ return null;
8844
+ };
8845
+ let videoBytes = null;
8846
+ for (let attempt = 1; attempt <= 3 && !videoBytes; attempt++) {
8847
+ videoBytes = await trySdkDownloadOnce();
8848
+ if (!videoBytes) await new Promise((r2) => setTimeout(r2, 600 * attempt));
8849
+ }
8850
+ if (!videoBytes) {
8851
+ throw new Error("failed to download valid video bytes");
8811
8852
  }
8812
- const videoBytes = await fsp__namespace.default.readFile(tmpOut);
8813
8853
  const header = videoBytes.subarray(0, 256);
8814
8854
  const headerStr = header.toString("latin1");
8815
8855
  let outExt = ".mp4";
@@ -8819,23 +8859,32 @@ app.post("/api/v1/video", rateLimitMiddleware, async (req, res) => {
8819
8859
  if (videoMime && /webm/i.test(videoMime)) {
8820
8860
  outExt = ".webm";
8821
8861
  outMime = "video/webm";
8862
+ } else if (videoMime && /quicktime/i.test(videoMime)) {
8863
+ outExt = ".mov";
8864
+ outMime = "video/quicktime";
8822
8865
  } else if (isWebm) {
8823
8866
  outExt = ".webm";
8824
8867
  outMime = "video/webm";
8825
8868
  } else if (hasFtyp) {
8826
- outExt = ".mp4";
8827
- outMime = "video/mp4";
8869
+ const majorBrand = header.subarray(8, 12).toString("latin1");
8870
+ if ((majorBrand || "").toLowerCase().startsWith("qt")) {
8871
+ outExt = ".mov";
8872
+ outMime = "video/quicktime";
8873
+ } else {
8874
+ outExt = ".mp4";
8875
+ outMime = "video/mp4";
8876
+ }
8828
8877
  }
8829
8878
  await fsp__namespace.default.rm(tmpDir, { recursive: true, force: true });
8830
8879
  const promptHash = hashPrompt(prompt);
8831
8880
  const manifest = {
8832
8881
  kind: "video",
8833
- request: { promptHash, seed, params: { size: [w, h2], fps, duration, format: outExt.replace(".", "") }, model: veoModel, provider: "google" },
8882
+ request: { promptHash, seed, params: { size: [w, h2], fps: effectiveFps, duration: effectiveDuration, format: outExt.replace(".", "") }, model: veoModel, provider: "google" },
8834
8883
  artifacts: [],
8835
8884
  metrics: { durationMs: Date.now() - startedMs, retries: 0, fallbacks: 0 },
8836
8885
  trace: traceId
8837
8886
  };
8838
- const saved = await saveArtifacts({ root: process.cwd(), kind: "video" }, [{ bytes: videoBytes, ext: outExt }], manifest);
8887
+ const saved = await saveArtifacts({ root: process.cwd(), kind: "video", baseDir: "artifacts/media/videos", flat: false, skipManifest: true }, [{ bytes: videoBytes, ext: outExt }], manifest);
8839
8888
  const idemKey = req.headers["idempotency-key"] || void 0;
8840
8889
  const idToken = auth.substring("Bearer ".length).trim();
8841
8890
  const decoded = await decodeFirebaseToken(idToken).catch(() => null);
@@ -8850,7 +8899,7 @@ app.post("/api/v1/video", rateLimitMiddleware, async (req, res) => {
8850
8899
  updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
8851
8900
  uid
8852
8901
  });
8853
- return res.json({ success: true, data: { url: saved.manifestPath, files: saved.files, jobId: manifest.trace } });
8902
+ return res.json({ success: true, data: { url: saved.files[0] ? `/${saved.files[0]}` : void 0, files: saved.files, jobId: manifest.trace, applied: { durationSeconds: effectiveDuration, frameRate: effectiveFps } } });
8854
8903
  } catch (error) {
8855
8904
  console.error("[Video API] Error:", error);
8856
8905
  return res.status(500).json({