DEV Community

Cover image for From Asset Folder To 39 Products: Four Python Scripts For Metadata, Packaging And Upload Tracking

From Asset Folder To 39 Products: Four Python Scripts For Metadata, Packaging And Upload Tracking

I still have the rejection email open in a tab. Adobe Stock bounced 40 of my first 50 uploads in one batch. The reason was not the art. It was a CSV header with four columns where their uploader expects five. Vecteezy wants four columns. Dreamstime wants fifteen. I had written one CSV and assumed the rest. That single assumption cost me a week of re-uploads and nearly got the account flagged when I re-ran the batch and double-posted the files that did go through.

That failure is why I built four small Python scripts, and why those scripts ended up packaging 39 products, 856 MB of finished output, on a server with 1 vCPU, 2 GB RAM, and no GPU. Here is what actually worked.

Metadata is a formatting problem, not a creative one

All three major stock channels validate the CSV header before they look at a single image. A wrong column count gets rejected instantly, before review. So I stopped hand-editing spreadsheets and wrote make_metadata.py. You point it at a folder of files, name the platform, and it writes the exact header that platform demands: 5 columns for Adobe, 4 for Vecteezy, 15 for Dreamstime, and nothing else. No extra columns, no creative interpretation.

The honest caveat: if you do not pass a titles file, the script title-cases the filename and uses that as the title. That is fine for a first pass but replace it before you submit. Reviewers can smell blue_flower_0042.jpg turned into "Blue Flower 0042", and weak titles and keywords are the second most common rejection reason after format errors.

Double-posting is the fastest way to lose an account

This one scared me straight. When my first batch partially failed, I re-ran it. The files that had already gone through got posted again. Duplicate submissions are a review flag, and enough flags make reviewers distrust everything else you send.

The fix is embarrassingly simple: upload_tracker.py is a ledger. done "asset-001.jpg" marks an item, pending items.txt shows only what is left, status gives you the overview. A re-run now physically cannot double-post because the tracker remembers. Both this script and the metadata builder use only the Python standard library. No pip install, no dependencies, nothing to break in six months when a library updates.

A document is not a product until it looks like one

I had 300+ pages of research behind my products and no way to sell it. Markdown is great for writing and terrible for selling. md2pdf.py converts a Markdown file to a styled PDF in one command: headings, tables, lists, and links all render properly. It uses reportlab, one of exactly two third-party packages in the whole kit.

pack_product.py is the other half of that. It zips the product files and renders a 1280x720 cover and a 600x600 thumbnail using Pillow. A zip with a real cover sells. A bare file does not. I learned that from my own conversion numbers before I learned it from anyone's advice. The output goes to a _system folder by default, and you can redirect it with --dir or a PACK_ROOT environment variable, so nothing needs editing.

The folder layout is the part nobody warns you about

Thirty-nine products in, a catalogue without structure becomes a junk drawer. The layout this kit assumes is plain: assets/ for raw output, output/ for ready-to-submit files with their metadata CSV, packs/ for finished zips and covers, research/ for notes, scripts/ for tools.

The one rule I would tattoo on my own wall: keep research/ forever. I deleted my scrape logs once to "clean up" and then needed them two products later. Your notes and logs are the raw material for future products. Disk is cheaper than redoing research.

Small server habits that kept 19,000 lines of code alive

The parent pipeline behind these scripts is about 19,000 lines, and all of it ran on that 1 vCPU, 2 GB box. What made it survivable:

One process at a time. Fanning out image jobs in parallel on a single core does not speed anything up, it just swaps.

Free memory between items. A gc.collect() after each asset keeps long batches from creeping upward.

Measure peak RSS, not average. resource.getrusage(RUSAGE_SELF).ru_maxrss shows the real ceiling. Average memory lies; the peak is what OOM-kills you.

One file per subprocess for heavy render steps, so the OS actually gets the memory back.

Render at the resolution you need. Inspecting a preview at 60 dpi instead of 300 cut my render time by more than I expected, and the preview does not need the full resolution anyway.

Where this came from

I am not going to pretend these four scripts are magic. They are boring, small, standard-library-first tools that solve the four things that actually stall solo makers: metadata headers that pass validation, a ledger that prevents double-posting, one-command PDFs, and packaging with real covers. They packaged 39 products for me and they run anywhere Python 3.9 runs, including a server with 2 GB of RAM.

If you want the same tools instead of the same tutorial, I packaged them as the pipeline starter kit: the four scripts, the requirements file with just Pillow and reportlab, and the folder conventions from this post. It is the exact setup from my own pipeline, not a cleaned-up rewrite of it.

Get pipeline starter kit


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

Top comments (0)