Video preview posters
This commit is contained in:
parent
be9772c90e
commit
434e55f303
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -23,6 +23,7 @@ dependencies = [
|
||||||
"natsort",
|
"natsort",
|
||||||
"pathvalidate",
|
"pathvalidate",
|
||||||
"pillow",
|
"pillow",
|
||||||
|
"pyav",
|
||||||
"pyjwt",
|
"pyjwt",
|
||||||
"pymupdf",
|
"pymupdf",
|
||||||
"sanic",
|
"sanic",
|
||||||
|
|
Loading…
Reference in New Issue
Block a user