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)