365天纸飞机AKB48视频,我用Golang写了一个循环播放器,结果把自己看哭了
- 旅游
- 2026-07-28 12:56:16
- 58
为什么突然想写这个?一个莫名其妙的冲动
事情是这样的,上个月我整理硬盘,翻出一个叫“365天纸飞机AKB48视频”的文件夹,那是去年某个深夜,我蹲在B站刷到的一个挑战视频——有人坚持每天折一只纸飞机,背景音乐永远是AKB48的《365日の紙飛行機》,我当时觉得这事儿挺傻的,但出于某种奇怪的心理,我居然把那个系列视频全下载了。
然后我就在想:如果换成Golang来写一个程序,每天自动播放当天的视频片段,会是什么效果? 这个念头一冒出来,我就知道完蛋了——不写出来今晚别想睡觉。
先说说这个“365天纸飞机AKB48视频”到底是什么鬼
其实这个挑战挺简单的:一个人每天折一只纸飞机,对着镜头展示,背景放AKB48的《365日の紙飛行機》,但问题在于——你得坚持整整一年。
我数了数,下载下来的视频总共有:
- 365个独立片段(一天一个,不重样)
- 总时长大概是18小时47分钟
- 每个片段长度从2分半到4分钟不等
- 分辨率从480p到1080p都有(肉眼可见的伤心)
最离谱的是第213天那个视频,明显是凌晨3点拍的,折纸手都在抖,但背景音乐一直没断,这种倔强,说实话有点戳人。
用Golang写个播放器?一开始我觉得很简单
我最初的思路是:用Go写一个CLI程序,每天自动播放对应日期的视频文件,start、stop、pause基本操作就行。
代码最初长这样:
package main
import (
"fmt"
"os/exec"
"strconv"
"time"
)
func main() {
day := time.Now().YearDay()
fmt.Printf("今天是第 %d 天\n", day)
filename := fmt.Sprintf("videos/day%03d.mp4", day)
cmd := exec.Command("ffplay", "-autoexit", filename)
cmd.Run()
}
跑起来倒是没问题,但看着黑漆漆的终端窗口,我感觉不对劲——这玩意儿跟折纸飞机本身一样单调。缺少仪式感。
真正的挑战不在代码,在“人”
费曼说,如果你不能把一个概念解释清楚,说明你没真正理解它,我当时就卡在这个地方:我是想复制那个视频挑战的行为,还是想传递那种“坚持”的感觉?
我重新看了几段视频,重点不在折纸的动作上,而是:
- 背景音乐循环播放时的轻微变化(有时候是歌的高潮部分被剪进来了)
- 那人的手在折纸时的细微颤抖
- 每天视频结尾的一句话总结(有时是“今天工作好累”,有时是“这架飞机应该能飞到月球”)
这些东西才是灵魂,Golang代码写得再漂亮,也写不出那种感觉,但我至少可以让播放器尊重这些细节。
第二版:带进度条和备注的播放器
我重写了代码,加入了两个关键功能:
显示当天视频的备注信息
我手动整理了所有视频的描述(从文件名里提取的注释部分),放在一个Go结构体里:
type DailyNote struct {
Day int
Note string
Mood string // 情绪标签
}
进度条可视化
用\r实现终端覆盖刷新效果,显示当天已经播放了多少:
func progressBar(current, total float64) string {
width := 50
filled := int((current / total) * float64(width))
bar := strings.Repeat("=", filled) + strings.Repeat(" ", width-filled)
return fmt.Sprintf("[%s] %.1f%%", bar, (current/total)*100)
}
这个进度条跑起来的时候,屏幕上一卡一卡地推进,配上那个循环的旋律——我突然觉得,这不就是在模拟时间本身吗?
为什么选Golang?不是因为它快,是因为它“笨”
说点实话,用Python写这个功能大概两小时就能搞定,Rust也行,但我选Go的理由有点怪:Golang的编译输出是一个静态二进制文件。
我不需要装ffmpeg、不需要Python环境、不需要依赖管理,双击就能跑,而“365天纸飞机”这个挑战本质上就是一个人对时间的笨拙坚持——Go的静态编译气质正好和它吻合。
而且Go的os/exec包让我可以直接调用ffplay播放视频,输出控制台日志,很简陋但够用,那种“刚刚够用”的感觉,和视频里折纸飞机的动作很像——不需要多精致,只要每天做。
重构:加入日期选择器和跳跃播放
写完之后我发现一个问题:没人会真的每天打开这个程序,大多数人(包括我自己)都是一口气刷完整个系列。
所以我加了一个交互模式:
fmt.Println("选择操作:")
fmt.Println("1. 播放今天")
fmt.Println("2. 随机播放一天")
fmt.Println("3. 连续播放(从第一天到第365天)")
连续播放模式最离谱,我试了一次,从头放到尾,坐在那儿看了18个小时,到第100天左右的时候,视频里的人折纸越来越熟练,但背景音乐开始出现不同版本的翻唱。第187天居然用的是钢琴版。
Golang的协程在这里帮了大忙——我可以同时启动时间显示、进度条、和下一个视频的预加载:
go func() {
for {
fmt.Printf("\r当前时间: %s | 进度: %s",
time.Now().Format("15:04:05"),
progressBar(float64(day), 365.0))
time.Sleep(1 * time.Second)
}
}()
那封藏在第365天视频里的信
视频播到第365天的时候,我正靠在椅子上发呆,折纸的那个人——实际上我之前一直以为是个AI生成的账号——他展示完最后一只纸飞机后,突然开口说话了:
“这架飞机……是真的,我折了整整一年,有时候我想放弃,但音乐一直在,如果你看到这里,谢谢你陪我走过这365天。”
Golang程序还在跑,终端里循环着最后一段旋律,我感觉眼眶有点发酸。
赶紧关掉了程序,但那个画面和声音一直留在脑子里。
技术总结?不,只是记录一种状态
我现在电脑里还留着这个Go程序,偶尔打开,随机跳到某一天看看。
技术上没什么了不起的:
- 200行不到的Go代码
- 用
ffplay播放视频 - 用
time包处理时间 - 用
sync包管理并发
但每次跑起来,看到那个简陋的进度条在终端里往前爬,我就想起一件事:也许我们写代码的意义,有时候不是解决问题,而是记录一种状态。
那个折纸飞机的人用365天证明了一件事——坚持不需要意义,坚持本身就是意义,而我用Golang把这个过程重新播放了一遍,虽然慢,虽然笨,但至少没弄丢。
最后说一句:如果你真想跑这个程序,别用别人的视频,自己拍365天,自己写代码,那才叫你的365天。
