DEV Community

Cover image for The 1280 720 Lesson: Why a Zip Isn't Shippable Until It Has a Cover"**

The 1280 720 Lesson: Why a Zip Isn't Shippable Until It Has a Cover"**

I had a folder of 39 finished products and no sales. Not "no sales yet", I mean a zip file sitting on my drive with a filename like bundle-final-v2.zip and nothing else. No cover image. No thumbnail. I genuinely thought the packaging was the easy part, the part you'd knock out in ten minutes after the real work was done.

It took me one failed upload and an evening of reading contributor docs to learn the opposite. The zip isn't the product. The 1280×720 cover is the product. The zip is the delivery mechanism.

What a marketplace actually looks at first

When you upload to Adobe Stock, the review process checks your metadata against a strict CSV format: five columns, and the header row has to match exactly or the file gets rejected before a human ever sees it. Vecteezy wants four columns. Dreamstime wants fifteen. I wrote make_metadata.py to generate all three from a folder of files, because hand-editing CSVs for 58 live assets was how I kept introducing errors.

But metadata gets you accepted. It doesn't get you clicked. On every marketplace and on Gumroad, the buyer sees a thumbnail before they see anything else. A zip file with no rendered preview is invisible. My first sale, $0.99, came from an asset that had a real cover, not from the bare files I'd uploaded before it.

The 1280×720 number is not arbitrary

I picked 1280×720 for covers because it's the standard 16:9 landscape that renders cleanly on Gumroad product pages and looks correct when marketplaces crop it down to smaller preview sizes. For square contexts, like sticker packs and icon bundles, I render a separate 600×600 thumbnail. Two renders, one script, and every product in the catalogue has both.

The mistake I made first was generating covers from the raw assets at full resolution. On my server, which is 1 vCPU and 2 GB RAM with no GPU, that meant one heavy image job could eat the whole box and the pipeline would stall. The fix was boring: render at the resolution you actually need. A cover at 1280×720 does not need a 300 dpi source render. Inspecting previews at 60 dpi, rendering finals at target size, and calling gc.collect() between items keeps peak memory flat. I measure with resource.getrusage(RUSAGE_SELF).ru_maxrss, not by vibes.

Packaging is where makers actually stall

Making the art is the fun part. What stalled me, and what stalls almost everyone I talk to, is the four steps after it:

  1. Metadata CSVs with the exact header each platform demands.
  2. Not double-posting, because running your batch twice is the fastest way to get an account flagged. I keep a ledger in upload_tracker.py that remembers what's done, so a re-run is safe.
  3. Turning a Markdown doc into something that looks like a product, which is one command with md2pdf.py and reportlab.
  4. The zip plus cover plus thumbnail, which is pack_product.py and Pillow.

That's the whole gap between "I made 1,700 assets" and "I have 39 shippable products". The first number is easy to hit with a prompt engine. The second number only moves when packaging is automated.

The folder structure that kept me sane

Without a fixed layout, 39 products became a junk drawer. Mine is simple: assets/ for raw output, output/ for ready-to-submit files plus the CSV, packs/ for finished zips and covers, research/ for notes and scrape logs, scripts/ for the tools.

One rule I learned the hard way: never delete research/. I once cleaned it up to "tidy the project" and threw away the raw material for three future products. Notes and logs are inventory, not clutter.

Why the cover matters more than the zip contents

Here's the uncomfortable part. The buyer cannot inspect the zip before purchase. They judge the cover, the title, and the price. My $0.99 first sale taught me that a modest asset with a clean, honest 1280×720 cover sells, and a great asset with no cover does not. Every hour I spent on cover rendering paid back faster than an hour spent on another vector variant.

So my rule now: no zip leaves packs/ without a cover and a thumbnail. If pack_product.py can't render them, the product isn't shippable. That single check is what turned a folder of files into a catalogue.

The tool I built from this

Everything above is now four standalone Python scripts: make_metadata.py (Adobe 5-col, Vecteezy 4-col, Dreamstime 15-col CSVs), upload_tracker.py, md2pdf.py, and pack_product.py with the 1280×720 cover and 600×600 thumbnail. Two dependencies total, Pillow and reportlab, and the metadata and tracker scripts run on the standard library alone. The whole thing runs on the same 1 vCPU, 2 GB box. It's part of the full digital asset factory system, with the same pipeline that produced 39 products and 856 MB of output from ~19,000 lines of code.

If you're at the "1,700 assets, no products" stage, this is the packaging layer that got me past it.

Get digital asset factory


Building digital products? Free guides & kits for digital product sellers.

Top comments (0)