Metadata: Use mime type to determine file format and exif parser #391
Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
parent
0023fdb1e2
commit
138dabd0c8
9
go.mod
9
go.mod
|
@ -6,14 +6,15 @@ require (
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
||||||
github.com/disintegration/imaging v1.6.2
|
github.com/disintegration/imaging v1.6.2
|
||||||
github.com/djherbis/times v1.2.0
|
github.com/djherbis/times v1.2.0
|
||||||
github.com/dsoprea/go-exif/v2 v2.0.0-20200717063959-46b1a0cd1772 // indirect
|
github.com/dsoprea/go-exif/v2 v2.0.0-20200717071058-9393e7afd446 // indirect
|
||||||
github.com/dsoprea/go-exif/v3 v3.0.0-20200717063959-46b1a0cd1772
|
github.com/dsoprea/go-exif/v3 v3.0.0-20200717071058-9393e7afd446
|
||||||
github.com/dsoprea/go-heic-exif-extractor v0.0.0-20200520190950-3ae4ff88a0d1
|
github.com/dsoprea/go-heic-exif-extractor v0.0.0-20200717090456-b3d9dcddffd1
|
||||||
github.com/dsoprea/go-iptc v0.0.0-20200610044640-bc9ca208b413 // indirect
|
github.com/dsoprea/go-iptc v0.0.0-20200610044640-bc9ca208b413 // indirect
|
||||||
github.com/dsoprea/go-jpeg-image-structure v0.0.0-20200615034914-d40a386309d2
|
github.com/dsoprea/go-jpeg-image-structure v0.0.0-20200717085400-dd2ba56ee6b8
|
||||||
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd // indirect
|
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd // indirect
|
||||||
github.com/dsoprea/go-photoshop-info-format v0.0.0-20200610045659-121dd752914d // indirect
|
github.com/dsoprea/go-photoshop-info-format v0.0.0-20200610045659-121dd752914d // indirect
|
||||||
github.com/dsoprea/go-png-image-structure v0.0.0-20200615034826-4cfc78940228
|
github.com/dsoprea/go-png-image-structure v0.0.0-20200615034826-4cfc78940228
|
||||||
|
github.com/dsoprea/go-tiff-image-structure v0.0.0-20200717073440-8ac81ec8b423
|
||||||
github.com/dsoprea/go-utility v0.0.0-20200717064901-2fccff4aa15e // indirect
|
github.com/dsoprea/go-utility v0.0.0-20200717064901-2fccff4aa15e // indirect
|
||||||
github.com/dustin/go-humanize v1.0.0
|
github.com/dustin/go-humanize v1.0.0
|
||||||
github.com/gin-gonic/gin v1.6.3
|
github.com/gin-gonic/gin v1.6.3
|
||||||
|
|
13
go.sum
13
go.sum
|
@ -48,16 +48,25 @@ github.com/dsoprea/go-exif/v2 v2.0.0-20200520183328-015129a9efd5/go.mod h1:9EXlP
|
||||||
github.com/dsoprea/go-exif/v2 v2.0.0-20200604193436-ca8584a0e1c4/go.mod h1:9EXlPeHfblFFnwu5UOqmP2eoZfJyAZ2Ri/Vki33ajO0=
|
github.com/dsoprea/go-exif/v2 v2.0.0-20200604193436-ca8584a0e1c4/go.mod h1:9EXlPeHfblFFnwu5UOqmP2eoZfJyAZ2Ri/Vki33ajO0=
|
||||||
github.com/dsoprea/go-exif/v2 v2.0.0-20200717063959-46b1a0cd1772 h1:M49UNOTa5sLju107lAoMsm93B/fHD02vWIoskmXMBm8=
|
github.com/dsoprea/go-exif/v2 v2.0.0-20200717063959-46b1a0cd1772 h1:M49UNOTa5sLju107lAoMsm93B/fHD02vWIoskmXMBm8=
|
||||||
github.com/dsoprea/go-exif/v2 v2.0.0-20200717063959-46b1a0cd1772/go.mod h1:oKrjk2kb3rAR5NbtSTLUMvMSbc+k8ZosI3MaVH47noc=
|
github.com/dsoprea/go-exif/v2 v2.0.0-20200717063959-46b1a0cd1772/go.mod h1:oKrjk2kb3rAR5NbtSTLUMvMSbc+k8ZosI3MaVH47noc=
|
||||||
|
github.com/dsoprea/go-exif/v2 v2.0.0-20200717071058-9393e7afd446 h1:ruDG+2wFz+k/mDNy8x1UqWEItWNLXpvGlLv05+TlZt4=
|
||||||
|
github.com/dsoprea/go-exif/v2 v2.0.0-20200717071058-9393e7afd446/go.mod h1:oKrjk2kb3rAR5NbtSTLUMvMSbc+k8ZosI3MaVH47noc=
|
||||||
|
github.com/dsoprea/go-exif/v3 v3.0.0-20200717053412-08f1b6708903/go.mod h1:0nsO1ce0mh5czxGeLo4+OCZ/C6Eo6ZlMWsz7rH/Gxv8=
|
||||||
github.com/dsoprea/go-exif/v3 v3.0.0-20200717063959-46b1a0cd1772 h1:l/wfrK3wEH7sYpJe+Y8ZdFJW3AmsDgPoAQq2RLgKPSQ=
|
github.com/dsoprea/go-exif/v3 v3.0.0-20200717063959-46b1a0cd1772 h1:l/wfrK3wEH7sYpJe+Y8ZdFJW3AmsDgPoAQq2RLgKPSQ=
|
||||||
github.com/dsoprea/go-exif/v3 v3.0.0-20200717063959-46b1a0cd1772/go.mod h1:0nsO1ce0mh5czxGeLo4+OCZ/C6Eo6ZlMWsz7rH/Gxv8=
|
github.com/dsoprea/go-exif/v3 v3.0.0-20200717063959-46b1a0cd1772/go.mod h1:0nsO1ce0mh5czxGeLo4+OCZ/C6Eo6ZlMWsz7rH/Gxv8=
|
||||||
|
github.com/dsoprea/go-exif/v3 v3.0.0-20200717071058-9393e7afd446 h1:96yylb+JH415u6V7ykNtnEBLaZUwS1S31TnAezcvnNE=
|
||||||
|
github.com/dsoprea/go-exif/v3 v3.0.0-20200717071058-9393e7afd446/go.mod h1:cg5SNYKHMmzxsr9X6ZeLh/nfBRHHp5PngtEPcujONtk=
|
||||||
github.com/dsoprea/go-heic-exif-extractor v0.0.0-20200520190950-3ae4ff88a0d1 h1:8Tbo+OYgg7i2G3fltmpWq1if1e752aMX7Zv/sNWWJUk=
|
github.com/dsoprea/go-heic-exif-extractor v0.0.0-20200520190950-3ae4ff88a0d1 h1:8Tbo+OYgg7i2G3fltmpWq1if1e752aMX7Zv/sNWWJUk=
|
||||||
github.com/dsoprea/go-heic-exif-extractor v0.0.0-20200520190950-3ae4ff88a0d1/go.mod h1:UwRKreeVikXn5OarSnt4OqovcEjsIgZVuc5svj7G5w4=
|
github.com/dsoprea/go-heic-exif-extractor v0.0.0-20200520190950-3ae4ff88a0d1/go.mod h1:UwRKreeVikXn5OarSnt4OqovcEjsIgZVuc5svj7G5w4=
|
||||||
|
github.com/dsoprea/go-heic-exif-extractor v0.0.0-20200717090456-b3d9dcddffd1 h1:R/EEzpxqQxeEcJ/z0EFTI1U6XsuOnepyp5o1uZg5c2E=
|
||||||
|
github.com/dsoprea/go-heic-exif-extractor v0.0.0-20200717090456-b3d9dcddffd1/go.mod h1:UwRKreeVikXn5OarSnt4OqovcEjsIgZVuc5svj7G5w4=
|
||||||
github.com/dsoprea/go-iptc v0.0.0-20200609062250-162ae6b44feb h1:gwjJjUr6FY7zAWVEueFPrcRHhd9+IK81TcItbqw2du4=
|
github.com/dsoprea/go-iptc v0.0.0-20200609062250-162ae6b44feb h1:gwjJjUr6FY7zAWVEueFPrcRHhd9+IK81TcItbqw2du4=
|
||||||
github.com/dsoprea/go-iptc v0.0.0-20200609062250-162ae6b44feb/go.mod h1:kYIdx9N9NaOyD7U6D+YtExN7QhRm+5kq7//yOsRXQtM=
|
github.com/dsoprea/go-iptc v0.0.0-20200609062250-162ae6b44feb/go.mod h1:kYIdx9N9NaOyD7U6D+YtExN7QhRm+5kq7//yOsRXQtM=
|
||||||
github.com/dsoprea/go-iptc v0.0.0-20200610044640-bc9ca208b413 h1:YDRiMEm32T60Kpm35YzOK9ZHgjsS1Qrid+XskNcsdp8=
|
github.com/dsoprea/go-iptc v0.0.0-20200610044640-bc9ca208b413 h1:YDRiMEm32T60Kpm35YzOK9ZHgjsS1Qrid+XskNcsdp8=
|
||||||
github.com/dsoprea/go-iptc v0.0.0-20200610044640-bc9ca208b413/go.mod h1:kYIdx9N9NaOyD7U6D+YtExN7QhRm+5kq7//yOsRXQtM=
|
github.com/dsoprea/go-iptc v0.0.0-20200610044640-bc9ca208b413/go.mod h1:kYIdx9N9NaOyD7U6D+YtExN7QhRm+5kq7//yOsRXQtM=
|
||||||
github.com/dsoprea/go-jpeg-image-structure v0.0.0-20200615034914-d40a386309d2 h1:8HmMqu64P4ZDGtcVwZDfmS4xuLXYjf2iery8teY7d9c=
|
github.com/dsoprea/go-jpeg-image-structure v0.0.0-20200615034914-d40a386309d2 h1:8HmMqu64P4ZDGtcVwZDfmS4xuLXYjf2iery8teY7d9c=
|
||||||
github.com/dsoprea/go-jpeg-image-structure v0.0.0-20200615034914-d40a386309d2/go.mod h1:ZoOP3yUG0HD1T4IUjIFsz/2OAB2yB4YX6NSm4K+uJRg=
|
github.com/dsoprea/go-jpeg-image-structure v0.0.0-20200615034914-d40a386309d2/go.mod h1:ZoOP3yUG0HD1T4IUjIFsz/2OAB2yB4YX6NSm4K+uJRg=
|
||||||
|
github.com/dsoprea/go-jpeg-image-structure v0.0.0-20200717085400-dd2ba56ee6b8 h1:cXCR9FOOkTEZ3t+asmy3lLv2AKYAah2igfx7WnNnVMc=
|
||||||
|
github.com/dsoprea/go-jpeg-image-structure v0.0.0-20200717085400-dd2ba56ee6b8/go.mod h1:ZoOP3yUG0HD1T4IUjIFsz/2OAB2yB4YX6NSm4K+uJRg=
|
||||||
github.com/dsoprea/go-logging v0.0.0-20190624164917-c4f10aab7696 h1:VGFnZAcLwPpt1sHlAxml+pGLZz9A2s+K/s1YNhPC91Y=
|
github.com/dsoprea/go-logging v0.0.0-20190624164917-c4f10aab7696 h1:VGFnZAcLwPpt1sHlAxml+pGLZz9A2s+K/s1YNhPC91Y=
|
||||||
github.com/dsoprea/go-logging v0.0.0-20190624164917-c4f10aab7696/go.mod h1:Nm/x2ZUNRW6Fe5C3LxdY1PyZY5wmDv/s5dkPJ/VB3iA=
|
github.com/dsoprea/go-logging v0.0.0-20190624164917-c4f10aab7696/go.mod h1:Nm/x2ZUNRW6Fe5C3LxdY1PyZY5wmDv/s5dkPJ/VB3iA=
|
||||||
github.com/dsoprea/go-logging v0.0.0-20200517223158-a10564966e9d h1:F/7L5wr/fP/SKeO5HuMlNEX9Ipyx2MbH2rV9G4zJRpk=
|
github.com/dsoprea/go-logging v0.0.0-20200517223158-a10564966e9d h1:F/7L5wr/fP/SKeO5HuMlNEX9Ipyx2MbH2rV9G4zJRpk=
|
||||||
|
@ -70,12 +79,16 @@ github.com/dsoprea/go-photoshop-info-format v0.0.0-20200610045659-121dd752914d h
|
||||||
github.com/dsoprea/go-photoshop-info-format v0.0.0-20200610045659-121dd752914d/go.mod h1:pqKB+ijp27cEcrHxhXVgUUMlSDRuGJJp1E+20Lj5H0E=
|
github.com/dsoprea/go-photoshop-info-format v0.0.0-20200610045659-121dd752914d/go.mod h1:pqKB+ijp27cEcrHxhXVgUUMlSDRuGJJp1E+20Lj5H0E=
|
||||||
github.com/dsoprea/go-png-image-structure v0.0.0-20200615034826-4cfc78940228 h1:GKAdOrszPH3mQ44eRg2kw9zBW0hi2L78ZNjkTx+cte0=
|
github.com/dsoprea/go-png-image-structure v0.0.0-20200615034826-4cfc78940228 h1:GKAdOrszPH3mQ44eRg2kw9zBW0hi2L78ZNjkTx+cte0=
|
||||||
github.com/dsoprea/go-png-image-structure v0.0.0-20200615034826-4cfc78940228/go.mod h1:aDYQkL/5gfRNZkoxiLTSWU4Y8/gV/4MVsy/MU9uwTak=
|
github.com/dsoprea/go-png-image-structure v0.0.0-20200615034826-4cfc78940228/go.mod h1:aDYQkL/5gfRNZkoxiLTSWU4Y8/gV/4MVsy/MU9uwTak=
|
||||||
|
github.com/dsoprea/go-tiff-image-structure v0.0.0-20200717073440-8ac81ec8b423 h1:aIXEGtyKFKqeNW2rc4cx3J2TLxQ9F5fwWPSbq6p6Fq8=
|
||||||
|
github.com/dsoprea/go-tiff-image-structure v0.0.0-20200717073440-8ac81ec8b423/go.mod h1:we+M+yrq8ifsA33a7C7p8E1ztBbdDYjMIC8RMm8KPL8=
|
||||||
github.com/dsoprea/go-utility v0.0.0-20200512094054-1abbbc781176 h1:CfXezFYb2STGOd1+n1HshvE191zVx+QX3A1nML5xxME=
|
github.com/dsoprea/go-utility v0.0.0-20200512094054-1abbbc781176 h1:CfXezFYb2STGOd1+n1HshvE191zVx+QX3A1nML5xxME=
|
||||||
github.com/dsoprea/go-utility v0.0.0-20200512094054-1abbbc781176/go.mod h1:95+K3z2L0mqsVYd6yveIv1lmtT3tcQQ3dVakPySffW8=
|
github.com/dsoprea/go-utility v0.0.0-20200512094054-1abbbc781176/go.mod h1:95+K3z2L0mqsVYd6yveIv1lmtT3tcQQ3dVakPySffW8=
|
||||||
github.com/dsoprea/go-utility v0.0.0-20200711062821-fab8125e9bdf h1:/w4QxepU4AHh3AuO6/g8y/YIIHH5+aKP3Bj8sg5cqhU=
|
github.com/dsoprea/go-utility v0.0.0-20200711062821-fab8125e9bdf h1:/w4QxepU4AHh3AuO6/g8y/YIIHH5+aKP3Bj8sg5cqhU=
|
||||||
github.com/dsoprea/go-utility v0.0.0-20200711062821-fab8125e9bdf/go.mod h1:95+K3z2L0mqsVYd6yveIv1lmtT3tcQQ3dVakPySffW8=
|
github.com/dsoprea/go-utility v0.0.0-20200711062821-fab8125e9bdf/go.mod h1:95+K3z2L0mqsVYd6yveIv1lmtT3tcQQ3dVakPySffW8=
|
||||||
github.com/dsoprea/go-utility v0.0.0-20200717064901-2fccff4aa15e h1:ojqYA1mU6LuRm8XzrVOvyfb000y59cbUcu6Wt8sFSAs=
|
github.com/dsoprea/go-utility v0.0.0-20200717064901-2fccff4aa15e h1:ojqYA1mU6LuRm8XzrVOvyfb000y59cbUcu6Wt8sFSAs=
|
||||||
github.com/dsoprea/go-utility v0.0.0-20200717064901-2fccff4aa15e/go.mod h1:KVK+/Hul09ujXAGq+42UBgCTnXkiJZRnLYdURGjQUwo=
|
github.com/dsoprea/go-utility v0.0.0-20200717064901-2fccff4aa15e/go.mod h1:KVK+/Hul09ujXAGq+42UBgCTnXkiJZRnLYdURGjQUwo=
|
||||||
|
github.com/dsoprea/go-utility/v2 v2.0.0-20200717064901-2fccff4aa15e h1:IxIbA7VbCNrwumIYjDoMOdf4KOSkMC6NJE4s8oRbE7E=
|
||||||
|
github.com/dsoprea/go-utility/v2 v2.0.0-20200717064901-2fccff4aa15e/go.mod h1:uAzdkPTub5Y9yQwXe8W4m2XuP0tK4a9Q/dantD0+uaU=
|
||||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
|
|
@ -3,7 +3,6 @@ package meta
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"path"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -15,6 +14,8 @@ import (
|
||||||
heicexif "github.com/dsoprea/go-heic-exif-extractor"
|
heicexif "github.com/dsoprea/go-heic-exif-extractor"
|
||||||
"github.com/dsoprea/go-jpeg-image-structure"
|
"github.com/dsoprea/go-jpeg-image-structure"
|
||||||
"github.com/dsoprea/go-png-image-structure"
|
"github.com/dsoprea/go-png-image-structure"
|
||||||
|
"github.com/dsoprea/go-tiff-image-structure"
|
||||||
|
"github.com/photoprism/photoprism/pkg/fs"
|
||||||
"github.com/photoprism/photoprism/pkg/txt"
|
"github.com/photoprism/photoprism/pkg/txt"
|
||||||
"gopkg.in/ugjka/go-tz.v2/tz"
|
"gopkg.in/ugjka/go-tz.v2/tz"
|
||||||
)
|
)
|
||||||
|
@ -38,14 +39,14 @@ func ValidDateTime(s string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exif parses an image file for Exif meta data and returns as Data struct.
|
// Exif parses an image file for Exif meta data and returns as Data struct.
|
||||||
func Exif(fileName string) (data Data, err error) {
|
func Exif(fileName string, fileType fs.FileType) (data Data, err error) {
|
||||||
err = data.Exif(fileName)
|
err = data.Exif(fileName, fileType)
|
||||||
|
|
||||||
return data, err
|
return data, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exif parses an image file for Exif meta data and returns as Data struct.
|
// Exif parses an image file for Exif meta data and returns as Data struct.
|
||||||
func (data *Data) Exif(fileName string) (err error) {
|
func (data *Data) Exif(fileName string, fileType fs.FileType) (err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if e := recover(); e != nil {
|
if e := recover(); e != nil {
|
||||||
err = fmt.Errorf("metadata: %s in %s (exif panic)\nstack: %s", e, txt.Quote(filepath.Base(fileName)), debug.Stack())
|
err = fmt.Errorf("metadata: %s in %s (exif panic)\nstack: %s", e, txt.Quote(filepath.Base(fileName)), debug.Stack())
|
||||||
|
@ -57,12 +58,11 @@ func (data *Data) Exif(fileName string) (err error) {
|
||||||
var parsed bool
|
var parsed bool
|
||||||
|
|
||||||
logName := txt.Quote(filepath.Base(fileName))
|
logName := txt.Quote(filepath.Base(fileName))
|
||||||
ext := strings.ToLower(path.Ext(fileName))
|
|
||||||
|
|
||||||
if ext == ".jpg" || ext == ".jpeg" {
|
if fileType == fs.TypeJpeg {
|
||||||
jmp := jpegstructure.NewJpegMediaParser()
|
jpegMp := jpegstructure.NewJpegMediaParser()
|
||||||
|
|
||||||
sl, err := jmp.ParseFile(fileName)
|
sl, err := jpegMp.ParseFile(fileName)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -81,10 +81,10 @@ func (data *Data) Exif(fileName string) (err error) {
|
||||||
} else {
|
} else {
|
||||||
parsed = true
|
parsed = true
|
||||||
}
|
}
|
||||||
} else if ext == ".png" {
|
} else if fileType == fs.TypePng {
|
||||||
pmp := pngstructure.NewPngMediaParser()
|
pngMp := pngstructure.NewPngMediaParser()
|
||||||
|
|
||||||
cs, err := pmp.ParseFile(fileName)
|
cs, err := pngMp.ParseFile(fileName)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -101,10 +101,10 @@ func (data *Data) Exif(fileName string) (err error) {
|
||||||
} else {
|
} else {
|
||||||
parsed = true
|
parsed = true
|
||||||
}
|
}
|
||||||
} else if ext == ".heic" {
|
} else if fileType == fs.TypeHEIF {
|
||||||
hmp := heicexif.NewHeicExifMediaParser()
|
heicMp := heicexif.NewHeicExifMediaParser()
|
||||||
|
|
||||||
cs, err := hmp.ParseFile(fileName)
|
cs, err := heicMp.ParseFile(fileName)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -121,6 +121,26 @@ func (data *Data) Exif(fileName string) (err error) {
|
||||||
} else {
|
} else {
|
||||||
parsed = true
|
parsed = true
|
||||||
}
|
}
|
||||||
|
} else if fileType == fs.TypeTiff {
|
||||||
|
tiffMp := tiffstructure.NewTiffMediaParser()
|
||||||
|
|
||||||
|
cs, err := tiffMp.ParseFile(fileName)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, rawExif, err = cs.Exif()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if err.Error() == "file does not have EXIF" {
|
||||||
|
return fmt.Errorf("metadata: no exif header in %s (parse tiff)", logName)
|
||||||
|
} else {
|
||||||
|
log.Warnf("metadata: %s in %s (parse tiff)", err, logName)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parsed = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !parsed {
|
if !parsed {
|
||||||
|
|
|
@ -3,12 +3,13 @@ package meta
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/photoprism/photoprism/pkg/fs"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestExif(t *testing.T) {
|
func TestExif(t *testing.T) {
|
||||||
t.Run("photoshop.jpg", func(t *testing.T) {
|
t.Run("photoshop.jpg", func(t *testing.T) {
|
||||||
data, err := Exif("testdata/photoshop.jpg")
|
data, err := Exif("testdata/photoshop.jpg", fs.TypeJpeg)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -37,7 +38,7 @@ func TestExif(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("ladybug.jpg", func(t *testing.T) {
|
t.Run("ladybug.jpg", func(t *testing.T) {
|
||||||
data, err := Exif("testdata/ladybug.jpg")
|
data, err := Exif("testdata/ladybug.jpg", fs.TypeJpeg)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -69,7 +70,7 @@ func TestExif(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("gopro_hd2.jpg", func(t *testing.T) {
|
t.Run("gopro_hd2.jpg", func(t *testing.T) {
|
||||||
data, err := Exif("testdata/gopro_hd2.jpg")
|
data, err := Exif("testdata/gopro_hd2.jpg", fs.TypeJpeg)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -98,7 +99,7 @@ func TestExif(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("tweethog.png", func(t *testing.T) {
|
t.Run("tweethog.png", func(t *testing.T) {
|
||||||
_, err := Exif("testdata/tweethog.png")
|
_, err := Exif("testdata/tweethog.png", fs.TypePng)
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("err should NOT be nil")
|
t.Fatal("err should NOT be nil")
|
||||||
|
@ -108,7 +109,7 @@ func TestExif(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("iphone_7.heic", func(t *testing.T) {
|
t.Run("iphone_7.heic", func(t *testing.T) {
|
||||||
data, err := Exif("testdata/iphone_7.heic")
|
data, err := Exif("testdata/iphone_7.heic", fs.TypeHEIF)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -129,7 +130,7 @@ func TestExif(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("gps-2000.jpg", func(t *testing.T) {
|
t.Run("gps-2000.jpg", func(t *testing.T) {
|
||||||
data, err := Exif("testdata/gps-2000.jpg")
|
data, err := Exif("testdata/gps-2000.jpg", fs.TypeJpeg)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -157,7 +158,7 @@ func TestExif(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("image-2011.jpg", func(t *testing.T) {
|
t.Run("image-2011.jpg", func(t *testing.T) {
|
||||||
data, err := Exif("testdata/image-2011.jpg")
|
data, err := Exif("testdata/image-2011.jpg", fs.TypeJpeg)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -192,7 +193,7 @@ func TestExif(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("ship.jpg", func(t *testing.T) {
|
t.Run("ship.jpg", func(t *testing.T) {
|
||||||
data, err := Exif("testdata/ship.jpg")
|
data, err := Exif("testdata/ship.jpg", fs.TypeJpeg)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -213,7 +214,7 @@ func TestExif(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("no-exif-data.jpg", func(t *testing.T) {
|
t.Run("no-exif-data.jpg", func(t *testing.T) {
|
||||||
_, err := Exif("testdata/no-exif-data.jpg")
|
_, err := Exif("testdata/no-exif-data.jpg", fs.TypeJpeg)
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("err should NOT be nil")
|
t.Fatal("err should NOT be nil")
|
||||||
|
@ -223,7 +224,7 @@ func TestExif(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("screenshot.png", func(t *testing.T) {
|
t.Run("screenshot.png", func(t *testing.T) {
|
||||||
data, err := Exif("testdata/screenshot.png")
|
data, err := Exif("testdata/screenshot.png", fs.TypePng)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -234,7 +235,7 @@ func TestExif(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("orientation.jpg", func(t *testing.T) {
|
t.Run("orientation.jpg", func(t *testing.T) {
|
||||||
data, err := Exif("testdata/orientation.jpg")
|
data, err := Exif("testdata/orientation.jpg", fs.TypeJpeg)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -262,13 +263,13 @@ func TestExif(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("gopher-preview.jpg", func(t *testing.T) {
|
t.Run("gopher-preview.jpg", func(t *testing.T) {
|
||||||
_, err := Exif("testdata/gopher-preview.jpg")
|
_, err := Exif("testdata/gopher-preview.jpg", fs.TypeJpeg)
|
||||||
|
|
||||||
assert.EqualError(t, err, "metadata: no exif header in gopher-preview.jpg (search and extract)")
|
assert.EqualError(t, err, "metadata: no exif header in gopher-preview.jpg (search and extract)")
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("huawei-gps-error.jpg", func(t *testing.T) {
|
t.Run("huawei-gps-error.jpg", func(t *testing.T) {
|
||||||
data, err := Exif("testdata/huawei-gps-error.jpg")
|
data, err := Exif("testdata/huawei-gps-error.jpg", fs.TypeJpeg)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -289,7 +290,7 @@ func TestExif(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("panorama360.jpg", func(t *testing.T) {
|
t.Run("panorama360.jpg", func(t *testing.T) {
|
||||||
data, err := Exif("testdata/panorama360.jpg")
|
data, err := Exif("testdata/panorama360.jpg", fs.TypeJpeg)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -315,7 +316,38 @@ func TestExif(t *testing.T) {
|
||||||
assert.Equal(t, "", data.CameraOwner)
|
assert.Equal(t, "", data.CameraOwner)
|
||||||
assert.Equal(t, "", data.CameraSerial)
|
assert.Equal(t, "", data.CameraSerial)
|
||||||
assert.Equal(t, 6, data.FocalLength)
|
assert.Equal(t, 6, data.FocalLength)
|
||||||
assert.Equal(t, 0, int(data.Orientation))
|
assert.Equal(t, 0, data.Orientation)
|
||||||
|
assert.Equal(t, "", data.Projection)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("exif-example.tiff", func(t *testing.T) {
|
||||||
|
data, err := Exif("testdata/exif-example.tiff", fs.TypeTiff)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// t.Logf("all: %+v", data.All)
|
||||||
|
|
||||||
|
assert.Equal(t, "", data.Artist)
|
||||||
|
assert.Equal(t, "0001-01-01T00:00:00Z", data.TakenAt.Format("2006-01-02T15:04:05Z"))
|
||||||
|
assert.Equal(t, "0001-01-01T00:00:00Z", data.TakenAtLocal.Format("2006-01-02T15:04:05Z"))
|
||||||
|
assert.Equal(t, "", data.Title)
|
||||||
|
assert.Equal(t, "", data.Keywords)
|
||||||
|
assert.Equal(t, "", data.Description)
|
||||||
|
assert.Equal(t, "", data.Copyright)
|
||||||
|
assert.Equal(t, 43, data.Height)
|
||||||
|
assert.Equal(t, 65, data.Width)
|
||||||
|
assert.Equal(t, float32(0), data.Lat)
|
||||||
|
assert.Equal(t, float32(0), data.Lng)
|
||||||
|
assert.Equal(t, 0, data.Altitude)
|
||||||
|
assert.Equal(t, "", data.Exposure)
|
||||||
|
assert.Equal(t, "", data.CameraMake)
|
||||||
|
assert.Equal(t, "", data.CameraModel)
|
||||||
|
assert.Equal(t, "", data.CameraOwner)
|
||||||
|
assert.Equal(t, "", data.CameraSerial)
|
||||||
|
assert.Equal(t, 0, data.FocalLength)
|
||||||
|
assert.Equal(t, 1, data.Orientation)
|
||||||
assert.Equal(t, "", data.Projection)
|
assert.Equal(t, "", data.Projection)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
BIN
internal/meta/testdata/exif-example.tiff
vendored
Normal file
BIN
internal/meta/testdata/exif-example.tiff
vendored
Normal file
Binary file not shown.
|
@ -166,6 +166,10 @@ func (ind *Index) Start(opt IndexOptions) fs.Done {
|
||||||
var files MediaFiles
|
var files MediaFiles
|
||||||
|
|
||||||
for _, f := range related.Files {
|
for _, f := range related.Files {
|
||||||
|
if ind.files.Ignore(f.RelName(originalsPath), f.ModTime(), opt.Rescan) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if done[f.FileName()].Processed() {
|
if done[f.FileName()].Processed() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -173,7 +177,6 @@ func (ind *Index) Start(opt IndexOptions) fs.Done {
|
||||||
files = append(files, f)
|
files = append(files, f)
|
||||||
filesIndexed++
|
filesIndexed++
|
||||||
done[f.FileName()] = fs.Processed
|
done[f.FileName()] = fs.Processed
|
||||||
ind.files.Add(f.RelName(originalsPath), f.ModTime())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
filesIndexed++
|
filesIndexed++
|
||||||
|
|
|
@ -574,6 +574,21 @@ func (m *MediaFile) IsJpeg() bool {
|
||||||
return m.MimeType() == fs.MimeTypeJpeg
|
return m.MimeType() == fs.MimeTypeJpeg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsPng returns true if this is a PNG file.
|
||||||
|
func (m *MediaFile) IsPng() bool {
|
||||||
|
return m.MimeType() == fs.MimeTypePng
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsGif returns true if this is a GIF file.
|
||||||
|
func (m *MediaFile) IsGif() bool {
|
||||||
|
return m.MimeType() == fs.MimeTypeGif
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsBitmap returns true if this is a bitmap file.
|
||||||
|
func (m *MediaFile) IsBitmap() bool {
|
||||||
|
return m.MimeType() == fs.MimeTypeBitmap
|
||||||
|
}
|
||||||
|
|
||||||
// IsJson return true if this media file is a json sidecar file.
|
// IsJson return true if this media file is a json sidecar file.
|
||||||
func (m *MediaFile) IsJson() bool {
|
func (m *MediaFile) IsJson() bool {
|
||||||
return m.HasFileType(fs.TypeJson)
|
return m.HasFileType(fs.TypeJson)
|
||||||
|
@ -581,11 +596,18 @@ func (m *MediaFile) IsJson() bool {
|
||||||
|
|
||||||
// FileType returns the file type (jpg, gif, tiff,...).
|
// FileType returns the file type (jpg, gif, tiff,...).
|
||||||
func (m *MediaFile) FileType() fs.FileType {
|
func (m *MediaFile) FileType() fs.FileType {
|
||||||
if m.IsJpeg() {
|
switch {
|
||||||
|
case m.IsJpeg():
|
||||||
return fs.TypeJpeg
|
return fs.TypeJpeg
|
||||||
|
case m.IsPng():
|
||||||
|
return fs.TypePng
|
||||||
|
case m.IsGif():
|
||||||
|
return fs.TypeGif
|
||||||
|
case m.IsBitmap():
|
||||||
|
return fs.TypeBitmap
|
||||||
|
default:
|
||||||
|
return fs.GetFileType(m.fileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
return fs.GetFileType(m.fileName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MediaType returns the media type (video, image, raw, sidecar,...).
|
// MediaType returns the media type (video, image, raw, sidecar,...).
|
||||||
|
@ -607,11 +629,6 @@ func (m *MediaFile) IsRaw() bool {
|
||||||
return m.HasFileType(fs.TypeRaw)
|
return m.HasFileType(fs.TypeRaw)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsPng returns true if this is a PNG file.
|
|
||||||
func (m *MediaFile) IsPng() bool {
|
|
||||||
return m.HasFileType(fs.TypePng)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsTiff returns true if this is a TIFF file.
|
// IsTiff returns true if this is a TIFF file.
|
||||||
func (m *MediaFile) IsTiff() bool {
|
func (m *MediaFile) IsTiff() bool {
|
||||||
return m.HasFileType(fs.TypeTiff)
|
return m.HasFileType(fs.TypeTiff)
|
||||||
|
@ -619,14 +636,8 @@ func (m *MediaFile) IsTiff() bool {
|
||||||
|
|
||||||
// IsImageOther returns true if this is a PNG, GIF, BMP or TIFF file.
|
// IsImageOther returns true if this is a PNG, GIF, BMP or TIFF file.
|
||||||
func (m *MediaFile) IsImageOther() bool {
|
func (m *MediaFile) IsImageOther() bool {
|
||||||
switch m.FileType() {
|
switch {
|
||||||
case fs.TypeBitmap:
|
case m.IsPng(), m.IsGif(), m.IsTiff(), m.IsBitmap():
|
||||||
return true
|
|
||||||
case fs.TypeGif:
|
|
||||||
return true
|
|
||||||
case fs.TypePng:
|
|
||||||
return true
|
|
||||||
case fs.TypeTiff:
|
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
|
@ -663,6 +674,11 @@ func (m *MediaFile) IsPhoto() bool {
|
||||||
return m.IsJpeg() || m.IsRaw() || m.IsHEIF() || m.IsImageOther()
|
return m.IsJpeg() || m.IsRaw() || m.IsHEIF() || m.IsImageOther()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExifSupported returns true if parsing exif metadata is supported for the media file type.
|
||||||
|
func (m *MediaFile) ExifSupported() bool {
|
||||||
|
return m.IsJpeg() || m.IsRaw() || m.IsHEIF() || m.IsPng() || m.IsTiff()
|
||||||
|
}
|
||||||
|
|
||||||
// IsMedia returns true if this is a media file (photo or video, not sidecar or other).
|
// IsMedia returns true if this is a media file (photo or video, not sidecar or other).
|
||||||
func (m *MediaFile) IsMedia() bool {
|
func (m *MediaFile) IsMedia() bool {
|
||||||
return m.IsJpeg() || m.IsVideo() || m.IsRaw() || m.IsHEIF() || m.IsImageOther()
|
return m.IsJpeg() || m.IsVideo() || m.IsRaw() || m.IsHEIF() || m.IsImageOther()
|
||||||
|
@ -707,7 +723,7 @@ func (m *MediaFile) HasJson() bool {
|
||||||
|
|
||||||
func (m *MediaFile) decodeDimensions() error {
|
func (m *MediaFile) decodeDimensions() error {
|
||||||
if !m.IsMedia() {
|
if !m.IsMedia() {
|
||||||
return fmt.Errorf("not a photo: %s", m.FileName())
|
return fmt.Errorf("failed decoding dimensions for %s", txt.Quote(m.BaseName()))
|
||||||
}
|
}
|
||||||
|
|
||||||
var width, height int
|
var width, height int
|
||||||
|
@ -719,7 +735,7 @@ func (m *MediaFile) decodeDimensions() error {
|
||||||
height = data.Height
|
height = data.Height
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.IsJpeg() {
|
if m.IsJpeg() || m.IsPng() || m.IsGif() {
|
||||||
file, err := os.Open(m.FileName())
|
file, err := os.Open(m.FileName())
|
||||||
|
|
||||||
if err != nil || file == nil {
|
if err != nil || file == nil {
|
||||||
|
|
|
@ -1098,9 +1098,13 @@ func TestMediaFile_IsPng(t *testing.T) {
|
||||||
conf := config.TestConfig()
|
conf := config.TestConfig()
|
||||||
|
|
||||||
mediaFile, err := NewMediaFile(conf.ExamplesPath() + "/tweethog.png")
|
mediaFile, err := NewMediaFile(conf.ExamplesPath() + "/tweethog.png")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, fs.TypePng, mediaFile.FileType())
|
||||||
|
assert.Equal(t, "image/png", mediaFile.MimeType())
|
||||||
assert.Equal(t, true, mediaFile.IsPng())
|
assert.Equal(t, true, mediaFile.IsPng())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1113,6 +1117,8 @@ func TestMediaFile_IsTiff(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
assert.Equal(t, fs.TypeJson, mediaFile.FileType())
|
||||||
|
assert.Equal(t, "text/plain; charset=utf-8", mediaFile.MimeType())
|
||||||
assert.Equal(t, false, mediaFile.IsTiff())
|
assert.Equal(t, false, mediaFile.IsTiff())
|
||||||
})
|
})
|
||||||
t.Run("/purple.tiff", func(t *testing.T) {
|
t.Run("/purple.tiff", func(t *testing.T) {
|
||||||
|
@ -1122,6 +1128,19 @@ func TestMediaFile_IsTiff(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
assert.Equal(t, fs.TypeTiff, mediaFile.FileType())
|
||||||
|
assert.Equal(t, "application/octet-stream", mediaFile.MimeType())
|
||||||
|
assert.Equal(t, true, mediaFile.IsTiff())
|
||||||
|
})
|
||||||
|
t.Run("/example.tiff", func(t *testing.T) {
|
||||||
|
conf := config.TestConfig()
|
||||||
|
|
||||||
|
mediaFile, err := NewMediaFile(conf.ExamplesPath() + "/example.tif")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
assert.Equal(t, fs.TypeTiff, mediaFile.FileType())
|
||||||
|
assert.Equal(t, "application/octet-stream", mediaFile.MimeType())
|
||||||
assert.Equal(t, true, mediaFile.IsTiff())
|
assert.Equal(t, true, mediaFile.IsTiff())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1161,6 +1180,9 @@ func TestMediaFile_IsImageOther(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
assert.Equal(t, fs.TypeBitmap, mediaFile.FileType())
|
||||||
|
assert.Equal(t, "image/bmp", mediaFile.MimeType())
|
||||||
|
assert.Equal(t, true, mediaFile.IsBitmap())
|
||||||
assert.Equal(t, true, mediaFile.IsImageOther())
|
assert.Equal(t, true, mediaFile.IsImageOther())
|
||||||
})
|
})
|
||||||
t.Run("/preloader.gif", func(t *testing.T) {
|
t.Run("/preloader.gif", func(t *testing.T) {
|
||||||
|
@ -1170,6 +1192,9 @@ func TestMediaFile_IsImageOther(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, fs.TypeGif, mediaFile.FileType())
|
||||||
|
assert.Equal(t, "image/gif", mediaFile.MimeType())
|
||||||
assert.Equal(t, true, mediaFile.IsImageOther())
|
assert.Equal(t, true, mediaFile.IsImageOther())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1409,8 +1434,9 @@ func TestMediaFile_decodeDimension(t *testing.T) {
|
||||||
|
|
||||||
decodeErr := mediaFile.decodeDimensions()
|
decodeErr := mediaFile.decodeDimensions()
|
||||||
|
|
||||||
assert.EqualError(t, decodeErr, "not a photo: "+conf.ExamplesPath()+"/Random.docx")
|
assert.EqualError(t, decodeErr, "failed decoding dimensions for Random.docx")
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("clock_purple.jpg", func(t *testing.T) {
|
t.Run("clock_purple.jpg", func(t *testing.T) {
|
||||||
conf := config.TestConfig()
|
conf := config.TestConfig()
|
||||||
|
|
||||||
|
@ -1424,6 +1450,7 @@ func TestMediaFile_decodeDimension(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("iphone_7.heic", func(t *testing.T) {
|
t.Run("iphone_7.heic", func(t *testing.T) {
|
||||||
conf := config.TestConfig()
|
conf := config.TestConfig()
|
||||||
|
|
||||||
|
@ -1437,6 +1464,57 @@ func TestMediaFile_decodeDimension(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("example.png", func(t *testing.T) {
|
||||||
|
conf := config.TestConfig()
|
||||||
|
|
||||||
|
mediaFile, err := NewMediaFile(conf.ExamplesPath() + "/example.png")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mediaFile.decodeDimensions(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, 100, mediaFile.Width())
|
||||||
|
assert.Equal(t, 67, mediaFile.Height())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("example.gif", func(t *testing.T) {
|
||||||
|
conf := config.TestConfig()
|
||||||
|
|
||||||
|
mediaFile, err := NewMediaFile(conf.ExamplesPath() + "/example.gif")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mediaFile.decodeDimensions(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, 100, mediaFile.Width())
|
||||||
|
assert.Equal(t, 67, mediaFile.Height())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("blue-go-video.mp4", func(t *testing.T) {
|
||||||
|
conf := config.TestConfig()
|
||||||
|
|
||||||
|
mediaFile, err := NewMediaFile(conf.ExamplesPath() + "/blue-go-video.mp4")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mediaFile.decodeDimensions(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, 1920, mediaFile.Width())
|
||||||
|
assert.Equal(t, 1080, mediaFile.Height())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMediaFile_Width(t *testing.T) {
|
func TestMediaFile_Width(t *testing.T) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package photoprism
|
package photoprism
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/photoprism/photoprism/internal/meta"
|
"github.com/photoprism/photoprism/internal/meta"
|
||||||
|
@ -14,10 +14,10 @@ func (m *MediaFile) MetaData() (result meta.Data) {
|
||||||
m.metaDataOnce.Do(func() {
|
m.metaDataOnce.Do(func() {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
if m.IsPhoto() {
|
if m.ExifSupported() {
|
||||||
err = m.metaData.Exif(m.FileName())
|
err = m.metaData.Exif(m.FileName(), m.FileType())
|
||||||
} else {
|
} else {
|
||||||
err = errors.New("not a photo")
|
err = fmt.Errorf("exif not supported: %s", txt.Quote(m.BaseName()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse JSON sidecar file names as Google Photos uses them ("img_1234.jpg.json").
|
// Parse JSON sidecar file names as Google Photos uses them ("img_1234.jpg.json").
|
||||||
|
|
|
@ -6,7 +6,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
MimeTypeJpeg = "image/jpeg"
|
MimeTypeJpeg = "image/jpeg"
|
||||||
|
MimeTypePng = "image/png"
|
||||||
|
MimeTypeGif = "image/gif"
|
||||||
|
MimeTypeBitmap = "image/bmp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MimeType returns the mime type of a file, empty string if unknown.
|
// MimeType returns the mime type of a file, empty string if unknown.
|
||||||
|
|
Loading…
Reference in a new issue