diff --git a/SideCarJpegCleaner b/SideCarJpegCleaner new file mode 100755 index 0000000..c49a5f5 Binary files /dev/null and b/SideCarJpegCleaner differ diff --git a/cmd/SideCarJpegCleaner/SideCarJpegCleaner.go b/cmd/SideCarJpegCleaner/SideCarJpegCleaner.go new file mode 100644 index 0000000..457a377 --- /dev/null +++ b/cmd/SideCarJpegCleaner/SideCarJpegCleaner.go @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2019 Philipp Haefelfinger (http://www.haefelfinger.ch/). All Rights Reserved. + * This application is licensed under GPLv2. See the LICENSE file in the root directory of the project. + */ + +package main + +import ( + "flag" + "git.haefelfinger.net/philipp.haefelfinger/SideCarJpegCleaner/internal/app" + "github.com/sirupsen/logrus" + "github.com/vharitonsky/iniflags" + "os" +) + +var ( + logLevel = flag.String("logLevel", "info", "The minimum log level required to write out a log message. (panic,fatal,error,warn,info,debug,trace)") +) + +func main() { + iniflags.Parse() + initializeLog() + logrus.Infoln("Starting jpeg cleaner...") + app.Run() +} + +func initializeLog() { + level, err := logrus.ParseLevel(*logLevel) + if err != nil { + level = logrus.DebugLevel + } + logrus.SetLevel(level) + logrus.SetOutput(os.Stdout) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..82b37ed --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module git.haefelfinger.net/philipp.haefelfinger/SideCarJpegCleaner + +go 1.12 + +require ( + github.com/sirupsen/logrus v1.4.2 + github.com/vharitonsky/iniflags v0.0.0-20180513140207-a33cd0b5f3de +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..6435db2 --- /dev/null +++ b/go.sum @@ -0,0 +1,11 @@ +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/vharitonsky/iniflags v0.0.0-20180513140207-a33cd0b5f3de h1:fkw+7JkxF3U1GzQoX9h69Wvtvxajo5Rbzy6+YMMzPIg= +github.com/vharitonsky/iniflags v0.0.0-20180513140207-a33cd0b5f3de/go.mod h1:irMhzlTz8+fVFj6CH2AN2i+WI5S6wWFtK3MBCIxIpyI= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/internal/app/app.go b/internal/app/app.go new file mode 100644 index 0000000..bb84628 --- /dev/null +++ b/internal/app/app.go @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2019 Philipp Haefelfinger (http://www.haefelfinger.ch/). All Rights Reserved. + * This application is licensed under GPLv2. See the LICENSE file in the root directory of the project. + */ + +package app + +import ( + "errors" + "flag" + "github.com/sirupsen/logrus" + "os" + "path" + "path/filepath" + "strings" +) + +var ( + imagesRootPath = flag.String("imagesRootPath", "", "This is the images root path that should be mirrored to piwigo.") + dryRun = flag.Bool("dryRun", true, "If set to true, all actions are run except the real filesystem modifications") +) + +func Run() { + imagesRootFolder := *imagesRootPath + if imagesRootFolder == "" { + logErrorAndExit(errors.New("No images root folder set, exiting."), 1) + } + + err, jpgFiles := findAllJpgFiles(imagesRootFolder) + if err != nil { + logrus.Errorf("Could not clean up %s", imagesRootFolder) + logErrorAndExit(err, 2) + } + + filesWithMissingSidecar := getJpesWithoutRawSideCar(jpgFiles) + logrus.Infof("Found %d of %d images to delete in %s", len(filesWithMissingSidecar), len(jpgFiles), imagesRootFolder) + + if len(jpgFiles) == len(filesWithMissingSidecar) { + logrus.Warnf("Did not find any sidecar files in %s. Are you sure this is the right path to look for jpgs without raw sidecars? Cancelling executing!", imagesRootFolder) + logErrorAndExit(errors.New("Total files of jpegs and missing sidecars are the same, cancelling!"), 3) + } + + if !(*dryRun) { + deleteFiles(filesWithMissingSidecar) + } else { + logrus.Warnln("Skipping delete of images as flag dryRun is set to true!") + } + +} + +func deleteFiles(filesWithMissingSidecar []string) { + for _, fileToDelete := range filesWithMissingSidecar { + logrus.Infof("Deleting %s", fileToDelete) + err := os.Remove(fileToDelete) + if err != nil { + logrus.Errorf("Could not delete %s because of %v", fileToDelete, err) + } + } +} + +func logErrorAndExit(err error, exitCode int) { + logrus.Errorln(err) + os.Exit(exitCode) +} + +func getJpesWithoutRawSideCar(jpgFiles []string) []string { + var filesWithMissingSidecar []string + for _, jpgFile := range jpgFiles { + dir, fileName := filepath.Split(jpgFile) + + newFileName := filenameWithoutExtension(fileName) + ".cr2" + newFilePath := filepath.Join(dir, newFileName) + + if exists, err := fileExists(newFilePath); exists { + logrus.Debugf("Found exising sidecar, keeping jpeg %s", jpgFile) + + } else if err == nil && !exists { + logrus.Infof("Found jpeg with missing raw file, schedule for delete %s", jpgFile) + filesWithMissingSidecar = append(filesWithMissingSidecar, jpgFile) + } else { + logrus.Errorf("There was an error on checking %s: %v", jpgFile, err) + } + } + return filesWithMissingSidecar +} + +func findAllJpgFiles(basePath string) (error, []string) { + var files []string + + err := filepath.Walk(basePath, func(path string, info os.FileInfo, err error) error { + if strings.HasPrefix(info.Name(), ".") { + logrus.Debugf("Skipping hidden file or directory %s", path) + return nil + } + + extension := strings.ToLower(filepath.Ext(path)) + if (extension != ".jpg" && extension != ".jpeg") || info.IsDir() { + return nil + } + + logrus.Debugf("Found file %s", path) + files = append(files, path) + + return nil + }) + return err, files +} + +func filenameWithoutExtension(fn string) string { + return strings.TrimSuffix(fn, path.Ext(fn)) +} + +func fileExists(filePath string) (bool, error) { + _, err := os.Stat(filePath) + if err == nil { + logrus.Debugf("file %s exists", filePath) + return true, nil + } else if os.IsNotExist(err) { + logrus.Debugf("file %s does not exist", filePath) + return false, nil + } else { + logrus.Debugf("file %s stat error: %v", filePath, err) + return false, err + } +}