DEV Community

Cover image for Outsourcing Metadata And Packaging When One Server And Four Scripts Stopped Scaling

Outsourcing Metadata And Packaging When One Server And Four Scripts Stopped Scaling

I hit the wall on a Tuesday night. I had a batch of 60 vector assets ready to go, and I was hand-typing titles and keywords into Adobe Stock's upload CSV for the third time that week. I fat-fingered a column, the upload preview showed my keywords in the wrong field, and I had to delete the whole submission and start over. That was the moment I realized the bottleneck was not the art. It was everything around the art.

At that point my pipeline was one server (1 vCPU, 2 GB RAM, no GPU) and four scripts, and it had already produced 39 finished products, 856 MB of output. But the metadata and packaging steps were still manual, and they were eating hours per batch. Here is what I changed and why it worked.

Metadata is a format problem, not a writing problem

All three of my channels reject a wrong CSV header instantly. Adobe wants a 5-column layout, Vecteezy wants 4 columns, Dreamstime wants 15. Get one column wrong and the whole file bounces before anyone looks at your art. I kept a spreadsheet of the exact headers and still messed them up under time pressure.

So I wrote make_metadata.py. It takes a folder of files and writes the exact header each platform demands, and nothing else. By default it title-cases the filename and uses that as the title, which is fine for a first pass. I pass --titles titles.json when I have real titles and keywords ready, because a filename-based title is a placeholder, not a submission. The script uses only the Python standard library, so there is nothing to install and nothing to break.

The honest part: the script does not write good keywords for you. It writes the correct container. The keyword work is still mine, and it is the part I refuse to outsource to a random LLM without checking, because stock platforms do reject spammy or mismatched keywords.

Double-posting is the fastest way to get flagged

The second failure mode was worse than a rejected CSV. I re-ran a batch script once and double-posted a set of assets. Stock platforms treat that as a quality signal, and not the good kind. A repeated upload on the same account gets flagged fast.

upload_tracker.py fixed this with a dumb, boring ledger. It is one text file that remembers what is done:

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

A re-run now skips anything already posted. No database, no framework, standard library only. The lesson I keep relearning: the tool that prevents your worst mistake does not need to be clever, it needs to be impossible to bypass by accident.

Packaging is where a folder of files becomes a product

A zip with a real cover sells. A bare file does not. I proved this to myself the lazy way, by listing a pack with no cover and watching it get zero attention compared to the ones with a 1280×720 cover and a 600×600 thumbnail.

pack_product.py bundles the product in one command: it zips the files and renders both images. It writes the outputs into a _system folder next to where you run it, and you can redirect that with --dir or the PACK_ROOT environment variable. Rendering uses Pillow, which is one of only two third-party packages in the whole toolkit. The other is reportlab, for turning a Markdown doc into a styled PDF with headings, tables, lists and links. Two dependencies for the whole packaging layer is a number I am proud of, because every extra dependency is another thing that breaks on a tiny server.

The 2 GB RAM habits that kept it running

None of this would matter if the server kept dying. It has 1 vCPU and 2 GB RAM, no GPU, and it ran the entire pipeline. The habits that made that possible:

One process at a time. Fanning out image jobs in parallel on a single core just thrashes. Free memory between items with gc.collect() after each asset. Measure peak RSS, not average, with resource.getrusage(RUSAGE_SELF).ru_maxrss, because the average lies and the peak is your real ceiling. Run heavy render steps one file per subprocess so memory actually returns to the OS. And render at the resolution you need: inspecting a preview at 60 dpi instead of 300 saves more memory than any clever code change I made.

What I still do by hand

To be clear about the limits: I write the titles and keywords. I review every asset before it goes in the output folder. I keep a fixed folder layout (assets/, output/, packs/, research/, scripts/) because a catalogue without structure becomes a junk drawer, and I learned the hard way never to delete research/. My scrape logs and notes are the raw material for future products, and I threw a set of them out once during a "cleanup" and regretted it immediately.

The scripts took over the parts that are pure format and repetition: the CSV headers, the ledger, the zip, the covers, the PDF. That is roughly the work that was eating my Tuesday nights. The creative judgment stayed with me, and it should stay with you too.

Where to get the tools

Everything above is in the vector prompt pack: the same four scripts that packaged those 39 products, with the install notes, the folder layout, and the RAM habits written down. It is a small, honest toolkit for solo makers, not a black box. If your bottleneck is metadata and packaging rather than the art, it will save you the same hours it saved me.

Get vector prompt pack


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

Top comments (0)