Update the project to run with modern uv/bun (or python/nodejs) environment #7
@ -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()
|
||||
|
Loading…
x
Reference in New Issue
Block a user