Refactored request context handling

This commit is contained in:
emeric
2025-10-01 22:11:32 +02:00
parent c62ac1d109
commit 0ccd095534
27 changed files with 594 additions and 473 deletions
@@ -47,12 +47,12 @@ namespace lms::api::subsonic
Response handleGetAlbumListRequestCommon(RequestContext& context, bool id3)
{
// Mandatory params
const std::string type{ getMandatoryParameterAs<std::string>(context.parameters, "type") };
const std::string type{ getMandatoryParameterAs<std::string>(context.getParameters(), "type") };
// Optional params
const MediaLibraryId mediaLibraryId{ getParameterAs<MediaLibraryId>(context.parameters, "musicFolderId").value_or(MediaLibraryId{}) };
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) };
const MediaLibraryId mediaLibraryId{ getParameterAs<MediaLibraryId>(context.getParameters(), "musicFolderId").value_or(MediaLibraryId{}) };
const std::size_t size{ getParameterAs<std::size_t>(context.getParameters(), "size").value_or(10) };
const std::size_t offset{ getParameterAs<std::size_t>(context.getParameters(), "offset").value_or(0) };
if (size > defaultMaxCountSize)
throw ParameterValueTooHighGenericError{ "size", defaultMaxCountSize };
@@ -62,7 +62,7 @@ namespace lms::api::subsonic
scrobbling::IScrobblingService& scrobblingService{ *core::Service<scrobbling::IScrobblingService>::get() };
feedback::IFeedbackService& feedbackService{ *core::Service<feedback::IFeedbackService>::get() };
auto transaction{ context.dbSession.createReadTransaction() };
auto transaction{ context.getDbSession().createReadTransaction() };
if (type == "alphabeticalByName")
{
@@ -71,7 +71,7 @@ namespace lms::api::subsonic
params.setRange(range);
params.filters.setMediaLibrary(mediaLibraryId);
releases = Release::findIds(context.dbSession, params);
releases = Release::findIds(context.getDbSession(), params);
}
else if (type == "alphabeticalByArtist")
{
@@ -80,14 +80,14 @@ namespace lms::api::subsonic
params.setRange(range);
params.filters.setMediaLibrary(mediaLibraryId);
releases = Release::findIds(context.dbSession, params);
releases = Release::findIds(context.getDbSession(), params);
}
else if (type == "byGenre")
{
// Mandatory param
const std::string genre{ getMandatoryParameterAs<std::string>(context.parameters, "genre") };
const std::string genre{ getMandatoryParameterAs<std::string>(context.getParameters(), "genre") };
if (const ClusterType::pointer clusterType{ ClusterType::find(context.dbSession, "GENRE") })
if (const ClusterType::pointer clusterType{ ClusterType::find(context.getDbSession(), "GENRE") })
{
if (const Cluster::pointer cluster{ clusterType->getCluster(genre) })
{
@@ -97,14 +97,14 @@ namespace lms::api::subsonic
params.setSortMethod(ReleaseSortMethod::Name);
params.setRange(range);
releases = Release::findIds(context.dbSession, params);
releases = Release::findIds(context.getDbSession(), params);
}
}
}
else if (type == "byYear")
{
const int fromYear{ getMandatoryParameterAs<int>(context.parameters, "fromYear") };
const int toYear{ getMandatoryParameterAs<int>(context.parameters, "toYear") };
const int fromYear{ getMandatoryParameterAs<int>(context.getParameters(), "fromYear") };
const int toYear{ getMandatoryParameterAs<int>(context.getParameters(), "toYear") };
Release::FindParameters params;
params.setSortMethod(fromYear > toYear ? ReleaseSortMethod::DateDesc : ReleaseSortMethod::DateAsc);
@@ -112,12 +112,12 @@ namespace lms::api::subsonic
params.setDateRange(YearRange{ std::min(fromYear, toYear), std::max(fromYear, toYear) });
params.filters.setMediaLibrary(mediaLibraryId);
releases = Release::findIds(context.dbSession, params);
releases = Release::findIds(context.getDbSession(), params);
}
else if (type == "frequent")
{
scrobbling::IScrobblingService::FindParameters params;
params.setUser(context.user->getId());
params.setUser(context.getUser()->getId());
params.setRange(range);
params.filters.setMediaLibrary(mediaLibraryId);
@@ -130,7 +130,7 @@ namespace lms::api::subsonic
params.setRange(range);
params.filters.setMediaLibrary(mediaLibraryId);
releases = Release::findIds(context.dbSession, params);
releases = Release::findIds(context.getDbSession(), params);
}
else if (type == "random")
{
@@ -141,12 +141,12 @@ namespace lms::api::subsonic
params.setRange(Range{ 0, size });
params.filters.setMediaLibrary(mediaLibraryId);
releases = Release::findIds(context.dbSession, params);
releases = Release::findIds(context.getDbSession(), params);
}
else if (type == "recent")
{
scrobbling::IScrobblingService::FindParameters params;
params.setUser(context.user->getId());
params.setUser(context.getUser()->getId());
params.setRange(range);
params.filters.setMediaLibrary(mediaLibraryId);
@@ -155,7 +155,7 @@ namespace lms::api::subsonic
else if (type == "starred")
{
feedback::IFeedbackService::FindParameters params;
params.setUser(context.user->getId());
params.setUser(context.getUser()->getId());
params.setRange(range);
params.filters.setMediaLibrary(mediaLibraryId);
@@ -166,12 +166,12 @@ namespace lms::api::subsonic
throw NotImplementedGenericError{};
}
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response response{ Response::createOkResponse(context.getServerProtocolVersion()) };
Response::Node& albumListNode{ response.createNode(id3 ? Response::Node::Key{ "albumList2" } : Response::Node::Key{ "albumList" }) };
for (const ReleaseId releaseId : releases.results)
{
const Release::pointer release{ Release::find(context.dbSession, releaseId) };
const Release::pointer release{ Release::find(context.getDbSession(), releaseId) };
albumListNode.addArrayChild("album", createAlbumNode(context, release, id3));
}
@@ -181,11 +181,11 @@ namespace lms::api::subsonic
Response handleGetStarredRequestCommon(RequestContext& context, bool id3)
{
// Optional parameters
const MediaLibraryId mediaLibrary{ getParameterAs<MediaLibraryId>(context.parameters, "musicFolderId").value_or(MediaLibraryId{}) };
const MediaLibraryId mediaLibrary{ getParameterAs<MediaLibraryId>(context.getParameters(), "musicFolderId").value_or(MediaLibraryId{}) };
auto transaction{ context.dbSession.createReadTransaction() };
auto transaction{ context.getDbSession().createReadTransaction() };
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response response{ Response::createOkResponse(context.getServerProtocolVersion()) };
Response::Node& starredNode{ response.createNode(id3 ? Response::Node::Key{ "starred2" } : Response::Node::Key{ "starred" }) };
feedback::IFeedbackService& feedbackService{ *core::Service<feedback::IFeedbackService>::get() };
@@ -194,29 +194,29 @@ namespace lms::api::subsonic
if (id3)
{
feedback::IFeedbackService::ArtistFindParameters artistFindParams;
artistFindParams.setUser(context.user->getId());
artistFindParams.setUser(context.getUser()->getId());
artistFindParams.setSortMethod(ArtistSortMethod::SortName);
for (const ArtistId artistId : feedbackService.findStarredArtists(artistFindParams).results)
{
if (auto artist{ Artist::find(context.dbSession, artistId) })
if (auto artist{ Artist::find(context.getDbSession(), artistId) })
starredNode.addArrayChild("artist", createArtistNode(context, artist));
}
}
feedback::IFeedbackService::FindParameters findParameters;
findParameters.setUser(context.user->getId());
findParameters.setUser(context.getUser()->getId());
findParameters.filters.setMediaLibrary(mediaLibrary);
for (const ReleaseId releaseId : feedbackService.findStarredReleases(findParameters).results)
{
if (auto release{ Release::find(context.dbSession, releaseId) })
if (auto release{ Release::find(context.getDbSession(), releaseId) })
starredNode.addArrayChild("album", createAlbumNode(context, release, id3));
}
for (const TrackId trackId : feedbackService.findStarredTracks(findParameters).results)
{
if (auto track{ Track::find(context.dbSession, trackId) })
starredNode.addArrayChild("song", createSongNode(context, track, context.user));
if (auto track{ Track::find(context.getDbSession(), trackId) })
starredNode.addArrayChild("song", createSongNode(context, track, context.getUser()));
}
return response;
@@ -236,23 +236,23 @@ namespace lms::api::subsonic
Response handleGetRandomSongsRequest(RequestContext& context)
{
// Optional params
const MediaLibraryId mediaLibraryId{ getParameterAs<MediaLibraryId>(context.parameters, "musicFolderId").value_or(MediaLibraryId{}) };
std::size_t size{ getParameterAs<std::size_t>(context.parameters, "size").value_or(50) };
const MediaLibraryId mediaLibraryId{ getParameterAs<MediaLibraryId>(context.getParameters(), "musicFolderId").value_or(MediaLibraryId{}) };
std::size_t size{ getParameterAs<std::size_t>(context.getParameters(), "size").value_or(50) };
if (size > defaultMaxCountSize)
throw ParameterValueTooHighGenericError{ "size", defaultMaxCountSize };
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response response{ Response::createOkResponse(context.getServerProtocolVersion()) };
Response::Node& randomSongsNode{ response.createNode("randomSongs") };
auto transaction{ context.dbSession.createReadTransaction() };
auto transaction{ context.getDbSession().createReadTransaction() };
Track::FindParameters params;
params.setSortMethod(TrackSortMethod::Random);
params.setRange(Range{ 0, size });
params.filters.setMediaLibrary(mediaLibraryId);
Track::find(context.dbSession, params, [&](const Track::pointer& track) {
randomSongsNode.addArrayChild("song", createSongNode(context, track, context.user));
Track::find(context.getDbSession(), params, [&](const Track::pointer& track) {
randomSongsNode.addArrayChild("song", createSongNode(context, track, context.getUser()));
});
return response;
@@ -261,19 +261,19 @@ namespace lms::api::subsonic
Response handleGetSongsByGenreRequest(RequestContext& context)
{
// Mandatory params
std::string genre{ getMandatoryParameterAs<std::string>(context.parameters, "genre") };
std::string genre{ getMandatoryParameterAs<std::string>(context.getParameters(), "genre") };
// Optional params
const MediaLibraryId mediaLibrary{ getParameterAs<MediaLibraryId>(context.parameters, "musicFolderId").value_or(MediaLibraryId{}) };
std::size_t count{ getParameterAs<std::size_t>(context.parameters, "count").value_or(10) };
const MediaLibraryId mediaLibrary{ getParameterAs<MediaLibraryId>(context.getParameters(), "musicFolderId").value_or(MediaLibraryId{}) };
std::size_t count{ getParameterAs<std::size_t>(context.getParameters(), "count").value_or(10) };
if (count > defaultMaxCountSize)
throw ParameterValueTooHighGenericError{ "count", defaultMaxCountSize };
std::size_t offset{ getParameterAs<std::size_t>(context.parameters, "offset").value_or(0) };
std::size_t offset{ getParameterAs<std::size_t>(context.getParameters(), "offset").value_or(0) };
auto transaction{ context.dbSession.createReadTransaction() };
auto transaction{ context.getDbSession().createReadTransaction() };
auto clusterType{ ClusterType::find(context.dbSession, "GENRE") };
auto clusterType{ ClusterType::find(context.getDbSession(), "GENRE") };
if (!clusterType)
throw RequestedDataNotFoundError{};
@@ -281,7 +281,7 @@ namespace lms::api::subsonic
if (!cluster)
throw RequestedDataNotFoundError{};
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response response{ Response::createOkResponse(context.getServerProtocolVersion()) };
Response::Node& songsByGenreNode{ response.createNode("songsByGenre") };
Track::FindParameters params;
@@ -289,8 +289,8 @@ namespace lms::api::subsonic
params.filters.setMediaLibrary(mediaLibrary);
params.setRange(Range{ offset, count });
Track::find(context.dbSession, params, [&](const Track::pointer& track) {
songsByGenreNode.addArrayChild("song", createSongNode(context, track, context.user));
Track::find(context.getDbSession(), params, [&](const Track::pointer& track) {
songsByGenreNode.addArrayChild("song", createSongNode(context, track, context.getUser()));
});
return response;
+30 -30
View File
@@ -38,18 +38,18 @@ namespace lms::api::subsonic
Response handleGetBookmarks(RequestContext& context)
{
auto transaction{ context.dbSession.createReadTransaction() };
auto transaction{ context.getDbSession().createReadTransaction() };
const auto bookmarkIds{ TrackBookmark::find(context.dbSession, context.user->getId()) };
const auto bookmarkIds{ TrackBookmark::find(context.getDbSession(), context.getUser()->getId()) };
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response response{ Response::createOkResponse(context.getServerProtocolVersion()) };
Response::Node& bookmarksNode{ response.createNode("bookmarks") };
for (const TrackBookmarkId bookmarkId : bookmarkIds.results)
{
const TrackBookmark::pointer bookmark{ TrackBookmark::find(context.dbSession, bookmarkId) };
const TrackBookmark::pointer bookmark{ TrackBookmark::find(context.getDbSession(), bookmarkId) };
Response::Node bookmarkNode{ createBookmarkNode(bookmark) };
bookmarkNode.addChild("entry", createSongNode(context, bookmark->getTrack(), context.user));
bookmarkNode.addChild("entry", createSongNode(context, bookmark->getTrack(), context.getUser()));
bookmarksNode.addArrayChild("bookmark", std::move(bookmarkNode));
}
@@ -59,51 +59,51 @@ namespace lms::api::subsonic
Response handleCreateBookmark(RequestContext& context)
{
// Mandatory params
TrackId trackId{ getMandatoryParameterAs<TrackId>(context.parameters, "id") };
unsigned long position{ getMandatoryParameterAs<unsigned long>(context.parameters, "position") };
const std::optional<std::string> comment{ getParameterAs<std::string>(context.parameters, "comment") };
TrackId trackId{ getMandatoryParameterAs<TrackId>(context.getParameters(), "id") };
unsigned long position{ getMandatoryParameterAs<unsigned long>(context.getParameters(), "position") };
const std::optional<std::string> comment{ getParameterAs<std::string>(context.getParameters(), "comment") };
auto transaction{ context.dbSession.createWriteTransaction() };
auto transaction{ context.getDbSession().createWriteTransaction() };
const Track::pointer track{ Track::find(context.dbSession, trackId) };
const Track::pointer track{ Track::find(context.getDbSession(), trackId) };
if (!track)
throw RequestedDataNotFoundError{};
// Replace any existing bookmark
auto bookmark{ TrackBookmark::find(context.dbSession, context.user->getId(), trackId) };
auto bookmark{ TrackBookmark::find(context.getDbSession(), context.getUser()->getId(), trackId) };
if (!bookmark)
bookmark = context.dbSession.create<TrackBookmark>(context.user, track);
bookmark = context.getDbSession().create<TrackBookmark>(context.getUser(), track);
bookmark.modify()->setOffset(std::chrono::milliseconds{ position });
if (comment)
bookmark.modify()->setComment(*comment);
return Response::createOkResponse(context.serverProtocolVersion);
return Response::createOkResponse(context.getServerProtocolVersion());
}
Response handleDeleteBookmark(RequestContext& context)
{
// Mandatory params
TrackId trackId{ getMandatoryParameterAs<TrackId>(context.parameters, "id") };
TrackId trackId{ getMandatoryParameterAs<TrackId>(context.getParameters(), "id") };
auto transaction{ context.dbSession.createWriteTransaction() };
auto transaction{ context.getDbSession().createWriteTransaction() };
auto bookmark{ TrackBookmark::find(context.dbSession, context.user->getId(), trackId) };
auto bookmark{ TrackBookmark::find(context.getDbSession(), context.getUser()->getId(), trackId) };
if (!bookmark)
throw RequestedDataNotFoundError{};
bookmark.remove();
return Response::createOkResponse(context.serverProtocolVersion);
return Response::createOkResponse(context.getServerProtocolVersion());
}
// Use a dedicated internal playlist
Response handleGetPlayQueue(RequestContext& context)
{
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response response{ Response::createOkResponse(context.getServerProtocolVersion()) };
auto transaction{ context.dbSession.createReadTransaction() };
const db::PlayQueue::pointer playQueue{ db::PlayQueue::find(context.dbSession, context.user->getId(), "subsonic") };
auto transaction{ context.getDbSession().createReadTransaction() };
const db::PlayQueue::pointer playQueue{ db::PlayQueue::find(context.getDbSession(), context.getUser()->getId(), "subsonic") };
if (playQueue)
{
Response::Node& playQueueNode{ response.createNode("playQueue") };
@@ -115,7 +115,7 @@ namespace lms::api::subsonic
}
// mandatory fields
playQueueNode.setAttribute("username", context.user->getLoginName());
playQueueNode.setAttribute("username", context.getUser()->getLoginName());
playQueueNode.setAttribute("changed", core::stringUtils::toISO8601String(playQueue->getLastModifiedDateTime()));
playQueueNode.setAttribute("changedBy", "unknown"); // we don't store the client name (could be several same clients on several devices...)
@@ -130,9 +130,9 @@ namespace lms::api::subsonic
Response handleSavePlayQueue(RequestContext& context)
{
// optional params
std::vector<db::TrackId> trackIds{ getMultiParametersAs<TrackId>(context.parameters, "id") };
const std::optional<db::TrackId> currentTrackId{ getParameterAs<db::TrackId>(context.parameters, "current") };
const std::chrono::milliseconds currentPositionInTrack{ getParameterAs<std::size_t>(context.parameters, "current").value_or(0) };
std::vector<db::TrackId> trackIds{ getMultiParametersAs<TrackId>(context.getParameters(), "id") };
const std::optional<db::TrackId> currentTrackId{ getParameterAs<db::TrackId>(context.getParameters(), "current") };
const std::chrono::milliseconds currentPositionInTrack{ getParameterAs<std::size_t>(context.getParameters(), "current").value_or(0) };
std::vector<db::Track::pointer> tracks;
tracks.reserve(trackIds.size());
@@ -140,20 +140,20 @@ namespace lms::api::subsonic
// no id means we clear the play queue (see https://github.com/opensubsonic/open-subsonic-api/pull/106)
if (!trackIds.empty())
{
auto transaction{ context.dbSession.createReadTransaction() };
auto transaction{ context.getDbSession().createReadTransaction() };
for (db::TrackId trackId : trackIds)
{
if (db::Track::pointer track{ db::Track::find(context.dbSession, trackId) })
if (db::Track::pointer track{ db::Track::find(context.getDbSession(), trackId) })
tracks.push_back(track);
}
}
{
auto transaction{ context.dbSession.createWriteTransaction() };
auto transaction{ context.getDbSession().createWriteTransaction() };
db::PlayQueue::pointer playQueue{ db::PlayQueue::find(context.dbSession, context.user->getId(), "subsonic") };
db::PlayQueue::pointer playQueue{ db::PlayQueue::find(context.getDbSession(), context.getUser()->getId(), "subsonic") };
if (!playQueue)
playQueue = context.dbSession.create<db::PlayQueue>(context.user, "subsonic");
playQueue = context.getDbSession().create<db::PlayQueue>(context.getUser(), "subsonic");
playQueue.modify()->clear();
std::size_t index{};
@@ -171,6 +171,6 @@ namespace lms::api::subsonic
playQueue.modify()->setLastModifiedDateTime(Wt::WDateTime::currentDateTime());
}
return Response::createOkResponse(context.serverProtocolVersion);
return Response::createOkResponse(context.getServerProtocolVersion());
}
} // namespace lms::api::subsonic
+85 -85
View File
@@ -91,7 +91,7 @@ namespace lms::api::subsonic
params.setParentDirectory(parentDirectory->getId());
params.setSortMethod(DirectorySortMethod::Name);
Directory::find(context.dbSession, params, [&](const Directory::pointer& directory) {
Directory::find(context.getDbSession(), params, [&](const Directory::pointer& directory) {
const std::string_view name{ directory->getName() };
assert(!name.empty());
@@ -114,7 +114,7 @@ namespace lms::api::subsonic
const std::size_t meanTrackCountPerArtist{ (count / artistIds.size()) + 1 };
auto transaction{ context.dbSession.createReadTransaction() };
auto transaction{ context.getDbSession().createReadTransaction() };
std::vector<TrackId> tracks;
tracks.reserve(count);
@@ -126,7 +126,7 @@ namespace lms::api::subsonic
params.setRange(Range{ 0, meanTrackCountPerArtist });
params.setSortMethod(TrackSortMethod::Random);
const auto artistTracks{ Track::findIds(context.dbSession, params) };
const auto artistTracks{ Track::findIds(context.getDbSession(), params) };
tracks.insert(std::end(tracks),
std::begin(artistTracks.results),
std::end(artistTracks.results));
@@ -145,7 +145,7 @@ namespace lms::api::subsonic
const std::size_t meanTrackCountPerRelease{ (count / releaseIds.size()) + 1 };
auto transaction{ context.dbSession.createReadTransaction() };
auto transaction{ context.getDbSession().createReadTransaction() };
std::vector<TrackId> tracks;
tracks.reserve(count);
@@ -157,7 +157,7 @@ namespace lms::api::subsonic
params.setRange(Range{ 0, meanTrackCountPerRelease });
params.setSortMethod(TrackSortMethod::Random);
const auto releaseTracks{ Track::findIds(context.dbSession, params) };
const auto releaseTracks{ Track::findIds(context.getDbSession(), params) };
tracks.insert(std::end(tracks),
std::begin(releaseTracks.results),
std::end(releaseTracks.results));
@@ -174,31 +174,31 @@ namespace lms::api::subsonic
Response handleGetSimilarSongsRequestCommon(RequestContext& context, bool id3)
{
// Optional params
std::size_t count{ getParameterAs<std::size_t>(context.parameters, "count").value_or(50) };
std::size_t count{ getParameterAs<std::size_t>(context.getParameters(), "count").value_or(50) };
if (count > defaultMaxCountSize)
throw ParameterValueTooHighGenericError{ "count", defaultMaxCountSize };
std::vector<TrackId> tracks;
if (const auto artistId{ getParameterAs<ArtistId>(context.parameters, "id") })
if (const auto artistId{ getParameterAs<ArtistId>(context.getParameters(), "id") })
tracks = findSimilarSongs(context, *artistId, count);
else if (const auto releaseId{ getParameterAs<ReleaseId>(context.parameters, "id") })
else if (const auto releaseId{ getParameterAs<ReleaseId>(context.getParameters(), "id") })
tracks = findSimilarSongs(context, *releaseId, count);
else if (const auto trackId{ getParameterAs<TrackId>(context.parameters, "id") })
else if (const auto trackId{ getParameterAs<TrackId>(context.getParameters(), "id") })
tracks = findSimilarSongs(context, *trackId, count);
else
throw BadParameterGenericError{ "id" };
core::random::shuffleContainer(tracks);
auto transaction{ context.dbSession.createReadTransaction() };
auto transaction{ context.getDbSession().createReadTransaction() };
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response response{ Response::createOkResponse(context.getServerProtocolVersion()) };
Response::Node& similarSongsNode{ response.createNode(id3 ? Response::Node::Key{ "similarSongs2" } : Response::Node::Key{ "similarSongs" }) };
for (const TrackId trackId : tracks)
{
const Track::pointer track{ Track::find(context.dbSession, trackId) };
similarSongsNode.addArrayChild("song", createSongNode(context, track, context.user));
const Track::pointer track{ Track::find(context.getDbSession(), trackId) };
similarSongsNode.addArrayChild("song", createSongNode(context, track, context.getUser()));
}
return response;
@@ -223,11 +223,11 @@ namespace lms::api::subsonic
Response handleGetMusicFoldersRequest(RequestContext& context)
{
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response response{ Response::createOkResponse(context.getServerProtocolVersion()) };
Response::Node& musicFoldersNode{ response.createNode("musicFolders") };
auto transaction{ context.dbSession.createReadTransaction() };
MediaLibrary::find(context.dbSession, [&](const MediaLibrary::pointer& library) {
auto transaction{ context.getDbSession().createReadTransaction() };
MediaLibrary::find(context.getDbSession(), [&](const MediaLibrary::pointer& library) {
Response::Node& musicFolderNode{ musicFoldersNode.createArrayChild("musicFolder") };
musicFolderNode.setAttribute("id", library->getId().getValue());
@@ -240,16 +240,16 @@ namespace lms::api::subsonic
Response handleGetIndexesRequest(RequestContext& context)
{
// Optional params
const MediaLibraryId mediaLibrary{ getParameterAs<MediaLibraryId>(context.parameters, "musicFolderId").value_or(MediaLibraryId{}) };
const MediaLibraryId mediaLibrary{ getParameterAs<MediaLibraryId>(context.getParameters(), "musicFolderId").value_or(MediaLibraryId{}) };
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response response{ Response::createOkResponse(context.getServerProtocolVersion()) };
Response::Node& indexesNode{ response.createNode("indexes") };
indexesNode.setAttribute("ignoredArticles", "");
indexesNode.setAttribute("lastModified", reportedDummyDateULong); // TODO report last file write?
auto transaction{ context.dbSession.createReadTransaction() };
auto transaction{ context.getDbSession().createReadTransaction() };
const std::vector<Directory::pointer> rootDirectories{ getRootDirectories(context.dbSession, mediaLibrary) };
const std::vector<Directory::pointer> rootDirectories{ getRootDirectories(context.getDbSession(), mediaLibrary) };
IndexMap indexedDirectories;
for (const Directory::pointer& rootdirectory : rootDirectories)
@@ -257,8 +257,8 @@ namespace lms::api::subsonic
Track::FindParameters params;
params.setDirectory(rootdirectory->getId());
Track::find(context.dbSession, params, [&](const Track::pointer& track) {
indexesNode.addArrayChild("child", createSongNode(context, track, context.user));
Track::find(context.getDbSession(), params, [&](const Track::pointer& track) {
indexesNode.addArrayChild("child", createSongNode(context, track, context.getUser()));
});
getIndexedChildDirectories(context, rootdirectory, indexedDirectories);
@@ -287,21 +287,21 @@ namespace lms::api::subsonic
Response handleGetMusicDirectoryRequest(RequestContext& context)
{
// Mandatory params
const auto directoryId{ getMandatoryParameterAs<DirectoryId>(context.parameters, "id") };
const auto directoryId{ getMandatoryParameterAs<DirectoryId>(context.getParameters(), "id") };
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response response{ Response::createOkResponse(context.getServerProtocolVersion()) };
Response::Node& directoryNode{ response.createNode("directory") };
auto transaction{ context.dbSession.createReadTransaction() };
auto transaction{ context.getDbSession().createReadTransaction() };
const Directory::pointer directory{ Directory::find(context.dbSession, directoryId) };
const Directory::pointer directory{ Directory::find(context.getDbSession(), directoryId) };
if (!directory)
throw RequestedDataNotFoundError{};
if (const Release::pointer release{ getReleaseFromDirectory(context.dbSession, directoryId) })
if (const Release::pointer release{ getReleaseFromDirectory(context.getDbSession(), directoryId) })
{
directoryNode.setAttribute("playCount", core::Service<scrobbling::IScrobblingService>::get()->getCount(context.user->getId(), release->getId()));
if (const Wt::WDateTime dateTime{ core::Service<feedback::IFeedbackService>::get()->getStarredDateTime(context.user->getId(), release->getId()) }; dateTime.isValid())
directoryNode.setAttribute("playCount", core::Service<scrobbling::IScrobblingService>::get()->getCount(context.getUser()->getId(), release->getId()));
if (const Wt::WDateTime dateTime{ core::Service<feedback::IFeedbackService>::get()->getStarredDateTime(context.getUser()->getId(), release->getId()) }; dateTime.isValid())
directoryNode.setAttribute("starred", core::stringUtils::toISO8601String(dateTime));
}
@@ -317,8 +317,8 @@ namespace lms::api::subsonic
params.setParentDirectory(directory->getId());
params.setSortMethod(DirectorySortMethod::Name);
Directory::find(context.dbSession, params, [&](const Directory::pointer& subDirectory) {
const Release::pointer release{ getReleaseFromDirectory(context.dbSession, subDirectory->getId()) };
Directory::find(context.getDbSession(), params, [&](const Directory::pointer& subDirectory) {
const Release::pointer release{ getReleaseFromDirectory(context.getDbSession(), subDirectory->getId()) };
if (release)
{
@@ -343,8 +343,8 @@ namespace lms::api::subsonic
params.setDirectory(directory->getId());
params.setSortMethod(TrackSortMethod::AbsoluteFilePath);
Track::find(context.dbSession, params, [&](const Track::pointer& track) {
directoryNode.addArrayChild("child", createSongNode(context, track, context.user));
Track::find(context.getDbSession(), params, [&](const Track::pointer& track) {
directoryNode.addArrayChild("child", createSongNode(context, track, context.getUser()));
});
}
@@ -353,13 +353,13 @@ namespace lms::api::subsonic
Response handleGetGenresRequest(RequestContext& context)
{
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response response{ Response::createOkResponse(context.getServerProtocolVersion()) };
Response::Node& genresNode{ response.createNode("genres") };
auto transaction{ context.dbSession.createReadTransaction() };
auto transaction{ context.getDbSession().createReadTransaction() };
const ClusterType::pointer clusterType{ ClusterType::find(context.dbSession, "GENRE") };
const ClusterType::pointer clusterType{ ClusterType::find(context.getDbSession(), "GENRE") };
if (clusterType)
{
const auto clusters{ clusterType->getClusters() };
@@ -374,9 +374,9 @@ namespace lms::api::subsonic
Response handleGetArtistsRequest(RequestContext& context)
{
// Optional params
const MediaLibraryId mediaLibrary{ getParameterAs<MediaLibraryId>(context.parameters, "musicFolderId").value_or(MediaLibraryId{}) };
const MediaLibraryId mediaLibrary{ getParameterAs<MediaLibraryId>(context.getParameters(), "musicFolderId").value_or(MediaLibraryId{}) };
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response response{ Response::createOkResponse(context.getServerProtocolVersion()) };
Response::Node& artistsNode{ response.createNode("artists") };
artistsNode.setAttribute("ignoredArticles", "");
@@ -384,10 +384,10 @@ namespace lms::api::subsonic
Artist::FindParameters parameters;
{
auto transaction{ context.dbSession.createReadTransaction() };
auto transaction{ context.getDbSession().createReadTransaction() };
parameters.setSortMethod(ArtistSortMethod::SortName);
switch (context.user->getSubsonicArtistListMode())
switch (context.getUser()->getSubsonicArtistListMode())
{
case SubsonicArtistListMode::AllArtists:
break;
@@ -411,10 +411,10 @@ namespace lms::api::subsonic
bool hasMoreArtists{ true };
while (hasMoreArtists)
{
auto transaction{ context.dbSession.createReadTransaction() };
auto transaction{ context.getDbSession().createReadTransaction() };
parameters.setRange(Range{ currentArtistOffset, batchSize });
const auto artists{ Artist::find(context.dbSession, parameters) };
const auto artists{ Artist::find(context.getDbSession(), parameters) };
for (const Artist::pointer& artist : artists.results)
{
std::string_view sortName{ artist->getSortName() };
@@ -441,9 +441,9 @@ namespace lms::api::subsonic
for (const ArtistId artistId : artistIds)
{
auto transaction{ context.dbSession.createReadTransaction() };
auto transaction{ context.getDbSession().createReadTransaction() };
if (const Artist::pointer artist{ Artist::find(context.dbSession, artistId) })
if (const Artist::pointer artist{ Artist::find(context.getDbSession(), artistId) })
indexNode.addArrayChild("artist", createArtistNode(context, artist));
}
}
@@ -454,18 +454,18 @@ namespace lms::api::subsonic
Response handleGetArtistRequest(RequestContext& context)
{
// Mandatory params
ArtistId id{ getMandatoryParameterAs<ArtistId>(context.parameters, "id") };
ArtistId id{ getMandatoryParameterAs<ArtistId>(context.getParameters(), "id") };
auto transaction{ context.dbSession.createReadTransaction() };
auto transaction{ context.getDbSession().createReadTransaction() };
const Artist::pointer artist{ Artist::find(context.dbSession, id) };
const Artist::pointer artist{ Artist::find(context.getDbSession(), id) };
if (!artist)
throw RequestedDataNotFoundError{};
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response response{ Response::createOkResponse(context.getServerProtocolVersion()) };
Response::Node artistNode{ createArtistNode(context, artist) };
const auto releases{ Release::find(context.dbSession, Release::FindParameters{}.setArtist(artist->getId())) };
const auto releases{ Release::find(context.getDbSession(), Release::FindParameters{}.setArtist(artist->getId())) };
for (const Release::pointer& release : releases.results)
artistNode.addArrayChild("album", createAlbumNode(context, release, true /* id3 */));
@@ -477,18 +477,18 @@ namespace lms::api::subsonic
Response handleGetAlbumRequest(RequestContext& context)
{
// Mandatory params
ReleaseId id{ getMandatoryParameterAs<ReleaseId>(context.parameters, "id") };
ReleaseId id{ getMandatoryParameterAs<ReleaseId>(context.getParameters(), "id") };
auto transaction{ context.dbSession.createReadTransaction() };
auto transaction{ context.getDbSession().createReadTransaction() };
Release::pointer release{ Release::find(context.dbSession, id) };
Release::pointer release{ Release::find(context.getDbSession(), id) };
if (!release)
throw RequestedDataNotFoundError{};
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response response{ Response::createOkResponse(context.getServerProtocolVersion()) };
Response::Node albumNode{ createAlbumNode(context, release, true /* id3 */) };
const auto tracks{ Track::find(context.dbSession, Track::FindParameters{}.setRelease(id).setSortMethod(TrackSortMethod::Release)) };
const auto tracks{ Track::find(context.getDbSession(), Track::FindParameters{}.setRelease(id).setSortMethod(TrackSortMethod::Release)) };
for (const Track::pointer& track : tracks.results)
albumNode.addArrayChild("song", createSongNode(context, track, true /* id3 */));
@@ -500,16 +500,16 @@ namespace lms::api::subsonic
Response handleGetSongRequest(RequestContext& context)
{
// Mandatory params
TrackId id{ getMandatoryParameterAs<TrackId>(context.parameters, "id") };
TrackId id{ getMandatoryParameterAs<TrackId>(context.getParameters(), "id") };
auto transaction{ context.dbSession.createReadTransaction() };
auto transaction{ context.getDbSession().createReadTransaction() };
const Track::pointer track{ Track::find(context.dbSession, id) };
const Track::pointer track{ Track::find(context.getDbSession(), id) };
if (!track)
throw RequestedDataNotFoundError{};
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
response.addNode("song", createSongNode(context, track, context.user));
Response response{ Response::createOkResponse(context.getServerProtocolVersion()) };
response.addNode("song", createSongNode(context, track, context.getUser()));
return response;
}
@@ -517,24 +517,24 @@ namespace lms::api::subsonic
Response handleGetArtistInfo2Request(RequestContext& context)
{
// Mandatory params
ArtistId id{ getMandatoryParameterAs<ArtistId>(context.parameters, "id") };
ArtistId id{ getMandatoryParameterAs<ArtistId>(context.getParameters(), "id") };
// Optional params
std::size_t count{ getParameterAs<std::size_t>(context.parameters, "count").value_or(20) };
std::size_t count{ getParameterAs<std::size_t>(context.getParameters(), "count").value_or(20) };
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response response{ Response::createOkResponse(context.getServerProtocolVersion()) };
Response::Node& artistInfoNode{ response.createNode(Response::Node::Key{ "artistInfo2" }) };
{
auto transaction{ context.dbSession.createReadTransaction() };
auto transaction{ context.getDbSession().createReadTransaction() };
const Artist::pointer artist{ Artist::find(context.dbSession, id) };
const Artist::pointer artist{ Artist::find(context.getDbSession(), id) };
if (!artist)
throw RequestedDataNotFoundError{};
if (const std::optional<core::UUID> artistMBID{ artist->getMBID() })
{
switch (context.responseFormat)
switch (context.getResponseFormat())
{
case ResponseFormat::json:
artistInfoNode.setAttribute("musicBrainzId", artistMBID->getAsString());
@@ -545,10 +545,10 @@ namespace lms::api::subsonic
}
}
ArtistInfo::find(context.dbSession, id, Range{ .offset = 0, .size = 1 }, [&](const ArtistInfo::pointer& artistInfo) {
ArtistInfo::find(context.getDbSession(), id, Range{ .offset = 0, .size = 1 }, [&](const ArtistInfo::pointer& artistInfo) {
if (!artistInfo->getBiography().empty())
{
switch (context.responseFormat)
switch (context.getResponseFormat())
{
case ResponseFormat::json:
artistInfoNode.setAttribute("biography", artistInfo->getBiography());
@@ -564,11 +564,11 @@ namespace lms::api::subsonic
auto similarArtistsId{ core::Service<recommendation::IRecommendationService>::get()->getSimilarArtists(id, { TrackArtistLinkType::Artist, TrackArtistLinkType::ReleaseArtist }, count) };
{
auto transaction{ context.dbSession.createReadTransaction() };
auto transaction{ context.getDbSession().createReadTransaction() };
for (const ArtistId similarArtistId : similarArtistsId)
{
const Artist::pointer similarArtist{ Artist::find(context.dbSession, similarArtistId) };
const Artist::pointer similarArtist{ Artist::find(context.getDbSession(), similarArtistId) };
if (similarArtist)
artistInfoNode.addArrayChild("similarArtist", createArtistNode(context, similarArtist));
}
@@ -579,14 +579,14 @@ namespace lms::api::subsonic
Response handleGetAlbumInfo(RequestContext& context)
{
const db::DirectoryId directoryId{ getMandatoryParameterAs<db::DirectoryId>(context.parameters, "id") };
const db::DirectoryId directoryId{ getMandatoryParameterAs<db::DirectoryId>(context.getParameters(), "id") };
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response response{ Response::createOkResponse(context.getServerProtocolVersion()) };
{
auto transaction{ context.dbSession.createReadTransaction() };
auto transaction{ context.getDbSession().createReadTransaction() };
if (db::Release::pointer release{ getReleaseFromDirectory(context.dbSession, directoryId) })
if (db::Release::pointer release{ getReleaseFromDirectory(context.getDbSession(), directoryId) })
response.addNode("albumInfo", createAlbumInfoNode(context, release));
}
return response;
@@ -594,14 +594,14 @@ namespace lms::api::subsonic
Response handleGetAlbumInfo2(RequestContext& context)
{
const db::ReleaseId releaseId{ getMandatoryParameterAs<db::ReleaseId>(context.parameters, "id") };
const db::ReleaseId releaseId{ getMandatoryParameterAs<db::ReleaseId>(context.getParameters(), "id") };
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response response{ Response::createOkResponse(context.getServerProtocolVersion()) };
{
auto transaction{ context.dbSession.createReadTransaction() };
auto transaction{ context.getDbSession().createReadTransaction() };
if (db::Release::pointer release{ db::Release::find(context.dbSession, releaseId) })
if (db::Release::pointer release{ db::Release::find(context.getDbSession(), releaseId) })
response.addNode("albumInfo", createAlbumInfoNode(context, release));
}
@@ -621,29 +621,29 @@ namespace lms::api::subsonic
Response handleGetTopSongs(RequestContext& context)
{
// Mandatory params
std::string_view artistName{ getMandatoryParameterAs<std::string_view>(context.parameters, "artist") };
std::size_t count{ getParameterAs<std::size_t>(context.parameters, "count").value_or(50) };
std::string_view artistName{ getMandatoryParameterAs<std::string_view>(context.getParameters(), "artist") };
std::size_t count{ getParameterAs<std::size_t>(context.getParameters(), "count").value_or(50) };
if (count > defaultMaxCountSize)
throw ParameterValueTooHighGenericError{ "count", defaultMaxCountSize };
auto transaction{ context.dbSession.createReadTransaction() };
auto transaction{ context.getDbSession().createReadTransaction() };
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response response{ Response::createOkResponse(context.getServerProtocolVersion()) };
Response::Node& topSongs{ response.createNode("topSongs") };
const auto artists{ Artist::find(context.dbSession, artistName) };
const auto artists{ Artist::find(context.getDbSession(), artistName) };
if (artists.size() == 1)
{
scrobbling::IScrobblingService::FindParameters params;
params.setUser(context.user->getId());
params.setUser(context.getUser()->getId());
params.setRange(db::Range{ 0, count });
params.setArtist(artists.front()->getId());
const auto trackIds{ core::Service<scrobbling::IScrobblingService>::get()->getTopTracks(params) };
for (const TrackId trackId : trackIds.results)
{
if (Track::pointer track{ Track::find(context.dbSession, trackId) })
topSongs.addArrayChild("song", createSongNode(context, track, context.user));
if (Track::pointer track{ Track::find(context.getDbSession(), trackId) })
topSongs.addArrayChild("song", createSongNode(context, track, context.getUser()));
}
}
@@ -113,72 +113,72 @@ namespace lms::api::subsonic
Response handleStarRequest(RequestContext& context)
{
StarParameters params{ getStarParameters(context.parameters) };
StarParameters params{ getStarParameters(context.getParameters()) };
for (const DirectoryId id : params.directoryIds)
{
if (const ReleaseId releaseId{ getReleaseIdFromDirectory(context.dbSession, id) }; releaseId.isValid())
core::Service<feedback::IFeedbackService>::get()->star(context.user->getId(), releaseId);
if (const ReleaseId releaseId{ getReleaseIdFromDirectory(context.getDbSession(), id) }; releaseId.isValid())
core::Service<feedback::IFeedbackService>::get()->star(context.getUser()->getId(), releaseId);
}
for (const ArtistId id : params.artistIds)
core::Service<feedback::IFeedbackService>::get()->star(context.user->getId(), id);
core::Service<feedback::IFeedbackService>::get()->star(context.getUser()->getId(), id);
for (const ReleaseId id : params.releaseIds)
core::Service<feedback::IFeedbackService>::get()->star(context.user->getId(), id);
core::Service<feedback::IFeedbackService>::get()->star(context.getUser()->getId(), id);
for (const TrackId id : params.trackIds)
core::Service<feedback::IFeedbackService>::get()->star(context.user->getId(), id);
core::Service<feedback::IFeedbackService>::get()->star(context.getUser()->getId(), id);
return Response::createOkResponse(context.serverProtocolVersion);
return Response::createOkResponse(context.getServerProtocolVersion());
}
Response handleUnstarRequest(RequestContext& context)
{
const StarParameters params{ getStarParameters(context.parameters) };
const StarParameters params{ getStarParameters(context.getParameters()) };
for (const DirectoryId id : params.directoryIds)
{
if (const ReleaseId releaseId{ getReleaseIdFromDirectory(context.dbSession, id) }; releaseId.isValid())
core::Service<feedback::IFeedbackService>::get()->unstar(context.user->getId(), releaseId);
if (const ReleaseId releaseId{ getReleaseIdFromDirectory(context.getDbSession(), id) }; releaseId.isValid())
core::Service<feedback::IFeedbackService>::get()->unstar(context.getUser()->getId(), releaseId);
}
for (const ArtistId id : params.artistIds)
core::Service<feedback::IFeedbackService>::get()->unstar(context.user->getId(), id);
core::Service<feedback::IFeedbackService>::get()->unstar(context.getUser()->getId(), id);
for (const ReleaseId id : params.releaseIds)
core::Service<feedback::IFeedbackService>::get()->unstar(context.user->getId(), id);
core::Service<feedback::IFeedbackService>::get()->unstar(context.getUser()->getId(), id);
for (const TrackId id : params.trackIds)
core::Service<feedback::IFeedbackService>::get()->unstar(context.user->getId(), id);
core::Service<feedback::IFeedbackService>::get()->unstar(context.getUser()->getId(), id);
return Response::createOkResponse(context.serverProtocolVersion);
return Response::createOkResponse(context.getServerProtocolVersion());
}
Response handleSetRating(RequestContext& context)
{
const RatingParameters params{ getRatingParameters(context.parameters) };
const RatingParameters params{ getRatingParameters(context.getParameters()) };
if (const ArtistId * artistId{ std::get_if<ArtistId>(&params.id) })
core::Service<feedback::IFeedbackService>::get()->setRating(context.user->getId(), *artistId, params.rating);
core::Service<feedback::IFeedbackService>::get()->setRating(context.getUser()->getId(), *artistId, params.rating);
else if (const DirectoryId * directoryId{ std::get_if<DirectoryId>(&params.id) })
{
if (const ReleaseId releaseId{ getReleaseIdFromDirectory(context.dbSession, *directoryId) }; releaseId.isValid())
core::Service<feedback::IFeedbackService>::get()->setRating(context.user->getId(), releaseId, params.rating);
if (const ReleaseId releaseId{ getReleaseIdFromDirectory(context.getDbSession(), *directoryId) }; releaseId.isValid())
core::Service<feedback::IFeedbackService>::get()->setRating(context.getUser()->getId(), releaseId, params.rating);
}
else if (const ReleaseId * releaseId{ std::get_if<ReleaseId>(&params.id) })
core::Service<feedback::IFeedbackService>::get()->setRating(context.user->getId(), *releaseId, params.rating);
core::Service<feedback::IFeedbackService>::get()->setRating(context.getUser()->getId(), *releaseId, params.rating);
else if (const TrackId * trackId{ std::get_if<TrackId>(&params.id) })
core::Service<feedback::IFeedbackService>::get()->setRating(context.user->getId(), *trackId, params.rating);
core::Service<feedback::IFeedbackService>::get()->setRating(context.getUser()->getId(), *trackId, params.rating);
return Response::createOkResponse(context.serverProtocolVersion);
return Response::createOkResponse(context.getServerProtocolVersion());
}
Response handleScrobble(RequestContext& context)
{
const std::vector<TrackId> ids{ getMandatoryMultiParametersAs<TrackId>(context.parameters, "id") };
const std::vector<unsigned long> times{ getMultiParametersAs<unsigned long>(context.parameters, "time") };
const bool submission{ getParameterAs<bool>(context.parameters, "submission").value_or(true) };
const std::vector<TrackId> ids{ getMandatoryMultiParametersAs<TrackId>(context.getParameters(), "id") };
const std::vector<unsigned long> times{ getMultiParametersAs<unsigned long>(context.getParameters(), "time") };
const bool submission{ getParameterAs<bool>(context.getParameters(), "submission").value_or(true) };
// playing now => only one at a time
if (!submission && ids.size() > 1)
@@ -190,13 +190,13 @@ namespace lms::api::subsonic
if (!submission)
{
core::Service<scrobbling::IScrobblingService>::get()->listenStarted({ context.user->getId(), ids.front() });
core::Service<scrobbling::IScrobblingService>::get()->listenStarted({ context.getUser()->getId(), ids.front() });
}
else
{
if (times.empty())
{
core::Service<scrobbling::IScrobblingService>::get()->listenFinished({ context.user->getId(), ids.front() });
core::Service<scrobbling::IScrobblingService>::get()->listenFinished({ context.getUser()->getId(), ids.front() });
}
else
{
@@ -204,11 +204,11 @@ namespace lms::api::subsonic
{
const TrackId trackId{ ids[i] };
const unsigned long time{ times[i] };
core::Service<scrobbling::IScrobblingService>::get()->addTimedListen({ { context.user->getId(), trackId }, Wt::WDateTime::fromTime_t(static_cast<std::time_t>(time / 1000)) });
core::Service<scrobbling::IScrobblingService>::get()->addTimedListen({ { context.getUser()->getId(), trackId }, Wt::WDateTime::fromTime_t(static_cast<std::time_t>(time / 1000)) });
}
}
}
return Response::createOkResponse(context.serverProtocolVersion);
return Response::createOkResponse(context.getServerProtocolVersion());
}
} // namespace lms::api::subsonic
@@ -51,7 +51,7 @@ namespace lms::api::subsonic::Scan
Response handleGetScanStatus(RequestContext& context)
{
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response response{ Response::createOkResponse(context.getServerProtocolVersion()) };
response.addNode("scanStatus", createStatusResponseNode());
return response;
@@ -61,7 +61,7 @@ namespace lms::api::subsonic::Scan
{
core::Service<IScannerService>::get()->requestImmediateScan();
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response response{ Response::createOkResponse(context.getServerProtocolVersion()) };
response.addNode("scanStatus", createStatusResponseNode());
return response;
@@ -176,20 +176,20 @@ namespace lms::api::subsonic
StreamParameters getStreamParameters(RequestContext& context)
{
// Mandatory params
const auto trackId{ getParameterAs<db::TrackId>(context.parameters, "id") };
const auto podcastEpisodeId{ getParameterAs<db::PodcastEpisodeId>(context.parameters, "id") };
const auto trackId{ getParameterAs<db::TrackId>(context.getParameters(), "id") };
const auto podcastEpisodeId{ getParameterAs<db::PodcastEpisodeId>(context.getParameters(), "id") };
if (!trackId && !podcastEpisodeId)
throw RequiredParameterMissingError{ "id" };
const AudioFileId audioId{ trackId ? AudioFileId{ *trackId } : AudioFileId{ *podcastEpisodeId } };
// Optional params
std::size_t maxBitRate{ getParameterAs<std::size_t>(context.parameters, "maxBitRate").value_or(0) * 1000 }; // "If set to zero, no limit is imposed", given in kpbs
const std::string format{ getParameterAs<std::string>(context.parameters, "format").value_or("") };
std::size_t timeOffset{ getParameterAs<std::size_t>(context.parameters, "timeOffset").value_or(0) };
bool estimateContentLength{ getParameterAs<bool>(context.parameters, "estimateContentLength").value_or(false) };
std::size_t maxBitRate{ getParameterAs<std::size_t>(context.getParameters(), "maxBitRate").value_or(0) * 1000 }; // "If set to zero, no limit is imposed", given in kpbs
const std::string format{ getParameterAs<std::string>(context.getParameters(), "format").value_or("") };
std::size_t timeOffset{ getParameterAs<std::size_t>(context.getParameters(), "timeOffset").value_or(0) };
bool estimateContentLength{ getParameterAs<bool>(context.getParameters(), "estimateContentLength").value_or(false) };
const AudioFileInfo audioFileInfo{ getAudioFileInfo(context.dbSession, audioId) };
const AudioFileInfo audioFileInfo{ getAudioFileInfo(context.getDbSession(), audioId) };
StreamParameters parameters;
@@ -205,8 +205,8 @@ namespace lms::api::subsonic
std::optional<transcoding::OutputFormat> requestedFormat{ subsonicStreamFormatToAvOutputFormat(format) };
if (!requestedFormat)
{
if (context.user->getSubsonicEnableTranscodingByDefault())
requestedFormat = userTranscodeFormatToAvFormat(context.user->getSubsonicDefaultTranscodingOutputFormat());
if (context.getUser()->getSubsonicEnableTranscodingByDefault())
requestedFormat = userTranscodeFormatToAvFormat(context.getUser()->getSubsonicDefaultTranscodingOutputFormat());
}
if (!requestedFormat && (maxBitRate == 0 || audioFileInfo.bitrate <= maxBitRate))
@@ -231,9 +231,9 @@ namespace lms::api::subsonic
// Need to transcode here
if (!requestedFormat)
requestedFormat = userTranscodeFormatToAvFormat(context.user->getSubsonicDefaultTranscodingOutputFormat());
requestedFormat = userTranscodeFormatToAvFormat(context.getUser()->getSubsonicDefaultTranscodingOutputFormat());
if (!bitrate)
bitrate = context.user->getSubsonicDefaultTranscodingOutputBitrate();
bitrate = context.getUser()->getSubsonicDefaultTranscodingOutputBitrate();
if (maxBitRate)
bitrate = std::min<std::size_t>(bitrate, maxBitRate);
@@ -248,13 +248,13 @@ namespace lms::api::subsonic
Response handleGetLyrics(RequestContext& context)
{
std::string artistName{ getParameterAs<std::string>(context.parameters, "artist").value_or("") };
std::string titleName{ getParameterAs<std::string>(context.parameters, "title").value_or("") };
std::string artistName{ getParameterAs<std::string>(context.getParameters(), "artist").value_or("") };
std::string titleName{ getParameterAs<std::string>(context.getParameters(), "title").value_or("") };
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response response{ Response::createOkResponse(context.getServerProtocolVersion()) };
// best effort search, as this API is really limited
auto transaction{ context.dbSession.createReadTransaction() };
auto transaction{ context.getDbSession().createReadTransaction() };
db::Track::FindParameters params;
params.setName(titleName);
@@ -262,7 +262,7 @@ namespace lms::api::subsonic
params.setRange(db::Range{ .offset = 0, .size = 2 });
// Choice: we return nothing if there are too many results
const auto tracks{ db::Track::findIds(context.dbSession, params) };
const auto tracks{ db::Track::findIds(context.getDbSession(), params) };
if (tracks.results.size() == 1)
{
// Choice: we return only the first lyrics if the track has many lyrics
@@ -271,7 +271,7 @@ namespace lms::api::subsonic
lyricsParams.setSortMethod(db::TrackLyricsSortMethod::ExternalFirst);
lyricsParams.setRange(db::Range{ 0, 1 });
db::TrackLyrics::find(context.dbSession, lyricsParams, [&](const db::TrackLyrics::pointer& lyrics) {
db::TrackLyrics::find(context.getDbSession(), lyricsParams, [&](const db::TrackLyrics::pointer& lyrics) {
response.addNode("lyrics", createLyricsNode(context, lyrics));
});
}
@@ -282,14 +282,14 @@ namespace lms::api::subsonic
Response handleGetLyricsBySongId(RequestContext& context)
{
// mandatory params
db::TrackId id{ getMandatoryParameterAs<db::TrackId>(context.parameters, "id") };
db::TrackId id{ getMandatoryParameterAs<db::TrackId>(context.getParameters(), "id") };
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response response{ Response::createOkResponse(context.getServerProtocolVersion()) };
Response::Node& lyricsList{ response.createNode("lyricsList") };
lyricsList.createEmptyArrayChild("structuredLyrics");
auto transaction{ context.dbSession.createReadTransaction() };
const db::Track::pointer track{ db::Track::find(context.dbSession, id) };
auto transaction{ context.getDbSession().createReadTransaction() };
const db::Track::pointer track{ db::Track::find(context.getDbSession(), id) };
if (track)
{
db::TrackLyrics::FindParameters params;
@@ -297,7 +297,7 @@ namespace lms::api::subsonic
params.setExternal(true); // First try to only report external lyrics as they are often duplicate of embedded lyrics and support more features
bool hasExternalLyrics{};
db::TrackLyrics::find(context.dbSession, params, [&](const db::TrackLyrics::pointer& lyrics) {
db::TrackLyrics::find(context.getDbSession(), params, [&](const db::TrackLyrics::pointer& lyrics) {
lyricsList.addArrayChild("structuredLyrics", createStructuredLyricsNode(context, lyrics));
hasExternalLyrics = true;
});
@@ -305,7 +305,7 @@ namespace lms::api::subsonic
if (!hasExternalLyrics)
{
params.setExternal(false);
db::TrackLyrics::find(context.dbSession, params, [&](const db::TrackLyrics::pointer& lyrics) {
db::TrackLyrics::find(context.getDbSession(), params, [&](const db::TrackLyrics::pointer& lyrics) {
lyricsList.addArrayChild("structuredLyrics", createStructuredLyricsNode(context, lyrics));
});
}
@@ -322,13 +322,13 @@ namespace lms::api::subsonic
if (!continuation)
{
// Mandatory params
db::TrackId id{ getMandatoryParameterAs<db::TrackId>(context.parameters, "id") };
db::TrackId id{ getMandatoryParameterAs<db::TrackId>(context.getParameters(), "id") };
std::filesystem::path trackPath;
{
auto transaction{ context.dbSession.createReadTransaction() };
auto transaction{ context.getDbSession().createReadTransaction() };
auto track{ db::Track::find(context.dbSession, id) };
auto track{ db::Track::find(context.getDbSession(), id) };
if (!track)
throw RequestedDataNotFoundError{};
@@ -381,9 +381,9 @@ namespace lms::api::subsonic
void handleGetCoverArt(RequestContext& context, const Wt::Http::Request& /*request*/, Wt::Http::Response& response)
{
// Mandatory params
const CoverArtId coverArtId{ getMandatoryParameterAs<CoverArtId>(context.parameters, "id") };
const CoverArtId coverArtId{ getMandatoryParameterAs<CoverArtId>(context.getParameters(), "id") };
std::optional<std::size_t> size{ getParameterAs<std::size_t>(context.parameters, "size") };
std::optional<std::size_t> size{ getParameterAs<std::size_t>(context.getParameters(), "size") };
if (size)
*size = std::clamp(*size, std::size_t{ 32 }, std::size_t{ 2048 });
+41 -41
View File
@@ -53,9 +53,9 @@ namespace lms::api::subsonic
Response handleGetPlaylistsRequest(RequestContext& context)
{
auto transaction{ context.dbSession.createReadTransaction() };
auto transaction{ context.getDbSession().createReadTransaction() };
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response response{ Response::createOkResponse(context.getServerProtocolVersion()) };
Response::Node& playlistsNode{ response.createNode("playlists") };
auto addTrackList{ [&](const db::TrackList::pointer& trackList) {
@@ -65,10 +65,10 @@ namespace lms::api::subsonic
// First add user's playlists
{
TrackList::FindParameters params;
params.setUser(context.user->getId());
params.setUser(context.getUser()->getId());
params.setType(TrackListType::PlayList);
db::TrackList::find(context.dbSession, params, [&](const db::TrackList::pointer& trackList) {
db::TrackList::find(context.getDbSession(), params, [&](const db::TrackList::pointer& trackList) {
addTrackList(trackList);
});
}
@@ -78,10 +78,10 @@ namespace lms::api::subsonic
TrackList::FindParameters params;
params.setVisibility(TrackList::Visibility::Public);
params.setType(TrackListType::PlayList);
params.setExcludedUser(context.user->getId());
params.setExcludedUser(context.getUser()->getId());
db::TrackList::find(context.dbSession, params, [&](const db::TrackList::pointer& trackList) {
assert(trackList->getUserId() != context.user->getId());
db::TrackList::find(context.getDbSession(), params, [&](const db::TrackList::pointer& trackList) {
assert(trackList->getUserId() != context.getUser()->getId());
addTrackList(trackList);
});
}
@@ -92,23 +92,23 @@ namespace lms::api::subsonic
Response handleGetPlaylistRequest(RequestContext& context)
{
// Mandatory params
TrackListId trackListId{ getMandatoryParameterAs<TrackListId>(context.parameters, "id") };
TrackListId trackListId{ getMandatoryParameterAs<TrackListId>(context.getParameters(), "id") };
auto transaction{ context.dbSession.createReadTransaction() };
auto transaction{ context.getDbSession().createReadTransaction() };
TrackList::pointer trackList{ TrackList::find(context.dbSession, trackListId) };
TrackList::pointer trackList{ TrackList::find(context.getDbSession(), trackListId) };
if (!trackList || trackList->getType() != TrackListType::PlayList)
throw RequestedDataNotFoundError{};
if (trackList->getUserId() != context.user->getId() && trackList->getVisibility() != TrackList::Visibility::Public)
if (trackList->getUserId() != context.getUser()->getId() && trackList->getVisibility() != TrackList::Visibility::Public)
throw RequestedDataNotFoundError{};
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response response{ Response::createOkResponse(context.getServerProtocolVersion()) };
Response::Node playlistNode{ createPlaylistNode(context, trackList) };
auto entries{ trackList->getEntries() };
for (const TrackListEntry::pointer& entry : entries.results)
playlistNode.addArrayChild("entry", createSongNode(context, entry->getTrack(), context.user));
playlistNode.addArrayChild("entry", createSongNode(context, entry->getTrack(), context.getUser()));
response.addNode("playlist", std::move(playlistNode));
@@ -118,21 +118,21 @@ namespace lms::api::subsonic
Response handleCreatePlaylistRequest(RequestContext& context)
{
// Optional params
const auto id{ getParameterAs<TrackListId>(context.parameters, "playlistId") };
auto name{ getParameterAs<std::string>(context.parameters, "name") };
const auto id{ getParameterAs<TrackListId>(context.getParameters(), "playlistId") };
auto name{ getParameterAs<std::string>(context.getParameters(), "name") };
std::vector<TrackId> trackIds{ getMultiParametersAs<TrackId>(context.parameters, "songId") };
std::vector<TrackId> trackIds{ getMultiParametersAs<TrackId>(context.getParameters(), "songId") };
if (!name && !id)
throw RequiredParameterMissingError{ "name or playlistId" };
auto transaction{ context.dbSession.createWriteTransaction() };
auto transaction{ context.getDbSession().createWriteTransaction() };
TrackList::pointer trackList;
if (id)
{
trackList = TrackList::find(context.dbSession, *id);
checkTrackListModificationAccess(trackList, context.user->getId());
trackList = TrackList::find(context.getDbSession(), *id);
checkTrackListModificationAccess(trackList, context.getUser()->getId());
if (name)
trackList.modify()->setName(*name);
@@ -142,26 +142,26 @@ namespace lms::api::subsonic
}
else
{
trackList = context.dbSession.create<TrackList>(*name, TrackListType::PlayList);
trackList.modify()->setUser(context.user);
trackList = context.getDbSession().create<TrackList>(*name, TrackListType::PlayList);
trackList.modify()->setUser(context.getUser());
trackList.modify()->setVisibility(TrackList::Visibility::Private);
}
for (const TrackId trackId : trackIds)
{
Track::pointer track{ Track::find(context.dbSession, trackId) };
Track::pointer track{ Track::find(context.getDbSession(), trackId) };
if (!track)
continue;
context.dbSession.create<TrackListEntry>(track, trackList);
context.getDbSession().create<TrackListEntry>(track, trackList);
}
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response response{ Response::createOkResponse(context.getServerProtocolVersion()) };
Response::Node playlistNode{ createPlaylistNode(context, trackList) };
auto entries{ trackList->getEntries() };
for (const TrackListEntry::pointer& entry : entries.results)
playlistNode.addArrayChild("entry", createSongNode(context, entry->getTrack(), context.user));
playlistNode.addArrayChild("entry", createSongNode(context, entry->getTrack(), context.getUser()));
response.addNode("playlist", std::move(playlistNode));
@@ -171,19 +171,19 @@ namespace lms::api::subsonic
Response handleUpdatePlaylistRequest(RequestContext& context)
{
// Mandatory params
TrackListId id{ getMandatoryParameterAs<TrackListId>(context.parameters, "playlistId") };
TrackListId id{ getMandatoryParameterAs<TrackListId>(context.getParameters(), "playlistId") };
// Optional parameters
auto name{ getParameterAs<std::string>(context.parameters, "name") };
auto isPublic{ getParameterAs<bool>(context.parameters, "public") };
auto name{ getParameterAs<std::string>(context.getParameters(), "name") };
auto isPublic{ getParameterAs<bool>(context.getParameters(), "public") };
std::vector<TrackId> trackIdsToAdd{ getMultiParametersAs<TrackId>(context.parameters, "songIdToAdd") };
std::vector<std::size_t> trackPositionsToRemove{ getMultiParametersAs<std::size_t>(context.parameters, "songIndexToRemove") };
std::vector<TrackId> trackIdsToAdd{ getMultiParametersAs<TrackId>(context.getParameters(), "songIdToAdd") };
std::vector<std::size_t> trackPositionsToRemove{ getMultiParametersAs<std::size_t>(context.getParameters(), "songIndexToRemove") };
auto transaction{ context.dbSession.createWriteTransaction() };
auto transaction{ context.getDbSession().createWriteTransaction() };
TrackList::pointer trackList{ TrackList::find(context.dbSession, id) };
checkTrackListModificationAccess(trackList, context.user->getId());
TrackList::pointer trackList{ TrackList::find(context.getDbSession(), id) };
checkTrackListModificationAccess(trackList, context.getUser()->getId());
if (name)
trackList.modify()->setName(*name);
@@ -206,27 +206,27 @@ namespace lms::api::subsonic
// Add tracks
for (const TrackId trackIdToAdd : trackIdsToAdd)
{
Track::pointer track{ Track::find(context.dbSession, trackIdToAdd) };
Track::pointer track{ Track::find(context.getDbSession(), trackIdToAdd) };
if (!track)
continue;
context.dbSession.create<TrackListEntry>(track, trackList);
context.getDbSession().create<TrackListEntry>(track, trackList);
}
return Response::createOkResponse(context.serverProtocolVersion);
return Response::createOkResponse(context.getServerProtocolVersion());
}
Response handleDeletePlaylistRequest(RequestContext& context)
{
TrackListId id{ getMandatoryParameterAs<TrackListId>(context.parameters, "id") };
TrackListId id{ getMandatoryParameterAs<TrackListId>(context.getParameters(), "id") };
auto transaction{ context.dbSession.createWriteTransaction() };
auto transaction{ context.getDbSession().createWriteTransaction() };
TrackList::pointer trackList{ TrackList::find(context.dbSession, id) };
checkTrackListModificationAccess(trackList, context.user->getId());
TrackList::pointer trackList{ TrackList::find(context.getDbSession(), id) };
checkTrackListModificationAccess(trackList, context.getUser()->getId());
trackList.remove();
return Response::createOkResponse(context.serverProtocolVersion);
return Response::createOkResponse(context.getServerProtocolVersion());
}
} // namespace lms::api::subsonic
+23 -23
View File
@@ -34,14 +34,14 @@ namespace lms::api::subsonic
{
Response handleGetPodcasts(RequestContext& context)
{
const bool includeEpisodes{ getParameterAs<bool>(context.parameters, "includeEpisodes").value_or(true) };
const std::optional<db::PodcastId> podcastId{ getParameterAs<db::PodcastId>(context.parameters, "id") };
const bool includeEpisodes{ getParameterAs<bool>(context.getParameters(), "includeEpisodes").value_or(true) };
const std::optional<db::PodcastId> podcastId{ getParameterAs<db::PodcastId>(context.getParameters(), "id") };
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response response{ Response::createOkResponse(context.getServerProtocolVersion()) };
Response::Node& podcastsNode{ response.createNode("podcasts") };
podcastsNode.createEmptyArrayChild("channel");
auto transaction{ context.dbSession.createReadTransaction() };
auto transaction{ context.getDbSession().createReadTransaction() };
auto processPodcast{ [&](const db::Podcast::pointer& podcast) {
podcastsNode.addArrayChild("channel", createPodcastNode(context, podcast, includeEpisodes));
@@ -49,34 +49,34 @@ namespace lms::api::subsonic
if (podcastId.has_value())
{
db::Podcast::pointer podcast{ db::Podcast::find(context.dbSession, podcastId.value()) };
db::Podcast::pointer podcast{ db::Podcast::find(context.getDbSession(), podcastId.value()) };
if (!podcast)
throw RequestedDataNotFoundError{};
processPodcast(podcast);
}
else
db::Podcast::find(context.dbSession, processPodcast);
db::Podcast::find(context.getDbSession(), processPodcast);
return response;
}
Response handleGetNewestPodcasts(RequestContext& context)
{
std::size_t count{ getParameterAs<std::size_t>(context.parameters, "count").value_or(20) };
std::size_t count{ getParameterAs<std::size_t>(context.getParameters(), "count").value_or(20) };
count = std::min<std::size_t>(count, 100);
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response response{ Response::createOkResponse(context.getServerProtocolVersion()) };
Response::Node& newestPodcastsNode{ response.createNode("newestPodcasts") };
newestPodcastsNode.createEmptyArrayChild("episode");
{
auto transaction{ context.dbSession.createReadTransaction() };
auto transaction{ context.getDbSession().createReadTransaction() };
db::PodcastEpisode::FindParameters findParameters;
findParameters.setRange(db::Range{ .offset = 0, .size = count });
db::PodcastEpisode::find(context.dbSession, findParameters, [&](const db::PodcastEpisode::pointer& episode) {
db::PodcastEpisode::find(context.getDbSession(), findParameters, [&](const db::PodcastEpisode::pointer& episode) {
newestPodcastsNode.addArrayChild("episode", createPodcastEpisodeNode(episode));
});
}
@@ -88,13 +88,13 @@ namespace lms::api::subsonic
{
core::Service<podcast::IPodcastService>::get()->refreshPodcasts();
return Response::createOkResponse(context.serverProtocolVersion);
return Response::createOkResponse(context.getServerProtocolVersion());
}
Response handleCreatePodcastChannel(RequestContext& context)
{
// Mandatory parameters
const std::string url{ getMandatoryParameterAs<std::string>(context.parameters, "url") };
const std::string url{ getMandatoryParameterAs<std::string>(context.getParameters(), "url") };
if (url.empty() || !(url.starts_with("http://") || url.starts_with("https://")))
throw BadParameterGenericError{ "Invalid url" };
@@ -102,52 +102,52 @@ namespace lms::api::subsonic
// no effect if podcast already exists
core::Service<podcast::IPodcastService>::get()->addPodcast(url);
return Response::createOkResponse(context.serverProtocolVersion);
return Response::createOkResponse(context.getServerProtocolVersion());
}
Response handleDeletePodcastChannel(RequestContext& context)
{
// Mandatory parameters
const db::PodcastId podcastId{ getMandatoryParameterAs<db::PodcastId>(context.parameters, "id") };
const db::PodcastId podcastId{ getMandatoryParameterAs<db::PodcastId>(context.getParameters(), "id") };
if (!core::Service<podcast::IPodcastService>::get()->removePodcast(podcastId))
throw RequestedDataNotFoundError{};
return Response::createOkResponse(context.serverProtocolVersion);
return Response::createOkResponse(context.getServerProtocolVersion());
}
Response handleDeletePodcastEpisode(RequestContext& context)
{
// Mandatory parameters
const db::PodcastEpisodeId episodeId{ getMandatoryParameterAs<db::PodcastEpisodeId>(context.parameters, "id") };
const db::PodcastEpisodeId episodeId{ getMandatoryParameterAs<db::PodcastEpisodeId>(context.getParameters(), "id") };
if (!core::Service<podcast::IPodcastService>::get()->deletePodcastEpisode(episodeId))
throw RequestedDataNotFoundError{};
return Response::createOkResponse(context.serverProtocolVersion);
return Response::createOkResponse(context.getServerProtocolVersion());
}
Response handleDownloadPodcastEpisode(RequestContext& context)
{
// Mandatory parameters
const db::PodcastEpisodeId episodeId{ getMandatoryParameterAs<db::PodcastEpisodeId>(context.parameters, "id") };
const db::PodcastEpisodeId episodeId{ getMandatoryParameterAs<db::PodcastEpisodeId>(context.getParameters(), "id") };
if (!core::Service<podcast::IPodcastService>::get()->downloadPodcastEpisode(episodeId))
throw RequestedDataNotFoundError{};
return Response::createOkResponse(context.serverProtocolVersion);
return Response::createOkResponse(context.getServerProtocolVersion());
}
Response handleGetPodcastEpisode(RequestContext& context)
{
// Mandatory parameters
const db::PodcastEpisodeId episodeId{ getMandatoryParameterAs<db::PodcastEpisodeId>(context.parameters, "id") };
const db::PodcastEpisodeId episodeId{ getMandatoryParameterAs<db::PodcastEpisodeId>(context.getParameters(), "id") };
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response response{ Response::createOkResponse(context.getServerProtocolVersion()) };
auto transaction{ context.dbSession.createReadTransaction() };
auto transaction{ context.getDbSession().createReadTransaction() };
const db::PodcastEpisode::pointer episode{ db::PodcastEpisode::find(context.dbSession, episodeId) };
const db::PodcastEpisode::pointer episode{ db::PodcastEpisode::find(context.getDbSession(), episodeId) };
if (!episode)
throw RequestedDataNotFoundError{};
+29 -29
View File
@@ -118,14 +118,14 @@ namespace lms::api::subsonic
{
// For now, no need to optimize all this
// Find all the directories that match the name and that do not contain any track (considered by the legacy API as artists)
const std::size_t artistCount{ getParameterAs<std::size_t>(context.parameters, "artistCount").value_or(20) };
const std::size_t artistCount{ getParameterAs<std::size_t>(context.getParameters(), "artistCount").value_or(20) };
if (artistCount == 0)
return;
if (artistCount > defaultMaxCountSize)
throw ParameterValueTooHighGenericError{ "artistCount", defaultMaxCountSize };
const std::size_t artistOffset{ getParameterAs<std::size_t>(context.parameters, "artistOffset").value_or(0) };
const std::size_t artistOffset{ getParameterAs<std::size_t>(context.getParameters(), "artistOffset").value_or(0) };
Directory::FindParameters params;
params.setKeywords(keywords);
@@ -133,7 +133,7 @@ namespace lms::api::subsonic
params.setWithNoTrack(true);
params.setMediaLibrary(mediaLibrary);
Directory::find(context.dbSession, params, [&](const Directory::pointer& directory) {
Directory::find(context.getDbSession(), params, [&](const Directory::pointer& directory) {
Response::Node childNode;
childNode.setAttribute("id", idToString(directory->getId()));
childNode.setAttribute("name", directory->getName());
@@ -147,14 +147,14 @@ namespace lms::api::subsonic
{
static ScanTracker<ArtistId> currentScansInProgress;
const std::size_t artistCount{ getParameterAs<std::size_t>(context.parameters, "artistCount").value_or(20) };
const std::size_t artistCount{ getParameterAs<std::size_t>(context.getParameters(), "artistCount").value_or(20) };
if (artistCount == 0)
return;
if (artistCount > defaultMaxCountSize)
throw ParameterValueTooHighGenericError{ "artistCount", defaultMaxCountSize };
const std::size_t artistOffset{ getParameterAs<std::size_t>(context.parameters, "artistOffset").value_or(0) };
const std::size_t artistOffset{ getParameterAs<std::size_t>(context.getParameters(), "artistOffset").value_or(0) };
ArtistId lastRetrievedId;
auto findArtists{ [&] {
@@ -164,7 +164,7 @@ namespace lms::api::subsonic
params.setRange(Range{ artistOffset, artistCount });
params.setSortMethod(ArtistSortMethod::Id); // must be consistent with both methods
Artist::find(context.dbSession, params, [&](const Artist::pointer& artist) {
Artist::find(context.getDbSession(), params, [&](const Artist::pointer& artist) {
searchResultNode.addArrayChild("artist", createArtistNode(context, artist));
lastRetrievedId = artist->getId();
});
@@ -177,9 +177,9 @@ namespace lms::api::subsonic
else
{
ScanTracker<ArtistId>::ScanInfo scanInfo{
.clientAddress = context.clientIpAddr,
.clientName = context.clientInfo.name,
.user = context.user->getId(),
.clientAddress = context.getClientIpAddr(),
.clientName = std::string{ context.getClientName() },
.user = context.getUser()->getId(),
.library = mediaLibrary,
.offset = artistOffset
};
@@ -187,7 +187,7 @@ namespace lms::api::subsonic
if (ArtistId cachedLastRetrievedId{ currentScansInProgress.extractLastRetrievedObjectId(scanInfo) }; cachedLastRetrievedId.isValid())
{
Artist::find(
context.dbSession, cachedLastRetrievedId, artistCount, [&](const Artist::pointer& artist) {
context.getDbSession(), cachedLastRetrievedId, artistCount, [&](const Artist::pointer& artist) {
searchResultNode.addArrayChild("artist", createArtistNode(context, artist));
},
mediaLibrary);
@@ -210,14 +210,14 @@ namespace lms::api::subsonic
{
static ScanTracker<ReleaseId> currentScansInProgress;
const std::size_t albumCount{ getParameterAs<std::size_t>(context.parameters, "albumCount").value_or(20) };
const std::size_t albumCount{ getParameterAs<std::size_t>(context.getParameters(), "albumCount").value_or(20) };
if (albumCount == 0)
return;
if (albumCount > defaultMaxCountSize)
throw ParameterValueTooHighGenericError{ "albumCount", defaultMaxCountSize };
const std::size_t albumOffset{ getParameterAs<std::size_t>(context.parameters, "albumOffset").value_or(0) };
const std::size_t albumOffset{ getParameterAs<std::size_t>(context.getParameters(), "albumOffset").value_or(0) };
ReleaseId lastRetrievedId;
@@ -228,7 +228,7 @@ namespace lms::api::subsonic
params.filters.setMediaLibrary(mediaLibrary);
params.setSortMethod(ReleaseSortMethod::Id); // must be consistent with both methods
Release::find(context.dbSession, params, [&](const Release::pointer& release) {
Release::find(context.getDbSession(), params, [&](const Release::pointer& release) {
searchResultNode.addArrayChild("album", createAlbumNode(context, release, id3));
lastRetrievedId = release->getId();
});
@@ -241,9 +241,9 @@ namespace lms::api::subsonic
else
{
ScanTracker<ReleaseId>::ScanInfo scanInfo{
.clientAddress = context.clientIpAddr,
.clientName = context.clientInfo.name,
.user = context.user->getId(),
.clientAddress = context.getClientIpAddr(),
.clientName = std::string{ context.getClientName() },
.user = context.getUser()->getId(),
.library = mediaLibrary,
.offset = albumOffset
};
@@ -251,7 +251,7 @@ namespace lms::api::subsonic
if (ReleaseId cachedLastRetrievedId{ currentScansInProgress.extractLastRetrievedObjectId(scanInfo) }; cachedLastRetrievedId.isValid())
{
Release::find(
context.dbSession, cachedLastRetrievedId, albumCount, [&](const Release::pointer& release) {
context.getDbSession(), cachedLastRetrievedId, albumCount, [&](const Release::pointer& release) {
searchResultNode.addArrayChild("album", createAlbumNode(context, release, id3));
},
mediaLibrary);
@@ -274,14 +274,14 @@ namespace lms::api::subsonic
{
static ScanTracker<TrackId> currentScansInProgress;
const std::size_t songCount{ getParameterAs<std::size_t>(context.parameters, "songCount").value_or(20) };
const std::size_t songCount{ getParameterAs<std::size_t>(context.getParameters(), "songCount").value_or(20) };
if (songCount == 0)
return;
if (songCount > defaultMaxCountSize)
throw ParameterValueTooHighGenericError{ "songCount", defaultMaxCountSize };
const std::size_t songOffset{ getParameterAs<std::size_t>(context.parameters, "songOffset").value_or(0) };
const std::size_t songOffset{ getParameterAs<std::size_t>(context.getParameters(), "songOffset").value_or(0) };
TrackId lastRetrievedId;
@@ -292,7 +292,7 @@ namespace lms::api::subsonic
params.filters.setMediaLibrary(mediaLibrary);
params.setSortMethod(TrackSortMethod::Id); // must be consistent with both methods
Track::find(context.dbSession, params, [&](const Track::pointer& track) {
Track::find(context.getDbSession(), params, [&](const Track::pointer& track) {
searchResultNode.addArrayChild("song", createSongNode(context, track, id3));
lastRetrievedId = track->getId();
});
@@ -305,9 +305,9 @@ namespace lms::api::subsonic
else
{
ScanTracker<TrackId>::ScanInfo scanInfo{
.clientAddress = context.clientIpAddr,
.clientName = context.clientInfo.name,
.user = context.user->getId(),
.clientAddress = context.getClientIpAddr(),
.clientName = std::string{ context.getClientName() },
.user = context.getUser()->getId(),
.library = mediaLibrary,
.offset = songOffset
};
@@ -315,7 +315,7 @@ namespace lms::api::subsonic
if (TrackId cachedLastRetrievedId{ currentScansInProgress.extractLastRetrievedObjectId(scanInfo) }; cachedLastRetrievedId.isValid())
{
Track::find(
context.dbSession, cachedLastRetrievedId, songCount, [&](const Track::pointer& track) {
context.getDbSession(), cachedLastRetrievedId, songCount, [&](const Track::pointer& track) {
searchResultNode.addArrayChild("song", createSongNode(context, track, id3));
},
mediaLibrary);
@@ -337,24 +337,24 @@ namespace lms::api::subsonic
Response handleSearchRequestCommon(RequestContext& context, bool id3)
{
// Mandatory params
const std::string queryString{ getMandatoryParameterAs<std::string>(context.parameters, "query") };
const std::string queryString{ getMandatoryParameterAs<std::string>(context.getParameters(), "query") };
std::string_view query{ queryString };
// Optional params
const MediaLibraryId mediaLibrary{ getParameterAs<MediaLibraryId>(context.parameters, "musicFolderId").value_or(MediaLibraryId{}) };
const MediaLibraryId mediaLibrary{ getParameterAs<MediaLibraryId>(context.getParameters(), "musicFolderId").value_or(MediaLibraryId{}) };
// Symfonium adds extra ""
if (context.clientInfo.name == "Symfonium")
if (context.getClientName() == "Symfonium")
query = core::stringUtils::stringTrim(query, "\"");
std::vector<std::string_view> keywords;
if (!query.empty())
keywords = core::stringUtils::splitString(query, ' ');
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response response{ Response::createOkResponse(context.getServerProtocolVersion()) };
Response::Node& searchResultNode{ response.createNode(id3 ? "searchResult3" : "searchResult2") };
auto transaction{ context.dbSession.createReadTransaction() };
auto transaction{ context.getDbSession().createReadTransaction() };
if (id3)
findRequestedArtists(context, keywords, mediaLibrary, searchResultNode);
+3 -3
View File
@@ -4,12 +4,12 @@ namespace lms::api::subsonic
{
Response handlePingRequest(RequestContext& context)
{
return Response::createOkResponse(context.serverProtocolVersion);
return Response::createOkResponse(context.getServerProtocolVersion());
}
Response handleGetLicenseRequest(RequestContext& context)
{
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response response{ Response::createOkResponse(context.getServerProtocolVersion()) };
Response::Node& licenseNode{ response.createNode("license") };
licenseNode.setAttribute("licenseExpires", "2035-09-03T14:46:43");
@@ -21,7 +21,7 @@ namespace lms::api::subsonic
Response handleGetOpenSubsonicExtensions(RequestContext& context)
{
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response response{ Response::createOkResponse(context.getServerProtocolVersion()) };
{
Response::Node& transcodeOffsetNode{ response.createArrayNode("openSubsonicExtensions") };
@@ -16,24 +16,24 @@ namespace lms::api::subsonic
{
void checkUserIsMySelfOrAdmin(RequestContext& context, const std::string& username)
{
if (context.user->getLoginName() != username && !context.user->isAdmin())
if (context.getUser()->getLoginName() != username && !context.getUser()->isAdmin())
throw UserNotAuthorizedError{};
}
} // namespace
Response handleGetUserRequest(RequestContext& context)
{
std::string username{ getMandatoryParameterAs<std::string>(context.parameters, "username") };
std::string username{ getMandatoryParameterAs<std::string>(context.getParameters(), "username") };
auto transaction{ context.dbSession.createReadTransaction() };
auto transaction{ context.getDbSession().createReadTransaction() };
checkUserIsMySelfOrAdmin(context, username);
const User::pointer user{ User::find(context.dbSession, username) };
const User::pointer user{ User::find(context.getDbSession(), username) };
if (!user)
throw RequestedDataNotFoundError{};
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response response{ Response::createOkResponse(context.getServerProtocolVersion()) };
response.addNode("user", createUserNode(context, user));
return response;
@@ -41,11 +41,11 @@ namespace lms::api::subsonic
Response handleGetUsersRequest(RequestContext& context)
{
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response response{ Response::createOkResponse(context.getServerProtocolVersion()) };
Response::Node& usersNode{ response.createNode("users") };
auto transaction{ context.dbSession.createReadTransaction() };
User::find(context.dbSession, User::FindParameters{}, [&](const User::pointer& user) {
auto transaction{ context.getDbSession().createReadTransaction() };
User::find(context.getDbSession(), User::FindParameters{}, [&](const User::pointer& user) {
usersNode.addArrayChild("user", createUserNode(context, user));
});