为什么我要用Go写这个?
上个月我妈生日,我翻了半天相册发现去年答应给她做的“每日祝福视频”才录了17天,那种愧疚感,你懂吧?我发誓要找个一劳永逸的办法——于是想到了写个程序,自动把一年的祝福视频片段拼起来。
选Go语言纯粹是因为它编译快、部署简单,而且我受不了Python打包时装依赖的破事,Go的ffmpeg绑定虽然不如Python的moviepy直观,但胜在稳定,跑一整天都不带崩的。
核心思路:把“一年”变成可计算的数据
一年有365天,每天对应一段祝福,这听上去像是个简单的循环,但真做起来全是坑:月份天数不一样、闰年怎么办、周末和节假日要不要特别处理?
我的方案是把“祝福内容”和“日期规则”解耦,先定义数据结构:
type DailyBlessing struct {
DayOfYear int // 第几天,1-365
Date string // 格式 "01-02"
Message string // 祝福内容
VideoPath string // 生成的视频片段路径
}
然后分三步走:生成文案 → 渲染文字 → 视频合成。
第一步:用Go生成365条不重样的祝福文案
别指望手写365条——那还不如自己录视频呢,我的技巧是模板+随机组合:
var greetings = []string{
"愿你今天笑容灿烂",
"平凡日子也有光",
"记得喝水,照顾好自己",
}
var closings = []string{
"——来自远方的挂念",
"——爱你的家人",
"——那个总忘事的朋友",
}
然后用math/rand按日期做种子,保证同一天永远生成同样的“随机”组合,这样就算程序重启,只要日期不变,输出就不会变。

