[KongchangAI]
· 3 min read· 1,522 words

One Folder, Three Platform CSVs: Automating Adobe, Vecteezy, and Dreamstime Metadata

One Folder, Three Platform CSVs: Automating Adobe, Vecteezy, and Dreamstime Metadata

Treat asset metadata as a build artifact: scripts for headers, a ledger against duplicates, subprocesses for memory.

A stock asset seller turns multi-platform submission from hand-built spreadsheets into a scripted pipeline. make_metadata.py hardcodes the headers for Adobe Stock (5 columns), Vecteezy (4), and Dreamstime (15), generating three CSVs from one folder with headers that never drift. A plain-text ledger prevents duplicate uploads, a fixed directory layout keeps things findable, and on a 1 vCPU / 2 GB server the pipeline relies on serial execution, per-file gc.collect(), peak RSS monitoring, and one file per subprocess.

The most tedious part of selling stock assets isn't creating them — it's submitting them. The same batch of files has to become three CSVs with completely different headers, and the first thing every platform checks is the header: get one column wrong and the entire batch gets rejected. The author shares a complete solution that evolved from hand-building spreadsheets to a scripted pipeline, built on one core principle: treat metadata as a build artifact of code, not a document you edit by hand.

rss source: Building Adobe, Vecteezy and Dreamstime CSVs From One Folder of Assets

Headers Are the Gate — Manage Them Like Code

Adobe Stock needs 5 columns, Vecteezy needs 4, and Dreamstime needs 15. All three reject bad headers outright, before any human or model review — there's no such thing as "partial credit." The author learned this the hard way last March: a 120-row asset sheet had an extra sixth column, and pasting it into Adobe produced an instant parse error. Not a single row made it into review.

The root cause is assembling things by hand in a spreadsheet. A spreadsheet isn't a build artifact — it's a breeding ground for silent errors. The fix was to write make_metadata.py, hardcoding each platform's correct header into the script and only ever outputting that one format:

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

One folder, three CSVs, and headers that never drift — because they aren't typed out, they're fixed in code. When there's no title file the script falls back to a title-cased filename, which works for a first pass but should be replaced before submission — a filename is not a set of keywords.

CSV header validation: When asset platforms accept bulk uploads (usually CSVs or platform-specific spreadsheet templates), they first do a structural parse, reading the file in as a data table. This step only checks whether the column names, count, and order match the template exactly — it doesn't understand the semantics of the content. Because it's the gate that lets an entire batch into the review queue, any column deviation makes the parser fail on the whole table. Platforms typically don't tolerate errors row by row, and they won't give you a friendly "column N is wrong" message — just a parse error. This is why assembling sheets by hand in Excel is especially dangerous: merged cells, hidden columns, trailing commas, and BOM encoding can all silently add a column or change column order in the export. Hardcoding the header in a script essentially makes the table structure a version-controlled single source of truth, rather than relying on a manually copied template.

Duplicate Listings Are the Fastest Way to Get Flagged

The author made this mistake early on: reran a batch of jobs overnight, forgot that half of them had already been uploaded, and piled duplicates into the review queue. Duplicate submissions make reviewers lose trust in your entire catalog — and for a small seller, trust is your account's only real asset.

The solution is dumb but effective: a ledger. upload_tracker.py uses a plain-text file to remember what's already been published, relying only on the standard library — no dependencies, no database:

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

Check pending before each upload, check status before marking anything done, and completed items are skipped automatically on a rerun. The author's verdict: a boring solution is the one you'll actually keep using.

One Directory Structure — Don't Build a Junk Drawer

Once you're past forty or fifty products, what slows you down isn't generation speed — it's not being able to find anything. The whole pipeline assumes this layout:

project/
  assets/    raw output
  output/    files to submit + metadata.csv
  packs/     finished archives + covers
  research/  notes and sources
  scripts/   tooling scripts

There's one rule the author learned only by deleting files: research/ is kept forever. He once deleted his notes to "clean up," then spent a week rebuilding decisions he'd already made. Scrape logs and notes are the raw material for next quarter's products.

Survival Habits on a Small Server

