DEV Community

Cover image for The Packaging Layer That Makes One Asset Sell Three Ways: Zips, Covers And 1280x720 Renders

The Packaging Layer That Makes One Asset Sell Three Ways: Zips, Covers And 1280x720 Renders

I had 30 finished assets sitting in a folder and nothing to sell. This was a real moment. I had the art done, the prompt engine worked, and I spent a whole evening manually zipping files, screenshotting covers at whatever size happened to be on my screen, and hand-typing CSV rows in a spreadsheet. Around asset 12 I pasted a row into Adobe Stock's uploader and got rejected instantly. Wrong column count. I had written 6 columns for a template that demands exactly 5. Every row after that was wrong too.

That night I stopped making art and started writing the packaging layer. Here is what I learned about why one asset should sell three ways, and what the scripts that do it actually look like.

The three ways one asset sells

The same body of work can ship as three distinct products, and each one needs different packaging:

  1. Stock uploads. Individual files plus a metadata CSV in the exact format each platform demands. Adobe Stock wants a 5-column header. Vecteezy wants 4 columns. Dreamstime wants 15. Get the header wrong and the upload is rejected before anyone looks at your art.
  2. Product packs. A zip of related files, sold as one unit on Gumroad or your own site. This needs a cover that reads at a glance.
  3. Listing renders. A 1280x720 cover and a 600x600 thumbnail. These are the images buyers actually see. A bare zip with no preview does not sell, I know because my first listing had none and sat at zero.

One asset folder feeds all three. The mistake is treating them as three separate chores done by hand.

Metadata is a format problem, not a writing problem

The CSV headers are not negotiable. Adobe's uploader checks the header row and rejects a wrong one immediately, which is what burned me. So I wrote make_metadata.py to emit the header each platform demands and nothing else:

python make_metadata.py ./my-assets --platform adobe   --out metadata.csv --ext .jpg
python make_metadata.py ./my-assets --platform vecteezy --out vecteezy.csv --ext .jpg
Enter fullscreen mode Exit fullscreen mode

It walks a folder, title-cases filenames into titles, and writes the rows. You pass a titles.json if you have real titles and keywords. Without one, the filename title is fine for a first pass, but replace it before you submit. This script uses the Python standard library alone. No pip install. Two platforms checked, one command each, and the header can never be wrong again.

Double-posting is the fastest way to get flagged

This one scared me straight. Running a batch twice, posting the same asset to the same platform twice, is a known trigger for account review. When you automate anything, re-runs are not an if, they are a when. A cron job fires twice, you forget a batch already went out, whatever.

upload_tracker.py is a tiny ledger:

python upload_tracker.py pending items.txt
python upload_tracker.py done "asset-001.jpg"
python upload_tracker.py status
Enter fullscreen mode Exit fullscreen mode

It remembers what you posted so a re-run skips finished items. Also standard library only. It is maybe the least glamorous 100 lines I have written and it has prevented more damage than anything else in the pipeline.

Covers and zips in one command

pack_product.py bundles a product and renders its images:

python pack_product.py my-product "My Product" "A short subtitle" 9
Enter fullscreen mode Exit fullscreen mode

That writes my-product.zip, a 1280x720 cover, and a 600x600 thumbnail into a _system folder next to where you run it. Point it elsewhere with --dir or the PACK_ROOT environment variable. The renders use Pillow, which is one of only two third-party packages in the whole kit. The other is reportlab, for md2pdf.py, which turns a Markdown doc into a styled PDF product in one command.

Why 1280x720 and 600x600? Because those are the sizes that look right in listing previews and thumbnail grids without wasting render time. Render at the resolution you need. Inspecting a preview at 60 dpi instead of 300 is one of the habits that let this whole thing run on a 1 vCPU, 2 GB RAM server with no GPU.

The small-server habits that make this repeatable

The full pipeline, the one that packaged 39 products totaling 856 MB of output, ran on that tiny box. It works because of habits, not hardware:

  • One process at a time. Never fan out image jobs in parallel on 1 core.
  • Call gc.collect() after each asset to free memory between items.
  • Measure peak RSS with resource.getrusage(RUSAGE_SELF).ru_maxrss, not average. Average lies about your ceiling.
  • One file per subprocess for heavy render steps, so memory actually returns to the OS.

None of this is clever. It is just what lets a solo maker run a real pipeline on hardware that costs almost nothing.

Where the kit comes in

Everything above is now four standalone scripts: make_metadata.py, upload_tracker.py, md2pdf.py, pack_product.py. No framework, no cloud account, two pip packages. They are the same ones that packaged 39 products, backed by around 19,000 lines of pipeline code in the parent project and 300+ pages of research behind the rules they enforce. I put them together as the ai sticker factory playbook, and if you want the packaging layer without rebuilding it from a rejected CSV at midnight, it is there.

Get ai sticker factory playbook


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

Top comments (0)