mirror of
https://github.com/navidrome/navidrome.git
synced 2025-06-09 03:42:23 +03:00
Integrate ArtistIndex logic into Artist
This commit is contained in:
parent
035fdaeb96
commit
c0beaa6377
@ -50,8 +50,8 @@ func (c *BrowsingController) getArtistIndex(r *http.Request, ifModifiedSince tim
|
|||||||
res.Index[i].Name = idx.ID
|
res.Index[i].Name = idx.ID
|
||||||
res.Index[i].Artists = make([]responses.Artist, len(idx.Artists))
|
res.Index[i].Artists = make([]responses.Artist, len(idx.Artists))
|
||||||
for j, a := range idx.Artists {
|
for j, a := range idx.Artists {
|
||||||
res.Index[i].Artists[j].Id = a.ArtistID
|
res.Index[i].Artists[j].Id = a.ID
|
||||||
res.Index[i].Artists[j].Name = a.Artist
|
res.Index[i].Artists[j].Name = a.Name
|
||||||
res.Index[i].Artists[j].AlbumCount = a.AlbumCount
|
res.Index[i].Artists[j].AlbumCount = a.AlbumCount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,15 +23,14 @@ type Browser interface {
|
|||||||
GetGenres() (model.Genres, error)
|
GetGenres() (model.Genres, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBrowser(pr model.PropertyRepository, fr model.MediaFolderRepository, ir model.ArtistIndexRepository,
|
func NewBrowser(pr model.PropertyRepository, fr model.MediaFolderRepository,
|
||||||
ar model.ArtistRepository, alr model.AlbumRepository, mr model.MediaFileRepository, gr model.GenreRepository) Browser {
|
ar model.ArtistRepository, alr model.AlbumRepository, mr model.MediaFileRepository, gr model.GenreRepository) Browser {
|
||||||
return &browser{pr, fr, ir, ar, alr, mr, gr}
|
return &browser{pr, fr, ar, alr, mr, gr}
|
||||||
}
|
}
|
||||||
|
|
||||||
type browser struct {
|
type browser struct {
|
||||||
propRepo model.PropertyRepository
|
propRepo model.PropertyRepository
|
||||||
folderRepo model.MediaFolderRepository
|
folderRepo model.MediaFolderRepository
|
||||||
indexRepo model.ArtistIndexRepository
|
|
||||||
artistRepo model.ArtistRepository
|
artistRepo model.ArtistRepository
|
||||||
albumRepo model.AlbumRepository
|
albumRepo model.AlbumRepository
|
||||||
mfileRepo model.MediaFileRepository
|
mfileRepo model.MediaFileRepository
|
||||||
@ -52,7 +51,7 @@ func (b *browser) Indexes(ifModifiedSince time.Time) (model.ArtistIndexes, time.
|
|||||||
}
|
}
|
||||||
|
|
||||||
if lastModified.After(ifModifiedSince) {
|
if lastModified.After(ifModifiedSince) {
|
||||||
indexes, err := b.indexRepo.GetAll()
|
indexes, err := b.artistRepo.GetIndex()
|
||||||
return indexes, lastModified, err
|
return indexes, lastModified, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,13 @@ type Artist struct {
|
|||||||
Name string
|
Name string
|
||||||
AlbumCount int
|
AlbumCount int
|
||||||
}
|
}
|
||||||
|
type Artists []Artist
|
||||||
|
|
||||||
|
type ArtistIndex struct {
|
||||||
|
ID string
|
||||||
|
Artists Artists
|
||||||
|
}
|
||||||
|
type ArtistIndexes []ArtistIndex
|
||||||
|
|
||||||
type ArtistRepository interface {
|
type ArtistRepository interface {
|
||||||
CountAll() (int64, error)
|
CountAll() (int64, error)
|
||||||
@ -14,6 +21,5 @@ type ArtistRepository interface {
|
|||||||
PurgeInactive(active Artists) error
|
PurgeInactive(active Artists) error
|
||||||
Search(q string, offset int, size int) (Artists, error)
|
Search(q string, offset int, size int) (Artists, error)
|
||||||
Refresh(ids ...string) error
|
Refresh(ids ...string) error
|
||||||
|
GetIndex() (ArtistIndexes, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Artists []Artist
|
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
package model
|
|
||||||
|
|
||||||
type ArtistInfo struct {
|
|
||||||
ArtistID string
|
|
||||||
Artist string
|
|
||||||
AlbumCount int
|
|
||||||
}
|
|
||||||
|
|
||||||
type ArtistIndex struct {
|
|
||||||
ID string
|
|
||||||
Artists ArtistInfos
|
|
||||||
}
|
|
||||||
|
|
||||||
type ArtistInfos []ArtistInfo
|
|
||||||
type ArtistIndexes []ArtistIndex
|
|
||||||
|
|
||||||
// TODO Combine ArtistIndex with Artist
|
|
||||||
type ArtistIndexRepository interface {
|
|
||||||
Put(m *ArtistIndex) error
|
|
||||||
Refresh() error
|
|
||||||
GetAll() (ArtistIndexes, error)
|
|
||||||
DeleteAll() error
|
|
||||||
}
|
|
@ -2,11 +2,14 @@ package persistence
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/astaxie/beego/orm"
|
"github.com/astaxie/beego/orm"
|
||||||
|
"github.com/cloudsonic/sonic-server/conf"
|
||||||
"github.com/cloudsonic/sonic-server/log"
|
"github.com/cloudsonic/sonic-server/log"
|
||||||
"github.com/cloudsonic/sonic-server/model"
|
"github.com/cloudsonic/sonic-server/model"
|
||||||
|
"github.com/cloudsonic/sonic-server/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Artist struct {
|
type Artist struct {
|
||||||
@ -17,14 +20,27 @@ type Artist struct {
|
|||||||
|
|
||||||
type artistRepository struct {
|
type artistRepository struct {
|
||||||
searchableRepository
|
searchableRepository
|
||||||
|
indexGroups utils.IndexGroups
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewArtistRepository() model.ArtistRepository {
|
func NewArtistRepository() model.ArtistRepository {
|
||||||
r := &artistRepository{}
|
r := &artistRepository{}
|
||||||
|
r.indexGroups = utils.ParseIndexGroups(conf.Sonic.IndexGroups)
|
||||||
r.tableName = "artist"
|
r.tableName = "artist"
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *artistRepository) getIndexKey(a *Artist) string {
|
||||||
|
name := strings.ToLower(utils.NoArticle(a.Name))
|
||||||
|
for k, v := range r.indexGroups {
|
||||||
|
key := strings.ToLower(k)
|
||||||
|
if strings.HasPrefix(name, key) {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "#"
|
||||||
|
}
|
||||||
|
|
||||||
func (r *artistRepository) Put(a *model.Artist) error {
|
func (r *artistRepository) Put(a *model.Artist) error {
|
||||||
ta := Artist(*a)
|
ta := Artist(*a)
|
||||||
return withTx(func(o orm.Ormer) error {
|
return withTx(func(o orm.Ormer) error {
|
||||||
@ -45,6 +61,34 @@ func (r *artistRepository) Get(id string) (*model.Artist, error) {
|
|||||||
return &a, nil
|
return &a, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO Cache the index (recalculate when there are changes to the DB)
|
||||||
|
func (r *artistRepository) GetIndex() (model.ArtistIndexes, error) {
|
||||||
|
var all []Artist
|
||||||
|
_, err := r.newQuery(Db()).OrderBy("name").All(&all)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fullIdx := make(map[string]*model.ArtistIndex)
|
||||||
|
for _, a := range all {
|
||||||
|
ax := r.getIndexKey(&a)
|
||||||
|
idx, ok := fullIdx[ax]
|
||||||
|
if !ok {
|
||||||
|
idx = &model.ArtistIndex{ID: ax}
|
||||||
|
fullIdx[ax] = idx
|
||||||
|
}
|
||||||
|
idx.Artists = append(idx.Artists, model.Artist(a))
|
||||||
|
}
|
||||||
|
var result model.ArtistIndexes
|
||||||
|
for _, idx := range fullIdx {
|
||||||
|
result = append(result, *idx)
|
||||||
|
}
|
||||||
|
sort.Slice(result, func(i, j int) bool {
|
||||||
|
return result[i].ID < result[j].ID
|
||||||
|
})
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *artistRepository) Refresh(ids ...string) error {
|
func (r *artistRepository) Refresh(ids ...string) error {
|
||||||
type refreshArtist struct {
|
type refreshArtist struct {
|
||||||
Artist
|
Artist
|
||||||
@ -71,17 +115,17 @@ where f.artist_id in ('%s') group by f.artist_id order by f.id`, strings.Join(id
|
|||||||
|
|
||||||
var toInsert []Artist
|
var toInsert []Artist
|
||||||
var toUpdate []Artist
|
var toUpdate []Artist
|
||||||
for _, al := range artists {
|
for _, ar := range artists {
|
||||||
if al.Compilation {
|
if ar.Compilation {
|
||||||
al.AlbumArtist = "Various Artists"
|
ar.AlbumArtist = "Various Artists"
|
||||||
}
|
}
|
||||||
if al.AlbumArtist != "" {
|
if ar.AlbumArtist != "" {
|
||||||
al.Name = al.AlbumArtist
|
ar.Name = ar.AlbumArtist
|
||||||
}
|
}
|
||||||
if al.CurrentId != "" {
|
if ar.CurrentId != "" {
|
||||||
toUpdate = append(toUpdate, al.Artist)
|
toUpdate = append(toUpdate, ar.Artist)
|
||||||
} else {
|
} else {
|
||||||
toInsert = append(toInsert, al.Artist)
|
toInsert = append(toInsert, ar.Artist)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(toInsert) > 0 {
|
if len(toInsert) > 0 {
|
||||||
@ -93,7 +137,7 @@ where f.artist_id in ('%s') group by f.artist_id order by f.id`, strings.Join(id
|
|||||||
}
|
}
|
||||||
if len(toUpdate) > 0 {
|
if len(toUpdate) > 0 {
|
||||||
for _, al := range toUpdate {
|
for _, al := range toUpdate {
|
||||||
_, err := o.Update(&al, "name", "album_count")
|
_, err := o.Update(&al)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -13,18 +13,47 @@ var _ = Describe("ArtistRepository", func() {
|
|||||||
repo = NewArtistRepository()
|
repo = NewArtistRepository()
|
||||||
})
|
})
|
||||||
|
|
||||||
It("saves and retrieves data", func() {
|
Describe("Put/Get", func() {
|
||||||
Expect(repo.Get("1")).To(Equal(&model.Artist{ID: "1", Name: "Saara Saara", AlbumCount: 2}))
|
It("saves and retrieves data", func() {
|
||||||
|
Expect(repo.Get("1")).To(Equal(&artistSaaraSaara))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("overrides data if ID already exists", func() {
|
||||||
|
Expect(repo.Put(&model.Artist{ID: "1", Name: "Saara Saara is The Best!", AlbumCount: 3})).To(BeNil())
|
||||||
|
Expect(repo.Get("1")).To(Equal(&model.Artist{ID: "1", Name: "Saara Saara is The Best!", AlbumCount: 3}))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("returns ErrNotFound when the ID does not exist", func() {
|
||||||
|
_, err := repo.Get("999")
|
||||||
|
Expect(err).To(MatchError(model.ErrNotFound))
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
It("overrides data if ID already exists", func() {
|
Describe("GetIndex", func() {
|
||||||
Expect(repo.Put(&model.Artist{ID: "1", Name: "Saara Saara is The Best!", AlbumCount: 3})).To(BeNil())
|
It("returns the index", func() {
|
||||||
Expect(repo.Get("1")).To(Equal(&model.Artist{ID: "1", Name: "Saara Saara is The Best!", AlbumCount: 3}))
|
idx, err := repo.GetIndex()
|
||||||
})
|
Expect(err).To(BeNil())
|
||||||
|
Expect(idx).To(Equal(model.ArtistIndexes{
|
||||||
It("returns ErrNotFound when the ID does not exist", func() {
|
{
|
||||||
_, err := repo.Get("999")
|
ID: "B",
|
||||||
Expect(err).To(MatchError(model.ErrNotFound))
|
Artists: model.Artists{
|
||||||
|
artistBeatles,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "K",
|
||||||
|
Artists: model.Artists{
|
||||||
|
artistKraftwerk,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "S",
|
||||||
|
Artists: model.Artists{
|
||||||
|
{ID: "1", Name: "Saara Saara is The Best!", AlbumCount: 3},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Describe("PurgeInactive", func() {
|
Describe("PurgeInactive", func() {
|
||||||
|
@ -1,145 +0,0 @@
|
|||||||
package persistence
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/astaxie/beego/orm"
|
|
||||||
"github.com/cloudsonic/sonic-server/conf"
|
|
||||||
"github.com/cloudsonic/sonic-server/model"
|
|
||||||
"github.com/cloudsonic/sonic-server/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ArtistInfo struct {
|
|
||||||
ID int `orm:"pk;auto;column(id)"`
|
|
||||||
Idx string
|
|
||||||
ArtistID string `orm:"column(artist_id)"`
|
|
||||||
Artist string
|
|
||||||
AlbumCount int
|
|
||||||
}
|
|
||||||
|
|
||||||
type tempIndex map[string]model.ArtistInfo
|
|
||||||
|
|
||||||
type artistIndexRepository struct {
|
|
||||||
sqlRepository
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewArtistIndexRepository() model.ArtistIndexRepository {
|
|
||||||
r := &artistIndexRepository{}
|
|
||||||
r.tableName = "artist_info"
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *artistIndexRepository) Put(idx *model.ArtistIndex) error {
|
|
||||||
return withTx(func(o orm.Ormer) error {
|
|
||||||
_, err := r.newQuery(o).Filter("idx", idx.ID).Delete()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, artist := range idx.Artists {
|
|
||||||
a := ArtistInfo{
|
|
||||||
Idx: idx.ID,
|
|
||||||
ArtistID: artist.ArtistID,
|
|
||||||
Artist: artist.Artist,
|
|
||||||
AlbumCount: artist.AlbumCount,
|
|
||||||
}
|
|
||||||
err := r.insert(o, &a)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *artistIndexRepository) Refresh() error {
|
|
||||||
o := Db()
|
|
||||||
|
|
||||||
indexGroups := utils.ParseIndexGroups(conf.Sonic.IndexGroups)
|
|
||||||
artistIndex := make(map[string]tempIndex)
|
|
||||||
|
|
||||||
var artists []Artist
|
|
||||||
_, err := o.QueryTable(&Artist{}).All(&artists)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, ar := range artists {
|
|
||||||
r.collectIndex(indexGroups, &ar, artistIndex)
|
|
||||||
}
|
|
||||||
|
|
||||||
return r.saveIndex(artistIndex)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *artistIndexRepository) collectIndex(ig utils.IndexGroups, a *Artist, artistIndex map[string]tempIndex) {
|
|
||||||
name := a.Name
|
|
||||||
indexName := strings.ToLower(utils.NoArticle(name))
|
|
||||||
if indexName == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
group := r.findGroup(ig, indexName)
|
|
||||||
artists := artistIndex[group]
|
|
||||||
if artists == nil {
|
|
||||||
artists = make(tempIndex)
|
|
||||||
artistIndex[group] = artists
|
|
||||||
}
|
|
||||||
artists[indexName] = model.ArtistInfo{ArtistID: a.ID, Artist: a.Name, AlbumCount: a.AlbumCount}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *artistIndexRepository) findGroup(ig utils.IndexGroups, name string) string {
|
|
||||||
for k, v := range ig {
|
|
||||||
key := strings.ToLower(k)
|
|
||||||
if strings.HasPrefix(name, key) {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "#"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *artistIndexRepository) saveIndex(artistIndex map[string]tempIndex) error {
|
|
||||||
r.DeleteAll()
|
|
||||||
for k, temp := range artistIndex {
|
|
||||||
idx := &model.ArtistIndex{ID: k}
|
|
||||||
for _, v := range temp {
|
|
||||||
idx.Artists = append(idx.Artists, v)
|
|
||||||
}
|
|
||||||
err := r.Put(idx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *artistIndexRepository) GetAll() (model.ArtistIndexes, error) {
|
|
||||||
var all []ArtistInfo
|
|
||||||
_, err := r.newQuery(Db()).OrderBy("idx", "artist").All(&all)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
fullIdx := make(map[string]*model.ArtistIndex)
|
|
||||||
for _, a := range all {
|
|
||||||
idx, ok := fullIdx[a.Idx]
|
|
||||||
if !ok {
|
|
||||||
idx = &model.ArtistIndex{ID: a.Idx}
|
|
||||||
fullIdx[a.Idx] = idx
|
|
||||||
}
|
|
||||||
idx.Artists = append(idx.Artists, model.ArtistInfo{
|
|
||||||
ArtistID: a.ArtistID,
|
|
||||||
Artist: a.Artist,
|
|
||||||
AlbumCount: a.AlbumCount,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
var result model.ArtistIndexes
|
|
||||||
for _, idx := range fullIdx {
|
|
||||||
result = append(result, *idx)
|
|
||||||
}
|
|
||||||
sort.Slice(result, func(i, j int) bool {
|
|
||||||
return result[i].ID < result[j].ID
|
|
||||||
})
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ model.ArtistIndexRepository = (*artistIndexRepository)(nil)
|
|
@ -1,40 +0,0 @@
|
|||||||
package persistence
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/cloudsonic/sonic-server/model"
|
|
||||||
. "github.com/onsi/ginkgo"
|
|
||||||
. "github.com/onsi/gomega"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ = Describe("Artist Index", func() {
|
|
||||||
var repo model.ArtistIndexRepository
|
|
||||||
|
|
||||||
BeforeEach(func() {
|
|
||||||
repo = NewArtistIndexRepository()
|
|
||||||
err := repo.DeleteAll()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
It("successfully persists data", func() {
|
|
||||||
idx1 := model.ArtistIndex{
|
|
||||||
ID: "D",
|
|
||||||
Artists: model.ArtistInfos{
|
|
||||||
{ArtistID: "4", Artist: "Doobie Brothers", AlbumCount: 2},
|
|
||||||
{ArtistID: "3", Artist: "The Doors", AlbumCount: 5},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
idx2 := model.ArtistIndex{
|
|
||||||
ID: "S",
|
|
||||||
Artists: model.ArtistInfos{
|
|
||||||
{ArtistID: "1", Artist: "Saara Saara", AlbumCount: 3},
|
|
||||||
{ArtistID: "2", Artist: "Seu Jorge", AlbumCount: 1},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
Expect(repo.Put(&idx1)).To(BeNil())
|
|
||||||
Expect(repo.Put(&idx2)).To(BeNil())
|
|
||||||
Expect(repo.GetAll()).To(Equal(model.ArtistIndexes{idx1, idx2}))
|
|
||||||
})
|
|
||||||
})
|
|
@ -1,40 +0,0 @@
|
|||||||
package persistence
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/cloudsonic/sonic-server/model"
|
|
||||||
)
|
|
||||||
|
|
||||||
func CreateMockArtistIndexRepo() *MockArtistIndex {
|
|
||||||
return &MockArtistIndex{}
|
|
||||||
}
|
|
||||||
|
|
||||||
type MockArtistIndex struct {
|
|
||||||
model.ArtistIndexRepository
|
|
||||||
data model.ArtistIndexes
|
|
||||||
err bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockArtistIndex) SetError(err bool) {
|
|
||||||
m.err = err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockArtistIndex) SetData(j string, length int) {
|
|
||||||
m.data = make(model.ArtistIndexes, length)
|
|
||||||
err := json.Unmarshal([]byte(j), &m.data)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("ERROR: ", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockArtistIndex) GetAll() (model.ArtistIndexes, error) {
|
|
||||||
if m.err {
|
|
||||||
return nil, errors.New("Error!")
|
|
||||||
}
|
|
||||||
return m.data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ model.ArtistIndexRepository = (*MockArtistIndex)(nil)
|
|
@ -74,7 +74,6 @@ func initORM(dbPath string) error {
|
|||||||
orm.RegisterModel(new(Artist))
|
orm.RegisterModel(new(Artist))
|
||||||
orm.RegisterModel(new(Album))
|
orm.RegisterModel(new(Album))
|
||||||
orm.RegisterModel(new(MediaFile))
|
orm.RegisterModel(new(MediaFile))
|
||||||
orm.RegisterModel(new(ArtistInfo))
|
|
||||||
orm.RegisterModel(new(Checksum))
|
orm.RegisterModel(new(Checksum))
|
||||||
orm.RegisterModel(new(Property))
|
orm.RegisterModel(new(Property))
|
||||||
orm.RegisterModel(new(Playlist))
|
orm.RegisterModel(new(Playlist))
|
||||||
|
@ -8,7 +8,6 @@ var Set = wire.NewSet(
|
|||||||
NewArtistRepository,
|
NewArtistRepository,
|
||||||
NewMediaFileRepository,
|
NewMediaFileRepository,
|
||||||
NewAlbumRepository,
|
NewAlbumRepository,
|
||||||
NewArtistIndexRepository,
|
|
||||||
NewCheckSumRepository,
|
NewCheckSumRepository,
|
||||||
NewPropertyRepository,
|
NewPropertyRepository,
|
||||||
NewPlaylistRepository,
|
NewPlaylistRepository,
|
||||||
|
@ -21,18 +21,16 @@ type Repositories struct {
|
|||||||
mediaFile model.MediaFileRepository
|
mediaFile model.MediaFileRepository
|
||||||
album model.AlbumRepository
|
album model.AlbumRepository
|
||||||
artist model.ArtistRepository
|
artist model.ArtistRepository
|
||||||
index model.ArtistIndexRepository
|
|
||||||
playlist model.PlaylistRepository
|
playlist model.PlaylistRepository
|
||||||
property model.PropertyRepository
|
property model.PropertyRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(mfRepo model.MediaFileRepository, albumRepo model.AlbumRepository, artistRepo model.ArtistRepository, idxRepo model.ArtistIndexRepository, plsRepo model.PlaylistRepository, folderRepo model.MediaFolderRepository, property model.PropertyRepository) *Scanner {
|
func New(mfRepo model.MediaFileRepository, albumRepo model.AlbumRepository, artistRepo model.ArtistRepository, plsRepo model.PlaylistRepository, folderRepo model.MediaFolderRepository, property model.PropertyRepository) *Scanner {
|
||||||
repos := Repositories{
|
repos := Repositories{
|
||||||
folder: folderRepo,
|
folder: folderRepo,
|
||||||
mediaFile: mfRepo,
|
mediaFile: mfRepo,
|
||||||
album: albumRepo,
|
album: albumRepo,
|
||||||
artist: artistRepo,
|
artist: artistRepo,
|
||||||
index: idxRepo,
|
|
||||||
playlist: plsRepo,
|
playlist: plsRepo,
|
||||||
property: property,
|
property: property,
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,6 @@ var _ = XDescribe("TODO: REMOVE", func() {
|
|||||||
mediaFile: persistence.NewMediaFileRepository(),
|
mediaFile: persistence.NewMediaFileRepository(),
|
||||||
album: persistence.NewAlbumRepository(),
|
album: persistence.NewAlbumRepository(),
|
||||||
artist: persistence.NewArtistRepository(),
|
artist: persistence.NewArtistRepository(),
|
||||||
index: persistence.NewArtistIndexRepository(),
|
|
||||||
playlist: nil,
|
playlist: nil,
|
||||||
}
|
}
|
||||||
It("WORKS!", func() {
|
It("WORKS!", func() {
|
||||||
|
@ -79,11 +79,6 @@ func (s *TagScanner) Scan(ctx context.Context, lastModifiedSince time.Time) erro
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.repos.index.Refresh()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,13 +4,11 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/cloudsonic/sonic-server/conf"
|
"github.com/cloudsonic/sonic-server/conf"
|
||||||
"github.com/cloudsonic/sonic-server/log"
|
"github.com/cloudsonic/sonic-server/log"
|
||||||
"github.com/cloudsonic/sonic-server/model"
|
"github.com/cloudsonic/sonic-server/model"
|
||||||
"github.com/cloudsonic/sonic-server/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Scanner interface {
|
type Scanner interface {
|
||||||
@ -21,29 +19,25 @@ type Scanner interface {
|
|||||||
Playlists() map[string]*model.Playlist
|
Playlists() map[string]*model.Playlist
|
||||||
}
|
}
|
||||||
|
|
||||||
type tempIndex map[string]model.ArtistInfo
|
|
||||||
|
|
||||||
type Importer struct {
|
type Importer struct {
|
||||||
scanner Scanner
|
scanner Scanner
|
||||||
mediaFolder string
|
mediaFolder string
|
||||||
mfRepo model.MediaFileRepository
|
mfRepo model.MediaFileRepository
|
||||||
albumRepo model.AlbumRepository
|
albumRepo model.AlbumRepository
|
||||||
artistRepo model.ArtistRepository
|
artistRepo model.ArtistRepository
|
||||||
idxRepo model.ArtistIndexRepository
|
|
||||||
plsRepo model.PlaylistRepository
|
plsRepo model.PlaylistRepository
|
||||||
propertyRepo model.PropertyRepository
|
propertyRepo model.PropertyRepository
|
||||||
lastScan time.Time
|
lastScan time.Time
|
||||||
lastCheck time.Time
|
lastCheck time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewImporter(mediaFolder string, scanner Scanner, mfRepo model.MediaFileRepository, albumRepo model.AlbumRepository, artistRepo model.ArtistRepository, idxRepo model.ArtistIndexRepository, plsRepo model.PlaylistRepository, propertyRepo model.PropertyRepository) *Importer {
|
func NewImporter(mediaFolder string, scanner Scanner, mfRepo model.MediaFileRepository, albumRepo model.AlbumRepository, artistRepo model.ArtistRepository, plsRepo model.PlaylistRepository, propertyRepo model.PropertyRepository) *Importer {
|
||||||
return &Importer{
|
return &Importer{
|
||||||
scanner: scanner,
|
scanner: scanner,
|
||||||
mediaFolder: mediaFolder,
|
mediaFolder: mediaFolder,
|
||||||
mfRepo: mfRepo,
|
mfRepo: mfRepo,
|
||||||
albumRepo: albumRepo,
|
albumRepo: albumRepo,
|
||||||
artistRepo: artistRepo,
|
artistRepo: artistRepo,
|
||||||
idxRepo: idxRepo,
|
|
||||||
plsRepo: plsRepo,
|
plsRepo: plsRepo,
|
||||||
propertyRepo: propertyRepo,
|
propertyRepo: propertyRepo,
|
||||||
}
|
}
|
||||||
@ -131,7 +125,6 @@ func (i *Importer) importLibrary() (err error) {
|
|||||||
log.Debug("Imported artists", "total", len(ars))
|
log.Debug("Imported artists", "total", len(ars))
|
||||||
pls := i.importPlaylists()
|
pls := i.importPlaylists()
|
||||||
log.Debug("Imported playlists", "total", len(pls))
|
log.Debug("Imported playlists", "total", len(pls))
|
||||||
i.importArtistIndex()
|
|
||||||
|
|
||||||
log.Debug("Purging old data")
|
log.Debug("Purging old data")
|
||||||
if err := i.mfRepo.PurgeInactive(mfs); err != nil {
|
if err := i.mfRepo.PurgeInactive(mfs); err != nil {
|
||||||
@ -239,19 +232,6 @@ func (i *Importer) importArtists() model.Artists {
|
|||||||
return ars
|
return ars
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Importer) importArtistIndex() {
|
|
||||||
indexGroups := utils.ParseIndexGroups(conf.Sonic.IndexGroups)
|
|
||||||
artistIndex := make(map[string]tempIndex)
|
|
||||||
|
|
||||||
for _, ar := range i.scanner.Artists() {
|
|
||||||
i.collectIndex(indexGroups, ar, artistIndex)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := i.saveIndex(artistIndex); err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *Importer) importPlaylists() model.Playlists {
|
func (i *Importer) importPlaylists() model.Playlists {
|
||||||
pls := make(model.Playlists, len(i.scanner.Playlists()))
|
pls := make(model.Playlists, len(i.scanner.Playlists()))
|
||||||
j := 0
|
j := 0
|
||||||
@ -267,44 +247,3 @@ func (i *Importer) importPlaylists() model.Playlists {
|
|||||||
}
|
}
|
||||||
return pls
|
return pls
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Importer) collectIndex(ig utils.IndexGroups, a *model.Artist, artistIndex map[string]tempIndex) {
|
|
||||||
name := a.Name
|
|
||||||
indexName := strings.ToLower(utils.NoArticle(name))
|
|
||||||
if indexName == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
group := i.findGroup(ig, indexName)
|
|
||||||
artists := artistIndex[group]
|
|
||||||
if artists == nil {
|
|
||||||
artists = make(tempIndex)
|
|
||||||
artistIndex[group] = artists
|
|
||||||
}
|
|
||||||
artists[indexName] = model.ArtistInfo{ArtistID: a.ID, Artist: a.Name, AlbumCount: a.AlbumCount}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *Importer) findGroup(ig utils.IndexGroups, name string) string {
|
|
||||||
for k, v := range ig {
|
|
||||||
key := strings.ToLower(k)
|
|
||||||
if strings.HasPrefix(name, key) {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "#"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *Importer) saveIndex(artistIndex map[string]tempIndex) error {
|
|
||||||
i.idxRepo.DeleteAll()
|
|
||||||
for k, temp := range artistIndex {
|
|
||||||
idx := &model.ArtistIndex{ID: k}
|
|
||||||
for _, v := range temp {
|
|
||||||
idx.Artists = append(idx.Artists, v)
|
|
||||||
}
|
|
||||||
err := i.idxRepo.Put(idx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
@ -1,74 +0,0 @@
|
|||||||
package scanner_legacy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/cloudsonic/sonic-server/model"
|
|
||||||
"github.com/cloudsonic/sonic-server/tests"
|
|
||||||
"github.com/cloudsonic/sonic-server/utils"
|
|
||||||
. "github.com/smartystreets/goconvey/convey"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCollectIndex(t *testing.T) {
|
|
||||||
tests.Init(t, false)
|
|
||||||
|
|
||||||
ig := utils.IndexGroups{"A": "A", "B": "B", "Tom": "Tom", "X": "X-Z"}
|
|
||||||
|
|
||||||
importer := &Importer{}
|
|
||||||
|
|
||||||
Convey("Simple Name", t, func() {
|
|
||||||
a := &model.Artist{Name: "Björk"}
|
|
||||||
artistIndex := make(map[string]tempIndex)
|
|
||||||
|
|
||||||
importer.collectIndex(ig, a, artistIndex)
|
|
||||||
|
|
||||||
So(artistIndex, ShouldContainKey, "B")
|
|
||||||
So(artistIndex["B"], ShouldContainKey, "björk")
|
|
||||||
|
|
||||||
for _, k := range []string{"A", "Tom", "X-Z", "#"} {
|
|
||||||
So(artistIndex, ShouldNotContainKey, k)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
Convey("Name not in the index", t, func() {
|
|
||||||
a := &model.Artist{Name: "Kraftwerk"}
|
|
||||||
artistIndex := make(map[string]tempIndex)
|
|
||||||
|
|
||||||
importer.collectIndex(ig, a, artistIndex)
|
|
||||||
|
|
||||||
So(artistIndex, ShouldContainKey, "#")
|
|
||||||
So(artistIndex["#"], ShouldContainKey, "kraftwerk")
|
|
||||||
|
|
||||||
for _, k := range []string{"A", "B", "Tom", "X-Z"} {
|
|
||||||
So(artistIndex, ShouldNotContainKey, k)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
Convey("Name starts with an article", t, func() {
|
|
||||||
a := &model.Artist{Name: "The The"}
|
|
||||||
artistIndex := make(map[string]tempIndex)
|
|
||||||
|
|
||||||
importer.collectIndex(ig, a, artistIndex)
|
|
||||||
|
|
||||||
So(artistIndex, ShouldContainKey, "#")
|
|
||||||
So(artistIndex["#"], ShouldContainKey, "the")
|
|
||||||
|
|
||||||
for _, k := range []string{"A", "B", "Tom", "X-Z"} {
|
|
||||||
So(artistIndex, ShouldNotContainKey, k)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
Convey("Name match a multichar entry", t, func() {
|
|
||||||
a := &model.Artist{Name: "Tom Waits"}
|
|
||||||
artistIndex := make(map[string]tempIndex)
|
|
||||||
|
|
||||||
importer.collectIndex(ig, a, artistIndex)
|
|
||||||
|
|
||||||
So(artistIndex, ShouldContainKey, "Tom")
|
|
||||||
So(artistIndex["Tom"], ShouldContainKey, "tom waits")
|
|
||||||
|
|
||||||
for _, k := range []string{"A", "B", "X-Z", "#"} {
|
|
||||||
So(artistIndex, ShouldNotContainKey, k)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
@ -24,12 +24,11 @@ func CreateApp(musicFolder string) *server.Server {
|
|||||||
mediaFileRepository := persistence.NewMediaFileRepository()
|
mediaFileRepository := persistence.NewMediaFileRepository()
|
||||||
albumRepository := persistence.NewAlbumRepository()
|
albumRepository := persistence.NewAlbumRepository()
|
||||||
artistRepository := persistence.NewArtistRepository()
|
artistRepository := persistence.NewArtistRepository()
|
||||||
artistIndexRepository := persistence.NewArtistIndexRepository()
|
|
||||||
playlistRepository := persistence.NewPlaylistRepository()
|
playlistRepository := persistence.NewPlaylistRepository()
|
||||||
propertyRepository := persistence.NewPropertyRepository()
|
propertyRepository := persistence.NewPropertyRepository()
|
||||||
importer := scanner_legacy.NewImporter(musicFolder, itunesScanner, mediaFileRepository, albumRepository, artistRepository, artistIndexRepository, playlistRepository, propertyRepository)
|
importer := scanner_legacy.NewImporter(musicFolder, itunesScanner, mediaFileRepository, albumRepository, artistRepository, playlistRepository, propertyRepository)
|
||||||
mediaFolderRepository := persistence.NewMediaFolderRepository()
|
mediaFolderRepository := persistence.NewMediaFolderRepository()
|
||||||
scannerScanner := scanner.New(mediaFileRepository, albumRepository, artistRepository, artistIndexRepository, playlistRepository, mediaFolderRepository, propertyRepository)
|
scannerScanner := scanner.New(mediaFileRepository, albumRepository, artistRepository, playlistRepository, mediaFolderRepository, propertyRepository)
|
||||||
serverServer := server.New(importer, scannerScanner)
|
serverServer := server.New(importer, scannerScanner)
|
||||||
return serverServer
|
return serverServer
|
||||||
}
|
}
|
||||||
@ -37,12 +36,11 @@ func CreateApp(musicFolder string) *server.Server {
|
|||||||
func CreateSubsonicAPIRouter() *api.Router {
|
func CreateSubsonicAPIRouter() *api.Router {
|
||||||
propertyRepository := persistence.NewPropertyRepository()
|
propertyRepository := persistence.NewPropertyRepository()
|
||||||
mediaFolderRepository := persistence.NewMediaFolderRepository()
|
mediaFolderRepository := persistence.NewMediaFolderRepository()
|
||||||
artistIndexRepository := persistence.NewArtistIndexRepository()
|
|
||||||
artistRepository := persistence.NewArtistRepository()
|
artistRepository := persistence.NewArtistRepository()
|
||||||
albumRepository := persistence.NewAlbumRepository()
|
albumRepository := persistence.NewAlbumRepository()
|
||||||
mediaFileRepository := persistence.NewMediaFileRepository()
|
mediaFileRepository := persistence.NewMediaFileRepository()
|
||||||
genreRepository := persistence.NewGenreRepository()
|
genreRepository := persistence.NewGenreRepository()
|
||||||
browser := engine.NewBrowser(propertyRepository, mediaFolderRepository, artistIndexRepository, artistRepository, albumRepository, mediaFileRepository, genreRepository)
|
browser := engine.NewBrowser(propertyRepository, mediaFolderRepository, artistRepository, albumRepository, mediaFileRepository, genreRepository)
|
||||||
cover := engine.NewCover(mediaFileRepository, albumRepository)
|
cover := engine.NewCover(mediaFileRepository, albumRepository)
|
||||||
nowPlayingRepository := persistence.NewNowPlayingRepository()
|
nowPlayingRepository := persistence.NewNowPlayingRepository()
|
||||||
listGenerator := engine.NewListGenerator(albumRepository, mediaFileRepository, nowPlayingRepository)
|
listGenerator := engine.NewListGenerator(albumRepository, mediaFileRepository, nowPlayingRepository)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user