99 lines
3.5 KiB
Python
99 lines
3.5 KiB
Python
"""Router voix : catalogue GLOBAL, déploiement par tablette, ingestion admin,
|
|
verrou d'exclusivité, ré-enrôlement, déclencheur auto."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from fastapi import APIRouter
|
|
from pydantic import BaseModel
|
|
|
|
from kazeia_central.core.context import Ctx
|
|
from . import orchestrator as vo
|
|
|
|
|
|
class _Sync(BaseModel):
|
|
delete_source: bool = False
|
|
|
|
|
|
class _AutoSync(BaseModel):
|
|
enabled: bool
|
|
delete_source: bool | None = None
|
|
|
|
|
|
class _Lock(BaseModel):
|
|
profile_id: str
|
|
|
|
|
|
class _Deploy(BaseModel):
|
|
voice_id: str
|
|
|
|
|
|
def make_router(ctx: Ctx) -> APIRouter:
|
|
r = APIRouter(prefix="/api/voices", tags=["voices"])
|
|
g, now = ctx.guard, ctx.now_ms
|
|
|
|
@r.get("/health")
|
|
def voices_health():
|
|
return {"encoder_available": ctx.bridge.available(), "ov_python": ctx.bridge.ov_python}
|
|
|
|
# ⚠️ Littéraux (autosync, catalog) AVANT /{serial} — ordre de déclaration.
|
|
@r.get("/autosync")
|
|
def autosync_status():
|
|
return ctx.autosync.status()
|
|
|
|
@r.post("/autosync")
|
|
def autosync_set(body: _AutoSync):
|
|
ctx.autosync.set_enabled(body.enabled, delete_source=body.delete_source)
|
|
ctx.store.audit("autosync_set", actor=ctx.operator,
|
|
detail={"enabled": body.enabled, "delete_source": ctx.autosync.delete_source},
|
|
now=now())
|
|
return ctx.autosync.status()
|
|
|
|
# ---- catalogue GLOBAL (vue sans tablette) -----------------------------
|
|
@r.get("/catalog")
|
|
def voices_catalog():
|
|
return g(lambda: vo.catalog(ctx.store))
|
|
|
|
@r.post("/catalog/{voice_id}/lock")
|
|
def voices_lock(voice_id: str, body: _Lock):
|
|
g(lambda: ctx.store.lock_voice(voice_id, body.profile_id, now=now()))
|
|
ctx.store.audit("voice_lock", actor=ctx.operator, target=voice_id,
|
|
detail={"profile_id": body.profile_id}, now=now())
|
|
return {"voice_id": voice_id, "locked_profile_id": body.profile_id}
|
|
|
|
@r.post("/catalog/{voice_id}/unlock")
|
|
def voices_unlock(voice_id: str):
|
|
g(lambda: ctx.store.unlock_voice(voice_id, now=now()))
|
|
ctx.store.audit("voice_unlock", actor=ctx.operator, target=voice_id, now=now())
|
|
return {"voice_id": voice_id, "locked_profile_id": None}
|
|
|
|
@r.post("/catalog/{voice_id}/reenroll")
|
|
def voices_reenroll(voice_id: str):
|
|
return g(lambda: vo.reenroll_voice(ctx.adb, ctx.store, ctx.bridge, voice_id,
|
|
now=now(), actor=ctx.operator))
|
|
|
|
# ---- vue par tablette : chargées + chargeables ------------------------
|
|
@r.get("/{serial}")
|
|
def voices_for_tablet(serial: str):
|
|
return g(lambda: vo.list_for_tablet(ctx.adb, ctx.store, serial))
|
|
|
|
@r.get("/{serial}/admin")
|
|
def voices_admin_list(serial: str):
|
|
return g(lambda: vo.list_admin_voices(ctx.adb, ctx.store, serial))
|
|
|
|
@r.post("/{serial}/admin/sync")
|
|
def voices_admin_sync(serial: str, body: _Sync):
|
|
return g(lambda: vo.sync_admin(ctx.adb, ctx.store, ctx.bridge, serial,
|
|
delete_source=body.delete_source, now=now(), actor=ctx.operator))
|
|
|
|
@r.post("/{serial}/deploy")
|
|
def voices_deploy(serial: str, body: _Deploy):
|
|
return g(lambda: vo.deploy_voice(ctx.adb, ctx.store, ctx.bridge, serial,
|
|
body.voice_id, now=now(), actor=ctx.operator))
|
|
|
|
@r.post("/{serial}/undeploy")
|
|
def voices_undeploy(serial: str, body: _Deploy):
|
|
return g(lambda: vo.undeploy_voice(ctx.adb, ctx.store, serial, body.voice_id,
|
|
now=now(), actor=ctx.operator))
|
|
|
|
return r
|