去年三月的一个晚上,他把同一套批量任务跑了两遍。不是有意为之——第一遍看起来卡住了,他手动终止,修掉自以为的bug,又重新跑了一次。实际上它并没有卡住。结果是60条重复投稿同时躺在三个平台的审核队列里,而在其中一家平台上,重复批次恰好是那种会让审核员把你的整个账号翻出来仔细看的操作。
当晚他写下了 upload_tracker.py,此后每一批任务都带着它跑。这段经历里真正值得说的,是重复投稿为什么会发生,以及什么办法才真的拦得住它。
平台不会替你兜底
三大图库平台在投稿环节都没有做那种能帮到你的去重。Adobe Stock 要求CSV表头是五列(Filename、Title、Keywords、Category、Releases),表头写错会立刻被拒,但同一个JPG用同一个文件名传两次,它照样收下。Vecteezy 用四列表头,行为一致。Dreamstime 要十五列,对元数据格式最严格,可对CSV格式的严格并不等于会去比对你的投稿历史。
去重的担子完全落在投稿人自己身上。这一点让他意外——他本以为至少会有个哈希校验,或者一句警告。没有。同一个文件传两遍,它就进去了,进入审核,占掉一个本该留给真实作品的审核名额。
账本比记性可靠
解法简单到有点难为情:一个纯文本文件,每个素材一行,只在该素材真正上传完成之后才追加。工具只有三条命令:
- pending:列出还没做完的素材
- done:把某个素材标记为已完成
- status:查看当前状态
关键设计在于 done 是一个独立的手动步骤。批量脚本不会在把任务排进队列时自动标记完成,因为排队不等于上传。这一点他也踩过坑:早期版本在生成CSV时就把素材标成完成,结果上传中途崩溃,账本就开始对他撒谎。现在账本只记录人工确认过的上传。
因为是只追加的纯文本,他可以用 grep 读它,在两台机器之间 diff 它,从任何备份里恢复它。没有数据库,没有依赖。upload_tracker.py 和 make_metadata.py 都只跑在Python标准库上,连虚拟环境都不需要。
同一本账,在花钱之前先查CSV
问题的另一半是:重跑会重新生成看起来和新的一模一样的元数据文件。所以在 make_metadata.py 为 Adobe(5列)、Vecteezy(4列)或 Dreamstime(15列)写入一行之前,拿文件名去账本里对一下是件顺手的事。已经标记完成的文件名会被跳过,而不是被悄悄重写。正是这一道检查,让第二次重跑那种灾难没法再发生。
表头规则在这里同样要紧。三个平台都会在上传时立刻拒掉错误的CSV表头。脚本只写每个平台要求的那一行表头,别的不写——他实在不想再经历一次40个文件上传失败之后才发现表头有问题。
小机器上的批量卫生
他整条流水线跑在1个vCPU、2GB内存、没有GPU的机器上,所以账本本身也必须便宜。它确实便宜:往文本文件里追加一行,成本为零。让这台机器上其余批量任务保持安全的习惯,在这里同样适用:
- 一次只跑一个进程,绝不在单核上并行铺开图片任务
- 每个素材处理完后调用 gc.collect() 释放内存
- 用 resource.getrusage(RUSAGE_SELF).ru_maxrss 测峰值内存,而不是看平均值,因为平均值会掩盖那个杀死进程的尖峰
- 重渲染步骤每个子进程只处理一个文件,让内存真正还给操作系统
批量任务崩溃,本来就是大多数重复投稿的源头,所以稳定性和去重其实是同一个问题。只要这一轮跑得下来,你就不需要重跑。
实际跑起来是什么样
账本上线之后,他用这条流水线跑完了39个成品、约856 MB输出,横跨三个平台,重复投稿为零。整个父项目大约19000行流水线代码,账本大概只占80行。他说,如果着火,这80行是他第一个要抢出来的。
现在的工作流是:生成素材,用 make_metadata.py 构建各平台的CSV,每次上传前查一遍 pending,每次确认上传后标记 done,会话结束时跑一次 status 核对数量。三十秒的记账,换掉一个下午删重复文件,外加一场他并不想要的账号审查。
想搭同样的东西,四个脚本——元数据构建器、上传账本、Markdown转PDF、以及一个能渲染1280x720封面和600x600缩略图的产品打包器——被打包成 Pipeline Starter Kit。账本和元数据构建器只需要 Python 3.9+。
热门跟贴