The Stdlib Ledger That Stops a Re-Run From Double-Posting Uploads
The Stdlib Ledger That Stops a Re-Run From Double-Posting Uploads I nearly lost an upload account to a rerun. My batch script had already pushed 200 files to a stock site. I wanted to check one thing, so I ran the scri
The Stdlib Ledger That Stops a Re-Run From Double-Posting Uploads
I nearly lost an upload account to a rerun. My batch script had already pushed 200 files to a stock site. I wanted to check one thing, so I ran the script again. It did not check anything. It started uploading from the top. I caught it around item 40 by watching the progress bar scroll past filenames I recognized. The platform had not flagged me yet, but that is luck, not a system.
The fix was not a smarter uploader. It was a ledger. upload_tracker.py is about 40 lines of standard library Python, and it does one thing: it remembers what you have already posted. That alone removed the most dangerous failure mode in the whole pipeline.
Why a rerun is the fastest way to get flagged
Stock platforms treat repeated identical submissions as spam. A second upload of the same file is not just wasted time, it is a signal. Do it across a few hundred files and you move from "new seller" to "pattern to review."
The usual workaround is a naming convention plus eyeballing a folder. That breaks the moment you batch-generate files, because regenerated filenames shift. The ledger moves the source of truth out of the filesystem and into a state file that only the tracker writes.
The commands are deliberately dumb:
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
No database, no cloud, no install. upload_tracker.py uses the standard library alone. The ledger is local state, and local state on a 1 vCPU, 2 GB box is exactly what you want.
The rule the ledger enforces: pending is the only input
Once the tracker exists, the workflow changes shape. You do not run a batch from your asset folder. You run it from pending. The batch script asks the ledger what is unfinished, and the ledger returns only items with no done record.
That gives you three guarantees for free:
- Rerun safety. If a job dies halfway, rerun it. The completed half is skipped.
- Resumability. You can stop at any point and pick up tomorrow.
-
Auditability.
statustells you exactly what went out, without reading platform dashboards.
I have not double-posted since. Not once. The ledger costs nothing to maintain because it has no dependencies, and the failure it prevents is the one that cannot be undone.
The same rule, applied to CSVs
The double-post problem has a quieter twin: rejected batches. All three channels reject a wrong CSV header instantly, and a rejected batch wastes a review cycle. make_metadata.py writes the exact header each platform demands:
- Adobe: 5 columns
- Vecteezy: 4 columns
- Dreamstime: 15 columns
One command per platform:
python make_metadata.py ./my-assets --platform adobe --out metadata.csv --ext .jpg
It also uses only the standard library. It reads a folder, writes the header the platform demands, and nothing else. If you pass --titles titles.json it uses your real titles and keywords. Without it, filenames are title-cased as a first pass. That is fine for testing, but replace it before you submit.
Two packages, and only two
The rest of the kit stays lean by design. Only pack_product.py needs Pillow for covers and thumbnails, and only md2pdf.py needs reportlab for PDF output. If you use just the metadata and tracker scripts, you can skip pip install entirely. On a machine this small, fewer dependencies means fewer failure modes.
The packaging step follows the same pattern. pack_product.py bundles a zip with a 1280x720 cover and a 600x600 thumbnail, because a zip with a real cover sells and a bare file does not. Output goes to a _system folder, and you can redirect it with --dir or the PACK_ROOT environment variable, with no script edits.
What this looked like in production
This is not a thought experiment. The parent project produced 39 finished products, 856 MB of output, backed by roughly 19,000 lines of pipeline code and more than 300 pages of research, all on 1 vCPU, 2 GB RAM, no GPU. The habits that made that possible:
- One process at a time. Never fan out image jobs on a 1-core box.
-
Free memory between items. Call
gc.collect()after each asset. -
Measure peak RSS, not average.
resource.getrusage(RUSAGE_SELF).ru_maxrsstells you the real ceiling. - Render at the resolution you need. A 60 dpi preview beats a 300 dpi render for inspection.
The ledger sits above all of this. It is the cheapest component in the stack and the one that taught me the most: in an automated pipeline, the state file matters more than the code that runs.
What I packaged from this
I pulled the four working scripts into a starter kit: make_metadata.py, upload_tracker.py, md2pdf.py and pack_product.py, with the folder layout that scales (assets/, output/, packs/, research/, scripts/) and the RAM notes above. Keep research/ forever, by the way. I deleted mine once to "clean up" and will not repeat that mistake.
The tracker is the piece that makes the kit safe to run more than once. Everything else is packaging.
Building digital products? Free guides & kits for digital product sellers.
Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.