From 6c31eeb6b062a2a082bc4eb1d06c66c3b3b975ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20=C3=85hall?= Date: Mon, 28 Oct 2024 14:40:19 +0100 Subject: [PATCH] Simpler timelapse --- main.go | 98 +++++++++++++++++++-------------------------------------- 1 file changed, 33 insertions(+), 65 deletions(-) diff --git a/main.go b/main.go index 6882ead..90a4a56 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( // Standard + "bufio" "flag" "fmt" "os" @@ -101,27 +102,6 @@ func sortFiles(a, b string) int { // {{{ return 0 } // }}} -func separateDays(files []string) *map[string][]string { // {{{ - days := make(map[string][]string, 128) - - var t time.Time - var date string - var fnames []string - var found bool - for _, f := range files { - t = filenameToTime(f, true) - date = t.Format("2006-01-02") - - fnames, found = days[date] - if !found { - fnames = []string{} - } - fnames = append(fnames, f) - days[date] = fnames - } - - return &days -} // }}} func filesToSequenceFile(fname string, files []string) { // {{{ out, err := os.OpenFile(fname, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644) if err != nil { @@ -144,43 +124,47 @@ func updateEXIFTimestamp(files []string) { // {{{ for _, f := range files { t := filenameToTime(f, true) fmt.Printf("exif for %s\n", f) - exiftool := exec.Command("exiftool", "-CreateDate="+t.Format("2006-01-02 15:04"), "-overwrite_original", f) + exiftool := exec.Command("exiftool", "-CreateDate="+t.Format("2006-01-02 15:04"), "-ImageDescription="+t.Format("2006-01-02 15"), "-overwrite_original", f) exiftool.Run() } } // }}} -func timelapse(date string, files []string) { // {{{ - sequenceFile := fmt.Sprintf("/tmp/timelapse/sequence_%s", date) - videoFile := fmt.Sprintf("/tmp/timelapse/video_%s.mp4", date) - text := `drawtext=text='%{metadata\:DateTimeDigitized}':fontsize=30:x=10:y=10:fontcolor=#71b045:bordercolor=black:borderw=1` +func timelapse(files []string) { // {{{ + sequenceFile := fmt.Sprintf("/tmp/timelapse_sequence") + text := `drawtext=text='%{metadata\:ImageDescription}':fontsize=30:x=10:y=10:fontcolor=#71b045:bordercolor=black:borderw=1` filesToSequenceFile(sequenceFile, files) - os.Remove(videoFile) - ffmpeg := exec.Command("ffmpeg", "-r", framerate, "-safe", "0", "-f", "concat", "-i", sequenceFile, "-filter_complex", text, "-c:v", "libx264", "-crf", "23", videoFile) - out, err := ffmpeg.CombinedOutput() - if err != nil { - fmt.Printf("%s\n", out) - panic(err) - } -} // }}} -func concatVideos(unsortedDates []string) { // {{{ - fname := "/tmp/timelapse/sequence_all" - var videos []string - - for _, date := range unsortedDates { - videos = append(videos, "/tmp/timelapse/video_"+date+".mp4") - } - slices.Sort(videos) - - filesToSequenceFile(fname, videos) os.Remove(outFilename) - ffmpeg := exec.Command("ffmpeg", "-safe", "0", "-f", "concat", "-i", fname, "-c", "copy", outFilename) - out, err := ffmpeg.CombinedOutput() + ffmpeg := exec.Command("ffmpeg", "-r", framerate, "-safe", "0", "-f", "concat", "-i", sequenceFile, "-filter_complex", text, "-c:v", "libx264", "-crf", "23", "-progress", "-", "-nostats", outFilename) + + progress, err := ffmpeg.StdoutPipe() if err != nil { - fmt.Printf("%s\n", out) panic(err) } -} // }}} + err = ffmpeg.Start() + if err != nil { + panic(err) + } + + // Store position to overwrite progress later. + fmt.Print("\x1b[s") + + rxp := regexp.MustCompile(`^frame=(\d+)$`) + ffmpegReader := bufio.NewScanner(progress) + var frameStr []string + var frame int + totalNumFrames := len(files) + for ffmpegReader.Scan() { + frameStr = rxp.FindStringSubmatch(ffmpegReader.Text()) + if len(frameStr) < 2 { + continue + } + frame, _ = strconv.Atoi(frameStr[1]) + fmt.Printf("\x1b[u%.0f%% ", float32(frame) / float32(totalNumFrames) * 100) + } + + ffmpeg.Wait() +} // }}} func main() { parseCommandLine() if version { @@ -190,21 +174,5 @@ func main() { files := findFiles(startDir) slices.SortStableFunc(files, sortFiles) - sortedDays := separateDays(files) - - os.Mkdir("/tmp/timelapse", 0755) - var dates []string - for date, files := range *sortedDays { - fmt.Printf("Create timelapse for %s\n", date) - dates = append(dates, date) - // This is now done when downloading the image from the camera, - // since it takes too damn long time when running this program. - // It is preserved for the times when the source images are missing - // the EXIF data. - // updateEXIFTimestamp(files) - timelapse(date, files) - } - - fmt.Println("Assemble day videos to complete timelapse") - concatVideos(dates) + timelapse(files) }