mirror of
https://github.com/navidrome/navidrome.git
synced 2025-04-18 21:07:44 +03:00
Implemented artists indexing, with Gomate
This commit is contained in:
parent
7c75084249
commit
df957814a0
@ -9,6 +9,7 @@ github.com/smartystreets/goconvey = commit:899ed5a
|
|||||||
github.com/karlkfi/inject = commit:fe06da2
|
github.com/karlkfi/inject = commit:fe06da2
|
||||||
github.com/dhowden/tag = commit:bccc91e
|
github.com/dhowden/tag = commit:bccc91e
|
||||||
github.com/nfnt/resize = commit:4d93a29
|
github.com/nfnt/resize = commit:4d93a29
|
||||||
|
github.com/deluan/gomate =
|
||||||
|
|
||||||
[res]
|
[res]
|
||||||
include = conf
|
include = conf
|
||||||
|
@ -16,6 +16,7 @@ install:
|
|||||||
- go get github.com/smartystreets/goconvey
|
- go get github.com/smartystreets/goconvey
|
||||||
- go get github.com/dhowden/tag
|
- go get github.com/dhowden/tag
|
||||||
- go get github.com/nfnt/resize
|
- go get github.com/nfnt/resize
|
||||||
|
- go get github.com/deluan/gomate
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- go test ./... -v
|
- go test ./... -v
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
package conf
|
package conf
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/deluan/gomate"
|
||||||
"github.com/deluan/gosonic/domain"
|
"github.com/deluan/gosonic/domain"
|
||||||
"github.com/deluan/gosonic/engine"
|
"github.com/deluan/gosonic/engine"
|
||||||
"github.com/deluan/gosonic/persistence"
|
"github.com/deluan/gosonic/persistence"
|
||||||
"github.com/deluan/gosonic/utils"
|
"github.com/deluan/gosonic/utils"
|
||||||
|
|
||||||
|
"github.com/deluan/gosonic/scanner"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -22,4 +25,11 @@ func init() {
|
|||||||
utils.DefineSingleton(new(engine.ListGenerator), engine.NewListGenerator)
|
utils.DefineSingleton(new(engine.ListGenerator), engine.NewListGenerator)
|
||||||
utils.DefineSingleton(new(engine.Cover), engine.NewCover)
|
utils.DefineSingleton(new(engine.Cover), engine.NewCover)
|
||||||
utils.DefineSingleton(new(engine.Playlists), engine.NewPlaylists)
|
utils.DefineSingleton(new(engine.Playlists), engine.NewPlaylists)
|
||||||
|
utils.DefineSingleton(new(engine.Search), engine.NewSearch)
|
||||||
|
|
||||||
|
// Other dependencies
|
||||||
|
utils.DefineSingleton(new(scanner.Scanner), scanner.NewItunesScanner)
|
||||||
|
utils.DefineSingleton(new(gomate.Indexer), func() gomate.Indexer {
|
||||||
|
return gomate.NewIndexer(gomate.NewLedisEmbeddedDB(persistence.Db()))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
29
engine/search.go
Normal file
29
engine/search.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package engine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/deluan/gomate"
|
||||||
|
"github.com/deluan/gosonic/domain"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Search interface {
|
||||||
|
IndexArtist(ar *domain.Artist) error
|
||||||
|
//IndexAlbum(al domain.Album) error
|
||||||
|
//IndexMediaFile(mf domain.MediaFile) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type search struct {
|
||||||
|
artistRepo domain.ArtistRepository
|
||||||
|
albumRepo domain.AlbumRepository
|
||||||
|
mfileRepo domain.MediaFileRepository
|
||||||
|
indexer gomate.Indexer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSearch(ar domain.ArtistRepository, alr domain.AlbumRepository, mr domain.MediaFileRepository, idx gomate.Indexer) Search {
|
||||||
|
return search{ar, alr, mr, idx}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s search) IndexArtist(ar *domain.Artist) error {
|
||||||
|
return s.indexer.Index(ar.Id, strings.ToLower(ar.Name))
|
||||||
|
}
|
@ -1,10 +1,11 @@
|
|||||||
package persistence
|
package persistence
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/astaxie/beego"
|
"github.com/astaxie/beego"
|
||||||
"github.com/siddontang/ledisdb/config"
|
"github.com/siddontang/ledisdb/config"
|
||||||
"github.com/siddontang/ledisdb/ledis"
|
"github.com/siddontang/ledisdb/ledis"
|
||||||
"sync"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -13,7 +14,7 @@ var (
|
|||||||
once sync.Once
|
once sync.Once
|
||||||
)
|
)
|
||||||
|
|
||||||
func db() *ledis.DB {
|
func Db() *ledis.DB {
|
||||||
once.Do(func() {
|
once.Do(func() {
|
||||||
config := config.NewConfigDefault()
|
config := config.NewConfigDefault()
|
||||||
config.DataDir = beego.AppConfig.String("dbPath")
|
config.DataDir = beego.AppConfig.String("dbPath")
|
||||||
@ -29,6 +30,6 @@ func db() *ledis.DB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func dropDb() {
|
func dropDb() {
|
||||||
db()
|
Db()
|
||||||
_ledisInstance.FlushAll()
|
_ledisInstance.FlushAll()
|
||||||
}
|
}
|
@ -41,13 +41,13 @@ func (r *ledisRepository) NewId(fields ...string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *ledisRepository) CountAll() (int64, error) {
|
func (r *ledisRepository) CountAll() (int64, error) {
|
||||||
size, err := db().ZCard([]byte(r.table + "s:all"))
|
size, err := Db().ZCard([]byte(r.table + "s:all"))
|
||||||
return size, err
|
return size, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ledisRepository) getAllIds() (map[string]bool, error) {
|
func (r *ledisRepository) getAllIds() (map[string]bool, error) {
|
||||||
m := make(map[string]bool)
|
m := make(map[string]bool)
|
||||||
pairs, err := db().ZRange([]byte(r.table+"s:all"), 0, -1)
|
pairs, err := Db().ZRange([]byte(r.table+"s:all"), 0, -1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return m, err
|
return m, err
|
||||||
}
|
}
|
||||||
@ -66,7 +66,7 @@ func (r *ledisRepository) DeleteAll(ids map[string]bool) error {
|
|||||||
// Delete from parent:parentId:table (ZSet)
|
// Delete from parent:parentId:table (ZSet)
|
||||||
if r.parentTable != "" {
|
if r.parentTable != "" {
|
||||||
parentKey := []byte(fmt.Sprintf("%s:%s:%s", r.table, id, r.parentIdField))
|
parentKey := []byte(fmt.Sprintf("%s:%s:%s", r.table, id, r.parentIdField))
|
||||||
pid, err := db().Get(parentKey)
|
pid, err := Db().Get(parentKey)
|
||||||
var parentId string
|
var parentId string
|
||||||
if err := json.Unmarshal(pid, &parentId); err != nil {
|
if err := json.Unmarshal(pid, &parentId); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -75,7 +75,7 @@ func (r *ledisRepository) DeleteAll(ids map[string]bool) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
parentKey = []byte(fmt.Sprintf("%s:%s:%ss", r.parentTable, parentId, r.table))
|
parentKey = []byte(fmt.Sprintf("%s:%s:%ss", r.parentTable, parentId, r.table))
|
||||||
if _, err := db().ZRem(parentKey, []byte(id)); err != nil {
|
if _, err := Db().ZRem(parentKey, []byte(id)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -90,19 +90,19 @@ func (r *ledisRepository) DeleteAll(ids map[string]bool) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delete from table:all (ZSet)
|
// Delete from table:all (ZSet)
|
||||||
_, err := db().ZRem([]byte(allKey), keys...)
|
_, err := Db().ZRem([]byte(allKey), keys...)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ledisRepository) deleteRecord(id string) error {
|
func (r *ledisRepository) deleteRecord(id string) error {
|
||||||
keys := r.getFieldKeys(id)
|
keys := r.getFieldKeys(id)
|
||||||
_, err := db().Del(keys...)
|
_, err := Db().Del(keys...)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ledisRepository) Exists(id string) (bool, error) {
|
func (r *ledisRepository) Exists(id string) (bool, error) {
|
||||||
res, _ := db().ZScore([]byte(r.table+"s:all"), []byte(id))
|
res, _ := Db().ZScore([]byte(r.table+"s:all"), []byte(id))
|
||||||
return res != ledis.InvalidScore, nil
|
return res != ledis.InvalidScore, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,19 +117,19 @@ func (r *ledisRepository) saveOrUpdate(id string, entity interface{}) error {
|
|||||||
for f, v := range h {
|
for f, v := range h {
|
||||||
key := recordPrefix + f
|
key := recordPrefix + f
|
||||||
value, _ := json.Marshal(v)
|
value, _ := json.Marshal(v)
|
||||||
if err := db().Set([]byte(key), value); err != nil {
|
if err := Db().Set([]byte(key), value); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sid := ledis.ScorePair{0, []byte(id)}
|
sid := ledis.ScorePair{0, []byte(id)}
|
||||||
if _, err = db().ZAdd([]byte(allKey), sid); err != nil {
|
if _, err = Db().ZAdd([]byte(allKey), sid); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if parentCollectionKey := r.getParentRelationKey(entity); parentCollectionKey != "" {
|
if parentCollectionKey := r.getParentRelationKey(entity); parentCollectionKey != "" {
|
||||||
_, err = db().ZAdd([]byte(parentCollectionKey), sid)
|
_, err = Db().ZAdd([]byte(parentCollectionKey), sid)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -184,7 +184,7 @@ func (r *ledisRepository) readEntity(id string) (interface{}, error) {
|
|||||||
|
|
||||||
fieldKeys := r.getFieldKeys(id)
|
fieldKeys := r.getFieldKeys(id)
|
||||||
|
|
||||||
res, err := db().MGet(fieldKeys...)
|
res, err := Db().MGet(fieldKeys...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -229,7 +229,7 @@ func (r *ledisRepository) loadFromSet(setName string, entities interface{}, qo .
|
|||||||
if o.SortBy != "" {
|
if o.SortBy != "" {
|
||||||
sortKey = []byte(fmt.Sprintf("%s:*:%s", r.table, o.SortBy))
|
sortKey = []byte(fmt.Sprintf("%s:*:%s", r.table, o.SortBy))
|
||||||
}
|
}
|
||||||
response, err := db().XZSort([]byte(setName), o.Offset, o.Size, o.Alpha, o.Desc, sortKey, r.getFieldKeys("*"))
|
response, err := Db().XZSort([]byte(setName), o.Offset, o.Size, o.Alpha, o.Desc, sortKey, r.getFieldKeys("*"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ import (
|
|||||||
"github.com/deluan/gosonic/consts"
|
"github.com/deluan/gosonic/consts"
|
||||||
"github.com/deluan/gosonic/domain"
|
"github.com/deluan/gosonic/domain"
|
||||||
"github.com/deluan/gosonic/engine"
|
"github.com/deluan/gosonic/engine"
|
||||||
"github.com/deluan/gosonic/persistence"
|
|
||||||
"github.com/deluan/gosonic/utils"
|
"github.com/deluan/gosonic/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -26,16 +25,16 @@ type tempIndex map[string]domain.ArtistInfo
|
|||||||
|
|
||||||
func StartImport() {
|
func StartImport() {
|
||||||
go func() {
|
go func() {
|
||||||
i := &Importer{
|
// TODO Move all to DI
|
||||||
scanner: &ItunesScanner{},
|
i := &Importer{mediaFolder: beego.AppConfig.String("musicFolder")}
|
||||||
mediaFolder: beego.AppConfig.String("musicFolder"),
|
utils.ResolveDependency(&i.mfRepo)
|
||||||
mfRepo: persistence.NewMediaFileRepository(),
|
utils.ResolveDependency(&i.albumRepo)
|
||||||
albumRepo: persistence.NewAlbumRepository(),
|
utils.ResolveDependency(&i.artistRepo)
|
||||||
artistRepo: persistence.NewArtistRepository(),
|
utils.ResolveDependency(&i.idxRepo)
|
||||||
idxRepo: persistence.NewArtistIndexRepository(),
|
utils.ResolveDependency(&i.plsRepo)
|
||||||
plsRepo: persistence.NewPlaylistRepository(),
|
utils.ResolveDependency(&i.propertyRepo)
|
||||||
propertyRepo: persistence.NewPropertyRepository(),
|
utils.ResolveDependency(&i.search)
|
||||||
}
|
utils.ResolveDependency(&i.scanner)
|
||||||
i.Run()
|
i.Run()
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
@ -50,6 +49,7 @@ type Importer struct {
|
|||||||
idxRepo domain.ArtistIndexRepository
|
idxRepo domain.ArtistIndexRepository
|
||||||
plsRepo domain.PlaylistRepository
|
plsRepo domain.PlaylistRepository
|
||||||
propertyRepo engine.PropertyRepository
|
propertyRepo engine.PropertyRepository
|
||||||
|
search engine.Search
|
||||||
lastScan time.Time
|
lastScan time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,6 +124,9 @@ func (i *Importer) importLibrary() (err error) {
|
|||||||
if err := i.artistRepo.Put(ar); err != nil {
|
if err := i.artistRepo.Put(ar); err != nil {
|
||||||
beego.Error(err)
|
beego.Error(err)
|
||||||
}
|
}
|
||||||
|
if err := i.search.IndexArtist(ar); err != nil {
|
||||||
|
beego.Error("Error indexing artist:", err)
|
||||||
|
}
|
||||||
i.collectIndex(indexGroups, ar, artistIndex)
|
i.collectIndex(indexGroups, ar, artistIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,6 +31,10 @@ type ItunesScanner struct {
|
|||||||
lastModifiedSince time.Time
|
lastModifiedSince time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewItunesScanner() *ItunesScanner {
|
||||||
|
return &ItunesScanner{}
|
||||||
|
}
|
||||||
|
|
||||||
type plsRelation struct {
|
type plsRelation struct {
|
||||||
pID string
|
pID string
|
||||||
parentPID string
|
parentPID string
|
||||||
|
@ -25,6 +25,10 @@ func DefineSingleton(ptr interface{}, constructor interface{}) interface{} {
|
|||||||
return ptr
|
return ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ResolveDependency(ptr interface{}) {
|
||||||
|
inject.ExtractAssignable(Graph, ptr)
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
definitions = make(map[reflect.Type]interface{})
|
definitions = make(map[reflect.Type]interface{})
|
||||||
Graph = inject.NewGraph()
|
Graph = inject.NewGraph()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user