- Add Python FastAPI backend with Pydantic validation - Port WhiskClient and MetaAIClient to Python - Create API routers for all endpoints - Add Swagger/ReDoc documentation at /docs - Update Dockerfile for multi-service container - Add lib/api.ts frontend client - Update README for V3
92 lines
3.3 KiB
Python
92 lines
3.3 KiB
Python
"""
|
|
References Router - Reference image upload
|
|
"""
|
|
from fastapi import APIRouter, HTTPException
|
|
from models.requests import ReferenceUploadRequest
|
|
from models.responses import ReferenceUploadResponse, ErrorResponse
|
|
from services.whisk_client import WhiskClient
|
|
import json
|
|
|
|
router = APIRouter(tags=["References"])
|
|
|
|
|
|
@router.post(
|
|
"/references/upload",
|
|
response_model=ReferenceUploadResponse,
|
|
responses={
|
|
400: {"model": ErrorResponse},
|
|
401: {"model": ErrorResponse},
|
|
500: {"model": ErrorResponse}
|
|
}
|
|
)
|
|
async def upload_reference(request: ReferenceUploadRequest):
|
|
"""
|
|
Upload a reference image for Whisk generation.
|
|
|
|
- **imageBase64**: Base64 encoded image data
|
|
- **mimeType**: Image MIME type (image/jpeg, image/png, image/webp, image/gif)
|
|
- **category**: Reference category (subject, scene, style)
|
|
- **cookies**: Whisk authentication cookies
|
|
"""
|
|
# Validate MIME type
|
|
allowed_types = ['image/jpeg', 'image/png', 'image/webp', 'image/gif']
|
|
if request.mimeType not in allowed_types:
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail=f"Unsupported file type: {request.mimeType}. Please use JPG, PNG, or WEBP."
|
|
)
|
|
|
|
# Normalize cookies
|
|
valid_cookies = request.cookies.strip()
|
|
is_json = False
|
|
|
|
trimmed = valid_cookies.strip()
|
|
if trimmed.startswith('[') or trimmed.startswith('{'):
|
|
is_json = True
|
|
try:
|
|
cookie_array = json.loads(trimmed)
|
|
if isinstance(cookie_array, list):
|
|
valid_cookies = "; ".join(
|
|
f"{c['name']}={c['value']}" for c in cookie_array
|
|
)
|
|
print(f"[API] Successfully parsed {len(cookie_array)} cookies from JSON.")
|
|
elif isinstance(cookie_array, dict) and 'name' in cookie_array and 'value' in cookie_array:
|
|
valid_cookies = f"{cookie_array['name']}={cookie_array['value']}"
|
|
except Exception as e:
|
|
print(f"[API] Failed to parse cookie JSON, falling back to raw value: {e}")
|
|
|
|
# Validate cookie format
|
|
if '=' not in valid_cookies:
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail='Invalid Cookie Format. Cookies must be in "name=value" format or a JSON list.'
|
|
)
|
|
|
|
print(f"[API] Uploading reference image ({request.category}, {request.mimeType})...")
|
|
print(f"[API] Using cookies (first 100 chars): {valid_cookies[:100]}...")
|
|
print(f"[API] Cookie was JSON: {is_json}")
|
|
|
|
try:
|
|
client = WhiskClient(valid_cookies)
|
|
|
|
# Remove data URI header if present
|
|
raw_base64 = request.imageBase64
|
|
if raw_base64.startswith('data:'):
|
|
raw_base64 = raw_base64.split(',', 1)[1] if ',' in raw_base64 else raw_base64
|
|
|
|
media_id = await client.upload_reference_image(
|
|
raw_base64,
|
|
request.mimeType,
|
|
request.category
|
|
)
|
|
|
|
if not media_id:
|
|
raise HTTPException(status_code=500, detail="Upload returned no ID")
|
|
|
|
return ReferenceUploadResponse(success=True, id=media_id)
|
|
|
|
except ValueError as e:
|
|
raise HTTPException(status_code=400, detail=str(e))
|
|
except Exception as e:
|
|
print(f"Reference Upload API failed: {e}")
|
|
raise HTTPException(status_code=500, detail=str(e))
|