Rework DB session + transactions. Now can handle multiple read only transactions in parallel
This commit is contained in:
+35
-18
@@ -25,6 +25,7 @@
|
||||
#include "Cluster.hpp"
|
||||
#include "Release.hpp"
|
||||
#include "SqlQuery.hpp"
|
||||
#include "Session.hpp"
|
||||
#include "Track.hpp"
|
||||
#include "User.hpp"
|
||||
|
||||
@@ -40,34 +41,44 @@ _MBID {MBID}
|
||||
}
|
||||
|
||||
std::vector<Artist::pointer>
|
||||
Artist::getByName(Wt::Dbo::Session& session, const std::string& name)
|
||||
Artist::getByName(Session& session, const std::string& name)
|
||||
{
|
||||
Wt::Dbo::collection<Artist::pointer> res = session.find<Artist>().where("name = ?").bind( std::string{name, 0, _maxNameLength} );
|
||||
session.checkSharedLocked();
|
||||
|
||||
Wt::Dbo::collection<Artist::pointer> res = session.getDboSession().find<Artist>().where("name = ?").bind( std::string{name, 0, _maxNameLength} );
|
||||
return std::vector<Artist::pointer>(res.begin(), res.end());
|
||||
}
|
||||
|
||||
Artist::pointer
|
||||
Artist::getByMBID(Wt::Dbo::Session& session, const std::string& mbid)
|
||||
Artist::getByMBID(Session& session, const std::string& mbid)
|
||||
{
|
||||
return session.find<Artist>().where("mbid = ?").bind(mbid);
|
||||
session.checkSharedLocked();
|
||||
return session.getDboSession().find<Artist>().where("mbid = ?").bind(mbid);
|
||||
}
|
||||
|
||||
Artist::pointer
|
||||
Artist::getById(Wt::Dbo::Session& session, IdType id)
|
||||
Artist::getById(Session& session, IdType id)
|
||||
{
|
||||
return session.find<Artist>().where("id = ?").bind(id);
|
||||
session.checkSharedLocked();
|
||||
return session.getDboSession().find<Artist>().where("id = ?").bind(id);
|
||||
}
|
||||
|
||||
Artist::pointer
|
||||
Artist::create(Wt::Dbo::Session& session, const std::string& name, const std::string& MBID)
|
||||
Artist::create(Session& session, const std::string& name, const std::string& MBID)
|
||||
{
|
||||
return session.add(std::make_unique<Artist>(name, MBID));
|
||||
session.checkUniqueLocked();
|
||||
|
||||
Artist::pointer res {session.getDboSession().add(std::make_unique<Artist>(name, MBID))};
|
||||
session.getDboSession().flush();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<Artist::pointer>
|
||||
Artist::getAll(Wt::Dbo::Session& session, boost::optional<std::size_t> offset, boost::optional<std::size_t> size)
|
||||
Artist::getAll(Session& session, boost::optional<std::size_t> offset, boost::optional<std::size_t> size)
|
||||
{
|
||||
Wt::Dbo::collection<pointer> res = session.find<Artist>()
|
||||
session.checkSharedLocked();
|
||||
Wt::Dbo::collection<pointer> res = session.getDboSession().find<Artist>()
|
||||
.offset(offset ? static_cast<int>(*offset) : -1)
|
||||
.limit(size ? static_cast<int>(*size) : -1)
|
||||
.orderBy("sort_name COLLATE NOCASE");
|
||||
@@ -76,19 +87,22 @@ Artist::getAll(Wt::Dbo::Session& session, boost::optional<std::size_t> offset, b
|
||||
}
|
||||
|
||||
std::vector<Artist::pointer>
|
||||
Artist::getAllOrphans(Wt::Dbo::Session& session)
|
||||
Artist::getAllOrphans(Session& session)
|
||||
{
|
||||
Wt::Dbo::collection<Wt::Dbo::ptr<Artist>> res {session.query<Wt::Dbo::ptr<Artist>>("SELECT DISTINCT a FROM artist a WHERE NOT EXISTS(SELECT 1 FROM track t INNER JOIN track_artist_link t_a_l ON t_a_l.artist_id = a.id WHERE t.id = t_a_l.track_id)")};
|
||||
session.checkSharedLocked();
|
||||
Wt::Dbo::collection<Wt::Dbo::ptr<Artist>> res {session.getDboSession().query<Wt::Dbo::ptr<Artist>>("SELECT DISTINCT a FROM artist a WHERE NOT EXISTS(SELECT 1 FROM track t INNER JOIN track_artist_link t_a_l ON t_a_l.artist_id = a.id WHERE t.id = t_a_l.track_id)")};
|
||||
|
||||
return std::vector<pointer>(res.begin(), res.end());
|
||||
}
|
||||
|
||||
static
|
||||
Wt::Dbo::Query<Artist::pointer>
|
||||
getQuery(Wt::Dbo::Session& session,
|
||||
getQuery(Session& session,
|
||||
const std::set<IdType>& clusterIds,
|
||||
const std::vector<std::string>& keywords)
|
||||
{
|
||||
session.checkSharedLocked();
|
||||
|
||||
WhereClause where;
|
||||
|
||||
std::ostringstream oss;
|
||||
@@ -115,7 +129,7 @@ getQuery(Wt::Dbo::Session& session,
|
||||
|
||||
oss << " ORDER BY a.sort_name COLLATE NOCASE";
|
||||
|
||||
Wt::Dbo::Query<Artist::pointer> query = session.query<Artist::pointer>( oss.str() );
|
||||
Wt::Dbo::Query<Artist::pointer> query = session.getDboSession().query<Artist::pointer>( oss.str() );
|
||||
|
||||
for (const std::string& bindArg : where.getBindArgs())
|
||||
{
|
||||
@@ -126,20 +140,22 @@ getQuery(Wt::Dbo::Session& session,
|
||||
}
|
||||
|
||||
std::vector<Artist::pointer>
|
||||
Artist::getByFilter(Wt::Dbo::Session& session, const std::set<IdType>& clusters)
|
||||
Artist::getByFilter(Session& session, const std::set<IdType>& clusters)
|
||||
{
|
||||
session.checkSharedLocked();
|
||||
bool more;
|
||||
return getByFilter(session, clusters, {}, {}, {}, more);
|
||||
}
|
||||
|
||||
std::vector<Artist::pointer>
|
||||
Artist::getByFilter(Wt::Dbo::Session& session,
|
||||
Artist::getByFilter(Session& session,
|
||||
const std::set<IdType>& clusters,
|
||||
const std::vector<std::string>& keywords,
|
||||
boost::optional<std::size_t> offset,
|
||||
boost::optional<std::size_t> size,
|
||||
bool& moreResults)
|
||||
{
|
||||
session.checkSharedLocked();
|
||||
Wt::Dbo::collection<Artist::pointer> collection = getQuery(session, clusters, keywords)
|
||||
.limit(size ? static_cast<int>(*size) + 1 : -1)
|
||||
.offset(offset ? static_cast<int>(*offset) : -1);
|
||||
@@ -158,9 +174,10 @@ Artist::getByFilter(Wt::Dbo::Session& session,
|
||||
}
|
||||
|
||||
std::vector<Artist::pointer>
|
||||
Artist::getLastAdded(Wt::Dbo::Session& session, Wt::WDateTime after, boost::optional<std::size_t> limit)
|
||||
Artist::getLastAdded(Session& session, Wt::WDateTime after, boost::optional<std::size_t> limit)
|
||||
{
|
||||
Wt::Dbo::collection<Artist::pointer> res = session.query<Artist::pointer>("SELECT a from artist a INNER JOIN track_artist_link t_a_l ON t_a_l.artist_id = a.id INNER JOIN track t ON t.id = t_a_l.track_id")
|
||||
session.checkSharedLocked();
|
||||
Wt::Dbo::collection<Artist::pointer> res = session.getDboSession().query<Artist::pointer>("SELECT a from artist a INNER JOIN track_artist_link t_a_l ON t_a_l.artist_id = a.id INNER JOIN track t ON t.id = t_a_l.track_id")
|
||||
.where("t.file_added > ?").bind(after)
|
||||
.groupBy("a.id")
|
||||
.orderBy("t.file_added DESC")
|
||||
|
||||
+10
-10
@@ -36,6 +36,7 @@ namespace Database
|
||||
class Cluster;
|
||||
class ClusterType;
|
||||
class Release;
|
||||
class Session;
|
||||
class Track;
|
||||
class User;
|
||||
|
||||
@@ -49,21 +50,21 @@ class Artist : public Wt::Dbo::Dbo<Artist>
|
||||
Artist(const std::string& name, const std::string& MBID = "");
|
||||
|
||||
// Accessors
|
||||
static pointer getByMBID(Wt::Dbo::Session& session, const std::string& MBID);
|
||||
static pointer getById(Wt::Dbo::Session& session, IdType id);
|
||||
static std::vector<pointer> getByName(Wt::Dbo::Session& session, const std::string& name);
|
||||
static std::vector<pointer> getByFilter(Wt::Dbo::Session& session,
|
||||
static pointer getByMBID(Session& session, const std::string& MBID);
|
||||
static pointer getById(Session& session, IdType id);
|
||||
static std::vector<pointer> getByName(Session& session, const std::string& name);
|
||||
static std::vector<pointer> getByFilter(Session& session,
|
||||
const std::set<IdType>& clusters); // at least one track that belongs to these clusters
|
||||
static std::vector<pointer> getByFilter(Wt::Dbo::Session& session,
|
||||
static std::vector<pointer> getByFilter(Session& session,
|
||||
const std::set<IdType>& clusters, // at least one track that belongs to these clusters
|
||||
const std::vector<std::string>& keywords, // name must match all of these keywords
|
||||
boost::optional<std::size_t> offset,
|
||||
boost::optional<std::size_t> size,
|
||||
bool& moreExpected);
|
||||
|
||||
static std::vector<pointer> getAll(Wt::Dbo::Session& session, boost::optional<std::size_t> offset = {}, boost::optional<std::size_t> size = {});
|
||||
static std::vector<pointer> getAllOrphans(Wt::Dbo::Session& session); // No track related
|
||||
static std::vector<pointer> getLastAdded(Wt::Dbo::Session& session, Wt::WDateTime after, boost::optional<std::size_t> size = {});
|
||||
static std::vector<pointer> getAll(Session& session, boost::optional<std::size_t> offset = {}, boost::optional<std::size_t> size = {});
|
||||
static std::vector<pointer> getAllOrphans(Session& session); // No track related
|
||||
static std::vector<pointer> getLastAdded(Session& session, Wt::WDateTime after, boost::optional<std::size_t> size = {});
|
||||
|
||||
// Accessors
|
||||
const std::string& getName(void) const { return _name; }
|
||||
@@ -84,8 +85,7 @@ class Artist : public Wt::Dbo::Dbo<Artist>
|
||||
void setSortName(const std::string& sortName);
|
||||
|
||||
// Create
|
||||
static pointer create(Wt::Dbo::Session& session, const std::string& name, const std::string& MBID = "");
|
||||
|
||||
static pointer create(Session& session, const std::string& name, const std::string& MBID = "");
|
||||
|
||||
template<class Action>
|
||||
void persist(Action& a)
|
||||
|
||||
+46
-18
@@ -22,6 +22,7 @@
|
||||
#include "Artist.hpp"
|
||||
#include "Release.hpp"
|
||||
#include "ScanSettings.hpp"
|
||||
#include "Session.hpp"
|
||||
#include "SqlQuery.hpp"
|
||||
#include "Track.hpp"
|
||||
|
||||
@@ -38,31 +39,42 @@ Cluster::Cluster(Wt::Dbo::ptr<ClusterType> type, std::string name)
|
||||
}
|
||||
|
||||
Cluster::pointer
|
||||
Cluster::create(Wt::Dbo::Session& session, Wt::Dbo::ptr<ClusterType> type, std::string name)
|
||||
Cluster::create(Session& session, Wt::Dbo::ptr<ClusterType> type, std::string name)
|
||||
{
|
||||
return session.add(std::make_unique<Cluster>(type, name));
|
||||
session.checkUniqueLocked();
|
||||
|
||||
Cluster::pointer res {session.getDboSession().add(std::make_unique<Cluster>(type, name))};
|
||||
session.getDboSession().flush();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<Cluster::pointer>
|
||||
Cluster::getAll(Wt::Dbo::Session& session)
|
||||
Cluster::getAll(Session& session)
|
||||
{
|
||||
Wt::Dbo::collection<Cluster::pointer> res = session.find<Cluster>();
|
||||
session.checkSharedLocked();
|
||||
|
||||
Wt::Dbo::collection<Cluster::pointer> res {session.getDboSession().find<Cluster>()};
|
||||
|
||||
return std::vector<Cluster::pointer>(res.begin(), res.end());
|
||||
}
|
||||
|
||||
std::vector<Cluster::pointer>
|
||||
Cluster::getAllOrphans(Wt::Dbo::Session& session)
|
||||
Cluster::getAllOrphans(Session& session)
|
||||
{
|
||||
Wt::Dbo::collection<Cluster::pointer> res {session.query<Cluster::pointer>("SELECT DISTINCT c FROM cluster c WHERE NOT EXISTS(SELECT 1 FROM track t INNER JOIN track_cluster t_c ON t.id = t_c.track_id)")};
|
||||
session.checkSharedLocked();
|
||||
|
||||
Wt::Dbo::collection<Cluster::pointer> res {session.getDboSession().query<Cluster::pointer>("SELECT DISTINCT c FROM cluster c WHERE NOT EXISTS(SELECT 1 FROM track t INNER JOIN track_cluster t_c ON t.id = t_c.track_id)")};
|
||||
|
||||
return std::vector<Cluster::pointer>(res.begin(), res.end());
|
||||
}
|
||||
|
||||
Cluster::pointer
|
||||
Cluster::getById(Wt::Dbo::Session& session, IdType id)
|
||||
Cluster::getById(Session& session, IdType id)
|
||||
{
|
||||
return session.find<Cluster>().where("id = ?").bind(id);
|
||||
session.checkSharedLocked();
|
||||
|
||||
return session.getDboSession().find<Cluster>().where("id = ?").bind(id);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -74,6 +86,9 @@ Cluster::addTrack(Wt::Dbo::ptr<Track> track)
|
||||
std::vector<Wt::Dbo::ptr<Track>>
|
||||
Cluster::getTracks(int offset, int limit) const
|
||||
{
|
||||
assert(session());
|
||||
assert(IdIsValid(self()->id()));
|
||||
|
||||
Wt::Dbo::collection<Track::pointer> res = session()->query<Track::pointer>("SELECT t FROM track t INNER JOIN cluster c ON c.id = t_c.cluster_id INNER JOIN track_cluster t_c ON t_c.track_id = t.id")
|
||||
.where("c.id = ?").bind(self()->id())
|
||||
.offset(offset)
|
||||
@@ -113,38 +128,51 @@ ClusterType::ClusterType(std::string name)
|
||||
}
|
||||
|
||||
std::vector<ClusterType::pointer>
|
||||
ClusterType::getAllOrphans(Wt::Dbo::Session& session)
|
||||
ClusterType::getAllOrphans(Session& session)
|
||||
{
|
||||
Wt::Dbo::collection<pointer> res = session.query<Wt::Dbo::ptr<ClusterType>>("select c_t from cluster_type c_t LEFT OUTER JOIN cluster c ON c_t.id = c.cluster_type_id WHERE c.id IS NULL");
|
||||
session.checkSharedLocked();
|
||||
|
||||
Wt::Dbo::collection<pointer> res = session.getDboSession().query<Wt::Dbo::ptr<ClusterType>>("select c_t from cluster_type c_t LEFT OUTER JOIN cluster c ON c_t.id = c.cluster_type_id WHERE c.id IS NULL");
|
||||
|
||||
return std::vector<pointer>(res.begin(), res.end());
|
||||
}
|
||||
|
||||
|
||||
ClusterType::pointer
|
||||
ClusterType::getByName(Wt::Dbo::Session& session, std::string name)
|
||||
ClusterType::getByName(Session& session, std::string name)
|
||||
{
|
||||
return session.find<ClusterType>().where("name = ?").bind(name);
|
||||
session.checkSharedLocked();
|
||||
|
||||
return session.getDboSession().find<ClusterType>().where("name = ?").bind(name);
|
||||
}
|
||||
|
||||
ClusterType::pointer
|
||||
ClusterType::getById(Wt::Dbo::Session& session, IdType id)
|
||||
ClusterType::getById(Session& session, IdType id)
|
||||
{
|
||||
return session.find<ClusterType>().where("id= ?").bind(id);
|
||||
session.checkSharedLocked();
|
||||
|
||||
return session.getDboSession().find<ClusterType>().where("id= ?").bind(id);
|
||||
}
|
||||
|
||||
std::vector<ClusterType::pointer>
|
||||
ClusterType::getAll(Wt::Dbo::Session& session)
|
||||
ClusterType::getAll(Session& session)
|
||||
{
|
||||
Wt::Dbo::collection<pointer> res = session.find<ClusterType>();
|
||||
session.checkSharedLocked();
|
||||
|
||||
Wt::Dbo::collection<pointer> res = session.getDboSession().find<ClusterType>();
|
||||
|
||||
return std::vector<pointer>(res.begin(), res.end());
|
||||
}
|
||||
|
||||
ClusterType::pointer
|
||||
ClusterType::create(Wt::Dbo::Session& session, std::string name)
|
||||
ClusterType::create(Session& session, std::string name)
|
||||
{
|
||||
return session.add(std::make_unique<ClusterType>(name));
|
||||
session.checkUniqueLocked();
|
||||
|
||||
ClusterType::pointer res {session.getDboSession().add(std::make_unique<ClusterType>(name))};
|
||||
session.getDboSession().flush();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
Cluster::pointer
|
||||
|
||||
+11
-10
@@ -33,6 +33,7 @@ namespace Database {
|
||||
class Track;
|
||||
class ClusterType;
|
||||
class ScanSettings;
|
||||
class Session;
|
||||
|
||||
class Cluster : public Wt::Dbo::Dbo<Cluster>
|
||||
{
|
||||
@@ -43,12 +44,12 @@ class Cluster : public Wt::Dbo::Dbo<Cluster>
|
||||
Cluster(Wt::Dbo::ptr<ClusterType> type, std::string name);
|
||||
|
||||
// Find utility
|
||||
static std::vector<pointer> getAll(Wt::Dbo::Session& session);
|
||||
static std::vector<pointer> getAllOrphans(Wt::Dbo::Session& session);
|
||||
static pointer getById(Wt::Dbo::Session& session, IdType id);
|
||||
static std::vector<pointer> getAll(Session& session);
|
||||
static std::vector<pointer> getAllOrphans(Session& session);
|
||||
static pointer getById(Session& session, IdType id);
|
||||
|
||||
// Create utility
|
||||
static pointer create(Wt::Dbo::Session& session, Wt::Dbo::ptr<ClusterType> type, std::string name);
|
||||
static pointer create(Session& session, Wt::Dbo::ptr<ClusterType> type, std::string name);
|
||||
|
||||
// Accessors
|
||||
const std::string& getName() const { return _name; }
|
||||
@@ -89,13 +90,13 @@ class ClusterType : public Wt::Dbo::Dbo<ClusterType>
|
||||
ClusterType() {}
|
||||
ClusterType(std::string name);
|
||||
|
||||
static std::vector<pointer> getAllOrphans(Wt::Dbo::Session& session);
|
||||
static pointer getByName(Wt::Dbo::Session& session, std::string name);
|
||||
static pointer getById(Wt::Dbo::Session& session, IdType id);
|
||||
static std::vector<pointer> getAll(Wt::Dbo::Session& session);
|
||||
static std::vector<pointer> getAllOrphans(Session& session);
|
||||
static pointer getByName(Session& session, std::string name);
|
||||
static pointer getById(Session& session, IdType id);
|
||||
static std::vector<pointer> getAll(Session& session);
|
||||
|
||||
static pointer create(Wt::Dbo::Session& session, std::string name);
|
||||
static void remove(Wt::Dbo::Session& session, std::string name);
|
||||
static pointer create(Session& session, std::string name);
|
||||
static void remove(Session& session, std::string name);
|
||||
|
||||
// Accessors
|
||||
const std::string& getName(void) const { return _name; }
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "Database.hpp"
|
||||
|
||||
#include <Wt/Dbo/FixedSqlConnectionPool.h>
|
||||
#include <Wt/Dbo/backend/Sqlite3.h>
|
||||
|
||||
#include "utils/Logger.hpp"
|
||||
#include "User.hpp"
|
||||
|
||||
namespace Database {
|
||||
|
||||
// Session living class handling the database and the login
|
||||
Database::Database(const boost::filesystem::path& dbPath)
|
||||
{
|
||||
LMS_LOG(DB, INFO) << "Creating connection pool on file " << dbPath.string();
|
||||
|
||||
std::unique_ptr<Wt::Dbo::backend::Sqlite3> connection {std::make_unique<Wt::Dbo::backend::Sqlite3>(dbPath.string())};
|
||||
connection->executeSql("pragma journal_mode=WAL");
|
||||
// connection->setProperty("show-queries", "true");
|
||||
|
||||
auto connectionPool = std::make_unique<Wt::Dbo::FixedSqlConnectionPool>(std::move(connection), 10);
|
||||
connectionPool->setTimeout(std::chrono::seconds(10));
|
||||
|
||||
_connectionPool = std::move(connectionPool);
|
||||
|
||||
{
|
||||
auto session {createSession()};
|
||||
session->prepareTables();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::unique_ptr<Session>
|
||||
Database::createSession()
|
||||
{
|
||||
return std::unique_ptr<Session>{new Session {_sharedMutex, *_connectionPool.get()}};
|
||||
}
|
||||
|
||||
} // namespace Database
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <shared_mutex>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <Wt/Dbo/SqlConnectionPool.h>
|
||||
|
||||
#include "Session.hpp"
|
||||
|
||||
namespace Database {
|
||||
|
||||
// Session living class handling the database and the login
|
||||
class Database
|
||||
{
|
||||
public:
|
||||
|
||||
Database(const boost::filesystem::path& dbPath);
|
||||
|
||||
std::unique_ptr<Session> createSession();
|
||||
|
||||
private:
|
||||
std::shared_timed_mutex _sharedMutex;
|
||||
std::unique_ptr<Wt::Dbo::SqlConnectionPool> _connectionPool;
|
||||
};
|
||||
|
||||
} // namespace Database
|
||||
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <memory>
|
||||
|
||||
#include <Wt/Dbo/Dbo.h>
|
||||
#include <Wt/Dbo/SqlConnectionPool.h>
|
||||
|
||||
#include <Wt/Auth/Dbo/UserDatabase.h>
|
||||
#include <Wt/Auth/Login.h>
|
||||
#include <Wt/Auth/PasswordService.h>
|
||||
|
||||
#include "User.hpp"
|
||||
|
||||
namespace Database {
|
||||
|
||||
using UserDatabase = Wt::Auth::Dbo::UserDatabase<AuthInfo>;
|
||||
|
||||
// Session living class handling the database and the login
|
||||
class Handler
|
||||
{
|
||||
public:
|
||||
|
||||
Handler(Wt::Dbo::SqlConnectionPool& connectionPool);
|
||||
~Handler();
|
||||
|
||||
Wt::Dbo::Session& getSession() { return _session; }
|
||||
|
||||
void optimize();
|
||||
|
||||
Wt::Dbo::ptr<User> getCurrentUser(); // get the current user, may return empty
|
||||
Wt::Dbo::ptr<User> getUser(const std::string& loginName);
|
||||
Wt::Dbo::ptr<User> getUser(const Wt::Auth::User& authUser);
|
||||
Wt::Dbo::ptr<User> createUser(const Wt::Auth::User& authUser);
|
||||
|
||||
Wt::Auth::AbstractUserDatabase& getUserDatabase();
|
||||
Wt::Auth::Login& getLogin() { return _login; } // TODO move
|
||||
|
||||
// Long living shared associated services
|
||||
static void configureAuth();
|
||||
|
||||
static const Wt::Auth::AuthService& getAuthService();
|
||||
static const Wt::Auth::PasswordService& getPasswordService();
|
||||
|
||||
static std::unique_ptr<Wt::Dbo::SqlConnectionPool> createConnectionPool(boost::filesystem::path db);
|
||||
|
||||
private:
|
||||
|
||||
Wt::Dbo::Session _session;
|
||||
UserDatabase* _users;
|
||||
Wt::Auth::Login _login;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Database
|
||||
|
||||
|
||||
+46
-24
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "Artist.hpp"
|
||||
#include "Cluster.hpp"
|
||||
#include "Session.hpp"
|
||||
#include "SqlQuery.hpp"
|
||||
#include "Track.hpp"
|
||||
#include "User.hpp"
|
||||
@@ -38,41 +39,56 @@ _MBID(MBID)
|
||||
}
|
||||
|
||||
std::vector<Release::pointer>
|
||||
Release::getByName(Wt::Dbo::Session& session, const std::string& name)
|
||||
Release::getByName(Session& session, const std::string& name)
|
||||
{
|
||||
Wt::Dbo::collection<Release::pointer> res = session.find<Release>().where("name = ?").bind( std::string(name, 0, _maxNameLength) );
|
||||
session.checkUniqueLocked();
|
||||
|
||||
Wt::Dbo::collection<Release::pointer> res = session.getDboSession().find<Release>().where("name = ?").bind( std::string(name, 0, _maxNameLength) );
|
||||
return std::vector<Release::pointer>(res.begin(), res.end());
|
||||
}
|
||||
|
||||
Release::pointer
|
||||
Release::getByMBID(Wt::Dbo::Session& session, const std::string& mbid)
|
||||
Release::getByMBID(Session& session, const std::string& mbid)
|
||||
{
|
||||
return session.find<Release>().where("mbid = ?").bind(mbid);
|
||||
session.checkSharedLocked();
|
||||
|
||||
return session.getDboSession().find<Release>().where("mbid = ?").bind(mbid);
|
||||
}
|
||||
|
||||
Release::pointer
|
||||
Release::getById(Wt::Dbo::Session& session, IdType id)
|
||||
Release::getById(Session& session, IdType id)
|
||||
{
|
||||
return session.find<Release>().where("id = ?").bind(id);
|
||||
session.checkSharedLocked();
|
||||
|
||||
return session.getDboSession().find<Release>().where("id = ?").bind(id);
|
||||
}
|
||||
|
||||
Release::pointer
|
||||
Release::create(Wt::Dbo::Session& session, const std::string& name, const std::string& MBID)
|
||||
Release::create(Session& session, const std::string& name, const std::string& MBID)
|
||||
{
|
||||
return session.add(std::make_unique<Release>(name, MBID));
|
||||
session.checkSharedLocked();
|
||||
|
||||
Release::pointer res {session.getDboSession().add(std::make_unique<Release>(name, MBID))};
|
||||
session.getDboSession().flush();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
Release::getCount(Wt::Dbo::Session& session)
|
||||
Release::getCount(Session& session)
|
||||
{
|
||||
Wt::Dbo::collection<pointer> releases {session.find<Release>()};
|
||||
session.checkSharedLocked();
|
||||
|
||||
Wt::Dbo::collection<pointer> releases {session.getDboSession().find<Release>()};
|
||||
return releases.size();
|
||||
}
|
||||
|
||||
std::vector<Release::pointer>
|
||||
Release::getAll(Wt::Dbo::Session& session, boost::optional<std::size_t> offset, boost::optional<std::size_t> size)
|
||||
Release::getAll(Session& session, boost::optional<std::size_t> offset, boost::optional<std::size_t> size)
|
||||
{
|
||||
Wt::Dbo::collection<pointer> res = session.find<Release>()
|
||||
session.checkSharedLocked();
|
||||
|
||||
Wt::Dbo::collection<pointer> res = session.getDboSession().find<Release>()
|
||||
.offset(offset ? static_cast<int>(*offset) : -1)
|
||||
.limit(size ? static_cast<int>(*size) : -1)
|
||||
.orderBy("name COLLATE NOCASE");
|
||||
@@ -81,9 +97,11 @@ Release::getAll(Wt::Dbo::Session& session, boost::optional<std::size_t> offset,
|
||||
}
|
||||
|
||||
std::vector<Release::pointer>
|
||||
Release::getAllRandom(Wt::Dbo::Session& session, boost::optional<std::size_t> size)
|
||||
Release::getAllRandom(Session& session, boost::optional<std::size_t> size)
|
||||
{
|
||||
Wt::Dbo::collection<pointer> res = session.find<Release>()
|
||||
session.checkSharedLocked();
|
||||
|
||||
Wt::Dbo::collection<pointer> res = session.getDboSession().find<Release>()
|
||||
.limit(size ? static_cast<int>(*size) : -1)
|
||||
.orderBy("RANDOM()");
|
||||
|
||||
@@ -91,17 +109,21 @@ Release::getAllRandom(Wt::Dbo::Session& session, boost::optional<std::size_t> si
|
||||
}
|
||||
|
||||
std::vector<Release::pointer>
|
||||
Release::getAllOrphans(Wt::Dbo::Session& session)
|
||||
Release::getAllOrphans(Session& session)
|
||||
{
|
||||
Wt::Dbo::collection<Release::pointer> res = session.query<Wt::Dbo::ptr<Release>>("select r from release r LEFT OUTER JOIN Track t ON r.id = t.release_id WHERE t.id IS NULL");
|
||||
session.checkSharedLocked();
|
||||
|
||||
Wt::Dbo::collection<Release::pointer> res = session.getDboSession().query<Wt::Dbo::ptr<Release>>("select r from release r LEFT OUTER JOIN Track t ON r.id = t.release_id WHERE t.id IS NULL");
|
||||
|
||||
return std::vector<pointer>(res.begin(), res.end());
|
||||
}
|
||||
|
||||
std::vector<Release::pointer>
|
||||
Release::getLastAdded(Wt::Dbo::Session& session, Wt::WDateTime after, boost::optional<std::size_t> offset, boost::optional<std::size_t> limit)
|
||||
Release::getLastAdded(Session& session, Wt::WDateTime after, boost::optional<std::size_t> offset, boost::optional<std::size_t> limit)
|
||||
{
|
||||
Wt::Dbo::collection<Release::pointer> res = session.query<Release::pointer>("SELECT r from release r INNER JOIN track t ON r.id = t.release_id")
|
||||
session.checkSharedLocked();
|
||||
|
||||
Wt::Dbo::collection<Release::pointer> res = session.getDboSession().query<Release::pointer>("SELECT r from release r INNER JOIN track t ON r.id = t.release_id")
|
||||
.where("t.file_added > ?").bind(after)
|
||||
.groupBy("r.id")
|
||||
.orderBy("t.file_added DESC")
|
||||
@@ -113,9 +135,9 @@ Release::getLastAdded(Wt::Dbo::Session& session, Wt::WDateTime after, boost::opt
|
||||
|
||||
static
|
||||
Wt::Dbo::Query<Release::pointer>
|
||||
getQuery(Wt::Dbo::Session& session,
|
||||
getQuery(Session& session,
|
||||
const std::set<IdType>& clusterIds,
|
||||
const std::vector<std::string> keywords)
|
||||
const std::vector<std::string>& keywords)
|
||||
{
|
||||
WhereClause where;
|
||||
|
||||
@@ -144,7 +166,7 @@ getQuery(Wt::Dbo::Session& session,
|
||||
|
||||
oss << " ORDER BY r.name COLLATE NOCASE";
|
||||
|
||||
Wt::Dbo::Query<Release::pointer> query = session.query<Release::pointer>( oss.str() );
|
||||
Wt::Dbo::Query<Release::pointer> query = session.getDboSession().query<Release::pointer>( oss.str() );
|
||||
|
||||
for (const std::string& bindArg : where.getBindArgs())
|
||||
query.bind(bindArg);
|
||||
@@ -153,16 +175,16 @@ getQuery(Wt::Dbo::Session& session,
|
||||
}
|
||||
|
||||
std::vector<Release::pointer>
|
||||
Release::getByFilter(Wt::Dbo::Session& session, const std::set<IdType>& clusterIds)
|
||||
Release::getByFilter(Session& session, const std::set<IdType>& clusterIds)
|
||||
{
|
||||
bool moreResults;
|
||||
return getByFilter(session, clusterIds, {}, {}, {}, moreResults);
|
||||
}
|
||||
|
||||
std::vector<Release::pointer>
|
||||
Release::getByFilter(Wt::Dbo::Session& session,
|
||||
Release::getByFilter(Session& session,
|
||||
const std::set<IdType>& clusterIds,
|
||||
const std::vector<std::string> keywords,
|
||||
const std::vector<std::string>& keywords,
|
||||
boost::optional<std::size_t> offset,
|
||||
boost::optional<std::size_t> size,
|
||||
bool& moreResults)
|
||||
|
||||
+12
-12
@@ -46,19 +46,19 @@ class Release : public Wt::Dbo::Dbo<Release>
|
||||
Release(const std::string& name, const std::string& MBID = "");
|
||||
|
||||
// Accessors
|
||||
static std::size_t getCount(Wt::Dbo::Session& session);
|
||||
static pointer getByMBID(Wt::Dbo::Session& session, const std::string& MBID);
|
||||
static std::vector<pointer> getByName(Wt::Dbo::Session& session, const std::string& name);
|
||||
static pointer getById(Wt::Dbo::Session& session, IdType id);
|
||||
static std::vector<pointer> getAllOrphans(Wt::Dbo::Session& session); // no track related
|
||||
static std::vector<pointer> getAll(Wt::Dbo::Session& session, boost::optional<std::size_t> offset = {}, boost::optional<std::size_t> size = {});
|
||||
static std::vector<pointer> getAllRandom(Wt::Dbo::Session& session, boost::optional<std::size_t> size = {});
|
||||
static std::vector<pointer> getLastAdded(Wt::Dbo::Session& session, Wt::WDateTime after, boost::optional<std::size_t> offset = {}, boost::optional<std::size_t> size = {});
|
||||
static std::size_t getCount(Session& session);
|
||||
static pointer getByMBID(Session& session, const std::string& MBID);
|
||||
static std::vector<pointer> getByName(Session& session, const std::string& name);
|
||||
static pointer getById(Session& session, IdType id);
|
||||
static std::vector<pointer> getAllOrphans(Session& session); // no track related
|
||||
static std::vector<pointer> getAll(Session& session, boost::optional<std::size_t> offset = {}, boost::optional<std::size_t> size = {});
|
||||
static std::vector<pointer> getAllRandom(Session& session, boost::optional<std::size_t> size = {});
|
||||
static std::vector<pointer> getLastAdded(Session& session, Wt::WDateTime after, boost::optional<std::size_t> offset = {}, boost::optional<std::size_t> size = {});
|
||||
|
||||
static std::vector<pointer> getByFilter(Wt::Dbo::Session& session, const std::set<IdType>& clusters);
|
||||
static std::vector<pointer> getByFilter(Wt::Dbo::Session& session,
|
||||
static std::vector<pointer> getByFilter(Session& session, const std::set<IdType>& clusters);
|
||||
static std::vector<pointer> getByFilter(Session& session,
|
||||
const std::set<IdType>& clusters, // at least one track that belongs to these clusters
|
||||
const std::vector<std::string> keywords, // name must match all of these keywords
|
||||
const std::vector<std::string>& keywords, // name must match all of these keywords
|
||||
boost::optional<std::size_t> offset,
|
||||
boost::optional<std::size_t> size,
|
||||
bool& moreExpected);
|
||||
@@ -72,7 +72,7 @@ class Release : public Wt::Dbo::Dbo<Release>
|
||||
std::vector<std::vector<Wt::Dbo::ptr<Cluster>>> getClusterGroups(std::vector<Wt::Dbo::ptr<ClusterType>> clusterTypes, std::size_t size) const;
|
||||
|
||||
// Create
|
||||
static pointer create(Wt::Dbo::Session& session, const std::string& name, const std::string& MBID = "");
|
||||
static pointer create(Session& session, const std::string& name, const std::string& MBID = "");
|
||||
|
||||
// Utility functions
|
||||
boost::optional<int> getReleaseYear(bool originalDate = false) const; // 0 if unknown or various
|
||||
|
||||
@@ -25,10 +25,11 @@
|
||||
#include "utils/Utils.hpp"
|
||||
|
||||
#include "Cluster.hpp"
|
||||
#include "Session.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
std::set<std::string> defaultClusterTypeNames =
|
||||
const std::set<std::string> defaultClusterTypeNames =
|
||||
{
|
||||
"GENRE",
|
||||
"ALBUMGROUPING",
|
||||
@@ -40,31 +41,38 @@ std::set<std::string> defaultClusterTypeNames =
|
||||
|
||||
namespace Database {
|
||||
|
||||
void
|
||||
ScanSettings::init(Session& session)
|
||||
{
|
||||
session.checkUniqueLocked();
|
||||
|
||||
pointer settings {get(session)};
|
||||
if (settings)
|
||||
return;
|
||||
|
||||
settings = session.getDboSession().add(std::make_unique<ScanSettings>());
|
||||
settings.modify()->setClusterTypes(session, defaultClusterTypeNames );
|
||||
}
|
||||
|
||||
ScanSettings::pointer
|
||||
ScanSettings::get(Wt::Dbo::Session& session)
|
||||
ScanSettings::get(Session& session)
|
||||
{
|
||||
pointer settings = session.find<ScanSettings>();
|
||||
if (!settings)
|
||||
{
|
||||
settings = session.add(std::make_unique<ScanSettings>());
|
||||
settings.modify()->setClusterTypes(defaultClusterTypeNames);
|
||||
}
|
||||
session.checkSharedLocked();
|
||||
|
||||
return settings;
|
||||
return session.getDboSession().find<ScanSettings>();
|
||||
}
|
||||
|
||||
std::set<boost::filesystem::path>
|
||||
ScanSettings::getAudioFileExtensions() const
|
||||
{
|
||||
auto extensions = splitString(_audioFileExtensions, " ");
|
||||
return std::set<boost::filesystem::path>(extensions.begin(), extensions.end());
|
||||
return std::set<boost::filesystem::path>(std::cbegin(extensions), std::cend(extensions));
|
||||
}
|
||||
|
||||
std::vector<ClusterType::pointer>
|
||||
ScanSettings::getClusterTypes() const
|
||||
{
|
||||
return std::vector<ClusterType::pointer>(_clusterTypes.begin(), _clusterTypes.end());
|
||||
return std::vector<ClusterType::pointer>(std::cbegin(_clusterTypes), std::cend(_clusterTypes));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -73,20 +81,34 @@ ScanSettings::setMediaDirectory(boost::filesystem::path p)
|
||||
_mediaDirectory = stringTrimEnd(p.string(), "/\\");
|
||||
}
|
||||
|
||||
void
|
||||
ScanSettings::setClusterTypes(const std::set<std::string>& clusterTypeNames)
|
||||
template <typename It>
|
||||
std::set<std::string> getNames(It begin, It end)
|
||||
{
|
||||
bool needRescan = false;
|
||||
assert(session());
|
||||
std::set<std::string> names;
|
||||
std::transform(begin, end, std::inserter(names, std::begin(names)),
|
||||
[](const ClusterType::pointer& clusterType)
|
||||
{
|
||||
return clusterType->getName();
|
||||
});
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
void
|
||||
ScanSettings::setClusterTypes(Session& session, const std::set<std::string>& clusterTypeNames)
|
||||
{
|
||||
session.checkUniqueLocked();
|
||||
|
||||
bool needRescan {};
|
||||
|
||||
// Create any missing cluster type
|
||||
for (const auto& clusterTypeName : clusterTypeNames)
|
||||
for (const std::string& clusterTypeName : clusterTypeNames)
|
||||
{
|
||||
auto clusterType = ClusterType::getByName(*session(), clusterTypeName);
|
||||
auto clusterType {ClusterType::getByName(session, clusterTypeName)};
|
||||
if (!clusterType)
|
||||
{
|
||||
LMS_LOG(DB, INFO) << "Creating cluster type " << clusterTypeName;
|
||||
clusterType = ClusterType::create(*session(), clusterTypeName);
|
||||
clusterType = ClusterType::create(session, clusterTypeName);
|
||||
_clusterTypes.insert(clusterType);
|
||||
|
||||
needRescan = true;
|
||||
@@ -94,7 +116,7 @@ ScanSettings::setClusterTypes(const std::set<std::string>& clusterTypeNames)
|
||||
}
|
||||
|
||||
// Delete no longer existing cluster types
|
||||
for (auto clusterType : _clusterTypes)
|
||||
for (ClusterType::pointer& clusterType : _clusterTypes)
|
||||
{
|
||||
if (std::none_of(clusterTypeNames.begin(), clusterTypeNames.end(),
|
||||
[clusterType](const std::string& name) { return name == clusterType->getName(); }))
|
||||
@@ -108,6 +130,5 @@ ScanSettings::setClusterTypes(const std::set<std::string>& clusterTypeNames)
|
||||
_scanVersion += 1;
|
||||
}
|
||||
|
||||
|
||||
} // namespace Database
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
namespace Database {
|
||||
|
||||
class ClusterType;
|
||||
class Session;
|
||||
|
||||
class ScanSettings : public Wt::Dbo::Dbo<ScanSettings>
|
||||
{
|
||||
@@ -40,7 +41,9 @@ class ScanSettings : public Wt::Dbo::Dbo<ScanSettings>
|
||||
Monthly
|
||||
};
|
||||
|
||||
static pointer get(Wt::Dbo::Session& session);
|
||||
static void init(Session& session);
|
||||
|
||||
static pointer get(Session& session);
|
||||
|
||||
// Getters
|
||||
std::size_t getScanVersion() const { return _scanVersion; }
|
||||
@@ -54,7 +57,7 @@ class ScanSettings : public Wt::Dbo::Dbo<ScanSettings>
|
||||
void setMediaDirectory(boost::filesystem::path p);
|
||||
void setUpdateStartTime(Wt::WTime t) { _startTime = t; }
|
||||
void setUpdatePeriod(UpdatePeriod p) { _updatePeriod = p; }
|
||||
void setClusterTypes(const std::set<std::string>& clusterTypeNames);
|
||||
void setClusterTypes(Session& session, const std::set<std::string>& clusterTypeNames);
|
||||
void setAudioFileExtensions(std::set<boost::filesystem::path> fileExtensions);
|
||||
|
||||
template<class Action>
|
||||
|
||||
@@ -17,10 +17,7 @@
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "DatabaseHandler.hpp"
|
||||
|
||||
#include <Wt/Dbo/FixedSqlConnectionPool.h>
|
||||
#include <Wt/Dbo/backend/Sqlite3.h>
|
||||
#include "Session.hpp"
|
||||
|
||||
#include <Wt/Auth/Dbo/AuthInfo.h>
|
||||
#include <Wt/Auth/Dbo/UserDatabase.h>
|
||||
@@ -43,6 +40,7 @@
|
||||
#include "TrackArtistLink.hpp"
|
||||
#include "TrackList.hpp"
|
||||
#include "TrackFeatures.hpp"
|
||||
#include "User.hpp"
|
||||
|
||||
namespace Database {
|
||||
|
||||
@@ -60,15 +58,24 @@ class VersionInfo
|
||||
public:
|
||||
using pointer = Wt::Dbo::ptr<VersionInfo>;
|
||||
|
||||
static VersionInfo::pointer get(Wt::Dbo::Session& session)
|
||||
static VersionInfo::pointer getOrCreate(Session& session)
|
||||
{
|
||||
pointer versionInfo {session.find<VersionInfo>()};
|
||||
session.checkUniqueLocked();
|
||||
|
||||
pointer versionInfo {session.getDboSession().find<VersionInfo>()};
|
||||
if (!versionInfo)
|
||||
versionInfo = session.add(std::make_unique<VersionInfo>());
|
||||
return session.getDboSession().add(std::make_unique<VersionInfo>());
|
||||
|
||||
return versionInfo;
|
||||
}
|
||||
|
||||
static VersionInfo::pointer get(Session& session)
|
||||
{
|
||||
session.checkSharedLocked();
|
||||
|
||||
return session.getDboSession().find<VersionInfo>();
|
||||
}
|
||||
|
||||
Version getVersion() const { return _version; }
|
||||
void setVersion(Version version) { _version = static_cast<int>(version); }
|
||||
|
||||
@@ -82,18 +89,18 @@ class VersionInfo
|
||||
int _version {LMS_DATABASE_VERSION};
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
doDatabaseMigrationIfNeeded(Wt::Dbo::Session& session)
|
||||
Session::doDatabaseMigrationIfNeeded()
|
||||
{
|
||||
Wt::Dbo::Transaction transaction {session};
|
||||
auto uniqueTransaction {createUniqueTransaction()};
|
||||
|
||||
static const std::string outdatedMsg {"Outdated database, please rebuild it (delete the .db file and restart)"};
|
||||
|
||||
Version version;
|
||||
try
|
||||
{
|
||||
version = VersionInfo::get(session)->getVersion();
|
||||
version = VersionInfo::getOrCreate(*this)->getVersion();
|
||||
LMS_LOG(DB, INFO) << "Database version = " << version;
|
||||
if (version == LMS_DATABASE_VERSION)
|
||||
return;
|
||||
}
|
||||
@@ -109,30 +116,30 @@ doDatabaseMigrationIfNeeded(Wt::Dbo::Session& session)
|
||||
|
||||
LMS_LOG(DB, INFO) << "Migrating database from version 3...";
|
||||
|
||||
session.execute(R"(CREATE TABLE IF NOT EXISTS "user_artist_starred" (
|
||||
_session.execute(R"(CREATE TABLE IF NOT EXISTS "user_artist_starred" (
|
||||
"user_id" bigint,
|
||||
"artist_id" bigint,
|
||||
primary key ("user_id", "artist_id"),
|
||||
constraint "fk_user_artist_starred_key1" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred,
|
||||
constraint "fk_user_artist_starred_key2" foreign key ("artist_id") references "artist" ("id") deferrable initially deferred);)");
|
||||
session.execute(R"(CREATE INDEX "user_artist_starred_user" on "user_artist_starred" ("user_id");)");
|
||||
session.execute(R"(CREATE INDEX "user_artist_starred_artist" on "user_artist_starred" ("artist_id");)");
|
||||
session.execute(R"(CREATE TABLE IF NOT EXISTS "user_release_starred" (
|
||||
_session.execute(R"(CREATE INDEX "user_artist_starred_user" on "user_artist_starred" ("user_id");)");
|
||||
_session.execute(R"(CREATE INDEX "user_artist_starred_artist" on "user_artist_starred" ("artist_id");)");
|
||||
_session.execute(R"(CREATE TABLE IF NOT EXISTS "user_release_starred" (
|
||||
"user_id" bigint,
|
||||
"release_id" bigint,
|
||||
primary key ("user_id", "release_id"),
|
||||
constraint "fk_user_release_starred_key1" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred,
|
||||
constraint "fk_user_release_starred_key2" foreign key ("release_id") references "release" ("id") on delete cascade deferrable initially deferred);)");
|
||||
session.execute(R"(CREATE INDEX "user_release_starred_user" on "user_release_starred" ("user_id");)");
|
||||
session.execute(R"(CREATE INDEX "user_release_starred_release" on "user_release_starred" ("release_id");)");
|
||||
session.execute(R"(CREATE TABLE IF NOT EXISTS "user_track_starred" (
|
||||
_session.execute(R"(CREATE INDEX "user_release_starred_user" on "user_release_starred" ("user_id");)");
|
||||
_session.execute(R"(CREATE INDEX "user_release_starred_release" on "user_release_starred" ("release_id");)");
|
||||
_session.execute(R"(CREATE TABLE IF NOT EXISTS "user_track_starred" (
|
||||
"user_id" bigint,
|
||||
"track_id" bigint,
|
||||
primary key ("user_id", "track_id"),
|
||||
constraint "fk_user_track_starred_key1" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred,
|
||||
constraint "fk_user_track_starred_key2" foreign key ("track_id") references "track" ("id") on delete cascade deferrable initially deferred);)");
|
||||
session.execute(R"(CREATE INDEX "user_track_starred_user" on "user_track_starred" ("user_id");)");
|
||||
session.execute(R"(CREATE INDEX "user_track_starred_track" on "user_track_starred" ("track_id");)");
|
||||
_session.execute(R"(CREATE INDEX "user_track_starred_user" on "user_track_starred" ("user_id");)");
|
||||
_session.execute(R"(CREATE INDEX "user_track_starred_track" on "user_track_starred" ("track_id");)");
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -140,12 +147,12 @@ doDatabaseMigrationIfNeeded(Wt::Dbo::Session& session)
|
||||
throw LmsException {outdatedMsg};
|
||||
}
|
||||
|
||||
VersionInfo::get(session).modify()->setVersion(LMS_DATABASE_VERSION);
|
||||
VersionInfo::get(*this).modify()->setVersion(LMS_DATABASE_VERSION);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Handler::configureAuth(void)
|
||||
Session::configureAuth(void)
|
||||
{
|
||||
authService.setEmailVerificationEnabled(false);
|
||||
authService.setAuthTokensEnabled(true, "lmsauth");
|
||||
@@ -176,19 +183,20 @@ Handler::configureAuth(void)
|
||||
}
|
||||
|
||||
const Wt::Auth::AuthService&
|
||||
Handler::getAuthService()
|
||||
Session::getAuthService()
|
||||
{
|
||||
return authService;
|
||||
}
|
||||
|
||||
const Wt::Auth::PasswordService&
|
||||
Handler::getPasswordService()
|
||||
Session::getPasswordService()
|
||||
{
|
||||
return passwordService;
|
||||
}
|
||||
|
||||
|
||||
Handler::Handler(Wt::Dbo::SqlConnectionPool& connectionPool)
|
||||
Session::Session(std::shared_timed_mutex& mutex, Wt::Dbo::SqlConnectionPool& connectionPool)
|
||||
: _mutex {mutex}
|
||||
{
|
||||
_session.setConnectionPool(connectionPool);
|
||||
|
||||
@@ -212,9 +220,76 @@ Handler::Handler(Wt::Dbo::SqlConnectionPool& connectionPool)
|
||||
_session.mapClass<AuthInfo::AuthTokenType>("auth_token");
|
||||
_session.mapClass<User>("user");
|
||||
|
||||
try {
|
||||
Wt::Dbo::Transaction transaction {_session};
|
||||
_users = std::make_unique<UserDatabase>(_session);
|
||||
}
|
||||
|
||||
// TODO make this per database
|
||||
static thread_local bool hasSharedLock {false};
|
||||
static thread_local bool hasUniqueLock {false};
|
||||
|
||||
UniqueTransaction::UniqueTransaction(std::shared_timed_mutex& mutex, Wt::Dbo::Session& session)
|
||||
: _lock {mutex},
|
||||
_transaction {session}
|
||||
{
|
||||
assert(!hasSharedLock);
|
||||
assert(!hasUniqueLock);
|
||||
hasUniqueLock = true;
|
||||
LMS_LOG(DB, DEBUG) << "UniqueTransaction ACQUIRED";
|
||||
}
|
||||
|
||||
UniqueTransaction::~UniqueTransaction()
|
||||
{
|
||||
assert(hasUniqueLock);
|
||||
hasUniqueLock = false;
|
||||
LMS_LOG(DB, DEBUG) << "UniqueTransaction RELEASED";
|
||||
}
|
||||
|
||||
SharedTransaction::SharedTransaction(std::shared_timed_mutex& mutex, Wt::Dbo::Session& session)
|
||||
: _lock {mutex},
|
||||
_transaction {session}
|
||||
{
|
||||
assert(!hasSharedLock);
|
||||
assert(!hasUniqueLock);
|
||||
hasSharedLock = true;
|
||||
LMS_LOG(DB, DEBUG) << "SharedTransaction ACQUIRED";
|
||||
}
|
||||
|
||||
SharedTransaction::~SharedTransaction()
|
||||
{
|
||||
assert(hasSharedLock);
|
||||
hasSharedLock = false;
|
||||
LMS_LOG(DB, DEBUG) << "SharedTransaction RELEASED";
|
||||
}
|
||||
|
||||
void
|
||||
Session::checkUniqueLocked()
|
||||
{
|
||||
assert(hasUniqueLock);
|
||||
}
|
||||
|
||||
void
|
||||
Session::checkSharedLocked()
|
||||
{
|
||||
assert(hasUniqueLock || hasSharedLock);
|
||||
}
|
||||
|
||||
std::unique_ptr<UniqueTransaction>
|
||||
Session::createUniqueTransaction()
|
||||
{
|
||||
return std::unique_ptr<UniqueTransaction>(new UniqueTransaction{_mutex, _session});
|
||||
}
|
||||
|
||||
std::unique_ptr<SharedTransaction>
|
||||
Session::createSharedTransaction()
|
||||
{
|
||||
return std::unique_ptr<SharedTransaction>(new SharedTransaction{_mutex, _session});
|
||||
}
|
||||
|
||||
void
|
||||
Session::prepareTables()
|
||||
{
|
||||
// Creation case
|
||||
try {
|
||||
_session.createTables();
|
||||
|
||||
LMS_LOG(DB, INFO) << "Tables created";
|
||||
@@ -224,12 +299,11 @@ Handler::Handler(Wt::Dbo::SqlConnectionPool& connectionPool)
|
||||
LMS_LOG(DB, ERROR) << "Cannot create tables: " << e.what();
|
||||
}
|
||||
|
||||
doDatabaseMigrationIfNeeded(_session);
|
||||
doDatabaseMigrationIfNeeded();
|
||||
|
||||
// Indexes
|
||||
{
|
||||
Wt::Dbo::Transaction transaction {_session};
|
||||
|
||||
// Indexes
|
||||
auto uniqueTransaction {createUniqueTransaction()};
|
||||
_session.execute("CREATE INDEX IF NOT EXISTS artist_name_idx ON artist(name)");
|
||||
_session.execute("CREATE INDEX IF NOT EXISTS artist_sort_name_nocase_idx ON artist(sort_name COLLATE NOCASE)");
|
||||
_session.execute("CREATE INDEX IF NOT EXISTS artist_mbid_idx ON artist(mbid)");
|
||||
@@ -255,29 +329,101 @@ Handler::Handler(Wt::Dbo::SqlConnectionPool& connectionPool)
|
||||
_session.execute("CREATE INDEX IF NOT EXISTS track_artist_link_type_idx ON track_artist_link(type)");
|
||||
}
|
||||
|
||||
_users = new UserDatabase(_session);
|
||||
}
|
||||
// Initial settings tables
|
||||
{
|
||||
auto uniqueTransaction {createUniqueTransaction()};
|
||||
|
||||
Handler::~Handler()
|
||||
{
|
||||
delete _users;
|
||||
ScanSettings::init(*this);
|
||||
SimilaritySettings::init(*this);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Handler::optimize()
|
||||
Session::optimize()
|
||||
{
|
||||
Wt::Dbo::Transaction transaction {_session};
|
||||
auto uniqueTransaction {createUniqueTransaction()};
|
||||
|
||||
_session.execute("ANALYZE");
|
||||
}
|
||||
|
||||
std::string
|
||||
Session::getUserLoginName(Wt::Dbo::ptr<User> user)
|
||||
{
|
||||
const Wt::Auth::User authUser {_users->findWithId(std::to_string(user.id()))};
|
||||
if (!authUser.isValid())
|
||||
throw LmsException {"Invalid user state"};
|
||||
|
||||
return authUser.identity(Wt::Auth::Identity::LoginName).toUTF8();
|
||||
}
|
||||
|
||||
bool
|
||||
Session::checkUserPassword(const std::string& loginName, const std::string& password)
|
||||
{
|
||||
auto transaction {createUniqueTransaction()};
|
||||
|
||||
auto authUser {_users->findWithIdentity(Wt::Auth::Identity::LoginName, loginName)};
|
||||
if (!authUser.isValid())
|
||||
return false; // TODO const time?
|
||||
|
||||
return passwordService.verifyPassword(authUser, password) == Wt::Auth::PasswordResult::PasswordValid;
|
||||
}
|
||||
|
||||
void
|
||||
Session::updateUserPassword(Wt::Dbo::ptr<User> user, const std::string& password)
|
||||
{
|
||||
const Wt::Auth::User authUser {_users->findWithId(std::to_string(user.id()))};
|
||||
if (!authUser.isValid())
|
||||
throw LmsException {"Bad user state"};
|
||||
passwordService.updatePassword(authUser, password);
|
||||
}
|
||||
|
||||
Wt::WDateTime
|
||||
Session::getUserLastLoginAttempt(Wt::Dbo::ptr<User> user)
|
||||
{
|
||||
const Wt::Auth::User authUser {_users->findWithId(std::to_string(user.id()))};
|
||||
if (!authUser.isValid())
|
||||
throw LmsException {"Bad user state"};
|
||||
|
||||
return authUser.lastLoginAttempt();
|
||||
}
|
||||
|
||||
void
|
||||
Session::removeUser(Database::User::pointer user)
|
||||
{
|
||||
checkUniqueLocked();
|
||||
|
||||
auto authUser = _users->findWithId(std::to_string(user.id()));
|
||||
_users->deleteUser(authUser);
|
||||
user.remove();
|
||||
}
|
||||
|
||||
Wt::Auth::AbstractUserDatabase&
|
||||
Handler::getUserDatabase()
|
||||
Session::getUserDatabase()
|
||||
{
|
||||
return *_users;
|
||||
}
|
||||
|
||||
User::pointer
|
||||
Handler::getCurrentUser()
|
||||
Session::createUser(const std::string& loginName, const std::string& password)
|
||||
{
|
||||
Wt::Auth::User authUser {_users->registerNew()};
|
||||
if (!authUser.isValid())
|
||||
{
|
||||
LMS_LOG(DB, ERROR) << "Invalid authUser";
|
||||
return {};
|
||||
}
|
||||
User::pointer user {User::create(*this)};
|
||||
Wt::Dbo::ptr<AuthInfo> authInfo = _users->find(authUser);
|
||||
authInfo.modify()->setUser(user);
|
||||
|
||||
authUser.setIdentity(Wt::Auth::Identity::LoginName, loginName);
|
||||
passwordService.updatePassword(authUser, password);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
User::pointer
|
||||
Session::getLoggedUser()
|
||||
{
|
||||
if (_login.loggedIn())
|
||||
return getUser(_login.user());
|
||||
@@ -286,10 +432,10 @@ Handler::getCurrentUser()
|
||||
}
|
||||
|
||||
User::pointer
|
||||
Handler::getUser(const Wt::Auth::User& authUser)
|
||||
Session::getUser(const Wt::Auth::User& authUser)
|
||||
{
|
||||
if (!authUser.isValid()) {
|
||||
LMS_LOG(DB, ERROR) << "Handler::getUser: invalid authUser";
|
||||
LMS_LOG(DB, ERROR) << "Session::getUser: invalid authUser";
|
||||
return User::pointer();
|
||||
}
|
||||
|
||||
@@ -299,7 +445,7 @@ Handler::getUser(const Wt::Auth::User& authUser)
|
||||
}
|
||||
|
||||
User::pointer
|
||||
Handler::getUser(const std::string& loginName)
|
||||
Session::getUser(const std::string& loginName)
|
||||
{
|
||||
auto authUser {getUserDatabase().findWithIdentity(Wt::Auth::Identity::LoginName, loginName)};
|
||||
if (!authUser.isValid())
|
||||
@@ -308,36 +454,4 @@ Handler::getUser(const std::string& loginName)
|
||||
return getUser(authUser);
|
||||
}
|
||||
|
||||
User::pointer
|
||||
Handler::createUser(const Wt::Auth::User& authUser)
|
||||
{
|
||||
if (!authUser.isValid())
|
||||
{
|
||||
LMS_LOG(DB, ERROR) << "Handler::getUser: invalid authUser";
|
||||
return User::pointer();
|
||||
}
|
||||
|
||||
User::pointer user = _session.add(std::make_unique<User>());
|
||||
Wt::Dbo::ptr<AuthInfo> authInfo = _users->find(authUser);
|
||||
authInfo.modify()->setUser(user);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
std::unique_ptr<Wt::Dbo::SqlConnectionPool>
|
||||
Handler::createConnectionPool(boost::filesystem::path p)
|
||||
{
|
||||
LMS_LOG(DB, INFO) << "Creating connection pool on file " << p.string();
|
||||
|
||||
auto connection = std::make_unique<Wt::Dbo::backend::Sqlite3>(p.string());
|
||||
connection->executeSql("pragma journal_mode=WAL");
|
||||
// connection->setProperty("show-queries", "true");
|
||||
|
||||
auto pool = std::make_unique<Wt::Dbo::FixedSqlConnectionPool>(std::move(connection), 1);
|
||||
pool->setTimeout(std::chrono::seconds(10));
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
|
||||
} // namespace Database
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <shared_mutex>
|
||||
#include <mutex>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <memory>
|
||||
|
||||
#include <Wt/Dbo/Dbo.h>
|
||||
#include <Wt/Dbo/SqlConnectionPool.h>
|
||||
#include <Wt/Auth/Dbo/AuthInfo.h>
|
||||
|
||||
#include <Wt/Auth/Dbo/UserDatabase.h>
|
||||
#include <Wt/Auth/Login.h>
|
||||
#include <Wt/Auth/PasswordService.h>
|
||||
|
||||
#include "User.hpp"
|
||||
|
||||
namespace Database {
|
||||
|
||||
using AuthInfo = Wt::Auth::Dbo::AuthInfo<User>;
|
||||
using UserDatabase = Wt::Auth::Dbo::UserDatabase<AuthInfo>;
|
||||
|
||||
class UniqueTransaction
|
||||
{
|
||||
public:
|
||||
~UniqueTransaction();
|
||||
|
||||
private:
|
||||
friend class Session;
|
||||
UniqueTransaction(std::shared_timed_mutex& mutex, Wt::Dbo::Session& session);
|
||||
|
||||
std::unique_lock<std::shared_timed_mutex> _lock;
|
||||
Wt::Dbo::Transaction _transaction;
|
||||
};
|
||||
|
||||
class SharedTransaction
|
||||
{
|
||||
public:
|
||||
~SharedTransaction();
|
||||
|
||||
private:
|
||||
friend class Session;
|
||||
SharedTransaction(std::shared_timed_mutex& mutex, Wt::Dbo::Session& session);
|
||||
|
||||
std::shared_lock<std::shared_timed_mutex> _lock;
|
||||
Wt::Dbo::Transaction _transaction;
|
||||
};
|
||||
|
||||
class Session
|
||||
{
|
||||
public:
|
||||
Session(const Session&) = delete;
|
||||
Session(Session&&) = delete;
|
||||
Session& operator=(const Session&) = delete;
|
||||
Session& operator=(Session&&) = delete;
|
||||
|
||||
std::unique_ptr<UniqueTransaction> createUniqueTransaction();
|
||||
std::unique_ptr<SharedTransaction> createSharedTransaction();
|
||||
|
||||
void checkUniqueLocked();
|
||||
void checkSharedLocked();
|
||||
|
||||
void optimize();
|
||||
|
||||
// User management
|
||||
Wt::Dbo::ptr<User> getLoggedUser(); // get the current user, may return empty
|
||||
Wt::Dbo::ptr<User> getUser(const std::string& loginName);
|
||||
std::string getUserLoginName(Wt::Dbo::ptr<User> user);
|
||||
Wt::Dbo::ptr<User> createUser(const std::string& loginName, const std::string& password);
|
||||
void removeUser(Wt::Dbo::ptr<User> user);
|
||||
bool checkUserPassword(const std::string& loginName, const std::string& password);
|
||||
void updateUserPassword(Wt::Dbo::ptr<User> user, const std::string& password);
|
||||
Wt::WDateTime getUserLastLoginAttempt(Wt::Dbo::ptr<User> user);
|
||||
|
||||
Wt::Auth::AbstractUserDatabase& getUserDatabase();
|
||||
Wt::Auth::Login& getLogin() { return _login; } // TODO move
|
||||
|
||||
// Long living shared associated services
|
||||
static void configureAuth();
|
||||
static const Wt::Auth::AuthService& getAuthService();
|
||||
static const Wt::Auth::PasswordService& getPasswordService();
|
||||
|
||||
Wt::Dbo::Session& getDboSession() { return _session; }
|
||||
|
||||
private:
|
||||
friend class Database;
|
||||
|
||||
Session(std::shared_timed_mutex& mutex, Wt::Dbo::SqlConnectionPool& connectionPool);
|
||||
|
||||
void doDatabaseMigrationIfNeeded();
|
||||
void prepareTables(); // need to run only once at startup
|
||||
|
||||
Wt::Dbo::ptr<User> getUser(const Wt::Auth::User& authUser);
|
||||
|
||||
std::shared_timed_mutex& _mutex;
|
||||
Wt::Dbo::Session _session;
|
||||
std::unique_ptr<UserDatabase> _users;
|
||||
Wt::Auth::Login _login;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Database
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "utils/Logger.hpp"
|
||||
#include "utils/Utils.hpp"
|
||||
|
||||
#include "Session.hpp"
|
||||
#include "TrackFeatures.hpp"
|
||||
|
||||
namespace Database {
|
||||
@@ -33,7 +34,7 @@ struct TrackFeatureInfo
|
||||
double weight;
|
||||
};
|
||||
|
||||
static std::vector<TrackFeatureInfo> defaultFeatures =
|
||||
static const std::vector<TrackFeatureInfo> defaultFeatures =
|
||||
{
|
||||
{ "lowlevel.spectral_contrast_coeffs.median", 6, 1. },
|
||||
{ "lowlevel.erbbands.median", 40, 1. },
|
||||
@@ -53,24 +54,37 @@ _settings(settings)
|
||||
}
|
||||
|
||||
SimilaritySettingsFeature::pointer
|
||||
SimilaritySettingsFeature::create(Wt::Dbo::Session& session, Wt::Dbo::ptr<SimilaritySettings> settings, const std::string& name, std::size_t nbDimensions, double weight)
|
||||
SimilaritySettingsFeature::create(Session& session, Wt::Dbo::ptr<SimilaritySettings> settings, const std::string& name, std::size_t nbDimensions, double weight)
|
||||
{
|
||||
return session.add(std::make_unique<SimilaritySettingsFeature>(settings, name, nbDimensions, weight));
|
||||
session.checkUniqueLocked();
|
||||
|
||||
SimilaritySettingsFeature::pointer res {session.getDboSession().add(std::make_unique<SimilaritySettingsFeature>(settings, name, nbDimensions, weight))};
|
||||
session.getDboSession().flush();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
SimilaritySettings::pointer
|
||||
SimilaritySettings::get(Wt::Dbo::Session& session)
|
||||
void
|
||||
SimilaritySettings::init(Session& session)
|
||||
{
|
||||
pointer settings = session.find<SimilaritySettings>();
|
||||
if (!settings)
|
||||
{
|
||||
settings = session.add(std::make_unique<SimilaritySettings>());
|
||||
session.checkUniqueLocked();
|
||||
|
||||
for (const auto& feature : defaultFeatures)
|
||||
SimilaritySettingsFeature::create(session, settings, feature.name, feature.nbDimensions, feature.weight);
|
||||
}
|
||||
pointer settings {session.getDboSession().find<SimilaritySettings>()};
|
||||
if (settings)
|
||||
return;
|
||||
|
||||
return settings;
|
||||
settings = session.getDboSession().add(std::make_unique<SimilaritySettings>());
|
||||
for (const auto& feature : defaultFeatures)
|
||||
SimilaritySettingsFeature::create(session, settings, feature.name, feature.nbDimensions, feature.weight);
|
||||
}
|
||||
|
||||
|
||||
SimilaritySettings::pointer
|
||||
SimilaritySettings::get(Session& session)
|
||||
{
|
||||
session.checkSharedLocked();
|
||||
|
||||
return session.getDboSession().find<SimilaritySettings>();
|
||||
}
|
||||
|
||||
std::vector<Wt::Dbo::ptr<SimilaritySettingsFeature>>
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
namespace Database {
|
||||
|
||||
class Session;
|
||||
class SimilaritySettings;
|
||||
|
||||
class SimilaritySettingsFeature : public Wt::Dbo::Dbo<SimilaritySettingsFeature>
|
||||
@@ -33,7 +34,7 @@ class SimilaritySettingsFeature : public Wt::Dbo::Dbo<SimilaritySettingsFeature
|
||||
SimilaritySettingsFeature() = default;
|
||||
SimilaritySettingsFeature(Wt::Dbo::ptr<SimilaritySettings> settings, const std::string& name, std::size_t nbDimensions, double weight);
|
||||
|
||||
static pointer create(Wt::Dbo::Session& session, Wt::Dbo::ptr<SimilaritySettings> settings, const std::string& name, std::size_t nbDimensions, double weight = 1);
|
||||
static pointer create(Session& session, Wt::Dbo::ptr<SimilaritySettings> settings, const std::string& name, std::size_t nbDimensions, double weight = 1);
|
||||
|
||||
const std::string& getName() const { return _name; } ;
|
||||
std::size_t getNbDimensions() const { return static_cast<std::size_t>(_nbDimensions); }
|
||||
@@ -70,7 +71,8 @@ class SimilaritySettings : public Wt::Dbo::Dbo<SimilaritySettings>
|
||||
using pointer = Wt::Dbo::ptr<SimilaritySettings>;
|
||||
|
||||
// Utils
|
||||
static pointer get(Wt::Dbo::Session& session);
|
||||
static void init(Session& session);
|
||||
static pointer get(Session& session);
|
||||
|
||||
// Accessors Read
|
||||
std::size_t getVersion() const { return _settingsVersion; }
|
||||
|
||||
+70
-38
@@ -27,6 +27,7 @@
|
||||
#include "Cluster.hpp"
|
||||
#include "Release.hpp"
|
||||
#include "TrackFeatures.hpp"
|
||||
#include "Session.hpp"
|
||||
#include "SqlQuery.hpp"
|
||||
|
||||
namespace Database {
|
||||
@@ -38,18 +39,22 @@ _filePath( p.string() )
|
||||
}
|
||||
|
||||
std::vector<Track::pointer>
|
||||
Track::getAll(Wt::Dbo::Session& session, boost::optional<std::size_t> limit)
|
||||
Track::getAll(Session& session, boost::optional<std::size_t> limit)
|
||||
{
|
||||
Wt::Dbo::collection<Track::pointer> res {session.find<Track>()
|
||||
session.checkSharedLocked();
|
||||
|
||||
Wt::Dbo::collection<Track::pointer> res {session.getDboSession().find<Track>()
|
||||
.limit(limit ? static_cast<int>(*limit) : -1)};
|
||||
|
||||
return std::vector<Track::pointer>(std::cbegin(res), std::cend(res));
|
||||
}
|
||||
|
||||
std::vector<Track::pointer>
|
||||
Track::getAllRandom(Wt::Dbo::Session& session, boost::optional<std::size_t> limit)
|
||||
Track::getAllRandom(Session& session, boost::optional<std::size_t> limit)
|
||||
{
|
||||
Wt::Dbo::collection<Track::pointer> res {session.find<Track>()
|
||||
session.checkSharedLocked();
|
||||
|
||||
Wt::Dbo::collection<Track::pointer> res {session.getDboSession().find<Track>()
|
||||
.limit(limit ? static_cast<int>(*limit) : -1)
|
||||
.orderBy("RANDOM()")};
|
||||
|
||||
@@ -57,67 +62,88 @@ Track::getAllRandom(Wt::Dbo::Session& session, boost::optional<std::size_t> limi
|
||||
}
|
||||
|
||||
std::vector<IdType>
|
||||
Track::getAllIds(Wt::Dbo::Session& session)
|
||||
Track::getAllIds(Session& session)
|
||||
{
|
||||
Wt::Dbo::Transaction transaction(session);
|
||||
Wt::Dbo::collection<IdType> res = session.query<IdType>("SELECT id from track");
|
||||
session.checkSharedLocked();
|
||||
|
||||
Wt::Dbo::collection<IdType> res = session.getDboSession().query<IdType>("SELECT id FROM track");
|
||||
return std::vector<IdType>(res.begin(), res.end());
|
||||
}
|
||||
|
||||
Track::pointer
|
||||
Track::getByPath(Wt::Dbo::Session& session, const boost::filesystem::path& p)
|
||||
Track::getByPath(Session& session, const boost::filesystem::path& p)
|
||||
{
|
||||
return session.find<Track>().where("file_path = ?").bind(p.string());
|
||||
session.checkSharedLocked();
|
||||
|
||||
return session.getDboSession().find<Track>().where("file_path = ?").bind(p.string());
|
||||
}
|
||||
|
||||
Track::pointer
|
||||
Track::getById(Wt::Dbo::Session& session, IdType id)
|
||||
Track::getById(Session& session, IdType id)
|
||||
{
|
||||
return session.find<Track>().where("id = ?").bind(id);
|
||||
session.checkSharedLocked();
|
||||
|
||||
return session.getDboSession().find<Track>()
|
||||
.where("id = ?").bind(id);
|
||||
}
|
||||
|
||||
Track::pointer
|
||||
Track::getByMBID(Wt::Dbo::Session& session, const std::string& mbid)
|
||||
Track::getByMBID(Session& session, const std::string& mbid)
|
||||
{
|
||||
return session.find<Track>().where("mbid = ?").bind(mbid);
|
||||
session.checkSharedLocked();
|
||||
|
||||
return session.getDboSession().find<Track>()
|
||||
.where("mbid = ?").bind(mbid);
|
||||
}
|
||||
|
||||
Track::pointer
|
||||
Track::create(Wt::Dbo::Session& session, const boost::filesystem::path& p)
|
||||
Track::create(Session& session, const boost::filesystem::path& p)
|
||||
{
|
||||
return session.add(std::make_unique<Track>(p));
|
||||
session.checkUniqueLocked();
|
||||
|
||||
Track::pointer res {session.getDboSession().add(std::make_unique<Track>(p))};
|
||||
session.getDboSession().flush();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<boost::filesystem::path>
|
||||
Track::getAllPaths(Wt::Dbo::Session& session)
|
||||
Track::getAllPaths(Session& session)
|
||||
{
|
||||
Wt::Dbo::Transaction transaction(session);
|
||||
Wt::Dbo::collection<std::string> res = session.query<std::string>("SELECT file_path from track");
|
||||
session.checkSharedLocked();
|
||||
|
||||
Wt::Dbo::collection<std::string> res = session.getDboSession().query<std::string>("SELECT file_path FROM track");
|
||||
return std::vector<boost::filesystem::path>(res.begin(), res.end());
|
||||
}
|
||||
|
||||
std::vector<Track::pointer>
|
||||
Track::getMBIDDuplicates(Wt::Dbo::Session& session)
|
||||
Track::getMBIDDuplicates(Session& session)
|
||||
{
|
||||
Wt::Dbo::collection<pointer> res = session.query<pointer>( "SELECT track FROM track WHERE mbid in (SELECT mbid FROM track WHERE mbid <> '' GROUP BY mbid HAVING COUNT (*) > 1)").orderBy("track.release_id,track.disc_number,track.track_number,track.mbid");
|
||||
session.checkSharedLocked();
|
||||
|
||||
Wt::Dbo::collection<pointer> res = session.getDboSession().query<pointer>( "SELECT track FROM track WHERE mbid in (SELECT mbid FROM track WHERE mbid <> '' GROUP BY mbid HAVING COUNT (*) > 1)").orderBy("track.release_id,track.disc_number,track.track_number,track.mbid");
|
||||
return std::vector<pointer>(res.begin(), res.end());
|
||||
}
|
||||
|
||||
std::vector<Track::pointer>
|
||||
Track::getLastAdded(Wt::Dbo::Session& session, Wt::WDateTime after, int limit)
|
||||
Track::getLastAdded(Session& session, const Wt::WDateTime& after, boost::optional<std::size_t> limit)
|
||||
{
|
||||
Wt::Dbo::collection<Track::pointer> res = session.find<Track>()
|
||||
session.checkSharedLocked();
|
||||
|
||||
Wt::Dbo::collection<Track::pointer> res = session.getDboSession().find<Track>()
|
||||
.where("file_added > ?").bind(after)
|
||||
.orderBy("file_added DESC")
|
||||
.limit(limit);
|
||||
.limit(limit ? static_cast<int>(*limit) : -1);
|
||||
|
||||
return std::vector<pointer>(res.begin(), res.end());
|
||||
}
|
||||
|
||||
std::vector<Track::pointer>
|
||||
Track::getAllWithMBIDAndMissingFeatures(Wt::Dbo::Session& session)
|
||||
Track::getAllWithMBIDAndMissingFeatures(Session& session)
|
||||
{
|
||||
Wt::Dbo::collection<pointer> res = session.query<pointer>
|
||||
session.checkSharedLocked();
|
||||
|
||||
Wt::Dbo::collection<pointer> res = session.getDboSession().query<pointer>
|
||||
("SELECT t FROM track t")
|
||||
.where("LENGTH(t.mbid) > 0")
|
||||
.where("NOT EXISTS (SELECT * FROM track_features t_f WHERE t_f.track_id = t.id)");
|
||||
@@ -125,14 +151,14 @@ Track::getAllWithMBIDAndMissingFeatures(Wt::Dbo::Session& session)
|
||||
}
|
||||
|
||||
std::vector<IdType>
|
||||
Track::getAllIdsWithFeatures(Wt::Dbo::Session& session, boost::optional<std::size_t> limit)
|
||||
Track::getAllIdsWithFeatures(Session& session, boost::optional<std::size_t> limit)
|
||||
{
|
||||
int size {limit ? static_cast<int>(*limit) : -1};
|
||||
session.checkSharedLocked();
|
||||
|
||||
Wt::Dbo::collection<IdType> res = session.query<IdType>
|
||||
Wt::Dbo::collection<IdType> res = session.getDboSession().query<IdType>
|
||||
("SELECT t.id FROM track t")
|
||||
.where("EXISTS (SELECT * from track_features t_f WHERE t_f.track_id = t.id)")
|
||||
.limit(size);
|
||||
.limit(limit ? static_cast<int>(*limit) : -1);
|
||||
|
||||
return std::vector<IdType>(res.begin(), res.end());
|
||||
}
|
||||
@@ -153,10 +179,12 @@ Track::hasTrackFeatures() const
|
||||
|
||||
static
|
||||
Wt::Dbo::Query< Track::pointer >
|
||||
getQuery(Wt::Dbo::Session& session,
|
||||
getQuery(Session& session,
|
||||
const std::set<IdType>& clusterIds,
|
||||
const std::vector<std::string> keywords)
|
||||
const std::vector<std::string>& keywords)
|
||||
{
|
||||
session.checkSharedLocked();
|
||||
|
||||
WhereClause where;
|
||||
|
||||
std::ostringstream oss;
|
||||
@@ -184,7 +212,7 @@ getQuery(Wt::Dbo::Session& session,
|
||||
|
||||
oss << " ORDER BY t.name COLLATE NOCASE";
|
||||
|
||||
Wt::Dbo::Query<Track::pointer> query = session.query<Track::pointer>( oss.str() );
|
||||
Wt::Dbo::Query<Track::pointer> query = session.getDboSession().query<Track::pointer>( oss.str() );
|
||||
|
||||
for (const std::string& bindArg : where.getBindArgs())
|
||||
query.bind(bindArg);
|
||||
@@ -193,13 +221,15 @@ getQuery(Wt::Dbo::Session& session,
|
||||
}
|
||||
|
||||
std::vector<Track::pointer>
|
||||
Track::getByFilter(Wt::Dbo::Session& session,
|
||||
Track::getByFilter(Session& session,
|
||||
const std::set<IdType>& clusterIds,
|
||||
const std::vector<std::string> keywords,
|
||||
const std::vector<std::string>& keywords,
|
||||
boost::optional<std::size_t> offset,
|
||||
boost::optional<std::size_t> size,
|
||||
bool& moreResults)
|
||||
{
|
||||
session.checkSharedLocked();
|
||||
|
||||
Wt::Dbo::collection<pointer> collection = getQuery(session, clusterIds, keywords)
|
||||
.limit(size ? static_cast<int>(*size) + 1 : -1)
|
||||
.offset(offset ? static_cast<int>(*offset) : -1);
|
||||
@@ -218,16 +248,18 @@ Track::getByFilter(Wt::Dbo::Session& session,
|
||||
}
|
||||
|
||||
std::vector<Track::pointer>
|
||||
Track::getByFilter(Wt::Dbo::Session& session,
|
||||
Track::getByFilter(Session& session,
|
||||
const std::set<IdType>& clusters)
|
||||
{
|
||||
session.checkSharedLocked();
|
||||
|
||||
bool moreResults;
|
||||
|
||||
return getByFilter(session,
|
||||
clusters,
|
||||
std::vector<std::string> {},
|
||||
boost::optional<std::size_t> {},
|
||||
boost::optional<std::size_t> {},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
moreResults);
|
||||
}
|
||||
|
||||
|
||||
+16
-16
@@ -53,29 +53,29 @@ class Track : public Wt::Dbo::Dbo<Track>
|
||||
Track(const boost::filesystem::path& p);
|
||||
|
||||
// Find utility functions
|
||||
static pointer getByPath(Wt::Dbo::Session& session, const boost::filesystem::path& p);
|
||||
static pointer getById(Wt::Dbo::Session& session, IdType id);
|
||||
static pointer getByMBID(Wt::Dbo::Session& session, const std::string& MBID);
|
||||
static std::vector<pointer> getByFilter(Wt::Dbo::Session& session,
|
||||
static pointer getByPath(Session& session, const boost::filesystem::path& p);
|
||||
static pointer getById(Session& session, IdType id);
|
||||
static pointer getByMBID(Session& session, const std::string& MBID);
|
||||
static std::vector<pointer> getByFilter(Session& session,
|
||||
const std::set<IdType>& clusters); // tracks that belong to these clusters
|
||||
static std::vector<pointer> getByFilter(Wt::Dbo::Session& session,
|
||||
const std::set<IdType>& clusters, // tracks that belong to these clusters
|
||||
const std::vector<std::string> keywords, // name must match all of these keywords
|
||||
static std::vector<pointer> getByFilter(Session& session,
|
||||
const std::set<IdType>& clusters, // tracks that belong to these clusters
|
||||
const std::vector<std::string>& keywords, // name must match all of these keywords
|
||||
boost::optional<std::size_t> offset,
|
||||
boost::optional<std::size_t> size,
|
||||
bool& moreExpected);
|
||||
|
||||
static std::vector<pointer> getAll(Wt::Dbo::Session& session, boost::optional<std::size_t> limit = {});
|
||||
static std::vector<pointer> getAllRandom(Wt::Dbo::Session& session, boost::optional<std::size_t> limit = {});
|
||||
static std::vector<IdType> getAllIds(Wt::Dbo::Session& session); // nested transaction
|
||||
static std::vector<boost::filesystem::path> getAllPaths(Wt::Dbo::Session& session); // nested transaction
|
||||
static std::vector<pointer> getMBIDDuplicates(Wt::Dbo::Session& session);
|
||||
static std::vector<pointer> getLastAdded(Wt::Dbo::Session& session, Wt::WDateTime after, int size = 1);
|
||||
static std::vector<pointer> getAllWithMBIDAndMissingFeatures(Wt::Dbo::Session& session); // nested transaction
|
||||
static std::vector<IdType> getAllIdsWithFeatures(Wt::Dbo::Session& session, boost::optional<std::size_t> limit = {}); // nested transaction
|
||||
static std::vector<pointer> getAll(Session& session, boost::optional<std::size_t> limit = {});
|
||||
static std::vector<pointer> getAllRandom(Session& session, boost::optional<std::size_t> limit = {});
|
||||
static std::vector<IdType> getAllIds(Session& session); // nested transaction
|
||||
static std::vector<boost::filesystem::path> getAllPaths(Session& session); // nested transaction
|
||||
static std::vector<pointer> getMBIDDuplicates(Session& session);
|
||||
static std::vector<pointer> getLastAdded(Session& session, const Wt::WDateTime& after, boost::optional<std::size_t> size = 1);
|
||||
static std::vector<pointer> getAllWithMBIDAndMissingFeatures(Session& session);
|
||||
static std::vector<IdType> getAllIdsWithFeatures(Session& session, boost::optional<std::size_t> limit = {});
|
||||
|
||||
// Create utility
|
||||
static pointer create(Wt::Dbo::Session& session, const boost::filesystem::path& p);
|
||||
static pointer create(Session& session, const boost::filesystem::path& p);
|
||||
|
||||
// Accessors
|
||||
void setScanVersion(std::size_t version) { _scanVersion = version; }
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "TrackArtistLink.hpp"
|
||||
|
||||
#include "Artist.hpp"
|
||||
#include "Session.hpp"
|
||||
#include "Track.hpp"
|
||||
|
||||
namespace Database {
|
||||
@@ -32,9 +33,14 @@ _artist {artist}
|
||||
}
|
||||
|
||||
TrackArtistLink::pointer
|
||||
TrackArtistLink::create(Wt::Dbo::Session& session, Wt::Dbo::ptr<Track> track, Wt::Dbo::ptr<Artist> artist,Type type)
|
||||
TrackArtistLink::create(Session& session, Wt::Dbo::ptr<Track> track, Wt::Dbo::ptr<Artist> artist,Type type)
|
||||
{
|
||||
return session.add(std::make_unique<TrackArtistLink>(track, artist, type));
|
||||
session.checkUniqueLocked();
|
||||
|
||||
TrackArtistLink::pointer res {session.getDboSession().add(std::make_unique<TrackArtistLink>(track, artist, type))};
|
||||
session.getDboSession().flush();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
namespace Database {
|
||||
|
||||
class Artist;
|
||||
class Session;
|
||||
class Track;
|
||||
|
||||
class TrackArtistLink
|
||||
@@ -51,7 +52,7 @@ class TrackArtistLink
|
||||
TrackArtistLink() = default;
|
||||
TrackArtistLink(Wt::Dbo::ptr<Track> track, Wt::Dbo::ptr<Artist> artist, Type type);
|
||||
|
||||
static pointer create(Wt::Dbo::Session& session, Wt::Dbo::ptr<Track> track, Wt::Dbo::ptr<Artist> artist,Type type);
|
||||
static pointer create(Session& session, Wt::Dbo::ptr<Track> track, Wt::Dbo::ptr<Artist> artist,Type type);
|
||||
|
||||
Wt::Dbo::ptr<Track> getTrack() const { return _track; }
|
||||
Wt::Dbo::ptr<Artist> getArtist() const { return _artist; }
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
|
||||
#include "utils/Logger.hpp"
|
||||
#include "Session.hpp"
|
||||
#include "Track.hpp"
|
||||
|
||||
namespace Database {
|
||||
@@ -34,9 +35,10 @@ _track(track)
|
||||
}
|
||||
|
||||
TrackFeatures::pointer
|
||||
TrackFeatures::create(Wt::Dbo::Session& session, Wt::Dbo::ptr<Track> track, const std::string& jsonEncodedFeatures)
|
||||
TrackFeatures::create(Session& session, Wt::Dbo::ptr<Track> track, const std::string& jsonEncodedFeatures)
|
||||
{
|
||||
return session.add(std::make_unique<TrackFeatures>(track, jsonEncodedFeatures));
|
||||
session.checkUniqueLocked();
|
||||
return session.getDboSession().add(std::make_unique<TrackFeatures>(track, jsonEncodedFeatures));
|
||||
}
|
||||
|
||||
std::vector<double>
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
namespace Database {
|
||||
|
||||
class Session;
|
||||
class Track;
|
||||
|
||||
class TrackFeatures : public Wt::Dbo::Dbo<TrackFeatures>
|
||||
@@ -39,7 +40,7 @@ class TrackFeatures : public Wt::Dbo::Dbo<TrackFeatures>
|
||||
TrackFeatures(Wt::Dbo::ptr<Track> track, const std::string& jsonEncodedFeatures);
|
||||
|
||||
// Create utility
|
||||
static pointer create(Wt::Dbo::Session& session, Wt::Dbo::ptr<Track> track, const std::string& jsonEncodedFeatures);
|
||||
static pointer create(Session& session, Wt::Dbo::ptr<Track> track, const std::string& jsonEncodedFeatures);
|
||||
|
||||
std::vector<double> getFeatures(const std::string& featureNode) const;
|
||||
bool getFeatures(std::map<std::string /*featureNode*/, std::vector<double> /*values*/>& featureNodes) const;
|
||||
|
||||
+30
-46
@@ -26,6 +26,7 @@
|
||||
#include "Artist.hpp"
|
||||
#include "Cluster.hpp"
|
||||
#include "Release.hpp"
|
||||
#include "Session.hpp"
|
||||
#include "User.hpp"
|
||||
#include "Track.hpp"
|
||||
|
||||
@@ -41,38 +42,35 @@ TrackList::TrackList(const std::string& name, Type type, bool isPublic, Wt::Dbo:
|
||||
}
|
||||
|
||||
TrackList::pointer
|
||||
TrackList::create(Wt::Dbo::Session& session, const std::string& name, Type type, bool isPublic, Wt::Dbo::ptr<User> user)
|
||||
TrackList::create(Session& session, const std::string& name, Type type, bool isPublic, Wt::Dbo::ptr<User> user)
|
||||
{
|
||||
session.checkUniqueLocked();
|
||||
assert(user);
|
||||
|
||||
auto res = session.add( std::make_unique<TrackList>(name, type, isPublic, user) );
|
||||
session.flush();
|
||||
auto res = session.getDboSession().add( std::make_unique<TrackList>(name, type, isPublic, user) );
|
||||
session.getDboSession().flush();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
TrackListEntry::pointer
|
||||
TrackList::add(IdType trackId)
|
||||
{
|
||||
assert(session());
|
||||
assert(self());
|
||||
|
||||
return TrackListEntry::create(*session(), Database::Track::getById(*session(), trackId), self());
|
||||
}
|
||||
|
||||
TrackList::pointer
|
||||
TrackList::get(Wt::Dbo::Session& session, const std::string& name, Type type, Wt::Dbo::ptr<User> user)
|
||||
TrackList::get(Session& session, const std::string& name, Type type, Wt::Dbo::ptr<User> user)
|
||||
{
|
||||
return session.find<TrackList>()
|
||||
session.checkSharedLocked();
|
||||
assert(user);
|
||||
|
||||
return session.getDboSession().find<TrackList>()
|
||||
.where("name = ?").bind(name)
|
||||
.where("type = ?").bind(type)
|
||||
.where("user_id = ?").bind(user.id());
|
||||
}
|
||||
|
||||
std::vector<TrackList::pointer>
|
||||
TrackList::getAll(Wt::Dbo::Session& session, Wt::Dbo::ptr<User> user)
|
||||
TrackList::getAll(Session& session, Wt::Dbo::ptr<User> user)
|
||||
{
|
||||
Wt::Dbo::collection<TrackList::pointer> res = session.find<TrackList>()
|
||||
session.checkSharedLocked();
|
||||
|
||||
Wt::Dbo::collection<TrackList::pointer> res = session.getDboSession().find<TrackList>()
|
||||
.where("user_id = ?").bind(user.id())
|
||||
.orderBy("name COLLATE NOCASE");
|
||||
|
||||
@@ -80,9 +78,11 @@ TrackList::getAll(Wt::Dbo::Session& session, Wt::Dbo::ptr<User> user)
|
||||
}
|
||||
|
||||
std::vector<TrackList::pointer>
|
||||
TrackList::getAll(Wt::Dbo::Session& session, Wt::Dbo::ptr<User> user, Type type)
|
||||
TrackList::getAll(Session& session, Wt::Dbo::ptr<User> user, Type type)
|
||||
{
|
||||
Wt::Dbo::collection<TrackList::pointer> res = session.find<TrackList>()
|
||||
session.checkSharedLocked();
|
||||
|
||||
Wt::Dbo::collection<TrackList::pointer> res = session.getDboSession().find<TrackList>()
|
||||
.where("user_id = ?").bind(user.id())
|
||||
.where("type = ?").bind(type)
|
||||
.orderBy("name COLLATE NOCASE");
|
||||
@@ -91,9 +91,11 @@ TrackList::getAll(Wt::Dbo::Session& session, Wt::Dbo::ptr<User> user, Type type)
|
||||
}
|
||||
|
||||
TrackList::pointer
|
||||
TrackList::getById(Wt::Dbo::Session& session, IdType id)
|
||||
TrackList::getById(Session& session, IdType id)
|
||||
{
|
||||
return session.find<TrackList>().where("id = ?").bind(id);
|
||||
session.checkSharedLocked();
|
||||
|
||||
return session.getDboSession().find<TrackList>().where("id = ?").bind(id);
|
||||
}
|
||||
|
||||
|
||||
@@ -200,23 +202,6 @@ TrackList::getDuration() const
|
||||
return query.resultValue();
|
||||
}
|
||||
|
||||
void
|
||||
TrackList::shuffle()
|
||||
{
|
||||
assert(session());
|
||||
|
||||
auto entries = getEntries();
|
||||
|
||||
auto now = std::chrono::system_clock::now();
|
||||
std::mt19937 randGenerator(std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count());
|
||||
|
||||
std::shuffle(entries.begin(), entries.end(), randGenerator);
|
||||
|
||||
clear();
|
||||
for (auto entry : entries)
|
||||
TrackListEntry::create(*session(), entry->getTrack(), self());
|
||||
}
|
||||
|
||||
std::vector<Artist::pointer>
|
||||
TrackList::getTopArtists(std::size_t limit) const
|
||||
{
|
||||
@@ -269,26 +254,25 @@ TrackListEntry::TrackListEntry(Wt::Dbo::ptr<Track> track, Wt::Dbo::ptr<TrackList
|
||||
|
||||
}
|
||||
|
||||
TrackListEntry::TrackListEntry()
|
||||
{
|
||||
}
|
||||
|
||||
TrackListEntry::pointer
|
||||
TrackListEntry::create(Wt::Dbo::Session& session, Wt::Dbo::ptr<Track> track, Wt::Dbo::ptr<TrackList> tracklist)
|
||||
TrackListEntry::create(Session& session, Wt::Dbo::ptr<Track> track, Wt::Dbo::ptr<TrackList> tracklist)
|
||||
{
|
||||
session.checkUniqueLocked();
|
||||
assert(track);
|
||||
assert(tracklist);
|
||||
|
||||
auto res = session.add( std::make_unique<TrackListEntry>( track, tracklist) );
|
||||
session.flush();
|
||||
auto res = session.getDboSession().add( std::make_unique<TrackListEntry>( track, tracklist) );
|
||||
session.getDboSession().flush();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
TrackListEntry::pointer
|
||||
TrackListEntry::getById(Wt::Dbo::Session& session, IdType id)
|
||||
TrackListEntry::getById(Session& session, IdType id)
|
||||
{
|
||||
return session.find<TrackListEntry>().where("id = ?").bind(id);
|
||||
session.checkSharedLocked();
|
||||
|
||||
return session.getDboSession().find<TrackListEntry>().where("id = ?").bind(id);
|
||||
}
|
||||
|
||||
} // namespace Database
|
||||
|
||||
+12
-13
@@ -30,11 +30,12 @@
|
||||
namespace Database {
|
||||
|
||||
class Artist;
|
||||
class Cluster;
|
||||
class Release;
|
||||
class User;
|
||||
class Session;
|
||||
class Track;
|
||||
class TrackListEntry;
|
||||
class Cluster;
|
||||
class User;
|
||||
|
||||
class TrackList : public Wt::Dbo::Dbo<TrackList>
|
||||
{
|
||||
@@ -56,13 +57,13 @@ class TrackList : public Wt::Dbo::Dbo<TrackList>
|
||||
std::vector<Wt::Dbo::ptr<Track>> getTopTracks(std::size_t limit = 1) const;
|
||||
|
||||
// Search utility
|
||||
static pointer get(Wt::Dbo::Session& session, const std::string& name, Type type, Wt::Dbo::ptr<User> user);
|
||||
static pointer getById(Wt::Dbo::Session& session, IdType tracklistId);
|
||||
static std::vector<pointer> getAll(Wt::Dbo::Session& session, Wt::Dbo::ptr<User> user);
|
||||
static std::vector<pointer> getAll(Wt::Dbo::Session& session, Wt::Dbo::ptr<User> user, Type type);
|
||||
static pointer get(Session& session, const std::string& name, Type type, Wt::Dbo::ptr<User> user);
|
||||
static pointer getById(Session& session, IdType tracklistId);
|
||||
static std::vector<pointer> getAll(Session& session, Wt::Dbo::ptr<User> user);
|
||||
static std::vector<pointer> getAll(Session& session, Wt::Dbo::ptr<User> user, Type type);
|
||||
|
||||
// Create utility
|
||||
static pointer create(Wt::Dbo::Session& session, const std::string& name, Type type, bool isPublic, Wt::Dbo::ptr<User> user);
|
||||
static pointer create(Session& session, const std::string& name, Type type, bool isPublic, Wt::Dbo::ptr<User> user);
|
||||
|
||||
// Accessors
|
||||
std::string getName() const { return _name; }
|
||||
@@ -73,9 +74,7 @@ class TrackList : public Wt::Dbo::Dbo<TrackList>
|
||||
// Modifiers
|
||||
void setName(const std::string& name) { _name = name; }
|
||||
void setIsPublic(bool isPublic) { _isPublic = isPublic; }
|
||||
Wt::Dbo::ptr<TrackListEntry> add(IdType trackId);
|
||||
void clear() { _entries.clear(); }
|
||||
void shuffle();
|
||||
void clear() { _entries.clear(); }
|
||||
|
||||
// Get tracks, ordered by position
|
||||
std::size_t getCount() const;
|
||||
@@ -120,13 +119,13 @@ class TrackListEntry : public Wt::Dbo::Dbo<TrackListEntry>
|
||||
|
||||
using pointer = Wt::Dbo::ptr<TrackListEntry>;
|
||||
|
||||
TrackListEntry();
|
||||
TrackListEntry() = default;
|
||||
TrackListEntry(Wt::Dbo::ptr<Track> track, Wt::Dbo::ptr<TrackList> tracklist);
|
||||
|
||||
static pointer getById(Wt::Dbo::Session& session, IdType id);
|
||||
static pointer getById(Session& session, IdType id);
|
||||
|
||||
// Create utility
|
||||
static pointer create(Wt::Dbo::Session& session, Wt::Dbo::ptr<Track> track, Wt::Dbo::ptr<TrackList> tracklist);
|
||||
static pointer create(Session& session, Wt::Dbo::ptr<Track> track, Wt::Dbo::ptr<TrackList> tracklist);
|
||||
|
||||
// Accessors
|
||||
Wt::Dbo::ptr<Track> getTrack() const { return _track; }
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Wt/Dbo/Dbo.h>
|
||||
#include <Wt/Dbo/ptr.h>
|
||||
|
||||
namespace Database {
|
||||
using IdType = Wt::Dbo::dbo_default_traits::IdType;
|
||||
|
||||
+32
-29
@@ -21,11 +21,15 @@
|
||||
|
||||
#include "Artist.hpp"
|
||||
#include "Release.hpp"
|
||||
#include "Session.hpp"
|
||||
#include "Track.hpp"
|
||||
#include "TrackList.hpp"
|
||||
|
||||
namespace Database {
|
||||
|
||||
static const std::string playedListName {"__played_tracks__"};
|
||||
static const std::string queuedListName {"__queued_tracks__"};
|
||||
|
||||
const std::set<Bitrate>
|
||||
User::audioTranscodeAllowedBitrates =
|
||||
{
|
||||
@@ -37,35 +41,48 @@ User::audioTranscodeAllowedBitrates =
|
||||
};
|
||||
|
||||
User::User()
|
||||
: _maxAudioTranscodeBitrate{static_cast<int>(*audioTranscodeAllowedBitrates.rbegin())}
|
||||
: _maxAudioTranscodeBitrate {static_cast<int>(*audioTranscodeAllowedBitrates.rbegin())}
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::vector<User::pointer>
|
||||
User::getAll(Wt::Dbo::Session& session)
|
||||
User::getAll(Session& session)
|
||||
{
|
||||
Wt::Dbo::collection<pointer> res = session.find<User>();
|
||||
session.checkSharedLocked();
|
||||
|
||||
Wt::Dbo::collection<pointer> res = session.getDboSession().find<User>();
|
||||
return std::vector<pointer>(res.begin(), res.end());
|
||||
}
|
||||
|
||||
User::pointer
|
||||
User::getDemo(Wt::Dbo::Session& session)
|
||||
User::getDemo(Session& session)
|
||||
{
|
||||
pointer res = session.find<User>().where("type = ?").bind(Type::DEMO);
|
||||
session.checkSharedLocked();
|
||||
|
||||
pointer res = session.getDboSession().find<User>().where("type = ?").bind(Type::DEMO);
|
||||
return res;
|
||||
}
|
||||
|
||||
User::pointer
|
||||
User::create(Wt::Dbo::Session& session)
|
||||
User::create(Session& session)
|
||||
{
|
||||
return session.add(std::make_unique<User>());
|
||||
session.checkUniqueLocked();
|
||||
|
||||
User::pointer user {session.getDboSession().add(std::make_unique<User>())};
|
||||
|
||||
TrackList::create(session, playedListName, TrackList::Type::Internal, false, user);
|
||||
TrackList::create(session, queuedListName, TrackList::Type::Internal, false, user);
|
||||
|
||||
session.getDboSession().flush();
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
User::pointer
|
||||
User::getById(Wt::Dbo::Session& session, IdType id)
|
||||
User::getById(Session& session, IdType id)
|
||||
{
|
||||
return session.find<User>().where("id = ?").bind( id );
|
||||
return session.getDboSession().find<User>().where("id = ?").bind( id );
|
||||
}
|
||||
|
||||
void
|
||||
@@ -95,35 +112,21 @@ User::getMaxAudioTranscodeBitrate(void) const
|
||||
}
|
||||
|
||||
Wt::Dbo::ptr<TrackList>
|
||||
User::getPlayedTrackList() const
|
||||
User::getPlayedTrackList(Session& session) const
|
||||
{
|
||||
static const std::string listName = "__played_tracks__";
|
||||
|
||||
assert(self());
|
||||
assert(IdIsValid(self()->id()));
|
||||
assert(session());
|
||||
session.checkSharedLocked();
|
||||
|
||||
auto res = TrackList::get(*session(), listName, TrackList::Type::Internal, self());
|
||||
if (!res)
|
||||
res = TrackList::create(*session(), listName, TrackList::Type::Internal, false, self());
|
||||
|
||||
return res;
|
||||
return TrackList::get(session, playedListName, TrackList::Type::Internal, self());
|
||||
}
|
||||
|
||||
Wt::Dbo::ptr<TrackList>
|
||||
User::getQueuedTrackList() const
|
||||
User::getQueuedTrackList(Session& session) const
|
||||
{
|
||||
static const std::string listName = "__queued_tracks__";
|
||||
|
||||
assert(self());
|
||||
assert(IdIsValid(self()->id()));
|
||||
assert(session());
|
||||
session.checkSharedLocked();
|
||||
|
||||
auto res = TrackList::get(*session(), listName, TrackList::Type::Internal, self());
|
||||
if (!res)
|
||||
res = TrackList::create(*session(), listName, TrackList::Type::Internal, false, self());
|
||||
|
||||
return res;
|
||||
return TrackList::get(session, queuedListName, TrackList::Type::Internal, self());
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -22,17 +22,15 @@
|
||||
#include <vector>
|
||||
|
||||
#include <Wt/Dbo/Dbo.h>
|
||||
#include <Wt/Auth/Dbo/AuthInfo.h>
|
||||
|
||||
#include "Types.hpp"
|
||||
|
||||
namespace Database {
|
||||
|
||||
class User;
|
||||
using AuthInfo = Wt::Auth::Dbo::AuthInfo<User>;
|
||||
|
||||
class Artist;
|
||||
class Release;
|
||||
class Session;
|
||||
class TrackList;
|
||||
class Track;
|
||||
|
||||
@@ -69,12 +67,13 @@ class User : public Wt::Dbo::Dbo<User>
|
||||
User();
|
||||
|
||||
// utility
|
||||
static pointer create(Wt::Dbo::Session& session);
|
||||
static pointer create(Session& session);
|
||||
|
||||
// accessors
|
||||
static pointer getById(Wt::Dbo::Session& session, IdType id);
|
||||
static std::vector<pointer> getAll(Wt::Dbo::Session& session);
|
||||
static pointer getDemo(Wt::Dbo::Session& session);
|
||||
static pointer getById(Session& session, IdType id);
|
||||
static pointer getByLoginName(const std::string& loginName);
|
||||
static std::vector<pointer> getAll(Session& session);
|
||||
static pointer getDemo(Session& session);
|
||||
|
||||
// write
|
||||
void setType(Type type) { _type = type; }
|
||||
@@ -97,8 +96,8 @@ class User : public Wt::Dbo::Dbo<User>
|
||||
bool isRepeatAllSet() const { return _repeatAll; }
|
||||
bool isRadioSet() const { return _radio; }
|
||||
|
||||
Wt::Dbo::ptr<TrackList> getQueuedTrackList() const;
|
||||
Wt::Dbo::ptr<TrackList> getPlayedTrackList() const;
|
||||
Wt::Dbo::ptr<TrackList> getPlayedTrackList(Session& session) const;
|
||||
Wt::Dbo::ptr<TrackList> getQueuedTrackList(Session& session) const;
|
||||
|
||||
void starArtist(Wt::Dbo::ptr<Artist> artist);
|
||||
void unstarArtist(Wt::Dbo::ptr<Artist> artist);
|
||||
|
||||
Reference in New Issue
Block a user