climage 0.5.0 → 0.5.2
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/README.md +8 -7
- package/dist/cli.js +63 -19
- package/dist/cli.js.map +1 -1
- package/dist/index.js +63 -19
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -624,6 +624,9 @@ var falProvider = {
|
|
|
624
624
|
|
|
625
625
|
// src/providers/google.ts
|
|
626
626
|
import { GoogleGenAI } from "@google/genai";
|
|
627
|
+
import { mkdtemp, readFile, rm } from "fs/promises";
|
|
628
|
+
import { tmpdir } from "os";
|
|
629
|
+
import { join } from "path";
|
|
627
630
|
function getGeminiApiKey(env) {
|
|
628
631
|
return env.GEMINI_API_KEY || env.GOOGLE_API_KEY || env.GOOGLE_GENAI_API_KEY;
|
|
629
632
|
}
|
|
@@ -645,6 +648,7 @@ function log3(...args) {
|
|
|
645
648
|
var MODEL_ALIASES = {
|
|
646
649
|
"nano-banana": "gemini-2.5-flash-image",
|
|
647
650
|
"nano-banana-pro": "gemini-3-pro-image-preview",
|
|
651
|
+
"nano-banana-2": "gemini-3.1-flash-image-preview",
|
|
648
652
|
// Veo (video)
|
|
649
653
|
veo2: "veo-2.0-generate-001",
|
|
650
654
|
"veo-2": "veo-2.0-generate-001",
|
|
@@ -671,7 +675,26 @@ function imageToGoogleFormat(imageInput) {
|
|
|
671
675
|
}
|
|
672
676
|
return { fileUri: imageInput };
|
|
673
677
|
}
|
|
674
|
-
|
|
678
|
+
function imageToVeoFormat(imageInput) {
|
|
679
|
+
if (imageInput.startsWith("data:")) {
|
|
680
|
+
const parsed = parseDataUri(imageInput);
|
|
681
|
+
if (!parsed?.data) {
|
|
682
|
+
throw new Error("Failed to parse data URI for Veo image input");
|
|
683
|
+
}
|
|
684
|
+
return { imageBytes: parsed.data, mimeType: parsed.mimeType };
|
|
685
|
+
}
|
|
686
|
+
if (imageInput.startsWith("gs://")) {
|
|
687
|
+
return { gcsUri: imageInput };
|
|
688
|
+
}
|
|
689
|
+
throw new Error(
|
|
690
|
+
`Veo image inputs must be data: URIs or gs:// URIs (got ${imageInput.slice(0, 24)}...)`
|
|
691
|
+
);
|
|
692
|
+
}
|
|
693
|
+
var GEMINI_IMAGE_MODELS = [
|
|
694
|
+
"gemini-2.5-flash-image",
|
|
695
|
+
"gemini-3-pro-image-preview",
|
|
696
|
+
"gemini-3.1-flash-image-preview"
|
|
697
|
+
];
|
|
675
698
|
function resolveModel(model) {
|
|
676
699
|
if (!model) return "gemini-2.5-flash-image";
|
|
677
700
|
return MODEL_ALIASES[model] ?? model;
|
|
@@ -689,6 +712,31 @@ async function downloadBytes3(url) {
|
|
|
689
712
|
log3(`Downloaded ${ab.byteLength} bytes in ${Date.now() - start}ms, type: ${ct}`);
|
|
690
713
|
return { bytes: new Uint8Array(ab), mimeType: ct };
|
|
691
714
|
}
|
|
715
|
+
async function downloadGeneratedVideo(ai, generatedVideo) {
|
|
716
|
+
const video = generatedVideo?.video;
|
|
717
|
+
if (video?.videoBytes) {
|
|
718
|
+
return {
|
|
719
|
+
bytes: new Uint8Array(Buffer.from(video.videoBytes, "base64")),
|
|
720
|
+
mimeType: video.mimeType
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
if (video?.uri && !video.uri.startsWith("gs://")) {
|
|
724
|
+
try {
|
|
725
|
+
return await downloadBytes3(video.uri);
|
|
726
|
+
} catch (err) {
|
|
727
|
+
log3("Direct video download failed, falling back to ai.files.download:", String(err));
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
const tempDir = await mkdtemp(join(tmpdir(), "climage-veo-"));
|
|
731
|
+
const downloadPath = join(tempDir, "video.mp4");
|
|
732
|
+
try {
|
|
733
|
+
await ai.files.download({ file: generatedVideo, downloadPath });
|
|
734
|
+
const buf = await readFile(downloadPath);
|
|
735
|
+
return { bytes: new Uint8Array(buf), mimeType: video?.mimeType ?? "video/mp4" };
|
|
736
|
+
} finally {
|
|
737
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
738
|
+
}
|
|
739
|
+
}
|
|
692
740
|
async function sleep2(ms) {
|
|
693
741
|
await new Promise((r) => setTimeout(r, ms));
|
|
694
742
|
}
|
|
@@ -754,15 +802,15 @@ async function generateWithVeo(ai, model, req) {
|
|
|
754
802
|
const config = {
|
|
755
803
|
numberOfVideos: req.n,
|
|
756
804
|
...req.aspectRatio ? { aspectRatio: req.aspectRatio } : {},
|
|
757
|
-
// Add duration if specified (Veo
|
|
758
|
-
...req.duration !== void 0 ? { durationSeconds:
|
|
805
|
+
// Add duration if specified (Veo supports 4-8 seconds depending on model)
|
|
806
|
+
...req.duration !== void 0 ? { durationSeconds: req.duration } : {}
|
|
759
807
|
};
|
|
760
808
|
if (req.inputImages?.length && isVeo31Model(model)) {
|
|
761
809
|
const referenceImages = req.inputImages.slice(0, 3).map((img) => {
|
|
762
|
-
const imageData =
|
|
810
|
+
const imageData = imageToVeoFormat(img);
|
|
763
811
|
return {
|
|
764
812
|
image: imageData,
|
|
765
|
-
referenceType: "
|
|
813
|
+
referenceType: "ASSET"
|
|
766
814
|
};
|
|
767
815
|
});
|
|
768
816
|
config.referenceImages = referenceImages;
|
|
@@ -775,12 +823,12 @@ async function generateWithVeo(ai, model, req) {
|
|
|
775
823
|
};
|
|
776
824
|
const firstFrameImage = req.startFrame ?? (req.inputImages?.length === 1 ? req.inputImages[0] : void 0);
|
|
777
825
|
if (firstFrameImage && isVeo31Model(model)) {
|
|
778
|
-
const imageData =
|
|
826
|
+
const imageData = imageToVeoFormat(firstFrameImage);
|
|
779
827
|
generateParams.image = imageData;
|
|
780
828
|
log3("Added first frame image");
|
|
781
829
|
}
|
|
782
830
|
if (req.endFrame && isVeo31Model(model)) {
|
|
783
|
-
const lastFrameData =
|
|
831
|
+
const lastFrameData = imageToVeoFormat(req.endFrame);
|
|
784
832
|
config.lastFrame = lastFrameData;
|
|
785
833
|
log3("Added last frame for interpolation");
|
|
786
834
|
}
|
|
@@ -810,26 +858,22 @@ async function generateWithVeo(ai, model, req) {
|
|
|
810
858
|
for (let i = 0; i < Math.min(videos.length, req.n); i++) {
|
|
811
859
|
const v = videos[i];
|
|
812
860
|
log3(`Processing video ${i}:`, JSON.stringify(v).slice(0, 300));
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
log3(`Video ${i} has no URI, skipping`);
|
|
861
|
+
if (!v?.video) {
|
|
862
|
+
log3(`Video ${i} has no video payload, skipping`);
|
|
816
863
|
continue;
|
|
817
864
|
}
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
);
|
|
822
|
-
}
|
|
823
|
-
const { bytes, mimeType } = await downloadBytes3(uri);
|
|
824
|
-
out.push({
|
|
865
|
+
const uri = v?.video?.uri;
|
|
866
|
+
const { bytes, mimeType } = await downloadGeneratedVideo(ai, v);
|
|
867
|
+
const item = {
|
|
825
868
|
kind: "video",
|
|
826
869
|
provider: "google",
|
|
827
870
|
model,
|
|
828
871
|
index: i,
|
|
829
|
-
url: uri,
|
|
830
872
|
bytes,
|
|
831
873
|
...mimeType !== void 0 ? { mimeType } : {}
|
|
832
|
-
}
|
|
874
|
+
};
|
|
875
|
+
if (uri) item.url = uri;
|
|
876
|
+
out.push(item);
|
|
833
877
|
}
|
|
834
878
|
if (!out.length) throw new Error("Google Veo returned videos but none were downloadable");
|
|
835
879
|
log3(`Successfully generated ${out.length} video(s)`);
|