climage 0.4.1 → 0.5.0
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 +12 -6
- package/dist/cli.js +53 -11
- package/dist/cli.js.map +1 -1
- package/dist/index.js +53 -11
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -100,11 +100,12 @@ Set one of:
|
|
|
100
100
|
|
|
101
101
|
**Models:**
|
|
102
102
|
|
|
103
|
-
| Model
|
|
104
|
-
|
|
|
105
|
-
| `fal-ai/flux/dev`
|
|
106
|
-
| `fal-ai/flux/pro`
|
|
107
|
-
| `fal-ai/flux-realism`
|
|
103
|
+
| Model | Description |
|
|
104
|
+
| ------------------------------------------ | -------------------------------------- |
|
|
105
|
+
| `fal-ai/flux/dev` | **Default.** Flux dev (fast & popular) |
|
|
106
|
+
| `fal-ai/flux/pro` | Flux pro (higher quality) |
|
|
107
|
+
| `fal-ai/flux-realism` | Photorealistic style |
|
|
108
|
+
| `fal-ai/kling-video/v3/pro/image-to-video` | Kling v3 Pro image-to-video |
|
|
108
109
|
|
|
109
110
|
Example:
|
|
110
111
|
|
|
@@ -166,6 +167,9 @@ npx climage "the scene comes to life" --video --provider google --start-frame sc
|
|
|
166
167
|
# Image-to-video with fal.ai
|
|
167
168
|
npx climage "dramatic camera zoom" --video --provider fal --start-frame photo.jpg
|
|
168
169
|
|
|
170
|
+
# Image-to-video with fal.ai Kling v3 Pro
|
|
171
|
+
npx climage "dramatic camera zoom" --video --provider fal --model fal-ai/kling-video/v3/pro/image-to-video --start-frame photo.jpg
|
|
172
|
+
|
|
169
173
|
# Image-to-video with xAI
|
|
170
174
|
npx climage "animate this scene" --video --provider xai --start-frame cat.png
|
|
171
175
|
```
|
|
@@ -204,7 +208,9 @@ npx climage "character in motion" --video --provider fal --input ref1.png --inpu
|
|
|
204
208
|
| Image-to-Video | Yes | Yes | Yes | No |
|
|
205
209
|
| Video Interpolation | Yes | No | Yes | No |
|
|
206
210
|
| Max Input Images | 3 | 1 | 7 | 2 |
|
|
207
|
-
| Video Duration (seconds) | 4-8 | 1-15 | 2-
|
|
211
|
+
| Video Duration (seconds) | 4-8 | 1-15 | 2-15\* | N/A |
|
|
212
|
+
|
|
213
|
+
\* Model-specific on fal.ai (e.g. Vidu: 2-8, Kling v3 Pro: 3-15).
|
|
208
214
|
|
|
209
215
|
## Library API
|
|
210
216
|
|
package/dist/cli.js
CHANGED
|
@@ -285,8 +285,7 @@ async function generateXaiVideo(req, apiKey) {
|
|
|
285
285
|
prompt: req.prompt,
|
|
286
286
|
model,
|
|
287
287
|
...req.aspectRatio ? { aspect_ratio: req.aspectRatio } : {},
|
|
288
|
-
|
|
289
|
-
...imageUrl ? { image_url: imageUrl } : {},
|
|
288
|
+
...imageUrl ? { image: { url: imageUrl } } : {},
|
|
290
289
|
// Add duration (xAI supports 1-15 seconds)
|
|
291
290
|
...req.duration !== void 0 ? { duration: req.duration } : {}
|
|
292
291
|
};
|
|
@@ -294,7 +293,7 @@ async function generateXaiVideo(req, apiKey) {
|
|
|
294
293
|
"Request body:",
|
|
295
294
|
JSON.stringify({
|
|
296
295
|
...createBody,
|
|
297
|
-
|
|
296
|
+
image: createBody.image ? { url: `...(${String(createBody.image.url).length} chars)` } : void 0
|
|
298
297
|
})
|
|
299
298
|
);
|
|
300
299
|
log("Calling xAI videos/generations...");
|
|
@@ -438,8 +437,15 @@ var DEFAULT_IMAGE_MODEL = "fal-ai/flux/dev";
|
|
|
438
437
|
var DEFAULT_IMAGE_TO_IMAGE_MODEL = "fal-ai/flux/dev/image-to-image";
|
|
439
438
|
var DEFAULT_VIDEO_MODEL = "fal-ai/ltxv-2/text-to-video/fast";
|
|
440
439
|
var DEFAULT_IMAGE_TO_VIDEO_MODEL = "fal-ai/vidu/q2/image-to-video";
|
|
440
|
+
var KLING_V3_PRO_IMAGE_TO_VIDEO_MODEL = "fal-ai/kling-video/v3/pro/image-to-video";
|
|
441
441
|
var DEFAULT_START_END_VIDEO_MODEL = "fal-ai/vidu/start-end-to-video";
|
|
442
442
|
var DEFAULT_REFERENCE_VIDEO_MODEL = "fal-ai/vidu/q2/reference-to-video";
|
|
443
|
+
function isKlingV3Model(model) {
|
|
444
|
+
return model === KLING_V3_PRO_IMAGE_TO_VIDEO_MODEL || model.startsWith("fal-ai/kling-video/v3/");
|
|
445
|
+
}
|
|
446
|
+
function isViduModel(model) {
|
|
447
|
+
return model.includes("/vidu/");
|
|
448
|
+
}
|
|
443
449
|
function selectVideoModel(req) {
|
|
444
450
|
if (req.model) return req.model;
|
|
445
451
|
if (req.startFrame && req.endFrame) {
|
|
@@ -458,9 +464,12 @@ function selectImageModel(req) {
|
|
|
458
464
|
if (req.inputImages?.length) return DEFAULT_IMAGE_TO_IMAGE_MODEL;
|
|
459
465
|
return DEFAULT_IMAGE_MODEL;
|
|
460
466
|
}
|
|
461
|
-
function mapAspectRatio(aspectRatio) {
|
|
467
|
+
function mapAspectRatio(aspectRatio, model) {
|
|
462
468
|
if (!aspectRatio) return void 0;
|
|
463
469
|
const ar = aspectRatio.trim();
|
|
470
|
+
if (model && isKlingV3Model(model)) {
|
|
471
|
+
return ar;
|
|
472
|
+
}
|
|
464
473
|
if (ar === "1:1") return "square";
|
|
465
474
|
if (ar === "4:3") return "landscape_4_3";
|
|
466
475
|
if (ar === "16:9") return "landscape_16_9";
|
|
@@ -468,29 +477,50 @@ function mapAspectRatio(aspectRatio) {
|
|
|
468
477
|
if (ar === "9:16") return "portrait_16_9";
|
|
469
478
|
return ar;
|
|
470
479
|
}
|
|
471
|
-
function buildVideoInput(req) {
|
|
480
|
+
function buildVideoInput(req, model) {
|
|
472
481
|
const input = {
|
|
473
482
|
prompt: req.prompt
|
|
474
483
|
};
|
|
475
484
|
if (req.startFrame && req.endFrame) {
|
|
476
485
|
input.start_image_url = req.startFrame;
|
|
477
486
|
input.end_image_url = req.endFrame;
|
|
487
|
+
const ar = mapAspectRatio(req.aspectRatio, model);
|
|
488
|
+
if (ar) input.aspect_ratio = ar;
|
|
489
|
+
if (req.duration) input.duration = String(req.duration);
|
|
478
490
|
return input;
|
|
479
491
|
}
|
|
480
492
|
if (req.inputImages?.length && !req.startFrame) {
|
|
493
|
+
if (isKlingV3Model(model)) {
|
|
494
|
+
input.start_image_url = req.inputImages[0];
|
|
495
|
+
const ar2 = mapAspectRatio(req.aspectRatio, model);
|
|
496
|
+
if (ar2) input.aspect_ratio = ar2;
|
|
497
|
+
if (req.duration) input.duration = String(req.duration);
|
|
498
|
+
return input;
|
|
499
|
+
}
|
|
481
500
|
input.reference_image_urls = req.inputImages.slice(0, 7);
|
|
482
|
-
const ar = mapAspectRatio(req.aspectRatio);
|
|
501
|
+
const ar = mapAspectRatio(req.aspectRatio, model);
|
|
483
502
|
if (ar) input.aspect_ratio = ar;
|
|
484
503
|
if (req.duration) input.duration = String(req.duration);
|
|
485
504
|
return input;
|
|
486
505
|
}
|
|
487
506
|
const imageUrl = req.startFrame ?? req.inputImages?.[0];
|
|
488
507
|
if (imageUrl) {
|
|
489
|
-
|
|
508
|
+
if (isKlingV3Model(model)) {
|
|
509
|
+
input.start_image_url = imageUrl;
|
|
510
|
+
const ar = mapAspectRatio(req.aspectRatio, model);
|
|
511
|
+
if (ar) input.aspect_ratio = ar;
|
|
512
|
+
} else {
|
|
513
|
+
input.image_url = imageUrl;
|
|
514
|
+
}
|
|
490
515
|
if (req.duration) input.duration = String(req.duration);
|
|
491
516
|
return input;
|
|
492
517
|
}
|
|
493
|
-
|
|
518
|
+
if (isKlingV3Model(model)) {
|
|
519
|
+
throw new Error(
|
|
520
|
+
`Model ${model} requires --start-frame (or --input) because it is image-to-video only`
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
const imageSize = mapAspectRatio(req.aspectRatio, model);
|
|
494
524
|
if (imageSize) input.image_size = imageSize;
|
|
495
525
|
if (req.n) input.num_videos = req.n;
|
|
496
526
|
return input;
|
|
@@ -515,8 +545,8 @@ var falCapabilities = {
|
|
|
515
545
|
supportsCustomAspectRatio: true,
|
|
516
546
|
supportsVideoInterpolation: true,
|
|
517
547
|
// Vidu start-end-to-video
|
|
518
|
-
videoDurationRange: [2,
|
|
519
|
-
//
|
|
548
|
+
videoDurationRange: [2, 15],
|
|
549
|
+
// Most models are 2-8; Kling v3 supports up to 15
|
|
520
550
|
supportsImageEditing: true
|
|
521
551
|
};
|
|
522
552
|
var falProvider = {
|
|
@@ -544,7 +574,19 @@ var falProvider = {
|
|
|
544
574
|
fal.config({ credentials: key });
|
|
545
575
|
const model = req.kind === "video" ? selectVideoModel(req) : selectImageModel(req);
|
|
546
576
|
log2(verbose, "Selected model:", model);
|
|
547
|
-
|
|
577
|
+
if (req.kind === "video" && req.duration !== void 0) {
|
|
578
|
+
if (isKlingV3Model(model) && (req.duration < 3 || req.duration > 15)) {
|
|
579
|
+
throw new Error(
|
|
580
|
+
`Model ${model} supports video duration 3-15s, but ${req.duration}s requested`
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
if (isViduModel(model) && (req.duration < 2 || req.duration > 8)) {
|
|
584
|
+
throw new Error(
|
|
585
|
+
`Model ${model} supports video duration 2-8s, but ${req.duration}s requested`
|
|
586
|
+
);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
const input = req.kind === "video" ? buildVideoInput(req, model) : buildImageInput(req);
|
|
548
590
|
const inputSummary = { ...input };
|
|
549
591
|
for (const key2 of ["image_url", "start_image_url", "end_image_url"]) {
|
|
550
592
|
if (typeof inputSummary[key2] === "string" && inputSummary[key2].startsWith("data:")) {
|