/
exif_parser.go
131 lines (109 loc) Β· 3.57 KB
/
exif_parser.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
126
127
128
129
130
131
package meta
import (
"fmt"
"path/filepath"
"runtime/debug"
"strings"
"github.com/dsoprea/go-exif/v3"
heicexif "github.com/dsoprea/go-heic-exif-extractor/v2"
jpegstructure "github.com/dsoprea/go-jpeg-image-structure/v2"
pngstructure "github.com/dsoprea/go-png-image-structure/v2"
tiffstructure "github.com/dsoprea/go-tiff-image-structure/v2"
"github.com/photoprism/photoprism/pkg/clean"
"github.com/photoprism/photoprism/pkg/fs"
)
func RawExif(fileName string, fileFormat fs.Type, bruteForce bool) (rawExif []byte, err error) {
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("%s in %s (raw exif panic)\nstack: %s", e, clean.Log(filepath.Base(fileName)), debug.Stack())
}
}()
// Extract raw Exif block.
var parsed bool
// Sanitized and shortened file name for logs.
logName := clean.Log(filepath.Base(fileName))
// Try Exif parser for specific media file format first.
switch fileFormat {
case fs.ImageJPEG:
jpegMp := jpegstructure.NewJpegMediaParser()
sl, err := jpegMp.ParseFile(fileName)
if err != nil {
log.Infof("metadata: %s while parsing jpeg file %s", err, logName)
} else {
_, rawExif, err = sl.Exif()
if err != nil {
if !bruteForce || strings.HasPrefix(err.Error(), "no exif header") {
return rawExif, fmt.Errorf("found no exif header")
} else if strings.HasPrefix(err.Error(), "no exif data") {
log.Debugf("metadata: failed parsing %s, starting brute-force search (parse jpeg)", logName)
} else {
log.Infof("metadata: %s in %s, starting brute-force search (parse jpeg)", err, logName)
}
} else {
parsed = true
}
}
case fs.ImagePNG:
pngMp := pngstructure.NewPngMediaParser()
cs, err := pngMp.ParseFile(fileName)
if err != nil {
return rawExif, fmt.Errorf("%s while parsing png file", err)
} else {
_, rawExif, err = cs.Exif()
if err != nil {
if err.Error() == "file does not have EXIF" || strings.HasPrefix(err.Error(), "no exif data") {
return rawExif, fmt.Errorf("found no exif header")
} else {
log.Infof("metadata: %s in %s (parse png)", err, logName)
}
} else {
parsed = true
}
}
case fs.ImageHEIF, fs.ImageHEIC, fs.ImageHEICS, fs.ImageAVIF, fs.ImageAVIFS:
heicMp := heicexif.NewHeicExifMediaParser()
cs, err := heicMp.ParseFile(fileName)
if err != nil {
return rawExif, fmt.Errorf("%s while parsing heic file", err)
} else {
_, rawExif, err = cs.Exif()
if err != nil {
if err.Error() == "file does not have EXIF" || strings.HasPrefix(err.Error(), "no exif data") {
return rawExif, fmt.Errorf("found no exif header")
} else {
log.Infof("metadata: %s in %s (parse heic)", err, logName)
}
} else {
parsed = true
}
}
case fs.ImageTIFF:
tiffMp := tiffstructure.NewTiffMediaParser()
cs, err := tiffMp.ParseFile(fileName)
if err != nil {
return rawExif, fmt.Errorf("%s while parsing tiff file", err)
} else {
_, rawExif, err = cs.Exif()
if err != nil {
if err.Error() == "file does not have EXIF" || strings.HasPrefix(err.Error(), "no exif data") {
return rawExif, fmt.Errorf("found no exif header")
} else {
log.Infof("metadata: %s in %s (parse tiff)", err, logName)
}
} else {
parsed = true
}
}
default:
log.Debugf("metadata: no native file format support for %s, performing brute-force exif search", logName)
bruteForce = true
}
// Start brute-force search for Exif data?
if !parsed && bruteForce {
rawExif, err = exif.SearchFileAndExtractExif(fileName)
if err != nil {
return rawExif, fmt.Errorf("found no exif data")
}
}
return rawExif, nil
}