NODE_PREFIX = "Comfyui-Kling-Wrapper" NODE_CATEGORY = NODE_PREFIX CLIENT_TYPE = "COMFYUI_KLING_WRAPPER_API_CLIENT" LIPSYNC_INPUT_TYPE = "COMFYUI_KLING_WRAPPER_LIPSYNC_INPUT" ELEMENT_TYPE = "COMFYUI_KLING_WRAPPER_ELEMENT" ELEMENT_LIST_TYPE = "COMFYUI_KLING_WRAPPER_ELEMENT_LIST" CUSTOM_VOICE_LIST_TYPE = "COMFYUI_KLING_WRAPPER_CUSTOM_VOICE_LIST" DEFAULT_VIDEO_ASPECT_RATIOS = ["16:9", "9:16", "1:1"] EXTENDED_IMAGE_ASPECT_RATIOS = ["16:9", "9:16", "1:1", "4:3", "3:4", "3:2", "2:3", "21:9", "auto"] DEFAULT_IMAGE_RESOLUTIONS = ["1k", "2k"] DEFAULT_VIDEO_DURATIONS = [str(value) for value in range(3, 16)] DEFAULT_MODES = ["std", "pro"] VIDEO_GENERATION_MODES = ["std", "pro", "4k"] SHOT_TYPES = ["single", "intelligence"] SOUND_OPTIONS = ["off", "on"] IMAGE_MODEL_CAPABILITIES = { "kling-v1": { "resolutions": ["1k", "2k"], "aspect_ratios": ["16:9", "9:16", "1:1", "4:3", "3:4", "3:2", "2:3"], "supports_image_reference": True, "supports_image_fidelity": True, "supports_human_fidelity": True, "supports_watermark": True, }, "kling-v1-5": { "resolutions": ["1k", "2k"], "aspect_ratios": ["16:9", "9:16", "1:1", "4:3", "3:4", "3:2", "2:3", "21:9"], "supports_image_reference": True, "supports_image_fidelity": True, "supports_human_fidelity": True, "supports_watermark": True, }, "kling-v2": { "resolutions": ["1k", "2k"], "aspect_ratios": ["16:9", "9:16", "1:1", "4:3", "3:4", "3:2", "2:3"], "supports_image_reference": True, "supports_image_fidelity": True, "supports_human_fidelity": True, "supports_watermark": True, }, "kling-v2-new": { "resolutions": ["1k", "2k"], "aspect_ratios": ["16:9", "9:16", "1:1", "4:3", "3:4", "3:2", "2:3"], "requires_input_image": True, "supports_image_reference": True, "supports_image_fidelity": True, "supports_human_fidelity": True, "supports_watermark": True, }, "kling-v2-1": { "resolutions": ["1k", "2k"], "aspect_ratios": ["16:9", "9:16", "1:1", "4:3", "3:4", "3:2", "2:3"], "supports_image_reference": True, "supports_image_fidelity": False, "supports_human_fidelity": False, "supports_watermark": True, }, "kling-image-o1": { "resolutions": ["1k", "2k"], "aspect_ratios": ["1:1", "16:9", "4:3", "3:2", "2:3", "3:4", "9:16", "21:9"], "supports_auto_aspect_on_image_input": True, "supports_image_reference": False, "supports_image_fidelity": False, "supports_human_fidelity": False, "supports_negative_prompt": False, "supports_cfg_scale": False, "supports_watermark": True, }, "kling-v3": { "resolutions": ["1k", "2k"], "aspect_ratios": ["1:1", "16:9", "4:3", "3:2", "2:3", "3:4", "9:16", "21:9"], "supports_image_reference": False, "supports_image_fidelity": False, "supports_human_fidelity": False, "supports_negative_prompt": False, "supports_cfg_scale": False, "supports_watermark": True, }, "kling-v3-omni": { "resolutions": ["1k", "2k"], "aspect_ratios": ["1:1", "16:9", "4:3", "3:2", "2:3", "3:4", "9:16", "21:9"], "supports_image_reference": False, "supports_image_fidelity": False, "supports_human_fidelity": False, "supports_negative_prompt": False, "supports_cfg_scale": False, "supports_watermark": True, }, } VIDEO_MODEL_CAPABILITIES = { "kling-v1": { "tasks": {"text2video", "image2video"}, "modes": ["std", "pro"], "durations": ["5", "10"], "supports_camera_control": True, "supports_image_tail": False, "supports_image_list": False, "supports_element_list": False, "supports_reference_video": False, "supports_sound": False, "supports_voice_list": False, "shot_types": ["single"], "supports_watermark": True, }, "kling-v1-5": { "tasks": {"image2video"}, "modes": ["std", "pro"], "durations": ["5", "10"], "supports_camera_control": True, "supports_image_tail": True, "supports_image_list": False, "supports_element_list": False, "supports_reference_video": False, "supports_sound": False, "supports_voice_list": False, "shot_types": ["single"], "supports_watermark": True, }, "kling-v1-6": { "tasks": {"text2video", "image2video"}, "modes": ["std", "pro"], "durations": ["5", "10"], "supports_camera_control": True, "supports_image_tail": True, "supports_image_list": False, "supports_element_list": False, "supports_reference_video": False, "supports_sound": False, "supports_voice_list": False, "shot_types": ["single"], "supports_watermark": True, }, "kling-v2-master": { "tasks": {"text2video", "image2video"}, "modes": ["std", "pro"], "durations": ["5", "10"], "supports_camera_control": False, "supports_image_tail": False, "supports_image_list": False, "supports_element_list": False, "supports_reference_video": False, "supports_sound": False, "supports_voice_list": False, "shot_types": ["single"], "supports_watermark": True, }, "kling-v2-1": { "tasks": {"text2video", "image2video", "multi_image2video"}, "modes": ["std", "pro"], "durations": ["5", "10"], "supports_camera_control": False, "supports_image_tail": True, "supports_image_list": True, "supports_element_list": False, "supports_reference_video": False, "supports_sound": False, "supports_voice_list": False, "shot_types": ["single"], "supports_watermark": True, }, "kling-v2-1-master": { "tasks": {"text2video", "image2video"}, "modes": ["std", "pro"], "durations": ["5", "10"], "supports_camera_control": False, "supports_image_tail": True, "supports_image_list": False, "supports_element_list": False, "supports_reference_video": False, "supports_sound": False, "supports_voice_list": False, "shot_types": ["single"], "supports_watermark": True, }, "kling-v2-5-turbo": { "tasks": {"text2video", "image2video", "multi_image2video"}, "modes": ["std", "pro"], "durations": ["5", "10"], "supports_camera_control": False, "supports_image_tail": True, "image_tail_modes": ["pro"], "supports_image_list": True, "supports_element_list": False, "supports_reference_video": False, "supports_sound": False, "supports_voice_list": False, "shot_types": ["single"], "supports_watermark": True, }, "kling-v2-6": { "tasks": {"text2video", "image2video"}, "modes": ["pro"], "durations": ["5", "10"], "supports_camera_control": False, "supports_image_tail": False, "supports_image_list": False, "supports_element_list": False, "supports_reference_video": False, "supports_sound": True, "supports_voice_list": True, "supports_negative_prompt": False, "supports_cfg_scale": False, "shot_types": ["single"], "supports_watermark": True, }, "kling-video-o1": { "tasks": {"text2video", "image2video", "multi_image2video"}, "modes": ["std", "pro"], "durations": ["5", "10"], "supports_camera_control": False, "supports_image_tail": True, "supports_image_list": True, "supports_element_list": True, "supports_reference_video": True, "supports_sound": False, "supports_voice_list": False, "supports_negative_prompt": False, "supports_cfg_scale": False, "shot_types": ["single"], "supports_watermark": True, }, "kling-v3": { "tasks": {"text2video", "image2video", "multi_image2video"}, "modes": ["std", "pro", "4k"], "durations": ["3", "5", "10", "15"], "supports_camera_control": False, "supports_image_tail": True, "supports_image_list": True, "supports_element_list": True, "supports_reference_video": False, "supports_sound": True, "supports_voice_list": False, "supports_negative_prompt": False, "supports_cfg_scale": False, "shot_types": ["single", "intelligence"], "supports_watermark": True, }, "kling-v3-omni": { "tasks": {"text2video", "image2video", "multi_image2video"}, "modes": ["std", "pro", "4k"], "durations": ["3", "5", "10", "15"], "reference_video_durations": ["3", "5", "10"], "supports_camera_control": False, "supports_image_tail": True, "supports_image_list": True, "supports_element_list": True, "supports_reference_video": True, "supports_sound": True, "supports_voice_list": True, "supports_negative_prompt": False, "supports_cfg_scale": False, "shot_types": ["single", "intelligence"], "supports_watermark": True, }, "kling-v3-turbo": { "tasks": {"text2video", "image2video"}, "modes": ["std", "pro", "4k"], "durations": [str(value) for value in range(3, 16)], "supports_camera_control": False, "supports_image_tail": False, "supports_image_list": False, "supports_element_list": False, "supports_reference_video": False, "supports_sound": False, "supports_voice_list": False, "supports_negative_prompt": False, "supports_cfg_scale": False, "shot_types": ["single"], "supports_watermark": True, }, } # ComfyUI input options are static, so we keep the visible dropdowns aligned # with models confirmed for the active integration paths. Some documented # models may still exist in VIDEO_MODEL_CAPABILITIES for future use, but remain # hidden until endpoint support is confirmed. IMAGE_GENERATION_MODELS = [ "kling-v1", "kling-v1-5", "kling-v2", "kling-v2-new", "kling-v2-1", "kling-v3", ] TEXT_TO_VIDEO_MODELS = [ "kling-v1", "kling-v1-6", "kling-v2-master", "kling-v2-1-master", "kling-v2-5-turbo", "kling-v2-6", "kling-v3", "kling-v3-omni", "kling-v3-turbo", ] IMAGE_TO_VIDEO_MODELS = [ "kling-v1", "kling-v1-5", "kling-v1-6", "kling-v2-master", "kling-v2-1", "kling-v2-1-master", "kling-v2-5-turbo", "kling-v2-6", "kling-v3", "kling-v3-omni", "kling-v3-turbo", ] # Multi-image generation is routed through image2video with a primary `image` # plus supplemental `image_list`. MULTI_IMAGE_TO_VIDEO_MODELS = [ "kling-v2-1", "kling-v2-5-turbo", "kling-v3", "kling-v3-omni", ] def get_image_capability(model_name): if model_name not in IMAGE_MODEL_CAPABILITIES: raise ValueError(f"Unsupported image generation model: {model_name}") return IMAGE_MODEL_CAPABILITIES[model_name] def get_video_capability(model_name): if model_name not in VIDEO_MODEL_CAPABILITIES: raise ValueError(f"Unsupported video generation model: {model_name}") return VIDEO_MODEL_CAPABILITIES[model_name] def validate_image_generation_request( model_name, has_input_image=False, aspect_ratio=None, resolution=None, image_reference_mode="None", image_fidelity=None, human_fidelity=None, ): capability = get_image_capability(model_name) if resolution and resolution not in capability["resolutions"]: raise ValueError( f"Model {model_name} only supports resolutions: {', '.join(capability['resolutions'])}." ) if capability.get("requires_input_image") and not has_input_image: raise ValueError(f"Model {model_name} requires an input image for this node.") if aspect_ratio: supports_auto = capability.get("supports_auto_aspect_on_image_input", False) if aspect_ratio == "auto": if not has_input_image or not supports_auto: raise ValueError(f"Model {model_name} does not support aspect_ratio=auto for this request.") elif aspect_ratio not in capability["aspect_ratios"]: raise ValueError( f"Model {model_name} only supports aspect ratios: {', '.join(capability['aspect_ratios'])}." ) if image_reference_mode != "None" and not capability.get("supports_image_reference", False): raise ValueError(f"Model {model_name} does not support image_reference.") uses_reference_controls = has_input_image or image_reference_mode != "None" if uses_reference_controls and image_fidelity is not None and not capability.get("supports_image_fidelity", False): raise ValueError(f"Model {model_name} does not support image_fidelity.") if uses_reference_controls and human_fidelity is not None and not capability.get("supports_human_fidelity", False): raise ValueError(f"Model {model_name} does not support human_fidelity.") return capability def validate_video_generation_request( task_name, model_name, mode, duration, shot_type="single", has_image_tail=False, has_image_list=False, has_element_list=False, has_reference_video=False, has_sound=False, has_voice_list=False, has_camera_control=False, element_types=None, ): capability = get_video_capability(model_name) if task_name not in capability["tasks"]: raise ValueError(f"Model {model_name} does not support {task_name}.") if mode and mode not in capability["modes"]: raise ValueError(f"Model {model_name} only supports modes: {', '.join(capability['modes'])}.") if duration and duration not in capability["durations"]: raise ValueError( f"Model {model_name} only supports durations: {', '.join(capability['durations'])} seconds." ) if shot_type not in capability["shot_types"]: raise ValueError(f"Model {model_name} does not support shot_type={shot_type}.") if has_camera_control and not capability.get("supports_camera_control", False): raise ValueError(f"Model {model_name} does not support camera control.") if model_name == "kling-v3-omni" and has_reference_video and (has_sound or has_voice_list): raise ValueError("kling-v3-omni cannot use reference_video together with native audio generation.") if model_name == "kling-v3-omni" and mode == "4k" and has_reference_video: raise ValueError("kling-v3-omni does not support mode=4k with reference_video.") if has_sound and not capability.get("supports_sound", False): raise ValueError(f"Model {model_name} does not support sound control.") if has_voice_list and not capability.get("supports_voice_list", False): raise ValueError(f"Model {model_name} does not support voice_list.") if has_image_tail: if task_name == "text2video": raise ValueError("image_tail is only available for image-based video generation.") if not capability.get("supports_image_tail", False): raise ValueError(f"Model {model_name} does not support image_tail.") image_tail_modes = capability.get("image_tail_modes") if image_tail_modes and mode not in image_tail_modes: raise ValueError( f"Model {model_name} only supports image_tail in modes: {', '.join(image_tail_modes)}." ) if has_image_list and not capability.get("supports_image_list", False): raise ValueError(f"Model {model_name} does not support image_list.") if has_element_list and not capability.get("supports_element_list", False): raise ValueError(f"Model {model_name} does not support element_list.") if has_reference_video: if task_name == "text2video": raise ValueError("reference_video is only available for image-based video generation.") if not capability.get("supports_reference_video", False): raise ValueError(f"Model {model_name} does not support reference_video.") reference_video_durations = capability.get("reference_video_durations") if reference_video_durations and duration not in reference_video_durations: raise ValueError( f"Model {model_name} only supports reference_video at durations: " f"{', '.join(reference_video_durations)} seconds." ) if shot_type == "intelligence" and has_image_tail: raise ValueError("Multi-shot generation does not support start and end frames.") if model_name == "kling-video-o1": if duration not in {"5", "10"}: raise ValueError("kling-video-o1 currently only supports 5s or 10s generation in this node.") if has_image_tail and has_element_list: raise ValueError("kling-video-o1 does not support binding elements when using start and end frames.") if element_types and any(element_type == "video_character" for element_type in element_types): raise ValueError("kling-video-o1 only supports multi-image elements, not video-character elements.") return capability