manual submit
This commit is contained in:
@@ -3,10 +3,11 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import mimetypes
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Body, Depends, File, Form, HTTPException, UploadFile
|
||||
from fastapi import APIRouter, Body, Depends, File, Form, HTTPException, Query, UploadFile
|
||||
from fastapi.responses import Response
|
||||
|
||||
from web.auth import require_auth
|
||||
@@ -15,10 +16,38 @@ from web.deps import get_config, get_user_registry
|
||||
router = APIRouter()
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_ALLOWED_EXTS = {".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp"}
|
||||
_MAX_UPLOAD_BYTES = 50 * 1024 * 1024 # 50 MB
|
||||
_SAFE_SLOT_RE = re.compile(r'^[a-zA-Z0-9_\-]+$')
|
||||
|
||||
|
||||
def _validate_slot_key(slot_key: str) -> None:
|
||||
if not _SAFE_SLOT_RE.match(slot_key):
|
||||
raise HTTPException(400, "slot_key may only contain letters, digits, hyphens, underscores")
|
||||
|
||||
|
||||
@router.get("")
|
||||
async def list_inputs(_: dict = Depends(require_auth)):
|
||||
"""List all input images (Discord + web uploads)."""
|
||||
async def list_inputs(
|
||||
_: dict = Depends(require_auth),
|
||||
persons: list[str] = Query(default=[], alias="persons", description="Filter by person name/alias substring (repeatable)"),
|
||||
):
|
||||
"""List all input images (Discord + web uploads). Optionally filter by persons."""
|
||||
active_persons = [p.strip() for p in persons if p.strip()]
|
||||
if active_persons:
|
||||
import face_db as face_db_mod
|
||||
from input_image_db import get_images_by_ids
|
||||
all_ids: set[int] = set()
|
||||
for p in active_persons:
|
||||
ids = face_db_mod.get_source_ids_for_person_query(p, "input")
|
||||
all_ids.update(ids)
|
||||
images = list(get_images_by_ids(list(all_ids))) if all_ids else []
|
||||
if images:
|
||||
person_map = face_db_mod.get_persons_for_source_id_map(
|
||||
[img["id"] for img in images], "input"
|
||||
)
|
||||
for img in images:
|
||||
img["detected_persons"] = person_map.get(img["id"], [])
|
||||
return images
|
||||
from input_image_db import get_all_images
|
||||
rows = get_all_images()
|
||||
return [dict(r) for r in rows]
|
||||
@@ -44,8 +73,16 @@ async def upload_input(
|
||||
if config is None:
|
||||
raise HTTPException(503, "Config not available")
|
||||
|
||||
if slot_key:
|
||||
_validate_slot_key(slot_key)
|
||||
|
||||
data = await file.read()
|
||||
filename = file.filename or "upload.png"
|
||||
ext = Path(filename).suffix.lower()
|
||||
if ext not in _ALLOWED_EXTS:
|
||||
raise HTTPException(415, f"Unsupported file type '{ext}'. Allowed: {sorted(_ALLOWED_EXTS)}")
|
||||
if len(data) > _MAX_UPLOAD_BYTES:
|
||||
raise HTTPException(413, "File too large (max 50 MB)")
|
||||
|
||||
from input_image_db import upsert_image, activate_image_for_slot
|
||||
row_id = upsert_image(
|
||||
@@ -72,7 +109,30 @@ async def upload_input(
|
||||
if comfy:
|
||||
comfy.state_manager.set_override(slot_key, activated_filename)
|
||||
|
||||
return {"id": row_id, "filename": filename, "slot_key": slot_key, "activated_filename": activated_filename}
|
||||
# Face scan — runs synchronously here (~1-2 s); unknown faces returned to UI
|
||||
pending_faces: list[dict] = []
|
||||
try:
|
||||
from face_service import get_face_service
|
||||
import face_db as _face_db
|
||||
_face_db.init_db()
|
||||
svc = get_face_service()
|
||||
if svc.available:
|
||||
results = await svc.scan_input_image(row_id, data)
|
||||
pending_faces = [
|
||||
{"detection_id": r.detection_id, "face_index": r.face_index, "bbox": r.bbox}
|
||||
for r in results
|
||||
if r.matched_person_id is None
|
||||
]
|
||||
except Exception as exc:
|
||||
logger.warning("Face scan failed for upload row_id=%d: %s", row_id, exc)
|
||||
|
||||
return {
|
||||
"id": row_id,
|
||||
"filename": filename,
|
||||
"slot_key": slot_key,
|
||||
"activated_filename": activated_filename,
|
||||
"pending_faces": pending_faces,
|
||||
}
|
||||
|
||||
|
||||
@router.post("/{row_id}/activate")
|
||||
@@ -92,6 +152,7 @@ async def activate_input(
|
||||
raise HTTPException(404, "Image not found")
|
||||
|
||||
user_label: str = user["sub"]
|
||||
_validate_slot_key(slot_key)
|
||||
namespaced_key = f"{user_label}_{slot_key}"
|
||||
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user