Cache user data in subsonic request (to simplify endpoints)

This commit is contained in:
emeric
2024-04-27 16:48:19 +02:00
parent fcb5b11f81
commit edd407a6c9
12 changed files with 87 additions and 171 deletions
@@ -37,7 +37,6 @@ namespace lms::db
namespace lms::auth namespace lms::auth
{ {
class IAuthTokenService; class IAuthTokenService;
class IPasswordService class IPasswordService
+3 -2
View File
@@ -23,13 +23,14 @@
#include <Wt/Http/Request.h> #include <Wt/Http/Request.h>
#include "database/UserId.hpp" #include "database/Object.hpp"
#include "ClientInfo.hpp" #include "ClientInfo.hpp"
#include "ProtocolVersion.hpp" #include "ProtocolVersion.hpp"
namespace lms::db namespace lms::db
{ {
class Session; class Session;
class User;
} }
namespace lms::api::subsonic namespace lms::api::subsonic
@@ -38,7 +39,7 @@ namespace lms::api::subsonic
{ {
const Wt::Http::ParameterMap& parameters; const Wt::Http::ParameterMap& parameters;
db::Session& dbSession; db::Session& dbSession;
db::UserId userId; const db::ObjectPtr<db::User> user;
ClientInfo clientInfo; ClientInfo clientInfo;
ProtocolVersion serverProtocolVersion; ProtocolVersion serverProtocolVersion;
bool enableOpenSubsonic{ true }; bool enableOpenSubsonic{ true };
+12 -8
View File
@@ -135,13 +135,7 @@ namespace lms::api::subsonic
void checkUserTypeIsAllowed(RequestContext& context, core::EnumSet<db::UserType> allowedUserTypes) void checkUserTypeIsAllowed(RequestContext& context, core::EnumSet<db::UserType> allowedUserTypes)
{ {
auto transaction{ context.dbSession.createReadTransaction() }; if (!allowedUserTypes.contains(context.user->getType()))
db::User::pointer currentUser{ db::User::find(context.dbSession, context.userId) };
if (!currentUser)
throw RequestedDataNotFoundError{};
if (!allowedUserTypes.contains(currentUser->getType()))
throw UserNotAuthorizedError{}; throw UserNotAuthorizedError{};
} }
@@ -421,7 +415,17 @@ namespace lms::api::subsonic
bool enableOpenSubsonic{ _openSubsonicDisabledClients.find(clientInfo.name) == std::cend(_openSubsonicDisabledClients) }; bool enableOpenSubsonic{ _openSubsonicDisabledClients.find(clientInfo.name) == std::cend(_openSubsonicDisabledClients) };
bool enableDefaultCover{ _defaultCoverClients.find(clientInfo.name) != std::cend(_openSubsonicDisabledClients) }; bool enableDefaultCover{ _defaultCoverClients.find(clientInfo.name) != std::cend(_openSubsonicDisabledClients) };
return { parameters, _db.getTLSSession(), userId, clientInfo, getServerProtocolVersion(clientInfo.name), enableOpenSubsonic, enableDefaultCover }; db::User::pointer user;
{
db::Session& session{ _db.getTLSSession() };
auto transaction{ session.createReadTransaction() };
user = db::User::find(session, userId);
if (!user)
throw UserNotAuthorizedError{};
}
return { parameters, _db.getTLSSession(), user, clientInfo, getServerProtocolVersion(clientInfo.name), enableOpenSubsonic, enableDefaultCover };
} }
db::UserId SubsonicResource::authenticateUser(const Wt::Http::Request& request, const ClientInfo& clientInfo) db::UserId SubsonicResource::authenticateUser(const Wt::Http::Request& request, const ClientInfo& clientInfo)
@@ -26,6 +26,7 @@
#include <Wt/Http/Response.h> #include <Wt/Http/Response.h>
#include "database/Types.hpp" #include "database/Types.hpp"
#include "database/UserId.hpp"
#include "ClientInfo.hpp" #include "ClientInfo.hpp"
#include "RequestContext.hpp" #include "RequestContext.hpp"
@@ -60,10 +60,6 @@ namespace lms::api::subsonic
auto transaction{ context.dbSession.createReadTransaction() }; auto transaction{ context.dbSession.createReadTransaction() };
User::pointer user{ User::find(context.dbSession, context.userId) };
if (!user)
throw UserNotAuthorizedError{};
if (type == "alphabeticalByName") if (type == "alphabeticalByName")
{ {
Release::FindParameters params; Release::FindParameters params;
@@ -117,7 +113,7 @@ namespace lms::api::subsonic
else if (type == "frequent") else if (type == "frequent")
{ {
scrobbling::IScrobblingService::FindParameters params; scrobbling::IScrobblingService::FindParameters params;
params.setUser(context.userId); params.setUser(context.user->getId());
params.setRange(range); params.setRange(range);
params.setMediaLibrary(mediaLibraryId); params.setMediaLibrary(mediaLibraryId);
@@ -146,7 +142,7 @@ namespace lms::api::subsonic
else if (type == "recent") else if (type == "recent")
{ {
scrobbling::IScrobblingService::FindParameters params; scrobbling::IScrobblingService::FindParameters params;
params.setUser(context.userId); params.setUser(context.user->getId());
params.setRange(range); params.setRange(range);
params.setMediaLibrary(mediaLibraryId); params.setMediaLibrary(mediaLibraryId);
@@ -155,7 +151,7 @@ namespace lms::api::subsonic
else if (type == "starred") else if (type == "starred")
{ {
feedback::IFeedbackService::FindParameters params; feedback::IFeedbackService::FindParameters params;
params.setUser(context.userId); params.setUser(context.user->getId());
params.setRange(range); params.setRange(range);
params.setMediaLibrary(mediaLibraryId); params.setMediaLibrary(mediaLibraryId);
@@ -172,7 +168,7 @@ namespace lms::api::subsonic
for (const ReleaseId releaseId : releases.results) for (const ReleaseId releaseId : releases.results)
{ {
const Release::pointer release{ Release::find(context.dbSession, releaseId) }; const Release::pointer release{ Release::find(context.dbSession, releaseId) };
albumListNode.addArrayChild("album", createAlbumNode(context, release, user, id3)); albumListNode.addArrayChild("album", createAlbumNode(context, release, context.user, id3));
} }
return response; return response;
@@ -185,10 +181,6 @@ namespace lms::api::subsonic
auto transaction{ context.dbSession.createReadTransaction() }; auto transaction{ context.dbSession.createReadTransaction() };
User::pointer user{ User::find(context.dbSession, context.userId) };
if (!user)
throw UserNotAuthorizedError{};
Response response{ Response::createOkResponse(context.serverProtocolVersion) }; Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response::Node& starredNode{ response.createNode(id3 ? Response::Node::Key{ "starred2" } : Response::Node::Key{ "starred" }) }; Response::Node& starredNode{ response.createNode(id3 ? Response::Node::Key{ "starred2" } : Response::Node::Key{ "starred" }) };
@@ -196,29 +188,29 @@ namespace lms::api::subsonic
{ {
feedback::IFeedbackService::ArtistFindParameters artistFindParams; feedback::IFeedbackService::ArtistFindParameters artistFindParams;
artistFindParams.setUser(context.userId); artistFindParams.setUser(context.user->getId());
artistFindParams.setSortMethod(ArtistSortMethod::SortName); artistFindParams.setSortMethod(ArtistSortMethod::SortName);
for (const ArtistId artistId : feedbackService.findStarredArtists(artistFindParams).results) for (const ArtistId artistId : feedbackService.findStarredArtists(artistFindParams).results)
{ {
if (auto artist{ Artist::find(context.dbSession, artistId) }) if (auto artist{ Artist::find(context.dbSession, artistId) })
starredNode.addArrayChild("artist", createArtistNode(context, artist, user, id3)); starredNode.addArrayChild("artist", createArtistNode(context, artist, context.user, id3));
} }
} }
feedback::IFeedbackService::FindParameters findParameters; feedback::IFeedbackService::FindParameters findParameters;
findParameters.setUser(context.userId); findParameters.setUser(context.user->getId());
findParameters.setMediaLibrary(mediaLibrary); findParameters.setMediaLibrary(mediaLibrary);
for (const ReleaseId releaseId : feedbackService.findStarredReleases(findParameters).results) for (const ReleaseId releaseId : feedbackService.findStarredReleases(findParameters).results)
{ {
if (auto release{ Release::find(context.dbSession, releaseId) }) if (auto release{ Release::find(context.dbSession, releaseId) })
starredNode.addArrayChild("album", createAlbumNode(context, release, user, id3)); starredNode.addArrayChild("album", createAlbumNode(context, release, context.user, id3));
} }
for (const TrackId trackId : feedbackService.findStarredTracks(findParameters).results) for (const TrackId trackId : feedbackService.findStarredTracks(findParameters).results)
{ {
if (auto track{ Track::find(context.dbSession, trackId) }) if (auto track{ Track::find(context.dbSession, trackId) })
starredNode.addArrayChild("song", createSongNode(context, track, user)); starredNode.addArrayChild("song", createSongNode(context, track, context.user));
} }
return response; return response;
@@ -243,15 +235,11 @@ namespace lms::api::subsonic
if (size > defaultMaxCountSize) if (size > defaultMaxCountSize)
throw ParameterValueTooHighGenericError{ "size", defaultMaxCountSize }; throw ParameterValueTooHighGenericError{ "size", defaultMaxCountSize };
auto transaction{ context.dbSession.createReadTransaction() };
User::pointer user{ User::find(context.dbSession, context.userId) };
if (!user)
throw UserNotAuthorizedError{};
Response response{ Response::createOkResponse(context.serverProtocolVersion) }; Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response::Node& randomSongsNode{ response.createNode("randomSongs") }; Response::Node& randomSongsNode{ response.createNode("randomSongs") };
auto transaction{ context.dbSession.createReadTransaction() };
Track::FindParameters params; Track::FindParameters params;
params.setSortMethod(TrackSortMethod::Random); params.setSortMethod(TrackSortMethod::Random);
params.setRange(Range{ 0, size }); params.setRange(Range{ 0, size });
@@ -259,7 +247,7 @@ namespace lms::api::subsonic
Track::find(context.dbSession, params, [&](const Track::pointer& track) Track::find(context.dbSession, params, [&](const Track::pointer& track)
{ {
randomSongsNode.addArrayChild("song", createSongNode(context, track, user)); randomSongsNode.addArrayChild("song", createSongNode(context, track, context.user));
}); });
return response; return response;
@@ -288,10 +276,6 @@ namespace lms::api::subsonic
if (!cluster) if (!cluster)
throw RequestedDataNotFoundError{}; throw RequestedDataNotFoundError{};
User::pointer user{ User::find(context.dbSession, context.userId) };
if (!user)
throw UserNotAuthorizedError{};
Response response{ Response::createOkResponse(context.serverProtocolVersion) }; Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response::Node& songsByGenreNode{ response.createNode("songsByGenre") }; Response::Node& songsByGenreNode{ response.createNode("songsByGenre") };
@@ -302,7 +286,7 @@ namespace lms::api::subsonic
Track::find(context.dbSession, params, [&](const Track::pointer& track) Track::find(context.dbSession, params, [&](const Track::pointer& track)
{ {
songsByGenreNode.addArrayChild("song", createSongNode(context, track, user)); songsByGenreNode.addArrayChild("song", createSongNode(context, track, context.user));
}); });
return response; return response;
@@ -36,11 +36,7 @@ namespace lms::api::subsonic
{ {
auto transaction{ context.dbSession.createReadTransaction() }; auto transaction{ context.dbSession.createReadTransaction() };
User::pointer user{ User::find(context.dbSession, context.userId) }; const auto bookmarkIds{ TrackBookmark::find(context.dbSession, context.user->getId()) };
if (!user)
throw UserNotAuthorizedError{};
const auto bookmarkIds{ TrackBookmark::find(context.dbSession, user->getId()) };
Response response{ Response::createOkResponse(context.serverProtocolVersion) }; Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response::Node& bookmarksNode{ response.createNode("bookmarks") }; Response::Node& bookmarksNode{ response.createNode("bookmarks") };
@@ -49,7 +45,7 @@ namespace lms::api::subsonic
{ {
const TrackBookmark::pointer bookmark{ TrackBookmark::find(context.dbSession, bookmarkId) }; const TrackBookmark::pointer bookmark{ TrackBookmark::find(context.dbSession, bookmarkId) };
Response::Node bookmarkNode{ createBookmarkNode(bookmark) }; Response::Node bookmarkNode{ createBookmarkNode(bookmark) };
bookmarkNode.addChild("entry", createSongNode(context, bookmark->getTrack(), user)); bookmarkNode.addChild("entry", createSongNode(context, bookmark->getTrack(), context.user));
bookmarksNode.addArrayChild("bookmark", std::move(bookmarkNode)); bookmarksNode.addArrayChild("bookmark", std::move(bookmarkNode));
} }
@@ -65,18 +61,14 @@ namespace lms::api::subsonic
auto transaction{ context.dbSession.createWriteTransaction() }; auto transaction{ context.dbSession.createWriteTransaction() };
const User::pointer user{ User::find(context.dbSession, context.userId) };
if (!user)
throw UserNotAuthorizedError{};
const Track::pointer track{ Track::find(context.dbSession, trackId) }; const Track::pointer track{ Track::find(context.dbSession, trackId) };
if (!track) if (!track)
throw RequestedDataNotFoundError{}; throw RequestedDataNotFoundError{};
// Replace any existing bookmark // Replace any existing bookmark
auto bookmark{ TrackBookmark::find(context.dbSession, user->getId(), trackId) }; auto bookmark{ TrackBookmark::find(context.dbSession, context.user->getId(), trackId) };
if (!bookmark) if (!bookmark)
bookmark = context.dbSession.create<TrackBookmark>(user, track); bookmark = context.dbSession.create<TrackBookmark>(context.user, track);
bookmark.modify()->setOffset(std::chrono::milliseconds{ position }); bookmark.modify()->setOffset(std::chrono::milliseconds{ position });
if (comment) if (comment)
@@ -92,7 +84,7 @@ namespace lms::api::subsonic
auto transaction{ context.dbSession.createWriteTransaction() }; auto transaction{ context.dbSession.createWriteTransaction() };
auto bookmark{ TrackBookmark::find(context.dbSession, context.userId, trackId) }; auto bookmark{ TrackBookmark::find(context.dbSession, context.user->getId(), trackId) };
if (!bookmark) if (!bookmark)
throw RequestedDataNotFoundError{}; throw RequestedDataNotFoundError{};
+14 -49
View File
@@ -75,15 +75,11 @@ namespace lms::api::subsonic
{ {
auto transaction{ context.dbSession.createReadTransaction() }; auto transaction{ context.dbSession.createReadTransaction() };
User::pointer user{ User::find(context.dbSession, context.userId) };
if (!user)
throw UserNotAuthorizedError{};
for (const ArtistId similarArtistId : similarArtistsId) for (const ArtistId similarArtistId : similarArtistsId)
{ {
const Artist::pointer similarArtist{ Artist::find(context.dbSession, similarArtistId) }; const Artist::pointer similarArtist{ Artist::find(context.dbSession, similarArtistId) };
if (similarArtist) if (similarArtist)
artistInfoNode.addArrayChild("similarArtist", createArtistNode(context, similarArtist, user, id3)); artistInfoNode.addArrayChild("similarArtist", createArtistNode(context, similarArtist, context.user, id3));
} }
} }
@@ -105,12 +101,8 @@ namespace lms::api::subsonic
{ {
auto transaction{ context.dbSession.createReadTransaction() }; auto transaction{ context.dbSession.createReadTransaction() };
User::pointer user{ User::find(context.dbSession, context.userId) };
if (!user)
throw UserNotAuthorizedError{};
parameters.setSortMethod(ArtistSortMethod::SortName); parameters.setSortMethod(ArtistSortMethod::SortName);
switch (user->getSubsonicArtistListMode()) switch (context.user->getSubsonicArtistListMode())
{ {
case SubsonicArtistListMode::AllArtists: case SubsonicArtistListMode::AllArtists:
break; break;
@@ -165,12 +157,9 @@ namespace lms::api::subsonic
for (const ArtistId artistId : artistIds) for (const ArtistId artistId : artistIds)
{ {
auto transaction{ context.dbSession.createReadTransaction() }; auto transaction{ context.dbSession.createReadTransaction() };
User::pointer user{ User::find(context.dbSession, context.userId) };
if (!user)
throw UserNotAuthorizedError{};
if (const Artist::pointer artist{ Artist::find(context.dbSession, artistId) }) if (const Artist::pointer artist{ Artist::find(context.dbSession, artistId) })
indexNode.addArrayChild("artist", createArtistNode(context, artist, user, id3)); indexNode.addArrayChild("artist", createArtistNode(context, artist, context.user, id3));
} }
} }
@@ -265,16 +254,12 @@ namespace lms::api::subsonic
auto transaction{ context.dbSession.createReadTransaction() }; auto transaction{ context.dbSession.createReadTransaction() };
User::pointer user{ User::find(context.dbSession, context.userId) };
if (!user)
throw UserNotAuthorizedError{};
Response response{ Response::createOkResponse(context.serverProtocolVersion) }; Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response::Node& similarSongsNode{ response.createNode(id3 ? Response::Node::Key{ "similarSongs2" } : Response::Node::Key{ "similarSongs" }) }; Response::Node& similarSongsNode{ response.createNode(id3 ? Response::Node::Key{ "similarSongs2" } : Response::Node::Key{ "similarSongs" }) };
for (const TrackId trackId : tracks) for (const TrackId trackId : tracks)
{ {
const Track::pointer track{ Track::find(context.dbSession, trackId) }; const Track::pointer track{ Track::find(context.dbSession, trackId) };
similarSongsNode.addArrayChild("song", createSongNode(context, track, user)); similarSongsNode.addArrayChild("song", createSongNode(context, track, context.user));
} }
return response; return response;
@@ -319,10 +304,6 @@ namespace lms::api::subsonic
auto transaction{ context.dbSession.createReadTransaction() }; auto transaction{ context.dbSession.createReadTransaction() };
User::pointer user{ User::find(context.dbSession, context.userId) };
if (!user)
throw UserNotAuthorizedError{};
if (root) if (root)
{ {
directoryNode.setAttribute("id", idToString(RootId{})); directoryNode.setAttribute("id", idToString(RootId{}));
@@ -331,7 +312,7 @@ namespace lms::api::subsonic
// TODO: this does not scale when a lot of artists are present // TODO: this does not scale when a lot of artists are present
Artist::find(context.dbSession, Artist::FindParameters{}.setSortMethod(ArtistSortMethod::SortName), [&](const Artist::pointer& artist) Artist::find(context.dbSession, Artist::FindParameters{}.setSortMethod(ArtistSortMethod::SortName), [&](const Artist::pointer& artist)
{ {
directoryNode.addArrayChild("child", createArtistNode(context, artist, user, false /* no id3 */)); directoryNode.addArrayChild("child", createArtistNode(context, artist, context.user, false /* no id3 */));
}); });
} }
else if (artistId) else if (artistId)
@@ -346,7 +327,7 @@ namespace lms::api::subsonic
Release::find(context.dbSession, Release::FindParameters{}.setArtist(*artistId), [&](const Release::pointer& release) Release::find(context.dbSession, Release::FindParameters{}.setArtist(*artistId), [&](const Release::pointer& release)
{ {
directoryNode.addArrayChild("child", createAlbumNode(context, release, user, false /* no id3 */)); directoryNode.addArrayChild("child", createAlbumNode(context, release, context.user, false /* no id3 */));
}); });
} }
else if (releaseId) else if (releaseId)
@@ -361,7 +342,7 @@ namespace lms::api::subsonic
Track::find(context.dbSession, Track::FindParameters{}.setRelease(*releaseId).setSortMethod(TrackSortMethod::Release), [&](const Track::pointer& track) Track::find(context.dbSession, Track::FindParameters{}.setRelease(*releaseId).setSortMethod(TrackSortMethod::Release), [&](const Track::pointer& track)
{ {
directoryNode.addArrayChild("child", createSongNode(context, track, user)); directoryNode.addArrayChild("child", createSongNode(context, track, context.user));
}); });
} }
else else
@@ -406,16 +387,12 @@ namespace lms::api::subsonic
if (!artist) if (!artist)
throw RequestedDataNotFoundError{}; throw RequestedDataNotFoundError{};
User::pointer user{ User::find(context.dbSession, context.userId) };
if (!user)
throw UserNotAuthorizedError{};
Response response{ Response::createOkResponse(context.serverProtocolVersion) }; Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response::Node artistNode{ createArtistNode(context, artist, user, true /* id3 */) }; Response::Node artistNode{ createArtistNode(context, artist, context.user, true /* id3 */) };
const auto releases{ Release::find(context.dbSession, Release::FindParameters {}.setArtist(artist->getId())) }; const auto releases{ Release::find(context.dbSession, Release::FindParameters {}.setArtist(artist->getId())) };
for (const Release::pointer& release : releases.results) for (const Release::pointer& release : releases.results)
artistNode.addArrayChild("album", createAlbumNode(context, release, user, true /* id3 */)); artistNode.addArrayChild("album", createAlbumNode(context, release, context.user, true /* id3 */));
response.addNode("artist", std::move(artistNode)); response.addNode("artist", std::move(artistNode));
@@ -433,16 +410,12 @@ namespace lms::api::subsonic
if (!release) if (!release)
throw RequestedDataNotFoundError{}; throw RequestedDataNotFoundError{};
User::pointer user{ User::find(context.dbSession, context.userId) };
if (!user)
throw UserNotAuthorizedError{};
Response response{ Response::createOkResponse(context.serverProtocolVersion) }; Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response::Node albumNode{ createAlbumNode(context, release, user, true /* id3 */) }; Response::Node albumNode{ createAlbumNode(context, release, context.user, true /* id3 */) };
const auto tracks{ Track::find(context.dbSession, Track::FindParameters{}.setRelease(id).setSortMethod(TrackSortMethod::Release)) }; const auto tracks{ Track::find(context.dbSession, Track::FindParameters{}.setRelease(id).setSortMethod(TrackSortMethod::Release)) };
for (const Track::pointer& track : tracks.results) for (const Track::pointer& track : tracks.results)
albumNode.addArrayChild("song", createSongNode(context, track, user)); albumNode.addArrayChild("song", createSongNode(context, track, context.user));
response.addNode("album", std::move(albumNode)); response.addNode("album", std::move(albumNode));
@@ -460,12 +433,8 @@ namespace lms::api::subsonic
if (!track) if (!track)
throw RequestedDataNotFoundError{}; throw RequestedDataNotFoundError{};
User::pointer user{ User::find(context.dbSession, context.userId) };
if (!user)
throw UserNotAuthorizedError{};
Response response{ Response::createOkResponse(context.serverProtocolVersion) }; Response response{ Response::createOkResponse(context.serverProtocolVersion) };
response.addNode("song", createSongNode(context, track, user)); response.addNode("song", createSongNode(context, track, context.user));
return response; return response;
} }
@@ -504,15 +473,11 @@ namespace lms::api::subsonic
if (artists.size() != 1) if (artists.size() != 1)
throw RequestedDataNotFoundError{}; throw RequestedDataNotFoundError{};
User::pointer user{ User::find(context.dbSession, context.userId) };
if (!user)
throw UserNotAuthorizedError{};
Response response{ Response::createOkResponse(context.serverProtocolVersion) }; Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response::Node& topSongs{ response.createNode("topSongs") }; Response::Node& topSongs{ response.createNode("topSongs") };
scrobbling::IScrobblingService::FindParameters params; scrobbling::IScrobblingService::FindParameters params;
params.setUser(context.userId); params.setUser(context.user->getId());
params.setRange(db::Range{ 0, count }); params.setRange(db::Range{ 0, count });
params.setArtist(artists.front()->getId()); params.setArtist(artists.front()->getId());
@@ -520,7 +485,7 @@ namespace lms::api::subsonic
for (const TrackId trackId : trackIds.results) for (const TrackId trackId : trackIds.results)
{ {
if (Track::pointer track{ Track::find(context.dbSession, trackId) }) if (Track::pointer track{ Track::find(context.dbSession, trackId) })
topSongs.addArrayChild("song", createSongNode(context, track, user)); topSongs.addArrayChild("song", createSongNode(context, track, context.user));
} }
return response; return response;
@@ -24,6 +24,7 @@
#include "database/ArtistId.hpp" #include "database/ArtistId.hpp"
#include "database/ReleaseId.hpp" #include "database/ReleaseId.hpp"
#include "database/TrackId.hpp" #include "database/TrackId.hpp"
#include "database/User.hpp"
#include "services/feedback/IFeedbackService.hpp" #include "services/feedback/IFeedbackService.hpp"
#include "services/scrobbling/IScrobblingService.hpp" #include "services/scrobbling/IScrobblingService.hpp"
#include "core/Service.hpp" #include "core/Service.hpp"
@@ -61,13 +62,13 @@ namespace lms::api::subsonic
StarParameters params{ getStarParameters(context.parameters) }; StarParameters params{ getStarParameters(context.parameters) };
for (const ArtistId id : params.artistIds) for (const ArtistId id : params.artistIds)
core::Service<feedback::IFeedbackService>::get()->star(context.userId, id); core::Service<feedback::IFeedbackService>::get()->star(context.user->getId(), id);
for (const ReleaseId id : params.releaseIds) for (const ReleaseId id : params.releaseIds)
core::Service<feedback::IFeedbackService>::get()->star(context.userId, id); core::Service<feedback::IFeedbackService>::get()->star(context.user->getId(), id);
for (const TrackId id : params.trackIds) for (const TrackId id : params.trackIds)
core::Service<feedback::IFeedbackService>::get()->star(context.userId, id); core::Service<feedback::IFeedbackService>::get()->star(context.user->getId(), id);
return Response::createOkResponse(context.serverProtocolVersion); return Response::createOkResponse(context.serverProtocolVersion);
} }
@@ -77,13 +78,13 @@ namespace lms::api::subsonic
StarParameters params{ getStarParameters(context.parameters) }; StarParameters params{ getStarParameters(context.parameters) };
for (const ArtistId id : params.artistIds) for (const ArtistId id : params.artistIds)
core::Service<feedback::IFeedbackService>::get()->unstar(context.userId, id); core::Service<feedback::IFeedbackService>::get()->unstar(context.user->getId(), id);
for (const ReleaseId id : params.releaseIds) for (const ReleaseId id : params.releaseIds)
core::Service<feedback::IFeedbackService>::get()->unstar(context.userId, id); core::Service<feedback::IFeedbackService>::get()->unstar(context.user->getId(), id);
for (const TrackId id : params.trackIds) for (const TrackId id : params.trackIds)
core::Service<feedback::IFeedbackService>::get()->unstar(context.userId, id); core::Service<feedback::IFeedbackService>::get()->unstar(context.user->getId(), id);
return Response::createOkResponse(context.serverProtocolVersion); return Response::createOkResponse(context.serverProtocolVersion);
} }
@@ -104,13 +105,13 @@ namespace lms::api::subsonic
if (!submission) if (!submission)
{ {
core::Service<scrobbling::IScrobblingService>::get()->listenStarted({ context.userId, ids.front() }); core::Service<scrobbling::IScrobblingService>::get()->listenStarted({ context.user->getId(), ids.front() });
} }
else else
{ {
if (times.empty()) if (times.empty())
{ {
core::Service<scrobbling::IScrobblingService>::get()->listenFinished({ context.userId, ids.front() }); core::Service<scrobbling::IScrobblingService>::get()->listenFinished({ context.user->getId(), ids.front() });
} }
else else
{ {
@@ -118,7 +119,7 @@ namespace lms::api::subsonic
{ {
const TrackId trackId{ ids[i] }; const TrackId trackId{ ids[i] };
const unsigned long time{ times[i] }; const unsigned long time{ times[i] };
core::Service<scrobbling::IScrobblingService>::get()->addTimedListen({ {context.userId, trackId}, Wt::WDateTime::fromTime_t(static_cast<std::time_t>(time / 1000)) }); core::Service<scrobbling::IScrobblingService>::get()->addTimedListen({ {context.user->getId(), trackId}, Wt::WDateTime::fromTime_t(static_cast<std::time_t>(time / 1000)) });
} }
} }
} }
@@ -129,10 +129,6 @@ namespace lms::api::subsonic
auto transaction{ context.dbSession.createReadTransaction() }; auto transaction{ context.dbSession.createReadTransaction() };
const User::pointer user{ User::find(context.dbSession, context.userId) };
if (!user)
throw UserNotAuthorizedError{};
const auto track{ Track::find(context.dbSession, id) }; const auto track{ Track::find(context.dbSession, id) };
if (!track) if (!track)
throw RequestedDataNotFoundError{}; throw RequestedDataNotFoundError{};
@@ -147,8 +143,8 @@ namespace lms::api::subsonic
std::optional<av::transcoding::OutputFormat> requestedFormat{ subsonicStreamFormatToAvOutputFormat(format) }; std::optional<av::transcoding::OutputFormat> requestedFormat{ subsonicStreamFormatToAvOutputFormat(format) };
if (!requestedFormat) if (!requestedFormat)
{ {
if (user->getSubsonicEnableTranscodingByDefault()) if (context.user->getSubsonicEnableTranscodingByDefault())
requestedFormat = userTranscodeFormatToAvFormat(user->getSubsonicDefaultTranscodingOutputFormat()); requestedFormat = userTranscodeFormatToAvFormat(context.user->getSubsonicDefaultTranscodingOutputFormat());
} }
if (!requestedFormat && (maxBitRate == 0 || track->getBitrate() <= maxBitRate )) if (!requestedFormat && (maxBitRate == 0 || track->getBitrate() <= maxBitRate ))
@@ -172,9 +168,9 @@ namespace lms::api::subsonic
} }
if (!requestedFormat) if (!requestedFormat)
requestedFormat = userTranscodeFormatToAvFormat(user->getSubsonicDefaultTranscodingOutputFormat()); requestedFormat = userTranscodeFormatToAvFormat(context.user->getSubsonicDefaultTranscodingOutputFormat());
if (!bitrate) if (!bitrate)
bitrate = std::min<std::size_t>(user->getSubsonicDefaultTranscodingOutputBitrate(), maxBitRate); bitrate = std::min<std::size_t>(context.user->getSubsonicDefaultTranscodingOutputBitrate(), maxBitRate);
av::transcoding::OutputParameters& outputParameters{ parameters.outputParameters.emplace() }; av::transcoding::OutputParameters& outputParameters{ parameters.outputParameters.emplace() };
@@ -40,7 +40,7 @@ namespace lms::api::subsonic
Response::Node& playlistsNode{ response.createNode("playlists") }; Response::Node& playlistsNode{ response.createNode("playlists") };
TrackList::FindParameters params; TrackList::FindParameters params;
params.setUser(context.userId); params.setUser(context.user->getId());
params.setType(TrackListType::Playlist); params.setType(TrackListType::Playlist);
auto tracklistIds{ TrackList::find(context.dbSession, params) }; auto tracklistIds{ TrackList::find(context.dbSession, params) };
@@ -60,10 +60,6 @@ namespace lms::api::subsonic
auto transaction{ context.dbSession.createReadTransaction() }; auto transaction{ context.dbSession.createReadTransaction() };
User::pointer user{ User::find(context.dbSession, context.userId) };
if (!user)
throw UserNotAuthorizedError{};
TrackList::pointer tracklist{ TrackList::find(context.dbSession, trackListId) }; TrackList::pointer tracklist{ TrackList::find(context.dbSession, trackListId) };
if (!tracklist) if (!tracklist)
throw RequestedDataNotFoundError{}; throw RequestedDataNotFoundError{};
@@ -73,7 +69,7 @@ namespace lms::api::subsonic
auto entries{ tracklist->getEntries() }; auto entries{ tracklist->getEntries() };
for (const TrackListEntry::pointer& entry : entries.results) for (const TrackListEntry::pointer& entry : entries.results)
playlistNode.addArrayChild("entry", createSongNode(context, entry->getTrack(), user)); playlistNode.addArrayChild("entry", createSongNode(context, entry->getTrack(), context.user));
response.addNode("playlist", std::move(playlistNode)); response.addNode("playlist", std::move(playlistNode));
@@ -93,16 +89,12 @@ namespace lms::api::subsonic
auto transaction{ context.dbSession.createWriteTransaction() }; auto transaction{ context.dbSession.createWriteTransaction() };
User::pointer user{ User::find(context.dbSession, context.userId) };
if (!user)
throw UserNotAuthorizedError{};
TrackList::pointer tracklist; TrackList::pointer tracklist;
if (id) if (id)
{ {
tracklist = TrackList::find(context.dbSession, *id); tracklist = TrackList::find(context.dbSession, *id);
if (!tracklist if (!tracklist
|| tracklist->getUser() != user || tracklist->getUser() != context.user
|| tracklist->getType() != TrackListType::Playlist) || tracklist->getType() != TrackListType::Playlist)
{ {
throw RequestedDataNotFoundError{}; throw RequestedDataNotFoundError{};
@@ -113,7 +105,7 @@ namespace lms::api::subsonic
} }
else else
{ {
tracklist = context.dbSession.create<TrackList>(*name, TrackListType::Playlist, false, user); tracklist = context.dbSession.create<TrackList>(*name, TrackListType::Playlist, false, context.user);
} }
for (const TrackId trackId : trackIds) for (const TrackId trackId : trackIds)
@@ -130,7 +122,7 @@ namespace lms::api::subsonic
auto entries{ tracklist->getEntries() }; auto entries{ tracklist->getEntries() };
for (const TrackListEntry::pointer& entry : entries.results) for (const TrackListEntry::pointer& entry : entries.results)
playlistNode.addArrayChild("entry", createSongNode(context, entry->getTrack(), user)); playlistNode.addArrayChild("entry", createSongNode(context, entry->getTrack(), context.user));
response.addNode("playlist", std::move(playlistNode)); response.addNode("playlist", std::move(playlistNode));
@@ -151,13 +143,9 @@ namespace lms::api::subsonic
auto transaction{ context.dbSession.createWriteTransaction() }; auto transaction{ context.dbSession.createWriteTransaction() };
User::pointer user{ User::find(context.dbSession, context.userId) };
if (!user)
throw UserNotAuthorizedError{};
TrackList::pointer tracklist{ TrackList::find(context.dbSession, id) }; TrackList::pointer tracklist{ TrackList::find(context.dbSession, id) };
if (!tracklist if (!tracklist
|| tracklist->getUser() != user || tracklist->getUser() != context.user
|| tracklist->getType() != TrackListType::Playlist) || tracklist->getType() != TrackListType::Playlist)
{ {
throw RequestedDataNotFoundError{}; throw RequestedDataNotFoundError{};
@@ -200,13 +188,9 @@ namespace lms::api::subsonic
auto transaction{ context.dbSession.createWriteTransaction() }; auto transaction{ context.dbSession.createWriteTransaction() };
User::pointer user{ User::find(context.dbSession, context.userId) };
if (!user)
throw UserNotAuthorizedError{};
TrackList::pointer tracklist{ TrackList::find(context.dbSession, id) }; TrackList::pointer tracklist{ TrackList::find(context.dbSession, id) };
if (!tracklist if (!tracklist
|| tracklist->getUser() != user || tracklist->getUser() != context.user
|| tracklist->getType() != TrackListType::Playlist) || tracklist->getType() != TrackListType::Playlist)
{ {
throw RequestedDataNotFoundError{}; throw RequestedDataNotFoundError{};
@@ -334,13 +334,9 @@ namespace lms::api::subsonic
auto transaction{ context.dbSession.createReadTransaction() }; auto transaction{ context.dbSession.createReadTransaction() };
User::pointer user{ User::find(context.dbSession, context.userId) }; findRequestedArtists(context, id3, keywords, mediaLibrary, context.user, searchResultNode);
if (!user) findRequestedAlbums(context, id3, keywords, mediaLibrary, context.user, searchResultNode);
throw UserNotAuthorizedError{}; findRequestedTracks(context, keywords, mediaLibrary, context.user, searchResultNode);
findRequestedArtists(context, id3, keywords, mediaLibrary, user, searchResultNode);
findRequestedAlbums(context, id3, keywords, mediaLibrary, user, searchResultNode);
findRequestedTracks(context, keywords, mediaLibrary, user, searchResultNode);
return response; return response;
} }
@@ -15,11 +15,7 @@ namespace lms::api::subsonic
namespace { namespace {
void checkUserIsMySelfOrAdmin(RequestContext& context, const std::string& username) void checkUserIsMySelfOrAdmin(RequestContext& context, const std::string& username)
{ {
User::pointer currentUser{ User::find(context.dbSession, context.userId) }; if (context.user->getLoginName() != username && !context.user->isAdmin())
if (!currentUser)
throw RequestedDataNotFoundError{};
if (currentUser->getLoginName() != username && !currentUser->isAdmin())
throw UserNotAuthorizedError{}; throw UserNotAuthorizedError{};
} }
} }
@@ -44,17 +40,14 @@ namespace lms::api::subsonic
Response handleGetUsersRequest(RequestContext& context) Response handleGetUsersRequest(RequestContext& context)
{ {
auto transaction{ context.dbSession.createReadTransaction() };
Response response{ Response::createOkResponse(context.serverProtocolVersion) }; Response response{ Response::createOkResponse(context.serverProtocolVersion) };
Response::Node& usersNode{ response.createNode("users") }; Response::Node& usersNode{ response.createNode("users") };
const auto userIds{ User::find(context.dbSession, User::FindParameters {}) }; auto transaction{ context.dbSession.createReadTransaction() };
for (const UserId userId : userIds.results) User::find(context.dbSession, User::FindParameters{}, [&](const User::pointer& user)
{ {
const User::pointer user{ User::find(context.dbSession, userId) };
usersNode.addArrayChild("user", createUserNode(user)); usersNode.addArrayChild("user", createUserNode(user));
} });
return response; return response;
} }
@@ -77,7 +70,7 @@ namespace lms::api::subsonic
userId = user->getId(); userId = user->getId();
} }
auto removeCreatedUser{ [&]() auto removeCreatedUser{ [&]
{ {
auto transaction {context.dbSession.createWriteTransaction()}; auto transaction {context.dbSession.createWriteTransaction()};
User::pointer user{ User::find(context.dbSession, userId) }; User::pointer user{ User::find(context.dbSession, userId) };
@@ -119,7 +112,7 @@ namespace lms::api::subsonic
throw RequestedDataNotFoundError{}; throw RequestedDataNotFoundError{};
// cannot delete ourself // cannot delete ourself
if (user->getId() == context.userId) if (user->getId() == context.user->getId())
throw UserNotAuthorizedError{}; throw UserNotAuthorizedError{};
user.remove(); user.remove();