当前位置:首页 > 汽车 > 正文

用Golang写一年365天带娃的视频,这事儿我认真琢磨过

  • 汽车
  • 2026-07-01 13:43:24
  • 326
摘要: 为什么突然想用Golang干这事儿?说实话,最开始我也没想过用代码来管理带娃视频,直到有一天,手机提示“存储空间不足”,我才发现...

为什么突然想用Golang干这事儿?

说实话,最开始我也没想过用代码来管理带娃视频,直到有一天,手机提示“存储空间不足”,我才发现——好家伙,一年下来拍了三百多个视频,从娃第一次翻身到喊出“爸爸”,全挤在相册里,手动分类?那得熬几个通宵,于是我想:既然我是写Go的,不如让程序替我干活?

Golang的并发特性特别适合处理大量文件,而且编译出来的东西跑起来贼快,不像Python脚本还要装依赖,我当时的思路很简单:把视频按日期自动归档,顺便提取关键帧做成缩略图

第一步:扫描全年视频文件

我先写了个函数,遍历指定目录下的所有视频,Go的filepath.Walk非常好用,但注意别把系统文件也扫进来,得过滤格式:

func scanVideos(root string) []string {
    var videos []string
    filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
        if err != nil { return nil}
        if !info.IsDir() && strings.HasSuffix(strings.ToLower(info.Name()), ".mp4") {
            videos = append(videos, path)
        }
        return nil
    })
    return videos
}

这一步看着简单,但有个坑:视频文件名最好带上日期,比如2023-01-15_第一次站起来.mp4,我家孩子他妈习惯用手机默认命名,全是IMG_20230115_xxxx.mp4,这就得从文件名里截取日期,你可以用正则:

re := regexp.MustCompile(`(\d{4})(\d{2})(\d{2})`)
match := re.FindStringSubmatch(filename)
// match[1]是年,match[2]是月,match[3]是日

第二步:按月份和事件类型打标签

一年365天,视频不是均匀分布的,娃刚学会爬那两周,每天拍十条;后几个月慢慢少了,我建了个简单的标签系统,用Golang的map搞定:

月份 视频数量 主要标签
1月 45 满月、黄疸消退
3月 62 抬头练习、第一次翻身
6月 38 添加辅食、出牙
9月 29 学爬、生病记录
12月 41 学走路、生日派对

标签怎么提取?我用了最笨的办法——关键词匹配,比如文件名包含“翻身”、“爬”、“走路”就自动打标签,Go的strings.Contains效率很高,配合sync.Map做并发标签写入,跑1000个视频只要几秒:

var tagMap sync.Map
for _, v := range videos {
    if strings.Contains(strings.ToLower(v), "翻身") {
        tagMap.Store(v, "大动作发育")
    }
}

这方法不完美,有时候把“翻身农奴把歌唱”之类的奇怪文件名也会打上标签(还好我没存这种视频),后来我干脆加了个人工确认步骤:把自动标签结果写入JSON文件,用json.Decode读出来手动改。

第三步:生成时间线缩略图

光分类不够,视频太多,想快速找到“去年春天带娃去公园”那段,总不能一个个点开看,我借助ffmpeg(用Go的exec.Command调用)提取关键帧:

func extractThumbnail(videoPath, outputPath string) error {
    cmd := exec.Command("ffmpeg", "-i", videoPath, "-ss", "00:00:05", 
        "-vframes", "1", outputPath)
    return cmd.Run()
}

这里有个小技巧:别每帧都取,优化性能,我是在转码时顺便把缩略图命名成[原始文件名]_thumb.jpg,然后塞进一个与视频同名的文件夹里,这样打开目录时,缩略图网格一目了然,比相册自带的“时光轴”好用。

第四步:自动生成年度回顾视频

这是最花哨的功能——把一年视频剪辑成一个5分钟的集锦,用Golang拼接ffmpeg命令,读取之前生成的标签JSON,按月份抽取代表性片段:

  1. 先按标签权重排序:学走路类的权重设高,因为那是里程碑事件
  2. 每个月份至少选一条视频,避免某个月被跳过
  3. 用ffmpeg的concat滤镜把片段连起来,加上片头片尾

片头我用Go的text/template生成文字:“2023年度带娃实录”,字体选思源黑体(免费),背景音乐从/music目录随机选一首,第一次跑出来的视频有34分钟,后来限制每个片段3~5秒,硬是压到了7分钟。

这事儿真不能全自动,有一次程序把娃嚎啕大哭的片段加到“快乐时光”标签下,生成出的视频简直像恐怖片,从那以后,我加了个人工审核步骤:输出一个待确认.tsv文件,里面列出每个片段的时间和标签,在电脑上快速过一遍。

说点实在的坑

这条路不是一帆风顺的,有几个问题我必须提一下:

  1. 时间戳偏移:手机视频的创建时间可能被修改过(比如微信传过来的),得用ffprobe读元数据里的creation_time
  2. 文件重复:老人用不同手机拍同一个场景,得用md5校验去重,Go标准库就有crypto/md5,别用sha256慢死了
  3. 内存控制:处理4K视频时,一次性加载所有文件路径会占用不少内存,改用filepath.Walk的回调方式,边扫描边处理

还有,别像我一样在笔记本上跑这个程序——风扇转得比飞机发动机还响,建议放到云服务器上,或者晚上睡觉前跑,用time.AfterFunc设置定时任务。

这事儿到底值不值?

说实话,如果你一年只拍十来个视频,手动整理更快,但像我家这样,从产房到周岁,手机里攒了300多G素材的,Go写的这个小工具真的能救命,整理完那一刻,你看着时间线从春天到冬天,从哭闹到会叫“妈妈”,那种感觉——不是用语言能描述的。

代码放在GitHub上,叫baby-year(别搜,我真的写了一个,但太丑了不好意思公开),各位自己动手折腾的时候,记得给程序留个退出开关,用signal.Notify监听Ctrl+C,别像我第一次一样,跑了一半想停只能拔电源。

用Golang写一年365天带娃的视频,这事儿我认真琢磨过