5151)
5252from comfy_api_nodes .apis .kling_api import (
5353 ImageToVideoWithAudioRequest ,
54+ MotionControlRequest ,
5455 OmniImageParamImage ,
5556 OmniParamImage ,
5657 OmniParamVideo ,
@@ -2163,6 +2164,91 @@ async def execute(
21632164 return IO .NodeOutput (await download_url_to_video_output (final_response .data .task_result .videos [0 ].url ))
21642165
21652166
2167+ class MotionControl (IO .ComfyNode ):
2168+
2169+ @classmethod
2170+ def define_schema (cls ) -> IO .Schema :
2171+ return IO .Schema (
2172+ node_id = "KlingMotionControl" ,
2173+ display_name = "Kling Motion Control" ,
2174+ category = "api node/video/Kling" ,
2175+ inputs = [
2176+ IO .String .Input ("prompt" , multiline = True ),
2177+ IO .Image .Input ("reference_image" ),
2178+ IO .Video .Input (
2179+ "reference_video" ,
2180+ tooltip = "Motion reference video used to drive movement/expression.\n "
2181+ "Duration limits depend on character_orientation:\n "
2182+ " - image: 3–10s (max 10s)\n "
2183+ " - video: 3–30s (max 30s)" ,
2184+ ),
2185+ IO .Boolean .Input ("keep_original_sound" , default = True ),
2186+ IO .Combo .Input (
2187+ "character_orientation" ,
2188+ options = ["video" , "image" ],
2189+ tooltip = "Controls where the character's facing/orientation comes from.\n "
2190+ "video: movements, expressions, camera moves, and orientation "
2191+ "follow the motion reference video (other details via prompt).\n "
2192+ "image: movements and expressions still follow the motion reference video, "
2193+ "but the character orientation matches the reference image (camera/other details via prompt)." ,
2194+ ),
2195+ IO .Combo .Input ("mode" , options = ["pro" , "std" ]),
2196+ ],
2197+ outputs = [
2198+ IO .Video .Output (),
2199+ ],
2200+ hidden = [
2201+ IO .Hidden .auth_token_comfy_org ,
2202+ IO .Hidden .api_key_comfy_org ,
2203+ IO .Hidden .unique_id ,
2204+ ],
2205+ is_api_node = True ,
2206+ )
2207+
2208+ @classmethod
2209+ async def execute (
2210+ cls ,
2211+ prompt : str ,
2212+ reference_image : Input .Image ,
2213+ reference_video : Input .Video ,
2214+ keep_original_sound : bool ,
2215+ character_orientation : str ,
2216+ mode : str ,
2217+ ) -> IO .NodeOutput :
2218+ validate_string (prompt , max_length = 2500 )
2219+ validate_image_dimensions (reference_image , min_width = 340 , min_height = 340 )
2220+ validate_image_aspect_ratio (reference_image , (1 , 2.5 ), (2.5 , 1 ))
2221+ if character_orientation == "image" :
2222+ validate_video_duration (reference_video , min_duration = 3 , max_duration = 10 )
2223+ else :
2224+ validate_video_duration (reference_video , min_duration = 3 , max_duration = 30 )
2225+ validate_video_dimensions (reference_video , min_width = 340 , min_height = 340 , max_width = 3850 , max_height = 3850 )
2226+ response = await sync_op (
2227+ cls ,
2228+ ApiEndpoint (path = "/proxy/kling/v1/videos/motion-control" , method = "POST" ),
2229+ response_model = TaskStatusResponse ,
2230+ data = MotionControlRequest (
2231+ prompt = prompt ,
2232+ image_url = (await upload_images_to_comfyapi (cls , reference_image ))[0 ],
2233+ video_url = await upload_video_to_comfyapi (cls , reference_video ),
2234+ keep_original_sound = "yes" if keep_original_sound else "no" ,
2235+ character_orientation = character_orientation ,
2236+ mode = mode ,
2237+ ),
2238+ )
2239+ if response .code :
2240+ raise RuntimeError (
2241+ f"Kling request failed. Code: { response .code } , Message: { response .message } , Data: { response .data } "
2242+ )
2243+ final_response = await poll_op (
2244+ cls ,
2245+ ApiEndpoint (path = f"/proxy/kling/v1/videos/motion-control/{ response .data .task_id } " ),
2246+ response_model = TaskStatusResponse ,
2247+ status_extractor = lambda r : (r .data .task_status if r .data else None ),
2248+ )
2249+ return IO .NodeOutput (await download_url_to_video_output (final_response .data .task_result .videos [0 ].url ))
2250+
2251+
21662252class KlingExtension (ComfyExtension ):
21672253 @override
21682254 async def get_node_list (self ) -> list [type [IO .ComfyNode ]]:
@@ -2188,6 +2274,7 @@ async def get_node_list(self) -> list[type[IO.ComfyNode]]:
21882274 OmniProImageNode ,
21892275 TextToVideoWithAudio ,
21902276 ImageToVideoWithAudio ,
2277+ MotionControl ,
21912278 ]
21922279
21932280
0 commit comments