从第一天到第三百六十五天,我用Go语言写了一个记录365天视频的傻瓜工具

你刷到过那种“一年每天拍一秒钟”的视频吗?我特别佩服能做满三百六十五天的人,但说实话,等我真去试的时候,第一个月就乱套了——手机里存了一...

你刷到过那种“一年每天拍一秒钟”的视频吗?我特别佩服能做满三百六十五天的人,但说实话,等我真去试的时候,第一个月就乱套了——手机里存了一堆素材,文件名全是IMG_20250413_203114.mp4,根本不知道哪天拍的是哪天,我决定用Go语言写个小工具,把“第一天到第三十五天视频”这种整理工作变得不那么痛苦。

为什么偏偏是Go?

我承认,Python写脚本更快,但Go有它的倔脾气:编译出来就是个二进制文件,扔到哪儿都能跑,不用装环境,你要知道,我家的老笔记本连Python都懒得装,而且Go处理文件操作、并发重命名、时间计算这些事,写起来有种奇特的干脆感。

更重要的是,Go的标准库就把时间处理搞定了。time.Time直接带时区,不用像某些语言那样加载一吨依赖,这对我这种“能省一步是一步”的心态特别友好——毕竟365天视频整理这么琐碎的事,工具越轻越好。

我的需求其实很简单(但细节很烦)

动手之前,我给自己列了个需求清单,说白了就是:

  • 输入:一个文件夹里乱糟糟的视频,文件名带日期(比如从GoPro、手机导出的)
  • 输出:统一的命名格式,比如day001.mp4day002.mp4一直到day365.mp4
  • 规则:按拍摄时间排序,而不是按文件名或者修改时间,不然你拷贝一次文件,顺序就全乱了
  • 容错:有些视频可能没拍,名字要跳过(比如今天累了,没录),不能强制连续编号

后来我才发现,最麻烦的不是代码本身,而是“第一天”到底从哪天算起,是你开始拍的那天?还是元旦?还是听说这个挑战的那天?我最后决定用命令行参数让用户自己指定起始日期,默认用项目里最早那个视频的拍摄时间当第一天,这样最符合直觉。

核心代码长这样(带点我的碎碎念)

下面是我自己用的函数,逻辑不算复杂,但有几个坑我踩了很久,你可以直接拿去改。

package main
import (
    "fmt"
    "os"
    "path/filepath"
    "sort"
    "strings"
    "time"
)
type videoInfo struct {
    path     string
    shotTime time.Time
    dayIndex int
}
// 核心:从文件名里提取拍摄时间
// 不同设备命名不同,我把我遇到的情况写进来了
func extractShotTime(filename string) (time.Time, error) {
    layouts := []string{
        "20060102_150405", // 常见安卓格式: 20250414_183022
        "2006-01-02 15.04.05", // 苹果有时候这样
        "IMG_20060102_150405", // 苹果带IMG前缀
        "20060102-150405",     // GoPro风格
    }
    for _, layout := range layouts {
        for _, sep := range []string{" ", "_", "-", "."} {
            // 这里做了个笨办法:把文件名里的间隔统一替换,再匹配
            tmp := strings.ReplaceAll(filename, sep, "_")
            // 其实我省略了一些细节,但核心思路就是这样
        }
        t, err := time.Parse(layout, filename)
        if err == nil {
            return t, nil
        }
    }
    return time.Time{}, fmt.Errorf("无法从 %s 中提取时间", filename)
}

然后主流程是这样的思路:

  1. 读目录,筛出所有.mp4.mov.avi文件(我自己主要拍mp4)
  2. 依次尝试提取拍摄时间,提取失败的扔到error_log.txt里,不中断整个流程
  3. 排序(按时间从小到大),然后编号
  4. 关键一步:重命名为dayXXX.mp4之前,先检查目标文件是否存在,比如你之前已经整理过一部分了,重新跑时不能把覆盖了,我加了个布尔参数--force,默认不覆盖。

对了,还有个特别容易忽视的细节:时区,如果你在手机上看拍摄时间是对的,但用Go解析时,默认用UTC,导致排序错乱,我一开始就栽在这上面,后来强制加了time.Local,才正常。

表格:365天视频整理的三阶段策略

我用这个工具整理的节奏大概分三阶段,给你做个参考,不用一次全整理完,我会骗自己“今天只搞一周的”。

阶段 时间范围 主要行动 工具重点
起步期 第1天 - 第30天 每天拍,但可能漏拍几天 工具要能跳过缺失的编号,不能中断
中期调整 第31天 - 第200天 开始有废片、重复片段 需要加一个--dedupe选项,按大小和hash去重
冲刺期 第201天 - 第365天 素材积累多,硬盘告急 批量压缩+重命名同时进行,用Go的os/exec调用ffmpeg

这个表格的意思就是:别追求一步到位,你第一天写个简单版本,能跑就行;到了中期,你会发现缺漏;后期可能需要加压缩功能,Go的好处是编译快,改完立刻能跑。

并发重命名:多快好省但小心犯错

