Skip to content
Snippets Groups Projects
Commit bd15a896 authored by Bruno Michel's avatar Bruno Michel
Browse files

Try to improve photo metadata

The EXIF datetime from a photo has no timezone. We have reused some code
from perkeep to try harder to detect the correct datetime for photos.
parent 50ca10a3
Branches
Tags
No related merge requests found
...@@ -5,6 +5,7 @@ go 1.12 ...@@ -5,6 +5,7 @@ go 1.12
require ( require (
github.com/Masterminds/semver v1.5.0 github.com/Masterminds/semver v1.5.0
github.com/appleboy/go-fcm v0.1.4 github.com/appleboy/go-fcm v0.1.4
github.com/bradfitz/latlong v0.0.0-20170410180902-f3db6d0dff40
github.com/cozy/goexif2 v0.0.0-20180125141006-830968571cff github.com/cozy/goexif2 v0.0.0-20180125141006-830968571cff
github.com/cozy/gomail v0.0.0-20170313100128-1395d9a6a6c0 github.com/cozy/gomail v0.0.0-20170313100128-1395d9a6a6c0
github.com/cozy/httpcache v0.0.0-20180914105234-d3dc4988de66 github.com/cozy/httpcache v0.0.0-20180914105234-d3dc4988de66
......
...@@ -18,6 +18,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= ...@@ -18,6 +18,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/bradfitz/latlong v0.0.0-20170410180902-f3db6d0dff40 h1:wsnz4B2CSHJ09pwtMReU/GRqWDsI7XSasq7Nphem3Xk=
github.com/bradfitz/latlong v0.0.0-20170410180902-f3db6d0dff40/go.mod h1:ZcXX9BndVQx6Q/JM6B8x7dLE9sl20S+TQsv4KO7tEQk=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
......
...@@ -2,11 +2,14 @@ package vfs ...@@ -2,11 +2,14 @@ package vfs
import ( import (
"bytes" "bytes"
"errors"
"fmt" "fmt"
"image" "image"
"io" "io"
"io/ioutil" "io/ioutil"
"math" "math"
"strings"
"sync"
"time" "time"
// Packages image/... are not used explicitly in the code below, // Packages image/... are not used explicitly in the code below,
...@@ -18,7 +21,9 @@ import ( ...@@ -18,7 +21,9 @@ import (
// Same for image/webp // Same for image/webp
_ "golang.org/x/image/webp" _ "golang.org/x/image/webp"
"github.com/bradfitz/latlong"
"github.com/cozy/goexif2/exif" "github.com/cozy/goexif2/exif"
"github.com/cozy/goexif2/tiff"
"github.com/dhowden/tag" "github.com/dhowden/tag"
) )
...@@ -222,8 +227,10 @@ func (e *ExifExtractor) Result() Metadata { ...@@ -222,8 +227,10 @@ func (e *ExifExtractor) Result() Metadata {
x := <-e.ch x := <-e.ch
switch x := x.(type) { switch x := x.(type) {
case *exif.Exif: case *exif.Exif:
localTZ := false
if dt, err := x.DateTime(); err == nil { if dt, err := x.DateTime(); err == nil {
m["datetime"] = dt m["datetime"] = dt
localTZ = dt.Location() == time.Local
} }
if flash, err := x.Flash(); err == nil { if flash, err := x.Flash(); err == nil {
m["flash"] = flash m["flash"] = flash
...@@ -234,6 +241,13 @@ func (e *ExifExtractor) Result() Metadata { ...@@ -234,6 +241,13 @@ func (e *ExifExtractor) Result() Metadata {
"lat": lat, "lat": lat,
"long": long, "long": long,
} }
if localTZ {
if loc := lookupLocation(latlong.LookupZoneName(lat, long)); loc != nil {
if t, err := exifDateTimeInLocation(x, loc); err == nil {
m["datetime"] = t
}
}
}
} }
} }
if _, ok := m["width"]; !ok { if _, ok := m["width"]; !ok {
...@@ -259,6 +273,57 @@ func (e *ExifExtractor) Result() Metadata { ...@@ -259,6 +273,57 @@ func (e *ExifExtractor) Result() Metadata {
return m return m
} }
// Code taken from perkeep
// https://github.com/perkeep/perkeep/blob/7f17c0483f2e86575ed87aac35fb75154b16b7f4/pkg/schema/schema.go#L1043-L1094
// This is basically a copy of the exif.Exif.DateTime() method, except:
// * it takes a *time.Location to assume
// * the caller already assumes there's no timezone offset or GPS time
// in the EXIF, so any of that code can be ignored.
func exifDateTimeInLocation(x *exif.Exif, loc *time.Location) (time.Time, error) {
tag, err := x.Get(exif.DateTimeOriginal)
if err != nil {
tag, err = x.Get(exif.DateTime)
if err != nil {
return time.Time{}, err
}
}
if tag.Format() != tiff.StringVal {
return time.Time{}, errors.New("DateTime[Original] not in string format")
}
const exifTimeLayout = "2006:01:02 15:04:05"
dateStr := strings.TrimRight(string(tag.Val), "\x00")
return time.ParseInLocation(exifTimeLayout, dateStr, loc)
}
var zoneCache struct {
sync.RWMutex
m map[string]*time.Location
}
func lookupLocation(zone string) *time.Location {
if zone == "" {
return nil
}
zoneCache.RLock()
l, ok := zoneCache.m[zone]
zoneCache.RUnlock()
if ok {
return l
}
loc, err := time.LoadLocation(zone)
zoneCache.Lock()
if zoneCache.m == nil {
zoneCache.m = make(map[string]*time.Location)
}
zoneCache.m[zone] = loc // even if nil
zoneCache.Unlock()
if err != nil {
return nil
}
return loc
}
// AudioExtractor is used to extract album/artist/etc. from audio // AudioExtractor is used to extract album/artist/etc. from audio
type AudioExtractor struct { type AudioExtractor struct {
w *io.PipeWriter w *io.PipeWriter
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment