view video_file_store.py @ 24:1a9a8af1aa19

breadcrumbs and tree view UI
author drewp@bigasterisk.com
date Mon, 17 Apr 2023 13:35:18 -0700
parents cf6842952e13
children ed16fdbb3996
line wrap: on
line source

import asyncio
import hashlib
import re
import os
from dataclasses import dataclass
from pathlib import Path
from typing import Iterable, Iterator, NewType

IGNORE = {'_thumb'}


@dataclass
class VideoFile:
    diskPath: Path
    webRelPath: str
    label: str
    # perms, playlists, req by/when


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 IGNORE:
                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)))

    async def save(self, name: str, chunks: Iterator[bytes]):
        p = self.top / name
        if p.exists():
            raise ValueError(f'{p} exists')
        data = b''
        for c in chunks:
            data += c
        p.write_bytes(data)

    def folderTree(self):
        out = {'name': 'TOP'}

        def fill(node: dict, pathToHere: Path):
            for subName in sorted(os.listdir(pathToHere)):
                if subName in IGNORE:
                    continue
                subDir = pathToHere / subName
                if subDir.is_dir():
                    subNode = {'name': subName}
                    node.setdefault('children', []).append(subNode)
                    fill(subNode, subDir)

        fill(out, self.top)
        return out