Simplify Subsonic API handler implementation

This commit is contained in:
Deluan 2022-11-21 12:57:56 -05:00
parent cd41d9a419
commit 19af11efbe
18 changed files with 280 additions and 564 deletions

View File

@ -6,7 +6,6 @@ import (
"strconv"
"time"
"github.com/navidrome/navidrome/core/scrobbler"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/server/subsonic/filter"
@ -14,20 +13,7 @@ import (
"github.com/navidrome/navidrome/utils"
)
type AlbumListController struct {
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) {
func (api *Router) getAlbumList(r *http.Request) (model.Albums, int64, error) {
typ, err := requiredParamString(r, "type")
if err != nil {
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.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 {
log.Error(r, "Error retrieving albums", "error", err)
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 {
log.Error(r, "Error counting albums", "error", err)
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
}
func (c *AlbumListController) GetAlbumList(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
albums, count, err := c.getAlbumList(r)
func (api *Router) GetAlbumList(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
albums, count, err := api.getAlbumList(r)
if err != nil {
return nil, err
}
@ -103,8 +89,8 @@ func (c *AlbumListController) GetAlbumList(w http.ResponseWriter, r *http.Reques
return response, nil
}
func (c *AlbumListController) GetAlbumList2(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
albums, pageCount, err := c.getAlbumList(r)
func (api *Router) GetAlbumList2(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
albums, pageCount, err := api.getAlbumList(r)
if err != nil {
return nil, err
}
@ -116,20 +102,20 @@ func (c *AlbumListController) GetAlbumList2(w http.ResponseWriter, r *http.Reque
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()
options := filter.Starred()
artists, err := c.ds.Artist(ctx).GetAll(options)
artists, err := api.ds.Artist(ctx).GetAll(options)
if err != nil {
log.Error(r, "Error retrieving starred artists", "error", err)
return nil, err
}
albums, err := c.ds.Album(ctx).GetAllWithoutGenres(options)
albums, err := api.ds.Album(ctx).GetAllWithoutGenres(options)
if err != nil {
log.Error(r, "Error retrieving starred albums", "error", err)
return nil, err
}
mediaFiles, err := c.ds.MediaFile(ctx).GetAll(options)
mediaFiles, err := api.ds.MediaFile(ctx).GetAll(options)
if err != nil {
log.Error(r, "Error retrieving starred mediaFiles", "error", err)
return nil, err
@ -143,8 +129,8 @@ func (c *AlbumListController) GetStarred(w http.ResponseWriter, r *http.Request)
return response, nil
}
func (c *AlbumListController) GetStarred2(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
resp, err := c.GetStarred(w, r)
func (api *Router) GetStarred2(r *http.Request) (*responses.Subsonic, error) {
resp, err := api.GetStarred(r)
if err != nil {
return nil, err
}
@ -154,9 +140,9 @@ func (c *AlbumListController) GetStarred2(w http.ResponseWriter, r *http.Request
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()
npInfo, err := c.scrobbler.GetNowPlaying(ctx)
npInfo, err := api.scrobbler.GetNowPlaying(ctx)
if err != nil {
log.Error(r, "Error retrieving now playing list", "error", err)
return nil, err
@ -166,7 +152,7 @@ func (c *AlbumListController) GetNowPlaying(w http.ResponseWriter, r *http.Reque
response.NowPlaying = &responses.NowPlaying{}
response.NowPlaying.Entry = make([]responses.NowPlayingEntry, len(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 {
return nil, err
}
@ -180,13 +166,13 @@ func (c *AlbumListController) GetNowPlaying(w http.ResponseWriter, r *http.Reque
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)
genre := utils.ParamString(r, "genre")
fromYear := utils.ParamInt(r, "fromYear", 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 {
log.Error(r, "Error retrieving random songs", "error", err)
return nil, err
@ -198,12 +184,12 @@ func (c *AlbumListController) GetRandomSongs(w http.ResponseWriter, r *http.Requ
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)
offset := utils.MinInt(utils.ParamInt(r, "offset", 0), 500)
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 {
log.Error(r, "Error retrieving random songs", "error", err)
return nil, err
@ -215,8 +201,8 @@ func (c *AlbumListController) GetSongsByGenre(w http.ResponseWriter, r *http.Req
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.Max = size
return c.ds.MediaFile(ctx).GetAll(opts)
return api.ds.MediaFile(ctx).GetAll(opts)
}

View File

@ -14,8 +14,8 @@ import (
. "github.com/onsi/gomega"
)
var _ = Describe("AlbumListController", func() {
var controller *AlbumListController
var _ = Describe("Album Lists", func() {
var router *Router
var ds model.DataStore
var mockRepo *tests.MockAlbumRepo
var w *httptest.ResponseRecorder
@ -24,7 +24,7 @@ var _ = Describe("AlbumListController", func() {
BeforeEach(func() {
ds = &tests.MockDataStore{}
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()
})
@ -34,7 +34,7 @@ var _ = Describe("AlbumListController", func() {
mockRepo.SetData(model.Albums{
{ID: "1"}, {ID: "2"},
})
resp, err := controller.GetAlbumList(w, r)
resp, err := router.GetAlbumList(w, r)
Expect(err).To(BeNil())
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() {
r := newGetRequest()
_, err := controller.GetAlbumList(w, r)
_, err := router.GetAlbumList(w, r)
var subErr subError
isSubError := errors.As(err, &subErr)
@ -59,7 +59,7 @@ var _ = Describe("AlbumListController", func() {
mockRepo.SetError(true)
r := newGetRequest("type=newest")
_, err := controller.GetAlbumList(w, r)
_, err := router.GetAlbumList(w, r)
Expect(err).ToNot(BeNil())
var subErr subError
@ -74,7 +74,7 @@ var _ = Describe("AlbumListController", func() {
mockRepo.SetData(model.Albums{
{ID: "1"}, {ID: "2"},
})
resp, err := controller.GetAlbumList2(w, r)
resp, err := router.GetAlbumList2(w, r)
Expect(err).To(BeNil())
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() {
r := newGetRequest()
_, err := controller.GetAlbumList2(w, r)
_, err := router.GetAlbumList2(w, r)
var subErr subError
errors.As(err, &subErr)
@ -99,7 +99,7 @@ var _ = Describe("AlbumListController", func() {
mockRepo.SetError(true)
r := newGetRequest("type=newest")
_, err := controller.GetAlbumList2(w, r)
_, err := router.GetAlbumList2(w, r)
var subErr subError
errors.As(err, &subErr)

View File

@ -23,36 +23,37 @@ import (
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 {
http.Handler
DataStore model.DataStore
Artwork core.Artwork
Streamer core.MediaStreamer
Archiver core.Archiver
Players core.Players
ExternalMetadata core.ExternalMetadata
Playlists core.Playlists
Scanner scanner.Scanner
Broker events.Broker
Scrobbler scrobbler.PlayTracker
ds model.DataStore
artwork core.Artwork
streamer core.MediaStreamer
archiver core.Archiver
players core.Players
externalMetadata core.ExternalMetadata
playlists core.Playlists
scanner scanner.Scanner
broker events.Broker
scrobbler scrobbler.PlayTracker
}
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,
playlists core.Playlists, scrobbler scrobbler.PlayTracker) *Router {
r := &Router{
DataStore: ds,
Artwork: artwork,
Streamer: streamer,
Archiver: archiver,
Players: players,
ExternalMetadata: externalMetadata,
Playlists: playlists,
Scanner: scanner,
Broker: broker,
Scrobbler: scrobbler,
ds: ds,
artwork: artwork,
streamer: streamer,
archiver: archiver,
players: players,
externalMetadata: externalMetadata,
playlists: playlists,
scanner: scanner,
broker: broker,
scrobbler: scrobbler,
}
r.Handler = r.routes()
return r
@ -63,100 +64,89 @@ func (api *Router) routes() http.Handler {
r.Use(postFormToQueryParams)
r.Use(checkRequiredParameters)
r.Use(authenticate(api.DataStore))
r.Use(authenticate(api.ds))
// TODO Validate version
// Subsonic endpoints, grouped by controller
r.Group(func(r chi.Router) {
c := initSystemController(api)
withPlayer := r.With(getPlayer(api.Players))
h(withPlayer, "ping", c.Ping)
h(withPlayer, "getLicense", c.GetLicense)
r.Use(getPlayer(api.players))
h(r, "ping", api.Ping)
h(r, "getLicense", api.GetLicense)
})
r.Group(func(r chi.Router) {
c := initBrowsingController(api)
withPlayer := r.With(getPlayer(api.Players))
h(withPlayer, "getMusicFolders", c.GetMusicFolders)
h(withPlayer, "getIndexes", c.GetIndexes)
h(withPlayer, "getArtists", c.GetArtists)
h(withPlayer, "getGenres", c.GetGenres)
h(withPlayer, "getMusicDirectory", c.GetMusicDirectory)
h(withPlayer, "getArtist", c.GetArtist)
h(withPlayer, "getAlbum", c.GetAlbum)
h(withPlayer, "getSong", c.GetSong)
h(withPlayer, "getArtistInfo", c.GetArtistInfo)
h(withPlayer, "getArtistInfo2", c.GetArtistInfo2)
h(withPlayer, "getTopSongs", c.GetTopSongs)
h(withPlayer, "getSimilarSongs", c.GetSimilarSongs)
h(withPlayer, "getSimilarSongs2", c.GetSimilarSongs2)
r.Use(getPlayer(api.players))
h(r, "getMusicFolders", api.GetMusicFolders)
h(r, "getIndexes", api.GetIndexes)
h(r, "getArtists", api.GetArtists)
h(r, "getGenres", api.GetGenres)
h(r, "getMusicDirectory", api.GetMusicDirectory)
h(r, "getArtist", api.GetArtist)
h(r, "getAlbum", api.GetAlbum)
h(r, "getSong", api.GetSong)
h(r, "getArtistInfo", api.GetArtistInfo)
h(r, "getArtistInfo2", api.GetArtistInfo2)
h(r, "getTopSongs", api.GetTopSongs)
h(r, "getSimilarSongs", api.GetSimilarSongs)
h(r, "getSimilarSongs2", api.GetSimilarSongs2)
})
r.Group(func(r chi.Router) {
c := initAlbumListController(api)
withPlayer := r.With(getPlayer(api.Players))
h(withPlayer, "getAlbumList", c.GetAlbumList)
h(withPlayer, "getAlbumList2", c.GetAlbumList2)
h(withPlayer, "getStarred", c.GetStarred)
h(withPlayer, "getStarred2", c.GetStarred2)
h(withPlayer, "getNowPlaying", c.GetNowPlaying)
h(withPlayer, "getRandomSongs", c.GetRandomSongs)
h(withPlayer, "getSongsByGenre", c.GetSongsByGenre)
r.Use(getPlayer(api.players))
hr(r, "getAlbumList", api.GetAlbumList)
hr(r, "getAlbumList2", api.GetAlbumList2)
h(r, "getStarred", api.GetStarred)
h(r, "getStarred2", api.GetStarred2)
h(r, "getNowPlaying", api.GetNowPlaying)
h(r, "getRandomSongs", api.GetRandomSongs)
h(r, "getSongsByGenre", api.GetSongsByGenre)
})
r.Group(func(r chi.Router) {
c := initMediaAnnotationController(api)
withPlayer := r.With(getPlayer(api.Players))
h(withPlayer, "setRating", c.SetRating)
h(withPlayer, "star", c.Star)
h(withPlayer, "unstar", c.Unstar)
h(withPlayer, "scrobble", c.Scrobble)
r.Use(getPlayer(api.players))
h(r, "setRating", api.SetRating)
h(r, "star", api.Star)
h(r, "unstar", api.Unstar)
h(r, "scrobble", api.Scrobble)
})
r.Group(func(r chi.Router) {
c := initPlaylistsController(api)
withPlayer := r.With(getPlayer(api.Players))
h(withPlayer, "getPlaylists", c.GetPlaylists)
h(withPlayer, "getPlaylist", c.GetPlaylist)
h(withPlayer, "createPlaylist", c.CreatePlaylist)
h(withPlayer, "deletePlaylist", c.DeletePlaylist)
h(withPlayer, "updatePlaylist", c.UpdatePlaylist)
r.Use(getPlayer(api.players))
h(r, "getPlaylists", api.GetPlaylists)
h(r, "getPlaylist", api.GetPlaylist)
h(r, "createPlaylist", api.CreatePlaylist)
h(r, "deletePlaylist", api.DeletePlaylist)
h(r, "updatePlaylist", api.UpdatePlaylist)
})
r.Group(func(r chi.Router) {
c := initBookmarksController(api)
withPlayer := r.With(getPlayer(api.Players))
h(withPlayer, "getBookmarks", c.GetBookmarks)
h(withPlayer, "createBookmark", c.CreateBookmark)
h(withPlayer, "deleteBookmark", c.DeleteBookmark)
h(withPlayer, "getPlayQueue", c.GetPlayQueue)
h(withPlayer, "savePlayQueue", c.SavePlayQueue)
r.Use(getPlayer(api.players))
h(r, "getBookmarks", api.GetBookmarks)
h(r, "createBookmark", api.CreateBookmark)
h(r, "deleteBookmark", api.DeleteBookmark)
h(r, "getPlayQueue", api.GetPlayQueue)
h(r, "savePlayQueue", api.SavePlayQueue)
})
r.Group(func(r chi.Router) {
c := initSearchingController(api)
withPlayer := r.With(getPlayer(api.Players))
h(withPlayer, "search2", c.Search2)
h(withPlayer, "search3", c.Search3)
r.Use(getPlayer(api.players))
h(r, "search2", api.Search2)
h(r, "search3", api.Search3)
})
r.Group(func(r chi.Router) {
c := initUsersController(api)
h(r, "getUser", c.GetUser)
h(r, "getUsers", c.GetUsers)
h(r, "getUser", api.GetUser)
h(r, "getUsers", api.GetUsers)
})
r.Group(func(r chi.Router) {
c := initLibraryScanningController(api)
h(r, "getScanStatus", c.GetScanStatus)
h(r, "startScan", c.StartScan)
h(r, "getScanStatus", api.GetScanStatus)
h(r, "startScan", api.StartScan)
})
r.Group(func(r chi.Router) {
c := initMediaRetrievalController(api)
// configure request throttling
maxRequests := utils.MaxInt(2, runtime.NumCPU())
withThrottle := r.With(middleware.ThrottleBacklog(maxRequests, consts.RequestThrottleBacklogLimit, consts.RequestThrottleBacklogTimeout))
h(withThrottle, "getAvatar", c.GetAvatar)
h(withThrottle, "getCoverArt", c.GetCoverArt)
h(withThrottle, "getLyrics", c.GetLyrics)
r.Use(middleware.ThrottleBacklog(maxRequests, consts.RequestThrottleBacklogLimit, consts.RequestThrottleBacklogTimeout))
hr(r, "getAvatar", api.GetAvatar)
hr(r, "getCoverArt", api.GetCoverArt)
h(r, "getLyrics", api.GetLyrics)
})
r.Group(func(r chi.Router) {
c := initStreamController(api)
withPlayer := r.With(getPlayer(api.Players))
h(withPlayer, "stream", c.Stream)
h(withPlayer, "download", c.Download)
r.Use(getPlayer(api.players))
hr(r, "stream", api.Stream)
hr(r, "download", api.Download)
})
// Not Implemented (yet?)
@ -176,9 +166,9 @@ func (api *Router) routes() http.Handler {
return r
}
// 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) {
// Add the Subsonic handler that requires a http.ResponseWriter, with and without `.view` extension.
// Ex: if path = `stream` it will create the routes `/stream` and `/stream.view`
func hr(r chi.Router, path string, f handlerRaw) {
handle := func(w http.ResponseWriter, r *http.Request) {
res, err := f(w, r)
if err != nil {
@ -208,6 +198,14 @@ func h(r chi.Router, path string, f handler) {
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
func h501(r *chi.Mux, paths ...string) {
for _, path := range paths {

View File

@ -10,18 +10,10 @@ import (
"github.com/navidrome/navidrome/utils"
)
type BookmarksController struct {
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) {
func (api *Router) GetBookmarks(r *http.Request) (*responses.Subsonic, error) {
user, _ := request.UserFrom(r.Context())
repo := c.ds.MediaFile(r.Context())
repo := api.ds.MediaFile(r.Context())
bmks, err := repo.GetBookmarks()
if err != nil {
return nil, err
@ -43,7 +35,7 @@ func (c *BookmarksController) GetBookmarks(w http.ResponseWriter, r *http.Reques
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")
if err != nil {
return nil, err
@ -52,7 +44,7 @@ func (c *BookmarksController) CreateBookmark(w http.ResponseWriter, r *http.Requ
comment := utils.ParamString(r, "comment")
position := utils.ParamInt64(r, "position", 0)
repo := c.ds.MediaFile(r.Context())
repo := api.ds.MediaFile(r.Context())
err = repo.AddBookmark(id, comment, position)
if err != nil {
return nil, err
@ -60,13 +52,13 @@ func (c *BookmarksController) CreateBookmark(w http.ResponseWriter, r *http.Requ
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")
if err != nil {
return nil, err
}
repo := c.ds.MediaFile(r.Context())
repo := api.ds.MediaFile(r.Context())
err = repo.DeleteBookmark(id)
if err != nil {
return nil, err
@ -74,10 +66,10 @@ func (c *BookmarksController) DeleteBookmark(w http.ResponseWriter, r *http.Requ
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())
repo := c.ds.PlayQueue(r.Context())
repo := api.ds.PlayQueue(r.Context())
pq, err := repo.Retrieve(user.ID)
if err != nil {
return nil, err
@ -95,7 +87,7 @@ func (c *BookmarksController) GetPlayQueue(w http.ResponseWriter, r *http.Reques
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")
if err != nil {
return nil, err
@ -122,7 +114,7 @@ func (c *BookmarksController) SavePlayQueue(w http.ResponseWriter, r *http.Reque
UpdatedAt: time.Time{},
}
repo := c.ds.PlayQueue(r.Context())
repo := api.ds.PlayQueue(r.Context())
err = repo.Store(pq)
if err != nil {
return nil, err

View File

@ -16,17 +16,8 @@ import (
"github.com/navidrome/navidrome/utils"
)
type BrowsingController struct {
ds model.DataStore
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()
func (api *Router) GetMusicFolders(r *http.Request) (*responses.Subsonic, error) {
mediaFolderList, _ := api.ds.MediaFolder(r.Context()).GetAll()
folders := make([]responses.MusicFolder, len(mediaFolderList))
for i, f := range mediaFolderList {
folders[i].Id = f.ID
@ -37,14 +28,14 @@ func (c *BrowsingController) GetMusicFolders(w http.ResponseWriter, r *http.Requ
return response, nil
}
func (c *BrowsingController) getArtistIndex(ctx context.Context, mediaFolderId int, ifModifiedSince time.Time) (*responses.Indexes, error) {
folder, err := c.ds.MediaFolder(ctx).Get(int32(mediaFolderId))
func (api *Router) getArtistIndex(ctx context.Context, mediaFolderId int, ifModifiedSince time.Time) (*responses.Indexes, error) {
folder, err := api.ds.MediaFolder(ctx).Get(int32(mediaFolderId))
if err != nil {
log.Error(ctx, "Error retrieving MediaFolder", "id", mediaFolderId, 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 {
log.Error(ctx, "Error retrieving LastScan property", err)
return nil, err
@ -54,7 +45,7 @@ func (c *BrowsingController) getArtistIndex(ctx context.Context, mediaFolderId i
ms, _ := strconv.ParseInt(l, 10, 64)
lastModified := utils.ToTime(ms)
if lastModified.After(ifModifiedSince) {
indexes, err = c.ds.Artist(ctx).GetIndex()
indexes, err = api.ds.Artist(ctx).GetIndex()
if err != nil {
log.Error(ctx, "Error retrieving Indexes", err)
return nil, err
@ -74,11 +65,11 @@ func (c *BrowsingController) getArtistIndex(ctx context.Context, mediaFolderId i
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)
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 {
return nil, err
}
@ -88,9 +79,9 @@ func (c *BrowsingController) GetIndexes(w http.ResponseWriter, r *http.Request)
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)
res, err := c.getArtistIndex(r.Context(), musicFolderId, time.Time{})
res, err := api.getArtistIndex(r.Context(), musicFolderId, time.Time{})
if err != nil {
return nil, err
}
@ -100,11 +91,11 @@ func (c *BrowsingController) GetArtists(w http.ResponseWriter, r *http.Request)
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")
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) {
log.Error(r, "Requested ID not found ", "id", id)
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) {
case *model.Artist:
dir, err = c.buildArtistDirectory(ctx, v)
dir, err = api.buildArtistDirectory(ctx, v)
case *model.Album:
dir, err = c.buildAlbumDirectory(ctx, v)
dir, err = api.buildAlbumDirectory(ctx, v)
default:
log.Error(r, "Requested ID of invalid type", "id", id, "entity", v)
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
}
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")
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) {
log.Error(ctx, "Requested ArtistID not found ", "id", id)
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
}
albums, err := c.ds.Album(ctx).GetAllWithoutGenres(filter.AlbumsByArtistID(id))
albums, err := api.ds.Album(ctx).GetAllWithoutGenres(filter.AlbumsByArtistID(id))
if err != nil {
log.Error(ctx, "Error retrieving albums by artist", "id", id, "name", artist.Name, err)
return nil, err
}
response := newResponse()
response.ArtistWithAlbumsID3 = c.buildArtist(ctx, artist, albums)
response.ArtistWithAlbumsID3 = api.buildArtist(ctx, artist, albums)
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")
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) {
log.Error(ctx, "Requested AlbumID not found ", "id", id)
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
}
mfs, err := c.ds.MediaFile(ctx).GetAll(filter.SongsByAlbum(id))
mfs, err := api.ds.MediaFile(ctx).GetAll(filter.SongsByAlbum(id))
if err != nil {
log.Error(ctx, "Error retrieving tracks from album", "id", id, "name", album.Name, err)
return nil, err
}
response := newResponse()
response.AlbumWithSongsID3 = c.buildAlbum(ctx, album, mfs)
response.AlbumWithSongsID3 = api.buildAlbum(ctx, album, mfs)
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")
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) {
log.Error(r, "Requested MediaFileID not found ", "id", id)
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
}
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()
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 {
log.Error(r, err)
return nil, err
@ -224,7 +215,7 @@ func (c *BrowsingController) GetGenres(w http.ResponseWriter, r *http.Request) (
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()
id, err := requiredParamString(r, "id")
if err != nil {
@ -233,7 +224,7 @@ func (c *BrowsingController) GetArtistInfo(w http.ResponseWriter, r *http.Reques
count := utils.ParamInt(r, "count", 20)
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 {
return nil, err
}
@ -253,8 +244,8 @@ func (c *BrowsingController) GetArtistInfo(w http.ResponseWriter, r *http.Reques
return response, nil
}
func (c *BrowsingController) GetArtistInfo2(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
info, err := c.GetArtistInfo(w, r)
func (api *Router) GetArtistInfo2(r *http.Request) (*responses.Subsonic, error) {
info, err := api.GetArtistInfo(r)
if err != nil {
return nil, err
}
@ -275,7 +266,7 @@ func (c *BrowsingController) GetArtistInfo2(w http.ResponseWriter, r *http.Reque
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()
id, err := requiredParamString(r, "id")
if err != nil {
@ -283,7 +274,7 @@ func (c *BrowsingController) GetSimilarSongs(w http.ResponseWriter, r *http.Requ
}
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 {
return nil, err
}
@ -295,8 +286,8 @@ func (c *BrowsingController) GetSimilarSongs(w http.ResponseWriter, r *http.Requ
return response, nil
}
func (c *BrowsingController) GetSimilarSongs2(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
res, err := c.GetSimilarSongs(w, r)
func (api *Router) GetSimilarSongs2(r *http.Request) (*responses.Subsonic, error) {
res, err := api.GetSimilarSongs(r)
if err != nil {
return nil, err
}
@ -308,7 +299,7 @@ func (c *BrowsingController) GetSimilarSongs2(w http.ResponseWriter, r *http.Req
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()
artist, err := requiredParamString(r, "artist")
if err != nil {
@ -316,7 +307,7 @@ func (c *BrowsingController) GetTopSongs(w http.ResponseWriter, r *http.Request)
}
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 {
return nil, err
}
@ -328,7 +319,7 @@ func (c *BrowsingController) GetTopSongs(w http.ResponseWriter, r *http.Request)
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.Id = artist.ID
dir.Name = artist.Name
@ -342,7 +333,7 @@ func (c *BrowsingController) buildArtistDirectory(ctx context.Context, artist *m
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 {
return nil, err
}
@ -351,14 +342,14 @@ func (c *BrowsingController) buildArtistDirectory(ctx context.Context, artist *m
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.ArtistID3 = toArtistID3(ctx, *artist)
a.Album = childrenFromAlbums(ctx, albums)
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.Id = album.ID
dir.Name = album.Name
@ -374,7 +365,7 @@ func (c *BrowsingController) buildAlbumDirectory(ctx context.Context, album *mod
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 {
return nil, err
}
@ -383,7 +374,7 @@ func (c *BrowsingController) buildAlbumDirectory(ctx context.Context, album *mod
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.Id = album.ID
dir.Name = album.Name

View File

@ -7,24 +7,15 @@ import (
"github.com/navidrome/navidrome/conf"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/model/request"
"github.com/navidrome/navidrome/scanner"
"github.com/navidrome/navidrome/server/subsonic/responses"
"github.com/navidrome/navidrome/utils"
)
type LibraryScanningController struct {
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) {
func (api *Router) GetScanStatus(r *http.Request) (*responses.Subsonic, error) {
// TODO handle multiple mediafolders
ctx := r.Context()
mediaFolder := conf.Server.MusicFolder
status, err := c.scanner.Status(mediaFolder)
status, err := api.scanner.Status(mediaFolder)
if err != nil {
log.Error(ctx, "Error retrieving Scanner status", err)
return nil, newError(responses.ErrorGeneric, "Internal Error")
@ -39,7 +30,7 @@ func (c *LibraryScanningController) GetScanStatus(w http.ResponseWriter, r *http
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()
loggedUser, ok := request.UserFrom(ctx)
if !ok {
@ -55,7 +46,7 @@ func (c *LibraryScanningController) StartScan(w http.ResponseWriter, r *http.Req
go func() {
start := time.Now()
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 {
log.Error(ctx, "Error scanning", err)
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))
}()
return c.GetScanStatus(w, r)
return api.GetScanStatus(r)
}

View File

@ -17,17 +17,7 @@ import (
"github.com/navidrome/navidrome/utils"
)
type MediaAnnotationController struct {
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) {
func (api *Router) SetRating(r *http.Request) (*responses.Subsonic, error) {
id, err := requiredParamString(r, "id")
if err != nil {
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)
err = c.setRating(r.Context(), id, rating)
err = api.setRating(r.Context(), id, rating)
if errors.Is(err, model.ErrNotFound) {
log.Error(r, err)
@ -52,23 +42,23 @@ func (c *MediaAnnotationController) SetRating(w http.ResponseWriter, r *http.Req
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 resource string
entity, err := core.GetEntityByID(ctx, c.ds, id)
entity, err := core.GetEntityByID(ctx, api.ds, id)
if err != nil {
return err
}
switch entity.(type) {
case *model.Artist:
repo = c.ds.Artist(ctx)
repo = api.ds.Artist(ctx)
resource = "artist"
case *model.Album:
repo = c.ds.Album(ctx)
repo = api.ds.Album(ctx)
resource = "album"
default:
repo = c.ds.MediaFile(ctx)
repo = api.ds.MediaFile(ctx)
resource = "song"
}
err = repo.SetRating(rating, id)
@ -76,11 +66,11 @@ func (c *MediaAnnotationController) setRating(ctx context.Context, id string, ra
return err
}
event := &events.RefreshResource{}
c.broker.SendMessage(ctx, event.With(resource, id))
api.broker.SendMessage(ctx, event.With(resource, id))
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")
albumIds := utils.ParamStrings(r, "albumId")
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, artistIds...)
err := c.setStar(r.Context(), true, ids...)
err := api.setStar(r.Context(), true, ids...)
if err != nil {
return nil, err
}
@ -98,7 +88,7 @@ func (c *MediaAnnotationController) Star(w http.ResponseWriter, r *http.Request)
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")
albumIds := utils.ParamStrings(r, "albumId")
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, artistIds...)
err := c.setStar(r.Context(), false, ids...)
err := api.setStar(r.Context(), false, ids...)
if err != nil {
return nil, err
}
@ -116,7 +106,7 @@ func (c *MediaAnnotationController) Unstar(w http.ResponseWriter, r *http.Reques
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 {
return nil
}
@ -126,7 +116,7 @@ func (c *MediaAnnotationController) setStar(ctx context.Context, star bool, ids
return nil
}
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 {
exist, err := tx.Album(ctx).Exists(id)
if err != nil {
@ -158,7 +148,7 @@ func (c *MediaAnnotationController) setStar(ctx context.Context, star bool, ids
}
event = event.With("song", id)
}
c.broker.SendMessage(ctx, event)
api.broker.SendMessage(ctx, event)
return nil
})
@ -173,7 +163,7 @@ func (c *MediaAnnotationController) setStar(ctx context.Context, star bool, ids
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")
if err != nil {
return nil, err
@ -186,12 +176,12 @@ func (c *MediaAnnotationController) Scrobble(w http.ResponseWriter, r *http.Requ
ctx := r.Context()
if submission {
err := c.scrobblerSubmit(ctx, ids, times)
err := api.scrobblerSubmit(ctx, ids, times)
if err != nil {
log.Error(ctx, "Error registering scrobbles", "ids", ids, "times", times, err)
}
} else {
err := c.scrobblerNowPlaying(ctx, ids[0])
err := api.scrobblerNowPlaying(ctx, ids[0])
if err != nil {
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
}
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
log.Debug(ctx, "Scrobbling tracks", "ids", ids, "times", times)
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})
}
return c.playTracker.Submit(ctx, submissions)
return api.scrobbler.Submit(ctx, submissions)
}
func (c *MediaAnnotationController) scrobblerNowPlaying(ctx context.Context, trackId string) error {
mf, err := c.ds.MediaFile(ctx).Get(trackId)
func (api *Router) scrobblerNowPlaying(ctx context.Context, trackId string) error {
mf, err := api.ds.MediaFile(ctx).Get(trackId)
if err != nil {
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)
err = c.playTracker.NowPlaying(ctx, clientId, client, trackId)
err = api.scrobbler.NowPlaying(ctx, clientId, client, trackId)
return err
}

View File

@ -4,7 +4,6 @@ import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"time"
"github.com/navidrome/navidrome/model/request"
@ -19,8 +18,7 @@ import (
)
var _ = Describe("MediaAnnotationController", func() {
var controller *MediaAnnotationController
var w *httptest.ResponseRecorder
var router *Router
var ds model.DataStore
var playTracker *fakePlayTracker
var eventBroker *fakeEventBroker
@ -31,8 +29,7 @@ var _ = Describe("MediaAnnotationController", func() {
ds = &tests.MockDataStore{}
playTracker = &fakePlayTracker{}
eventBroker = &fakeEventBroker{}
controller = NewMediaAnnotationController(ds, playTracker, eventBroker)
w = httptest.NewRecorder()
router = New(ds, nil, nil, nil, nil, nil, nil, eventBroker, nil, playTracker)
})
Describe("Scrobble", func() {
@ -40,7 +37,7 @@ var _ = Describe("MediaAnnotationController", func() {
submissionTime := time.Now()
r := newGetRequest("id=12", "id=34")
_, err := controller.Scrobble(w, r)
_, err := router.Scrobble(r)
Expect(err).ToNot(HaveOccurred())
Expect(playTracker.Submissions).To(HaveLen(2))
@ -57,7 +54,7 @@ var _ = Describe("MediaAnnotationController", func() {
t2 := utils.ToMillis(time2)
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(playTracker.Submissions).To(HaveLen(2))
@ -70,7 +67,7 @@ var _ = Describe("MediaAnnotationController", func() {
It("checks if number of ids match number of times", func() {
r := newGetRequest("id=12", "id=34", "time=1111")
_, err := controller.Scrobble(w, r)
_, err := router.Scrobble(r)
Expect(err).To(HaveOccurred())
Expect(playTracker.Submissions).To(BeEmpty())
@ -86,14 +83,14 @@ var _ = Describe("MediaAnnotationController", func() {
})
It("does not scrobble", func() {
_, err := controller.Scrobble(w, req)
_, err := router.Scrobble(req)
Expect(err).ToNot(HaveOccurred())
Expect(playTracker.Submissions).To(BeEmpty())
})
It("registers a NowPlaying", func() {
_, err := controller.Scrobble(w, req)
_, err := router.Scrobble(req)
Expect(err).ToNot(HaveOccurred())
Expect(playTracker.Playing).To(HaveLen(1))
@ -109,7 +106,7 @@ type fakePlayTracker struct {
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 {
return f.Error
}
@ -120,11 +117,11 @@ func (f *fakePlayTracker) NowPlaying(ctx context.Context, playerId string, playe
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
}
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 {
return f.Error
}
@ -139,7 +136,7 @@ type fakeEventBroker struct {
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)
}

View File

@ -8,7 +8,6 @@ import (
"github.com/navidrome/navidrome/conf"
"github.com/navidrome/navidrome/consts"
"github.com/navidrome/navidrome/core"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/resources"
@ -18,37 +17,28 @@ import (
"github.com/navidrome/navidrome/utils/gravatar"
)
type MediaRetrievalController struct {
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) {
func (api *Router) GetAvatar(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
if !conf.Server.EnableGravatar {
return c.getPlaceHolderAvatar(w, r)
return api.getPlaceHolderAvatar(w, r)
}
username, err := requiredParamString(r, "username")
if err != nil {
return nil, err
}
ctx := r.Context()
u, err := c.ds.User(ctx).FindByUsername(username)
u, err := api.ds.User(ctx).FindByUsername(username)
if err != nil {
return nil, err
}
if u.Email == "" {
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)
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)
if err != nil {
log.Error(r, "Image not found", err)
@ -60,13 +50,13 @@ func (c *MediaRetrievalController) getPlaceHolderAvatar(w http.ResponseWriter, r
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")
size := utils.ParamInt(r, "size", 0)
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) {
log.Error(r, "Couldn't find coverArt", "id", id, err)
return nil, newError(responses.ErrorDataNotFound, "Artwork not found")
@ -92,13 +82,13 @@ func isSynced(rawLyrics string) bool {
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")
title := utils.ParamString(r, "title")
response := newResponse()
lyrics := responses.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 {
return nil, err

View File

@ -15,7 +15,7 @@ import (
)
var _ = Describe("MediaRetrievalController", func() {
var controller *MediaRetrievalController
var router *Router
var ds model.DataStore
mockRepo := &mockedMediaFile{}
var artwork *fakeArtwork
@ -26,7 +26,7 @@ var _ = Describe("MediaRetrievalController", func() {
MockedMediaFile: mockRepo,
}
artwork = &fakeArtwork{}
controller = NewMediaRetrievalController(artwork, ds)
router = New(ds, artwork, nil, nil, nil, nil, nil, nil, nil, nil)
w = httptest.NewRecorder()
})
@ -34,7 +34,7 @@ var _ = Describe("MediaRetrievalController", func() {
It("should return data for that id", func() {
artwork.data = "image data"
r := newGetRequest("id=34", "size=128")
_, err := controller.GetCoverArt(w, r)
_, err := router.GetCoverArt(w, r)
Expect(err).To(BeNil())
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() {
r := newGetRequest()
_, err := controller.GetCoverArt(w, r)
_, err := router.GetCoverArt(w, r)
Expect(err).To(BeNil())
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() {
artwork.err = model.ErrNotFound
r := newGetRequest("id=34", "size=128")
_, err := controller.GetCoverArt(w, r)
_, err := router.GetCoverArt(w, r)
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() {
artwork.err = errors.New("weird error")
r := newGetRequest("id=34", "size=128")
_, err := controller.GetCoverArt(w, r)
_, err := router.GetCoverArt(w, r)
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",
},
})
response, err := controller.GetLyrics(w, r)
response, err := router.GetLyrics(r)
if err != nil {
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() {
r := newGetRequest("artist=Dheeraj", "title=Rinkiya+Ke+Papa")
mockRepo.SetData(model.MediaFiles{})
response, err := controller.GetLyrics(w, r)
response, err := router.GetLyrics(r)
if err != nil {
log.Error("You're missing something.", err)
}
@ -110,7 +110,7 @@ type fakeArtwork struct {
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 {
return nil, c.err
}
@ -129,7 +129,7 @@ var _ = Describe("isSynced", func() {
})
It("returns true if lyrics contain timestamps", func() {
Expect(isSynced(`NF Real Music
[00:00] ksdjjs
[00:00] First line
[00:00.85] JUST LIKE YOU
[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))
@ -148,6 +148,6 @@ func (m *mockedMediaFile) SetData(mfs model.MediaFiles) {
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
}

View File

@ -6,49 +6,39 @@ import (
"fmt"
"net/http"
"github.com/navidrome/navidrome/core"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/server/subsonic/responses"
"github.com/navidrome/navidrome/utils"
)
type PlaylistsController struct {
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) {
func (api *Router) GetPlaylists(r *http.Request) (*responses.Subsonic, error) {
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 {
log.Error(r, err)
return nil, err
}
playlists := make([]responses.Playlist, len(allPls))
for i, p := range allPls {
playlists[i] = *c.buildPlaylist(p)
playlists[i] = *api.buildPlaylist(p)
}
response := newResponse()
response.Playlists = &responses.Playlists{Playlist: playlists}
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()
id, err := requiredParamString(r, "id")
if err != nil {
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) {
pls, err := c.ds.Playlist(ctx).GetWithTracks(id)
func (api *Router) getPlaylist(ctx context.Context, id string) (*responses.Subsonic, error) {
pls, err := api.ds.Playlist(ctx).GetWithTracks(id)
if errors.Is(err, model.ErrNotFound) {
log.Error(ctx, err.Error(), "id", id)
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.Playlist = c.buildPlaylistWithSongs(ctx, pls)
response.Playlist = api.buildPlaylistWithSongs(ctx, pls)
return response, nil
}
func (c *PlaylistsController) create(ctx context.Context, playlistId, name string, ids []string) (string, error) {
err := c.ds.WithTx(func(tx model.DataStore) error {
func (api *Router) create(ctx context.Context, playlistId, name string, ids []string) (string, error) {
err := api.ds.WithTx(func(tx model.DataStore) error {
owner := getUser(ctx)
var pls *model.Playlist
var err error
@ -91,7 +81,7 @@ func (c *PlaylistsController) create(ctx context.Context, playlistId, name strin
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()
songIds := utils.ParamStrings(r, "songId")
playlistId := utils.ParamString(r, "playlistId")
@ -99,20 +89,20 @@ func (c *PlaylistsController) CreatePlaylist(w http.ResponseWriter, r *http.Requ
if playlistId == "" && name == "" {
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 {
log.Error(r, 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")
if err != nil {
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) {
return nil, newError(responses.ErrorAuthorizationFail)
}
@ -123,7 +113,7 @@ func (c *PlaylistsController) DeletePlaylist(w http.ResponseWriter, r *http.Requ
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")
if err != nil {
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("-- 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) {
return nil, newError(responses.ErrorAuthorizationFail)
}
@ -162,15 +152,15 @@ func (c *PlaylistsController) UpdatePlaylist(w http.ResponseWriter, r *http.Requ
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{
Playlist: *c.buildPlaylist(*p),
Playlist: *api.buildPlaylist(*p),
}
pls.Entry = childrenFromMediaFiles(ctx, p.MediaFiles())
return pls
}
func (c *PlaylistsController) buildPlaylist(p model.Playlist) *responses.Playlist {
func (api *Router) buildPlaylist(p model.Playlist) *responses.Playlist {
pls := &responses.Playlist{}
pls.Id = p.ID
pls.Name = p.Name

View File

@ -1,4 +1,4 @@
//go:build linux || darwin
//go:build unix
// TODO Fix snapshot tests in Windows
// Response Snapshot tests. Only run in Linux and macOS, as they fail in Windows

View File

@ -16,10 +16,6 @@ import (
"github.com/navidrome/navidrome/utils"
)
type SearchingController struct {
ds model.DataStore
}
type searchParams struct {
query string
artistCount int
@ -30,11 +26,7 @@ type searchParams struct {
songOffset int
}
func NewSearchingController(ds model.DataStore) *SearchingController {
return &SearchingController{ds: ds}
}
func (c *SearchingController) getParams(r *http.Request) (*searchParams, error) {
func (api *Router) getParams(r *http.Request) (*searchParams, error) {
var err error
sp := &searchParams{}
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
}
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()
q := sanitize.Accents(strings.ToLower(strings.TrimSuffix(sp.query, "*")))
ctx := r.Context()
wg := &sync.WaitGroup{}
wg.Add(3)
go func() { mediaFiles = doSearch(ctx, wg, c.ds.MediaFile(ctx).Search, q, sp.songOffset, sp.songCount) }()
go func() { albums = doSearch(ctx, wg, c.ds.Album(ctx).Search, q, sp.albumOffset, sp.albumCount) }()
go func() { artists = doSearch(ctx, wg, c.ds.Artist(ctx).Search, q, sp.artistOffset, sp.artistCount) }()
go func() {
mediaFiles = doSearch(ctx, wg, api.ds.MediaFile(ctx).Search, q, sp.songOffset, sp.songCount)
}()
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()
if ctx.Err() == nil {
@ -98,12 +94,12 @@ func (c *SearchingController) searchAll(r *http.Request, sp *searchParams) (medi
return mediaFiles, albums, artists
}
func (c *SearchingController) Search2(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
sp, err := c.getParams(r)
func (api *Router) Search2(r *http.Request) (*responses.Subsonic, error) {
sp, err := api.getParams(r)
if err != nil {
return nil, err
}
mfs, als, as := c.searchAll(r, sp)
mfs, als, as := api.searchAll(r, sp)
response := newResponse()
searchResult2 := &responses.SearchResult2{}
@ -126,13 +122,13 @@ func (c *SearchingController) Search2(w http.ResponseWriter, r *http.Request) (*
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()
sp, err := c.getParams(r)
sp, err := api.getParams(r)
if err != nil {
return nil, err
}
mfs, als, as := c.searchAll(r, sp)
mfs, als, as := api.searchAll(r, sp)
response := newResponse()
searchResult3 := &responses.SearchResult3{}

View File

@ -16,17 +16,7 @@ import (
"github.com/navidrome/navidrome/utils"
)
type StreamController struct {
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) {
func (api *Router) Stream(w http.ResponseWriter, r *http.Request) (*responses.Subsonic, error) {
ctx := r.Context()
id, err := requiredParamString(r, "id")
if err != nil {
@ -36,7 +26,7 @@ func (c *StreamController) Stream(w http.ResponseWriter, r *http.Request) (*resp
format := utils.ParamString(r, "format")
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 {
return nil, err
}
@ -82,7 +72,7 @@ func (c *StreamController) Stream(w http.ResponseWriter, r *http.Request) (*resp
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()
username, _ := request.UsernameFrom(ctx)
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")
}
entity, err := core.GetEntityByID(ctx, c.ds, id)
entity, err := core.GetEntityByID(ctx, api.ds, id)
if err != nil {
return nil, err
}
@ -109,7 +99,7 @@ func (c *StreamController) Download(w http.ResponseWriter, r *http.Request) (*re
switch v := entity.(type) {
case *model.MediaFile:
stream, err := c.streamer.NewStream(ctx, id, "raw", 0)
stream, err := api.streamer.NewStream(ctx, id, "raw", 0)
if err != nil {
return nil, err
}
@ -120,13 +110,13 @@ func (c *StreamController) Download(w http.ResponseWriter, r *http.Request) (*re
return nil, nil
case *model.Album:
setHeaders(v.Name)
err = c.archiver.ZipAlbum(ctx, id, w)
err = api.archiver.ZipAlbum(ctx, id, w)
case *model.Artist:
setHeaders(v.Name)
err = c.archiver.ZipArtist(ctx, id, w)
err = api.archiver.ZipArtist(ctx, id, w)
case *model.Playlist:
setHeaders(v.Name)
err = c.archiver.ZipPlaylist(ctx, id, w)
err = api.archiver.ZipPlaylist(ctx, id, w)
default:
err = model.ErrNotFound
}

View File

@ -12,11 +12,11 @@ func NewSystemController() *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
}
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.License = &responses.License{Valid: true}
return response, nil

View File

@ -7,14 +7,8 @@ import (
"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
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())
if !ok {
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
}
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())
if !ok {
return nil, newError(responses.ErrorGeneric, "Internal error")

View File

@ -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",
),
)

View File

@ -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))
}