view video.py @ 43:a7b644dc1b4b

ridiculous fix for vite not proxying urls with . in them
author drewp@bigasterisk.com
date Fri, 06 Dec 2024 00:58:46 -0800
parents b5b29f6ef5cb
children 239a83d46d48
line wrap: on
line source

import asyncio
from functools import partial
import json
import logging

import uvicorn
from prometheus_client import Gauge
from sse_starlette.sse import EventSourceResponse
from starlette.applications import Starlette
from starlette.requests import Request
from starlette.responses import HTMLResponse, JSONResponse, Response
from starlette.routing import Route
from starlette_exporter import PrometheusMiddleware, handle_metrics

import dl_queue
from video_file_store import VideoFileStore
from video_ingest import VideoIngest
from mongo_required import open_mongo_or_die
import pymongo.database
import thumbnail

logging.basicConfig(level=logging.DEBUG)
log = logging.getLogger()
logging.getLogger('sse_starlette').setLevel(logging.WARNING)
logging.getLogger('pymongo').setLevel(logging.INFO)


def root(req):
    return HTMLResponse("api")


async def videos(req: Request) -> JSONResponse:
    subdir = req.query_params.get('subdir', '/')
    if not subdir.endswith('/'):
        # raise ValueError(f'not a dir {subdir=}')
        # ok we'll list the parent dir underneath
        subdir = '/'.join(subdir.split('/')[:-1])  # todo move to FE
    else:
        subdir = subdir[:-1]
    if subdir == "":
        subdir = "./"
    if not (subdir.startswith('/') or subdir == './'):
        raise ValueError(f'not a dir {subdir=}')
    subdir = subdir[1:]
    log.debug(f'videos request corrected to: {subdir=}')

    vfInDir = store.findInDir(subdir)
    return JSONResponse({
        "videos": [{
            'webRelPath': vf.webRelPath,
            'webDataPath': vf.webDataPath,
            'label': vf.label,
        } for vf in vfInDir],
        "subdirs":
        list(store.findSubdirs(subdir)),
    })


def folderTree(req: Request) -> JSONResponse:
    return JSONResponse(store.folderTree())


async def ingestVideoUrl(req: Request) -> Response:
    folder = req.query_params['folder']
    url = await req.body()
    await svc.ingestUrl(url.decode('utf8'), folder)
    return Response(status_code=202)


async def ingestVideoUpload(req: Request) -> Response:
    name = req.query_params['name']
    await svc.addContent(name, req.body())
    return Response(status_code=200)


async def ingestQueue(req: Request) -> EventSourceResponse:

    async def g():
        async for ev in svc.events():
            yield json.dumps(ev)

    return EventSourceResponse(g())


def getDiskPath(fs, webRelPath):
    doc = fs.find_one({'webRelPath': webRelPath})
    if doc is None:
        raise ValueError
    return doc['diskPath']


async def getThumbnail(db: pymongo.database.Database,
                       req: Request) -> Response:
    webRelPath = req.query_params['webRelPath']
    fs = db.get_collection('fs')
    diskPath = getDiskPath(fs, webRelPath)
    th = db.get_collection('thumb')
    async with asyncio.timeout(10):
        data = await thumbnail.getThumbnailData(th, diskPath)
        return Response(content=data, media_type='image/jpeg')


db = open_mongo_or_die().get_database('video')
store = VideoFileStore(db.get_collection('fs'))
svc = VideoIngest(store)


def main():

    app = Starlette(
        debug=True,
        routes=[
            Route('/video/api/', root),
            Route('/video/api/videos', videos),
            Route('/video/api/folderTree', folderTree),
            Route('/video/api/ingest/videoUpload',
                  ingestVideoUpload,
                  methods=['POST']),
            Route('/video/api/ingest/videoUrl',
                  ingestVideoUrl,
                  methods=['POST']),
            Route('/video/api/ingest/queue', ingestQueue),
            Route('/video/api/thumbnail', partial(getThumbnail, db)),
        ],
    )

    app.add_middleware(PrometheusMiddleware, app_name='video_api')
    app.add_route("/video/api/metrics", handle_metrics)
    app.add_route("/metrics", handle_metrics)

    app.state.processTask = asyncio.create_task(dl_queue.process())
    return app


uvicorn.run(main,
            host="0.0.0.0",
            port=8004,
            log_level=logging.INFO,
            factory=True)