在一起365发抖音视频,用Golang写一段浪漫的自动化脚本

你有没有想过,365天,每天发一条抖音,记录你和那个人的点点滴滴?听起来挺浪漫的,对吧?但实际操作起来——天哪,谁有那个精力天天剪辑、配...

你有没有想过,365天,每天发一条抖音,记录你和那个人的点点滴滴?听起来挺浪漫的,对吧?但实际操作起来——天哪,谁有那个精力天天剪辑、配乐、想文案?我试过,第三天就放弃了,后来我琢磨着,能不能用Golang写个脚本,让这事儿自动跑起来?

还真让我捣鼓出来了。

为什么是Golang,不是Python?

说实话,一开始我想用Python,Python做视频处理确实方便,有moviepy、opencv这些库,但问题出在部署上——我要让这个脚本在我家那台老旧NUC上7x24小时运行,Python那套环境管理真是让人头疼,虚拟环境、依赖冲突、版本兼容……光是折腾环境就能消耗掉一半的热情。

Golang就不一样了,编译成一个二进制文件,扔到服务器上就能跑。不用装Python、不用pip install、不用担心依赖版本,而且Golang的并发模型处理视频转码、上传这些IO密集型任务,简直不要太舒服。

package main
import (
    "fmt"
    "time"
    "os/exec"
    "log"
)
func main() {
    fmt.Println("开始执行365天抖音自动化任务...")
    // 每天执行一次
    ticker := time.NewTicker(24 * time.Hour)
    for range ticker.C {
        startDailyTask()
    }
}

你看,核心调度就这几行,但真正有价值的部分,是后面那些细节。

视频素材从哪里来?—— 建立你的“回忆库”

别想着每天现拍素材,那不现实,你需要的是一套素材管理机制

素材收集策略

我给自己定了个规矩:平时随手的照片、视频片段、甚至语音备忘录,都扔到一个目录里,然后脚本会按日期筛选。

func collectMedia() ([]string, error) {
    baseDir := "/home/media/365"
    files, err := ioutil.ReadDir(baseDir)
    if err != nil {
        return nil, err
    }
    var todayFiles []string
    today := time.Now().Format("2006-01-02")
    for _, file := range files {
        // 按文件名中的日期筛选
        if strings.Contains(file.Name(), today) {
            todayFiles = append(todayFiles, filepath.Join(baseDir, file.Name()))
        }
    }
    return todayFiles, nil
}

一开始我犯了个错——把所有素材堆在一个文件夹里,结果运行半年后,找素材比找针还难,后来学乖了,用年月日三级目录管理:

/media/365/
├── 2024/
│   ├── 01/
│   │   ├── 2024-01-01_新年第一缕阳光.mp4
│   │   ├── 2024-01-01_早餐合影.jpg
│   │   └── 2024-01-02_下雪啦.mov
│   ├── 02/
│   │   └── ...
│   └── ...
└── 2025/
    └── ...

这个结构看着简单,但在Golang里处理起来特别顺畅。filepath.Walk配合正则表达式,几行代码就能搞定素材筛选。

视频合成:封面的魔力

光有素材还不够,365天发同一种风格,别说观众,你自己都看腻了。你得让每天的推送有一点不同

动态生成封面

我用了Golang的image包和golang.org/x/image/font来生成封面文字,效果上,我会在视频前3秒叠加一段字幕——在一起的第218天”。

func createCoverText(days int) (string, error) {
    // 创建一个600x800的图片作为封面
    img := image.NewRGBA(image.Rect(0, 0, 600, 800))
    draw.Draw(img, img.Bounds(), &image.Uniform{C: color.White}, image.Point{}, draw.Src)
    // 添加文字
    text := fmt.Sprintf("第%d天", days)
    // 使用合适的字体渲染……
    // 保存为临时文件
    outputPath := "/tmp/cover_" + time.Now().Format("150405") + ".png"
    f, _ := os.Create(outputPath)
    defer f.Close()
    png.Encode(f, img)
    return outputPath, nil
}

