make_metadata.py 如何寫入各圖片素材平台指定的欄位標題
How make_metadata.py Writes the Exact Column Headers Each Stock Platform Demands
make_metadata.py 會按 Adobe Stock、Vecteezy 和 Dreamstime 的要求,為 CSV 寫入完全相符的欄位標題,避免欄名錯誤令整批素材遭拒。三個平台分別要求 5、4 和 15 欄;程式亦可透過 --titles titles.json 提供自訂標題和關鍵字。配套的 upload_tracker.py 逐個檔案記錄上載狀態,避免重複上載。
I lost a whole afternoon last month to a rejected Adobe Stock CSV. 240 files, ready to go, all rejected in seconds with a header error. The problem was not the art. It was that I had hand-typed the column headers from memory, and I got one of them wrong. Adobe does not guess what you meant. The upload parser reads the first row, does not find the header it expects, and stops. Every file in the batch comes back.
That failure is why make_metadata.py exists. It writes the exact header each platform demands, and nothing else. Here is what I learned building it.
Why the header is the whole game
Each stock platform has its own CSV shape, and they do not match.
Adobe Stock wants 5 columns. Vecteezy wants 4. Dreamstime wants 15. If you submit a 5-column file to Dreamstime, it fails. If you add an extra column Adobe does not expect, it fails. The review checks are strict about this because the CSV is how their ingestion system maps your file to a listing. A wrong header means the batch never even reaches a human reviewer.
So the script has the three headers hard-coded. There is no template file to edit, no config to misconfigure. When you run:
python make_metadata.py ./my-assets --platform adobe --out metadata.csv --ext .jpg
the first row it writes is the Adobe 5-column header, character for character. Same idea for Vecteezy's 4 columns and Dreamstime's 15. I got the headers right by checking them against real uploads, not documentation alone, because documentation drifts and the upload parser does not.
What goes in the rows
The header is only half of it. Each row needs a title, keywords, and category per file.
The default behavior is simple: the filename gets title-cased and becomes the title. That is fine for a first pass, and honest about its limits. A file called geometric-fox-pattern-04.jpg becomes "Geometric Fox Pattern 04". It will pass the parser. It will not win search ranking. So the script also accepts --titles titles.json, where you supply real titles and keywords per file. My workflow is: generate the batch, run the script without titles to get the structure right, then fill in the JSON before submitting anything. Replacing a default title takes minutes. Replacing a rejected batch takes an afternoon I already spent once.
One rule I enforce on myself: keywords come from the actual image content, not from the prompt. Reviewers do check. A mismatch between keywords and visible content is one of the quiet ways batches get partially rejected, and partial rejections are worse than full ones because you do not always notice which files failed.
The second mistake I only made once
After fixing the header problem, I re-ran the batch. Then I re-ran it again a day later after a small edit. Double-posted. That is the fastest way I know to get an account flagged, and it happens to everyone who scripts uploads without a ledger.
upload_tracker.py is a plain standard-library ledger. Before uploading, you check what is actually left:
python upload_tracker.py pending items.txt
python upload_tracker.py done "asset-001.jpg"
python upload_tracker.py status
A re-run cannot double-post because "done" is recorded per file, per the ledger, not per your memory of what you did. It uses the standard library alone, no install at all. Same for make_metadata.py. Only two scripts in my whole pipeline need third-party packages: Pillow for cover rendering and reportlab for PDFs.
The constraints that shaped the script
This pipeline runs on a 1 vCPU, 2 GB RAM server with no GPU. That sounds like a limitation. Mostly it is a discipline.
One process at a time, never parallel image jobs on a single core. gc.collect() between assets. Measure peak RSS with resource.getrusage(RUSAGE_SELF).ru_maxrss, not average, because the peak is what kills a 2 GB box. And render at the resolution you need: inspecting a preview at 60 dpi instead of 300 is the difference between fitting in memory and swapping.
make_metadata.py fits this easily because it is just text processing. A CSV build over 240 files takes seconds and barely registers in RAM. The metadata step is never the bottleneck. The image generation is, which is why I keep the metadata step boring and reliable.
Why I did not use a spreadsheet
I tried the manual CSV route first. Open in a spreadsheet app, type the header, fill rows, export. It fails in predictable ways: spreadsheet apps silently reformat things, add quoting you did not ask for, and mangle encodings. A script that writes the file directly produces the same bytes every time. When a platform rejects a batch, I know it is the content, not the tool.
The script is one of four in the pipeline starter kit, alongside the tracker, md2pdf.py for turning Markdown docs into styled PDFs, and pack_product.py for bundling a zip with a 1280×720 cover and a 600×600 thumbnail. Together they packaged 39 products, about 856 MB of output, from the vector prompt pack work and everything after it. Around 19,000 lines of pipeline code total, with 300+ pages of research behind the rules the scripts enforce, all validated on that same 1 vCPU / 2 GB RAM box.
I built the kit because I kept making the same three or four mistakes and wanted them to be structurally impossible. If you are packaging stock art or templates and the CSV step is eating your time, it is the tool I wish I had before that rejected batch.
Building digital products? Free guides & kits for digital product sellers.
來源:DEV Community · dev.to