Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
6c31eeb6b0 |
98
main.go
98
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)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user