Backend: Rename MediaFile.Exif() to MetaData() #172
Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
parent
c147eee30f
commit
b21ad9bece
11 changed files with 69 additions and 348 deletions
|
@ -7,6 +7,7 @@ import (
|
|||
"trimmer.io/go-xmp/xmp"
|
||||
)
|
||||
|
||||
// Data represents image meta data.
|
||||
type Data struct {
|
||||
UUID string
|
||||
TakenAt time.Time
|
||||
|
|
|
@ -14,7 +14,7 @@ import (
|
|||
"trimmer.io/go-xmp/xmp"
|
||||
)
|
||||
|
||||
// Exif parses an image file and returns its Exif data.
|
||||
// Exif parses an image file for Exif meta data and returns as Data struct.
|
||||
func Exif(filename string) (data Data, err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
"trimmer.io/go-xmp/xmp"
|
||||
)
|
||||
|
||||
// Exif returns parses an XMP and returns its data.
|
||||
// XMP parses an XMP file and returns a Data struct.
|
||||
func XMP(filename string) (data Data, err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
|
|
|
@ -42,7 +42,7 @@ func TestConvert_ToJpeg(t *testing.T) {
|
|||
|
||||
assert.Empty(t, err, "ToJpeg() failed")
|
||||
|
||||
infoJpeg, err := imageJpeg.Exif()
|
||||
infoJpeg, err := imageJpeg.MetaData()
|
||||
|
||||
assert.Nilf(t, err, "UpdateExif() failed for "+imageJpeg.Filename())
|
||||
|
||||
|
@ -52,8 +52,6 @@ func TestConvert_ToJpeg(t *testing.T) {
|
|||
|
||||
assert.Equal(t, jpegFilename, imageJpeg.filename)
|
||||
|
||||
assert.False(t, infoJpeg == nil || err != nil, "Could not read UpdateExif data of JPEG image")
|
||||
|
||||
assert.Equal(t, "Canon EOS 7D", infoJpeg.CameraModel)
|
||||
|
||||
rawFilename := conf.ImportPath() + "/raw/IMG_2567.CR2"
|
||||
|
@ -70,9 +68,7 @@ func TestConvert_ToJpeg(t *testing.T) {
|
|||
|
||||
assert.NotEqual(t, rawFilename, imageRaw.filename)
|
||||
|
||||
infoRaw, err := imageRaw.Exif()
|
||||
|
||||
assert.False(t, infoRaw == nil || err != nil, "Could not read UpdateExif data of RAW image")
|
||||
infoRaw, err := imageRaw.MetaData()
|
||||
|
||||
assert.Equal(t, "Canon EOS 6D", infoRaw.CameraModel)
|
||||
}
|
||||
|
@ -100,9 +96,7 @@ func TestConvert_Path(t *testing.T) {
|
|||
|
||||
assert.Equal(t, jpegFilename, image.filename, "FileName must be the same")
|
||||
|
||||
infoRaw, err := image.Exif()
|
||||
|
||||
assert.False(t, infoRaw == nil || err != nil, "Could not read UpdateExif data of RAW image")
|
||||
infoRaw, err := image.MetaData()
|
||||
|
||||
assert.Equal(t, "Canon EOS 6D", infoRaw.CameraModel, "UpdateCamera model should be Canon EOS M10")
|
||||
|
||||
|
|
|
@ -1,276 +0,0 @@
|
|||
package photoprism
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/dsoprea/go-exif"
|
||||
"gopkg.in/ugjka/go-tz.v2/tz"
|
||||
)
|
||||
|
||||
// Exif represents MediaFile metadata.
|
||||
type Exif struct {
|
||||
UUID string
|
||||
TakenAt time.Time
|
||||
TakenAtLocal time.Time
|
||||
TimeZone string
|
||||
Artist string
|
||||
CameraMake string
|
||||
CameraModel string
|
||||
Description string
|
||||
LensMake string
|
||||
LensModel string
|
||||
Flash bool
|
||||
FocalLength int
|
||||
Exposure string
|
||||
Aperture float64
|
||||
FNumber float64
|
||||
Iso int
|
||||
Lat float64
|
||||
Lng float64
|
||||
Altitude int
|
||||
Width int
|
||||
Height int
|
||||
Orientation int
|
||||
All map[string]string
|
||||
}
|
||||
|
||||
var im *exif.IfdMapping
|
||||
|
||||
func IfdMapping() *exif.IfdMapping {
|
||||
if im != nil {
|
||||
return im
|
||||
}
|
||||
|
||||
im = exif.NewIfdMapping()
|
||||
|
||||
if err := exif.LoadStandardIfds(im); err != nil {
|
||||
log.Errorf("could not parse exif config: %s", err.Error())
|
||||
}
|
||||
|
||||
return im
|
||||
}
|
||||
|
||||
// Exif returns exif meta data of a media file.
|
||||
func (m *MediaFile) Exif() (result *Exif, err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
result = m.exifData
|
||||
err = fmt.Errorf("error while parsing exif data: %s", e)
|
||||
}
|
||||
}()
|
||||
|
||||
if m == nil {
|
||||
return nil, errors.New("can't parse exif data: file instance is nil")
|
||||
}
|
||||
|
||||
if m.exifData != nil {
|
||||
return m.exifData, nil
|
||||
}
|
||||
|
||||
if !m.IsJpeg() && !m.IsRaw() && !m.IsHEIF() {
|
||||
return nil, errors.New(fmt.Sprintf("media file not compatible with exif: \"%s\"", m.Filename()))
|
||||
}
|
||||
|
||||
m.exifData = &Exif{}
|
||||
|
||||
rawExif, err := exif.SearchFileAndExtractExif(m.Filename())
|
||||
|
||||
if err != nil {
|
||||
return m.exifData, err
|
||||
}
|
||||
|
||||
ti := exif.NewTagIndex()
|
||||
|
||||
tags := make(map[string]string)
|
||||
im := IfdMapping()
|
||||
|
||||
visitor := func(fqIfdPath string, ifdIndex int, tagId uint16, tagType exif.TagType, valueContext exif.ValueContext) (err error) {
|
||||
ifdPath, err := im.StripPathPhraseIndices(fqIfdPath)
|
||||
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
it, err := ti.Get(ifdPath, tagId)
|
||||
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
valueString := ""
|
||||
|
||||
if tagType.Type() != exif.TypeUndefined {
|
||||
valueString, err = tagType.ResolveAsString(valueContext, true)
|
||||
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if it.Name != "" && valueString != "" {
|
||||
tags[it.Name] = valueString
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err = exif.Visit(exif.IfdStandard, im, ti, rawExif, visitor)
|
||||
|
||||
if err != nil {
|
||||
return m.exifData, err
|
||||
}
|
||||
|
||||
if value, ok := tags["Artist"]; ok {
|
||||
m.exifData.Artist = strings.Replace(value, "\"", "", -1)
|
||||
}
|
||||
|
||||
if value, ok := tags["Model"]; ok {
|
||||
m.exifData.CameraModel = strings.Replace(value, "\"", "", -1)
|
||||
}
|
||||
|
||||
if value, ok := tags["Make"]; ok {
|
||||
m.exifData.CameraMake = strings.Replace(value, "\"", "", -1)
|
||||
}
|
||||
|
||||
if value, ok := tags["LensMake"]; ok {
|
||||
m.exifData.LensMake = strings.Replace(value, "\"", "", -1)
|
||||
}
|
||||
|
||||
if value, ok := tags["LensModel"]; ok {
|
||||
m.exifData.LensModel = strings.Replace(value, "\"", "", -1)
|
||||
}
|
||||
|
||||
if value, ok := tags["ExposureTime"]; ok {
|
||||
m.exifData.Exposure = value
|
||||
}
|
||||
|
||||
if value, ok := tags["FNumber"]; ok {
|
||||
values := strings.Split(value, "/")
|
||||
|
||||
if len(values) == 2 && values[1] != "0" && values[1] != "" {
|
||||
number, _ := strconv.ParseFloat(values[0], 64)
|
||||
denom, _ := strconv.ParseFloat(values[1], 64)
|
||||
|
||||
m.exifData.FNumber = math.Round((number/denom)*1000) / 1000
|
||||
}
|
||||
}
|
||||
|
||||
if value, ok := tags["ApertureValue"]; ok {
|
||||
values := strings.Split(value, "/")
|
||||
|
||||
if len(values) == 2 && values[1] != "0" && values[1] != "" {
|
||||
number, _ := strconv.ParseFloat(values[0], 64)
|
||||
denom, _ := strconv.ParseFloat(values[1], 64)
|
||||
|
||||
m.exifData.Aperture = math.Round((number/denom)*1000) / 1000
|
||||
}
|
||||
}
|
||||
|
||||
if value, ok := tags["FocalLengthIn35mmFilm"]; ok {
|
||||
if i, err := strconv.Atoi(value); err == nil {
|
||||
m.exifData.FocalLength = i
|
||||
}
|
||||
} else if value, ok := tags["FocalLength"]; ok {
|
||||
values := strings.Split(value, "/")
|
||||
|
||||
if len(values) == 2 && values[1] != "0" && values[1] != "" {
|
||||
number, _ := strconv.ParseFloat(values[0], 64)
|
||||
denom, _ := strconv.ParseFloat(values[1], 64)
|
||||
|
||||
m.exifData.FocalLength = int(math.Round((number/denom)*1000) / 1000)
|
||||
}
|
||||
}
|
||||
|
||||
if value, ok := tags["ISOSpeedRatings"]; ok {
|
||||
if i, err := strconv.Atoi(value); err == nil {
|
||||
m.exifData.Iso = i
|
||||
}
|
||||
}
|
||||
|
||||
if value, ok := tags["ImageUniqueID"]; ok {
|
||||
m.exifData.UUID = value
|
||||
}
|
||||
|
||||
if value, ok := tags["ImageWidth"]; ok {
|
||||
if i, err := strconv.Atoi(value); err == nil {
|
||||
m.exifData.Width = i
|
||||
}
|
||||
}
|
||||
|
||||
if value, ok := tags["ImageLength"]; ok {
|
||||
if i, err := strconv.Atoi(value); err == nil {
|
||||
m.exifData.Width = i
|
||||
}
|
||||
}
|
||||
|
||||
if value, ok := tags["Orientation"]; ok {
|
||||
if i, err := strconv.Atoi(value); err == nil {
|
||||
m.exifData.Orientation = i
|
||||
}
|
||||
} else {
|
||||
m.exifData.Orientation = 1
|
||||
}
|
||||
|
||||
_, index, err := exif.Collect(im, ti, rawExif)
|
||||
|
||||
if err != nil {
|
||||
return m.exifData, err
|
||||
}
|
||||
|
||||
if ifd, err := index.RootIfd.ChildWithIfdPath(exif.IfdPathStandardGps); err == nil {
|
||||
if gi, err := ifd.GpsInfo(); err == nil {
|
||||
m.exifData.Lat = gi.Latitude.Decimal()
|
||||
m.exifData.Lng = gi.Longitude.Decimal()
|
||||
m.exifData.Altitude = gi.Altitude
|
||||
}
|
||||
}
|
||||
|
||||
if m.exifData.Lat != 0 && m.exifData.Lng != 0 {
|
||||
zones, err := tz.GetZone(tz.Point{
|
||||
Lat: m.exifData.Lat,
|
||||
Lon: m.exifData.Lng,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
m.exifData.TimeZone = "UTC"
|
||||
}
|
||||
|
||||
m.exifData.TimeZone = zones[0]
|
||||
}
|
||||
|
||||
if value, ok := tags["DateTimeOriginal"]; ok {
|
||||
m.exifData.TakenAtLocal, _ = time.Parse("2006:01:02 15:04:05", value)
|
||||
|
||||
loc, err := time.LoadLocation(m.exifData.TimeZone)
|
||||
|
||||
if err != nil {
|
||||
m.exifData.TakenAt = m.exifData.TakenAtLocal
|
||||
log.Warnf("no location for timezone: %s", err.Error())
|
||||
} else if tl, err := time.ParseInLocation("2006:01:02 15:04:05", value, loc); err == nil {
|
||||
m.exifData.TakenAt = tl.UTC()
|
||||
} else {
|
||||
log.Warnf("could parse time: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if value, ok := tags["Flash"]; ok {
|
||||
if i, err := strconv.Atoi(value); err == nil && i&1 == 1 {
|
||||
m.exifData.Flash = true
|
||||
}
|
||||
}
|
||||
|
||||
if value, ok := tags["ImageDescription"]; ok {
|
||||
m.exifData.Description = strings.Replace(value, "\"", "", -1)
|
||||
}
|
||||
|
||||
m.exifData.All = tags
|
||||
|
||||
return m.exifData, nil
|
||||
}
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/jinzhu/gorm"
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/internal/meta"
|
||||
"github.com/photoprism/photoprism/internal/txt"
|
||||
)
|
||||
|
||||
|
@ -28,7 +29,7 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions) IndexResult {
|
|||
|
||||
var photo entity.Photo
|
||||
var file, primaryFile entity.File
|
||||
var exifData *Exif
|
||||
var metaData meta.Data
|
||||
var photoQuery, fileQuery *gorm.DB
|
||||
var keywords []string
|
||||
var isNSFW bool
|
||||
|
@ -55,8 +56,8 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions) IndexResult {
|
|||
photoQuery = ind.db.Unscoped().First(&photo, "photo_path = ? AND photo_name = ?", filePath, fileBase)
|
||||
|
||||
if photoQuery.Error != nil && m.HasTimeAndPlace() {
|
||||
exifData, _ = m.Exif()
|
||||
photoQuery = ind.db.Unscoped().First(&photo, "photo_lat = ? AND photo_lng = ? AND taken_at = ?", exifData.Lat, exifData.Lng, exifData.TakenAt)
|
||||
metaData, _ = m.MetaData()
|
||||
photoQuery = ind.db.Unscoped().First(&photo, "photo_lat = ? AND photo_lng = ? AND taken_at = ?", metaData.Lat, metaData.Lng, metaData.TakenAt)
|
||||
}
|
||||
} else {
|
||||
photoQuery = ind.db.Unscoped().First(&photo, "id = ?", file.PhotoID)
|
||||
|
@ -95,19 +96,19 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions) IndexResult {
|
|||
|
||||
if fileChanged || o.UpdateExif {
|
||||
// Read UpdateExif data
|
||||
if exifData, err := m.Exif(); err == nil {
|
||||
photo.PhotoLat = exifData.Lat
|
||||
photo.PhotoLng = exifData.Lng
|
||||
photo.TakenAt = exifData.TakenAt
|
||||
photo.TakenAtLocal = exifData.TakenAtLocal
|
||||
photo.TimeZone = exifData.TimeZone
|
||||
photo.PhotoAltitude = exifData.Altitude
|
||||
photo.PhotoArtist = exifData.Artist
|
||||
if metaData, err := m.MetaData(); err == nil {
|
||||
photo.PhotoLat = metaData.Lat
|
||||
photo.PhotoLng = metaData.Lng
|
||||
photo.TakenAt = metaData.TakenAt
|
||||
photo.TakenAtLocal = metaData.TakenAtLocal
|
||||
photo.TimeZone = metaData.TimeZone
|
||||
photo.PhotoAltitude = metaData.Altitude
|
||||
photo.PhotoArtist = metaData.Artist
|
||||
|
||||
if len(exifData.UUID) > 15 {
|
||||
log.Debugf("index: file uuid \"%s\"", exifData.UUID)
|
||||
if len(metaData.UUID) > 15 {
|
||||
log.Debugf("index: file uuid \"%s\"", metaData.UUID)
|
||||
|
||||
file.FileUUID = exifData.UUID
|
||||
file.FileUUID = metaData.UUID
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ func (m *MediaFile) Location() (*entity.Location, error) {
|
|||
return m.location, nil
|
||||
}
|
||||
|
||||
data, err := m.Exif()
|
||||
data, err := m.MetaData()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -9,11 +9,13 @@ import (
|
|||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/djherbis/times"
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/internal/file"
|
||||
"github.com/photoprism/photoprism/internal/meta"
|
||||
)
|
||||
|
||||
// MediaFile represents a single photo, video or sidecar file.
|
||||
|
@ -27,7 +29,8 @@ type MediaFile struct {
|
|||
perceptualHash string
|
||||
width int
|
||||
height int
|
||||
exifData *Exif
|
||||
once sync.Once
|
||||
metaData meta.Data
|
||||
location *entity.Location
|
||||
}
|
||||
|
||||
|
@ -53,7 +56,7 @@ func (m *MediaFile) DateCreated() time.Time {
|
|||
|
||||
m.dateCreated = time.Now()
|
||||
|
||||
info, err := m.Exif()
|
||||
info, err := m.MetaData()
|
||||
|
||||
if err == nil && !info.TakenAt.IsZero() && info.TakenAt.Year() > 1000 {
|
||||
m.dateCreated = info.TakenAt
|
||||
|
@ -83,7 +86,7 @@ func (m *MediaFile) DateCreated() time.Time {
|
|||
}
|
||||
|
||||
func (m *MediaFile) HasTimeAndPlace() bool {
|
||||
exifData, err := m.Exif()
|
||||
exifData, err := m.MetaData()
|
||||
|
||||
if err != nil {
|
||||
return false
|
||||
|
@ -96,7 +99,7 @@ func (m *MediaFile) HasTimeAndPlace() bool {
|
|||
|
||||
// CameraModel returns the camera model with which the media file was created.
|
||||
func (m *MediaFile) CameraModel() string {
|
||||
info, err := m.Exif()
|
||||
info, err := m.MetaData()
|
||||
|
||||
var result string
|
||||
|
||||
|
@ -109,7 +112,7 @@ func (m *MediaFile) CameraModel() string {
|
|||
|
||||
// CameraMake returns the make of the camera with which the file was created.
|
||||
func (m *MediaFile) CameraMake() string {
|
||||
info, err := m.Exif()
|
||||
info, err := m.MetaData()
|
||||
|
||||
var result string
|
||||
|
||||
|
@ -122,7 +125,7 @@ func (m *MediaFile) CameraMake() string {
|
|||
|
||||
// LensModel returns the lens model of a media file.
|
||||
func (m *MediaFile) LensModel() string {
|
||||
info, err := m.Exif()
|
||||
info, err := m.MetaData()
|
||||
|
||||
var result string
|
||||
|
||||
|
@ -135,7 +138,7 @@ func (m *MediaFile) LensModel() string {
|
|||
|
||||
// LensMake returns the make of the Lens.
|
||||
func (m *MediaFile) LensMake() string {
|
||||
info, err := m.Exif()
|
||||
info, err := m.MetaData()
|
||||
|
||||
var result string
|
||||
|
||||
|
@ -148,7 +151,7 @@ func (m *MediaFile) LensMake() string {
|
|||
|
||||
// FocalLength return the length of the focal for a file.
|
||||
func (m *MediaFile) FocalLength() int {
|
||||
info, err := m.Exif()
|
||||
info, err := m.MetaData()
|
||||
|
||||
var result int
|
||||
|
||||
|
@ -161,7 +164,7 @@ func (m *MediaFile) FocalLength() int {
|
|||
|
||||
// FNumber returns the F number with which the media file was created.
|
||||
func (m *MediaFile) FNumber() float64 {
|
||||
info, err := m.Exif()
|
||||
info, err := m.MetaData()
|
||||
|
||||
var result float64
|
||||
|
||||
|
@ -174,7 +177,7 @@ func (m *MediaFile) FNumber() float64 {
|
|||
|
||||
// Iso returns the iso rating as int.
|
||||
func (m *MediaFile) Iso() int {
|
||||
info, err := m.Exif()
|
||||
info, err := m.MetaData()
|
||||
|
||||
var result int
|
||||
|
||||
|
@ -187,7 +190,7 @@ func (m *MediaFile) Iso() int {
|
|||
|
||||
// Exposure returns the exposure time as string.
|
||||
func (m *MediaFile) Exposure() string {
|
||||
info, err := m.Exif()
|
||||
info, err := m.MetaData()
|
||||
|
||||
var result string
|
||||
|
||||
|
@ -284,7 +287,7 @@ func (m *MediaFile) RelatedFiles() (result RelatedFiles, err error) {
|
|||
result.main = resultFile
|
||||
} else if resultFile.IsJpeg() && len(result.main.Filename()) > len(resultFile.Filename()) {
|
||||
result.main = resultFile
|
||||
} else if resultFile.IsImageOther() {
|
||||
} else if resultFile.IsImageOther() {
|
||||
result.main = resultFile
|
||||
}
|
||||
|
||||
|
@ -592,7 +595,7 @@ func (m *MediaFile) decodeDimensions() error {
|
|||
|
||||
var width, height int
|
||||
|
||||
exif, err := m.Exif()
|
||||
exif, err := m.MetaData()
|
||||
|
||||
if err == nil {
|
||||
width = exif.Width
|
||||
|
@ -671,7 +674,7 @@ func (m *MediaFile) AspectRatio() float64 {
|
|||
|
||||
// Orientation returns the orientation of a MediaFile.
|
||||
func (m *MediaFile) Orientation() int {
|
||||
if exif, err := m.Exif(); err == nil {
|
||||
if exif, err := m.MetaData(); err == nil {
|
||||
return exif.Orientation
|
||||
}
|
||||
|
||||
|
|
11
internal/photoprism/metadata.go
Normal file
11
internal/photoprism/metadata.go
Normal file
|
@ -0,0 +1,11 @@
|
|||
package photoprism
|
||||
|
||||
import (
|
||||
"github.com/photoprism/photoprism/internal/meta"
|
||||
)
|
||||
|
||||
// MetaData returns exif meta data of a media file.
|
||||
func (m *MediaFile) MetaData() (result meta.Data, err error) {
|
||||
m.once.Do(func() { m.metaData, err = meta.Exif(m.Filename()) })
|
||||
return m.metaData, err
|
||||
}
|
|
@ -5,6 +5,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/meta"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
@ -16,11 +17,11 @@ func TestMediaFile_Exif_JPEG(t *testing.T) {
|
|||
|
||||
assert.Nil(t, err)
|
||||
|
||||
info, err := img.Exif()
|
||||
info, err := img.MetaData()
|
||||
|
||||
assert.Empty(t, err)
|
||||
|
||||
assert.IsType(t, &Exif{}, info)
|
||||
assert.IsType(t, meta.Data{}, info)
|
||||
|
||||
assert.Equal(t, "", info.UUID)
|
||||
assert.Equal(t, "2013-11-26 13:53:55 +0000 UTC", info.TakenAt.String())
|
||||
|
@ -40,8 +41,8 @@ func TestMediaFile_Exif_JPEG(t *testing.T) {
|
|||
assert.Equal(t, -33.45347, info.Lat)
|
||||
assert.Equal(t, 25.764645, info.Lng)
|
||||
assert.Equal(t, 190, info.Altitude)
|
||||
assert.Equal(t, 1365, info.Width)
|
||||
assert.Equal(t, 0, info.Height)
|
||||
assert.Equal(t, 2048, info.Width)
|
||||
assert.Equal(t, 1365, info.Height)
|
||||
assert.Equal(t, false, info.Flash)
|
||||
assert.Equal(t, "", info.Description)
|
||||
t.Logf("UTC: %s", info.TakenAt.String())
|
||||
|
@ -53,11 +54,11 @@ func TestMediaFile_Exif_JPEG(t *testing.T) {
|
|||
|
||||
assert.Nil(t, err)
|
||||
|
||||
info, err := img.Exif()
|
||||
info, err := img.MetaData()
|
||||
|
||||
assert.Empty(t, err)
|
||||
|
||||
assert.IsType(t, &Exif{}, info)
|
||||
assert.IsType(t, meta.Data{}, info)
|
||||
|
||||
assert.Equal(t, "", info.UUID)
|
||||
assert.Equal(t, 1, info.Orientation)
|
||||
|
@ -74,7 +75,7 @@ func TestMediaFile_Exif_JPEG(t *testing.T) {
|
|||
assert.Equal(t, 200, info.Iso)
|
||||
assert.Equal(t, 0, info.Altitude)
|
||||
assert.Equal(t, 2048, info.Width)
|
||||
assert.Equal(t, 0, info.Height)
|
||||
assert.Equal(t, 2048, info.Height)
|
||||
assert.Equal(t, true, info.Flash)
|
||||
assert.Equal(t, "", info.Description)
|
||||
t.Logf("UTC: %s", info.TakenAt.String())
|
||||
|
@ -93,11 +94,11 @@ func TestMediaFile_Exif_DNG(t *testing.T) {
|
|||
|
||||
assert.Nil(t, err)
|
||||
|
||||
info, err := img.Exif()
|
||||
info, err := img.MetaData()
|
||||
|
||||
assert.Empty(t, err)
|
||||
|
||||
assert.IsType(t, &Exif{}, info)
|
||||
assert.IsType(t, meta.Data{}, info)
|
||||
|
||||
assert.Equal(t, "", info.UUID)
|
||||
assert.Equal(t, "2019-06-06 07:29:51 +0000 UTC", info.TakenAt.String())
|
||||
|
@ -114,8 +115,8 @@ func TestMediaFile_Exif_DNG(t *testing.T) {
|
|||
assert.Equal(t, 0.0, info.Lat)
|
||||
assert.Equal(t, 0.0, info.Lng)
|
||||
assert.Equal(t, 0, info.Altitude)
|
||||
assert.Equal(t, 171, info.Width)
|
||||
assert.Equal(t, 0, info.Height)
|
||||
assert.Equal(t, 256, info.Width)
|
||||
assert.Equal(t, 171, info.Height)
|
||||
assert.Equal(t, false, info.Flash)
|
||||
assert.Equal(t, "", info.Description)
|
||||
}
|
||||
|
@ -131,9 +132,9 @@ func TestMediaFile_Exif_HEIF(t *testing.T) {
|
|||
|
||||
assert.Nil(t, err)
|
||||
|
||||
info, err := img.Exif()
|
||||
info, err := img.MetaData()
|
||||
|
||||
assert.IsType(t, &Exif{}, info)
|
||||
assert.IsType(t, meta.Data{}, info)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
|
@ -143,9 +144,9 @@ func TestMediaFile_Exif_HEIF(t *testing.T) {
|
|||
|
||||
assert.Nil(t, err)
|
||||
|
||||
jpegInfo, err := jpeg.Exif()
|
||||
jpegInfo, err := jpeg.MetaData()
|
||||
|
||||
assert.IsType(t, &Exif{}, jpegInfo)
|
||||
assert.IsType(t, meta.Data{}, jpegInfo)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
|
@ -2,29 +2,15 @@ package photoprism
|
|||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"gopkg.in/ugjka/go-tz.v2/tz"
|
||||
)
|
||||
|
||||
// TimeZone returns the time zone where the photo was taken.
|
||||
func (m *MediaFile) TimeZone() (string, error) {
|
||||
meta, err := m.Exif()
|
||||
meta, err := m.MetaData()
|
||||
|
||||
if err != nil {
|
||||
return "UTC", errors.New("no image metadata")
|
||||
return "UTC", errors.New("file: unknown time zone, using UTC")
|
||||
}
|
||||
|
||||
if meta.Lat == 0 && meta.Lng == 0 {
|
||||
return "UTC", errors.New("no latitude and longitude in image metadata")
|
||||
}
|
||||
|
||||
zones, err := tz.GetZone(tz.Point{
|
||||
Lon: meta.Lng, Lat: meta.Lat,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return "UTC", errors.New("no matching zone found")
|
||||
}
|
||||
|
||||
return zones[0], nil
|
||||
return meta.TimeZone, nil
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue