Tweaks to make images + shelves look good on retina displays
[books.alexwlchan.net] / scripts / render_html.py
index 7853abf..c9f11e3 100755 (executable)
@@ -1,6 +1,7 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import datetime
+import hashlib
 import itertools
 import os
 import pathlib
@@ -13,9 +14,11 @@ import frontmatter
 from jinja2 import Environment, FileSystemLoader, select_autoescape
 import markdown
 from markdown.extensions.smarty import SmartyExtension
-from PIL import Image
 import smartypants
 
+from generate_bookshelf import create_shelf_data_uri
+from tint_colors import get_tint_colors, store_tint_color
+
 
 def rsync(dir1, dir2):
     subprocess.check_call(["rsync", "--recursive", "--delete", dir1, dir2])
@@ -160,10 +163,12 @@ def render_date(date_value):
         return date_obj.strftime("%B %Y")
 
 
-def render_individual_review(env, *, review_entry):
+def render_individual_review(env, *, review_entry, **kwargs):
     template = env.get_template("review.html")
     html = template.render(
-        review_entry=review_entry, title=f"My review of {review_entry.book.title}"
+        review_entry=review_entry,
+        title=f"My review of {review_entry.book.title}",
+        **kwargs
     )
 
     out_name = review_entry.out_path() / "index.html"
@@ -175,11 +180,10 @@ def render_individual_review(env, *, review_entry):
 def _create_new_thumbnail(src_path, dst_path):
     dst_path.parent.mkdir(exist_ok=True, parents=True)
 
-    im = Image.open(src_path)
-
-    if im.width > 240 and im.height > 240:
-        im.thumbnail((240, 240))
-    im.save(dst_path)
+    # Thumbnails are 240x240 max, then 2x for retina displays
+    subprocess.check_call([
+        "convert", src_path, "-resize", "480x480>", dst_path
+    ])
 
 
 def thumbnail_1x(name):
@@ -190,19 +194,10 @@ def thumbnail_1x(name):
 def _create_new_square(src_path, square_path):
     square_path.parent.mkdir(exist_ok=True, parents=True)
 
-    im = Image.open(src_path)
-    im.thumbnail((240, 240))
-
-    dimension = max(im.size)
-
-    new = Image.new("RGB", size=(dimension, dimension), color=(255, 255, 255))
-
-    if im.height > im.width:
-        new.paste(im, box=((dimension - im.width) // 2, 0))
-    else:
-        new.paste(im, box=(0, (dimension - im.height) // 2))
-
-    new.save(square_path)
+    subprocess.check_call([
+        "convert",
+        src_path, "-resize", "240x240", "-gravity", "center", "-background", "white", "-extent", "240x240", square_path
+    ])
 
 
 def create_thumbnails():
@@ -225,6 +220,15 @@ def create_thumbnails():
         elif src_path.stat().st_mtime > square_path.stat().st_mtime:
             _create_new_square(src_path, square_path)
 
+        store_tint_color(dst_path)
+
+
+CSS_HASH = hashlib.md5(open('static/style.css', 'rb').read()).hexdigest()
+
+
+def css_hash(_):
+    return f"md5:{CSS_HASH}"
+
 
 def main():
     env = Environment(
@@ -236,9 +240,13 @@ def main():
     env.filters["render_date"] = render_date
     env.filters["smartypants"] = smartypants.smartypants
     env.filters["thumbnail_1x"] = thumbnail_1x
+    env.filters["css_hash"] = css_hash
+    env.filters["create_shelf_data_uri"] = create_shelf_data_uri
 
     create_thumbnails()
 
+    tint_colors = get_tint_colors()
+
     rsync("src/covers/", "_html/covers/")
     rsync("static/", "_html/static/")
 
@@ -252,7 +260,11 @@ def main():
     )
 
     for review_entry in all_reviews:
-        render_individual_review(env, review_entry=review_entry)
+        render_individual_review(
+            env,
+            review_entry=review_entry,
+            tint_colors=tint_colors
+        )
 
     template = env.get_template("list_reviews.html")
     html = template.render(
@@ -264,6 +276,7 @@ def main():
         ],
         title="books i’ve read",
         this_year=str(datetime.datetime.now().year),
+        tint_colors=tint_colors,
     )
 
     out_path = pathlib.Path("_html") / "reviews/index.html"
@@ -278,7 +291,11 @@ def main():
     )
 
     template = env.get_template("list_reading.html")
-    html = template.render(all_reading=all_reading, title="books i’m currently reading")
+    html = template.render(
+        all_reading=all_reading,
+        title="books i’m currently reading",
+        tint_colors=tint_colors
+    )
 
     out_path = pathlib.Path("_html") / "reading/index.html"
     out_path.parent.mkdir(exist_ok=True, parents=True)
@@ -293,7 +310,11 @@ def main():
     all_plans = sorted(all_plans, key=lambda plan: plan.plan.date_added, reverse=True)
 
     template = env.get_template("list_plans.html")
-    html = template.render(all_plans=all_plans, title="books i want to read")
+    html = template.render(
+        all_plans=all_plans,
+        title="books i want to read",
+        tint_colors=tint_colors,
+    )
 
     out_path = pathlib.Path("_html") / "to-read/index.html"
     out_path.parent.mkdir(exist_ok=True, parents=True)
@@ -311,7 +332,9 @@ def main():
 
     template = env.get_template("list_will_never_read.html")
     html = template.render(
-        all_retired=all_retired, title="books i’m never going to read"
+        all_retired=all_retired,
+        title="books i’m never going to read",
+        tint_colors=tint_colors
     )
 
     out_path = pathlib.Path("_html") / "will-never-read/index.html"
@@ -321,7 +344,11 @@ def main():
     # Render the front page
 
     index_template = env.get_template("index.html")
-    html = index_template.render(text=open("src/index.md").read())
+    html = index_template.render(
+        text=open("src/index.md").read(),
+        reviews=all_reviews[:5],
+        tint_colors=tint_colors
+    )
 
     index_path = pathlib.Path("_html") / "index.html"
     index_path.write_text(html)