#!/usr/bin/env python3 "Creates a static site for redacted.life" import argparse from collections import UserList from datetime import datetime import os import os.path as path import re import subprocess import sys import jinja2 import markdown def gen_name(date, slug): "Returns to file name" return date.strftime("%Y-%m-%d-") + slug class EpisodeList(UserList): "Represents list of episodes" def __init__(self, data, output, template): super().__init__(data) self.output = output self.template = template def sort(self, *_args, **_kwargs): "Sorts the EpisodeList" super().sort(key=lambda x: x.date, reverse=True) def generate_thumbnails(self): "Generates thumbnails for all the videos" if not path.isdir(self.output + "assets"): os.mkdir(self.output + "assets") if not path.isdir(self.output + "assets/thumbnails"): os.mkdir(self.output + "assets/thumbnails") for episode in self.data: location = (self.output + "assets/thumbnails/" + gen_name(episode.date, episode.slug) + ".jpg") episode.store_thumbnail(location) def generate_atom(self): "Generates the Atom feed" def generate_site(self): "Generates the entire " class Episode: "Represents one episode of podcast" def __init__(self, date, slug, title, show_notes, video_src): self.date = date self.slug = slug self.title = title self.show_notes = markdown.markdown(show_notes) self.video = video_src def render(self, template, thumbnail_src): "Renders the Episode with the given template" return template.render( title=self.title, show_notes=jinja2.Markup(self.show_notes), thumbnail_src=thumbnail_src, video_src=self.video ) def store_thumbnail(self, location): "Stores the thumbnail for given image at path" args = ["ffmpeg", "-i", self.video, "-ss", "00:00:01.000", "-vframes", "1", location] subprocess.run(args, check=False) def main(): "Main method" root = path.dirname(sys.argv[0]) parser = argparse.ArgumentParser() parser.add_argument("input_dir", help="Input directory") parser.add_argument("output_dir", help="Output directory") args = parser.parse_args() input_dir = args.input_dir.rstrip("/") + "/" output_dir = args.output_dir.rstrip("/") + "/" # Input validation if not all(path.isdir(i) for i in (input_dir, input_dir + "md", input_dir + "videos")): print("Invalid Input", file=sys.stderr) return if not path.isdir(args.output_dir): os.mkdir(args.output_dir) template = jinja2.Environment( loader=jinja2.FileSystemLoader(root), autoescape=jinja2.select_autoescape("html") ).get_template("index.html") podcast = EpisodeList([], output_dir, template) split = re.compile(r"((?P\d{4}-[01]?\d-[0123]?\d)-(?P.*).md)") for file in os.listdir(input_dir + "md"): match = split.match(file) if not match: print(f"Invalid filename: {file}", file=sys.stderr) continue date = datetime.strptime(match.group("date"), "%Y-%M-%d") slug = match.group("slug") with open(input_dir + "md/" + file) as episode: title = episode.readline() show_notes = episode.read() video = input_dir + "videos/" + gen_name(date, slug) + ".mp4" podcast.append(Episode(date, slug, title, show_notes, video)) podcast.sort() podcast.generate_thumbnails() podcast.generate_atom() podcast.generate_site() if __name__ == "__main__": main()