mirror of
https://git.vectorsigma.ru/public/photoview.git
synced 2026-08-03 21:19:18 +00:00
Refactor EXIF parsing
This commit is contained in:
51
api/scanner/exif/exif.go
Normal file
51
api/scanner/exif/exif.go
Normal 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
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user