Bel LaPointe 2024-09-18 00:45:35 -04:00
parent 84f9896ff4
commit 122c2d09ec
2 changed files with 158 additions and 0 deletions

155
testdata/ffmpeg.d/cmd/prune/main.go vendored Normal file
View File

@ -0,0 +1,155 @@
package main
import (
"context"
"fmt"
"io"
"math/big"
"os"
"os/exec"
"os/signal"
"path"
"slices"
"sort"
"strconv"
"strings"
"syscall"
"time"
)
func main() {
ctx, can := signal.NotifyContext(context.Background(), syscall.SIGINT)
defer can()
if err := Run(ctx, os.Args[1:]); err != nil {
panic(err)
}
}
func Run(ctx context.Context, args []string) error {
cams, err := lsd(args[0])
if err != nil {
return err
}
for _, cam := range cams {
files, err := lsf(cam)
if err != nil {
return err
} else if len(files) < 1 {
continue
}
lastChunk := strings.Split(path.Base(files[len(files)-1]), ".")[0]
files = slices.DeleteFunc(files, func(f string) bool {
return strings.Contains(f, lastChunk)
})
if len(files) == 0 {
continue
}
lastMovementAt := time.Unix(0, 0)
prevF := files[0]
for _, f := range files[1:] {
prev, err := os.Stat(prevF)
if err != nil {
return err
}
if ok, err := hasMovement(ctx, prevF, f); err != nil {
return err
} else if ok {
lastMovementAt = prev.ModTime()
}
target := "trash"
if prev.ModTime().Before(lastMovementAt.Add(3 * time.Minute)) {
target = "movement"
}
if err := func() error {
gName := strings.ReplaceAll(prevF, "record", target)
if gName == prevF {
return fmt.Errorf("would overwrite original %s", prevF)
}
os.MkdirAll(path.Dir(gName), os.ModePerm)
f, err := os.Open(prevF)
if err != nil {
return err
}
defer f.Close()
g, err := os.Create(gName)
if err != nil {
return err
}
defer g.Close()
if _, err := io.Copy(g, f); err != nil {
return err
}
return os.Remove(prevF)
}(); err != nil {
return err
}
prevF = f
}
}
return nil
}
func hasMovement(ctx context.Context, a, b string) (bool, error) {
sizeOfCmd := exec.CommandContext(ctx, "identify", a)
sizeOfOutput, err := sizeOfCmd.CombinedOutput()
if err != nil {
return false, fmt.Errorf("failed to identify %s: (%w) %s", a, err, sizeOfOutput)
}
hw := strings.Fields(string(sizeOfOutput))[2]
h, err := strconv.ParseInt(strings.Split(hw, "x")[0], 10, 16)
if err != nil {
return false, err
}
w, err := strconv.ParseInt(strings.Split(hw, "x")[1], 10, 16)
if err != nil {
return false, err
}
total := h * w
compareCmd := exec.CommandContext(ctx, "compare", "-metric", "AE", "-fuzz", "15%", a, b, "/dev/null")
compareOutput, _ := compareCmd.CombinedOutput()
f, _, err := big.ParseFloat(string(compareOutput), 10, 0, big.ToNearestEven)
if err != nil {
return false, err
}
i := new(big.Int)
f.Int(i)
delta := i.Int64()
percentPixelsChanged := int(100.0 * float64(delta) / float64(total))
return percentPixelsChanged > 10, nil
}
func lsd(d string) ([]string, error) {
return ls(d, true)
}
func lsf(d string) ([]string, error) {
return ls(d, false)
}
func ls(d string, dirs bool) ([]string, error) {
entries, err := os.ReadDir(d)
results := make([]string, 0, len(entries))
for i := range entries {
if dirs == entries[i].IsDir() {
results = append(results, path.Join(d, entries[i].Name()))
}
}
sort.Strings(results)
return results, err
}

3
testdata/ffmpeg.d/go.mod vendored Normal file
View File

@ -0,0 +1,3 @@
module ffmpeg.d
go 1.22.3