""" Aniworld Application Setup Script This script handles initial setup, dependency installation, database initialization, and configuration for the Aniworld application. Usage: python setup.py [--environment {development|production}] [--no-deps] python setup.py --help """ import argparse import asyncio import os import subprocess import sys from pathlib import Path class SetupManager: """Manages application setup and initialization.""" def __init__( self, environment: str = "development", skip_deps: bool = False ): """ Initialize setup manager. Args: environment: Environment mode (development or production) skip_deps: Skip dependency installation """ self.environment = environment self.skip_deps = skip_deps self.project_root = Path(__file__).parent.parent self.conda_env = "AniWorld" # ============================================================================ # Logging # ============================================================================ @staticmethod def log_info(message: str) -> None: """Log info message.""" print(f"\033[34m[INFO]\033[0m {message}") @staticmethod def log_success(message: str) -> None: """Log success message.""" print(f"\033[32m[SUCCESS]\033[0m {message}") @staticmethod def log_warning(message: str) -> None: """Log warning message.""" print(f"\033[33m[WARNING]\033[0m {message}") @staticmethod def log_error(message: str) -> None: """Log error message.""" print(f"\033[31m[ERROR]\033[0m {message}") # ============================================================================ # Validation # ============================================================================ def validate_environment(self) -> bool: """ Validate environment parameter. Returns: True if valid, False otherwise """ valid_envs = {"development", "production", "testing"} if self.environment not in valid_envs: self.log_error( f"Invalid environment: {self.environment}. " f"Must be one of: {valid_envs}" ) return False self.log_success(f"Environment: {self.environment}") return True def check_conda_env(self) -> bool: """ Check if conda environment exists. Returns: True if exists, False otherwise """ result = subprocess.run( ["conda", "env", "list"], capture_output=True, text=True ) if self.conda_env in result.stdout: self.log_success(f"Conda environment '{self.conda_env}' found") return True self.log_error( f"Conda environment '{self.conda_env}' not found. " f"Create with: conda create -n {self.conda_env} python=3.11" ) return False def check_python_version(self) -> bool: """ Check Python version. Returns: True if version >= 3.9, False otherwise """ if sys.version_info < (3, 9): self.log_error( f"Python 3.9+ required. Current: {sys.version_info.major}." f"{sys.version_info.minor}" ) return False self.log_success( f"Python version: {sys.version_info.major}." f"{sys.version_info.minor}" ) return True # ============================================================================ # Directory Setup # ============================================================================ def create_directories(self) -> bool: """ Create necessary directories. Returns: True if successful, False otherwise """ try: directories = [ "logs", "data", "data/config_backups", "Temp", "tests", "scripts", ] self.log_info("Creating directories...") for directory in directories: dir_path = self.project_root / directory dir_path.mkdir(parents=True, exist_ok=True) self.log_success("Directories created") return True except Exception as e: self.log_error(f"Failed to create directories: {e}") return False # ============================================================================ # Dependency Installation # ============================================================================ def install_dependencies(self) -> bool: """ Install Python dependencies. Returns: True if successful, False otherwise """ if self.skip_deps: self.log_warning("Skipping dependency installation") return True try: requirements_file = self.project_root / "requirements.txt" if not requirements_file.exists(): self.log_error( f"requirements.txt not found at {requirements_file}" ) return False self.log_info("Installing dependencies...") subprocess.run( ["conda", "run", "-n", self.conda_env, "pip", "install", "-q", "-r", str(requirements_file)], check=True ) self.log_success("Dependencies installed") return True except subprocess.CalledProcessError as e: self.log_error(f"Failed to install dependencies: {e}") return False # ============================================================================ # Environment Configuration # ============================================================================ def create_env_files(self) -> bool: """ Create environment configuration files. Returns: True if successful, False otherwise """ try: self.log_info("Creating environment configuration files...") env_file = self.project_root / f".env.{self.environment}" if env_file.exists(): self.log_warning(f"{env_file.name} already exists") return True # Create environment file with defaults env_content = self._get_env_template() env_file.write_text(env_content) self.log_success(f"Created {env_file.name}") return True except Exception as e: self.log_error(f"Failed to create env files: {e}") return False def _get_env_template(self) -> str: """ Get environment file template. Returns: Environment file content """ if self.environment == "production": return """# Aniworld Production Configuration # IMPORTANT: Set these values before running in production # Security (REQUIRED - generate new values) JWT_SECRET_KEY=change-this-to-a-secure-random-key PASSWORD_SALT=change-this-to-a-secure-random-salt MASTER_PASSWORD_HASH=change-this-to-hashed-password # Database (REQUIRED - use PostgreSQL or MySQL in production) DATABASE_URL=postgresql://user:password@localhost/aniworld DATABASE_POOL_SIZE=20 DATABASE_MAX_OVERFLOW=10 # Application ENVIRONMENT=production ANIME_DIRECTORY=/var/lib/aniworld TEMP_DIRECTORY=/tmp/aniworld # Server HOST=0.0.0.0 PORT=8000 WORKERS=4 # Security CORS_ORIGINS=https://yourdomain.com ALLOWED_HOSTS=yourdomain.com # Logging LOG_LEVEL=WARNING LOG_FILE=logs/production.log LOG_ROTATION_SIZE=10485760 LOG_RETENTION_DAYS=30 # Performance API_RATE_LIMIT=60 SESSION_TIMEOUT_HOURS=24 MAX_CONCURRENT_DOWNLOADS=3 """ else: # development return """# Aniworld Development Configuration # Security (Development defaults - NOT for production) JWT_SECRET_KEY=dev-secret-key-change-in-production PASSWORD_SALT=dev-salt-change-in-production MASTER_PASSWORD_HASH=$2b$12$wP0KBVbJKVAb8CdSSXw0NeGTKCkbw4fSAFXIqR2/wDqPSEBn9w7lS MASTER_PASSWORD=password # Database DATABASE_URL=sqlite:///./data/aniworld_dev.db # Application ENVIRONMENT=development ANIME_DIRECTORY=/tmp/aniworld_dev TEMP_DIRECTORY=/tmp/aniworld_dev/temp # Server HOST=127.0.0.1 PORT=8000 WORKERS=1 # Security CORS_ORIGINS=* # Logging LOG_LEVEL=DEBUG LOG_FILE=logs/development.log # Performance API_RATE_LIMIT=1000 SESSION_TIMEOUT_HOURS=168 MAX_CONCURRENT_DOWNLOADS=1 """ # ============================================================================ # Database Initialization # ============================================================================ async def init_database(self) -> bool: """ Initialize database. Returns: True if successful, False otherwise """ try: self.log_info("Initializing database...") # Import and run database initialization os.chdir(self.project_root) from src.server.database import init_db await init_db() self.log_success("Database initialized") return True except Exception as e: self.log_error(f"Failed to initialize database: {e}") return False # ============================================================================ # Summary # ============================================================================ def print_summary(self) -> None: """Print setup summary.""" self.log_info("=" * 50) self.log_info("Setup Summary") self.log_info("=" * 50) self.log_info(f"Environment: {self.environment}") self.log_info(f"Conda Environment: {self.conda_env}") self.log_info(f"Project Root: {self.project_root}") self.log_info("") self.log_success("Setup complete!") self.log_info("") self.log_info("Next steps:") self.log_info("1. Configure .env files with your settings") if self.environment == "production": self.log_info("2. Set up database (PostgreSQL/MySQL)") self.log_info("3. Configure security settings") self.log_info("4. Run: ./scripts/start.sh production") else: self.log_info("2. Run: ./scripts/start.sh development") self.log_info("") # ============================================================================ # Main Setup # ============================================================================ async def run(self) -> int: """ Run setup process. Returns: 0 if successful, 1 otherwise """ print("\033[34m" + "=" * 50 + "\033[0m") print("\033[34mAniworld Application Setup\033[0m") print("\033[34m" + "=" * 50 + "\033[0m") print() # Validation if not self.validate_environment(): return 1 if not self.check_python_version(): return 1 if not self.check_conda_env(): return 1 # Setup if not self.create_directories(): return 1 if not self.create_env_files(): return 1 if not self.install_dependencies(): return 1 # Initialize database if not await self.init_database(): return 1 # Summary self.print_summary() return 0 async def main() -> int: """ Main entry point. Returns: Exit code """ parser = argparse.ArgumentParser( description="Aniworld Application Setup" ) parser.add_argument( "--environment", choices=["development", "production", "testing"], default="development", help="Environment to set up (default: development)" ) parser.add_argument( "--no-deps", action="store_true", help="Skip dependency installation" ) args = parser.parse_args() setup = SetupManager( environment=args.environment, skip_deps=args.no_deps ) return await setup.run() if __name__ == "__main__": exit_code = asyncio.run(main()) sys.exit(exit_code)