Initial commit

This commit is contained in:
2019-08-04 23:47:12 +03:00
commit f1716e1fa2
13 changed files with 623 additions and 0 deletions

15
pkg/metadata/base.go Normal file
View File

@@ -0,0 +1,15 @@
package metadata
import (
"time"
)
// Metadata contains meta data for the files have to be processed
type Metadata struct {
Time time.Time
}
// Extractor interface for Metadata extractors
type Extractor interface {
Extract(string) (Metadata, error)
}

45
pkg/metadata/default.go Normal file
View File

@@ -0,0 +1,45 @@
package metadata
import (
"path"
"strings"
"time"
)
const defaultTimeLayout = "20060102_150405"
// DefaultExtractor extract metadata from all file types, not covered by special extractors
//
// Gets the meta data from the file's name
type DefaultExtractor struct {
Layout string
}
// NewDefaultExtractor returns new DefaultExtractor's instance
func NewDefaultExtractor() *DefaultExtractor {
return &DefaultExtractor{Layout: defaultTimeLayout}
}
// NewDefaultExtractorWithLayout returns DefaultExtractor with custom time layout
func NewDefaultExtractorWithLayout(l string) *DefaultExtractor {
return &DefaultExtractor{Layout: l}
}
// Extract returns Metadata from specified filename using its name to parse Time
func (d *DefaultExtractor) Extract(fp string) (Metadata, error) {
_, fName := path.Split(fp)
// Remove extension
fName = strings.Replace(fName, path.Ext(fName), "", 1)
// If there more than one photo in one second, cameras append ~N to the end of file name (before extension)
if strings.ContainsRune(fName, '~') {
fName = fName[:strings.IndexRune(fName, '~')]
}
t, err := time.ParseInLocation(d.Layout, fName, time.Local)
if err != nil {
return Metadata{}, err
}
return Metadata{Time: t}, nil
}

View File

@@ -0,0 +1,70 @@
package metadata
import (
"reflect"
"testing"
"time"
)
func TestDefaultExtractor_Extract(t *testing.T) {
local, err := time.LoadLocation("Europe/Moscow")
if err != nil {
t.Fatal(err)
}
time.Local = local
type args struct {
fp string
}
tests := []struct {
name string
args args
want Metadata
wantErr bool
}{
{
name: "Normal",
args: args{"20190321_120325.jpg"},
want: Metadata{
Time: func() time.Time {
tm, err := time.Parse(time.RFC3339, "2019-03-21T12:03:25+03:00")
if err != nil {
t.Fatal(err)
}
return tm
}(),
},
wantErr: false,
},
{
name: "Name with tilda",
args: args{"20190321_120325~2.jpg"},
want: Metadata{
Time: func() time.Time {
tm, err := time.Parse(time.RFC3339, "2019-03-21T12:03:25+03:00")
if err != nil {
t.Fatal(err)
}
return tm
}(),
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
d := NewDefaultExtractor()
if err != nil {
t.Fatal(err)
}
got, err := d.Extract(tt.args.fp)
if (err != nil) != tt.wantErr {
t.Errorf("Extract() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("Extract() got = %v, want %v", got, tt.want)
}
})
}
}

40
pkg/metadata/jpeg.go Normal file
View File

@@ -0,0 +1,40 @@
package metadata
import (
"os"
"github.com/rwcarlsen/goexif/exif"
)
// JpegExtractor meta data extractor for the jpeg files
type JpegExtractor struct {
}
// NewJpegExtractor returns new JpegExtractor
func NewJpegExtractor() *JpegExtractor {
return &JpegExtractor{}
}
// Extract returns Metadata from specified jpeg file reading its exif data
//
// TODO: Fallback to default extractor on exif reading/parsing error
func (j *JpegExtractor) Extract(fp string) (Metadata, error) {
f, err := os.Open(fp)
if err != nil {
return Metadata{}, err
}
x, err := exif.Decode(f)
if err != nil {
return Metadata{}, err
}
time, err := x.DateTime()
if err != nil {
return Metadata{}, err
}
meta := Metadata{
Time: time,
}
return meta, nil
}