| |
| """ |
| Comprehensive Gradio Launcher for ChatterboxTTS |
| Automatically handles all requirements, installation, and setup |
| """ |
|
|
| import sys |
| import os |
| import subprocess |
| import importlib |
| import pkg_resources |
| from pathlib import Path |
| import time |
|
|
| class GradioLauncher: |
| def __init__(self): |
| self.required_packages = { |
| |
| 'gradio': {'min_version': '4.0.0', 'install_name': 'gradio>=4.0.0'}, |
| 'torch': {'min_version': '2.0.0', 'install_name': 'torch>=2.0.0'}, |
| 'torchaudio': {'min_version': '2.0.0', 'install_name': 'torchaudio>=2.0.0'}, |
| 'transformers': {'min_version': '4.20.0', 'install_name': 'transformers>=4.20.0'}, |
| 'huggingface_hub': {'min_version': '0.15.0', 'install_name': 'huggingface_hub>=0.15.0'}, |
| 'safetensors': {'min_version': '0.3.0', 'install_name': 'safetensors>=0.3.0'}, |
|
|
| |
| 'soundfile': {'min_version': '0.12.0', 'install_name': 'soundfile>=0.12.0'}, |
| 'librosa': {'min_version': '0.10.0', 'install_name': 'librosa>=0.10.0'}, |
| 'pydub': {'min_version': '0.25.0', 'install_name': 'pydub>=0.25.0'}, |
|
|
| |
| 'parselmouth': {'min_version': '0.4.3', 'install_name': 'praat-parselmouth>=0.4.3', 'optional': True}, |
| 'matplotlib': {'min_version': '3.5.0', 'install_name': 'matplotlib>=3.5.0'}, |
| 'scipy': {'min_version': '1.8.0', 'install_name': 'scipy>=1.8.0'}, |
| 'numpy': {'min_version': '1.21.0', 'install_name': 'numpy>=1.21.0'}, |
|
|
| |
| 'psutil': {'min_version': '5.8.0', 'install_name': 'psutil>=5.8.0'}, |
| 'vaderSentiment': {'min_version': '3.3.0', 'install_name': 'vaderSentiment>=3.3.0'}, |
| } |
|
|
| self.chatterbox_git_url = 'git+https://github.com/resemble-ai/chatterbox-tts.git' |
| self.optional_packages = ['parselmouth', 'pynvml'] |
|
|
| def print_header(self): |
| """Print launcher header""" |
| print("=" * 70) |
| print("π ChatterboxTTS Gradio Launcher") |
| print("=" * 70) |
| print("π§ Comprehensive setup and dependency manager") |
| print("π¦ Automatically installs missing requirements") |
| print("π Launches web interface when ready") |
| print("-" * 70) |
|
|
| def check_python_version(self): |
| """Check if Python version is compatible""" |
| print("π Checking Python version...") |
|
|
| version_info = sys.version_info |
| if version_info.major < 3 or (version_info.major == 3 and version_info.minor < 8): |
| print("β Error: Python 3.8+ required") |
| print(f" Current version: {version_info.major}.{version_info.minor}.{version_info.micro}") |
| print(" Please upgrade Python and try again") |
| sys.exit(1) |
|
|
| print(f"β
Python {version_info.major}.{version_info.minor}.{version_info.micro} - Compatible") |
|
|
| def check_working_directory(self): |
| """Verify we're in the correct directory""" |
| print("π Checking working directory...") |
|
|
|
|
| if missing_files: |
| print(f"β Error: Missing required files/directories: {', '.join(missing_files)}") |
| print(" Please run this script from the ChatterboxTTS root directory") |
| print(" Expected structure:") |
| print(" βββ gradio_main_interface.py") |
| print(" βββ gradio_tabs/") |
| print(" βββ config/") |
| print(" βββ src/") |
| print(" βββ ...") |
| return False |
|
|
| print("β
Working directory structure verified") |
| return True |
|
|
| def create_directories(self): |
| """Create required directories if they don't exist""" |
| print("π Creating required directories...") |
|
|
| directories = ['Voice_Samples', 'Text_Input', 'Audiobook', 'Output', 'voice_analyzer'] |
| created = [] |
|
|
| for dir_name in directories: |
| dir_path = Path(dir_name) |
| if not dir_path.exists(): |
| dir_path.mkdir(parents=True, exist_ok=True) |
| created.append(dir_name) |
|
|
| if created: |
| print(f"β
Created directories: {', '.join(created)}") |
| else: |
| print("β
All required directories exist") |
|
|
| def check_package_installed(self, package_name): |
| """Check if a package is installed and get its version""" |
| |
| if hasattr(self, 'venv_python') and Path(self.venv_python).exists(): |
| try: |
| cmd = [self.venv_python, '-c', f''' |
| try: |
| import {package_name} |
| print("INSTALLED", getattr({package_name}, "__version__", "0.0.0")) |
| except ImportError: |
| print("NOT_INSTALLED") |
| '''] |
| result = subprocess.run(cmd, capture_output=True, text=True, timeout=10) |
| if result.returncode == 0: |
| output = result.stdout.strip() |
| if output.startswith("INSTALLED"): |
| version = output.split(" ", 1)[1] if " " in output else "0.0.0" |
| return True, version |
| else: |
| return False, None |
| except Exception: |
| pass |
|
|
| |
| try: |
| if package_name == 'parselmouth': |
| |
| import parselmouth |
| return True, getattr(parselmouth, '__version__', '0.0.0') |
| else: |
| module = importlib.import_module(package_name) |
| version = getattr(module, '__version__', '0.0.0') |
| return True, version |
| except ImportError: |
| try: |
| |
| pkg = pkg_resources.get_distribution(package_name) |
| return True, pkg.version |
| except (pkg_resources.DistributionNotFound, ImportError): |
| return False, None |
|
|
| def compare_versions(self, current, required): |
| """Compare version strings""" |
| try: |
| current_parts = [int(x) for x in current.split('.')] |
| required_parts = [int(x) for x in required.split('.')] |
|
|
| |
| max_len = max(len(current_parts), len(required_parts)) |
| current_parts.extend([0] * (max_len - len(current_parts))) |
| required_parts.extend([0] * (max_len - len(required_parts))) |
|
|
| return current_parts >= required_parts |
| except (ValueError, AttributeError): |
| |
| return True |
|
|
| def setup_virtual_environment(self): |
| """Set up virtual environment if in externally managed environment""" |
| venv_path = Path("venv") |
|
|
| if not venv_path.exists(): |
| print("π§ Creating virtual environment (externally managed Python detected)...") |
| try: |
| result = subprocess.run( |
| [sys.executable, '-m', 'venv', 'venv'], |
| capture_output=True, |
| text=True, |
| timeout=60 |
| ) |
| if result.returncode != 0: |
| print(f" β Failed to create virtual environment: {result.stderr}") |
| return False |
| print(" β
Virtual environment created") |
| except Exception as e: |
| print(f" β Error creating virtual environment: {e}") |
| return False |
| else: |
| print("π§ Using existing virtual environment...") |
|
|
| |
| if os.name == 'nt': |
| self.venv_python = str(venv_path / "Scripts" / "python.exe") |
| self.venv_pip = str(venv_path / "Scripts" / "pip.exe") |
| else: |
| self.venv_python = str(venv_path / "bin" / "python") |
| self.venv_pip = str(venv_path / "bin" / "pip") |
|
|
| |
| try: |
| result = subprocess.run([self.venv_python, '--version'], capture_output=True, text=True) |
| if result.returncode == 0: |
| print(f" β
Virtual environment Python: {result.stdout.strip()}") |
| return True |
| else: |
| print(" β Virtual environment Python not working") |
| return False |
| except Exception as e: |
| print(f" β Error testing virtual environment: {e}") |
| return False |
|
|
| def install_package(self, package_spec): |
| """Install a package using pip (with virtual environment support)""" |
| try: |
| print(f" Installing {package_spec}...") |
|
|
| |
| pip_executable = getattr(self, 'venv_pip', None) |
| if pip_executable and Path(pip_executable).exists(): |
| cmd = [pip_executable, 'install', package_spec] |
| else: |
| cmd = [sys.executable, '-m', 'pip', 'install', package_spec] |
|
|
| result = subprocess.run( |
| cmd, |
| capture_output=True, |
| text=True, |
| timeout=300 |
| ) |
|
|
| if result.returncode == 0: |
| print(f" β
Successfully installed {package_spec}") |
| return True |
| else: |
| print(f" β Failed to install {package_spec}") |
| print(f" Error: {result.stderr}") |
|
|
| |
| if "externally-managed-environment" in result.stderr and not hasattr(self, 'venv_python'): |
| print(" π Detected externally managed environment, setting up virtual environment...") |
| if self.setup_virtual_environment(): |
| |
| return self.install_package(package_spec) |
|
|
| return False |
|
|
| except subprocess.TimeoutExpired: |
| print(f" β° Installation of {package_spec} timed out") |
| return False |
| except Exception as e: |
| print(f" β Error installing {package_spec}: {str(e)}") |
| return False |
|
|
| def check_and_install_requirements(self): |
| """Check and install all required packages""" |
| print("π¦ Checking package requirements...") |
|
|
| missing_packages = [] |
| outdated_packages = [] |
| optional_missing = [] |
|
|
| |
| for package_name, info in self.required_packages.items(): |
| is_installed, current_version = self.check_package_installed(package_name) |
| min_version = info['min_version'] |
| is_optional = info.get('optional', False) |
|
|
| if not is_installed: |
| if is_optional: |
| optional_missing.append((package_name, info)) |
| print(f" β οΈ Optional package missing: {package_name}") |
| else: |
| missing_packages.append((package_name, info)) |
| print(f" β Missing required package: {package_name}") |
| elif current_version and not self.compare_versions(current_version, min_version): |
| if is_optional: |
| print(f" β οΈ Optional package outdated: {package_name} {current_version} < {min_version}") |
| else: |
| outdated_packages.append((package_name, info)) |
| print(f" β Outdated package: {package_name} {current_version} < {min_version}") |
| else: |
| status = "β
" if not is_optional else "π§" |
| print(f" {status} {package_name}: {current_version}") |
|
|
| |
| if missing_packages or outdated_packages: |
| print(f"\nπ§ Installing {len(missing_packages + outdated_packages)} required packages...") |
|
|
| for package_name, info in missing_packages + outdated_packages: |
| install_spec = info['install_name'] |
| if not self.install_package(install_spec): |
| print(f"β Critical error: Failed to install {package_name}") |
| return False |
|
|
| |
| print("π€ Checking ChatterboxTTS installation...") |
| try: |
| import chatterbox |
| print(" β
ChatterboxTTS already installed") |
| except ImportError: |
| print(" π₯ Installing ChatterboxTTS from GitHub...") |
| if not self.install_package(self.chatterbox_git_url): |
| print(" β οΈ ChatterboxTTS installation failed - some features may not work") |
|
|
| |
| if optional_missing: |
| print(f"\nπ― Installing {len(optional_missing)} optional packages...") |
| for package_name, info in optional_missing: |
| install_spec = info['install_name'] |
| if self.install_package(install_spec): |
| print(f" β
Optional package {package_name} installed successfully") |
| else: |
| print(f" β οΈ Optional package {package_name} failed - voice analysis may be limited") |
|
|
| return True |
|
|
| def check_gpu_availability(self): |
| """Check for GPU availability""" |
| print("π₯οΈ Checking GPU availability...") |
|
|
| try: |
| import torch |
| if torch.cuda.is_available(): |
| gpu_count = torch.cuda.device_count() |
| gpu_name = torch.cuda.get_device_name(0) |
| print(f" β
CUDA GPU available: {gpu_name} ({gpu_count} device{'s' if gpu_count > 1 else ''})") |
| return True |
| elif hasattr(torch.backends, 'mps') and torch.backends.mps.is_available(): |
| print(" β
Apple Metal Performance Shaders (MPS) available") |
| return True |
| else: |
| print(" β οΈ No GPU acceleration available - using CPU") |
| print(" π‘ For better performance, consider using a GPU-enabled environment") |
| return False |
| except Exception as e: |
| print(f" β Error checking GPU: {str(e)}") |
| return False |
|
|
| def verify_installation(self): |
| """Verify that all components can be imported""" |
| print("π Verifying installation...") |
|
|
| critical_imports = [ |
| ('gradio', 'Gradio web interface'), |
| ('torch', 'PyTorch machine learning'), |
| ('transformers', 'Hugging Face transformers'), |
| ('librosa', 'Audio processing'), |
| ('soundfile', 'Audio file I/O'), |
| ('numpy', 'Numerical computing'), |
| ('matplotlib', 'Plotting and visualization') |
| ] |
|
|
| optional_imports = [ |
| ('parselmouth', 'Praat voice analysis'), |
| ('scipy', 'Scientific computing'), |
| ('psutil', 'System monitoring') |
| ] |
|
|
| failed_critical = [] |
| failed_optional = [] |
|
|
| |
| for module_name, description in critical_imports: |
| try: |
| importlib.import_module(module_name) |
| print(f" β
{description}") |
| except ImportError as e: |
| print(f" β {description}: {str(e)}") |
| failed_critical.append(module_name) |
|
|
| |
| for module_name, description in optional_imports: |
| try: |
| importlib.import_module(module_name) |
| print(f" π§ {description}") |
| except ImportError: |
| print(f" β οΈ {description}: Not available") |
| failed_optional.append(module_name) |
|
|
| if failed_critical: |
| print(f"\nβ Critical imports failed: {', '.join(failed_critical)}") |
| print(" The interface may not work properly") |
| return False |
|
|
| if failed_optional: |
| print(f"\nβ οΈ Optional features unavailable: {', '.join(failed_optional)}") |
| print(" Voice analysis features may be limited") |
|
|
| print("β
Installation verification complete") |
| return True |
|
|
| def launch_interface(self): |
| """Launch the Gradio interface""" |
| print("\nπ Launching ChatterboxTTS Gradio Interface...") |
| print("-" * 50) |
|
|
| |
| if hasattr(self, 'venv_python') and Path(self.venv_python).exists(): |
| print("π§ Using virtual environment Python...") |
| try: |
| print("π Starting web server...") |
| print("π± Interface will be available in your browser") |
| print("π Default URL: http://localhost:7860") |
|
|
| if os.getenv("RUNPOD_POD_ID"): |
| print("βοΈ RunPod deployment detected") |
| elif os.getenv("COLAB_GPU"): |
| print("βοΈ Google Colab detected - sharing link will be generated") |
|
|
| print("\n" + "=" * 50) |
| print("π LAUNCHING CHATTERBOX TTS!") |
| print("=" * 50) |
|
|
| |
| subprocess.run([self.venv_python, "gradio_main_interface.py"]) |
|
|
| except KeyboardInterrupt: |
| print("\n\nπ Shutdown requested by user") |
| print(" Thanks for using ChatterboxTTS!") |
| sys.exit(0) |
| except Exception as e: |
| print(f"\nβ Error launching with virtual environment: {str(e)}") |
| print(" Falling back to direct import...") |
| self._launch_direct() |
| else: |
| self._launch_direct() |
|
|
| def _launch_direct(self): |
| """Launch interface by direct import""" |
| try: |
| |
| from gradio_main_interface import launch_interface |
|
|
| print("π Starting web server...") |
| print("π± Interface will be available in your browser") |
| print("π Default URL: http://localhost:7860") |
|
|
| if os.getenv("RUNPOD_POD_ID"): |
| print("βοΈ RunPod deployment detected") |
| elif os.getenv("COLAB_GPU"): |
| print("βοΈ Google Colab detected - sharing link will be generated") |
|
|
| print("\n" + "=" * 50) |
| print("π LAUNCHING CHATTERBOX TTS!") |
| print("=" * 50) |
|
|
| |
| time.sleep(2) |
|
|
| |
| launch_interface() |
|
|
| except KeyboardInterrupt: |
| print("\n\nπ Shutdown requested by user") |
| print(" Thanks for using ChatterboxTTS!") |
| sys.exit(0) |
| except Exception as e: |
| print(f"\nβ Error launching interface: {str(e)}") |
| print("\nTroubleshooting tips:") |
| print("1. Check that all dependencies are installed") |
| print("2. Verify you're in the correct directory") |
| if hasattr(self, 'venv_python'): |
| print(f"3. Try running: {self.venv_python} gradio_main_interface.py") |
| else: |
| print("3. Try running: python3 gradio_main_interface.py") |
| sys.exit(1) |
|
|
| def run(self): |
| """Run the complete launcher process""" |
| self.print_header() |
|
|
| |
| self.check_python_version() |
|
|
| |
| if not self.check_working_directory(): |
| sys.exit(1) |
|
|
| |
| self.create_directories() |
|
|
| |
| if not self.check_and_install_requirements(): |
| print("\nβ Failed to install required packages") |
| sys.exit(1) |
|
|
| |
| self.check_gpu_availability() |
|
|
| |
| if not self.verify_installation(): |
| print("\nβ οΈ Installation verification failed") |
| print(" Proceeding anyway - some features may not work") |
|
|
| |
| self.launch_interface() |
|
|
| def main(): |
| """Main entry point""" |
| launcher = GradioLauncher() |
| launcher.run() |
|
|
| if __name__ == "__main__": |
| |
| import sys |
| import os |
| sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), 'src')) |
| |
| |
| os.environ["OMP_NUM_THREADS"] = "1" |
|
|
| |
| try: |
| |
| import gradio_main_interface |
|
|
| |
| demo = gradio_main_interface.create_main_interface() |
| demo.launch( |
| server_name="0.0.0.0", |
| server_port=7860, |
| share=False, |
| show_error=True |
| ) |
| except ImportError as e: |
| print(f"β Failed to import gradio_main_interface: {e}") |
| |
| launcher = GradioLauncher() |
| launcher.launch_interface() |
|
|