Skip to content
This repository has been archived by the owner on Nov 23, 2024. It is now read-only.

Commit

Permalink
Merge pull request #1 from razenxc/cli-implementation
Browse files Browse the repository at this point in the history
Cli implementation
  • Loading branch information
razenxc authored Jul 31, 2024
2 parents 8064a45 + 1822ecb commit 1cffd60
Show file tree
Hide file tree
Showing 10 changed files with 235 additions and 112 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.vscode/
build/
build/
old/
25 changes: 22 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# SortFilesWithEXIFdata
## Sort your media with the EXIF data by date on directories
# SortEx
## Sort your files with EXIF data by date on directories
### [Technical Documentation](TD.md)

```
photo1.jpg 2024_05_14
Expand All @@ -8,7 +9,7 @@ photo3.jpeg 2023_12_05
```

## Usage
## Running
- run it with **go** from terminal
- <code>go run .</code>
- compile it with **go** from terminal
Expand All @@ -17,5 +18,23 @@ photo3.jpeg 2023_12_05
- download it with **releases page**
- [**Here**](https://github.com/razenxc/SortFilesWithEXIFdata/releases)

## Usage
\* colors means compatible arguments with each other - oranges with oranges, yellows with yellows, red and green only themself
- help
`-h --help`🔴
- sort files in current dir
`-c --current`🟠
- sort files in specific dir
`-d --determined "/home/user/media"`🟡
- select the path where the files will be sorted
`-s --save "/home/user/sorthere` 🟠🟡
- should files without data be transferred? (true by default)
`-m --move-no-data false` 🟠🟡
- undo changes if something went wrong
`-b --restore-backup "/home/user/mydir/31_7_2024-21_30_4-backup.sfbackup"`🟢

## The Screenshot
![image](https://github.com/user-attachments/assets/d698ad99-4d37-4c68-bd0c-0cdd54226c6e)

## Thirdparty
- [goexif](https://github.com/rwcarlsen/goexif)
61 changes: 61 additions & 0 deletions TD.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Technical Documentation of "Sort Files with EXIF data" cli program

Problem: Once, I got the corrupted sd card in my camera, I was able to recover all the data, but all the photos were randomly "scattered" in one directory and with random names.

Solution: I decided to write a program that would sort these photos into directories separated by date.

Example:
```
1ABCD.png 23_01_2020
2BCDE.png -> 02_06_2023
3CDEF.png 13_08_2024
```

## Program should:
- be cli ✅

`sortex -h`

`sortex --help`

- sort files in current dir ✅

`sortex -c`

`sortex --current`

- sort files in determined dir ✅

`sortex -d`

`sortex --determined`

- sort files from dirs in current dirrectory ❌

`sortex -c`

`sortex --internal-dirs`
- select the path where files will be sorted ✅

`sortex -s "/home/sort/here"`

`sortex --save "/home/sort/here"`

## Also
- set dir date format ❌

`-sdf "mm:dd:yyyy"`

`--set-dir-format`

- should files without exif data be transferred? (`true` by default) ✅

`-m`

`--move-no-data false`

- restore a backup ✅

`-b "/fullpath/to/a/backup/file"`

`--restore-backup "/fullpath/to/a/backup/file"`
19 changes: 19 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package cmd

import (
"log"

"github.com/spf13/cobra"
)

var rootCmd = &cobra.Command{
Use: "sortex",
Long: "Sort your files with EXIF data by date on directories. *The numbers down below in the description of the arguments, mean that are compatible with each other - ones with ones, twos with twos, nought only with itself.",
Run: sort,
}

func Execute() {
if err := rootCmd.Execute(); err != nil {
log.Fatal(err)
}
}
104 changes: 104 additions & 0 deletions cmd/run.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package cmd

import (
"fmt"
"log"
"os"
"sortex/sortex"

"github.com/spf13/cobra"
)

var currentDir bool
var determinedPath string
var internalDirs bool
var resultPath string
var moveWithoutEx bool
var restoreBackup string

func init() {
rootCmd.PersistentFlags().BoolVarP(&currentDir, "current", "c", false, "(1)sort files in current dir")
rootCmd.PersistentFlags().StringVarP(&determinedPath, "determined", "d", "", "(2)sort files in specific dir")
//rootCmd.PersistentFlags().BoolVarP(&internalDirs, "internal-dirs", "i", false, "(1)(2)sort files in all internal dirs")
rootCmd.PersistentFlags().StringVarP(&resultPath, "save", "s", "", "(1)(2)select the path where the files will be sorted")
rootCmd.PersistentFlags().BoolVarP(&moveWithoutEx, "move-no-data", "m", true, "(1)(2)should files without data be transferred? (true by default)")
rootCmd.PersistentFlags().StringVarP(&restoreBackup, "restore-backup", "b", "", "(0)undo changes if something went wrong")
}

func sort(cmd *cobra.Command, args []string) {
var path string
var fileTypes []string = []string{"all"}

if currentDir && determinedPath == "" {
fmt.Println("[OK] sortex -c")
p, err := os.Getwd()
if err != nil {
log.Fatal("Error while getting current dirrectory. Use -d")
}
path = p
} else if !currentDir && determinedPath != "" {
fmt.Println("[OK] sortex -d \"" + determinedPath + "\"")
path = determinedPath
} else if currentDir && determinedPath != "" {
fmt.Println("[CANNOT USE] sortex -c -d \"" + determinedPath + "\"")
} else if restoreBackup != "" {
err := sortex.RevertChanges(restoreBackup)
if err != nil {
log.Fatal("[ERROR] -", err)
}
} else {
log.Fatal("[CANNOT USE] sortex -h --help")
}

dir, err := os.ReadDir(path)
if err != nil {
log.Fatal(err)
}

var backupData []sortex.BackupJSON
var movePath = path

for _, value := range dir {
fullPath := fmt.Sprintf("%v/%v", path, value.Name())
fmt.Println("full path", fullPath)

if sortex.CheckTypes(fullPath, fileTypes) || fileTypes[0] == "all" {
fmt.Println("--------------------------------")
if internalDirs && sortex.IsItDir(fullPath) && fileTypes[0] == "all" {
fmt.Println("path", fullPath, "is skipped because its directory ")
continue
}

data, err := sortex.GetData(fullPath)
if err != nil {
log.Println(err)
if !moveWithoutEx {
continue
}
}
fmt.Println("exif data:", data)

if resultPath != "" {
movePath = resultPath
}
newDir, err := sortex.CreateFolder(&data, movePath)
if err != nil {
log.Println(err)
}
fmt.Println("newDir:", newDir)

err = sortex.MoveToDir(fullPath, newDir+"/"+value.Name())
if err != nil {
log.Println(err)
}

backupData = append(backupData, sortex.BackupJSON{
OldPath: fullPath,
NewPath: newDir + "/" + value.Name(),
})
} else {
fmt.Println("path", fullPath, "is skipped because its type is not in the list")
}
}
sortex.CreateBackup(movePath, backupData)
}
12 changes: 10 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
module SortFilesWithEXIFdata
module sortex

go 1.22.2

require github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd
require (
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd
github.com/spf13/cobra v1.8.1
)

require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
)
10 changes: 10 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,2 +1,12 @@
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd h1:CmH9+J6ZSsIjUK3dcGsnCnO41eRBOnY12zwkn5qVwgc=
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
103 changes: 2 additions & 101 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,108 +1,9 @@
package main

import (
"SortFilesWithEXIFdata/sortfiles"
"bufio"
"fmt"
"log"
"os"
"strings"
"sortex/cmd"
)

func main() {
// Path to dir with files to sort
input, err := readString("Enter full path to dir. with files: ")
if err != nil {
log.Fatal(err)
}
path := input

dir, err := os.ReadDir(path)
if err != nil {
log.Fatal(err)
}

input, err = readString("Do you want revert changes? [y/n]: ")
if err != nil {
log.Fatal(err)
}
if input == "y" {
err := sortfiles.RevertChanges(path + "/backup.json")
if err != nil {
log.Fatal(err)
}
os.Exit(0)
}

// File types to sort
input, err = readString("Enter files types to sort separated by spaces \".jpeg .png .jpg\" or \"all\": ")
if err != nil {
log.Fatal(err)
}
fileTypes := strings.Split(input, " ")

// Ignore directories or not
var ignoreDirs bool
if fileTypes[0] == "all" {

input, err = readString("Do you want ignore directories? [y/n]: ")
if err != nil {
log.Fatal(err)
}

switch input {
case "y":
ignoreDirs = true
case "n":
ignoreDirs = false
default:
fmt.Println("Invalid input. 'y' or 'n'.")
}
}

var backupData []sortfiles.BackupJSON

for _, value := range dir {
fullPath := fmt.Sprintf("%v/%v", path, value.Name())
fmt.Println("full path", fullPath)

if sortfiles.CheckTypes(fullPath, fileTypes) || fileTypes[0] == "all" {
fmt.Println("--------------------------------")
if ignoreDirs && sortfiles.IsItDir(fullPath) && fileTypes[0] == "all" {
fmt.Println("path", fullPath, "is skipped because its directory ")
continue
}

data, err := sortfiles.GetData(fullPath)
if err != nil {
log.Println(err)
}
fmt.Println("exif data:", data)

newDir, err := sortfiles.CreateFolder(&data, path)
if err != nil {
log.Println(err)
}
fmt.Println("newDir:", newDir)

err = sortfiles.MoveToDir(fullPath, newDir+"/"+value.Name())
if err != nil {
log.Println(err)
}
backupData = append(backupData, sortfiles.BackupJSON{
OldPath: fullPath,
NewPath: newDir + "/" + value.Name(),
})
} else {
fmt.Println("path", fullPath, "is skipped because its type is not in the list")
}
}
sortfiles.CreateBackup(path, backupData)
}

func readString(prompt string) (string, error) {
reader := bufio.NewReader(os.Stdin)
fmt.Print(prompt)
input, err := reader.ReadString('\n')
return strings.TrimSpace(input), err
cmd.Execute()
}
8 changes: 4 additions & 4 deletions sortfiles/methods.go → sortex/methods.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package sortfiles
package sortex

import (
"encoding/json"
Expand Down Expand Up @@ -63,10 +63,10 @@ func IsItDir(filePath string) (status bool) {
}

func CreateBackup(pathToSave string, data []BackupJSON) error {
//cdt := getCurrentDateTime() // current data time
cdt := getCurrentDateTime() // current data time

//file, err := os.Create(pathToSave + "/" + fmt.Sprintf("%v_%v_%v-%v_%v_%v-backup", cdt.day, cdt.month, cdt.year, cdt.hour, cdt.minute, cdt.second) + ".json")
file, err := os.Create(pathToSave + "/backup.json")
file, err := os.Create(pathToSave + "/" + fmt.Sprintf("%v_%v_%v-%v_%v_%v-backup", cdt.day, cdt.month, cdt.year, cdt.hour, cdt.minute, cdt.second) + ".sfbackup")
//file, err := os.Create(pathToSave + "/backup.sfbackup")
if err != nil {
return err
}
Expand Down
Loading

0 comments on commit 1cffd60

Please sign in to comment.