import asyncio import io from pathlib import PurePosixPath from urllib.parse import unquote from PIL import Image from sanic import Blueprint, raw from sanic.exceptions import Forbidden, NotFound from cista import config from cista.util.filename import sanitize bp = Blueprint("preview", url_prefix="/preview") @bp.get("/") async def preview(req, path): """Preview a file""" width = int(req.query_string) if req.query_string else 768 rel = PurePosixPath(sanitize(unquote(path))) path = config.config.path / rel if not path.is_file(): raise NotFound("File not found") size = path.lstat().st_size if size > 20 * 10**6: raise Forbidden("File too large") img = await asyncio.get_event_loop().run_in_executor( req.app.ctx.threadexec, process_image, path, width ) return raw(img, content_type="image/webp") def process_image(path, maxsize): img = Image.open(path) w, h = img.size img.thumbnail((min(w, maxsize), min(h, maxsize))) # Save as webp imgdata = io.BytesIO() img.save(imgdata, format="webp", quality=70, method=6) return imgdata.getvalue()