kling-pro-cli 2026.6.30.0__py3-none-any.whl
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.
- kling_cli/__init__.py +1 -0
- kling_cli/__main__.py +5 -0
- kling_cli/commands/__init__.py +1 -0
- kling_cli/commands/info.py +53 -0
- kling_cli/commands/lipsync.py +176 -0
- kling_cli/commands/motion.py +103 -0
- kling_cli/commands/task.py +142 -0
- kling_cli/commands/video.py +416 -0
- kling_cli/core/__init__.py +1 -0
- kling_cli/core/client.py +119 -0
- kling_cli/core/config.py +39 -0
- kling_cli/core/exceptions.py +37 -0
- kling_cli/core/output.py +150 -0
- kling_cli/main.py +77 -0
- kling_pro_cli-2026.6.30.0.dist-info/METADATA +109 -0
- kling_pro_cli-2026.6.30.0.dist-info/RECORD +19 -0
- kling_pro_cli-2026.6.30.0.dist-info/WHEEL +4 -0
- kling_pro_cli-2026.6.30.0.dist-info/entry_points.txt +3 -0
- kling_pro_cli-2026.6.30.0.dist-info/licenses/LICENSE +21 -0
kling_cli/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Kling CLI - AI Kling Video Generation via AceDataCloud API."""
|
kling_cli/__main__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Kling CLI commands package."""
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Info and utility commands."""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from kling_cli.core.config import settings
|
|
6
|
+
from kling_cli.core.output import ASPECT_RATIOS, console, print_models
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@click.command()
|
|
10
|
+
def models() -> None:
|
|
11
|
+
"""List available Kling models."""
|
|
12
|
+
print_models()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@click.command("aspect-ratios")
|
|
16
|
+
def aspect_ratios() -> None:
|
|
17
|
+
"""List available aspect ratios."""
|
|
18
|
+
from rich.table import Table
|
|
19
|
+
|
|
20
|
+
table = Table(title="Available Aspect Ratios")
|
|
21
|
+
table.add_column("Ratio", style="bold cyan")
|
|
22
|
+
table.add_column("Orientation")
|
|
23
|
+
|
|
24
|
+
for ratio in ASPECT_RATIOS:
|
|
25
|
+
w, h = ratio.split(":")
|
|
26
|
+
if int(w) > int(h):
|
|
27
|
+
orientation = "Landscape"
|
|
28
|
+
elif int(w) < int(h):
|
|
29
|
+
orientation = "Portrait"
|
|
30
|
+
else:
|
|
31
|
+
orientation = "Square"
|
|
32
|
+
table.add_row(ratio, orientation)
|
|
33
|
+
|
|
34
|
+
console.print(table)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@click.command()
|
|
38
|
+
def config() -> None:
|
|
39
|
+
"""Show current configuration."""
|
|
40
|
+
from rich.table import Table
|
|
41
|
+
|
|
42
|
+
table = Table(title="Kling CLI Configuration")
|
|
43
|
+
table.add_column("Setting", style="bold cyan")
|
|
44
|
+
table.add_column("Value")
|
|
45
|
+
|
|
46
|
+
table.add_row("API Base URL", settings.api_base_url)
|
|
47
|
+
table.add_row(
|
|
48
|
+
"API Token",
|
|
49
|
+
f"{settings.api_token[:8]}..." if settings.api_token else "[red]Not set[/red]",
|
|
50
|
+
)
|
|
51
|
+
table.add_row("Request Timeout", f"{settings.request_timeout}s")
|
|
52
|
+
|
|
53
|
+
console.print(table)
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"""Lip-sync and talking-photo generation commands."""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from kling_cli.core.client import get_client
|
|
6
|
+
from kling_cli.core.exceptions import KlingError
|
|
7
|
+
from kling_cli.core.output import print_error, print_json, print_video_result
|
|
8
|
+
|
|
9
|
+
TALKING_PHOTO_MODELS = [
|
|
10
|
+
"kling-v1",
|
|
11
|
+
"kling-v1-6",
|
|
12
|
+
"kling-v2-master",
|
|
13
|
+
"kling-v2-1-master",
|
|
14
|
+
"kling-v2-5-turbo",
|
|
15
|
+
"kling-v2-6",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _validate_duration(
|
|
20
|
+
_ctx: click.Context, _param: click.Parameter, value: int | None
|
|
21
|
+
) -> int | None:
|
|
22
|
+
"""Validate talking-photo duration."""
|
|
23
|
+
if value is None or value in {5, 10}:
|
|
24
|
+
return value
|
|
25
|
+
raise click.BadParameter("Must be 5 or 10.")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@click.command("lip-sync")
|
|
29
|
+
@click.option("--video-id", default=None, help="Kling source video ID.")
|
|
30
|
+
@click.option("--video-url", default=None, help="Kling source video URL.")
|
|
31
|
+
@click.option(
|
|
32
|
+
"--mode",
|
|
33
|
+
required=True,
|
|
34
|
+
type=click.Choice(["audio2video", "text2video"]),
|
|
35
|
+
help="Lip-sync mode.",
|
|
36
|
+
)
|
|
37
|
+
@click.option("--audio-url", default=None, help="Audio URL for audio2video mode.")
|
|
38
|
+
@click.option(
|
|
39
|
+
"--audio-type",
|
|
40
|
+
type=click.Choice(["url", "file"]),
|
|
41
|
+
default=None,
|
|
42
|
+
help="Audio source type.",
|
|
43
|
+
)
|
|
44
|
+
@click.option("--audio-file", default=None, help="Path or content string for audio2video mode.")
|
|
45
|
+
@click.option("--text", default=None, help="Text for text2video mode.")
|
|
46
|
+
@click.option("--voice-id", default=None, help="Voice ID for text2video mode.")
|
|
47
|
+
@click.option(
|
|
48
|
+
"--voice-language",
|
|
49
|
+
type=click.Choice(["zh", "en"]),
|
|
50
|
+
default=None,
|
|
51
|
+
help="Voice language for text2video mode.",
|
|
52
|
+
)
|
|
53
|
+
@click.option(
|
|
54
|
+
"--voice-speed",
|
|
55
|
+
default=None,
|
|
56
|
+
type=float,
|
|
57
|
+
help="Voice speed multiplier for text2video mode (1.0 = normal).",
|
|
58
|
+
)
|
|
59
|
+
@click.option("--callback-url", default=None, help="Webhook callback URL.")
|
|
60
|
+
@click.option(
|
|
61
|
+
"--async",
|
|
62
|
+
"async_mode",
|
|
63
|
+
is_flag=True,
|
|
64
|
+
default=False,
|
|
65
|
+
help="Submit asynchronously; returns a task_id to poll instead of waiting.",
|
|
66
|
+
)
|
|
67
|
+
@click.option("--json", "output_json", is_flag=True, help="Output raw JSON.")
|
|
68
|
+
@click.pass_context
|
|
69
|
+
def lip_sync(
|
|
70
|
+
ctx: click.Context,
|
|
71
|
+
video_id: str | None,
|
|
72
|
+
video_url: str | None,
|
|
73
|
+
mode: str,
|
|
74
|
+
audio_url: str | None,
|
|
75
|
+
audio_type: str | None,
|
|
76
|
+
audio_file: str | None,
|
|
77
|
+
text: str | None,
|
|
78
|
+
voice_id: str | None,
|
|
79
|
+
voice_language: str | None,
|
|
80
|
+
voice_speed: float | None,
|
|
81
|
+
callback_url: str | None,
|
|
82
|
+
async_mode: bool,
|
|
83
|
+
output_json: bool,
|
|
84
|
+
) -> None:
|
|
85
|
+
"""Generate a lip-sync video."""
|
|
86
|
+
client = get_client(ctx.obj.get("token"))
|
|
87
|
+
try:
|
|
88
|
+
result = client.lip_sync(
|
|
89
|
+
video_id=video_id,
|
|
90
|
+
video_url=video_url,
|
|
91
|
+
mode=mode,
|
|
92
|
+
audio_url=audio_url,
|
|
93
|
+
audio_type=audio_type,
|
|
94
|
+
audio_file=audio_file,
|
|
95
|
+
text=text,
|
|
96
|
+
voice_id=voice_id,
|
|
97
|
+
voice_language=voice_language,
|
|
98
|
+
voice_speed=voice_speed,
|
|
99
|
+
callback_url=callback_url,
|
|
100
|
+
**({"async": True} if async_mode else {}),
|
|
101
|
+
)
|
|
102
|
+
if output_json:
|
|
103
|
+
print_json(result)
|
|
104
|
+
else:
|
|
105
|
+
print_video_result(result)
|
|
106
|
+
except KlingError as e:
|
|
107
|
+
print_error(e.message)
|
|
108
|
+
raise SystemExit(1) from e
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@click.command("talking-photo")
|
|
112
|
+
@click.option("--image-url", required=True, help="Source image URL.")
|
|
113
|
+
@click.option("--audio-url", required=True, help="Source audio URL.")
|
|
114
|
+
@click.option("--prompt", default=None, help="Prompt for talking photo generation.")
|
|
115
|
+
@click.option(
|
|
116
|
+
"-m",
|
|
117
|
+
"--model",
|
|
118
|
+
type=click.Choice(TALKING_PHOTO_MODELS),
|
|
119
|
+
default=None,
|
|
120
|
+
help="Model to use for generation.",
|
|
121
|
+
)
|
|
122
|
+
@click.option(
|
|
123
|
+
"--duration",
|
|
124
|
+
type=int,
|
|
125
|
+
callback=_validate_duration,
|
|
126
|
+
default=None,
|
|
127
|
+
help="Video duration in seconds.",
|
|
128
|
+
)
|
|
129
|
+
@click.option(
|
|
130
|
+
"--mode",
|
|
131
|
+
type=click.Choice(["std", "pro"]),
|
|
132
|
+
default=None,
|
|
133
|
+
help="Generation mode: std or pro.",
|
|
134
|
+
)
|
|
135
|
+
@click.option("--callback-url", default=None, help="Webhook callback URL.")
|
|
136
|
+
@click.option(
|
|
137
|
+
"--async",
|
|
138
|
+
"async_mode",
|
|
139
|
+
is_flag=True,
|
|
140
|
+
default=False,
|
|
141
|
+
help="Submit asynchronously; returns a task_id to poll instead of waiting.",
|
|
142
|
+
)
|
|
143
|
+
@click.option("--json", "output_json", is_flag=True, help="Output raw JSON.")
|
|
144
|
+
@click.pass_context
|
|
145
|
+
def talking_photo(
|
|
146
|
+
ctx: click.Context,
|
|
147
|
+
image_url: str,
|
|
148
|
+
audio_url: str,
|
|
149
|
+
prompt: str | None,
|
|
150
|
+
model: str | None,
|
|
151
|
+
duration: int | None,
|
|
152
|
+
mode: str | None,
|
|
153
|
+
callback_url: str | None,
|
|
154
|
+
async_mode: bool,
|
|
155
|
+
output_json: bool,
|
|
156
|
+
) -> None:
|
|
157
|
+
"""Generate a talking-photo video."""
|
|
158
|
+
client = get_client(ctx.obj.get("token"))
|
|
159
|
+
try:
|
|
160
|
+
result = client.talking_photo(
|
|
161
|
+
image_url=image_url,
|
|
162
|
+
audio_url=audio_url,
|
|
163
|
+
prompt=prompt,
|
|
164
|
+
model=model,
|
|
165
|
+
duration=duration,
|
|
166
|
+
mode=mode,
|
|
167
|
+
callback_url=callback_url,
|
|
168
|
+
**({"async": True} if async_mode else {}),
|
|
169
|
+
)
|
|
170
|
+
if output_json:
|
|
171
|
+
print_json(result)
|
|
172
|
+
else:
|
|
173
|
+
print_video_result(result)
|
|
174
|
+
except KlingError as e:
|
|
175
|
+
print_error(e.message)
|
|
176
|
+
raise SystemExit(1) from e
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""Motion generation command."""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from kling_cli.core.client import get_client
|
|
6
|
+
from kling_cli.core.exceptions import KlingError
|
|
7
|
+
from kling_cli.core.output import (
|
|
8
|
+
DEFAULT_MODE,
|
|
9
|
+
KLING_MODES,
|
|
10
|
+
print_error,
|
|
11
|
+
print_json,
|
|
12
|
+
print_video_result,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@click.command()
|
|
17
|
+
@click.option(
|
|
18
|
+
"--image-url",
|
|
19
|
+
required=True,
|
|
20
|
+
help="Reference image URL. Characters, backgrounds and elements in the generated video "
|
|
21
|
+
"are based on this image.",
|
|
22
|
+
)
|
|
23
|
+
@click.option(
|
|
24
|
+
"--video-url",
|
|
25
|
+
required=True,
|
|
26
|
+
help="Reference video URL. Character movements in the generated video are consistent "
|
|
27
|
+
"with those in this reference video.",
|
|
28
|
+
)
|
|
29
|
+
@click.option(
|
|
30
|
+
"--character-orientation",
|
|
31
|
+
default=None,
|
|
32
|
+
type=click.Choice(["image", "video"]),
|
|
33
|
+
help="Orientation of characters in the generated video: consistent with the image or video.",
|
|
34
|
+
)
|
|
35
|
+
@click.option(
|
|
36
|
+
"--mode",
|
|
37
|
+
type=click.Choice(KLING_MODES),
|
|
38
|
+
default=DEFAULT_MODE,
|
|
39
|
+
help="Generation mode: std (High performance) or pro (High quality).",
|
|
40
|
+
)
|
|
41
|
+
@click.option(
|
|
42
|
+
"--keep-original-sound/--no-keep-original-sound",
|
|
43
|
+
default=True,
|
|
44
|
+
help="Whether to keep the original sound from the reference video (default: keep).",
|
|
45
|
+
)
|
|
46
|
+
@click.option("--prompt", default=None, help="Text prompt (positive and/or negative descriptions).")
|
|
47
|
+
@click.option("--callback-url", default=None, help="Webhook callback URL.")
|
|
48
|
+
@click.option(
|
|
49
|
+
"--async",
|
|
50
|
+
"async_mode",
|
|
51
|
+
is_flag=True,
|
|
52
|
+
default=False,
|
|
53
|
+
help="Submit asynchronously; returns a task_id to poll instead of waiting.",
|
|
54
|
+
)
|
|
55
|
+
@click.option(
|
|
56
|
+
"--timeout", default=None, type=int, help="Timeout in seconds for the API to return data."
|
|
57
|
+
)
|
|
58
|
+
@click.option("--json", "output_json", is_flag=True, help="Output raw JSON.")
|
|
59
|
+
@click.pass_context
|
|
60
|
+
def motion(
|
|
61
|
+
ctx: click.Context,
|
|
62
|
+
image_url: str,
|
|
63
|
+
video_url: str,
|
|
64
|
+
character_orientation: str | None,
|
|
65
|
+
mode: str,
|
|
66
|
+
keep_original_sound: bool,
|
|
67
|
+
prompt: str | None,
|
|
68
|
+
callback_url: str | None,
|
|
69
|
+
async_mode: bool,
|
|
70
|
+
timeout: int | None,
|
|
71
|
+
output_json: bool,
|
|
72
|
+
) -> None:
|
|
73
|
+
"""Generate a motion video from a reference image and reference video.
|
|
74
|
+
|
|
75
|
+
The generated video's characters and elements are based on the reference image,
|
|
76
|
+
while the movements are based on the reference video.
|
|
77
|
+
|
|
78
|
+
Examples:
|
|
79
|
+
|
|
80
|
+
kling motion --image-url https://example.com/img.jpg --video-url https://example.com/ref.mp4
|
|
81
|
+
|
|
82
|
+
kling motion --image-url img.jpg --video-url ref.mp4 --prompt "A dancer performing"
|
|
83
|
+
"""
|
|
84
|
+
client = get_client(ctx.obj.get("token"))
|
|
85
|
+
try:
|
|
86
|
+
result = client.generate_motion(
|
|
87
|
+
image_url=image_url,
|
|
88
|
+
video_url=video_url,
|
|
89
|
+
character_orientation=character_orientation,
|
|
90
|
+
mode=mode,
|
|
91
|
+
keep_original_sound="yes" if keep_original_sound else "no",
|
|
92
|
+
prompt=prompt,
|
|
93
|
+
callback_url=callback_url,
|
|
94
|
+
**({"async": True} if async_mode else {}),
|
|
95
|
+
timeout=timeout,
|
|
96
|
+
)
|
|
97
|
+
if output_json:
|
|
98
|
+
print_json(result)
|
|
99
|
+
else:
|
|
100
|
+
print_video_result(result)
|
|
101
|
+
except KlingError as e:
|
|
102
|
+
print_error(e.message)
|
|
103
|
+
raise SystemExit(1) from e
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""Task management commands."""
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
|
|
7
|
+
from kling_cli.core.client import get_client
|
|
8
|
+
from kling_cli.core.exceptions import KlingError
|
|
9
|
+
from kling_cli.core.output import print_error, print_json, print_success, print_task_result
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@click.command()
|
|
13
|
+
@click.argument("task_id")
|
|
14
|
+
@click.option("--json", "output_json", is_flag=True, help="Output raw JSON.")
|
|
15
|
+
@click.pass_context
|
|
16
|
+
def task(
|
|
17
|
+
ctx: click.Context,
|
|
18
|
+
task_id: str,
|
|
19
|
+
output_json: bool,
|
|
20
|
+
) -> None:
|
|
21
|
+
"""Query a single task status.
|
|
22
|
+
|
|
23
|
+
TASK_ID is the task ID returned from generate commands.
|
|
24
|
+
|
|
25
|
+
Examples:
|
|
26
|
+
|
|
27
|
+
kling task abc123-def456
|
|
28
|
+
"""
|
|
29
|
+
client = get_client(ctx.obj.get("token"))
|
|
30
|
+
try:
|
|
31
|
+
result = client.query_task(id=task_id, action="retrieve")
|
|
32
|
+
if output_json:
|
|
33
|
+
print_json(result)
|
|
34
|
+
else:
|
|
35
|
+
print_task_result(result)
|
|
36
|
+
except KlingError as e:
|
|
37
|
+
print_error(e.message)
|
|
38
|
+
raise SystemExit(1) from e
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@click.command("tasks")
|
|
42
|
+
@click.argument("task_ids", nargs=-1, required=True)
|
|
43
|
+
@click.option("--json", "output_json", is_flag=True, help="Output raw JSON.")
|
|
44
|
+
@click.pass_context
|
|
45
|
+
def tasks_batch(
|
|
46
|
+
ctx: click.Context,
|
|
47
|
+
task_ids: tuple[str, ...],
|
|
48
|
+
output_json: bool,
|
|
49
|
+
) -> None:
|
|
50
|
+
"""Query multiple tasks at once.
|
|
51
|
+
|
|
52
|
+
TASK_IDS are space-separated task IDs.
|
|
53
|
+
|
|
54
|
+
Examples:
|
|
55
|
+
|
|
56
|
+
kling tasks abc123 def456 ghi789
|
|
57
|
+
"""
|
|
58
|
+
client = get_client(ctx.obj.get("token"))
|
|
59
|
+
try:
|
|
60
|
+
result = client.query_task(ids=list(task_ids), action="retrieve_batch")
|
|
61
|
+
if output_json:
|
|
62
|
+
print_json(result)
|
|
63
|
+
else:
|
|
64
|
+
print_task_result(result)
|
|
65
|
+
except KlingError as e:
|
|
66
|
+
print_error(e.message)
|
|
67
|
+
raise SystemExit(1) from e
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@click.command()
|
|
71
|
+
@click.argument("task_id")
|
|
72
|
+
@click.option(
|
|
73
|
+
"--interval",
|
|
74
|
+
type=int,
|
|
75
|
+
default=5,
|
|
76
|
+
help="Polling interval in seconds (default: 5).",
|
|
77
|
+
)
|
|
78
|
+
@click.option(
|
|
79
|
+
"--timeout",
|
|
80
|
+
"max_timeout",
|
|
81
|
+
type=int,
|
|
82
|
+
default=600,
|
|
83
|
+
help="Maximum wait time in seconds (default: 600).",
|
|
84
|
+
)
|
|
85
|
+
@click.option("--json", "output_json", is_flag=True, help="Output raw JSON.")
|
|
86
|
+
@click.pass_context
|
|
87
|
+
def wait(
|
|
88
|
+
ctx: click.Context,
|
|
89
|
+
task_id: str,
|
|
90
|
+
interval: int,
|
|
91
|
+
max_timeout: int,
|
|
92
|
+
output_json: bool,
|
|
93
|
+
) -> None:
|
|
94
|
+
"""Wait for a task to complete, polling periodically.
|
|
95
|
+
|
|
96
|
+
TASK_ID is the task ID to monitor.
|
|
97
|
+
|
|
98
|
+
Examples:
|
|
99
|
+
|
|
100
|
+
kling wait abc123
|
|
101
|
+
|
|
102
|
+
kling wait abc123 --interval 10 --timeout 300
|
|
103
|
+
"""
|
|
104
|
+
client = get_client(ctx.obj.get("token"))
|
|
105
|
+
elapsed = 0
|
|
106
|
+
|
|
107
|
+
try:
|
|
108
|
+
while elapsed < max_timeout:
|
|
109
|
+
result = client.query_task(id=task_id, action="retrieve")
|
|
110
|
+
data = result.get("data", {})
|
|
111
|
+
|
|
112
|
+
# Check completion - handle both list and dict responses
|
|
113
|
+
if isinstance(data, list) and data:
|
|
114
|
+
item = data[0]
|
|
115
|
+
elif isinstance(data, dict):
|
|
116
|
+
item = data
|
|
117
|
+
else:
|
|
118
|
+
item = {}
|
|
119
|
+
|
|
120
|
+
state = item.get("state", item.get("status", ""))
|
|
121
|
+
if state in ("succeeded", "completed", "complete", "succeed", "failed", "error"):
|
|
122
|
+
if output_json:
|
|
123
|
+
print_json(result)
|
|
124
|
+
else:
|
|
125
|
+
if state in ("failed", "error"):
|
|
126
|
+
print_error(f"Task {task_id} failed.")
|
|
127
|
+
else:
|
|
128
|
+
print_success(f"Task {task_id} completed!")
|
|
129
|
+
print_task_result(result)
|
|
130
|
+
return
|
|
131
|
+
|
|
132
|
+
if not output_json:
|
|
133
|
+
click.echo(f"Status: {state or 'pending'} (waited {elapsed}s)...", err=True)
|
|
134
|
+
|
|
135
|
+
time.sleep(interval)
|
|
136
|
+
elapsed += interval
|
|
137
|
+
|
|
138
|
+
print_error(f"Timeout: task {task_id} did not complete within {max_timeout}s")
|
|
139
|
+
raise SystemExit(1)
|
|
140
|
+
except KlingError as e:
|
|
141
|
+
print_error(e.message)
|
|
142
|
+
raise SystemExit(1) from e
|