import json from pathlib import Path ROOT = Path(__file__).resolve().parent NODE_PREFIX = "Comfyui-Kling-Wrapper" CLIENT_TYPE = "COMFYUI_KLING_WRAPPER_API_CLIENT" ELEMENT_TYPE = "COMFYUI_KLING_WRAPPER_ELEMENT" ELEMENT_LIST_TYPE = "COMFYUI_KLING_WRAPPER_ELEMENT_LIST" LIPSYNC_INPUT_TYPE = "COMFYUI_KLING_WRAPPER_LIPSYNC_INPUT" def input_slot(name, type_name, link=None, shape=7): slot = {"name": name, "type": type_name, "link": link} if type_name in {"IMAGE", "MASK"}: slot["shape"] = shape return slot def output_slot(name, type_name, links=None, slot_index=None): slot = {"name": name, "type": type_name, "links": links} if slot_index is not None: slot["slot_index"] = slot_index return slot def node(node_id, type_name, pos, size, order, widgets_values, inputs=None, outputs=None): return { "id": node_id, "type": type_name, "pos": pos, "size": size, "flags": {}, "order": order, "mode": 0, "inputs": inputs or [], "outputs": outputs or [], "properties": {"Node name for S&R": type_name}, "widgets_values": widgets_values, } def workflow(nodes, links): return { "id": "Comfyui-Kling-Wrapper-example", "revision": 0, "last_node_id": max(node["id"] for node in nodes), "last_link_id": max((link[0] for link in links), default=0), "nodes": nodes, "links": links, "groups": [], "config": {}, "extra": { "ds": { "scale": 0.9, "offset": [0, 0], }, "frontendVersion": "1.42.8", }, "version": 0.4, } def strip_client_dependency(data): data = json.loads(json.dumps(data)) client_node_type = f"{NODE_PREFIX} Client" removed_link_ids = set() removed_node_ids = set() input_index_maps = {} cleaned_nodes = [] for workflow_node in data["nodes"]: if workflow_node.get("type") == client_node_type: removed_node_ids.add(workflow_node["id"]) continue cleaned_inputs = [] input_index_maps[workflow_node["id"]] = {} for old_index, node_input in enumerate(workflow_node.get("inputs", [])): if node_input.get("type") == CLIENT_TYPE or node_input.get("name") == "client": if node_input.get("link") is not None: removed_link_ids.add(node_input["link"]) continue input_index_maps[workflow_node["id"]][old_index] = len(cleaned_inputs) cleaned_inputs.append(node_input) workflow_node["inputs"] = cleaned_inputs cleaned_nodes.append(workflow_node) cleaned_links = [] for workflow_link in data.get("links", []): link_id, src_node, _, dst_node, _, link_type = workflow_link if link_type == CLIENT_TYPE or src_node in removed_node_ids or dst_node in removed_node_ids: removed_link_ids.add(link_id) continue cleaned_link = list(workflow_link) destination_map = input_index_maps.get(dst_node, {}) cleaned_link[4] = destination_map.get(cleaned_link[4], cleaned_link[4]) cleaned_links.append(cleaned_link) for workflow_node in cleaned_nodes: for node_input in workflow_node.get("inputs", []): if node_input.get("link") in removed_link_ids: node_input["link"] = None for node_output in workflow_node.get("outputs", []): links = node_output.get("links") if isinstance(links, list): links = [link_id for link_id in links if link_id not in removed_link_ids] node_output["links"] = links or None data["nodes"] = cleaned_nodes data["links"] = cleaned_links data["last_node_id"] = max((node["id"] for node in cleaned_nodes), default=0) data["last_link_id"] = max((link[0] for link in cleaned_links), default=0) return data def client_node(node_id=1, order=0, area="china"): type_name = f"{NODE_PREFIX} Client" return node( node_id, type_name, [40, 60], [320, 210], order, ["", "", 1, 30, area], outputs=[output_slot("client", CLIENT_TYPE, slot_index=0)], ) def load_image_node(node_id, filename, pos, order): return node( node_id, "LoadImage", pos, [290, 340], order, [filename, "image"], outputs=[ output_slot("IMAGE", "IMAGE", slot_index=0), output_slot("MASK", "MASK", slot_index=1), ], ) def load_video_node(node_id, filename, pos, order): return node( node_id, "LoadVideo", pos, [320, 120], order, [filename], outputs=[output_slot("VIDEO", "VIDEO", slot_index=0)], ) def load_audio_node(node_id, filename, pos, order): return node( node_id, "LoadAudio", pos, [320, 120], order, [filename], outputs=[output_slot("AUDIO", "AUDIO", slot_index=0)], ) def save_image_node(node_id, filename_prefix, pos, order, link_id): return node( node_id, "SaveImage", pos, [320, 100], order, [filename_prefix], inputs=[input_slot("images", "IMAGE", link_id)], ) def preview_video_node(node_id, pos, order, link_id): type_name = f"{NODE_PREFIX} Preview Video" return node( node_id, type_name, pos, [430, 820], order, [], inputs=[input_slot("video_url", "STRING", link_id)], outputs=[output_slot("file_path", "STRING", slot_index=0)], ) def preview_audio_node(node_id, pos, order, link_id): type_name = f"{NODE_PREFIX} Preview Audio" return node( node_id, type_name, pos, [360, 180], order, [NODE_PREFIX + "-audio", True], inputs=[input_slot("audio_url", "STRING", link_id)], outputs=[ output_slot("audio", "AUDIO", slot_index=0), output_slot("file_path", "STRING", slot_index=1), ], ) def image_generator_node(node_id, pos, order): type_name = f"{NODE_PREFIX} Image Generator" return node( node_id, type_name, pos, [430, 370], order, [ "kling-v3", "A fashion editorial portrait of a young woman in soft morning light, realistic skin texture, elegant framing, filmic color grading.", "", "None", 0.5, 0.45, 1, "16:9", "1k", ], inputs=[input_slot("client", CLIENT_TYPE)], outputs=[output_slot("image", "IMAGE", slot_index=0)], ) def text2video_node(node_id, pos, order, model, prompt, mode="pro", aspect_ratio="16:9", duration="5", sound="off", voice_preset="None", shot_type="single", reference_video=""): type_name = f"{NODE_PREFIX} Text2Video" return node( node_id, type_name, pos, [430, 520], order, [ model, prompt, "", 0.5, mode, aspect_ratio, duration, "None", "horizontal", 0.5, sound, voice_preset, shot_type, reference_video, ], inputs=[input_slot("client", CLIENT_TYPE)], outputs=[ output_slot("url", "STRING", slot_index=0), output_slot("video_id", "STRING", slot_index=1), ], ) def image2video_node( node_id, pos, order, model, prompt="", mode="pro", duration="5", shot_type="single", reference_video="", sound="off", voice_preset="None", ): type_name = f"{NODE_PREFIX} Image2Video" return node( node_id, type_name, pos, [430, 520], order, [ model, prompt, "", 0.5, mode, duration, "None", "horizontal", 0.5, sound, voice_preset, shot_type, reference_video, ], inputs=[ input_slot("client", CLIENT_TYPE), input_slot("image", "IMAGE"), input_slot("image_tail", "IMAGE", None), input_slot("custom_voice_id", "STRING", None), ], outputs=[ output_slot("url", "STRING", slot_index=0), output_slot("video_id", "STRING", slot_index=1), ], ) def multi_images_to_video_node(node_id, pos, order): type_name = f"{NODE_PREFIX} Multi Images To Video" return node( node_id, type_name, pos, [430, 360], order, [ "kling-v3", "Keep the same person identity across the shot while the subject turns toward camera and smiles naturally.", "", "pro", "5", "16:9", ], inputs=[ input_slot("client", CLIENT_TYPE), input_slot("image_list", "IMAGE"), input_slot("image_tail", "IMAGE", None), ], outputs=[ output_slot("url", "STRING", slot_index=0), output_slot("video_id", "STRING", slot_index=1), ], ) def image_batch_node(node_id, pos, order): return node( node_id, "ImageBatch", pos, [320, 120], order, [], inputs=[ input_slot("image1", "IMAGE"), input_slot("image2", "IMAGE"), ], outputs=[output_slot("IMAGE", "IMAGE", slot_index=0)], ) def virtual_try_on_node(node_id, pos, order): type_name = f"{NODE_PREFIX} Virtual Try On" return node( node_id, type_name, pos, [360, 180], order, ["kolors-virtual-try-on-v1-5"], inputs=[ input_slot("client", CLIENT_TYPE), input_slot("human_image", "IMAGE"), input_slot("cloth_image", "IMAGE"), ], outputs=[output_slot("image", "IMAGE", slot_index=0)], ) def clothing_dupe_node(node_id, pos, order): type_name = f"{NODE_PREFIX} Clothing Dupe" return node( node_id, type_name, pos, [430, 420], order, [ "Lightweight breathable shirt for a clean commercial product showcase.", "720p", "16:9", "无", ], inputs=[ input_slot("source_video", "VIDEO"), input_slot("product_image_1", "IMAGE"), input_slot("product_image_2", "IMAGE", None), input_slot("product_image_3", "IMAGE", None), input_slot("product_image_4", "IMAGE", None), input_slot("product_image_5", "IMAGE", None), input_slot("model_image_1", "IMAGE", None), input_slot("model_image_2", "IMAGE", None), input_slot("model_image_3", "IMAGE", None), input_slot("model_image_4", "IMAGE", None), input_slot("model_image_5", "IMAGE", None), input_slot("bgm", "AUDIO", None), ], outputs=[ output_slot("video_url", "STRING", slot_index=0), output_slot("watermark_video_url", "STRING", slot_index=1), output_slot("task_id", "STRING", slot_index=2), output_slot("result_json", "STRING", slot_index=3), ], ) def image_expander_node(node_id, pos, order): type_name = f"{NODE_PREFIX} Image Expander" return node( node_id, type_name, pos, [360, 260], order, [0.25, 0.0, 0.0, 0.0, "Extend the background naturally with coherent lighting and scene details.", 1], inputs=[ input_slot("client", CLIENT_TYPE), input_slot("image", "IMAGE"), ], outputs=[output_slot("image", "IMAGE", slot_index=0)], ) def video_extender_node(node_id, pos, order): type_name = f"{NODE_PREFIX} Video Extender" return node( node_id, type_name, pos, [360, 180], order, ["", "Continue the shot naturally for a few more moments, keeping the same scene and motion direction."], inputs=[ input_slot("client", CLIENT_TYPE), input_slot("video_id", "STRING"), ], outputs=[ output_slot("url", "STRING", slot_index=0), output_slot("video_id", "STRING", slot_index=1), ], ) def text_to_audio_node(node_id, pos, order): type_name = f"{NODE_PREFIX} TextToAudio" return node( node_id, type_name, pos, [360, 180], order, ["Generate a calm cinematic ambient track with gentle piano and soft pads.", 6.0], inputs=[input_slot("client", CLIENT_TYPE)], outputs=[ output_slot("id", "STRING", slot_index=0), output_slot("url", "STRING", slot_index=1), ], ) def custom_voice_create_node(node_id, pos, order): type_name = f"{NODE_PREFIX} Custom Voice Create" return node( node_id, type_name, pos, [390, 260], order, ["Demo Voice", "", "", "", "", ""], inputs=[input_slot("audio", "AUDIO")], outputs=[ output_slot("voice_id", "STRING", slot_index=0), output_slot("voice_name", "STRING", slot_index=1), output_slot("trial_url", "STRING", slot_index=2), output_slot("task_id", "STRING", slot_index=3), output_slot("voice_json", "STRING", slot_index=4), ], ) def tts_node(node_id, pos, order): type_name = f"{NODE_PREFIX} TTS" return node( node_id, type_name, pos, [390, 260], order, ["这是一段用于测试克隆声音效果的短句。", "", "zh", 1.0], inputs=[input_slot("voice_id", "STRING")], outputs=[ output_slot("audio_id", "STRING", slot_index=0), output_slot("audio_url", "STRING", slot_index=1), output_slot("duration", "STRING", slot_index=2), output_slot("audio_json", "STRING", slot_index=3), ], ) def video_to_audio_node(node_id, pos, order): type_name = f"{NODE_PREFIX} Video2Audio" return node( node_id, type_name, pos, [390, 220], order, ["", "", "Footsteps on stone floor and distant city ambience.", "Cinematic ambient soundtrack with low strings.", False], inputs=[ input_slot("client", CLIENT_TYPE), input_slot("video_id", "STRING"), ], outputs=[ output_slot("videos_id", "STRING", slot_index=0), output_slot("videos_url", "STRING", slot_index=1), output_slot("audio_id", "STRING", slot_index=2), output_slot("audio_url_mp3", "STRING", slot_index=3), ], ) def lip_sync_text_input_node(node_id, pos, order): type_name = f"{NODE_PREFIX} Lip Sync Text Input" return node( node_id, type_name, pos, [360, 220], order, ["Welcome to Comfyui Kling Wrapper demo. This is a lip sync example generated from text.", "The Reader", "en", 1.0], outputs=[output_slot("input", LIPSYNC_INPUT_TYPE, slot_index=0)], ) def lip_sync_audio_input_node(node_id, pos, order): type_name = f"{NODE_PREFIX} Lip Sync Audio Input" return node( node_id, type_name, pos, [360, 240], order, ["", "", "", 0, 0], inputs=[input_slot("audio", "AUDIO")], outputs=[output_slot("input", LIPSYNC_INPUT_TYPE, slot_index=0)], ) def lip_sync_node(node_id, pos, order): type_name = f"{NODE_PREFIX} Lip Sync" return node( node_id, type_name, pos, [390, 220], order, ["", "", "", "", 0, 1.0, 1.0], inputs=[ input_slot("client", CLIENT_TYPE), input_slot("input", LIPSYNC_INPUT_TYPE), input_slot("video_id", "STRING"), ], outputs=[ output_slot("url", "STRING", slot_index=0), output_slot("video_id", "STRING", slot_index=1), ], ) def avatar_node(node_id, pos, order): type_name = f"{NODE_PREFIX} Avatar" return node( node_id, type_name, pos, [420, 320], order, ["std", "", "", "", "Speak naturally with subtle head movement and a friendly expression."], inputs=[ input_slot("image", "IMAGE"), input_slot("audio", "AUDIO"), ], outputs=[ output_slot("url", "STRING", slot_index=0), output_slot("video_id", "STRING", slot_index=1), ], ) def effects_node(node_id, pos, order): type_name = f"{NODE_PREFIX} Effects" return node( node_id, type_name, pos, [390, 220], order, ["yearbook", "kling-v1-6", "std", "5"], inputs=[ input_slot("client", CLIENT_TYPE), input_slot("image0", "IMAGE"), input_slot("image1", "IMAGE", None), ], outputs=[ output_slot("url", "STRING", slot_index=0), output_slot("video_id", "STRING", slot_index=1), ], ) def advanced_element_create_node(node_id, pos, order): type_name = f"{NODE_PREFIX} Advanced Element Create" return node( node_id, type_name, pos, [390, 278], order, [ "image_subject", "demo_subject", "A clear portrait of the subject with consistent identity traits for reuse in video generation.", "", "", "", ], inputs=[ input_slot("client", CLIENT_TYPE), input_slot("image", "IMAGE"), input_slot("image_list", "IMAGE", None), ], outputs=[ output_slot("element", ELEMENT_TYPE, slot_index=0), output_slot("element_id", "STRING", slot_index=1), output_slot("element_json", "STRING", slot_index=2), ], ) def element_list_builder_node(node_id, pos, order): type_name = f"{NODE_PREFIX} Element List Builder" return node( node_id, type_name, pos, [340, 180], order, [""], inputs=[input_slot("element_1", ELEMENT_TYPE)], outputs=[ output_slot("element_list", ELEMENT_LIST_TYPE, slot_index=0), output_slot("element_list_json", "STRING", slot_index=1), ], ) def motion_control_node(node_id, pos, order): type_name = f"{NODE_PREFIX} Motion Control" return node( node_id, type_name, pos, [430, 420], order, [ "kling-v2-6", "", "Transfer the body motion naturally while keeping the subject stable and realistic.", "", "pro", "auto", "video", True, "", ], inputs=[ input_slot("reference_image", "IMAGE"), input_slot("reference_video_input", "VIDEO"), ], outputs=[ output_slot("url", "STRING", slot_index=0), output_slot("video_id", "STRING", slot_index=1), ], ) def multimodal_video_edit_node(node_id, pos, order): type_name = f"{NODE_PREFIX} Multi Modal Video Edit" return node( node_id, type_name, pos, [470, 460], order, [ "kling-v3-omni", "Replace the subject's coat with the garment shown in the reference image while preserving motion and camera movement.", "https://example.com/base-video.mp4", "", "base", False, "std", "3", "16:9", "", ], inputs=[input_slot("image_list", "IMAGE", None)], outputs=[ output_slot("url", "STRING", slot_index=0), output_slot("video_id", "STRING", slot_index=1), ], ) def link(link_id, src_node, src_slot, dst_node, dst_slot, type_name): return [link_id, src_node, src_slot, dst_node, dst_slot, type_name] def write_json(name, data): (ROOT / name).write_text(json.dumps(data, indent=2, ensure_ascii=False) + "\n", encoding="utf-8") def write_examples_readme(): content = """# Comfyui-Kling-Wrapper Examples This directory contains importable ComfyUI workflow JSON examples and small Python API snippets for `Comfyui-Kling-Wrapper`. ## Before running the workflows - Create and fill in `config.local.json` in the repository root before importing the workflows. - You can copy `config.example.json` in the repository root as a starting template. - The provider is configured globally in `config.local.json` and is intentionally not exposed as a node widget. Use `"provider_default": "official"` for Kling or `"provider_default": "tencent_vod"` for Tencent VOD. - For Tencent VOD workflows, set `provider_default` to `tencent_vod` and configure `tencent_secret_id`, `tencent_secret_key`, and `tencent_sub_app_id`, or use the `TENCENTCLOUD_SECRET_ID` and `TENCENTCLOUD_SECRET_KEY` environment variables. - Replace placeholder filenames such as `example_portrait.png`, `example_subject_front.png`, `example_subject_ref_1.png`, `example_cloth.png`, `example_scene.png`, `example_lipsync_source.mp4`, `example_lipsync_audio.wav`, and `example_voice_reference.mp3` with files that exist in your ComfyUI input directory. - Replace placeholder URLs such as `https://example.com/reference-motion.mp4` with real URLs when needed. - Native preset and cloned voices are available for `kling-v2-6`; the active API rejects `voice_list` for `kling-v3`. - `kling-v3-omni` is visible in the video generation dropdowns for comparison with `kling-v3`; `kling-video-o1` remains hidden until live endpoint support is confirmed. ## Important workflow notes - `05_comfyui_kling_wrapper_text2video_v26_sound.json` is the native audio example and is built around `kling-v2-6`. - `06_comfyui_kling_wrapper_advanced_element_subject_to_image2video.json` requires 1 frontal portrait plus 1-3 additional photos of the same subject. Background or scene images do not count as advanced-element reference images. - If you need both subject binding and speech, generate the bound video first and then add speech with the lip-sync or audio nodes. - `kling-v3-omni` can combine `custom_voice_id` or `custom_voice_list` with `element_list`; older models still keep stricter local guards. - `Motion Control` accepts either a direct http(s) reference-video URL or uploaded/local video workflow inputs. For local media, use a video loader node that provides either `VIDEO` or `reference_video_frames` plus `reference_video_info`. Set `duration` to `auto` if you want it to match the uploaded motion reference length. - `Lip Sync` now calls `identify-face` and `advanced-lip-sync`; leave `face_id` empty to use the first detected face. - `Avatar` accepts one and only one of `AUDIO`, `audio_id`, `audio_file`, or `audio_url`. - `Image2Video.custom_voice_id` is the single-custom-voice path. For multiple voices, connect `Custom Voice List Builder.custom_voice_list` to `Image2Video.custom_voice_list`, set `sound=on`, and use ordered markers such as `<<>>` and `<<>>`. ## Included files - `01_comfyui_kling_wrapper_image_generation_v3.json`: basic image generation with a verified `kling-v3` preset. - `02_comfyui_kling_wrapper_text2video_v3_intelligence.json`: text-to-video with `kling-v3` intelligent multi-shot mode. - `03_comfyui_kling_wrapper_image2video_v3.json`: single-image to video using the visible 3.0-era model set. - `04_comfyui_kling_wrapper_multi_images_to_video_v3.json`: multi-image identity-consistent video generation. - `05_comfyui_kling_wrapper_text2video_v26_sound.json`: `kling-v2-6` talking-head example with native voice presets. - `06_comfyui_kling_wrapper_advanced_element_subject_to_image2video.json`: advanced custom element creation plus `element_list` binding in image-to-video. - `07_comfyui_kling_wrapper_motion_control_v26.json`: motion-control workflow centered on a reference image plus uploaded reference-video inputs. - `08_comfyui_kling_wrapper_virtual_try_on.json`: virtual try-on image workflow. - `09_comfyui_kling_wrapper_image_expand.json`: image expansion workflow. - `10_comfyui_kling_wrapper_video_extend_chain.json`: clip generation followed by video extension. - `11_comfyui_kling_wrapper_text_to_audio.json`: text-to-audio workflow. - `12_comfyui_kling_wrapper_video_to_audio.json`: video-to-audio workflow. - `13_comfyui_kling_wrapper_lip_sync_from_text.json`: lip-sync workflow driven by text input. - `14_comfyui_kling_wrapper_effects_single_image.json`: single-image effects workflow. - `15_comfyui_kling_wrapper_advanced_element_subject_with_video2audio_music.json`: advanced subject binding followed by `Video2Audio` to add generated music/ambience to the result. - `16_comfyui_kling_wrapper_video_character_to_image2video.json`: create a `video_character` element from a public `video_url`, then bind that element into `Image2Video`. - `17_comfyui_kling_wrapper_lip_sync_from_uploaded_audio_video.json`: advanced lip-sync workflow using direct `LoadAudio` and `LoadVideo` uploads. - `18_comfyui_kling_wrapper_custom_voice_tts_preview.json`: create a custom voice and preview its returned `trial_url`. - `19_comfyui_kling_wrapper_avatar_from_uploaded_audio_image.json`: create an Avatar video from one image and uploaded audio. - `20_comfyui_kling_wrapper_custom_voice_image2video.json`: clone a voice and connect its `voice_id` to Kling v2.6 image-to-video. - `21_tencent_vod_text2video.json`: minimal Tencent VOD text-to-video workflow. - `22_tencent_vod_image2video.json`: minimal Tencent VOD image-to-video workflow. - `23_tencent_vod_multi_images_to_video.json`: Tencent VOD multi-image reference generation. - `24_tencent_vod_motion_control.json`: Tencent VOD motion-control workflow. - `25_tencent_vod_multimodal_video_edit.json`: Tencent VOD 3.0-Omni video editing with an image reference. - `26_tencent_vod_advanced_element_create.json`: Tencent VOD advanced subject creation. - `27_tencent_vod_custom_voice_create.json`: Tencent VOD custom voice creation and trial preview. - `28_comfyui_kling_wrapper_clothing_dupe.json`: official Kling Clothing Dupe workflow using uploaded video, product/model images, and BGM audio. - `api_examples.py`: small Python snippets that mirror the same upgraded API wrappers. """ (ROOT / "README.md").write_text(content, encoding="utf-8") def write_api_examples(): content = '''import base64 import os from pathlib import Path from py.api import ( AdvancedLipSync, AdvancedCustomElements, Avatar, Client, CustomVoiceCreate, FaceIdentify, Image2Video, ImageGenerator, MotionControl, MultiImages2Video, TTS, Text2Video, ) def image_to_base64(path: str) -> str: return base64.b64encode(Path(path).read_bytes()).decode("utf-8") def build_client(in_china: bool = True) -> Client: access_key = os.environ["KLING_ACCESS_KEY"] secret_key = os.environ["KLING_SECRET_KEY"] return Client(access_key, secret_key, in_china=in_china, timeout=30, poll_interval=2.0) def example_image_generation(client: Client): task = ImageGenerator() task.model_name = "kling-v3" task.prompt = "A cinematic editorial portrait with elegant framing and realistic skin texture." task.aspect_ratio = "16:9" task.resolution = "1k" return task.run(client) def example_text2video_v3(client: Client): task = Text2Video() task.model_name = "kling-v3" task.prompt = "A traveler walks through an old alley at dusk with cinematic multi-shot storytelling." task.mode = "pro" task.aspect_ratio = "16:9" task.duration = "5" task.shot_type = "intelligence" return task.run(client) def example_text2video_v26_sound(client: Client): task = Text2Video() task.model_name = "kling-v2-6" task.prompt = "A presenter speaks directly to camera in a clean studio." task.mode = "pro" task.aspect_ratio = "16:9" task.duration = "5" task.sound = "on" task.voice_list = [{"voice_id": "reader_en_m-v1"}] return task.run(client) def example_create_advanced_element(client: Client, frontal_image_url: str, reference_image_urls: list[str]): if not 1 <= len(reference_image_urls) <= 3: raise ValueError("Provide 1-3 additional reference image URLs for the same subject.") task = AdvancedCustomElements() task.element_name = "demo_subject" task.element_description = "A clear portrait of the subject with consistent identity traits for reuse in video generation." task.reference_type = "image_refer" task.element_image_list = { "frontal_image": frontal_image_url, "refer_images": [{"image_url": url} for url in reference_image_urls], } return task.run(client) def example_image2video_with_element(client: Client, image_path: str, element_id: str): task = Image2Video() task.model_name = "kling-v3" task.image = image_to_base64(image_path) task.prompt = "Keep the same subject identity while the camera slowly pushes in." task.mode = "pro" task.duration = "5" task.element_list = [{"element_id": element_id}] return task.run(client) def example_multi_images_to_video(client: Client, image_paths: list[str]): if len(image_paths) < 2: raise ValueError("Provide at least two image paths.") task = MultiImages2Video() task.model_name = "kling-v3" encoded = [image_to_base64(path) for path in image_paths] task.image = encoded[0] task.image_list = encoded[1:] task.prompt = "Maintain the same identity across the shot with natural motion." task.mode = "pro" task.duration = "5" task.aspect_ratio = "16:9" return task.run(client) def example_motion_control(client: Client, image_path: str, reference_video_url: str): task = MotionControl() task.model_name = "kling-v2-6" task.reference_image = image_to_base64(image_path) task.reference_video = reference_video_url task.prompt = "Transfer the motion naturally while keeping the subject stable and realistic." task.mode = "pro" task.duration = "5" return task.run(client) def example_create_custom_voice(client: Client, voice_url: str): task = CustomVoiceCreate() task.voice_name = "Demo Voice" task.voice_url = voice_url return task.run(client) def example_avatar(client: Client, image_path: str, audio_url: str): task = Avatar() task.image = image_to_base64(image_path) task.sound_file = audio_url task.prompt = "Speak naturally with subtle head movement and a friendly expression." task.mode = "std" return task.run(client) def example_advanced_lip_sync(client: Client, video_url: str, audio_url: str, audio_duration_ms: int): identify = FaceIdentify() identify.video_url = video_url face_result = identify.run(client) if not face_result.face_data: raise ValueError("No face was detected in the source video.") task = AdvancedLipSync() task.session_id = face_result.session_id task.face_choose = [{ "face_id": face_result.face_data[0].face_id, "sound_file": audio_url, "sound_start_time": 0, "sound_end_time": audio_duration_ms, "sound_insert_time": 0, "sound_volume": 1.0, "original_audio_volume": 1.0, }] return task.run(client) def example_image2video_with_custom_voice( client: Client, image_path: str, voice_id: str, dialogue: str, ): task = Image2Video() task.model_name = "kling-v2-6" task.image = image_to_base64(image_path) task.prompt = f'Portrait subject <<>> says: "{dialogue}"' task.mode = "pro" task.duration = "10" task.sound = "on" task.voice_list = [{"voice_id": voice_id}] return task.run(client) def example_tts_with_voice_id(client: Client, voice_id: str): task = TTS() task.text = "这是一段用于测试克隆声音效果的短句。" task.voice_id = voice_id task.voice_language = "zh" task.voice_speed = 1.0 return task.run(client) if __name__ == "__main__": print("Set KLING_ACCESS_KEY and KLING_SECRET_KEY before running these examples.") print("Import this file and call the helper that matches the workflow you want to test.") ''' (ROOT / "api_examples.py").write_text(content, encoding="utf-8") def generate_workflows(): examples = {} nodes = [ client_node(1, 0), image_generator_node(2, [430, 40], 1), save_image_node(3, "Comfyui-Kling-Wrapper-image-generation", [900, 60], 2, 2), ] links = [ link(1, 1, 0, 2, 0, CLIENT_TYPE), link(2, 2, 0, 3, 0, "IMAGE"), ] nodes[1]["inputs"][0]["link"] = 1 nodes[1]["outputs"][0]["links"] = [2] examples["01_comfyui_kling_wrapper_image_generation_v3.json"] = workflow(nodes, links) nodes = [ client_node(1, 0), text2video_node( 2, [430, 20], 1, "kling-v3", "A traveler moves through an old alley at dusk, cinematic atmosphere, natural motion, evolving composition with coherent multi-shot storytelling.", mode="pro", shot_type="intelligence", ), preview_video_node(3, [910, 20], 2, 2), ] links = [ link(1, 1, 0, 2, 0, CLIENT_TYPE), link(2, 2, 0, 3, 0, "STRING"), ] nodes[1]["inputs"][0]["link"] = 1 nodes[1]["outputs"][0]["links"] = [2] examples["02_comfyui_kling_wrapper_text2video_v3_intelligence.json"] = workflow(nodes, links) nodes = [ client_node(1, 0), load_image_node(2, "example_portrait.png", [60, 340], 1), image2video_node( 3, [430, 200], 2, "kling-v3", "A cinematic close-up portrait. The subject breathes naturally, hair and clothing move subtly in the wind, and the camera slowly pushes in.", mode="pro", ), preview_video_node(4, [920, 200], 3, 3), ] links = [ link(1, 1, 0, 3, 0, CLIENT_TYPE), link(2, 2, 0, 3, 1, "IMAGE"), link(3, 3, 0, 4, 0, "STRING"), ] nodes[2]["inputs"][0]["link"] = 1 nodes[2]["inputs"][1]["link"] = 2 nodes[2]["outputs"][0]["links"] = [3] examples["03_comfyui_kling_wrapper_image2video_v3.json"] = workflow(nodes, links) nodes = [ client_node(1, 0), load_image_node(2, "example_subject_a.png", [40, 330], 1), load_image_node(3, "example_subject_b.png", [40, 700], 2), image_batch_node(4, [370, 510], 3), multi_images_to_video_node(5, [700, 420], 4), preview_video_node(6, [1180, 420], 5, 5), ] links = [ link(1, 1, 0, 5, 0, CLIENT_TYPE), link(2, 2, 0, 4, 0, "IMAGE"), link(3, 3, 0, 4, 1, "IMAGE"), link(4, 4, 0, 5, 1, "IMAGE"), link(5, 5, 0, 6, 0, "STRING"), ] nodes[3]["inputs"][0]["link"] = 2 nodes[3]["inputs"][1]["link"] = 3 nodes[4]["inputs"][0]["link"] = 1 nodes[4]["inputs"][1]["link"] = 4 nodes[4]["outputs"][0]["links"] = [5] examples["04_comfyui_kling_wrapper_multi_images_to_video_v3.json"] = workflow(nodes, links) nodes = [ client_node(1, 0), text2video_node( 2, [430, 20], 1, "kling-v2-6", "A presenter speaks directly to camera in a clean studio, natural gestures, subtle head motion, clear talking-head composition.", mode="pro", sound="on", voice_preset="The Reader | reader_en_m-v1", ), preview_video_node(3, [910, 20], 2, 2), ] links = [ link(1, 1, 0, 2, 0, CLIENT_TYPE), link(2, 2, 0, 3, 0, "STRING"), ] nodes[1]["inputs"][0]["link"] = 1 nodes[1]["outputs"][0]["links"] = [2] examples["05_comfyui_kling_wrapper_text2video_v26_sound.json"] = workflow(nodes, links) nodes = [ client_node(1, 0), load_image_node(2, "example_subject_front.png", [30, 100], 1), load_image_node(3, "example_subject_ref_1.png", [30, 470], 2), load_image_node(4, "example_subject_ref_2.png", [30, 840], 3), image_batch_node(5, [360, 640], 4), load_image_node(6, "example_scene.png", [360, 980], 5), advanced_element_create_node(7, [720, 180], 6), element_list_builder_node(8, [1160, 180], 7), image2video_node( 9, [1160, 500], 8, "kling-v3", "Keep the same subject identity while the camera slowly moves and the background remains coherent.", mode="pro", ), preview_video_node(10, [1640, 500], 9, 10), ] links = [ link(1, 1, 0, 7, 0, CLIENT_TYPE), link(2, 2, 0, 7, 1, "IMAGE"), link(3, 3, 0, 5, 0, "IMAGE"), link(4, 4, 0, 5, 1, "IMAGE"), link(5, 5, 0, 7, 2, "IMAGE"), link(6, 7, 0, 8, 0, ELEMENT_TYPE), link(7, 1, 0, 9, 0, CLIENT_TYPE), link(8, 6, 0, 9, 1, "IMAGE"), link(9, 8, 0, 9, 4, ELEMENT_LIST_TYPE), link(10, 9, 0, 10, 0, "STRING"), ] nodes[6]["inputs"][0]["link"] = 1 nodes[6]["inputs"][1]["link"] = 2 nodes[6]["inputs"][2]["link"] = 5 nodes[4]["inputs"][0]["link"] = 3 nodes[4]["inputs"][1]["link"] = 4 nodes[7]["inputs"][0]["link"] = 6 nodes[8]["inputs"][0]["link"] = 7 nodes[8]["inputs"][1]["link"] = 8 nodes[8]["inputs"].append(input_slot("element_list", ELEMENT_LIST_TYPE, 9)) nodes[8]["outputs"][0]["links"] = [10] examples["06_comfyui_kling_wrapper_advanced_element_subject_to_image2video.json"] = workflow(nodes, links) nodes = [ load_image_node(2, "example_motion_subject.png", [40, 260], 1), load_video_node(3, "example_motion_reference.mp4", [40, 670], 2), motion_control_node(4, [460, 220], 3), preview_video_node(5, [980, 220], 4, 3), ] links = [ link(1, 2, 0, 4, 0, "IMAGE"), link(2, 3, 0, 4, 1, "VIDEO"), link(3, 4, 0, 5, 0, "STRING"), ] nodes[0]["outputs"][0]["links"] = [1] nodes[1]["outputs"][0]["links"] = [2] nodes[2]["inputs"][0]["link"] = 1 nodes[2]["inputs"][1]["link"] = 2 nodes[2]["outputs"][0]["links"] = [3] examples["07_comfyui_kling_wrapper_motion_control_v26.json"] = workflow(nodes, links) nodes = [ client_node(1, 0), load_image_node(2, "example_model.png", [40, 150], 1), load_image_node(3, "example_cloth.png", [40, 520], 2), virtual_try_on_node(4, [420, 310], 3), save_image_node(5, "Comfyui-Kling-Wrapper-virtual-try-on", [860, 320], 4, 4), ] links = [ link(1, 1, 0, 4, 0, CLIENT_TYPE), link(2, 2, 0, 4, 1, "IMAGE"), link(3, 3, 0, 4, 2, "IMAGE"), link(4, 4, 0, 5, 0, "IMAGE"), ] nodes[3]["inputs"][0]["link"] = 1 nodes[3]["inputs"][1]["link"] = 2 nodes[3]["inputs"][2]["link"] = 3 nodes[3]["outputs"][0]["links"] = [4] examples["08_comfyui_kling_wrapper_virtual_try_on.json"] = workflow(nodes, links) nodes = [ client_node(1, 0), load_image_node(2, "example_expand.png", [40, 220], 1), image_expander_node(3, [430, 180], 2), save_image_node(4, "Comfyui-Kling-Wrapper-image-expand", [860, 190], 3, 3), ] links = [ link(1, 1, 0, 3, 0, CLIENT_TYPE), link(2, 2, 0, 3, 1, "IMAGE"), link(3, 3, 0, 4, 0, "IMAGE"), ] nodes[2]["inputs"][0]["link"] = 1 nodes[2]["inputs"][1]["link"] = 2 nodes[2]["outputs"][0]["links"] = [3] examples["09_comfyui_kling_wrapper_image_expand.json"] = workflow(nodes, links) nodes = [ client_node(1, 0), text2video_node( 2, [420, 20], 1, "kling-v3", "A quiet street scene at sunrise with subtle camera drift and natural environmental motion.", mode="pro", ), video_extender_node(3, [870, 40], 2), preview_video_node(4, [1290, 40], 3, 4), ] links = [ link(1, 1, 0, 2, 0, CLIENT_TYPE), link(2, 1, 0, 3, 0, CLIENT_TYPE), link(3, 2, 1, 3, 1, "STRING"), link(4, 3, 0, 4, 0, "STRING"), ] nodes[1]["inputs"][0]["link"] = 1 nodes[2]["inputs"][0]["link"] = 2 nodes[2]["inputs"][1]["link"] = 3 nodes[2]["outputs"][0]["links"] = [4] examples["10_comfyui_kling_wrapper_video_extend_chain.json"] = workflow(nodes, links) nodes = [ client_node(1, 0), text_to_audio_node(2, [430, 80], 1), preview_audio_node(3, [870, 80], 2, 2), ] links = [ link(1, 1, 0, 2, 0, CLIENT_TYPE), link(2, 2, 1, 3, 0, "STRING"), ] nodes[1]["inputs"][0]["link"] = 1 nodes[1]["outputs"][1]["links"] = [2] examples["11_comfyui_kling_wrapper_text_to_audio.json"] = workflow(nodes, links) nodes = [ client_node(1, 0), text2video_node( 2, [420, 20], 1, "kling-v3", "A person walks through an empty hall with slow cinematic movement.", mode="pro", ), video_to_audio_node(3, [880, 30], 2), preview_audio_node(4, [1330, 30], 3, 4), ] links = [ link(1, 1, 0, 2, 0, CLIENT_TYPE), link(2, 1, 0, 3, 0, CLIENT_TYPE), link(3, 2, 1, 3, 1, "STRING"), link(4, 3, 3, 4, 0, "STRING"), ] nodes[1]["inputs"][0]["link"] = 1 nodes[2]["inputs"][0]["link"] = 2 nodes[2]["inputs"][1]["link"] = 3 nodes[2]["outputs"][3]["links"] = [4] examples["12_comfyui_kling_wrapper_video_to_audio.json"] = workflow(nodes, links) nodes = [ client_node(1, 0), text2video_node( 2, [420, 20], 1, "kling-v3", "A portrait subject looks at camera with subtle idle motion.", mode="pro", ), lip_sync_text_input_node(3, [420, 420], 2), lip_sync_node(4, [880, 240], 3), preview_video_node(5, [1320, 240], 4, 5), ] links = [ link(1, 1, 0, 2, 0, CLIENT_TYPE), link(2, 1, 0, 4, 0, CLIENT_TYPE), link(3, 3, 0, 4, 1, LIPSYNC_INPUT_TYPE), link(4, 2, 1, 4, 2, "STRING"), link(5, 4, 0, 5, 0, "STRING"), ] nodes[1]["inputs"][0]["link"] = 1 nodes[3]["inputs"][0]["link"] = 2 nodes[3]["inputs"][1]["link"] = 3 nodes[3]["inputs"][2]["link"] = 4 nodes[3]["outputs"][0]["links"] = [5] examples["13_comfyui_kling_wrapper_lip_sync_from_text.json"] = workflow(nodes, links) nodes = [ client_node(1, 0), load_image_node(2, "example_effect_input.png", [40, 220], 1), effects_node(3, [430, 180], 2), preview_video_node(4, [900, 180], 3, 3), ] links = [ link(1, 1, 0, 3, 0, CLIENT_TYPE), link(2, 2, 0, 3, 1, "IMAGE"), link(3, 3, 0, 4, 0, "STRING"), ] nodes[2]["inputs"][0]["link"] = 1 nodes[2]["inputs"][1]["link"] = 2 nodes[2]["outputs"][0]["links"] = [3] examples["14_comfyui_kling_wrapper_effects_single_image.json"] = workflow(nodes, links) nodes = [ load_audio_node(1, "example_lipsync_audio.wav", [40, 40], 0), load_video_node(2, "example_lipsync_source.mp4", [40, 300], 1), lip_sync_audio_input_node(3, [420, 40], 2), lip_sync_node(4, [860, 180], 3), preview_video_node(5, [1340, 180], 4, 4), ] nodes[3]["inputs"] = nodes[3]["inputs"][:2] + [input_slot("video_input", "VIDEO")] links = [ link(1, 1, 0, 3, 0, "AUDIO"), link(2, 3, 0, 4, 1, LIPSYNC_INPUT_TYPE), link(3, 2, 0, 4, 2, "VIDEO"), link(4, 4, 0, 5, 0, "STRING"), ] nodes[0]["outputs"][0]["links"] = [1] nodes[1]["outputs"][0]["links"] = [3] nodes[2]["inputs"][0]["link"] = 1 nodes[2]["outputs"][0]["links"] = [2] nodes[3]["inputs"][1]["link"] = 2 nodes[3]["inputs"][2]["link"] = 3 nodes[3]["outputs"][0]["links"] = [4] examples["17_comfyui_kling_wrapper_lip_sync_from_uploaded_audio_video.json"] = workflow(nodes, links) nodes = [ load_audio_node(1, "example_voice_reference.mp3", [40, 80], 0), custom_voice_create_node(2, [420, 80], 1), preview_audio_node(3, [880, 120], 2, 2), ] links = [ link(1, 1, 0, 2, 0, "AUDIO"), link(2, 2, 2, 3, 0, "STRING"), ] nodes[0]["outputs"][0]["links"] = [1] nodes[1]["inputs"][0]["link"] = 1 nodes[1]["outputs"][2]["links"] = [2] nodes[2]["inputs"][0]["link"] = 2 examples["18_comfyui_kling_wrapper_custom_voice_tts_preview.json"] = workflow(nodes, links) nodes = [ load_image_node(1, "example_avatar_portrait.png", [40, 80], 0), load_audio_node(2, "example_avatar_speech.wav", [40, 480], 1), avatar_node(3, [460, 200], 2), preview_video_node(4, [980, 200], 3, 3), ] links = [ link(1, 1, 0, 3, 0, "IMAGE"), link(2, 2, 0, 3, 1, "AUDIO"), link(3, 3, 0, 4, 0, "STRING"), ] nodes[0]["outputs"][0]["links"] = [1] nodes[1]["outputs"][0]["links"] = [2] nodes[2]["inputs"][0]["link"] = 1 nodes[2]["inputs"][1]["link"] = 2 nodes[2]["outputs"][0]["links"] = [3] examples["19_comfyui_kling_wrapper_avatar_from_uploaded_audio_image.json"] = workflow(nodes, links) nodes = [ load_audio_node(1, "example_voice_reference.mp3", [40, 40], 0), load_image_node(2, "example_talking_portrait.png", [40, 360], 1), custom_voice_create_node(3, [420, 40], 2), image2video_node( 4, [860, 260], 3, "kling-v2-6", 'The portrait subject <<>> says: "Welcome to this custom voice demonstration."', mode="pro", duration="10", sound="on", ), preview_video_node(5, [1380, 260], 4, 4), ] links = [ link(1, 1, 0, 3, 0, "AUDIO"), link(2, 2, 0, 4, 1, "IMAGE"), link(3, 3, 0, 4, 3, "STRING"), link(4, 4, 0, 5, 0, "STRING"), ] nodes[0]["outputs"][0]["links"] = [1] nodes[1]["outputs"][0]["links"] = [2] nodes[2]["inputs"][0]["link"] = 1 nodes[2]["outputs"][0]["links"] = [3] nodes[3]["inputs"][1]["link"] = 2 nodes[3]["inputs"][3]["link"] = 3 nodes[3]["outputs"][0]["links"] = [4] examples["20_comfyui_kling_wrapper_custom_voice_image2video.json"] = workflow(nodes, links) nodes = [ text2video_node( 1, [60, 60], 0, "kling-v3", "A red fox runs through fresh snow, cinematic natural motion.", mode="std", duration="3", ), preview_video_node(2, [580, 60], 1, 1), ] links = [link(1, 1, 0, 2, 0, "STRING")] nodes[0]["outputs"][0]["links"] = [1] examples["21_tencent_vod_text2video.json"] = workflow(nodes, links) nodes = [ load_image_node(1, "example_portrait.png", [40, 60], 0), image2video_node( 2, [390, 60], 1, "kling-v3", "The subject turns toward camera while the clothing moves naturally.", mode="std", duration="3", ), preview_video_node(3, [900, 60], 2, 2), ] links = [ link(1, 1, 0, 2, 1, "IMAGE"), link(2, 2, 0, 3, 0, "STRING"), ] nodes[0]["outputs"][0]["links"] = [1] nodes[1]["inputs"][1]["link"] = 1 nodes[1]["outputs"][0]["links"] = [2] examples["22_tencent_vod_image2video.json"] = workflow(nodes, links) nodes = [ load_image_node(1, "example_subject_a.png", [40, 40], 0), load_image_node(2, "example_subject_b.png", [40, 420], 1), image_batch_node(3, [390, 230], 2), multi_images_to_video_node(4, [730, 160], 3), preview_video_node(5, [1220, 160], 4, 4), ] links = [ link(1, 1, 0, 3, 0, "IMAGE"), link(2, 2, 0, 3, 1, "IMAGE"), link(3, 3, 0, 4, 1, "IMAGE"), link(4, 4, 0, 5, 0, "STRING"), ] nodes[0]["outputs"][0]["links"] = [1] nodes[1]["outputs"][0]["links"] = [2] nodes[2]["inputs"][0]["link"] = 1 nodes[2]["inputs"][1]["link"] = 2 nodes[2]["outputs"][0]["links"] = [3] nodes[3]["inputs"][1]["link"] = 3 nodes[3]["outputs"][0]["links"] = [4] examples["23_tencent_vod_multi_images_to_video.json"] = workflow(nodes, links) nodes = [ load_image_node(1, "example_portrait.png", [40, 40], 0), load_video_node(2, "example_motion_reference.mp4", [40, 420], 1), motion_control_node(3, [430, 170], 2), preview_video_node(4, [930, 170], 3, 3), ] links = [ link(1, 1, 0, 3, 0, "IMAGE"), link(2, 2, 0, 3, 1, "VIDEO"), link(3, 3, 0, 4, 0, "STRING"), ] nodes[0]["outputs"][0]["links"] = [1] nodes[1]["outputs"][0]["links"] = [2] nodes[2]["inputs"][0]["link"] = 1 nodes[2]["inputs"][1]["link"] = 2 nodes[2]["outputs"][0]["links"] = [3] examples["24_tencent_vod_motion_control.json"] = workflow(nodes, links) nodes = [ load_image_node(1, "example_garment_reference.png", [40, 120], 0), multimodal_video_edit_node(2, [410, 80], 1), preview_video_node(3, [940, 80], 2, 2), ] links = [ link(1, 1, 0, 2, 0, "IMAGE"), link(2, 2, 0, 3, 0, "STRING"), ] nodes[0]["outputs"][0]["links"] = [1] nodes[1]["inputs"][0]["link"] = 1 nodes[1]["outputs"][0]["links"] = [2] examples["25_tencent_vod_multimodal_video_edit.json"] = workflow(nodes, links) nodes = [ load_image_node(1, "example_subject_front.png", [40, 40], 0), load_image_node(2, "example_subject_ref_1.png", [40, 420], 1), image_batch_node(3, [390, 230], 2), advanced_element_create_node(4, [730, 150], 3), ] links = [ link(1, 1, 0, 4, 1, "IMAGE"), link(2, 2, 0, 3, 0, "IMAGE"), link(3, 3, 0, 4, 2, "IMAGE"), ] nodes[0]["outputs"][0]["links"] = [1] nodes[1]["outputs"][0]["links"] = [2] nodes[2]["inputs"][0]["link"] = 2 nodes[2]["outputs"][0]["links"] = [3] nodes[3]["inputs"][1]["link"] = 1 nodes[3]["inputs"][2]["link"] = 3 examples["26_tencent_vod_advanced_element_create.json"] = workflow(nodes, links) nodes = [ load_audio_node(1, "example_voice_reference.mp3", [40, 80], 0), custom_voice_create_node(2, [420, 80], 1), preview_audio_node(3, [880, 120], 2, 2), ] links = [ link(1, 1, 0, 2, 0, "AUDIO"), link(2, 2, 2, 3, 0, "STRING"), ] nodes[0]["outputs"][0]["links"] = [1] nodes[1]["inputs"][0]["link"] = 1 nodes[1]["outputs"][2]["links"] = [2] nodes[2]["inputs"][0]["link"] = 2 examples["27_tencent_vod_custom_voice_create.json"] = workflow(nodes, links) nodes = [ load_video_node(1, "example_clothing_reference.mp4", [40, 40], 0), load_image_node(2, "example_product.png", [40, 250], 1), load_image_node(3, "example_model.png", [40, 610], 2), load_audio_node(4, "example_bgm.wav", [40, 980], 3), clothing_dupe_node(5, [430, 300], 4), preview_video_node(6, [930, 300], 5, 5), ] links = [ link(1, 1, 0, 5, 0, "VIDEO"), link(2, 2, 0, 5, 1, "IMAGE"), link(3, 3, 0, 5, 6, "IMAGE"), link(4, 4, 0, 5, 11, "AUDIO"), link(5, 5, 0, 6, 0, "STRING"), ] for index, link_id in enumerate((1, 2, 3, 4)): nodes[index]["outputs"][0]["links"] = [link_id] nodes[4]["inputs"][0]["link"] = 1 nodes[4]["inputs"][1]["link"] = 2 nodes[4]["inputs"][6]["link"] = 3 nodes[4]["inputs"][11]["link"] = 4 nodes[4]["outputs"][0]["links"] = [5] examples["28_comfyui_kling_wrapper_clothing_dupe.json"] = workflow(nodes, links) for name, data in examples.items(): write_json(name, strip_client_dependency(data)) if __name__ == "__main__": write_examples_readme() write_api_examples() generate_workflows() print(f"Wrote refreshed examples into {ROOT}")