- Implement generic local-first fallback utility for all TTS engines - Fix ChatterBox and F5-TTS duplicate model loading during language fallback - Add F5-TTS language grouping to load models once per language group - Fix pt-br language mapping support in language mapper - Add model tracking to prevent redundant loading in multilingual engine - Optimize SRT nodes to use unified language-grouped processing This ensures efficient model loading with proper local model reuse, eliminating unnecessary downloads and duplicate model loading for low-VRAM scenarios while maintaining full language support.
68 lines
2.5 KiB
Python
68 lines
2.5 KiB
Python
"""
|
|
Model Fallback Utils - Generic utilities for TTS engine model fallback handling
|
|
Provides simple local-first fallback behavior for ANY TTS engine
|
|
"""
|
|
|
|
import os
|
|
from typing import Optional, Callable, Any, List
|
|
from pathlib import Path
|
|
|
|
|
|
def try_local_first(
|
|
search_paths: List[str],
|
|
local_loader: Callable[[str], Any],
|
|
fallback_loader: Callable[[], Any],
|
|
fallback_name: str = "fallback model",
|
|
original_request: str = "requested model"
|
|
) -> Any:
|
|
"""
|
|
Generic local-first model loading with HuggingFace fallback.
|
|
Works for ANY TTS engine - no hardcoded engine knowledge.
|
|
|
|
Args:
|
|
search_paths: List of directory paths to check for local models
|
|
local_loader: Function that loads a local model given a path: (path) -> model
|
|
fallback_loader: Function that downloads/loads fallback model: () -> model
|
|
fallback_name: Name for logging (e.g., "English", "F5TTS_Base")
|
|
original_request: Original request for logging (e.g., "French", "F5-DE")
|
|
|
|
Returns:
|
|
Loaded model instance
|
|
|
|
Example:
|
|
# ChatterBox usage:
|
|
return try_local_first(
|
|
search_paths=["models/chatterbox"],
|
|
local_loader=lambda path: ChatterboxTTS.from_local(path, device),
|
|
fallback_loader=lambda: ChatterboxTTS.from_pretrained(device, "English"),
|
|
fallback_name="English",
|
|
original_request="French"
|
|
)
|
|
"""
|
|
print(f"🔄 {original_request} not found, checking local {fallback_name} before download...")
|
|
|
|
# Try each search path
|
|
for search_path in search_paths:
|
|
path = Path(search_path)
|
|
if path.exists() and path.is_dir():
|
|
try:
|
|
# Check if directory has any files (basic validation)
|
|
if any(path.iterdir()):
|
|
print(f"📁 Found local {fallback_name} at {search_path}, using instead of download")
|
|
return local_loader(str(path))
|
|
except Exception as e:
|
|
print(f"⚠️ Local {fallback_name} at {search_path} failed to load: {e}")
|
|
continue
|
|
|
|
# No local model found or all failed - use fallback
|
|
print(f"📦 Loading {fallback_name} from HuggingFace as final fallback")
|
|
return fallback_loader()
|
|
|
|
|
|
def get_models_dir() -> Optional[str]:
|
|
"""Get ComfyUI models directory if available."""
|
|
try:
|
|
import folder_paths
|
|
return folder_paths.models_dir
|
|
except ImportError:
|
|
return None |