2020-08-28 09:27:25 +02:00
|
|
|
package meta
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"path/filepath"
|
|
|
|
"runtime/debug"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/dsoprea/go-exif/v3"
|
|
|
|
heicexif "github.com/dsoprea/go-heic-exif-extractor"
|
2020-11-21 18:08:41 +01:00
|
|
|
jpegstructure "github.com/dsoprea/go-jpeg-image-structure"
|
|
|
|
pngstructure "github.com/dsoprea/go-png-image-structure"
|
|
|
|
tiffstructure "github.com/dsoprea/go-tiff-image-structure"
|
2020-08-28 09:27:25 +02:00
|
|
|
"github.com/photoprism/photoprism/pkg/fs"
|
|
|
|
"github.com/photoprism/photoprism/pkg/txt"
|
|
|
|
)
|
|
|
|
|
2020-12-12 17:20:31 +01:00
|
|
|
func RawExif(fileName string, fileType fs.FileFormat) (rawExif []byte, err error) {
|
2020-08-28 09:27:25 +02:00
|
|
|
defer func() {
|
|
|
|
if e := recover(); e != nil {
|
|
|
|
err = fmt.Errorf("metadata: %s in %s (raw exif panic)\nstack: %s", e, txt.Quote(filepath.Base(fileName)), debug.Stack())
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
// Extract raw EXIF block.
|
|
|
|
var parsed bool
|
|
|
|
|
|
|
|
logName := txt.Quote(filepath.Base(fileName))
|
|
|
|
|
2020-12-12 17:20:31 +01:00
|
|
|
if fileType == fs.FormatJpeg {
|
2020-08-28 09:27:25 +02:00
|
|
|
jpegMp := jpegstructure.NewJpegMediaParser()
|
|
|
|
|
|
|
|
sl, err := jpegMp.ParseFile(fileName)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
log.Warnf("metadata: %s in %s (parse jpeg)", err, logName)
|
|
|
|
} else {
|
|
|
|
_, rawExif, err = sl.Exif()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
if strings.HasPrefix(err.Error(), "no exif header") {
|
|
|
|
return rawExif, fmt.Errorf("metadata: no exif header in %s (parse jpeg)", logName)
|
|
|
|
} else if strings.HasPrefix(err.Error(), "no exif data") {
|
|
|
|
log.Debugf("metadata: failed parsing %s, starting brute-force search (parse jpeg)", logName)
|
|
|
|
} else {
|
|
|
|
log.Warnf("metadata: %s in %s, starting brute-force search (parse jpeg)", err, logName)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
parsed = true
|
|
|
|
}
|
|
|
|
}
|
2020-12-12 17:20:31 +01:00
|
|
|
} else if fileType == fs.FormatPng {
|
2020-08-28 09:27:25 +02:00
|
|
|
pngMp := pngstructure.NewPngMediaParser()
|
|
|
|
|
|
|
|
cs, err := pngMp.ParseFile(fileName)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return rawExif, fmt.Errorf("metadata: %s in %s (parse png)", err, logName)
|
|
|
|
}
|
|
|
|
|
|
|
|
_, rawExif, err = cs.Exif()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
if err.Error() == "file does not have EXIF" {
|
|
|
|
return rawExif, fmt.Errorf("metadata: no exif header in %s (parse png)", logName)
|
|
|
|
} else {
|
|
|
|
log.Warnf("metadata: %s in %s (parse png)", err, logName)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
parsed = true
|
|
|
|
}
|
2020-12-12 17:20:31 +01:00
|
|
|
} else if fileType == fs.FormatHEIF {
|
2020-08-28 09:27:25 +02:00
|
|
|
heicMp := heicexif.NewHeicExifMediaParser()
|
|
|
|
|
|
|
|
cs, err := heicMp.ParseFile(fileName)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return rawExif, fmt.Errorf("metadata: %s in %s (parse heic)", err, logName)
|
|
|
|
}
|
|
|
|
|
|
|
|
_, rawExif, err = cs.Exif()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
if err.Error() == "file does not have EXIF" {
|
|
|
|
return rawExif, fmt.Errorf("metadata: no exif header in %s (parse heic)", logName)
|
|
|
|
} else {
|
|
|
|
log.Warnf("metadata: %s in %s (parse heic)", err, logName)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
parsed = true
|
|
|
|
}
|
2020-12-12 17:20:31 +01:00
|
|
|
} else if fileType == fs.FormatTiff {
|
2020-08-28 09:27:25 +02:00
|
|
|
tiffMp := tiffstructure.NewTiffMediaParser()
|
|
|
|
|
|
|
|
cs, err := tiffMp.ParseFile(fileName)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return rawExif, fmt.Errorf("metadata: %s in %s (parse tiff)", err, logName)
|
|
|
|
}
|
|
|
|
|
|
|
|
_, rawExif, err = cs.Exif()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
if err.Error() == "file does not have EXIF" {
|
|
|
|
return rawExif, fmt.Errorf("metadata: no exif header in %s (parse tiff)", logName)
|
|
|
|
} else {
|
|
|
|
log.Warnf("metadata: %s in %s (parse tiff)", err, logName)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
parsed = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !parsed {
|
|
|
|
rawExif, err = exif.SearchFileAndExtractExif(fileName)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return rawExif, fmt.Errorf("metadata: no exif header in %s (search and extract)", logName)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return rawExif, nil
|
|
|
|
}
|