DEV Community

Cover image for No GPU, No Cloud: Four Python Scripts That Package Stock Assets for Three Platforms

No GPU, No Cloud: Four Python Scripts That Package Stock Assets for Three Platforms

No GPU, No Cloud: Four Python Scripts That Package Stock Assets for Three Platforms

I still remember the moment the rejection landed. I had spent a weekend generating a batch of vector assets, opened the Adobe Stock contributor portal, and watched the upload fail on the CSV uploaded to the system. Wrong column count. Seven hours of production, blocked by a header row. That was the day I stopped trusting "I'll just build the CSV by hand" and wrote the first of the four scripts that now package everything I sell.

Here is what actually ships my work, and why each piece exists.

The four scripts and the two dependencies

The whole kit is four standalone Python files. No framework, no cloud account, no GPU.

  • make_metadata.py builds the exact Adobe (5 column), Vecteezy (4 column) and Dreamstime (15 column) upload CSVs from a folder of files.
  • upload_tracker.py is a ledger that remembers what you already posted, so a re-run never double-posts.
  • md2pdf.py turns a Markdown file into a styled PDF with headings, tables, lists and links.
  • pack_product.py bundles a product: it zips the files and renders a cover at 1280x720 plus a thumbnail at 600x600.

Only two third-party packages are involved: Pillow for the covers in pack_product.py, and reportlab for the PDF output in md2pdf.py. The metadata and tracker scripts use the standard library alone, so you can run them without installing anything.

Why metadata is the real bottleneck

All three platforms reject a wrong CSV header instantly. Not on review, not a day later. Instantly, at upload. Adobe wants five columns, Vecteezy wants four, Dreamstime wants fifteen, and none of them agree on the order.

make_metadata.py writes 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
python make_metadata.py ./my-assets --platform dreamstime --out dreamstime.csv --ext .jpg
Enter fullscreen mode Exit fullscreen mode

You can pass --titles titles.json to supply real titles and keywords. Without it, the filename gets title-cased and used as the title, which is fine for a first pass. Replace it before you submit for real.

The double-post trap

The fastest way to get a contributor account flagged is running your batch twice. It happens to almost everyone: you upload, something looks off, you re-run the script, and now the same asset exists twice.

upload_tracker.py makes that impossible:

python upload_tracker.py pending items.txt   # shows what is NOT done yet
python upload_tracker.py done "asset-001.jpg"
python upload_tracker.py status
Enter fullscreen mode Exit fullscreen mode

A ledger beats memory. On a 39 product run, nobody remembers which sticker got posted on Tuesday.

Packaging on 1 vCPU and 2 GB of RAM

This entire pipeline ran on a single core, 2 GB of RAM, with no GPU. That is not a brag, it is a constraint I had to design around. A few habits made it work:

  • One process at a time. Never fan out image jobs in parallel on a one core box.
  • Free memory between items. Call gc.collect() after each asset.
  • Measure peak RSS, not average. resource.getrusage(RUSAGE_SELF).ru_maxrss tells you the real ceiling.
  • Process one file per subprocess for heavy render steps, so memory goes back to the OS.
  • Render at the resolution you need. Checking a preview at 60 dpi beats rendering it at 300.

Packaging itself is one command:

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

That writes my-product.zip, my-product-cover.jpg and my-product-thumb.jpg into a _system folder. Point it elsewhere with --dir or the PACK_ROOT environment variable, no need to edit the script.

A zip with a real cover sells. A bare file does not.

The folder layout that keeps a catalogue from rotting

Structure is what separates a product line from a junk drawer:

project/
  assets/            raw output (one file per asset)
  output/            ready-to-submit files + metadata.csv
  packs/             finished product zips + covers
  research/          your notes and sources
  scripts/           these tools
Enter fullscreen mode Exit fullscreen mode

Keep research/ forever. Your scrape logs and notes are the raw material for the next product. Deleting them to clean up is a mistake this project made once and will not repeat.

The numbers behind this

These scripts are the packaging layer of a pipeline that produced 39 finished products, 856 MB of output, backed by roughly 19,000 lines of pipeline code in the parent project and 300 plus pages of research behind the rules the scripts enforce. All of it validated on 1 vCPU, 2 GB of RAM and no GPU.

Above the four scripts sits a free stack: an orchestrator that schedules jobs, retries failures and makes decisions, plus a local LLM proxy for model access, both at zero cost. No cloud bill, no rented GPU.

Where to get the full system

The scripts are the tool. The rules they enforce, the folder layout, the RAM discipline, the platform header specs and the order of operations, are the playbook. I packaged all of it as the ai sticker factory playbook, because the four scripts alone will still leave you guessing what to feed them and in what order.

Build the pipeline once. Then let it run while you make more art.

Get ai sticker factory playbook


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

Top comments (0)