2 Commits

Author SHA1 Message Date
77b3850958 add support for .heic format
- change img lib to support heic format
- for images without exif data or exif.time=0, don't change their name
- print number of converted & total images
- print list of unconverted files
2024-02-13 20:34:10 +01:00
eec14720b9 update readme.md 2024-01-26 22:28:23 +01:00
5 changed files with 119 additions and 30 deletions

View File

@@ -3,11 +3,18 @@ Simple script to help a friend to set the image date to the file date for all th
#### Usage #### Usage
- Get the binary from https://github.com/arnaucube/exif-dates/releases - Get the binary from https://github.com/arnaucube/exif-dates/releases
- Execute the binary - Execute the binary `> ./exif-dates`
```
> ./exif-dates
```
Alternatively can specify the input directory: `> ./exif-dates inputDir` ```
> ./exif-dates -h
version: v0.0.2_2024-01-26
Usage of ./exif-dates:
-input, -i string
input directory (default "./")
-output, -o string
output directory (default "output")
-version, -v
print current version
```
This will generate a new directory `output` which contains all the images with the image date as the file date (both file's access and modification times), and also with the image date as the filename. This will generate a new directory `output` which contains all the images with the image date as the file date (both file's access and modification times), and also with the image date as the filename.

View File

@@ -10,3 +10,4 @@ GOOS=windows GOARCH=amd64 go build -o bin/exif-dates-amd64.exe *.go
echo "building macOS binaries" echo "building macOS binaries"
GOOS=darwin GOARCH=amd64 go build -o bin/exif-dates-amd64-darwin *.go GOOS=darwin GOARCH=amd64 go build -o bin/exif-dates-amd64-darwin *.go
GOOS=darwin GOARCH=arm64 go build -o bin/exif-dates-arm64-darwin *.go

12
go.mod
View File

@@ -2,4 +2,14 @@ module exif-dates
go 1.21.0 go 1.21.0
require github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd require github.com/evanoberholster/imagemeta v0.3.1
require (
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/philhofer/fwd v1.1.2 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/rs/zerolog v1.29.0 // indirect
github.com/tinylib/msgp v1.1.8 // indirect
golang.org/x/sys v0.5.0 // indirect
)

65
go.sum
View File

@@ -1,2 +1,63 @@
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd h1:CmH9+J6ZSsIjUK3dcGsnCnO41eRBOnY12zwkn5qVwgc= github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/evanoberholster/imagemeta v0.3.1 h1:E4GUjXcvlVMjP9joN25+bBNf3Al3MTTfMqCrDOCW+LE=
github.com/evanoberholster/imagemeta v0.3.1/go.mod h1:V0vtDJmjTqvwAYO8r+u33NRVIMXQb0qSqEfImoKEiXM=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw=
github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w=
github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0=
github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

54
main.go
View File

@@ -10,14 +10,14 @@ import (
"path" "path"
"time" "time"
"github.com/rwcarlsen/goexif/exif" "github.com/evanoberholster/imagemeta"
) )
const version = "v0.0.2_2024-01-26" const version = "v0.0.3_2024-02-13"
const layout = "2006-01-02T15:04:05.000Z" const layout = "2006-01-02T15:04:05.000Z"
func main() { func main() {
fmt.Println("version:", version) fmt.Printf("version: %s\n (get latest version at https://github.com/arnaucube/exif-dates/releases )\n\n", version)
var versionFlag bool var versionFlag bool
var inputDirFlag, outputDirFlag string var inputDirFlag, outputDirFlag string
@@ -42,16 +42,20 @@ func main() {
log.Fatal(err) log.Fatal(err)
} }
nImgsDetected := 0
nImgsConverted := 0
var unconvertedFileNames []string
for _, e := range entries { for _, e := range entries {
// skip subdirectories // skip subdirectories
if e.IsDir() { if e.IsDir() {
continue continue
} }
nImgsDetected++
fileName := inputDirFlag + "/" + e.Name() fileName := inputDirFlag + "/" + e.Name()
fileExtension := path.Ext(fileName) fileExtension := path.Ext(fileName)
// get file // get file
fmt.Printf("---FileName: %s,", fileName) fmt.Printf("--> FileName: %s,", fileName)
f, err := os.Open(fileName) f, err := os.Open(fileName)
if err != nil { if err != nil {
fmt.Println("\ne", err) fmt.Println("\ne", err)
@@ -60,25 +64,29 @@ func main() {
// get camera date // get camera date
date, err := getExifDate(f) date, err := getExifDate(f)
if err != nil && (err == io.EOF || err.Error() == "exif: failed to find exif intro marker") {
fmt.Println(" not an img file, skipping file")
continue
}
if err != nil { if err != nil {
fmt.Println("err", err) fmt.Printf(" Error: %s\n\n", err)
unconvertedFileNames = append(unconvertedFileNames, fileName)
continue continue
} }
fmt.Println("\n DATE", date) fmt.Println("\n DATE", date)
dateString := date.String()
if date.IsZero() {
// if date is not set (=0), do not use the zero date as
// name, and reuse the original name of the file
fn := e.Name()
dateString = fn[0 : len(fn)-len(fileExtension)]
}
newFileName := getValidFileName(outputDirFlag+"/"+date.String(), fileExtension) newFileName := getValidFileName(outputDirFlag+"/"+dateString, fileExtension)
newFileName = newFileName + fileExtension newFileName = newFileName + fileExtension
fmt.Println(" storing the new img as", newFileName) fmt.Printf(" storing the new img as %s\n\n", newFileName)
// duplicate the original file into newFileName // duplicate the original file into newFileName
f, err = os.Open(fileName) f, err = os.Open(fileName)
if err != nil { if err != nil {
fmt.Println("\ne", err) fmt.Printf("\nerror (os.Open): %s\n\n", err)
} }
defer f.Close() defer f.Close()
fo, err := os.Create(newFileName) fo, err := os.Create(newFileName)
@@ -101,24 +109,26 @@ func main() {
} }
} }
// set the img date into the file date (file's access and modification times) // set the img date into the file date (file's access and modification times)
// Notice that when exif.time=0, this will be the date being
// set here. In a future version might change it to just reuse
// the original file date.
os.Chtimes(newFileName, *date, *date) os.Chtimes(newFileName, *date, *date)
nImgsConverted++
}
fmt.Printf("converted %d images out of %d\n", nImgsConverted, nImgsDetected)
fmt.Println("Unconverted images:")
for i := 0; i < len(unconvertedFileNames); i++ {
fmt.Println(" ", unconvertedFileNames[i])
} }
} }
func getExifDate(f *os.File) (*time.Time, error) { func getExifDate(f *os.File) (*time.Time, error) {
x, err := exif.Decode(f) x, err := imagemeta.Decode(f)
if err != nil { if err != nil {
return nil, err return nil, err
} }
t := x.DateTimeOriginal()
d, err := x.DateTime() return &t, nil
if err != nil {
return nil, err
}
return &d, nil
} }
func getValidFileName(fileName, fileExtension string) string { func getValidFileName(fileName, fileExtension string) string {