这段代码我改了七八版,一开始用的是系统默认字体,结果在服务器上渲染出来全是乱码,后来才发现要手动指定字体文件路径,Golang在这方面确实没Python方便,但胜在稳定——字体文件一放,管你什么操作系统都能跑。

FFmpeg调用的那些坑

真正的视频合成,我交给了FFmpeg,Golang通过exec.Command调用它:

func renderVideo(inputFiles, outputFile string, days int) error {
    // 构建FFmpeg命令
    args := []string{
        "-i", inputFiles,
        "-vf", fmt.Sprintf("drawtext=text='第%d天':fontfile=/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc:fontsize=24:fontcolor=white:x=10:y=10", days),
        "-c:v", "libx264",
        "-preset", "fast",
        outputFile,
    }
    cmd := exec.Command("ffmpeg", args...)
    return cmd.Run()
}

注意那个fontfile参数——别忘了指定,我第一次跑的时候画面里根本没有文字,查了半天才发现是字体路径问题,还有那个-preset fast,预设值用fast就够了,别用slow或者placebo,慢不是问题,问题是CPU占用率飙升到100%,我那台NUC直接罢工两周。

抖音API对接:不是想象中的那么简单

上传抖音,需要调用抖音开放平台的API,这里有个头疼的事——抖音的授权过期机制

Token刷新策略

抖音的access_token有效期只有7天,你不可能每7天手动刷新一次,所以脚本里必须内置自动刷新token的功能。

type TokenManager struct {
    accessToken  string
    refreshToken string
    expiresAt    time.Time
    client       *http.Client
}
func (tm *TokenManager) GetValidToken() (string, error) {
    if time.Now().Before(tm.expiresAt) {
        return tm.accessToken, nil
    }
    // 提前一天刷新
    if time.Now().Add(24*time.Hour).After(tm.expiresAt) {
        return tm.refresh()
    }
    return tm.accessToken, nil
}

这个结构体,我用了一个sync.RWMutex来保证并发安全,毕竟这脚本可能同时处理多个视频,token别被抢走了。

视频上传的断点续传

抖音API有个限制——单次上传文件不能超过50MB,但有时候视频会超过这个大小,特别是如果你用高清素材拼接,这时候就得用分片上传。

Golang的net/http包对分片上传的支持很不错,但要注意一点:分片大小必须是整MB,抖音那边有要求,我设成4MB一片,上传失败后自动重试3次。

func uploadVideoPart(filePath string, partNumber int, totalParts int) error {
    data := map[string]interface{}{
        "file": multipartFile,
        "part_number": partNumber,
        "total_parts": totalParts,
    }
    // 实际调用用POST请求
    // 重试逻辑
    for i := 0; i < 3; i++ {
        err := doUpload(data)
        if err == nil {
            return nil
        }
        time.Sleep(5 * time.Second)
    }
    return fmt.Errorf("上传第%d部分失败", partNumber)
}

这段代码看起来简单,但我调了整整一天——抖音的分片上传接口返回的响应格式和文档上写的不一样,最后用fmt.Println打印原始响应才找到问题,最朴素的调试方法反而最管用。

文案生成:让文字有点温度

光发视频,没有文案,点赞量上不去,但每天写文案?我写不出来。

我用的是模板+随机替换,先准备一批模板,然后每天随机取一个,再填充当天的数据。

在一起365发抖音视频,用Golang写一段浪漫的自动化脚本

模板ID 替换变量
T001 在一起的{day}天,{mood} day, mood
T002 今天是我们{day},{event} day, event
T003 {day},和{person}一起{mood} day, person, mood

变量从JSON配置文件里读取,每天轮换。

func generateCaption(days int) string {
    moods := []string{"开心", "温暖", "感动", "甜蜜", "安稳"}
    events := []string{"一起看了日出", "一起吃了火锅", "一起遛狗", "一起宅家"}
    text := fmt.Sprintf("在一起的第%d天,s。", days, events[rand.Intn(len(events))])
    if days%100 == 0 {
        text = fmt.Sprintf("🎉在一起%d天啦!%s", days, events[rand.Intn(len(events))])
    }
    return text
}

