Extract metadata as integers
Extract all metada as integers fomr exiftool. Signed-off-by: Kjeldgaard <Kjeldgaard@users.noreply.github.com>
This commit is contained in:
parent
1f9f6ebf9a
commit
7b5305579b
|
@ -5,7 +5,7 @@ go 1.16
|
|||
require (
|
||||
github.com/99designs/gqlgen v0.13.0
|
||||
github.com/Kagami/go-face v0.0.0-20200825065730-3dd2d74dccfb
|
||||
github.com/barasher/go-exiftool v1.3.2
|
||||
github.com/barasher/go-exiftool v1.3.2 // indirect
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/go-sql-driver/mysql v1.5.0
|
||||
github.com/gorilla/handlers v1.5.1
|
||||
|
@ -13,6 +13,7 @@ require (
|
|||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/h2non/filetype v1.1.1
|
||||
github.com/joho/godotenv v1.3.0
|
||||
github.com/kjeldgaard/go-exiftool v1.3.3
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/sabhiram/go-gitignore v0.0.0-20201211210132-54b8a0bf510f
|
||||
github.com/vektah/gqlparser/v2 v2.1.0
|
||||
|
|
|
@ -117,6 +117,8 @@ github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
|||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kjeldgaard/go-exiftool v1.3.3 h1:tDlkcHtTuduu7UgopqGS/ooh1tmEgtIhlBjmTXzjxbg=
|
||||
github.com/kjeldgaard/go-exiftool v1.3.3/go.mod h1:Nmi1VkXBbr48Kq5ZJjo29RD+a3PdWVGlL+8bSssxFbU=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
|
|
|
@ -1890,8 +1890,8 @@ type MediaEXIF {
|
|||
focalLength: Float
|
||||
"A formatted description of the flash settings, when the image was taken"
|
||||
flash: String
|
||||
"A formated description of the mode for adjusting the exposure of the image"
|
||||
exposureProgram: String
|
||||
"An index describing the mode for adjusting the exposure of the image"
|
||||
exposureProgram: Int
|
||||
}
|
||||
|
||||
type VideoMetadata {
|
||||
|
@ -4616,9 +4616,9 @@ func (ec *executionContext) _MediaEXIF_exposureProgram(ctx context.Context, fiel
|
|||
if resTmp == nil {
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(*string)
|
||||
res := resTmp.(*int64)
|
||||
fc.Result = res
|
||||
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
|
||||
return ec.marshalOInt2ᚖint64(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _MediaURL_url(ctx context.Context, field graphql.CollectedField, obj *models.MediaURL) (ret graphql.Marshaler) {
|
||||
|
|
|
@ -16,7 +16,7 @@ type MediaEXIF struct {
|
|||
FocalLength *float64
|
||||
Flash *string
|
||||
Orientation *int64
|
||||
ExposureProgram *string
|
||||
ExposureProgram *int64
|
||||
GPSLatitude *float64
|
||||
GPSLongitude *float64
|
||||
}
|
||||
|
|
|
@ -313,8 +313,8 @@ type MediaEXIF {
|
|||
focalLength: Float
|
||||
"A formatted description of the flash settings, when the image was taken"
|
||||
flash: String
|
||||
"A formated description of the mode for adjusting the exposure of the image"
|
||||
exposureProgram: String
|
||||
"An index describing the mode for adjusting the exposure of the image"
|
||||
exposureProgram: Int
|
||||
}
|
||||
|
||||
type VideoMetadata {
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/kjeldgaard/go-exiftool"
|
||||
"github.com/photoview/photoview/api/graphql/models"
|
||||
)
|
||||
|
||||
|
@ -31,7 +32,16 @@ func SaveEXIF(tx *gorm.DB, media *models.Media) (*models.MediaEXIF, error) {
|
|||
}
|
||||
}
|
||||
|
||||
var parser exifParser = &externalExifParser{}
|
||||
// Decide between internal or external Exif parser
|
||||
et, err := exiftool.NewExiftool()
|
||||
et.Close()
|
||||
var parser exifParser
|
||||
if err != nil {
|
||||
parser = &internalExifParser{}
|
||||
} else {
|
||||
parser = &externalExifParser{}
|
||||
}
|
||||
|
||||
|
||||
exif, err := parser.ParseExif(media)
|
||||
if err != nil {
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/barasher/go-exiftool"
|
||||
"github.com/kjeldgaard/go-exiftool"
|
||||
"github.com/photoview/photoview/api/graphql/models"
|
||||
)
|
||||
|
||||
|
@ -14,7 +14,10 @@ type externalExifParser struct{}
|
|||
|
||||
func (p *externalExifParser) ParseExif(media *models.Media) (returnExif *models.MediaEXIF, returnErr error) {
|
||||
// Init ExifTool
|
||||
et, err := exiftool.NewExiftool()
|
||||
|
||||
et2 := exiftool.Charset("-n")
|
||||
|
||||
et, err := .NewExiftool(et2)
|
||||
if err != nil {
|
||||
log.Printf("Error initializing ExifTool: %s\n", err)
|
||||
return nil, err
|
||||
|
@ -60,7 +63,6 @@ func (p *externalExifParser) ParseExif(media *models.Media) (returnExif *models.
|
|||
if err == nil {
|
||||
newExif.DateShot = &dateTime
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Get exposure time
|
||||
|
@ -88,7 +90,7 @@ func (p *externalExifParser) ParseExif(media *models.Media) (returnExif *models.
|
|||
focalLen, err := fileInfo.GetString("FocalLength")
|
||||
if err == nil {
|
||||
log.Printf("Focal length: %s", focalLen)
|
||||
reg, err := regexp.Compile("[0-9.]+")
|
||||
reg, _ := regexp.Compile("[0-9.]+")
|
||||
focalLenStr := reg.FindString(focalLen)
|
||||
focalLenFloat, err := strconv.ParseFloat(focalLenStr, 64)
|
||||
if err == nil {
|
||||
|
@ -104,44 +106,19 @@ func (p *externalExifParser) ParseExif(media *models.Media) (returnExif *models.
|
|||
}
|
||||
|
||||
// Get orientation
|
||||
orientation, err := fileInfo.GetString("Orientation")
|
||||
log.Printf("Orientation: %s", orientation)
|
||||
orientation, err := fileInfo.GetInt("Orientation")
|
||||
if err == nil {
|
||||
if orientation == "Horizontal (normal)" {
|
||||
var orientationInt int64 = 0
|
||||
newExif.Orientation = &orientationInt
|
||||
} else {
|
||||
reg, err := regexp.Compile("CCW")
|
||||
if err == nil {
|
||||
if reg.MatchString(orientation) {
|
||||
//Counter clockwise
|
||||
reg, err := regexp.Compile("[0-9.]+")
|
||||
orientationStr := reg.FindString(orientation)
|
||||
orientationInt, err := strconv.ParseInt(orientationStr, 10, 64)
|
||||
if err == nil {
|
||||
log.Printf("Orientation: %d", orientationInt)
|
||||
newExif.Orientation = &orientationInt
|
||||
}
|
||||
} else {
|
||||
// Clockwise
|
||||
reg, err := regexp.Compile("[0-9.]+")
|
||||
orientationStr := reg.FindString(orientation)
|
||||
orientationInt, err := strconv.ParseInt(orientationStr, 10, 64)
|
||||
if err == nil {
|
||||
orientationInt = orientationInt * -1
|
||||
log.Printf("Orientation: %d", orientationInt)
|
||||
newExif.Orientation = &orientationInt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
log.Printf("Orientation: %d", orientation)
|
||||
newExif.Orientation = &orientation
|
||||
}
|
||||
|
||||
// Get exposure program
|
||||
expProgram, err := fileInfo.GetString("ExposureProgram")
|
||||
expProgram, err := fileInfo.GetStrings("ExposureProgram")
|
||||
if err == nil {
|
||||
log.Printf("Exposure Program: %s", expProgram)
|
||||
newExif.ExposureProgram = &expProgram
|
||||
for _, value := range expProgram {
|
||||
log.Printf("%s", value)
|
||||
}
|
||||
//log.Printf("Exposure Program: %d", expProgram)
|
||||
}
|
||||
|
||||
// GPS coordinates - longitude
|
||||
|
|
|
@ -0,0 +1,191 @@
|
|||
package exif
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"math/big"
|
||||
"os"
|
||||
|
||||
"github.com/photoview/photoview/api/graphql/models"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/xor-gate/goexif2/exif"
|
||||
"github.com/xor-gate/goexif2/mknote"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
}()
|
||||
|
||||
exifTags, err := exif.Decode(photoFile)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Could not decode EXIF")
|
||||
}
|
||||
|
||||
newExif := models.MediaEXIF{}
|
||||
|
||||
model, err := p.readStringTag(exifTags, exif.Model, media)
|
||||
if err == nil {
|
||||
newExif.Camera = model
|
||||
}
|
||||
|
||||
maker, err := p.readStringTag(exifTags, exif.Make, media)
|
||||
if err == nil {
|
||||
newExif.Maker = maker
|
||||
}
|
||||
|
||||
lens, err := p.readStringTag(exifTags, exif.LensModel, media)
|
||||
if err == nil {
|
||||
newExif.Lens = lens
|
||||
}
|
||||
|
||||
date, err := exifTags.DateTime()
|
||||
if err == nil {
|
||||
newExif.DateShot = &date
|
||||
}
|
||||
|
||||
exposure, err := p.readRationalTag(exifTags, exif.ExposureTime, media)
|
||||
if err == nil {
|
||||
exposureStr := exposure.RatString()
|
||||
newExif.Exposure = &exposureStr
|
||||
}
|
||||
|
||||
apertureRat, err := p.readRationalTag(exifTags, exif.FNumber, media)
|
||||
if err == nil {
|
||||
aperture, _ := apertureRat.Float64()
|
||||
newExif.Aperture = &aperture
|
||||
}
|
||||
|
||||
isoTag, err := exifTags.Get(exif.ISOSpeedRatings)
|
||||
if err != nil {
|
||||
log.Printf("WARN: Could not read ISOSpeedRatings from EXIF: %s\n", media.Title)
|
||||
} else {
|
||||
iso, err := isoTag.Int(0)
|
||||
if err != nil {
|
||||
log.Printf("WARN: Could not parse EXIF ISOSpeedRatings as integer: %s\n", media.Title)
|
||||
} else {
|
||||
iso64 := int64(iso)
|
||||
newExif.Iso = &iso64
|
||||
}
|
||||
}
|
||||
|
||||
focalLengthTag, err := exifTags.Get(exif.FocalLength)
|
||||
if err == nil {
|
||||
focalLengthRat, err := focalLengthTag.Rat(0)
|
||||
if err == nil {
|
||||
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 {
|
||||
focalLenFloat := float64(focalLength)
|
||||
newExif.FocalLength = &focalLenFloat
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flash, err := exifTags.Flash()
|
||||
if err == nil {
|
||||
newExif.Flash = &flash
|
||||
}
|
||||
|
||||
orientation, err := p.readIntegerTag(exifTags, exif.Orientation, media)
|
||||
if err == nil {
|
||||
orientation64 := int64(*orientation)
|
||||
newExif.Orientation = &orientation64
|
||||
}
|
||||
|
||||
exposureProgram, err := p.readIntegerTag(exifTags, exif.ExposureProgram, media)
|
||||
if err == nil {
|
||||
exposureProgram64 := int64(*exposureProgram)
|
||||
newExif.ExposureProgram = &exposureProgram64
|
||||
}
|
||||
|
||||
lat, long, err := exifTags.LatLong()
|
||||
if err == nil {
|
||||
newExif.GPSLatitude = &lat
|
||||
newExif.GPSLongitude = &long
|
||||
}
|
||||
|
||||
returnExif = &newExif
|
||||
return
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
return &value, nil
|
||||
}
|
||||
|
||||
log.Printf("WARN: EXIF tag %s returned null: %s\n", name, media.Title)
|
||||
return nil, errors.New("exif tag returned null")
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
log.Printf("WARN: EXIF tag %s returned null: %s\n", name, media.Title)
|
||||
return nil, errors.New("exif tag returned null")
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
return &value, nil
|
||||
}
|
||||
|
||||
log.Printf("WARN: EXIF tag %s returned null: %s\n", name, media.Title)
|
||||
return nil, errors.New("exif tag returned null")
|
||||
}
|
|
@ -134,6 +134,20 @@ const exifNameLookup = {
|
|||
flash: 'Flash',
|
||||
}
|
||||
|
||||
// From https://exiftool.org/TagNames/EXIF.html
|
||||
const exposurePrograms = {
|
||||
0: 'Not defined',
|
||||
1: 'Manual',
|
||||
2: 'Normal program',
|
||||
3: 'Aperture priority',
|
||||
4: 'Shutter priority',
|
||||
5: 'Creative program',
|
||||
6: 'Action program',
|
||||
7: 'Portrait mode',
|
||||
8: 'Landscape mode ',
|
||||
9: 'Bulb',
|
||||
}
|
||||
|
||||
const SidebarContent = ({ media, hidePreview }) => {
|
||||
let exifItems = []
|
||||
|
||||
|
@ -152,7 +166,7 @@ const SidebarContent = ({ media, hidePreview }) => {
|
|||
|
||||
exif.dateShot = new Date(exif.dateShot).toLocaleString()
|
||||
if (exif.exposureProgram) {
|
||||
exif.exposureProgram = `${exif.exposureProgram}`
|
||||
exif.exposureProgram = exposurePrograms[exif.exposureProgram]
|
||||
}
|
||||
|
||||
if (exif.aperture) {
|
||||
|
|
Loading…
Reference in New Issue