2796 lines
138 KiB
Python
2796 lines
138 KiB
Python
# ==========================================================
|
||
# __init__.py — Registro nodi IAMCCS
|
||
# ==========================================================
|
||
|
||
import logging
|
||
import os
|
||
import shutil
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
|
||
def _iamccs_expose_ffmpeg():
|
||
"""Expose the bundled imageio-ffmpeg binary as ffmpeg.exe for Comfy nodes."""
|
||
try:
|
||
import imageio_ffmpeg
|
||
|
||
ffmpeg_path = Path(imageio_ffmpeg.get_ffmpeg_exe())
|
||
if not ffmpeg_path.exists():
|
||
return
|
||
fallback_dir = Path(sys.executable).parent / "ffmpeg-bin"
|
||
shim_dir = Path(os.environ.get("IAMCCS_FFMPEG_SHIM_DIR", fallback_dir))
|
||
shim_dir.mkdir(parents=True, exist_ok=True)
|
||
shim_path = shim_dir / "ffmpeg.exe"
|
||
if not shim_path.exists():
|
||
try:
|
||
os.link(str(ffmpeg_path), str(shim_path))
|
||
except Exception:
|
||
shutil.copy2(str(ffmpeg_path), str(shim_path))
|
||
os.environ["IMAGEIO_FFMPEG_EXE"] = str(shim_path)
|
||
os.environ["PATH"] = str(shim_dir) + os.pathsep + os.environ.get("PATH", "")
|
||
logging.getLogger(__name__).info("[IAMCCS] FFmpeg exposed at %s", shim_path)
|
||
except Exception as exc:
|
||
logging.getLogger(__name__).warning("[IAMCCS] FFmpeg auto-expose skipped: %s", exc)
|
||
|
||
|
||
_iamccs_expose_ffmpeg()
|
||
|
||
|
||
def _iamccs_register_h3_union_model_patch_paths():
|
||
"""Expose legacy shared ControlNet folders to ComfyUI's MODEL_PATCH loader.
|
||
|
||
Current ComfyUI stores MiniMax H3 Fun Union under ``models/model_patches``.
|
||
Existing IAMCCS installations may keep the same 2.3 GB file in a shared
|
||
``models/controlnet`` path (for example X:\\Models). Register only folders
|
||
containing an H3 Fun Union-looking file, avoiding duplicate copies and
|
||
leaving every other ControlNet untouched.
|
||
"""
|
||
try:
|
||
import folder_paths
|
||
|
||
for root in folder_paths.get_folder_paths("controlnet"):
|
||
directory = Path(root)
|
||
if not directory.is_dir():
|
||
continue
|
||
found = any(
|
||
p.is_file()
|
||
and "minimax" in p.name.lower()
|
||
and "h3" in p.name.lower()
|
||
and "controlnet" in p.name.lower()
|
||
for p in directory.rglob("*")
|
||
)
|
||
if found:
|
||
folder_paths.add_model_folder_path("model_patches", str(directory), is_default=False)
|
||
logging.getLogger(__name__).info(
|
||
"[IAMCCS] H3 Fun Union compatibility path registered as model_patches: %s",
|
||
directory,
|
||
)
|
||
except Exception as exc:
|
||
logging.getLogger(__name__).warning("[IAMCCS] H3 model-patch path registration skipped: %s", exc)
|
||
|
||
|
||
_iamccs_register_h3_union_model_patch_paths()
|
||
|
||
from .iamccs_comfy_compat import apply_iamccs_comfy_compat_patches
|
||
|
||
apply_iamccs_comfy_compat_patches()
|
||
|
||
# ComfyUI frontend assets
|
||
WEB_DIRECTORY = "web"
|
||
|
||
from .iamccs_wan_lora_stack import (
|
||
IAMCCS_WanLoRAStack,
|
||
IAMCCS_ModelWithLoRA,
|
||
)
|
||
from .iamccs_wan_lora_stack_simple import (
|
||
IAMCCS_WanLoRAStackModelIO,
|
||
)
|
||
from .iamccs_wan_lora_schedule import (
|
||
IAMCCS_WanLoRASchedule,
|
||
)
|
||
from .iamccs_wan_lora_hooks import (
|
||
IAMCCS_WanLoRAHookSchedule,
|
||
IAMCCS_ApplyLoRAHooksToConditioning,
|
||
IAMCCS_ApplyScheduledWanLoRAFromConditioning,
|
||
IAMCCS_BuildScheduledWanModelBank,
|
||
IAMCCS_SelectScheduledWanModelFromConditioning,
|
||
IAMCCS_SelectScheduledWanModelPairFromConditioning,
|
||
)
|
||
from .iamccs_wan_lora_runtime_bridge import (
|
||
IAMCCS_WanLoRARuntimeBridge,
|
||
IAMCCS_ModelWithLoRA_RuntimeBridge,
|
||
)
|
||
|
||
from .iamccs_ltx2_lora_stack import (
|
||
IAMCCS_LTX2_LoRAStack,
|
||
IAMCCS_LTX2_LoRAStackStaged,
|
||
IAMCCS_ModelWithLoRA_LTX2,
|
||
IAMCCS_ModelWithLoRA_LTX2_Staged,
|
||
IAMCCS_LTX2_LoRAStackModelIO,
|
||
)
|
||
|
||
from .iamccs_ltx2_lora_stack_segmented6 import (
|
||
IAMCCS_LTX2_LoRAStackSegmented6,
|
||
IAMCCS_LTX2_ModelWithLoRA_Segmented6,
|
||
)
|
||
|
||
from .iamccs_ltx2_tools import (
|
||
IAMCCS_LTX2_FrameRateSync,
|
||
IAMCCS_LTX2_Validator,
|
||
IAMCCS_LTX2_TimeFrameCount,
|
||
IAMCCS_LTX2_EnsureFrames8nPlus1,
|
||
IAMCCS_LTX2_EnsureMinFrames,
|
||
IAMCCS_LTX2_ControlPreprocess,
|
||
IAMCCS_LTX2_ImageBatchPadReflect,
|
||
IAMCCS_LTX2_ImageBatchCropByPad,
|
||
IAMCCS_SegmentPlanner,
|
||
IAMCCS_SegmentPlannerSettings,
|
||
IAMCCS_AudioSegmentAutoPlanner,
|
||
IAMCCS_SegmentPlannerLinked,
|
||
IAMCCS_SegmentPlannerFramesLinked,
|
||
IAMCCS_SegmentPlanFromPlanner,
|
||
IAMCCS_SourceRangeFromSegmentPlan,
|
||
IAMCCS_TwoSegmentPlanner,
|
||
IAMCCS_ThreeSegmentPlanner,
|
||
IAMCCS_SegmentSwitch,
|
||
)
|
||
|
||
from .iamccs_ltx2_extension_module import (
|
||
IAMCCS_LTX2_ExtensionModule,
|
||
IAMCCS_LTX2_ExtensionModule_Disk,
|
||
IAMCCS_LTX2_JointRefreshLatent,
|
||
IAMCCS_LTX2_JointRefreshLatent_Disk,
|
||
IAMCCS_LoadImagesFromDirLite,
|
||
IAMCCS_ImageBatchRangeLite,
|
||
IAMCCS_SourceFramesToDisk,
|
||
IAMCCS_StartDirToVideoLatent,
|
||
IAMCCS_StartImagesToVideoLatent,
|
||
IAMCCS_VideoCombineFromDir,
|
||
IAMCCS_LTX2_ExtensionModule_simple,
|
||
IAMCCS_LTX2_GetImageFromBatch,
|
||
IAMCCS_LTX2_ReferenceImageSwitch,
|
||
IAMCCS_LTX2_ReferenceStartFramesInjector,
|
||
IAMCCS_LTX2_FrameCountValidator,
|
||
IAMCCS_LTX2_FirstLastFramesController,
|
||
IAMCCS_LTX2_ContextLatent,
|
||
IAMCCS_LTX2_MiddleFrames,
|
||
IAMCCS_LTX2_FirstLastLatentControl,
|
||
IAMCCS_LTX2_FirstLastLatentControl_Pro,
|
||
)
|
||
|
||
from .iamccs_ltx_guide_port import (
|
||
IAMCCS_WDC_MultiImageLoader,
|
||
IAMCCS_WDC_LTXKeyframer,
|
||
IAMCCS_WDC_LTXSequencer,
|
||
IAMCCS_CineLTXSequencerExact,
|
||
IAMCCS_CineFLFEngineSimple,
|
||
IAMCCS_WDC_LTXSequencerFixed5,
|
||
)
|
||
|
||
from .iamccs_cine_nodes import (
|
||
IAMCCS_CineReferenceBoard,
|
||
IAMCCS_CineLTXSequencer,
|
||
IAMCCS_CineAllInOneFLFEngine,
|
||
IAMCCS_CinePromptRelayTimeline,
|
||
IAMCCS_CineShotboardTimelinePro,
|
||
IAMCCS_CineShotboardPlannerPro,
|
||
IAMCCS_CineShotboardPlannerProV2,
|
||
IAMCCS_CineShotboardPlannerV3,
|
||
IAMCCS_CineShotboardLite,
|
||
IAMCCS_CineShotboardPlannerProLegacy,
|
||
IAMCCS_CineInfo,
|
||
IAMCCS_CineInfoV2,
|
||
IAMCCS_CineFLFProductor,
|
||
IAMCCS_CineFilmmaker,
|
||
IAMCCS_CineFilmmakerBackend,
|
||
IAMCCS_CineShotboardBackendPro,
|
||
IAMCCS_CineFilmmakerGuide,
|
||
IAMCCS_CineFilmmakerGuide1to1,
|
||
IAMCCS_CineFilmmakerCropGuides1to1,
|
||
IAMCCS_CineSwitch,
|
||
IAMCCS_CinePromptRelayLatentShapeSync,
|
||
IAMCCS_CineFLFLengthCompensator,
|
||
IAMCCS_CinePromptRelaySafeEncode,
|
||
IAMCCS_CineRelayOrBypass,
|
||
IAMCCS_CinePromptArchitect,
|
||
IAMCCS_BoardMaker,
|
||
IAMCCS_CineMusicVideoPlanner,
|
||
IAMCCS_CineShotPlanner,
|
||
IAMCCS_CineRefLatentControl,
|
||
IAMCCS_CineAudioPromptDirector,
|
||
IAMCCS_CinePromptRelayAdapter,
|
||
IAMCCS_CinePromptComposer,
|
||
IAMCCS_CineShotLineBuilder,
|
||
IAMCCS_CineV2VTimelineLineBuilder,
|
||
IAMCCS_CineLineStacker,
|
||
IAMCCS_CineMultiGenDirector,
|
||
IAMCCS_CineShotAudioDirector,
|
||
IAMCCS_CineV2VTimelineDirector,
|
||
IAMCCS_CineV2VAssetSelector,
|
||
IAMCCS_CineWorkflowInspector,
|
||
)
|
||
from .iamccs_cine_shotboard_planner_v4 import IAMCCS_CineShotboardPlannerV4
|
||
from .iamccs_minimax_h3_shotboard import (
|
||
NODE_CLASS_MAPPINGS as IAMCCS_MINIMAX_H3_NODE_CLASS_MAPPINGS,
|
||
NODE_DISPLAY_NAME_MAPPINGS as IAMCCS_MINIMAX_H3_NODE_DISPLAY_NAME_MAPPINGS,
|
||
)
|
||
from .iamccs_prompter import (
|
||
NODE_CLASS_MAPPINGS as IAMCCS_PROMPTER_NODE_CLASS_MAPPINGS,
|
||
NODE_DISPLAY_NAME_MAPPINGS as IAMCCS_PROMPTER_NODE_DISPLAY_NAME_MAPPINGS,
|
||
)
|
||
from .cine_nodes import (
|
||
NODE_CLASS_MAPPINGS as IAMCCS_CINE_NODES_NODE_CLASS_MAPPINGS,
|
||
NODE_DISPLAY_NAME_MAPPINGS as IAMCCS_CINE_NODES_NODE_DISPLAY_NAME_MAPPINGS,
|
||
)
|
||
from .iamccs_cine_h3_vision_info import (
|
||
NODE_CLASS_MAPPINGS as IAMCCS_CINE_H3_VISION_NODE_CLASS_MAPPINGS,
|
||
NODE_DISPLAY_NAME_MAPPINGS as IAMCCS_CINE_H3_VISION_NODE_DISPLAY_NAME_MAPPINGS,
|
||
)
|
||
from .iamccs_cine_shotboard_planner_v5_v2v import (
|
||
IAMCCS_CineShotboardPlannerV5V2V,
|
||
IAMCCS_CineShotboardV5V2VRouter,
|
||
)
|
||
from .iamccs_cine_shotboard_v4_backend import (
|
||
IAMCCS_CineShotboardV4Backend,
|
||
IAMCCS_CineShotboardV4Guide,
|
||
IAMCCS_CineShotboardV4CropGuides,
|
||
IAMCCS_LTXVideoDurationCrop,
|
||
)
|
||
from .iamccs_cine_resolution_parity import IAMCCS_CineResolutionParityTranslator
|
||
from .iamccs_cine_stage_switch import IAMCCS_CineStage2BypassSwitch
|
||
from .iamccs_cine_stage2_preview_toggle import IAMCCS_CineStage2PreviewToggle
|
||
from .iamccs_cine_flf_productor_dyno import IAMCCS_CineFLFProductorDyno
|
||
from .iamccs_cine_flf_engine_simple_dyno import IAMCCS_CineFLFEngineSimpleDyno
|
||
from .iamccs_cine_duration_lock import (
|
||
IAMCCS_CineBoardDurationLock,
|
||
IAMCCS_CineLatentDurationCrop,
|
||
IAMCCS_CineShotboardTailTrimPolicy,
|
||
IAMCCS_CineShotboardFinalFrameTrim,
|
||
)
|
||
from .iamccs_shotboarder_exporter_pro import IAMCCS_ShotboarderAudVidExporterPRO
|
||
from .iamccs_cine_temporal_cut_barrier import IAMCCS_CineTemporalCutBarrier
|
||
|
||
from .iamccs_ltx2_temporal_overlap_samplers import (
|
||
IAMCCS_LTX2_ConditionNextLatentWithPrevOverlap,
|
||
IAMCCS_LTX2_InitLatentSampler,
|
||
IAMCCS_LTX2_LoopingSampler,
|
||
IAMCCS_LTX2_OneShotLowRAMLooper,
|
||
IAMCCS_LTX2_ExtendSampler,
|
||
)
|
||
|
||
from .iamccs_wan_svipro_motion import (
|
||
IAMCCS_WanImageMotion,
|
||
WanImageMotionPro as WanImageMotionProPlus,
|
||
IAMCCS_WanImageMotionPro_Simple as IAMCCS_WanImageMotionProPlus_Simple,
|
||
IAMCCS_WanImageMotionInductive,
|
||
IAMCCS_WanSVIToFLFBridgePro as IAMCCS_WanSVIToFLFBridgeProPlus,
|
||
IAMCCS_WanSVIToFLFBridgePro_Simple as IAMCCS_WanSVIToFLFBridgeProPlus_Simple,
|
||
WanMotionProTrimmer,
|
||
IAMCCS_WanPrevTailPrep,
|
||
)
|
||
|
||
from .iamccs_wan_long_length import (
|
||
IAMCCS_WanSviFlfTimeline,
|
||
IAMCCS_WanSviFlfTimelinePick,
|
||
IAMCCS_WanLongPlanner,
|
||
IAMCCS_WanContinuityGuide,
|
||
IAMCCS_WanPromptPhasePlanner,
|
||
IAMCCS_WanPromptLoopInfo,
|
||
IAMCCS_WanIndexedPromptEncode,
|
||
IAMCCS_WanImageBatchFrameSelect,
|
||
)
|
||
|
||
from .iamccs_wan_svipro_motion_legacy import (
|
||
WanImageMotionProLegacy,
|
||
)
|
||
|
||
from .iamccs_autolink import (
|
||
IAMCCS_SetAutoLink,
|
||
IAMCCS_GetAutoLink,
|
||
IAMCCS_AutoLinkConverter,
|
||
IAMCCS_AutoLinkArguments,
|
||
)
|
||
|
||
from .iamccs_gguf_accelerator import (
|
||
IAMCCS_GGUF_accelerator,
|
||
)
|
||
|
||
from .iamccs_sampler_advanced_v1 import (
|
||
IAMCCS_SamplerAdvancedVersion1,
|
||
)
|
||
|
||
from .iamccs_influencer_sampler import (
|
||
IAMCCSInfluencerSampler,
|
||
IAMCCSInfluencerLatent,
|
||
)
|
||
|
||
from .iamccs_bus_group import (
|
||
IAMCCS_bus_group,
|
||
)
|
||
|
||
from .iamccs_image_resize import (
|
||
IAMCCS_ImageResizeBatchSafe,
|
||
IAMCCS_LoadResizeSegmentFromDir,
|
||
)
|
||
|
||
from .iamccs_multiswitch import (
|
||
IAMCCS_MultiSwitch,
|
||
)
|
||
|
||
from .iamccs_lazy_switch import (
|
||
IAMCCS_LazyAnySwitch,
|
||
)
|
||
|
||
from .iamccs_navigator import (
|
||
IAMCCS_Navigator,
|
||
)
|
||
|
||
from .iamccs_hw_supporter import (
|
||
IAMCCS_HwSupporter,
|
||
IAMCCS_HwSupporterAny,
|
||
IAMCCS_HardMemoryPurge,
|
||
IAMCCS_VRAMCleanup,
|
||
IAMCCS_VRAMFlushLatent,
|
||
IAMCCS_VAEDecodeTiledSafe,
|
||
IAMCCS_VAEDecodeToDisk,
|
||
)
|
||
|
||
from .iamccs_hw_probe_node import (
|
||
IAMCCS_HWProbeRecommendations,
|
||
)
|
||
|
||
from .iamccs_detail_atelier import (
|
||
IAMCCS_DetailAtelier,
|
||
IAMCCS_DetailAtelierAdvanced,
|
||
IAMCCS_DetailAtelierSampler,
|
||
)
|
||
|
||
from .iamccs_qwen_vl_flf import (
|
||
IAMCCS_QWEN_VL_FLF,
|
||
IAMCCS_QWEN_VL_FLF_Advanced,
|
||
)
|
||
|
||
from .iamccs_move_ahead import (
|
||
IAMCCS_MoveAhead,
|
||
IAMCCS_MoveAheadEnforcer,
|
||
IAMCCS_MotionScale,
|
||
IAMCCS_MotionScaleAdvanced,
|
||
)
|
||
|
||
from .iamccs_motion_bridge import (
|
||
IAMCCS_MotionBridgeSave,
|
||
IAMCCS_MotionBridgeLoad,
|
||
IAMCCS_LatentTailSlice,
|
||
)
|
||
|
||
from .iamccs_audio_extender import (
|
||
IAMCCS_AudioExtensionMath,
|
||
IAMCCS_AudioExtender,
|
||
IAMCCS_AudioTimelineAssembler,
|
||
IAMCCS_AudioTimelineGate,
|
||
)
|
||
|
||
from .iamccs_cine_audio_dialogue import (
|
||
IAMCCS_CineSpeech1PromptCompiler,
|
||
IAMCCS_CineAudioTranscriptPromptCompiler,
|
||
IAMCCS_CineVideoToWooshInputs,
|
||
IAMCCS_CineTimelineAudioMixer,
|
||
IAMCCS_AudioBoardArranger,
|
||
IAMCCS_CineDialogueLineRouter,
|
||
IAMCCS_CineInfo3,
|
||
IAMCCS_BoardMaker_DialogueFoley,
|
||
IAMCCS_CineSpeechLength,
|
||
IAMCCS_CineDialogueDurationPlanner,
|
||
IAMCCS_CineAudioDurationProbe,
|
||
IAMCCS_CineDialogueTimingReconciler,
|
||
IAMCCS_CineWooshFoleyChunkPlanner,
|
||
IAMCCS_CineFinalAudioMixer,
|
||
IAMCCS_CineEmotionButtons,
|
||
IAMCCS_CineDialoguePromptKit,
|
||
)
|
||
from .audio.audio_bus_out import IAMCCS_BusOut
|
||
from .iamccs_audioboard_direct_adapter import IAMCCS_AudioBoardDirectShotboardAdapter
|
||
from .audio.audio_board_mixer import IAMCCS_AudioBoardMixer
|
||
from .audio.audio_control_efx import IAMCCS_ControlAudEfx
|
||
from .audio.audio_control_efx_panel import IAMCCS_ControlAudEfxPanel
|
||
from .audio.dialogue_tag_editor import IAMCCS_DialogueTagEditor, IAMCCS_DialogueAudioBoardBridge
|
||
from .audio.cine_audio_info import IAMCCS_CineAudioInfo
|
||
from .audio.audio_phase_lazy import IAMCCS_AudioPhaseLazyGate
|
||
from .iamccs_ideogram_storyboard_frame_designer import (
|
||
IAMCCS_StoryboardFrameDesigner,
|
||
IAMCCS_StoryboardFrameDesignerV2,
|
||
IAMCCS_IdeoInfo,
|
||
IAMCCS_IdeoInpaintPrep,
|
||
IAMCCS_IdeoMaskedPixels,
|
||
IAMCCS_IdeogramJSONPreviewPass,
|
||
IAMCCS_IdeoNoiseDebug,
|
||
IAMCCS_IdeoRandomNoiseNoCache,
|
||
IAMCCS_IdeoImageHashDebug,
|
||
)
|
||
from .iamccs_ideo_translate import IAMCCS_IdeoTranslate
|
||
from .iamccs_ideogram_storyboard_sheet import IAMCCS_IdeogramStoryboardSheet, IAMCCS_StoryboardCaptionSheet
|
||
from .iamccs_ideogram_sheet_builder import IAMCCS_IdeogramSheetBuilder
|
||
from .iamccs_storyboard_auto_crop import IAMCCS_StoryboardAutoCropGrid, IAMCCS_StoryboardAutoCropGridPRO
|
||
from .iamccs_target_crop import IAMCCS_TargetCrop
|
||
from .iamccs_gemma_assist import IAMCCS_GemmaAssistLazyGate, IAMCCS_GemmaAssistOutput
|
||
from .iamccs_storyboard_prompt_contact_sheet import IAMCCS_StoryboardPromptContactSheet
|
||
from .iamccs_goyai_paint import IAMCCS_GoyAICanvasPaint
|
||
from .iamccs_flashvsr_bridge import IAMCCS_FlashVSRPanelBatchPrep, IAMCCS_FlashVSRPanelBatchRestore
|
||
|
||
from .iamccs_ltx2_segment_queue import (
|
||
IAMCCS_LTX2_BlendLatentBridge,
|
||
IAMCCS_LTX2_LastFrameBridgeLoad,
|
||
IAMCCS_LTX2_LastFrameBridgeSave,
|
||
IAMCCS_LTX2_LoadLatentBridge,
|
||
IAMCCS_LTX2_LongVideoWrapperPrep,
|
||
IAMCCS_LTX2_LongVideoWrapperPrepDisk,
|
||
IAMCCS_LTX2_SaveLatentBridge,
|
||
IAMCCS_LTX2_SegmentQueueLoop,
|
||
)
|
||
|
||
from .iamccs_image_resize import (
|
||
IAMCCS_ImageResizeBatchSafe,
|
||
)
|
||
|
||
from .iamccs_value_monitor import (
|
||
IAMCCS_IntValueMonitor,
|
||
)
|
||
|
||
from .iamccs_flux_klein_multigen import (
|
||
IAMCCS_FluxKleinMultiGen,
|
||
IAMCCS_FluxKleinRefine,
|
||
IAMCCS_ImageBatch6,
|
||
)
|
||
|
||
from .iamccs_qwen_multigen import (
|
||
IAMCCS_QwenMultiGen,
|
||
)
|
||
|
||
from .iamccs_krea2_multigen import (
|
||
IAMCCS_Krea2MultiGen,
|
||
)
|
||
|
||
from .iamccs_redhead_node import (
|
||
IAMCCSRedheadNode,
|
||
)
|
||
|
||
from .iamccs_multiline_prompt_splitter import (
|
||
IAMCCS_MultilinePromptSplitter8,
|
||
)
|
||
|
||
from .iamccs_supernode_modular import (
|
||
IAMCCS_SupernodeBase,
|
||
IAMCCS_SupernodeModule,
|
||
)
|
||
|
||
from .iamccs_auimg2vid_goal1 import (
|
||
IAMCCS_ProjectTimelinePlanner,
|
||
IAMCCS_Ltx2HelperModules_ProjectTimelinePlanner,
|
||
IAMCCS_Ltx2HelperModules_Planner,
|
||
IAMCCS_Ltx2HelperModules_AudioTimeline,
|
||
IAMCCS_Ltx2HelperModules_KeyframeTimeline,
|
||
IAMCCS_Ltx2HelperModules_RefreshPolicy,
|
||
IAMCCS_Ltx2HelperModules_ReanchorLatent,
|
||
IAMCCS_Ltx2HelperModules_DiskExtension,
|
||
IAMCCS_Ltx2HelperModules_RuntimeBridge,
|
||
IAMCCS_Ltx2HelperModules_Continuity,
|
||
IAMCCS_Ltx2HelperModules_Finalize,
|
||
IAMCCS_AUIMG2VID_ProjectTimelinePlanner,
|
||
IAMCCS_AUIMG2VID_Planner,
|
||
IAMCCS_AUIMG2VID_AudioTimeline,
|
||
IAMCCS_AUIMG2VID_KeyframeTimeline,
|
||
IAMCCS_AUIMG2VID_RefreshPolicy,
|
||
IAMCCS_AUIMG2VID_ReanchorLatent,
|
||
IAMCCS_AUIMG2VID_DiskExtension,
|
||
IAMCCS_AUIMG2VID_RuntimeBridge,
|
||
IAMCCS_AUIMG2VID_Continuity,
|
||
IAMCCS_AUIMG2VID_Finalize,
|
||
)
|
||
|
||
from .iamccs_supernodes_exec import (
|
||
IAMCCS_SuperNodes_AUIMG2VIDExecutablePlanner,
|
||
IAMCCS_SuperNodes_AUIMG2VIDExecutableRender,
|
||
IAMCCS_SuperNodes_AUIMG2VIDExecutableVAE,
|
||
IAMCCS_SuperNodes_AUIMG2VIDExecutableFinalize,
|
||
)
|
||
from .iamccs_supernodes_second_stage import IAMCCS_SuperNodes_SecondStage
|
||
|
||
try:
|
||
from .iamccs_scail_identity import (
|
||
IAMCCS_ScailIdentitySeeder,
|
||
IAMCCS_ScailIdentityTracker,
|
||
IAMCCS_ScailMultiReference,
|
||
)
|
||
except Exception as exc:
|
||
logging.warning("IAMCCS SCAIL Identity nodes unavailable: %s", exc)
|
||
IAMCCS_ScailIdentitySeeder = None
|
||
IAMCCS_ScailIdentityTracker = None
|
||
IAMCCS_ScailMultiReference = None
|
||
|
||
try:
|
||
from .iamccs_scail_extends import (
|
||
IAMCCS_ScailExtends,
|
||
IAMCCS_ScailExtendPlan,
|
||
)
|
||
except Exception as exc:
|
||
logging.warning("IAMCCS SCAIL Extends nodes unavailable: %s", exc)
|
||
IAMCCS_ScailExtends = None
|
||
IAMCCS_ScailExtendPlan = None
|
||
|
||
try:
|
||
from .iamccs_wananimate_extends import (
|
||
IAMCCS_WanAnimateExtends,
|
||
IAMCCS_WanAnimateExtendPlan,
|
||
)
|
||
except Exception as exc:
|
||
logging.warning("IAMCCS WanAnimate Extends nodes unavailable: %s", exc)
|
||
IAMCCS_WanAnimateExtends = None
|
||
IAMCCS_WanAnimateExtendPlan = None
|
||
|
||
try:
|
||
from .iamccs_wananimate2_extends import (
|
||
IAMCCS_WanAnimate2Extends,
|
||
IAMCCS_WanAnimate2ExtendPlan,
|
||
)
|
||
except Exception as exc:
|
||
logging.warning("IAMCCS Wan-Animate-2 Extends nodes unavailable: %s", exc)
|
||
IAMCCS_WanAnimate2Extends = None
|
||
IAMCCS_WanAnimate2ExtendPlan = None
|
||
|
||
try:
|
||
from .v2v_shotboard_easy_wananimate2 import IAMCCS_WanAnimate2ShotboardBridge
|
||
except Exception as exc:
|
||
logging.warning("IAMCCS Wan-Animate-2 Shotboard bridge unavailable: %s", exc)
|
||
IAMCCS_WanAnimate2ShotboardBridge = None
|
||
|
||
# Nodi principali
|
||
NODE_CLASS_MAPPINGS = {
|
||
"IAMCCS_WanLoRAStack": IAMCCS_WanLoRAStack,
|
||
"IAMCCS_ModelWithLoRA": IAMCCS_ModelWithLoRA,
|
||
"IAMCCS_WanLoRAStackModelIO": IAMCCS_WanLoRAStackModelIO,
|
||
"IAMCCS_WanLoRASchedule": IAMCCS_WanLoRASchedule,
|
||
"IAMCCS_WanLoRAHookSchedule": IAMCCS_WanLoRAHookSchedule,
|
||
"IAMCCS_ApplyLoRAHooksToConditioning": IAMCCS_ApplyLoRAHooksToConditioning,
|
||
"IAMCCS_ApplyScheduledWanLoRAFromConditioning": IAMCCS_ApplyScheduledWanLoRAFromConditioning,
|
||
"IAMCCS_BuildScheduledWanModelBank": IAMCCS_BuildScheduledWanModelBank,
|
||
"IAMCCS_SelectScheduledWanModelFromConditioning": IAMCCS_SelectScheduledWanModelFromConditioning,
|
||
"IAMCCS_SelectScheduledWanModelPairFromConditioning": IAMCCS_SelectScheduledWanModelPairFromConditioning,
|
||
"IAMCCS_WanLoRARuntimeBridge": IAMCCS_WanLoRARuntimeBridge,
|
||
"IAMCCS_ModelWithLoRA_RuntimeBridge": IAMCCS_ModelWithLoRA_RuntimeBridge,
|
||
# Backward-compatible key (kept as-is for existing workflows)
|
||
"iamccs_ltx2_lora_stack": IAMCCS_LTX2_LoRAStack,
|
||
# Preferred explicit names
|
||
"IAMCCS_LTX2_LoRAStack": IAMCCS_LTX2_LoRAStack,
|
||
"IAMCCS_LTX2_LoRAStackStaged": IAMCCS_LTX2_LoRAStackStaged,
|
||
"IAMCCS_ModelWithLoRA_LTX2": IAMCCS_ModelWithLoRA_LTX2,
|
||
"IAMCCS_ModelWithLoRA_LTX2_Staged": IAMCCS_ModelWithLoRA_LTX2_Staged,
|
||
"IAMCCS_LTX2_LoRAStackModelIO": IAMCCS_LTX2_LoRAStackModelIO,
|
||
"IAMCCS_LTX2_LoRAStackSegmented6": IAMCCS_LTX2_LoRAStackSegmented6,
|
||
"IAMCCS_LTX2_ModelWithLoRA_Segmented6": IAMCCS_LTX2_ModelWithLoRA_Segmented6,
|
||
|
||
"IAMCCS_LTX2_FrameRateSync": IAMCCS_LTX2_FrameRateSync,
|
||
"IAMCCS_LTX2_Validator": IAMCCS_LTX2_Validator,
|
||
"IAMCCS_LTX2_TimeFrameCount": IAMCCS_LTX2_TimeFrameCount,
|
||
"IAMCCS_LTX2_EnsureFrames8nPlus1": IAMCCS_LTX2_EnsureFrames8nPlus1,
|
||
"IAMCCS_LTX2_EnsureMinFrames": IAMCCS_LTX2_EnsureMinFrames,
|
||
"IAMCCS_LTX2_ControlPreprocess": IAMCCS_LTX2_ControlPreprocess,
|
||
"IAMCCS_LTX2_ImageBatchPadReflect": IAMCCS_LTX2_ImageBatchPadReflect,
|
||
"IAMCCS_LTX2_ImageBatchCropByPad": IAMCCS_LTX2_ImageBatchCropByPad,
|
||
"IAMCCS_SegmentPlanner": IAMCCS_SegmentPlanner,
|
||
"IAMCCS_SegmentPlannerSettings": IAMCCS_SegmentPlannerSettings,
|
||
"IAMCCS_AudioSegmentAutoPlanner": IAMCCS_AudioSegmentAutoPlanner,
|
||
"IAMCCS_SegmentPlannerLinked": IAMCCS_SegmentPlannerLinked,
|
||
"IAMCCS_SegmentPlannerFramesLinked": IAMCCS_SegmentPlannerFramesLinked,
|
||
"IAMCCS_SegmentPlanFromPlanner": IAMCCS_SegmentPlanFromPlanner,
|
||
"IAMCCS_SourceRangeFromSegmentPlan": IAMCCS_SourceRangeFromSegmentPlan,
|
||
"IAMCCS_TwoSegmentPlanner": IAMCCS_TwoSegmentPlanner,
|
||
"IAMCCS_ThreeSegmentPlanner": IAMCCS_ThreeSegmentPlanner,
|
||
"IAMCCS_SegmentSwitch": IAMCCS_SegmentSwitch,
|
||
"IAMCCS_LTX2_ExtensionModule": IAMCCS_LTX2_ExtensionModule,
|
||
"IAMCCS_LTX2_ExtensionModule_Disk": IAMCCS_LTX2_ExtensionModule_Disk,
|
||
"IAMCCS_LTX2_JointRefreshLatent": IAMCCS_LTX2_JointRefreshLatent,
|
||
"IAMCCS_LTX2_JointRefreshLatent_Disk": IAMCCS_LTX2_JointRefreshLatent_Disk,
|
||
"IAMCCS_LoadImagesFromDirLite": IAMCCS_LoadImagesFromDirLite,
|
||
"IAMCCS_ImageBatchRangeLite": IAMCCS_ImageBatchRangeLite,
|
||
"IAMCCS_SourceFramesToDisk": IAMCCS_SourceFramesToDisk,
|
||
"IAMCCS_StartDirToVideoLatent": IAMCCS_StartDirToVideoLatent,
|
||
"IAMCCS_StartImagesToVideoLatent": IAMCCS_StartImagesToVideoLatent,
|
||
"IAMCCS_VideoCombineFromDir": IAMCCS_VideoCombineFromDir,
|
||
"IAMCCS_LTX2_ExtensionModule_simple": IAMCCS_LTX2_ExtensionModule_simple,
|
||
"IAMCCS_LTX2_GetImageFromBatch": IAMCCS_LTX2_GetImageFromBatch,
|
||
"IAMCCS_LTX2_ReferenceImageSwitch": IAMCCS_LTX2_ReferenceImageSwitch,
|
||
"IAMCCS_LTX2_ReferenceStartFramesInjector": IAMCCS_LTX2_ReferenceStartFramesInjector,
|
||
"IAMCCS_LTX2_FrameCountValidator": IAMCCS_LTX2_FrameCountValidator,
|
||
"IAMCCS_LTX2_FirstLastFramesController": IAMCCS_LTX2_FirstLastFramesController,
|
||
"IAMCCS_LTX2_ContextLatent": IAMCCS_LTX2_ContextLatent,
|
||
"IAMCCS_LTX2_MiddleFrames": IAMCCS_LTX2_MiddleFrames,
|
||
"IAMCCS_LTX2_FirstLastLatentControl": IAMCCS_LTX2_FirstLastLatentControl,
|
||
"IAMCCS_LTX2_FirstLastLatentControl_Pro": IAMCCS_LTX2_FirstLastLatentControl_Pro,
|
||
"IAMCCS_CineReferenceBoard": IAMCCS_CineReferenceBoard,
|
||
"IAMCCS_CineLTXSequencer": IAMCCS_CineLTXSequencer,
|
||
"IAMCCS_CineAllInOneFLFEngine": IAMCCS_CineAllInOneFLFEngine,
|
||
"IAMCCS_CinePromptRelayTimeline": IAMCCS_CinePromptRelayTimeline,
|
||
"IAMCCS_CineShotboardTimelinePro": IAMCCS_CineShotboardTimelinePro,
|
||
"IAMCCS_CineShotboardPlannerPro": IAMCCS_CineShotboardPlannerPro,
|
||
"IAMCCS_CineShotboardPlannerProV2": IAMCCS_CineShotboardPlannerProV2,
|
||
"IAMCCS_CineShotboardPlannerV3": IAMCCS_CineShotboardPlannerV3,
|
||
"IAMCCS_CineShotboardPlannerV4": IAMCCS_CineShotboardPlannerV4,
|
||
"IAMCCS_CineShotboardPlannerV5V2V": IAMCCS_CineShotboardPlannerV5V2V,
|
||
"IAMCCS_CineShotboardV5V2VRouter": IAMCCS_CineShotboardV5V2VRouter,
|
||
"IAMCCS_CineShotboardV4Backend": IAMCCS_CineShotboardV4Backend,
|
||
"IAMCCS_CineShotboardV4Guide": IAMCCS_CineShotboardV4Guide,
|
||
"IAMCCS_CineShotboardV4CropGuides": IAMCCS_CineShotboardV4CropGuides,
|
||
"IAMCCS_LTXVideoDurationCrop": IAMCCS_LTXVideoDurationCrop,
|
||
"IAMCCS_CineShotboardLite": IAMCCS_CineShotboardLite,
|
||
"IAMCCS_CineShotboardPlannerProLegacy": IAMCCS_CineShotboardPlannerProLegacy,
|
||
"IAMCCS_CineResolutionParityTranslator": IAMCCS_CineResolutionParityTranslator,
|
||
"IAMCCS_CineStage2BypassSwitch": IAMCCS_CineStage2BypassSwitch,
|
||
"IAMCCS_CineStage2PreviewToggle": IAMCCS_CineStage2PreviewToggle,
|
||
"IAMCCS_CineInfo": IAMCCS_CineInfo,
|
||
"IAMCCS_CineInfoV2": IAMCCS_CineInfoV2,
|
||
"IAMCCS_CineFLFProductor": IAMCCS_CineFLFProductor,
|
||
"IAMCCS_CineFLFProductorDyno": IAMCCS_CineFLFProductorDyno,
|
||
"IAMCCS_CineFilmmaker": IAMCCS_CineFilmmaker,
|
||
"IAMCCS_CineFilmmakerBackend": IAMCCS_CineFilmmakerBackend,
|
||
"IAMCCS_CineShotboardBackendPro": IAMCCS_CineShotboardBackendPro,
|
||
"IAMCCS_CineFilmmakerGuide": IAMCCS_CineFilmmakerGuide,
|
||
"IAMCCS_CineFilmmakerGuide1to1": IAMCCS_CineFilmmakerGuide1to1,
|
||
"IAMCCS_CineFilmmakerCropGuides1to1": IAMCCS_CineFilmmakerCropGuides1to1,
|
||
"IAMCCS_CineSwitch": IAMCCS_CineSwitch,
|
||
"IAMCCS_CinePromptRelayLatentShapeSync": IAMCCS_CinePromptRelayLatentShapeSync,
|
||
"IAMCCS_CineFLFLengthCompensator": IAMCCS_CineFLFLengthCompensator,
|
||
"IAMCCS_CineBoardDurationLock": IAMCCS_CineBoardDurationLock,
|
||
"IAMCCS_CineLatentDurationCrop": IAMCCS_CineLatentDurationCrop,
|
||
"IAMCCS_CineShotboardTailTrimPolicy": IAMCCS_CineShotboardTailTrimPolicy,
|
||
"IAMCCS_CineShotboardFinalFrameTrim": IAMCCS_CineShotboardFinalFrameTrim,
|
||
"IAMCCS_shotboarder_aud+vid_exporter_PRO": IAMCCS_ShotboarderAudVidExporterPRO,
|
||
"IAMCCS_CineTemporalCutBarrier": IAMCCS_CineTemporalCutBarrier,
|
||
"IAMCCS_CinePromptRelaySafeEncode": IAMCCS_CinePromptRelaySafeEncode,
|
||
"IAMCCS_CineRelayOrBypass": IAMCCS_CineRelayOrBypass,
|
||
"IAMCCS_CinePromptArchitect": IAMCCS_CinePromptArchitect,
|
||
"IAMCCS_BoardMaker": IAMCCS_BoardMaker,
|
||
"IAMCCS_CineMusicVideoPlanner": IAMCCS_CineMusicVideoPlanner,
|
||
"IAMCCS_CineShotPlanner": IAMCCS_CineShotPlanner,
|
||
"IAMCCS_CineRefLatentControl": IAMCCS_CineRefLatentControl,
|
||
"IAMCCS_CineAudioPromptDirector": IAMCCS_CineAudioPromptDirector,
|
||
"IAMCCS_CinePromptRelayAdapter": IAMCCS_CinePromptRelayAdapter,
|
||
"IAMCCS_CinePromptComposer": IAMCCS_CinePromptComposer,
|
||
"IAMCCS_CineShotLineBuilder": IAMCCS_CineShotLineBuilder,
|
||
"IAMCCS_CineV2VTimelineLineBuilder": IAMCCS_CineV2VTimelineLineBuilder,
|
||
"IAMCCS_CineLineStacker": IAMCCS_CineLineStacker,
|
||
"IAMCCS_CineMultiGenDirector": IAMCCS_CineMultiGenDirector,
|
||
"IAMCCS_CineShotAudioDirector": IAMCCS_CineShotAudioDirector,
|
||
"IAMCCS_CineV2VTimelineDirector": IAMCCS_CineV2VTimelineDirector,
|
||
"IAMCCS_CineV2VAssetSelector": IAMCCS_CineV2VAssetSelector,
|
||
"IAMCCS_CineWorkflowInspector": IAMCCS_CineWorkflowInspector,
|
||
"IAMCCS_WDC_MultiImageLoader": IAMCCS_WDC_MultiImageLoader,
|
||
"IAMCCS_WDC_LTXKeyframer": IAMCCS_WDC_LTXKeyframer,
|
||
"IAMCCS_WDC_LTXSequencer": IAMCCS_WDC_LTXSequencer,
|
||
"IAMCCS_CineLTXSequencerExact": IAMCCS_CineLTXSequencerExact,
|
||
"IAMCCS_CineFLFEngineSimple": IAMCCS_CineFLFEngineSimple,
|
||
"IAMCCS_CineFLFEngineSimpleDyno": IAMCCS_CineFLFEngineSimpleDyno,
|
||
"IAMCCS_WDC_LTXSequencerFixed5": IAMCCS_WDC_LTXSequencerFixed5,
|
||
"IAMCCS_LTX2_InitLatentSampler": IAMCCS_LTX2_InitLatentSampler,
|
||
"IAMCCS_LTX2_LoopingSampler": IAMCCS_LTX2_LoopingSampler,
|
||
"IAMCCS_LTX2_OneShotLowRAMLooper": IAMCCS_LTX2_OneShotLowRAMLooper,
|
||
"IAMCCS_LTX2_ExtendSampler": IAMCCS_LTX2_ExtendSampler,
|
||
"IAMCCS_LTX2_ConditionNextLatentWithPrevOverlap": IAMCCS_LTX2_ConditionNextLatentWithPrevOverlap,
|
||
"IAMCCS_WanImageMotion": IAMCCS_WanImageMotion,
|
||
# Backward-compat alias: workflow JSONs saved with the _AdaIN name still load.
|
||
"IAMCCS_WanImageMotion_AdaIN": IAMCCS_WanImageMotion,
|
||
"WanImageMotionPro": WanImageMotionProPlus,
|
||
"IAMCCS_WanImageMotionPro_AdaIN": WanImageMotionProPlus,
|
||
# Keep the historic key on the current implementation so existing workflows
|
||
# load the new Plus node with continuity profiles and presets.
|
||
"IAMCCS_WanImageMotionPro": WanImageMotionProPlus,
|
||
# Explicit legacy entrypoint for older raw-only behavior.
|
||
"WanImageMotionProLegacy": WanImageMotionProLegacy,
|
||
"IAMCCS_WanImageMotionProLegacy": WanImageMotionProLegacy,
|
||
"WanImageMotionProPlus": WanImageMotionProPlus,
|
||
"IAMCCS_WanImageMotionProPlus": WanImageMotionProPlus,
|
||
"IAMCCS_WanImageMotionProPlus_Simple": IAMCCS_WanImageMotionProPlus_Simple,
|
||
"IAMCCS_WanImageMotionInductive": IAMCCS_WanImageMotionInductive,
|
||
"IAMCCS_WanSVIToFLFBridgeProPlus": IAMCCS_WanSVIToFLFBridgeProPlus,
|
||
"IAMCCS_WanSVIToFLFBridgeProPlus_Simple": IAMCCS_WanSVIToFLFBridgeProPlus_Simple,
|
||
"WanMotionProTrimmer": WanMotionProTrimmer,
|
||
"IAMCCS_WanPrevTailPrep": IAMCCS_WanPrevTailPrep,
|
||
"IAMCCS_WanLongPlanner": IAMCCS_WanLongPlanner,
|
||
"IAMCCS_WanSviFlfTimeline": IAMCCS_WanSviFlfTimeline,
|
||
"IAMCCS_WanSviFlfTimelinePick": IAMCCS_WanSviFlfTimelinePick,
|
||
"IAMCCS_WanContinuityGuide": IAMCCS_WanContinuityGuide,
|
||
"IAMCCS_WanPromptPhasePlanner": IAMCCS_WanPromptPhasePlanner,
|
||
"IAMCCS_WanPromptLoopInfo": IAMCCS_WanPromptLoopInfo,
|
||
"IAMCCS_WanIndexedPromptEncode": IAMCCS_WanIndexedPromptEncode,
|
||
"IAMCCS_WanImageBatchFrameSelect": IAMCCS_WanImageBatchFrameSelect,
|
||
|
||
"IAMCCS_SetAutoLink": IAMCCS_SetAutoLink,
|
||
"IAMCCS_GetAutoLink": IAMCCS_GetAutoLink,
|
||
"IAMCCS_AutoLinkConverter": IAMCCS_AutoLinkConverter,
|
||
"IAMCCS_AutoLinkArguments": IAMCCS_AutoLinkArguments,
|
||
|
||
"IAMCCS_GGUF_accelerator": IAMCCS_GGUF_accelerator,
|
||
|
||
"IAMCCS_SamplerAdvancedVersion1": IAMCCS_SamplerAdvancedVersion1,
|
||
"IAMCCS_InfluencerSampler": IAMCCSInfluencerSampler,
|
||
"IAMCCS_InfluencerLatent": IAMCCSInfluencerLatent,
|
||
|
||
"IAMCCS_bus_group": IAMCCS_bus_group,
|
||
|
||
"IAMCCS_MultiSwitch": IAMCCS_MultiSwitch,
|
||
"IAMCCS_LazyAnySwitch": IAMCCS_LazyAnySwitch,
|
||
"IAMCCS_Navigator": IAMCCS_Navigator,
|
||
|
||
"IAMCCS_HwSupporter": IAMCCS_HwSupporter,
|
||
"IAMCCS_HwSupporterAny": IAMCCS_HwSupporterAny,
|
||
"IAMCCS_HardMemoryPurge": IAMCCS_HardMemoryPurge,
|
||
"IAMCCS_VRAMCleanup": IAMCCS_VRAMCleanup,
|
||
"IAMCCS_VRAMFlushLatent": IAMCCS_VRAMFlushLatent,
|
||
"IAMCCS_VAEDecodeTiledSafe": IAMCCS_VAEDecodeTiledSafe,
|
||
"IAMCCS_VAEDecodeToDisk": IAMCCS_VAEDecodeToDisk,
|
||
"IAMCCS_HWProbeRecommendations": IAMCCS_HWProbeRecommendations,
|
||
"IAMCCS_DetailAtelier": IAMCCS_DetailAtelier,
|
||
"IAMCCS_DetailAtelierAdvanced": IAMCCS_DetailAtelierAdvanced,
|
||
"IAMCCS_DetailAtelierSampler": IAMCCS_DetailAtelierSampler,
|
||
|
||
"IAMCCS_MoveAhead": IAMCCS_MoveAhead,
|
||
"IAMCCS_MoveAheadEnforcer": IAMCCS_MoveAheadEnforcer,
|
||
"IAMCCS_MotionScale": IAMCCS_MotionScale,
|
||
"IAMCCS_MotionScaleAdvanced": IAMCCS_MotionScaleAdvanced,
|
||
|
||
"IAMCCS_MotionBridgeSave": IAMCCS_MotionBridgeSave,
|
||
"IAMCCS_MotionBridgeLoad": IAMCCS_MotionBridgeLoad,
|
||
"IAMCCS_LatentTailSlice": IAMCCS_LatentTailSlice,
|
||
"IAMCCS_AudioExtensionMath": IAMCCS_AudioExtensionMath,
|
||
"IAMCCS_AudioExtender": IAMCCS_AudioExtender,
|
||
"IAMCCS_AudioTimelineAssembler": IAMCCS_AudioTimelineAssembler,
|
||
"IAMCCS_AudioTimelineGate": IAMCCS_AudioTimelineGate,
|
||
"IAMCCS_BoardMaker_DialogueFoley": IAMCCS_BoardMaker_DialogueFoley,
|
||
"IAMCCS_CineInfo3": IAMCCS_CineInfo3,
|
||
"IAMCCS_CineDialogueLineRouter": IAMCCS_CineDialogueLineRouter,
|
||
"IAMCCS_CineTimelineAudioMixer": IAMCCS_CineTimelineAudioMixer,
|
||
"IAMCCS_AudioBoardArranger": IAMCCS_AudioBoardArranger,
|
||
"IAMCCS_BusOut": IAMCCS_BusOut,
|
||
"IAMCCS_AudioBoardDirectShotboardAdapter": IAMCCS_AudioBoardDirectShotboardAdapter,
|
||
"IAMCCS_AudioBoardMixer": IAMCCS_AudioBoardMixer,
|
||
"IAMCCS_ControlAudEfx": IAMCCS_ControlAudEfx,
|
||
"IAMCCS_ControlAudEfxPanel": IAMCCS_ControlAudEfxPanel,
|
||
"IAMCCS_DialogueTagEditor": IAMCCS_DialogueTagEditor,
|
||
"IAMCCS_DialogueAudioBoardBridge": IAMCCS_DialogueAudioBoardBridge,
|
||
"IAMCCS_CineAudioInfo": IAMCCS_CineAudioInfo,
|
||
"IAMCCS_AudioPhaseLazyGate": IAMCCS_AudioPhaseLazyGate,
|
||
"IAMCCS_StoryboardFrameDesigner": IAMCCS_StoryboardFrameDesigner,
|
||
"IAMCCS_StoryboardFrameDesignerV2": IAMCCS_StoryboardFrameDesignerV2,
|
||
"IAMCCS_IdeoInfo": IAMCCS_IdeoInfo,
|
||
"IAMCCS_IdeoInpaintPrep": IAMCCS_IdeoInpaintPrep,
|
||
"IAMCCS_IdeoMaskedPixels": IAMCCS_IdeoMaskedPixels,
|
||
"IAMCCS_IdeogramJSONPreviewPass": IAMCCS_IdeogramJSONPreviewPass,
|
||
"IAMCCS_IdeoNoiseDebug": IAMCCS_IdeoNoiseDebug,
|
||
"IAMCCS_IdeoRandomNoiseNoCache": IAMCCS_IdeoRandomNoiseNoCache,
|
||
"IAMCCS_IdeoImageHashDebug": IAMCCS_IdeoImageHashDebug,
|
||
"IAMCCS_IdeoTranslate": IAMCCS_IdeoTranslate,
|
||
"IAMCCS_IdeogramStoryboardSheet": IAMCCS_IdeogramStoryboardSheet,
|
||
"IAMCCS_IdeogramSheetBuilder": IAMCCS_IdeogramSheetBuilder,
|
||
"IAMCCS_StoryboardCaptionSheet": IAMCCS_StoryboardCaptionSheet,
|
||
"IAMCCS_StoryboardAutoCropGrid": IAMCCS_StoryboardAutoCropGrid,
|
||
"IAMCCS_StoryboardAutoCropGridPRO": IAMCCS_StoryboardAutoCropGridPRO,
|
||
"IAMCCS_TargetCrop": IAMCCS_TargetCrop,
|
||
"IAMCCS_GemmaAssistLazyGate": IAMCCS_GemmaAssistLazyGate,
|
||
"IAMCCS_GemmaAssistOutput": IAMCCS_GemmaAssistOutput,
|
||
"IAMCCS_StoryboardPromptContactSheet": IAMCCS_StoryboardPromptContactSheet,
|
||
"IAMCCS_GoyAICanvasPaint": IAMCCS_GoyAICanvasPaint,
|
||
"IAMCCS_FlashVSRPanelBatchPrep": IAMCCS_FlashVSRPanelBatchPrep,
|
||
"IAMCCS_FlashVSRPanelBatchRestore": IAMCCS_FlashVSRPanelBatchRestore,
|
||
"IAMCCS_CineVideoToWooshInputs": IAMCCS_CineVideoToWooshInputs,
|
||
"IAMCCS_CineSpeech1PromptCompiler": IAMCCS_CineSpeech1PromptCompiler,
|
||
"IAMCCS_CineAudioTranscriptPromptCompiler": IAMCCS_CineAudioTranscriptPromptCompiler,
|
||
"IAMCCS_CineSpeechLength": IAMCCS_CineSpeechLength,
|
||
"IAMCCS_CineDialogueDurationPlanner": IAMCCS_CineDialogueDurationPlanner,
|
||
"IAMCCS_CineAudioDurationProbe": IAMCCS_CineAudioDurationProbe,
|
||
"IAMCCS_CineDialogueTimingReconciler": IAMCCS_CineDialogueTimingReconciler,
|
||
"IAMCCS_CineWooshFoleyChunkPlanner": IAMCCS_CineWooshFoleyChunkPlanner,
|
||
"IAMCCS_CineFinalAudioMixer": IAMCCS_CineFinalAudioMixer,
|
||
"IAMCCS_CineEmotionButtons": IAMCCS_CineEmotionButtons,
|
||
"IAMCCS_CineDialoguePromptKit": IAMCCS_CineDialoguePromptKit,
|
||
"IAMCCS_LTX2_LastFrameBridgeSave": IAMCCS_LTX2_LastFrameBridgeSave,
|
||
"IAMCCS_LTX2_BlendLatentBridge": IAMCCS_LTX2_BlendLatentBridge,
|
||
"IAMCCS_LTX2_LastFrameBridgeLoad": IAMCCS_LTX2_LastFrameBridgeLoad,
|
||
"IAMCCS_LTX2_LoadLatentBridge": IAMCCS_LTX2_LoadLatentBridge,
|
||
"IAMCCS_LTX2_LongVideoWrapperPrep": IAMCCS_LTX2_LongVideoWrapperPrep,
|
||
"IAMCCS_LTX2_LongVideoWrapperPrepDisk": IAMCCS_LTX2_LongVideoWrapperPrepDisk,
|
||
"IAMCCS_LTX2_SaveLatentBridge": IAMCCS_LTX2_SaveLatentBridge,
|
||
"IAMCCS_LTX2_SegmentQueueLoop": IAMCCS_LTX2_SegmentQueueLoop,
|
||
"IAMCCS_ImageResizeBatchSafe": IAMCCS_ImageResizeBatchSafe,
|
||
"IAMCCS_LoadResizeSegmentFromDir": IAMCCS_LoadResizeSegmentFromDir,
|
||
"IAMCCS_IntValueMonitor": IAMCCS_IntValueMonitor,
|
||
"IAMCCS_QwenMultiGen": IAMCCS_QwenMultiGen,
|
||
"IAMCCS_Krea2MultiGen": IAMCCS_Krea2MultiGen,
|
||
"IAMCCS_RedheadNode": IAMCCSRedheadNode,
|
||
"IAMCCS_FluxKleinMultiGen": IAMCCS_FluxKleinMultiGen,
|
||
"IAMCCS_FluxKleinRefine": IAMCCS_FluxKleinRefine,
|
||
"IAMCCS_ImageBatch6": IAMCCS_ImageBatch6,
|
||
"IAMCCS_MultilinePromptSplitter8": IAMCCS_MultilinePromptSplitter8,
|
||
"IAMCCS_SupernodeBase": IAMCCS_SupernodeBase,
|
||
"IAMCCS_SupernodeModule": IAMCCS_SupernodeModule,
|
||
"IAMCCS_ProjectTimelinePlanner": IAMCCS_ProjectTimelinePlanner,
|
||
"IAMCCS_Ltx2HelperModules_ProjectTimelinePlanner": IAMCCS_Ltx2HelperModules_ProjectTimelinePlanner,
|
||
"IAMCCS_Ltx2HelperModules_Planner": IAMCCS_Ltx2HelperModules_Planner,
|
||
"IAMCCS_Ltx2HelperModules_AudioTimeline": IAMCCS_Ltx2HelperModules_AudioTimeline,
|
||
"IAMCCS_Ltx2HelperModules_KeyframeTimeline": IAMCCS_Ltx2HelperModules_KeyframeTimeline,
|
||
"IAMCCS_Ltx2HelperModules_RefreshPolicy": IAMCCS_Ltx2HelperModules_RefreshPolicy,
|
||
"IAMCCS_Ltx2HelperModules_ReanchorLatent": IAMCCS_Ltx2HelperModules_ReanchorLatent,
|
||
"IAMCCS_Ltx2HelperModules_DiskExtension": IAMCCS_Ltx2HelperModules_DiskExtension,
|
||
"IAMCCS_Ltx2HelperModules_RuntimeBridge": IAMCCS_Ltx2HelperModules_RuntimeBridge,
|
||
"IAMCCS_Ltx2HelperModules_Continuity": IAMCCS_Ltx2HelperModules_Continuity,
|
||
"IAMCCS_Ltx2HelperModules_Finalize": IAMCCS_Ltx2HelperModules_Finalize,
|
||
"IAMCCS_AUIMG2VID_ProjectTimelinePlanner": IAMCCS_AUIMG2VID_ProjectTimelinePlanner,
|
||
"IAMCCS_AUIMG2VID_Planner": IAMCCS_AUIMG2VID_Planner,
|
||
"IAMCCS_AUIMG2VID_AudioTimeline": IAMCCS_AUIMG2VID_AudioTimeline,
|
||
"IAMCCS_AUIMG2VID_KeyframeTimeline": IAMCCS_AUIMG2VID_KeyframeTimeline,
|
||
"IAMCCS_AUIMG2VID_RefreshPolicy": IAMCCS_AUIMG2VID_RefreshPolicy,
|
||
"IAMCCS_AUIMG2VID_ReanchorLatent": IAMCCS_AUIMG2VID_ReanchorLatent,
|
||
"IAMCCS_AUIMG2VID_DiskExtension": IAMCCS_AUIMG2VID_DiskExtension,
|
||
"IAMCCS_AUIMG2VID_RuntimeBridge": IAMCCS_AUIMG2VID_RuntimeBridge,
|
||
"IAMCCS_AUIMG2VID_Continuity": IAMCCS_AUIMG2VID_Continuity,
|
||
"IAMCCS_AUIMG2VID_Finalize": IAMCCS_AUIMG2VID_Finalize,
|
||
"IAMCCS-SuperNodes AU+IMG2VID Exec Planner": IAMCCS_SuperNodes_AUIMG2VIDExecutablePlanner,
|
||
"IAMCCS-SuperNodes AU+IMG2VID Exec Render": IAMCCS_SuperNodes_AUIMG2VIDExecutableRender,
|
||
"IAMCCS-SuperNodes AU+IMG2VID Exec VAE": IAMCCS_SuperNodes_AUIMG2VIDExecutableVAE,
|
||
"IAMCCS-SuperNodes Second Stage": IAMCCS_SuperNodes_SecondStage,
|
||
**({"IAMCCS-SuperNodes AU+IMG2VID Exec Finalize": IAMCCS_SuperNodes_AUIMG2VIDExecutableFinalize} if IAMCCS_SuperNodes_AUIMG2VIDExecutableFinalize is not None else {}),
|
||
|
||
**({
|
||
"IAMCCS_ScailIdentitySeeder": IAMCCS_ScailIdentitySeeder,
|
||
"IAMCCS_ScailIdentityTracker": IAMCCS_ScailIdentityTracker,
|
||
"IAMCCS_ScailMultiReference": IAMCCS_ScailMultiReference,
|
||
} if IAMCCS_ScailIdentityTracker is not None else {}),
|
||
|
||
**({
|
||
"IAMCCS_ScailExtends": IAMCCS_ScailExtends,
|
||
"IAMCCS_ScailExtendPlan": IAMCCS_ScailExtendPlan,
|
||
} if IAMCCS_ScailExtends is not None else {}),
|
||
|
||
**({
|
||
"IAMCCS_WanAnimateExtends": IAMCCS_WanAnimateExtends,
|
||
"IAMCCS_WanAnimateExtendPlan": IAMCCS_WanAnimateExtendPlan,
|
||
} if IAMCCS_WanAnimateExtends is not None else {}),
|
||
|
||
**({
|
||
"IAMCCS_WanAnimate2Extends": IAMCCS_WanAnimate2Extends,
|
||
"IAMCCS_WanAnimate2ExtendPlan": IAMCCS_WanAnimate2ExtendPlan,
|
||
} if IAMCCS_WanAnimate2Extends is not None else {}),
|
||
|
||
**({
|
||
"IAMCCS_WanAnimate2ShotboardBridge": IAMCCS_WanAnimate2ShotboardBridge,
|
||
} if IAMCCS_WanAnimate2ShotboardBridge is not None else {}),
|
||
|
||
# QwenVL First/Last Frame (registered only if QwenVL is installed)
|
||
**({"IAMCCS_QWEN_VL_FLF": IAMCCS_QWEN_VL_FLF,
|
||
"IAMCCS_QWEN_VL_FLF_Advanced": IAMCCS_QWEN_VL_FLF_Advanced,
|
||
} if IAMCCS_QWEN_VL_FLF is not None else {}),
|
||
|
||
# MiniMax H3 Shotboard (isolated module; no V3/V4 dependency)
|
||
**IAMCCS_MINIMAX_H3_NODE_CLASS_MAPPINGS,
|
||
|
||
# Structured MiniMax H3 prompt authoring (isolated Python + JS files)
|
||
**IAMCCS_PROMPTER_NODE_CLASS_MAPPINGS,
|
||
|
||
# Native IAMCCS cinema finishing nodes (isolated from generation backends)
|
||
**IAMCCS_CINE_NODES_NODE_CLASS_MAPPINGS,
|
||
|
||
# Local VLM visual continuity analysis transported through CineLinX
|
||
**IAMCCS_CINE_H3_VISION_NODE_CLASS_MAPPINGS,
|
||
|
||
}
|
||
|
||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||
"IAMCCS_WanAnimate2ShotboardBridge": "Wan Animate 2 Shotboard Backend Bridge",
|
||
"IAMCCS_ScailExtends": "IAMCCS SCAIL Extends",
|
||
"IAMCCS_ScailExtendPlan": "IAMCCS SCAIL Extend Plan",
|
||
"IAMCCS_WanAnimateExtends": "IAMCCS WanAnimate Extends",
|
||
"IAMCCS_WanAnimateExtendPlan": "IAMCCS WanAnimate Extend Plan",
|
||
"IAMCCS_WanAnimate2Extends": "IAMCCS Wan-Animate-2 Extends",
|
||
"IAMCCS_WanAnimate2ExtendPlan": "IAMCCS Wan-Animate-2 Extend Plan",
|
||
"IAMCCS_ScailIdentitySeeder": "IAMCCS SCAIL Identity Seeder",
|
||
"IAMCCS_ScailIdentityTracker": "IAMCCS SCAIL Identity Tracker",
|
||
"IAMCCS_ScailMultiReference": "IAMCCS SCAIL Multi-Reference (experimental)",
|
||
"IAMCCS_WanLoRAStack": "LoRA Stack (WAN-style remap)",
|
||
"IAMCCS_ModelWithLoRA": "Apply LoRA to MODEL (Native)",
|
||
"IAMCCS_WanLoRAStackModelIO": "LoRA Stack (Model In?Out) WAN",
|
||
"IAMCCS_WanLoRASchedule": "LoRA Schedule (WAN, ranged)",
|
||
"IAMCCS_WanLoRAHookSchedule": "LoRA Schedule (WAN, hooks)",
|
||
"IAMCCS_ApplyLoRAHooksToConditioning": "Apply LoRA Hooks to Conditioning",
|
||
"IAMCCS_ApplyScheduledWanLoRAFromConditioning": "Apply Scheduled WAN LoRA From Conditioning",
|
||
"IAMCCS_BuildScheduledWanModelBank": "Build Scheduled WAN Model Bank",
|
||
"IAMCCS_SelectScheduledWanModelFromConditioning": "Select Scheduled WAN Model From Conditioning",
|
||
"IAMCCS_SelectScheduledWanModelPairFromConditioning": "Select Scheduled WAN Model Pair From Conditioning",
|
||
"IAMCCS_WanLoRARuntimeBridge": "LoRA Runtime Bridge (WAN, loop-safe)",
|
||
"IAMCCS_ModelWithLoRA_RuntimeBridge": "Apply LoRA to MODEL (Runtime Bridge)",
|
||
"iamccs_ltx2_lora_stack": "iamccs_ltx2_lora_stack (3 slots)",
|
||
"IAMCCS_LTX2_LoRAStack": "LoRA Stack (LTX-2, 3 slots)",
|
||
"IAMCCS_LTX2_LoRAStackStaged": "LoRA Stack (LTX-2, staged: stage1+stage2) (BETA)",
|
||
"IAMCCS_ModelWithLoRA_LTX2": "Apply LoRA to MODEL (LTX-2, quiet logs)",
|
||
"IAMCCS_ModelWithLoRA_LTX2_Staged": "Apply LoRA to MODEL (LTX-2, staged) (BETA)",
|
||
"IAMCCS_LTX2_LoRAStackModelIO": "LoRA Stack (Model In?Out) LTX-2",
|
||
"IAMCCS_LTX2_LoRAStackSegmented6": "LoRA Stack (LTX-2, segmented: 3 seg × 2 stages)",
|
||
"IAMCCS_LTX2_ModelWithLoRA_Segmented6": "Apply LoRA to MODEL (LTX-2, segmented: 3 seg × 2 stages)",
|
||
|
||
"IAMCCS_LTX2_FrameRateSync": "LTX-2 FrameRate Sync (int+float)",
|
||
"IAMCCS_LTX2_Validator": "LTX-2 Validator",
|
||
"IAMCCS_LTX2_TimeFrameCount": "LTX-2 TimeFrameCount",
|
||
"IAMCCS_LTX2_EnsureFrames8nPlus1": "LTX-2 Ensure Frames (8n + 1)",
|
||
"IAMCCS_LTX2_EnsureMinFrames": "LTX-2 Ensure Minimum Frames",
|
||
"IAMCCS_LTX2_ControlPreprocess": "LTX-2 Control Preprocess (aux)",
|
||
"IAMCCS_LTX2_ImageBatchPadReflect": "LTX-2 Pad Reflect (IMAGE batch)",
|
||
"IAMCCS_LTX2_ImageBatchCropByPad": "LTX-2 Crop By Pad (IMAGE batch)",
|
||
"IAMCCS_SegmentPlanner": "Segment Planner (song -> LTX frames)",
|
||
"IAMCCS_SegmentPlannerSettings": "Segment Planner Settings (shared)",
|
||
"IAMCCS_AudioSegmentAutoPlanner": "Audio Segment Auto Planner (audio -> segmenti)",
|
||
"IAMCCS_SegmentPlannerLinked": "Segment Planner Linked (shared inputs)",
|
||
"IAMCCS_SegmentPlannerFramesLinked": "Segment Planner Linked (total generation frames)",
|
||
"IAMCCS_SegmentPlanFromPlanner": "Segment Plan From Planner (per index)",
|
||
"IAMCCS_SourceRangeFromSegmentPlan": "Source Range From Segment Plan",
|
||
"IAMCCS_TwoSegmentPlanner": "Two Segment Planner (stable 2SEG)",
|
||
"IAMCCS_ThreeSegmentPlanner": "Three Segment Planner (stable 3SEG)",
|
||
"IAMCCS_SegmentSwitch": "Segment Switch (by segment_index)",
|
||
"IAMCCS_LTX2_ExtensionModule": "LTX-2 Extension Module ??",
|
||
"IAMCCS_LTX2_ExtensionModule_Disk": "LTX-2 Extension Module (Disk / Low RAM) ??",
|
||
"IAMCCS_LTX2_JointRefreshLatent": "LTX-2 Joint Refresh Latent",
|
||
"IAMCCS_LTX2_JointRefreshLatent_Disk": "LTX-2 Joint Refresh Latent (Disk)",
|
||
"IAMCCS_LoadImagesFromDirLite": "Load Images From Dir (Lite) ??",
|
||
"IAMCCS_ImageBatchRangeLite": "Image Batch Range (VRAM) ???",
|
||
"IAMCCS_SourceFramesToDisk": "Source Frames To Disk ????",
|
||
"IAMCCS_StartDirToVideoLatent": "Start Dir To Video Latent ??",
|
||
"IAMCCS_StartImagesToVideoLatent": "Start Images To Video Latent ??",
|
||
"IAMCCS_VideoCombineFromDir": "Video Combine From Dir ???",
|
||
"IAMCCS_LTX2_ExtensionModule_simple": "LTX-2 Extension Module (simple) ??",
|
||
"IAMCCS_LTX2_GetImageFromBatch": "LTX-2 Get Images From Batch ???",
|
||
"IAMCCS_LTX2_ReferenceImageSwitch": "LTX-2 Reference Image Switch ??",
|
||
"IAMCCS_LTX2_ReferenceStartFramesInjector": "LTX-2 Inject Reference Into Start Frames ??",
|
||
"IAMCCS_LTX2_FrameCountValidator": "LTX-2 Frame Count Validator ? (8n+1)",
|
||
"IAMCCS_LTX2_FirstLastFramesController": "LTX-2 First/Last Frames Controller ??",
|
||
"IAMCCS_LTX2_ContextLatent": "LTX-2 Context ? Latent (continue) ??",
|
||
"IAMCCS_LTX2_MiddleFrames": "LTX-2 Middle Frames (accumulator) ??",
|
||
"IAMCCS_LTX2_FirstLastLatentControl": "LTX-2 First/Last ? Latent (noise_mask) ??",
|
||
"IAMCCS_LTX2_FirstLastLatentControl_Pro": "LTX-2 First/Last ? Latent (Pro, slot caps) ??",
|
||
"IAMCCS_CineReferenceBoard": "IAMCCS Cine Reference Board",
|
||
"IAMCCS_CineLTXSequencer": "IAMCCS Cine FLF Timeline Sequencer",
|
||
"IAMCCS_CineAllInOneFLFEngine": "IAMCCS Cine AllInOne FLF Engine",
|
||
"IAMCCS_CinePromptRelayTimeline": "IAMCCS Cine PromptRelay Timeline",
|
||
"IAMCCS_CineShotboardTimelinePro": "IAMCCS Cine Shotboard Timeline Pro",
|
||
"IAMCCS_CineShotboardPlannerPro": "IAMCCS Cine Shotboard Planner Pro",
|
||
"IAMCCS_CineShotboardPlannerProV2": "IAMCCS Cine Shotboard Planner Pro V2",
|
||
"IAMCCS_CineShotboardPlannerV3": "IAMCCS Cine Shotboard Planner V3",
|
||
"IAMCCS_CineShotboardPlannerV4": "IAMCCS Cine Shotboard Planner V4",
|
||
"IAMCCS_CineShotboardPlannerV5V2V": "IAMCCS Cine Shotboard Planner V5 V2V",
|
||
"IAMCCS_CineShotboardV5V2VRouter": "IAMCCS Cine Shotboard V5 V2V Router",
|
||
"IAMCCS_CineShotboardV4Backend": "IAMCCS Cine Shotboard V4 Backend",
|
||
"IAMCCS_CineShotboardV4Guide": "IAMCCS Cine Shotboard V4 Guide",
|
||
"IAMCCS_CineShotboardV4CropGuides": "IAMCCS Cine Shotboard V4 Crop Guides",
|
||
"IAMCCS_LTXVideoDurationCrop": "IAMCCS LTX Video Duration Crop",
|
||
"IAMCCS_CineShotboardLite": "IAMCCS Cine Shotboard Lite",
|
||
"IAMCCS_CineShotboardPlannerProLegacy": "IAMCCS Cine Shotboard Planner Pro Legacy Outputs",
|
||
"IAMCCS_CineResolutionParityTranslator": "IAMCCS Cine Resolution Parity Translator",
|
||
"IAMCCS_CineStage2BypassSwitch": "IAMCCS Cine Stage 2 Bypass Switch",
|
||
"IAMCCS_CineStage2PreviewToggle": "IAMCCS Cine Stage 2 Preview Toggle",
|
||
"IAMCCS_CineInfo": "IAMCCS CineInfo",
|
||
"IAMCCS_CineInfoV2": "IAMCCS CineInfo V2",
|
||
"IAMCCS_CineFLFProductor": "IAMCCS Cine FLF Productor",
|
||
"IAMCCS_CineFLFProductorDyno": "IAMCCS Cine FLF Productor Dyno",
|
||
"IAMCCS_CineFilmmaker": "IAMCCS Cine Filmmaker",
|
||
"IAMCCS_CineFilmmakerBackend": "IAMCCS Cine Filmmaker Backend",
|
||
"IAMCCS_CineShotboardBackendPro": "IAMCCS Cine Shotboard Backend Pro",
|
||
"IAMCCS_CineFilmmakerGuide": "IAMCCS Cine Filmmaker Guide",
|
||
"IAMCCS_CineFilmmakerGuide1to1": "IAMCCS Cine Filmmaker Guide 1:1",
|
||
"IAMCCS_CineFilmmakerCropGuides1to1": "IAMCCS Cine Filmmaker Crop Guides 1:1",
|
||
"IAMCCS_CineSwitch": "IAMCCS CineSwitch Lazy FLF/PromptRelay",
|
||
"IAMCCS_CinePromptRelayLatentShapeSync": "IAMCCS Cine PromptRelay Latent Shape Sync",
|
||
"IAMCCS_CineFLFLengthCompensator": "IAMCCS Cine FLF Length Compensator",
|
||
"IAMCCS_CineBoardDurationLock": "IAMCCS Cine Board Duration Lock",
|
||
"IAMCCS_CineLatentDurationCrop": "IAMCCS Cine Latent Duration Crop",
|
||
"IAMCCS_CineShotboardTailTrimPolicy": "IAMCCS Cine Shotboard Tail Trim Policy",
|
||
"IAMCCS_CineShotboardFinalFrameTrim": "IAMCCS Cine Shotboard Final Frame Trim",
|
||
"IAMCCS_shotboarder_aud+vid_exporter_PRO": "IAMCCS Shotboarder Aud+Vid Exporter PRO",
|
||
"IAMCCS_CineTemporalCutBarrier": "IAMCCS Cine Temporal Cut Barrier (Experimental)",
|
||
"IAMCCS_CinePromptRelaySafeEncode": "IAMCCS Cine PromptRelay Safe Encode",
|
||
"IAMCCS_CineRelayOrBypass": "IAMCCS Cine Relay Or Bypass",
|
||
"IAMCCS_CinePromptArchitect": "IAMCCS CinePrompt Architect",
|
||
"IAMCCS_BoardMaker": "IAMCCS_BoardMaker",
|
||
"IAMCCS_CineMusicVideoPlanner": "IAMCCS Cine Videoclip Maker Planner",
|
||
"IAMCCS_CineShotPlanner": "IAMCCS Cine Shot Planner",
|
||
"IAMCCS_CineRefLatentControl": "IAMCCS Cine Reference Latent Control",
|
||
"IAMCCS_CineAudioPromptDirector": "IAMCCS Cine Audio Prompt Director",
|
||
"IAMCCS_CinePromptRelayAdapter": "IAMCCS Cine PromptRelay Adapter",
|
||
"IAMCCS_CinePromptComposer": "IAMCCS Cine Prompt Composer",
|
||
"IAMCCS_CineShotLineBuilder": "IAMCCS Cine Shot Line Builder",
|
||
"IAMCCS_CineV2VTimelineLineBuilder": "IAMCCS Cine V2V Line Builder",
|
||
"IAMCCS_CineLineStacker": "IAMCCS Cine Line Stacker",
|
||
"IAMCCS_CineMultiGenDirector": "IAMCCS Cine Multi-Generation Director",
|
||
"IAMCCS_CineShotAudioDirector": "IAMCCS Cine Shot Audio Director",
|
||
"IAMCCS_CineV2VTimelineDirector": "IAMCCS Cine V2V Timeline Director",
|
||
"IAMCCS_CineV2VAssetSelector": "IAMCCS Cine V2V Asset Selector",
|
||
"IAMCCS_CineWorkflowInspector": "IAMCCS Cine Workflow Inspector",
|
||
"IAMCCS_WDC_MultiImageLoader": "IAMCCS Cine Reference Board (legacy alias)",
|
||
"IAMCCS_WDC_LTXKeyframer": "IAMCCS Cine LTX Keyframer (legacy alias)",
|
||
"IAMCCS_WDC_LTXSequencer": "IAMCCS Cine LTX Sequencer (legacy alias)",
|
||
"IAMCCS_CineLTXSequencerExact": "IAMCCS Cine LTX Sequencer Exact",
|
||
"IAMCCS_CineFLFEngineSimple": "IAMCCS Cine FLF Engine Simple",
|
||
"IAMCCS_CineFLFEngineSimpleDyno": "IAMCCS Cine FLF Engine Simple Dyno",
|
||
"IAMCCS_WDC_LTXSequencerFixed5": "IAMCCS Cine LTX Sequencer Fixed 5 (legacy alias)",
|
||
"IAMCCS_LTX2_InitLatentSampler": "LTX-2 Init Latent Sampler ??",
|
||
"IAMCCS_LTX2_LoopingSampler": "LTX-2 Looping Sampler (temporal overlap) ??",
|
||
"IAMCCS_LTX2_OneShotLowRAMLooper": "LTX-2 One-Shot Low-RAM Looper ??",
|
||
"IAMCCS_LTX2_ExtendSampler": "LTX-2 Extend Sampler (temporal overlap) ??",
|
||
"IAMCCS_LTX2_ConditionNextLatentWithPrevOverlap": "LTX-2 Condition Next Latent (prev overlap) ??",
|
||
"IAMCCS_WanImageMotion": "WanImageMotion",
|
||
"IAMCCS_WanImageMotion_AdaIN": "WanImageMotion",
|
||
"WanImageMotionPro": "WanImageMotionPro Plus",
|
||
"IAMCCS_WanImageMotionPro_AdaIN": "WanImageMotionPro Plus",
|
||
"WanImageMotionProLegacy": "WanImageMotionPro Legacy",
|
||
"IAMCCS_WanImageMotionProLegacy": "WanImageMotionPro Legacy",
|
||
"WanImageMotionProPlus": "WanImageMotionPro Plus",
|
||
"IAMCCS_WanImageMotionProPlus": "WanImageMotionPro Plus",
|
||
"IAMCCS_WanImageMotionProPlus_Simple": "WanImageMotionPro Plus Simple",
|
||
"IAMCCS_WanImageMotionInductive": "WanImageMotion Inductive",
|
||
"IAMCCS_WanSVIToFLFBridgeProPlus": "Wan SVI?FLF Bridge Pro Plus",
|
||
"IAMCCS_WanSVIToFLFBridgeProPlus_Simple": "Wan SVI?FLF Bridge Pro Plus",
|
||
"WanMotionProTrimmer": "WanMotionProTrimmer (trim overshoot tail)",
|
||
"IAMCCS_WanPrevTailPrep": "Wan Prev Tail Prep",
|
||
"IAMCCS_WanLongPlanner": "Wan Long Planner",
|
||
"IAMCCS_WanSviFlfTimeline": "Wan SVI/FLF Timeline",
|
||
"IAMCCS_WanSviFlfTimelinePick": "Wan SVI/FLF Timeline Pick",
|
||
"IAMCCS_WanContinuityGuide": "Wan Continuity Guide",
|
||
"IAMCCS_WanPromptPhasePlanner": "Wan Prompt Phase Planner",
|
||
"IAMCCS_WanPromptLoopInfo": "Wan Prompt Loop Info",
|
||
"IAMCCS_WanIndexedPromptEncode": "Wan Indexed Prompt Encode",
|
||
"IAMCCS_WanImageBatchFrameSelect": "Wan Image Batch Frame Select",
|
||
|
||
"IAMCCS_SetAutoLink": "Set AutoLink",
|
||
"IAMCCS_GetAutoLink": "Get AutoLink",
|
||
"IAMCCS_AutoLinkConverter": "AutoLink Converter",
|
||
"IAMCCS_AutoLinkArguments": "AutoLink Arguments",
|
||
|
||
"IAMCCS_GGUF_accelerator": "GGUF Accelerator (patch_on_device)",
|
||
|
||
"IAMCCS_IntValueMonitor": "INT Value Monitor",
|
||
"IAMCCS_QwenMultiGen": "IAMCCS Qwen Multi-Gen",
|
||
"IAMCCS_Krea2MultiGen": "IAMCCS Krea 2 Identity Multi-Gen",
|
||
"IAMCCS_RedheadNode": "IAMCCS Redhead Node (Krea2)",
|
||
"IAMCCS_FluxKleinMultiGen": "Flux Klein Multi-Gen",
|
||
"IAMCCS_FluxKleinRefine": "Flux Klein Refine (Local NO PAID)",
|
||
"IAMCCS_ImageBatch6": "IAMCCS Image Batch 6",
|
||
"IAMCCS_StoryboardCaptionSheet": "IAMCCS Storyboard Caption Sheet",
|
||
"IAMCCS_StoryboardAutoCropGrid": "IAMCCS Storyboard Auto Crop Grid",
|
||
"IAMCCS_StoryboardAutoCropGridPRO": "IAMCCS Storyboard Auto Crop Grid PRO",
|
||
"IAMCCS_MultilinePromptSplitter8": "Multiline Prompt Splitter (8 outputs)",
|
||
"IAMCCS_SupernodeBase": "Supernode Base (contract + linx)",
|
||
"IAMCCS_SupernodeModule": "Supernode Module (cascade contract + linx)",
|
||
"IAMCCS_ProjectTimelinePlanner": "IAMCCS Project Timeline Planner",
|
||
"IAMCCS_Ltx2HelperModules_ProjectTimelinePlanner": "IAMCCS_Ltx2HelperModules Project Timeline Planner",
|
||
"IAMCCS_Ltx2HelperModules_Planner": "IAMCCS_Ltx2HelperModules Planner",
|
||
"IAMCCS_Ltx2HelperModules_AudioTimeline": "IAMCCS_Ltx2HelperModules Audio Timeline",
|
||
"IAMCCS_Ltx2HelperModules_KeyframeTimeline": "IAMCCS_Ltx2HelperModules Keyframe Timeline",
|
||
"IAMCCS_Ltx2HelperModules_RefreshPolicy": "IAMCCS_Ltx2HelperModules Refresh Policy",
|
||
"IAMCCS_Ltx2HelperModules_ReanchorLatent": "IAMCCS_Ltx2HelperModules Reanchor Latent",
|
||
"IAMCCS_Ltx2HelperModules_DiskExtension": "IAMCCS_Ltx2HelperModules Disk Extension",
|
||
"IAMCCS_Ltx2HelperModules_RuntimeBridge": "IAMCCS_Ltx2HelperModules Runtime Bridge",
|
||
"IAMCCS_Ltx2HelperModules_Continuity": "IAMCCS_Ltx2HelperModules Continuity",
|
||
"IAMCCS_Ltx2HelperModules_Finalize": "IAMCCS_Ltx2HelperModules Finalize",
|
||
"IAMCCS_AUIMG2VID_ProjectTimelinePlanner": "AU+IMG2VID Project Timeline Planner (legacy alias)",
|
||
"IAMCCS_AUIMG2VID_Planner": "AU+IMG2VID Planner (legacy alias)",
|
||
"IAMCCS_AUIMG2VID_AudioTimeline": "AU+IMG2VID Audio Timeline (legacy alias)",
|
||
"IAMCCS_AUIMG2VID_KeyframeTimeline": "AU+IMG2VID Keyframe Timeline (legacy alias)",
|
||
"IAMCCS_AUIMG2VID_RefreshPolicy": "AU+IMG2VID Refresh Policy (legacy alias)",
|
||
"IAMCCS_AUIMG2VID_ReanchorLatent": "AU+IMG2VID Reanchor Latent (legacy alias)",
|
||
"IAMCCS_AUIMG2VID_DiskExtension": "AU+IMG2VID Disk Extension (legacy alias)",
|
||
"IAMCCS_AUIMG2VID_RuntimeBridge": "AU+IMG2VID Runtime Bridge (legacy alias)",
|
||
"IAMCCS_AUIMG2VID_Continuity": "AU+IMG2VID Continuity (legacy alias)",
|
||
"IAMCCS_AUIMG2VID_Finalize": "AU+IMG2VID Finalize (legacy alias)",
|
||
"IAMCCS-SuperNodes AU+IMG2VID Exec Planner": "IAMCCS-SuperNodes AU+IMG2VID Exec Planner",
|
||
"IAMCCS-SuperNodes AU+IMG2VID Exec Render": "IAMCCS-SuperNodes AU+IMG2VID Exec Render",
|
||
"IAMCCS-SuperNodes AU+IMG2VID Exec VAE": "IAMCCS-SuperNodes AU+IMG2VID Exec VAE",
|
||
"IAMCCS-SuperNodes Second Stage": "IAMCCS-SuperNodes Second Stage",
|
||
"IAMCCS-SuperNodes AU+IMG2VID Exec Finalize": "IAMCCS-SuperNodes AU+IMG2VID Exec Finalize",
|
||
|
||
"IAMCCS_SamplerAdvancedVersion1": "Sampler Advanced v1",
|
||
"IAMCCS_InfluencerSampler": "IAMCCS Influencer Sampler",
|
||
"IAMCCS_InfluencerLatent": "IAMCCS Influencer Latent / VAE",
|
||
|
||
"IAMCCS_bus_group": "Bus Group (Mute + Solo) (frontend-only)",
|
||
|
||
"IAMCCS_MultiSwitch": "MultiSwitch (dynamic inputs)",
|
||
"IAMCCS_LazyAnySwitch": "Lazy MultiGen Switch (Qwen / Flux)",
|
||
"IAMCCS_Navigator": "IAMCCS Navigator",
|
||
"IAMCCS_WanSviArgs": "Wan SVI Args",
|
||
"IAMCCS_WanSviChainRunner": "Wan SVI Chain Runner",
|
||
"IAMCCS_WanSviSegmentPick": "Wan SVI Segment Pick",
|
||
|
||
"IAMCCS_HwSupporter": "HW Supporter (auto VRAM/attention/torch knobs)",
|
||
"IAMCCS_HwSupporterAny": "HW Supporter (ANY passthrough)",
|
||
"IAMCCS_HardMemoryPurge": "Hard RAM/VRAM Purge (trim working set)",
|
||
"IAMCCS_VRAMCleanup": "VRAM Cleanup (unload + empty cache)",
|
||
"IAMCCS_VRAMFlushLatent": "VRAM Flush ? Latent passthrough (empty cache)",
|
||
"IAMCCS_VAEDecodeTiledSafe": "VAE Decode Tiled (safe, optional cleanup)",
|
||
"IAMCCS_VAEDecodeToDisk": "VAE Decode ? Disk (frames, low RAM)",
|
||
"IAMCCS_HWProbeRecommendations": "HW Probe Recommendations (JSON)",
|
||
"IAMCCS_DetailAtelier": "IAMCCS Detail Atelier",
|
||
"IAMCCS_DetailAtelierAdvanced": "IAMCCS Detail Atelier Advanced",
|
||
"IAMCCS_DetailAtelierSampler": "IAMCCS Detail Atelier",
|
||
|
||
"IAMCCS_MoveAhead": "MoveAhead (FreeLong spectral blend) ??",
|
||
"IAMCCS_MoveAheadEnforcer": "MoveAhead Enforcer (3-tier motion lock) ??",
|
||
"IAMCCS_MotionScale": "MotionScale (temporal RoPE scale) ?",
|
||
"IAMCCS_MotionScaleAdvanced": "MotionScale Advanced (RoPE + theta) ?",
|
||
|
||
"IAMCCS_MotionBridgeSave": "Motion Bridge Save ????",
|
||
"IAMCCS_MotionBridgeLoad": "Motion Bridge Load ????",
|
||
"IAMCCS_LatentTailSlice": "Latent Tail Slice ??",
|
||
"IAMCCS_AudioExtensionMath": "Audio Extension Math (timeline sync)",
|
||
"IAMCCS_AudioExtender": "Audio Extender (segment + overlap)",
|
||
"IAMCCS_AudioTimelineAssembler": "Audio Timeline Assembler (full track)",
|
||
"IAMCCS_AudioTimelineGate": "Audio Timeline Gate (continue/stop)",
|
||
"IAMCCS_BoardMaker_DialogueFoley": "IAMCCS BoardMaker Dialogue Foley",
|
||
"IAMCCS_CineInfo3": "IAMCCS Cine Info 3",
|
||
"IAMCCS_CineDialogueLineRouter": "IAMCCS Dialogue Line Router",
|
||
"IAMCCS_CineTimelineAudioMixer": "IAMCCS Timeline Audio Mixer",
|
||
"IAMCCS_AudioBoardArranger": "IAMCCS AudioBoard Arranger",
|
||
"IAMCCS_BusOut": "IAMCCS BusOut",
|
||
"IAMCCS_AudioBoardDirectShotboardAdapter": "IAMCCS AudioBoard Direct -> Shotboard Adapter",
|
||
"IAMCCS_AudioBoardMixer": "IAMCCS AudioBoard Mixer",
|
||
"IAMCCS_ControlAudEfx": "IAMCCS ControlAudEfx",
|
||
"IAMCCS_ControlAudEfxPanel": "IAMCCS ControlAudEfx Panel",
|
||
"IAMCCS_DialogueTagEditor": "IAMCCS Dialogue Tag Editor",
|
||
"IAMCCS_DialogueAudioBoardBridge": "IAMCCS Dialogue AudioBoard Bridge",
|
||
"IAMCCS_CineAudioInfo": "IAMCCS CineAudioInfo",
|
||
"IAMCCS_AudioPhaseLazyGate": "IAMCCS Audio Phase Lazy Gate",
|
||
"IAMCCS_StoryboardFrameDesigner": "IAMCCS StoryboardFrame + TextInFrame Director",
|
||
"IAMCCS_StoryboardFrameDesignerV2": "IAMCCS StoryboardFrame V2 + Image Canvas i2i",
|
||
"IAMCCS_IdeoInfo": "IDEO_INFO",
|
||
"IAMCCS_IdeoInpaintPrep": "IAMCCS Ideo Inpaint Prep",
|
||
"IAMCCS_IdeoMaskedPixels": "IAMCCS Ideo Masked Pixels",
|
||
"IAMCCS_IdeogramJSONPreviewPass": "IAMCCS Ideogram JSON Preview / Pass",
|
||
"IAMCCS_IdeoNoiseDebug": "IDEO Noise Debug",
|
||
"IAMCCS_IdeoRandomNoiseNoCache": "IDEO Random Noise No Cache",
|
||
"IAMCCS_IdeoImageHashDebug": "IDEO Image Hash Debug",
|
||
"IAMCCS_TargetCrop": "IAMCCS Target Crop",
|
||
"IAMCCS_IdeoTranslate": "IAMCCS IdeoTranslate",
|
||
"IAMCCS_IdeogramStoryboardSheet": "IAMCCS Ideogram Storyboard Sheet",
|
||
"IAMCCS_IdeogramSheetBuilder": "IAMCCS Ideogram Sheet Builder",
|
||
"IAMCCS_GemmaAssistLazyGate": "IAMCCS Gemma Assist Lazy Gate",
|
||
"IAMCCS_GemmaAssistOutput": "IAMCCS Gemma Assist Output",
|
||
"IAMCCS_StoryboardPromptContactSheet": "IAMCCS Storyboard Prompt Contact Sheet",
|
||
"IAMCCS_GoyAICanvasPaint": "GoyAIcanvas Paint (Image + Mask)",
|
||
"IAMCCS_FlashVSRPanelBatchPrep": "IAMCCS FlashVSR Panel Batch Prep",
|
||
"IAMCCS_FlashVSRPanelBatchRestore": "IAMCCS FlashVSR Panel Batch Restore",
|
||
"IAMCCS_CineVideoToWooshInputs": "IAMCCS Video To Woosh Inputs",
|
||
"IAMCCS_CineSpeech1PromptCompiler": "IAMCCS Speech1 Prompt Compiler",
|
||
"IAMCCS_CineAudioTranscriptPromptCompiler": "IAMCCS Audio Transcript Prompt Compiler",
|
||
"IAMCCS_CineSpeechLength": "IAMCCS Speech Length Calculator",
|
||
"IAMCCS_CineDialogueDurationPlanner": "IAMCCS Dialogue Duration Planner",
|
||
"IAMCCS_CineAudioDurationProbe": "IAMCCS Audio Duration Probe",
|
||
"IAMCCS_CineDialogueTimingReconciler": "IAMCCS Dialogue Timing Reconciler",
|
||
"IAMCCS_CineWooshFoleyChunkPlanner": "IAMCCS Woosh Foley Chunk Planner",
|
||
"IAMCCS_CineFinalAudioMixer": "IAMCCS Final Audio Mixer",
|
||
"IAMCCS_CineEmotionButtons": "IAMCCS Emotion Buttons",
|
||
"IAMCCS_CineDialoguePromptKit": "IAMCCS Dialogue Prompt Kit",
|
||
"IAMCCS_LTX2_LastFrameBridgeSave": "LTX-2 Last Frame Bridge Save ?????",
|
||
"IAMCCS_LTX2_BlendLatentBridge": "LTX-2 Blend Latent Bridge ???",
|
||
"IAMCCS_LTX2_LastFrameBridgeLoad": "LTX-2 Last Frame Bridge Load ???",
|
||
"IAMCCS_LTX2_LoadLatentBridge": "LTX-2 Load Latent Bridge ??",
|
||
"IAMCCS_LTX2_LongVideoWrapperPrep": "LTX-2 Long Video Wrapper Prep ??",
|
||
"IAMCCS_LTX2_LongVideoWrapperPrepDisk": "LTX-2 Long Video Wrapper Prep (Disk) ????",
|
||
"IAMCCS_LTX2_SaveLatentBridge": "LTX-2 Save Latent Bridge ??",
|
||
"IAMCCS_LTX2_SegmentQueueLoop": "LTX-2 Segment Queue Loop ??",
|
||
"IAMCCS_ImageResizeBatchSafe": "Image Resize Batch Safe (IAMCCS)",
|
||
"IAMCCS_LoadResizeSegmentFromDir": "Load + Resize Segment From Dir ??",
|
||
|
||
# QwenVL FLF
|
||
**({"IAMCCS_QWEN_VL_FLF": "QwenVL FLF — First/Last Frame Prompt ??",
|
||
"IAMCCS_QWEN_VL_FLF_Advanced": "QwenVL FLF — First/Last Frame Prompt (Advanced) ??",
|
||
} if IAMCCS_QWEN_VL_FLF is not None else {}),
|
||
|
||
# MiniMax H3 Shotboard (isolated module; no V3/V4 dependency)
|
||
**IAMCCS_MINIMAX_H3_NODE_DISPLAY_NAME_MAPPINGS,
|
||
|
||
**IAMCCS_PROMPTER_NODE_DISPLAY_NAME_MAPPINGS,
|
||
**IAMCCS_CINE_NODES_NODE_DISPLAY_NAME_MAPPINGS,
|
||
**IAMCCS_CINE_H3_VISION_NODE_DISPLAY_NAME_MAPPINGS,
|
||
|
||
}
|
||
|
||
try:
|
||
from .cine_wan_shotboard_pure import (
|
||
NODE_CLASS_MAPPINGS as _IAMCCS_WAN_SHOTBOARD_PURE_NODE_CLASS_MAPPINGS,
|
||
NODE_DISPLAY_NAME_MAPPINGS as _IAMCCS_WAN_SHOTBOARD_PURE_NODE_DISPLAY_NAME_MAPPINGS,
|
||
)
|
||
NODE_CLASS_MAPPINGS.update(_IAMCCS_WAN_SHOTBOARD_PURE_NODE_CLASS_MAPPINGS)
|
||
NODE_DISPLAY_NAME_MAPPINGS.update(_IAMCCS_WAN_SHOTBOARD_PURE_NODE_DISPLAY_NAME_MAPPINGS)
|
||
except Exception as _iamccs_wan_shotboard_pure_error:
|
||
print(f"[IAMCCS WAN PURE] optional module not loaded: {_iamccs_wan_shotboard_pure_error}")
|
||
|
||
try:
|
||
from .cine_multigeneration import (
|
||
NODE_CLASS_MAPPINGS as _IAMCCS_MULTIGENERATION_NODE_CLASS_MAPPINGS,
|
||
NODE_DISPLAY_NAME_MAPPINGS as _IAMCCS_MULTIGENERATION_NODE_DISPLAY_NAME_MAPPINGS,
|
||
)
|
||
NODE_CLASS_MAPPINGS.update(_IAMCCS_MULTIGENERATION_NODE_CLASS_MAPPINGS)
|
||
NODE_DISPLAY_NAME_MAPPINGS.update(_IAMCCS_MULTIGENERATION_NODE_DISPLAY_NAME_MAPPINGS)
|
||
except Exception as _iamccs_multigeneration_error:
|
||
print(f"[IAMCCS Multigeneration] optional module not loaded: {_iamccs_multigeneration_error}")
|
||
|
||
try:
|
||
from .engine_v2v import (
|
||
NODE_CLASS_MAPPINGS as _IAMCCS_ENGINE_V2V_NODE_CLASS_MAPPINGS,
|
||
NODE_DISPLAY_NAME_MAPPINGS as _IAMCCS_ENGINE_V2V_NODE_DISPLAY_NAME_MAPPINGS,
|
||
)
|
||
NODE_CLASS_MAPPINGS.update(_IAMCCS_ENGINE_V2V_NODE_CLASS_MAPPINGS)
|
||
NODE_DISPLAY_NAME_MAPPINGS.update(_IAMCCS_ENGINE_V2V_NODE_DISPLAY_NAME_MAPPINGS)
|
||
except Exception as _iamccs_engine_v2v_error:
|
||
print(f"[IAMCCS Engine V2V] optional module not loaded: {_iamccs_engine_v2v_error}")
|
||
|
||
try:
|
||
from .v2v_shotboard_free import (
|
||
NODE_CLASS_MAPPINGS as _IAMCCS_V2V_SHOTBOARD_FREE_NODE_CLASS_MAPPINGS,
|
||
NODE_DISPLAY_NAME_MAPPINGS as _IAMCCS_V2V_SHOTBOARD_FREE_NODE_DISPLAY_NAME_MAPPINGS,
|
||
)
|
||
NODE_CLASS_MAPPINGS.update(_IAMCCS_V2V_SHOTBOARD_FREE_NODE_CLASS_MAPPINGS)
|
||
NODE_DISPLAY_NAME_MAPPINGS.update(_IAMCCS_V2V_SHOTBOARD_FREE_NODE_DISPLAY_NAME_MAPPINGS)
|
||
except Exception as _iamccs_v2v_shotboard_free_error:
|
||
print(f"[IAMCCS V2V Shotboard Free] optional module not loaded: {_iamccs_v2v_shotboard_free_error}")
|
||
|
||
try:
|
||
from .v2v_shotboard_easy_wananimate2 import (
|
||
NODE_CLASS_MAPPINGS as _IAMCCS_V2V_SHOTBOARD_EASY_WANANIMATE2_NODE_CLASS_MAPPINGS,
|
||
NODE_DISPLAY_NAME_MAPPINGS as _IAMCCS_V2V_SHOTBOARD_EASY_WANANIMATE2_NODE_DISPLAY_NAME_MAPPINGS,
|
||
)
|
||
NODE_CLASS_MAPPINGS.update(_IAMCCS_V2V_SHOTBOARD_EASY_WANANIMATE2_NODE_CLASS_MAPPINGS)
|
||
NODE_DISPLAY_NAME_MAPPINGS.update(_IAMCCS_V2V_SHOTBOARD_EASY_WANANIMATE2_NODE_DISPLAY_NAME_MAPPINGS)
|
||
except Exception as _iamccs_v2v_shotboard_easy_wananimate2_error:
|
||
print(
|
||
"[IAMCCS V2V Shotboard Easy WanAnimate2] optional module not loaded: "
|
||
f"{_iamccs_v2v_shotboard_easy_wananimate2_error}"
|
||
)
|
||
|
||
|
||
try:
|
||
import importlib.util as _iamccs_importlib_util
|
||
|
||
_iamccs_cine_pp_path = os.path.join(os.path.dirname(__file__), "cine-pp", "post_aa.py")
|
||
_iamccs_cine_pp_spec = _iamccs_importlib_util.spec_from_file_location(
|
||
"iamccs_cine_pp_post_aa",
|
||
_iamccs_cine_pp_path,
|
||
)
|
||
if _iamccs_cine_pp_spec is None or _iamccs_cine_pp_spec.loader is None:
|
||
raise ImportError(f"Cannot load IAMCCS Cine-PP module from {_iamccs_cine_pp_path}")
|
||
_iamccs_cine_pp_module = _iamccs_importlib_util.module_from_spec(_iamccs_cine_pp_spec)
|
||
_iamccs_cine_pp_spec.loader.exec_module(_iamccs_cine_pp_module)
|
||
NODE_CLASS_MAPPINGS.update(getattr(_iamccs_cine_pp_module, "NODE_CLASS_MAPPINGS", {}))
|
||
NODE_DISPLAY_NAME_MAPPINGS.update(getattr(_iamccs_cine_pp_module, "NODE_DISPLAY_NAME_MAPPINGS", {}))
|
||
except Exception as _iamccs_cine_pp_error:
|
||
print(f"[IAMCCS Cine-PP] optional module not loaded: {_iamccs_cine_pp_error}")
|
||
|
||
WEB_DIRECTORY = "./web"
|
||
|
||
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS", "WEB_DIRECTORY"]
|
||
|
||
from .iamccs_h3_face_swap import NODE_CLASS_MAPPINGS as _FACE_SWAP_NODES, NODE_DISPLAY_NAME_MAPPINGS as _FACE_SWAP_NAMES
|
||
NODE_CLASS_MAPPINGS.update(_FACE_SWAP_NODES)
|
||
NODE_DISPLAY_NAME_MAPPINGS.update(_FACE_SWAP_NAMES)
|
||
|
||
|
||
def _print_startup_banner() -> None:
|
||
# Print once per process.
|
||
if getattr(_print_startup_banner, "_done", False):
|
||
return
|
||
_print_startup_banner._done = True # type: ignore[attr-defined]
|
||
|
||
banner = r"""
|
||
___ _ __ __ ____ ____ ____ ____ _
|
||
|_ _| / \ | \/ |/ ___/ ___|/ ___| | _ \ ___ ___| | _____
|
||
| | / _ \ | |\/| | | | | \___ \ | |_) / _ \ / __| |/ / __|
|
||
| | / ___ \| | | | |__| |___ ___) || __/ (_) | (__| <\__ \
|
||
|___/_/ \_\_| |_|\____\____||____/ |_| \___/ \___|_|\_\___/
|
||
|
||
"""
|
||
log = logging.getLogger("IAMCCS")
|
||
log.info("%s", banner)
|
||
log.info("by IAMCCS (follow me on patreon.com/IAMCCS or carminecristalloscalzi.com)")
|
||
|
||
try:
|
||
keys = sorted(list(NODE_CLASS_MAPPINGS.keys()))
|
||
log.info("IAMCCS nodes loaded: %d", len(keys))
|
||
# Keep log readable: print in chunks.
|
||
chunk = []
|
||
for k in keys:
|
||
chunk.append(k)
|
||
if len(chunk) >= 10:
|
||
log.info("- %s", ", ".join(chunk))
|
||
chunk = []
|
||
if chunk:
|
||
log.info("- %s", ", ".join(chunk))
|
||
except Exception:
|
||
pass
|
||
|
||
|
||
def setup_api_routes() -> None:
|
||
"""IAMCCS API routes used by frontend widgets."""
|
||
|
||
try:
|
||
from server import PromptServer
|
||
from aiohttp import web
|
||
|
||
from .iamccs_hw_probe import recommend_settings
|
||
|
||
routes = PromptServer.instance.routes
|
||
|
||
@routes.get("/api/iamccs/cine/view_image")
|
||
async def iamccs_cine_view_image(request):
|
||
try:
|
||
q = request.rel_url.query
|
||
path = q.get("path", "")
|
||
if not path:
|
||
return web.Response(status=400, text="Missing path")
|
||
path = os.path.abspath(os.path.expanduser(path))
|
||
allowed_ext = {".png", ".jpg", ".jpeg", ".webp", ".bmp", ".gif", ".tif", ".tiff"}
|
||
ext = os.path.splitext(path)[1].lower()
|
||
if ext not in allowed_ext:
|
||
return web.Response(status=400, text="Unsupported image extension")
|
||
if not os.path.exists(path) or not os.path.isfile(path):
|
||
return web.Response(status=404, text="Image not found")
|
||
response = web.FileResponse(path)
|
||
response.headers["Cache-Control"] = "no-store, no-cache, must-revalidate, max-age=0"
|
||
response.headers["Pragma"] = "no-cache"
|
||
response.headers["Expires"] = "0"
|
||
return response
|
||
except Exception as e:
|
||
return web.Response(status=500, text=str(e))
|
||
|
||
@routes.post("/api/iamccs/cine/video_editor/purge_parking")
|
||
async def iamccs_video_editor_purge_parking(request):
|
||
"""Remove only the active Video Editor session's parked media."""
|
||
try:
|
||
from .cine_multigeneration import _parking_root, _safe_slug
|
||
|
||
try:
|
||
payload = await request.json()
|
||
except Exception:
|
||
payload = {}
|
||
session_key = _safe_slug(payload.get("session_key") or "shotboard_video_editor_v1")
|
||
all_sessions = bool(payload.get("all_sessions", False))
|
||
session_dir = os.path.abspath(_parking_root(session_key))
|
||
parking_root = os.path.abspath(os.path.dirname(session_dir))
|
||
if os.path.commonpath([parking_root, session_dir]) != parking_root:
|
||
return web.json_response({"error": "Invalid parking session path."}, status=400)
|
||
|
||
targets = [session_dir]
|
||
if all_sessions:
|
||
targets = []
|
||
if os.path.isdir(parking_root):
|
||
for entry in os.scandir(parking_root):
|
||
if entry.is_dir() and not entry.is_symlink():
|
||
target = os.path.abspath(entry.path)
|
||
if os.path.commonpath([parking_root, target]) == parking_root:
|
||
targets.append(target)
|
||
|
||
deleted_files = 0
|
||
deleted_bytes = 0
|
||
failed_paths = []
|
||
for target in targets:
|
||
if not os.path.isdir(target):
|
||
continue
|
||
for base, _, names in os.walk(target, followlinks=False):
|
||
for name in names:
|
||
candidate = os.path.join(base, name)
|
||
try:
|
||
if os.path.islink(candidate) or os.path.isfile(candidate):
|
||
deleted_bytes += int(os.lstat(candidate).st_size)
|
||
deleted_files += 1
|
||
except Exception:
|
||
pass
|
||
for entry in list(os.scandir(target)):
|
||
try:
|
||
if entry.is_symlink() or entry.is_file():
|
||
os.remove(entry.path)
|
||
elif entry.is_dir():
|
||
shutil.rmtree(entry.path)
|
||
except FileNotFoundError:
|
||
pass
|
||
except Exception as exc:
|
||
failed_paths.append({"path": entry.path, "error": str(exc)})
|
||
os.makedirs(target, exist_ok=True)
|
||
|
||
remaining_files = 0
|
||
remaining_bytes = 0
|
||
remaining_root = parking_root if all_sessions else session_dir
|
||
if os.path.isdir(remaining_root):
|
||
for base, _, names in os.walk(remaining_root, followlinks=False):
|
||
for name in names:
|
||
candidate = os.path.join(base, name)
|
||
try:
|
||
if os.path.islink(candidate) or os.path.isfile(candidate):
|
||
remaining_files += 1
|
||
remaining_bytes += int(os.lstat(candidate).st_size)
|
||
except Exception:
|
||
pass
|
||
|
||
return web.json_response({
|
||
"ok": True,
|
||
"session_key": session_key,
|
||
"folder": parking_root if all_sessions else session_dir,
|
||
"all_sessions": all_sessions,
|
||
"sessions": len(targets),
|
||
"deleted_files": deleted_files,
|
||
"deleted_bytes": deleted_bytes,
|
||
"failed_files": len(failed_paths),
|
||
"failed_paths": failed_paths[:20],
|
||
"remaining_files": remaining_files,
|
||
"remaining_bytes": remaining_bytes,
|
||
})
|
||
except Exception as exc:
|
||
return web.json_response({"error": str(exc)}, status=500)
|
||
|
||
@routes.post("/api/iamccs/cine/video_editor/ensure_preview_video")
|
||
async def iamccs_video_editor_ensure_preview_video(request):
|
||
"""Create the lightweight browser playback proxy for an existing parked take."""
|
||
try:
|
||
from .cine_multigeneration import _parking_root, _safe_slug, ensure_parked_take_preview_video
|
||
|
||
try:
|
||
payload = await request.json()
|
||
except Exception:
|
||
payload = {}
|
||
session_key = _safe_slug(payload.get("session_key") or "shotboard_video_editor_v1")
|
||
parking_path = str(payload.get("parking_tensor_path") or "").strip()
|
||
if not parking_path:
|
||
return web.json_response({"error": "Missing parked take path."}, status=400)
|
||
session_dir = os.path.abspath(_parking_root(session_key))
|
||
parking_root = os.path.abspath(os.path.dirname(session_dir))
|
||
source = os.path.abspath(parking_path)
|
||
if os.path.commonpath([parking_root, source]) != parking_root:
|
||
return web.json_response({"error": "Preview source is outside IAMCCS parking."}, status=400)
|
||
preview = ensure_parked_take_preview_video(source)
|
||
return web.json_response({"ok": True, **preview})
|
||
except Exception as exc:
|
||
return web.json_response({"error": str(exc)}, status=500)
|
||
|
||
@routes.post("/api/iamccs/cine/transform_reference")
|
||
async def iamccs_cine_transform_reference(request):
|
||
try:
|
||
import json
|
||
import re
|
||
import time
|
||
import folder_paths
|
||
from PIL import Image, ImageOps
|
||
|
||
def _float(value, default):
|
||
try:
|
||
return float(value)
|
||
except Exception:
|
||
return float(default)
|
||
|
||
def _int(value, default):
|
||
try:
|
||
return int(round(float(value)))
|
||
except Exception:
|
||
return int(default)
|
||
|
||
def _inside_dir(path, root):
|
||
try:
|
||
rel = os.path.relpath(os.path.abspath(path), os.path.abspath(root))
|
||
return rel != os.pardir and not rel.startswith(os.pardir + os.sep) and not os.path.isabs(rel)
|
||
except Exception:
|
||
return False
|
||
|
||
def _input_relative(path, input_root):
|
||
try:
|
||
if not _inside_dir(path, input_root):
|
||
return None
|
||
return os.path.relpath(os.path.abspath(path), os.path.abspath(input_root)).replace(os.sep, "/")
|
||
except Exception:
|
||
return None
|
||
|
||
def _safe_paste(canvas, layer, x, y):
|
||
x = int(round(x))
|
||
y = int(round(y))
|
||
src_l = max(0, -x)
|
||
src_t = max(0, -y)
|
||
dst_l = max(0, x)
|
||
dst_t = max(0, y)
|
||
w = min(layer.size[0] - src_l, canvas.size[0] - dst_l)
|
||
h = min(layer.size[1] - src_t, canvas.size[1] - dst_t)
|
||
if w <= 0 or h <= 0:
|
||
return
|
||
crop = layer.crop((src_l, src_t, src_l + w, src_t + h))
|
||
if crop.mode == "RGBA":
|
||
canvas.paste(crop.convert("RGB"), (dst_l, dst_t), crop.getchannel("A"))
|
||
else:
|
||
canvas.paste(crop.convert("RGB"), (dst_l, dst_t))
|
||
|
||
data = await request.json()
|
||
source = str(data.get("path") or data.get("source_path") or "").strip()
|
||
if not source:
|
||
return web.json_response({"error": "Missing source path"}, status=400)
|
||
|
||
input_dir = folder_paths.get_input_directory()
|
||
source_is_absolute = os.path.isabs(source)
|
||
if source_is_absolute:
|
||
source_path = os.path.abspath(os.path.expanduser(source))
|
||
else:
|
||
source_path = os.path.abspath(os.path.join(input_dir, source.replace("/", os.sep)))
|
||
if not os.path.exists(source_path) or not os.path.isfile(source_path):
|
||
return web.json_response({"error": f"Source image not found: {source}"}, status=404)
|
||
|
||
target_w = max(64, min(8192, _int(data.get("width"), 768)))
|
||
target_h = max(64, min(8192, _int(data.get("height"), 432)))
|
||
fit_mode = str(data.get("fit_mode") or "cover").strip().lower()
|
||
if fit_mode not in {"cover", "contain"}:
|
||
fit_mode = "cover"
|
||
zoom = max(1.0, min(16.0, _float(data.get("zoom"), 1.0)))
|
||
pan_x = max(-1.0, min(1.0, _float(data.get("pan_x"), 0.0)))
|
||
pan_y = max(-1.0, min(1.0, _float(data.get("pan_y"), 0.0)))
|
||
rotation = max(-45.0, min(45.0, _float(data.get("rotation"), 0.0)))
|
||
|
||
resample_name = str(data.get("resample") or "lanczos").lower()
|
||
resampling = {
|
||
"nearest": Image.Resampling.NEAREST,
|
||
"bilinear": Image.Resampling.BILINEAR,
|
||
"bicubic": Image.Resampling.BICUBIC,
|
||
"lanczos": Image.Resampling.LANCZOS,
|
||
}.get(resample_name, Image.Resampling.LANCZOS)
|
||
|
||
crop_box = None
|
||
transform_mode = "composite"
|
||
with Image.open(source_path) as im:
|
||
im = ImageOps.exif_transpose(im).convert("RGBA")
|
||
src_w, src_h = im.size
|
||
fill = tuple(int(v) for v in im.convert("RGB").resize((1, 1), Image.Resampling.BILINEAR).getpixel((0, 0)))
|
||
|
||
preview_crop_box = None
|
||
raw_crop_box = data.get("crop_box")
|
||
if abs(rotation) < 0.001 and isinstance(raw_crop_box, (list, tuple)) and len(raw_crop_box) >= 4:
|
||
try:
|
||
l = max(0.0, min(float(src_w - 1), float(raw_crop_box[0])))
|
||
t = max(0.0, min(float(src_h - 1), float(raw_crop_box[1])))
|
||
r = max(l + 1.0, min(float(src_w), float(raw_crop_box[2])))
|
||
b = max(t + 1.0, min(float(src_h), float(raw_crop_box[3])))
|
||
left_i = max(0, min(src_w - 1, int(round(l))))
|
||
top_i = max(0, min(src_h - 1, int(round(t))))
|
||
right_i = max(left_i + 1, min(src_w, int(round(r))))
|
||
bottom_i = max(top_i + 1, min(src_h, int(round(b))))
|
||
preview_crop_box = (left_i, top_i, right_i, bottom_i)
|
||
except Exception:
|
||
preview_crop_box = None
|
||
|
||
if preview_crop_box is not None:
|
||
crop_box = preview_crop_box
|
||
out = im.crop(crop_box).convert("RGB").resize((target_w, target_h), resampling)
|
||
transform_mode = "ui_preview_crop"
|
||
else:
|
||
fit_scale = min(target_w / float(src_w), target_h / float(src_h)) if fit_mode == "contain" else max(target_w / float(src_w), target_h / float(src_h))
|
||
scale = max(0.0001, fit_scale * zoom)
|
||
display_w = max(1, int(round(src_w * scale)))
|
||
display_h = max(1, int(round(src_h * scale)))
|
||
max_shift_x = max(0.0, (display_w - target_w) / 2.0)
|
||
max_shift_y = max(0.0, (display_h - target_h) / 2.0)
|
||
left = (target_w - display_w) / 2.0 + pan_x * max_shift_x
|
||
top = (target_h - display_h) / 2.0 + pan_y * max_shift_y
|
||
layer = im.resize((display_w, display_h), resampling)
|
||
if abs(rotation) > 0.001:
|
||
center_x = left + display_w / 2.0
|
||
center_y = top + display_h / 2.0
|
||
layer = layer.rotate(-rotation, resample=Image.Resampling.BICUBIC, expand=True, fillcolor=(0, 0, 0, 0))
|
||
left = center_x - layer.size[0] / 2.0
|
||
top = center_y - layer.size[1] / 2.0
|
||
out = Image.new("RGB", (target_w, target_h), fill)
|
||
_safe_paste(out, layer, left, top)
|
||
crop_box = {
|
||
"display_left": left,
|
||
"display_top": top,
|
||
"display_width": layer.size[0],
|
||
"display_height": layer.size[1],
|
||
"source_width": src_w,
|
||
"source_height": src_h,
|
||
}
|
||
|
||
source_dir = os.path.dirname(source_path)
|
||
fallback_dir = os.path.join(input_dir, "IAMCCS_newimages")
|
||
source_subdir = os.path.dirname(source.replace("/", os.sep)) if not source_is_absolute else ""
|
||
if source_dir and os.path.isdir(source_dir):
|
||
out_dir = source_dir
|
||
elif source_subdir:
|
||
out_dir = os.path.join(input_dir, source_subdir)
|
||
else:
|
||
out_dir = fallback_dir
|
||
try:
|
||
os.makedirs(out_dir, exist_ok=True)
|
||
test_path = os.path.join(out_dir, ".iamccs_write_test")
|
||
with open(test_path, "w", encoding="utf-8") as test_file:
|
||
test_file.write("ok")
|
||
try:
|
||
os.remove(test_path)
|
||
except Exception:
|
||
pass
|
||
except Exception:
|
||
out_dir = fallback_dir
|
||
os.makedirs(out_dir, exist_ok=True)
|
||
|
||
stem = os.path.splitext(os.path.basename(source_path))[0]
|
||
stem = re.sub(r"[^A-Za-z0-9_.-]+", "_", stem).strip("._")[:60] or "cine_ref"
|
||
filename = f"{stem}_cineedit_{int(time.time() * 1000)}.png"
|
||
out_path = os.path.abspath(os.path.join(out_dir, filename))
|
||
out.save(out_path, "PNG", optimize=True)
|
||
|
||
rel_path = _input_relative(out_path, input_dir)
|
||
ui_path = out_path
|
||
metadata = {
|
||
"source_path": source_path,
|
||
"edited_path": out_path,
|
||
"path": out_path,
|
||
"display_path": out_path,
|
||
"relative_path": rel_path,
|
||
"project_adjacent": os.path.abspath(out_dir) == os.path.abspath(source_dir),
|
||
"transform": {
|
||
"width": target_w,
|
||
"height": target_h,
|
||
"fit_mode": fit_mode,
|
||
"zoom": zoom,
|
||
"pan_x": pan_x,
|
||
"pan_y": pan_y,
|
||
"rotation": rotation,
|
||
"resample": resample_name,
|
||
"mode": transform_mode,
|
||
"crop_box": crop_box,
|
||
"crop_box_source": data.get("crop_box_source") or ("ui_preview" if transform_mode == "ui_preview_crop" else "backend_composite"),
|
||
},
|
||
}
|
||
with open(out_path + ".json", "w", encoding="utf-8") as meta_file:
|
||
json.dump(metadata, meta_file, indent=2)
|
||
|
||
return web.json_response({
|
||
"ok": True,
|
||
"path": out_path,
|
||
"display_path": out_path,
|
||
"relative_path": rel_path,
|
||
"absolute_path": out_path,
|
||
"cache_bust": int(time.time() * 1000),
|
||
"metadata": metadata,
|
||
})
|
||
except Exception as e:
|
||
return web.json_response({"error": str(e)}, status=500)
|
||
|
||
@routes.post("/api/iamccs/cine/save_shotboard_package")
|
||
async def iamccs_cine_save_shotboard_package(request):
|
||
try:
|
||
import base64
|
||
import copy
|
||
import json
|
||
import re
|
||
import shutil
|
||
import time
|
||
import folder_paths
|
||
|
||
def _sanitize(value, fallback="cine_shotboard_package"):
|
||
clean = re.sub(r'[<>:"/\\|?*\x00-\x1F]+', "_", str(value or fallback).strip())
|
||
clean = re.sub(r"\s+", "_", clean).strip("._")
|
||
return (clean[:90] or fallback)
|
||
|
||
def _split_paths(value):
|
||
if isinstance(value, list):
|
||
return [str(item).strip() for item in value if str(item or "").strip()]
|
||
raw = str(value or "").strip()
|
||
if not raw:
|
||
return []
|
||
try:
|
||
parsed = json.loads(raw)
|
||
if isinstance(parsed, list):
|
||
return [str(item).strip() for item in parsed if str(item or "").strip()]
|
||
except Exception:
|
||
pass
|
||
if "\n" in raw or "\r" in raw:
|
||
return [item.strip() for item in raw.splitlines() if item.strip()]
|
||
return [item.strip() for item in raw.split(",") if item.strip()] if "," in raw else [raw]
|
||
|
||
def _add_path(paths, seen, value):
|
||
clean = str(value or "").strip()
|
||
if clean and clean not in seen:
|
||
seen.add(clean)
|
||
paths.append(clean)
|
||
|
||
def _collect_paths(board):
|
||
paths = []
|
||
seen = set()
|
||
|
||
def _collect_payload(payload):
|
||
if not isinstance(payload, dict):
|
||
return
|
||
for path in _split_paths(payload.get("image_paths")):
|
||
_add_path(paths, seen, path)
|
||
for item in payload.get("images") or []:
|
||
if isinstance(item, dict):
|
||
_add_path(paths, seen, item.get("path") or item.get("original_path") or item.get("filename") or item.get("name"))
|
||
for seg in (payload.get("segments") or []):
|
||
if isinstance(seg, dict):
|
||
_add_path(paths, seen, seg.get("imageTruthPath") or seg.get("image_truth_path") or seg.get("imageFile") or seg.get("image_file") or seg.get("path"))
|
||
for row in (payload.get("rows") or []):
|
||
if isinstance(row, dict):
|
||
_add_path(paths, seen, row.get("imageTruthPath") or row.get("image_truth_path") or row.get("imageFile") or row.get("image_file") or row.get("path"))
|
||
|
||
def _collect_multi(container):
|
||
if not isinstance(container, dict):
|
||
return
|
||
multi = container.get("multiGeneration") if isinstance(container.get("multiGeneration"), dict) else container
|
||
visual_timelines = multi.get("visualTimelines") if isinstance(multi.get("visualTimelines"), dict) else {}
|
||
for visual in visual_timelines.values():
|
||
_collect_payload(visual)
|
||
for item in container.get("timelines") or []:
|
||
if isinstance(item, dict):
|
||
_collect_payload(item.get("timeline") if isinstance(item.get("timeline"), dict) else item)
|
||
|
||
for path in _split_paths(board.get("image_paths")):
|
||
_add_path(paths, seen, path)
|
||
for item in board.get("images") or []:
|
||
if isinstance(item, dict):
|
||
_add_path(paths, seen, item.get("path") or item.get("original_path") or item.get("filename") or item.get("name"))
|
||
for seg in (board.get("segments") or []):
|
||
if isinstance(seg, dict):
|
||
_add_path(paths, seen, seg.get("imageFile") or seg.get("image_file") or seg.get("path"))
|
||
timeline = board.get("timeline")
|
||
if isinstance(timeline, dict):
|
||
_collect_payload(timeline)
|
||
for seg in (timeline.get("segments") or []):
|
||
if isinstance(seg, dict):
|
||
_add_path(paths, seen, seg.get("imageFile") or seg.get("image_file") or seg.get("path"))
|
||
timeline_data = board.get("timeline_data")
|
||
if isinstance(timeline_data, str) and timeline_data.strip():
|
||
try:
|
||
parsed = json.loads(timeline_data)
|
||
for path in _split_paths(parsed.get("image_paths") if isinstance(parsed, dict) else None):
|
||
_add_path(paths, seen, path)
|
||
for seg in (parsed.get("segments") if isinstance(parsed, dict) else []) or []:
|
||
if isinstance(seg, dict):
|
||
_add_path(paths, seen, seg.get("imageFile") or seg.get("image_file") or seg.get("path"))
|
||
if isinstance(parsed, dict):
|
||
_collect_payload(parsed)
|
||
_collect_multi(parsed)
|
||
except Exception:
|
||
pass
|
||
_collect_multi(board)
|
||
_collect_multi(timeline)
|
||
return paths
|
||
|
||
def _resolve_source(path, input_dir):
|
||
clean = str(path or "").strip()
|
||
if not clean or clean.startswith("data:"):
|
||
return None
|
||
if os.path.isabs(clean):
|
||
return os.path.abspath(os.path.expanduser(clean))
|
||
return os.path.abspath(os.path.join(input_dir, clean.replace("/", os.sep)))
|
||
|
||
def _rewrite_segments(segments, path_map):
|
||
if not isinstance(segments, list):
|
||
return
|
||
for seg in segments:
|
||
if not isinstance(seg, dict):
|
||
continue
|
||
for key in ("imageTruthPath", "image_truth_path", "imageFile", "image_file", "path"):
|
||
value = str(seg.get(key) or "").strip()
|
||
if value in path_map:
|
||
seg[key] = path_map[value]
|
||
|
||
def _rewrite_board_paths(board, ordered_paths, path_map):
|
||
def _rewrite_payload(payload, rewrite_image_paths=False):
|
||
if not isinstance(payload, dict):
|
||
return
|
||
if rewrite_image_paths:
|
||
source_paths = _split_paths(payload.get("image_paths"))
|
||
if source_paths:
|
||
payload["image_paths"] = [path_map.get(path, path) for path in source_paths if path_map.get(path, path)]
|
||
if isinstance(payload.get("images"), list):
|
||
for item in payload["images"]:
|
||
if not isinstance(item, dict):
|
||
continue
|
||
value = str(item.get("path") or item.get("original_path") or item.get("filename") or item.get("name") or "").strip()
|
||
if value in path_map:
|
||
item["original_path"] = value
|
||
item["path"] = path_map[value]
|
||
_rewrite_segments(payload.get("segments"), path_map)
|
||
_rewrite_segments(payload.get("rows"), path_map)
|
||
|
||
def _rewrite_multi(container):
|
||
if not isinstance(container, dict):
|
||
return
|
||
multi = container.get("multiGeneration") if isinstance(container.get("multiGeneration"), dict) else container
|
||
visual_timelines = multi.get("visualTimelines") if isinstance(multi.get("visualTimelines"), dict) else {}
|
||
for visual in visual_timelines.values():
|
||
_rewrite_payload(visual, True)
|
||
for item in container.get("timelines") or []:
|
||
if not isinstance(item, dict):
|
||
continue
|
||
_rewrite_payload(item, True)
|
||
if isinstance(item.get("timeline"), dict):
|
||
_rewrite_payload(item["timeline"], True)
|
||
|
||
board["image_paths"] = [path_map.get(path, path) for path in ordered_paths if path_map.get(path, path)]
|
||
if isinstance(board.get("images"), list):
|
||
for item in board["images"]:
|
||
if not isinstance(item, dict):
|
||
continue
|
||
value = str(item.get("path") or item.get("original_path") or item.get("filename") or item.get("name") or "").strip()
|
||
if value in path_map:
|
||
item["original_path"] = value
|
||
item["path"] = path_map[value]
|
||
_rewrite_segments(board.get("segments"), path_map)
|
||
_rewrite_segments(board.get("rows"), path_map)
|
||
_rewrite_multi(board)
|
||
if isinstance(board.get("timeline"), dict):
|
||
board["timeline"]["image_paths"] = [path_map.get(path, path) for path in ordered_paths if path_map.get(path, path)]
|
||
_rewrite_segments(board["timeline"].get("segments"), path_map)
|
||
_rewrite_segments(board["timeline"].get("rows"), path_map)
|
||
_rewrite_multi(board["timeline"])
|
||
if isinstance(board.get("timeline_data"), str) and board["timeline_data"].strip():
|
||
try:
|
||
parsed = json.loads(board["timeline_data"])
|
||
if isinstance(parsed, dict):
|
||
parsed["image_paths"] = [path_map.get(path, path) for path in ordered_paths if path_map.get(path, path)]
|
||
_rewrite_segments(parsed.get("segments"), path_map)
|
||
_rewrite_segments(parsed.get("rows"), path_map)
|
||
_rewrite_multi(parsed)
|
||
board["timeline_data"] = json.dumps(parsed, indent=2)
|
||
except Exception:
|
||
pass
|
||
|
||
def _write_data_url(value, out_path):
|
||
header, _, payload = str(value or "").partition(",")
|
||
if not header.startswith("data:image") or not payload:
|
||
return False
|
||
with open(out_path, "wb") as fh:
|
||
fh.write(base64.b64decode(payload))
|
||
return True
|
||
|
||
def _write_audio_data_url(value, out_path):
|
||
header, _, payload = str(value or "").partition(",")
|
||
if not header.startswith("data:audio/") or not payload:
|
||
return False
|
||
with open(out_path, "wb") as fh:
|
||
fh.write(base64.b64decode(payload))
|
||
return True
|
||
|
||
def _collect_audio_paths(payload):
|
||
paths = []
|
||
seen = set()
|
||
|
||
def add(value):
|
||
clean = str(value or "").strip()
|
||
if clean and clean not in seen:
|
||
seen.add(clean)
|
||
paths.append(clean)
|
||
|
||
def visit(value, owner_key=""):
|
||
if isinstance(value, list):
|
||
for item in value:
|
||
visit(item, owner_key)
|
||
elif isinstance(value, dict):
|
||
# sourceSegment/sourceAudioFile can point to the original
|
||
# three-minute source. A package must carry the rendered
|
||
# excerpts/chunks and MA asset, never silently expand back
|
||
# to that source file.
|
||
for key in ("audioFile", "audio_file"):
|
||
if owner_key == "sourceSegment":
|
||
continue
|
||
add(value.get(key))
|
||
for key, item in value.items():
|
||
visit(item, key)
|
||
|
||
visit(payload)
|
||
return paths
|
||
|
||
def _rewrite_audio_paths(payload, path_map):
|
||
if isinstance(payload, list):
|
||
for item in payload:
|
||
_rewrite_audio_paths(item, path_map)
|
||
return
|
||
if not isinstance(payload, dict):
|
||
return
|
||
for key in ("audioFile", "sourceAudioFile", "audio_file", "source_audio_file"):
|
||
value = str(payload.get(key) or "").strip()
|
||
if value in path_map:
|
||
payload[f"original_{key}"] = value
|
||
payload[key] = path_map[value]
|
||
for item in payload.values():
|
||
_rewrite_audio_paths(item, path_map)
|
||
|
||
data = await request.json()
|
||
board = data.get("board")
|
||
if not isinstance(board, dict):
|
||
return web.json_response({"error": "Missing board object"}, status=400)
|
||
|
||
input_dir = folder_paths.get_input_directory()
|
||
package_root = os.path.join(input_dir, "IAMCCS_shotboard_packages")
|
||
package_name = _sanitize(data.get("package_name") or data.get("label") or f"cine_shotboard_{int(time.time())}")
|
||
package_dir = os.path.join(package_root, package_name)
|
||
images_dir = os.path.join(package_dir, "images")
|
||
audio_dir = os.path.join(package_dir, "audio")
|
||
os.makedirs(images_dir, exist_ok=True)
|
||
os.makedirs(audio_dir, exist_ok=True)
|
||
|
||
original_board = copy.deepcopy(board)
|
||
ordered_paths = _collect_paths(original_board)
|
||
path_map = {}
|
||
manifest_images = []
|
||
allowed_ext = {".png", ".jpg", ".jpeg", ".webp", ".bmp", ".gif", ".tif", ".tiff", ".avif"}
|
||
|
||
for index, original_path in enumerate(ordered_paths, start=1):
|
||
source_path = _resolve_source(original_path, input_dir)
|
||
source_name = os.path.basename(source_path or original_path) or f"ref_{index:03d}.png"
|
||
ext = os.path.splitext(source_name)[1].lower()
|
||
if ext not in allowed_ext:
|
||
ext = ".png"
|
||
clean_stem = _sanitize(os.path.splitext(source_name)[0], f"ref_{index:03d}")[:52]
|
||
filename = f"ref_{index:03d}_{clean_stem}{ext}"
|
||
target_path = os.path.join(images_dir, filename)
|
||
rel_path = "/".join(["IAMCCS_shotboard_packages", package_name, "images", filename])
|
||
entry = {
|
||
"ref": index,
|
||
"original_path": original_path,
|
||
"package_path": rel_path,
|
||
"filename": filename,
|
||
}
|
||
try:
|
||
if str(original_path).startswith("data:image"):
|
||
if not _write_data_url(original_path, target_path):
|
||
raise ValueError("Unsupported data URL")
|
||
else:
|
||
if not source_path or not os.path.isfile(source_path):
|
||
raise FileNotFoundError(f"Image not found: {original_path}")
|
||
shutil.copy2(source_path, target_path)
|
||
entry["source_path"] = source_path
|
||
entry["bytes"] = os.path.getsize(target_path)
|
||
path_map[original_path] = rel_path
|
||
except Exception as err:
|
||
entry["error"] = str(err)
|
||
manifest_images.append(entry)
|
||
|
||
audio_path_map = {}
|
||
manifest_audio = []
|
||
allowed_audio_ext = {".wav", ".mp3", ".flac", ".ogg", ".m4a", ".aac", ".aiff", ".aif", ".opus"}
|
||
for index, original_path in enumerate(_collect_audio_paths(original_board), start=1):
|
||
source_path = _resolve_source(original_path, input_dir)
|
||
source_name = os.path.basename(source_path or original_path) or f"audio_{index:03d}.wav"
|
||
ext = os.path.splitext(source_name)[1].lower()
|
||
if ext not in allowed_audio_ext:
|
||
ext = ".wav"
|
||
clean_stem = _sanitize(os.path.splitext(source_name)[0], f"audio_{index:03d}")[:52]
|
||
filename = f"audio_{index:03d}_{clean_stem}{ext}"
|
||
target_path = os.path.join(audio_dir, filename)
|
||
rel_path = "/".join(["IAMCCS_shotboard_packages", package_name, "audio", filename])
|
||
entry = {
|
||
"index": index,
|
||
"original_path": original_path,
|
||
"package_path": rel_path,
|
||
"filename": filename,
|
||
}
|
||
try:
|
||
if str(original_path).startswith("data:audio"):
|
||
if not _write_audio_data_url(original_path, target_path):
|
||
raise ValueError("Unsupported audio data URL")
|
||
else:
|
||
if not source_path or not os.path.isfile(source_path):
|
||
raise FileNotFoundError(f"Audio not found: {original_path}")
|
||
shutil.copy2(source_path, target_path)
|
||
entry["source_path"] = source_path
|
||
entry["bytes"] = os.path.getsize(target_path)
|
||
audio_path_map[original_path] = rel_path
|
||
except Exception as err:
|
||
entry["error"] = str(err)
|
||
manifest_audio.append(entry)
|
||
|
||
packaged_board = copy.deepcopy(original_board)
|
||
_rewrite_board_paths(packaged_board, ordered_paths, path_map)
|
||
_rewrite_audio_paths(packaged_board, audio_path_map)
|
||
saved_at = time.strftime("%Y-%m-%dT%H:%M:%S%z")
|
||
packaged_board["metadata"] = {
|
||
**(packaged_board.get("metadata") or {}),
|
||
"packaged_at": saved_at,
|
||
"package_schema": "iamccs.cine.shotboard.package",
|
||
}
|
||
packaged_board["package"] = {
|
||
"name": package_name,
|
||
"root": package_dir,
|
||
"images_dir": "images",
|
||
"images": manifest_images,
|
||
"audio_dir": "audio",
|
||
"audio": manifest_audio,
|
||
}
|
||
|
||
manifest = {
|
||
"metadata": {
|
||
"schema": "iamccs.cine.shotboard.package",
|
||
"schema_version": 2,
|
||
"saved_at": saved_at,
|
||
"package_name": package_name,
|
||
"package_root": package_dir,
|
||
},
|
||
"board_file": "board.json",
|
||
"images_dir": "images",
|
||
"image_count": len(ordered_paths),
|
||
"images": manifest_images,
|
||
"audio_dir": "audio",
|
||
"audio_count": len(manifest_audio),
|
||
"audio": manifest_audio,
|
||
}
|
||
|
||
with open(os.path.join(package_dir, "board.json"), "w", encoding="utf-8") as fh:
|
||
json.dump(packaged_board, fh, indent=2, ensure_ascii=False)
|
||
with open(os.path.join(package_dir, "manifest.json"), "w", encoding="utf-8") as fh:
|
||
json.dump(manifest, fh, indent=2, ensure_ascii=False)
|
||
|
||
failed = sum(1 for item in manifest_images if item.get("error"))
|
||
failed_audio = sum(1 for item in manifest_audio if item.get("error"))
|
||
return web.json_response({
|
||
"ok": True,
|
||
"package_name": package_name,
|
||
"package_dir": package_dir,
|
||
"board_file": os.path.join(package_dir, "board.json"),
|
||
"manifest_file": os.path.join(package_dir, "manifest.json"),
|
||
"image_count": len(ordered_paths),
|
||
"failed_images": failed,
|
||
"images": manifest_images,
|
||
"audio_count": len(manifest_audio),
|
||
"failed_audio": failed_audio,
|
||
"audio": manifest_audio,
|
||
})
|
||
except Exception as e:
|
||
return web.json_response({"error": str(e)}, status=500)
|
||
|
||
@routes.post("/api/iamccs/audio/choose_package_folder")
|
||
async def iamccs_audio_choose_package_folder(request):
|
||
try:
|
||
import os
|
||
import folder_paths
|
||
|
||
data = await request.json()
|
||
initial_dir = str(data.get("initial_dir") or "").strip()
|
||
if not initial_dir or not os.path.isdir(os.path.abspath(os.path.expanduser(initial_dir))):
|
||
initial_dir = os.path.join(folder_paths.get_input_directory(), "IAMCCS_audioboard_packages")
|
||
os.makedirs(initial_dir, exist_ok=True)
|
||
try:
|
||
import tkinter as tk
|
||
from tkinter import filedialog
|
||
except Exception as err:
|
||
return web.json_response({"ok": False, "error": f"Folder picker unavailable: {err}"}, status=500)
|
||
|
||
root = tk.Tk()
|
||
root.withdraw()
|
||
try:
|
||
root.attributes("-topmost", True)
|
||
except Exception:
|
||
pass
|
||
try:
|
||
folder = filedialog.askdirectory(
|
||
parent=root,
|
||
initialdir=initial_dir,
|
||
title="Choose AudioBoard package folder",
|
||
mustexist=False,
|
||
)
|
||
finally:
|
||
try:
|
||
root.destroy()
|
||
except Exception:
|
||
pass
|
||
if not folder:
|
||
return web.json_response({"ok": False, "cancelled": True})
|
||
folder = os.path.abspath(os.path.expanduser(folder))
|
||
os.makedirs(folder, exist_ok=True)
|
||
return web.json_response({"ok": True, "folder": folder})
|
||
except Exception as e:
|
||
return web.json_response({"ok": False, "error": str(e)}, status=500)
|
||
|
||
@routes.post("/api/iamccs/audio/save_audioboard_json")
|
||
async def iamccs_audio_save_audioboard_json(request):
|
||
try:
|
||
import copy
|
||
import json
|
||
import os
|
||
import re
|
||
import time
|
||
import folder_paths
|
||
|
||
def _sanitize(value, fallback="audioboard"):
|
||
clean = re.sub(r'[<>:"/\\|?*\x00-\x1F]+', "_", str(value or fallback).strip())
|
||
clean = re.sub(r"\s+", "_", clean).strip("._")
|
||
return (clean[:90] or fallback)
|
||
|
||
def _strip_embedded_audio_payloads(item):
|
||
if isinstance(item, dict):
|
||
if isinstance(item.get("packageAudioExcerpt"), dict):
|
||
meta = dict(item.get("packageAudioExcerpt") or {})
|
||
meta.pop("dataUrl", None)
|
||
item["packageAudioExcerpt"] = meta
|
||
if isinstance(item.get("masterExcerpt"), dict):
|
||
item["masterExcerpt"].pop("dataUrl", None)
|
||
for value in item.values():
|
||
_strip_embedded_audio_payloads(value)
|
||
elif isinstance(item, list):
|
||
for value in item:
|
||
_strip_embedded_audio_payloads(value)
|
||
|
||
data = await request.json()
|
||
board = data.get("board")
|
||
if not isinstance(board, dict):
|
||
return web.json_response({"error": "Missing AudioBoard object"}, status=400)
|
||
|
||
target_root = str(data.get("target_root") or data.get("package_root") or "").strip()
|
||
if target_root:
|
||
target_root = os.path.abspath(os.path.expanduser(target_root))
|
||
else:
|
||
target_root = os.path.join(folder_paths.get_input_directory(), "IAMCCS_audioboards")
|
||
os.makedirs(target_root, exist_ok=True)
|
||
|
||
board_name = _sanitize(data.get("board_name") or data.get("package_name") or data.get("label") or f"audioboard_{int(time.time())}")
|
||
filename = f"{board_name}.json"
|
||
out_path = os.path.join(target_root, filename)
|
||
|
||
saved_at = time.strftime("%Y-%m-%dT%H:%M:%S%z")
|
||
saved_board = copy.deepcopy(board)
|
||
_strip_embedded_audio_payloads(saved_board)
|
||
saved_board["audioBoardPackageName"] = board_name
|
||
saved_board["projectName"] = board_name
|
||
saved_board["metadata"] = {
|
||
**(saved_board.get("metadata") or {}),
|
||
"saved_at": saved_at,
|
||
"schema": "iamccs.audio_board.metadata",
|
||
"schema_version": 1,
|
||
"save_mode": "metadata_only",
|
||
"audio_policy": "references_existing_audio",
|
||
}
|
||
saved_board["package"] = {
|
||
**(saved_board.get("package") or {}),
|
||
"kind": "audio_board_metadata",
|
||
"name": board_name,
|
||
"audio_policy": "references_existing_audio",
|
||
"includes_audio_files": False,
|
||
"saved_at": saved_at,
|
||
}
|
||
|
||
with open(out_path, "w", encoding="utf-8") as fh:
|
||
json.dump(saved_board, fh, indent=2, ensure_ascii=False)
|
||
|
||
return web.json_response({
|
||
"ok": True,
|
||
"board_name": board_name,
|
||
"file": out_path,
|
||
"folder": target_root,
|
||
})
|
||
except Exception as e:
|
||
return web.json_response({"error": str(e)}, status=500)
|
||
|
||
@routes.post("/api/iamccs/audio/save_audioboard_package")
|
||
async def iamccs_audio_save_audioboard_package(request):
|
||
try:
|
||
import base64
|
||
import copy
|
||
import json
|
||
import re
|
||
import shutil
|
||
import time
|
||
import folder_paths
|
||
|
||
def _sanitize(value, fallback="audioboard_package"):
|
||
clean = re.sub(r'[<>:"/\\|?*\x00-\x1F]+', "_", str(value or fallback).strip())
|
||
clean = re.sub(r"\s+", "_", clean).strip("._")
|
||
return (clean[:90] or fallback)
|
||
|
||
def _resolve_audio_source(path, input_dir):
|
||
clean = str(path or "").strip()
|
||
if not clean or clean.startswith("data:"):
|
||
return None
|
||
if os.path.isabs(clean):
|
||
return os.path.abspath(os.path.expanduser(clean))
|
||
return os.path.abspath(os.path.join(input_dir, clean.replace("/", os.sep)))
|
||
|
||
def _rewrite_audio_paths(board, path_map, segment_path_map=None):
|
||
segment_path_map = segment_path_map or {}
|
||
def _rewrite_segment(seg):
|
||
if not isinstance(seg, dict):
|
||
return
|
||
excerpt_meta = seg.get("packageAudioExcerpt") if isinstance(seg.get("packageAudioExcerpt"), dict) else {}
|
||
if excerpt_meta:
|
||
excerpt_meta.pop("dataUrl", None)
|
||
seg["packageAudioExcerpt"] = excerpt_meta
|
||
seg_id = str(seg.get("id") or "")
|
||
if seg_id and seg_id in segment_path_map:
|
||
rel_value = segment_path_map[seg_id]
|
||
for key in ("audioFile", "path"):
|
||
value = str(seg.get(key) or "").strip()
|
||
if value:
|
||
seg[f"original_{key}"] = value
|
||
seg[key] = rel_value
|
||
seg["fileName"] = os.path.basename(rel_value)
|
||
if excerpt_meta:
|
||
excerpt_meta["package_path"] = rel_value
|
||
seg["packageAudioExcerpt"] = excerpt_meta
|
||
return
|
||
for key in ("audioFile", "fileName", "path"):
|
||
value = str(seg.get(key) or "").strip()
|
||
if value in path_map:
|
||
seg[f"original_{key}"] = value
|
||
seg[key] = path_map[value]
|
||
for key in ("sourceAudioFile", "source_audio_file"):
|
||
value = str(seg.get(key) or "").strip()
|
||
if value in path_map:
|
||
seg[f"original_{key}"] = value
|
||
seg[key] = path_map[value]
|
||
|
||
for seg in board.get("audioSegments") or []:
|
||
_rewrite_segment(seg)
|
||
multi = board.get("multiGeneration") if isinstance(board.get("multiGeneration"), dict) else {}
|
||
for key in ("audioSegmentsAll", "allAudioSegments", "sourceAudioSegmentsAll"):
|
||
for seg in multi.get(key) or []:
|
||
_rewrite_segment(seg)
|
||
for seg in (multi.get("audioByTimeline") or {}).values():
|
||
for item in seg if isinstance(seg, list) else []:
|
||
_rewrite_segment(item)
|
||
for key in ("sourceSegment", "masterAudioAsset", "master_audio_asset", "masterExcerpt"):
|
||
_rewrite_segment(multi.get(key))
|
||
for key in ("masterAudioAsset", "master_audio_asset", "masterExcerpt"):
|
||
_rewrite_segment(board.get(key))
|
||
|
||
def _write_audio_data_url(value, out_path):
|
||
header, _, payload = str(value or "").partition(",")
|
||
if not header.startswith("data:audio/") or not payload:
|
||
return False
|
||
with open(out_path, "wb") as fh:
|
||
fh.write(base64.b64decode(payload))
|
||
return True
|
||
|
||
def _master_excerpt_from_board(board):
|
||
if not isinstance(board, dict):
|
||
return {}
|
||
direct = board.get("masterExcerpt")
|
||
if isinstance(direct, dict):
|
||
return direct
|
||
multi = board.get("multiGeneration") if isinstance(board.get("multiGeneration"), dict) else {}
|
||
item = multi.get("masterExcerpt") if isinstance(multi.get("masterExcerpt"), dict) else {}
|
||
return item if isinstance(item, dict) else {}
|
||
|
||
def _store_master_excerpt(board, item):
|
||
if not isinstance(board, dict) or not isinstance(item, dict):
|
||
return
|
||
board["masterExcerpt"] = item
|
||
multi = board.get("multiGeneration") if isinstance(board.get("multiGeneration"), dict) else {}
|
||
multi["masterExcerpt"] = item
|
||
board["multiGeneration"] = multi
|
||
|
||
data = await request.json()
|
||
board = data.get("board")
|
||
if not isinstance(board, dict):
|
||
return web.json_response({"error": "Missing AudioBoard object"}, status=400)
|
||
|
||
input_dir = folder_paths.get_input_directory()
|
||
requested_root = str(data.get("target_root") or data.get("package_root") or "").strip()
|
||
package_root = os.path.abspath(os.path.expanduser(requested_root)) if requested_root else os.path.join(input_dir, "IAMCCS_audioboard_packages")
|
||
package_name = _sanitize(data.get("package_name") or data.get("label") or f"audioboard_{int(time.time())}")
|
||
package_dir = os.path.join(package_root, package_name)
|
||
audio_dir = os.path.join(package_dir, "audio")
|
||
os.makedirs(audio_dir, exist_ok=True)
|
||
input_abs = os.path.abspath(input_dir).lower()
|
||
package_root_abs = os.path.abspath(package_root).lower()
|
||
package_root_is_input = package_root_abs == input_abs or package_root_abs.startswith(input_abs + os.sep)
|
||
|
||
def _package_audio_ref(filename, target_path):
|
||
if package_root_is_input:
|
||
rel_to_input = os.path.relpath(target_path, input_dir).replace(os.sep, "/")
|
||
return rel_to_input
|
||
return os.path.abspath(target_path)
|
||
|
||
original_board = copy.deepcopy(board)
|
||
segments = original_board.get("audioSegments") if isinstance(original_board.get("audioSegments"), list) else []
|
||
manifest_audio = []
|
||
path_map = {}
|
||
segment_path_map = {}
|
||
master_excerpt_entry = {}
|
||
allowed_ext = {".wav", ".mp3", ".flac", ".ogg", ".m4a", ".aac", ".aiff", ".aif", ".opus"}
|
||
|
||
for index, seg in enumerate(segments, start=1):
|
||
if not isinstance(seg, dict):
|
||
continue
|
||
original_path = str(seg.get("audioFile") or seg.get("fileName") or seg.get("path") or "").strip()
|
||
entry = {
|
||
"index": index,
|
||
"id": str(seg.get("id") or f"audio_{index:03d}"),
|
||
"track": int(float(seg.get("track", 0) or 0)),
|
||
"start": int(float(seg.get("start", 0) or 0)),
|
||
"length": int(float(seg.get("length", 0) or 0)),
|
||
"original_path": original_path,
|
||
}
|
||
source_path = _resolve_audio_source(original_path, input_dir)
|
||
source_name = os.path.basename(source_path or original_path) or f"audio_{index:03d}.wav"
|
||
ext = os.path.splitext(source_name)[1].lower()
|
||
if ext not in allowed_ext:
|
||
ext = ".wav"
|
||
stem = _sanitize(os.path.splitext(source_name)[0], f"audio_{index:03d}")[:52]
|
||
filename = f"audio_{index:03d}_{stem}{ext}"
|
||
excerpt = seg.get("packageAudioExcerpt") if isinstance(seg.get("packageAudioExcerpt"), dict) else {}
|
||
if excerpt:
|
||
filename = _sanitize(excerpt.get("fileName") or f"audio_{index:03d}_{stem}_excerpt.wav", f"audio_{index:03d}_excerpt.wav")
|
||
if not filename.lower().endswith(".wav"):
|
||
filename = f"{os.path.splitext(filename)[0]}.wav"
|
||
target_path = os.path.join(audio_dir, filename)
|
||
rel_path = _package_audio_ref(filename, target_path)
|
||
try:
|
||
if excerpt:
|
||
if _write_audio_data_url(excerpt.get("dataUrl"), target_path):
|
||
entry.update({
|
||
"source_path": source_path or "",
|
||
"bytes": os.path.getsize(target_path),
|
||
"package_path": rel_path,
|
||
"package_mode": "excerpt",
|
||
"trim_start_frame": int(float(excerpt.get("trimStartFrame", seg.get("trimStart", 0)) or 0)),
|
||
"length_frames": int(float(excerpt.get("lengthFrames", seg.get("length", 0)) or 0)),
|
||
"fps": float(excerpt.get("fps", 24) or 24),
|
||
"duration_seconds": float(excerpt.get("durationSeconds", 0) or 0),
|
||
"mime": "audio/wav",
|
||
})
|
||
if original_path:
|
||
path_map[original_path] = rel_path
|
||
segment_path_map[str(seg.get("id") or f"audio_{index:03d}")] = rel_path
|
||
else:
|
||
entry["error"] = excerpt.get("error") or "Missing segment excerpt dataUrl; refused to copy full source audio"
|
||
elif source_path and os.path.isfile(source_path):
|
||
shutil.copy2(source_path, target_path)
|
||
entry["source_path"] = source_path
|
||
entry["bytes"] = os.path.getsize(target_path)
|
||
entry["package_path"] = rel_path
|
||
entry["package_mode"] = "source_copy"
|
||
if original_path:
|
||
path_map[original_path] = rel_path
|
||
else:
|
||
entry["error"] = excerpt.get("error") or f"Audio not found: {original_path}"
|
||
except Exception as err:
|
||
entry["error"] = str(err)
|
||
manifest_audio.append(entry)
|
||
|
||
master_excerpt = _master_excerpt_from_board(original_board)
|
||
if master_excerpt:
|
||
master_excerpt_entry = {
|
||
"role": "master_excerpt",
|
||
"source_segment_id": str(master_excerpt.get("sourceSegmentId") or ""),
|
||
"source_audio_file": str(master_excerpt.get("sourceAudioFile") or ""),
|
||
"trim_start_frame": int(float(master_excerpt.get("trimStartFrame", 0) or 0)),
|
||
"length_frames": int(float(master_excerpt.get("lengthFrames", 0) or 0)),
|
||
"fps": float(master_excerpt.get("fps", 24) or 24),
|
||
"duration_seconds": float(master_excerpt.get("durationSeconds", 0) or 0),
|
||
}
|
||
filename = _sanitize(master_excerpt.get("fileName") or "master_excerpt.wav", "master_excerpt.wav")
|
||
if not filename.lower().endswith(".wav"):
|
||
filename = f"{os.path.splitext(filename)[0]}.wav"
|
||
target_path = os.path.join(audio_dir, filename)
|
||
rel_path = _package_audio_ref(filename, target_path)
|
||
try:
|
||
if _write_audio_data_url(master_excerpt.get("dataUrl"), target_path):
|
||
master_excerpt_entry.update({
|
||
"filename": filename,
|
||
"package_path": rel_path,
|
||
"bytes": os.path.getsize(target_path),
|
||
"mime": "audio/wav",
|
||
})
|
||
else:
|
||
master_excerpt_entry["error"] = master_excerpt.get("error") or "Missing master excerpt dataUrl"
|
||
except Exception as err:
|
||
master_excerpt_entry["error"] = str(err)
|
||
|
||
packaged_board = copy.deepcopy(original_board)
|
||
_rewrite_audio_paths(packaged_board, path_map, segment_path_map)
|
||
if master_excerpt:
|
||
packaged_excerpt = copy.deepcopy(master_excerpt)
|
||
packaged_excerpt.pop("dataUrl", None)
|
||
if master_excerpt_entry.get("package_path"):
|
||
packaged_excerpt["audioFile"] = master_excerpt_entry["package_path"]
|
||
packaged_excerpt["path"] = master_excerpt_entry["package_path"]
|
||
packaged_excerpt["packagePath"] = master_excerpt_entry["package_path"]
|
||
if master_excerpt_entry.get("error"):
|
||
packaged_excerpt["error"] = master_excerpt_entry["error"]
|
||
_store_master_excerpt(packaged_board, packaged_excerpt)
|
||
saved_at = time.strftime("%Y-%m-%dT%H:%M:%S%z")
|
||
packaged_board["metadata"] = {
|
||
**(packaged_board.get("metadata") or {}),
|
||
"packaged_at": saved_at,
|
||
"package_schema": "iamccs.audio.audioboard.package",
|
||
}
|
||
packaged_board["package"] = {
|
||
"name": package_name,
|
||
"root": package_dir,
|
||
"audio_dir": "audio",
|
||
"audio": manifest_audio,
|
||
"master_excerpt": master_excerpt_entry,
|
||
}
|
||
|
||
manifest = {
|
||
"metadata": {
|
||
"schema": "iamccs.audio.audioboard.package",
|
||
"schema_version": 1,
|
||
"saved_at": saved_at,
|
||
"package_name": package_name,
|
||
"package_root": package_dir,
|
||
},
|
||
"board_file": "audioboard.json",
|
||
"audio_dir": "audio",
|
||
"audio_count": len(manifest_audio),
|
||
"failed_audio": sum(1 for item in manifest_audio if item.get("error")),
|
||
"audio": manifest_audio,
|
||
"master_excerpt": master_excerpt_entry,
|
||
"trackSettings": packaged_board.get("trackSettings") or [],
|
||
"masterBus": packaged_board.get("masterBus") or {},
|
||
}
|
||
|
||
with open(os.path.join(package_dir, "audioboard.json"), "w", encoding="utf-8") as fh:
|
||
json.dump(packaged_board, fh, indent=2, ensure_ascii=False)
|
||
with open(os.path.join(package_dir, "manifest.json"), "w", encoding="utf-8") as fh:
|
||
json.dump(manifest, fh, indent=2, ensure_ascii=False)
|
||
|
||
return web.json_response({
|
||
"ok": True,
|
||
"package_name": package_name,
|
||
"package_dir": package_dir,
|
||
"board_file": os.path.join(package_dir, "audioboard.json"),
|
||
"manifest_file": os.path.join(package_dir, "manifest.json"),
|
||
"audio_count": len(manifest_audio),
|
||
"failed_audio": manifest["failed_audio"],
|
||
"audio": manifest_audio,
|
||
})
|
||
except Exception as e:
|
||
return web.json_response({"error": str(e)}, status=500)
|
||
|
||
@routes.post("/api/iamccs/audio/restore_package_assets")
|
||
async def iamccs_audio_restore_package_assets(request):
|
||
try:
|
||
import copy
|
||
import os
|
||
import re
|
||
import shutil
|
||
import time
|
||
import folder_paths
|
||
|
||
data = await request.json()
|
||
board = data.get("board") if isinstance(data.get("board"), dict) else {}
|
||
if not board:
|
||
return web.json_response({"error": "Missing AudioBoard object"}, status=400)
|
||
|
||
def _sanitize(value, fallback="audioboard_import"):
|
||
clean = re.sub(r'[<>:"/\\|?*\x00-\x1F]+', "_", str(value or fallback).strip())
|
||
clean = re.sub(r"\s+", "_", clean).strip("._")
|
||
return clean[:80] or fallback
|
||
|
||
def _resolve(path, input_dir):
|
||
clean = str(path or "").strip()
|
||
if not clean or clean.startswith("data:"):
|
||
return ""
|
||
if os.path.isabs(clean):
|
||
return os.path.abspath(os.path.expanduser(clean))
|
||
return os.path.abspath(os.path.join(input_dir, clean.replace("/", os.sep)))
|
||
|
||
candidates = []
|
||
seen = set()
|
||
|
||
def _add(item):
|
||
if not isinstance(item, dict):
|
||
return
|
||
path = str(item.get("audioFile") or item.get("path") or "").strip()
|
||
if not path or path in seen:
|
||
return
|
||
seen.add(path)
|
||
candidates.append(path)
|
||
|
||
for item in board.get("audioSegments") or []:
|
||
_add(item)
|
||
multi = board.get("multiGeneration") if isinstance(board.get("multiGeneration"), dict) else {}
|
||
for key in ("audioSegmentsAll", "allAudioSegments", "sourceAudioSegmentsAll"):
|
||
for item in multi.get(key) or []:
|
||
_add(item)
|
||
for items in (multi.get("audioByTimeline") or {}).values():
|
||
for item in items if isinstance(items, list) else []:
|
||
_add(item)
|
||
for key in ("masterAudioAsset", "master_audio_asset", "masterExcerpt"):
|
||
_add(board.get(key))
|
||
_add(multi.get(key))
|
||
|
||
input_dir = folder_paths.get_input_directory()
|
||
package_name = _sanitize(data.get("package_name") or board.get("audioBoardPackageName") or board.get("projectName") or f"audioboard_{int(time.time())}")
|
||
target_dir = os.path.join(input_dir, "IAMCCS_imported_audioboard", package_name)
|
||
os.makedirs(target_dir, exist_ok=True)
|
||
mapping = {}
|
||
restored = []
|
||
allowed_ext = {".wav", ".mp3", ".flac", ".ogg", ".m4a", ".aac", ".aiff", ".aif", ".opus"}
|
||
for index, original in enumerate(candidates, start=1):
|
||
source = _resolve(original, input_dir)
|
||
if not source or not os.path.isfile(source):
|
||
continue
|
||
name = os.path.basename(source)
|
||
stem, ext = os.path.splitext(name)
|
||
if ext.lower() not in allowed_ext:
|
||
ext = ".wav"
|
||
filename = f"{index:03d}_{_sanitize(stem, 'audio')[:52]}{ext}"
|
||
target = os.path.join(target_dir, filename)
|
||
shutil.copy2(source, target)
|
||
rel = os.path.relpath(target, input_dir).replace(os.sep, "/")
|
||
mapping[original] = rel
|
||
restored.append({"original_path": original, "input_path": rel, "bytes": os.path.getsize(target)})
|
||
|
||
return web.json_response({"ok": True, "mapping": mapping, "restored": restored})
|
||
except Exception as e:
|
||
return web.json_response({"error": str(e)}, status=500)
|
||
|
||
@routes.get("/api/iamccs/hw_probe")
|
||
async def iamccs_hw_probe_endpoint(request):
|
||
try:
|
||
q = request.rel_url.query
|
||
def _to_int(x):
|
||
try:
|
||
return int(float(x))
|
||
except Exception:
|
||
return None
|
||
def _to_float(x):
|
||
try:
|
||
return float(x)
|
||
except Exception:
|
||
return None
|
||
|
||
width = _to_int(q.get("width"))
|
||
height = _to_int(q.get("height"))
|
||
frames = _to_int(q.get("frames"))
|
||
fps = _to_float(q.get("fps"))
|
||
|
||
data = recommend_settings(width=width, height=height, frames=frames, fps=fps)
|
||
logging.getLogger("IAMCCS.API").info(
|
||
"[iamccs/hw_probe] cuda=%s vram_gb=%s ram_gb=%s profile=%s vae_tile=%s frames=%s fps=%s",
|
||
data.get("hardware", {}).get("cuda_available"),
|
||
data.get("hardware", {}).get("cuda_total_vram_gb"),
|
||
data.get("hardware", {}).get("system_ram_gb"),
|
||
data.get("recommendations", {}).get("hw_supporter", {}).get("profile"),
|
||
data.get("recommendations", {}).get("vae_decode", {}).get("tile_size"),
|
||
frames,
|
||
fps,
|
||
)
|
||
return web.json_response(data)
|
||
except Exception as e:
|
||
return web.json_response({"error": str(e)}, status=500)
|
||
|
||
@routes.post("/api/iamccs/h3/advice")
|
||
async def iamccs_h3_advice_endpoint(request):
|
||
import asyncio
|
||
from .iamccs_h3_advisor import advice
|
||
try:
|
||
payload = await request.json()
|
||
if not isinstance(payload, dict):
|
||
raise ValueError("Expected a settings object")
|
||
return web.json_response(await asyncio.to_thread(advice, payload))
|
||
except (ValueError, TypeError) as exc:
|
||
return web.json_response({"error": str(exc)}, status=400)
|
||
|
||
except Exception as e:
|
||
# Never hard-fail ComfyUI startup due to optional API endpoints.
|
||
logging.getLogger("IAMCCS.API").warning("Could not setup IAMCCS API routes: %r", e)
|
||
|
||
|
||
|
||
# IAMCCS Shotboard V4 / Motion Sketch optional module
|
||
try:
|
||
from .cine_shotboard_v4 import (
|
||
NODE_CLASS_MAPPINGS as _IAMCCS_SHOTBOARD_V4_NODE_CLASS_MAPPINGS,
|
||
NODE_DISPLAY_NAME_MAPPINGS as _IAMCCS_SHOTBOARD_V4_NODE_DISPLAY_NAME_MAPPINGS,
|
||
)
|
||
NODE_CLASS_MAPPINGS.update(_IAMCCS_SHOTBOARD_V4_NODE_CLASS_MAPPINGS)
|
||
NODE_DISPLAY_NAME_MAPPINGS.update(_IAMCCS_SHOTBOARD_V4_NODE_DISPLAY_NAME_MAPPINGS)
|
||
except Exception as e:
|
||
logging.getLogger("IAMCCS").warning("IAMCCS Shotboard V4 MotionSketch nodes unavailable: %r", e)
|
||
|
||
# Shotboard V4 retake R2 is deliberately isolated from the legacy V4 backend.
|
||
# If its optional module is absent, every existing V3/V4 node keeps loading.
|
||
try:
|
||
from .iamccs_cine_shotboard_v4_retake_r2 import (
|
||
NODE_CLASS_MAPPINGS as _IAMCCS_SHOTBOARD_V4_RETAKE_R2_NODE_CLASS_MAPPINGS,
|
||
NODE_DISPLAY_NAME_MAPPINGS as _IAMCCS_SHOTBOARD_V4_RETAKE_R2_NODE_DISPLAY_NAME_MAPPINGS,
|
||
)
|
||
NODE_CLASS_MAPPINGS.update(_IAMCCS_SHOTBOARD_V4_RETAKE_R2_NODE_CLASS_MAPPINGS)
|
||
NODE_DISPLAY_NAME_MAPPINGS.update(_IAMCCS_SHOTBOARD_V4_RETAKE_R2_NODE_DISPLAY_NAME_MAPPINGS)
|
||
except Exception as e:
|
||
logging.getLogger("IAMCCS").warning("IAMCCS Shotboard V4 Retake R2 nodes unavailable: %r", e)
|
||
|
||
# LTX-2.5 Shotboard V3B is isolated from the production V3/V4 implementations.
|
||
# A missing optional dependency therefore never prevents existing IAMCCS nodes
|
||
# from loading; runtime dependency checks happen only when its backend executes.
|
||
try:
|
||
from .iamccs_ltx25_shotboard_v3b import (
|
||
NODE_CLASS_MAPPINGS as _IAMCCS_LTX25_V3B_NODE_CLASS_MAPPINGS,
|
||
NODE_DISPLAY_NAME_MAPPINGS as _IAMCCS_LTX25_V3B_NODE_DISPLAY_NAME_MAPPINGS,
|
||
)
|
||
NODE_CLASS_MAPPINGS.update(_IAMCCS_LTX25_V3B_NODE_CLASS_MAPPINGS)
|
||
NODE_DISPLAY_NAME_MAPPINGS.update(_IAMCCS_LTX25_V3B_NODE_DISPLAY_NAME_MAPPINGS)
|
||
except Exception as e:
|
||
logging.getLogger("IAMCCS").warning("IAMCCS LTX-2.5 Shotboard V3B nodes unavailable: %r", e)
|
||
|
||
# NextFrameBuilder is isolated so an optional Qwen/GGUF dependency cannot stop
|
||
# the rest of IAMCCS-nodes from registering at ComfyUI startup.
|
||
try:
|
||
from .iamccs_next_frame_builder import (
|
||
NODE_CLASS_MAPPINGS as _IAMCCS_NEXT_FRAME_BUILDER_NODE_CLASS_MAPPINGS,
|
||
NODE_DISPLAY_NAME_MAPPINGS as _IAMCCS_NEXT_FRAME_BUILDER_NODE_DISPLAY_NAME_MAPPINGS,
|
||
)
|
||
NODE_CLASS_MAPPINGS.update(_IAMCCS_NEXT_FRAME_BUILDER_NODE_CLASS_MAPPINGS)
|
||
NODE_DISPLAY_NAME_MAPPINGS.update(_IAMCCS_NEXT_FRAME_BUILDER_NODE_DISPLAY_NAME_MAPPINGS)
|
||
except Exception as e:
|
||
logging.getLogger("IAMCCS").warning("IAMCCS NextFrameBuilder nodes unavailable: %r", e)
|
||
|
||
# Setup API routes when extension loads
|
||
setup_api_routes()
|
||
|
||
# Print banner after we are fully imported and mappings exist.
|
||
_print_startup_banner()
|
||
|
||
|
||
def _iamccs_install_ltx2_vae_encode_autofix() -> None:
|
||
"""Prevents hard-crash when LTX-2 VAE receives invalid frame counts.
|
||
|
||
Lightricks video VAE encode requires a frame count of the form 1 + 8*x.
|
||
Some workflows can produce off-by-a-few batches (e.g. 240 instead of 241),
|
||
which otherwise raises ValueError and stops execution.
|
||
|
||
This patch pads by repeating the last frame up to the next valid count.
|
||
Opt-in via IAMCCS_LTX2_VAE_ENCODE_AUTOFIX=1.
|
||
"""
|
||
|
||
# Default OFF: user requested workflow-level fixes without monkeypatching VAE.
|
||
if str(os.getenv("IAMCCS_LTX2_VAE_ENCODE_AUTOFIX", "0")).strip().lower() in {"0", "false", "no", "off"}:
|
||
return
|
||
|
||
log = logging.getLogger("IAMCCS.LTX2.VAE")
|
||
|
||
try:
|
||
import torch
|
||
except Exception:
|
||
return
|
||
|
||
try:
|
||
from comfy.ldm.lightricks.vae import causal_video_autoencoder as _cvae
|
||
except Exception:
|
||
# ComfyUI / LTXVideo not installed or import path changed.
|
||
return
|
||
|
||
cls = getattr(_cvae, "CausalVideoAutoencoder", None)
|
||
if cls is None:
|
||
return
|
||
|
||
orig_encode = getattr(cls, "encode", None)
|
||
if orig_encode is None:
|
||
return
|
||
|
||
if getattr(orig_encode, "__iamccs_ltx2_autofix__", False):
|
||
return
|
||
|
||
def _round_up_8n1(frames: int) -> int:
|
||
frames = int(frames)
|
||
if frames <= 1:
|
||
return 1
|
||
rem = (frames - 1) % 8
|
||
if rem == 0:
|
||
return frames
|
||
return frames + (8 - rem)
|
||
|
||
def _is_valid_8n1(frames: int) -> bool:
|
||
frames = int(frames)
|
||
return frames >= 1 and (frames - 1) % 8 == 0
|
||
|
||
def _pad_repeat_last(x: "torch.Tensor", dim: int, pad: int) -> "torch.Tensor":
|
||
# Take last slice along `dim` (keeps dimension) and repeat it `pad` times.
|
||
slc = [slice(None)] * x.ndim
|
||
slc[dim] = slice(-1, None)
|
||
last = x[tuple(slc)]
|
||
reps = [1] * x.ndim
|
||
reps[dim] = int(pad)
|
||
last_rep = last.repeat(*reps)
|
||
return torch.cat([x, last_rep], dim=dim)
|
||
|
||
def _candidate_frame_dims(x: "torch.Tensor") -> list[int]:
|
||
# Most common layouts:
|
||
# - (B, C, T, H, W) -> frames dim = 2
|
||
# - (T, H, W, C) -> frames dim = 0 (ComfyUI IMAGE batches)
|
||
# We only try dims that are >1 and *not obviously channels*.
|
||
dims: list[int] = []
|
||
if x.ndim == 5:
|
||
# Prefer T, then fallbacks
|
||
dims = [2, 0, 1]
|
||
elif x.ndim == 4:
|
||
dims = [0]
|
||
else:
|
||
dims = [0]
|
||
|
||
out: list[int] = []
|
||
for d in dims:
|
||
try:
|
||
size = int(x.shape[d])
|
||
except Exception:
|
||
continue
|
||
if size <= 1:
|
||
continue
|
||
# Heuristic: channels are usually small (1..4). Don't treat that as frames.
|
||
if size in (1, 2, 3, 4) and x.ndim >= 4 and d in (1, 3):
|
||
continue
|
||
out.append(d)
|
||
# Ensure uniqueness, preserve order
|
||
seen = set()
|
||
unique: list[int] = []
|
||
for d in out:
|
||
if d in seen:
|
||
continue
|
||
seen.add(d)
|
||
unique.append(d)
|
||
return unique
|
||
|
||
def encode_patched(self, pixels_in: "torch.Tensor"):
|
||
try:
|
||
return orig_encode(self, pixels_in)
|
||
except ValueError as e:
|
||
msg = str(e)
|
||
if "Invalid number of frames" not in msg:
|
||
raise
|
||
|
||
if not isinstance(pixels_in, torch.Tensor) or pixels_in.ndim < 4:
|
||
raise
|
||
|
||
# Try padding along the most likely frame dimension(s).
|
||
last_err: Exception | None = e
|
||
for dim in _candidate_frame_dims(pixels_in):
|
||
frames_in = int(pixels_in.shape[dim])
|
||
if _is_valid_8n1(frames_in):
|
||
continue
|
||
|
||
frames_fixed = _round_up_8n1(frames_in)
|
||
pad = frames_fixed - frames_in
|
||
if pad <= 0:
|
||
continue
|
||
|
||
try:
|
||
pixels_fixed = _pad_repeat_last(pixels_in, dim=dim, pad=pad)
|
||
log.warning(
|
||
"[LTX2 VAE encode autofix] Padded frames dim=%d %d -> %d (pad=%d) to satisfy 1+8*x rule",
|
||
dim,
|
||
frames_in,
|
||
frames_fixed,
|
||
pad,
|
||
)
|
||
return orig_encode(self, pixels_fixed)
|
||
except Exception as ee:
|
||
last_err = ee
|
||
continue
|
||
|
||
# If all attempts failed, re-raise the original ValueError.
|
||
raise e
|
||
|
||
encode_patched.__iamccs_ltx2_autofix__ = True
|
||
setattr(cls, "encode", encode_patched)
|
||
|
||
|
||
_iamccs_install_ltx2_vae_encode_autofix()
|
||
|
||
# Krea 2 Studio is intentionally registered from IAMCCS-nodes rather than
|
||
# embedded in an application edition. The frontend consumes only this stable
|
||
# node contract, so the prototype can later be propagated without duplicating
|
||
# the regional-conditioning implementation.
|
||
try:
|
||
from .iamccs_krea2_studio import (
|
||
NODE_CLASS_MAPPINGS as _KREA2_STUDIO_CLASSES,
|
||
NODE_DISPLAY_NAME_MAPPINGS as _KREA2_STUDIO_NAMES,
|
||
)
|
||
NODE_CLASS_MAPPINGS.update(_KREA2_STUDIO_CLASSES)
|
||
NODE_DISPLAY_NAME_MAPPINGS.update(_KREA2_STUDIO_NAMES)
|
||
except Exception as _krea2_studio_error:
|
||
log.exception("[IAMCCS] Krea 2 Studio registration failed: %s", _krea2_studio_error)
|
||
|
||
try:
|
||
from .iamccs_ahead_seam_editor import register_routes as _register_ahead_seams
|
||
from .iamccs_ahead_control_node import NODE_CLASS_MAPPINGS as _ahead_classes, NODE_DISPLAY_NAME_MAPPINGS as _ahead_names
|
||
NODE_CLASS_MAPPINGS.update(_ahead_classes)
|
||
NODE_DISPLAY_NAME_MAPPINGS.update(_ahead_names)
|
||
_register_ahead_seams()
|
||
except Exception as _ahead_seam_error:
|
||
log.exception("[IAMCCS] Ahead seam editor registration failed: %s", _ahead_seam_error)
|