The entire pipeline runs on a machine with 1 vCPU, 2 GB of RAM, and no GPU — and that constraint shaped the design more than any platform rule. A few habits that actually work:

  • Run one process at a time. On a single core, parallel image jobs bring no gain — only OOM.
  • Call gc.collect() after each asset finishes. This is the difference between a stable loop and a crash at item 90.
  • Watch peak RSS, not averages, measured with resource.getrusage(RUSAGE_SELF).ru_maxrss. Averages hide the ceiling.
  • For heavy rendering steps, have each subprocess handle exactly one file — that's the only way memory actually goes back to the OS.
  • Render at the resolution you need. Previews at 60 dpi instead of 300 dpi save a huge amount of that 2 GB.

As for dependencies, keep only Pillow (for covers) and reportlab (for PDFs) — everything else is standard library. On a 2 GB machine, every extra dependency is a future 3 a.m. debugging session.

Peak RSS and ru_maxrss: RSS (Resident Set Size) is the size of the pages a process is actually using and keeping resident in physical memory, excluding anything swapped out to disk. On Linux, resource.getrusage(RUSAGE_SELF).ru_maxrss returns the peak RSS since the process started, in KB (on macOS it's in bytes, so watch the conversion when going cross-platform). The reason to look at peak rather than average is that the OOM killer triggers on the instantaneous high-water mark: a job that averages only 300 MB but spikes to 1.9 GB during one render step shows nothing in the average — yet the process gets killed at that moment. Recording peak RSS helps you pinpoint which stage is eating memory, so you can decide whether to split steps, lower render resolution, or switch to one subprocess per file.

Why subprocesses can actually return memory: Python's reference counting and generational garbage collection only free the heap memory used by Python objects themselves, but image processing, PDF generation, and similar operations often allocate large buffers at the C extension layer (the libraries behind Pillow, reportlab, etc.). That memory isn't necessarily returned to the OS the moment the object is freed — it may sit in the process's allocator for reuse. That's why long-running loop processes show RSS climbing in steps and never coming down. Put each heavy task in its own subprocess and exit after one file, and the OS reclaims its entire address space in one go when the process ends — the only way to guarantee memory truly goes back to the system. The main process just handles scheduling and aggregation, staying lightweight itself.

Packaging Is Half the Sale

Archives with a 1280x720 cover and a 600x600 thumbnail sell; bare files don't — the author says his own catalog's sales data proves it. pack_product.py handles the packaging and both image renders in one command:

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

The zip, cover, and thumbnail all go into a _system folder, redirectable via --dir or the PACK_ROOT environment variable — no script edits, no per-product config.

What This Actually Produced

This isn't theory. These scripts produced 39 finished products and 856 MB of output, from a roughly 19,000-line parent pipeline, backed by 300+ pages of research on platform rules — the scripts execute those rules, all validated on the same 1 vCPU / 2 GB machine. The author packaged the full method (including the prompt engine and the sticker niche section) into an AI sticker factory playbook.

For multi-platform sellers, there's really only one transferable lesson: reliability at submission time comes from engineering, not willpower. Hand headers to code, duplicates to a ledger, memory to subprocesses — and save the remaining energy for creating.

One-line Summary

Treat asset metadata as a build artifact: scripts generate headers, a ledger prevents duplicates, subprocesses control memory.

Paragraph Summary

The article describes how a stock asset seller turned multi-platform submission from hand-built spreadsheets into a scripted pipeline. The core idea is treating metadata as a build artifact of code: make_metadata.py hardcodes the headers for Adobe Stock (5 columns), Vecteezy (4 columns), and Dreamstime (15 columns), generating three CSVs from one asset folder with headers that never drift. A plain-text ledger, upload_tracker.py, records what's already been uploaded to avoid duplicate submissions that erode account trust. It fixes the directory structure as assets/output/packs/research/scripts and stresses that research notes must be kept forever. On a 1 vCPU / 2 GB machine with no GPU, it sticks to single-process serial execution, per-file gc.collect(), watching peak RSS, one file per subprocess for heavy tasks, rendering at reduced resolution as needed, and keeping dependencies to just Pillow and reportlab. pack_product.py produces the zip, a 1280x720 cover, and a 600x600 thumbnail in one command — and the author says packaged products clearly sell better.

#Adobe Stock CSV#Vecteezy CSV#Dreamstime CSV#stock asset metadata automation#upload tracker#peak RSS memory management
Share:

Related articles