Wrote a little slideshow maker in python to convert a series of screenshots into a webpage. useful to capture screenshots of talks and convert them into a web page. written with the help of chatgpt. You can see one in action here.
#!/usr/bin/env python3
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "pillow",
# ]
# ///
import os
import sys
import argparse
from PIL import Image
from pathlib import Path
from xml.dom.minidom import Document
def get_creation_time(filepath):
return int(os.path.getmtime(filepath))
def compress_to_webp(image_path, output_path):
img = Image.open(image_path).convert("RGB")
img.save(output_path, "webp", quality=85, method=6)
def generate_html(slide_title, images, output_path):
doc = Document()
html = doc.createElement("html")
doc.appendChild(html)
head = doc.createElement("head")
meta = doc.createElement("meta")
meta.setAttribute("charset", "UTF-8")
meta2 = doc.createElement("meta")
meta2.setAttribute("name", "viewport")
meta2.setAttribute("content", "width=device-width, initial-scale=1.0")
script = doc.createElement("script")
script.appendChild(doc.createTextNode(""))
script.setAttribute("src", "https://cdn.tailwindcss.com")
title = doc.createElement("title")
title.appendChild(doc.createTextNode(slide_title))
for el in [meta, meta2, script, title]:
head.appendChild(el)
html.appendChild(head)
body = doc.createElement("body")
body.setAttribute("class", "bg-gray-100")
# Page title
page_title = doc.createElement("h1")
page_title.setAttribute("class", "text-4xl font-bold text-center text-gray-900 mb-12")
page_title.appendChild(doc.createTextNode(slide_title))
container = doc.createElement("div")
container.setAttribute("class", "max-w-screen-lg mx-auto p-8 space-y-16")
container.appendChild(page_title)
for img in images:
slide = doc.createElement("div")
slide.setAttribute("class", "bg-white rounded-lg shadow-lg p-4 flex justify-center")
img_tag = doc.createElement("img")
img_tag.setAttribute("src", img)
img_tag.setAttribute("class", "max-w-full max-h-screen object-contain")
slide.appendChild(img_tag)
container.appendChild(slide)
body.appendChild(container)
html.appendChild(body)
with open(output_path, "w") as f:
f.write(doc.toprettyxml(indent=" "))
def main():
parser = argparse.ArgumentParser(description="Compress images and generate a Tailwind-styled HTML slideshow")
parser.add_argument("input_dir", help="Directory containing input images")
parser.add_argument("output_dir", help="Directory to save output images and index.html")
parser.add_argument("--skip", action="store_true", help="Skip compression and renaming, use existing images")
# add a title argument
parser.add_argument("--title", default="Slideshow",help="Title of the slideshow")
args = parser.parse_args()
input_dir = Path(args.input_dir)
output_dir = Path(args.output_dir)
output_dir.mkdir(parents=True, exist_ok=True)
supported_exts = [".jpg", ".jpeg", ".png", ".bmp", ".tiff", ".webp"]
images = [f for f in input_dir.iterdir() if f.suffix.lower() in supported_exts]
if args.skip:
sorted_imgs = [str(f.name) for f in sorted(images)]
for f in images:
target = output_dir / f.name
if not target.exists():
target.write_bytes(f.read_bytes())
else:
timestamp_to_file = {}
for img in images:
ts = get_creation_time(img)
new_name = f"{ts}.webp"
output_file = output_dir / new_name
compress_to_webp(img, output_file)
timestamp_to_file[ts] = new_name
sorted_imgs = [str(timestamp_to_file[k]) for k in sorted(timestamp_to_file)]
generate_html(args.title, sorted_imgs, output_dir / "index.html")
if __name__ == "__main__":
main()