// Fetch returns a group from the collection with the given path. func (l *Library) Fetch(c index.Collection, path []string) (group, error) { if len(path) == 0 { return build(c, index.Key("Root")), nil } var g index.Group = c k := index.Key(path[0]) g = c.Get(k) if g == nil { return group{}, fmt.Errorf("invalid path: near '%v'", path[0]) } index.Sort(g.Tracks(), index.MultiSort(index.SortByInt("DiscNumber"), index.SortByInt("TrackNumber"))) g = index.Transform(g, index.SplitList("Artist", "AlbumArtist", "Composer")) c = index.Collect(g, index.ByPrefix("Name")) g = index.SubTransform(c, index.TrimEnumPrefix) g = index.SumGroupIntAttr("TotalTime", g) commonFields := []attr.Interface{ attr.String("Album"), attr.Strings("Artist"), attr.Strings("AlbumArtist"), attr.Strings("Composer"), attr.Int("Year"), attr.Int("BitRate"), attr.Int("DiscNumber"), } g = index.CommonGroupAttr(commonFields, g) g = index.RemoveEmptyCollections(g) for i, p := range path[1:] { var ok bool c, ok = g.(index.Collection) if !ok { return group{}, fmt.Errorf("retrieved Group is not a Collection") } k = index.Key(p) g = c.Get(k) if g == nil { return group{}, fmt.Errorf("invalid path near '%v'", path[1:][i]) } if _, ok = g.(index.Collection); !ok { if i == len(path[1:])-1 { break } return group{}, fmt.Errorf("retrieved Group isn't a Collection: %v", p) } } if g == nil { return group{}, fmt.Errorf("could not find group") } g = index.FirstTrackAttr(attr.String("ID"), g) return build(g, k), nil }
func TestFirstTrackAttr(t *testing.T) { table := []struct { in Group field attr.Interface out interface{} }{ // One group with no tracks { in: group{ name: "Group One", }, field: attr.String("Name"), out: nil, }, { in: group{ name: "Group One", }, field: attr.Strings("Artist"), out: nil, }, // One group with one track { in: group{ name: "Group One", tracks: []Track{ testTrack{ Name: "Track One", }, }, }, field: attr.String("Name"), out: "Track One", }, { in: group{ name: "Group One", tracks: []Track{ testTrack{ Artist: "Track One", }, }, }, field: attr.Strings("Artist"), out: []string{"Track One"}, }, // One group with two tracks, empty first field { in: group{ name: "Group One", tracks: []Track{ testTrack{}, testTrack{ Name: "Track Two", Artist: "Artist One", }, }, }, field: attr.String("Artist"), out: nil, }, { in: group{ name: "Group One", tracks: []Track{ testTrack{}, testTrack{ Name: "Track Two", Artist: "Artist One", }, }, }, field: attr.Strings("Artist"), out: nil, }, // One collection, one group, one track { in: testCol{ name: "Group One (collection)", keys: []Key{"Group-One-One"}, grps: map[Key]Group{ "Group-One-One": group{ name: "Group One (One)", tracks: []Track{ testTrack{ Name: "Track One", }, }, }, }, }, field: attr.String("Name"), out: "Track One", }, // One group with one track { in: group{ name: "Group One", tracks: []Track{ testTrack{ Duration: 1234, }, }, }, field: attr.Int("Duration"), out: 1234, }, // One group with two tracks, empty first field { in: group{ name: "Group One", tracks: []Track{ testTrack{}, testTrack{ Duration: 1234, Artist: "Artist One", }, }, }, field: attr.Int("Duration"), out: nil, }, // One collection, one group, one track { in: testCol{ name: "Group One (collection)", keys: []Key{"Group-One-One"}, grps: map[Key]Group{ "Group-One-One": group{ name: "Group One (One)", tracks: []Track{ testTrack{ Duration: 1234, }, }, }, }, }, field: attr.Int("Duration"), out: 1234, }, } for ii, tt := range table { g := FirstTrackAttr(tt.field, tt.in) got := g.Field(tt.field.Name()) if !reflect.DeepEqual(got, tt.out) { t.Errorf("[%d] got %#v, expected %#v", ii, got, tt.out) } } }
func TestCommonGroupAttr(t *testing.T) { table := []struct { in Group fields []attr.Interface out []interface{} }{ // One group with one track, unset common field { in: group{ name: "Group One", tracks: []Track{ testTrack{ Name: "Track One", }, }, }, fields: []attr.Interface{attr.String("Artist")}, out: []interface{}{nil}, }, // One group with one track, unset common int field { in: group{ name: "Group One", tracks: []Track{ testTrack{ Name: "Empty common field", }, }, }, fields: []attr.Interface{attr.Int("Year")}, out: []interface{}{nil}, }, // One group with one track, unset common (string) and (int) fields { in: group{ name: "Group One", tracks: []Track{ testTrack{ Name: "Track One", }, }, }, fields: []attr.Interface{attr.String("Artist"), attr.Int("Year")}, out: []interface{}{nil, nil}, }, // One group with one track, set strings field { in: group{ name: "Group One", tracks: []Track{ testTrack{ stringsMap: map[string][]string{ "Artist": []string{"First Artist", "Second Artist"}, }, }, }, }, fields: []attr.Interface{attr.Strings("Artist")}, out: []interface{}{[]string{"First Artist", "Second Artist"}}, }, // One group with two tracks, check intersection of artists list { in: group{ name: "Group One", tracks: []Track{ testTrack{ stringsMap: map[string][]string{ "Artist": []string{"First Artist", "Second Artist"}, }, }, testTrack{ stringsMap: map[string][]string{ "Artist": []string{"First Artist"}, }, }, }, }, fields: []attr.Interface{attr.Strings("Artist")}, out: []interface{}{[]string{"First Artist"}}, }, // One group with two tracks, empty first string & int fields { in: group{ name: "Group One", tracks: []Track{ testTrack{ Name: "Track One", }, testTrack{ Name: "Track Two", Artist: "Artist One", Year: 1984, }, }, }, fields: []attr.Interface{attr.String("Artist"), attr.Int("Year")}, out: []interface{}{nil, nil}, }, // One group with two tracks, empty first field { in: group{ name: "Group One", tracks: []Track{ testTrack{ Name: "Track One", }, testTrack{ Name: "Track Two", Year: 1985, }, }, }, fields: []attr.Interface{attr.Int("Year")}, out: []interface{}{nil}, }, // One group with one track, common string/int fields { in: group{ name: "Group One", tracks: []Track{ testTrack{ Name: "Track One", Artist: "Artist One", Composer: "Composer One", Year: 1984, }, }, }, fields: []attr.Interface{attr.String("Artist"), attr.Int("Year")}, out: []interface{}{"Artist One", 1984}, }, // One group with one track, common string fields for Artist and Composer { in: group{ name: "Group One", tracks: []Track{ testTrack{ Name: "Track One", Artist: "Artist One", Composer: "Composer One", }, }, }, fields: []attr.Interface{attr.String("Artist"), attr.String("Composer")}, out: []interface{}{"Artist One", "Composer One"}, }, // One group with two tracks, common fields across pairs { in: group{ name: "Group One", tracks: []Track{ testTrack{ Name: "Track One", Artist: "Artist One", Composer: "Composer One", }, testTrack{ Name: "Track Two", Artist: "Artist Two", Composer: "Composer One", }, }, }, fields: []attr.Interface{attr.String("Artist"), attr.String("Composer")}, out: []interface{}{nil, "Composer One"}, }, // One collection, one group, one track { in: testCol{ name: "Group Three (collection)", keys: []Key{"Group-Three-One"}, grps: map[Key]Group{ "Group-Three-One": group{ name: "Group One (Three)", tracks: []Track{ testTrack{ Name: "Track One", Artist: "Artist One", }, }, }, }, }, fields: []attr.Interface{attr.String("Artist"), attr.String("Composer")}, out: []interface{}{"Artist One", nil}, }, // One collection, three groups, many tracks! { in: testCol{ name: "Root", keys: []Key{"Group-One", "Group-Two", "Group-Three"}, grps: map[Key]Group{ "Group-One": group{ name: "Group One", tracks: []Track{ testTrack{ Name: "Track One", Artist: "Artist One", Year: 1984, }, }, }, "Group-Two": group{ name: "Group Two", tracks: []Track{ testTrack{ Name: "Track One", Artist: "Artist One", Year: 1984, }, testTrack{ Name: "Track Two", Artist: "Artist One", Year: 1984, }, }, }, "Group-Three": testCol{ name: "Group Three (collection)", keys: []Key{"Group-Three-One"}, grps: map[Key]Group{ "Group-Three-One": group{ name: "Group One (Three)", tracks: []Track{ testTrack{ Name: "Track One", Artist: "Artist One", Year: 1984, }, testTrack{ Name: "Track Two", Artist: "Artist One", Year: 2000, }, testTrack{ Name: "Track One", Artist: "Artist One", Year: 1984, }, }, }, }, }, }, }, fields: []attr.Interface{attr.String("Artist"), attr.Int("Year")}, out: []interface{}{"Artist One", nil}, }, } for ii, tt := range table { g := CommonGroupAttr(tt.fields, tt.in) got := make([]interface{}, len(tt.out)) for i, f := range tt.fields { got[i] = g.Field(f.Name()) } if !reflect.DeepEqual(got, tt.out) { t.Errorf("[%d] got %#v, expected %#v", ii, got, tt.out) } } }