Skip to content

Instantly share code, notes, and snippets.

@Garmelon
Last active January 14, 2022 15:03
Show Gist options
  • Save Garmelon/54f8978083cefc97fe374b6e5a4ed6b6 to your computer and use it in GitHub Desktop.
Save Garmelon/54f8978083cefc97fe374b6e5a4ed6b6 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
# A small script to collect the xkcd countdown frames from munvoseli's
# xkcd-countdown page or from explainxkcd and render them as a video.
# Requires beautifulsoup4 and requests
# Requires ffmpeg to be installed
import argparse
from pathlib import Path
import re
import subprocess
import bs4
import requests
FPS = 10
TMP = "tmp"
OUT = "out.mp4"
TMP_PATH = Path(TMP)
OUT_PATH = Path(OUT)
def image_names():
i = 1
while True:
yield f"{i:04}.png"
i += 1
def crawl_munvoseli():
r = requests.get("https://raw.githubusercontent.com/munvoseli/xkcd-countdown/master/framestr.js")
for line in re.fullmatch(r".*`(.*)`.*", r.text, flags=re.DOTALL).group(1).splitlines():
img_hash = re.fullmatch(r"(\S+)\s.*", line).group(1)
yield f"https://xkcd.com/count-wimRikmef/imgs/{img_hash}.png"
def crawl_explainxkcd():
r = requests.get("https://www.explainxkcd.com/wiki/index.php/Countdown_in_header_text/images")
soup = bs4.BeautifulSoup(r.text, "html.parser")
image_ul = soup.find(id = "Images").parent.find_next_sibling("ul")
for image in image_ul.find_all("img"):
yield f"https://www.explainxkcd.com/{image['src']}"
def render_result():
OUT_PATH.unlink(missing_ok=True)
subprocess.run(["ffmpeg", "-r", f"{FPS}", "-i", "tmp/%04d.png", "-pix_fmt", "yuv420p", OUT_PATH], capture_output=True)
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--explainxkcd", action="store_true",
help="Use explainxkcd.com instead of munvoseli.github.com/xkcd-countdown")
args = parser.parse_args()
crawl = crawl_explainxkcd if args.explainxkcd else crawl_munvoseli
tmp = Path("tmp")
tmp.mkdir(parents=True, exist_ok=True)
for (url, name) in zip(crawl(), image_names()):
path = TMP_PATH / name
if path.exists():
print(f"Skipping {name}")
continue
print(f"Downloading {name} from {url}")
r = requests.get(url)
with open(path, "wb") as f:
for chunk in r.iter_content(chunk_size=1024):
f.write(chunk)
print(f"Rendering clip to {OUT_PATH}")
render_result()
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment