climage 0.5.0 → 0.5.1
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/cli.js +57 -18
- package/dist/cli.js.map +1 -1
- package/dist/index.js +57 -18
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -653,6 +653,9 @@ var falProvider = {
|
|
|
653
653
|
|
|
654
654
|
// src/providers/google.ts
|
|
655
655
|
import { GoogleGenAI } from "@google/genai";
|
|
656
|
+
import { mkdtemp, readFile, rm } from "fs/promises";
|
|
657
|
+
import { tmpdir } from "os";
|
|
658
|
+
import { join } from "path";
|
|
656
659
|
function getGeminiApiKey(env) {
|
|
657
660
|
return env.GEMINI_API_KEY || env.GOOGLE_API_KEY || env.GOOGLE_GENAI_API_KEY;
|
|
658
661
|
}
|
|
@@ -700,6 +703,21 @@ function imageToGoogleFormat(imageInput) {
|
|
|
700
703
|
}
|
|
701
704
|
return { fileUri: imageInput };
|
|
702
705
|
}
|
|
706
|
+
function imageToVeoFormat(imageInput) {
|
|
707
|
+
if (imageInput.startsWith("data:")) {
|
|
708
|
+
const parsed = parseDataUri(imageInput);
|
|
709
|
+
if (!parsed?.data) {
|
|
710
|
+
throw new Error("Failed to parse data URI for Veo image input");
|
|
711
|
+
}
|
|
712
|
+
return { imageBytes: parsed.data, mimeType: parsed.mimeType };
|
|
713
|
+
}
|
|
714
|
+
if (imageInput.startsWith("gs://")) {
|
|
715
|
+
return { gcsUri: imageInput };
|
|
716
|
+
}
|
|
717
|
+
throw new Error(
|
|
718
|
+
`Veo image inputs must be data: URIs or gs:// URIs (got ${imageInput.slice(0, 24)}...)`
|
|
719
|
+
);
|
|
720
|
+
}
|
|
703
721
|
var GEMINI_IMAGE_MODELS = ["gemini-2.5-flash-image", "gemini-3-pro-image-preview"];
|
|
704
722
|
function resolveModel(model) {
|
|
705
723
|
if (!model) return "gemini-2.5-flash-image";
|
|
@@ -718,6 +736,31 @@ async function downloadBytes3(url) {
|
|
|
718
736
|
log3(`Downloaded ${ab.byteLength} bytes in ${Date.now() - start}ms, type: ${ct}`);
|
|
719
737
|
return { bytes: new Uint8Array(ab), mimeType: ct };
|
|
720
738
|
}
|
|
739
|
+
async function downloadGeneratedVideo(ai, generatedVideo) {
|
|
740
|
+
const video = generatedVideo?.video;
|
|
741
|
+
if (video?.videoBytes) {
|
|
742
|
+
return {
|
|
743
|
+
bytes: new Uint8Array(Buffer.from(video.videoBytes, "base64")),
|
|
744
|
+
mimeType: video.mimeType
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
if (video?.uri && !video.uri.startsWith("gs://")) {
|
|
748
|
+
try {
|
|
749
|
+
return await downloadBytes3(video.uri);
|
|
750
|
+
} catch (err) {
|
|
751
|
+
log3("Direct video download failed, falling back to ai.files.download:", String(err));
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
const tempDir = await mkdtemp(join(tmpdir(), "climage-veo-"));
|
|
755
|
+
const downloadPath = join(tempDir, "video.mp4");
|
|
756
|
+
try {
|
|
757
|
+
await ai.files.download({ file: generatedVideo, downloadPath });
|
|
758
|
+
const buf = await readFile(downloadPath);
|
|
759
|
+
return { bytes: new Uint8Array(buf), mimeType: video?.mimeType ?? "video/mp4" };
|
|
760
|
+
} finally {
|
|
761
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
762
|
+
}
|
|
763
|
+
}
|
|
721
764
|
async function sleep2(ms) {
|
|
722
765
|
await new Promise((r) => setTimeout(r, ms));
|
|
723
766
|
}
|
|
@@ -783,15 +826,15 @@ async function generateWithVeo(ai, model, req) {
|
|
|
783
826
|
const config = {
|
|
784
827
|
numberOfVideos: req.n,
|
|
785
828
|
...req.aspectRatio ? { aspectRatio: req.aspectRatio } : {},
|
|
786
|
-
// Add duration if specified (Veo
|
|
787
|
-
...req.duration !== void 0 ? { durationSeconds:
|
|
829
|
+
// Add duration if specified (Veo supports 4-8 seconds depending on model)
|
|
830
|
+
...req.duration !== void 0 ? { durationSeconds: req.duration } : {}
|
|
788
831
|
};
|
|
789
832
|
if (req.inputImages?.length && isVeo31Model(model)) {
|
|
790
833
|
const referenceImages = req.inputImages.slice(0, 3).map((img) => {
|
|
791
|
-
const imageData =
|
|
834
|
+
const imageData = imageToVeoFormat(img);
|
|
792
835
|
return {
|
|
793
836
|
image: imageData,
|
|
794
|
-
referenceType: "
|
|
837
|
+
referenceType: "ASSET"
|
|
795
838
|
};
|
|
796
839
|
});
|
|
797
840
|
config.referenceImages = referenceImages;
|
|
@@ -804,12 +847,12 @@ async function generateWithVeo(ai, model, req) {
|
|
|
804
847
|
};
|
|
805
848
|
const firstFrameImage = req.startFrame ?? (req.inputImages?.length === 1 ? req.inputImages[0] : void 0);
|
|
806
849
|
if (firstFrameImage && isVeo31Model(model)) {
|
|
807
|
-
const imageData =
|
|
850
|
+
const imageData = imageToVeoFormat(firstFrameImage);
|
|
808
851
|
generateParams.image = imageData;
|
|
809
852
|
log3("Added first frame image");
|
|
810
853
|
}
|
|
811
854
|
if (req.endFrame && isVeo31Model(model)) {
|
|
812
|
-
const lastFrameData =
|
|
855
|
+
const lastFrameData = imageToVeoFormat(req.endFrame);
|
|
813
856
|
config.lastFrame = lastFrameData;
|
|
814
857
|
log3("Added last frame for interpolation");
|
|
815
858
|
}
|
|
@@ -839,26 +882,22 @@ async function generateWithVeo(ai, model, req) {
|
|
|
839
882
|
for (let i = 0; i < Math.min(videos.length, req.n); i++) {
|
|
840
883
|
const v = videos[i];
|
|
841
884
|
log3(`Processing video ${i}:`, JSON.stringify(v).slice(0, 300));
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
log3(`Video ${i} has no URI, skipping`);
|
|
885
|
+
if (!v?.video) {
|
|
886
|
+
log3(`Video ${i} has no video payload, skipping`);
|
|
845
887
|
continue;
|
|
846
888
|
}
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
);
|
|
851
|
-
}
|
|
852
|
-
const { bytes, mimeType } = await downloadBytes3(uri);
|
|
853
|
-
out.push({
|
|
889
|
+
const uri = v?.video?.uri;
|
|
890
|
+
const { bytes, mimeType } = await downloadGeneratedVideo(ai, v);
|
|
891
|
+
const item = {
|
|
854
892
|
kind: "video",
|
|
855
893
|
provider: "google",
|
|
856
894
|
model,
|
|
857
895
|
index: i,
|
|
858
|
-
url: uri,
|
|
859
896
|
bytes,
|
|
860
897
|
...mimeType !== void 0 ? { mimeType } : {}
|
|
861
|
-
}
|
|
898
|
+
};
|
|
899
|
+
if (uri) item.url = uri;
|
|
900
|
+
out.push(item);
|
|
862
901
|
}
|
|
863
902
|
if (!out.length) throw new Error("Google Veo returned videos but none were downloadable");
|
|
864
903
|
log3(`Successfully generated ${out.length} video(s)`);
|