Go的goroutine太好用了,我忍不住就加了并行处理,但你要知道,文件重命名涉及到同一个目录的元数据操作,并发太高会有问题,我试过5个并发,结果有俩文件报错“文件名已存在”。

后来我改成这样:分两阶段,第一阶段并发读取所有文件信息(这个很快),第二阶段串行执行重命名,因为重命名本身只要毫秒级,串行根本感觉不到慢,但安全很多,代码长这样(简化版):

func renameSequentially(files []videoInfo) error {
    for i := range files {
        from := files[i].path
        to := filepath.Join(filepath.Dir(from), fmt.Sprintf("day%03d.mp4", files[i].dayIndex))
        if _, err := os.Stat(to); err == nil && !forceOverwrite {
            continue // 目标已存在,跳过
        }
        if err := os.Rename(from, to); err != nil {
            return fmt.Errorf("重命名失败: %s -> %s, 原因: %v", from, to, err)
        }
        // 小提示:打印进度,不然你以为卡死了
        fmt.Printf("✓ %s -> %s\n", filepath.Base(from), filepath.Base(to))
    }
    return nil
}

这段代码看着简单,但那个fmt.Printf特别重要,整理365个文件的时候,下一秒没反应,你会怀疑程序挂了,有了这个输出,看着一行行跳过去,心里踏实。

整理一年视频,我悟到的三个道理

这工具写完之后,我真用了一年(坚持下来了,虽然中间断过十几天),回顾这段经历,有三个感受值得分享:

第一,缺失的日子不必硬补**,我中间感冒了差不多一周,没拍,原本想用旧素材凑数,后来想想没必要,工具帮我跳过那些天,让编号自动错位,但我自己心里清楚哪里断的,365天视频的意义不在连续性,而在你确实记录了某段时间。

第二,命名规则比你想的还重要**,当视频多到上千个,你按dayXXX排序,文件管理器里排列得整整齐齐,而且因为前面补了零,day2不会排在day10后面,这不算什么高深技巧,但用的时候会觉得“真是明智”。

第三,Go标准库的time包太够用了**,我一开始担心要引入什么复杂的时间库,其实没用上。time.Parse加上自定义格式,几行就搞定,标准库就是最可靠的,少折腾依赖,多留点时间给拍摄本身。

关于日志和错误处理(现实很骨感)

真实场景里,你的素材可能会出各种幺蛾子:某台相机的时间没设置对、有的视频是屏幕录制(时间戳异常)、还有从微信保存下来的视频(名字完全不规则),我的工具里加了个error_log.txt,所有解析不了的都写进去。

从第一天到第三百六十五天,我用Go语言写了一个记录365天视频的傻瓜工具

第一周跑完,日志里有二十多条,后来我耐心看,发现多数是微信保存的wx_camera_20250415_234213.mp4这种格式,于是我加了一行解析规则,专门对付它,看,工具就是这样“喂”出来的——不是一次写好的,是一边用一边补。

哦对,还有个大坑:文件名里可能有空格,Linux下没问题,但如果你在Windows上跑,重命名时带空格的文件名能烦死你,我统一在重命名前把空格替换成下划线,免得后续脚本出问题,这种事靠文档记录太麻烦,代码里顺手处理掉才省心。

最后分享下完整的命令行用法(我的日常操作)

我通常这样跑:

./videoday -input /home/me/videos/2025 -start "2025-01-01" -format mp4 -force=false

如果第一天不是1月1号,我会用-start指定,如果忘记指定,程序会自动找最早视频的拍摄时间。

还有一个-dry-run参数,很重要。先跑一遍,只打印不改文件名,看看会怎么命名,确认没问题了再真正执行,这习惯救了我好几次,有一次发现时区没设置对,差点把后半年视频全错位了。

这工具现在还在我的硬盘里,被我改得有点“四不像”了——因为中间加了好多功能(压缩、去重、甚至生成HTML索引页),但最核心的那个“从第一天到第三百六十五天视频”的rename功能,我一行没删,每次打开终端,跑一遍,看着那些day001.mp4day002.mp4排得整整齐齐,就觉得这一年没白折腾。

本文来自作者[kyadmin]投稿,不代表be365立场,如若转载,请注明出处:http://hljbesthome.com/keji/2515.html

(1)

文章推荐

发表回复

本站作者才能评论

评论列表(4条)

  • kyadmin
    kyadmin 2026-08-26

    我是be365的签约作者“kyadmin”!

  • kyadmin
    kyadmin 2026-08-26

    希望本篇文章《从第一天到第三百六十五天,我用Go语言写了一个记录365天视频的傻瓜工具》能对你有所帮助!

  • kyadmin
    kyadmin 2026-08-26

    本站[be365]内容主要涵盖:be365,be365网站,be365网址,ball365体育,Be365排名

  • kyadmin
    kyadmin 2026-08-26

    本文概览:你刷到过那种“一年每天拍一秒钟”的视频吗?我特别佩服能做满三百六十五天的人,但说实话,等我真去试的时候,第一个月就乱套了——手机里存了一...

    联系我们

    工作时间:周一至周五,9:30-18:30,节假日休息

    关注我们