climage 0.4.2 → 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 CHANGED
@@ -100,11 +100,12 @@ Set one of:
100
100
 
101
101
  **Models:**
102
102
 
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 |
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-8 | N/A |
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
@@ -437,8 +437,15 @@ var DEFAULT_IMAGE_MODEL = "fal-ai/flux/dev";
437
437
  var DEFAULT_IMAGE_TO_IMAGE_MODEL = "fal-ai/flux/dev/image-to-image";
438
438
  var DEFAULT_VIDEO_MODEL = "fal-ai/ltxv-2/text-to-video/fast";
439
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";
440
441
  var DEFAULT_START_END_VIDEO_MODEL = "fal-ai/vidu/start-end-to-video";
441
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
+ }
442
449
  function selectVideoModel(req) {
443
450
  if (req.model) return req.model;
444
451
  if (req.startFrame && req.endFrame) {
@@ -457,9 +464,12 @@ function selectImageModel(req) {
457
464
  if (req.inputImages?.length) return DEFAULT_IMAGE_TO_IMAGE_MODEL;
458
465
  return DEFAULT_IMAGE_MODEL;
459
466
  }
460
- function mapAspectRatio(aspectRatio) {
467
+ function mapAspectRatio(aspectRatio, model) {
461
468
  if (!aspectRatio) return void 0;
462
469
  const ar = aspectRatio.trim();
470
+ if (model && isKlingV3Model(model)) {
471
+ return ar;
472
+ }
463
473
  if (ar === "1:1") return "square";
464
474
  if (ar === "4:3") return "landscape_4_3";
465
475
  if (ar === "16:9") return "landscape_16_9";
@@ -467,29 +477,50 @@ function mapAspectRatio(aspectRatio) {
467
477
  if (ar === "9:16") return "portrait_16_9";
468
478
  return ar;
469
479
  }
470
- function buildVideoInput(req) {
480
+ function buildVideoInput(req, model) {
471
481
  const input = {
472
482
  prompt: req.prompt
473
483
  };
474
484
  if (req.startFrame && req.endFrame) {
475
485
  input.start_image_url = req.startFrame;
476
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);
477
490
  return input;
478
491
  }
479
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
+ }
480
500
  input.reference_image_urls = req.inputImages.slice(0, 7);
481
- const ar = mapAspectRatio(req.aspectRatio);
501
+ const ar = mapAspectRatio(req.aspectRatio, model);
482
502
  if (ar) input.aspect_ratio = ar;
483
503
  if (req.duration) input.duration = String(req.duration);
484
504
  return input;
485
505
  }
486
506
  const imageUrl = req.startFrame ?? req.inputImages?.[0];
487
507
  if (imageUrl) {
488
- input.image_url = imageUrl;
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
+ }
489
515
  if (req.duration) input.duration = String(req.duration);
490
516
  return input;
491
517
  }
492
- const imageSize = mapAspectRatio(req.aspectRatio);
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);
493
524
  if (imageSize) input.image_size = imageSize;
494
525
  if (req.n) input.num_videos = req.n;
495
526
  return input;
@@ -514,8 +545,8 @@ var falCapabilities = {
514
545
  supportsCustomAspectRatio: true,
515
546
  supportsVideoInterpolation: true,
516
547
  // Vidu start-end-to-video
517
- videoDurationRange: [2, 8],
518
- // Vidu supports 2-8 seconds
548
+ videoDurationRange: [2, 15],
549
+ // Most models are 2-8; Kling v3 supports up to 15
519
550
  supportsImageEditing: true
520
551
  };
521
552
  var falProvider = {
@@ -543,7 +574,19 @@ var falProvider = {
543
574
  fal.config({ credentials: key });
544
575
  const model = req.kind === "video" ? selectVideoModel(req) : selectImageModel(req);
545
576
  log2(verbose, "Selected model:", model);
546
- const input = req.kind === "video" ? buildVideoInput(req) : buildImageInput(req);
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);
547
590
  const inputSummary = { ...input };
548
591
  for (const key2 of ["image_url", "start_image_url", "end_image_url"]) {
549
592
  if (typeof inputSummary[key2] === "string" && inputSummary[key2].startsWith("data:")) {