1
+ import http .client
2
+ import json
3
+ import base64
4
+ import time
5
+
6
+ class KlingImageToVideo :
7
+ def __init__ (self , api_token , api_url ):
8
+ """初始化 Kling 图像生成视频转换器
9
+
10
+ 参数:
11
+ api_token: API 密钥
12
+ api_url: API 节点地址
13
+ """
14
+ self .api_url = api_url
15
+ self .api_token = api_token
16
+ # 初始化 HTTP 连接
17
+ self .conn = http .client .HTTPSConnection (self .api_url )
18
+ self .endpoint = "/kling/v1/videos/image2video"
19
+ # 设置请求头
20
+ self .headers = {
21
+ 'Authorization' : f'Bearer { self .api_token } ' ,
22
+ 'Content-Type' : 'application/json'
23
+ }
24
+
25
+ @staticmethod
26
+ def get_image_base64 (image_path ):
27
+ """将图片转换为 base64 编码形式
28
+
29
+ 参数:
30
+ image_path: 图片路径
31
+ 返回:
32
+ base64 编码后的图片字符串
33
+ """
34
+ with open (image_path , "rb" ) as image_file :
35
+ return base64 .b64encode (image_file .read ()).decode ("utf-8" )
36
+
37
+ def _kling_generate_video (self , model_name , image , prompt ,
38
+ image_tail = None , negative_prompt = "" ,
39
+ cfg_scale = 0.5 , mode = "std" , duration = "5" ,
40
+ camera_control = None , static_mask = None , dynamic_masks = None ,
41
+ callback_url = "" , external_task_id = "" ):
42
+ """使用 kling 进行图像转视频
43
+
44
+ 参数:
45
+ model_name: str, 模型版本 kling-v1, kling-v1-5, kling-v1-6
46
+ image: str, 起始图片,base64编码或URL
47
+ prompt: str, 正向提示词
48
+ image_tail: str, 结束图片,base64编码或URL
49
+ negative_prompt: str, 负向提示词
50
+ cfg_scale: float, 生成视频的自由度,取值范围:[0, 1]
51
+ mode: str, 生成模式:std(标准模式) 或 pro(专家模式)
52
+ duration: str, 视频时长(秒):5 或 10
53
+ camera_control: dict, 摄像机控制参数
54
+ static_mask: str, 静态区域遮罩,base64编码或URL
55
+ dynamic_masks: list, 动态区域遮罩列表
56
+ callback_url: str, 回调地址
57
+ external_task_id: str, 自定义任务ID
58
+ 返回:
59
+ task_id: 生成任务的 id
60
+ """
61
+ # 构建请求体,请求的核心参数
62
+ payload = {
63
+ "model_name" : model_name ,
64
+ "image" : image ,
65
+ "prompt" : prompt ,
66
+ "negative_prompt" : negative_prompt ,
67
+ "cfg_scale" : cfg_scale ,
68
+ "mode" : mode ,
69
+ "duration" : duration ,
70
+ "callback_url" : callback_url
71
+ }
72
+
73
+ # 如果提供了结束图片
74
+ if image_tail :
75
+ payload ["image_tail" ] = image_tail
76
+
77
+ # 如果提供了摄像机控制参数
78
+ if camera_control :
79
+ payload ["camera_control" ] = camera_control
80
+
81
+ # 如果提供了静态遮罩
82
+ if static_mask :
83
+ payload ["static_mask" ] = static_mask
84
+
85
+ # 如果提供了动态遮罩
86
+ if dynamic_masks :
87
+ payload ["dynamic_masks" ] = dynamic_masks
88
+
89
+ # 如果提供了自定义任务ID
90
+ if external_task_id :
91
+ payload ["external_task_id" ] = external_task_id
92
+
93
+ # 发送 POST 请求,提交视频生成任务
94
+ self .conn .request ("POST" , self .endpoint , json .dumps (payload ), self .headers )
95
+ # 获取响应
96
+ res = self .conn .getresponse ()
97
+ # 读取响应内容并解析为 JSON
98
+ json_data = json .loads (res .read ().decode ("utf-8" ))
99
+
100
+ if 'code' in json_data and json_data ['code' ] == 0 :
101
+ # 成功则返回提交的任务 id
102
+ return json_data ['data' ]['task_id' ]
103
+ else :
104
+ # 失败则返回错误信息
105
+ raise Exception (f"API调用失败:{ json_data ['message' ]} " )
106
+
107
+ def _query_video_result (self , task_id ):
108
+ """使用查询接口获取生成视频结果
109
+
110
+ 参数:
111
+ task_id: 生成任务的 id
112
+ 返回:
113
+ video_url: 视频 url,任务未完成时返回 None
114
+ video_id: 视频 id,任务未完成时返回 None
115
+ """
116
+ # 构建查询路径
117
+ query_path = f"/kling/v1/videos/generations/{ task_id } "
118
+
119
+ # 发送 GET 请求,查询视频生成任务状态
120
+ self .conn .request ("GET" , query_path , None , self .headers )
121
+ # 获取响应
122
+ res = self .conn .getresponse ()
123
+ # 读取响应内容并解析为 JSON
124
+ json_data = json .loads (res .read ().decode ("utf-8" ))
125
+
126
+ # 检查响应是否成功
127
+ if json_data ['code' ] == 0 :
128
+ # 如果任务状态为成功,则返回视频 url 和 id
129
+ if json_data ['data' ]['task_status' ] == "succeed" :
130
+ video_url = json_data ['data' ]['task_result' ]['videos' ][0 ]['url' ]
131
+ video_id = json_data ['data' ]['task_result' ]['videos' ][0 ]['id' ]
132
+ return video_url , video_id
133
+ else :
134
+ return None , None
135
+ else :
136
+ # 如果查询失败,抛出异常
137
+ raise Exception (f"查询失败: { json_data ['message' ]} " )
138
+
139
+ def generate_video (self , model_name , image , prompt ,
140
+ image_tail = None , negative_prompt = "" ,
141
+ cfg_scale = 0.5 , mode = "std" , duration = "5" ,
142
+ camera_control = None , static_mask = None , dynamic_masks = None ,
143
+ callback_url = "" , external_task_id = "" , timeout = 600 ):
144
+ """实现功能,根据图片生成视频并返回结果
145
+
146
+ 参数:
147
+ model_name: str, 模型版本 kling-v1, kling-v1-5, kling-v1-6
148
+ image: str, 起始图片URL或本地文件路径
149
+ prompt: str, 正向提示词
150
+ image_tail: str, 结束图片URL或本地文件路径
151
+ negative_prompt: str, 负向提示词
152
+ cfg_scale: float, 生成视频的自由度,取值范围:[0, 1]
153
+ mode: str, 生成模式:std(标准模式) 或 pro(专家模式)
154
+ duration: str, 视频时长(秒):5 或 10
155
+ camera_control: dict, 摄像机控制参数
156
+ static_mask: str, 静态区域遮罩URL或本地文件路径
157
+ dynamic_masks: list, 动态区域遮罩列表
158
+ callback_url: str, 回调地址
159
+ external_task_id: str, 自定义任务ID
160
+ timeout: int, 超时时间(秒)
161
+ 返回:
162
+ video_url: 视频URL
163
+ video_id: 视频ID
164
+ """
165
+ # 处理起始图片输入
166
+ if image .startswith (('http://' , 'https://' , 'ftp://' )):
167
+ # 如果是URL,直接使用
168
+ image_data = image
169
+ else :
170
+ # 否则当作本地文件路径处理,转换为base64
171
+ try :
172
+ image_data = KlingImageToVideo .get_image_base64 (image )
173
+ except Exception as e :
174
+ raise ValueError (f"无法读取起始图像文件: { str (e )} " )
175
+
176
+ # 处理结束图片输入(如果有)
177
+ image_tail_data = None
178
+ if image_tail :
179
+ if image_tail .startswith (('http://' , 'https://' , 'ftp://' )):
180
+ # 如果是URL,直接使用
181
+ image_tail_data = image_tail
182
+ else :
183
+ # 否则当作本地文件路径处理,转换为base64
184
+ try :
185
+ image_tail_data = KlingImageToVideo .get_image_base64 (image_tail )
186
+ except Exception as e :
187
+ raise ValueError (f"无法读取结束图像文件: { str (e )} " )
188
+
189
+ # 处理静态遮罩(如果有)
190
+ static_mask_data = None
191
+ if static_mask :
192
+ if static_mask .startswith (('http://' , 'https://' , 'ftp://' )):
193
+ # 如果是URL,直接使用
194
+ static_mask_data = static_mask
195
+ else :
196
+ # 否则当作本地文件路径处理,转换为base64
197
+ try :
198
+ static_mask_data = KlingImageToVideo .get_image_base64 (static_mask )
199
+ except Exception as e :
200
+ raise ValueError (f"无法读取静态遮罩文件: { str (e )} " )
201
+
202
+ # 处理动态遮罩(如果有)
203
+ if dynamic_masks :
204
+ processed_masks = []
205
+ for mask_item in dynamic_masks :
206
+ processed_item = mask_item .copy ()
207
+
208
+ # 处理遮罩图像
209
+ if mask_item .get ('mask' ):
210
+ mask_image = mask_item ['mask' ]
211
+ if mask_image .startswith (('http://' , 'https://' , 'ftp://' )):
212
+ # 如果是URL,直接使用
213
+ processed_item ['mask' ] = mask_image
214
+ else :
215
+ # 否则当作本地文件路径处理,转换为base64
216
+ try :
217
+ processed_item ['mask' ] = KlingImageToVideo .get_image_base64 (mask_image )
218
+ except Exception as e :
219
+ raise ValueError (f"无法读取动态遮罩文件: { str (e )} " )
220
+
221
+ processed_masks .append (processed_item )
222
+
223
+ dynamic_masks = processed_masks
224
+
225
+ # 调用生成视频 API 提交任务
226
+ task_id = self ._kling_generate_video (
227
+ model_name , image_data , prompt ,
228
+ image_tail_data , negative_prompt ,
229
+ cfg_scale , mode , duration ,
230
+ camera_control , static_mask_data , dynamic_masks ,
231
+ callback_url , external_task_id
232
+ )
233
+
234
+ start_time = time .time ()
235
+
236
+ # 轮询等待生成完成
237
+ while True :
238
+ # 查询任务状态
239
+ video_url , video_id = self ._query_video_result (task_id )
240
+ # 如果任务完成,返回结果
241
+ if video_url is not None :
242
+ return video_url , video_id
243
+ # 如果超时,返回 None
244
+ if time .time () - start_time > timeout :
245
+ print (f"请求达到 { timeout } 秒超时" )
246
+ return None , None
247
+ # 轮询间隔 3 秒
248
+ time .sleep (3 )
249
+ print (f"等待视频生成,{ int (time .time () - start_time )} 秒" , flush = True )
250
+
251
+
252
+ # 使用示例
253
+ if __name__ == "__main__" :
254
+ API_URL = "www.dmxapi.cn" # API 节点地址
255
+ DMX_API_TOKEN = "sk-XXXXXXXXXXXXXX" # API 密钥
256
+
257
+ # 创建图像转视频生成器实例
258
+ kling_image_to_video = KlingImageToVideo (api_token = DMX_API_TOKEN , api_url = API_URL )
259
+
260
+ # 示例摄像机控制
261
+ # camera_control = {
262
+ # "type": "forward_up", # 预定义运镜类型 可选 “simple”, “down_back”, “forward_up”, “right_turn_forward”, “left_turn_forward”
263
+ # # 如果使用 simple 类型,需要配置以下参数(六选一)
264
+ # # "config": {
265
+ # # "horizontal": 0, # 水平运镜 [-10, 10]
266
+ # # "vertical": 0, # 垂直运镜 [-10, 10]
267
+ # # "pan": 5, # 水平摇镜 [-10, 10]
268
+ # # "tilt": 0, # 垂直摇镜 [-10, 10]
269
+ # # "roll": 0, # 旋转运镜 [-10, 10]
270
+ # # "zoom": 0 # 变焦 [-10, 10]
271
+ # # }
272
+ # }
273
+
274
+ # 生成视频
275
+ video_url , video_id = kling_image_to_video .generate_video (
276
+ model_name = "kling-v1-6" , # [必选] 模型版本 kling-v1, kling-v1-5, kling-v1-6
277
+ image = "/Users/dmxapi/Desktop/dmx.png" , # [必选] 起始图片,可以是 URL 或 本地文件 路径
278
+ prompt = "生成图中的几只动物走路的场景" , # [必选] 正向提示词
279
+ # image_tail="end.jpg", # 结束图片,可以是 URL 或 本地文件 路径
280
+ # negative_prompt="模糊, 扭曲", # 负向提示词
281
+ # cfg_scale=0.5, # 生成视频的自由度,取值范围:[0, 1]
282
+ # mode="std", # 生成模式:std(标准模式) 或 pro(专家模式)
283
+ # duration="5", # 视频时长(秒):5 或 10
284
+ # camera_control=camera_control, # 摄像机控制参数
285
+ # static_mask="mask.png", # 静态区域遮罩,可以是URL或本地文件路径
286
+ # dynamic_masks=[{ # 动态区域遮罩列表
287
+ # "mask": "mask.png", # 动态区域遮罩,可以是URL或本地文件路径
288
+ # "trajectories": [
289
+ # {"x": 100, "y": 100}, # 起始点
290
+ # {"x": 150, "y": 200}, # 中间点
291
+ # {"x": 200, "y": 300} # 结束点
292
+ # ]
293
+ # }],
294
+ # callback_url="", # 回调地址
295
+ # external_task_id="", # 自定义任务ID
296
+ # timeout=600 # 等待超时时间(秒)
297
+ )
298
+
299
+ print ("生成的视频URL:" , video_url )
300
+ print ("生成的视频ID:" , video_id )
0 commit comments