大数跨境

跑通一条AI音乐流水线后:我把踩过的13个坑,全写进了脚本

跑通一条AI音乐流水线后:我把踩过的13个坑,全写进了脚本 AI造万物
2026-09-28
25
导读:Suno 歌曲页那个显眼的下载按钮,存进磁盘的是封面图——为此报废过一整批mp3。三条音频直链全试过,全死。而整条链路自动化到最后,唯一卡住的是一串短信验证码。

跑通一条 AI 音乐流水线后:我把踩过的 13 个坑,全写进了脚本

素材说明:本文基于本人实际运行的 WorkBuddy + Suno + 番茄音乐
 自动化生产链路整理,涉及的具体脚本、判据、故障现象均为实测记录,
 非产品推广,亦不构成任何平台规则建议。文中的坑按「当时怎么栽的」如实写。


01 先说这条流水线到底跑成了什么

不是 demo,是量产的。最近一个系列已经稳定做到:歌词 → Suno 生成 → 抓音频 → 做封面 → 上传平台 → 签约上架,中间人工干预的时间接近于零。

成品规模你可以先有个概念:在这套流程里,每首歌交付 5 个文件——普通话音频、藏语音频、两份歌词、一张 2048×2048 封面。目前整个系列已经跑完全程并上架了。

整条链路拆开是 13 个脚本、八步流水线:

# 1) 写歌词 # 2) 登录(一次性,登录态持久化) python scripts/suno_login.py # 3) 批量下发到 Suno(按 (曲名, 语言) 幂等) python scripts/suno_send.py 41 60 # 4) 核对 href 有没有指错歌 python scripts/check_hrefs.py 41 60 # 5) 抓音频(MediaRecorder 录解码流 → ffmpeg 转 mp3) python scripts/batch_audio.py 41 60 # 6) 歌词归档 python scripts/copy_lyrics.py 41 60 # 7) 合成封面 python scripts/compose_covers.py # 8) 三重校验 python scripts/verify_audio.py 41 60 

再加上传环节的三个:prep_lyrics.py(歌词清洗)→ publish.py(批量填表提交)→ sign_contract.py(签约)。

看上去平平无奇。但这篇文章真正有价值的不是这八步,是每一步底下埋着的坑。

下面这 13 个坑,全部是我真栽过的。它们分成四组。


02 第一组:为什么"下载"按钮是陷阱(第 1—4 坑)

这是整条流水线上最贵的一课,因为我付出的代价是一整批成品全部报废。

坑 1:Suno 歌曲页的 Download 按钮,下的是封面图,不是音频

这是最反直觉的一条。歌曲页面上那个显眼的 Download,鼠标点下去,存进磁盘的是一个 JPEG 封面图。

早期版本的脚本就是这么干的,于是产出了整整一批 .mp3 后缀的文件,双击一个都放不出声。

可怕的地方在于它骗得过肉眼:这些假 mp3 大小 300—800 KB,看起来非常合理。

所以现在任何成品入库前,先验三个字节的文件头:

head -c 3 "<file>.mp3" | xxd -p # 494433 / FFEx  → 真音频 # ffd8ff         → JPEG,假的! 

这条现在写死在 verify_audio.py 里。所有"看起来下载成功了"的动作,都必须过这一关才算数。

坑 2—4:三条直链,全死

那你自然会想:不点按钮,直接请求音频 URL 行不行?

我把能找到的三条路全试了,记录如下:

路径
现象
cloudfront.net/....m4a
返回 200,但文件内容没有任何 MP4 atom(moov/ftyp 全缺),是一堆高熵随机字节
audiopipe.suno.ai/?item_id=...
返回 200 audio/mp3,0 字节
cdn1.suno.ai/<id>.mp3 403 MissingKey
(需要 CloudFront 签名 URL)

三条路,三种不同的死法,结论只有一个:HTTP 层面拿不到音频,今天不行。

于是只能换一条完全不同的思路——让浏览器自己解码,然后录下来。

具体做法是:打开歌曲页 → 点播放,让 React 把一个真实的 blob: 源绑到 <audio> 元素上 → 在页面里搭一条

AudioContext → createMediaElementSource(audioEl) → MediaStreamDestination 

→ 把这个 stream 喂给 MediaRecorder(audio/webm;codecs=opus,128 kbps)→ 播放到结束 → 取出 chunks → ffmpeg 转成 mp3。

有个细节我觉得很漂亮:不要把它 reconnect 回 ac.destination。 这样整个录制过程用户的扬声器一声不响——你可以在后台批量录几十首,旁边的人毫无察觉。

代价是慢。因为必须真的播一遍,所以一首歌≈它的实际时长,60 首就是一小时起。这是这条路上唯一没法优化的成本。


03 第二组:看起来成功了,其实全错了(第 5—8 坑)

坑 5:Playwright 的 ctx.request 不走代理

这是我调试时间最长的一个坑,因为它不报错,只是静默失败。

整套脚本依赖 suno.com 的内部 API。而国内访问需要走本机代理,脚本用 --proxy-server 参数启动 Chrome,浏览器内的请求一切正常。

但 Playwright 提供的 ctx.request 是 Node 侧的独立客户端,完全不认浏览器进程的代理参数。结果就是:API 请求全部超时,而报错被 try/except 吞掉,流程悄悄降级成了一个本来就注定失败的备用方案。

后来的规矩:所有 API 请求必须放进浏览器里发——page.evaluate + fetch。

坑 6:不能用「打开页面数藏文字符」来核对语种

生成完要核对每首歌的链接有没有张冠李戴。最直观的方法是打开页面,数页面上藏文字符的数量。

实测会误判。 有一批里,两首完全正确的藏语歌被报成了错配——原因仅仅是那两个页面当时没渲染出歌词。

