mirror of
https://github.com/navidrome/navidrome.git
synced 2025-04-07 06:27:36 +03:00
Searching by artists, spike mode
This commit is contained in:
parent
29c2925a1c
commit
ef31d1aca0
9 changed files with 136 additions and 28 deletions
|
@ -3,10 +3,11 @@ package api
|
||||||
import (
|
import (
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/astaxie/beego"
|
"github.com/astaxie/beego"
|
||||||
"github.com/deluan/gosonic/api/responses"
|
"github.com/deluan/gosonic/api/responses"
|
||||||
"github.com/deluan/gosonic/utils"
|
"github.com/deluan/gosonic/utils"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type BaseAPIController struct{ beego.Controller }
|
type BaseAPIController struct{ beego.Controller }
|
||||||
|
@ -23,14 +24,18 @@ func (c *BaseAPIController) RequiredParamString(param string, msg string) string
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *BaseAPIController) ParamString(param string) string {
|
||||||
|
return c.Input().Get(param)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *BaseAPIController) ParamTime(param string) time.Time {
|
func (c *BaseAPIController) ParamTime(param string) time.Time {
|
||||||
var value int64
|
var value int64
|
||||||
c.Ctx.Input.Bind(&value, param)
|
c.Ctx.Input.Bind(&value, param)
|
||||||
return utils.ToTime(value)
|
return utils.ToTime(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *BaseAPIController) ParamInt(param string) int {
|
func (c *BaseAPIController) ParamInt(param string, def int) int {
|
||||||
var value int
|
value := def
|
||||||
c.Ctx.Input.Bind(&value, param)
|
c.Ctx.Input.Bind(&value, param)
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,8 +40,8 @@ func (c *GetAlbumListController) Get() {
|
||||||
c.SendError(responses.ERROR_GENERIC, "Not implemented!")
|
c.SendError(responses.ERROR_GENERIC, "Not implemented!")
|
||||||
}
|
}
|
||||||
|
|
||||||
offset := c.ParamInt("offset")
|
offset := c.ParamInt("offset", 0)
|
||||||
size := utils.MinInt(c.ParamInt("size"), 500)
|
size := utils.MinInt(c.ParamInt("size", 0), 500)
|
||||||
|
|
||||||
albums, err := method(offset, size)
|
albums, err := method(offset, size)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -19,7 +19,7 @@ func (c *GetCoverArtController) Prepare() {
|
||||||
|
|
||||||
func (c *GetCoverArtController) Get() {
|
func (c *GetCoverArtController) Get() {
|
||||||
id := c.RequiredParamString("id", "id parameter required")
|
id := c.RequiredParamString("id", "id parameter required")
|
||||||
size := c.ParamInt("size")
|
size := c.ParamInt("size", 0)
|
||||||
|
|
||||||
err := c.cover.Get(id, size, c.Ctx.ResponseWriter)
|
err := c.cover.Get(id, size, c.Ctx.ResponseWriter)
|
||||||
|
|
||||||
|
|
|
@ -6,18 +6,19 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Subsonic struct {
|
type Subsonic struct {
|
||||||
XMLName xml.Name `xml:"http://subsonic.org/restapi subsonic-response" json:"-"`
|
XMLName xml.Name `xml:"http://subsonic.org/restapi subsonic-response" json:"-"`
|
||||||
Status string `xml:"status,attr" json:"status"`
|
Status string `xml:"status,attr" json:"status"`
|
||||||
Version string `xml:"version,attr" json:"version"`
|
Version string `xml:"version,attr" json:"version"`
|
||||||
Error *Error `xml:"error,omitempty" json:"error,omitempty"`
|
Error *Error `xml:"error,omitempty" json:"error,omitempty"`
|
||||||
License *License `xml:"license,omitempty" json:"license,omitempty"`
|
License *License `xml:"license,omitempty" json:"license,omitempty"`
|
||||||
MusicFolders *MusicFolders `xml:"musicFolders,omitempty" json:"musicFolders,omitempty"`
|
MusicFolders *MusicFolders `xml:"musicFolders,omitempty" json:"musicFolders,omitempty"`
|
||||||
Indexes *Indexes `xml:"indexes,omitempty" json:"indexes,omitempty"`
|
Indexes *Indexes `xml:"indexes,omitempty" json:"indexes,omitempty"`
|
||||||
Directory *Directory `xml:"directory,omitempty" json:"directory,omitempty"`
|
Directory *Directory `xml:"directory,omitempty" json:"directory,omitempty"`
|
||||||
User *User `xml:"user,omitempty" json:"user,omitempty"`
|
User *User `xml:"user,omitempty" json:"user,omitempty"`
|
||||||
AlbumList *AlbumList `xml:"albumList,omitempty" json:"albumList,omitempty"`
|
AlbumList *AlbumList `xml:"albumList,omitempty" json:"albumList,omitempty"`
|
||||||
Playlists *Playlists `xml:"playlists,omitempty" json:"playlists,omitempty"`
|
Playlists *Playlists `xml:"playlists,omitempty" json:"playlists,omitempty"`
|
||||||
Playlist *PlaylistWithSongs `xml:"playlist,omitempty" json:"playlist,omitempty"`
|
Playlist *PlaylistWithSongs `xml:"playlist,omitempty" json:"playlist,omitempty"`
|
||||||
|
SearchResult2 *SearchResult2 `xml:"searchResult2,omitempty" json:"searchResult2,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type JsonWrapper struct {
|
type JsonWrapper struct {
|
||||||
|
@ -119,6 +120,12 @@ type PlaylistWithSongs struct {
|
||||||
Entry []Child `xml:"entry" json:"entry,omitempty"`
|
Entry []Child `xml:"entry" json:"entry,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SearchResult2 struct {
|
||||||
|
Artist []Artist `xml:"artist" json:"artist,omitempty"`
|
||||||
|
Album []Child `xml:"album" json:"album,omitempty"`
|
||||||
|
Song []Child `xml:"song" json:"song,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
Username string `xml:"username,attr" json:"username"`
|
Username string `xml:"username,attr" json:"username"`
|
||||||
Email string `xml:"email,attr,omitempty" json:"email,omitempty"`
|
Email string `xml:"email,attr,omitempty" json:"email,omitempty"`
|
||||||
|
|
43
api/search.go
Normal file
43
api/search.go
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/astaxie/beego"
|
||||||
|
"github.com/deluan/gosonic/api/responses"
|
||||||
|
"github.com/deluan/gosonic/engine"
|
||||||
|
"github.com/deluan/gosonic/utils"
|
||||||
|
"github.com/karlkfi/inject"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SearchingController struct {
|
||||||
|
BaseAPIController
|
||||||
|
search engine.Search
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SearchingController) Prepare() {
|
||||||
|
inject.ExtractAssignable(utils.Graph, &c.search)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SearchingController) Search2() {
|
||||||
|
query := c.RequiredParamString("query", "Parameter query required")
|
||||||
|
artistCount := c.ParamInt("artistCount", 20)
|
||||||
|
artistOffset := c.ParamInt("artistOffset", 0)
|
||||||
|
//albumCount := c.ParamInt("albumCount", 20)
|
||||||
|
//albumOffset := c.ParamInt("albumOffset", 0)
|
||||||
|
//songCount := c.ParamInt("songCount", 20)
|
||||||
|
//songOffset := c.ParamInt("songOffset", 0)
|
||||||
|
|
||||||
|
as, err := c.search.SearchArtist(query, artistOffset, artistCount)
|
||||||
|
if err != nil {
|
||||||
|
beego.Error("Error searching for Artists:", err)
|
||||||
|
c.SendError(responses.ERROR_GENERIC, "Internal Error")
|
||||||
|
}
|
||||||
|
|
||||||
|
response := c.NewEmpty()
|
||||||
|
searchResult2 := &responses.SearchResult2{}
|
||||||
|
searchResult2.Artist = make([]responses.Artist, len(*as))
|
||||||
|
for i, a := range *as {
|
||||||
|
searchResult2.Artist[i] = responses.Artist{Id: a.Id, Name: a.Name}
|
||||||
|
}
|
||||||
|
response.SearchResult2 = searchResult2
|
||||||
|
c.SendResponse(response)
|
||||||
|
}
|
|
@ -38,7 +38,7 @@ func (c *StreamController) Prepare() {
|
||||||
// TODO Still getting the "Conn.Write wrote more than the declared Content-Length" error.
|
// TODO Still getting the "Conn.Write wrote more than the declared Content-Length" error.
|
||||||
// Don't know if this causes any issues
|
// Don't know if this causes any issues
|
||||||
func (c *StreamController) Stream() {
|
func (c *StreamController) Stream() {
|
||||||
maxBitRate := c.ParamInt("maxBitRate")
|
maxBitRate := c.ParamInt("maxBitRate", 0)
|
||||||
maxBitRate = utils.MinInt(c.mf.BitRate, maxBitRate)
|
maxBitRate = utils.MinInt(c.mf.BitRate, maxBitRate)
|
||||||
|
|
||||||
beego.Debug("Streaming file", c.id, ":", c.mf.Path)
|
beego.Debug("Streaming file", c.id, ":", c.mf.Path)
|
||||||
|
|
|
@ -29,7 +29,13 @@ func init() {
|
||||||
|
|
||||||
// Other dependencies
|
// Other dependencies
|
||||||
utils.DefineSingleton(new(scanner.Scanner), scanner.NewItunesScanner)
|
utils.DefineSingleton(new(scanner.Scanner), scanner.NewItunesScanner)
|
||||||
utils.DefineSingleton(new(gomate.Indexer), func() gomate.Indexer {
|
utils.DefineSingleton(new(gomate.DB), func() gomate.DB {
|
||||||
return gomate.NewIndexer(gomate.NewLedisEmbeddedDB(persistence.Db()))
|
return gomate.NewLedisEmbeddedDB(persistence.Db())
|
||||||
})
|
})
|
||||||
|
//utils.DefineSingleton(new(gomate.Indexer), func() gomate.Indexer {
|
||||||
|
// return gomate.NewIndexer(gomate.NewLedisEmbeddedDB(persistence.Db()))
|
||||||
|
//})
|
||||||
|
//utils.DefineSingleton(new(gomate.Searcher), func() gomate.Searcher {
|
||||||
|
// return gomate.NewSearcher(gomate.NewLedisEmbeddedDB(persistence.Db()))
|
||||||
|
//})
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,8 @@ func mapEndpoints() {
|
||||||
beego.NSRouter("/getIndexes.view", &api.BrowsingController{}, "*:GetIndexes"),
|
beego.NSRouter("/getIndexes.view", &api.BrowsingController{}, "*:GetIndexes"),
|
||||||
beego.NSRouter("/getMusicDirectory.view", &api.BrowsingController{}, "*:GetDirectory"),
|
beego.NSRouter("/getMusicDirectory.view", &api.BrowsingController{}, "*:GetDirectory"),
|
||||||
|
|
||||||
|
beego.NSRouter("/search2.view", &api.SearchingController{}, "*:Search2"),
|
||||||
|
|
||||||
beego.NSRouter("/getCoverArt.view", &api.GetCoverArtController{}, "*:Get"),
|
beego.NSRouter("/getCoverArt.view", &api.GetCoverArtController{}, "*:Get"),
|
||||||
beego.NSRouter("/stream.view", &api.StreamController{}, "*:Stream"),
|
beego.NSRouter("/stream.view", &api.StreamController{}, "*:Stream"),
|
||||||
beego.NSRouter("/download.view", &api.StreamController{}, "*:Download"),
|
beego.NSRouter("/download.view", &api.StreamController{}, "*:Download"),
|
||||||
|
|
|
@ -12,31 +12,76 @@ type Search interface {
|
||||||
IndexArtist(ar *domain.Artist) error
|
IndexArtist(ar *domain.Artist) error
|
||||||
IndexAlbum(al *domain.Album) error
|
IndexAlbum(al *domain.Album) error
|
||||||
IndexMediaFile(mf *domain.MediaFile) error
|
IndexMediaFile(mf *domain.MediaFile) error
|
||||||
|
|
||||||
|
SearchArtist(q string, offset int, size int) (*domain.Artists, error)
|
||||||
|
//SearchAlbum(q string, offset int, size int) (*domain.Albums, error)
|
||||||
|
//SearchSong(q string, offset int, size int) (*domain.MediaFiles, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type search struct {
|
type search struct {
|
||||||
artistRepo domain.ArtistRepository
|
artistRepo domain.ArtistRepository
|
||||||
albumRepo domain.AlbumRepository
|
albumRepo domain.AlbumRepository
|
||||||
mfileRepo domain.MediaFileRepository
|
mfileRepo domain.MediaFileRepository
|
||||||
indexer gomate.Indexer
|
idxArtist gomate.Indexer
|
||||||
|
idxAlbum gomate.Indexer
|
||||||
|
idxSong gomate.Indexer
|
||||||
|
sArtist gomate.Searcher
|
||||||
|
sAlbum gomate.Searcher
|
||||||
|
sSong gomate.Searcher
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSearch(ar domain.ArtistRepository, alr domain.AlbumRepository, mr domain.MediaFileRepository, idx gomate.Indexer) Search {
|
func NewSearch(ar domain.ArtistRepository, alr domain.AlbumRepository, mr domain.MediaFileRepository, db gomate.DB) Search {
|
||||||
return search{ar, alr, mr, idx}
|
s := search{artistRepo: ar, albumRepo: alr, mfileRepo: mr}
|
||||||
|
s.idxArtist = gomate.NewIndexer(db, "gomate-artist-idx")
|
||||||
|
s.sArtist = gomate.NewSearcher(db, "gomate-artist-idx")
|
||||||
|
s.idxAlbum = gomate.NewIndexer(db, "gomate-album-idx")
|
||||||
|
s.sAlbum = gomate.NewSearcher(db, "gomate-album-idx")
|
||||||
|
s.idxSong = gomate.NewIndexer(db, "gomate-song-idx")
|
||||||
|
s.sSong = gomate.NewSearcher(db, "gomate-song-idx")
|
||||||
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s search) ClearAll() error {
|
func (s search) ClearAll() error {
|
||||||
return s.indexer.Clear()
|
return s.idxArtist.Clear()
|
||||||
|
return s.idxAlbum.Clear()
|
||||||
|
return s.idxSong.Clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s search) IndexArtist(ar *domain.Artist) error {
|
func (s search) IndexArtist(ar *domain.Artist) error {
|
||||||
return s.indexer.Index("ar-"+ar.Id, strings.ToLower(ar.Name))
|
return s.idxArtist.Index(ar.Id, strings.ToLower(ar.Name))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s search) IndexAlbum(al *domain.Album) error {
|
func (s search) IndexAlbum(al *domain.Album) error {
|
||||||
return s.indexer.Index("al-"+al.Id, strings.ToLower(al.Name))
|
return s.idxAlbum.Index(al.Id, strings.ToLower(al.Name))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s search) IndexMediaFile(mf *domain.MediaFile) error {
|
func (s search) IndexMediaFile(mf *domain.MediaFile) error {
|
||||||
return s.indexer.Index("mf-"+mf.Id, strings.ToLower(mf.Title))
|
return s.idxSong.Index(mf.Id, strings.ToLower(mf.Title))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s search) SearchArtist(q string, offset int, size int) (*domain.Artists, error) {
|
||||||
|
q = strings.TrimSuffix(q, "*")
|
||||||
|
res, err := s.sArtist.Search(q)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
as := make(domain.Artists, 0, len(res))
|
||||||
|
for _, id := range res {
|
||||||
|
a, err := s.artistRepo.Get(id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
as = append(as, *a)
|
||||||
|
}
|
||||||
|
return &as, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//func (s search) SearchAlbum(q string, offset int, size int) (*domain.Albums, error) {
|
||||||
|
// q := strings.TrimSuffix(q, "*")
|
||||||
|
// return nil
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//func (s search) SearchSong(q string, offset int, size int) (*domain.MediaFiles, error) {
|
||||||
|
// q := strings.TrimSuffix(q, "*")
|
||||||
|
// return nil
|
||||||
|
//}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue