Refactor EXIF parsing

This commit is contained in:
viktorstrate
2021-01-19 16:39:57 +01:00
parent 9691572154
commit 46ae5115bb
3 changed files with 71 additions and 43 deletions

51
api/scanner/exif/exif.go Normal file
View File

@@ -0,0 +1,51 @@
package exif
import (
"log"
"github.com/pkg/errors"
"gorm.io/gorm"
"github.com/photoview/photoview/api/graphql/models"
)
type exifParser interface {
ParseExif(media *models.Media) (*models.MediaEXIF, error)
}
// SaveEXIF scans the media file for exif metadata and saves it in the database if found
func SaveEXIF(tx *gorm.DB, media *models.Media) (*models.MediaEXIF, error) {
log.Printf("Scanning for EXIF: %s", media.Path)
{
// Check if EXIF data already exists
if media.ExifID != nil {
var exif models.MediaEXIF
if err := tx.First(&exif, media.ExifID).Error; err != nil {
return nil, errors.Wrap(err, "get EXIF for media from database")
}
return &exif, nil
}
}
var parser exifParser = &internalExifParser{}
exif, err := parser.ParseExif(media)
if err != nil {
return nil, errors.Wrap(err, "failed to parse exif data")
}
if exif == nil {
return nil, nil
}
// Add EXIF to database and link to media
if err := tx.Model(&media).Association("Exif").Replace(exif); err != nil {
return nil, errors.Wrap(err, "save media exif to database")
}
return exif, nil
}

View File

@@ -1,4 +1,4 @@
package scanner
package exif
import (
"fmt"
@@ -6,31 +6,16 @@ import (
"math/big"
"os"
"github.com/pkg/errors"
"gorm.io/gorm"
"github.com/photoview/photoview/api/graphql/models"
"github.com/pkg/errors"
"github.com/xor-gate/goexif2/exif"
"github.com/xor-gate/goexif2/mknote"
)
func ScanEXIF(tx *gorm.DB, media *models.Media) (returnExif *models.MediaEXIF, returnErr error) {
log.Printf("Scanning for EXIF")
{
// Check if EXIF data already exists
if media.ExifID != nil {
var exif models.MediaEXIF
if err := tx.First(&exif, media.ExifID).Error; err != nil {
return nil, errors.Wrap(err, "get EXIF for media from database")
}
return &exif, nil
}
}
// internalExifParser is an exif parser that parses the media without the use of external tools
type internalExifParser struct{}
func (p *internalExifParser) ParseExif(media *models.Media) (returnExif *models.MediaEXIF, returnErr error) {
photoFile, err := os.Open(media.Path)
if err != nil {
return nil, err
@@ -53,17 +38,17 @@ func ScanEXIF(tx *gorm.DB, media *models.Media) (returnExif *models.MediaEXIF, r
newExif := models.MediaEXIF{}
model, err := readStringTag(exifTags, exif.Model, media)
model, err := p.readStringTag(exifTags, exif.Model, media)
if err == nil {
newExif.Camera = model
}
maker, err := readStringTag(exifTags, exif.Make, media)
maker, err := p.readStringTag(exifTags, exif.Make, media)
if err == nil {
newExif.Maker = maker
}
lens, err := readStringTag(exifTags, exif.LensModel, media)
lens, err := p.readStringTag(exifTags, exif.LensModel, media)
if err == nil {
newExif.Lens = lens
}
@@ -73,13 +58,13 @@ func ScanEXIF(tx *gorm.DB, media *models.Media) (returnExif *models.MediaEXIF, r
newExif.DateShot = &date
}
exposure, err := readRationalTag(exifTags, exif.ExposureTime, media)
exposure, err := p.readRationalTag(exifTags, exif.ExposureTime, media)
if err == nil {
exposureStr := exposure.RatString()
newExif.Exposure = &exposureStr
}
apertureRat, err := readRationalTag(exifTags, exif.FNumber, media)
apertureRat, err := p.readRationalTag(exifTags, exif.FNumber, media)
if err == nil {
aperture, _ := apertureRat.Float64()
newExif.Aperture = &aperture
@@ -125,12 +110,12 @@ func ScanEXIF(tx *gorm.DB, media *models.Media) (returnExif *models.MediaEXIF, r
newExif.Flash = &flash
}
orientation, err := readIntegerTag(exifTags, exif.Orientation, media)
orientation, err := p.readIntegerTag(exifTags, exif.Orientation, media)
if err == nil {
newExif.Orientation = orientation
}
exposureProgram, err := readIntegerTag(exifTags, exif.ExposureProgram, media)
exposureProgram, err := p.readIntegerTag(exifTags, exif.ExposureProgram, media)
if err == nil {
newExif.ExposureProgram = exposureProgram
}
@@ -141,20 +126,11 @@ func ScanEXIF(tx *gorm.DB, media *models.Media) (returnExif *models.MediaEXIF, r
newExif.GPSLonitude = &long
}
// If exif is empty
if newExif == (models.MediaEXIF{}) {
return nil, nil
returnExif = &newExif
return
}
// Add EXIF to database and link to media
if err := tx.Model(&media).Association("Exif").Replace(newExif); err != nil {
return nil, errors.Wrap(err, "save media exif to database")
}
return &newExif, nil
}
func readStringTag(tags *exif.Exif, name exif.FieldName, media *models.Media) (*string, error) {
func (p *internalExifParser) readStringTag(tags *exif.Exif, name exif.FieldName, media *models.Media) (*string, error) {
tag, err := tags.Get(name)
if err != nil {
return nil, errors.Wrapf(err, "could not read %s from EXIF: %s", name, media.Title)
@@ -173,7 +149,7 @@ func readStringTag(tags *exif.Exif, name exif.FieldName, media *models.Media) (*
return nil, errors.New("exif tag returned null")
}
func readRationalTag(tags *exif.Exif, name exif.FieldName, media *models.Media) (*big.Rat, error) {
func (p *internalExifParser) readRationalTag(tags *exif.Exif, name exif.FieldName, media *models.Media) (*big.Rat, error) {
tag, err := tags.Get(name)
if err != nil {
return nil, errors.Wrapf(err, "could not read %s from EXIF: %s", name, media.Title)
@@ -192,7 +168,7 @@ func readRationalTag(tags *exif.Exif, name exif.FieldName, media *models.Media)
return nil, errors.New("exif tag returned null")
}
func readIntegerTag(tags *exif.Exif, name exif.FieldName, media *models.Media) (*int, error) {
func (p *internalExifParser) readIntegerTag(tags *exif.Exif, name exif.FieldName, media *models.Media) (*int, error) {
tag, err := tags.Get(name)
if err != nil {
return nil, errors.Wrapf(err, "could not read %s from EXIF: %s", name, media.Title)

View File

@@ -11,6 +11,7 @@ import (
"strings"
"github.com/photoview/photoview/api/graphql/models"
"github.com/photoview/photoview/api/scanner/exif"
"github.com/pkg/errors"
"gorm.io/gorm"
)
@@ -165,9 +166,9 @@ func ScanMedia(tx *gorm.DB, mediaPath string, albumId int, cache *AlbumScannerCa
return nil, false, errors.Wrap(err, "could not insert media into database")
}
_, err = ScanEXIF(tx, &media)
_, err = exif.SaveEXIF(tx, &media)
if err != nil {
log.Printf("WARN: ScanEXIF for %s failed: %s\n", mediaName, err)
log.Printf("WARN: SaveEXIF for %s failed: %s\n", mediaName, err)
}
if media.Type == models.MediaTypeVideo {