现在改走 API:取 metadata.prompt(也就是当初提交的歌词原文),按中文歌词的整行重合度打分。秒级完成,不受渲染影响。

坑 7:Suno 每次提交生成 2 个 clip,两个都是成品

这条如果不知道,会造成灾难性的误改。

提交一次,Suno 返回两首,两首都能用。所以"链接A 是不是这首歌"不能做是非判断,只能做比例判断:

当前链接的匹配得分 ÷ 全库最高分 ≥ 0.6 → 保留

只在确实指向了另一首歌时才替换。

实测一组 121 条记录里,真正错配的只有 2 条。如果没有这个比例判据,一次就会误改一大批。

还有一条配套铁律:校验失败后绝对不要重发同一首歌。 重发等于重复扣积分,还会在曲库里留下重复的 clip。失败项应该交给 resolve_hrefs.py 从已有曲库里认领。

坑 8:ImageGen 并发会撞文件名

做封面时如果用 ImageGen 批量生成底图,有个很隐蔽的问题:同一秒发起的多个生成任务会拿到同名输出文件,互相覆盖。

有一批 20 张底图里,四对撞了车,磁盘上只剩下最后写的那张——画面内容和曲名完全对不上。

防御办法很土但有效:生成后逐张打开核对画面,撞车的重生成,并且错开发起时间。


04 第三组:上传表单的五个坎(第 9—13 坑)

到了平台这一步,坑的性质变了——不是技术难题,是前端页面的交互细节。

坑 9:发布页初始一张表单都没有

打开上传页面,页面上没有"歌曲1"这一栏。每一首歌,都要自己点一次"点击添加歌曲"才能冒出来。

坑 10:上传成功后,表单索引会位移

每上传一个音频,页面上的 input 框数量就变了。如果代码里写死了第几个框是什么,第二首开始全部错位。

稳妥做法是按关键词动态定位——找页面上写着"请上传wav"、"仅支持TXT"的那个框,而不是数它是第几个。

坑 11:艺人选择按钮点不动

页面上有个"添加自己"的按钮,怎么点都没反应。后来改成给 input 直接 fill(艺人),再补 ArrowDown + Enter + Escape,秒过。

坑 12:封面上传后有裁剪弹窗

传完封面会弹一个裁剪框,必须点"确定"才算完成。不点的话,这一步悄悄卡住。

坑 13:最后一步的按钮叫"确认签署",不叫"提交"

这是最后一个坑。填完三步表单,页面上找"提交",找不到——按钮文字是确认签署。

(附带一条 Windows 专属:残留的自动化 Chrome 进程会让下次启动直接秒退,每次跑之前得先清理一遍。)


05 每一步都要能重来

比解决单个坑更重要的是一个设计原则:幂等。

因为这套流水线跑一次就是几十首、几个小时,中间 Chrome 崩了、断网了、超时了,随时会发生。如果每次中断都要从头再来,那这套东西根本不可用。

所以每个脚本都写了跳过逻辑:

脚本
跳过条件
suno_send.py (曲名, 语言)
 已发过 → 跳过
batch_audio.py
目标 mp3 存在且通过校验 → 跳过
copy_lyrics.py
目标 txt 存在且 > 200 字节 → 跳过
compose_covers.py
目标 jpg 存在且 > 200KB → 跳过

任何一步中断,直接重跑同一条命令就能续传。 这一条带来的心理安全感,比任何单个功能都重要。


06 唯一必须人走的一步

整条链路自动化到了什么程度?我可以诚实地说:只剩一步做不到。

提交完成后,作品状态是"待授权",需要签一份电子合同。签约环节平台强制要求手机短信验证码——这串六位数字,是目前唯一无法自动化的东西。

处理办法是让脚本尽量把等待成本降到零:它会自动点"获取验证码",然后轮询一个本地文件;你把收到的验证码写进那个文件,脚本自动填码提交。

还有个贴心的设计:如果人不在,每 4 分钟自动重发一次验证码,总共等 30 分钟。所以不管你什么时候回到电脑前,手里的码都是有效的。

另外平台的签约通道有两套(第三方电子签 + 飞书电子签),脚本两边都支持。


07 这套东西真正的价值不在脚本

回头看,这十几个脚本加起来代码量并不大,任何一个熟练工程师两周都能写完。

它们真正的价值是把「怎么摔的」记录下来了。

我可以用一句话告诉你"Suno 的下载按钮下的是封面",但如果你没有亲眼见过那一整批放不出声的 mp3,你大概率不会在写第一行代码的时候,就把文件头校验写死进去。

同理:resolve_hrefs.py 里那个 0.6 的阈值,背后是一批被误判的藏语歌;--fill-only(只填表不提交)这个参数的存在,是因为调试期在平台上留下过三条同名的空记录。

这些都不是设计出来的,是赔出来的。

所以如果你也想搭一条类似的流水线,我的建议不是去找那个"最好的脚本",而是——老老实实先手工跑一遍完整流程,把你每一个"咦,怎么不对"的瞬间记下来。

那些瞬间,才是这套系统真正的规格说明书。

最后补一句与钱有关的实话:这条流水线没有节省时间,第一版跑通花的时间远超手工点几十首歌。它节省的是第三次以后的时间,以及你把自己从重复劳动里摘出来的可能性。

前两次永远亏。值不值,取决于你要做几次。


文中所涉脚本与判据均为个人实测环境下的产物,平台页面可能随时改版,请以实际情况为准。

【声明】内容源于网络
0
0
AI造万物
科技改变生活。
内容 23
粉丝 0
AI造万物 科技改变生活。
总阅读1.5k
粉丝0
内容23