I lost an hour to a cover image once, and I want to explain why that hour is now two lines of Python.
The moment: I had finished a product zip, thrown a screenshot of one of the assets on top as the cover, and uploaded it. The listing looked fine on my laptop. On the sales page it was a 900×500 rectangle sitting in a grid of square thumbnails, cropped weird, title unreadable. I fixed it by hand, then fixed it again on the next product, then wrote pack_product.py so I would never fix it by hand again.
Here is what the script does and why it renders two images instead of one.
Two images, because there are two surfaces
Every place your product shows up falls into one of two shapes. Wide banners, like a Gumroad or Payhip hero image, want something close to 1280×720. Square grids, like marketplace listings and category pages, want 600×600. If you upload one image and let the platform crop it, the platform decides what gets cut off. It will usually cut off your title.
So pack_product.py renders both from the same source folder:
-
my-product-cover.jpg, 1280×720, with the product title and subtitle laid in -
my-product-thumb.jpg, 600×600, centered so nothing important sits near an edge
One command:
python pack_product.py my-product "My Product" "A short subtitle" 9
The 9 is the number of preview assets to pull from the folder for the collage. It writes my-product.zip plus both images into a _system folder. You can move that with --dir or the PACK_ROOT environment variable, so the script never needs editing.
Why Pillow and why it fits a 2 GB server
The whole pipeline runs on 1 vCPU, 2 GB RAM, no GPU. Pillow is the only imaging dependency, and it is enough. The cover is text and a grid of preview images, not a render job.
Two habits matter at this size. First, one process at a time. Fanning out image jobs in parallel on a single core just adds swap pressure. Second, Pillow loads full decoded bitmaps into RAM, so the script calls gc.collect() after each asset and I measure peak RSS with resource.getrusage(RUSAGE_SELF).ru_maxrss, not average usage. Peak is what gets your process killed.
The other trick is resolution discipline. The cover only needs to be 1280 pixels wide, so there is no reason to touch a 300 dpi source. The script downsamples previews before compositing. Inspecting at the resolution you need is the single easiest memory win in this whole pipeline.
The zip is half the product
The cover gets the click. The zip gets the refund rate. pack_product.py bundles the actual files with a sensible internal structure, so a buyer unzips into something that looks organized instead of forty loose files. Same reason md2pdf.py exists next to it: content that looks like a product sells like a product. A bare file does not.
Both scripts are deliberately separate. Packaging and document rendering fail in different ways, and when one breaks at 2 a.m. I want to know which one in five seconds.
Where this sits in the pipeline
The four scripts in the starter kit split cleanly by dependency:
-
make_metadata.pywrites the exact upload CSVs: Adobe's 5-column header, Vecteezy's 4-column, Dreamstime's 15-column. All three channels reject a wrong header instantly, so the script writes the header they demand and nothing else. -
upload_tracker.pyis a ledger so a re-run never double-posts. Double-posting a batch is the fastest way to get a stock account flagged. -
md2pdf.pyneeds reportlab. Everything else needs Pillow or nothing.make_metadata.pyandupload_tracker.pyrun on the standard library alone, no install.
The numbers, honestly
This is the same pipeline that packaged 39 products, 856 MB of output, from about 19,000 lines of parent-project code and 300+ pages of research behind the rules the scripts enforce. On the product side: 1,860 assets made, 1,077 ready or submitted. The first paid sale was for the Digital Asset Factory itself, $2.80. I am not going to pretend that number is anything but a start, but it is a real number, from a system that ran on a server that costs almost nothing.
If you want the same four scripts, the folder layout that keeps a catalogue from turning into a junk drawer, and the RAM notes for running it on a 1-core box, that is the Digital Asset Factory starter kit. It is the tool I built out of this work, not a course.
Building digital products? Free guides & kits for digital product sellers.
Top comments (0)