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 ADDED
@@ -0,0 +1 @@
1
+ """Kling CLI - AI Kling Video Generation via AceDataCloud API."""
kling_cli/__main__.py ADDED
@@ -0,0 +1,5 @@
1
+ """Allow running as python -m kling_cli."""
2
+
3
+ from kling_cli.main import cli
4
+
5
+ cli()
@@ -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