这里有个坑:Go的rand.Seed在1.20版本后不推荐用了,我查了文档,现在应该用rand.New(rand.NewSource(seed)),否则每次跑结果都不一样。
第二步:文字渲染成图片,要像真人写的
光有文字不够,视频需要画面,我用golang.org/x/image/font配合系统字体,把祝福文字画到一张1920x1080的JPEG上。
字体选择上,别用默认的宋体,太生硬,我推荐用思源黑体或楷体,至少看起来像人写的。
img := image.NewRGBA(image.Rect(0, 0, 1920, 1080)) // 画个渐变底色,免得全白太单调 drawGradient(img, dayOfYear) // dayOfYear决定颜色偏移 // 在中央写祝福语 drawText(img, message, fontFace, textColor)
说实话,调文字居中的代码我卡了半小时。font.MeasureString返回的是像素宽度,但字间距在中文里不稳定,最后我偷懒用了golang.org/x/image/measure包,才勉强对齐。
第三步:每天的视频片段拼接——ffmpeg调用
这里是我的主战场,Go标准库没有视频处理能力,我用os/exec调用系统ffmpeg,每生成一张图片,就转成3秒的视频片段:
cmd := exec.Command("ffmpeg",
"-loop", "1",
"-i", imgPath,
"-t", "3",
"-c:v", "libx264",
"-pix_fmt", "yuv420p",
fragmentPath)
注意:-pix_fmt yuv420p必须加,不然生成的视频在手机上播放会花屏。
365个片段全拼起来,用concat协议最简单,我写了段脚本把文件列表写进一个txt,
ffmpeg -f concat -safe 0 -i list.txt -c copy final.mp4
手动跑过一次,生成的视频有18分钟长——刚好可以放一首歌的时间。
处理特殊情况:闰年、节假日、纪念日
写代码最容易忽略边缘情况,比如2024年是闰年,2月29日得算进去,我的数据里DayOfYear范围是1-366,但祝福内容按365天循环——额外那天我用了一条单独的分支:
if isLeapYear(year) && dayOfYear == 366 {
message = "四年等一回,今天特别想你"
}
节假日我建了个表,春节”“七夕”等,查time.Now().Format("01-02"),如果匹配到就覆盖当天文案。这个表不用搞太全,先放5个最重要的节日就行。
性能优化:并发处理365个视频片段
刚开始是单循环,生成365张图片+365次ffmpeg调用,实测跑了23分钟,太慢了!
后来我开goroutine,用errgroup并行处理,同时跑8个进程,机器差点卡死,但时间缩短到4分钟,代码长这样:
g, ctx := errgroup.WithContext(context.Background())
g.SetLimit(8) // 最多8个并发
for i := 1; i <= 365; i++ {
i := i // 闭包陷阱!
g.Go(func() error {
return generateFragment(i)
})
}
if err := g.Wait(); err != nil {
log.Fatal(err)
}
记得i := i那行,不然goroutine闭包捕获的变量会错乱——我犯过这个错,生成了365个一模一样的“第365天”祝福。
实际运行效果
我跑了一次完整流程,生成了blessings_2025.mp4,大小约800MB,画质还不错,但有个问题:所有片段都是图片,没有真人出镜,我妈看了说“像幻灯片”,不算成功。
后来我加了点过渡效果:片段之间用xfade滤镜,渐变时长0.5秒,代码在ffmpeg命令里变成:
ffmpeg -i 001.mp4 -i 002.mp4 -filter_complex "[0:v][1:v]xfade=transition=fade:duration=0.5:offset=2.5[v]" -map "[v]" output.mp4
不过这样只能连续拼接两个片段,全串起来需要链式处理,我偷懒了,直接用concat,过渡效果就没做,如果你的需求要平滑转场,得用更复杂的过滤图(filter graph),那玩意儿调试起来真要命。
一个更简单的替代方案?说实话
写着写着我发现自己有点“过度工程”了。如果你只是想要一个“一年祝福视频”,其实用手机录屏+剪映模板更方便,Go方案适合那种“每年自动生成,批量产出”的场景——比如公司给客户发定制贺卡视频。
但既然你问了Go怎么实现,我这个轮子也算能转。关键代码都在上面了,剩下就是你自己调字体、调颜色、调转场的事。
我最终版本大概写了600行Go代码,加上一些配置文件,说实话,折腾这事的乐趣比结果大,我妈最后看到视频还是感动的,虽然画面上只有彩色背景和文字,没有我的脸。
哦对了,还有个小坑:ffmpeg路径要在环境变量里配好,不然exec.Command找不到,在Windows上我直接写死了路径,在Linux服务器上用的usr/bin/ffmpeg——这根本不是Go的问题,是部署环境的问题。
最终的文件结构
bless-video/
├── main.go // 入口
├── textgen.go // 文案生成
├── render.go // 图片渲染
├── video.go // ffmpeg调用
├── holidays.json // 节假日表
└── output/ // 生成结果
你可能会想,这值得吗?为了一个视频写600行代码,我的答案是:如果你有一次成功跑通,明年就不用再动手了——毕竟365天祝福这种事,不是一年就结束的吧。
现在这台服务器还挂了个定时任务,每年1月1日凌晨自动跑一次,生成整年的祝福视频存到网盘里,我爸妈到时候自己去看就行,技术改变生活,虽然改变的方式有点笨拙。
最后那个holidays.json我还在慢慢补充,等几年后它就能覆盖所有传统节日了,人生嘛,不就是在这样的循环里找到一点新意思。
本文来自作者[kyadmin]投稿,不代表be365立场,如若转载,请注明出处:http://hljbesthome.com/lvyou/2476.html
评论列表(4条)
我是be365的签约作者“kyadmin”!
希望本篇文章《用Go语言写一个365天祝福视频生成器,从零到一的全过程》能对你有所帮助!
本站[be365]内容主要涵盖:be365,be365网站,be365网址,ball365体育,Be365排名
本文概览:为什么我要用Go写这个?上个月我妈生日,我翻了半天相册发现去年答应给她做的“每日祝福视频”才录了17天,那种愧疚感,你懂吧?我发誓要...