import os import yaml import shutil from pathlib import Path import uuid try: import tomllib except ModuleNotFoundError: # Py<3.11 import tomli as tomllib import streamlit as st from databases import set_db_path, init_schema, set_blockchain_enabled from api_client import APIClient from utils import ensure_dirs from auth import initialize_auth_system, render_login_form, render_sidebar, require_login from mobile_verification import render_mobile_verification_screen, get_user_permissions from compliance_client import compliance_client from page_modules.process_video import render_process_video_page from page_modules.analyze_transcriptions import render_analyze_transcriptions_page from page_modules.statistics import render_statistics_page from page_modules.validation import render_validation_page # -- Move DB / vídeos en entorns de desplegament -- os.environ["STREAMLIT_DATA_DIRECTORY"] = "/tmp/.streamlit" Path("/tmp/.streamlit").mkdir(parents=True, exist_ok=True) # Vídeos estàtics: prioritzar demo/data/media, caure a demo/videos si cal. # Es copien a /tmp/data/videos només per a entorns de desplegament; la BD # principal d'usuaris es fa servir directament des de DATA_DIR/users.db. runtime_videos = Path("/tmp/data/videos") if not runtime_videos.exists(): Path("/tmp/data").mkdir(parents=True, exist_ok=True) base_dir = Path(__file__).parent candidates = [ base_dir / "data" / "media", # nova ubicació base_dir / "videos", # compatibilitat enrere ] static_videos = None for cand in candidates: if cand.exists(): static_videos = cand break if static_videos is not None: shutil.copytree(static_videos, runtime_videos, dirs_exist_ok=True) # --- Config --- def _load_yaml(path="config.yaml") -> dict: with open(path, "r", encoding="utf-8") as f: cfg = yaml.safe_load(f) or {} # interpolación sencilla de ${VARS} si las usas en el YAML def _subst(s: str) -> str: return os.path.expandvars(s) if isinstance(s, str) else s # aplica sustitución en los campos que te interesan if "api" in cfg: cfg["api"]["base_url"] = _subst(cfg["api"].get("base_url", "")) cfg["api"]["token"] = _subst(cfg["api"].get("token", "")) cfg["api"]["tts_url"] = _subst(cfg["api"].get("tts_url", "")) if "storage" in cfg and "root_dir" in cfg["storage"]: cfg["storage"]["root_dir"] = _subst(cfg["storage"]["root_dir"]) if "sqlite" in cfg and "path" in cfg["sqlite"]: cfg["sqlite"]["path"] = _subst(cfg["sqlite"]["path"]) return cfg CFG = _load_yaml("config.yaml") # Ajuste de variables según tu esquema YAML # Para la interfaz demo, usaremos siempre la carpeta "temp" local DATA_DIR = "temp" BACKEND_BASE_URL = CFG.get("api", {}).get("base_url", "http://localhost:8000") TTS_URL = "https://veureu-tts.hf.space" # Forzar URL correcta print(f"🔧 TTS_URL configurada: {TTS_URL}") print(f"🔧 Tipo de TTS_URL: {type(TTS_URL)}") print(f"🔧 Longitud de TTS_URL: {len(TTS_URL)}") print(f"🔧 TTS_URL repr: {repr(TTS_URL)}") USE_MOCK = bool(CFG.get("app", {}).get("use_mock", False)) # si no la tienes en el yaml, queda False BLOCKCHAIN_ENABLED = bool(CFG.get("blockchain", {}).get("enabled", False)) API_TOKEN = CFG.get("api", {}).get("token") or os.getenv("API_SHARED_TOKEN") os.makedirs(DATA_DIR, exist_ok=True) ensure_dirs(DATA_DIR) DB_PATH = os.path.join(DATA_DIR, "users.db") set_db_path(DB_PATH) # Configurar si els esdeveniments s'han de registrar a SQLite o a AWS QLDB set_blockchain_enabled(BLOCKCHAIN_ENABLED) init_schema() # Initialize authentication system and sync default users initialize_auth_system(DB_PATH) api = APIClient(BACKEND_BASE_URL, use_mock=USE_MOCK, data_dir=DATA_DIR, token=API_TOKEN, tts_url=TTS_URL) # Identificador de sessió per a traça d'esdeveniments if "session_id" not in st.session_state: st.session_state.session_id = str(uuid.uuid4()) st.set_page_config(page_title="Veureu — Audiodescripció", page_icon="🎬", layout="wide") # Initialize session state for user if "user" not in st.session_state: st.session_state.user = None # Render sidebar and get navigation page, role = render_sidebar() # Pre-login screen if not st.session_state.user: st.title("Veureu — Audiodescripció") render_login_form() st.stop() # Post-login: Verificación por móvil si es necesaria if st.session_state.user and 'sms_verified' not in st.session_state: st.session_state.sms_verified = None permissions = None if st.session_state.user: username = st.session_state.user['username'] role = st.session_state.user['role'] # Obtener permisos para ver si requiere SMS permissions = get_user_permissions(role, st.session_state.get('sms_verified')) # Si requiere SMS y aún no está verificado/omitido, mostrar pantalla de verificación if permissions["requires_sms"] and st.session_state.sms_verified is None: result = render_mobile_verification_screen(username, role) if result is None: # Aún en proceso de verificación st.stop() # Si result es True o False, ya se ha completado/omitido y continúa # --- Pages --- if page == "Processar vídeo nou": require_login(render_login_form) permissions = get_user_permissions(role, st.session_state.get('sms_verified')) if not permissions["procesar_videos"]: st.error("No tens permisos per processar nous vídeos. Verifica el teu mòbil per obtenir accés complet.") st.stop() render_process_video_page(api, BACKEND_BASE_URL) elif page == "Analitzar video-transcripcions": require_login(render_login_form) permissions = get_user_permissions(role, st.session_state.get('sms_verified')) render_analyze_transcriptions_page(api, permissions) elif page == "Estadístiques": require_login(render_login_form) render_statistics_page() elif page == "Validació": require_login(render_login_form) permissions = get_user_permissions(role, st.session_state.get('sms_verified')) render_validation_page(compliance_client, runtime_videos, permissions, username)