fix: correct Kling API params per official docs

- camera_control: only kling-v1 and kling-v1-5 support it (not v3)
- For preset types (down_back etc.), config must be absent — only
  'simple' type uses config fields
- camera_control and image_tail are mutually exclusive in I2V
- cfg_scale not supported by kling-v2.x models — now skipped
- duration sent as string to match API examples
- v3/v3-omni removed from camera control UI list

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Vadym Samoilenko 2026-04-24 18:38:31 +01:00
parent 746cd42f6c
commit a2358ba01c
2 changed files with 40 additions and 25 deletions

View file

@ -146,15 +146,20 @@ class KlingAPI {
* Text-to-Video generation
*/
public function textToVideo($prompt, $opts = []) {
$modelName = $opts['model_name'] ?? 'kling-v3';
$payload = [
'prompt' => $prompt,
'model_name' => $opts['model_name'] ?? 'kling-v2-6',
'mode' => $opts['mode'] ?? 'std',
'duration' => intval($opts['duration'] ?? 5),
'prompt' => $prompt,
'model_name' => $modelName,
'mode' => $opts['mode'] ?? 'std',
'duration' => strval(intval($opts['duration'] ?? 5)),
'aspect_ratio' => $opts['aspect_ratio'] ?? '16:9',
'cfg_scale' => floatval($opts['cfg_scale'] ?? 0.5)
];
// cfg_scale not supported by v2.x models
if (!preg_match('/^kling-v2/', $modelName)) {
$payload['cfg_scale'] = floatval($opts['cfg_scale'] ?? 0.5);
}
if (!empty($opts['negative_prompt'])) {
$payload['negative_prompt'] = $opts['negative_prompt'];
}
@ -163,14 +168,16 @@ class KlingAPI {
$payload['sound'] = 'on';
}
// Camera/motion control — only supported by kling-v1, kling-v1-5, kling-v3, kling-v3-omni
$cameraControlModels = ['kling-v1', 'kling-v1-5', 'kling-v3', 'kling-v3-omni'];
// camera_control only supported by kling-v1 and kling-v1-5 per capability map
if (!empty($opts['camera_control']) && $opts['camera_control'] !== 'none'
&& in_array($opts['model_name'] ?? '', $cameraControlModels)) {
$payload['camera_control'] = [
'type' => $opts['camera_control'],
'config' => ['horizontal' => 0, 'vertical' => 0, 'pan' => 0, 'tilt' => 0, 'roll' => 0, 'zoom' => 0]
];
&& in_array($modelName, ['kling-v1', 'kling-v1-5'])) {
$cameraType = $opts['camera_control'];
// For preset types, config must be null/absent; only 'simple' type uses config
if ($cameraType === 'simple') {
$payload['camera_control'] = ['type' => 'simple', 'config' => ['horizontal' => 0, 'vertical' => 0, 'pan' => 0, 'tilt' => 0, 'roll' => 0, 'zoom' => 0]];
} else {
$payload['camera_control'] = ['type' => $cameraType];
}
}
error_log("Kling T2V request: model={$payload['model_name']}, mode={$payload['mode']}, duration={$payload['duration']}");
@ -184,14 +191,19 @@ class KlingAPI {
* Image-to-Video generation
*/
public function imageToVideo($images, $prompt, $opts = []) {
$modelName = $opts['model_name'] ?? 'kling-v3';
$payload = [
'model_name' => $opts['model_name'] ?? 'kling-v2-6',
'mode' => $opts['mode'] ?? 'std',
'duration' => intval($opts['duration'] ?? 5),
'model_name' => $modelName,
'mode' => $opts['mode'] ?? 'std',
'duration' => strval(intval($opts['duration'] ?? 5)),
'aspect_ratio' => $opts['aspect_ratio'] ?? '16:9',
'cfg_scale' => floatval($opts['cfg_scale'] ?? 0.5)
];
// cfg_scale not supported by v2.x models
if (!preg_match('/^kling-v2/', $modelName)) {
$payload['cfg_scale'] = floatval($opts['cfg_scale'] ?? 0.5);
}
if (!empty($prompt)) {
$payload['prompt'] = $prompt;
}
@ -204,14 +216,17 @@ class KlingAPI {
$payload['sound'] = 'on';
}
// Camera/motion control — only supported by kling-v1, kling-v1-5, kling-v3, kling-v3-omni
$cameraControlModels = ['kling-v1', 'kling-v1-5', 'kling-v3', 'kling-v3-omni'];
// camera_control only for kling-v1/v1-5; also mutually exclusive with image_tail
$hasTailFrame = isset($images[1]);
if (!empty($opts['camera_control']) && $opts['camera_control'] !== 'none'
&& in_array($opts['model_name'] ?? '', $cameraControlModels)) {
$payload['camera_control'] = [
'type' => $opts['camera_control'],
'config' => ['horizontal' => 0, 'vertical' => 0, 'pan' => 0, 'tilt' => 0, 'roll' => 0, 'zoom' => 0]
];
&& in_array($modelName, ['kling-v1', 'kling-v1-5'])
&& !$hasTailFrame) {
$cameraType = $opts['camera_control'];
if ($cameraType === 'simple') {
$payload['camera_control'] = ['type' => 'simple', 'config' => ['horizontal' => 0, 'vertical' => 0, 'pan' => 0, 'tilt' => 0, 'roll' => 0, 'zoom' => 0]];
} else {
$payload['camera_control'] = ['type' => $cameraType];
}
}
// Start frame — field name is `image`, value is clean base64 (no data URI prefix)

View file

@ -240,8 +240,8 @@ const VideoGenTab = ({ activeProjectId, rerunData, onRerunLoaded, onBusyChange,
{ value: 'kling-v1-6', label: 'V1.6', description: 'Legacy' },
{ value: 'kling-v1', label: 'V1', description: 'Cam control' },
];
// Models that support camera / motion control
const klingCameraControlModels = ['kling-v1', 'kling-v1-5', 'kling-v3', 'kling-v3-omni'];
// camera_control presets only supported by kling-v1 and kling-v1-5 per official capability map
const klingCameraControlModels = ['kling-v1', 'kling-v1-5'];
const cameraPresets = [
{ value: 'none', label: 'None' },