mirror of
https://github.com/navidrome/navidrome.git
synced 2025-06-09 03:42:23 +03:00
Simplify Subsonic API handler implementation
This commit is contained in:
parent
cd41d9a419
commit
19af11efbe
@ -6,7 +6,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/navidrome/navidrome/core/scrobbler"
|
|
||||||
"github.com/navidrome/navidrome/log"
|
"github.com/navidrome/navidrome/log"
|
||||||
"github.com/navidrome/navidrome/model"
|
"github.com/navidrome/navidrome/model"
|
||||||
"github.com/navidrome/navidrome/server/subsonic/filter"
|
"github.com/navidrome/navidrome/server/subsonic/filter"
|
||||||
@ -14,20 +13,7 @@ import (
|
|||||||
"github.com/navidrome/navidrome/utils"
|
"github.com/navidrome/navidrome/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AlbumListController struct {
|
func (api *Router) getAlbumList(r *http.Request) (model.Albums, int64, error) {
|
||||||
ds model.DataStore
|
|
||||||
scrobbler scrobbler.PlayTracker
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewAlbumListController(ds model.DataStore, scrobbler scrobbler.PlayTracker) *AlbumListController {
|
|
||||||
c := &AlbumListController{
|
|
||||||
ds: ds,
|
|
||||||
scrobbler: scrobbler,
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *AlbumListController) getAlbumList(r *http.Request) (model.Albums, int64, error) {
|
|
||||||
typ, err := requiredParamString(r, "type")
|
typ, err := requiredParamString(r, "type")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
@ -74,14 +60,14 @@ func (c *AlbumListController) getAlbumList(r *http.Request) (model.Albums, int64
|
|||||||
|
|
||||||
opts.Offset = utils.ParamInt(r, "offset", 0)
|
opts.Offset = utils.ParamInt(r, "offset", 0)
|
||||||
opts.Max = utils.MinInt(utils.ParamInt(r, "size", 10), 500)
|
opts.Max = utils.MinInt(utils.ParamInt(r, "size", 10), 500)
|
||||||
albums, err := c.ds.Album(r.Context()).GetAllWithoutGenres(opts)
|
albums, err := api.ds.Album(r.Context()).GetAllWithoutGenres(opts)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(r, "Error retrieving albums", "error", err)
|
log.Error(r, "Error retrieving albums", "error", err)
|
||||||
return nil, 0, newError(responses.ErrorGeneric, "internal error")
|
return nil, 0, newError(responses.ErrorGeneric, "internal error")
|
||||||
}
|
}
|
||||||
|
|
||||||
count, err := c.ds.Album(r.Context()).CountAll(opts)
|
count, err := api.ds.Album(r.Context()).CountAll(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(r, "Error counting albums", "error", err)
|
log.Error(r, "Error counting albums", "error", err)
|
||||||
return nil, 0, newError(responses.ErrorGeneric, "internal error")
|
return nil, 0, newError(responses.ErrorGeneric, "internal error")
|
||||||
@ -90,8 +76,8 @@ func (c *AlbumListController) getAlbumList(r *http.Request) (model.Albums, int64
|
|||||||
return albums, count, nil
|
return albums, count, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AlbumListController) GetAlbumList(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) GetAlbumList(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
||||||
albums, count, err := c.getAlbumList(r)
|
albums, count, err := api.getAlbumList(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -103,8 +89,8 @@ func (c *AlbumListController) GetAlbumList(w http.ResponseWriter, r *http.Reques
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AlbumListController) GetAlbumList2(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) GetAlbumList2(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
||||||
albums, pageCount, err := c.getAlbumList(r)
|
albums, pageCount, err := api.getAlbumList(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -116,20 +102,20 @@ func (c *AlbumListController) GetAlbumList2(w http.ResponseWriter, r *http.Reque
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AlbumListController) GetStarred(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) GetStarred(r *http.Request) (*responses.Subsonic, error) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
options := filter.Starred()
|
options := filter.Starred()
|
||||||
artists, err := c.ds.Artist(ctx).GetAll(options)
|
artists, err := api.ds.Artist(ctx).GetAll(options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(r, "Error retrieving starred artists", "error", err)
|
log.Error(r, "Error retrieving starred artists", "error", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
albums, err := c.ds.Album(ctx).GetAllWithoutGenres(options)
|
albums, err := api.ds.Album(ctx).GetAllWithoutGenres(options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(r, "Error retrieving starred albums", "error", err)
|
log.Error(r, "Error retrieving starred albums", "error", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
mediaFiles, err := c.ds.MediaFile(ctx).GetAll(options)
|
mediaFiles, err := api.ds.MediaFile(ctx).GetAll(options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(r, "Error retrieving starred mediaFiles", "error", err)
|
log.Error(r, "Error retrieving starred mediaFiles", "error", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -143,8 +129,8 @@ func (c *AlbumListController) GetStarred(w http.ResponseWriter, r *http.Request)
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AlbumListController) GetStarred2(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) GetStarred2(r *http.Request) (*responses.Subsonic, error) {
|
||||||
resp, err := c.GetStarred(w, r)
|
resp, err := api.GetStarred(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -154,9 +140,9 @@ func (c *AlbumListController) GetStarred2(w http.ResponseWriter, r *http.Request
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AlbumListController) GetNowPlaying(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) GetNowPlaying(r *http.Request) (*responses.Subsonic, error) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
npInfo, err := c.scrobbler.GetNowPlaying(ctx)
|
npInfo, err := api.scrobbler.GetNowPlaying(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(r, "Error retrieving now playing list", "error", err)
|
log.Error(r, "Error retrieving now playing list", "error", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -166,7 +152,7 @@ func (c *AlbumListController) GetNowPlaying(w http.ResponseWriter, r *http.Reque
|
|||||||
response.NowPlaying = &responses.NowPlaying{}
|
response.NowPlaying = &responses.NowPlaying{}
|
||||||
response.NowPlaying.Entry = make([]responses.NowPlayingEntry, len(npInfo))
|
response.NowPlaying.Entry = make([]responses.NowPlayingEntry, len(npInfo))
|
||||||
for i, np := range npInfo {
|
for i, np := range npInfo {
|
||||||
mf, err := c.ds.MediaFile(ctx).Get(np.TrackID)
|
mf, err := api.ds.MediaFile(ctx).Get(np.TrackID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -180,13 +166,13 @@ func (c *AlbumListController) GetNowPlaying(w http.ResponseWriter, r *http.Reque
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AlbumListController) GetRandomSongs(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) GetRandomSongs(r *http.Request) (*responses.Subsonic, error) {
|
||||||
size := utils.MinInt(utils.ParamInt(r, "size", 10), 500)
|
size := utils.MinInt(utils.ParamInt(r, "size", 10), 500)
|
||||||
genre := utils.ParamString(r, "genre")
|
genre := utils.ParamString(r, "genre")
|
||||||
fromYear := utils.ParamInt(r, "fromYear", 0)
|
fromYear := utils.ParamInt(r, "fromYear", 0)
|
||||||
toYear := utils.ParamInt(r, "toYear", 0)
|
toYear := utils.ParamInt(r, "toYear", 0)
|
||||||
|
|
||||||
songs, err := c.getSongs(r.Context(), 0, size, filter.SongsByRandom(genre, fromYear, toYear))
|
songs, err := api.getSongs(r.Context(), 0, size, filter.SongsByRandom(genre, fromYear, toYear))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(r, "Error retrieving random songs", "error", err)
|
log.Error(r, "Error retrieving random songs", "error", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -198,12 +184,12 @@ func (c *AlbumListController) GetRandomSongs(w http.ResponseWriter, r *http.Requ
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AlbumListController) GetSongsByGenre(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) GetSongsByGenre(r *http.Request) (*responses.Subsonic, error) {
|
||||||
count := utils.MinInt(utils.ParamInt(r, "count", 10), 500)
|
count := utils.MinInt(utils.ParamInt(r, "count", 10), 500)
|
||||||
offset := utils.MinInt(utils.ParamInt(r, "offset", 0), 500)
|
offset := utils.MinInt(utils.ParamInt(r, "offset", 0), 500)
|
||||||
genre := utils.ParamString(r, "genre")
|
genre := utils.ParamString(r, "genre")
|
||||||
|
|
||||||
songs, err := c.getSongs(r.Context(), offset, count, filter.SongsByGenre(genre))
|
songs, err := api.getSongs(r.Context(), offset, count, filter.SongsByGenre(genre))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(r, "Error retrieving random songs", "error", err)
|
log.Error(r, "Error retrieving random songs", "error", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -215,8 +201,8 @@ func (c *AlbumListController) GetSongsByGenre(w http.ResponseWriter, r *http.Req
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AlbumListController) getSongs(ctx context.Context, offset, size int, opts filter.Options) (model.MediaFiles, error) {
|
func (api *Router) getSongs(ctx context.Context, offset, size int, opts filter.Options) (model.MediaFiles, error) {
|
||||||
opts.Offset = offset
|
opts.Offset = offset
|
||||||
opts.Max = size
|
opts.Max = size
|
||||||
return c.ds.MediaFile(ctx).GetAll(opts)
|
return api.ds.MediaFile(ctx).GetAll(opts)
|
||||||
}
|
}
|
||||||
|
@ -14,8 +14,8 @@ import (
|
|||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = Describe("AlbumListController", func() {
|
var _ = Describe("Album Lists", func() {
|
||||||
var controller *AlbumListController
|
var router *Router
|
||||||
var ds model.DataStore
|
var ds model.DataStore
|
||||||
var mockRepo *tests.MockAlbumRepo
|
var mockRepo *tests.MockAlbumRepo
|
||||||
var w *httptest.ResponseRecorder
|
var w *httptest.ResponseRecorder
|
||||||
@ -24,7 +24,7 @@ var _ = Describe("AlbumListController", func() {
|
|||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
ds = &tests.MockDataStore{}
|
ds = &tests.MockDataStore{}
|
||||||
mockRepo = ds.Album(ctx).(*tests.MockAlbumRepo)
|
mockRepo = ds.Album(ctx).(*tests.MockAlbumRepo)
|
||||||
controller = NewAlbumListController(ds, nil)
|
router = New(ds, nil, nil, nil, nil, nil, nil, nil, nil, nil)
|
||||||
w = httptest.NewRecorder()
|
w = httptest.NewRecorder()
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ var _ = Describe("AlbumListController", func() {
|
|||||||
mockRepo.SetData(model.Albums{
|
mockRepo.SetData(model.Albums{
|
||||||
{ID: "1"}, {ID: "2"},
|
{ID: "1"}, {ID: "2"},
|
||||||
})
|
})
|
||||||
resp, err := controller.GetAlbumList(w, r)
|
resp, err := router.GetAlbumList(w, r)
|
||||||
|
|
||||||
Expect(err).To(BeNil())
|
Expect(err).To(BeNil())
|
||||||
Expect(resp.AlbumList.Album[0].Id).To(Equal("1"))
|
Expect(resp.AlbumList.Album[0].Id).To(Equal("1"))
|
||||||
@ -46,7 +46,7 @@ var _ = Describe("AlbumListController", func() {
|
|||||||
|
|
||||||
It("should fail if missing type parameter", func() {
|
It("should fail if missing type parameter", func() {
|
||||||
r := newGetRequest()
|
r := newGetRequest()
|
||||||
_, err := controller.GetAlbumList(w, r)
|
_, err := router.GetAlbumList(w, r)
|
||||||
var subErr subError
|
var subErr subError
|
||||||
isSubError := errors.As(err, &subErr)
|
isSubError := errors.As(err, &subErr)
|
||||||
|
|
||||||
@ -59,7 +59,7 @@ var _ = Describe("AlbumListController", func() {
|
|||||||
mockRepo.SetError(true)
|
mockRepo.SetError(true)
|
||||||
r := newGetRequest("type=newest")
|
r := newGetRequest("type=newest")
|
||||||
|
|
||||||
_, err := controller.GetAlbumList(w, r)
|
_, err := router.GetAlbumList(w, r)
|
||||||
|
|
||||||
Expect(err).ToNot(BeNil())
|
Expect(err).ToNot(BeNil())
|
||||||
var subErr subError
|
var subErr subError
|
||||||
@ -74,7 +74,7 @@ var _ = Describe("AlbumListController", func() {
|
|||||||
mockRepo.SetData(model.Albums{
|
mockRepo.SetData(model.Albums{
|
||||||
{ID: "1"}, {ID: "2"},
|
{ID: "1"}, {ID: "2"},
|
||||||
})
|
})
|
||||||
resp, err := controller.GetAlbumList2(w, r)
|
resp, err := router.GetAlbumList2(w, r)
|
||||||
|
|
||||||
Expect(err).To(BeNil())
|
Expect(err).To(BeNil())
|
||||||
Expect(resp.AlbumList2.Album[0].Id).To(Equal("1"))
|
Expect(resp.AlbumList2.Album[0].Id).To(Equal("1"))
|
||||||
@ -86,7 +86,7 @@ var _ = Describe("AlbumListController", func() {
|
|||||||
|
|
||||||
It("should fail if missing type parameter", func() {
|
It("should fail if missing type parameter", func() {
|
||||||
r := newGetRequest()
|
r := newGetRequest()
|
||||||
_, err := controller.GetAlbumList2(w, r)
|
_, err := router.GetAlbumList2(w, r)
|
||||||
|
|
||||||
var subErr subError
|
var subErr subError
|
||||||
errors.As(err, &subErr)
|
errors.As(err, &subErr)
|
||||||
@ -99,7 +99,7 @@ var _ = Describe("AlbumListController", func() {
|
|||||||
mockRepo.SetError(true)
|
mockRepo.SetError(true)
|
||||||
r := newGetRequest("type=newest")
|
r := newGetRequest("type=newest")
|
||||||
|
|
||||||
_, err := controller.GetAlbumList2(w, r)
|
_, err := router.GetAlbumList2(w, r)
|
||||||
|
|
||||||
var subErr subError
|
var subErr subError
|
||||||
errors.As(err, &subErr)
|
errors.As(err, &subErr)
|
||||||
|
@ -23,36 +23,37 @@ import (
|
|||||||
|
|
||||||
const Version = "1.16.1"
|
const Version = "1.16.1"
|
||||||
|
|
||||||
type handler = func(http.ResponseWriter, *http.Request) (*responses.Subsonic, error)
|
type handler = func(*http.Request) (*responses.Subsonic, error)
|
||||||
|
type handlerRaw = func(http.ResponseWriter, *http.Request) (*responses.Subsonic, error)
|
||||||
|
|
||||||
type Router struct {
|
type Router struct {
|
||||||
http.Handler
|
http.Handler
|
||||||
DataStore model.DataStore
|
ds model.DataStore
|
||||||
Artwork core.Artwork
|
artwork core.Artwork
|
||||||
Streamer core.MediaStreamer
|
streamer core.MediaStreamer
|
||||||
Archiver core.Archiver
|
archiver core.Archiver
|
||||||
Players core.Players
|
players core.Players
|
||||||
ExternalMetadata core.ExternalMetadata
|
externalMetadata core.ExternalMetadata
|
||||||
Playlists core.Playlists
|
playlists core.Playlists
|
||||||
Scanner scanner.Scanner
|
scanner scanner.Scanner
|
||||||
Broker events.Broker
|
broker events.Broker
|
||||||
Scrobbler scrobbler.PlayTracker
|
scrobbler scrobbler.PlayTracker
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(ds model.DataStore, artwork core.Artwork, streamer core.MediaStreamer, archiver core.Archiver,
|
func New(ds model.DataStore, artwork core.Artwork, streamer core.MediaStreamer, archiver core.Archiver,
|
||||||
players core.Players, externalMetadata core.ExternalMetadata, scanner scanner.Scanner, broker events.Broker,
|
players core.Players, externalMetadata core.ExternalMetadata, scanner scanner.Scanner, broker events.Broker,
|
||||||
playlists core.Playlists, scrobbler scrobbler.PlayTracker) *Router {
|
playlists core.Playlists, scrobbler scrobbler.PlayTracker) *Router {
|
||||||
r := &Router{
|
r := &Router{
|
||||||
DataStore: ds,
|
ds: ds,
|
||||||
Artwork: artwork,
|
artwork: artwork,
|
||||||
Streamer: streamer,
|
streamer: streamer,
|
||||||
Archiver: archiver,
|
archiver: archiver,
|
||||||
Players: players,
|
players: players,
|
||||||
ExternalMetadata: externalMetadata,
|
externalMetadata: externalMetadata,
|
||||||
Playlists: playlists,
|
playlists: playlists,
|
||||||
Scanner: scanner,
|
scanner: scanner,
|
||||||
Broker: broker,
|
broker: broker,
|
||||||
Scrobbler: scrobbler,
|
scrobbler: scrobbler,
|
||||||
}
|
}
|
||||||
r.Handler = r.routes()
|
r.Handler = r.routes()
|
||||||
return r
|
return r
|
||||||
@ -63,100 +64,89 @@ func (api *Router) routes() http.Handler {
|
|||||||
|
|
||||||
r.Use(postFormToQueryParams)
|
r.Use(postFormToQueryParams)
|
||||||
r.Use(checkRequiredParameters)
|
r.Use(checkRequiredParameters)
|
||||||
r.Use(authenticate(api.DataStore))
|
r.Use(authenticate(api.ds))
|
||||||
// TODO Validate version
|
// TODO Validate version
|
||||||
|
|
||||||
// Subsonic endpoints, grouped by controller
|
// Subsonic endpoints, grouped by controller
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
c := initSystemController(api)
|
r.Use(getPlayer(api.players))
|
||||||
withPlayer := r.With(getPlayer(api.Players))
|
h(r, "ping", api.Ping)
|
||||||
h(withPlayer, "ping", c.Ping)
|
h(r, "getLicense", api.GetLicense)
|
||||||
h(withPlayer, "getLicense", c.GetLicense)
|
|
||||||
})
|
})
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
c := initBrowsingController(api)
|
r.Use(getPlayer(api.players))
|
||||||
withPlayer := r.With(getPlayer(api.Players))
|
h(r, "getMusicFolders", api.GetMusicFolders)
|
||||||
h(withPlayer, "getMusicFolders", c.GetMusicFolders)
|
h(r, "getIndexes", api.GetIndexes)
|
||||||
h(withPlayer, "getIndexes", c.GetIndexes)
|
h(r, "getArtists", api.GetArtists)
|
||||||
h(withPlayer, "getArtists", c.GetArtists)
|
h(r, "getGenres", api.GetGenres)
|
||||||
h(withPlayer, "getGenres", c.GetGenres)
|
h(r, "getMusicDirectory", api.GetMusicDirectory)
|
||||||
h(withPlayer, "getMusicDirectory", c.GetMusicDirectory)
|
h(r, "getArtist", api.GetArtist)
|
||||||
h(withPlayer, "getArtist", c.GetArtist)
|
h(r, "getAlbum", api.GetAlbum)
|
||||||
h(withPlayer, "getAlbum", c.GetAlbum)
|
h(r, "getSong", api.GetSong)
|
||||||
h(withPlayer, "getSong", c.GetSong)
|
h(r, "getArtistInfo", api.GetArtistInfo)
|
||||||
h(withPlayer, "getArtistInfo", c.GetArtistInfo)
|
h(r, "getArtistInfo2", api.GetArtistInfo2)
|
||||||
h(withPlayer, "getArtistInfo2", c.GetArtistInfo2)
|
h(r, "getTopSongs", api.GetTopSongs)
|
||||||
h(withPlayer, "getTopSongs", c.GetTopSongs)
|
h(r, "getSimilarSongs", api.GetSimilarSongs)
|
||||||
h(withPlayer, "getSimilarSongs", c.GetSimilarSongs)
|
h(r, "getSimilarSongs2", api.GetSimilarSongs2)
|
||||||
h(withPlayer, "getSimilarSongs2", c.GetSimilarSongs2)
|
|
||||||
})
|
})
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
c := initAlbumListController(api)
|
r.Use(getPlayer(api.players))
|
||||||
withPlayer := r.With(getPlayer(api.Players))
|
hr(r, "getAlbumList", api.GetAlbumList)
|
||||||
h(withPlayer, "getAlbumList", c.GetAlbumList)
|
hr(r, "getAlbumList2", api.GetAlbumList2)
|
||||||
h(withPlayer, "getAlbumList2", c.GetAlbumList2)
|
h(r, "getStarred", api.GetStarred)
|
||||||
h(withPlayer, "getStarred", c.GetStarred)
|
h(r, "getStarred2", api.GetStarred2)
|
||||||
h(withPlayer, "getStarred2", c.GetStarred2)
|
h(r, "getNowPlaying", api.GetNowPlaying)
|
||||||
h(withPlayer, "getNowPlaying", c.GetNowPlaying)
|
h(r, "getRandomSongs", api.GetRandomSongs)
|
||||||
h(withPlayer, "getRandomSongs", c.GetRandomSongs)
|
h(r, "getSongsByGenre", api.GetSongsByGenre)
|
||||||
h(withPlayer, "getSongsByGenre", c.GetSongsByGenre)
|
|
||||||
})
|
})
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
c := initMediaAnnotationController(api)
|
r.Use(getPlayer(api.players))
|
||||||
withPlayer := r.With(getPlayer(api.Players))
|
h(r, "setRating", api.SetRating)
|
||||||
h(withPlayer, "setRating", c.SetRating)
|
h(r, "star", api.Star)
|
||||||
h(withPlayer, "star", c.Star)
|
h(r, "unstar", api.Unstar)
|
||||||
h(withPlayer, "unstar", c.Unstar)
|
h(r, "scrobble", api.Scrobble)
|
||||||
h(withPlayer, "scrobble", c.Scrobble)
|
|
||||||
})
|
})
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
c := initPlaylistsController(api)
|
r.Use(getPlayer(api.players))
|
||||||
withPlayer := r.With(getPlayer(api.Players))
|
h(r, "getPlaylists", api.GetPlaylists)
|
||||||
h(withPlayer, "getPlaylists", c.GetPlaylists)
|
h(r, "getPlaylist", api.GetPlaylist)
|
||||||
h(withPlayer, "getPlaylist", c.GetPlaylist)
|
h(r, "createPlaylist", api.CreatePlaylist)
|
||||||
h(withPlayer, "createPlaylist", c.CreatePlaylist)
|
h(r, "deletePlaylist", api.DeletePlaylist)
|
||||||
h(withPlayer, "deletePlaylist", c.DeletePlaylist)
|
h(r, "updatePlaylist", api.UpdatePlaylist)
|
||||||
h(withPlayer, "updatePlaylist", c.UpdatePlaylist)
|
|
||||||
})
|
})
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
c := initBookmarksController(api)
|
r.Use(getPlayer(api.players))
|
||||||
withPlayer := r.With(getPlayer(api.Players))
|
h(r, "getBookmarks", api.GetBookmarks)
|
||||||
h(withPlayer, "getBookmarks", c.GetBookmarks)
|
h(r, "createBookmark", api.CreateBookmark)
|
||||||
h(withPlayer, "createBookmark", c.CreateBookmark)
|
h(r, "deleteBookmark", api.DeleteBookmark)
|
||||||
h(withPlayer, "deleteBookmark", c.DeleteBookmark)
|
h(r, "getPlayQueue", api.GetPlayQueue)
|
||||||
h(withPlayer, "getPlayQueue", c.GetPlayQueue)
|
h(r, "savePlayQueue", api.SavePlayQueue)
|
||||||
h(withPlayer, "savePlayQueue", c.SavePlayQueue)
|
|
||||||
})
|
})
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
c := initSearchingController(api)
|
r.Use(getPlayer(api.players))
|
||||||
withPlayer := r.With(getPlayer(api.Players))
|
h(r, "search2", api.Search2)
|
||||||
h(withPlayer, "search2", c.Search2)
|
h(r, "search3", api.Search3)
|
||||||
h(withPlayer, "search3", c.Search3)
|
|
||||||
})
|
})
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
c := initUsersController(api)
|
h(r, "getUser", api.GetUser)
|
||||||
h(r, "getUser", c.GetUser)
|
h(r, "getUsers", api.GetUsers)
|
||||||
h(r, "getUsers", c.GetUsers)
|
|
||||||
})
|
})
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
c := initLibraryScanningController(api)
|
h(r, "getScanStatus", api.GetScanStatus)
|
||||||
h(r, "getScanStatus", c.GetScanStatus)
|
h(r, "startScan", api.StartScan)
|
||||||
h(r, "startScan", c.StartScan)
|
|
||||||
})
|
})
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
c := initMediaRetrievalController(api)
|
|
||||||
// configure request throttling
|
// configure request throttling
|
||||||
maxRequests := utils.MaxInt(2, runtime.NumCPU())
|
maxRequests := utils.MaxInt(2, runtime.NumCPU())
|
||||||
withThrottle := r.With(middleware.ThrottleBacklog(maxRequests, consts.RequestThrottleBacklogLimit, consts.RequestThrottleBacklogTimeout))
|
r.Use(middleware.ThrottleBacklog(maxRequests, consts.RequestThrottleBacklogLimit, consts.RequestThrottleBacklogTimeout))
|
||||||
h(withThrottle, "getAvatar", c.GetAvatar)
|
hr(r, "getAvatar", api.GetAvatar)
|
||||||
h(withThrottle, "getCoverArt", c.GetCoverArt)
|
hr(r, "getCoverArt", api.GetCoverArt)
|
||||||
h(withThrottle, "getLyrics", c.GetLyrics)
|
h(r, "getLyrics", api.GetLyrics)
|
||||||
})
|
})
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
c := initStreamController(api)
|
r.Use(getPlayer(api.players))
|
||||||
withPlayer := r.With(getPlayer(api.Players))
|
hr(r, "stream", api.Stream)
|
||||||
h(withPlayer, "stream", c.Stream)
|
hr(r, "download", api.Download)
|
||||||
h(withPlayer, "download", c.Download)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// Not Implemented (yet?)
|
// Not Implemented (yet?)
|
||||||
@ -176,9 +166,9 @@ func (api *Router) routes() http.Handler {
|
|||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the Subsonic handler, with and without `.view` extension
|
// Add the Subsonic handler that requires a http.ResponseWriter, with and without `.view` extension.
|
||||||
// Ex: if path = `ping` it will create the routes `/ping` and `/ping.view`
|
// Ex: if path = `stream` it will create the routes `/stream` and `/stream.view`
|
||||||
func h(r chi.Router, path string, f handler) {
|
func hr(r chi.Router, path string, f handlerRaw) {
|
||||||
handle := func(w http.ResponseWriter, r *http.Request) {
|
handle := func(w http.ResponseWriter, r *http.Request) {
|
||||||
res, err := f(w, r)
|
res, err := f(w, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -208,6 +198,14 @@ func h(r chi.Router, path string, f handler) {
|
|||||||
r.HandleFunc("/"+path+".view", handle)
|
r.HandleFunc("/"+path+".view", handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add the Subsonic handler, with and without `.view` extension
|
||||||
|
// Ex: if path = `ping` it will create the routes `/ping` and `/ping.view`
|
||||||
|
func h(r chi.Router, path string, f handler) {
|
||||||
|
hr(r, path, func(_ http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
||||||
|
return f(r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Add a handler that returns 501 - Not implemented. Used to signal that an endpoint is not implemented yet
|
// Add a handler that returns 501 - Not implemented. Used to signal that an endpoint is not implemented yet
|
||||||
func h501(r *chi.Mux, paths ...string) {
|
func h501(r *chi.Mux, paths ...string) {
|
||||||
for _, path := range paths {
|
for _, path := range paths {
|
||||||
|
@ -10,18 +10,10 @@ import (
|
|||||||
"github.com/navidrome/navidrome/utils"
|
"github.com/navidrome/navidrome/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type BookmarksController struct {
|
func (api *Router) GetBookmarks(r *http.Request) (*responses.Subsonic, error) {
|
||||||
ds model.DataStore
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewBookmarksController(ds model.DataStore) *BookmarksController {
|
|
||||||
return &BookmarksController{ds: ds}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *BookmarksController) GetBookmarks(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
|
||||||
user, _ := request.UserFrom(r.Context())
|
user, _ := request.UserFrom(r.Context())
|
||||||
|
|
||||||
repo := c.ds.MediaFile(r.Context())
|
repo := api.ds.MediaFile(r.Context())
|
||||||
bmks, err := repo.GetBookmarks()
|
bmks, err := repo.GetBookmarks()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -43,7 +35,7 @@ func (c *BookmarksController) GetBookmarks(w http.ResponseWriter, r *http.Reques
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *BookmarksController) CreateBookmark(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) CreateBookmark(r *http.Request) (*responses.Subsonic, error) {
|
||||||
id, err := requiredParamString(r, "id")
|
id, err := requiredParamString(r, "id")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -52,7 +44,7 @@ func (c *BookmarksController) CreateBookmark(w http.ResponseWriter, r *http.Requ
|
|||||||
comment := utils.ParamString(r, "comment")
|
comment := utils.ParamString(r, "comment")
|
||||||
position := utils.ParamInt64(r, "position", 0)
|
position := utils.ParamInt64(r, "position", 0)
|
||||||
|
|
||||||
repo := c.ds.MediaFile(r.Context())
|
repo := api.ds.MediaFile(r.Context())
|
||||||
err = repo.AddBookmark(id, comment, position)
|
err = repo.AddBookmark(id, comment, position)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -60,13 +52,13 @@ func (c *BookmarksController) CreateBookmark(w http.ResponseWriter, r *http.Requ
|
|||||||
return newResponse(), nil
|
return newResponse(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *BookmarksController) DeleteBookmark(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) DeleteBookmark(r *http.Request) (*responses.Subsonic, error) {
|
||||||
id, err := requiredParamString(r, "id")
|
id, err := requiredParamString(r, "id")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
repo := c.ds.MediaFile(r.Context())
|
repo := api.ds.MediaFile(r.Context())
|
||||||
err = repo.DeleteBookmark(id)
|
err = repo.DeleteBookmark(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -74,10 +66,10 @@ func (c *BookmarksController) DeleteBookmark(w http.ResponseWriter, r *http.Requ
|
|||||||
return newResponse(), nil
|
return newResponse(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *BookmarksController) GetPlayQueue(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) GetPlayQueue(r *http.Request) (*responses.Subsonic, error) {
|
||||||
user, _ := request.UserFrom(r.Context())
|
user, _ := request.UserFrom(r.Context())
|
||||||
|
|
||||||
repo := c.ds.PlayQueue(r.Context())
|
repo := api.ds.PlayQueue(r.Context())
|
||||||
pq, err := repo.Retrieve(user.ID)
|
pq, err := repo.Retrieve(user.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -95,7 +87,7 @@ func (c *BookmarksController) GetPlayQueue(w http.ResponseWriter, r *http.Reques
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *BookmarksController) SavePlayQueue(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) SavePlayQueue(r *http.Request) (*responses.Subsonic, error) {
|
||||||
ids, err := requiredParamStrings(r, "id")
|
ids, err := requiredParamStrings(r, "id")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -122,7 +114,7 @@ func (c *BookmarksController) SavePlayQueue(w http.ResponseWriter, r *http.Reque
|
|||||||
UpdatedAt: time.Time{},
|
UpdatedAt: time.Time{},
|
||||||
}
|
}
|
||||||
|
|
||||||
repo := c.ds.PlayQueue(r.Context())
|
repo := api.ds.PlayQueue(r.Context())
|
||||||
err = repo.Store(pq)
|
err = repo.Store(pq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -16,17 +16,8 @@ import (
|
|||||||
"github.com/navidrome/navidrome/utils"
|
"github.com/navidrome/navidrome/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type BrowsingController struct {
|
func (api *Router) GetMusicFolders(r *http.Request) (*responses.Subsonic, error) {
|
||||||
ds model.DataStore
|
mediaFolderList, _ := api.ds.MediaFolder(r.Context()).GetAll()
|
||||||
em core.ExternalMetadata
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewBrowsingController(ds model.DataStore, em core.ExternalMetadata) *BrowsingController {
|
|
||||||
return &BrowsingController{ds: ds, em: em}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *BrowsingController) GetMusicFolders(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
|
||||||
mediaFolderList, _ := c.ds.MediaFolder(r.Context()).GetAll()
|
|
||||||
folders := make([]responses.MusicFolder, len(mediaFolderList))
|
folders := make([]responses.MusicFolder, len(mediaFolderList))
|
||||||
for i, f := range mediaFolderList {
|
for i, f := range mediaFolderList {
|
||||||
folders[i].Id = f.ID
|
folders[i].Id = f.ID
|
||||||
@ -37,14 +28,14 @@ func (c *BrowsingController) GetMusicFolders(w http.ResponseWriter, r *http.Requ
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *BrowsingController) getArtistIndex(ctx context.Context, mediaFolderId int, ifModifiedSince time.Time) (*responses.Indexes, error) {
|
func (api *Router) getArtistIndex(ctx context.Context, mediaFolderId int, ifModifiedSince time.Time) (*responses.Indexes, error) {
|
||||||
folder, err := c.ds.MediaFolder(ctx).Get(int32(mediaFolderId))
|
folder, err := api.ds.MediaFolder(ctx).Get(int32(mediaFolderId))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(ctx, "Error retrieving MediaFolder", "id", mediaFolderId, err)
|
log.Error(ctx, "Error retrieving MediaFolder", "id", mediaFolderId, err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
l, err := c.ds.Property(ctx).DefaultGet(model.PropLastScan+"-"+folder.Path, "-1")
|
l, err := api.ds.Property(ctx).DefaultGet(model.PropLastScan+"-"+folder.Path, "-1")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(ctx, "Error retrieving LastScan property", err)
|
log.Error(ctx, "Error retrieving LastScan property", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -54,7 +45,7 @@ func (c *BrowsingController) getArtistIndex(ctx context.Context, mediaFolderId i
|
|||||||
ms, _ := strconv.ParseInt(l, 10, 64)
|
ms, _ := strconv.ParseInt(l, 10, 64)
|
||||||
lastModified := utils.ToTime(ms)
|
lastModified := utils.ToTime(ms)
|
||||||
if lastModified.After(ifModifiedSince) {
|
if lastModified.After(ifModifiedSince) {
|
||||||
indexes, err = c.ds.Artist(ctx).GetIndex()
|
indexes, err = api.ds.Artist(ctx).GetIndex()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(ctx, "Error retrieving Indexes", err)
|
log.Error(ctx, "Error retrieving Indexes", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -74,11 +65,11 @@ func (c *BrowsingController) getArtistIndex(ctx context.Context, mediaFolderId i
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *BrowsingController) GetIndexes(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) GetIndexes(r *http.Request) (*responses.Subsonic, error) {
|
||||||
musicFolderId := utils.ParamInt(r, "musicFolderId", 0)
|
musicFolderId := utils.ParamInt(r, "musicFolderId", 0)
|
||||||
ifModifiedSince := utils.ParamTime(r, "ifModifiedSince", time.Time{})
|
ifModifiedSince := utils.ParamTime(r, "ifModifiedSince", time.Time{})
|
||||||
|
|
||||||
res, err := c.getArtistIndex(r.Context(), musicFolderId, ifModifiedSince)
|
res, err := api.getArtistIndex(r.Context(), musicFolderId, ifModifiedSince)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -88,9 +79,9 @@ func (c *BrowsingController) GetIndexes(w http.ResponseWriter, r *http.Request)
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *BrowsingController) GetArtists(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) GetArtists(r *http.Request) (*responses.Subsonic, error) {
|
||||||
musicFolderId := utils.ParamInt(r, "musicFolderId", 0)
|
musicFolderId := utils.ParamInt(r, "musicFolderId", 0)
|
||||||
res, err := c.getArtistIndex(r.Context(), musicFolderId, time.Time{})
|
res, err := api.getArtistIndex(r.Context(), musicFolderId, time.Time{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -100,11 +91,11 @@ func (c *BrowsingController) GetArtists(w http.ResponseWriter, r *http.Request)
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *BrowsingController) GetMusicDirectory(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) GetMusicDirectory(r *http.Request) (*responses.Subsonic, error) {
|
||||||
id := utils.ParamString(r, "id")
|
id := utils.ParamString(r, "id")
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
|
|
||||||
entity, err := core.GetEntityByID(ctx, c.ds, id)
|
entity, err := core.GetEntityByID(ctx, api.ds, id)
|
||||||
if errors.Is(err, model.ErrNotFound) {
|
if errors.Is(err, model.ErrNotFound) {
|
||||||
log.Error(r, "Requested ID not found ", "id", id)
|
log.Error(r, "Requested ID not found ", "id", id)
|
||||||
return nil, newError(responses.ErrorDataNotFound, "Directory not found")
|
return nil, newError(responses.ErrorDataNotFound, "Directory not found")
|
||||||
@ -118,9 +109,9 @@ func (c *BrowsingController) GetMusicDirectory(w http.ResponseWriter, r *http.Re
|
|||||||
|
|
||||||
switch v := entity.(type) {
|
switch v := entity.(type) {
|
||||||
case *model.Artist:
|
case *model.Artist:
|
||||||
dir, err = c.buildArtistDirectory(ctx, v)
|
dir, err = api.buildArtistDirectory(ctx, v)
|
||||||
case *model.Album:
|
case *model.Album:
|
||||||
dir, err = c.buildAlbumDirectory(ctx, v)
|
dir, err = api.buildAlbumDirectory(ctx, v)
|
||||||
default:
|
default:
|
||||||
log.Error(r, "Requested ID of invalid type", "id", id, "entity", v)
|
log.Error(r, "Requested ID of invalid type", "id", id, "entity", v)
|
||||||
return nil, newError(responses.ErrorDataNotFound, "Directory not found")
|
return nil, newError(responses.ErrorDataNotFound, "Directory not found")
|
||||||
@ -136,11 +127,11 @@ func (c *BrowsingController) GetMusicDirectory(w http.ResponseWriter, r *http.Re
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *BrowsingController) GetArtist(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) GetArtist(r *http.Request) (*responses.Subsonic, error) {
|
||||||
id := utils.ParamString(r, "id")
|
id := utils.ParamString(r, "id")
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
|
|
||||||
artist, err := c.ds.Artist(ctx).Get(id)
|
artist, err := api.ds.Artist(ctx).Get(id)
|
||||||
if errors.Is(err, model.ErrNotFound) {
|
if errors.Is(err, model.ErrNotFound) {
|
||||||
log.Error(ctx, "Requested ArtistID not found ", "id", id)
|
log.Error(ctx, "Requested ArtistID not found ", "id", id)
|
||||||
return nil, newError(responses.ErrorDataNotFound, "Artist not found")
|
return nil, newError(responses.ErrorDataNotFound, "Artist not found")
|
||||||
@ -150,22 +141,22 @@ func (c *BrowsingController) GetArtist(w http.ResponseWriter, r *http.Request) (
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
albums, err := c.ds.Album(ctx).GetAllWithoutGenres(filter.AlbumsByArtistID(id))
|
albums, err := api.ds.Album(ctx).GetAllWithoutGenres(filter.AlbumsByArtistID(id))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(ctx, "Error retrieving albums by artist", "id", id, "name", artist.Name, err)
|
log.Error(ctx, "Error retrieving albums by artist", "id", id, "name", artist.Name, err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
response := newResponse()
|
response := newResponse()
|
||||||
response.ArtistWithAlbumsID3 = c.buildArtist(ctx, artist, albums)
|
response.ArtistWithAlbumsID3 = api.buildArtist(ctx, artist, albums)
|
||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *BrowsingController) GetAlbum(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) GetAlbum(r *http.Request) (*responses.Subsonic, error) {
|
||||||
id := utils.ParamString(r, "id")
|
id := utils.ParamString(r, "id")
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
|
|
||||||
album, err := c.ds.Album(ctx).Get(id)
|
album, err := api.ds.Album(ctx).Get(id)
|
||||||
if errors.Is(err, model.ErrNotFound) {
|
if errors.Is(err, model.ErrNotFound) {
|
||||||
log.Error(ctx, "Requested AlbumID not found ", "id", id)
|
log.Error(ctx, "Requested AlbumID not found ", "id", id)
|
||||||
return nil, newError(responses.ErrorDataNotFound, "Album not found")
|
return nil, newError(responses.ErrorDataNotFound, "Album not found")
|
||||||
@ -175,22 +166,22 @@ func (c *BrowsingController) GetAlbum(w http.ResponseWriter, r *http.Request) (*
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
mfs, err := c.ds.MediaFile(ctx).GetAll(filter.SongsByAlbum(id))
|
mfs, err := api.ds.MediaFile(ctx).GetAll(filter.SongsByAlbum(id))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(ctx, "Error retrieving tracks from album", "id", id, "name", album.Name, err)
|
log.Error(ctx, "Error retrieving tracks from album", "id", id, "name", album.Name, err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
response := newResponse()
|
response := newResponse()
|
||||||
response.AlbumWithSongsID3 = c.buildAlbum(ctx, album, mfs)
|
response.AlbumWithSongsID3 = api.buildAlbum(ctx, album, mfs)
|
||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *BrowsingController) GetSong(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) GetSong(r *http.Request) (*responses.Subsonic, error) {
|
||||||
id := utils.ParamString(r, "id")
|
id := utils.ParamString(r, "id")
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
|
|
||||||
mf, err := c.ds.MediaFile(ctx).Get(id)
|
mf, err := api.ds.MediaFile(ctx).Get(id)
|
||||||
if errors.Is(err, model.ErrNotFound) {
|
if errors.Is(err, model.ErrNotFound) {
|
||||||
log.Error(r, "Requested MediaFileID not found ", "id", id)
|
log.Error(r, "Requested MediaFileID not found ", "id", id)
|
||||||
return nil, newError(responses.ErrorDataNotFound, "Song not found")
|
return nil, newError(responses.ErrorDataNotFound, "Song not found")
|
||||||
@ -206,9 +197,9 @@ func (c *BrowsingController) GetSong(w http.ResponseWriter, r *http.Request) (*r
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *BrowsingController) GetGenres(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) GetGenres(r *http.Request) (*responses.Subsonic, error) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
genres, err := c.ds.Genre(ctx).GetAll(model.QueryOptions{Sort: "song_count, album_count, name desc", Order: "desc"})
|
genres, err := api.ds.Genre(ctx).GetAll(model.QueryOptions{Sort: "song_count, album_count, name desc", Order: "desc"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(r, err)
|
log.Error(r, err)
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -224,7 +215,7 @@ func (c *BrowsingController) GetGenres(w http.ResponseWriter, r *http.Request) (
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *BrowsingController) GetArtistInfo(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) GetArtistInfo(r *http.Request) (*responses.Subsonic, error) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
id, err := requiredParamString(r, "id")
|
id, err := requiredParamString(r, "id")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -233,7 +224,7 @@ func (c *BrowsingController) GetArtistInfo(w http.ResponseWriter, r *http.Reques
|
|||||||
count := utils.ParamInt(r, "count", 20)
|
count := utils.ParamInt(r, "count", 20)
|
||||||
includeNotPresent := utils.ParamBool(r, "includeNotPresent", false)
|
includeNotPresent := utils.ParamBool(r, "includeNotPresent", false)
|
||||||
|
|
||||||
artist, err := c.em.UpdateArtistInfo(ctx, id, count, includeNotPresent)
|
artist, err := api.externalMetadata.UpdateArtistInfo(ctx, id, count, includeNotPresent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -253,8 +244,8 @@ func (c *BrowsingController) GetArtistInfo(w http.ResponseWriter, r *http.Reques
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *BrowsingController) GetArtistInfo2(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) GetArtistInfo2(r *http.Request) (*responses.Subsonic, error) {
|
||||||
info, err := c.GetArtistInfo(w, r)
|
info, err := api.GetArtistInfo(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -275,7 +266,7 @@ func (c *BrowsingController) GetArtistInfo2(w http.ResponseWriter, r *http.Reque
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *BrowsingController) GetSimilarSongs(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) GetSimilarSongs(r *http.Request) (*responses.Subsonic, error) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
id, err := requiredParamString(r, "id")
|
id, err := requiredParamString(r, "id")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -283,7 +274,7 @@ func (c *BrowsingController) GetSimilarSongs(w http.ResponseWriter, r *http.Requ
|
|||||||
}
|
}
|
||||||
count := utils.ParamInt(r, "count", 50)
|
count := utils.ParamInt(r, "count", 50)
|
||||||
|
|
||||||
songs, err := c.em.SimilarSongs(ctx, id, count)
|
songs, err := api.externalMetadata.SimilarSongs(ctx, id, count)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -295,8 +286,8 @@ func (c *BrowsingController) GetSimilarSongs(w http.ResponseWriter, r *http.Requ
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *BrowsingController) GetSimilarSongs2(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) GetSimilarSongs2(r *http.Request) (*responses.Subsonic, error) {
|
||||||
res, err := c.GetSimilarSongs(w, r)
|
res, err := api.GetSimilarSongs(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -308,7 +299,7 @@ func (c *BrowsingController) GetSimilarSongs2(w http.ResponseWriter, r *http.Req
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *BrowsingController) GetTopSongs(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) GetTopSongs(r *http.Request) (*responses.Subsonic, error) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
artist, err := requiredParamString(r, "artist")
|
artist, err := requiredParamString(r, "artist")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -316,7 +307,7 @@ func (c *BrowsingController) GetTopSongs(w http.ResponseWriter, r *http.Request)
|
|||||||
}
|
}
|
||||||
count := utils.ParamInt(r, "count", 50)
|
count := utils.ParamInt(r, "count", 50)
|
||||||
|
|
||||||
songs, err := c.em.TopSongs(ctx, artist, count)
|
songs, err := api.externalMetadata.TopSongs(ctx, artist, count)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -328,7 +319,7 @@ func (c *BrowsingController) GetTopSongs(w http.ResponseWriter, r *http.Request)
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *BrowsingController) buildArtistDirectory(ctx context.Context, artist *model.Artist) (*responses.Directory, error) {
|
func (api *Router) buildArtistDirectory(ctx context.Context, artist *model.Artist) (*responses.Directory, error) {
|
||||||
dir := &responses.Directory{}
|
dir := &responses.Directory{}
|
||||||
dir.Id = artist.ID
|
dir.Id = artist.ID
|
||||||
dir.Name = artist.Name
|
dir.Name = artist.Name
|
||||||
@ -342,7 +333,7 @@ func (c *BrowsingController) buildArtistDirectory(ctx context.Context, artist *m
|
|||||||
dir.Starred = &artist.StarredAt
|
dir.Starred = &artist.StarredAt
|
||||||
}
|
}
|
||||||
|
|
||||||
albums, err := c.ds.Album(ctx).GetAllWithoutGenres(filter.AlbumsByArtistID(artist.ID))
|
albums, err := api.ds.Album(ctx).GetAllWithoutGenres(filter.AlbumsByArtistID(artist.ID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -351,14 +342,14 @@ func (c *BrowsingController) buildArtistDirectory(ctx context.Context, artist *m
|
|||||||
return dir, nil
|
return dir, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *BrowsingController) buildArtist(ctx context.Context, artist *model.Artist, albums model.Albums) *responses.ArtistWithAlbumsID3 {
|
func (api *Router) buildArtist(ctx context.Context, artist *model.Artist, albums model.Albums) *responses.ArtistWithAlbumsID3 {
|
||||||
a := &responses.ArtistWithAlbumsID3{}
|
a := &responses.ArtistWithAlbumsID3{}
|
||||||
a.ArtistID3 = toArtistID3(ctx, *artist)
|
a.ArtistID3 = toArtistID3(ctx, *artist)
|
||||||
a.Album = childrenFromAlbums(ctx, albums)
|
a.Album = childrenFromAlbums(ctx, albums)
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *BrowsingController) buildAlbumDirectory(ctx context.Context, album *model.Album) (*responses.Directory, error) {
|
func (api *Router) buildAlbumDirectory(ctx context.Context, album *model.Album) (*responses.Directory, error) {
|
||||||
dir := &responses.Directory{}
|
dir := &responses.Directory{}
|
||||||
dir.Id = album.ID
|
dir.Id = album.ID
|
||||||
dir.Name = album.Name
|
dir.Name = album.Name
|
||||||
@ -374,7 +365,7 @@ func (c *BrowsingController) buildAlbumDirectory(ctx context.Context, album *mod
|
|||||||
dir.Starred = &album.StarredAt
|
dir.Starred = &album.StarredAt
|
||||||
}
|
}
|
||||||
|
|
||||||
mfs, err := c.ds.MediaFile(ctx).GetAll(filter.SongsByAlbum(album.ID))
|
mfs, err := api.ds.MediaFile(ctx).GetAll(filter.SongsByAlbum(album.ID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -383,7 +374,7 @@ func (c *BrowsingController) buildAlbumDirectory(ctx context.Context, album *mod
|
|||||||
return dir, nil
|
return dir, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *BrowsingController) buildAlbum(ctx context.Context, album *model.Album, mfs model.MediaFiles) *responses.AlbumWithSongsID3 {
|
func (api *Router) buildAlbum(ctx context.Context, album *model.Album, mfs model.MediaFiles) *responses.AlbumWithSongsID3 {
|
||||||
dir := &responses.AlbumWithSongsID3{}
|
dir := &responses.AlbumWithSongsID3{}
|
||||||
dir.Id = album.ID
|
dir.Id = album.ID
|
||||||
dir.Name = album.Name
|
dir.Name = album.Name
|
||||||
|
@ -7,24 +7,15 @@ import (
|
|||||||
"github.com/navidrome/navidrome/conf"
|
"github.com/navidrome/navidrome/conf"
|
||||||
"github.com/navidrome/navidrome/log"
|
"github.com/navidrome/navidrome/log"
|
||||||
"github.com/navidrome/navidrome/model/request"
|
"github.com/navidrome/navidrome/model/request"
|
||||||
"github.com/navidrome/navidrome/scanner"
|
|
||||||
"github.com/navidrome/navidrome/server/subsonic/responses"
|
"github.com/navidrome/navidrome/server/subsonic/responses"
|
||||||
"github.com/navidrome/navidrome/utils"
|
"github.com/navidrome/navidrome/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LibraryScanningController struct {
|
func (api *Router) GetScanStatus(r *http.Request) (*responses.Subsonic, error) {
|
||||||
scanner scanner.Scanner
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewLibraryScanningController(scanner scanner.Scanner) *LibraryScanningController {
|
|
||||||
return &LibraryScanningController{scanner: scanner}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *LibraryScanningController) GetScanStatus(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
|
||||||
// TODO handle multiple mediafolders
|
// TODO handle multiple mediafolders
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
mediaFolder := conf.Server.MusicFolder
|
mediaFolder := conf.Server.MusicFolder
|
||||||
status, err := c.scanner.Status(mediaFolder)
|
status, err := api.scanner.Status(mediaFolder)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(ctx, "Error retrieving Scanner status", err)
|
log.Error(ctx, "Error retrieving Scanner status", err)
|
||||||
return nil, newError(responses.ErrorGeneric, "Internal Error")
|
return nil, newError(responses.ErrorGeneric, "Internal Error")
|
||||||
@ -39,7 +30,7 @@ func (c *LibraryScanningController) GetScanStatus(w http.ResponseWriter, r *http
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *LibraryScanningController) StartScan(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) StartScan(r *http.Request) (*responses.Subsonic, error) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
loggedUser, ok := request.UserFrom(ctx)
|
loggedUser, ok := request.UserFrom(ctx)
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -55,7 +46,7 @@ func (c *LibraryScanningController) StartScan(w http.ResponseWriter, r *http.Req
|
|||||||
go func() {
|
go func() {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
log.Info(ctx, "Triggering manual scan", "fullScan", fullScan, "user", loggedUser.UserName)
|
log.Info(ctx, "Triggering manual scan", "fullScan", fullScan, "user", loggedUser.UserName)
|
||||||
err := c.scanner.RescanAll(ctx, fullScan)
|
err := api.scanner.RescanAll(ctx, fullScan)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(ctx, "Error scanning", err)
|
log.Error(ctx, "Error scanning", err)
|
||||||
return
|
return
|
||||||
@ -63,5 +54,5 @@ func (c *LibraryScanningController) StartScan(w http.ResponseWriter, r *http.Req
|
|||||||
log.Info(ctx, "Manual scan complete", "user", loggedUser.UserName, "elapsed", time.Since(start).Round(100*time.Millisecond))
|
log.Info(ctx, "Manual scan complete", "user", loggedUser.UserName, "elapsed", time.Since(start).Round(100*time.Millisecond))
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return c.GetScanStatus(w, r)
|
return api.GetScanStatus(r)
|
||||||
}
|
}
|
||||||
|
@ -17,17 +17,7 @@ import (
|
|||||||
"github.com/navidrome/navidrome/utils"
|
"github.com/navidrome/navidrome/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MediaAnnotationController struct {
|
func (api *Router) SetRating(r *http.Request) (*responses.Subsonic, error) {
|
||||||
ds model.DataStore
|
|
||||||
playTracker scrobbler.PlayTracker
|
|
||||||
broker events.Broker
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewMediaAnnotationController(ds model.DataStore, playTracker scrobbler.PlayTracker, broker events.Broker) *MediaAnnotationController {
|
|
||||||
return &MediaAnnotationController{ds: ds, playTracker: playTracker, broker: broker}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *MediaAnnotationController) SetRating(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
|
||||||
id, err := requiredParamString(r, "id")
|
id, err := requiredParamString(r, "id")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -38,7 +28,7 @@ func (c *MediaAnnotationController) SetRating(w http.ResponseWriter, r *http.Req
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.Debug(r, "Setting rating", "rating", rating, "id", id)
|
log.Debug(r, "Setting rating", "rating", rating, "id", id)
|
||||||
err = c.setRating(r.Context(), id, rating)
|
err = api.setRating(r.Context(), id, rating)
|
||||||
|
|
||||||
if errors.Is(err, model.ErrNotFound) {
|
if errors.Is(err, model.ErrNotFound) {
|
||||||
log.Error(r, err)
|
log.Error(r, err)
|
||||||
@ -52,23 +42,23 @@ func (c *MediaAnnotationController) SetRating(w http.ResponseWriter, r *http.Req
|
|||||||
return newResponse(), nil
|
return newResponse(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MediaAnnotationController) setRating(ctx context.Context, id string, rating int) error {
|
func (api *Router) setRating(ctx context.Context, id string, rating int) error {
|
||||||
var repo model.AnnotatedRepository
|
var repo model.AnnotatedRepository
|
||||||
var resource string
|
var resource string
|
||||||
|
|
||||||
entity, err := core.GetEntityByID(ctx, c.ds, id)
|
entity, err := core.GetEntityByID(ctx, api.ds, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
switch entity.(type) {
|
switch entity.(type) {
|
||||||
case *model.Artist:
|
case *model.Artist:
|
||||||
repo = c.ds.Artist(ctx)
|
repo = api.ds.Artist(ctx)
|
||||||
resource = "artist"
|
resource = "artist"
|
||||||
case *model.Album:
|
case *model.Album:
|
||||||
repo = c.ds.Album(ctx)
|
repo = api.ds.Album(ctx)
|
||||||
resource = "album"
|
resource = "album"
|
||||||
default:
|
default:
|
||||||
repo = c.ds.MediaFile(ctx)
|
repo = api.ds.MediaFile(ctx)
|
||||||
resource = "song"
|
resource = "song"
|
||||||
}
|
}
|
||||||
err = repo.SetRating(rating, id)
|
err = repo.SetRating(rating, id)
|
||||||
@ -76,11 +66,11 @@ func (c *MediaAnnotationController) setRating(ctx context.Context, id string, ra
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
event := &events.RefreshResource{}
|
event := &events.RefreshResource{}
|
||||||
c.broker.SendMessage(ctx, event.With(resource, id))
|
api.broker.SendMessage(ctx, event.With(resource, id))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MediaAnnotationController) Star(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) Star(r *http.Request) (*responses.Subsonic, error) {
|
||||||
ids := utils.ParamStrings(r, "id")
|
ids := utils.ParamStrings(r, "id")
|
||||||
albumIds := utils.ParamStrings(r, "albumId")
|
albumIds := utils.ParamStrings(r, "albumId")
|
||||||
artistIds := utils.ParamStrings(r, "artistId")
|
artistIds := utils.ParamStrings(r, "artistId")
|
||||||
@ -90,7 +80,7 @@ func (c *MediaAnnotationController) Star(w http.ResponseWriter, r *http.Request)
|
|||||||
ids = append(ids, albumIds...)
|
ids = append(ids, albumIds...)
|
||||||
ids = append(ids, artistIds...)
|
ids = append(ids, artistIds...)
|
||||||
|
|
||||||
err := c.setStar(r.Context(), true, ids...)
|
err := api.setStar(r.Context(), true, ids...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -98,7 +88,7 @@ func (c *MediaAnnotationController) Star(w http.ResponseWriter, r *http.Request)
|
|||||||
return newResponse(), nil
|
return newResponse(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MediaAnnotationController) Unstar(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) Unstar(r *http.Request) (*responses.Subsonic, error) {
|
||||||
ids := utils.ParamStrings(r, "id")
|
ids := utils.ParamStrings(r, "id")
|
||||||
albumIds := utils.ParamStrings(r, "albumId")
|
albumIds := utils.ParamStrings(r, "albumId")
|
||||||
artistIds := utils.ParamStrings(r, "artistId")
|
artistIds := utils.ParamStrings(r, "artistId")
|
||||||
@ -108,7 +98,7 @@ func (c *MediaAnnotationController) Unstar(w http.ResponseWriter, r *http.Reques
|
|||||||
ids = append(ids, albumIds...)
|
ids = append(ids, albumIds...)
|
||||||
ids = append(ids, artistIds...)
|
ids = append(ids, artistIds...)
|
||||||
|
|
||||||
err := c.setStar(r.Context(), false, ids...)
|
err := api.setStar(r.Context(), false, ids...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -116,7 +106,7 @@ func (c *MediaAnnotationController) Unstar(w http.ResponseWriter, r *http.Reques
|
|||||||
return newResponse(), nil
|
return newResponse(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MediaAnnotationController) setStar(ctx context.Context, star bool, ids ...string) error {
|
func (api *Router) setStar(ctx context.Context, star bool, ids ...string) error {
|
||||||
if len(ids) == 0 {
|
if len(ids) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -126,7 +116,7 @@ func (c *MediaAnnotationController) setStar(ctx context.Context, star bool, ids
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
event := &events.RefreshResource{}
|
event := &events.RefreshResource{}
|
||||||
err := c.ds.WithTx(func(tx model.DataStore) error {
|
err := api.ds.WithTx(func(tx model.DataStore) error {
|
||||||
for _, id := range ids {
|
for _, id := range ids {
|
||||||
exist, err := tx.Album(ctx).Exists(id)
|
exist, err := tx.Album(ctx).Exists(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -158,7 +148,7 @@ func (c *MediaAnnotationController) setStar(ctx context.Context, star bool, ids
|
|||||||
}
|
}
|
||||||
event = event.With("song", id)
|
event = event.With("song", id)
|
||||||
}
|
}
|
||||||
c.broker.SendMessage(ctx, event)
|
api.broker.SendMessage(ctx, event)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -173,7 +163,7 @@ func (c *MediaAnnotationController) setStar(ctx context.Context, star bool, ids
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MediaAnnotationController) Scrobble(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) Scrobble(r *http.Request) (*responses.Subsonic, error) {
|
||||||
ids, err := requiredParamStrings(r, "id")
|
ids, err := requiredParamStrings(r, "id")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -186,12 +176,12 @@ func (c *MediaAnnotationController) Scrobble(w http.ResponseWriter, r *http.Requ
|
|||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
|
|
||||||
if submission {
|
if submission {
|
||||||
err := c.scrobblerSubmit(ctx, ids, times)
|
err := api.scrobblerSubmit(ctx, ids, times)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(ctx, "Error registering scrobbles", "ids", ids, "times", times, err)
|
log.Error(ctx, "Error registering scrobbles", "ids", ids, "times", times, err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err := c.scrobblerNowPlaying(ctx, ids[0])
|
err := api.scrobblerNowPlaying(ctx, ids[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(ctx, "Error setting NowPlaying", "id", ids[0], err)
|
log.Error(ctx, "Error setting NowPlaying", "id", ids[0], err)
|
||||||
}
|
}
|
||||||
@ -200,7 +190,7 @@ func (c *MediaAnnotationController) Scrobble(w http.ResponseWriter, r *http.Requ
|
|||||||
return newResponse(), nil
|
return newResponse(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MediaAnnotationController) scrobblerSubmit(ctx context.Context, ids []string, times []time.Time) error {
|
func (api *Router) scrobblerSubmit(ctx context.Context, ids []string, times []time.Time) error {
|
||||||
var submissions []scrobbler.Submission
|
var submissions []scrobbler.Submission
|
||||||
log.Debug(ctx, "Scrobbling tracks", "ids", ids, "times", times)
|
log.Debug(ctx, "Scrobbling tracks", "ids", ids, "times", times)
|
||||||
for i, id := range ids {
|
for i, id := range ids {
|
||||||
@ -213,11 +203,11 @@ func (c *MediaAnnotationController) scrobblerSubmit(ctx context.Context, ids []s
|
|||||||
submissions = append(submissions, scrobbler.Submission{TrackID: id, Timestamp: t})
|
submissions = append(submissions, scrobbler.Submission{TrackID: id, Timestamp: t})
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.playTracker.Submit(ctx, submissions)
|
return api.scrobbler.Submit(ctx, submissions)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MediaAnnotationController) scrobblerNowPlaying(ctx context.Context, trackId string) error {
|
func (api *Router) scrobblerNowPlaying(ctx context.Context, trackId string) error {
|
||||||
mf, err := c.ds.MediaFile(ctx).Get(trackId)
|
mf, err := api.ds.MediaFile(ctx).Get(trackId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -234,6 +224,6 @@ func (c *MediaAnnotationController) scrobblerNowPlaying(ctx context.Context, tra
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.Info(ctx, "Now Playing", "title", mf.Title, "artist", mf.Artist, "user", username, "player", player.Name)
|
log.Info(ctx, "Now Playing", "title", mf.Title, "artist", mf.Artist, "user", username, "player", player.Name)
|
||||||
err = c.playTracker.NowPlaying(ctx, clientId, client, trackId)
|
err = api.scrobbler.NowPlaying(ctx, clientId, client, trackId)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/navidrome/navidrome/model/request"
|
"github.com/navidrome/navidrome/model/request"
|
||||||
@ -19,8 +18,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var _ = Describe("MediaAnnotationController", func() {
|
var _ = Describe("MediaAnnotationController", func() {
|
||||||
var controller *MediaAnnotationController
|
var router *Router
|
||||||
var w *httptest.ResponseRecorder
|
|
||||||
var ds model.DataStore
|
var ds model.DataStore
|
||||||
var playTracker *fakePlayTracker
|
var playTracker *fakePlayTracker
|
||||||
var eventBroker *fakeEventBroker
|
var eventBroker *fakeEventBroker
|
||||||
@ -31,8 +29,7 @@ var _ = Describe("MediaAnnotationController", func() {
|
|||||||
ds = &tests.MockDataStore{}
|
ds = &tests.MockDataStore{}
|
||||||
playTracker = &fakePlayTracker{}
|
playTracker = &fakePlayTracker{}
|
||||||
eventBroker = &fakeEventBroker{}
|
eventBroker = &fakeEventBroker{}
|
||||||
controller = NewMediaAnnotationController(ds, playTracker, eventBroker)
|
router = New(ds, nil, nil, nil, nil, nil, nil, eventBroker, nil, playTracker)
|
||||||
w = httptest.NewRecorder()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
Describe("Scrobble", func() {
|
Describe("Scrobble", func() {
|
||||||
@ -40,7 +37,7 @@ var _ = Describe("MediaAnnotationController", func() {
|
|||||||
submissionTime := time.Now()
|
submissionTime := time.Now()
|
||||||
r := newGetRequest("id=12", "id=34")
|
r := newGetRequest("id=12", "id=34")
|
||||||
|
|
||||||
_, err := controller.Scrobble(w, r)
|
_, err := router.Scrobble(r)
|
||||||
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(playTracker.Submissions).To(HaveLen(2))
|
Expect(playTracker.Submissions).To(HaveLen(2))
|
||||||
@ -57,7 +54,7 @@ var _ = Describe("MediaAnnotationController", func() {
|
|||||||
t2 := utils.ToMillis(time2)
|
t2 := utils.ToMillis(time2)
|
||||||
r := newGetRequest("id=12", "id=34", fmt.Sprintf("time=%d", t1), fmt.Sprintf("time=%d", t2))
|
r := newGetRequest("id=12", "id=34", fmt.Sprintf("time=%d", t1), fmt.Sprintf("time=%d", t2))
|
||||||
|
|
||||||
_, err := controller.Scrobble(w, r)
|
_, err := router.Scrobble(r)
|
||||||
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(playTracker.Submissions).To(HaveLen(2))
|
Expect(playTracker.Submissions).To(HaveLen(2))
|
||||||
@ -70,7 +67,7 @@ var _ = Describe("MediaAnnotationController", func() {
|
|||||||
It("checks if number of ids match number of times", func() {
|
It("checks if number of ids match number of times", func() {
|
||||||
r := newGetRequest("id=12", "id=34", "time=1111")
|
r := newGetRequest("id=12", "id=34", "time=1111")
|
||||||
|
|
||||||
_, err := controller.Scrobble(w, r)
|
_, err := router.Scrobble(r)
|
||||||
|
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).To(HaveOccurred())
|
||||||
Expect(playTracker.Submissions).To(BeEmpty())
|
Expect(playTracker.Submissions).To(BeEmpty())
|
||||||
@ -86,14 +83,14 @@ var _ = Describe("MediaAnnotationController", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
It("does not scrobble", func() {
|
It("does not scrobble", func() {
|
||||||
_, err := controller.Scrobble(w, req)
|
_, err := router.Scrobble(req)
|
||||||
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(playTracker.Submissions).To(BeEmpty())
|
Expect(playTracker.Submissions).To(BeEmpty())
|
||||||
})
|
})
|
||||||
|
|
||||||
It("registers a NowPlaying", func() {
|
It("registers a NowPlaying", func() {
|
||||||
_, err := controller.Scrobble(w, req)
|
_, err := router.Scrobble(req)
|
||||||
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(playTracker.Playing).To(HaveLen(1))
|
Expect(playTracker.Playing).To(HaveLen(1))
|
||||||
@ -109,7 +106,7 @@ type fakePlayTracker struct {
|
|||||||
Error error
|
Error error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakePlayTracker) NowPlaying(ctx context.Context, playerId string, playerName string, trackId string) error {
|
func (f *fakePlayTracker) NowPlaying(_ context.Context, playerId string, _ string, trackId string) error {
|
||||||
if f.Error != nil {
|
if f.Error != nil {
|
||||||
return f.Error
|
return f.Error
|
||||||
}
|
}
|
||||||
@ -120,11 +117,11 @@ func (f *fakePlayTracker) NowPlaying(ctx context.Context, playerId string, playe
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakePlayTracker) GetNowPlaying(ctx context.Context) ([]scrobbler.NowPlayingInfo, error) {
|
func (f *fakePlayTracker) GetNowPlaying(_ context.Context) ([]scrobbler.NowPlayingInfo, error) {
|
||||||
return nil, f.Error
|
return nil, f.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakePlayTracker) Submit(ctx context.Context, submissions []scrobbler.Submission) error {
|
func (f *fakePlayTracker) Submit(_ context.Context, submissions []scrobbler.Submission) error {
|
||||||
if f.Error != nil {
|
if f.Error != nil {
|
||||||
return f.Error
|
return f.Error
|
||||||
}
|
}
|
||||||
@ -139,7 +136,7 @@ type fakeEventBroker struct {
|
|||||||
Events []events.Event
|
Events []events.Event
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeEventBroker) SendMessage(ctx context.Context, event events.Event) {
|
func (f *fakeEventBroker) SendMessage(_ context.Context, event events.Event) {
|
||||||
f.Events = append(f.Events, event)
|
f.Events = append(f.Events, event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
|
|
||||||
"github.com/navidrome/navidrome/conf"
|
"github.com/navidrome/navidrome/conf"
|
||||||
"github.com/navidrome/navidrome/consts"
|
"github.com/navidrome/navidrome/consts"
|
||||||
"github.com/navidrome/navidrome/core"
|
|
||||||
"github.com/navidrome/navidrome/log"
|
"github.com/navidrome/navidrome/log"
|
||||||
"github.com/navidrome/navidrome/model"
|
"github.com/navidrome/navidrome/model"
|
||||||
"github.com/navidrome/navidrome/resources"
|
"github.com/navidrome/navidrome/resources"
|
||||||
@ -18,37 +17,28 @@ import (
|
|||||||
"github.com/navidrome/navidrome/utils/gravatar"
|
"github.com/navidrome/navidrome/utils/gravatar"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MediaRetrievalController struct {
|
func (api *Router) GetAvatar(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
||||||
artwork core.Artwork
|
|
||||||
ds model.DataStore
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewMediaRetrievalController(artwork core.Artwork, ds model.DataStore) *MediaRetrievalController {
|
|
||||||
return &MediaRetrievalController{artwork: artwork, ds: ds}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *MediaRetrievalController) GetAvatar(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
|
||||||
if !conf.Server.EnableGravatar {
|
if !conf.Server.EnableGravatar {
|
||||||
return c.getPlaceHolderAvatar(w, r)
|
return api.getPlaceHolderAvatar(w, r)
|
||||||
}
|
}
|
||||||
username, err := requiredParamString(r, "username")
|
username, err := requiredParamString(r, "username")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
u, err := c.ds.User(ctx).FindByUsername(username)
|
u, err := api.ds.User(ctx).FindByUsername(username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if u.Email == "" {
|
if u.Email == "" {
|
||||||
log.Warn(ctx, "User needs an email for gravatar to work", "username", username)
|
log.Warn(ctx, "User needs an email for gravatar to work", "username", username)
|
||||||
return c.getPlaceHolderAvatar(w, r)
|
return api.getPlaceHolderAvatar(w, r)
|
||||||
}
|
}
|
||||||
http.Redirect(w, r, gravatar.Url(u.Email, 0), http.StatusFound)
|
http.Redirect(w, r, gravatar.Url(u.Email, 0), http.StatusFound)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MediaRetrievalController) getPlaceHolderAvatar(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) getPlaceHolderAvatar(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
||||||
f, err := resources.FS().Open(consts.PlaceholderAvatar)
|
f, err := resources.FS().Open(consts.PlaceholderAvatar)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(r, "Image not found", err)
|
log.Error(r, "Image not found", err)
|
||||||
@ -60,13 +50,13 @@ func (c *MediaRetrievalController) getPlaceHolderAvatar(w http.ResponseWriter, r
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MediaRetrievalController) GetCoverArt(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) GetCoverArt(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
||||||
id := utils.ParamStringDefault(r, "id", "non-existent")
|
id := utils.ParamStringDefault(r, "id", "non-existent")
|
||||||
size := utils.ParamInt(r, "size", 0)
|
size := utils.ParamInt(r, "size", 0)
|
||||||
|
|
||||||
w.Header().Set("cache-control", "public, max-age=315360000")
|
w.Header().Set("cache-control", "public, max-age=315360000")
|
||||||
|
|
||||||
imgReader, err := c.artwork.Get(r.Context(), id, size)
|
imgReader, err := api.artwork.Get(r.Context(), id, size)
|
||||||
if errors.Is(err, model.ErrNotFound) {
|
if errors.Is(err, model.ErrNotFound) {
|
||||||
log.Error(r, "Couldn't find coverArt", "id", id, err)
|
log.Error(r, "Couldn't find coverArt", "id", id, err)
|
||||||
return nil, newError(responses.ErrorDataNotFound, "Artwork not found")
|
return nil, newError(responses.ErrorDataNotFound, "Artwork not found")
|
||||||
@ -92,13 +82,13 @@ func isSynced(rawLyrics string) bool {
|
|||||||
return r.MatchString(rawLyrics)
|
return r.MatchString(rawLyrics)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MediaRetrievalController) GetLyrics(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) GetLyrics(r *http.Request) (*responses.Subsonic, error) {
|
||||||
artist := utils.ParamString(r, "artist")
|
artist := utils.ParamString(r, "artist")
|
||||||
title := utils.ParamString(r, "title")
|
title := utils.ParamString(r, "title")
|
||||||
response := newResponse()
|
response := newResponse()
|
||||||
lyrics := responses.Lyrics{}
|
lyrics := responses.Lyrics{}
|
||||||
response.Lyrics = &lyrics
|
response.Lyrics = &lyrics
|
||||||
media_files, err := c.ds.MediaFile(r.Context()).GetAll(filter.SongsWithLyrics(artist, title))
|
media_files, err := api.ds.MediaFile(r.Context()).GetAll(filter.SongsWithLyrics(artist, title))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -15,7 +15,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var _ = Describe("MediaRetrievalController", func() {
|
var _ = Describe("MediaRetrievalController", func() {
|
||||||
var controller *MediaRetrievalController
|
var router *Router
|
||||||
var ds model.DataStore
|
var ds model.DataStore
|
||||||
mockRepo := &mockedMediaFile{}
|
mockRepo := &mockedMediaFile{}
|
||||||
var artwork *fakeArtwork
|
var artwork *fakeArtwork
|
||||||
@ -26,7 +26,7 @@ var _ = Describe("MediaRetrievalController", func() {
|
|||||||
MockedMediaFile: mockRepo,
|
MockedMediaFile: mockRepo,
|
||||||
}
|
}
|
||||||
artwork = &fakeArtwork{}
|
artwork = &fakeArtwork{}
|
||||||
controller = NewMediaRetrievalController(artwork, ds)
|
router = New(ds, artwork, nil, nil, nil, nil, nil, nil, nil, nil)
|
||||||
w = httptest.NewRecorder()
|
w = httptest.NewRecorder()
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ var _ = Describe("MediaRetrievalController", func() {
|
|||||||
It("should return data for that id", func() {
|
It("should return data for that id", func() {
|
||||||
artwork.data = "image data"
|
artwork.data = "image data"
|
||||||
r := newGetRequest("id=34", "size=128")
|
r := newGetRequest("id=34", "size=128")
|
||||||
_, err := controller.GetCoverArt(w, r)
|
_, err := router.GetCoverArt(w, r)
|
||||||
|
|
||||||
Expect(err).To(BeNil())
|
Expect(err).To(BeNil())
|
||||||
Expect(artwork.recvId).To(Equal("34"))
|
Expect(artwork.recvId).To(Equal("34"))
|
||||||
@ -44,7 +44,7 @@ var _ = Describe("MediaRetrievalController", func() {
|
|||||||
|
|
||||||
It("should return placeholder if id parameter is missing (mimicking Subsonic)", func() {
|
It("should return placeholder if id parameter is missing (mimicking Subsonic)", func() {
|
||||||
r := newGetRequest()
|
r := newGetRequest()
|
||||||
_, err := controller.GetCoverArt(w, r)
|
_, err := router.GetCoverArt(w, r)
|
||||||
|
|
||||||
Expect(err).To(BeNil())
|
Expect(err).To(BeNil())
|
||||||
Expect(w.Body.String()).To(Equal(artwork.data))
|
Expect(w.Body.String()).To(Equal(artwork.data))
|
||||||
@ -53,7 +53,7 @@ var _ = Describe("MediaRetrievalController", func() {
|
|||||||
It("should fail when the file is not found", func() {
|
It("should fail when the file is not found", func() {
|
||||||
artwork.err = model.ErrNotFound
|
artwork.err = model.ErrNotFound
|
||||||
r := newGetRequest("id=34", "size=128")
|
r := newGetRequest("id=34", "size=128")
|
||||||
_, err := controller.GetCoverArt(w, r)
|
_, err := router.GetCoverArt(w, r)
|
||||||
|
|
||||||
Expect(err).To(MatchError("Artwork not found"))
|
Expect(err).To(MatchError("Artwork not found"))
|
||||||
})
|
})
|
||||||
@ -61,7 +61,7 @@ var _ = Describe("MediaRetrievalController", func() {
|
|||||||
It("should fail when there is an unknown error", func() {
|
It("should fail when there is an unknown error", func() {
|
||||||
artwork.err = errors.New("weird error")
|
artwork.err = errors.New("weird error")
|
||||||
r := newGetRequest("id=34", "size=128")
|
r := newGetRequest("id=34", "size=128")
|
||||||
_, err := controller.GetCoverArt(w, r)
|
_, err := router.GetCoverArt(w, r)
|
||||||
|
|
||||||
Expect(err).To(MatchError("weird error"))
|
Expect(err).To(MatchError("weird error"))
|
||||||
})
|
})
|
||||||
@ -78,7 +78,7 @@ var _ = Describe("MediaRetrievalController", func() {
|
|||||||
Lyrics: "[00:18.80]We're no strangers to love\n[00:22.80]You know the rules and so do I",
|
Lyrics: "[00:18.80]We're no strangers to love\n[00:22.80]You know the rules and so do I",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
response, err := controller.GetLyrics(w, r)
|
response, err := router.GetLyrics(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("You're missing something.", err)
|
log.Error("You're missing something.", err)
|
||||||
}
|
}
|
||||||
@ -90,7 +90,7 @@ var _ = Describe("MediaRetrievalController", func() {
|
|||||||
It("should return empty subsonic response if the record corresponding to the given artist & title is not found", func() {
|
It("should return empty subsonic response if the record corresponding to the given artist & title is not found", func() {
|
||||||
r := newGetRequest("artist=Dheeraj", "title=Rinkiya+Ke+Papa")
|
r := newGetRequest("artist=Dheeraj", "title=Rinkiya+Ke+Papa")
|
||||||
mockRepo.SetData(model.MediaFiles{})
|
mockRepo.SetData(model.MediaFiles{})
|
||||||
response, err := controller.GetLyrics(w, r)
|
response, err := router.GetLyrics(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("You're missing something.", err)
|
log.Error("You're missing something.", err)
|
||||||
}
|
}
|
||||||
@ -110,7 +110,7 @@ type fakeArtwork struct {
|
|||||||
recvSize int
|
recvSize int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *fakeArtwork) Get(ctx context.Context, id string, size int) (io.ReadCloser, error) {
|
func (c *fakeArtwork) Get(_ context.Context, id string, size int) (io.ReadCloser, error) {
|
||||||
if c.err != nil {
|
if c.err != nil {
|
||||||
return nil, c.err
|
return nil, c.err
|
||||||
}
|
}
|
||||||
@ -129,7 +129,7 @@ var _ = Describe("isSynced", func() {
|
|||||||
})
|
})
|
||||||
It("returns true if lyrics contain timestamps", func() {
|
It("returns true if lyrics contain timestamps", func() {
|
||||||
Expect(isSynced(`NF Real Music
|
Expect(isSynced(`NF Real Music
|
||||||
[00:00] ksdjjs
|
[00:00] First line
|
||||||
[00:00.85] JUST LIKE YOU
|
[00:00.85] JUST LIKE YOU
|
||||||
[00:00.85] Just in case my car goes off the highway`)).To(Equal(true))
|
[00:00.85] Just in case my car goes off the highway`)).To(Equal(true))
|
||||||
Expect(isSynced("[04:02:50.85] Never gonna give you up")).To(Equal(true))
|
Expect(isSynced("[04:02:50.85] Never gonna give you up")).To(Equal(true))
|
||||||
@ -148,6 +148,6 @@ func (m *mockedMediaFile) SetData(mfs model.MediaFiles) {
|
|||||||
m.data = mfs
|
m.data = mfs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockedMediaFile) GetAll(options ...model.QueryOptions) (model.MediaFiles, error) {
|
func (m *mockedMediaFile) GetAll(...model.QueryOptions) (model.MediaFiles, error) {
|
||||||
return m.data, nil
|
return m.data, nil
|
||||||
}
|
}
|
||||||
|
@ -6,49 +6,39 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/navidrome/navidrome/core"
|
|
||||||
"github.com/navidrome/navidrome/log"
|
"github.com/navidrome/navidrome/log"
|
||||||
"github.com/navidrome/navidrome/model"
|
"github.com/navidrome/navidrome/model"
|
||||||
"github.com/navidrome/navidrome/server/subsonic/responses"
|
"github.com/navidrome/navidrome/server/subsonic/responses"
|
||||||
"github.com/navidrome/navidrome/utils"
|
"github.com/navidrome/navidrome/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PlaylistsController struct {
|
func (api *Router) GetPlaylists(r *http.Request) (*responses.Subsonic, error) {
|
||||||
ds model.DataStore
|
|
||||||
pls core.Playlists
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPlaylistsController(ds model.DataStore, pls core.Playlists) *PlaylistsController {
|
|
||||||
return &PlaylistsController{ds: ds, pls: pls}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *PlaylistsController) GetPlaylists(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
allPls, err := c.ds.Playlist(ctx).GetAll(model.QueryOptions{Sort: "name"})
|
allPls, err := api.ds.Playlist(ctx).GetAll(model.QueryOptions{Sort: "name"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(r, err)
|
log.Error(r, err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
playlists := make([]responses.Playlist, len(allPls))
|
playlists := make([]responses.Playlist, len(allPls))
|
||||||
for i, p := range allPls {
|
for i, p := range allPls {
|
||||||
playlists[i] = *c.buildPlaylist(p)
|
playlists[i] = *api.buildPlaylist(p)
|
||||||
}
|
}
|
||||||
response := newResponse()
|
response := newResponse()
|
||||||
response.Playlists = &responses.Playlists{Playlist: playlists}
|
response.Playlists = &responses.Playlists{Playlist: playlists}
|
||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PlaylistsController) GetPlaylist(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) GetPlaylist(r *http.Request) (*responses.Subsonic, error) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
id, err := requiredParamString(r, "id")
|
id, err := requiredParamString(r, "id")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return c.getPlaylist(ctx, id)
|
return api.getPlaylist(ctx, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PlaylistsController) getPlaylist(ctx context.Context, id string) (*responses.Subsonic, error) {
|
func (api *Router) getPlaylist(ctx context.Context, id string) (*responses.Subsonic, error) {
|
||||||
pls, err := c.ds.Playlist(ctx).GetWithTracks(id)
|
pls, err := api.ds.Playlist(ctx).GetWithTracks(id)
|
||||||
if errors.Is(err, model.ErrNotFound) {
|
if errors.Is(err, model.ErrNotFound) {
|
||||||
log.Error(ctx, err.Error(), "id", id)
|
log.Error(ctx, err.Error(), "id", id)
|
||||||
return nil, newError(responses.ErrorDataNotFound, "Directory not found")
|
return nil, newError(responses.ErrorDataNotFound, "Directory not found")
|
||||||
@ -59,12 +49,12 @@ func (c *PlaylistsController) getPlaylist(ctx context.Context, id string) (*resp
|
|||||||
}
|
}
|
||||||
|
|
||||||
response := newResponse()
|
response := newResponse()
|
||||||
response.Playlist = c.buildPlaylistWithSongs(ctx, pls)
|
response.Playlist = api.buildPlaylistWithSongs(ctx, pls)
|
||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PlaylistsController) create(ctx context.Context, playlistId, name string, ids []string) (string, error) {
|
func (api *Router) create(ctx context.Context, playlistId, name string, ids []string) (string, error) {
|
||||||
err := c.ds.WithTx(func(tx model.DataStore) error {
|
err := api.ds.WithTx(func(tx model.DataStore) error {
|
||||||
owner := getUser(ctx)
|
owner := getUser(ctx)
|
||||||
var pls *model.Playlist
|
var pls *model.Playlist
|
||||||
var err error
|
var err error
|
||||||
@ -91,7 +81,7 @@ func (c *PlaylistsController) create(ctx context.Context, playlistId, name strin
|
|||||||
return playlistId, err
|
return playlistId, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PlaylistsController) CreatePlaylist(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) CreatePlaylist(r *http.Request) (*responses.Subsonic, error) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
songIds := utils.ParamStrings(r, "songId")
|
songIds := utils.ParamStrings(r, "songId")
|
||||||
playlistId := utils.ParamString(r, "playlistId")
|
playlistId := utils.ParamString(r, "playlistId")
|
||||||
@ -99,20 +89,20 @@ func (c *PlaylistsController) CreatePlaylist(w http.ResponseWriter, r *http.Requ
|
|||||||
if playlistId == "" && name == "" {
|
if playlistId == "" && name == "" {
|
||||||
return nil, errors.New("required parameter name is missing")
|
return nil, errors.New("required parameter name is missing")
|
||||||
}
|
}
|
||||||
id, err := c.create(ctx, playlistId, name, songIds)
|
id, err := api.create(ctx, playlistId, name, songIds)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(r, err)
|
log.Error(r, err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return c.getPlaylist(ctx, id)
|
return api.getPlaylist(ctx, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PlaylistsController) DeletePlaylist(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) DeletePlaylist(r *http.Request) (*responses.Subsonic, error) {
|
||||||
id, err := requiredParamString(r, "id")
|
id, err := requiredParamString(r, "id")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = c.ds.Playlist(r.Context()).Delete(id)
|
err = api.ds.Playlist(r.Context()).Delete(id)
|
||||||
if errors.Is(err, model.ErrNotAuthorized) {
|
if errors.Is(err, model.ErrNotAuthorized) {
|
||||||
return nil, newError(responses.ErrorAuthorizationFail)
|
return nil, newError(responses.ErrorAuthorizationFail)
|
||||||
}
|
}
|
||||||
@ -123,7 +113,7 @@ func (c *PlaylistsController) DeletePlaylist(w http.ResponseWriter, r *http.Requ
|
|||||||
return newResponse(), nil
|
return newResponse(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PlaylistsController) UpdatePlaylist(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) UpdatePlaylist(r *http.Request) (*responses.Subsonic, error) {
|
||||||
playlistId, err := requiredParamString(r, "playlistId")
|
playlistId, err := requiredParamString(r, "playlistId")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -151,7 +141,7 @@ func (c *PlaylistsController) UpdatePlaylist(w http.ResponseWriter, r *http.Requ
|
|||||||
log.Trace(r, fmt.Sprintf("-- Adding: '%v'", songsToAdd))
|
log.Trace(r, fmt.Sprintf("-- Adding: '%v'", songsToAdd))
|
||||||
log.Trace(r, fmt.Sprintf("-- Removing: '%v'", songIndexesToRemove))
|
log.Trace(r, fmt.Sprintf("-- Removing: '%v'", songIndexesToRemove))
|
||||||
|
|
||||||
err = c.pls.Update(r.Context(), playlistId, plsName, comment, public, songsToAdd, songIndexesToRemove)
|
err = api.playlists.Update(r.Context(), playlistId, plsName, comment, public, songsToAdd, songIndexesToRemove)
|
||||||
if errors.Is(err, model.ErrNotAuthorized) {
|
if errors.Is(err, model.ErrNotAuthorized) {
|
||||||
return nil, newError(responses.ErrorAuthorizationFail)
|
return nil, newError(responses.ErrorAuthorizationFail)
|
||||||
}
|
}
|
||||||
@ -162,15 +152,15 @@ func (c *PlaylistsController) UpdatePlaylist(w http.ResponseWriter, r *http.Requ
|
|||||||
return newResponse(), nil
|
return newResponse(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PlaylistsController) buildPlaylistWithSongs(ctx context.Context, p *model.Playlist) *responses.PlaylistWithSongs {
|
func (api *Router) buildPlaylistWithSongs(ctx context.Context, p *model.Playlist) *responses.PlaylistWithSongs {
|
||||||
pls := &responses.PlaylistWithSongs{
|
pls := &responses.PlaylistWithSongs{
|
||||||
Playlist: *c.buildPlaylist(*p),
|
Playlist: *api.buildPlaylist(*p),
|
||||||
}
|
}
|
||||||
pls.Entry = childrenFromMediaFiles(ctx, p.MediaFiles())
|
pls.Entry = childrenFromMediaFiles(ctx, p.MediaFiles())
|
||||||
return pls
|
return pls
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PlaylistsController) buildPlaylist(p model.Playlist) *responses.Playlist {
|
func (api *Router) buildPlaylist(p model.Playlist) *responses.Playlist {
|
||||||
pls := &responses.Playlist{}
|
pls := &responses.Playlist{}
|
||||||
pls.Id = p.ID
|
pls.Id = p.ID
|
||||||
pls.Name = p.Name
|
pls.Name = p.Name
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
//go:build linux || darwin
|
//go:build unix
|
||||||
|
|
||||||
// TODO Fix snapshot tests in Windows
|
// TODO Fix snapshot tests in Windows
|
||||||
// Response Snapshot tests. Only run in Linux and macOS, as they fail in Windows
|
// Response Snapshot tests. Only run in Linux and macOS, as they fail in Windows
|
||||||
|
@ -16,10 +16,6 @@ import (
|
|||||||
"github.com/navidrome/navidrome/utils"
|
"github.com/navidrome/navidrome/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SearchingController struct {
|
|
||||||
ds model.DataStore
|
|
||||||
}
|
|
||||||
|
|
||||||
type searchParams struct {
|
type searchParams struct {
|
||||||
query string
|
query string
|
||||||
artistCount int
|
artistCount int
|
||||||
@ -30,11 +26,7 @@ type searchParams struct {
|
|||||||
songOffset int
|
songOffset int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSearchingController(ds model.DataStore) *SearchingController {
|
func (api *Router) getParams(r *http.Request) (*searchParams, error) {
|
||||||
return &SearchingController{ds: ds}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *SearchingController) getParams(r *http.Request) (*searchParams, error) {
|
|
||||||
var err error
|
var err error
|
||||||
sp := &searchParams{}
|
sp := &searchParams{}
|
||||||
sp.query, err = requiredParamString(r, "query")
|
sp.query, err = requiredParamString(r, "query")
|
||||||
@ -78,15 +70,19 @@ func doSearch[T any](ctx context.Context, wg *sync.WaitGroup, s searchFunc[T], q
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *SearchingController) searchAll(r *http.Request, sp *searchParams) (mediaFiles model.MediaFiles, albums model.Albums, artists model.Artists) {
|
func (api *Router) searchAll(r *http.Request, sp *searchParams) (mediaFiles model.MediaFiles, albums model.Albums, artists model.Artists) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
q := sanitize.Accents(strings.ToLower(strings.TrimSuffix(sp.query, "*")))
|
q := sanitize.Accents(strings.ToLower(strings.TrimSuffix(sp.query, "*")))
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
wg := &sync.WaitGroup{}
|
wg := &sync.WaitGroup{}
|
||||||
wg.Add(3)
|
wg.Add(3)
|
||||||
go func() { mediaFiles = doSearch(ctx, wg, c.ds.MediaFile(ctx).Search, q, sp.songOffset, sp.songCount) }()
|
go func() {
|
||||||
go func() { albums = doSearch(ctx, wg, c.ds.Album(ctx).Search, q, sp.albumOffset, sp.albumCount) }()
|
mediaFiles = doSearch(ctx, wg, api.ds.MediaFile(ctx).Search, q, sp.songOffset, sp.songCount)
|
||||||
go func() { artists = doSearch(ctx, wg, c.ds.Artist(ctx).Search, q, sp.artistOffset, sp.artistCount) }()
|
}()
|
||||||
|
go func() { albums = doSearch(ctx, wg, api.ds.Album(ctx).Search, q, sp.albumOffset, sp.albumCount) }()
|
||||||
|
go func() {
|
||||||
|
artists = doSearch(ctx, wg, api.ds.Artist(ctx).Search, q, sp.artistOffset, sp.artistCount)
|
||||||
|
}()
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
if ctx.Err() == nil {
|
if ctx.Err() == nil {
|
||||||
@ -98,12 +94,12 @@ func (c *SearchingController) searchAll(r *http.Request, sp *searchParams) (medi
|
|||||||
return mediaFiles, albums, artists
|
return mediaFiles, albums, artists
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *SearchingController) Search2(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) Search2(r *http.Request) (*responses.Subsonic, error) {
|
||||||
sp, err := c.getParams(r)
|
sp, err := api.getParams(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
mfs, als, as := c.searchAll(r, sp)
|
mfs, als, as := api.searchAll(r, sp)
|
||||||
|
|
||||||
response := newResponse()
|
response := newResponse()
|
||||||
searchResult2 := &responses.SearchResult2{}
|
searchResult2 := &responses.SearchResult2{}
|
||||||
@ -126,13 +122,13 @@ func (c *SearchingController) Search2(w http.ResponseWriter, r *http.Request) (*
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *SearchingController) Search3(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) Search3(r *http.Request) (*responses.Subsonic, error) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
sp, err := c.getParams(r)
|
sp, err := api.getParams(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
mfs, als, as := c.searchAll(r, sp)
|
mfs, als, as := api.searchAll(r, sp)
|
||||||
|
|
||||||
response := newResponse()
|
response := newResponse()
|
||||||
searchResult3 := &responses.SearchResult3{}
|
searchResult3 := &responses.SearchResult3{}
|
||||||
|
@ -16,17 +16,7 @@ import (
|
|||||||
"github.com/navidrome/navidrome/utils"
|
"github.com/navidrome/navidrome/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type StreamController struct {
|
func (api *Router) Stream(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
||||||
streamer core.MediaStreamer
|
|
||||||
archiver core.Archiver
|
|
||||||
ds model.DataStore
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewStreamController(streamer core.MediaStreamer, archiver core.Archiver, ds model.DataStore) *StreamController {
|
|
||||||
return &StreamController{streamer: streamer, archiver: archiver, ds: ds}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *StreamController) Stream(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
id, err := requiredParamString(r, "id")
|
id, err := requiredParamString(r, "id")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -36,7 +26,7 @@ func (c *StreamController) Stream(w http.ResponseWriter, r *http.Request) (*resp
|
|||||||
format := utils.ParamString(r, "format")
|
format := utils.ParamString(r, "format")
|
||||||
estimateContentLength := utils.ParamBool(r, "estimateContentLength", false)
|
estimateContentLength := utils.ParamBool(r, "estimateContentLength", false)
|
||||||
|
|
||||||
stream, err := c.streamer.NewStream(ctx, id, format, maxBitRate)
|
stream, err := api.streamer.NewStream(ctx, id, format, maxBitRate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -82,7 +72,7 @@ func (c *StreamController) Stream(w http.ResponseWriter, r *http.Request) (*resp
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *StreamController) Download(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) Download(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
username, _ := request.UsernameFrom(ctx)
|
username, _ := request.UsernameFrom(ctx)
|
||||||
id, err := requiredParamString(r, "id")
|
id, err := requiredParamString(r, "id")
|
||||||
@ -95,7 +85,7 @@ func (c *StreamController) Download(w http.ResponseWriter, r *http.Request) (*re
|
|||||||
return nil, newError(responses.ErrorAuthorizationFail, "downloads are disabled")
|
return nil, newError(responses.ErrorAuthorizationFail, "downloads are disabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
entity, err := core.GetEntityByID(ctx, c.ds, id)
|
entity, err := core.GetEntityByID(ctx, api.ds, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -109,7 +99,7 @@ func (c *StreamController) Download(w http.ResponseWriter, r *http.Request) (*re
|
|||||||
|
|
||||||
switch v := entity.(type) {
|
switch v := entity.(type) {
|
||||||
case *model.MediaFile:
|
case *model.MediaFile:
|
||||||
stream, err := c.streamer.NewStream(ctx, id, "raw", 0)
|
stream, err := api.streamer.NewStream(ctx, id, "raw", 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -120,13 +110,13 @@ func (c *StreamController) Download(w http.ResponseWriter, r *http.Request) (*re
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
case *model.Album:
|
case *model.Album:
|
||||||
setHeaders(v.Name)
|
setHeaders(v.Name)
|
||||||
err = c.archiver.ZipAlbum(ctx, id, w)
|
err = api.archiver.ZipAlbum(ctx, id, w)
|
||||||
case *model.Artist:
|
case *model.Artist:
|
||||||
setHeaders(v.Name)
|
setHeaders(v.Name)
|
||||||
err = c.archiver.ZipArtist(ctx, id, w)
|
err = api.archiver.ZipArtist(ctx, id, w)
|
||||||
case *model.Playlist:
|
case *model.Playlist:
|
||||||
setHeaders(v.Name)
|
setHeaders(v.Name)
|
||||||
err = c.archiver.ZipPlaylist(ctx, id, w)
|
err = api.archiver.ZipPlaylist(ctx, id, w)
|
||||||
default:
|
default:
|
||||||
err = model.ErrNotFound
|
err = model.ErrNotFound
|
||||||
}
|
}
|
||||||
|
@ -12,11 +12,11 @@ func NewSystemController() *SystemController {
|
|||||||
return &SystemController{}
|
return &SystemController{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *SystemController) Ping(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) Ping(_ *http.Request) (*responses.Subsonic, error) {
|
||||||
return newResponse(), nil
|
return newResponse(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *SystemController) GetLicense(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) GetLicense(_ *http.Request) (*responses.Subsonic, error) {
|
||||||
response := newResponse()
|
response := newResponse()
|
||||||
response.License = &responses.License{Valid: true}
|
response.License = &responses.License{Valid: true}
|
||||||
return response, nil
|
return response, nil
|
||||||
|
@ -7,14 +7,8 @@ import (
|
|||||||
"github.com/navidrome/navidrome/server/subsonic/responses"
|
"github.com/navidrome/navidrome/server/subsonic/responses"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UsersController struct{}
|
|
||||||
|
|
||||||
func NewUsersController() *UsersController {
|
|
||||||
return &UsersController{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO This is a placeholder. The real one has to read this info from a config file or the database
|
// TODO This is a placeholder. The real one has to read this info from a config file or the database
|
||||||
func (c *UsersController) GetUser(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) GetUser(r *http.Request) (*responses.Subsonic, error) {
|
||||||
loggedUser, ok := request.UserFrom(r.Context())
|
loggedUser, ok := request.UserFrom(r.Context())
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, newError(responses.ErrorGeneric, "Internal error")
|
return nil, newError(responses.ErrorGeneric, "Internal error")
|
||||||
@ -30,7 +24,7 @@ func (c *UsersController) GetUser(w http.ResponseWriter, r *http.Request) (*resp
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *UsersController) GetUsers(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
|
func (api *Router) GetUsers(r *http.Request) (*responses.Subsonic, error) {
|
||||||
loggedUser, ok := request.UserFrom(r.Context())
|
loggedUser, ok := request.UserFrom(r.Context())
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, newError(responses.ErrorGeneric, "Internal error")
|
return nil, newError(responses.ErrorGeneric, "Internal error")
|
||||||
|
@ -1,112 +0,0 @@
|
|||||||
// Code generated by Wire. DO NOT EDIT.
|
|
||||||
|
|
||||||
//go:generate go run github.com/google/wire/cmd/wire
|
|
||||||
//go:build !wireinject
|
|
||||||
// +build !wireinject
|
|
||||||
|
|
||||||
package subsonic
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/google/wire"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Injectors from wire_injectors.go:
|
|
||||||
|
|
||||||
func initSystemController(router *Router) *SystemController {
|
|
||||||
systemController := NewSystemController()
|
|
||||||
return systemController
|
|
||||||
}
|
|
||||||
|
|
||||||
func initBrowsingController(router *Router) *BrowsingController {
|
|
||||||
dataStore := router.DataStore
|
|
||||||
externalMetadata := router.ExternalMetadata
|
|
||||||
browsingController := NewBrowsingController(dataStore, externalMetadata)
|
|
||||||
return browsingController
|
|
||||||
}
|
|
||||||
|
|
||||||
func initAlbumListController(router *Router) *AlbumListController {
|
|
||||||
dataStore := router.DataStore
|
|
||||||
playTracker := router.Scrobbler
|
|
||||||
albumListController := NewAlbumListController(dataStore, playTracker)
|
|
||||||
return albumListController
|
|
||||||
}
|
|
||||||
|
|
||||||
func initMediaAnnotationController(router *Router) *MediaAnnotationController {
|
|
||||||
dataStore := router.DataStore
|
|
||||||
playTracker := router.Scrobbler
|
|
||||||
broker := router.Broker
|
|
||||||
mediaAnnotationController := NewMediaAnnotationController(dataStore, playTracker, broker)
|
|
||||||
return mediaAnnotationController
|
|
||||||
}
|
|
||||||
|
|
||||||
func initPlaylistsController(router *Router) *PlaylistsController {
|
|
||||||
dataStore := router.DataStore
|
|
||||||
playlists := router.Playlists
|
|
||||||
playlistsController := NewPlaylistsController(dataStore, playlists)
|
|
||||||
return playlistsController
|
|
||||||
}
|
|
||||||
|
|
||||||
func initSearchingController(router *Router) *SearchingController {
|
|
||||||
dataStore := router.DataStore
|
|
||||||
searchingController := NewSearchingController(dataStore)
|
|
||||||
return searchingController
|
|
||||||
}
|
|
||||||
|
|
||||||
func initUsersController(router *Router) *UsersController {
|
|
||||||
usersController := NewUsersController()
|
|
||||||
return usersController
|
|
||||||
}
|
|
||||||
|
|
||||||
func initMediaRetrievalController(router *Router) *MediaRetrievalController {
|
|
||||||
artwork := router.Artwork
|
|
||||||
dataStore := router.DataStore
|
|
||||||
mediaRetrievalController := NewMediaRetrievalController(artwork, dataStore)
|
|
||||||
return mediaRetrievalController
|
|
||||||
}
|
|
||||||
|
|
||||||
func initStreamController(router *Router) *StreamController {
|
|
||||||
mediaStreamer := router.Streamer
|
|
||||||
archiver := router.Archiver
|
|
||||||
dataStore := router.DataStore
|
|
||||||
streamController := NewStreamController(mediaStreamer, archiver, dataStore)
|
|
||||||
return streamController
|
|
||||||
}
|
|
||||||
|
|
||||||
func initBookmarksController(router *Router) *BookmarksController {
|
|
||||||
dataStore := router.DataStore
|
|
||||||
bookmarksController := NewBookmarksController(dataStore)
|
|
||||||
return bookmarksController
|
|
||||||
}
|
|
||||||
|
|
||||||
func initLibraryScanningController(router *Router) *LibraryScanningController {
|
|
||||||
scanner := router.Scanner
|
|
||||||
libraryScanningController := NewLibraryScanningController(scanner)
|
|
||||||
return libraryScanningController
|
|
||||||
}
|
|
||||||
|
|
||||||
// wire_injectors.go:
|
|
||||||
|
|
||||||
var allProviders = wire.NewSet(
|
|
||||||
NewSystemController,
|
|
||||||
NewBrowsingController,
|
|
||||||
NewAlbumListController,
|
|
||||||
NewMediaAnnotationController,
|
|
||||||
NewPlaylistsController,
|
|
||||||
NewSearchingController,
|
|
||||||
NewUsersController,
|
|
||||||
NewMediaRetrievalController,
|
|
||||||
NewStreamController,
|
|
||||||
NewBookmarksController,
|
|
||||||
NewLibraryScanningController, wire.FieldsOf(
|
|
||||||
new(*Router),
|
|
||||||
"DataStore",
|
|
||||||
"Artwork",
|
|
||||||
"Streamer",
|
|
||||||
"Archiver",
|
|
||||||
"ExternalMetadata",
|
|
||||||
"Scanner",
|
|
||||||
"Broker",
|
|
||||||
"Scrobbler",
|
|
||||||
"Playlists",
|
|
||||||
),
|
|
||||||
)
|
|
@ -1,77 +0,0 @@
|
|||||||
//go:build wireinject
|
|
||||||
|
|
||||||
package subsonic
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/google/wire"
|
|
||||||
)
|
|
||||||
|
|
||||||
var allProviders = wire.NewSet(
|
|
||||||
NewSystemController,
|
|
||||||
NewBrowsingController,
|
|
||||||
NewAlbumListController,
|
|
||||||
NewMediaAnnotationController,
|
|
||||||
NewPlaylistsController,
|
|
||||||
NewSearchingController,
|
|
||||||
NewUsersController,
|
|
||||||
NewMediaRetrievalController,
|
|
||||||
NewStreamController,
|
|
||||||
NewBookmarksController,
|
|
||||||
NewLibraryScanningController,
|
|
||||||
wire.FieldsOf(
|
|
||||||
new(*Router),
|
|
||||||
"DataStore",
|
|
||||||
"Artwork",
|
|
||||||
"Streamer",
|
|
||||||
"Archiver",
|
|
||||||
"ExternalMetadata",
|
|
||||||
"Scanner",
|
|
||||||
"Broker",
|
|
||||||
"Scrobbler",
|
|
||||||
"Playlists",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
func initSystemController(router *Router) *SystemController {
|
|
||||||
panic(wire.Build(allProviders))
|
|
||||||
}
|
|
||||||
|
|
||||||
func initBrowsingController(router *Router) *BrowsingController {
|
|
||||||
panic(wire.Build(allProviders))
|
|
||||||
}
|
|
||||||
|
|
||||||
func initAlbumListController(router *Router) *AlbumListController {
|
|
||||||
panic(wire.Build(allProviders))
|
|
||||||
}
|
|
||||||
|
|
||||||
func initMediaAnnotationController(router *Router) *MediaAnnotationController {
|
|
||||||
panic(wire.Build(allProviders))
|
|
||||||
}
|
|
||||||
|
|
||||||
func initPlaylistsController(router *Router) *PlaylistsController {
|
|
||||||
panic(wire.Build(allProviders))
|
|
||||||
}
|
|
||||||
|
|
||||||
func initSearchingController(router *Router) *SearchingController {
|
|
||||||
panic(wire.Build(allProviders))
|
|
||||||
}
|
|
||||||
|
|
||||||
func initUsersController(router *Router) *UsersController {
|
|
||||||
panic(wire.Build(allProviders))
|
|
||||||
}
|
|
||||||
|
|
||||||
func initMediaRetrievalController(router *Router) *MediaRetrievalController {
|
|
||||||
panic(wire.Build(allProviders))
|
|
||||||
}
|
|
||||||
|
|
||||||
func initStreamController(router *Router) *StreamController {
|
|
||||||
panic(wire.Build(allProviders))
|
|
||||||
}
|
|
||||||
|
|
||||||
func initBookmarksController(router *Router) *BookmarksController {
|
|
||||||
panic(wire.Build(allProviders))
|
|
||||||
}
|
|
||||||
|
|
||||||
func initLibraryScanningController(router *Router) *LibraryScanningController {
|
|
||||||
panic(wire.Build(allProviders))
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user