1package fileop
2
3import (
4 "math"
5 "time"
6
7 "github.com/barasher/go-exiftool"
8
9 "git.sr.ht/~gabrielgio/img/pkg/components/media"
10)
11
12func ReadExif(path string) (*media.MediaEXIF, error) {
13 et, err := exiftool.NewExiftool()
14 if err != nil {
15 return nil, err
16 }
17 defer et.Close()
18
19 newExif := &media.MediaEXIF{}
20 fileInfo := et.ExtractMetadata(path)[0]
21
22 // Get description
23 description, err := fileInfo.GetString("ImageDescription")
24 if err == nil {
25 newExif.Description = &description
26 }
27
28 // Get camera model
29 model, err := fileInfo.GetString("Model")
30 if err == nil {
31 newExif.Camera = &model
32 }
33
34 // Get Camera make
35 make, err := fileInfo.GetString("Make")
36 if err == nil {
37 newExif.Maker = &make
38 }
39
40 // Get lens
41 lens, err := fileInfo.GetString("LensModel")
42 if err == nil {
43 newExif.Lens = &lens
44 }
45
46 //Get time of photo
47 createDateKeys := []string{
48 "CreationDate",
49 "DateTimeOriginal",
50 "CreateDate",
51 "TrackCreateDate",
52 "MediaCreateDate",
53 "FileCreateDate",
54 "ModifyDate",
55 "TrackModifyDate",
56 "MediaModifyDate",
57 "FileModifyDate",
58 }
59 for _, createDateKey := range createDateKeys {
60 date, err := fileInfo.GetString(createDateKey)
61 if err == nil {
62 layout := "2006:01:02 15:04:05"
63 dateTime, err := time.Parse(layout, date)
64 if err == nil {
65 newExif.DateShot = &dateTime
66 } else {
67 layoutWithOffset := "2006:01:02 15:04:05+02:00"
68 dateTime, err = time.Parse(layoutWithOffset, date)
69 if err == nil {
70 newExif.DateShot = &dateTime
71 }
72 }
73 break
74 }
75 }
76
77 // Get exposure time
78 exposureTime, err := fileInfo.GetFloat("ExposureTime")
79 if err == nil {
80 newExif.Exposure = &exposureTime
81 }
82
83 // Get aperture
84 aperture, err := fileInfo.GetFloat("Aperture")
85 if err == nil {
86 newExif.Aperture = &aperture
87 }
88
89 // Get ISO
90 iso, err := fileInfo.GetInt("ISO")
91 if err == nil {
92 newExif.Iso = &iso
93 }
94
95 // Get focal length
96 focalLen, err := fileInfo.GetFloat("FocalLength")
97 if err == nil {
98 newExif.FocalLength = &focalLen
99 }
100
101 // Get flash info
102 flash, err := fileInfo.GetInt("Flash")
103 if err == nil {
104 newExif.Flash = &flash
105 }
106
107 // Get orientation
108 orientation, err := fileInfo.GetInt("Orientation")
109 if err == nil {
110 newExif.Orientation = &orientation
111 }
112
113 // Get exposure program
114 expProgram, err := fileInfo.GetInt("ExposureProgram")
115 if err == nil {
116 newExif.ExposureProgram = &expProgram
117 }
118
119 // GPS coordinates - longitude
120 longitudeRaw, err := fileInfo.GetFloat("GPSLongitude")
121 if err == nil {
122 newExif.GPSLongitude = &longitudeRaw
123 }
124
125 // GPS coordinates - latitude
126 latitudeRaw, err := fileInfo.GetFloat("GPSLatitude")
127 if err == nil {
128 newExif.GPSLatitude = &latitudeRaw
129 }
130
131 sanitizeEXIF(newExif)
132
133 return newExif, nil
134}
135
136// isFloatReal returns true when the float value represents a real number
137// (different than +Inf, -Inf or NaN)
138func isFloatReal(v float64) bool {
139 if math.IsInf(v, 1) {
140 return false
141 } else if math.IsInf(v, -1) {
142 return false
143 } else if math.IsNaN(v) {
144 return false
145 }
146 return true
147}
148
149// sanitizeEXIF removes any EXIF float64 field that is not a real number (+Inf,
150// -Inf or Nan)
151func sanitizeEXIF(exif *media.MediaEXIF) {
152 if exif.Exposure != nil && !isFloatReal(*exif.Exposure) {
153 exif.Exposure = nil
154 }
155 if exif.Aperture != nil && !isFloatReal(*exif.Aperture) {
156 exif.Aperture = nil
157 }
158 if exif.FocalLength != nil && !isFloatReal(*exif.FocalLength) {
159 exif.FocalLength = nil
160 }
161 if (exif.GPSLatitude != nil && !isFloatReal(*exif.GPSLatitude)) ||
162 (exif.GPSLongitude != nil && !isFloatReal(*exif.GPSLongitude)) {
163 exif.GPSLatitude = nil
164 exif.GPSLongitude = nil
165 }
166}