Tajweed-AI / app.py
hetchyy's picture
Import spaces before CUDA initialization
95ef3dd
"""
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()