一年前的今天,我点了一根蜡烛,拍了个视频,365天后,想再点一根,但发现手机里存了太多视频,找不到之前的了,你是不是也遇到过这种问题?今天就用Go语言写个小工具,帮你管理“一周年点蜡烛视频”这种周期事件,顺便把365天的倒计时和自动化任务都搞定。
核心需求:为什么是365?
先别急着写代码,一周年点蜡烛,核心就是“重复提醒”加“视频管理”,365天这个数字,其实是个周期,Go语言处理时间周期特别顺手,因为标准库time包提供了AddDate、Sub这些方法。
package main
import (
"fmt"
"time"
)
func main() {
// 假设去年今天你点了蜡烛
lastCandleDay := time.Date(2024, 3, 15, 20, 0, 0, 0, time.Local)
nextCandleDay := lastCandleDay.AddDate(1, 0, 0) // 加一年
fmt.Printf("下次点蜡烛时间:%v\n", nextCandleDay)
}
等等,这里有个坑——闰年,2月29号的人怎么办?Go的AddDate会自动处理,比如2024年是闰年,AddDate(1,0,0)会变成2025年3月1日,但如果你想要严格365天(不考虑闰年),就得用Add加365*24*time.Hour。
视频文件名里的时间戳提取
点蜡烛视频的文件名往往带日期,比如candle_20240315.mp4,用Go的正则表达式提取:
import (
"regexp"
"strconv"
"strings"
)
func extractDateFromFilename(filename string) (time.Time, error) {
re := regexp.MustCompile(`(\d{4})(\d{2})(\d{2})`)
match := re.FindStringSubmatch(filename)
if match == nil {
return time.Time{}, fmt.Errorf("没找到日期格式")
}
year, _ := strconv.Atoi(match[1])
month, _ := strconv.Atoi(match[2])
day, _ := strconv.Atoi(match[3])
return time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.Local), nil
}
这个函数看起来简单,但实际用的时候要注意:文件名里可能有多个数字,比如2024_03_15_candle_v2.mp4,得调整正则,或者用更严格的匹配,我自己的视频就经常命名不一致,后来统一用candle_YYYYMMDD_HHMMSS.mp4格式才解决。
目录遍历与视频筛选
假设你所有蜡烛视频都放在/videos/candles/目录下,用filepath.Walk或者os.ReadDir遍历:
import (
"os"
"path/filepath"
)
func listCandleVideos(root string) ([]string, error) {
var videos []string
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && strings.HasSuffix(strings.ToLower(info.Name()), ".mp4") {
if strings.Contains(strings.ToLower(info.Name()), "candle") {
videos = append(videos, path)
}
}
return nil
})
return videos, err
}
这里有个小技巧:用strings.ToLower做大小写忽略,因为有些相机拍出来的文件名是大写的,还有,别用filepath.Ext判断扩展名,因为.MP4和.mp4都是有效的,我吃过这个亏,后来改成了HasSuffix。
倒计时计算与通知
核心功能:计算下一次点蜡烛还有几天,配合time.Now():
func daysUntilNextCandle(lastVideoTime time.Time) int {
next := lastVideoTime.AddDate(1, 0, 0)
now := time.Now()
diff := next.Sub(now)
days := int(diff.Hours() / 24)
if days < 0 {
return 0 // 已经过期
}
return days
}
如果上次视频是昨天拍的,下次是明年今天,倒计时365天,这没问题,如果用户今天刚点了蜡烛,那下次是明年今天,倒计时也是365天,这个逻辑看起来合理,但实际场景里用户可能漏了一年——比如去年没拍,今年才想起来,这时候就应该找最近的一个蜡烛视频,算它到下一个365天的差值。
自动化:每天检查一次
用time.Ticker实现日常检查,顺便生成“点蜡烛提醒”:
func startDailyCheck(interval time.Duration) {
ticker := time.NewTicker(interval)
for range ticker.C {
videos, err := listCandleVideos("/videos/candles/")
if err != nil {
fmt.Println("目录读取失败:", err)
continue
}
if len(videos) == 0 {
fmt.Println("还没有蜡烛视频呢!")
continue
}
// 取最新视频的时间
latest := getLatestVideoTime(videos)
days := daysUntilNextCandle(latest)
if days == 0 {
fmt.Println("⚠️ 今天是点蜡烛的日子!记得拍视频!")
// 这里可以调用系统通知,或者发邮件
} else {
fmt.Printf("距离下次点蜡烛还有 %d 天\n", days)
}
}
}
对了,getLatestVideoTime这个函数得自己实现,方法就是遍历视频列表,提取时间戳,比较哪个最大,可以用sort.Slice实现:

