当前位置:首页 > 能源 > 正文

用Golang写一封给她的情书,打造属于你们的情侣一周年视频365纪念

  • 能源
  • 2026-07-03 20:36:47
  • 278
摘要: 说起来你可能不信,我跟我女朋友在一起一周年的时候,我干了一件挺“程序员的浪漫”的事——没用任何视频剪辑软件,纯用Golang写了...

说起来你可能不信,我跟我女朋友在一起一周年的时候,我干了一件挺“程序员的浪漫”的事——没用任何视频剪辑软件,纯用Golang写了个程序,把365天的照片、视频片段、聊天记录,自动拼成了一个纪念视频,这事儿后来发朋友圈,炸了,好多朋友问我是怎么做到的,今天就把这整个思路、代码框架、踩过的坑,全抖出来。

为什么要用Golang做这件事?

你可能会想:“我直接用剪映、PR、Final Cut Pro不就行了?折腾Go干嘛?”

我的回答很实在:因为我不想在周年纪念日前一个月,天天熬夜拖时间轴,而且我想让视频里每个片段、每段文案、每首BGM,都有“我们俩独有的逻辑”,比如我们第一次见面的那天,视频里会自动配上当时的聊天记录截图;我们吵架又和好的那周,色调会偏冷再转暖——这种“数据驱动的情感叙事”,普通剪辑软件做不到,但Go可以。

还有一个更自私的理由:我想用一个她看不懂、但感知得到的方式来证明“这一年我有多认真”

第一步:数据收集 —— 把365天变成代码里的变量

写程序前,我先干了最笨的活:把365天里所有可能用到的素材,按照日期命名好,扔进一个文件夹

我当时的目录结构大概是这样的:

anniversary_video/
├── photos/          # 每天选1-3张照片,命名如 2023-05-20_01.jpg
├── clips/           # 短视频片段(散步、做饭、生日),MP4格式
├── music/           # BGM,两首:“遇见”和你的“小幸运”
├── chat_export/     # 微信聊天记录导出的TXT,每天一个文件
├── special_dates/   # 特殊日期标记:第一次约会、异地见面等等
└── config.json      # 配置文件,放片头片尾文案、色调偏好

Go最大的好处是 对文件系统的操作极其舒服os.ReadDir一把梭,把所有日期排序,然后用time.Parse解析文件名里的日期,算出“距离纪念日还有多少天”,这就构成了视频的时间轴骨架。

聊个踩过的坑:聊天记录清洗

微信导出的聊天记录里,有表情、语音转文字失败的标记、撤回消息残留,甚至还有错别字,我写了两个函数来处理:

func cleanChat(raw string) string {
    // 正则去掉表情代码 [微笑] [流泪] 这种
    // 去掉系统通知“你撤回了一条消息”
    // 统一把“我滴个妈呀”改成“我的天”(她口头禅)
}

为什么要做这种“脏活”?因为视频里要用字幕展示对话,原文出现的错别字或者系统残留,会瞬间破坏气氛,有过一次体验:视频里突然冒出“[语音]”,她差点问我“这是哪个姑娘发的语音?”……

第二步:合成引擎 —— 核心是FFmpeg的Go版“遥控器”

Go本身不处理视频编码,它干的事是 调度FFmpeg,我用的是os/exec包,像下命令一样让FFmpeg帮我渲染每一段。

大致的渲染逻辑是这样的:

按周生成片段(每周一个短视频)

每周的素材归到一个临时目录,用ffmpegconcat协议把照片和视频串起来,照片停留3秒,视频按原长度。

# 实际Go代码里是把下面这个命令拼接出来执行
ffmpeg -f concat -safe 0 -i week_01.txt -vf "fps=24,scale=1920:1080" -c:v libx264 week_01.mp4

给片段“加情绪标签”

我从她朋友圈、微博、甚至聊天记录里提取关键词,给每周打一个情绪标签:happyquarrelsweetmiss,然后根据标签调整色调:

  • happy:饱和度+20,亮度+5
  • quarrel:对比度+10,饱和度-10(画面稍微灰一点,但别太丧)
  • miss:色调偏蓝,加一点胶片颗粒效果

我用了一个简单的JSON配置来映射:

