Recently Written · git

subread-extension

Show your own .srt over a YouTube video, in time with it. Chrome and Firefox.

git clone https://github.com/equwal/subread-extension

Log | Files | Refs


commit 2920e58dea363c4dbdf3cc4c774e067ef8c052b9
equwal <truex@equwal.com>
2026-09-22 20:48:39 -0700

Add the Chrome Web Store screenshot and promo tile, and the script that makes them

 README.md                              |   1 +
 store/graphics/promo-440x280.png       | Bin 0 -> 8783 bytes
 store/graphics/screenshot-1280x800.png | Bin 0 -> 121766 bytes
 store/listing.md                       |  13 ++++++----
 tools/graphics.py                      |  43 +++++++++++++++++++++++++++++++++
 5 files changed, 52 insertions(+), 5 deletions(-)
diff --git a/README.md b/README.md
index 82f2cb7..5c85131 100644
--- a/README.md
+++ b/README.md
@@ -35,6 +35,7 @@ YouTube, and YouTube's terms do not allow it; this one only draws text.
 | `tests/srt.test.cjs` | `node --test tests/srt.test.cjs` |
 | `tests/mock/server.py` | A stand-in for a watch page, to try the content script by hand |
 | `tools/pack.py` | Makes `dist/subread-chrome.zip` and `dist/subread-firefox.zip` |
+| `tools/graphics.py` | Makes the Chrome screenshot and promo tile in `store/graphics/` |
 | `store/listing.md` | The texts and answers for the two stores |
 
 ## Try it
diff --git a/store/graphics/promo-440x280.png b/store/graphics/promo-440x280.png
new file mode 100644
index 0000000..f515f85
Binary files /dev/null and b/store/graphics/promo-440x280.png differ
diff --git a/store/graphics/screenshot-1280x800.png b/store/graphics/screenshot-1280x800.png
new file mode 100644
index 0000000..11ff0f6
Binary files /dev/null and b/store/graphics/screenshot-1280x800.png differ
diff --git a/store/listing.md b/store/listing.md
index 5a8d114..7c4bbb4 100644
--- a/store/listing.md
+++ b/store/listing.md
@@ -43,9 +43,12 @@ Private: the extension does not read, record or download the video or its sound,
 | Remote code | No |
 | Data usage | Collects none of the listed kinds of data. All three certifications: yes |
 
-## Graphics still to make
+## Graphics
 
-Chrome wants at least one screenshot of 1280 x 800 (or 640 x 400) and a
-440 x 280 promo tile; AMO wants none but shows screenshots when there are
-some. Take them from a real YouTube audiobook with the panel open and a line
-on screen. The icon is `icons/128.png`.
+`python tools/graphics.py` makes `store/graphics/screenshot-1280x800.png` and
+`store/graphics/promo-440x280.png` for Chrome. AMO wants none but shows
+screenshots when there are some. The icon is `icons/128.png`.
+
+The screenshot comes from the stand-in page, and its file button shows the
+language of the browser that took it. Replace it with a shot of a real YouTube
+audiobook, with the panel open and a line on screen.
diff --git a/tools/graphics.py b/tools/graphics.py
new file mode 100644
index 0000000..6f0e412
--- /dev/null
+++ b/tools/graphics.py
@@ -0,0 +1,43 @@
+"""Make the Chrome Web Store graphics in store/graphics/.
+
+screenshot-1280x800.png: docs/screenshots/player.png on a dark field.
+promo-440x280.png: the icon and the name.
+"""
+import pathlib
+from PIL import Image, ImageDraw, ImageFont
+
+ROOT = pathlib.Path(__file__).resolve().parent.parent
+OUT = ROOT / "store" / "graphics"
+BG = (15, 15, 15)
+
+def font(size: int) -> ImageFont.ImageFont:
+    for name in ("segoeuib.ttf", "arialbd.ttf", "DejaVuSans-Bold.ttf"):
+        try:
+            return ImageFont.truetype(name, size)
+        except OSError:
+            pass
+    return ImageFont.load_default()
+
+def screenshot() -> None:
+    shot = Image.open(ROOT / "docs" / "screenshots" / "player.png").convert("RGB")
+    shot = shot.resize((1280, round(shot.height * 1280 / shot.width)), Image.LANCZOS)
+    canvas = Image.new("RGB", (1280, 800), BG)
+    canvas.paste(shot, (0, (800 - shot.height) // 2))
+    canvas.save(OUT / "screenshot-1280x800.png")
+
+def promo() -> None:
+    canvas = Image.new("RGB", (440, 280), BG)
+    icon = Image.open(ROOT / "icons" / "128.png").convert("RGBA")
+    canvas.paste(icon, ((440 - 128) // 2, 40), icon)
+    draw = ImageDraw.Draw(canvas)
+    for text, size, y in (("SubRead", 36, 190), ("your subtitles on YouTube", 20, 236)):
+        f = font(size)
+        w = draw.textlength(text, font=f)
+        draw.text(((440 - w) / 2, y), text, font=f, fill=(240, 240, 240))
+    canvas.save(OUT / "promo-440x280.png")
+
+OUT.mkdir(parents=True, exist_ok=True)
+screenshot()
+promo()
+for p in sorted(OUT.iterdir()):
+    print(p.relative_to(ROOT), Image.open(p).size)