|
|
import os |
|
|
import yaml |
|
|
import shutil |
|
|
from pathlib import Path |
|
|
import uuid |
|
|
from datetime import datetime |
|
|
try: |
|
|
import tomllib |
|
|
except ModuleNotFoundError: |
|
|
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 persistent_data_gate import ensure_temp_databases, maybe_publish_monthly_actions_digest |
|
|
from mobile_verification import render_mobile_verification_screen, get_user_permissions |
|
|
from compliance_client import compliance_client |
|
|
from page_modules.new_video_processing import render_process_video_page |
|
|
from page_modules.analyze_audiodescriptions import render_analyze_audiodescriptions_page |
|
|
from page_modules.statistics import render_statistics_page |
|
|
from page_modules.validation import render_validation_page |
|
|
from page_modules.machine_room import render_machine_room_page |
|
|
|
|
|
|
|
|
|
|
|
os.environ["STREAMLIT_DATA_DIRECTORY"] = "/tmp/.streamlit" |
|
|
Path("/tmp/.streamlit").mkdir(parents=True, exist_ok=True) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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", |
|
|
base_dir / "videos", |
|
|
] |
|
|
|
|
|
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) |
|
|
|
|
|
|
|
|
|
|
|
def _load_yaml(path="config.yaml") -> dict: |
|
|
with open(path, "r", encoding="utf-8") as f: |
|
|
cfg = yaml.safe_load(f) or {} |
|
|
|
|
|
def _subst(s: str) -> str: |
|
|
return os.path.expandvars(s) if isinstance(s, str) else s |
|
|
|
|
|
|
|
|
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") |
|
|
|
|
|
|
|
|
|
|
|
DATA_DIR = "temp" |
|
|
|
|
|
|
|
|
BACKEND_BASE_URL = ( |
|
|
CFG.get("api", {}).get("base_url") |
|
|
or os.getenv("API_BASE_URL") |
|
|
or "http://localhost:8000" |
|
|
) |
|
|
|
|
|
TTS_URL = "https://veureu-tts.hf.space" |
|
|
USE_MOCK = bool(CFG.get("app", {}).get("use_mock", False)) |
|
|
|
|
|
COMPLIANCE_CFG = CFG.get("compliance", {}) or {} |
|
|
PRIVATE_BLOCKCHAIN_ENABLE = bool( |
|
|
COMPLIANCE_CFG.get("private_blockchain_enabled", |
|
|
COMPLIANCE_CFG.get("private_blockchain_enable", False)) |
|
|
) |
|
|
PUBLIC_BLOCKCHAIN_ENABLE = bool( |
|
|
COMPLIANCE_CFG.get("public_blockchain_enabled", |
|
|
COMPLIANCE_CFG.get("public_blockchain_enable", False)) |
|
|
) |
|
|
MONTHLY_DIGEST_ENABLED = bool(COMPLIANCE_CFG.get("monthly_digest_enabled", False)) |
|
|
|
|
|
|
|
|
API_TOKEN = CFG.get("api", {}).get("token") or os.getenv("VEUREU_TOKEN") |
|
|
|
|
|
|
|
|
api = APIClient(BACKEND_BASE_URL, use_mock=USE_MOCK, data_dir=DATA_DIR, token=API_TOKEN, tts_url=TTS_URL) |
|
|
|
|
|
os.makedirs(DATA_DIR, exist_ok=True) |
|
|
ensure_dirs(DATA_DIR) |
|
|
|
|
|
base_dir = Path(__file__).parent |
|
|
ensure_temp_databases(base_dir, api) |
|
|
|
|
|
DB_PATH = os.path.join(DATA_DIR, "db", "users.db") |
|
|
set_db_path(DB_PATH) |
|
|
|
|
|
|
|
|
set_blockchain_enabled(PRIVATE_BLOCKCHAIN_ENABLE) |
|
|
|
|
|
|
|
|
|
|
|
maybe_publish_monthly_actions_digest( |
|
|
base_dir, |
|
|
compliance_client, |
|
|
public_blockchain_enabled=PUBLIC_BLOCKCHAIN_ENABLE, |
|
|
monthly_digest_enabled=MONTHLY_DIGEST_ENABLED, |
|
|
) |
|
|
|
|
|
init_schema() |
|
|
|
|
|
|
|
|
initialize_auth_system(DB_PATH) |
|
|
|
|
|
|
|
|
if "session_id" not in st.session_state: |
|
|
st.session_state.session_id = str(uuid.uuid4()) |
|
|
|
|
|
|
|
|
st.session_state.api_client = api |
|
|
|
|
|
st.set_page_config(page_title="Veureu — Audiodescripció", page_icon="🎬", layout="wide") |
|
|
|
|
|
|
|
|
st.markdown( |
|
|
""" |
|
|
<style> |
|
|
.stButton > button[kind="primary"] { |
|
|
background-color: #f97316; |
|
|
border-color: #ea580c; |
|
|
color: white; |
|
|
} |
|
|
.stButton > button[kind="primary"]:hover { |
|
|
background-color: #ea580c; |
|
|
border-color: #c2410c; |
|
|
} |
|
|
</style> |
|
|
""", |
|
|
unsafe_allow_html=True, |
|
|
) |
|
|
|
|
|
|
|
|
if "user" not in st.session_state: |
|
|
st.session_state.user = None |
|
|
|
|
|
|
|
|
page, role = render_sidebar() |
|
|
|
|
|
|
|
|
if not st.session_state.user: |
|
|
st.title("Veureu — Audiodescripció") |
|
|
render_login_form() |
|
|
st.stop() |
|
|
|
|
|
|
|
|
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'] |
|
|
|
|
|
|
|
|
permissions = get_user_permissions(role, st.session_state.get('sms_verified')) |
|
|
|
|
|
|
|
|
if permissions["requires_sms"] and st.session_state.sms_verified is None: |
|
|
result = render_mobile_verification_screen(username, role) |
|
|
if result is None: |
|
|
|
|
|
st.stop() |
|
|
|
|
|
|
|
|
|
|
|
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 audiodescripcions": |
|
|
require_login(render_login_form) |
|
|
permissions = get_user_permissions(role, st.session_state.get('sms_verified')) |
|
|
render_analyze_audiodescriptions_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) |
|
|
|
|
|
elif page == "Sala de màquines": |
|
|
require_login(render_login_form) |
|
|
|
|
|
if st.session_state.user and st.session_state.user.get("username") == "verd": |
|
|
render_machine_room_page() |
|
|
else: |
|
|
st.error("No tens permisos per accedir a aquesta pantalla.") |
|
|
|