注意,这里的文案生成是伪随机,我用rand.Seed(time.Now().UnixNano())初始化种子,确保每天不同,但如果你希望某些特殊日子(比如100天、一周年)有特殊文案,得手动写条件判断。

部署:别让脚本死在第二天

脚本写好了,怎么让它一直跑?

进程守护

我用了supervisor来管理这个Golang二进制文件,配置文件大概长这样:

[program:douyin365]
command=/home/user/douyin365/douyin365
directory=/home/user/douyin365
autostart=true
autorestart=true
stderr_logfile=/var/log/douyin365_err.log
stdout_logfile=/var/log/douyin365_out.log

日志很重要,第一次部署,我没开日志,结果脚本跑了三天突然挂了,完全不知道原因,开了日志才发现——哦,原来是磁盘满了,视频文件越攒越多,本地工作目录炸了。

解决办法是在脚本里加一个磁盘清理步骤:

func cleanUpOldVideos() error {
    cmd := exec.Command("find", "/tmp", "-name", "*.mp4", "-mtime", "+7", "-delete")
    return cmd.Run()
}

7天前的临时视频,删掉,别等到磁盘满了再手动清理,那时候已经晚了。

定时任务的坑

你可能想说:“用cron不就行了?”我一开始也这么想,但cron和Golang脚本配合,有几个坑要注意:

  1. 环境变量:cron执行时环境变量和你登录时不一样,Golang脚本里如果用了os.Getenv获取某些配置,在cron下可能拿不到。
  2. 工作目录:cron默认从HOME目录执行,如果脚本里用到相对路径,得在脚本开头os.Chdir
  3. 日志输出:cron会把stdout邮件发给你(如果没配置邮件则丢弃),建议把日志写到文件里。

后来我干脆在脚本里内置了一个定时器循环,直接for range ticker.C,然后用supervisor保活,简单粗暴,还避免了cron的这些问题。

实际运行中的小意外

运行了半年,遇到不少“惊喜”:

  • 视频编码不兼容:手机拍的视频编码格式五花八门,有些抖音直接拒绝上传,后来我在FFmpeg命令里加了-c:v libx264强制转码。
  • 网络波动:上传到一半,WiFi断了,需要实现断点续传。
  • API限流:抖音API有调用频率限制,每天只能上传30条,所以我设了每天23:50上传,避开高峰期。
  • 服务器时区:服务器用UTC时间,结果视频发布的时间和实际差8小时,最后在代码里time.LoadLocation("Asia/Shanghai")设置时区。

这些小问题,单个看都不致命,但堆在一起,能让你的规划彻底泡汤。别想着一次性设计完美,而是保持迭代,每次修复一个问题。


你现在看完这篇,可能觉得“哇,好麻烦”,但想想看,365天后,你收到一整年的视频合集,每一帧都是那些共同走过的日子,那种感觉,值得这半小时的折腾。

Golang跑起来很安静,风扇都不怎么转,你只需要给它一个电源,别的交给时间。

哦对了,记得把视频也导出一份到移动硬盘——因为抖音账号哪天万一被封了呢?

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

(18)

文章推荐

发表回复

本站作者才能评论

评论列表(4条)

  • kyadmin
    kyadmin 2026-07-07

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

  • kyadmin
    kyadmin 2026-07-07

    希望本篇文章《在一起365发抖音视频,用Golang写一段浪漫的自动化脚本》能对你有所帮助!

  • kyadmin
    kyadmin 2026-07-07

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

  • kyadmin
    kyadmin 2026-07-07

    本文概览:你有没有想过,365天,每天发一条抖音,记录你和那个人的点点滴滴?听起来挺浪漫的,对吧?但实际操作起来——天哪,谁有那个精力天天剪辑、配...

    联系我们

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

    关注我们