from __future__ import annotations import json try: from .loop_nodes import ANY_TYPE from .camera_config import ( build_camera_config_json, build_camera_orbit_config_json, build_qwen_camera_config_json, camera_angle_choices, camera_detail_choices, camera_distance_choices, camera_lens_choices, camera_mode_choices, camera_orbit_focus_choices, camera_orbit_framing_choices, camera_orientation_choices, camera_phone_choices, camera_priority_choices, camera_shot_choices, ) except ImportError: # Allows local smoke tests from the repository root. from loop_nodes import ANY_TYPE from camera_config import ( build_camera_config_json, build_camera_orbit_config_json, build_qwen_camera_config_json, camera_angle_choices, camera_detail_choices, camera_distance_choices, camera_lens_choices, camera_mode_choices, camera_orbit_focus_choices, camera_orbit_framing_choices, camera_orientation_choices, camera_phone_choices, camera_priority_choices, camera_shot_choices, ) SXCP_CAMERA_CONFIG = "SXCP_CAMERA_CONFIG" class SxCPCameraControl: @classmethod def INPUT_TYPES(cls): return { "required": { "camera_mode": (camera_mode_choices(), {"default": "handheld_selfie"}), "shot_size": (camera_shot_choices(), {"default": "auto"}), "angle": (camera_angle_choices(), {"default": "auto"}), "lens": (camera_lens_choices(), {"default": "smartphone_wide"}), "distance": (camera_distance_choices(), {"default": "arm_length"}), "orientation": (camera_orientation_choices(), {"default": "vertical_story"}), "phone_visibility": (camera_phone_choices(), {"default": "phone_visible"}), "priority": (camera_priority_choices(), {"default": "locked"}), "camera_detail": (camera_detail_choices(), {"default": "compact"}), } } RETURN_TYPES = (SXCP_CAMERA_CONFIG,) RETURN_NAMES = ("camera_config",) FUNCTION = "build" CATEGORY = "prompt_builder" def build( self, camera_mode, shot_size, angle, lens, distance, orientation, phone_visibility, priority, camera_detail, ): return ( build_camera_config_json( camera_mode=camera_mode, shot_size=shot_size, angle=angle, lens=lens, distance=distance, orientation=orientation, phone_visibility=phone_visibility, priority=priority, camera_detail=camera_detail, ), ) class SxCPCameraOrbitControl: @classmethod def INPUT_TYPES(cls): return { "required": { "enabled": ("BOOLEAN", {"default": True}), "camera_mode": (camera_mode_choices(), {"default": "standard"}), "horizontal_angle": ("INT", {"default": 0, "min": 0, "max": 359, "step": 1}), "vertical_angle": ("INT", {"default": 0, "min": -90, "max": 90, "step": 1}), "zoom": ("FLOAT", {"default": 5.0, "min": 0.0, "max": 10.0, "step": 0.1}), "framing": (camera_orbit_framing_choices(), {"default": "from_zoom"}), "subject_focus": (camera_orbit_focus_choices(), {"default": "auto"}), "lens": (camera_lens_choices(), {"default": "auto"}), "orientation": (camera_orientation_choices(), {"default": "auto"}), "phone_visibility": (camera_phone_choices(), {"default": "auto"}), "priority": (camera_priority_choices(), {"default": "locked"}), "camera_detail": (camera_detail_choices(), {"default": "compact"}), "include_degrees": ("BOOLEAN", {"default": True}), } } RETURN_TYPES = (SXCP_CAMERA_CONFIG, "STRING", "STRING") RETURN_NAMES = ("camera_config", "camera_prompt", "camera_info_json") FUNCTION = "build" CATEGORY = "prompt_builder" def build( self, enabled, camera_mode, horizontal_angle, vertical_angle, zoom, framing, subject_focus, lens, orientation, phone_visibility, priority, camera_detail, include_degrees, ): config = build_camera_orbit_config_json( enabled=enabled, camera_mode=camera_mode, horizontal_angle=horizontal_angle, vertical_angle=vertical_angle, zoom=zoom, framing=framing, subject_focus=subject_focus, lens=lens, orientation=orientation, phone_visibility=phone_visibility, priority=priority, camera_detail=camera_detail, include_degrees=include_degrees, ) parsed = json.loads(config) camera_prompt = parsed.get("custom_camera_prompt", "") return config, camera_prompt, json.dumps(parsed, ensure_ascii=True, sort_keys=True) class SxCPQwenCameraTranslator: @classmethod def INPUT_TYPES(cls): return { "required": { "qwen_prompt": ("STRING", {"default": ""}), "prefer_camera_info": ("BOOLEAN", {"default": True}), "camera_mode": (camera_mode_choices(), {"default": "standard"}), "subject_focus": (camera_orbit_focus_choices(), {"default": "auto"}), "lens": (camera_lens_choices(), {"default": "auto"}), "orientation": (camera_orientation_choices(), {"default": "auto"}), "phone_visibility": (camera_phone_choices(), {"default": "auto"}), "priority": (camera_priority_choices(), {"default": "locked"}), "camera_detail": (camera_detail_choices(), {"default": "compact"}), "include_degrees": ("BOOLEAN", {"default": False}), "suppress_phone_visibility": ("BOOLEAN", {"default": True}), }, "optional": { "camera_info": (ANY_TYPE,), }, } RETURN_TYPES = (SXCP_CAMERA_CONFIG, "STRING", "STRING") RETURN_NAMES = ("camera_config", "camera_prompt", "camera_info_json") FUNCTION = "build" CATEGORY = "prompt_builder" def build( self, qwen_prompt, prefer_camera_info, camera_mode, subject_focus, lens, orientation, phone_visibility, priority, camera_detail, include_degrees, suppress_phone_visibility, camera_info=None, ): config = build_qwen_camera_config_json( qwen_prompt=qwen_prompt or "", camera_info=camera_info, prefer_camera_info=prefer_camera_info, camera_mode=camera_mode, subject_focus=subject_focus, lens=lens, orientation=orientation, phone_visibility=phone_visibility, priority=priority, camera_detail=camera_detail, include_degrees=include_degrees, suppress_phone_visibility=suppress_phone_visibility, ) parsed = json.loads(config) camera_prompt = parsed.get("custom_camera_prompt", "") return config, camera_prompt, json.dumps(parsed, ensure_ascii=True, sort_keys=True) NODE_CLASS_MAPPINGS = { "SxCPCameraControl": SxCPCameraControl, "SxCPCameraOrbitControl": SxCPCameraOrbitControl, "SxCPQwenCameraTranslator": SxCPQwenCameraTranslator, } NODE_DISPLAY_NAME_MAPPINGS = { "SxCPCameraControl": "SxCP Camera Control", "SxCPCameraOrbitControl": "SxCP Camera Orbit Control", "SxCPQwenCameraTranslator": "SxCP Qwen Camera Translator", }