diff --git a/install_and_run_windows.py b/install_and_run_windows.py new file mode 100644 index 0000000..18b08cd --- /dev/null +++ b/install_and_run_windows.py @@ -0,0 +1,306 @@ +import os +import subprocess +import sys +import time +import shutil +import requests +import traceback +import logging + +# Configure logging +logging.basicConfig( + level=logging.DEBUG, + format='%(asctime)s - %(levelname)s - %(message)s', + handlers=[ + logging.FileHandler('install_log.txt'), + logging.StreamHandler(sys.stdout) + ] +) + +def run_command(command): + try: + logging.info(f"Running command: {' '.join(command)}") + result = subprocess.run(command, check=True, capture_output=True, text=True) + logging.debug(f"Command output: {result.stdout}") + return result.stdout + except subprocess.CalledProcessError as e: + logging.error(f"Error executing command: {command}") + logging.error(f"Error message: {str(e)}") + logging.error(f"Error output: {e.stderr}") + raise + +def check_program_installed(program): + logging.info(f"Checking if {program} is installed...") + try: + result = shutil.which(program) + logging.info(f"{program} is {'installed' if result else 'not installed'}") + return result is not None + except Exception as e: + logging.error(f"Error checking if {program} is installed: {str(e)}") + logging.error(traceback.format_exc()) + raise + +def check_choco(): + return check_program_installed('choco') + +def install_choco(): + logging.info("Installing Chocolatey...") + try: + run_command(['powershell', '-Command', "Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))"]) + except Exception as e: + logging.error(f"Error installing Chocolatey: {str(e)}") + logging.error(traceback.format_exc()) + raise + +def install_dependencies(): + dependencies = ['git', 'curl', 'ffmpeg'] + missing_dependencies = [dep for dep in dependencies if not check_program_installed(dep)] + + if missing_dependencies: + if not check_choco(): + logging.info("Chocolatey is not installed.") + install_choco() + + for dependency in missing_dependencies: + logging.info(f"Installing {dependency}...") + try: + run_command(['choco', 'install', dependency, '-y']) + except subprocess.CalledProcessError as e: + logging.error(f"Failed to install {dependency}.") + raise + else: + logging.info("All dependencies are already installed.") + +def install_conda(install_path): + logging.info("Installing Miniconda...") + try: + conda_installer = 'Miniconda3-latest-Windows-x86_64.exe' + run_command(['curl', '-O', f'https://repo.anaconda.com/miniconda/{conda_installer}']) + run_command([conda_installer, '/InstallationType=JustMe', '/RegisterPython=0', '/S', f'/D={install_path}']) + os.remove(conda_installer) + except Exception as e: + logging.error(f"Error installing Miniconda: {str(e)}") + logging.error(traceback.format_exc()) + raise + +def check_conda(conda_path): + logging.info(f"Checking if conda is installed at {conda_path}...") + try: + conda_exe = os.path.join(conda_path, 'Scripts', 'conda.exe') + result = os.path.exists(conda_exe) + logging.info(f"Conda is {'installed' if result else 'not installed'} at {conda_path}") + return result + except Exception as e: + logging.error(f"Error checking if conda is installed at {conda_path}: {str(e)}") + logging.error(traceback.format_exc()) + raise + +def create_conda_env(conda_path, env_name, python_version): + logging.info(f"Creating conda environment {env_name}...") + try: + run_command([f'{conda_path}\\Scripts\\conda.exe', 'create', '-n', env_name, f'python={python_version}', '-y']) + except subprocess.CalledProcessError as e: + logging.error(f"Failed to create conda environment {env_name}") + logging.error(f"Error message: {str(e)}") + raise + +def install_requirements(conda_path, env_name, requirements_file): + logging.info(f"Installing requirements for {env_name} from {requirements_file}...") + try: + run_command([f'{conda_path}\\Scripts\\conda.exe', 'run', '-n', env_name, 'pip', 'install', '-r', requirements_file]) + except subprocess.CalledProcessError as e: + logging.error(f"Failed to install requirements for {env_name} from {requirements_file}") + logging.error(f"Error message: {str(e)}") + raise + +def install_voicecraft_api_dependencies(conda_path, env_name): + logging.info(f"Installing VoiceCraft API dependencies in {env_name}...") + try: + run_command([f'{conda_path}\\Scripts\\conda.exe', 'run', '-n', env_name, 'conda', 'install', 'pytorch==2.0.1', 'torchvision==0.15.2', 'torchaudio==2.0.2', 'pytorch-cuda=11.7', '-c', 'pytorch', '-c', 'nvidia', '-y']) + run_command([f'{conda_path}\\Scripts\\conda.exe', 'run', '-n', env_name, 'conda', 'install', '-c', 'conda-forge', 'montreal-forced-aligner=2.2.17', 'openfst=1.8.2', 'kaldi=5.5.1068', '-y']) + except subprocess.CalledProcessError as e: + logging.error(f"Failed to install VoiceCraft API dependencies in {env_name}") + logging.error(f"Error message: {str(e)}") + raise + +def download_mfa_models(conda_path, env_name): + logging.info(f"Downloading MFA models in {env_name}...") + try: + run_command([f'{conda_path}\\Scripts\\conda.exe', 'run', '-n', env_name, 'mfa', 'model', 'download', 'dictionary', 'english_us_arpa']) + run_command([f'{conda_path}\\Scripts\\conda.exe', 'run', '-n', env_name, 'mfa', 'model', 'download', 'acoustic', 'english_us_arpa']) + except subprocess.CalledProcessError as e: + logging.error(f"Failed to download MFA models in {env_name}") + logging.error(f"Error message: {str(e)}") + raise + +def install_audiocraft(conda_path, env_name, voicecraft_repo_path): + logging.info(f"Installing audiocraft package in {env_name}...") + try: + audiocraft_repo = 'https://github.com/facebookresearch/audiocraft.git' + audiocraft_commit = 'c5157b5bf14bf83449c17ea1eeb66c19fb4bc7f0' + + # Change to the VoiceCraft repository directory + os.chdir(voicecraft_repo_path) + + # Install audiocraft package + run_command([f'{conda_path}\\Scripts\\conda.exe', 'run', '-n', env_name, 'pip', 'install', '-e', f'git+{audiocraft_repo}@{audiocraft_commit}#egg=audiocraft']) + except subprocess.CalledProcessError as e: + logging.error(f"Failed to install audiocraft package in {env_name}") + logging.error(f"Error message: {str(e)}") + raise + +def run_voicecraft_api_server(conda_path, env_name, api_script_path, voicecraft_repo_path): + logging.info(f"Running VoiceCraft API server in {env_name}...") + try: + # Change to the VoiceCraft repository directory + os.chdir(voicecraft_repo_path) + + voicecraft_server_command = [f'{conda_path}\\Scripts\\conda.exe', 'run', '-n', env_name, 'python', api_script_path] + subprocess.Popen(voicecraft_server_command, creationflags=subprocess.CREATE_NEW_CONSOLE) + except Exception as e: + logging.error(f"Failed to run VoiceCraft API server in {env_name}") + logging.error(f"Error message: {str(e)}") + logging.error(traceback.format_exc()) + raise + +def check_voicecraft_server_online(url, max_attempts=30, wait_interval=10): + attempt = 1 + while attempt <= max_attempts: + try: + logging.info(f"Checking if VoiceCraft server is online at {url} (Attempt {attempt}/{max_attempts})...") + response = requests.get(url) + if response.status_code == 200: + logging.info("VoiceCraft server is online.") + return True + except requests.exceptions.RequestException as e: + logging.warning(f"VoiceCraft server is not online. Waiting... (Attempt {attempt}/{max_attempts})") + + time.sleep(wait_interval) + attempt += 1 + + logging.error("VoiceCraft server failed to come online within the specified attempts.") + return False + +def download_pretrained_models(repo_path): + pretrained_models_dir = os.path.join(repo_path, 'pretrained_models') + os.makedirs(pretrained_models_dir, exist_ok=True) + + encodec_url = 'https://huggingface.co/pyp1/VoiceCraft/resolve/main/encodec_4cb2048_giga.th' + giga330m_url = 'https://huggingface.co/pyp1/VoiceCraft/resolve/main/gigaHalfLibri330M_TTSEnhanced_max16s.pth' + + encodec_path = os.path.join(pretrained_models_dir, 'encodec_4cb2048_giga.th') + giga330m_path = os.path.join(pretrained_models_dir, 'gigaHalfLibri330M_TTSEnhanced_max16s.pth') + + if not os.path.exists(encodec_path): + logging.info("Downloading encodec_4cb2048_giga.th...") + try: + run_command(['curl', '-L', encodec_url, '-o', encodec_path]) + except subprocess.CalledProcessError as e: + logging.error(f"Failed to download encodec_4cb2048_giga.th") + logging.error(f"Error message: {str(e)}") + raise + else: + logging.info("encodec_4cb2048_giga.th already exists. Skipping download.") + + if not os.path.exists(giga330m_path): + logging.info("Downloading gigaHalfLibri330M_TTSEnhanced_max16s.pth...") + try: + run_command(['curl', '-L', giga330m_url, '-o', giga330m_path]) + except subprocess.CalledProcessError as e: + logging.error(f"Failed to download giga330M.pth") + logging.error(f"Error message: {str(e)}") + raise + else: + logging.info("giga330M.pth already exists. Skipping download.") + +def replace_files(repo_path, file_mappings): + for src_file, dest_file in file_mappings.items(): + src_path = os.path.join(repo_path, src_file) + dest_path = os.path.join(repo_path, dest_file) + try: + shutil.copy2(src_path, dest_path) + logging.info(f"Replaced file: {dest_file}") + except Exception as e: + logging.error(f"Failed to replace file: {dest_file}") + logging.error(f"Error message: {str(e)}") + logging.error(traceback.format_exc()) + raise + +def main(): + voicecraft_path = os.path.join(os.getcwd(), 'VoiceCraft_API') + + if not os.path.exists(voicecraft_path): + # Check and install dependencies + install_dependencies() + + os.makedirs(voicecraft_path, exist_ok=True) + logging.info(f"Created VoiceCraft_API folder at {voicecraft_path}") + + # Clone repository + logging.info("Cloning VoiceCraft_API repository...") + try: + run_command(['git', 'clone', 'https://github.com/lukaszliniewicz/VoiceCraft_API.git', voicecraft_path]) + except subprocess.CalledProcessError as e: + logging.error(f"Failed to clone VoiceCraft_API repository") + logging.error(f"Error message: {str(e)}") + raise + + # Install Miniconda + conda_path = os.path.join(voicecraft_path, 'conda') + install_conda(conda_path) + + # Check if conda is installed correctly + if not check_conda(conda_path): + logging.error("Conda installation failed. Please check the installation logs.") + return + + voicecraft_env_name = 'voicecraft_api_installer' + + if not os.path.exists(os.path.join(conda_path, 'envs', voicecraft_env_name)): + # Create voicecraft_api_installer environment + create_conda_env(conda_path, voicecraft_env_name, '3.9.16') + + # Install VoiceCraft API dependencies + install_requirements(conda_path, voicecraft_env_name, os.path.join(voicecraft_path, 'requirements.txt')) + install_voicecraft_api_dependencies(conda_path, voicecraft_env_name) + download_mfa_models(conda_path, voicecraft_env_name) + + # Install audiocraft package + install_audiocraft(conda_path, voicecraft_env_name, voicecraft_path) + else: + logging.info(f"Environment {voicecraft_env_name} already exists. Skipping installation.") + + # Replace files in the VoiceCraft repo + file_mappings = { + 'audiocraft_windows/cluster.py': 'src/audiocraft/audiocraft/utils/cluster.py', + 'audiocraft_windows/environment.py': 'src/audiocraft/audiocraft/environment.py', + 'audiocraft_windows/checkpoint.py': 'src/audiocraft/audiocraft/utils/checkpoint.py' + } + replace_files(voicecraft_path, file_mappings) + + # Download pretrained models + download_pretrained_models(voicecraft_path) + else: + logging.info("VoiceCraft_API folder exists. Skipping installation steps.") + + # Get the conda path + conda_path = os.path.join(voicecraft_path, 'conda') + + # Run VoiceCraft API server + api_script_path = os.path.join(voicecraft_path, 'api.py') + run_voicecraft_api_server(conda_path, 'voicecraft_api_installer', api_script_path, voicecraft_path) + + # Wait for VoiceCraft server to come online + voicecraft_server_url = 'http://127.0.0.1:8245/docs' + if not check_voicecraft_server_online(voicecraft_server_url): + logging.error("VoiceCraft server failed to come online. Exiting...") + return + + logging.info("VoiceCraft API installation and server startup completed successfully.") + +if __name__ == '__main__': + try: + main() + except Exception as e: + logging.error(f"An error occurred during execution: {str(e)}") + logging.error(traceback.format_exc()) \ No newline at end of file