# System imports - needed throughout the module import sys import os # Smart numba compatibility handling try: from utils.compatibility import setup_numba_compatibility setup_numba_compatibility(quick_startup=True, verbose=False) except ImportError: # __init__.py already handled compatibility testing pass # Version and constants VERSION = "5.8.1" IS_DEV = False # Set to False for release builds VERSION_DISPLAY = f"v{VERSION}" + (" (dev)" if IS_DEV else "") SEPARATOR = "=" * 70 """ TTS Audio Suite - Universal multi-engine TTS extension for ComfyUI Unified architecture supporting ChatterBox, F5-TTS, and future engines like RVC Features modular engine adapters, character voice management, and comprehensive audio processing """ import warnings warnings.filterwarnings('ignore', message='.*PerthNet.*') warnings.filterwarnings('ignore', message='.*LoRACompatibleLinear.*') warnings.filterwarnings('ignore', message='.*requires authentication.*') import folder_paths import importlib.util # Add current directory to path for absolute imports current_dir = os.path.dirname(__file__) if current_dir not in sys.path: sys.path.insert(0, current_dir) # Transformers version check deferred to first engine use to avoid # importing transformers (~1.3s) at plugin load time. # The check runs lazily via _check_transformers_version() below. _transformers_version_checked = False def _check_transformers_version(): """Check transformers version compatibility on first engine use. Deferred from module level to avoid the ~1.3s import cost at startup. Called automatically by nodes that need transformers. """ global _transformers_version_checked if _transformers_version_checked: return _transformers_version_checked = True try: import transformers from packaging import version required_version = "4.51.3" current_version = transformers.__version__ if version.parse(current_version) < version.parse(required_version): print(f"๐Ÿšจ COMPATIBILITY WARNING:") print(f" Transformers version {current_version} is too old (requires >={required_version})") print(f" This WILL cause errors like 'DynamicCache property has no setter'") print(f" ๐Ÿ“‹ SOLUTION: Run this command to upgrade:") print(f" pip install --upgrade transformers>={required_version}") print(f" (Or use your environment's package manager)") print() except Exception as e: print(f"โš ๏ธ Could not check transformers version: {e}") print(" If you encounter DynamicCache errors, upgrade transformers to >=4.51.3") # Import nodes using direct file loading to avoid package path issues def load_node_module(module_name, file_name): """Load a node module from the nodes directory""" module_path = os.path.join(current_dir, "nodes", file_name) spec = importlib.util.spec_from_file_location(module_name, module_path) module = importlib.util.module_from_spec(spec) # Add to sys.modules to allow internal imports within the module sys.modules[module_name] = module spec.loader.exec_module(module) return module # Load engine nodes try: chatterbox_engine_module = load_node_module("chatterbox_engine_node", "engines/chatterbox_engine_node.py") ChatterBoxEngineNode = chatterbox_engine_module.ChatterBoxEngineNode CHATTERBOX_ENGINE_AVAILABLE = True except Exception as e: print(f"โŒ ChatterBox Engine failed: {e}") CHATTERBOX_ENGINE_AVAILABLE = False try: f5tts_engine_module = load_node_module("f5tts_engine_node", "engines/f5tts_engine_node.py") F5TTSEngineNode = f5tts_engine_module.F5TTSEngineNode F5TTS_ENGINE_AVAILABLE = True except Exception as e: print(f"โŒ F5 TTS Engine failed: {e}") F5TTS_ENGINE_AVAILABLE = False try: higgs_audio_engine_module = load_node_module("higgs_audio_engine_node", "engines/higgs_audio_engine_node.py") HiggsAudioEngineNode = higgs_audio_engine_module.HiggsAudioEngineNode HIGGS_AUDIO_ENGINE_AVAILABLE = True except Exception as e: print(f"โŒ Higgs Audio Engine failed: {e}") HIGGS_AUDIO_ENGINE_AVAILABLE = False try: higgs_audio_v3_engine_module = load_node_module("higgs_audio_v3_engine_node", "engines/higgs_audio_v3_engine_node.py") HiggsAudioV3EngineNode = higgs_audio_v3_engine_module.HiggsAudioV3EngineNode HIGGS_AUDIO_V3_ENGINE_AVAILABLE = True except Exception as e: print(f"โŒ Higgs Audio v3 Engine failed: {e}") HIGGS_AUDIO_V3_ENGINE_AVAILABLE = False try: step_audio_editx_engine_module = load_node_module("step_audio_editx_engine_node", "engines/step_audio_editx_engine_node.py") StepAudioEditXEngineNode = step_audio_editx_engine_module.StepAudioEditXEngineNode STEP_AUDIO_EDITX_ENGINE_AVAILABLE = True except Exception as e: print(f"โŒ Step Audio EditX Engine failed: {e}") STEP_AUDIO_EDITX_ENGINE_AVAILABLE = False try: vibevoice_engine_module = load_node_module("vibevoice_engine_node", "engines/vibevoice_engine_node.py") VibeVoiceEngineNode = vibevoice_engine_module.VibeVoiceEngineNode VIBEVOICE_ENGINE_AVAILABLE = True except Exception as e: print(f"โŒ VibeVoice Engine failed: {e}") VIBEVOICE_ENGINE_AVAILABLE = False try: qwen3_tts_engine_module = load_node_module("qwen3_tts_engine_node", "engines/qwen3_tts_engine_node.py") Qwen3TTSEngineNode = qwen3_tts_engine_module.Qwen3TTSEngineNode QWEN3_TTS_ENGINE_AVAILABLE = True except Exception as e: print(f"โŒ Qwen3-TTS Engine failed: {e}") QWEN3_TTS_ENGINE_AVAILABLE = False try: moss_soundeffect_v2_engine_module = load_node_module( "moss_soundeffect_v2_engine_node", "engines/moss_soundeffect_v2_engine_node.py" ) MossSoundEffectV2EngineNode = moss_soundeffect_v2_engine_module.MossSoundEffectV2EngineNode MOSS_SOUNDEFFECT_V2_ENGINE_AVAILABLE = True except Exception as e: print(f"โŒ MOSS-SoundEffect v2 Engine failed: {e}") MOSS_SOUNDEFFECT_V2_ENGINE_AVAILABLE = False try: moss_tts_engine_module = load_node_module("moss_tts_engine_node", "engines/moss_tts_engine_node.py") MossTTSEngineNode = moss_tts_engine_module.MossTTSEngineNode MOSS_TTS_ENGINE_AVAILABLE = True except Exception as e: print(f"โŒ MOSS-TTS Engine failed: {e}") MOSS_TTS_ENGINE_AVAILABLE = False try: granite_asr_engine_module = load_node_module("granite_asr_engine_node", "engines/granite_asr_engine_node.py") GraniteASREngineNode = granite_asr_engine_module.GraniteASREngineNode GRANITE_ASR_ENGINE_AVAILABLE = True except Exception as e: print(f"โŒ Granite ASR Engine failed: {e}") GRANITE_ASR_ENGINE_AVAILABLE = False try: echo_tts_engine_module = load_node_module("echo_tts_engine_node", "engines/echo_tts_engine_node.py") EchoTTSEngineNode = echo_tts_engine_module.EchoTTSEngineNode ECHO_TTS_ENGINE_AVAILABLE = True except Exception as e: print(f"โŒ Echo-TTS Engine failed: {e}") ECHO_TTS_ENGINE_AVAILABLE = False try: dots_tts_engine_module = load_node_module("dots_tts_engine_node", "engines/dots_tts_engine_node.py") DotsTTSEngineNode = dots_tts_engine_module.DotsTTSEngineNode DOTS_TTS_ENGINE_AVAILABLE = True except Exception as e: print(f"โŒ Dots TTS Engine failed: {e}") DOTS_TTS_ENGINE_AVAILABLE = False try: dramabox_engine_module = load_node_module("dramabox_engine_node", "engines/dramabox_engine_node.py") DramaBoxEngineNode = dramabox_engine_module.DramaBoxEngineNode DRAMABOX_ENGINE_AVAILABLE = True except Exception as e: print(f"โŒ DramaBox Engine failed: {e}") DRAMABOX_ENGINE_AVAILABLE = False try: fish_audio_s2_engine_module = load_node_module("fish_audio_s2_engine_node", "engines/fish_audio_s2_engine_node.py") FishAudioS2EngineNode = fish_audio_s2_engine_module.FishAudioS2EngineNode FISH_AUDIO_S2_ENGINE_AVAILABLE = True except Exception as e: print(f"โŒ Fish Audio S2 Pro Engine failed: {e}") FISH_AUDIO_S2_ENGINE_AVAILABLE = False try: audio_cpp_engine_module = load_node_module("audio_cpp_engine_node", "engines/audio_cpp_engine_node.py") AudioCppEngineNode = audio_cpp_engine_module.AudioCppEngineNode AUDIO_CPP_ENGINE_AVAILABLE = True except Exception as e: print(f"โŒ audio.cpp Engine failed: {e}") AUDIO_CPP_ENGINE_AVAILABLE = False try: omnivoice_engine_module = load_node_module("omnivoice_engine_node", "engines/omnivoice_engine_node.py") OmniVoiceEngineNode = omnivoice_engine_module.OmniVoiceEngineNode OMNIVOICE_ENGINE_AVAILABLE = True except Exception as e: print(f"โŒ OmniVoice Engine failed: {e}") OMNIVOICE_ENGINE_AVAILABLE = False try: omnivoice_instruction_builder_module = load_node_module("omnivoice_instruction_builder_node", "omnivoice/omnivoice_instruction_builder_node.py") OmniVoiceInstructionBuilderNode = omnivoice_instruction_builder_module.OmniVoiceInstructionBuilderNode OMNIVOICE_INSTRUCTION_BUILDER_AVAILABLE = True except Exception as e: print(f"โŒ Visual Tag Builder failed: {e}") OMNIVOICE_INSTRUCTION_BUILDER_AVAILABLE = False try: chatterbox_official_23lang_engine_module = load_node_module("chatterbox_official_23lang_engine_node", "engines/chatterbox_official_23lang_engine_node.py") ChatterBoxOfficial23LangEngineNode = chatterbox_official_23lang_engine_module.ChatterBoxOfficial23LangEngineNode CHATTERBOX_OFFICIAL_23LANG_ENGINE_AVAILABLE = True except Exception as e: print(f"โŒ ChatterBox Official 23-Lang Engine failed: {e}") CHATTERBOX_OFFICIAL_23LANG_ENGINE_AVAILABLE = False try: index_tts_engine_module = load_node_module("index_tts_engine_node", "engines/index_tts_engine_node.py") IndexTTSEngineNode = index_tts_engine_module.IndexTTSEngineNode INDEX_TTS_ENGINE_AVAILABLE = True except Exception as e: print(f"โŒ IndexTTS Engine failed: {e}") INDEX_TTS_ENGINE_AVAILABLE = False try: cosyvoice_engine_module = load_node_module("cosyvoice_engine_node", "engines/cosyvoice_engine_node.py") CosyVoiceEngineNode = cosyvoice_engine_module.CosyVoiceEngineNode COSYVOICE_ENGINE_AVAILABLE = True except Exception as e: print(f"โŒ CosyVoice3 Engine failed: {e}") COSYVOICE_ENGINE_AVAILABLE = False # IndexTTS-2 Emotion Options Node try: index_tts_emotion_options_module = load_node_module("index_tts_emotion_options_node", "engines/index_tts_emotion_options_node.py") IndexTTSEmotionOptionsNode = index_tts_emotion_options_module.IndexTTSEmotionOptionsNode INDEX_TTS_EMOTION_OPTIONS_AVAILABLE = True except Exception as e: print(f"โŒ IndexTTS-2 Emotion Options failed: {e}") INDEX_TTS_EMOTION_OPTIONS_AVAILABLE = False # QwenEmotion Text Analysis Node try: qwen_emotion_module = load_node_module("qwen_emotion_node", "index_tts/qwen_emotion_node.py") QwenEmotionNode = qwen_emotion_module.QwenEmotionNode QWEN_EMOTION_AVAILABLE = True except Exception as e: print(f"โŒ QwenEmotion Text Analysis failed: {e}") QWEN_EMOTION_AVAILABLE = False # Load shared nodes try: character_voices_module = load_node_module("character_voices_node", "shared/character_voices_node.py") CharacterVoicesNode = character_voices_module.CharacterVoicesNode CHARACTER_VOICES_AVAILABLE = True except Exception as e: print(f"โŒ Character Voices failed: {e}") CHARACTER_VOICES_AVAILABLE = False try: refresh_voice_cache_module = load_node_module("refresh_voice_cache_node", "shared/refresh_voice_cache_node.py") RefreshVoiceCacheNode = refresh_voice_cache_module.RefreshVoiceCacheNode REFRESH_VOICE_CACHE_AVAILABLE = True except Exception as e: print(f"โŒ Refresh Voice Cache failed: {e}") REFRESH_VOICE_CACHE_AVAILABLE = False try: save_character_voice_module = load_node_module("save_character_voice_node", "shared/save_character_voice_node.py") SaveCharacterVoiceNode = save_character_voice_module.SaveCharacterVoiceNode SAVE_CHARACTER_VOICE_AVAILABLE = True except Exception as e: print(f"โŒ Save Character Voice failed: {e}") SAVE_CHARACTER_VOICE_AVAILABLE = False try: unified_voice_designer_module = load_node_module("unified_voice_designer_node", "shared/unified_voice_designer_node.py") UnifiedVoiceDesignerNode = unified_voice_designer_module.UnifiedVoiceDesignerNode UNIFIED_VOICE_DESIGNER_AVAILABLE = True except Exception as e: print(f"โŒ Voice Designer failed: {e}") UNIFIED_VOICE_DESIGNER_AVAILABLE = False # Load unified nodes try: unified_text_module = load_node_module("unified_tts_text_node", "unified/tts_text_node.py") UnifiedTTSTextNode = unified_text_module.UnifiedTTSTextNode UNIFIED_TEXT_AVAILABLE = True except Exception as e: print(f"โŒ Unified TTS Text failed: {e}") UNIFIED_TEXT_AVAILABLE = False try: unified_srt_module = load_node_module("unified_tts_srt_node", "unified/tts_srt_node.py") UnifiedTTSSRTNode = unified_srt_module.UnifiedTTSSRTNode UNIFIED_SRT_AVAILABLE = True except Exception as e: print(f"โŒ Unified TTS SRT failed: {e}") UNIFIED_SRT_AVAILABLE = False try: unified_sound_effects_module = load_node_module("unified_sound_effects_node", "unified/sound_effects_node.py") UnifiedSoundEffectsNode = unified_sound_effects_module.UnifiedSoundEffectsNode UNIFIED_SOUND_EFFECTS_AVAILABLE = True except Exception as e: print(f"โŒ Sound Effects failed: {e}") UNIFIED_SOUND_EFFECTS_AVAILABLE = False try: unified_vc_module = load_node_module("unified_voice_changer_node", "unified/voice_changer_node.py") UnifiedVoiceChangerNode = unified_vc_module.UnifiedVoiceChangerNode UNIFIED_VC_AVAILABLE = True except Exception as e: print(f"โŒ Unified Voice Changer failed: {e}") UNIFIED_VC_AVAILABLE = False try: unified_asr_module = load_node_module("unified_asr_transcribe_node", "unified/asr_transcribe_node.py") UnifiedASRTranscribeNode = unified_asr_module.UnifiedASRTranscribeNode UNIFIED_ASR_AVAILABLE = True except Exception as e: print(f"โŒ Unified ASR Transcribe failed: {e}") UNIFIED_ASR_AVAILABLE = False try: unified_training_module = load_node_module("unified_model_training_node", "unified/model_training_node.py") UnifiedModelTrainingNode = unified_training_module.UnifiedModelTrainingNode UNIFIED_TRAINING_AVAILABLE = True except Exception as e: print(f"โŒ Unified Model Training failed: {e}") UNIFIED_TRAINING_AVAILABLE = False try: srt_advanced_options_module = load_node_module("srt_advanced_options_node", "subtitles/srt_advanced_options_node.py") SRTAdvancedOptionsNode = srt_advanced_options_module.SRTAdvancedOptionsNode SRT_ADVANCED_OPTIONS_AVAILABLE = True except Exception as e: print(f"โŒ SRT Advanced Options failed: {e}") SRT_ADVANCED_OPTIONS_AVAILABLE = False try: text_to_srt_builder_module = load_node_module("text_to_srt_builder_node", "subtitles/text_to_srt_builder_node.py") TextToSRTBuilderNode = text_to_srt_builder_module.TextToSRTBuilderNode TEXT_TO_SRT_BUILDER_AVAILABLE = True except Exception as e: print(f"โŒ Text to SRT Builder failed: {e}") TEXT_TO_SRT_BUILDER_AVAILABLE = False # Load support nodes try: audio_recorder_module = load_node_module("chatterbox_audio_recorder_node", "audio/recorder_node.py") ChatterBoxVoiceCapture = audio_recorder_module.ChatterBoxVoiceCapture VOICE_CAPTURE_AVAILABLE = True except Exception as e: print(f"โŒ Voice Capture failed: {e}") VOICE_CAPTURE_AVAILABLE = False # Load audio analysis nodes try: audio_analyzer_module = load_node_module("chatterbox_audio_analyzer_node", "audio/analyzer_node.py") AudioAnalyzerNode = audio_analyzer_module.AudioAnalyzerNode AUDIO_ANALYZER_AVAILABLE = True except Exception as e: print(f"โŒ Audio Analyzer failed: {e}") AUDIO_ANALYZER_AVAILABLE = False try: audio_analyzer_options_module = load_node_module("chatterbox_audio_analyzer_options_node", "audio/analyzer_options_node.py") AudioAnalyzerOptionsNode = audio_analyzer_options_module.AudioAnalyzerOptionsNode AUDIO_ANALYZER_OPTIONS_AVAILABLE = True except Exception as e: print(f"โŒ Audio Analyzer Options failed: {e}") AUDIO_ANALYZER_OPTIONS_AVAILABLE = False # Load F5-TTS Edit nodes try: f5tts_edit_module = load_node_module("chatterbox_f5tts_edit_node", "f5tts/f5tts_edit_node.py") F5TTSEditNode = f5tts_edit_module.F5TTSEditNode F5TTS_EDIT_AVAILABLE = True except Exception as e: print(f"โŒ F5-TTS Edit failed: {e}") F5TTS_EDIT_AVAILABLE = False try: f5tts_edit_options_module = load_node_module("chatterbox_f5tts_edit_options_node", "f5tts/f5tts_edit_options_node.py") F5TTSEditOptionsNode = f5tts_edit_options_module.F5TTSEditOptionsNode F5TTS_EDIT_OPTIONS_AVAILABLE = True except Exception as e: print(f"โŒ F5-TTS Edit Options failed: {e}") F5TTS_EDIT_OPTIONS_AVAILABLE = False # Load Step Audio EditX Audio Editor node try: step_audio_editx_editor_module = load_node_module("step_audio_editx_audio_editor_node", "step_audio_editx_special/step_audio_editx_audio_editor_node.py") StepAudioEditXAudioEditorNode = step_audio_editx_editor_module.StepAudioEditXAudioEditorNode STEP_AUDIO_EDITX_EDITOR_AVAILABLE = True except Exception as e: print(f"โŒ Step Audio EditX Audio Editor failed: {e}") STEP_AUDIO_EDITX_EDITOR_AVAILABLE = False # Load RVC nodes try: rvc_engine_module = load_node_module("rvc_engine_node", "engines/rvc_engine_node.py") RVCEngineNode = rvc_engine_module.RVCEngineNode RVC_ENGINE_AVAILABLE = True except Exception as e: print(f"โŒ RVC Engine failed: {e}") RVC_ENGINE_AVAILABLE = False try: rvc_pitch_options_module = load_node_module("rvc_pitch_options_node", "audio/rvc_pitch_options_node.py") RVCPitchOptionsNode = rvc_pitch_options_module.RVCPitchOptionsNode RVC_PITCH_OPTIONS_AVAILABLE = True except Exception as e: print(f"โŒ RVC Pitch Options failed: {e}") RVC_PITCH_OPTIONS_AVAILABLE = False try: vocal_removal_module = load_node_module("vocal_removal_node", "audio/vocal_removal_node.py") VocalRemovalNode = vocal_removal_module.VocalRemovalNode VOCAL_REMOVAL_AVAILABLE = True except Exception as e: print(f"โŒ Vocal/Noise Removal failed: {e}") VOCAL_REMOVAL_AVAILABLE = False try: merge_audio_module = load_node_module("merge_audio_node", "audio/merge_audio_node.py") MergeAudioNode = merge_audio_module.MergeAudioNode MERGE_AUDIO_AVAILABLE = True except Exception as e: print(f"โŒ Merge Audio failed: {e}") MERGE_AUDIO_AVAILABLE = False try: voice_fixer_module = load_node_module("voice_fixer_node", "audio/voice_fixer_node.py") VoiceFixerNode = voice_fixer_module.VoiceFixerNode VOICE_FIXER_AVAILABLE = True except Exception as e: print(f"โŒ Voice Fixer Audio Restoration failed: {e}") VOICE_FIXER_AVAILABLE = False try: load_rvc_model_module = load_node_module("load_rvc_model_node", "models/load_rvc_model_node.py") LoadRVCModelNode = load_rvc_model_module.LoadRVCModelNode LOAD_RVC_MODEL_AVAILABLE = True except Exception as e: print(f"โŒ Load RVC Character Model failed: {e}") LOAD_RVC_MODEL_AVAILABLE = False try: rvc_dataset_prep_module = load_node_module("rvc_dataset_prep_node", "training/rvc_dataset_prep_node.py") RVCDatasetPrepNode = rvc_dataset_prep_module.RVCDatasetPrepNode RVC_DATASET_PREP_AVAILABLE = True except Exception as e: print(f"โŒ RVC Dataset Prep failed: {e}") RVC_DATASET_PREP_AVAILABLE = False try: rvc_training_config_module = load_node_module("rvc_training_config_node", "training/rvc_training_config_node.py") RVCTrainingConfigNode = rvc_training_config_module.RVCTrainingConfigNode RVC_TRAINING_CONFIG_AVAILABLE = True except Exception as e: print(f"โŒ RVC Training Config failed: {e}") RVC_TRAINING_CONFIG_AVAILABLE = False try: moss_dataset_prep_module = load_node_module("moss_dataset_prep_node", "training/moss_dataset_prep_node.py") MossDatasetPrepNode = moss_dataset_prep_module.MossDatasetPrepNode MOSS_DATASET_PREP_AVAILABLE = True except Exception as e: print(f"โŒ MOSS Dataset Prep failed: {e}") MOSS_DATASET_PREP_AVAILABLE = False try: moss_training_config_module = load_node_module("moss_training_config_node", "training/moss_training_config_node.py") MossTrainingConfigNode = moss_training_config_module.MossTrainingConfigNode MOSS_TRAINING_CONFIG_AVAILABLE = True except Exception as e: print(f"โŒ MOSS Training Config failed: {e}") MOSS_TRAINING_CONFIG_AVAILABLE = False try: moss_clip_staging_module = load_node_module("moss_clip_staging_node", "training/moss_clip_staging_node.py") MossClipStagingNode = moss_clip_staging_module.MossClipStagingNode MOSS_CLIP_STAGING_AVAILABLE = True except Exception as e: print(f"โŒ MOSS Clip Staging failed: {e}") MOSS_CLIP_STAGING_AVAILABLE = False try: moss_dataset_rows_module = load_node_module("moss_dataset_rows_node", "training/moss_dataset_rows_node.py") MossDatasetRowsNode = moss_dataset_rows_module.MossDatasetRowsNode MOSS_DATASET_ROWS_AVAILABLE = True except Exception as e: print(f"โŒ MOSS Dataset Rows failed: {e}") MOSS_DATASET_ROWS_AVAILABLE = False try: dramabox_dataset_prep_module = load_node_module("dramabox_dataset_prep_node", "training/dramabox_dataset_prep_node.py") DramaBoxDatasetPrepNode = dramabox_dataset_prep_module.DramaBoxDatasetPrepNode DRAMABOX_DATASET_PREP_AVAILABLE = True except Exception as e: print(f"โŒ DramaBox Dataset Prep failed: {e}") DRAMABOX_DATASET_PREP_AVAILABLE = False try: dramabox_dataset_rows_module = load_node_module("dramabox_dataset_rows_node", "training/dramabox_dataset_rows_node.py") DramaBoxDatasetRowsNode = dramabox_dataset_rows_module.DramaBoxDatasetRowsNode DRAMABOX_DATASET_ROWS_AVAILABLE = True except Exception as e: print(f"โŒ DramaBox Dataset Rows failed: {e}") DRAMABOX_DATASET_ROWS_AVAILABLE = False try: dramabox_training_config_module = load_node_module("dramabox_training_config_node", "training/dramabox_training_config_node.py") DramaBoxTrainingConfigNode = dramabox_training_config_module.DramaBoxTrainingConfigNode DRAMABOX_TRAINING_CONFIG_AVAILABLE = True except Exception as e: print(f"โŒ DramaBox Training Config failed: {e}") DRAMABOX_TRAINING_CONFIG_AVAILABLE = False try: phoneme_text_normalizer_module = load_node_module("phoneme_text_normalizer_node", "text/phoneme_text_normalizer_node.py") PhonemeTextNormalizer = phoneme_text_normalizer_module.PhonemeTextNormalizer PHONEME_TEXT_NORMALIZER_AVAILABLE = True except Exception as e: print(f"โŒ Phoneme Text Normalizer failed: {e}") PHONEME_TEXT_NORMALIZER_AVAILABLE = False try: asr_punctuation_truecase_module = load_node_module("asr_punctuation_truecase_node", "text/asr_punctuation_truecase_node.py") ASRPunctuationTruecaseNode = asr_punctuation_truecase_module.ASRPunctuationTruecaseNode ASR_PUNCTUATION_TRUECASE_AVAILABLE = True except Exception as e: print(f"โŒ ASR Punctuation / Truecase failed: {e}") ASR_PUNCTUATION_TRUECASE_AVAILABLE = False try: string_multiline_tag_editor_module = load_node_module("string_multiline_tag_editor_node", "text/tts_tag_editor_node.py") StringMultilineTagEditor = string_multiline_tag_editor_module.StringMultilineTagEditor STRING_MULTILINE_TAG_EDITOR_AVAILABLE = True except Exception as e: print(f"โŒ Multiline TTS Tag Editor failed: {e}") STRING_MULTILINE_TAG_EDITOR_AVAILABLE = False # Import foundation components for compatibility from utils.system.import_manager import import_manager # Legacy compatibility - keep these for existing workflows GLOBAL_AUDIO_CACHE = {} NODE_DIR = os.path.dirname(__file__) BUNDLED_CHATTERBOX_DIR = os.path.join(NODE_DIR, "chatterbox") BUNDLED_MODELS_DIR = os.path.join(NODE_DIR, "models", "chatterbox") # Get availability status from import manager availability = import_manager.get_availability_summary() CHATTERBOX_TTS_AVAILABLE = availability["tts"] CHATTERBOX_VC_AVAILABLE = availability["vc"] CHATTERBOX_AVAILABLE = availability["any_chatterbox"] USING_BUNDLED_CHATTERBOX = True # Default assumption def find_chatterbox_models(): """Find ChatterBox model files in order of priority - Legacy compatibility function""" model_paths = [] # 1. Check for bundled models in node folder bundled_model_path = os.path.join(BUNDLED_MODELS_DIR, "s3gen.pt") if os.path.exists(bundled_model_path): model_paths.append(("bundled", BUNDLED_MODELS_DIR)) return model_paths # Return immediately if bundled models found # 2. Check ComfyUI models folder - first check the new TTS organization comfyui_model_path_tts = os.path.join(folder_paths.models_dir, "TTS", "chatterbox", "s3gen.pt") if os.path.exists(comfyui_model_path_tts): model_paths.append(("comfyui", os.path.dirname(comfyui_model_path_tts))) return model_paths # 3. Check legacy location (direct chatterbox) for backward compatibility comfyui_model_path_legacy = os.path.join(folder_paths.models_dir, "chatterbox", "s3gen.pt") if os.path.exists(comfyui_model_path_legacy): model_paths.append(("comfyui", os.path.dirname(comfyui_model_path_legacy))) return model_paths # 3. HuggingFace download as fallback (only if no local models found) model_paths.append(("huggingface", None)) return model_paths # Import SRT node conditionally try: srt_module = load_node_module("chatterbox_srt_node", "chatterbox/chatterbox_srt_node.py") ChatterboxSRTTTSNode = srt_module.ChatterboxSRTTTSNode SRT_SUPPORT_AVAILABLE = True except (ImportError, FileNotFoundError, AttributeError): SRT_SUPPORT_AVAILABLE = False # Create dummy SRT node for compatibility class ChatterboxSRTTTSNode: @classmethod def INPUT_TYPES(cls): return {"required": {"error": ("STRING", {"default": "SRT support not available"})}} RETURN_TYPES = ("STRING",) FUNCTION = "error" CATEGORY = "ChatterBox Voice" def error(self, error): raise ImportError("SRT support not available - missing required modules") # Update SRT node availability based on import manager try: success, modules, source = import_manager.import_srt_modules() if success: SRT_SUPPORT_AVAILABLE = True # Make SRT modules available for legacy compatibility if needed SRTParser = modules.get("SRTParser") SRTSubtitle = modules.get("SRTSubtitle") SRTParseError = modules.get("SRTParseError") AudioTimingUtils = modules.get("AudioTimingUtils") TimedAudioAssembler = modules.get("TimedAudioAssembler") calculate_timing_adjustments = modules.get("calculate_timing_adjustments") AudioTimingError = modules.get("AudioTimingError") PhaseVocoderTimeStretcher = modules.get("PhaseVocoderTimeStretcher") FFmpegTimeStretcher = modules.get("FFmpegTimeStretcher") if IS_DEV: print(f"โœ… SRT TTS node available! (source: {source})") else: SRT_SUPPORT_AVAILABLE = False if IS_DEV: print("โŒ SRT support not available") except Exception: SRT_SUPPORT_AVAILABLE = False if IS_DEV: print("โŒ SRT support initialization failed") # The new unified architecture handles engine availability internally # Register unified nodes NODE_CLASS_MAPPINGS = {} NODE_DISPLAY_NAME_MAPPINGS = {} # Register engine nodes if CHATTERBOX_ENGINE_AVAILABLE: NODE_CLASS_MAPPINGS["ChatterBoxEngineNode"] = ChatterBoxEngineNode NODE_DISPLAY_NAME_MAPPINGS["ChatterBoxEngineNode"] = "โš™๏ธ ChatterBox TTS Engine" if F5TTS_ENGINE_AVAILABLE: NODE_CLASS_MAPPINGS["F5TTSEngineNode"] = F5TTSEngineNode NODE_DISPLAY_NAME_MAPPINGS["F5TTSEngineNode"] = "โš™๏ธ F5 TTS Engine" if HIGGS_AUDIO_ENGINE_AVAILABLE: NODE_CLASS_MAPPINGS["HiggsAudioEngineNode"] = HiggsAudioEngineNode NODE_DISPLAY_NAME_MAPPINGS["HiggsAudioEngineNode"] = "โš™๏ธ Higgs Audio 2 Engine" if HIGGS_AUDIO_V3_ENGINE_AVAILABLE: NODE_CLASS_MAPPINGS["HiggsAudioV3EngineNode"] = HiggsAudioV3EngineNode NODE_DISPLAY_NAME_MAPPINGS["HiggsAudioV3EngineNode"] = "โš™๏ธ Higgs Audio v3 Engine" if STEP_AUDIO_EDITX_ENGINE_AVAILABLE: NODE_CLASS_MAPPINGS["StepAudioEditXEngineNode"] = StepAudioEditXEngineNode NODE_DISPLAY_NAME_MAPPINGS["StepAudioEditXEngineNode"] = "โš™๏ธ Step Audio EditX Engine" if VIBEVOICE_ENGINE_AVAILABLE: NODE_CLASS_MAPPINGS["VibeVoiceEngineNode"] = VibeVoiceEngineNode NODE_DISPLAY_NAME_MAPPINGS["VibeVoiceEngineNode"] = "โš™๏ธ VibeVoice Engine" if QWEN3_TTS_ENGINE_AVAILABLE: NODE_CLASS_MAPPINGS["Qwen3TTSEngineNode"] = Qwen3TTSEngineNode NODE_DISPLAY_NAME_MAPPINGS["Qwen3TTSEngineNode"] = "โš™๏ธ Qwen3-TTS Engine" if MOSS_TTS_ENGINE_AVAILABLE: NODE_CLASS_MAPPINGS["MossTTSEngineNode"] = MossTTSEngineNode NODE_DISPLAY_NAME_MAPPINGS["MossTTSEngineNode"] = "โš™๏ธ MOSS-TTS Engine" if MOSS_SOUNDEFFECT_V2_ENGINE_AVAILABLE: NODE_CLASS_MAPPINGS["MossSoundEffectV2EngineNode"] = MossSoundEffectV2EngineNode NODE_DISPLAY_NAME_MAPPINGS["MossSoundEffectV2EngineNode"] = "โš™๏ธ MOSS SoundEffect v2 Engine" if GRANITE_ASR_ENGINE_AVAILABLE: NODE_CLASS_MAPPINGS["GraniteASREngineNode"] = GraniteASREngineNode NODE_DISPLAY_NAME_MAPPINGS["GraniteASREngineNode"] = "โš™๏ธ Granite ASR Engine" if ECHO_TTS_ENGINE_AVAILABLE: NODE_CLASS_MAPPINGS["EchoTTSEngineNode"] = EchoTTSEngineNode NODE_DISPLAY_NAME_MAPPINGS["EchoTTSEngineNode"] = "โš™๏ธ Echo-TTS Engine" if DOTS_TTS_ENGINE_AVAILABLE: NODE_CLASS_MAPPINGS["DotsTTSEngineNode"] = DotsTTSEngineNode NODE_DISPLAY_NAME_MAPPINGS["DotsTTSEngineNode"] = "โš™๏ธ Dots TTS Engine" if DRAMABOX_ENGINE_AVAILABLE: NODE_CLASS_MAPPINGS["DramaBoxEngineNode"] = DramaBoxEngineNode NODE_DISPLAY_NAME_MAPPINGS["DramaBoxEngineNode"] = "โš™๏ธ DramaBox Engine" if FISH_AUDIO_S2_ENGINE_AVAILABLE: NODE_CLASS_MAPPINGS["FishAudioS2EngineNode"] = FishAudioS2EngineNode NODE_DISPLAY_NAME_MAPPINGS["FishAudioS2EngineNode"] = "โš™๏ธ Fish Audio S2 Pro Engine" if AUDIO_CPP_ENGINE_AVAILABLE: NODE_CLASS_MAPPINGS["AudioCppEngineNode"] = AudioCppEngineNode NODE_DISPLAY_NAME_MAPPINGS["AudioCppEngineNode"] = "โš™๏ธ audio.cpp Multi-TTS Engine" if OMNIVOICE_ENGINE_AVAILABLE: NODE_CLASS_MAPPINGS["OmniVoiceEngineNode"] = OmniVoiceEngineNode NODE_DISPLAY_NAME_MAPPINGS["OmniVoiceEngineNode"] = "โš™๏ธ OmniVoice Engine" if OMNIVOICE_INSTRUCTION_BUILDER_AVAILABLE: NODE_CLASS_MAPPINGS["OmniVoiceInstructionBuilderNode"] = OmniVoiceInstructionBuilderNode NODE_DISPLAY_NAME_MAPPINGS["OmniVoiceInstructionBuilderNode"] = "๐Ÿ“ Visual Tag Builder" if CHATTERBOX_OFFICIAL_23LANG_ENGINE_AVAILABLE: NODE_CLASS_MAPPINGS["ChatterBoxOfficial23LangEngineNode"] = ChatterBoxOfficial23LangEngineNode NODE_DISPLAY_NAME_MAPPINGS["ChatterBoxOfficial23LangEngineNode"] = "โš™๏ธ ChatterBox Official 23-Lang Engine" if INDEX_TTS_ENGINE_AVAILABLE: NODE_CLASS_MAPPINGS["IndexTTSEngineNode"] = IndexTTSEngineNode NODE_DISPLAY_NAME_MAPPINGS["IndexTTSEngineNode"] = "โš™๏ธ IndexTTS 2 / 2.5 Engine" if COSYVOICE_ENGINE_AVAILABLE: NODE_CLASS_MAPPINGS["CosyVoiceEngineNode"] = CosyVoiceEngineNode NODE_DISPLAY_NAME_MAPPINGS["CosyVoiceEngineNode"] = "โš™๏ธ CosyVoice3 Engine" if INDEX_TTS_EMOTION_OPTIONS_AVAILABLE: NODE_CLASS_MAPPINGS["IndexTTSEmotionOptionsNode"] = IndexTTSEmotionOptionsNode NODE_DISPLAY_NAME_MAPPINGS["IndexTTSEmotionOptionsNode"] = "๐ŸŒˆ IndexTTS-2 Emotion Vectors" if QWEN_EMOTION_AVAILABLE: NODE_CLASS_MAPPINGS["QwenEmotionNode"] = QwenEmotionNode NODE_DISPLAY_NAME_MAPPINGS["QwenEmotionNode"] = "๐ŸŒˆ IndexTTS-2 Text Emotion" # Register shared nodes if CHARACTER_VOICES_AVAILABLE: NODE_CLASS_MAPPINGS["CharacterVoicesNode"] = CharacterVoicesNode NODE_DISPLAY_NAME_MAPPINGS["CharacterVoicesNode"] = "๐ŸŽญ Character Voices" if SAVE_CHARACTER_VOICE_AVAILABLE: NODE_CLASS_MAPPINGS["SaveCharacterVoiceNode"] = SaveCharacterVoiceNode NODE_DISPLAY_NAME_MAPPINGS["SaveCharacterVoiceNode"] = "๐Ÿ’พ Save Character Voice" if UNIFIED_VOICE_DESIGNER_AVAILABLE: NODE_CLASS_MAPPINGS["UnifiedVoiceDesignerNode"] = UnifiedVoiceDesignerNode NODE_DISPLAY_NAME_MAPPINGS["UnifiedVoiceDesignerNode"] = "๐ŸŽจ Voice Designer" # Register unified nodes if UNIFIED_TEXT_AVAILABLE: NODE_CLASS_MAPPINGS["UnifiedTTSTextNode"] = UnifiedTTSTextNode NODE_DISPLAY_NAME_MAPPINGS["UnifiedTTSTextNode"] = "๐ŸŽค TTS Text" if UNIFIED_SRT_AVAILABLE: NODE_CLASS_MAPPINGS["UnifiedTTSSRTNode"] = UnifiedTTSSRTNode NODE_DISPLAY_NAME_MAPPINGS["UnifiedTTSSRTNode"] = "๐Ÿ“บ TTS SRT" if UNIFIED_SOUND_EFFECTS_AVAILABLE: NODE_CLASS_MAPPINGS["UnifiedSoundEffectsNode"] = UnifiedSoundEffectsNode NODE_DISPLAY_NAME_MAPPINGS["UnifiedSoundEffectsNode"] = "๐ŸŒฉ๏ธ Sound Effects" if UNIFIED_VC_AVAILABLE: NODE_CLASS_MAPPINGS["UnifiedVoiceChangerNode"] = UnifiedVoiceChangerNode NODE_DISPLAY_NAME_MAPPINGS["UnifiedVoiceChangerNode"] = "๐Ÿ”„ Voice Changer" if UNIFIED_ASR_AVAILABLE: NODE_CLASS_MAPPINGS["UnifiedASRTranscribeNode"] = UnifiedASRTranscribeNode NODE_DISPLAY_NAME_MAPPINGS["UnifiedASRTranscribeNode"] = "โœ๏ธ ASR Transcribe" if UNIFIED_TRAINING_AVAILABLE: NODE_CLASS_MAPPINGS["UnifiedModelTrainingNode"] = UnifiedModelTrainingNode NODE_DISPLAY_NAME_MAPPINGS["UnifiedModelTrainingNode"] = "๐ŸŽ“ Model Training" if SRT_ADVANCED_OPTIONS_AVAILABLE: NODE_CLASS_MAPPINGS["SRTAdvancedOptionsNode"] = SRTAdvancedOptionsNode NODE_DISPLAY_NAME_MAPPINGS["SRTAdvancedOptionsNode"] = "๐Ÿ”ง SRT Advanced Options" if TEXT_TO_SRT_BUILDER_AVAILABLE: NODE_CLASS_MAPPINGS["TextToSRTBuilderNode"] = TextToSRTBuilderNode NODE_DISPLAY_NAME_MAPPINGS["TextToSRTBuilderNode"] = "๐Ÿ“บ Text to SRT Builder" # Register legacy support nodes if VOICE_CAPTURE_AVAILABLE: NODE_CLASS_MAPPINGS["ChatterBoxVoiceCapture"] = ChatterBoxVoiceCapture NODE_DISPLAY_NAME_MAPPINGS["ChatterBoxVoiceCapture"] = "๐ŸŽ™๏ธ Voice Capture" if AUDIO_ANALYZER_AVAILABLE: NODE_CLASS_MAPPINGS["ChatterBoxAudioAnalyzer"] = AudioAnalyzerNode NODE_DISPLAY_NAME_MAPPINGS["ChatterBoxAudioAnalyzer"] = "๐ŸŒŠ Audio Wave Analyzer" if AUDIO_ANALYZER_OPTIONS_AVAILABLE: NODE_CLASS_MAPPINGS["ChatterBoxAudioAnalyzerOptions"] = AudioAnalyzerOptionsNode NODE_DISPLAY_NAME_MAPPINGS["ChatterBoxAudioAnalyzerOptions"] = "๐Ÿ”ง Audio Analyzer Options" if F5TTS_EDIT_AVAILABLE: NODE_CLASS_MAPPINGS["ChatterBoxF5TTSEditVoice"] = F5TTSEditNode NODE_DISPLAY_NAME_MAPPINGS["ChatterBoxF5TTSEditVoice"] = "๐Ÿ‘„ F5-TTS Speech Editor" if F5TTS_EDIT_OPTIONS_AVAILABLE: NODE_CLASS_MAPPINGS["ChatterBoxF5TTSEditOptions"] = F5TTSEditOptionsNode NODE_DISPLAY_NAME_MAPPINGS["ChatterBoxF5TTSEditOptions"] = "๐Ÿ”ง F5-TTS Edit Options" if STEP_AUDIO_EDITX_EDITOR_AVAILABLE: NODE_CLASS_MAPPINGS["StepAudioEditXAudioEditorNode"] = StepAudioEditXAudioEditorNode NODE_DISPLAY_NAME_MAPPINGS["StepAudioEditXAudioEditorNode"] = "๐ŸŽจ Step Audio EditX - Audio Editor" # Load video analysis nodes try: mouth_movement_module = load_node_module("mouth_movement_analyzer_node", "video/mouth_movement_analyzer_node.py") MouthMovementAnalyzerNode = mouth_movement_module.MouthMovementAnalyzerNode MOUTH_MOVEMENT_AVAILABLE = True except Exception as e: print(f"โŒ Mouth Movement Analyzer failed: {e}") MOUTH_MOVEMENT_AVAILABLE = False try: viseme_options_module = load_node_module("viseme_options_node", "video/viseme_options_node.py") VisemeDetectionOptionsNode = viseme_options_module.VisemeDetectionOptionsNode VISEME_OPTIONS_AVAILABLE = True except Exception as e: print(f"โŒ Viseme Options failed: {e}") VISEME_OPTIONS_AVAILABLE = False # Register RVC nodes if RVC_ENGINE_AVAILABLE: NODE_CLASS_MAPPINGS["RVCEngineNode"] = RVCEngineNode NODE_DISPLAY_NAME_MAPPINGS["RVCEngineNode"] = "โš™๏ธ RVC Engine" if RVC_PITCH_OPTIONS_AVAILABLE: NODE_CLASS_MAPPINGS["RVCPitchOptionsNode"] = RVCPitchOptionsNode NODE_DISPLAY_NAME_MAPPINGS["RVCPitchOptionsNode"] = "๐Ÿ”ง RVC Pitch Extraction Options" if VOCAL_REMOVAL_AVAILABLE: NODE_CLASS_MAPPINGS["VocalRemovalNode"] = VocalRemovalNode NODE_DISPLAY_NAME_MAPPINGS["VocalRemovalNode"] = "๐Ÿค Noise or Vocal Removal" if MERGE_AUDIO_AVAILABLE: NODE_CLASS_MAPPINGS["MergeAudioNode"] = MergeAudioNode NODE_DISPLAY_NAME_MAPPINGS["MergeAudioNode"] = "๐Ÿฅช Merge Audio" if VOICE_FIXER_AVAILABLE: NODE_CLASS_MAPPINGS["VoiceFixerNode"] = VoiceFixerNode NODE_DISPLAY_NAME_MAPPINGS["VoiceFixerNode"] = "๐Ÿค Voice Fixer" if LOAD_RVC_MODEL_AVAILABLE: NODE_CLASS_MAPPINGS["LoadRVCModelNode"] = LoadRVCModelNode NODE_DISPLAY_NAME_MAPPINGS["LoadRVCModelNode"] = "๐ŸŽญ Load RVC Character Model" if RVC_DATASET_PREP_AVAILABLE: NODE_CLASS_MAPPINGS["RVCDatasetPrepNode"] = RVCDatasetPrepNode NODE_DISPLAY_NAME_MAPPINGS["RVCDatasetPrepNode"] = "๐Ÿ“ฆ RVC Dataset Prep" if RVC_TRAINING_CONFIG_AVAILABLE: NODE_CLASS_MAPPINGS["RVCTrainingConfigNode"] = RVCTrainingConfigNode NODE_DISPLAY_NAME_MAPPINGS["RVCTrainingConfigNode"] = "๐ŸŽ›๏ธ RVC Training Config" if MOSS_DATASET_PREP_AVAILABLE: NODE_CLASS_MAPPINGS["MossDatasetPrepNode"] = MossDatasetPrepNode NODE_DISPLAY_NAME_MAPPINGS["MossDatasetPrepNode"] = "๐Ÿ“ฆ MOSS Dataset Prep" if MOSS_TRAINING_CONFIG_AVAILABLE: NODE_CLASS_MAPPINGS["MossTrainingConfigNode"] = MossTrainingConfigNode NODE_DISPLAY_NAME_MAPPINGS["MossTrainingConfigNode"] = "๐ŸŽ›๏ธ MOSS Training Config" if MOSS_CLIP_STAGING_AVAILABLE: NODE_CLASS_MAPPINGS["MossClipStagingNode"] = MossClipStagingNode NODE_DISPLAY_NAME_MAPPINGS["MossClipStagingNode"] = "๐ŸŽž๏ธ Training Clip Staging" if MOSS_DATASET_ROWS_AVAILABLE: NODE_CLASS_MAPPINGS["MossDatasetRowsNode"] = MossDatasetRowsNode NODE_DISPLAY_NAME_MAPPINGS["MossDatasetRowsNode"] = "๐Ÿงพ MOSS Dataset Rows" if DRAMABOX_DATASET_PREP_AVAILABLE: NODE_CLASS_MAPPINGS["DramaBoxDatasetPrepNode"] = DramaBoxDatasetPrepNode NODE_DISPLAY_NAME_MAPPINGS["DramaBoxDatasetPrepNode"] = "๐Ÿ“ฆ DramaBox Dataset Prep" if DRAMABOX_DATASET_ROWS_AVAILABLE: NODE_CLASS_MAPPINGS["DramaBoxDatasetRowsNode"] = DramaBoxDatasetRowsNode NODE_DISPLAY_NAME_MAPPINGS["DramaBoxDatasetRowsNode"] = "๐Ÿงพ DramaBox Dataset Rows" if DRAMABOX_TRAINING_CONFIG_AVAILABLE: NODE_CLASS_MAPPINGS["DramaBoxTrainingConfigNode"] = DramaBoxTrainingConfigNode NODE_DISPLAY_NAME_MAPPINGS["DramaBoxTrainingConfigNode"] = "๐ŸŽ›๏ธ DramaBox Training Config" # Register text processing nodes if PHONEME_TEXT_NORMALIZER_AVAILABLE: NODE_CLASS_MAPPINGS["PhonemeTextNormalizer"] = PhonemeTextNormalizer NODE_DISPLAY_NAME_MAPPINGS["PhonemeTextNormalizer"] = "๐Ÿ“ Phoneme Text Normalizer" if ASR_PUNCTUATION_TRUECASE_AVAILABLE: NODE_CLASS_MAPPINGS["ASRPunctuationTruecaseNode"] = ASRPunctuationTruecaseNode NODE_DISPLAY_NAME_MAPPINGS["ASRPunctuationTruecaseNode"] = "๐Ÿ“ ASR Punctuation / Truecase" if STRING_MULTILINE_TAG_EDITOR_AVAILABLE: NODE_CLASS_MAPPINGS["StringMultilineTagEditor"] = StringMultilineTagEditor NODE_DISPLAY_NAME_MAPPINGS["StringMultilineTagEditor"] = "๐Ÿท๏ธ Multiline TTS Tag Editor" if REFRESH_VOICE_CACHE_AVAILABLE: NODE_CLASS_MAPPINGS["RefreshVoiceCacheNode"] = RefreshVoiceCacheNode NODE_DISPLAY_NAME_MAPPINGS["RefreshVoiceCacheNode"] = "โ™ป๏ธ Refresh Voice Cache" # Register video analysis nodes if MOUTH_MOVEMENT_AVAILABLE: NODE_CLASS_MAPPINGS["MouthMovementAnalyzer"] = MouthMovementAnalyzerNode NODE_DISPLAY_NAME_MAPPINGS["MouthMovementAnalyzer"] = "๐Ÿ—ฃ๏ธ Silent Speech Analyzer" if VISEME_OPTIONS_AVAILABLE: NODE_CLASS_MAPPINGS["VisemeDetectionOptionsNode"] = VisemeDetectionOptionsNode NODE_DISPLAY_NAME_MAPPINGS["VisemeDetectionOptionsNode"] = "๐Ÿ”ง Viseme Mouth Shape Options" # Print startup banner print(SEPARATOR) print(f"๐Ÿš€ TTS Audio Suite {VERSION_DISPLAY}") print("Universal multi-engine TTS extension for ComfyUI") # Show Python 3.13 compatibility status if sys.version_info >= (3, 13): print(f"๐Ÿ Python {sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro} detected") # ๐Ÿ”ฌ NUMBA WORKAROUND: Commented out - testing if still needed with numba 0.61.2+ and librosa 0.11.0+ # print("โšก Numba JIT disabled for librosa compatibility") # Removed legacy ChatterBox model discovery to improve startup performance # Model discovery now happens lazily when needed by individual engines # Import dependency checker try: from utils.system.dependency_checker import DependencyChecker, AsyncDependencyChecker DEPENDENCY_CHECKER_AVAILABLE = True except ImportError: DEPENDENCY_CHECKER_AVAILABLE = False AsyncDependencyChecker = None # Check for immediate PortAudio issues (show during startup) startup_warnings = [] # Check PortAudio availability for voice recording if VOICE_CAPTURE_AVAILABLE and hasattr(audio_recorder_module, 'SOUNDDEVICE_AVAILABLE') and not audio_recorder_module.SOUNDDEVICE_AVAILABLE: startup_warnings.append("โš ๏ธ PortAudio library not found - Voice recording disabled") startup_warnings.append(" Install with: sudo apt-get install portaudio19-dev (Linux) or brew install portaudio (macOS)") # Only show startup dependencies if there are warnings if startup_warnings: print("๐Ÿ“‹ System Dependencies:") for warning in startup_warnings: print(f" {warning}") # Start background dependency check (non-blocking) # This will print warnings after ComfyUI loads if there are missing dependencies if DEPENDENCY_CHECKER_AVAILABLE and AsyncDependencyChecker: try: async_checker = AsyncDependencyChecker() async_checker.start_background_check() except Exception: pass # Silently fail - background check is optional print(f"โœ… TTS Audio Suite {VERSION_DISPLAY} loaded with {len(NODE_DISPLAY_NAME_MAPPINGS)} nodes:") # Group engines first, then everything else nodes = sorted(NODE_DISPLAY_NAME_MAPPINGS.values()) engines = [n for n in nodes if "โš™๏ธ" in n] others = [n for n in nodes if "โš™๏ธ" not in n] for node in engines + others: print(f" โ€ข {node}") print(SEPARATOR) # Trigger voice discovery initialization in background thread after ComfyUI startup # This loads character cache or starts scanning in a non-blocking way try: import threading from utils.voice.discovery import voice_discovery def _init_voice_discovery_background(): """Initialize voice discovery in background after ComfyUI loads.""" import time # Wait a moment for ComfyUI to fully initialize time.sleep(0.5) # Trigger initialization (loads cache or starts background scan) voice_discovery._ensure_initialized() discovery_thread = threading.Thread(target=_init_voice_discovery_background, daemon=True) discovery_thread.start() except Exception: pass # Silently fail - discovery will happen on first use