Update the project to run with modern uv/bun (or python/nodejs) environment #7

Merged
LeoVasanko merged 43 commits from rejuvenile into main 2025-08-15 18:03:04 +01:00
Showing only changes of commit 2bce21a5ab - Show all commits

View File

@ -6,7 +6,6 @@ import urllib.parse
from pathlib import PurePosixPath
from urllib.parse import unquote
from wsgiref.handlers import format_date_time
import av
import av.datasets
import fitz # PyMuPDF
@ -36,13 +35,13 @@ async def preview(req, path):
etag = config.derived_secret(
"preview", rel, stat.st_mtime_ns, quality, maxsize, maxzoom
).hex()
savename = PurePosixPath(path.name).with_suffix(".webp")
savename = PurePosixPath(path.name).with_suffix(".avif")
headers = {
"etag": etag,
"last-modified": format_date_time(stat.st_mtime),
"cache-control": "max-age=604800, immutable"
+ ("" if config.config.public else ", private"),
"content-type": "image/webp",
"content-type": "image/avif",
"content-disposition": f"inline; filename*=UTF-8''{urllib.parse.quote(savename.as_posix())}",
}
if req.headers.if_none_match == etag:
@ -83,7 +82,7 @@ def process_image(path, *, maxsize, quality):
logger.error(f"Error rotating preview image: {e}")
# Save as webp
imgdata = io.BytesIO()
img.save(imgdata, format="webp", quality=quality, method=4)
img.save(imgdata, format="avif", quality=quality, method=4)
return imgdata.getvalue()
@ -94,48 +93,48 @@ def process_pdf(path, *, maxsize, maxzoom, quality, page_number=0):
zoom = min(maxsize / w, maxsize / h, maxzoom)
mat = fitz.Matrix(zoom, zoom)
pix = page.get_pixmap(matrix=mat)
return pix.pil_tobytes(format="webp", quality=quality, method=4)
return pix.pil_tobytes(format="avif", quality=quality, method=4)
def process_video(path, *, maxsize, quality):
with av.open(str(path)) as container:
stream = container.streams.video[0]
stream.codec_context.skip_frame = "NONKEY"
# Updated side data access for newer av versions
rot = 0
try:
# Try newer API first
if hasattr(stream, "side_data") and stream.side_data:
display_matrix = stream.side_data.get("DISPLAYMATRIX")
if display_matrix:
rot = (
display_matrix.rotation
if hasattr(display_matrix, "rotation")
else 0
)
except (AttributeError, KeyError):
# Fallback for older API or missing side data
rot = 0
container.seek(container.duration // 8)
try:
frame = next(container.decode(stream))
img = frame.to_image()
except StopIteration:
# If no frame found, try from beginning
container.seek(0)
frame = next(container.decode(stream))
img = frame.to_image()
del stream
img.thumbnail((maxsize, maxsize))
frame = None
imgdata = io.BytesIO()
if rot and rot != 0:
img = img.rotate(-rot, expand=True) # Negative rotation for correct orientation
img.save(imgdata, format="webp", quality=quality, method=4)
del img
with (
av.open(str(path)) as container,
av.open(imgdata, "w", format="avif") as ocontainer,
):
istream = container.streams.video[0]
istream.codec_context.skip_frame = "NONKEY"
container.seek((container.duration or 0) // 8)
for frame in container.decode(istream):
if frame.dts is not None:
break
else:
raise RuntimeError("No frames found in video")
# Resize frame to thumbnail size
if frame.width > maxsize or frame.height > maxsize:
scale_factor = min(maxsize / frame.width, maxsize / frame.height)
new_width = int(frame.width * scale_factor)
new_height = int(frame.height * scale_factor)
frame = frame.reformat(width=new_width, height=new_height)
ostream = ocontainer.add_stream("av1", options={"quality": str(quality)})
assert isinstance(ostream, av.VideoStream)
ostream.width = frame.width
ostream.height = frame.height
icc = istream.codec_context
occ = ostream.codec_context
# Copy HDR metadata from input video stream
occ.color_primaries = icc.color_primaries
occ.color_trc = icc.color_trc
occ.colorspace = icc.colorspace
occ.color_range = icc.color_range
ocontainer.mux(ostream.encode(frame))
ocontainer.mux(ostream.encode(None)) # Flush the stream
ret = imgdata.getvalue()
del imgdata
gc.collect()