Dev.to AI 🤖 Ai 👁 0 📖 4 min read

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

First paid sale: $2.80. Digital Asset Factory. By then I had 1,860 assets made and 1,077 ready or submitted. The sale did not come from better art. It came from a zip that finally had a cover on it. I shipped zips witho

First paid sale: $2.80. Digital Asset Factory. By then I had 1,860 assets made and 1,077 ready or submitted. The sale did not come from better art. It came from a zip that finally had a cover on it.

I shipped zips without covers for weeks. Bare folder, zip it, upload it, wonder why nobody clicked. The files were fine. The listing was not a listing. It was a gray placeholder in a grid of thumbnails that actually looked like products.

A zip is not a product until it renders as one

Storefronts show your package as an image before anyone reads a word. No image, no click. Every pack I build now writes three files in one pass: the zip, a 1280x720 cover, and a 600x600 thumbnail. Not "nice to have". The pack step does all three because I will skip an optional step every single time, and so will you.

pack_product.py takes the folder, a title, a subtitle, and a count, then writes all three into a _system folder next to where you run it. One command, no editing the script, --dir or PACK_ROOT if you want it elsewhere.

Why 1280x720 and not something clever

The preview box is wide. 1280x720 is 16:9, so it fills that box with no letterbox bars, no crop guessing. It is also small enough that Pillow renders it on a 1 vCPU box in well under a second, which matters when you are packing 39 products in a row. 600x600 is the square size, for the grids that crop to square. Two sizes, two renders, one source image. I tried a third size once. It added a render pass and changed nothing about sales.

Rule I keep: cover and thumbnail come from the same source, generated in the same run as the zip. If the zip is new, the cover is new. Stale covers are worse than none because they still look real.

The cover is cheap. The metadata header is the gate.

Covers cost a render. Metadata costs a rejection.

Adobe takes a 5 column header. Vecteezy takes 4. Dreamstime takes 15. Wrong header, or a column out of order, and the import fails instantly. Not after review. Immediately, whole batch.

make_metadata.py writes exactly the header each channel demands and nothing else. It builds the CSV from a folder of files, one flag for the platform, one for the extension. Stdlib only, so no install. Without --titles titles.json it title-cases the filename and uses that as the title. That is fine for a first pass and wrong for a submission. Replace the titles before you upload.

A re-run should be boring

Fastest way to get an account flagged is running your batch twice. Second upload of the same 40 files looks exactly like a bot.

upload_tracker.py is a ledger. pending shows what is not done. done "asset-001.jpg" marks one. status prints the count. Rerun the batch, nothing posts twice. It is a text file and a few subcommands, standard library, and it is the single cheapest protection in the pipeline.

All of it ran on 1 vCPU and 2 GB

No GPU, no cloud account, no fan-out. What made that work:

  • One process at a time. Never parallel image jobs on one core.
  • gc.collect() after each asset.
  • Measure peak RSS, not average: resource.getrusage(RUSAGE_SELF).ru_maxrss. Average hides the spike that kills you.
  • Heavy render steps in their own subprocess so memory goes back to the OS.
  • Render at the resolution you actually need. Inspecting a preview at 60 dpi beats rendering it at 300.

That list is boring on purpose. Boring is why it finished 39 products instead of dying at number 12.

The folder layout that survives a catalogue

project/
  assets/     raw output, one file per asset
  output/     ready-to-submit files + metadata.csv
  packs/      zips, covers, thumbnails
  research/   notes and sources
  scripts/    the tools

Keep research/ forever. Scrape logs and notes are raw material for the next product. I deleted a folder to "clean up" once. Will not do it again.

Numbers, not rounded up

1,860 assets made. 1,077 ready or submitted. 39 products packaged, 856 MB of output. About 19,000 lines of pipeline code in the parent project, and 300+ pages of research behind the rules the scripts enforce. Validated on 1 vCPU, 2 GB RAM, no GPU. First paid sale: $2.80.

That $2.80 is the whole point. It was not the biggest asset or the best vector. It was the first package that looked like something worth opening, because the zip shipped with a cover and correct metadata, generated in the same run, on the same tiny box.

Where this lives

The four scripts above are the packaging layer of a bigger system: metadata CSVs for three channels, the upload ledger, a Markdown to PDF step, and the pack step that forces a cover. I put the whole thing together as Digital Asset Factory, including the folder conventions, the RAM habits, and the rules the scripts enforce, so you do not have to rediscover the 1280x720 lesson by shipping bare zips for three weeks.

Get digital asset factory

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

📰 Read the original article on Dev.to AI

Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.