Spaces:
Sleeping
Sleeping
| """Application configuration management.""" | |
| import os | |
| import logging | |
| from typing import Dict, List, Optional, Any | |
| from dataclasses import dataclass, field | |
| from pathlib import Path | |
| logger = logging.getLogger(__name__) | |
| class TTSConfig: | |
| """Configuration for TTS providers.""" | |
| preferred_providers: List[str] = field(default_factory=lambda: ['chatterbox']) | |
| default_voice: str = 'default' | |
| default_speed: float = 1.0 | |
| default_language: str = 'en' | |
| enable_streaming: bool = True | |
| max_text_length: int = 10000 | |
| class STTConfig: | |
| """Configuration for STT providers.""" | |
| preferred_providers: List[str] = field(default_factory=lambda: ['whisper']) | |
| default_model: str = 'whisper' | |
| chunk_length_s: int = 30 | |
| batch_size: int = 16 | |
| enable_vad: bool = True | |
| class TranslationConfig: | |
| """Configuration for translation providers.""" | |
| default_provider: str = 'nllb' | |
| model_name: str = 'facebook/nllb-200-3.3B' | |
| max_chunk_length: int = 1000 | |
| batch_size: int = 8 | |
| cache_translations: bool = True | |
| class ProcessingConfig: | |
| """Configuration for audio processing pipeline.""" | |
| temp_dir: str = '/tmp/audio_processing' | |
| cleanup_temp_files: bool = True | |
| max_file_size_mb: int = 100 | |
| supported_audio_formats: List[str] = field(default_factory=lambda: ['wav', 'mp3', 'flac', 'ogg']) | |
| processing_timeout_seconds: int = 300 | |
| class LoggingConfig: | |
| """Configuration for logging.""" | |
| level: str = 'INFO' | |
| format: str = '%(asctime)s - %(name)s - %(levelname)s - %(message)s' | |
| enable_file_logging: bool = False | |
| log_file_path: str = 'app.log' | |
| max_log_file_size_mb: int = 10 | |
| backup_count: int = 5 | |
| class AppConfig: | |
| """Centralized application configuration management.""" | |
| def __init__(self, config_file: Optional[str] = None): | |
| """ | |
| Initialize application configuration. | |
| Args: | |
| config_file: Optional path to configuration file | |
| """ | |
| self.config_file = config_file | |
| self._config_data: Dict[str, Any] = {} | |
| # Initialize configuration sections | |
| self.tts = TTSConfig() | |
| self.stt = STTConfig() | |
| self.translation = TranslationConfig() | |
| self.processing = ProcessingConfig() | |
| self.logging = LoggingConfig() | |
| # Load configuration | |
| self._load_configuration() | |
| def _load_configuration(self) -> None: | |
| """Load configuration from environment variables and config file.""" | |
| try: | |
| # Load from environment variables first | |
| self._load_from_environment() | |
| # Load from config file if provided | |
| if self.config_file and os.path.exists(self.config_file): | |
| self._load_from_file() | |
| # Validate configuration | |
| self._validate_configuration() | |
| logger.info("Configuration loaded successfully") | |
| except Exception as e: | |
| logger.error(f"Failed to load configuration: {e}") | |
| # Use default configuration | |
| logger.info("Using default configuration") | |
| def _load_from_environment(self) -> None: | |
| """Load configuration from environment variables.""" | |
| # TTS Configuration | |
| if os.getenv('TTS_PREFERRED_PROVIDERS'): | |
| self.tts.preferred_providers = os.getenv('TTS_PREFERRED_PROVIDERS').split(',') | |
| if os.getenv('TTS_DEFAULT_VOICE'): | |
| self.tts.default_voice = os.getenv('TTS_DEFAULT_VOICE') | |
| if os.getenv('TTS_DEFAULT_SPEED'): | |
| self.tts.default_speed = float(os.getenv('TTS_DEFAULT_SPEED')) | |
| if os.getenv('TTS_DEFAULT_LANGUAGE'): | |
| self.tts.default_language = os.getenv('TTS_DEFAULT_LANGUAGE') | |
| if os.getenv('TTS_ENABLE_STREAMING'): | |
| self.tts.enable_streaming = os.getenv('TTS_ENABLE_STREAMING').lower() == 'true' | |
| # STT Configuration | |
| if os.getenv('STT_PREFERRED_PROVIDERS'): | |
| self.stt.preferred_providers = os.getenv('STT_PREFERRED_PROVIDERS').split(',') | |
| if os.getenv('STT_DEFAULT_MODEL'): | |
| self.stt.default_model = os.getenv('STT_DEFAULT_MODEL') | |
| if os.getenv('STT_CHUNK_LENGTH'): | |
| self.stt.chunk_length_s = int(os.getenv('STT_CHUNK_LENGTH')) | |
| if os.getenv('STT_BATCH_SIZE'): | |
| self.stt.batch_size = int(os.getenv('STT_BATCH_SIZE')) | |
| # Translation Configuration | |
| if os.getenv('TRANSLATION_DEFAULT_PROVIDER'): | |
| self.translation.default_provider = os.getenv('TRANSLATION_DEFAULT_PROVIDER') | |
| if os.getenv('TRANSLATION_MODEL_NAME'): | |
| self.translation.model_name = os.getenv('TRANSLATION_MODEL_NAME') | |
| if os.getenv('TRANSLATION_MAX_CHUNK_LENGTH'): | |
| self.translation.max_chunk_length = int(os.getenv('TRANSLATION_MAX_CHUNK_LENGTH')) | |
| # Processing Configuration | |
| if os.getenv('PROCESSING_TEMP_DIR'): | |
| self.processing.temp_dir = os.getenv('PROCESSING_TEMP_DIR') | |
| if os.getenv('PROCESSING_CLEANUP_TEMP_FILES'): | |
| self.processing.cleanup_temp_files = os.getenv('PROCESSING_CLEANUP_TEMP_FILES').lower() == 'true' | |
| if os.getenv('PROCESSING_MAX_FILE_SIZE_MB'): | |
| self.processing.max_file_size_mb = int(os.getenv('PROCESSING_MAX_FILE_SIZE_MB')) | |
| if os.getenv('PROCESSING_TIMEOUT_SECONDS'): | |
| self.processing.processing_timeout_seconds = int(os.getenv('PROCESSING_TIMEOUT_SECONDS')) | |
| # Logging Configuration | |
| if os.getenv('LOG_LEVEL'): | |
| self.logging.level = os.getenv('LOG_LEVEL') | |
| if os.getenv('LOG_FORMAT'): | |
| self.logging.format = os.getenv('LOG_FORMAT') | |
| if os.getenv('LOG_FILE_PATH'): | |
| self.logging.log_file_path = os.getenv('LOG_FILE_PATH') | |
| def _load_from_file(self) -> None: | |
| """Load configuration from file (JSON or YAML).""" | |
| try: | |
| import json | |
| with open(self.config_file, 'r') as f: | |
| if self.config_file.endswith('.json'): | |
| self._config_data = json.load(f) | |
| elif self.config_file.endswith(('.yml', '.yaml')): | |
| try: | |
| import yaml | |
| self._config_data = yaml.safe_load(f) | |
| except ImportError: | |
| logger.warning("PyYAML not installed, cannot load YAML config file") | |
| return | |
| else: | |
| logger.warning(f"Unsupported config file format: {self.config_file}") | |
| return | |
| # Apply configuration from file | |
| self._apply_config_data() | |
| except Exception as e: | |
| logger.error(f"Failed to load config file {self.config_file}: {e}") | |
| def _apply_config_data(self) -> None: | |
| """Apply configuration data from loaded file.""" | |
| if not self._config_data: | |
| return | |
| # Apply TTS configuration | |
| tts_config = self._config_data.get('tts', {}) | |
| if 'preferred_providers' in tts_config: | |
| self.tts.preferred_providers = tts_config['preferred_providers'] | |
| if 'default_voice' in tts_config: | |
| self.tts.default_voice = tts_config['default_voice'] | |
| if 'default_speed' in tts_config: | |
| self.tts.default_speed = tts_config['default_speed'] | |
| if 'default_language' in tts_config: | |
| self.tts.default_language = tts_config['default_language'] | |
| if 'enable_streaming' in tts_config: | |
| self.tts.enable_streaming = tts_config['enable_streaming'] | |
| # Apply STT configuration | |
| stt_config = self._config_data.get('stt', {}) | |
| if 'preferred_providers' in stt_config: | |
| self.stt.preferred_providers = stt_config['preferred_providers'] | |
| if 'default_model' in stt_config: | |
| self.stt.default_model = stt_config['default_model'] | |
| if 'chunk_length_s' in stt_config: | |
| self.stt.chunk_length_s = stt_config['chunk_length_s'] | |
| if 'batch_size' in stt_config: | |
| self.stt.batch_size = stt_config['batch_size'] | |
| # Apply Translation configuration | |
| translation_config = self._config_data.get('translation', {}) | |
| if 'default_provider' in translation_config: | |
| self.translation.default_provider = translation_config['default_provider'] | |
| if 'model_name' in translation_config: | |
| self.translation.model_name = translation_config['model_name'] | |
| if 'max_chunk_length' in translation_config: | |
| self.translation.max_chunk_length = translation_config['max_chunk_length'] | |
| # Apply Processing configuration | |
| processing_config = self._config_data.get('processing', {}) | |
| if 'temp_dir' in processing_config: | |
| self.processing.temp_dir = processing_config['temp_dir'] | |
| if 'cleanup_temp_files' in processing_config: | |
| self.processing.cleanup_temp_files = processing_config['cleanup_temp_files'] | |
| if 'max_file_size_mb' in processing_config: | |
| self.processing.max_file_size_mb = processing_config['max_file_size_mb'] | |
| if 'processing_timeout_seconds' in processing_config: | |
| self.processing.processing_timeout_seconds = processing_config['processing_timeout_seconds'] | |
| # Apply Logging configuration | |
| logging_config = self._config_data.get('logging', {}) | |
| if 'level' in logging_config: | |
| self.logging.level = logging_config['level'] | |
| if 'format' in logging_config: | |
| self.logging.format = logging_config['format'] | |
| if 'log_file_path' in logging_config: | |
| self.logging.log_file_path = logging_config['log_file_path'] | |
| def _validate_configuration(self) -> None: | |
| """Validate configuration values.""" | |
| # Validate TTS configuration | |
| if not (0.1 <= self.tts.default_speed <= 3.0): | |
| logger.warning(f"Invalid TTS speed {self.tts.default_speed}, using default 1.0") | |
| self.tts.default_speed = 1.0 | |
| if self.tts.max_text_length <= 0: | |
| logger.warning(f"Invalid max text length {self.tts.max_text_length}, using default 10000") | |
| self.tts.max_text_length = 10000 | |
| # Validate STT configuration | |
| if self.stt.chunk_length_s <= 0: | |
| logger.warning(f"Invalid chunk length {self.stt.chunk_length_s}, using default 30") | |
| self.stt.chunk_length_s = 30 | |
| if self.stt.batch_size <= 0: | |
| logger.warning(f"Invalid batch size {self.stt.batch_size}, using default 16") | |
| self.stt.batch_size = 16 | |
| # Validate Translation configuration | |
| if self.translation.max_chunk_length <= 0: | |
| logger.warning(f"Invalid max chunk length {self.translation.max_chunk_length}, using default 1000") | |
| self.translation.max_chunk_length = 1000 | |
| # Validate Processing configuration | |
| if self.processing.max_file_size_mb <= 0: | |
| logger.warning(f"Invalid max file size {self.processing.max_file_size_mb}, using default 100") | |
| self.processing.max_file_size_mb = 100 | |
| if self.processing.processing_timeout_seconds <= 0: | |
| logger.warning(f"Invalid timeout {self.processing.processing_timeout_seconds}, using default 300") | |
| self.processing.processing_timeout_seconds = 300 | |
| # Ensure temp directory exists | |
| try: | |
| Path(self.processing.temp_dir).mkdir(parents=True, exist_ok=True) | |
| except Exception as e: | |
| logger.warning(f"Failed to create temp directory {self.processing.temp_dir}: {e}") | |
| self.processing.temp_dir = '/tmp/audio_processing' | |
| Path(self.processing.temp_dir).mkdir(parents=True, exist_ok=True) | |
| def get_tts_config(self) -> Dict[str, Any]: | |
| """Get TTS configuration as dictionary.""" | |
| return { | |
| 'preferred_providers': self.tts.preferred_providers, | |
| 'default_voice': self.tts.default_voice, | |
| 'default_speed': self.tts.default_speed, | |
| 'default_language': self.tts.default_language, | |
| 'enable_streaming': self.tts.enable_streaming, | |
| 'max_text_length': self.tts.max_text_length | |
| } | |
| def get_stt_config(self) -> Dict[str, Any]: | |
| """Get STT configuration as dictionary.""" | |
| return { | |
| 'preferred_providers': self.stt.preferred_providers, | |
| 'default_model': self.stt.default_model, | |
| 'chunk_length_s': self.stt.chunk_length_s, | |
| 'batch_size': self.stt.batch_size, | |
| 'enable_vad': self.stt.enable_vad | |
| } | |
| def get_translation_config(self) -> Dict[str, Any]: | |
| """Get translation configuration as dictionary.""" | |
| return { | |
| 'default_provider': self.translation.default_provider, | |
| 'model_name': self.translation.model_name, | |
| 'max_chunk_length': self.translation.max_chunk_length, | |
| 'batch_size': self.translation.batch_size, | |
| 'cache_translations': self.translation.cache_translations | |
| } | |
| def get_processing_config(self) -> Dict[str, Any]: | |
| """Get processing configuration as dictionary.""" | |
| return { | |
| 'temp_dir': self.processing.temp_dir, | |
| 'cleanup_temp_files': self.processing.cleanup_temp_files, | |
| 'max_file_size_mb': self.processing.max_file_size_mb, | |
| 'supported_audio_formats': self.processing.supported_audio_formats, | |
| 'processing_timeout_seconds': self.processing.processing_timeout_seconds | |
| } | |
| def get_logging_config(self) -> Dict[str, Any]: | |
| """Get logging configuration as dictionary.""" | |
| return { | |
| 'level': self.logging.level, | |
| 'format': self.logging.format, | |
| 'enable_file_logging': self.logging.enable_file_logging, | |
| 'log_file_path': self.logging.log_file_path, | |
| 'max_log_file_size_mb': self.logging.max_log_file_size_mb, | |
| 'backup_count': self.logging.backup_count | |
| } | |
| def reload_configuration(self) -> None: | |
| """Reload configuration from sources.""" | |
| logger.info("Reloading configuration") | |
| self._load_configuration() | |
| def save_configuration(self, output_file: str) -> None: | |
| """ | |
| Save current configuration to file. | |
| Args: | |
| output_file: Path to output configuration file | |
| """ | |
| try: | |
| config_dict = { | |
| 'tts': self.get_tts_config(), | |
| 'stt': self.get_stt_config(), | |
| 'translation': self.get_translation_config(), | |
| 'processing': self.get_processing_config(), | |
| 'logging': self.get_logging_config() | |
| } | |
| import json | |
| with open(output_file, 'w') as f: | |
| json.dump(config_dict, f, indent=2) | |
| logger.info(f"Configuration saved to {output_file}") | |
| except Exception as e: | |
| logger.error(f"Failed to save configuration to {output_file}: {e}") | |
| raise | |
| def __str__(self) -> str: | |
| """String representation of configuration.""" | |
| return ( | |
| f"AppConfig(\n" | |
| f" TTS: {self.tts}\n" | |
| f" STT: {self.stt}\n" | |
| f" Translation: {self.translation}\n" | |
| f" Processing: {self.processing}\n" | |
| f" Logging: {self.logging}\n" | |
| f")" | |
| ) |