func getLatestVideoTime(videos []string) time.Time {
var times []time.Time
for _, v := range videos {
t, err := extractDateFromFilename(v)
if err != nil {
continue
}
times = append(times, t)
}
if len(times) == 0 {
return time.Time{}
}
sort.Slice(times, func(i, j int) bool {
return times[i].After(times[j])
})
return times[0]
}
性能考量:处理大量视频
如果365天里每天都有视频(比如你天天点蜡烛),那/videos/candles/里可能有365个文件,遍历它们没问题,但如果视频数量上万(比如你录了10年),用filepath.Walk就不太高效了,可以改用os.ReadDir只读一级目录,或者维护一个索引文件(比如candle_index.json)。
type CandleRecord struct {
Date time.Time `json:"date"`
Path string `json:"path"`
Remind bool `json:"remind"`
}
func loadIndex(path string) ([]CandleRecord, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, err
}
var records []CandleRecord
err = json.Unmarshal(data, &records)
return records, err
}
索引文件里记录每个蜡烛视频的时间、路径、是否已经提醒过,这样每天检查时只需要读索引,不用遍历所有文件,但索引文件得保持同步——你删除了视频,索引里也得删掉,这个可以用Go的fsnotify库监听文件变化,不过那就有点过了。
输出格式:表格展示一年记录
用户可能想看看过去365天里的蜡烛视频分布,用表格输出到终端或文件:
func printYearlyTable(records []CandleRecord) {
fmt.Println("| 日期 | 是否已提醒 | 文件路径 |")
fmt.Println("|------------|------------|------------------|")
for _, r := range records {
remindStr := "✅"
if !r.Remind {
remindStr = "❌"
}
fmt.Printf("| %s | %s | %s |\n",
r.Date.Format("2006-01-02"),
remindStr,
r.Path)
}
}
不过注意,Markdown表格在终端里看起来乱糟糟的,我一般用text/tabwriter包来对齐:
import "text/tabwriter"
func printTable(w io.Writer, records []CandleRecord) {
tw := tabwriter.NewWriter(w, 0, 0, 2, ' ', 0)
fmt.Fprintln(tw, "日期\t已提醒\t文件路径")
for _, r := range records {
remindStr := "是"
if !r.Remind {
remindStr = "否"
}
fmt.Fprintf(tw, "%s\t%s\t%s\n", r.Date.Format("2006-01-02"), remindStr, r.Path)
}
tw.Flush()
}
这样输出就对齐了,看着舒服。
错误处理与边界情况
写这类工具最烦的就是各种意外。
- 用户删了视频但没删索引:索引里记录的文件不存在,跳过就好,在日志里打印个警告。
- 文件名里没有日期:用
regexp匹配失败时,返回错误,用户手动修复。 - 视频跨了午夜:比如晚上11:59录的视频,文件名日期是当天,但蜡烛可能算在第二天,这个取决于用户定义,我倾向于用文件名日期,不纠结精确时刻。
- 时区问题:如果用户在不同时区录的视频,文件名里的时间可能是UTC,最好在代码里统一用UTC,或者在文件名里包含时区信息(比如
20240315T200000Z),但大多数人不会这样做,简单处理:都用本地时间,不跨时区。
Go的错误处理比较啰嗦,但没办法,可以用log.Fatal直接终止,也可以用errors.Wrap加堆栈信息,社区里喜欢用github.com/pkg/errors,但Go 1.13之后标准库的fmt.Errorf支持%w了,够用了。
扩展:结合外部服务
如果想把“点蜡烛提醒”发到手机或邮件,可以用Go的smtp包发邮件,或者用net/http调用Webhook。
import "net/smtp"
func sendEmail(to, subject, body string) error {
from := "your_email@gmail.com"
password := "app_password" // 注意:别硬编码!用环境变量
smtpHost := "smtp.gmail.com"
smtpPort := "587"
msg := []byte("From: " + from + "\r\n" +
"To: " + to + "\r\n" +
"Subject: " + subject + "\r\n" +
"\r\n" + body)
auth := smtp.PlainAuth("", from, password, smtpHost)
return smtp.SendMail(smtpHost+":"+smtpPort, auth, from, []string{to}, msg)
}
但说实话,密码这种东西最好别写在代码里,用os.Getenv("SMTP_PASSWORD")从环境变量读取,或者用Vault这类工具。
最终运行起来
把上面这些函数拼起来,写个main.go:
func main() {
go startDailyCheck(24 * time.Hour) // 每天检查一次
// 假设用户想看看今天是不是该点蜡烛了
fmt.Println("正在扫描蜡烛视频...")
videos, _ := listCandleVideos("/videos/candles/")
if len(videos) == 0 {
fmt.Println("没找到蜡烛视频,快去拍一个吧!")
return
}
latest := getLatestVideoTime(videos)
days := daysUntilNextCandle(latest)
if days == 0 {
fmt.Println("🎂 今天是一周年!点根蜡烛,拍个视频!")
} else {
fmt.Printf("距离一周年还有 %d 天,别忘了准备蜡烛哦\n", days)
}
}
运行一下,输出类似:
正在扫描蜡烛视频...
距离一周年还有 127 天,别忘了准备蜡烛哦
好了,代码和思路都给了,一周年点蜡烛视频,365天的循环,用Go语言处理起来不算复杂,核心就是日期处理、文件遍历、周期检查,如果你想记录的是“每365天”而不是“每一年”,那用Add(365*24*time.Hour)就好了,但无论如何,记得定期备份你的视频——别等到365天后才发现文件损坏了。
对了,如果你真的连续点了365天蜡烛,别忘了我说的索引文件优化,不然程序跑着跑着就慢了,实践出真知,试试看吧。
本文来自作者[kyadmin]投稿,不代表be365立场,如若转载,请注明出处:http://www.hljbesthome.com/fnagchan/1675.html
评论列表(4条)
我是be365的签约作者“kyadmin”!
希望本篇文章《用Go语言实现一周年点蜡烛视频的365天倒计时与自动化处理》能对你有所帮助!
本站[be365]内容主要涵盖:be365,be365网站,be365网址,ball365体育,Be365排名
本文概览:一年前的今天,我点了一根蜡烛,拍了个视频,365天后,想再点一根,但发现手机里存了太多视频,找不到之前的了,你是不是也遇到过这种问题?今...