""" Main Gradio application for Quran recitation practice. """ import argparse import os import sys from pathlib import Path # CRITICAL: Import spaces FIRST to avoid CUDA initialization before ZeroGPU setup try: import spaces except ImportError: pass import gradio as gr # Add parent directory to path for imports app_path = str(Path(__file__).parent) sys.path.insert(0, app_path) from config import IS_HF_SPACE, GRADIO_CONCURRENCY_LIMIT from recitation_engine.model_loader import get_model, get_models from recitation_engine.segment_processor import initialize_segment_models from recitation_engine.zero_gpu import ZERO_GPU_AVAILABLE from utils.phonemizer_utils import get_chapter_list from shared_state import ( is_initialized, set_initialized, set_processor as state_set_processor, set_model as state_set_model, set_chapters as state_set_chapters, set_model_bundles as state_set_model_bundles, ) def initialize_app(): """Initialize the application by loading model and data. Safe to call multiple times under Gradio reload. """ if is_initialized(): print("App already initialized. Skipping initialization.") return set_initialized(True) print("=" * 60) print("Initializing Recitation Application...") print("=" * 60) # Load primary model processor, model, error = get_model() state_set_processor(processor) state_set_model(model) bundles, bundle_errors = get_models() if error: print(f"\nWarning: {error}") print("The app will run, but transcription will not work.\n") elif ZERO_GPU_AVAILABLE: print("ZeroGPU support available: GPU time will be requested for audio processing.") # Multi-model bundles if not bundles: print("No ASR models loaded. Check MODEL_PATHS in config.py.") state_set_model_bundles([]) else: state_set_model_bundles(bundles) loaded_count = len([b for b in bundles if b.get("model")]) print(f"Loaded {loaded_count}/{len(bundles)} ASR models for multi-model mode.") # Chapters chapters = get_chapter_list() state_set_chapters(chapters) print(f"Loaded {len(chapters)} chapters") # Segmentation models (always initialized - VAD auto-detects segment count) initialize_segment_models() print("=" * 60) print("Initialization complete!") print("=" * 60) # ----------------------------------------------------------------------------- # Configuration # ----------------------------------------------------------------------------- DEFAULT_PORT = 7860 PORT = int(os.environ.get("GRADIO_SERVER_PORT", DEFAULT_PORT)) # HF Spaces auth HF_PASSWORD = os.environ.get("PASSWORD") # ----------------------------------------------------------------------------- # Argument parsing # ----------------------------------------------------------------------------- def parse_args(): parser = argparse.ArgumentParser( description="Quran Recitation Practice App" ) parser.add_argument( "--share", action="store_true", help="Create a public Gradio link" ) parser.add_argument( "--port", type=int, default=None, help="Port to run the server on" ) return parser.parse_args() # ----------------------------------------------------------------------------- # App construction # ----------------------------------------------------------------------------- # Gradio reload works by importing this file repeatedly. # Must be created at import time. initialize_app() # Initialize locale from environment (fallback for initial render) # The actual locale is set dynamically via ?__locale query param on page load # User preference is stored in localStorage and applied automatically from i8n import set_locale, get_available_locales env_locale = os.environ.get("TAJWEED_LOCALE", "en") if env_locale in get_available_locales(): set_locale(env_locale) print(f"Default locale: {env_locale}") # Import after initialization (depends on shared_state being populated) from ui.builder import build_interface demo = build_interface() # ----------------------------------------------------------------------------- # Launch logic # ----------------------------------------------------------------------------- def launch_app(): args = parse_args() port = args.port or PORT # Password protection on HF Space auth = None if IS_HF_SPACE and HF_PASSWORD: auth = lambda u, p: p == HF_PASSWORD # Enable Gradio's built-in i18n for component translations (e.g., Audio buttons) # Arabic translations for Gradio's built-in component strings i18n = gr.I18n( ar={ "common.audio.record": "تسجيل", "common.audio.stop": "إيقاف", "common.audio.upload": "رفع ملف", "common.audio.pause": "إيقاف مؤقت", "common.audio.play": "تشغيل", "common.clear": "مسح", } ) demo.queue(default_concurrency_limit=GRADIO_CONCURRENCY_LIMIT).launch( server_name="0.0.0.0", server_port=port, share=args.share, auth=auth, i18n=i18n, ) if __name__ == "__main__": launch_app()