Video preview posters

This commit is contained in:
Leo Vasanko 2023-11-18 16:44:46 -08:00
parent be9772c90e
commit 434e55f303
4 changed files with 35 additions and 2 deletions

View File

@ -11,7 +11,7 @@ from wsgiref.handlers import format_date_time
import brotli import brotli
import sanic.helpers import sanic.helpers
from blake3 import blake3 from blake3 import blake3
from sanic import Blueprint, Sanic, empty, raw from sanic import Blueprint, Sanic, empty, raw, redirect
from sanic.exceptions import Forbidden, NotFound from sanic.exceptions import Forbidden, NotFound
from sanic.log import logger from sanic.log import logger
from stream_zip import ZIP_AUTO, stream_zip from stream_zip import ZIP_AUTO, stream_zip
@ -198,6 +198,12 @@ async def wwwroot(req, path=""):
return raw(data, headers=headers) return raw(data, headers=headers)
@app.route("/favicon.ico", methods=["GET", "HEAD"])
async def favicon(req):
# Browsers keep asking for it when viewing files (not HTML with icon link)
return redirect("/assets/logo-97d1d7eb.svg", status=308)
def get_files(wanted: set) -> list[tuple[PurePosixPath, Path]]: def get_files(wanted: set) -> list[tuple[PurePosixPath, Path]]:
loc = PurePosixPath() loc = PurePosixPath()
idx = 0 idx = 0

View File

@ -1,10 +1,13 @@
import asyncio import asyncio
import io import io
import mimetypes
import urllib.parse import urllib.parse
from pathlib import PurePosixPath from pathlib import PurePosixPath
from urllib.parse import unquote from urllib.parse import unquote
from wsgiref.handlers import format_date_time from wsgiref.handlers import format_date_time
import av
import av.datasets
import fitz # PyMuPDF import fitz # PyMuPDF
from PIL import Image from PIL import Image
from sanic import Blueprint, empty, raw from sanic import Blueprint, empty, raw
@ -54,6 +57,8 @@ async def preview(req, path):
def dispatch(path, quality, maxsize, maxzoom): def dispatch(path, quality, maxsize, maxzoom):
if path.suffix.lower() in (".pdf", ".xps", ".epub", ".mobi"): if path.suffix.lower() in (".pdf", ".xps", ".epub", ".mobi"):
return process_pdf(path, quality=quality, maxsize=maxsize, maxzoom=maxzoom) return process_pdf(path, quality=quality, maxsize=maxsize, maxzoom=maxzoom)
if mimetypes.guess_type(path.name)[0].startswith("video/"):
return process_video(path, quality=quality, maxsize=maxsize)
return process_image(path, quality=quality, maxsize=maxsize) return process_image(path, quality=quality, maxsize=maxsize)
@ -86,3 +91,24 @@ def process_pdf(path, *, maxsize, maxzoom, quality, page_number=0):
mat = fitz.Matrix(zoom, zoom) mat = fitz.Matrix(zoom, zoom)
pix = page.get_pixmap(matrix=mat) pix = page.get_pixmap(matrix=mat)
return pix.pil_tobytes(format="webp", quality=quality, method=4) return pix.pil_tobytes(format="webp", quality=quality, method=4)
def process_video(path, *, maxsize, quality):
with av.open(str(path)) as container:
stream = container.streams.video[0]
rotation = (
stream.side_data
and stream.side_data.get(av.stream.SideData.DISPLAYMATRIX)
or 0
)
stream.codec_context.skip_frame = "NONKEY"
container.seek(container.duration // 8)
frame = next(container.decode(stream))
img = frame.to_image()
img.thumbnail((maxsize, maxsize))
imgdata = io.BytesIO()
if rotation:
img = img.rotate(rotation, expand=True)
img.save(imgdata, format="webp", quality=quality, method=4)
return imgdata.getvalue()

View File

@ -2,7 +2,7 @@
<img v-if=preview() :src="`${doc.previewurl}?${quality}&t=${doc.mtime}`" alt=""> <img v-if=preview() :src="`${doc.previewurl}?${quality}&t=${doc.mtime}`" alt="">
<img v-else-if=doc.img :src=doc.url alt=""> <img v-else-if=doc.img :src=doc.url alt="">
<span v-else-if=doc.dir class="folder icon"></span> <span v-else-if=doc.dir class="folder icon"></span>
<video ref=vid v-else-if=video() :src=doc.url controls preload=none @click.prevent>📄</video> <video ref=vid v-else-if=video() :src=doc.url :poster="`${doc.previewurl}?${quality}&t=${doc.mtime}`" controls preload=none @click.prevent>📄</video>
<audio ref=aud v-else-if=audio() :src=doc.url controls preload=metadata @click.stop>📄</audio> <audio ref=aud v-else-if=audio() :src=doc.url controls preload=metadata @click.stop>📄</audio>
<span v-else-if=archive() class="archive icon"></span> <span v-else-if=archive() class="archive icon"></span>
<span v-else class="file icon" :class="`ext-${doc.ext}`"></span> <span v-else class="file icon" :class="`ext-${doc.ext}`"></span>

View File

@ -23,6 +23,7 @@ dependencies = [
"natsort", "natsort",
"pathvalidate", "pathvalidate",
"pillow", "pillow",
"pyav",
"pyjwt", "pyjwt",
"pymupdf", "pymupdf",
"sanic", "sanic",