Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions examples/meta/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"time"

"github.com/recentralized/structure/data"
"github.com/recentralized/structure/index"
"github.com/recentralized/structure/meta"
)

Expand All @@ -28,6 +29,8 @@ func main() {

func buildMeta() (*meta.Meta, error) {

srcID := index.SrcID("e8400c72-f7d0-53f9-98ca-ee23238231fe")

doc := meta.New()
doc.Type = data.JPG
doc.Size = 1024
Expand All @@ -42,9 +45,13 @@ func buildMeta() (*meta.Meta, error) {
"ExposureTime": meta.ExifValue{ID: "ShutterSpeed", Val: "1/60"},
},
}
doc.Sidecar = meta.Content{
Exif: meta.Exif{
"FNumber": meta.ExifValue{ID: "0x829d", Val: 1.8},
doc.Src = map[index.SrcID]meta.SrcSpecific{
srcID: {
Sidecar: &meta.Content{
Exif: meta.Exif{
"FNumber": meta.ExifValue{ID: "0x829d", Val: 1.8},
},
},
},
}

Expand Down
14 changes: 9 additions & 5 deletions examples/meta/output.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,15 @@
}
}
},
"sidecar": {
"exif": {
"FNumber": {
"id": "0x829d",
"val": 1.8
"src": {
"e8400c72-f7d0-53f9-98ca-ee23238231fe": {
"sidecar": {
"exif": {
"FNumber": {
"id": "0x829d",
"val": 1.8
}
}
}
}
}
Expand Down
39 changes: 28 additions & 11 deletions meta/encoding.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,42 @@ import (
"time"

"github.com/recentralized/structure/data"
"github.com/recentralized/structure/index"
)

type metaJSON struct {
Version string `json:"version"`
Type data.Type `json:"type"`
ContentType data.Type `json:"content_type,omitempty"`
Size int64 `json:"size"`
Inherent *Content `json:"inherent,omitempty"`
Sidecar *Content `json:"sidecar,omitempty"`
SrcSpecific // Embedded fields.

Inherent *Content `json:"inherent,omitempty"`
Src map[index.SrcID]SrcSpecific `json:"src,omitempty"`

// V0 Fields
Sidecar *Content `json:"sidecar,omitempty"`
V0SrcSpecific // Embedded fields.
}

// MarshalJSON converts Meta to JSON.
func (m Meta) MarshalJSON() ([]byte, error) {
j := metaJSON{
Version: m.Version,
Type: m.Type,
Size: m.Size,
SrcSpecific: m.Srcs,
Version: m.Version,
Type: m.Type,
Size: m.Size,
V0SrcSpecific: m.V0Srcs,
}
if !m.Inherent.isZero() {
j.Inherent = &m.Inherent
}
if !m.Sidecar.isZero() {
j.Sidecar = &m.Sidecar
if !m.V0Sidecar.isZero() {
j.Sidecar = &m.V0Sidecar
}
if m.Src != nil {
j.Src = make(map[index.SrcID]SrcSpecific)
for k, v := range m.Src {
j.Src[k] = v
}
}
return json.Marshal(j)
}
Expand All @@ -46,13 +57,19 @@ func (m *Meta) UnmarshalJSON(data []byte) error {
m.Type = j.ContentType
}
m.Size = j.Size
if j.Src != nil {
m.Src = make(map[index.SrcID]SrcSpecific)
for k, v := range j.Src {
m.Src[k] = v
}
}
if j.Inherent != nil {
m.Inherent = *j.Inherent
}
if j.Sidecar != nil {
m.Sidecar = *j.Sidecar
m.V0Sidecar = *j.Sidecar
}
m.Srcs = j.SrcSpecific
m.V0Srcs = j.V0SrcSpecific
return nil
}

Expand Down
67 changes: 59 additions & 8 deletions meta/encoding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"time"

"github.com/recentralized/structure/data"
"github.com/recentralized/structure/index"
)

func TestMetaJSON(t *testing.T) {
Expand All @@ -28,6 +29,13 @@ func TestMetaJSON(t *testing.T) {
Version: "v1",
Type: data.JPG,
Size: 100,
},
json: `{"version":"v1","type":"jpg","size":100}`,
},
{
desc: "inherent",
meta: Meta{
Version: "v1",
Inherent: Content{
Created: time.Date(1, 2, 3, 4, 5, 6, 7, time.UTC),
Image: Image{
Expand All @@ -41,23 +49,66 @@ func TestMetaJSON(t *testing.T) {
},
},
},
Sidecar: Content{
Created: time.Date(2, 2, 3, 4, 5, 6, 7, time.UTC),
},
json: `{"version":"v1","type":"","size":0,"inherent":{"created":"0001-02-03T04:05:06.000000007Z","image":{"width":100,"height":60},"exif":{"CreateData":{"id":"0x9004","val":"2013:07:17 19:59:58"}}}}`,
},
{
desc: "src-specific: sidecar",
meta: Meta{
Version: "v1",
Src: map[index.SrcID]SrcSpecific{
index.SrcID("s1"): {
Sidecar: &Content{
Created: time.Date(1, 2, 3, 4, 5, 6, 7, time.UTC),
Image: Image{
Width: 100,
Height: 60,
},
Exif: Exif{
"CreateData": ExifValue{
ID: "0x9004",
Val: "2013:07:17 19:59:58",
},
},
},
},
},
},
json: `{"version":"v1","type":"jpg","size":100,"inherent":{"created":"0001-02-03T04:05:06.000000007Z","image":{"width":100,"height":60},"exif":{"CreateData":{"id":"0x9004","val":"2013:07:17 19:59:58"}}},"sidecar":{"created":"0002-02-03T04:05:06.000000007Z"}}`,
json: `{"version":"v1","type":"","size":0,"src":{"s1":{"sidecar":{"created":"0001-02-03T04:05:06.000000007Z","image":{"width":100,"height":60},"exif":{"CreateData":{"id":"0x9004","val":"2013:07:17 19:59:58"}}}}}}`,
},
{
desc: "src-specific fields: flickr",
desc: "src-specific: flickr",
meta: Meta{
Version: "v1",
Srcs: SrcSpecific{
Flickr: &FlickrMedia{
ID: "123",
Src: map[index.SrcID]SrcSpecific{
index.SrcID("s1"): {
Flickr: &FlickrMedia{
ID: "123",
},
},
},
},
json: `{"version":"v1","type":"","size":0,"flickr":{"id":"123"}}`,
json: `{"version":"v1","type":"","size":0,"src":{"s1":{"flickr":{"id":"123"}}}}`,
},
{
desc: "v0 sidecar",
meta: Meta{
Version: "v1",
V0Sidecar: Content{
Created: time.Date(2, 2, 3, 4, 5, 6, 7, time.UTC),
},
},
json: `{"version":"v1","type":"","size":0,"sidecar":{"created":"0002-02-03T04:05:06.000000007Z"}}`,
},
{
desc: "v0 src-specific",
meta: Meta{
Version: "v1",
V0Srcs: V0SrcSpecific{
//Flickr:
},
},
json: `{"version":"v1","type":"","size":0}`,
},
}
for _, tt := range tests {
Expand Down
29 changes: 25 additions & 4 deletions meta/meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import (
"encoding/json"
"errors"
"io"
"sort"
"time"

"github.com/recentralized/structure/data"
"github.com/recentralized/structure/index"
)

const (
Expand Down Expand Up @@ -34,12 +36,17 @@ type Meta struct {
// Metadata that came from the content itself.
Inherent Content

// Metadata that came from elsewhere on the source.
Src map[index.SrcID]SrcSpecific

// Metadata that came from nearby, such as an XMP sidecar file or other
// source of metadata.
Sidecar Content
// Deprecated: Read from V0 only. Use Src[srcID] instead.
V0Sidecar Content

// Metadata that came from the source of the data.
Srcs SrcSpecific
// Deprecated: Read from V0 only. Use Src[srcID] instead.
V0Srcs V0SrcSpecific
}

// New initializes a new Meta at the current version.
Expand Down Expand Up @@ -70,9 +77,17 @@ func ParseJSON(r io.Reader) (*Meta, error) {
// time available it returns time.Time's zero value.
func (m *Meta) DateCreated() time.Time {
times := []time.Time{
m.Sidecar.Created,
m.V0Sidecar.Created,
m.Inherent.Created,
}
if len(m.Src) > 0 {
for _, v := range m.Src {
if v.Sidecar != nil {
times = append(times, v.Sidecar.Created)
}
}
sort.Slice(times, func(i, j int) bool { return times[i].Before(times[j]) })
}
for _, t := range times {
if !t.IsZero() {
return t
Expand Down Expand Up @@ -102,7 +117,13 @@ type Image struct {

// SrcSpecific contains source-specific metadata.
type SrcSpecific struct {
Flickr *FlickrMedia `json:"flickr,omitempty"`
Sidecar *Content `json:"sidecar,omitempty"`
Flickr *FlickrMedia `json:"flickr,omitempty"`
}

// V0SrcSpecific contains source-specific metadata.
type V0SrcSpecific struct {
// TODO: port v0 sources
}

func (m Content) isZero() bool {
Expand Down
34 changes: 27 additions & 7 deletions meta/meta_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"reflect"
"testing"
"time"

"github.com/recentralized/structure/index"
)

func TestMetaDateCreated(t *testing.T) {
Expand All @@ -24,25 +26,43 @@ func TestMetaDateCreated(t *testing.T) {
want: time.Date(2001, 1, 1, 1, 1, 1, 1, time.UTC),
},
{
desc: "sidecar with date",
desc: "oldest of inherent or sidecars",
m: &Meta{
Sidecar: Content{Created: time.Date(2001, 2, 1, 1, 1, 1, 1, time.UTC)},
Inherent: Content{Created: time.Date(2009, 1, 1, 1, 1, 1, 1, time.UTC)},
Src: map[index.SrcID]SrcSpecific{
index.SrcID("a"): {
Sidecar: &Content{Created: time.Date(2004, 1, 1, 1, 1, 1, 1, time.UTC)},
},
index.SrcID("b"): {
Sidecar: &Content{Created: time.Date(2001, 1, 1, 1, 1, 1, 1, time.UTC)},
},
index.SrcID("c"): {
Sidecar: &Content{Created: time.Date(2003, 1, 1, 1, 1, 1, 1, time.UTC)},
},
},
},
want: time.Date(2001, 2, 1, 1, 1, 1, 1, time.UTC),
want: time.Date(2001, 1, 1, 1, 1, 1, 1, time.UTC),
},
{
desc: "prefers sidecar to inherent",
desc: "v0: sidecar with date",
m: &Meta{
Inherent: Content{Created: time.Date(2001, 1, 1, 1, 1, 1, 1, time.UTC)},
Sidecar: Content{Created: time.Date(2001, 2, 1, 1, 1, 1, 1, time.UTC)},
V0Sidecar: Content{Created: time.Date(2001, 2, 1, 1, 1, 1, 1, time.UTC)},
},
want: time.Date(2001, 2, 1, 1, 1, 1, 1, time.UTC),
},
{
desc: "v0: prefers sidecar to inherent",
m: &Meta{
Inherent: Content{Created: time.Date(2001, 1, 1, 1, 1, 1, 1, time.UTC)},
V0Sidecar: Content{Created: time.Date(2002, 1, 1, 1, 1, 1, 1, time.UTC)},
},
want: time.Date(2002, 1, 1, 1, 1, 1, 1, time.UTC),
},
}
for _, tt := range tests {
got := tt.m.DateCreated()
if got, want := got, tt.want; !reflect.DeepEqual(got, want) {
t.Errorf("%q Meta.DateCreated()\ngot %#v\nwant %#v", tt.desc, got, want)
t.Errorf("%q Meta.DateCreated()\ngot %s\nwant %s", tt.desc, got, want)
}
}
}
Expand Down