Mercurial > code > home > repos > video
diff video.py @ 6:ccfea3625cf6
render thumbs and display them (no video player at all atm)
author | drewp@bigasterisk.com |
---|---|
date | Sun, 09 Apr 2023 18:02:25 -0700 |
parents | 75b54be050bc |
children | 53d99454f394 |
line wrap: on
line diff
--- a/video.py Thu Mar 30 20:39:40 2023 -0700 +++ b/video.py Sun Apr 09 18:02:25 2023 -0700 @@ -1,4 +1,10 @@ +import asyncio +from dataclasses import dataclass +from pathlib import Path +import hashlib import logging +import re +from typing import Iterable from prometheus_client import Gauge from starlette.applications import Starlette @@ -7,30 +13,83 @@ from starlette.routing import Route from starlette_exporter import PrometheusMiddleware, handle_metrics -from video_service import findInDir, findSubdirs +from video_service import VideoFile logging.basicConfig(level=logging.DEBUG) log = logging.getLogger() +def vf(p: Path, label: str): + return VideoFile(p, './files/' + str(p.relative_to('/data')), label) + +def thumbWebPath(rel: str)->str: + return './files/' + rel + +@dataclass +class VideoFileStore: + top: Path + + def findInDir(self, subdir: str) -> Iterable[VideoFile]: + if subdir[0] != '/': raise ValueError + here = self.top / subdir[1:] + manifests = list(here.glob('*.mpd')) + if manifests: + p = manifests[0] + label = p.parent.name + yield vf(p, label) + return + for p in sorted(list(here.glob('*.mp4')) + list(here.glob('*.webm'))): + label = re.sub(r' \[[^\]]+\]\.\w+', '', p.name) + yield vf(p, label) + + + def findSubdirs(self, subdir: str) -> Iterable: + if subdir[0] != '/': raise ValueError + here = self.top / subdir[1:] + for p in here.iterdir(): + if p.is_dir() and p.name not in {'_thumb'}: + yield {'label': p.name, 'path': '/' + str(p.relative_to(self.top))} + + def thumbPath(self, vf: VideoFile) -> str: + sha256 = hashlib.sha256() + with open(vf.diskPath, 'rb') as f: + firstMb = f.read(1<<20) + sha256.update(firstMb) + cksum = sha256.hexdigest() + return f'_thumb/{cksum}.jpg' + + async def getOrCreateThumb(self, vf: VideoFile) -> str: + p = self.top / self.thumbPath(vf) + if not p.exists(): + sp = asyncio.create_subprocess_exec('ffmpegthumbnailer', + '-s', '250', + '-i', str(vf.diskPath), + '-o', str(p)) + await sp + return thumbWebPath(str(p.relative_to(self.top))) + def root(req): return HTMLResponse("api") -def videos(req: Request) -> JSONResponse: +async def videos(req: Request) -> JSONResponse: subdir = req.query_params.get('subdir', '/') # danger user input + vfInDir = store.findInDir(subdir) return JSONResponse({ "videos": [{ 'webRelPath': vf.webRelPath, 'label': vf.label, - } for vf in findInDir(subdir)], + 'thumbRelPath': await store.getOrCreateThumb(vf), + } for vf in vfInDir], "subdirs": - list(findSubdirs(subdir)), + list(store.findSubdirs(subdir)), }) +store = VideoFileStore(top=Path('/data')) def main(): + app = Starlette( debug=True, routes=[