994 lines
42 KiB
Python
994 lines
42 KiB
Python
# 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
|