pinforge
Build vertical 1000×1500 Pinterest pins from an image and a few lines of text, and work out what to publish first from a Google Search Console export — counted separately for every locale.
Two small things that turned out to matter when running a multilingual site's Pinterest account for a month.
pip install pinforge
Why per-locale ranking
If your site has several language versions, demand in each one is different. Summing impressions across the whole site and reusing that single order for every language sends your best material to the wrong audience.
Concrete example from the site this was written for — same 78 tarot cards, four languages, the most-searched card in each:
| Locale | Top item | Impressions |
|---|---|---|
| Ukrainian | Six of Wands | 308 |
| Russian | Ten of Cups | 270 |
| English | Four of Wands | 158 |
| French | The Magician | 19 |
Four different answers. Not the same list in a different order — different items.
from pinforge import rank_by_locale
queues = rank_by_locale(
"pages.csv", # Pages export from Search Console
item_pattern=r"/card/([^/?]+)$",
locales=("ru", "uk", "fr"), # anything else falls into default_locale
default_locale="en",
)
for slug, impressions in queues["uk"][:5]:
print(slug, impressions)
Works with the CSV as Search Console exports it, in any interface language — column names are detected, not hard-coded.
Composing a pin
from pinforge import PinStyle, compose
style = PinStyle(
script_font="GreatVibes-Regular.ttf", # brand name
body_font="PlaypenSans.ttf", # title and subtitle
seed=7, # same seed = same starfield
)
compose(
out="pin.png",
image="six-of-wands.jpg",
title="Six of Wands",
subtitle="Victory · Recognition · Success",
footer="example.com",
brand="Example",
style=style,
)
Produces a dark, starfield-backed pin: brand at the top, the image framed and
glowing in the middle, title and subtitle below, site address at the bottom.
Colours, size, star density and fonts are all on PinStyle.
Fonts are optional — without them Pillow's default is used and the result is plain but valid.
Command line
pinforge compose --image card.jpg --title "Six of Wands" \
--subtitle "Victory · Recognition · Success" \
--footer example.com --brand Example --out pin.png
pinforge rank --pages pages.csv --pattern '/card/([^/?]+)$' \
--locales ru,uk,fr --top 5
Where it came from
Written for tarot-magic.com, a tarot reference covering all 78 cards in English, Russian, Ukrainian and French. It generates the card pins for four language boards, and the ranking decides the order they go out in — which is how the per-locale difference above was discovered in the first place.
MIT licensed.
Metadata
Release files for pinforge 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pinforge-0.1.0.tar.gz | 7.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pinforge-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 16.7 kB
Release files / pinforge-0.1.0.tar.gz
| Download URL | pinforge-0.1.0.tar.gz |
|---|---|
| Size | 7.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d28b2a8cb0527847037f4b0bc68b67a0ba5cecb6dfc4b495ca4223ddbf4bd83d
|
|
BLAKE2b-256 checksum How to use checksums |
4dd489fe243e5c479c6206d689b4fb7c40ecbe9a6c58a4004ea30dbfc87c29ef
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.6
|
Release files / pinforge-0.1.0-py3-none-any.whl
| Download URL | pinforge-0.1.0-py3-none-any.whl |
|---|---|
| Size | 9.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3c129ff303844d45b2c1184988fb38c0522812bdffcd8a61ddafd3183fa26d23
|
|
BLAKE2b-256 checksum How to use checksums |
a6d62becbcf9021babdf3a406c8e587509ff4f471a1b1bb1c3e8d3c69917a742
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.6
|