{
  "色调映射": {
    "happy":  {"hue": 0, "sat": 1.2, "bright": 1.05},
    "quarrel": {"hue": 0, "sat": 0.8, "bright": 0.95},
    "miss": {"hue": 230, "sat": 
 0.9, "bright": 0.98}
  }
}

Go读取这个JSON,然后用ffmpegcolorbalancehue滤镜一条龙调整。效果嘛……她后来问我“为什么那几周画面颜色怪怪的”,我说“因为那几天咱俩在冷战”,她直接笑场了。

插入“聊天字幕”

这是最费心思的部分,我把聊天记录里一些“金句”提取出来,用FFmpeg的drawtext滤镜嵌入到视频里。

比如我们第一次说“我爱你”那天的对话,我让它持续显示10秒,字体用的是汉仪圆体(她喜欢圆圆的字)。

// 伪代码,实际更复杂
drawtext := fmt.Sprintf("drawtext=text='%s':fontfile=./fonts/圆体.ttf:fontsize=36:fontcolor=white:box=1:boxcolor=black@0.4:boxborderw=10:x=(w-text_w)/2:y=h-text_h-100:enable='between(t,0,10)'")

这里有个细节:不要把聊天记录全部堆上去,只选那些有“时间戳特征”的,

  • 凌晨3点还在聊 → “那晚咱俩都没睡,聊到天亮”
  • 吵架后她先发来的一条长语音转文字 → “我错了,但你要哄我”

第三步:整合成一个“365小时光流”

所有周片段都渲染好后,最后一步是用Go写一个 concat清单,让FFmpeg把所有片段无缝拼接,同时片头片尾用ass字幕文件(自定义特效)。

片头文案我自己写的,就一句话:“从2023.05.20到2024.05.19,365天,每一天都算树(数)”,片尾是她生日那天的一张拍立得,加上一行小字“下一年的代码,我先写着”。

一些你肯定会想知道的细节(和坑)

性能问题:跑了一整晚

我用的MacBook Pro M1,素材总共80GB,程序跑了大概6个小时,中间FFmpeg崩溃过两次,是因为某段视频编码参数设错了,我的建议是:每生成一周的片段就检查一下,别攒到最后才看

字幕乱码问题

Go里用drawtext时,中文字体路径一定要写对,而且字体文件别用系统字体,直接复制一份到项目目录,不然会出现一堆方框。

如何“让视频不像程序员流水线”

我加了 随机度:每天的照片顺序不按时间轴排,而是按她当时发的朋友圈文案里的情绪强度排序,还有BGM的切入切出,我用afade滤镜做了淡入淡出,贝兹菲尔特曲线的那种慢节奏,而不是一刀切。

BGM版权问题

我用的两首歌都是我们俩那时常听的,没有商用,纯粹自娱自乐,如果你也要公开秀恩爱,建议用AI作曲或者CC0协议的纯音乐,避免被平台下架。

这个视频最后的样子

说实话,成品并不完美。
有几段因为照片像素太差(她13年的手机拍的),画质感人,有一段字幕跟BGM踩点没对齐,差了0.5秒,甚至片头那个“树”字,她问我是不是打错了,我说“树就是数,谐音梗,浪漫不?”,她白了我一眼。

但她在看到第187天(我们第一次旅行)那段时,哭了。
然后扭头问我:“这个视频你写了多少行代码?”
我说:“大概1500行,还不包括删掉的重构。”
她说:“哦,那比情书还长。”

最后聊点题外话

用Golang做纪念视频这件事,本质上是一种把“数据”和“情感”强行捆绑的尝试,它不完美,甚至有点怪——哪有情侣一周年送个程序跑出来的视频的?但我觉得,技术本身就是一种表达方式,跟写诗、画画、弹吉他没什么两样。

如果你也想试试,代码其实不难,难的是 素材收集的耐心对细节的在意,每天拍一张照片、每周导出一次聊天记录、每月存一段视频片段,365天后,你会发现,这事儿最值的不是那个视频,而是你为了这个视频,认认真真地回看了这一年的每一天。

好了,我得去改改下一年的代码了——她说“下次视频里我的镜头要多一点”,程序员嘛,需求清晰,好办。

用Golang写一封给她的情书,打造属于你们的情侣一周年视频365纪念