Rework db range primitives + fixed bug when playing all in releases/tracks views + raised limit to 1000 for ranges in subsonic API
This commit is contained in:
@@ -29,6 +29,8 @@
|
||||
|
||||
namespace API::Subsonic
|
||||
{
|
||||
// Max count expected from all API methods that expose a count
|
||||
static inline constexpr std::size_t defaultMaxCountSize{ 1000 };
|
||||
|
||||
enum class ResponseFormat
|
||||
{
|
||||
|
||||
@@ -47,8 +47,8 @@ namespace API::Subsonic
|
||||
// Optional params
|
||||
const std::size_t size{ getParameterAs<std::size_t>(context.parameters, "size").value_or(10) };
|
||||
const std::size_t offset{ getParameterAs<std::size_t>(context.parameters, "offset").value_or(0) };
|
||||
if (size > 500)
|
||||
throw ParameterValueTooHighGenericError{ "size", 500 };
|
||||
if (size > defaultMaxCountSize)
|
||||
throw ParameterValueTooHighGenericError{ "size", defaultMaxCountSize };
|
||||
|
||||
const Range range{ offset, size };
|
||||
|
||||
@@ -122,7 +122,7 @@ namespace API::Subsonic
|
||||
// (no seed provided by subsonic, ot it would require to store some kind of context for each user/client when iterating over the random albums)
|
||||
Release::FindParameters params;
|
||||
params.setSortMethod(ReleaseSortMethod::Random);
|
||||
params.setRange({ 0, size });
|
||||
params.setRange(Range{ 0, size });
|
||||
|
||||
releases = Release::findIds(context.dbSession, params);
|
||||
}
|
||||
@@ -132,10 +132,15 @@ namespace API::Subsonic
|
||||
}
|
||||
else if (type == "starred")
|
||||
{
|
||||
releases = feedbackService.getStarredReleases(context.userId, {}, range);
|
||||
Feedback::IFeedbackService::FindParameters params;
|
||||
params.setUser(context.userId);
|
||||
params.setRange(range);
|
||||
releases = feedbackService.findStarredReleases(params);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw NotImplementedGenericError{};
|
||||
}
|
||||
|
||||
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
|
||||
Response::Node& albumListNode{ response.createNode(id3 ? Response::Node::Key{ "albumList2" } : Response::Node::Key{ "albumList" }) };
|
||||
@@ -162,19 +167,27 @@ namespace API::Subsonic
|
||||
|
||||
Feedback::IFeedbackService& feedbackService{ *Service<Feedback::IFeedbackService>::get() };
|
||||
|
||||
for (const ArtistId artistId : feedbackService.getStarredArtists(context.userId, {} /* clusters */, std::nullopt /* linkType */, ArtistSortMethod::BySortName, Range{}).results)
|
||||
Feedback::IFeedbackService::FindParameters findParameters;
|
||||
findParameters.setUser(context.userId);
|
||||
|
||||
{
|
||||
if (auto artist{ Artist::find(context.dbSession, artistId) })
|
||||
starredNode.addArrayChild("artist", createArtistNode(context, artist, user, id3));
|
||||
Feedback::IFeedbackService::ArtistFindParameters artistFindParams;
|
||||
artistFindParams.setUser(context.userId);
|
||||
artistFindParams.setSortMethod(ArtistSortMethod::BySortName);
|
||||
for (const ArtistId artistId : feedbackService.findStarredArtists(artistFindParams).results)
|
||||
{
|
||||
if (auto artist{ Artist::find(context.dbSession, artistId) })
|
||||
starredNode.addArrayChild("artist", createArtistNode(context, artist, user, id3));
|
||||
}
|
||||
}
|
||||
|
||||
for (const ReleaseId releaseId : feedbackService.getStarredReleases(context.userId, {} /* clusters */, Range{}).results)
|
||||
for (const ReleaseId releaseId : feedbackService.findStarredReleases(findParameters).results)
|
||||
{
|
||||
if (auto release{ Release::find(context.dbSession, releaseId) })
|
||||
starredNode.addArrayChild("album", createAlbumNode(context, release, user, id3));
|
||||
}
|
||||
|
||||
for (const TrackId trackId : feedbackService.getStarredTracks(context.userId, {} /* clusters */, Range{}).results)
|
||||
for (const TrackId trackId : feedbackService.findStarredTracks(findParameters).results)
|
||||
{
|
||||
if (auto track{ Track::find(context.dbSession, trackId) })
|
||||
starredNode.addArrayChild("song", createSongNode(context, track, user));
|
||||
@@ -198,8 +211,8 @@ namespace API::Subsonic
|
||||
{
|
||||
// Optional params
|
||||
std::size_t size{ getParameterAs<std::size_t>(context.parameters, "size").value_or(50) };
|
||||
if (size > 500)
|
||||
throw ParameterValueTooHighGenericError{"size", 500};
|
||||
if (size > defaultMaxCountSize)
|
||||
throw ParameterValueTooHighGenericError{ "size", defaultMaxCountSize };
|
||||
|
||||
auto transaction{ context.dbSession.createSharedTransaction() };
|
||||
|
||||
@@ -207,13 +220,17 @@ namespace API::Subsonic
|
||||
if (!user)
|
||||
throw UserNotAuthorizedError{};
|
||||
|
||||
const auto tracks{ Track::find(context.dbSession, Track::FindParameters {}.setSortMethod(TrackSortMethod::Random).setRange({0, size})) };
|
||||
|
||||
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
|
||||
|
||||
Response::Node& randomSongsNode{ response.createNode("randomSongs") };
|
||||
for (const Track::pointer& track : tracks.results)
|
||||
randomSongsNode.addArrayChild("song", createSongNode(context, track, user));
|
||||
|
||||
Track::FindParameters params;
|
||||
params.setSortMethod(TrackSortMethod::Random);
|
||||
params.setRange(Range{ 0, size });
|
||||
|
||||
Track::find(context.dbSession, params, [&](const Track::pointer& track)
|
||||
{
|
||||
randomSongsNode.addArrayChild("song", createSongNode(context, track, user));
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
@@ -225,8 +242,8 @@ namespace API::Subsonic
|
||||
|
||||
// Optional params
|
||||
std::size_t count{ getParameterAs<std::size_t>(context.parameters, "count").value_or(10) };
|
||||
if (count > 500)
|
||||
throw ParameterValueTooHighGenericError{"count", 500};
|
||||
if (count > defaultMaxCountSize)
|
||||
throw ParameterValueTooHighGenericError{"count", defaultMaxCountSize};
|
||||
|
||||
std::size_t offset{ getParameterAs<std::size_t>(context.parameters, "offset").value_or(0) };
|
||||
|
||||
@@ -249,11 +266,12 @@ namespace API::Subsonic
|
||||
|
||||
Track::FindParameters params;
|
||||
params.setClusters({ cluster->getId() });
|
||||
params.setRange({ offset, count });
|
||||
params.setRange(Range{ offset, count });
|
||||
|
||||
const auto tracks{ Track::find(context.dbSession, params) };
|
||||
for (const Track::pointer& track : tracks.results)
|
||||
songsByGenreNode.addArrayChild("song", createSongNode(context, track, user));
|
||||
Track::find(context.dbSession, params, [&](const Track::pointer& track)
|
||||
{
|
||||
songsByGenreNode.addArrayChild("song", createSongNode(context, track, user));
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace API::Subsonic
|
||||
if (!user)
|
||||
throw UserNotAuthorizedError{};
|
||||
|
||||
const auto bookmarkIds{ TrackBookmark::find(context.dbSession, user->getId(), Range {}) };
|
||||
const auto bookmarkIds{ TrackBookmark::find(context.dbSession, user->getId()) };
|
||||
|
||||
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
|
||||
Response::Node& bookmarksNode{ response.createNode("bookmarks") };
|
||||
|
||||
@@ -124,7 +124,7 @@ namespace API::Subsonic
|
||||
// first pass: dispatch the artists by first letter
|
||||
LMS_LOG(API_SUBSONIC, DEBUG) << "GetArtists: fetching all artists...";
|
||||
std::map<char, std::vector<ArtistId>> artistsSortedByFirstChar;
|
||||
std::size_t currentArtistOffset{0};
|
||||
std::size_t currentArtistOffset{ 0 };
|
||||
constexpr std::size_t batchSize{ 100 };
|
||||
bool hasMoreArtists{ true };
|
||||
while (hasMoreArtists)
|
||||
@@ -190,7 +190,7 @@ namespace API::Subsonic
|
||||
{
|
||||
Track::FindParameters params;
|
||||
params.setArtist(id);
|
||||
params.setRange({ 0, meanTrackCountPerArtist });
|
||||
params.setRange(Range{ 0, meanTrackCountPerArtist });
|
||||
params.setSortMethod(TrackSortMethod::Random);
|
||||
|
||||
const auto artistTracks{ Track::findIds(context.dbSession, params) };
|
||||
@@ -221,7 +221,7 @@ namespace API::Subsonic
|
||||
{
|
||||
Track::FindParameters params;
|
||||
params.setRelease(id);
|
||||
params.setRange({ 0, meanTrackCountPerRelease });
|
||||
params.setRange(Range{ 0, meanTrackCountPerRelease });
|
||||
params.setSortMethod(TrackSortMethod::Random);
|
||||
|
||||
const auto releaseTracks{ Track::findIds(context.dbSession, params) };
|
||||
@@ -242,6 +242,8 @@ namespace API::Subsonic
|
||||
{
|
||||
// Optional params
|
||||
std::size_t count{ getParameterAs<std::size_t>(context.parameters, "count").value_or(50) };
|
||||
if (count > defaultMaxCountSize)
|
||||
throw ParameterValueTooHighGenericError{ "count", defaultMaxCountSize };
|
||||
|
||||
std::vector<TrackId> tracks;
|
||||
|
||||
@@ -316,9 +318,11 @@ namespace API::Subsonic
|
||||
directoryNode.setAttribute("id", idToString(RootId{}));
|
||||
directoryNode.setAttribute("name", "Music");
|
||||
|
||||
const auto rootArtistIds{ Artist::find(context.dbSession, Artist::FindParameters {}.setSortMethod(ArtistSortMethod::BySortName)) };
|
||||
for (const Artist::pointer& artist : rootArtistIds.results)
|
||||
directoryNode.addArrayChild("child", createArtistNode(context, artist, user, false /* no id3 */));
|
||||
// TODO: this does not scale when a lot of artists are present
|
||||
Artist::find(context.dbSession, Artist::FindParameters{}.setSortMethod(ArtistSortMethod::BySortName), [&](const Artist::pointer& artist)
|
||||
{
|
||||
directoryNode.addArrayChild("child", createArtistNode(context, artist, user, false /* no id3 */));
|
||||
});
|
||||
}
|
||||
else if (artistId)
|
||||
{
|
||||
@@ -330,9 +334,10 @@ namespace API::Subsonic
|
||||
|
||||
directoryNode.setAttribute("name", Utils::makeNameFilesystemCompatible(artist->getName()));
|
||||
|
||||
const auto artistReleases{ Release::find(context.dbSession, Release::FindParameters {}.setArtist(*artistId)) };
|
||||
for (const Release::pointer& release : artistReleases.results)
|
||||
directoryNode.addArrayChild("child", createAlbumNode(context, release, user, false /* no id3 */));
|
||||
Release::find(context.dbSession, Release::FindParameters{}.setArtist(*artistId), [&](const Release::pointer& release)
|
||||
{
|
||||
directoryNode.addArrayChild("child", createAlbumNode(context, release, user, false /* no id3 */));
|
||||
});
|
||||
}
|
||||
else if (releaseId)
|
||||
{
|
||||
@@ -344,9 +349,10 @@ namespace API::Subsonic
|
||||
|
||||
directoryNode.setAttribute("name", Utils::makeNameFilesystemCompatible(release->getName()));
|
||||
|
||||
const auto tracks{ Track::find(context.dbSession, Track::FindParameters {}.setRelease(*releaseId).setSortMethod(TrackSortMethod::Release)) };
|
||||
for (const Track::pointer& track : tracks.results)
|
||||
directoryNode.addArrayChild("child", createSongNode(context, track, user));
|
||||
Track::find(context.dbSession, Track::FindParameters{}.setRelease(*releaseId).setSortMethod(TrackSortMethod::Release), [&](const Track::pointer& track)
|
||||
{
|
||||
directoryNode.addArrayChild("child", createSongNode(context, track, user));
|
||||
});
|
||||
}
|
||||
else
|
||||
throw BadParameterGenericError{ "id" };
|
||||
|
||||
@@ -57,12 +57,12 @@ namespace API::Subsonic
|
||||
std::size_t songCount{ getParameterAs<std::size_t>(context.parameters, "songCount").value_or(20) };
|
||||
std::size_t songOffset{ getParameterAs<std::size_t>(context.parameters, "songOffset").value_or(0) };
|
||||
|
||||
if (artistCount > 500)
|
||||
throw ParameterValueTooHighGenericError{ "artistCount", 500 };
|
||||
else if (albumCount > 500)
|
||||
throw ParameterValueTooHighGenericError{ "albumCount", 500 };
|
||||
else if (songCount > 500)
|
||||
throw ParameterValueTooHighGenericError{ "songCount", 500 };
|
||||
if (artistCount > defaultMaxCountSize)
|
||||
throw ParameterValueTooHighGenericError{ "artistCount", defaultMaxCountSize };
|
||||
else if (albumCount > defaultMaxCountSize)
|
||||
throw ParameterValueTooHighGenericError{ "albumCount", defaultMaxCountSize };
|
||||
else if (songCount > defaultMaxCountSize)
|
||||
throw ParameterValueTooHighGenericError{ "songCount", defaultMaxCountSize };
|
||||
|
||||
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
|
||||
Response::Node& searchResult2Node{ response.createNode(id3 ? "searchResult3" : "searchResult2") };
|
||||
@@ -77,30 +77,36 @@ namespace API::Subsonic
|
||||
{
|
||||
Artist::FindParameters params;
|
||||
params.setKeywords(keywords);
|
||||
params.setRange({ artistOffset, artistCount });
|
||||
params.setRange(Range{ artistOffset, artistCount });
|
||||
|
||||
for (const Artist::pointer& artist : Artist::find(context.dbSession, params).results)
|
||||
searchResult2Node.addArrayChild("artist", createArtistNode(context, artist, user, id3));
|
||||
Artist::find(context.dbSession, params, [&](const Artist::pointer& artist)
|
||||
{
|
||||
searchResult2Node.addArrayChild("artist", createArtistNode(context, artist, user, id3));
|
||||
});
|
||||
}
|
||||
|
||||
if (albumCount > 0)
|
||||
{
|
||||
Release::FindParameters params;
|
||||
params.setKeywords(keywords);
|
||||
params.setRange({ albumOffset, albumCount });
|
||||
params.setRange(Range{ albumOffset, albumCount });
|
||||
|
||||
for (const Release::pointer& release : Release::find(context.dbSession, params).results)
|
||||
searchResult2Node.addArrayChild("album", createAlbumNode(context, release, user, id3));
|
||||
Release::find(context.dbSession, params, [&](const Release::pointer& release)
|
||||
{
|
||||
searchResult2Node.addArrayChild("album", createAlbumNode(context, release, user, id3));
|
||||
});
|
||||
}
|
||||
|
||||
if (songCount > 0)
|
||||
{
|
||||
Track::FindParameters params;
|
||||
params.setKeywords(keywords);
|
||||
params.setRange({ songOffset, songCount });
|
||||
params.setRange(Range{ songOffset, songCount });
|
||||
|
||||
for (const Track::pointer& track : Track::find(context.dbSession, params).results)
|
||||
searchResult2Node.addArrayChild("song", createSongNode(context, track, user));
|
||||
Track::find(context.dbSession, params, [&](const Track::pointer& track)
|
||||
{
|
||||
searchResult2Node.addArrayChild("song", createSongNode(context, track, user));
|
||||
});
|
||||
}
|
||||
|
||||
return response;
|
||||
|
||||
Reference in New Issue
Block a user