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/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
|
}
|
|
@@ -671,6 +674,21 @@ function imageToGoogleFormat(imageInput) {
|
|
|
671
674
|
}
|
|
672
675
|
return { fileUri: imageInput };
|
|
673
676
|
}
|
|
677
|
+
function imageToVeoFormat(imageInput) {
|
|
678
|
+
if (imageInput.startsWith("data:")) {
|
|
679
|
+
const parsed = parseDataUri(imageInput);
|
|
680
|
+
if (!parsed?.data) {
|
|
681
|
+
throw new Error("Failed to parse data URI for Veo image input");
|
|
682
|
+
}
|
|
683
|
+
return { imageBytes: parsed.data, mimeType: parsed.mimeType };
|
|
684
|
+
}
|
|
685
|
+
if (imageInput.startsWith("gs://")) {
|
|
686
|
+
return { gcsUri: imageInput };
|
|
687
|
+
}
|
|
688
|
+
throw new Error(
|
|
689
|
+
`Veo image inputs must be data: URIs or gs:// URIs (got ${imageInput.slice(0, 24)}...)`
|
|
690
|
+
);
|
|
691
|
+
}
|
|
674
692
|
var GEMINI_IMAGE_MODELS = ["gemini-2.5-flash-image", "gemini-3-pro-image-preview"];
|
|
675
693
|
function resolveModel(model) {
|
|
676
694
|
if (!model) return "gemini-2.5-flash-image";
|
|
@@ -689,6 +707,31 @@ async function downloadBytes3(url) {
|
|
|
689
707
|
log3(`Downloaded ${ab.byteLength} bytes in ${Date.now() - start}ms, type: ${ct}`);
|
|
690
708
|
return { bytes: new Uint8Array(ab), mimeType: ct };
|
|
691
709
|
}
|
|
710
|
+
async function downloadGeneratedVideo(ai, generatedVideo) {
|
|
711
|
+
const video = generatedVideo?.video;
|
|
712
|
+
if (video?.videoBytes) {
|
|
713
|
+
return {
|
|
714
|
+
bytes: new Uint8Array(Buffer.from(video.videoBytes, "base64")),
|
|
715
|
+
mimeType: video.mimeType
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
if (video?.uri && !video.uri.startsWith("gs://")) {
|
|
719
|
+
try {
|
|
720
|
+
return await downloadBytes3(video.uri);
|
|
721
|
+
} catch (err) {
|
|
722
|
+
log3("Direct video download failed, falling back to ai.files.download:", String(err));
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
const tempDir = await mkdtemp(join(tmpdir(), "climage-veo-"));
|
|
726
|
+
const downloadPath = join(tempDir, "video.mp4");
|
|
727
|
+
try {
|
|
728
|
+
await ai.files.download({ file: generatedVideo, downloadPath });
|
|
729
|
+
const buf = await readFile(downloadPath);
|
|
730
|
+
return { bytes: new Uint8Array(buf), mimeType: video?.mimeType ?? "video/mp4" };
|
|
731
|
+
} finally {
|
|
732
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
733
|
+
}
|
|
734
|
+
}
|
|
692
735
|
async function sleep2(ms) {
|
|
693
736
|
await new Promise((r) => setTimeout(r, ms));
|
|
694
737
|
}
|
|
@@ -754,15 +797,15 @@ async function generateWithVeo(ai, model, req) {
|
|
|
754
797
|
const config = {
|
|
755
798
|
numberOfVideos: req.n,
|
|
756
799
|
...req.aspectRatio ? { aspectRatio: req.aspectRatio } : {},
|
|
757
|
-
// Add duration if specified (Veo
|
|
758
|
-
...req.duration !== void 0 ? { durationSeconds:
|
|
800
|
+
// Add duration if specified (Veo supports 4-8 seconds depending on model)
|
|
801
|
+
...req.duration !== void 0 ? { durationSeconds: req.duration } : {}
|
|
759
802
|
};
|
|
760
803
|
if (req.inputImages?.length && isVeo31Model(model)) {
|
|
761
804
|
const referenceImages = req.inputImages.slice(0, 3).map((img) => {
|
|
762
|
-
const imageData =
|
|
805
|
+
const imageData = imageToVeoFormat(img);
|
|
763
806
|
return {
|
|
764
807
|
image: imageData,
|
|
765
|
-
referenceType: "
|
|
808
|
+
referenceType: "ASSET"
|
|
766
809
|
};
|
|
767
810
|
});
|
|
768
811
|
config.referenceImages = referenceImages;
|
|
@@ -775,12 +818,12 @@ async function generateWithVeo(ai, model, req) {
|
|
|
775
818
|
};
|
|
776
819
|
const firstFrameImage = req.startFrame ?? (req.inputImages?.length === 1 ? req.inputImages[0] : void 0);
|
|
777
820
|
if (firstFrameImage && isVeo31Model(model)) {
|
|
778
|
-
const imageData =
|
|
821
|
+
const imageData = imageToVeoFormat(firstFrameImage);
|
|
779
822
|
generateParams.image = imageData;
|
|
780
823
|
log3("Added first frame image");
|
|
781
824
|
}
|
|
782
825
|
if (req.endFrame && isVeo31Model(model)) {
|
|
783
|
-
const lastFrameData =
|
|
826
|
+
const lastFrameData = imageToVeoFormat(req.endFrame);
|
|
784
827
|
config.lastFrame = lastFrameData;
|
|
785
828
|
log3("Added last frame for interpolation");
|
|
786
829
|
}
|
|
@@ -810,26 +853,22 @@ async function generateWithVeo(ai, model, req) {
|
|
|
810
853
|
for (let i = 0; i < Math.min(videos.length, req.n); i++) {
|
|
811
854
|
const v = videos[i];
|
|
812
855
|
log3(`Processing video ${i}:`, JSON.stringify(v).slice(0, 300));
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
log3(`Video ${i} has no URI, skipping`);
|
|
856
|
+
if (!v?.video) {
|
|
857
|
+
log3(`Video ${i} has no video payload, skipping`);
|
|
816
858
|
continue;
|
|
817
859
|
}
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
);
|
|
822
|
-
}
|
|
823
|
-
const { bytes, mimeType } = await downloadBytes3(uri);
|
|
824
|
-
out.push({
|
|
860
|
+
const uri = v?.video?.uri;
|
|
861
|
+
const { bytes, mimeType } = await downloadGeneratedVideo(ai, v);
|
|
862
|
+
const item = {
|
|
825
863
|
kind: "video",
|
|
826
864
|
provider: "google",
|
|
827
865
|
model,
|
|
828
866
|
index: i,
|
|
829
|
-
url: uri,
|
|
830
867
|
bytes,
|
|
831
868
|
...mimeType !== void 0 ? { mimeType } : {}
|
|
832
|
-
}
|
|
869
|
+
};
|
|
870
|
+
if (uri) item.url = uri;
|
|
871
|
+
out.push(item);
|
|
833
872
|
}
|
|
834
873
|
if (!out.length) throw new Error("Google Veo returned videos but none were downloadable");
|
|
835
874
|
log3(`Successfully generated ${out.length} video(s)`);
|