diff --git a/Changelog.txt b/Changelog.txt new file mode 100644 index 0000000..22132ee --- /dev/null +++ b/Changelog.txt @@ -0,0 +1,4 @@ +version 2.0 + +Breaking changes. +Introduced new command line options to control source and destination path, file modification time for fixed files, and user interface control option. \ No newline at end of file diff --git a/README.md b/README.md index 530d31a..0e308ec 100644 --- a/README.md +++ b/README.md @@ -9,19 +9,53 @@ In this cases users advised to open broken file in MS Paint (or something simila WhatsApp Jpeg Image Repair application solves this problem and can repair many broken files at once. Follow these steps: -1. Download application archive. Navigate to [the application releases](https://github.com/cdefgah/whatsapp-jpeg-repair/releases). Then expand `Assets` section and download either `WhatsAppJpegRepair-1.0-Windows.zip` or `WhatsAppJpegRepair-1.0-MacOS.zip` according to your operating system. +1. Download application archive. Navigate to [the application releases](https://github.com/cdefgah/whatsapp-jpeg-repair/releases). Then expand `Assets` section and download either `WhatsAppJpegRepair-2.0-Windows.zip` or `WhatsAppJpegRepair-2.0-MacOS.zip` according to your operating system. 2. Unzip application archive to an arbitrary folder. I recommend to use your home folder, for example `Documents` or `Desktop` folder, to prevent filesystem access control issues upon running the application. 3. Open application folder `WhatsAppJpegRepair`. 4. Place broken jpeg files from WhatsApp to the `whatsapp-files` directory, located in the application folder. 5. Run the application. 6. Go to the `fixed-files` folder to get fixed image files. -It is possible to specify custom source and destination folder for the application. Just specify source and destination folders as application parameters, for example: +There are following option available: + +`-srcPath` - contains path to the broken WhatsApp files +By default the application internal folder `whatsapp-files` is being used. +Currently this folder contains sample broken whatsapp jpeg images for demonstration purposes. + +Example: +``` +WhatsAppJpegRepair -srcPath=/home/Documents/Photos/WhatsAppFiles +``` + +this call will use `/home/Documents/Photos/WhatsAppFiles` folder as the source path to get broken whatsapp files. + +`-destPath` - contains path to the folder, where fixed files will be stored. +By default the application internal folder `fixed-files` is being used. +If this folder does not exist, it will be created at runtime. + +Example: +``` +WhatsAppJpegRepair -srcPath=/home/Documents/Photos/WhatsAppFiles -destPath=/home/Documents/FixedPhotos +``` +this call will use `/home/Documents/Photos/WhatsAppFiles` folder to look for broken whatsapp files, and will use `/home/Documents/FixedPhotos` folder to store fixed images. + +`-dontWaitToClose` - if set to true, will close application just as it finished processing, otherwise it will wait until user presses 'Enter' key. By default its value is `false`. + +Example: +``` +WhatsAppJpegRepair -srcPath=/home/Documents/Photos/WhatsAppFiles -dontWaitToClose=true +``` +this call will use folder `/home/Documents/Photos/WhatsAppFiles` as source file path, and application will be closes as it finished files processing. All fixed files will be stored to the default destination folder `fixed-files` (check `-destPath` option description above). + +`-useCurrentModificationDateTime` - if it set to true, then created fixed files will get current date/time as file modification time. By default it is `false`, and all created fixed files get the same file modification date/time as source (broken) image files. + ``` -WhatsAppJpegRepair c:/Users/username/Documents/whats-app-files/ c:/Users/username/Documents/correct files/ +WhatsAppJpegRepair -useCurrentModificationDateTime=true ``` +this call will use default source and destination folders (check `-srcPath` and `-destPath` options above), will wait until user presses Enter when the application completed the file processing, +and will set current date/time as file modification time for created fixed files. -If you are building the application from the source code, before using the built application, please delete `.gitkeep` files from the internal application folders. +There are no mandatory options provided. You can run the application without parameters, and all default values for options will be used. ## Building the application from the source diff --git a/WhatsAppJpegRepair.go b/WhatsAppJpegRepair.go index 002b8ed..0fd2312 100644 --- a/WhatsAppJpegRepair.go +++ b/WhatsAppJpegRepair.go @@ -1,6 +1,7 @@ package main import ( + "flag" "fmt" "image" "image/jpeg" @@ -9,79 +10,103 @@ import ( "os" "path" "path/filepath" + "runtime" ) func main() { + + fmt.Println(`WhatsAppJpegRepair version 2.0 Copyright (c) 2021 by Rafael Osipov (rafael.osipov@outlook.com) + Repairs jpeg images saved from WhatsApp application to prevent errors upon opening these images in the Adobe Photoshop. + Project web-site, source code and documentation: https://github.com/cdefgah/whatsapp-jpeg-repair`) + + const sourceFilesPathParamKey string = "srcPath" + const destinationFilesPathParamKey string = "destPath" + const useCurrentModificationDateTimeParamKey = "useCurrentModificationDateTime" + const dontWaitToCloseParamKey string = "dontWaitToClose" + const predefinedSourceFilesFolder string = "whatsapp-files" const predefinedDestinationFilesFolder string = "fixed-files" - const consoleTextDelimiter = "---------------------------------------" - - fmt.Println("WhatsAppJpegRepair version 1.0 Copyright (c) 2021 by Rafael Osipov") - fmt.Println("\nRepairs jpeg images saved from WhatsApp application to prevent errors upon opening these images in the Adobe Photoshop.") - fmt.Println("Project web-site: https://github.com/cdefgah/whatsapp-jpeg-repair") - fmt.Println(consoleTextDelimiter) - - fmt.Println("Usage:") - fmt.Println("\nWhen launched without parameters, application uses predefined folders.") - fmt.Println("For broken jpeg files the application uses internal folder: ", predefinedSourceFilesFolder) - fmt.Println("Fixed files will be stored in the internal folder: ", predefinedDestinationFilesFolder) - - fmt.Println("\nAlso it is possible to specify custom source and destination folders. Use the following approach:") - fmt.Println("\n\tWhatsAppJpegRepair ") - fmt.Println("\nExamples (for Windows, and MacOS respectively):") - fmt.Println("\n\tWhatsAppJpegRepair c:/temp/problem-files/ c:/temp/fixed-jpeg-files/") - fmt.Println("\n\tWhatsAppJpegRepair /home/username/Documents/broken-files /home/username/Documents/correct-files") - fmt.Println(consoleTextDelimiter) - - const paramsAmountForCustomFolders int = 3 - const paramsAmountForPredefinedFolders int = 1 - const codeRepairProcessSucceed int = 0 - const codeRepairProcessFailed int = -1 - - var sourceFilesPath string - var destinationFilesPath string - - switch args := os.Args; len(args) { - case paramsAmountForCustomFolders: - sourceFilesPath = args[1] - destinationFilesPath = args[2] - - case paramsAmountForPredefinedFolders: - currentWorkingFolder, err := filepath.Abs(filepath.Dir(os.Args[0])) - if err != nil { - log.Fatal(err) - } - sourceFilesPath = filepath.Join(currentWorkingFolder, predefinedSourceFilesFolder) - destinationFilesPath = filepath.Join(currentWorkingFolder, predefinedDestinationFilesFolder) + const appExitCodeRepairProcessSucceed int = 0 + const appExitCodeRepairProcessFailed int = -1 + + currentWorkingFolder, err := filepath.Abs(filepath.Dir(os.Args[0])) + if err != nil { + log.Fatal(err) + } + + var sourceFilesPath = filepath.Join(currentWorkingFolder, predefinedSourceFilesFolder) + var destinationFilesPath = filepath.Join(currentWorkingFolder, predefinedDestinationFilesFolder) + + var sampleSourceFilesPathDeclarationPrefix string = "-" + sourceFilesPathParamKey + "=" + var sampleDestinationFilesPathDeclarationPrefix string = "-" + destinationFilesPathParamKey + "=" + var sampleSourceFilesPathDeclaration string + var sampleDestinationFilesPathDeclaration string + if runtime.GOOS == "windows" { + sampleSourceFilesPathDeclaration = sampleSourceFilesPathDeclarationPrefix + "c:/Users/Username/Documents/brokenWhatsAppFiles\n" + sampleDestinationFilesPathDeclaration = sampleDestinationFilesPathDeclarationPrefix + "c:/Users/Username/Documents/fixedImageFiles\n" + } else { + sampleSourceFilesPathDeclaration = sampleSourceFilesPathDeclarationPrefix + "/home/username/Documents/brokenWhatsAppFiles\n" + sampleDestinationFilesPathDeclaration = sampleDestinationFilesPathDeclarationPrefix + "/home/username/Documents/fixedImageFiles\n" + } - default: - fmt.Println("Incorrect number of parameters. Either run this application without parameters or pass source and destination folder as described above.") - os.Exit(codeRepairProcessFailed) + sourcePathPtr := flag.String(sourceFilesPathParamKey, + sourceFilesPath, "Path to folder with broken whatsapp files. Declaration example: "+sampleSourceFilesPathDeclaration) + destPathPtr := flag.String(destinationFilesPathParamKey, + destinationFilesPath, `Path to folder where fixed files will be stored. + If folder does not exists, it will be created. + Declaration example: `+sampleDestinationFilesPathDeclaration) + + useCurrentModificationDateTimePtr := flag.Bool(useCurrentModificationDateTimeParamKey, false, `If set to true, sets current date time as file modification time. + By default uses source file modification date time.`) + dontWaitToClosePtr := flag.Bool(dontWaitToCloseParamKey, false, "If set to true, does not wait a key press until exits the application. By default is false.") + + // parsing and loading app params + flag.Parse() + sourceFilesPath = *sourcePathPtr + destinationFilesPath = *destPathPtr + var useCurrentModificationDateTime = *useCurrentModificationDateTimePtr + var dontWaitToCloseApp bool = *dontWaitToClosePtr + + fmt.Println("\n----------------------------------------------------------------------------------------") + flag.Usage() + + // creating destination folder if it does not exist + if _, err := os.Stat(*destPathPtr); os.IsNotExist(err) { + os.Mkdir(*destPathPtr, os.ModeDir) } - var repairCompletedSuccessfully bool = repairImageFiles(sourceFilesPath, destinationFilesPath) - fmt.Println("\nPress Enter to close the application") - fmt.Scanln() + // displaying effective parameter values + fmt.Println("\n----------------------------------------------------------------------------------------") + fmt.Println("Source folder path:", sourceFilesPath) + fmt.Println("Destination folder path:", destinationFilesPath) + fmt.Println("Use current modification date time for fixed files:", useCurrentModificationDateTime) + fmt.Println("Don't wait app to close:", dontWaitToCloseApp) + fmt.Println("----------------------------------------------------------------------------------------") + + var repairCompletedSuccessfully bool = repairImageFiles(sourceFilesPath, destinationFilesPath, useCurrentModificationDateTime) + + if !*dontWaitToClosePtr { + fmt.Println("\nPress Enter to close the application") + fmt.Scanln() + } if repairCompletedSuccessfully { - os.Exit(codeRepairProcessSucceed) + os.Exit(appExitCodeRepairProcessSucceed) } else { - os.Exit(codeRepairProcessFailed) + os.Exit(appExitCodeRepairProcessFailed) } } // Repairs broken jpeg image files // Gets location of broken files in sourceFolderPath variable // and the location of folder, where fixed files will be stored, in the destinationFolderPath variable +// set useCurrentModificationDateTime as the relevant parameter value. // Returns true, if there were no errors upon files processing, false otherwise. -func repairImageFiles(sourceFolderPath string, destinationFolderPath string) bool { +func repairImageFiles(sourceFolderPath string, destinationFolderPath string, useCurrentModificationDateTime bool) bool { var totalFilesCount int32 var processedFilesCount int32 - fmt.Println("Source folder path: ", sourceFolderPath) - fmt.Println("Destination folder path: ", destinationFolderPath) - files, err := ioutil.ReadDir(sourceFolderPath) if err != nil { log.Fatal(err) @@ -89,13 +114,15 @@ func repairImageFiles(sourceFolderPath string, destinationFolderPath string) boo for _, f := range files { if !f.IsDir() { totalFilesCount++ - if processSingleImageFile(sourceFolderPath, f.Name(), destinationFolderPath) { + if processSingleImageFile(sourceFolderPath, f.Name(), destinationFolderPath, useCurrentModificationDateTime) { processedFilesCount++ } } } fmt.Println("\nTotal files count: ", totalFilesCount, "Processed files count: ", processedFilesCount) + + fmt.Println("All fixed files are located in folder: ", destinationFolderPath) fmt.Println("\nDone!") return totalFilesCount == processedFilesCount @@ -105,15 +132,17 @@ func repairImageFiles(sourceFolderPath string, destinationFolderPath string) boo // sourceFolderPath - path to folder, where broken image files are located. // sourceFileNameWithExtension contains filename with extension of image file, that should be processed. // destinationFilesFolderPath contains path to folder, where fixed image file will be stored. +// useCurrentModificationDateTime if true, setting current date/time as file modification time for generated file. +// otherwise preserves source file modification date/time. // Returns true, if there were no errors upon file processing, false otherwise. -func processSingleImageFile(sourceFolderPath string, sourceFileNameWithExtension string, destinationFilesFolderPath string) bool { +func processSingleImageFile(sourceFolderPath string, sourceFileNameWithExtension string, destinationFilesFolderPath string, useCurrentModificationDateTime bool) bool { var sourceFilePath = filepath.Join(sourceFolderPath, sourceFileNameWithExtension) var sourceFileExtension = path.Ext(sourceFileNameWithExtension) var destinationFileWithExtension = sourceFileNameWithExtension[0:len(sourceFileNameWithExtension)-len(sourceFileExtension)] + ".jpg" var destinationFilePath = filepath.Join(destinationFilesFolderPath, destinationFileWithExtension) - fmt.Print("Processing file: ", sourceFilePath, " ............... ") + fmt.Print("Processing file: ", sourceFilePath, " .......................... ") image, err := getImageFromFilePath(sourceFilePath) if err != nil { @@ -129,6 +158,20 @@ func processSingleImageFile(sourceFolderPath string, sourceFileNameWithExtension defer f.Close() jpeg.Encode(f, image, nil) + // setting modification time as in source file if necessary + if !useCurrentModificationDateTime { + sourceFileStats, err := os.Stat(sourceFilePath) + if err != nil { + log.Fatal(err) + } + + var sourceFileModificationDateTime = sourceFileStats.ModTime() + err = os.Chtimes(destinationFilePath, sourceFileModificationDateTime, sourceFileModificationDateTime) + if err != nil { + fmt.Println(err) + } + } + fmt.Println("OK!") return true diff --git a/fixed-files/.gitkeep b/fixed-files/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/whatsapp-files/.gitkeep b/whatsapp-files/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/whatsapp-files/01.jpg b/whatsapp-files/01.jpg new file mode 100644 index 0000000..e62607b Binary files /dev/null and b/whatsapp-files/01.jpg differ diff --git a/whatsapp-files/02.jpg b/whatsapp-files/02.jpg new file mode 100644 index 0000000..5623176 Binary files /dev/null and b/whatsapp-files/02.jpg differ diff --git a/whatsapp-files/03.jpg b/whatsapp-files/03.jpg new file mode 100644 index 0000000..4fe8563 Binary files /dev/null and b/whatsapp-files/03.jpg differ diff --git a/whatsapp-files/04.jpg b/whatsapp-files/04.jpg new file mode 100644 index 0000000..e38e464 Binary files /dev/null and b/whatsapp-files/04.jpg differ diff --git a/whatsapp-files/05.jpg b/whatsapp-files/05.jpg new file mode 100644 index 0000000..321a83d Binary files /dev/null and b/whatsapp-files/05.jpg differ diff --git a/whatsapp-files/06.jpg b/whatsapp-files/06.jpg new file mode 100644 index 0000000..a0f2287 Binary files /dev/null and b/whatsapp-files/06.jpg differ diff --git a/whatsapp-files/07.jpg b/whatsapp-files/07.jpg new file mode 100644 index 0000000..3c22fa1 Binary files /dev/null and b/whatsapp-files/07.jpg differ