import json import os from shutil import copytree from pathlib import Path, PurePath import subprocess from jinja2 import Template, Environment, PackageLoader, select_autoescape import webvtt class VideoPage: THUMBNAIL_GEOMETRY = '200x200' def __init__(self, path): self.name = path.name self.original = None self.author = None self.path = path self.video = None self.vtt = None self.srt = None self.ass = None self.cover = None self.thumbnail = None self.files = [] self.search_media() def search_media(self): for entry in self.path.iterdir(): if entry.name == 'metadata.json': with entry.open('r') as metadatafile: self.metadata = json.load(metadatafile) if 'name' in self.metadata: self.name = self.metadata['name'] if 'original' in self.metadata: self.original = self.metadata['original'] if 'author' in self.metadata: self.author = self.metadata['author'] elif entry.name.endswith('mp4'): self.video = entry self.files.append(entry) elif entry.name.endswith('vtt'): self.vtt = entry elif entry.name.endswith('srt'): self.srt = entry self.files.append(entry) elif entry.name.endswith('ass'): self.ass = entry self.files.append(entry) elif entry.name.endswith('_thumbnail.jpg'): self.thumbnail = entry elif entry.name.endswith('jpg'): self.cover = entry elif entry.name == 'index.html': continue else: self.files.append(entry) if self.vtt is None and self.srt is not None: srtfile = self.path / self.srt self.vtt = self.path / "{}.vtt".format(self.path.name) webvtt.from_srt(str(self.srt.absolute())).save(str(self.vtt.absolute())) if self.cover is None and self.video is not None: self.cover = self.path / "{}.jpg".format(self.name) command = [ 'ffmpeg', '-loglevel', 'quiet', '-i', str(self.video.absolute()), '-vcodec', 'mjpeg', '-vframes', '1', '-an', '-f', 'rawvideo', '-ss', '2', str(self.cover.absolute()), ] subprocess.check_call(command) if self.thumbnail is None and self.cover is not None: self.thumbnail = self.path / "{}_thumbnail.jpg".format(self.name) subprocess.check_call([ 'convert', str(self.cover.absolute()), '-resize', self.THUMBNAIL_GEOMETRY, str(self.thumbnail.absolute()), ]) def get_context_data(self): return { 'video': self, } def render(self, builder, context): ctx = self.get_context_data() ctx.update(context) builder.render('video.html', self.path, ctx) class Builder: def __init__(self, root_folder, root_path=PurePath('/')): self.root_folder = root_folder self.root_path = root_path self.static_dir = Path(__file__).parent / 'static' self.env = Environment( loader=PackageLoader('negromateweb', 'templates'), autoescape=select_autoescape(['html']), ) self.env.filters['url'] = self.url def url(self, path): return self.root_path / path.relative_to(self.root_folder) def render(self, template, target, context): html_file = target / 'index.html' page_template = self.env.get_template(template) with html_file.open('w') as page: page.write(page_template.render(context)) def build(self): videos = [] for entry in self.root_folder.iterdir(): if entry.name == 'static': continue if entry.is_dir(): print("building {}".format(entry.name)) try: videopage = VideoPage(entry) except Exception as e: raise e print("Error: {}".format(e)) continue videos.append(videopage) global_context = { 'videos': videos, 'root_path': self.root_path, } for video in videos: video.render(self, global_context) self.render('home.html', self.root_folder, global_context) static = self.root_folder / 'static' if not static.exists(): path.mkdir() subprocess.check_call([ 'rsync', '-ra', str(self.static_dir), str(self.root_folder.absolute()), ])