/
exif.go
125 lines (108 loc) · 3.23 KB
/
exif.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// Package exif parse image files to extract key details.
package exif
import (
"bytes"
"github.com/thomasduchatelle/dphoto/delegate/backup/backupmodel"
"github.com/pkg/errors"
"github.com/rwcarlsen/goexif/exif"
log "github.com/sirupsen/logrus"
"image"
_ "image/jpeg"
_ "image/png"
"io"
"time"
)
func init() {
// note - Canon parser is failing on 2007 photos from a Canon camera
exif.RegisterParsers()
}
type Parser struct{}
func (p *Parser) Supports(media backupmodel.FoundMedia, mediaType backupmodel.MediaType) bool {
return mediaType == backupmodel.MediaTypeImage
}
func (p *Parser) ReadDetails(reader io.Reader, options backupmodel.DetailsReaderOptions) (*backupmodel.MediaDetails, error) {
buffer := bytes.NewBuffer(nil)
teeReader := io.TeeReader(reader, buffer)
x, err := exif.Decode(teeReader)
if err != nil {
log.WithError(err).Warn("no EXIF data found in file, try another way")
return p.readImageWithoutExif(io.MultiReader(buffer, reader))
}
latitude, longitude, err := x.LatLong()
if err != nil {
latitude = 0
longitude = 0
}
return &backupmodel.MediaDetails{
Width: p.getIntOrIgnore(x, exif.ImageWidth),
Height: p.getIntOrIgnore(x, exif.ImageLength),
Orientation: p.readOrientation(x),
DateTime: p.readDateTime(x, time.Time{}),
Make: p.getStringOrIgnore(x, exif.Make),
Model: p.getStringOrIgnore(x, exif.Model),
GPSLatitude: latitude,
GPSLongitude: longitude,
}, nil
}
func (p *Parser) readOrientation(x *exif.Exif) backupmodel.ImageOrientation {
switch p.getIntOrIgnore(x, exif.Orientation) {
case 3:
return backupmodel.OrientationLowerRight
case 6:
return backupmodel.OrientationUpperRight
case 8:
return backupmodel.OrientationLowerLeft
default:
return backupmodel.OrientationUpperLeft
}
}
func (p *Parser) readDateTime(x *exif.Exif, defaultDate time.Time) time.Time {
datetime := p.getStringOrIgnore(x, exif.DateTime)
if datetime == "" {
datetime = p.getStringOrIgnore(x, exif.DateTimeOriginal)
}
if datetime != "" {
exifTime, err := time.Parse("2006:01:02 15:04:05", datetime)
if err == nil {
return exifTime.UTC()
} else {
log.WithField("MediaAnalyser", "Exif").Warnf("Unsupported dfate format: %s", datetime)
}
}
return defaultDate
}
func (p *Parser) getStringOrIgnore(x *exif.Exif, model exif.FieldName) string {
if t, err := x.Get(model); err == nil && t != nil {
if val, err := t.StringVal(); err == nil {
return val
}
}
return ""
}
func (p *Parser) getIntOrIgnore(x *exif.Exif, model exif.FieldName) int {
if t, err := x.Get(model); err == nil && t != nil && t.Count > 0 {
if val, err := t.Int(0); err == nil {
return val
}
}
return 0
}
func (p *Parser) getFloatOrIgnore(x *exif.Exif, model exif.FieldName) float64 {
if t, err := x.Get(model); err == nil && t != nil && t.Count > 0 {
if val, err := t.Float(0); err == nil {
return val
}
}
return 0
}
func (p *Parser) readImageWithoutExif(reader io.Reader) (*backupmodel.MediaDetails, error) {
img, _, err := image.DecodeConfig(reader)
if err != nil {
return nil, errors.Wrapf(err, "Can't extract image dimentions")
}
return &backupmodel.MediaDetails{
Width: img.Width,
Height: img.Height,
Orientation: backupmodel.OrientationUpperLeft,
}, nil
}