Add binary RAW inspection and diagnostics v0.8.2
This commit is contained in:
1 parent
d17bc6a132
commit
2dee67021e
6 files changed
+87
-26
No files matched your search
+43
-10
@@ -1,7 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import base64
|
||||
import io
|
||||
import json
|
||||
import logging
|
||||
@@ -20,7 +19,7 @@ from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Annotated, Any
|
||||
|
||||
from fastapi import FastAPI, File, Form, HTTPException, UploadFile
|
||||
from fastapi import FastAPI, File, Form, HTTPException, Request, UploadFile
|
||||
from pydantic import BaseModel, Field
|
||||
from fastapi.responses import FileResponse, Response
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
@@ -86,7 +85,7 @@ APP_SUBTITLE = (
|
||||
or f"Local folder · in-place {ENABLED_FORMAT_LABEL} metadata"
|
||||
)
|
||||
APP_URL = os.getenv("APP_URL", "http://localhost:8080")
|
||||
APP_VERSION = "0.8.1"
|
||||
APP_VERSION = "0.8.2"
|
||||
MAX_UPLOAD_MB = int(os.getenv("MAX_UPLOAD_MB", "150"))
|
||||
MAX_UPLOAD_BYTES = MAX_UPLOAD_MB * 1024 * 1024
|
||||
PREVIEW_MAX_EDGE = int(os.getenv("PREVIEW_MAX_EDGE", "2400"))
|
||||
@@ -166,6 +165,17 @@ class FavoriteLocationInput(BaseModel):
|
||||
app = FastAPI(title=APP_TITLE, docs_url=None, redoc_url=None)
|
||||
|
||||
|
||||
@app.middleware("http")
|
||||
async def request_timing(request: Request, call_next: Any) -> Response:
|
||||
started = time.perf_counter()
|
||||
response = await call_next(request)
|
||||
elapsed_seconds = time.perf_counter() - started
|
||||
if request.url.path in {"/api/inspect", "/api/process"}:
|
||||
response.headers["X-Request-Duration-Ms"] = f"{elapsed_seconds * 1000:.1f}"
|
||||
logger.info("%s completed in %.2fs end-to-end on the server", request.url.path, elapsed_seconds)
|
||||
return response
|
||||
|
||||
|
||||
@app.get("/api/health")
|
||||
def health() -> dict[str, str]:
|
||||
return {"status": "ok", "version": APP_VERSION}
|
||||
@@ -827,22 +837,45 @@ async def create_preview(file: Annotated[UploadFile, File(...)]) -> Response:
|
||||
|
||||
|
||||
@app.post("/api/inspect")
|
||||
async def inspect_photo(file: Annotated[UploadFile, File(...)]) -> dict[str, Any]:
|
||||
"""Upload once and return both metadata and a browser-friendly preview."""
|
||||
async def inspect_photo(file: Annotated[UploadFile, File(...)]) -> Response:
|
||||
"""Return length-prefixed JSON metadata followed by binary JPEG preview data."""
|
||||
filename = file.filename or "photo.tiff"
|
||||
suffix = Path(filename).suffix.lower()
|
||||
if suffix not in SUPPORTED_EXTENSIONS or suffix not in SERVER_PREVIEW_EXTENSIONS:
|
||||
raise HTTPException(status_code=415, detail="This format is disabled or does not require server inspection.")
|
||||
with tempfile.TemporaryDirectory(prefix="photo-date-editor-inspect-") as temp_dir:
|
||||
temp_path = Path(temp_dir) / f"source{suffix}"
|
||||
copy_started = time.perf_counter()
|
||||
await _save_upload(file, temp_path, suffix)
|
||||
copy_seconds = time.perf_counter() - copy_started
|
||||
preview_started = time.perf_counter()
|
||||
preview_bytes = _preview_bytes(temp_path, suffix, filename)
|
||||
preview_seconds = time.perf_counter() - preview_started
|
||||
metadata_started = time.perf_counter()
|
||||
metadata = _read_metadata_path(temp_path)
|
||||
return {
|
||||
**metadata,
|
||||
"previewBase64": base64.b64encode(preview_bytes).decode("ascii"),
|
||||
"previewMediaType": "image/jpeg",
|
||||
}
|
||||
metadata_seconds = time.perf_counter() - metadata_started
|
||||
|
||||
metadata_bytes = json.dumps(metadata, ensure_ascii=False, separators=(",", ":")).encode("utf-8")
|
||||
body = len(metadata_bytes).to_bytes(4, "big") + metadata_bytes + preview_bytes
|
||||
logger.info(
|
||||
"Inspected %s: temporary copy %.2fs, preview %.2fs, metadata %.2fs",
|
||||
filename,
|
||||
copy_seconds,
|
||||
preview_seconds,
|
||||
metadata_seconds,
|
||||
)
|
||||
return Response(
|
||||
content=body,
|
||||
media_type="application/vnd.photo-date-editor.inspect",
|
||||
headers={
|
||||
"Cache-Control": "no-store",
|
||||
"Server-Timing": (
|
||||
f'copy;dur={copy_seconds * 1000:.1f}, '
|
||||
f'preview;dur={preview_seconds * 1000:.1f}, '
|
||||
f'metadata;dur={metadata_seconds * 1000:.1f}'
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@app.post("/api/metadata")
|
||||
|
||||
Reference in new issue
Block a user