feat: add artist filter to album view

This commit is contained in:
Deluan 2020-03-25 18:51:13 -04:00 committed by Deluan Quintão
parent c84a58ff7d
commit 100db2bcfd
11 changed files with 129 additions and 59 deletions

View File

@ -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
}

View File

@ -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:"-"`
@ -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)

View File

@ -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:"-"`

View File

@ -28,11 +28,19 @@ 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 Or{
exist("from media_file where album.id = media_file.album_id and media_file.artist_id='" + value.(string) + "'"),
exist("from media_file where album.id = media_file.album_id and media_file.album_artist_id='" + value.(string) + "'"),
}
}
func (r *albumRepository) CountAll(options ...model.QueryOptions) (int64, error) {
return r.count(Select(), options...)
}
@ -62,7 +70,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
@ -91,8 +99,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").

View File

@ -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

View File

@ -60,3 +60,10 @@ func toCamelCase(str string) string {
return strings.ToUpper(strings.Replace(s, "_", "", -1))
})
}
type exist string
func (e exist) ToSql() (string, []interface{}, error) {
sql := fmt.Sprintf("exists (select 1 %s)", e)
return sql, nil, nil
}

View File

@ -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++
}

View File

@ -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,

View File

@ -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()
@ -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
@ -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 == "" {
@ -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)))))
}

View File

@ -9,9 +9,11 @@ import {
FunctionField,
SearchInput,
NumberInput,
BooleanInput,
NullableBooleanInput,
Show,
SimpleShowLayout,
ReferenceInput,
AutocompleteInput,
TextField
} from 'react-admin'
import { DurationField, Pagination, Title } from '../common'
@ -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>
)

View File

@ -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) => (