Fix running with today's Python tooling and av module.

This commit is contained in:
Leo Vasanko 2025-08-13 10:15:22 -07:00
parent f38bb4bab9
commit aa1ea03bed
2 changed files with 37 additions and 12 deletions

View File

@ -18,8 +18,6 @@ from sanic.log import logger
from cista import config from cista import config
from cista.util.filename import sanitize from cista.util.filename import sanitize
DISPLAYMATRIX = av.stream.SideData.DISPLAYMATRIX
bp = Blueprint("preview", url_prefix="/preview") bp = Blueprint("preview", url_prefix="/preview")
@ -100,15 +98,39 @@ def process_video(path, *, maxsize, quality):
with av.open(str(path)) as container: with av.open(str(path)) as container:
stream = container.streams.video[0] stream = container.streams.video[0]
stream.codec_context.skip_frame = "NONKEY" stream.codec_context.skip_frame = "NONKEY"
rot = stream.side_data and stream.side_data.get(DISPLAYMATRIX) or 0
# 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) container.seek(container.duration // 8)
img = next(container.decode(stream)).to_image() 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 del stream
img.thumbnail((maxsize, maxsize)) img.thumbnail((maxsize, maxsize))
imgdata = io.BytesIO() imgdata = io.BytesIO()
if rot: if rot and rot != 0:
img = img.rotate(rot, expand=True) img = img.rotate(-rot, expand=True) # Negative rotation for correct orientation
img.save(imgdata, format="webp", quality=quality, method=4) img.save(imgdata, format="webp", quality=quality, method=4)
del img del img
ret = imgdata.getvalue() ret = imgdata.getvalue()

View File

@ -15,6 +15,7 @@ classifiers = [
requires-python = ">=3.11" requires-python = ">=3.11"
dependencies = [ dependencies = [
"argon2-cffi", "argon2-cffi",
"av",
"blake3", "blake3",
"brotli", "brotli",
"docopt", "docopt",
@ -23,7 +24,6 @@ dependencies = [
"natsort", "natsort",
"pathvalidate", "pathvalidate",
"pillow", "pillow",
"pyav",
"pyjwt", "pyjwt",
"pymupdf", "pymupdf",
"sanic", "sanic",
@ -50,15 +50,15 @@ source = "vcs"
[tool.hatch.build] [tool.hatch.build]
artifacts = ["cista/wwwroot"] artifacts = ["cista/wwwroot"]
targets.sdist.hooks.custom.path = "scripts/build-frontend.py" targets.sdist.hooks.custom.path = "scripts/build-frontend.py"
targets.sdist.include = [
"/cista",
]
hooks.vcs.version-file = "cista/_version.py" hooks.vcs.version-file = "cista/_version.py"
hooks.vcs.template = """ hooks.vcs.template = """
# This file is automatically generated by hatch build. # This file is automatically generated by hatch build.
__version__ = {version!r} __version__ = {version!r}
""" """
only-packages = true only-packages = true
targets.sdist.include = [
"/cista",
]
[tool.pytest.ini_options] [tool.pytest.ini_options]
addopts = [ addopts = [
@ -95,11 +95,14 @@ ignore = [
"TD0", "TD0",
"TRY", "TRY",
] ]
show-source = true
show-fixes = true
[tool.ruff.isort] [tool.ruff.isort]
known-first-party = ["cista"] known-first-party = ["cista"]
[tool.ruff.per-file-ignores] [tool.ruff.per-file-ignores]
"tests/*" = ["S", "ANN", "D", "INP"] "tests/*" = ["S", "ANN", "D", "INP"]
[dependency-groups]
dev = [
"pytest>=8.4.1",
]