Add exif parsing
This commit is contained in:
parent
890e36b7a8
commit
b10c607f3a
|
@ -3,13 +3,14 @@ CREATE TABLE IF NOT EXISTS photo_exif (
|
|||
camera varchar(256),
|
||||
maker varchar(256),
|
||||
lens varchar(256),
|
||||
dateShot timestamp,
|
||||
file_size_bytes bigint,
|
||||
dateShot timestamp NULL,
|
||||
exposure varchar(256),
|
||||
aperature float,
|
||||
aperture float,
|
||||
iso int(6),
|
||||
focal_length float,
|
||||
flash varchar(256),
|
||||
orientation int(1),
|
||||
exposure_program int(1),
|
||||
|
||||
PRIMARY KEY (exif_id)
|
||||
);
|
||||
|
|
|
@ -5,8 +5,6 @@ go 1.13
|
|||
require (
|
||||
github.com/99designs/gqlgen v0.10.2
|
||||
github.com/fatih/color v1.9.0
|
||||
github.com/go-chi/chi v3.3.2+incompatible
|
||||
github.com/go-chi/cors v1.0.0
|
||||
github.com/go-sql-driver/mysql v1.5.0
|
||||
github.com/golang-migrate/migrate v3.5.4+incompatible
|
||||
github.com/gorilla/mux v1.7.4
|
||||
|
@ -16,10 +14,9 @@ require (
|
|||
github.com/lib/pq v1.3.0
|
||||
github.com/nf/cr2 v0.0.0-20180623103828-4699471a17ed
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
||||
github.com/rs/cors v1.6.0
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
github.com/vektah/gqlparser v1.2.0
|
||||
github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0
|
||||
github.com/xor-gate/goexif2 v1.1.0
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
|
||||
golang.org/x/image v0.0.0-20200119044424-58c23975cae1
|
||||
)
|
||||
|
|
16
api/go.sum
16
api/go.sum
|
@ -6,17 +6,27 @@ github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo
|
|||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dsoprea/go-exif v0.0.0-20200126052615-bd04addaf40f h1:yIPu74TXwq1dxn9edEn4p1si4DW/c/h7sgEJ/zMZNcg=
|
||||
github.com/dsoprea/go-exif v0.0.0-20200126052615-bd04addaf40f/go.mod h1:lOaOt7+UEppOgyvRy749v3do836U/hw0YVJNjoyPaEs=
|
||||
github.com/dsoprea/go-exif/v2 v2.0.0-20200126052615-bd04addaf40f h1:LJ4lH4r8MgKEC5HeSNRw81lfVOlAM0xELOOvf712H0o=
|
||||
github.com/dsoprea/go-exif/v2 v2.0.0-20200126052615-bd04addaf40f/go.mod h1:Lm2lMM2zx8p4a34ZemkaUV95AnMl4ZvLbCUbwOvLC2E=
|
||||
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/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
|
||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
github.com/go-chi/chi v3.3.2+incompatible h1:uQNcQN3NsV1j4ANsPh42P4ew4t6rnRbJb8frvpp31qQ=
|
||||
github.com/go-chi/chi v3.3.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
|
||||
github.com/go-chi/cors v1.0.0 h1:e6x8k7uWbUwYs+aXDoiUzeQFT6l0cygBYyNhD7/1Tg0=
|
||||
github.com/go-chi/cors v1.0.0/go.mod h1:K2Yje0VW/SJzxiyMYu6iPQYa7hMjQX2i/F491VChg1I=
|
||||
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
|
||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang-migrate/migrate v3.5.4+incompatible h1:R7OzwvCJTCgwapPCiX6DyBiu2czIUMDCB118gFTKTUA=
|
||||
github.com/golang-migrate/migrate v3.5.4+incompatible/go.mod h1:IsVUlFN5puWOmXrqjgGUfIRIbU7mr8oNBE2tyERd9Wk=
|
||||
github.com/golang/geo v0.0.0-20190916061304-5b978397cfec h1:lJwO/92dFXWeXOZdoGXgptLmNLwynMSHUmU6besqtiw=
|
||||
github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
||||
github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/mux v1.6.1 h1:KOwqsTYZdeuMacU7CxjMNYEKeBvLbxW+psodrbcEa3A=
|
||||
github.com/gorilla/mux v1.6.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
|
@ -76,11 +86,15 @@ github.com/vektah/gqlparser v1.2.0/go.mod h1:bkVf0FX+Stjg/MHnm8mEyubuaArhNEqfQhF
|
|||
github.com/viktorstrate/photoview v0.0.0-20200119220544-691e4c7dc433 h1:n6jGnDctC9HI7B1rnc5ATQPYaaxQaJtS8bI6oI0QV34=
|
||||
github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0 h1:3UeQBvD0TFrlVjOeLOBz+CPAI8dnbqNSVwUwRrkp7vQ=
|
||||
github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0/go.mod h1:IXCdmsXIht47RaVFLEdVnh1t+pgYtTAhQGj73kz+2DM=
|
||||
github.com/xor-gate/goexif2 v1.1.0 h1:OvTZ5iEvsDhRWFjV5xY3wT7uHFna28nSSP7ucau+cXQ=
|
||||
github.com/xor-gate/goexif2 v1.1.0/go.mod h1:eRjn3VSkAwpNpxEx/CGmd0zg0JFGL3akrSMxnJ581AY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/image v0.0.0-20200119044424-58c23975cae1 h1:5h3ngYt7+vXCDZCup/HkCQgW5XwmSvR/nA2JmJ0RErg=
|
||||
golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
@ -96,5 +110,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
|
|||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
sourcegraph.com/sourcegraph/appdash v0.0.0-20180110180208-2cc67fd64755/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
|
||||
sourcegraph.com/sourcegraph/appdash-data v0.0.0-20151005221446-73f23eafcf67/go.mod h1:L5q+DGLGOQFpo1snNEkLOJT2d1YTW66rWNzatr3He1k=
|
||||
|
|
|
@ -20,12 +20,16 @@ resolver:
|
|||
autobind: []
|
||||
|
||||
models:
|
||||
ID:
|
||||
model: github.com/99designs/gqlgen/graphql.IntID
|
||||
User:
|
||||
model: github.com/viktorstrate/photoview/api/graphql/models.User
|
||||
Photo:
|
||||
model: github.com/viktorstrate/photoview/api/graphql/models.Photo
|
||||
PhotoURL:
|
||||
model: github.com/viktorstrate/photoview/api/graphql/models.PhotoURL
|
||||
PhotoEXIF:
|
||||
model: github.com/viktorstrate/photoview/api/graphql/models.PhotoEXIF
|
||||
Album:
|
||||
model: github.com/viktorstrate/photoview/api/graphql/models.Album
|
||||
ShareToken:
|
||||
|
|
|
@ -113,17 +113,18 @@ type ComplexityRoot struct {
|
|||
}
|
||||
|
||||
PhotoExif struct {
|
||||
Aperture func(childComplexity int) int
|
||||
Camera func(childComplexity int) int
|
||||
DateShot func(childComplexity int) int
|
||||
Exposure func(childComplexity int) int
|
||||
FileSize func(childComplexity int) int
|
||||
Flash func(childComplexity int) int
|
||||
FocalLength func(childComplexity int) int
|
||||
Iso func(childComplexity int) int
|
||||
Lens func(childComplexity int) int
|
||||
Maker func(childComplexity int) int
|
||||
Photo func(childComplexity int) int
|
||||
Aperture func(childComplexity int) int
|
||||
Camera func(childComplexity int) int
|
||||
DateShot func(childComplexity int) int
|
||||
Exposure func(childComplexity int) int
|
||||
ExposureProgram func(childComplexity int) int
|
||||
Flash func(childComplexity int) int
|
||||
FocalLength func(childComplexity int) int
|
||||
ID func(childComplexity int) int
|
||||
Iso func(childComplexity int) int
|
||||
Lens func(childComplexity int) int
|
||||
Maker func(childComplexity int) int
|
||||
Photo func(childComplexity int) int
|
||||
}
|
||||
|
||||
PhotoURL struct {
|
||||
|
@ -201,7 +202,7 @@ type PhotoResolver interface {
|
|||
Thumbnail(ctx context.Context, obj *models.Photo) (*models.PhotoURL, error)
|
||||
HighRes(ctx context.Context, obj *models.Photo) (*models.PhotoURL, error)
|
||||
Album(ctx context.Context, obj *models.Photo) (*models.Album, error)
|
||||
Exif(ctx context.Context, obj *models.Photo) (*models.PhotoExif, error)
|
||||
Exif(ctx context.Context, obj *models.Photo) (*models.PhotoEXIF, error)
|
||||
Shares(ctx context.Context, obj *models.Photo) ([]*models.ShareToken, error)
|
||||
Downloads(ctx context.Context, obj *models.Photo) ([]*models.PhotoDownload, error)
|
||||
}
|
||||
|
@ -629,12 +630,12 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||
|
||||
return e.complexity.PhotoExif.Exposure(childComplexity), true
|
||||
|
||||
case "PhotoEXIF.fileSize":
|
||||
if e.complexity.PhotoExif.FileSize == nil {
|
||||
case "PhotoEXIF.exposureProgram":
|
||||
if e.complexity.PhotoExif.ExposureProgram == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.PhotoExif.FileSize(childComplexity), true
|
||||
return e.complexity.PhotoExif.ExposureProgram(childComplexity), true
|
||||
|
||||
case "PhotoEXIF.flash":
|
||||
if e.complexity.PhotoExif.Flash == nil {
|
||||
|
@ -650,6 +651,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||
|
||||
return e.complexity.PhotoExif.FocalLength(childComplexity), true
|
||||
|
||||
case "PhotoEXIF.id":
|
||||
if e.complexity.PhotoExif.ID == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.PhotoExif.ID(childComplexity), true
|
||||
|
||||
case "PhotoEXIF.iso":
|
||||
if e.complexity.PhotoExif.Iso == nil {
|
||||
break
|
||||
|
@ -1192,6 +1200,7 @@ type Photo {
|
|||
|
||||
"EXIF metadata from the camera"
|
||||
type PhotoEXIF {
|
||||
id: Int!
|
||||
photo: Photo
|
||||
"The model name of the camera"
|
||||
camera: String
|
||||
|
@ -1200,8 +1209,6 @@ type PhotoEXIF {
|
|||
"The name of the lens"
|
||||
lens: String
|
||||
dateShot: Time
|
||||
"The formatted filesize of the image"
|
||||
fileSize: String
|
||||
"The exposure time of the image"
|
||||
exposure: String
|
||||
"The aperature stops of the image"
|
||||
|
@ -1209,9 +1216,11 @@ type PhotoEXIF {
|
|||
"The ISO setting of the image"
|
||||
iso: Int
|
||||
"The focal length of the lens, when the image was taken"
|
||||
focalLength: String
|
||||
focalLength: Float
|
||||
"A formatted description of the flash settings, when the image was taken"
|
||||
flash: String
|
||||
"An index describing the mode for adjusting the exposure of the image"
|
||||
exposureProgram: Int
|
||||
}
|
||||
`},
|
||||
)
|
||||
|
@ -3162,10 +3171,10 @@ func (ec *executionContext) _Photo_exif(ctx context.Context, field graphql.Colle
|
|||
if resTmp == nil {
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(*models.PhotoExif)
|
||||
res := resTmp.(*models.PhotoEXIF)
|
||||
rctx.Result = res
|
||||
ctx = ec.Tracer.StartFieldChildExecution(ctx)
|
||||
return ec.marshalOPhotoEXIF2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐPhotoExif(ctx, field.Selections, res)
|
||||
return ec.marshalOPhotoEXIF2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐPhotoEXIF(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Photo_shares(ctx context.Context, field graphql.CollectedField, obj *models.Photo) (ret graphql.Marshaler) {
|
||||
|
@ -3390,7 +3399,7 @@ func (ec *executionContext) _PhotoDownload_url(ctx context.Context, field graphq
|
|||
return ec.marshalNString2string(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _PhotoEXIF_photo(ctx context.Context, field graphql.CollectedField, obj *models.PhotoExif) (ret graphql.Marshaler) {
|
||||
func (ec *executionContext) _PhotoEXIF_id(ctx context.Context, field graphql.CollectedField, obj *models.PhotoEXIF) (ret graphql.Marshaler) {
|
||||
ctx = ec.Tracer.StartFieldExecution(ctx, field)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
|
@ -3403,13 +3412,50 @@ func (ec *executionContext) _PhotoEXIF_photo(ctx context.Context, field graphql.
|
|||
Object: "PhotoEXIF",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
IsMethod: true,
|
||||
}
|
||||
ctx = graphql.WithResolverContext(ctx, rctx)
|
||||
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.Photo, nil
|
||||
return obj.ID(), nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
if !ec.HasError(rctx) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(int)
|
||||
rctx.Result = res
|
||||
ctx = ec.Tracer.StartFieldChildExecution(ctx)
|
||||
return ec.marshalNInt2int(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _PhotoEXIF_photo(ctx context.Context, field graphql.CollectedField, obj *models.PhotoEXIF) (ret graphql.Marshaler) {
|
||||
ctx = ec.Tracer.StartFieldExecution(ctx, field)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
ec.Tracer.EndFieldExecution(ctx)
|
||||
}()
|
||||
rctx := &graphql.ResolverContext{
|
||||
Object: "PhotoEXIF",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: true,
|
||||
}
|
||||
ctx = graphql.WithResolverContext(ctx, rctx)
|
||||
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.Photo(), nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
|
@ -3424,7 +3470,7 @@ func (ec *executionContext) _PhotoEXIF_photo(ctx context.Context, field graphql.
|
|||
return ec.marshalOPhoto2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐPhoto(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _PhotoEXIF_camera(ctx context.Context, field graphql.CollectedField, obj *models.PhotoExif) (ret graphql.Marshaler) {
|
||||
func (ec *executionContext) _PhotoEXIF_camera(ctx context.Context, field graphql.CollectedField, obj *models.PhotoEXIF) (ret graphql.Marshaler) {
|
||||
ctx = ec.Tracer.StartFieldExecution(ctx, field)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
|
@ -3458,7 +3504,7 @@ func (ec *executionContext) _PhotoEXIF_camera(ctx context.Context, field graphql
|
|||
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _PhotoEXIF_maker(ctx context.Context, field graphql.CollectedField, obj *models.PhotoExif) (ret graphql.Marshaler) {
|
||||
func (ec *executionContext) _PhotoEXIF_maker(ctx context.Context, field graphql.CollectedField, obj *models.PhotoEXIF) (ret graphql.Marshaler) {
|
||||
ctx = ec.Tracer.StartFieldExecution(ctx, field)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
|
@ -3492,7 +3538,7 @@ func (ec *executionContext) _PhotoEXIF_maker(ctx context.Context, field graphql.
|
|||
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _PhotoEXIF_lens(ctx context.Context, field graphql.CollectedField, obj *models.PhotoExif) (ret graphql.Marshaler) {
|
||||
func (ec *executionContext) _PhotoEXIF_lens(ctx context.Context, field graphql.CollectedField, obj *models.PhotoEXIF) (ret graphql.Marshaler) {
|
||||
ctx = ec.Tracer.StartFieldExecution(ctx, field)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
|
@ -3526,7 +3572,7 @@ func (ec *executionContext) _PhotoEXIF_lens(ctx context.Context, field graphql.C
|
|||
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _PhotoEXIF_dateShot(ctx context.Context, field graphql.CollectedField, obj *models.PhotoExif) (ret graphql.Marshaler) {
|
||||
func (ec *executionContext) _PhotoEXIF_dateShot(ctx context.Context, field graphql.CollectedField, obj *models.PhotoEXIF) (ret graphql.Marshaler) {
|
||||
ctx = ec.Tracer.StartFieldExecution(ctx, field)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
|
@ -3560,41 +3606,7 @@ func (ec *executionContext) _PhotoEXIF_dateShot(ctx context.Context, field graph
|
|||
return ec.marshalOTime2ᚖtimeᚐTime(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _PhotoEXIF_fileSize(ctx context.Context, field graphql.CollectedField, obj *models.PhotoExif) (ret graphql.Marshaler) {
|
||||
ctx = ec.Tracer.StartFieldExecution(ctx, field)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
ec.Tracer.EndFieldExecution(ctx)
|
||||
}()
|
||||
rctx := &graphql.ResolverContext{
|
||||
Object: "PhotoEXIF",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
}
|
||||
ctx = graphql.WithResolverContext(ctx, rctx)
|
||||
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.FileSize, nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(*string)
|
||||
rctx.Result = res
|
||||
ctx = ec.Tracer.StartFieldChildExecution(ctx)
|
||||
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _PhotoEXIF_exposure(ctx context.Context, field graphql.CollectedField, obj *models.PhotoExif) (ret graphql.Marshaler) {
|
||||
func (ec *executionContext) _PhotoEXIF_exposure(ctx context.Context, field graphql.CollectedField, obj *models.PhotoEXIF) (ret graphql.Marshaler) {
|
||||
ctx = ec.Tracer.StartFieldExecution(ctx, field)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
|
@ -3628,7 +3640,7 @@ func (ec *executionContext) _PhotoEXIF_exposure(ctx context.Context, field graph
|
|||
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _PhotoEXIF_aperture(ctx context.Context, field graphql.CollectedField, obj *models.PhotoExif) (ret graphql.Marshaler) {
|
||||
func (ec *executionContext) _PhotoEXIF_aperture(ctx context.Context, field graphql.CollectedField, obj *models.PhotoEXIF) (ret graphql.Marshaler) {
|
||||
ctx = ec.Tracer.StartFieldExecution(ctx, field)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
|
@ -3662,7 +3674,7 @@ func (ec *executionContext) _PhotoEXIF_aperture(ctx context.Context, field graph
|
|||
return ec.marshalOFloat2ᚖfloat64(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _PhotoEXIF_iso(ctx context.Context, field graphql.CollectedField, obj *models.PhotoExif) (ret graphql.Marshaler) {
|
||||
func (ec *executionContext) _PhotoEXIF_iso(ctx context.Context, field graphql.CollectedField, obj *models.PhotoEXIF) (ret graphql.Marshaler) {
|
||||
ctx = ec.Tracer.StartFieldExecution(ctx, field)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
|
@ -3696,7 +3708,7 @@ func (ec *executionContext) _PhotoEXIF_iso(ctx context.Context, field graphql.Co
|
|||
return ec.marshalOInt2ᚖint(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _PhotoEXIF_focalLength(ctx context.Context, field graphql.CollectedField, obj *models.PhotoExif) (ret graphql.Marshaler) {
|
||||
func (ec *executionContext) _PhotoEXIF_focalLength(ctx context.Context, field graphql.CollectedField, obj *models.PhotoEXIF) (ret graphql.Marshaler) {
|
||||
ctx = ec.Tracer.StartFieldExecution(ctx, field)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
|
@ -3724,13 +3736,13 @@ func (ec *executionContext) _PhotoEXIF_focalLength(ctx context.Context, field gr
|
|||
if resTmp == nil {
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(*string)
|
||||
res := resTmp.(*float64)
|
||||
rctx.Result = res
|
||||
ctx = ec.Tracer.StartFieldChildExecution(ctx)
|
||||
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
|
||||
return ec.marshalOFloat2ᚖfloat64(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _PhotoEXIF_flash(ctx context.Context, field graphql.CollectedField, obj *models.PhotoExif) (ret graphql.Marshaler) {
|
||||
func (ec *executionContext) _PhotoEXIF_flash(ctx context.Context, field graphql.CollectedField, obj *models.PhotoEXIF) (ret graphql.Marshaler) {
|
||||
ctx = ec.Tracer.StartFieldExecution(ctx, field)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
|
@ -3764,6 +3776,40 @@ func (ec *executionContext) _PhotoEXIF_flash(ctx context.Context, field graphql.
|
|||
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _PhotoEXIF_exposureProgram(ctx context.Context, field graphql.CollectedField, obj *models.PhotoEXIF) (ret graphql.Marshaler) {
|
||||
ctx = ec.Tracer.StartFieldExecution(ctx, field)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
ec.Tracer.EndFieldExecution(ctx)
|
||||
}()
|
||||
rctx := &graphql.ResolverContext{
|
||||
Object: "PhotoEXIF",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
}
|
||||
ctx = graphql.WithResolverContext(ctx, rctx)
|
||||
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.ExposureProgram, nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(*int)
|
||||
rctx.Result = res
|
||||
ctx = ec.Tracer.StartFieldChildExecution(ctx)
|
||||
return ec.marshalOInt2ᚖint(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _PhotoURL_url(ctx context.Context, field graphql.CollectedField, obj *models.PhotoURL) (ret graphql.Marshaler) {
|
||||
ctx = ec.Tracer.StartFieldExecution(ctx, field)
|
||||
defer func() {
|
||||
|
@ -6531,7 +6577,7 @@ func (ec *executionContext) _PhotoDownload(ctx context.Context, sel ast.Selectio
|
|||
|
||||
var photoEXIFImplementors = []string{"PhotoEXIF"}
|
||||
|
||||
func (ec *executionContext) _PhotoEXIF(ctx context.Context, sel ast.SelectionSet, obj *models.PhotoExif) graphql.Marshaler {
|
||||
func (ec *executionContext) _PhotoEXIF(ctx context.Context, sel ast.SelectionSet, obj *models.PhotoEXIF) graphql.Marshaler {
|
||||
fields := graphql.CollectFields(ec.RequestContext, sel, photoEXIFImplementors)
|
||||
|
||||
out := graphql.NewFieldSet(fields)
|
||||
|
@ -6540,6 +6586,11 @@ func (ec *executionContext) _PhotoEXIF(ctx context.Context, sel ast.SelectionSet
|
|||
switch field.Name {
|
||||
case "__typename":
|
||||
out.Values[i] = graphql.MarshalString("PhotoEXIF")
|
||||
case "id":
|
||||
out.Values[i] = ec._PhotoEXIF_id(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
case "photo":
|
||||
out.Values[i] = ec._PhotoEXIF_photo(ctx, field, obj)
|
||||
case "camera":
|
||||
|
@ -6550,8 +6601,6 @@ func (ec *executionContext) _PhotoEXIF(ctx context.Context, sel ast.SelectionSet
|
|||
out.Values[i] = ec._PhotoEXIF_lens(ctx, field, obj)
|
||||
case "dateShot":
|
||||
out.Values[i] = ec._PhotoEXIF_dateShot(ctx, field, obj)
|
||||
case "fileSize":
|
||||
out.Values[i] = ec._PhotoEXIF_fileSize(ctx, field, obj)
|
||||
case "exposure":
|
||||
out.Values[i] = ec._PhotoEXIF_exposure(ctx, field, obj)
|
||||
case "aperture":
|
||||
|
@ -6562,6 +6611,8 @@ func (ec *executionContext) _PhotoEXIF(ctx context.Context, sel ast.SelectionSet
|
|||
out.Values[i] = ec._PhotoEXIF_focalLength(ctx, field, obj)
|
||||
case "flash":
|
||||
out.Values[i] = ec._PhotoEXIF_flash(ctx, field, obj)
|
||||
case "exposureProgram":
|
||||
out.Values[i] = ec._PhotoEXIF_exposureProgram(ctx, field, obj)
|
||||
default:
|
||||
panic("unknown field " + strconv.Quote(field.Name))
|
||||
}
|
||||
|
@ -7932,11 +7983,11 @@ func (ec *executionContext) marshalOPhoto2ᚖgithubᚗcomᚋviktorstrateᚋphoto
|
|||
return ec._Photo(ctx, sel, v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalOPhotoEXIF2githubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐPhotoExif(ctx context.Context, sel ast.SelectionSet, v models.PhotoExif) graphql.Marshaler {
|
||||
func (ec *executionContext) marshalOPhotoEXIF2githubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐPhotoEXIF(ctx context.Context, sel ast.SelectionSet, v models.PhotoEXIF) graphql.Marshaler {
|
||||
return ec._PhotoEXIF(ctx, sel, &v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalOPhotoEXIF2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐPhotoExif(ctx context.Context, sel ast.SelectionSet, v *models.PhotoExif) graphql.Marshaler {
|
||||
func (ec *executionContext) marshalOPhotoEXIF2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐPhotoEXIF(ctx context.Context, sel ast.SelectionSet, v *models.PhotoEXIF) graphql.Marshaler {
|
||||
if v == nil {
|
||||
return graphql.Null
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type AuthorizeResult struct {
|
||||
|
@ -39,30 +38,6 @@ type PhotoDownload struct {
|
|||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
// EXIF metadata from the camera
|
||||
type PhotoExif struct {
|
||||
Photo *Photo `json:"photo"`
|
||||
// The model name of the camera
|
||||
Camera *string `json:"camera"`
|
||||
// The maker of the camera
|
||||
Maker *string `json:"maker"`
|
||||
// The name of the lens
|
||||
Lens *string `json:"lens"`
|
||||
DateShot *time.Time `json:"dateShot"`
|
||||
// The formatted filesize of the image
|
||||
FileSize *string `json:"fileSize"`
|
||||
// The exposure time of the image
|
||||
Exposure *string `json:"exposure"`
|
||||
// The aperature stops of the image
|
||||
Aperture *float64 `json:"aperture"`
|
||||
// The ISO setting of the image
|
||||
Iso *int `json:"iso"`
|
||||
// The focal length of the lens, when the image was taken
|
||||
FocalLength *string `json:"focalLength"`
|
||||
// A formatted description of the flash settings, when the image was taken
|
||||
Flash *string `json:"flash"`
|
||||
}
|
||||
|
||||
type ScannerResult struct {
|
||||
Finished bool `json:"finished"`
|
||||
Success bool `json:"success"`
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
)
|
||||
|
||||
type PhotoEXIF struct {
|
||||
ExifID int
|
||||
Camera *string
|
||||
Maker *string
|
||||
Lens *string
|
||||
DateShot *time.Time
|
||||
Exposure *string
|
||||
Aperture *float64
|
||||
Iso *int
|
||||
FocalLength *float64
|
||||
Flash *string
|
||||
Orientation *int
|
||||
ExposureProgram *int
|
||||
}
|
||||
|
||||
func (exif *PhotoEXIF) Photo() *Photo {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (exif *PhotoEXIF) ID() int {
|
||||
return exif.ExifID
|
||||
}
|
||||
|
||||
func NewPhotoExifFromRow(row *sql.Row) (*PhotoEXIF, error) {
|
||||
exif := PhotoEXIF{}
|
||||
|
||||
if err := row.Scan(&exif.ExifID, &exif.Camera, &exif.Maker, &exif.Lens, &exif.DateShot, &exif.Exposure, &exif.Aperture, &exif.Iso, &exif.FocalLength, &exif.Flash, &exif.Orientation, &exif.ExposureProgram); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &exif, nil
|
||||
}
|
|
@ -146,7 +146,7 @@ func (r *photoResolver) Album(ctx context.Context, obj *models.Photo) (*models.A
|
|||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (r *photoResolver) Exif(ctx context.Context, obj *models.Photo) (*models.PhotoExif, error) {
|
||||
log.Println("Photo: EXIF not implemented")
|
||||
return nil, nil
|
||||
func (r *photoResolver) Exif(ctx context.Context, obj *models.Photo) (*models.PhotoEXIF, error) {
|
||||
row := r.Database.QueryRow("SELECT photo_exif.* FROM photo NATURAL JOIN photo_exif WHERE photo.photo_id = ?", obj.PhotoID)
|
||||
return models.NewPhotoExifFromRow(row)
|
||||
}
|
||||
|
|
|
@ -202,6 +202,7 @@ type Photo {
|
|||
|
||||
"EXIF metadata from the camera"
|
||||
type PhotoEXIF {
|
||||
id: Int!
|
||||
photo: Photo
|
||||
"The model name of the camera"
|
||||
camera: String
|
||||
|
@ -210,8 +211,6 @@ type PhotoEXIF {
|
|||
"The name of the lens"
|
||||
lens: String
|
||||
dateShot: Time
|
||||
"The formatted filesize of the image"
|
||||
fileSize: String
|
||||
"The exposure time of the image"
|
||||
exposure: String
|
||||
"The aperature stops of the image"
|
||||
|
@ -219,7 +218,9 @@ type PhotoEXIF {
|
|||
"The ISO setting of the image"
|
||||
iso: Int
|
||||
"The focal length of the lens, when the image was taken"
|
||||
focalLength: String
|
||||
focalLength: Float
|
||||
"A formatted description of the flash settings, when the image was taken"
|
||||
flash: String
|
||||
"An index describing the mode for adjusting the exposure of the image"
|
||||
exposureProgram: Int
|
||||
}
|
||||
|
|
|
@ -0,0 +1,248 @@
|
|||
package scanner
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"log"
|
||||
"math/big"
|
||||
"os"
|
||||
|
||||
"github.com/viktorstrate/photoview/api/graphql/models"
|
||||
"github.com/xor-gate/goexif2/exif"
|
||||
"github.com/xor-gate/goexif2/mknote"
|
||||
)
|
||||
|
||||
func ScanEXIF(tx *sql.Tx, photo *models.Photo) (*models.PhotoEXIF, error) {
|
||||
|
||||
log.Printf("Scanning for EXIF")
|
||||
|
||||
{
|
||||
// Check if EXIF data already exists
|
||||
if photo.ExifId != nil {
|
||||
row := tx.QueryRow("SELECT * FROM photo_exif WHERE exif_id = ?", photo.ExifId)
|
||||
return models.NewPhotoExifFromRow(row)
|
||||
}
|
||||
|
||||
row := tx.QueryRow("SELECT photo_exif.* FROM photo, photo_exif WHERE photo.exif_id = photo_exif.exif_id AND photo.photo_id = ?", photo.PhotoID)
|
||||
exifData, err := models.NewPhotoExifFromRow(row)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return nil, err
|
||||
} else if exifData != nil {
|
||||
return exifData, nil
|
||||
}
|
||||
}
|
||||
|
||||
photoFile, err := os.Open(photo.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
exif.RegisterParsers(mknote.All...)
|
||||
|
||||
exifTags, err := exif.Decode(photoFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// log.Printf("EXIF DATA FOR %s\n%s\n", photo.Title, exifTags.String())
|
||||
|
||||
valueNames := make([]string, 0)
|
||||
exifValues := make([]interface{}, 0)
|
||||
|
||||
model, err := readStringTag(exifTags, exif.Model, photo)
|
||||
if err == nil {
|
||||
valueNames = append(valueNames, "camera")
|
||||
exifValues = append(exifValues, model)
|
||||
}
|
||||
|
||||
maker, err := readStringTag(exifTags, exif.Make, photo)
|
||||
if err == nil {
|
||||
valueNames = append(valueNames, "maker")
|
||||
exifValues = append(exifValues, maker)
|
||||
}
|
||||
|
||||
lens, err := readStringTag(exifTags, exif.LensModel, photo)
|
||||
if err == nil {
|
||||
valueNames = append(valueNames, "lens")
|
||||
exifValues = append(exifValues, lens)
|
||||
}
|
||||
|
||||
date, err := exifTags.DateTime()
|
||||
if err == nil {
|
||||
valueNames = append(valueNames, "dateShot")
|
||||
exifValues = append(exifValues, date)
|
||||
}
|
||||
|
||||
exposure, err := readRationalTag(exifTags, exif.ExposureTime, photo)
|
||||
if err == nil {
|
||||
valueNames = append(valueNames, "exposure")
|
||||
exifValues = append(exifValues, exposure.RatString())
|
||||
}
|
||||
|
||||
apertureRat, err := readRationalTag(exifTags, exif.FNumber, photo)
|
||||
if err == nil {
|
||||
aperture, _ := apertureRat.Float32()
|
||||
valueNames = append(valueNames, "aperture")
|
||||
exifValues = append(exifValues, aperture)
|
||||
}
|
||||
|
||||
isoTag, err := exifTags.Get(exif.ISOSpeedRatings)
|
||||
if err != nil {
|
||||
log.Printf("WARN: Could not read ISOSpeedRatings from EXIF: %s\n", photo.Title)
|
||||
} else {
|
||||
iso, err := isoTag.Int(0)
|
||||
if err != nil {
|
||||
log.Printf("WARN: Could not parse EXIF ISOSpeedRatings as integer: %s\n", photo.Title)
|
||||
} else {
|
||||
valueNames = append(valueNames, "iso")
|
||||
exifValues = append(exifValues, iso)
|
||||
}
|
||||
}
|
||||
|
||||
focalLengthRat, err := readRationalTag(exifTags, exif.FocalLength, photo)
|
||||
if err == nil {
|
||||
focalLength, _ := focalLengthRat.Float32()
|
||||
valueNames = append(valueNames, "focal_length")
|
||||
exifValues = append(exifValues, 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
|
||||
tag, err := exifTags.Get(exif.FocalLength)
|
||||
if err == nil {
|
||||
focalLength, err := tag.Int(1)
|
||||
if err != nil {
|
||||
log.Printf("WARN: Could not parse EXIF FocalLength as integer: %s\n%s\n", photo.Title, err)
|
||||
} else {
|
||||
valueNames = append(valueNames, "focal_length")
|
||||
exifValues = append(exifValues, focalLength)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flash, err := exifTags.Flash()
|
||||
if err == nil {
|
||||
valueNames = append(valueNames, "flash")
|
||||
exifValues = append(exifValues, flash)
|
||||
}
|
||||
|
||||
orientation, err := readIntegerTag(exifTags, exif.Orientation, photo)
|
||||
if err == nil {
|
||||
valueNames = append(valueNames, "orientation")
|
||||
exifValues = append(exifValues, *orientation)
|
||||
}
|
||||
|
||||
exposureProgram, err := readIntegerTag(exifTags, exif.ExposureProgram, photo)
|
||||
if err == nil {
|
||||
valueNames = append(valueNames, "exposure_program")
|
||||
exifValues = append(exifValues, *exposureProgram)
|
||||
}
|
||||
|
||||
if len(valueNames) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
prepareQuestions := ""
|
||||
for range valueNames {
|
||||
prepareQuestions += "?,"
|
||||
}
|
||||
prepareQuestions = prepareQuestions[0 : len(prepareQuestions)-1]
|
||||
|
||||
columns := ""
|
||||
for _, name := range valueNames {
|
||||
columns += name + ","
|
||||
}
|
||||
columns = columns[0 : len(columns)-1]
|
||||
|
||||
// Insert into database
|
||||
result, err := tx.Exec("INSERT INTO photo_exif ("+columns+") VALUES ("+prepareQuestions+")", exifValues...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
exifID, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Link exif to photo in database
|
||||
result, err = tx.Exec("UPDATE photo SET exif_id = ? WHERE photo_id = ?", exifID, photo.PhotoID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if rowsAffected == 0 {
|
||||
return nil, errors.New("Linking exif to photo in database failed: 0 rows affected")
|
||||
}
|
||||
|
||||
// Return newly created exif row
|
||||
row := tx.QueryRow("SELECT * FROM photo_exif WHERE exif_id = ?", exifID)
|
||||
return models.NewPhotoExifFromRow(row)
|
||||
}
|
||||
|
||||
func readStringTag(tags *exif.Exif, name exif.FieldName, photo *models.Photo) (*string, error) {
|
||||
tag, err := tags.Get(name)
|
||||
if err != nil {
|
||||
log.Printf("WARN: Could not read %s from EXIF: %s\n", name, photo.Title)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if tag != nil {
|
||||
value, err := tag.StringVal()
|
||||
if err != nil {
|
||||
log.Printf("WARN: Could not parse %s from EXIF as string: %s\n", name, photo.Title)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &value, nil
|
||||
}
|
||||
|
||||
log.Printf("WARN: EXIF tag %s returned null: %s\n", name, photo.Title)
|
||||
return nil, errors.New("exif tag returned null")
|
||||
}
|
||||
|
||||
func readRationalTag(tags *exif.Exif, name exif.FieldName, photo *models.Photo) (*big.Rat, error) {
|
||||
tag, err := tags.Get(name)
|
||||
if err != nil {
|
||||
log.Printf("WARN: Could not read %s from EXIF: %s\n", name, photo.Title)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if tag != nil {
|
||||
value, err := tag.Rat(0)
|
||||
if err != nil {
|
||||
log.Printf("WARN: Could not parse %s from EXIF as rational: %s\n%s\n", name, photo.Title, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
log.Printf("WARN: EXIF tag %s returned null: %s\n", name, photo.Title)
|
||||
return nil, errors.New("exif tag returned null")
|
||||
}
|
||||
|
||||
func readIntegerTag(tags *exif.Exif, name exif.FieldName, photo *models.Photo) (*int, error) {
|
||||
tag, err := tags.Get(name)
|
||||
if err != nil {
|
||||
log.Printf("WARN: Could not read %s from EXIF: %s\n", name, photo.Title)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if tag != nil {
|
||||
value, err := tag.Int(0)
|
||||
if err != nil {
|
||||
log.Printf("WARN: Could not parse %s from EXIF as integer: %s\n%s\n", name, photo.Title, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &value, nil
|
||||
}
|
||||
|
||||
log.Printf("WARN: EXIF tag %s returned null: %s\n", name, photo.Title)
|
||||
return nil, errors.New("exif tag returned null")
|
||||
}
|
|
@ -41,6 +41,12 @@ func ScanPhoto(tx *sql.Tx, photoPath string, albumId int, content_type *string)
|
|||
return err
|
||||
}
|
||||
|
||||
_, err = ScanEXIF(tx, photo)
|
||||
if err != nil {
|
||||
log.Printf("ERROR: ScanEXIF for %s: %s\n", photoName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ProcessPhoto(tx, photo, content_type); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ func ProcessPhoto(tx *sql.Tx, photo *models.Photo, content_type *string) error {
|
|||
|
||||
log.Printf("Processing photo: %s\n", photo.Path)
|
||||
|
||||
imageData := processImageData{
|
||||
imageData := ProcessImageData{
|
||||
photoPath: photo.Path,
|
||||
}
|
||||
|
||||
|
@ -249,13 +249,13 @@ func encodeImageJPEG(photoPath string, photoImage image.Image, jpegOptions *jpeg
|
|||
return nil
|
||||
}
|
||||
|
||||
type processImageData struct {
|
||||
type ProcessImageData struct {
|
||||
photoPath string
|
||||
_photoImage image.Image
|
||||
_thumbnailImage image.Image
|
||||
}
|
||||
|
||||
func (img *processImageData) PhotoImage() (image.Image, error) {
|
||||
func (img *ProcessImageData) PhotoImage() (image.Image, error) {
|
||||
if img._photoImage != nil {
|
||||
return img._photoImage, nil
|
||||
}
|
||||
|
@ -276,7 +276,7 @@ func (img *processImageData) PhotoImage() (image.Image, error) {
|
|||
return img._photoImage, nil
|
||||
}
|
||||
|
||||
func (img *processImageData) ThumbnailImage() (image.Image, error) {
|
||||
func (img *ProcessImageData) ThumbnailImage() (image.Image, error) {
|
||||
photoImage, err := img.PhotoImage()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
Loading…
Reference in New Issue