做了个"两张照片生成对口型视频"的站,记录几个真踩过的坑

lianghuan 2026-10-14 09:39 1

先说是什么:上传两张人脸照片,选一个场景模板,生成一段两人说唱的视频。
不是文生视频——照片是必须输入。


做的过程中撞了几件挺有共性的事,记一下,也算给后面的人省点时间。




一、Next.js 的 .env.local 优先级高于 .env.production


这个坑最阴。构建成功、部署成功、健康检查全绿,网站打开是坏的。


原因是 Next 的加载顺序:


.env.local  >  .env.production  >  .env

.env.local 是给本地开发用的(官方文档也写了测试环境应忽略它),
但生产构建照样读它,而且优先级最高,会盖掉 .env.production 。


我本机的 .env.local 里 NEXT_PUBLIC_API_BASE 指向 localhost:5001 ,
结果构建产物里烧的就是 localhost——线上用户打开页面,浏览器去请求
他自己的 localhost 。


因为 NEXT_PUBLIC_ 是构建期内联进 JS 的,运行时改不了,所以要重新 build 。


后来在部署脚本里加了一道闸:构建前扫描产物,发现 localhost 直接让部署失败。
不然这个错每次都可能再犯,而且每一步看起来都是对的。




二、视频要 90 秒才返回,用户以为页面卡死


一开始 POST /tasks 是同步的:预处理(约 90 秒)+ 提交上游 + 返回。
实测那条请求 cost=93158ms 。按钮转圈一分半,什么都不显示。


改成"扣积分 → 建任务行 → 入队 → 立刻返回",真实生成在后台线程跑。
用户亚秒级拿到 task_no ,前端开始倒计时。


有两个细节值得说:




  • 顺序很重要。校验和扣分必须在入队之前,不然失败时账本里会多出
    spend/refund 两行噪音,用户看到余额没变但流水很脏。




  • 后台线程随进程重启而消失,会留下"积分已扣 + 任务永远 pending"。
    这个必须补偿——不是加个 atexit 就够( kill -9 时钩子不跑),
    而是取任务时自愈:卡太久且没有上游 id 的,判失败并退款。






四、没有 migrate 工具,create_all 不改已有表


项目用的是 db.create_all(),它只建缺失的表,不管已有表的结构。


我加了几个字段,本地测试全绿,上线直接炸 Unknown column 。
而且 MySQL 遇到第一个不存在的列就报错,不会一次告诉你全部缺哪些——
得补一个跑一次。


后来写了个脚本,拿代码模型和线上库逐表比对,把缺表/缺列/多余列列出来。
这类项目建议第一天就加这个,比手动 DESC 靠谱。




五、Stripe 的 Managed Payments 会强制要产品税码


这个账号默认开了 Managed Payments ,它要求价格来自后台预建的、
带 tax_code 的 Price 。但我的套餐价格存在自己数据库里,结账时用内联
price_data 传——直接被拒:


Invalid line_items[0]: the product tax code is missing.

Stripe 给了按会话覆盖的官方参数,加上就能用内联价格:


managed_payments[enabled]=false

不用去后台建一堆产品。代价是价格只有一处(自己的库),改价不用同步两边。




局限也说一下,免得点进去发现不对:



  • 场景是固定的 9 个模板,不能自定义

  • 必须两张人脸照片,产物就是两个人在场景里说唱;没有空镜、产品片

  • 每段视频成本不低,所以免费额度只够一条 5 秒


站在这: https://hotellobbyaiapp.com


有在做类似东西的,欢迎交流上面几个坑的其它解法。

最新回复 (1)
  • lianghuan 楼主 10-14 12:15
    1楼
    不知道为什么,大家看起来对这个不敢兴趣,是我做的不好吗
* 帖子来源V2EX
返回