Rework DB session + transactions. Now can handle multiple read only transactions in parallel

This commit is contained in:
emeric
2019-07-16 13:17:23 +02:00
parent 2a1fb0182b
commit 38ce2600d2
79 changed files with 1840 additions and 1369 deletions
+149 -142
View File
@@ -22,6 +22,7 @@
#include <mutex>
#include <numeric>
#include <random>
#include <thread>
#include <Wt/Auth/Identity.h>
#include <Wt/WLocalDateTime.h>
@@ -33,6 +34,7 @@
#include "database/Release.hpp"
#include "database/Track.hpp"
#include "database/TrackList.hpp"
#include "database/User.hpp"
#include "main/Service.hpp"
#include "similarity/SimilaritySearcher.hpp"
#include "utils/Logger.hpp"
@@ -113,10 +115,37 @@ struct ClientInfo
struct RequestContext
{
const Wt::Http::ParameterMap& parameters;
Database::Handler& db;
Database::Session& dbSession;
std::string userName;
};
// TODO handle multiple databases
static thread_local std::map<std::thread::id, std::unique_ptr<Database::Session>> dbSessions;
static
Database::Session&
getOrCreateDbSession(Database::Database& db)
{
static std::mutex mutex;
std::unique_lock<std::mutex> lock {mutex};
auto it {dbSessions.find(std::this_thread::get_id())};
if (it != dbSessions.end())
return *it->second;
auto res {dbSessions.emplace(std::this_thread::get_id(), db.createSession())};
assert(res.second);
return *(res.first->second);
}
static
void
cleanDbSessions()
{
dbSessions.clear();
}
// requests
using RequestHandlerFunc = std::function<Response(RequestContext& context)>;
static Response handlePingRequest(RequestContext& context);
@@ -289,23 +318,15 @@ getClientInfo(const Wt::Http::ParameterMap& parameters)
return res;
}
static
bool
checkPassword(Database::Handler& db, const ClientInfo& clientInfo)
SubsonicResource::SubsonicResource(Database::Database& db)
: _db {db}
{
auto authUser {db.getUserDatabase().findWithIdentity(Wt::Auth::Identity::LoginName, clientInfo.user)};
if (!authUser.isValid())
{
LMS_LOG(API_SUBSONIC, ERROR) << "Cannot find user '" << clientInfo.user << "'";
return false;
}
return db.getPasswordService().verifyPassword(authUser, clientInfo.password) == Wt::Auth::PasswordResult::PasswordValid;
}
SubsonicResource::SubsonicResource(Wt::Dbo::SqlConnectionPool& connectionPool)
: _db {connectionPool}
SubsonicResource::~SubsonicResource()
{
LMS_LOG(API_SUBSONIC, DEBUG) << "Cleaning db sessions...";
cleanDbSessions();
}
std::vector<std::string>
@@ -371,24 +392,20 @@ SubsonicResource::handleRequest(const Wt::Http::Request &request, Wt::Http::Resp
try
{
static std::mutex mutex;
Database::Session& dbSession {getOrCreateDbSession(_db)};
ClientInfo clientInfo {getClientInfo(parameters)};
const ClientInfo clientInfo {getClientInfo(parameters)};
std::unique_lock<std::mutex> lock{mutex}; // For now just handle request s one by one
if (!checkPassword(_db, clientInfo))
if (!dbSession.checkUserPassword(clientInfo.user, clientInfo.password))
throw Error {Error::Code::WrongUsernameOrPassword};
RequestContext requestContext {.parameters = parameters, .db = _db, .userName = clientInfo.user};
RequestContext requestContext {.parameters = parameters, .dbSession = dbSession, .userName = clientInfo.user};
auto itHandler {requestHandlers.find(request.path())};
if (itHandler != requestHandlers.end())
{
Response resp {(itHandler->second)(requestContext)};
lock.unlock();
resp.write(response.out(), format);
response.setMimeType(ResponseFormatToMimeType(format));
@@ -401,8 +418,6 @@ SubsonicResource::handleRequest(const Wt::Http::Request &request, Wt::Http::Resp
{
MediaRetrievalResult res {itStreamHandler->second(requestContext, request.continuation())};
lock.unlock();
if (!res.mimeType.empty())
response.setMimeType(res.mimeType);
if (!res.data.empty())
@@ -488,7 +503,7 @@ getTrackPath(const Database::Track::pointer& track)
static
Response::Node
trackToResponseNode(const Database::User::pointer& user, const Database::Track::pointer& track)
trackToResponseNode(const Database::Track::pointer& track, Database::Session& dbSession, const Database::User::pointer& user)
{
Response::Node trackResponse;
@@ -532,7 +547,7 @@ trackToResponseNode(const Database::User::pointer& user, const Database::Track::
trackResponse.setAttribute("starred", reportedStarredDate);
// Report the first GENRE for this track
Database::ClusterType::pointer clusterType {Database::ClusterType::getByName(*track.session(), genreClusterName)};
Database::ClusterType::pointer clusterType {Database::ClusterType::getByName(dbSession, genreClusterName)};
if (clusterType)
{
auto clusters {track->getClusterGroups({clusterType}, 1)};
@@ -545,7 +560,7 @@ trackToResponseNode(const Database::User::pointer& user, const Database::Track::
static
Response::Node
releaseToResponseNode(const Database::User::pointer& user, const Database::Release::pointer& release, bool id3)
releaseToResponseNode(const Database::Release::pointer& release, Database::Session& dbSession, const Database::User::pointer& user, bool id3)
{
Response::Node albumNode;
@@ -598,7 +613,7 @@ releaseToResponseNode(const Database::User::pointer& user, const Database::Relea
if (id3)
{
// Report the first GENRE for this track
Database::ClusterType::pointer clusterType {Database::ClusterType::getByName(*release.session(), genreClusterName)};
Database::ClusterType::pointer clusterType {Database::ClusterType::getByName(dbSession, genreClusterName)};
if (clusterType)
{
auto clusters {release->getClusterGroups({clusterType}, 1)};
@@ -668,16 +683,16 @@ handleCreatePlaylistRequest(RequestContext& context)
if (!name && !id)
throw Error {Error::Code::RequiredParameterMissing};
Wt::Dbo::Transaction transaction {context.db.getSession()};
auto transaction {context.dbSession.createSharedTransaction()};
Database::User::pointer user {context.db.getUser(context.userName)};
Database::User::pointer user {context.dbSession.getUser(context.userName)};
if (!user)
throw Error {Error::Code::RequestedDataNotFound};
Database::TrackList::pointer tracklist;
if (id)
{
tracklist = Database::TrackList::getById(context.db.getSession(), id->value);
tracklist = Database::TrackList::getById(context.dbSession, id->value);
if (!tracklist
|| tracklist->getUser() != user
|| tracklist->getType() != Database::TrackList::Type::Playlist)
@@ -690,16 +705,16 @@ handleCreatePlaylistRequest(RequestContext& context)
}
else
{
tracklist = Database::TrackList::create(context.db.getSession(), *name, Database::TrackList::Type::Playlist, false, user);
tracklist = Database::TrackList::create(context.dbSession, *name, Database::TrackList::Type::Playlist, false, user);
}
for (const Id& trackId : trackIds)
{
Database::Track::pointer track {Database::Track::getById(context.db.getSession(), trackId.value)};
Database::Track::pointer track {Database::Track::getById(context.dbSession, trackId.value)};
if (!track)
continue;
Database::TrackListEntry::create(context.db.getSession(), track, tracklist );
Database::TrackListEntry::create(context.dbSession, track, tracklist );
}
return Response::createOkResponse();
@@ -713,13 +728,13 @@ handleDeletePlaylistRequest(RequestContext& context)
if (id.type != Id::Type::Playlist)
throw Error {Error::CustomType::BadId};
Wt::Dbo::Transaction transaction {context.db.getSession()};
auto transaction {context.dbSession.createSharedTransaction()};
Database::User::pointer user {context.db.getUser(context.userName)};
Database::User::pointer user {context.dbSession.getUser(context.userName)};
if (!user)
throw Error {Error::Code::RequestedDataNotFound};
Database::TrackList::pointer tracklist {Database::TrackList::getById(context.db.getSession(), id.value)};
Database::TrackList::pointer tracklist {Database::TrackList::getById(context.dbSession, id.value)};
if (!tracklist
|| tracklist->getUser() != user
|| tracklist->getType() != Database::TrackList::Type::Playlist)
@@ -752,29 +767,29 @@ handleGetRandomSongsRequest(RequestContext& context)
std::size_t size {getParameterAs<std::size_t>(context.parameters, "size").get_value_or(50)};
size = std::min(size, std::size_t {500});
Wt::Dbo::Transaction transaction {context.db.getSession()};
auto transaction {context.dbSession.createSharedTransaction()};
Database::User::pointer user {context.db.getUser(context.userName)};
Database::User::pointer user {context.dbSession.getUser(context.userName)};
if (!user)
throw Error {Error::Code::RequestedDataNotFound};
auto tracks {Database::Track::getAllRandom(context.db.getSession(), size)};
auto tracks {Database::Track::getAllRandom(context.dbSession, size)};
Response response {Response::createOkResponse()};
Response::Node& randomSongsNode {response.createNode("randomSongs")};
for (const Database::Track::pointer& track : tracks)
randomSongsNode.addArrayChild("song", trackToResponseNode(user, track));
randomSongsNode.addArrayChild("song", trackToResponseNode(track, context.dbSession, user));
return response;
}
static
std::vector<Database::Release::pointer> getRandomAlbums(Wt::Dbo::Session& session, std::size_t offset, std::size_t size)
std::vector<Database::Release::pointer> getRandomAlbums(Database::Session& dbSession, std::size_t offset, std::size_t size)
{
std::vector<Database::Release::pointer> res;
std::size_t nbReleases {Database::Release::getCount(session)};
std::size_t nbReleases {Database::Release::getCount(dbSession)};
if (offset > nbReleases)
return res;
@@ -793,7 +808,7 @@ std::vector<Database::Release::pointer> getRandomAlbums(Wt::Dbo::Session& sessio
std::for_each(std::next(std::begin(indexes), offset), std::next(std::begin(indexes), offset + size),
[&](std::size_t offset)
{
auto release {Database::Release::getAll(session, offset, 1)};
auto release {Database::Release::getAll(dbSession, offset, 1)};
if (!release.empty())
res.emplace_back(release.front());
});
@@ -814,38 +829,38 @@ handleGetAlbumListRequestCommon(const RequestContext& context, bool id3)
std::vector<Database::Release::pointer> releases;
Wt::Dbo::Transaction transaction {context.db.getSession()};
auto transaction {context.dbSession.createSharedTransaction()};
Database::User::pointer user {context.db.getUser(context.userName)};
Database::User::pointer user {context.dbSession.getUser(context.userName)};
if (!user)
throw Error {Error::Code::RequestedDataNotFound};
if (type == "random")
{
releases = getRandomAlbums(context.db.getSession(), offset, size);
releases = getRandomAlbums(context.dbSession, offset, size);
}
else if (type == "newest")
{
auto after {Wt::WLocalDateTime::currentServerDateTime().toUTC().addMonths(-6)};
releases = Database::Release::getLastAdded(context.db.getSession(), after, offset, size);
releases = Database::Release::getLastAdded(context.dbSession, after, offset, size);
}
else if (type == "alphabeticalByName")
{
releases = Database::Release::getAll(context.db.getSession(), offset, size);
releases = Database::Release::getAll(context.dbSession, offset, size);
}
else if (type == "byGenre")
{
// Mandatory param
std::string genre {getMandatoryParameterAs<std::string>(context.parameters, "genre")};
Database::ClusterType::pointer clusterType {Database::ClusterType::getByName(context.db.getSession(), genreClusterName)};
Database::ClusterType::pointer clusterType {Database::ClusterType::getByName(context.dbSession, genreClusterName)};
if (clusterType)
{
Database::Cluster::pointer cluster {clusterType->getCluster(genre)};
if (cluster)
{
bool more;
releases = Database::Release::getByFilter(context.db.getSession(), {cluster.id()}, {}, offset, size, more);
releases = Database::Release::getByFilter(context.dbSession, {cluster.id()}, {}, offset, size, more);
}
}
}
@@ -856,7 +871,7 @@ handleGetAlbumListRequestCommon(const RequestContext& context, bool id3)
Response::Node& albumListNode {response.createNode(id3 ? "albumList2" : "albumList")};
for (const Database::Release::pointer& release : releases)
albumListNode.addArrayChild("album", releaseToResponseNode(user, release, id3));
albumListNode.addArrayChild("album", releaseToResponseNode(release, context.dbSession, user, id3));
return response;
}
@@ -882,22 +897,22 @@ handleGetAlbumRequest(RequestContext& context)
if (id.type != Id::Type::Release)
throw Error {Error::CustomType::BadId};
Wt::Dbo::Transaction transaction {context.db.getSession()};
auto transaction {context.dbSession.createSharedTransaction()};
Database::Release::pointer release {Database::Release::getById(context.db.getSession(), id.value)};
Database::Release::pointer release {Database::Release::getById(context.dbSession, id.value)};
if (!release)
throw Error {Error::Code::RequestedDataNotFound};
Database::User::pointer user {context.db.getUser(context.userName)};
Database::User::pointer user {context.dbSession.getUser(context.userName)};
if (!user)
throw Error {Error::Code::RequestedDataNotFound};
Response response {Response::createOkResponse()};
Response::Node releaseNode {releaseToResponseNode(user, release, true /* id3 */)};
Response::Node releaseNode {releaseToResponseNode(release, context.dbSession, user, true /* id3 */)};
auto tracks {release->getTracks()};
for (const Database::Track::pointer& track : tracks)
releaseNode.addArrayChild("song", trackToResponseNode(user, track));
releaseNode.addArrayChild("song", trackToResponseNode(track, context.dbSession, user));
response.addNode("album", std::move(releaseNode));
@@ -913,10 +928,10 @@ handleGetArtistRequest(RequestContext& context)
if (id.type != Id::Type::Artist)
throw Error {Error::CustomType::BadId};
Wt::Dbo::Transaction transaction {context.db.getSession()};
auto transaction {context.dbSession.createSharedTransaction()};
Database::Artist::pointer artist {Database::Artist::getById(context.db.getSession(), id.value)};
Database::User::pointer user {context.db.getUser(context.userName)};
Database::Artist::pointer artist {Database::Artist::getById(context.dbSession, id.value)};
Database::User::pointer user {context.dbSession.getUser(context.userName)};
if (!artist || !user)
throw Error {Error::Code::RequestedDataNotFound};
@@ -926,7 +941,7 @@ handleGetArtistRequest(RequestContext& context)
auto releases {artist->getReleases()};
for (const Database::Release::pointer& release : releases)
artistNode.addArrayChild("album", releaseToResponseNode(user, release, true /* id3 */));
artistNode.addArrayChild("album", releaseToResponseNode(release, context.dbSession, user, true /* id3 */));
response.addNode("artist", std::move(artistNode));
@@ -945,10 +960,10 @@ Response handleGetArtistInfoRequestCommon(RequestContext& context, bool id3)
// Optional params
std::size_t count {getParameterAs<std::size_t>(context.parameters, "count").get_value_or(10)};
Wt::Dbo::Transaction transaction {context.db.getSession()};
auto transaction {context.dbSession.createSharedTransaction()};
Database::Artist::pointer artist {Database::Artist::getById(context.db.getSession(), id.value)};
Database::User::pointer user {context.db.getUser(context.userName)};
Database::Artist::pointer artist {Database::Artist::getById(context.dbSession, id.value)};
Database::User::pointer user {context.dbSession.getUser(context.userName)};
if (!artist || !user)
throw Error {Error::Code::RequestedDataNotFound};
@@ -959,10 +974,10 @@ Response handleGetArtistInfoRequestCommon(RequestContext& context, bool id3)
if (!artist->getMBID().empty())
artistInfoNode.createChild("musicBrainzId").setValue(artist->getMBID());
auto similarArtistsId {getService<Similarity::Searcher>()->getSimilarArtists(context.db.getSession(), artist.id(), count)};
auto similarArtistsId {getService<Similarity::Searcher>()->getSimilarArtists(context.dbSession, artist.id(), count)};
for ( const auto& similarArtistId : similarArtistsId )
{
Database::Artist::pointer similarArtist {Database::Artist::getById(context.db.getSession(), similarArtistId)};
Database::Artist::pointer similarArtist {Database::Artist::getById(context.dbSession, similarArtistId)};
if (similarArtist)
artistInfoNode.addArrayChild("similarArtist", artistToResponseNode(user, similarArtist, id3));
@@ -989,13 +1004,13 @@ handleGetArtistsRequest(RequestContext& context)
Response::Node& indexNode {artistsNode.createArrayChild("index")};
indexNode.setAttribute("name", "?");
Wt::Dbo::Transaction transaction {context.db.getSession()};
auto transaction {context.dbSession.createSharedTransaction()};
Database::User::pointer user {context.db.getUser(context.userName)};
Database::User::pointer user {context.dbSession.getUser(context.userName)};
if (!user)
throw Error {Error::Code::RequestedDataNotFound};
auto artists {Database::Artist::getAll(context.db.getSession())};
auto artists {Database::Artist::getAll(context.dbSession)};
for (const Database::Artist::pointer& artist : artists)
indexNode.addArrayChild("artist", artistToResponseNode(user, artist, true /* id3 */));
@@ -1014,9 +1029,9 @@ handleGetMusicDirectoryRequest(RequestContext& context)
directoryNode.setAttribute("id", IdToString(id));
Wt::Dbo::Transaction transaction {context.db.getSession()};
auto transaction {context.dbSession.createSharedTransaction()};
Database::User::pointer user {context.db.getUser(context.userName)};
Database::User::pointer user {context.dbSession.getUser(context.userName)};
if (!user)
throw Error {Error::Code::RequestedDataNotFound};
@@ -1026,7 +1041,7 @@ handleGetMusicDirectoryRequest(RequestContext& context)
{
directoryNode.setAttribute("name", "Music");
auto artists {Database::Artist::getAll(context.db.getSession())};
auto artists {Database::Artist::getAll(context.dbSession)};
for (const Database::Artist::pointer& artist : artists)
directoryNode.addArrayChild("child", artistToResponseNode(user, artist, false /* no id3 */));
@@ -1035,7 +1050,7 @@ handleGetMusicDirectoryRequest(RequestContext& context)
case Id::Type::Artist:
{
auto artist {Database::Artist::getById(context.db.getSession(), id.value)};
auto artist {Database::Artist::getById(context.dbSession, id.value)};
if (!artist)
throw Error {Error::Code::RequestedDataNotFound};
@@ -1043,14 +1058,14 @@ handleGetMusicDirectoryRequest(RequestContext& context)
auto releases {artist->getReleases()};
for (const Database::Release::pointer& release : releases)
directoryNode.addArrayChild("child", releaseToResponseNode(user, release, false /* no id3 */));
directoryNode.addArrayChild("child", releaseToResponseNode(release, context.dbSession, user, false /* no id3 */));
break;
}
case Id::Type::Release:
{
auto release {Database::Release::getById(context.db.getSession(), id.value)};
auto release {Database::Release::getById(context.dbSession, id.value)};
if (!release)
throw Error {Error::Code::RequestedDataNotFound};
@@ -1058,7 +1073,7 @@ handleGetMusicDirectoryRequest(RequestContext& context)
auto tracks {release->getTracks()};
for (const Database::Track::pointer& track : tracks)
directoryNode.addArrayChild("child", trackToResponseNode(user, track));
directoryNode.addArrayChild("child", trackToResponseNode(track, context.dbSession, user));
break;
}
@@ -1090,9 +1105,9 @@ handleGetGenresRequest(RequestContext& context)
Response::Node& genresNode {response.createNode("genres")};
Wt::Dbo::Transaction transaction {context.db.getSession()};
auto transaction {context.dbSession.createSharedTransaction()};
auto clusterType {Database::ClusterType::getByName(context.db.getSession(), genreClusterName)};
auto clusterType {Database::ClusterType::getByName(context.dbSession, genreClusterName)};
if (clusterType)
{
auto clusters {clusterType->getClusters()};
@@ -1113,13 +1128,13 @@ handleGetIndexesRequest(RequestContext& context)
Response::Node& indexNode {artistsNode.createArrayChild("index")};
indexNode.setAttribute("name", "?");
Wt::Dbo::Transaction transaction {context.db.getSession()};
auto transaction {context.dbSession.createSharedTransaction()};
Database::User::pointer user {context.db.getUser(context.userName)};
Database::User::pointer user {context.dbSession.getUser(context.userName)};
if (!user)
throw Error {Error::Code::RequestedDataNotFound};
auto artists {Database::Artist::getAll(context.db.getSession())};
auto artists {Database::Artist::getAll(context.dbSession)};
for (const Database::Artist::pointer& artist : artists)
indexNode.addArrayChild("artist", artistToResponseNode(user, artist, false /* no id3 */));
@@ -1137,10 +1152,10 @@ handleGetSimilarSongsRequestCommon(RequestContext& context, bool id3)
// Optional params
std::size_t count {getParameterAs<std::size_t>(context.parameters, "count").get_value_or(50)};
Wt::Dbo::Transaction transaction {context.db.getSession()};
auto transaction {context.dbSession.createSharedTransaction()};
Database::Artist::pointer artist {Database::Artist::getById(context.db.getSession(), id.value)};
Database::User::pointer user {context.db.getUser(context.userName)};
Database::Artist::pointer artist {Database::Artist::getById(context.dbSession, id.value)};
Database::User::pointer user {context.dbSession.getUser(context.userName)};
if (!user || !artist)
throw Error {Error::Code::RequestedDataNotFound};
@@ -1148,10 +1163,10 @@ handleGetSimilarSongsRequestCommon(RequestContext& context, bool id3)
// "Returns a random collection of songs from the given artist and similar artists"
auto tracks {artist->getRandomTracks(count / 2)};
auto similarArtistsId {getService<Similarity::Searcher>()->getSimilarArtists(context.db.getSession(), artist.id(), 5)};
auto similarArtistsId {getService<Similarity::Searcher>()->getSimilarArtists(context.dbSession, artist.id(), 5)};
for ( const auto& similarArtistId : similarArtistsId )
{
Database::Artist::pointer similarArtist {Database::Artist::getById(context.db.getSession(), similarArtistId)};
Database::Artist::pointer similarArtist {Database::Artist::getById(context.dbSession, similarArtistId)};
if (!similarArtist)
continue;
@@ -1169,7 +1184,7 @@ handleGetSimilarSongsRequestCommon(RequestContext& context, bool id3)
Response response {Response::createOkResponse()};
Response::Node& similarSongsNode {response.createNode(id3 ? "similarSongs2" : "similarSongs")};
for (const Database::Track::pointer& track : tracks)
similarSongsNode.addArrayChild("song", trackToResponseNode(user, track));
similarSongsNode.addArrayChild("song", trackToResponseNode(track, context.dbSession, user));
return response;
}
@@ -1191,9 +1206,9 @@ static
Response
handleGetStarredRequestCommon(RequestContext& context, bool id3)
{
Wt::Dbo::Transaction transaction {context.db.getSession()};
auto transaction {context.dbSession.createSharedTransaction()};
Database::User::pointer user {context.db.getUser(context.userName)};
Database::User::pointer user {context.dbSession.getUser(context.userName)};
if (!user)
throw Error {Error::Code::RequestedDataNotFound};
@@ -1209,13 +1224,13 @@ handleGetStarredRequestCommon(RequestContext& context, bool id3)
{
auto releases {user->getStarredReleases()};
for (const Database::Release::pointer& release : releases)
starredNode.addArrayChild("album", releaseToResponseNode(user, release, id3));
starredNode.addArrayChild("album", releaseToResponseNode(release, context.dbSession, user, id3));
}
{
auto tracks {user->getStarredTracks()};
for (const Database::Track::pointer& track : tracks)
starredNode.addArrayChild("song", trackToResponseNode(user, track));
starredNode.addArrayChild("song", trackToResponseNode(track, context.dbSession, user));
}
return response;
@@ -1235,7 +1250,7 @@ handleGetStarred2Request(RequestContext& context)
}
Response::Node
tracklistToResponseNode(const Database::TrackList::pointer& tracklist, Database::Handler& db)
tracklistToResponseNode(const Database::TrackList::pointer& tracklist, Database::Session& dbSession)
{
Response::Node playlistNode;
@@ -1245,15 +1260,7 @@ tracklistToResponseNode(const Database::TrackList::pointer& tracklist, Database:
playlistNode.setAttribute("duration", std::to_string(std::chrono::duration_cast<std::chrono::seconds>(tracklist->getDuration()).count()));
playlistNode.setAttribute("public", tracklist->isPublic() ? "true" : "false");
playlistNode.setAttribute("created", "");
{
std::string userId {std::to_string(tracklist->getUser().id())};
Wt::Auth::User authUser { db.getUserDatabase().findWithId(userId)};
if (!authUser.isValid())
throw Error {Error::CustomType::InternalError};
playlistNode.setAttribute("owner", authUser.identity(Wt::Auth::Identity::LoginName).toUTF8());
}
playlistNode.setAttribute("owner", dbSession.getUserLoginName(tracklist->getUser()));
return playlistNode;
}
@@ -1266,19 +1273,19 @@ handleGetPlaylistRequest(RequestContext& context)
if (id.type != Id::Type::Playlist)
throw Error {Error::CustomType::BadId};
Wt::Dbo::Transaction transaction {context.db.getSession()};
auto transaction {context.dbSession.createSharedTransaction()};
Database::User::pointer user {context.db.getUser(context.userName)};
Database::TrackList::pointer tracklist {Database::TrackList::getById(context.db.getSession(), id.value)};
Database::User::pointer user {context.dbSession.getUser(context.userName)};
Database::TrackList::pointer tracklist {Database::TrackList::getById(context.dbSession, id.value)};
if (!user || !tracklist)
throw Error {Error::Code::RequestedDataNotFound};
Response response {Response::createOkResponse()};
Response::Node playlistNode {tracklistToResponseNode(tracklist, context.db)};
Response::Node playlistNode {tracklistToResponseNode(tracklist, context.dbSession)};
auto entries {tracklist->getEntries()};
for (const Database::TrackListEntry::pointer& entry : entries)
playlistNode.addArrayChild("entry", trackToResponseNode(user, entry->getTrack()));
playlistNode.addArrayChild("entry", trackToResponseNode(entry->getTrack(), context.dbSession, user));
response.addNode("playlist", playlistNode );
@@ -1288,18 +1295,18 @@ handleGetPlaylistRequest(RequestContext& context)
Response
handleGetPlaylistsRequest(RequestContext& context)
{
Wt::Dbo::Transaction transaction {context.db.getSession()};
auto transaction {context.dbSession.createSharedTransaction()};
Database::User::pointer user {context.db.getUser(context.userName)};
Database::User::pointer user {context.dbSession.getUser(context.userName)};
if (!user)
throw Error {Error::Code::RequestedDataNotFound};
Response response {Response::createOkResponse()};
Response::Node& playlistsNode {response.createNode("playlists")};
auto tracklists {Database::TrackList::getAll(context.db.getSession(), user, Database::TrackList::Type::Playlist)};
auto tracklists {Database::TrackList::getAll(context.dbSession, user, Database::TrackList::Type::Playlist)};
for (const Database::TrackList::pointer& tracklist : tracklists)
playlistsNode.addArrayChild("playlist", tracklistToResponseNode(tracklist, context.db));
playlistsNode.addArrayChild("playlist", tracklistToResponseNode(tracklist, context.dbSession));
return response;
}
@@ -1316,9 +1323,9 @@ handleGetSongsByGenreRequest(RequestContext& context)
std::size_t offset {getParameterAs<std::size_t>(context.parameters, "offset").get_value_or(0)};
Wt::Dbo::Transaction transaction {context.db.getSession()};
auto transaction {context.dbSession.createSharedTransaction()};
auto clusterType {Database::ClusterType::getByName(context.db.getSession(), genreClusterName)};
auto clusterType {Database::ClusterType::getByName(context.dbSession, genreClusterName)};
if (!clusterType)
throw Error {Error::Code::RequestedDataNotFound};
@@ -1326,7 +1333,7 @@ handleGetSongsByGenreRequest(RequestContext& context)
if (!cluster)
throw Error {Error::Code::RequestedDataNotFound};
Database::User::pointer user {context.db.getUser(context.userName)};
Database::User::pointer user {context.dbSession.getUser(context.userName)};
if (!user)
throw Error {Error::Code::RequestedDataNotFound};
@@ -1334,9 +1341,9 @@ handleGetSongsByGenreRequest(RequestContext& context)
Response::Node& songsByGenreNode {response.createNode("songsByGenre")};
bool more;
auto tracks {Database::Track::getByFilter(context.db.getSession(), {cluster.id()}, {}, offset, size, more)};
auto tracks {Database::Track::getByFilter(context.dbSession, {cluster.id()}, {}, offset, size, more)};
for (const Database::Track::pointer& track : tracks)
songsByGenreNode.addArrayChild("song", trackToResponseNode(user, track));
songsByGenreNode.addArrayChild("song", trackToResponseNode(track, context.dbSession, user));
return response;
}
@@ -1358,9 +1365,9 @@ handleSearchRequestCommon(RequestContext& context, bool id3)
std::size_t songCount {getParameterAs<std::size_t>(context.parameters, "songCount").get_value_or(20)};
std::size_t songOffset {getParameterAs<std::size_t>(context.parameters, "songOffset").get_value_or(0)};
Wt::Dbo::Transaction transaction {context.db.getSession()};
auto transaction {context.dbSession.createSharedTransaction()};
Database::User::pointer user {context.db.getUser(context.userName)};
Database::User::pointer user {context.dbSession.getUser(context.userName)};
if (!user)
throw Error {Error::Code::RequestedDataNotFound};
@@ -1369,21 +1376,21 @@ handleSearchRequestCommon(RequestContext& context, bool id3)
bool more;
{
auto artists {Database::Artist::getByFilter(context.db.getSession(), {}, keywords, artistOffset, artistCount, more)};
auto artists {Database::Artist::getByFilter(context.dbSession, {}, keywords, artistOffset, artistCount, more)};
for (const Database::Artist::pointer& artist : artists)
searchResult2Node.addArrayChild("artist", artistToResponseNode(user, artist, id3));
}
{
auto releases {Database::Release::getByFilter(context.db.getSession(), {}, keywords, albumOffset, albumCount, more)};
auto releases {Database::Release::getByFilter(context.dbSession, {}, keywords, albumOffset, albumCount, more)};
for (const Database::Release::pointer& release : releases)
searchResult2Node.addArrayChild("album", releaseToResponseNode(user, release, id3));
searchResult2Node.addArrayChild("album", releaseToResponseNode(release, context.dbSession, user, id3));
}
{
auto tracks {Database::Track::getByFilter(context.db.getSession(), {}, keywords, songOffset, songCount, more)};
auto tracks {Database::Track::getByFilter(context.dbSession, {}, keywords, songOffset, songCount, more)};
for (const Database::Track::pointer& track : tracks)
searchResult2Node.addArrayChild("song", trackToResponseNode(user, track));
searchResult2Node.addArrayChild("song", trackToResponseNode(track, context.dbSession, user));
}
return response;
@@ -1439,15 +1446,15 @@ handleStarRequest(RequestContext& context)
{
StarParameters params {getStarParameters(context.parameters)};
Wt::Dbo::Transaction transaction {context.db.getSession()};
auto transaction {context.dbSession.createSharedTransaction()};
Database::User::pointer user {context.db.getUser(context.userName)};
Database::User::pointer user {context.dbSession.getUser(context.userName)};
if (!user)
throw Error {Error::Code::RequestedDataNotFound};
for (const Id& id : params.artistIds)
{
Database::Artist::pointer artist {Database::Artist::getById(context.db.getSession(), id.value)};
Database::Artist::pointer artist {Database::Artist::getById(context.dbSession, id.value)};
if (!artist)
continue;
@@ -1456,7 +1463,7 @@ handleStarRequest(RequestContext& context)
for (const Id& id : params.releaseIds)
{
Database::Release::pointer release {Database::Release::getById(context.db.getSession(), id.value)};
Database::Release::pointer release {Database::Release::getById(context.dbSession, id.value)};
if (!release)
continue;
@@ -1465,7 +1472,7 @@ handleStarRequest(RequestContext& context)
for (const Id& id : params.trackIds)
{
Database::Track::pointer track {Database::Track::getById(context.db.getSession(), id.value)};
Database::Track::pointer track {Database::Track::getById(context.dbSession, id.value)};
if (!track)
continue;
@@ -1492,15 +1499,15 @@ handleUnstarRequest(RequestContext& context)
{
StarParameters params {getStarParameters(context.parameters)};
Wt::Dbo::Transaction transaction {context.db.getSession()};
auto transaction {context.dbSession.createSharedTransaction()};
Database::User::pointer user {context.db.getUser(context.userName)};
Database::User::pointer user {context.dbSession.getUser(context.userName)};
if (!user)
throw Error {Error::Code::RequestedDataNotFound};
for (const Id& id : params.artistIds)
{
Database::Artist::pointer artist {Database::Artist::getById(context.db.getSession(), id.value)};
Database::Artist::pointer artist {Database::Artist::getById(context.dbSession, id.value)};
if (!artist)
continue;
@@ -1509,7 +1516,7 @@ handleUnstarRequest(RequestContext& context)
for (const Id& id : params.releaseIds)
{
Database::Release::pointer release {Database::Release::getById(context.db.getSession(), id.value)};
Database::Release::pointer release {Database::Release::getById(context.dbSession, id.value)};
if (!release)
continue;
@@ -1518,7 +1525,7 @@ handleUnstarRequest(RequestContext& context)
for (const Id& id : params.trackIds)
{
Database::Track::pointer track {Database::Track::getById(context.db.getSession(), id.value)};
Database::Track::pointer track {Database::Track::getById(context.dbSession, id.value)};
if (!track)
continue;
@@ -1547,13 +1554,13 @@ handleUpdatePlaylistRequest(RequestContext& context)
std::vector<std::size_t> trackPositionsToRemove {getMultiParametersAs<std::size_t>(context.parameters, "songIndexToRemove")};
Wt::Dbo::Transaction transaction {context.db.getSession()};
auto transaction {context.dbSession.createSharedTransaction()};
Database::User::pointer user {context.db.getUser(context.userName)};
Database::User::pointer user {context.dbSession.getUser(context.userName)};
if (!user)
throw Error {Error::Code::RequestedDataNotFound};
Database::TrackList::pointer tracklist {Database::TrackList::getById(context.db.getSession(), id.value)};
Database::TrackList::pointer tracklist {Database::TrackList::getById(context.dbSession, id.value)};
if (!tracklist
|| tracklist->getUser() != user
|| tracklist->getType() != Database::TrackList::Type::Playlist)
@@ -1582,11 +1589,11 @@ handleUpdatePlaylistRequest(RequestContext& context)
// Add tracks
for (const Id& trackIdToAdd : trackIdsToAdd)
{
Database::Track::pointer track {Database::Track::getById(context.db.getSession(), trackIdToAdd.value)};
Database::Track::pointer track {Database::Track::getById(context.dbSession, trackIdToAdd.value)};
if (!track)
continue;
Database::TrackListEntry::create(context.db.getSession(), track, tracklist );
Database::TrackListEntry::create(context.dbSession, track, tracklist );
}
return Response::createOkResponse();
@@ -1604,9 +1611,9 @@ createTranscoder(RequestContext& context)
boost::filesystem::path trackPath;
{
Wt::Dbo::Transaction transaction {context.db.getSession()};
auto transaction {context.dbSession.createSharedTransaction()};
Database::User::pointer user {context.db.getUser(context.userName)};
Database::User::pointer user {context.dbSession.getUser(context.userName)};
if (!user)
throw Error {Error::Code::RequestedDataNotFound};
@@ -1616,7 +1623,7 @@ createTranscoder(RequestContext& context)
*maxBitRate = clamp(*maxBitRate, std::size_t {48}, user->getMaxAudioTranscodeBitrate() / 1000);
auto track {Database::Track::getById(context.db.getSession(), id.value)};
auto track {Database::Track::getById(context.dbSession, id.value)};
if (!track)
throw Error {Error::Code::RequestedDataNotFound};
@@ -1685,10 +1692,10 @@ handleGetCoverArt(RequestContext& context, Wt::Http::ResponseContinuation*)
switch (id.type)
{
case Id::Type::Track:
res.data = getService<CoverArt::Grabber>()->getFromTrack(context.db.getSession(), id.value, Image::Format::JPEG, size);
res.data = getService<CoverArt::Grabber>()->getFromTrack(context.dbSession, id.value, Image::Format::JPEG, size);
break;
case Id::Type::Release:
res.data = getService<CoverArt::Grabber>()->getFromRelease(context.db.getSession(), id.value, Image::Format::JPEG, size);
res.data = getService<CoverArt::Grabber>()->getFromRelease(context.dbSession, id.value, Image::Format::JPEG, size);
break;
default:
throw Error {Error::CustomType::BadId};