How shipping the exact scripts that packaged 39 products — not a tutorial — turns install friction into a sales pitch.

How shipping the exact scripts that packaged 39 products, not a tutorial, turns install friction into a sales pitch The moment I remember is a CSV header. I had 60 finished JPGs, an Adobe upload window open, and a spreadsheet I had "fixed" three times. The rejection came back instant and unhelpful. Wrong columns. I had renamed, reordered, and re-saved that file all evening, and the platform would not even look at the rows. Somewhere around 2 a.m. I realized I was not making art anymore. I was doing clerical work, badly, for the fourth time on one product. That night is why the thing I eventually shipped is not a tutorial. It is the same four scripts that got 39 products out the door, 856 MB of finished output, run on a single-core, 2 GB server. A tutorial ends at "here's how you'd do it." This ends at "here's the file that did it." Metadata is where uploads die first Adobe wants 5 columns. Vecteezy wants 4. Dreamstime wants 15. Each one rejects a wrong header be

How shipping the exact scripts that packaged 39 products — not a tutorial — turns install friction into a sales pitch.

How shipping the exact scripts that packaged 39 products, not a tutorial, turns install friction into a sales pitch

The moment I remember is a CSV header. I had 60 finished JPGs, an Adobe upload window open, and a spreadsheet I had "fixed" three times. The rejection came back instant and unhelpful. Wrong columns. I had renamed, reordered, and re-saved that file all evening, and the platform would not even look at the rows. Somewhere around 2 a.m. I realized I was not making art anymore. I was doing clerical work, badly, for the fourth time on one product.

That night is why the thing I eventually shipped is not a tutorial. It is the same four scripts that got 39 products out the door, 856 MB of finished output, run on a single-core, 2 GB server. A tutorial ends at "here's how you'd do it." This ends at "here's the file that did it."

Metadata is where uploads die first

Adobe wants 5 columns. Vecteezy wants 4. Dreamstime wants 15. Each one rejects a wrong header before it reads a single row, so a small format mistake reads like a broken product. The fix is not more care. It is one script that writes the header those platforms demand and nothing else.

make_metadata.py builds the exact upload CSV from a folder of files and a platform flag:

python make_metadata.py ./my-assets --platform dreamstime --out dreamstime.csv --ext .jpg

If you want real titles and keywords, pass --titles titles.json. Without it, the filename is title-cased into the title, which is fine for a first pass and a bad idea to submit. That one line saves the boring hours, and it means metadata is never the reason you stop.

Double posting is the fastest way to get flagged

The second failure was mine, and it was worse. I re-ran a batch that had partially gone through. Result: duplicate uploads, a flagged account, and a support ticket I did not enjoy writing.

upload_tracker.py is a ledger that remembers what you already marked done, so a re-run cannot double post:

python upload_tracker.py pending items.txt
python upload_tracker.py done "asset-001.jpg"
python upload_tracker.py status

It is not clever. It is the boring record you will not keep in your head across 39 products and several hundred files. That is exactly the point.

A zip with a cover sells, a bare file does not

The last two stalls are about turning a finished thing into a product people will buy. md2pdf.py turns a Markdown file into a clean, styled PDF with headings, tables, lists, and links:

python md2pdf.py content.md product.pdf "My Product Title"

pack_product.py bundles the product: it zips the files and renders a cover at 1280x720 plus a thumbnail at 600x600:

python pack_product.py my-product "My Product" "A short subtitle" 9

Output lands in a _system folder next to where you run it, and you can point it elsewhere with --dir or the PACK_ROOT environment variable. No editing the script.

It ran on 1 vCPU and 2 GB of RAM

This pipeline never touched a GPU. It worked because of a few habits worth copying:

  • One process at a time. Never fan out image jobs in parallel on a one-core box.
  • Call gc.collect() between items so memory comes back.
  • Measure peak RSS, not average: resource.getrusage(RUSAGE_SELF).ru_maxrss gives the real ceiling.
  • Run heavy render steps as one file per subprocess, so memory returns to the OS.
  • Render previews at the resolution you need. Inspecting at 60 dpi beats rendering at 300.

The four scripts use only two third-party packages, Pillow for covers and reportlab for PDF output. The other two use the standard library alone, so if metadata and tracking are all you need, you skip the install entirely:

python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

The folder layout that keeps a catalogue out of the junk drawer

Keep the structure fixed: assets/ for raw output, output/ for ready-to-submit files plus metadata.csv, packs/ for finished zips and covers, research/ for notes and sources, scripts/ for the tools.

Keep research/ forever. I deleted scrape logs and notes once to "clean up," and it cost me the raw material for later products. That was a mistake I will not repeat.

Where this came from

The four scripts sit under a parent project with roughly 19,000 lines of pipeline code and more than 300 pages of research behind the rules they enforce. They are validated on 1 vCPU, 2 GB of RAM, no GPU, which is to say they are validated where you probably live.

That is the whole sales pitch, and it is not a clever one. The install is short, and the scripts are the exact ones that finished the work. You are not buying a "here's how." You are buying the four files that did it, with the header already right.

compliance metadata kit is that packaging layer: the metadata builder, the upload ledger, the Markdown to PDF step, and the product packager.

Get compliance metadata kit

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

Share