make musicFolderIds ints (#169)
* make `musicFolderId`s ints
https://github.com/sentriz/gonic/issues/50#issuecomment-963257103
006c1dafee/Classes/Models/API%20Models/MediaFolder.swift (L16)
related #50
* add a test for multi folders and reused artists
This commit is contained in:
@@ -30,6 +30,7 @@ type Controller struct {
|
|||||||
CachePath string
|
CachePath string
|
||||||
CoverCachePath string
|
CoverCachePath string
|
||||||
PodcastsPath string
|
PodcastsPath string
|
||||||
|
MusicPaths []string
|
||||||
Jukebox *jukebox.Jukebox
|
Jukebox *jukebox.Jukebox
|
||||||
Scrobblers []scrobble.Scrobbler
|
Scrobblers []scrobble.Scrobbler
|
||||||
Podcasts *podcasts.Podcasts
|
Podcasts *podcasts.Podcasts
|
||||||
@@ -116,3 +117,14 @@ func (c *Controller) HR(h handlerSubsonicRaw) http.Handler {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Controller) getMusicFolder(p params.Params) string {
|
||||||
|
idx, err := p.GetInt("musicFolderId")
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if idx < 0 || idx > len(c.MusicPaths) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return c.MusicPaths[idx]
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -110,8 +111,13 @@ func makec(t *testing.T, roots []string) (*Controller, *mockfs.MockFS) {
|
|||||||
m.ResetDates()
|
m.ResetDates()
|
||||||
m.LogAlbums()
|
m.LogAlbums()
|
||||||
|
|
||||||
|
var absRoots []string
|
||||||
|
for _, root := range roots {
|
||||||
|
absRoots = append(absRoots, filepath.Join(m.TmpDir(), root))
|
||||||
|
}
|
||||||
|
|
||||||
base := &ctrlbase.Controller{DB: m.DB()}
|
base := &ctrlbase.Controller{DB: m.DB()}
|
||||||
return &Controller{Controller: base}, m
|
return &Controller{Controller: base, MusicPaths: absRoots}, m
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ func (c *Controller) ServeGetIndexes(r *http.Request) *spec.Response {
|
|||||||
Select("id").
|
Select("id").
|
||||||
Model(&db.Album{}).
|
Model(&db.Album{}).
|
||||||
Where("parent_id IS NULL")
|
Where("parent_id IS NULL")
|
||||||
if m, _ := params.Get("musicFolderId"); m != "" {
|
if m := c.getMusicFolder(params); m != "" {
|
||||||
rootQ = rootQ.
|
rootQ = rootQ.
|
||||||
Where("root_dir=?", m)
|
Where("root_dir=?", m)
|
||||||
}
|
}
|
||||||
@@ -145,7 +145,7 @@ func (c *Controller) ServeGetAlbumList(r *http.Request) *spec.Response {
|
|||||||
return spec.NewError(10, "unknown value `%s` for parameter 'type'", v)
|
return spec.NewError(10, "unknown value `%s` for parameter 'type'", v)
|
||||||
}
|
}
|
||||||
|
|
||||||
if m, _ := params.Get("musicFolderId"); m != "" {
|
if m := c.getMusicFolder(params); m != "" {
|
||||||
q = q.Where("root_dir=?", m)
|
q = q.Where("root_dir=?", m)
|
||||||
}
|
}
|
||||||
var folders []*db.Album
|
var folders []*db.Album
|
||||||
@@ -185,7 +185,7 @@ func (c *Controller) ServeSearchTwo(r *http.Request) *spec.Response {
|
|||||||
Select("id").
|
Select("id").
|
||||||
Model(&db.Album{}).
|
Model(&db.Album{}).
|
||||||
Where("parent_id IS NULL")
|
Where("parent_id IS NULL")
|
||||||
if m, _ := params.Get("musicFolderId"); m != "" {
|
if m := c.getMusicFolder(params); m != "" {
|
||||||
rootQ = rootQ.Where("root_dir=?", m)
|
rootQ = rootQ.Where("root_dir=?", m)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,7 +207,7 @@ func (c *Controller) ServeSearchTwo(r *http.Request) *spec.Response {
|
|||||||
Where(`tag_artist_id IS NOT NULL AND (right_path LIKE ? OR right_path_u_dec LIKE ?)`, query, query).
|
Where(`tag_artist_id IS NOT NULL AND (right_path LIKE ? OR right_path_u_dec LIKE ?)`, query, query).
|
||||||
Offset(params.GetOrInt("albumOffset", 0)).
|
Offset(params.GetOrInt("albumOffset", 0)).
|
||||||
Limit(params.GetOrInt("albumCount", 20))
|
Limit(params.GetOrInt("albumCount", 20))
|
||||||
if m, _ := params.Get("musicFolderId"); m != "" {
|
if m := c.getMusicFolder(params); m != "" {
|
||||||
q = q.Where("root_dir=?", m)
|
q = q.Where("root_dir=?", m)
|
||||||
}
|
}
|
||||||
if err := q.Find(&albums).Error; err != nil {
|
if err := q.Find(&albums).Error; err != nil {
|
||||||
@@ -224,7 +224,7 @@ func (c *Controller) ServeSearchTwo(r *http.Request) *spec.Response {
|
|||||||
Where("filename LIKE ? OR filename_u_dec LIKE ?", query, query).
|
Where("filename LIKE ? OR filename_u_dec LIKE ?", query, query).
|
||||||
Offset(params.GetOrInt("songOffset", 0)).
|
Offset(params.GetOrInt("songOffset", 0)).
|
||||||
Limit(params.GetOrInt("songCount", 20))
|
Limit(params.GetOrInt("songCount", 20))
|
||||||
if m, _ := params.Get("musicFolderId"); m != "" {
|
if m := c.getMusicFolder(params); m != "" {
|
||||||
q = q.
|
q = q.
|
||||||
Joins("JOIN albums ON albums.id=tracks.album_id").
|
Joins("JOIN albums ON albums.id=tracks.album_id").
|
||||||
Where("albums.root_dir=?", m)
|
Where("albums.root_dir=?", m)
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package ctrlsubsonic
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
_ "github.com/jinzhu/gorm/dialects/sqlite"
|
_ "github.com/jinzhu/gorm/dialects/sqlite"
|
||||||
@@ -14,8 +13,8 @@ func TestGetIndexes(t *testing.T) {
|
|||||||
|
|
||||||
runQueryCases(t, contr, contr.ServeGetIndexes, []*queryCase{
|
runQueryCases(t, contr, contr.ServeGetIndexes, []*queryCase{
|
||||||
{url.Values{}, "no_args", false},
|
{url.Values{}, "no_args", false},
|
||||||
{url.Values{"musicFolderId": {filepath.Join(m.TmpDir(), "m-0")}}, "with_music_folder_1", false},
|
{url.Values{"musicFolderId": {"0"}}, "with_music_folder_1", false},
|
||||||
{url.Values{"musicFolderId": {filepath.Join(m.TmpDir(), "m-1")}}, "with_music_folder_2", false},
|
{url.Values{"musicFolderId": {"1"}}, "with_music_folder_2", false},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ func (c *Controller) ServeGetArtists(r *http.Request) *spec.Response {
|
|||||||
Joins("LEFT JOIN albums sub ON artists.id=sub.tag_artist_id").
|
Joins("LEFT JOIN albums sub ON artists.id=sub.tag_artist_id").
|
||||||
Group("artists.id").
|
Group("artists.id").
|
||||||
Order("artists.name COLLATE NOCASE")
|
Order("artists.name COLLATE NOCASE")
|
||||||
if m, _ := params.Get("musicFolderId"); m != "" {
|
if m := c.getMusicFolder(params); m != "" {
|
||||||
q = q.Where("sub.root_dir=?", m)
|
q = q.Where("sub.root_dir=?", m)
|
||||||
}
|
}
|
||||||
if err := q.Find(&artists).Error; err != nil {
|
if err := q.Find(&artists).Error; err != nil {
|
||||||
@@ -148,7 +148,7 @@ func (c *Controller) ServeGetAlbumListTwo(r *http.Request) *spec.Response {
|
|||||||
default:
|
default:
|
||||||
return spec.NewError(10, "unknown value `%s` for parameter 'type'", listType)
|
return spec.NewError(10, "unknown value `%s` for parameter 'type'", listType)
|
||||||
}
|
}
|
||||||
if m, _ := params.Get("musicFolderId"); m != "" {
|
if m := c.getMusicFolder(params); m != "" {
|
||||||
q = q.Where("root_dir=?", m)
|
q = q.Where("root_dir=?", m)
|
||||||
}
|
}
|
||||||
var albums []*db.Album
|
var albums []*db.Album
|
||||||
@@ -188,7 +188,7 @@ func (c *Controller) ServeSearchThree(r *http.Request) *spec.Response {
|
|||||||
Where("name LIKE ? OR name_u_dec LIKE ?", query, query).
|
Where("name LIKE ? OR name_u_dec LIKE ?", query, query).
|
||||||
Offset(params.GetOrInt("artistOffset", 0)).
|
Offset(params.GetOrInt("artistOffset", 0)).
|
||||||
Limit(params.GetOrInt("artistCount", 20))
|
Limit(params.GetOrInt("artistCount", 20))
|
||||||
if m, _ := params.Get("musicFolderId"); m != "" {
|
if m := c.getMusicFolder(params); m != "" {
|
||||||
q = q.
|
q = q.
|
||||||
Joins("JOIN albums ON albums.tag_artist_id=artists.id").
|
Joins("JOIN albums ON albums.tag_artist_id=artists.id").
|
||||||
Where("albums.root_dir=?", m)
|
Where("albums.root_dir=?", m)
|
||||||
@@ -207,7 +207,7 @@ func (c *Controller) ServeSearchThree(r *http.Request) *spec.Response {
|
|||||||
Where("tag_title LIKE ? OR tag_title_u_dec LIKE ?", query, query).
|
Where("tag_title LIKE ? OR tag_title_u_dec LIKE ?", query, query).
|
||||||
Offset(params.GetOrInt("albumOffset", 0)).
|
Offset(params.GetOrInt("albumOffset", 0)).
|
||||||
Limit(params.GetOrInt("albumCount", 20))
|
Limit(params.GetOrInt("albumCount", 20))
|
||||||
if m, _ := params.Get("musicFolderId"); m != "" {
|
if m := c.getMusicFolder(params); m != "" {
|
||||||
q = q.Where("root_dir=?", m)
|
q = q.Where("root_dir=?", m)
|
||||||
}
|
}
|
||||||
if err := q.Find(&albums).Error; err != nil {
|
if err := q.Find(&albums).Error; err != nil {
|
||||||
@@ -224,7 +224,7 @@ func (c *Controller) ServeSearchThree(r *http.Request) *spec.Response {
|
|||||||
Where("tag_title LIKE ? OR tag_title_u_dec LIKE ?", query, query).
|
Where("tag_title LIKE ? OR tag_title_u_dec LIKE ?", query, query).
|
||||||
Offset(params.GetOrInt("songOffset", 0)).
|
Offset(params.GetOrInt("songOffset", 0)).
|
||||||
Limit(params.GetOrInt("songCount", 20))
|
Limit(params.GetOrInt("songCount", 20))
|
||||||
if m, _ := params.Get("musicFolderId"); m != "" {
|
if m := c.getMusicFolder(params); m != "" {
|
||||||
q = q.
|
q = q.
|
||||||
Joins("JOIN albums ON albums.id=tracks.album_id").
|
Joins("JOIN albums ON albums.id=tracks.album_id").
|
||||||
Where("albums.root_dir=?", m)
|
Where("albums.root_dir=?", m)
|
||||||
@@ -344,7 +344,7 @@ func (c *Controller) ServeGetSongsByGenre(r *http.Request) *spec.Response {
|
|||||||
Preload("Album").
|
Preload("Album").
|
||||||
Offset(params.GetOrInt("offset", 0)).
|
Offset(params.GetOrInt("offset", 0)).
|
||||||
Limit(params.GetOrInt("count", 10))
|
Limit(params.GetOrInt("count", 10))
|
||||||
if m, _ := params.Get("musicFolderId"); m != "" {
|
if m := c.getMusicFolder(params); m != "" {
|
||||||
q = q.Where("albums.root_dir=?", m)
|
q = q.Where("albums.root_dir=?", m)
|
||||||
}
|
}
|
||||||
if err := q.Find(&tracks).Error; err != nil {
|
if err := q.Find(&tracks).Error; err != nil {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package ctrlsubsonic
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -13,8 +12,8 @@ func TestGetArtists(t *testing.T) {
|
|||||||
|
|
||||||
runQueryCases(t, contr, contr.ServeGetArtists, []*queryCase{
|
runQueryCases(t, contr, contr.ServeGetArtists, []*queryCase{
|
||||||
{url.Values{}, "no_args", false},
|
{url.Values{}, "no_args", false},
|
||||||
{url.Values{"musicFolderId": {filepath.Join(m.TmpDir(), "m-0")}}, "with_music_folder_1", false},
|
{url.Values{"musicFolderId": {"0"}}, "with_music_folder_1", false},
|
||||||
{url.Values{"musicFolderId": {filepath.Join(m.TmpDir(), "m-1")}}, "with_music_folder_2", false},
|
{url.Values{"musicFolderId": {"1"}}, "with_music_folder_2", false},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -71,21 +71,11 @@ func (c *Controller) ServeScrobble(r *http.Request) *spec.Response {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) ServeGetMusicFolders(r *http.Request) *spec.Response {
|
func (c *Controller) ServeGetMusicFolders(r *http.Request) *spec.Response {
|
||||||
var roots []string
|
|
||||||
err := c.DB.
|
|
||||||
Model(&db.Album{}).
|
|
||||||
Pluck("DISTINCT(root_dir)", &roots).
|
|
||||||
Where("parent_id IS NULL").
|
|
||||||
Error
|
|
||||||
if err != nil {
|
|
||||||
return spec.NewError(0, "error getting roots: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sub := spec.NewResponse()
|
sub := spec.NewResponse()
|
||||||
sub.MusicFolders = &spec.MusicFolders{}
|
sub.MusicFolders = &spec.MusicFolders{}
|
||||||
sub.MusicFolders.List = make([]*spec.MusicFolder, len(roots))
|
sub.MusicFolders.List = make([]*spec.MusicFolder, len(c.MusicPaths))
|
||||||
for i, root := range roots {
|
for i, path := range c.MusicPaths {
|
||||||
sub.MusicFolders.List[i] = &spec.MusicFolder{ID: root, Name: filepath.Base(root)}
|
sub.MusicFolders.List[i] = &spec.MusicFolder{ID: i, Name: filepath.Base(path)}
|
||||||
}
|
}
|
||||||
return sub
|
return sub
|
||||||
}
|
}
|
||||||
@@ -226,7 +216,7 @@ func (c *Controller) ServeGetRandomSongs(r *http.Request) *spec.Response {
|
|||||||
q = q.Joins("JOIN track_genres ON track_genres.track_id=tracks.id")
|
q = q.Joins("JOIN track_genres ON track_genres.track_id=tracks.id")
|
||||||
q = q.Joins("JOIN genres ON genres.id=track_genres.genre_id AND genres.name=?", genre)
|
q = q.Joins("JOIN genres ON genres.id=track_genres.genre_id AND genres.name=?", genre)
|
||||||
}
|
}
|
||||||
if m, _ := params.Get("musicFolderId"); m != "" {
|
if m := c.getMusicFolder(params); m != "" {
|
||||||
q = q.Where("albums.root_dir=?", m)
|
q = q.Where("albums.root_dir=?", m)
|
||||||
}
|
}
|
||||||
q.Find(&tracks)
|
q.Find(&tracks)
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ type MusicFolders struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type MusicFolder struct {
|
type MusicFolder struct {
|
||||||
ID string `xml:"id,attr,omitempty" json:"id,omitempty"`
|
ID int `xml:"id,attr" json:"id"`
|
||||||
Name string `xml:"name,attr,omitempty" json:"name,omitempty"`
|
Name string `xml:"name,attr,omitempty" json:"name,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,6 +52,11 @@ func new(t *testing.T, dirs []string) *MockFS {
|
|||||||
for _, dir := range dirs {
|
for _, dir := range dirs {
|
||||||
absDirs = append(absDirs, filepath.Join(tmpDir, dir))
|
absDirs = append(absDirs, filepath.Join(tmpDir, dir))
|
||||||
}
|
}
|
||||||
|
for _, absDir := range absDirs {
|
||||||
|
if err := os.MkdirAll(absDir, os.ModePerm); err != nil {
|
||||||
|
t.Fatalf("mk abs dir: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
parser := &mreader{map[string]*Tags{}}
|
parser := &mreader{map[string]*Tags{}}
|
||||||
scanner := scanner.New(absDirs, true, dbc, ";", parser)
|
scanner := scanner.New(absDirs, true, dbc, ";", parser)
|
||||||
@@ -128,6 +133,7 @@ func (m *MockFS) RemoveAll(path string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockFS) LogItems() {
|
func (m *MockFS) LogItems() {
|
||||||
|
m.t.Logf("\nitems")
|
||||||
var dirs int
|
var dirs int
|
||||||
err := filepath.Walk(m.dir, func(path string, info fs.FileInfo, err error) error {
|
err := filepath.Walk(m.dir, func(path string, info fs.FileInfo, err error) error {
|
||||||
m.t.Logf("item %q", path)
|
m.t.Logf("item %q", path)
|
||||||
@@ -139,7 +145,7 @@ func (m *MockFS) LogItems() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
m.t.Fatalf("error logging items: %v", err)
|
m.t.Fatalf("error logging items: %v", err)
|
||||||
}
|
}
|
||||||
m.t.Logf("dirs: %d", dirs)
|
m.t.Logf("total %d", dirs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockFS) LogAlbums() {
|
func (m *MockFS) LogAlbums() {
|
||||||
@@ -150,7 +156,7 @@ func (m *MockFS) LogAlbums() {
|
|||||||
|
|
||||||
m.t.Logf("\nalbums")
|
m.t.Logf("\nalbums")
|
||||||
for _, album := range albums {
|
for _, album := range albums {
|
||||||
m.t.Logf("id %-3d root %-3s %-10s %-10s pid %-3d aid %-3d cov %-10s",
|
m.t.Logf("id %-3d root %-3s lr %-15s %-10s pid %-3d aid %-3d cov %-10s",
|
||||||
album.ID, album.RootDir, album.LeftPath, album.RightPath, album.ParentID, album.TagArtistID, album.Cover)
|
album.ID, album.RootDir, album.LeftPath, album.RightPath, album.ParentID, album.TagArtistID, album.Cover)
|
||||||
}
|
}
|
||||||
m.t.Logf("total %d", len(albums))
|
m.t.Logf("total %d", len(albums))
|
||||||
|
|||||||
@@ -324,3 +324,49 @@ func TestNewAlbumForExistingArtist(t *testing.T) {
|
|||||||
is.NoErr(m.DB().Find(&all).Error) // still only 3?
|
is.NoErr(m.DB().Find(&all).Error) // still only 3?
|
||||||
is.Equal(len(all), 3) // still only 3?
|
is.Equal(len(all), 3) // still only 3?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMultiFolderWithSharedArtist(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
is := is.New(t)
|
||||||
|
m := mockfs.NewWithDirs(t, []string{"m-0", "m-1"})
|
||||||
|
defer m.CleanUp()
|
||||||
|
|
||||||
|
const artistName = "artist-a"
|
||||||
|
|
||||||
|
m.AddTrack(fmt.Sprintf("m-0/%s/album-a/track-1.flac", artistName))
|
||||||
|
m.SetTags(fmt.Sprintf("m-0/%s/album-a/track-1.flac", artistName), func(tags *mockfs.Tags) {
|
||||||
|
tags.RawArtist = artistName
|
||||||
|
tags.RawAlbumArtist = artistName
|
||||||
|
tags.RawAlbum = "album-a"
|
||||||
|
tags.RawTitle = "track-1"
|
||||||
|
})
|
||||||
|
m.ScanAndClean()
|
||||||
|
|
||||||
|
m.AddTrack(fmt.Sprintf("m-1/%s/album-a/track-1.flac", artistName))
|
||||||
|
m.SetTags(fmt.Sprintf("m-1/%s/album-a/track-1.flac", artistName), func(tags *mockfs.Tags) {
|
||||||
|
tags.RawArtist = artistName
|
||||||
|
tags.RawAlbumArtist = artistName
|
||||||
|
tags.RawAlbum = "album-a"
|
||||||
|
tags.RawTitle = "track-1"
|
||||||
|
})
|
||||||
|
m.ScanAndClean()
|
||||||
|
|
||||||
|
sq := func(db *gorm.DB) *gorm.DB {
|
||||||
|
return db.
|
||||||
|
Select("*, count(sub.id) child_count, sum(sub.length) duration").
|
||||||
|
Joins("LEFT JOIN tracks sub ON albums.id=sub.album_id").
|
||||||
|
Group("albums.id")
|
||||||
|
}
|
||||||
|
|
||||||
|
var artist db.Artist
|
||||||
|
is.NoErr(m.DB().Where("name=?", artistName).Preload("Albums", sq).First(&artist).Error)
|
||||||
|
is.Equal(artist.Name, artistName)
|
||||||
|
is.Equal(len(artist.Albums), 2)
|
||||||
|
|
||||||
|
for _, album := range artist.Albums {
|
||||||
|
is.True(album.TagYear > 0)
|
||||||
|
is.Equal(album.TagArtistID, artist.ID)
|
||||||
|
is.True(album.ChildCount > 0)
|
||||||
|
is.True(album.Duration > 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ func New(opts Options) (*Server, error) {
|
|||||||
CachePath: opts.CachePath,
|
CachePath: opts.CachePath,
|
||||||
CoverCachePath: opts.CoverCachePath,
|
CoverCachePath: opts.CoverCachePath,
|
||||||
PodcastsPath: opts.PodcastPath,
|
PodcastsPath: opts.PodcastPath,
|
||||||
|
MusicPaths: opts.MusicPaths,
|
||||||
Jukebox: &jukebox.Jukebox{},
|
Jukebox: &jukebox.Jukebox{},
|
||||||
Scrobblers: []scrobble.Scrobbler{&lastfm.Scrobbler{DB: opts.DB}, &listenbrainz.Scrobbler{}},
|
Scrobblers: []scrobble.Scrobbler{&lastfm.Scrobbler{DB: opts.DB}, &listenbrainz.Scrobbler{}},
|
||||||
Podcasts: podcast,
|
Podcasts: podcast,
|
||||||
|
|||||||
Reference in New Issue
Block a user