1
Fork 0
photoview/api/scanner/exif/exif_parser_internal.go

189 lines
4.8 KiB
Go
Raw Normal View History

2021-01-19 16:39:57 +01:00
package exif
2020-02-24 23:30:08 +01:00
import (
"fmt"
2020-02-24 23:30:08 +01:00
"log"
"math/big"
"os"
2020-12-17 22:51:43 +01:00
"github.com/photoview/photoview/api/graphql/models"
2021-01-19 16:39:57 +01:00
"github.com/pkg/errors"
2020-02-24 23:30:08 +01:00
"github.com/xor-gate/goexif2/exif"
"github.com/xor-gate/goexif2/mknote"
)
2021-01-19 16:39:57 +01:00
// internalExifParser is an exif parser that parses the media without the use of external tools
type internalExifParser struct{}
2020-02-24 23:30:08 +01:00
2021-01-19 16:39:57 +01:00
func (p *internalExifParser) ParseExif(media *models.Media) (returnExif *models.MediaEXIF, returnErr error) {
photoFile, err := os.Open(media.Path)
2020-02-24 23:30:08 +01:00
if err != nil {
return nil, err
}
exif.RegisterParsers(mknote.All...)
// Recover if exif.Decode panics
defer func() {
if err := recover(); err != nil {
log.Printf("Recovered from panic: Exif decoding: %s\n", err)
returnErr = errors.New(fmt.Sprintf("Exif decoding panicked: %s\n", err))
}
}()
2020-02-24 23:30:08 +01:00
exifTags, err := exif.Decode(photoFile)
if err != nil {
return nil, errors.Wrap(err, "Could not decode EXIF")
2020-02-24 23:30:08 +01:00
}
2020-11-24 11:46:49 +01:00
newExif := models.MediaEXIF{}
2020-02-24 23:30:08 +01:00
2021-01-19 16:39:57 +01:00
model, err := p.readStringTag(exifTags, exif.Model, media)
2020-02-24 23:30:08 +01:00
if err == nil {
2020-11-24 11:46:49 +01:00
newExif.Camera = model
2020-02-24 23:30:08 +01:00
}
2021-01-19 16:39:57 +01:00
maker, err := p.readStringTag(exifTags, exif.Make, media)
2020-02-24 23:30:08 +01:00
if err == nil {
2020-11-24 11:46:49 +01:00
newExif.Maker = maker
2020-02-24 23:30:08 +01:00
}
2021-01-19 16:39:57 +01:00
lens, err := p.readStringTag(exifTags, exif.LensModel, media)
2020-02-24 23:30:08 +01:00
if err == nil {
2020-11-24 11:46:49 +01:00
newExif.Lens = lens
2020-02-24 23:30:08 +01:00
}
date, err := exifTags.DateTime()
if err == nil {
2020-11-24 11:46:49 +01:00
newExif.DateShot = &date
2020-02-24 23:30:08 +01:00
}
2021-01-19 16:39:57 +01:00
exposure, err := p.readRationalTag(exifTags, exif.ExposureTime, media)
2020-02-24 23:30:08 +01:00
if err == nil {
2020-11-24 11:46:49 +01:00
exposureStr := exposure.RatString()
newExif.Exposure = &exposureStr
2020-02-24 23:30:08 +01:00
}
2021-01-19 16:39:57 +01:00
apertureRat, err := p.readRationalTag(exifTags, exif.FNumber, media)
2020-02-24 23:30:08 +01:00
if err == nil {
2020-11-24 11:46:49 +01:00
aperture, _ := apertureRat.Float64()
newExif.Aperture = &aperture
2020-02-24 23:30:08 +01:00
}
isoTag, err := exifTags.Get(exif.ISOSpeedRatings)
if err != nil {
log.Printf("WARN: Could not read ISOSpeedRatings from EXIF: %s\n", media.Title)
2020-02-24 23:30:08 +01:00
} else {
iso, err := isoTag.Int(0)
if err != nil {
log.Printf("WARN: Could not parse EXIF ISOSpeedRatings as integer: %s\n", media.Title)
2020-02-24 23:30:08 +01:00
} else {
2020-11-24 11:46:49 +01:00
newExif.Iso = &iso
2020-02-24 23:30:08 +01:00
}
}
focalLengthTag, err := exifTags.Get(exif.FocalLength)
2020-02-24 23:30:08 +01:00
if err == nil {
focalLengthRat, err := focalLengthTag.Rat(0)
2020-02-24 23:30:08 +01:00
if err == nil {
2020-11-24 11:46:49 +01:00
focalLength, _ := focalLengthRat.Float64()
newExif.FocalLength = &focalLength
} else {
// For some photos, the focal length cannot be read as a rational value,
// but is instead the second value read as an integer
if err == nil {
focalLength, err := focalLengthTag.Int(1)
if err != nil {
log.Printf("WARN: Could not parse EXIF FocalLength as rational or integer: %s\n%s\n", media.Title, err)
} else {
2020-11-24 11:46:49 +01:00
focalLenFloat := float64(focalLength)
newExif.FocalLength = &focalLenFloat
}
2020-02-24 23:30:08 +01:00
}
}
}
flash, err := exifTags.Flash()
if err == nil {
2020-11-24 11:46:49 +01:00
newExif.Flash = &flash
2020-02-24 23:30:08 +01:00
}
2021-01-19 16:39:57 +01:00
orientation, err := p.readIntegerTag(exifTags, exif.Orientation, media)
2020-02-24 23:30:08 +01:00
if err == nil {
2020-11-24 11:46:49 +01:00
newExif.Orientation = orientation
2020-02-24 23:30:08 +01:00
}
2021-01-19 16:39:57 +01:00
exposureProgram, err := p.readIntegerTag(exifTags, exif.ExposureProgram, media)
2020-02-24 23:30:08 +01:00
if err == nil {
2020-11-24 11:46:49 +01:00
newExif.ExposureProgram = exposureProgram
2020-02-24 23:30:08 +01:00
}
lat, long, err := exifTags.LatLong()
if err == nil {
2020-11-24 11:46:49 +01:00
newExif.GPSLatitude = &lat
newExif.GPSLonitude = &long
}
2021-01-19 16:39:57 +01:00
returnExif = &newExif
return
2020-02-24 23:30:08 +01:00
}
2021-01-19 16:39:57 +01:00
func (p *internalExifParser) readStringTag(tags *exif.Exif, name exif.FieldName, media *models.Media) (*string, error) {
2020-02-24 23:30:08 +01:00
tag, err := tags.Get(name)
if err != nil {
return nil, errors.Wrapf(err, "could not read %s from EXIF: %s", name, media.Title)
2020-02-24 23:30:08 +01:00
}
if tag != nil {
value, err := tag.StringVal()
if err != nil {
return nil, errors.Wrapf(err, "could not parse %s from EXIF as string: %s", name, media.Title)
2020-02-24 23:30:08 +01:00
}
return &value, nil
}
log.Printf("WARN: EXIF tag %s returned null: %s\n", name, media.Title)
2020-02-24 23:30:08 +01:00
return nil, errors.New("exif tag returned null")
}
2021-01-19 16:39:57 +01:00
func (p *internalExifParser) readRationalTag(tags *exif.Exif, name exif.FieldName, media *models.Media) (*big.Rat, error) {
2020-02-24 23:30:08 +01:00
tag, err := tags.Get(name)
if err != nil {
return nil, errors.Wrapf(err, "could not read %s from EXIF: %s", name, media.Title)
2020-02-24 23:30:08 +01:00
}
if tag != nil {
value, err := tag.Rat(0)
if err != nil {
return nil, errors.Wrapf(err, "could not parse %s from EXIF as rational: %s", name, media.Title)
2020-02-24 23:30:08 +01:00
}
return value, nil
}
log.Printf("WARN: EXIF tag %s returned null: %s\n", name, media.Title)
2020-02-24 23:30:08 +01:00
return nil, errors.New("exif tag returned null")
}
2021-01-19 16:39:57 +01:00
func (p *internalExifParser) readIntegerTag(tags *exif.Exif, name exif.FieldName, media *models.Media) (*int, error) {
2020-02-24 23:30:08 +01:00
tag, err := tags.Get(name)
if err != nil {
return nil, errors.Wrapf(err, "could not read %s from EXIF: %s", name, media.Title)
2020-02-24 23:30:08 +01:00
}
if tag != nil {
value, err := tag.Int(0)
if err != nil {
return nil, errors.Wrapf(err, "Could not parse %s from EXIF as integer: %s", name, media.Title)
2020-02-24 23:30:08 +01:00
}
return &value, nil
}
log.Printf("WARN: EXIF tag %s returned null: %s\n", name, media.Title)
2020-02-24 23:30:08 +01:00
return nil, errors.New("exif tag returned null")
}