support fetching artist bio from filesystem
This commit is contained in:
parent
1490e9c1e6
commit
4621e9106f
|
@ -229,7 +229,7 @@ func disableExternalServices() {
|
|||
Server.LastFM.Enabled = false
|
||||
Server.Spotify.ID = ""
|
||||
Server.ListenBrainz.Enabled = false
|
||||
Server.Agents = ""
|
||||
Server.Agents = "filesystem"
|
||||
if Server.UILoginBackgroundURL == consts.DefaultUILoginBackgroundURL {
|
||||
Server.UILoginBackgroundURL = consts.DefaultUILoginBackgroundURLOffline
|
||||
}
|
||||
|
@ -336,7 +336,7 @@ func init() {
|
|||
viper.SetDefault("scanner.genreseparators", ";/,")
|
||||
viper.SetDefault("scanner.groupalbumreleases", false)
|
||||
|
||||
viper.SetDefault("agents", "lastfm,spotify,local,lrclib")
|
||||
viper.SetDefault("agents", "filesystem,lastfm,spotify,lrclib")
|
||||
viper.SetDefault("lastfm.enabled", true)
|
||||
viper.SetDefault("lastfm.language", "en")
|
||||
viper.SetDefault("lastfm.apikey", "")
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
package filesystem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/navidrome/navidrome/conf"
|
||||
"github.com/navidrome/navidrome/consts"
|
||||
"github.com/navidrome/navidrome/core/agents"
|
||||
"github.com/navidrome/navidrome/model"
|
||||
"github.com/navidrome/navidrome/utils"
|
||||
)
|
||||
|
||||
const filesystemAgentName = "filesystem"
|
||||
|
||||
var (
|
||||
supportedExtensions = []string{"lrc", "txt"}
|
||||
)
|
||||
|
||||
type filesystemAgent struct {
|
||||
ds model.DataStore
|
||||
}
|
||||
|
||||
func filesystemConstructor(ds model.DataStore) *filesystemAgent {
|
||||
return &filesystemAgent{
|
||||
ds: ds,
|
||||
}
|
||||
}
|
||||
|
||||
func (f *filesystemAgent) AgentName() string {
|
||||
return filesystemAgentName
|
||||
}
|
||||
|
||||
func (f *filesystemAgent) GetArtistBiography(ctx context.Context, id, name, mbid string) (string, error) {
|
||||
_, err := f.ds.Artist(ctx).Get(id)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
als, err := f.ds.Album(ctx).GetAll(model.QueryOptions{Filters: squirrel.Eq{"album_artist_id": id}})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var paths []string
|
||||
for _, al := range als {
|
||||
paths = append(paths, strings.Split(al.Paths, consts.Zwsp)...)
|
||||
}
|
||||
artistFolder := utils.LongestCommonPrefix(paths)
|
||||
if !strings.HasSuffix(artistFolder, string(filepath.Separator)) {
|
||||
artistFolder, _ = filepath.Split(artistFolder)
|
||||
}
|
||||
artistBioPath := filepath.Join(artistFolder, "artist.txt")
|
||||
contents, err := os.ReadFile(artistBioPath)
|
||||
if err != nil {
|
||||
return "", agents.ErrNotFound
|
||||
}
|
||||
return string(contents), nil
|
||||
}
|
||||
|
||||
func (f *filesystemAgent) GetSongLyrics(ctx context.Context, mf *model.MediaFile) (model.LyricList, error) {
|
||||
lyrics := model.LyricList{}
|
||||
extension := filepath.Ext(mf.Path)
|
||||
basePath := mf.Path[0 : len(mf.Path)-len(extension)]
|
||||
|
||||
for _, ext := range supportedExtensions {
|
||||
lrcPath := fmt.Sprintf("%s.%s", basePath, ext)
|
||||
contents, err := os.ReadFile(lrcPath)
|
||||
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
lyric, err := model.ToLyrics("xxx", string(contents))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lyrics = append(lyrics, *lyric)
|
||||
}
|
||||
|
||||
return lyrics, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
conf.AddHook(func() {
|
||||
agents.Register(filesystemAgentName, func(ds model.DataStore) agents.Interface {
|
||||
return filesystemConstructor(ds)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
var _ agents.ArtistBiographyRetriever = (*filesystemAgent)(nil)
|
||||
var _ agents.LyricsRetriever = (*filesystemAgent)(nil)
|
|
@ -1,8 +1,9 @@
|
|||
package agents
|
||||
package filesystem
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/navidrome/navidrome/core/agents"
|
||||
"github.com/navidrome/navidrome/model"
|
||||
"github.com/navidrome/navidrome/tests"
|
||||
. "github.com/navidrome/navidrome/utils/gg"
|
||||
|
@ -13,23 +14,73 @@ import (
|
|||
var _ = Describe("localAgent", func() {
|
||||
var ds model.DataStore
|
||||
var ctx context.Context
|
||||
var agent Interface
|
||||
var agent *filesystemAgent
|
||||
|
||||
BeforeEach(func() {
|
||||
ds = &tests.MockDataStore{}
|
||||
ctx = context.Background()
|
||||
agent = localsConstructor(ds)
|
||||
agent = filesystemConstructor(ds)
|
||||
})
|
||||
|
||||
Describe("GetArtistBiography", func() {
|
||||
BeforeEach(func() {
|
||||
ds.Artist(ctx).(*tests.MockArtistRepo).SetData(model.Artists{
|
||||
model.Artist{ID: "ar-1234",
|
||||
Name: "artist"},
|
||||
})
|
||||
})
|
||||
|
||||
It("should fetch artist biography", func() {
|
||||
|
||||
ds.Album(ctx).(*tests.MockAlbumRepo).SetData(model.Albums{
|
||||
model.Album{ID: "al-1234",
|
||||
AlbumArtistID: "ar-1234",
|
||||
Name: "album",
|
||||
Paths: "tests/fixtures/artist/an-album",
|
||||
},
|
||||
})
|
||||
|
||||
bio, err := agent.GetArtistBiography(ctx, "ar-1234", "album", "")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bio).To(Equal("This is an artist biography"))
|
||||
})
|
||||
|
||||
It("should fetch artist biography with slash", func() {
|
||||
ds.Album(ctx).(*tests.MockAlbumRepo).SetData(model.Albums{
|
||||
model.Album{ID: "al-1234",
|
||||
AlbumArtistID: "ar-1234",
|
||||
Name: "album",
|
||||
Paths: "tests/fixtures/artist/",
|
||||
},
|
||||
})
|
||||
|
||||
bio, err := agent.GetArtistBiography(ctx, "ar-1234", "album", "")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bio).To(Equal("This is an artist biography"))
|
||||
})
|
||||
|
||||
It("should error when file doesn't exist", func() {
|
||||
ds.Album(ctx).(*tests.MockAlbumRepo).SetData(model.Albums{
|
||||
model.Album{ID: "al-1234",
|
||||
AlbumArtistID: "ar-1234",
|
||||
Name: "album",
|
||||
Paths: "tests/fixtures/fake-artist/fake-album",
|
||||
},
|
||||
})
|
||||
|
||||
bio, err := agent.GetArtistBiography(ctx, "ar-1234", "album", "")
|
||||
Expect(err).To(Equal(agents.ErrNotFound))
|
||||
Expect(bio).To(Equal(""))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("GetSongLyrics", func() {
|
||||
It("should parse LRC file", func() {
|
||||
lyricFetcher, _ := agent.(LyricsRetriever)
|
||||
|
||||
mf := model.MediaFile{
|
||||
Path: "tests/fixtures/01 Invisible (RED) Edit Version.mp3",
|
||||
}
|
||||
|
||||
lyrics, err := lyricFetcher.GetSongLyrics(ctx, &mf)
|
||||
lyrics, err := agent.GetSongLyrics(ctx, &mf)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(lyrics).To(Equal(model.LyricList{
|
||||
{
|
||||
|
@ -48,13 +99,11 @@ var _ = Describe("localAgent", func() {
|
|||
})
|
||||
|
||||
It("should parse both LRC and TXT", func() {
|
||||
lyricFetcher, _ := agent.(LyricsRetriever)
|
||||
|
||||
mf := model.MediaFile{
|
||||
Path: "tests/fixtures/test.wav",
|
||||
}
|
||||
|
||||
lyrics, err := lyricFetcher.GetSongLyrics(ctx, &mf)
|
||||
lyrics, err := agent.GetSongLyrics(ctx, &mf)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(lyrics).To(Equal(model.LyricList{
|
||||
{
|
|
@ -0,0 +1,17 @@
|
|||
package filesystem
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/navidrome/navidrome/log"
|
||||
"github.com/navidrome/navidrome/tests"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestFilesystem(t *testing.T) {
|
||||
tests.Init(t, false)
|
||||
log.SetLevel(log.LevelFatal)
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Spotify Test Suite")
|
||||
}
|
|
@ -2,9 +2,6 @@ package agents
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/navidrome/navidrome/model"
|
||||
|
@ -12,10 +9,6 @@ import (
|
|||
|
||||
const LocalAgentName = "local"
|
||||
|
||||
var (
|
||||
supportedExtensions = []string{"lrc", "txt"}
|
||||
)
|
||||
|
||||
type localAgent struct {
|
||||
ds model.DataStore
|
||||
}
|
||||
|
@ -54,33 +47,8 @@ func (p *localAgent) GetArtistTopSongs(ctx context.Context, id, artistName, mbid
|
|||
return result, nil
|
||||
}
|
||||
|
||||
func (p *localAgent) GetSongLyrics(ctx context.Context, mf *model.MediaFile) (model.LyricList, error) {
|
||||
lyrics := model.LyricList{}
|
||||
extension := filepath.Ext(mf.Path)
|
||||
basePath := mf.Path[0 : len(mf.Path)-len(extension)]
|
||||
|
||||
for _, ext := range supportedExtensions {
|
||||
lrcPath := fmt.Sprintf("%s.%s", basePath, ext)
|
||||
contents, err := os.ReadFile(lrcPath)
|
||||
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
lyric, err := model.ToLyrics("xxx", string(contents))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lyrics = append(lyrics, *lyric)
|
||||
}
|
||||
|
||||
return lyrics, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
Register(LocalAgentName, localsConstructor)
|
||||
}
|
||||
|
||||
var _ ArtistTopSongsRetriever = (*localAgent)(nil)
|
||||
var _ LyricsRetriever = (*localAgent)(nil)
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/deluan/sanitize"
|
||||
"github.com/navidrome/navidrome/conf"
|
||||
"github.com/navidrome/navidrome/core/agents"
|
||||
_ "github.com/navidrome/navidrome/core/agents/filesystem"
|
||||
_ "github.com/navidrome/navidrome/core/agents/lastfm"
|
||||
_ "github.com/navidrome/navidrome/core/agents/listenbrainz"
|
||||
_ "github.com/navidrome/navidrome/core/agents/lrclib"
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
This is an artist biography
|
Loading…
Reference in New Issue