Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Artist filter #115

Merged
merged 3 commits into from
Mar 26, 2020
Merged
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
33 changes: 33 additions & 0 deletions db/migration/20200325185135_add_album_artist_id.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package migration

import (
"database/sql"
"github.com/pressly/goose"
)

func init() {
goose.AddMigration(Up20200325185135, Down20200325185135)
}

func Up20200325185135(tx *sql.Tx) error {
_, err := tx.Exec(`
alter table album
add album_artist_id varchar(255) default '';
create index album_artist_album_id
on album (album_artist_id);

alter table media_file
add album_artist_id varchar(255) default '';
create index media_file_artist_album_id
on media_file (album_artist_id);
`)
if err != nil {
return err
}
notice(tx, "A full rescan will be performed!")
return forceFullRescan(tx)
}

func Down20200325185135(tx *sql.Tx) error {
return nil
}
33 changes: 17 additions & 16 deletions model/album.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,22 @@ package model
import "time"

type Album struct {
ID string `json:"id" orm:"column(id)"`
Name string `json:"name"`
ArtistID string `json:"artistId" orm:"pk;column(artist_id)"`
CoverArtPath string `json:"coverArtPath"`
CoverArtId string `json:"coverArtId"`
Artist string `json:"artist"`
AlbumArtist string `json:"albumArtist"`
Year int `json:"year"`
Compilation bool `json:"compilation"`
SongCount int `json:"songCount"`
Duration float32 `json:"duration"`
Genre string `json:"genre"`
FullText string `json:"fullText"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
ID string `json:"id" orm:"column(id)"`
Name string `json:"name"`
CoverArtPath string `json:"coverArtPath"`
CoverArtId string `json:"coverArtId"`
ArtistID string `json:"artistId" orm:"pk;column(artist_id)"`
Artist string `json:"artist"`
AlbumArtistID string `json:"albumArtistId" orm:"pk;column(album_artist_id)"`
AlbumArtist string `json:"albumArtist"`
Year int `json:"year"`
Compilation bool `json:"compilation"`
SongCount int `json:"songCount"`
Duration float32 `json:"duration"`
Genre string `json:"genre"`
FullText string `json:"fullText"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`

// Annotations
PlayCount int `json:"-" orm:"-"`
Expand All @@ -34,7 +35,7 @@ type AlbumRepository interface {
Exists(id string) (bool, error)
Put(m *Album) error
Get(id string) (*Album, error)
FindByArtist(artistId string) (Albums, error)
FindByArtist(albumArtistId string) (Albums, error)
GetAll(...QueryOptions) (Albums, error)
GetRandom(...QueryOptions) (Albums, error)
GetStarred(options ...QueryOptions) (Albums, error)
Expand Down
43 changes: 22 additions & 21 deletions model/mediafile.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,28 @@ import (
)

type MediaFile struct {
ID string `json:"id" orm:"pk;column(id)"`
Path string `json:"path"`
Title string `json:"title"`
Album string `json:"album"`
Artist string `json:"artist"`
ArtistID string `json:"artistId" orm:"pk;column(artist_id)"`
AlbumArtist string `json:"albumArtist"`
AlbumID string `json:"albumId" orm:"pk;column(album_id)"`
HasCoverArt bool `json:"hasCoverArt"`
TrackNumber int `json:"trackNumber"`
DiscNumber int `json:"discNumber"`
Year int `json:"year"`
Size int `json:"size"`
Suffix string `json:"suffix"`
Duration float32 `json:"duration"`
BitRate int `json:"bitRate"`
Genre string `json:"genre"`
FullText string `json:"fullText"`
Compilation bool `json:"compilation"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
ID string `json:"id" orm:"pk;column(id)"`
Path string `json:"path"`
Title string `json:"title"`
Album string `json:"album"`
ArtistID string `json:"artistId" orm:"pk;column(artist_id)"`
Artist string `json:"artist"`
AlbumArtistID string `json:"albumArtistId"`
AlbumArtist string `json:"albumArtist"`
AlbumID string `json:"albumId" orm:"pk;column(album_id)"`
HasCoverArt bool `json:"hasCoverArt"`
TrackNumber int `json:"trackNumber"`
DiscNumber int `json:"discNumber"`
Year int `json:"year"`
Size int `json:"size"`
Suffix string `json:"suffix"`
Duration float32 `json:"duration"`
BitRate int `json:"bitRate"`
Genre string `json:"genre"`
FullText string `json:"fullText"`
Compilation bool `json:"compilation"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`

// Annotations
PlayCount int `json:"-" orm:"-"`
Expand Down
17 changes: 14 additions & 3 deletions persistence/album_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,22 @@ func NewAlbumRepository(ctx context.Context, o orm.Ormer) model.AlbumRepository
r.filterMappings = map[string]filterFunc{
"name": fullTextFilter,
"compilation": booleanFilter,
"artist_id": artistFilter,
}

return r
}

func artistFilter(field string, value interface{}) Sqlizer {
return Exists("media_file", And{
ConcatExpr("album_id=album.id"),
Or{
Eq{"artist_id": value},
Eq{"album_artist_id": value},
},
})
}

func (r *albumRepository) CountAll(options ...model.QueryOptions) (int64, error) {
return r.count(Select(), options...)
}
Expand Down Expand Up @@ -62,7 +73,7 @@ func (r *albumRepository) Get(id string) (*model.Album, error) {
}

func (r *albumRepository) FindByArtist(artistId string) (model.Albums, error) {
sq := r.selectAlbum().Where(Eq{"artist_id": artistId}).OrderBy("year")
sq := r.selectAlbum().Where(Eq{"album_artist_id": artistId}).OrderBy("year")
res := model.Albums{}
err := r.queryAll(sq, &res)
return res, err
Expand Down Expand Up @@ -91,8 +102,8 @@ func (r *albumRepository) Refresh(ids ...string) error {
HasCoverArt bool
}
var albums []refreshAlbum
sel := Select(`album_id as id, album as name, f.artist, f.album_artist, f.artist_id, f.compilation, f.genre,
max(f.year) as year, sum(f.duration) as duration, count(*) as song_count, a.id as current_id,
sel := Select(`album_id as id, album as name, f.artist, f.album_artist, f.artist_id, f.album_artist_id,
f.compilation, f.genre, max(f.year) as year, sum(f.duration) as duration, count(*) as song_count, a.id as current_id,
f.id as cover_art_id, f.path as cover_art_path, f.has_cover_art`).
From("media_file f").
LeftJoin("album a on f.album_id = a.id").
Expand Down
8 changes: 4 additions & 4 deletions persistence/artist_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,12 @@ func (r *artistRepository) Refresh(ids ...string) error {
Compilation bool
}
var artists []refreshArtist
sel := Select("f.artist_id as id", "f.artist as name", "f.album_artist", "f.compilation",
sel := Select("f.album_artist_id as id", "f.artist as name", "f.album_artist", "f.compilation",
"count(*) as album_count", "a.id as current_id").
From("album f").
LeftJoin("artist a on f.artist_id = a.id").
Where(Eq{"f.artist_id": ids}).
GroupBy("f.artist_id").OrderBy("f.id")
LeftJoin("artist a on f.album_artist_id = a.id").
Where(Eq{"f.album_artist_id": ids}).
GroupBy("f.album_artist_id").OrderBy("f.id")
err := r.queryAll(sel, &artists)
if err != nil {
return err
Expand Down
2 changes: 1 addition & 1 deletion persistence/artist_repository_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ var _ = Describe("ArtistRepository", func() {
})
})

Describe("Exist", func() {
Describe("Exists", func() {
It("returns true for an artist that is in the DB", func() {
Expect(repo.Exists("3")).To(BeTrue())
})
Expand Down
35 changes: 12 additions & 23 deletions persistence/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"fmt"
"regexp"
"strings"

"github.com/Masterminds/squirrel"
)

func toSqlArgs(rec interface{}) (map[string]interface{}, error) {
Expand Down Expand Up @@ -33,30 +35,17 @@ func toSnakeCase(str string) string {
return strings.ToLower(snake)
}

func ToStruct(m map[string]interface{}, rec interface{}, fieldNames []string) error {
var r = make(map[string]interface{}, len(m))
for _, f := range fieldNames {
v, ok := m[f]
if !ok {
return fmt.Errorf("invalid field '%s'", f)
}
r[toCamelCase(f)] = v
}
// Convert to JSON...
b, err := json.Marshal(r)
if err != nil {
return err
}

// ... then convert to struct
err = json.Unmarshal(b, &rec)
return err
func Exists(subTable string, cond squirrel.Sqlizer) exists {
return exists{subTable: subTable, cond: cond}
}

var matchUnderscore = regexp.MustCompile("_([A-Za-z])")
type exists struct {
subTable string
cond squirrel.Sqlizer
}

func toCamelCase(str string) string {
return matchUnderscore.ReplaceAllStringFunc(str, func(s string) string {
return strings.ToUpper(strings.Replace(s, "_", "", -1))
})
func (e exists) ToSql() (string, []interface{}, error) {
sql, args, err := e.cond.ToSql()
sql = fmt.Sprintf("exists (select 1 from %s where %s)", e.subTable, sql)
return sql, args, err
}
19 changes: 19 additions & 0 deletions persistence/helpers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package persistence

import (
"github.com/Masterminds/squirrel"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var _ = Describe("Helpers", func() {
Describe("Exists", func() {
It("constructs the correct EXISTS query", func() {
e := Exists("album", squirrel.Eq{"id": 1})
sql, args, err := e.ToSql()
Expect(sql).To(Equal("exists (select 1 from album where id = ?)"))
Expect(args).To(Equal([]interface{}{1}))
Expect(err).To(BeNil())
})
})
})
2 changes: 1 addition & 1 deletion persistence/mock_album_repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func (m *MockAlbum) FindByArtist(artistId string) (model.Albums, error) {
var res = make(model.Albums, len(m.data))
i := 0
for _, a := range m.data {
if a.ArtistID == artistId {
if a.AlbumArtistID == artistId {
res[i] = *a
i++
}
Expand Down
6 changes: 3 additions & 3 deletions persistence/persistence_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ var (
)

var (
albumSgtPeppers = model.Album{ID: "1", Name: "Sgt Peppers", Artist: "The Beatles", ArtistID: "3", Genre: "Rock", CoverArtId: "1", CoverArtPath: P("/beatles/1/sgt/a day.mp3"), SongCount: 1, Year: 1967, FullText: "sgt peppers the beatles"}
albumAbbeyRoad = model.Album{ID: "2", Name: "Abbey Road", Artist: "The Beatles", ArtistID: "3", Genre: "Rock", CoverArtId: "2", CoverArtPath: P("/beatles/1/come together.mp3"), SongCount: 1, Year: 1969, FullText: "abbey road the beatles"}
albumRadioactivity = model.Album{ID: "3", Name: "Radioactivity", Artist: "Kraftwerk", ArtistID: "2", Genre: "Electronic", CoverArtId: "3", CoverArtPath: P("/kraft/radio/radio.mp3"), SongCount: 2, FullText: "radioactivity kraftwerk"}
albumSgtPeppers = model.Album{ID: "1", Name: "Sgt Peppers", Artist: "The Beatles", AlbumArtistID: "3", Genre: "Rock", CoverArtId: "1", CoverArtPath: P("/beatles/1/sgt/a day.mp3"), SongCount: 1, Year: 1967, FullText: "sgt peppers the beatles"}
albumAbbeyRoad = model.Album{ID: "2", Name: "Abbey Road", Artist: "The Beatles", AlbumArtistID: "3", Genre: "Rock", CoverArtId: "2", CoverArtPath: P("/beatles/1/come together.mp3"), SongCount: 1, Year: 1969, FullText: "abbey road the beatles"}
albumRadioactivity = model.Album{ID: "3", Name: "Radioactivity", Artist: "Kraftwerk", AlbumArtistID: "2", Genre: "Electronic", CoverArtId: "3", CoverArtPath: P("/kraft/radio/radio.mp3"), SongCount: 2, FullText: "radioactivity kraftwerk"}
testAlbums = model.Albums{
albumSgtPeppers,
albumAbbeyRoad,
Expand Down
2 changes: 1 addition & 1 deletion persistence/playlist_repository_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ var _ = Describe("PlaylistRepository", func() {
})
})

Describe("Exist", func() {
Describe("Exists", func() {
It("returns true for an existing playlist", func() {
Expect(repo.Exists("11")).To(BeTrue())
})
Expand Down
24 changes: 16 additions & 8 deletions scanner/tag_scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,13 +241,10 @@ func (s *TagScanner) toMediaFile(md *Metadata) model.MediaFile {
mf.Album = md.Album()
mf.AlbumID = s.albumID(md)
mf.Album = s.mapAlbumName(md)
if md.Artist() == "" {
mf.Artist = consts.UnknownArtist
} else {
mf.Artist = md.Artist()
}
mf.ArtistID = s.artistID(md)
mf.AlbumArtist = md.AlbumArtist()
mf.Artist = s.mapArtistName(md)
mf.AlbumArtistID = s.albumArtistID(md)
mf.AlbumArtist = s.mapAlbumArtistName(md)
mf.Genre = md.Genre()
mf.Compilation = md.Compilation()
mf.Year = md.Year()
Expand Down Expand Up @@ -276,7 +273,7 @@ func (s *TagScanner) mapTrackTitle(md *Metadata) string {
return md.Title()
}

func (s *TagScanner) mapArtistName(md *Metadata) string {
func (s *TagScanner) mapAlbumArtistName(md *Metadata) string {
switch {
case md.Compilation():
return consts.VariousArtists
Expand All @@ -289,6 +286,13 @@ func (s *TagScanner) mapArtistName(md *Metadata) string {
}
}

func (s *TagScanner) mapArtistName(md *Metadata) string {
if md.Artist() != "" {
return md.Artist()
}
return consts.UnknownArtist
}

func (s *TagScanner) mapAlbumName(md *Metadata) string {
name := md.Album()
if name == "" {
Expand All @@ -302,10 +306,14 @@ func (s *TagScanner) trackID(md *Metadata) string {
}

func (s *TagScanner) albumID(md *Metadata) string {
albumPath := strings.ToLower(fmt.Sprintf("%s\\%s", s.mapArtistName(md), s.mapAlbumName(md)))
albumPath := strings.ToLower(fmt.Sprintf("%s\\%s", s.mapAlbumArtistName(md), s.mapAlbumName(md)))
return fmt.Sprintf("%x", md5.Sum([]byte(albumPath)))
}

func (s *TagScanner) artistID(md *Metadata) string {
return fmt.Sprintf("%x", md5.Sum([]byte(strings.ToLower(s.mapArtistName(md)))))
}

func (s *TagScanner) albumArtistID(md *Metadata) string {
return fmt.Sprintf("%x", md5.Sum([]byte(strings.ToLower(s.mapAlbumArtistName(md)))))
}
14 changes: 12 additions & 2 deletions ui/src/album/AlbumList.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import {
FunctionField,
SearchInput,
NumberInput,
BooleanInput,
NullableBooleanInput,
Show,
SimpleShowLayout,
ReferenceInput,
AutocompleteInput,
TextField
} from 'react-admin'
import { DurationField, Pagination, Title } from '../common'
Expand All @@ -20,7 +22,15 @@ import { useMediaQuery } from '@material-ui/core'
const AlbumFilter = (props) => (
<Filter {...props}>
<SearchInput source="name" alwaysOn />
<BooleanInput source="compilation" />
<ReferenceInput
source="artist_id"
reference="artist"
sort={{ field: 'name', order: 'ASC' }}
filterToQuery={(searchText) => ({ name: [searchText] })}
>
<AutocompleteInput emptyText="-- None --" />
</ReferenceInput>
<NullableBooleanInput source="compilation" />
<NumberInput source="year" />
</Filter>
)
Expand Down
4 changes: 3 additions & 1 deletion ui/src/artist/ArtistList.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ const ArtistFilter = (props) => (

const artistRowClick = (id, basePath, record) => {
const filter = { artist_id: id }
return `/album?filter=${JSON.stringify(filter)}&order=ASC&sort=year`
return `/album?filter=${JSON.stringify(
filter
)}&order=ASC&sort=year&displayedFilters={"compilation":true}`
}

const ArtistList = (props) => (
Expand Down