test passes

master
Bel LaPointe 2024-09-18 11:02:03 -04:00
parent 811d3294d4
commit 10122550d3
2 changed files with 75 additions and 71 deletions

View File

@ -4,7 +4,6 @@ import (
"context" "context"
"fmt" "fmt"
"io" "io"
"log"
"math/big" "math/big"
"os" "os"
"os/exec" "os/exec"
@ -15,7 +14,6 @@ import (
"strconv" "strconv"
"strings" "strings"
"syscall" "syscall"
"time"
) )
func main() { func main() {
@ -30,79 +28,65 @@ func main() {
func Run(ctx context.Context, args []string) error { func Run(ctx context.Context, args []string) error {
cams, err := lsd(args[0]) cams, err := lsd(args[0])
if err != nil { if err != nil {
return err return fmt.Errorf("failed to lsd %s: %w", args[0], err)
} }
for _, cam := range cams { for _, cam := range cams {
files, err := lsf(cam) files, err := lsf(cam)
if err != nil { if err != nil {
return err return fmt.Errorf("failed to lsf %s: %w", cam, err)
} else if len(files) < 1 { } else if len(files) < 1 {
continue continue
} }
lastChunk := strings.Split(path.Base(files[len(files)-1]), ".")[0] series := []string{}
files = slices.DeleteFunc(files, func(f string) bool { for _, f := range files {
return strings.Contains(f, lastChunk) series = append(series, strings.Split(path.Base(f), ".")[0])
}) }
if len(files) == 0 { series = slices.Compact(series)
if len(series) < 1 {
continue continue
} }
series = series[:len(series)-1]
lastMovementAt := time.Unix(0, 0) for _, series := range series {
prevF := files[0] if err := func() error {
for _, f := range files[1:] { seriesFiles := []string{}
prev, err := os.Stat(prevF) for _, file := range files {
if err != nil { if strings.HasPrefix(path.Base(file), series) {
return err seriesFiles = append(seriesFiles, file)
} }
}
if ok, err := hasMovement(ctx, prevF, f); err != nil { if seriesHasMovement, err := func() (bool, error) {
return err for i := 1; i < len(files); i++ {
} else if ok { f := files[i-1]
lastMovementAt = prev.ModTime() g := files[i]
} if hasMovement, err := hasMovement(ctx, f, g); err != nil {
return false, fmt.Errorf("failed to check for movement between %s and %s: %w", f, g, err)
target := "trash" } else if hasMovement {
if prev.ModTime().Before(lastMovementAt.Add(3 * time.Minute)) { return true, nil
target = "movement" }
} }
if target == "trash" { return false, nil
log.Println("deleting", prevF) }(); err != nil {
if err := os.Remove(prevF); err != nil {
return err return err
} else if seriesHasMovement {
for _, seriesFile := range seriesFiles {
if err := mv(strings.ReplaceAll(seriesFile, "record", "movement"), seriesFile); err != nil {
return fmt.Errorf("failed to mv series %s: %w", series, err)
}
}
} else {
for _, seriesFile := range seriesFiles {
if err := os.Remove(seriesFile); err != nil {
return fmt.Errorf("failed to rm series %s[%s]: %w", series, seriesFile, err)
}
}
} }
} else if err := func() error { return nil
gName := strings.ReplaceAll(prevF, "record", target)
if gName == prevF {
return fmt.Errorf("would overwrite original %s", prevF)
}
os.MkdirAll(path.Dir(gName), os.ModePerm)
log.Println("moving", prevF, "to", gName)
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 { }(); err != nil {
return err return err
} }
prevF = f
} }
} }
return nil return nil
@ -117,11 +101,11 @@ func hasMovement(ctx context.Context, a, b string) (bool, error) {
hw := strings.Fields(string(sizeOfOutput))[2] hw := strings.Fields(string(sizeOfOutput))[2]
h, err := strconv.ParseInt(strings.Split(hw, "x")[0], 10, 16) h, err := strconv.ParseInt(strings.Split(hw, "x")[0], 10, 16)
if err != nil { if err != nil {
return false, err return false, fmt.Errorf("failed parsing %s for HxW: %w", hw, err)
} }
w, err := strconv.ParseInt(strings.Split(hw, "x")[1], 10, 16) w, err := strconv.ParseInt(strings.Split(hw, "x")[1], 10, 16)
if err != nil { if err != nil {
return false, err return false, fmt.Errorf("failed parsing %s for HxW: %w", hw, err)
} }
total := h * w total := h * w
@ -130,7 +114,7 @@ func hasMovement(ctx context.Context, a, b string) (bool, error) {
f, _, err := big.ParseFloat(string(compareOutput), 10, 0, big.ToNearestEven) f, _, err := big.ParseFloat(string(compareOutput), 10, 0, big.ToNearestEven)
if err != nil { if err != nil {
return false, err return false, fmt.Errorf("failed to parse %s for a number of changed pixels: %w", compareOutput, err)
} }
i := new(big.Int) i := new(big.Int)
f.Int(i) f.Int(i)
@ -140,6 +124,29 @@ func hasMovement(ctx context.Context, a, b string) (bool, error) {
return percentPixelsChanged > 10, nil return percentPixelsChanged > 10, nil
} }
func mv(wPath, rPath string) error {
r, err := os.Open(rPath)
if err != nil {
return err
}
defer r.Close()
os.MkdirAll(path.Dir(wPath), os.ModePerm)
w, err := os.Create(wPath)
if err != nil {
return err
}
defer w.Close()
if _, err := io.Copy(w, r); err != nil {
w.Close()
os.Remove(wPath)
return err
}
return os.Remove(rPath)
}
func lsd(d string) ([]string, error) { func lsd(d string) ([]string, error) {
return ls(d, true) return ls(d, true)
} }

View File

@ -28,23 +28,20 @@ func TestRun(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
t.Log("lsd", path.Dir(d))
t.Log(lsd(path.Dir(d)))
t.Log("lsf", d)
t.Log(lsf(d))
if err := Run(context.Background(), []string{path.Dir(d)}); err != nil { if err := Run(context.Background(), []string{path.Dir(d)}); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if results, err := lsf(path.Join(path.Dir(d), "movement")); err != nil { if results, err := lsf(path.Join(path.Dir(d), "movement")); err != nil {
t.Fatal(err) t.Fatal("failed to lsf for results:", err)
} else if len(results) < 2 { } else if len(results) < 3 {
t.Fatal(results) t.Fatal(results)
} else if path.Base(results[0]) != "series.1.jpg" { } else if path.Base(results[0]) != "series.0.jpg" {
t.Fatal(results) t.Fatal(results)
} else if path.Base(results[1]) != "series.2.jpg" { } else if path.Base(results[1]) != "series.1.jpg" {
t.Fatal(results)
} else if path.Base(results[2]) != "series.2.jpg" {
t.Fatal(results) t.Fatal(results)
} }
// TODO assert originals deleted
} }