Mercurial > code > home > repos > video
annotate video.py @ 12:e60be5d74c07
css edits and ingest link
author | drewp@bigasterisk.com |
---|---|
date | Sat, 15 Apr 2023 15:20:12 -0700 |
parents | ccfea3625cf6 |
children | 53d99454f394 |
rev | line source |
---|---|
6
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
1 import asyncio |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
2 from dataclasses import dataclass |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
3 from pathlib import Path |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
4 import hashlib |
3 | 5 import logging |
6
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
6 import re |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
7 from typing import Iterable |
5
75b54be050bc
show one subdir of archive at once, with folders and parents-breadcrumbs
drewp@bigasterisk.com
parents:
4
diff
changeset
|
8 |
3 | 9 from prometheus_client import Gauge |
10 from starlette.applications import Starlette | |
11 from starlette.requests import Request | |
4
c8a41359505c
server provides video listing from disk
drewp@bigasterisk.com
parents:
3
diff
changeset
|
12 from starlette.responses import HTMLResponse, JSONResponse |
3 | 13 from starlette.routing import Route |
14 from starlette_exporter import PrometheusMiddleware, handle_metrics | |
5
75b54be050bc
show one subdir of archive at once, with folders and parents-breadcrumbs
drewp@bigasterisk.com
parents:
4
diff
changeset
|
15 |
6
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
16 from video_service import VideoFile |
0 | 17 |
3 | 18 logging.basicConfig(level=logging.DEBUG) |
19 log = logging.getLogger() | |
0 | 20 |
6
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
21 def vf(p: Path, label: str): |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
22 return VideoFile(p, './files/' + str(p.relative_to('/data')), label) |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
23 |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
24 def thumbWebPath(rel: str)->str: |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
25 return './files/' + rel |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
26 |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
27 @dataclass |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
28 class VideoFileStore: |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
29 top: Path |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
30 |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
31 def findInDir(self, subdir: str) -> Iterable[VideoFile]: |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
32 if subdir[0] != '/': raise ValueError |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
33 here = self.top / subdir[1:] |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
34 manifests = list(here.glob('*.mpd')) |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
35 if manifests: |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
36 p = manifests[0] |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
37 label = p.parent.name |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
38 yield vf(p, label) |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
39 return |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
40 for p in sorted(list(here.glob('*.mp4')) + list(here.glob('*.webm'))): |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
41 label = re.sub(r' \[[^\]]+\]\.\w+', '', p.name) |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
42 yield vf(p, label) |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
43 |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
44 |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
45 def findSubdirs(self, subdir: str) -> Iterable: |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
46 if subdir[0] != '/': raise ValueError |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
47 here = self.top / subdir[1:] |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
48 for p in here.iterdir(): |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
49 if p.is_dir() and p.name not in {'_thumb'}: |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
50 yield {'label': p.name, 'path': '/' + str(p.relative_to(self.top))} |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
51 |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
52 def thumbPath(self, vf: VideoFile) -> str: |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
53 sha256 = hashlib.sha256() |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
54 with open(vf.diskPath, 'rb') as f: |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
55 firstMb = f.read(1<<20) |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
56 sha256.update(firstMb) |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
57 cksum = sha256.hexdigest() |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
58 return f'_thumb/{cksum}.jpg' |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
59 |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
60 async def getOrCreateThumb(self, vf: VideoFile) -> str: |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
61 p = self.top / self.thumbPath(vf) |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
62 if not p.exists(): |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
63 sp = asyncio.create_subprocess_exec('ffmpegthumbnailer', |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
64 '-s', '250', |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
65 '-i', str(vf.diskPath), |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
66 '-o', str(p)) |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
67 await sp |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
68 return thumbWebPath(str(p.relative_to(self.top))) |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
69 |
0 | 70 |
3 | 71 def root(req): |
72 return HTMLResponse("api") | |
0 | 73 |
74 | |
6
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
75 async def videos(req: Request) -> JSONResponse: |
5
75b54be050bc
show one subdir of archive at once, with folders and parents-breadcrumbs
drewp@bigasterisk.com
parents:
4
diff
changeset
|
76 subdir = req.query_params.get('subdir', '/') # danger user input |
6
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
77 vfInDir = store.findInDir(subdir) |
4
c8a41359505c
server provides video listing from disk
drewp@bigasterisk.com
parents:
3
diff
changeset
|
78 return JSONResponse({ |
c8a41359505c
server provides video listing from disk
drewp@bigasterisk.com
parents:
3
diff
changeset
|
79 "videos": [{ |
c8a41359505c
server provides video listing from disk
drewp@bigasterisk.com
parents:
3
diff
changeset
|
80 'webRelPath': vf.webRelPath, |
c8a41359505c
server provides video listing from disk
drewp@bigasterisk.com
parents:
3
diff
changeset
|
81 'label': vf.label, |
6
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
82 'thumbRelPath': await store.getOrCreateThumb(vf), |
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
83 } for vf in vfInDir], |
5
75b54be050bc
show one subdir of archive at once, with folders and parents-breadcrumbs
drewp@bigasterisk.com
parents:
4
diff
changeset
|
84 "subdirs": |
6
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
85 list(store.findSubdirs(subdir)), |
4
c8a41359505c
server provides video listing from disk
drewp@bigasterisk.com
parents:
3
diff
changeset
|
86 }) |
c8a41359505c
server provides video listing from disk
drewp@bigasterisk.com
parents:
3
diff
changeset
|
87 |
6
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
88 store = VideoFileStore(top=Path('/data')) |
4
c8a41359505c
server provides video listing from disk
drewp@bigasterisk.com
parents:
3
diff
changeset
|
89 |
c8a41359505c
server provides video listing from disk
drewp@bigasterisk.com
parents:
3
diff
changeset
|
90 def main(): |
3 | 91 |
6
ccfea3625cf6
render thumbs and display them (no video player at all atm)
drewp@bigasterisk.com
parents:
5
diff
changeset
|
92 |
3 | 93 app = Starlette( |
94 debug=True, | |
95 routes=[ | |
4
c8a41359505c
server provides video listing from disk
drewp@bigasterisk.com
parents:
3
diff
changeset
|
96 Route('/video/api/', root), |
c8a41359505c
server provides video listing from disk
drewp@bigasterisk.com
parents:
3
diff
changeset
|
97 Route('/video/api/videos', videos), |
3 | 98 ], |
99 ) | |
100 | |
101 app.add_middleware(PrometheusMiddleware, app_name='video_api') | |
102 app.add_route("/metrics", handle_metrics) | |
4
c8a41359505c
server provides video listing from disk
drewp@bigasterisk.com
parents:
3
diff
changeset
|
103 return app |
3 | 104 |
105 | |
4
c8a41359505c
server provides video listing from disk
drewp@bigasterisk.com
parents:
3
diff
changeset
|
106 app = main() |