photoprism/internal/meta/exif_parser.go
Michael Mayer 68ba289d6c CLI: Add "photoprism show config/formats" subcommands
Replaces "photoprism config", which could only display
current configuration values. Codecs and file formats
have been refactored along the way.

Signed-off-by: Michael Mayer <michael@liquidbytes.net>
2022-04-12 13:28:28 +02:00

132 lines
3.5 KiB
Go

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/fs"
"github.com/photoprism/photoprism/pkg/sanitize"
)
func RawExif(fileName string, fileFormat fs.Format, 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, sanitize.Log(filepath.Base(fileName)), debug.Stack())
}
}()
// Extract raw Exif block.
var parsed bool
// Sanitized and shortened file name for logs.
logName := sanitize.Log(filepath.Base(fileName))
// Try Exif parser for specific media file format first.
switch fileFormat {
case fs.FormatJpeg:
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.FormatPng:
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.FormatHEIF:
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.FormatTiff:
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
}