Dedicated view for tracklist

This commit is contained in:
emeric
2022-06-30 21:18:42 +02:00
parent b4e8f55d15
commit 169a5f5d08
65 changed files with 784 additions and 423 deletions
@@ -45,7 +45,7 @@ namespace Auth
LMS_LOG(AUTH, DEBUG) << "Creating user '" << loginName << "', admin = " << (type == UserType::ADMIN);
user = User::create(session, loginName);
user = session.create<User>(loginName);
user.modify()->setType(type);
}
@@ -60,7 +60,7 @@ namespace Auth
if (!user)
throw Exception {"User deleted"};
Database::AuthToken::pointer authToken {Database::AuthToken::create(session, secretHash, expiry, user)};
Database::AuthToken::pointer authToken {session.create<Database::AuthToken>(secretHash, expiry, user)};
LMS_LOG(UI, DEBUG) << "Created auth token for user '" << user->getLoginName() << "', expiry = " << expiry.toString();
+12 -16
View File
@@ -40,6 +40,12 @@ _MBID {MBID ? MBID->getAsString() : ""}
{
}
Artist::pointer
Artist::create(Session& session, const std::string& name, const std::optional<UUID>& MBID)
{
return session.getDboSession().add(std::unique_ptr<Artist> {new Artist {name, MBID}});
}
std::size_t
Artist::getCount(Session& session)
{
@@ -81,17 +87,6 @@ Artist::exists(Session& session, ArtistId id)
return session.getDboSession().query<int>("SELECT 1 FROM artist").where("id = ?").bind(id).resultValue() == 1;
}
Artist::pointer
Artist::create(Session& session, const std::string& name, const std::optional<UUID>& MBID)
{
session.checkUniqueLocked();
Artist::pointer res {session.getDboSession().add(std::make_unique<Artist>(name, MBID))};
session.getDboSession().flush();
return res;
}
static
Wt::Dbo::Query<ArtistId>
createQuery(Session& session, const Artist::FindParameters& params)
@@ -119,13 +114,13 @@ createQuery(Session& session, const Artist::FindParameters& params)
for (std::string_view keyword : params.keywords)
{
clauses.push_back("a.name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'");
query.bind("%" + escapeLikeKeyword(keyword) + "%");
query.bind("%" + Utils::escapeLikeKeyword(keyword) + "%");
}
for (std::string_view keyword : params.keywords)
{
sortClauses.push_back("a.sort_name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'");
query.bind("%" + escapeLikeKeyword(keyword) + "%");
query.bind("%" + Utils::escapeLikeKeyword(keyword) + "%");
}
query.where("(" + StringUtils::joinStrings(clauses, " AND ") + ") OR (" + StringUtils::joinStrings(sortClauses, " AND ") + ")");
@@ -193,7 +188,7 @@ Artist::findAllOrphans(Session& session, Range range)
session.checkSharedLocked();
auto query {session.getDboSession().query<ArtistId>("SELECT DISTINCT a.id 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 execQuery(query, range);
return Utils::execQuery(query, range);
}
RangeResults<ArtistId>
@@ -202,7 +197,7 @@ Artist::find(Session& session, const FindParameters& params)
session.checkSharedLocked();
auto query {createQuery(session, params)};
return execQuery(query, params.range);
return Utils::execQuery(query, params.range);
}
RangeResults<ArtistId>
@@ -246,10 +241,11 @@ Artist::findSimilarArtists(EnumSet<TrackArtistLinkType> artistLinkTypes, Range r
.bind(getId())
.groupBy("a.id")
.orderBy("COUNT(*) DESC, RANDOM()")};
for (TrackArtistLinkType type : artistLinkTypes)
query.bind(type);
return execQuery(query, range);
return Utils::execQuery(query, range);
}
std::vector<std::vector<Cluster::pointer>>
@@ -38,12 +38,7 @@ namespace Database
AuthToken::pointer
AuthToken::create(Session& session, std::string_view value, const Wt::WDateTime& expiry, ObjectPtr<User> user)
{
session.checkUniqueLocked();
AuthToken::pointer res {session.getDboSession().add(std::make_unique<AuthToken>(value, expiry, user))};
session.getDboSession().flush();
return res;
return session.getDboSession().add(std::unique_ptr<AuthToken> {new AuthToken {value, expiry, user}});
}
void
+18 -28
View File
@@ -36,6 +36,12 @@ Cluster::Cluster(ObjectPtr<ClusterType> type, std::string_view name)
{
}
Cluster::pointer
Cluster::create(Session& session, ObjectPtr<ClusterType> type, std::string_view name)
{
return session.getDboSession().add(std::unique_ptr<Cluster> {new Cluster {type, name}});
}
std::size_t
Cluster::getCount(Session& session)
{
@@ -44,24 +50,13 @@ Cluster::getCount(Session& session)
return session.getDboSession().query<int>("SELECT COUNT(*) FROM cluster");
}
Cluster::pointer
Cluster::create(Session& session, ObjectPtr<ClusterType> type, std::string_view name)
{
session.checkUniqueLocked();
Cluster::pointer res {session.getDboSession().add(std::make_unique<Cluster>(type, name))};
session.getDboSession().flush();
return res;
}
RangeResults<ClusterId>
Cluster::find(Session& session, Range range)
{
session.checkSharedLocked();
auto query {session.getDboSession().query<ClusterId>("SELECT id FROM cluster")};
return execQuery(query, range);
return Utils::execQuery(query, range);
}
RangeResults<ClusterId>
@@ -70,7 +65,7 @@ Cluster::findOrphans(Session& session, Range range)
session.checkSharedLocked();
auto query {session.getDboSession().query<ClusterId>("SELECT DISTINCT c.id FROM cluster c WHERE NOT EXISTS(SELECT 1 FROM track_cluster t_c WHERE t_c.cluster_id = c.id)")};
return execQuery(query, range);
return Utils::execQuery(query, range);
}
Cluster::pointer
@@ -95,7 +90,7 @@ Cluster::getTracks(Range range) const
auto query {session()->query<TrackId>("SELECT t.id 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(getId())};
return execQuery(query, range);
return Utils::execQuery(query, range);
}
std::size_t
@@ -113,6 +108,12 @@ ClusterType::ClusterType(std::string_view name)
{
}
ClusterType::pointer
ClusterType::create(Session& session, const std::string& name)
{
return session.getDboSession().add(std::unique_ptr<ClusterType> {new ClusterType {name}});
}
std::size_t
ClusterType::getCount(Session& session)
{
@@ -132,7 +133,7 @@ ClusterType::findOrphans(Session& session, Range range)
" LEFT OUTER JOIN cluster c ON c_t.id = c.cluster_type_id")
.where("c.id IS NULL")};
return execQuery(query, range);
return Utils::execQuery(query, range);
}
RangeResults<ClusterTypeId>
@@ -144,7 +145,7 @@ ClusterType::findUsed(Session& session, Range range)
"SELECT DISTINCT c_t.id from cluster_type c_t")
.join("cluster c ON c_t.id = c.cluster_type_id")};
return execQuery(query, range);
return Utils::execQuery(query, range);
}
ClusterType::pointer
@@ -170,18 +171,7 @@ ClusterType::find(Session& session, Range range)
auto query {session.getDboSession().query<ClusterTypeId>("SELECT id from cluster_type")};
return execQuery(query, range);
}
ClusterType::pointer
ClusterType::create(Session& session, const std::string& name)
{
session.checkUniqueLocked();
ClusterType::pointer res {session.getDboSession().add(std::make_unique<ClusterType>(name))};
session.getDboSession().flush();
return res;
return Utils::execQuery(query, range);
}
Cluster::pointer
+13 -18
View File
@@ -141,6 +141,12 @@ namespace Database
, _track {getDboPtr(track)}
{}
Listen::pointer
Listen::create(Session& session, ObjectPtr<User> user, ObjectPtr<Track> track, Scrobbler scrobbler, const Wt::WDateTime& dateTime)
{
return session.getDboSession().add(std::unique_ptr<Listen> {new Listen {user, track, scrobbler, dateTime}});
}
std::size_t
Listen::getCount(Session& session)
{
@@ -172,7 +178,7 @@ namespace Database
if (parameters.scrobblingState)
query.where("scrobbling_state = ?").bind(*parameters.scrobblingState);
return execQuery(query, parameters.range);
return Utils::execQuery(query, parameters.range);
}
Listen::pointer
@@ -188,17 +194,6 @@ namespace Database
.resultValue();
}
Listen::pointer
Listen::create(Session& session, ObjectPtr<User> user, ObjectPtr<Track> track, Scrobbler scrobbler, const Wt::WDateTime& dateTime)
{
session.checkUniqueLocked();
Listen::pointer res {session.getDboSession().add(std::make_unique<Listen>(user, track, scrobbler, dateTime))};
session.getDboSession().flush();
return res;
}
RangeResults<ArtistId>
Listen::getTopArtists(Session& session,
UserId userId,
@@ -213,7 +208,7 @@ namespace Database
.orderBy("COUNT(a.id) DESC")
.groupBy("a.id")};
return execQuery(query, range);
return Utils::execQuery(query, range);
}
RangeResults<ReleaseId>
@@ -227,7 +222,7 @@ namespace Database
.orderBy("COUNT(r.id) DESC")
.groupBy("r.id")};
return execQuery(query, range);
return Utils::execQuery(query, range);
}
RangeResults<TrackId>
@@ -241,7 +236,7 @@ namespace Database
.orderBy("COUNT(t.id) DESC")
.groupBy("t.id")};
return execQuery(query, range);
return Utils::execQuery(query, range);
}
RangeResults<ArtistId>
@@ -256,7 +251,7 @@ namespace Database
.groupBy("a.id").having("l.date_time = MAX(l.date_time)")
.orderBy("l.date_time DESC")};
return execQuery(query, range);
return Utils::execQuery(query, range);
}
RangeResults<ReleaseId>
@@ -270,7 +265,7 @@ namespace Database
.groupBy("r.id").having("l.date_time = MAX(l.date_time)")
.orderBy("l.date_time DESC")};
return execQuery(query, range);
return Utils::execQuery(query, range);
}
RangeResults<TrackId>
@@ -284,7 +279,7 @@ namespace Database
.groupBy("t.id").having("l.date_time = MAX(l.date_time)")
.orderBy("l.date_time DESC")};
return execQuery(query, range);
return Utils::execQuery(query, range);
}
+11 -1
View File
@@ -576,12 +576,21 @@ CREATE TABLE IF NOT EXISTS "track_artist_link_backup" (
migrateFromV34(Session& session)
{
// Add scrobbling state
// By default, everythin needs to be sent
// By default, everything needs to be sent
session.getDboSession().execute("ALTER TABLE starred_artist ADD scrobbling_state INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast<int>(/*ScrobblingState::PendingAdd*/0)) + ")");
session.getDboSession().execute("ALTER TABLE starred_release ADD scrobbling_state INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast<int>(/*ScrobblingState::PendingAdd*/0)) + ")");
session.getDboSession().execute("ALTER TABLE starred_track ADD scrobbling_state INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast<int>(/*ScrobblingState::PendingAdd*/0)) + ")");
}
static
void
migrateFromV35(Session& session)
{
// Add creattion/last modif date time for tracklists
session.getDboSession().execute("ALTER TABLE tracklist ADD creation_date_time TEXT");
session.getDboSession().execute("ALTER TABLE tracklist ADD last_modified_date_time TEXT");
}
void
doDbMigration(Session& session)
{
@@ -623,6 +632,7 @@ CREATE TABLE IF NOT EXISTS "track_artist_link_backup" (
{32, migrateFromV32},
{33, migrateFromV33},
{34, migrateFromV34},
{35, migrateFromV35},
};
while (1)
@@ -26,7 +26,7 @@ namespace Database
class Session;
using Version = std::size_t;
static constexpr Version LMS_DATABASE_VERSION {35};
static constexpr Version LMS_DATABASE_VERSION {36};
class VersionInfo
{
public:
+11 -16
View File
@@ -57,7 +57,7 @@ createQuery(Session& session, const Release::FindParameters& params)
}
for (std::string_view keyword : params.keywords)
query.where("r.name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'").bind("%" + escapeLikeKeyword(keyword) + "%");
query.where("r.name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'").bind("%" + Utils::escapeLikeKeyword(keyword) + "%");
if (params.starringUser.isValid())
{
@@ -172,6 +172,12 @@ _MBID {MBID ? MBID->getAsString() : ""}
{
}
Release::pointer
Release::create(Session& session, const std::string& name, const std::optional<UUID>& MBID)
{
return session.getDboSession().add(std::unique_ptr<Release> {new Release {name, MBID}});
}
std::vector<Release::pointer>
Release::find(Session& session, const std::string& name)
{
@@ -214,17 +220,6 @@ Release::exists(Session& session, ReleaseId id)
return session.getDboSession().query<int>("SELECT 1 FROM release").where("id = ?").bind(id).resultValue() == 1;
}
Release::pointer
Release::create(Session& session, const std::string& name, const std::optional<UUID>& 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(Session& session)
{
@@ -238,7 +233,7 @@ Release::findOrderedByArtist(Session& session, Range range)
{
session.checkSharedLocked();
// TODO merge with execQuery
// TODO merge with find
auto query {session.getDboSession().query<ReleaseId>(
"SELECT DISTINCT r.id FROM release r"
" INNER JOIN track t ON r.id = t.release_id"
@@ -246,7 +241,7 @@ Release::findOrderedByArtist(Session& session, Range range)
" INNER JOIN artist a ON t_a_l.artist_id = a.id")
.orderBy("a.name COLLATE NOCASE, r.name COLLATE NOCASE")};
return execQuery(query, range);
return Utils::execQuery(query, range);
}
RangeResults<ReleaseId>
@@ -255,7 +250,7 @@ Release::findOrphans(Session& session, Range range)
session.checkSharedLocked();
auto query {session.getDboSession().query<ReleaseId>("select r.id from release r LEFT OUTER JOIN Track t ON r.id = t.release_id WHERE t.id IS NULL")};
return execQuery(query, range);
return Utils::execQuery(query, range);
}
RangeResults<ReleaseId>
@@ -265,7 +260,7 @@ Release::find(Session& session, const FindParameters& params)
auto query {createQuery(session, params)};
return execQuery(query, params.range);
return Utils::execQuery(query, params.range);
}
std::optional<std::size_t>
@@ -120,7 +120,7 @@ ScanSettings::setClusterTypes(Session& session, const std::set<std::string>& clu
if (!clusterType)
{
LMS_LOG(DB, INFO) << "Creating cluster type " << clusterTypeName;
clusterType = ClusterType::create(session, clusterTypeName);
clusterType = session.create<ClusterType>(clusterTypeName);
_clusterTypes.insert(getDboPtr(clusterType));
needRescan = true;
@@ -36,6 +36,12 @@ namespace Database
{
}
StarredArtist::pointer
StarredArtist::create(Session& session, ObjectPtr<Artist> artist, ObjectPtr<User> user, Scrobbler scrobbler)
{
return session.getDboSession().add(std::unique_ptr<StarredArtist> {new StarredArtist {artist, user, scrobbler}});
}
std::size_t
StarredArtist::getCount(Session& session)
{
@@ -61,20 +67,9 @@ namespace Database
.resultValue();
}
StarredArtist::pointer
StarredArtist::create(Session& session, ObjectPtr<Artist> artist, ObjectPtr<User> user, Scrobbler scrobbler)
{
session.checkUniqueLocked();
StarredArtist::pointer res {session.getDboSession().add(std::make_unique<StarredArtist>(artist, user, scrobbler))};
session.getDboSession().flush();
return res;
}
void
StarredArtist::setDateTime(const Wt::WDateTime& dateTime)
{
_dateTime = normalizeDateTime(dateTime);
_dateTime = Utils::normalizeDateTime(dateTime);
}
}
@@ -36,6 +36,12 @@ namespace Database
{
}
StarredRelease::pointer
StarredRelease::create(Session& session, ObjectPtr<Release> release, ObjectPtr<User> user, Scrobbler scrobbler)
{
return session.getDboSession().add(std::unique_ptr<StarredRelease>{new StarredRelease{release, user, scrobbler}});
}
std::size_t
StarredRelease::getCount(Session& session)
{
@@ -61,20 +67,9 @@ namespace Database
.resultValue();
}
StarredRelease::pointer
StarredRelease::create(Session& session, ObjectPtr<Release> release, ObjectPtr<User> user, Scrobbler scrobbler)
{
session.checkUniqueLocked();
StarredRelease::pointer res {session.getDboSession().add(std::make_unique<StarredRelease>(release, user, scrobbler))};
session.getDboSession().flush();
return res;
}
void
StarredRelease::setDateTime(const Wt::WDateTime& dateTime)
{
_dateTime = normalizeDateTime(dateTime);
_dateTime = Utils::normalizeDateTime(dateTime);
}
}
@@ -36,6 +36,12 @@ namespace Database
{
}
StarredTrack::pointer
StarredTrack::create(Session& session, ObjectPtr<Track> track, ObjectPtr<User> user, Scrobbler scrobbler)
{
return session.getDboSession().add(std::unique_ptr<StarredTrack> {new StarredTrack{track, user, scrobbler}});
}
std::size_t
StarredTrack::getCount(Session& session)
{
@@ -75,23 +81,12 @@ namespace Database
if (params.user.isValid())
query.where("s_t.user_id = ?").bind(params.user);
return execQuery(query, params.range);
}
StarredTrack::pointer
StarredTrack::create(Session& session, ObjectPtr<Track> track, ObjectPtr<User> user, Scrobbler scrobbler)
{
session.checkUniqueLocked();
StarredTrack::pointer res {session.getDboSession().add(std::make_unique<StarredTrack>(track, user, scrobbler))};
session.getDboSession().flush();
return res;
return Utils::execQuery(query, params.range);
}
void
StarredTrack::setDateTime(const Wt::WDateTime& dateTime)
{
_dateTime = normalizeDateTime(dateTime);
_dateTime = Utils::normalizeDateTime(dateTime);
}
}
+8 -13
View File
@@ -46,7 +46,7 @@ createQuery(Session& session, const Track::FindParameters& params)
auto query {session.getDboSession().query<TrackId>("SELECT DISTINCT t.id from track t")};
for (std::string_view keyword : params.keywords)
query.where("t.name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'").bind("%" + escapeLikeKeyword(keyword) + "%");
query.where("t.name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'").bind("%" + Utils::escapeLikeKeyword(keyword) + "%");
if (params.writtenAfter.isValid())
query.where("t.file_last_write > ?").bind(params.writtenAfter);
@@ -156,12 +156,7 @@ Track::Track(const std::filesystem::path& p)
Track::pointer
Track::create(Session& session, const std::filesystem::path& p)
{
session.checkUniqueLocked();
Track::pointer res {session.getDboSession().add(std::make_unique<Track>(p))};
session.getDboSession().flush();
return res;
return session.getDboSession().add(std::unique_ptr<Track> {new Track {p}});
}
std::size_t
@@ -219,7 +214,7 @@ Track::findPaths(Session& session, Range range)
// TODO Dbo traits on filesystem
auto query {session.getDboSession().query<QueryResultType>("SELECT id, file_path FROM track")};
RangeResults<QueryResultType> queryResults {execQuery(query, range)};
RangeResults<QueryResultType> queryResults {Utils::execQuery(query, range)};
RangeResults<PathResult> res;
res.range = queryResults.range;
@@ -243,7 +238,7 @@ Track::findRecordingMBIDDuplicates(Session& session, Range range)
auto query {session.getDboSession().query<TrackId>( "SELECT track.id FROM track WHERE recording_mbid in (SELECT recording_mbid FROM track WHERE recording_mbid <> '' GROUP BY recording_mbid HAVING COUNT (*) > 1)")
.orderBy("track.release_id,track.disc_number,track.track_number,track.recording_mbid")};
return execQuery(query, range);
return Utils::execQuery(query, range);
}
RangeResults<TrackId>
@@ -255,7 +250,7 @@ Track::findWithRecordingMBIDAndMissingFeatures(Session& session, Range range)
.where("LENGTH(t.recording_mbid) > 0")
.where("NOT EXISTS (SELECT * FROM track_features t_f WHERE t_f.track_id = t.id)")};
return execQuery(query, range);
return Utils::execQuery(query, range);
}
std::vector<Cluster::pointer>
@@ -284,7 +279,7 @@ Track::find(Session& session, const FindParameters& parameters)
auto query {createQuery(session, parameters)};
return execQuery(query, parameters.range);
return Utils::execQuery(query, parameters.range);
}
RangeResults<TrackId>
@@ -297,7 +292,7 @@ Track::findByNameAndReleaseName(Session& session, std::string_view trackName, st
.where("t.name = ?").bind(trackName)
.where("r.name = ?").bind(releaseName)};
return execQuery(query, Range {});
return Utils::execQuery(query, Range {});
}
RangeResults<TrackId>
@@ -328,7 +323,7 @@ Track::findSimilarTracks(Session& session, const std::vector<TrackId>& tracks, R
for (TrackId trackId : tracks)
query.bind(trackId);
return execQuery(query, range);
return Utils::execQuery(query, range);
}
void
@@ -33,6 +33,12 @@ _track {getDboPtr(track)}
{
}
TrackBookmark::pointer
TrackBookmark::create(Session& session, ObjectPtr<User> user, ObjectPtr<Track> track)
{
return session.getDboSession().add(std::unique_ptr<TrackBookmark> {new TrackBookmark {user, track}});
}
std::size_t
TrackBookmark::getCount(Session& session)
{
@@ -41,18 +47,6 @@ TrackBookmark::getCount(Session& session)
return session.getDboSession().query<int>("SELECT COUNT(*) FROM track_bookmark");
}
TrackBookmark::pointer
TrackBookmark::create(Session& session, ObjectPtr<User> user, ObjectPtr<Track> track)
{
session.checkUniqueLocked();
TrackBookmark::pointer res {session.getDboSession().add(std::make_unique<TrackBookmark>(user, track))};
session.getDboSession().flush();
return res;
}
RangeResults<TrackBookmarkId>
TrackBookmark::find(Session& session, UserId userId, Range range)
{
@@ -61,7 +55,7 @@ TrackBookmark::find(Session& session, UserId userId, Range range)
auto query {session.getDboSession().query<TrackBookmarkId>("SELECT id from track_bookmark")
.where("user_id = ?").bind(userId)};
return execQuery(query, range);
return Utils::execQuery(query, range);
}
TrackBookmark::pointer
@@ -36,6 +36,12 @@ _track {getDboPtr(track)}
{
}
TrackFeatures::pointer
TrackFeatures::create(Session& session, ObjectPtr<Track> track, const std::string& jsonEncodedFeatures)
{
return session.getDboSession().add(std::unique_ptr<TrackFeatures> {new TrackFeatures {track, jsonEncodedFeatures}});
}
std::size_t
TrackFeatures::getCount(Session& session)
{
@@ -71,18 +77,7 @@ TrackFeatures::find(Session& session, Range range)
auto query {session.getDboSession().query<TrackFeaturesId>("SELECT id from track_features")};
return execQuery(query, range);
}
TrackFeatures::pointer
TrackFeatures::create(Session& session, ObjectPtr<Track> track, const std::string& jsonEncodedFeatures)
{
session.checkUniqueLocked();
TrackFeatures::pointer res {session.getDboSession().add(std::make_unique<TrackFeatures>(track, jsonEncodedFeatures))};
session.getDboSession().flush();
return res;
return Utils::execQuery(query, range);
}
FeatureValues
+35 -21
View File
@@ -36,23 +36,20 @@
namespace Database {
TrackList::TrackList(std::string_view name, TrackListType type, bool isPublic, ObjectPtr<User> user)
: _name {name},
_type {type},
_isPublic {isPublic},
_user {getDboPtr(user)}
: _name {name}
, _type {type}
, _isPublic {isPublic}
, _creationDateTime {Wt::WDateTime::currentDateTime()}
, _lastModifiedDateTime {Wt::WDateTime::currentDateTime()}
, _user {getDboPtr(user)}
{
assert(user);
}
TrackList::pointer
TrackList::create(Session& session, std::string_view name, TrackListType type, bool isPublic, ObjectPtr<User> user)
{
session.checkUniqueLocked();
assert(user);
TrackList::pointer res {session.getDboSession().add( std::make_unique<TrackList>(name, type, isPublic, user) )};
session.getDboSession().flush();
return res;
return session.getDboSession().add(std::unique_ptr<TrackList> {new TrackList {name, type, isPublic, user}});
}
std::size_t
@@ -112,9 +109,19 @@ TrackList::find(Session& session, const FindParameters& params)
query.where(oss.str());
}
query.orderBy("t_l.name COLLATE NOCASE");
switch (params.sortMethod)
{
case TrackListSortMethod::None:
break;
case TrackListSortMethod::Name:
query.orderBy("t_l.name COLLATE NOCASE");
break;
case TrackListSortMethod::LastModifiedDesc:
query.orderBy("t_l.last_modified_date_time DESC");
break;
}
return execQuery(query, params.range);
return Utils::execQuery(query, params.range);
}
TrackList::pointer
@@ -173,7 +180,7 @@ TrackList::getEntryByTrackAndDateTime(ObjectPtr<Track> track, const Wt::WDateTim
return session()->find<TrackListEntry>()
.where("tracklist_id = ?").bind(getId())
.where("track_id = ?").bind(track->getId())
.where("date_time = ?").bind(normalizeDateTime(dateTime))
.where("date_time = ?").bind(Utils::normalizeDateTime(dateTime))
.resultValue();
}
@@ -596,23 +603,30 @@ TrackList::getTopTracks(const std::vector<ClusterId>& clusterIds, std::optional<
}
TrackListEntry::TrackListEntry(ObjectPtr<Track> track, ObjectPtr<TrackList> tracklist, const Wt::WDateTime& dateTime)
: _dateTime {normalizeDateTime(dateTime)}
: _dateTime {Utils::normalizeDateTime(dateTime)}
, _track {getDboPtr(track)}
, _tracklist {getDboPtr(tracklist)}
{
assert(track);
assert(tracklist);
}
TrackListEntry::pointer
TrackListEntry::create(Session& session, ObjectPtr<Track> track, ObjectPtr<TrackList> tracklist, const Wt::WDateTime& dateTime)
{
session.checkUniqueLocked();
assert(track);
assert(tracklist);
return session.getDboSession().add(std::unique_ptr<TrackListEntry>( new TrackListEntry {track, tracklist, dateTime}));
}
auto res = session.getDboSession().add(std::make_unique<TrackListEntry>( track, tracklist, dateTime));
session.getDboSession().flush();
void
TrackListEntry::onPostCreated()
{
_tracklist.modify()->setLastModifiedDateTime(Wt::WDateTime::currentDateTime());
}
return res;
void
TrackListEntry::onPreRemove()
{
_tracklist.modify()->setLastModifiedDateTime(Wt::WDateTime::currentDateTime());
}
TrackListEntry::pointer
+7 -12
View File
@@ -35,6 +35,12 @@ User::User(std::string_view loginName)
{
}
User::pointer
User::create(Session& session, std::string_view loginName)
{
return session.getDboSession().add(std::unique_ptr<User> {new User {loginName}});
}
std::size_t
User::getCount(Session& session)
{
@@ -53,7 +59,7 @@ User::find(Session& session, const FindParameters& params)
if (params.scrobbler)
query.where("scrobbler = ?").bind(*params.scrobbler);
return execQuery(query, params.range);
return Utils::execQuery(query, params.range);
}
User::pointer
@@ -64,17 +70,6 @@ User::findDemoUser(Session& session)
return session.getDboSession().find<User>().where("type = ?").bind(UserType::DEMO).resultValue();
}
User::pointer
User::create(Session& session, std::string_view loginName)
{
session.checkUniqueLocked();
User::pointer user {session.getDboSession().add(std::make_unique<User>(loginName))};
session.getDboSession().flush();
return user;
}
User::pointer
User::find(Session& session, UserId id)
{
+2 -3
View File
@@ -21,7 +21,7 @@
#include "utils/String.hpp"
namespace Database
namespace Database::Utils
{
std::string
escapeLikeKeyword(std::string_view keyword)
@@ -35,6 +35,5 @@ namespace Database
// force second resolution
return Wt::WDateTime::fromTime_t(dateTime.toTime_t());
}
} // namespace Database
} // namespace Database::Utils
+2 -2
View File
@@ -27,7 +27,7 @@
#include "services/database/Types.hpp"
namespace Database
namespace Database::Utils
{
#define ESCAPE_CHAR_STR "\\"
static inline constexpr char escapeChar {'\\'};
@@ -82,5 +82,5 @@ namespace Database
}
Wt::WDateTime normalizeDateTime(const Wt::WDateTime& dateTime);
} // namespace Database
} // namespace Database::Utils
@@ -72,7 +72,6 @@ class Artist : public Object<Artist, ArtistId>
};
Artist() = default;
Artist(const std::string& name, const std::optional<UUID>& MBID = {});
// Accessors
static std::size_t getCount(Session& session);
@@ -101,9 +100,6 @@ class Artist : public Object<Artist, ArtistId>
void setMBID(const std::optional<UUID>& mbid) { _MBID = mbid ? mbid->getAsString() : ""; }
void setSortName(const std::string& sortName);
// Create
static pointer create(Session& session, const std::string& name, const std::optional<UUID>& UUID = {});
template<class Action>
void persist(Action& a)
{
@@ -118,6 +114,11 @@ class Artist : public Object<Artist, ArtistId>
private:
static const std::size_t _maxNameLength = 128;
friend class Session;
// Create
Artist(const std::string& name, const std::optional<UUID>& MBID = {});
static pointer create(Session& session, const std::string& name, const std::optional<UUID>& UUID = {});
std::string _name;
std::string _sortName;
std::string _MBID; // Musicbrainz Identifier
@@ -36,10 +36,8 @@ namespace Database
{
public:
AuthToken() = default;
AuthToken(std::string_view value, const Wt::WDateTime& expiry, ObjectPtr<User> user);
// Utility
static pointer create(Session& session, std::string_view value, const Wt::WDateTime&expiry, ObjectPtr<User> user);
static void removeExpiredTokens(Session& session, const Wt::WDateTime& now);
static pointer find(Session& session, std::string_view value);
@@ -57,6 +55,10 @@ namespace Database
}
private:
friend class Session;
AuthToken(std::string_view value, const Wt::WDateTime& expiry, ObjectPtr<User> user);
static pointer create(Session& session, std::string_view value, const Wt::WDateTime&expiry, ObjectPtr<User> user);
std::string _value;
Wt::WDateTime _expiry;
Wt::Dbo::ptr<User> _user;
@@ -42,7 +42,6 @@ class Cluster : public Object<Cluster, ClusterId>
{
public:
Cluster() = default;
Cluster(ObjectPtr<ClusterType> type, std::string_view name);
// Find utility
static std::size_t getCount(Session& session);
@@ -50,9 +49,6 @@ class Cluster : public Object<Cluster, ClusterId>
static pointer find(Session& session, ClusterId id);
static RangeResults<ClusterId> findOrphans(Session& session, Range range);
// Create utility
static pointer create(Session& session, ObjectPtr<ClusterType> type, std::string_view name);
// Accessors
const std::string& getName() const { return _name; }
ObjectPtr<ClusterType> getType() const { return _clusterType; }
@@ -72,6 +68,10 @@ class Cluster : public Object<Cluster, ClusterId>
}
private:
friend class Session;
Cluster(ObjectPtr<ClusterType> type, std::string_view name);
static pointer create(Session& session, ObjectPtr<ClusterType> type, std::string_view name);
static const std::size_t _maxNameLength = 128;
std::string _name;
@@ -85,7 +85,6 @@ class ClusterType : public Object<ClusterType, ClusterTypeId>
{
public:
ClusterType() = default;
ClusterType(std::string_view name);
// Getters
static std::size_t getCount(Session& session);
@@ -95,7 +94,6 @@ class ClusterType : public Object<ClusterType, ClusterTypeId>
static RangeResults<ClusterTypeId> findOrphans(Session& session, Range range);
static RangeResults<ClusterTypeId> findUsed(Session& session, Range range);
static pointer create(Session& session, const std::string& name);
static void remove(Session& session, const std::string& name);
// Accessors
@@ -112,6 +110,10 @@ class ClusterType : public Object<ClusterType, ClusterTypeId>
}
private:
friend class Session;
ClusterType(std::string_view name);
static pointer create(Session& session, const std::string& name);
static const std::size_t _maxNameLength = 128;
std::string _name;
@@ -42,7 +42,6 @@ class Listen : public Object<Listen, ListenId>
{
public:
Listen() = default;
Listen(ObjectPtr<User> user, ObjectPtr<Track> track, Scrobbler scrobbler, const Wt::WDateTime& dateTime);
struct FindParameters
{
@@ -63,9 +62,6 @@ class Listen : public Object<Listen, ListenId>
static pointer find(Session& session, UserId userId, TrackId trackId, Scrobbler scrobbler, const Wt::WDateTime& dateTime);
static RangeResults<ListenId> find(Session& session, const FindParameters& parameters);
// Create
static pointer create(Session& session, ObjectPtr<User> user, ObjectPtr<Track> track, Scrobbler scrobbler, const Wt::WDateTime& dateTime);
// Stats
static RangeResults<ArtistId> getTopArtists(Session& session,
UserId userId,
@@ -120,6 +116,10 @@ class Listen : public Object<Listen, ListenId>
}
private:
friend class Session;
Listen(ObjectPtr<User> user, ObjectPtr<Track> track, Scrobbler scrobbler, const Wt::WDateTime& dateTime);
static pointer create(Session& session, ObjectPtr<User> user, ObjectPtr<Track> track, Scrobbler scrobbler, const Wt::WDateTime& dateTime);
Wt::WDateTime _dateTime;
Scrobbler _scrobbler;
ScrobblingState _scrobblingState {ScrobblingState::PendingAdd};
@@ -19,6 +19,7 @@
#pragma once
#include <Wt/WSignal.h>
#include <Wt/Dbo/ptr.h>
#include "services/database/IdType.hpp"
@@ -38,7 +39,11 @@ namespace Database
bool operator!=(const ObjectPtr& other) const { return other._obj != _obj; }
auto modify() { return _obj.modify(); }
void remove() { _obj.remove(); }
void remove()
{
_obj.modify()->onPreRemove();
_obj.remove();
}
private:
template <typename, typename> friend class Object;
@@ -61,6 +66,10 @@ namespace Database
typename Wt::Dbo::dbo_traits<T>::IdType id() const = delete;
protected:
template <typename> friend class ObjectPtr;
virtual void onPostCreated() {}
virtual void onPreRemove() {}
// Can get raw dbo ptr only from Objects
template <typename SomeObject>
static
@@ -79,7 +79,6 @@ class Release : public Object<Release, ReleaseId>
};
Release() = default;
Release(const std::string& name, const std::optional<UUID>& MBID = {});
// Accessors
static std::size_t getCount(Session& session);
@@ -98,9 +97,6 @@ class Release : public Object<Release, ReleaseId>
// size is the max number of cluster per cluster type
std::vector<std::vector<ObjectPtr<Cluster>>> getClusterGroups(const std::vector<ObjectPtr<ClusterType>>& clusterTypes, std::size_t size) const;
// Create
static pointer create(Session& session, const std::string& name, const std::optional<UUID>& MBID = {});
// Utility functions
std::optional<int> getReleaseYear(bool originalDate = false) const;
std::optional<std::string> getCopyright() const;
@@ -133,6 +129,10 @@ class Release : public Object<Release, ReleaseId>
}
private:
friend class Session;
Release(const std::string& name, const std::optional<UUID>& MBID = {});
static pointer create(Session& session, const std::string& name, const std::optional<UUID>& MBID = {});
static const std::size_t _maxNameLength {128};
std::string _name;
@@ -39,7 +39,8 @@ class ScanSettings : public Object<ScanSettings, ScanSettingsId>
{
public:
// Do not modify values (just add)
enum class UpdatePeriod {
enum class UpdatePeriod
{
Never = 0,
Daily,
Weekly,
@@ -19,16 +19,15 @@
#pragma once
#include <mutex>
#include <Wt/Dbo/Dbo.h>
#include <Wt/Dbo/SqlConnectionPool.h>
#include "utils/RecursiveSharedMutex.hpp"
#include "services/database/Object.hpp"
namespace Database
{
class UniqueTransaction
{
private:
@@ -73,11 +72,22 @@ namespace Database
Wt::Dbo::Session& getDboSession() { return _session; }
Db& getDb() { return _db; }
template <typename Object, typename... Args>
typename Object::pointer create(Args&&... args)
{
checkUniqueLocked();
typename Object::pointer res {Object::create(*this, std::forward<Args>(args)...)};
getDboSession().flush();
res.modify()->onPostCreated();
return res;
}
private:
Db& _db;
Wt::Dbo::Session _session;
};
} // namespace Database
@@ -38,16 +38,12 @@ namespace Database
{
public:
StarredArtist() = default;
StarredArtist(ObjectPtr<Artist> artist, ObjectPtr<User> user, Scrobbler scrobbler);
// Search utility
static std::size_t getCount(Session& session);
static pointer find(Session& session, StarredArtistId id);
static pointer find(Session& session, ArtistId artistId, UserId userId, Scrobbler scrobbler);
// Create utility
static pointer create(Session& session, ObjectPtr<Artist> artist, ObjectPtr<User> user, Scrobbler scrobbler);
// Accessors
ObjectPtr<Artist> getArtist() const { return _artist; }
ObjectPtr<User> getUser() const { return _user; }
@@ -71,6 +67,10 @@ namespace Database
}
private:
friend class Session;
StarredArtist(ObjectPtr<Artist> artist, ObjectPtr<User> user, Scrobbler scrobbler);
static pointer create(Session& session, ObjectPtr<Artist> artist, ObjectPtr<User> user, Scrobbler scrobbler);
Scrobbler _scrobbler; // for which scrobbler
ScrobblingState _scrobblingState {ScrobblingState::PendingAdd};
Wt::WDateTime _dateTime; // when it was starred
@@ -38,16 +38,12 @@ namespace Database
{
public:
StarredRelease() = default;
StarredRelease(ObjectPtr<Release> release, ObjectPtr<User> user, Scrobbler scrobbler);
// Search utility
static std::size_t getCount(Session& session);
static pointer find(Session& session, StarredReleaseId id);
static pointer find(Session& session, ReleaseId releaseId, UserId userId, Scrobbler scrobbler);
// Create utility
static pointer create(Session& session, ObjectPtr<Release> release, ObjectPtr<User> user, Scrobbler scrobbler);
// Accessors
ObjectPtr<Release> getRelease() const { return _release; }
ObjectPtr<User> getUser() const { return _user; }
@@ -71,6 +67,10 @@ namespace Database
}
private:
friend class Session;
StarredRelease(ObjectPtr<Release> release, ObjectPtr<User> user, Scrobbler scrobbler);
static pointer create(Session& session, ObjectPtr<Release> release, ObjectPtr<User> user, Scrobbler scrobbler);
Scrobbler _scrobbler; // for which scrobbler
ScrobblingState _scrobblingState {ScrobblingState::PendingAdd};
Wt::WDateTime _dateTime; // when it was starred
@@ -39,7 +39,6 @@ namespace Database
{
public:
StarredTrack() = default;
StarredTrack(ObjectPtr<Track> track, ObjectPtr<User> user, Scrobbler scrobbler);
struct FindParameters
{
@@ -59,9 +58,6 @@ namespace Database
static pointer find(Session& session, TrackId trackId, UserId userId, Scrobbler scrobbler);
static RangeResults<StarredTrackId> find(Session& session, const FindParameters& findParams);
// Create utility
static pointer create(Session& session, ObjectPtr<Track> track, ObjectPtr<User> user, Scrobbler scrobbler);
// Accessors
ObjectPtr<Track> getTrack() const { return _track; }
ObjectPtr<User> getUser() const { return _user; }
@@ -85,6 +81,10 @@ namespace Database
}
private:
friend class Session;
StarredTrack(ObjectPtr<Track> track, ObjectPtr<User> user, Scrobbler scrobbler);
static pointer create(Session& session, ObjectPtr<Track> track, ObjectPtr<User> user, Scrobbler scrobbler);
Scrobbler _scrobbler; // for which scrobbler
ScrobblingState _scrobblingState {ScrobblingState::PendingAdd};
Wt::WDateTime _dateTime; // when it was starred
@@ -91,7 +91,6 @@ class Track : public Object<Track, TrackId>
};
Track() = default;
Track(const std::filesystem::path& p);
// Find utility functions
static std::size_t getCount(Session& session);
@@ -107,9 +106,6 @@ class Track : public Object<Track, TrackId>
static RangeResults<TrackId> findRecordingMBIDDuplicates(Session& session, Range range);
static RangeResults<TrackId> findWithRecordingMBIDAndMissingFeatures(Session& session, Range range);
// Create utility
static pointer create(Session& session, const std::filesystem::path& p);
// Accessors
void setScanVersion(std::size_t version) { _scanVersion = version; }
void setTrackNumber(int num) { _trackNumber = num; }
@@ -196,6 +192,10 @@ class Track : public Object<Track, TrackId>
}
private:
friend class ::Database::Session;
Track(const std::filesystem::path& p);
static pointer create(Session& session, const std::filesystem::path& p);
static const std::size_t _maxNameLength = 128;
static const std::size_t _maxCopyrightLength = 128;
static const std::size_t _maxCopyrightURLLength = 128;
@@ -41,10 +41,6 @@ class TrackBookmark : public Object<TrackBookmark, TrackBookmarkId>
{
public:
TrackBookmark () = default;
TrackBookmark(ObjectPtr<User> user, ObjectPtr<Track> track);
// utility
static pointer create(Session& session, ObjectPtr<User> user, ObjectPtr<Track> track);
// Find utility functions
static std::size_t getCount(Session& session);
@@ -70,7 +66,12 @@ class TrackBookmark : public Object<TrackBookmark, TrackBookmarkId>
Wt::Dbo::belongsTo(a, _track, "track", Wt::Dbo::OnDeleteCascade);
Wt::Dbo::belongsTo(a, _user, "user", Wt::Dbo::OnDeleteCascade);
}
private:
friend class Session;
TrackBookmark(ObjectPtr<User> user, ObjectPtr<Track> track);
static pointer create(Session& session, ObjectPtr<User> user, ObjectPtr<Track> track);
static const std::size_t _maxCommentLength = 128;
std::chrono::duration<int, std::milli> _offset;
@@ -46,7 +46,6 @@ class TrackFeatures : public Object<TrackFeatures, TrackFeaturesId>
{
public:
TrackFeatures() = default;
TrackFeatures(ObjectPtr<Track> track, const std::string& jsonEncodedFeatures);
// Find utilities
static std::size_t getCount(Session& session);
@@ -54,9 +53,6 @@ class TrackFeatures : public Object<TrackFeatures, TrackFeaturesId>
static pointer find(Session& session, TrackId trackId);
static RangeResults<TrackFeaturesId> find(Session& session, Range range);
// Create utility
static pointer create(Session& session, ObjectPtr<Track> track, const std::string& jsonEncodedFeatures);
FeatureValues getFeatureValues(const FeatureName& feature) const;
FeatureValuesMap getFeatureValuesMap(const std::unordered_set<FeatureName>& featureNames) const;
@@ -71,6 +67,10 @@ class TrackFeatures : public Object<TrackFeatures, TrackFeaturesId>
}
private:
friend class Session;
TrackFeatures(ObjectPtr<Track> track, const std::string& jsonEncodedFeatures);
static pointer create(Session& session, ObjectPtr<Track> track, const std::string& jsonEncodedFeatures);
std::string _data;
Wt::Dbo::ptr<Track> _track;
};
@@ -48,7 +48,6 @@ class TrackList : public Object<TrackList, TrackListId>
{
public:
TrackList() = default;
TrackList(std::string_view name, TrackListType type, bool isPublic, ObjectPtr<User> user);
// Stats utility
std::vector<ObjectPtr<Artist>> getTopArtists(const std::vector<ClusterId>& clusterIds, std::optional<TrackArtistLinkType> linkType, std::optional<Range> range, bool& moreResults) const;
@@ -62,20 +61,19 @@ class TrackList : public Object<TrackList, TrackListId>
Range range;
std::optional<TrackListType> type;
UserId user; // only tracklists owned by this user
TrackListSortMethod sortMethod {TrackListSortMethod::None};
FindParameters& setClusters(const std::vector<ClusterId>& _clusters) { clusters = _clusters; return *this; }
FindParameters& setRange(Range _range) { range = _range; return *this; }
FindParameters& setType(TrackListType _type) { type = _type; return *this; }
FindParameters& setUser(UserId _user) { user = _user; return *this; }
FindParameters& setSortMethod(TrackListSortMethod _sortMethod) {sortMethod = _sortMethod; return *this; }
};
static std::size_t getCount(Session& session);
static pointer find(Session& session, std::string_view name, TrackListType type, UserId userId);
static pointer find(Session& session, TrackListId tracklistId);
static RangeResults<TrackListId> find(Session& session, const FindParameters& params);
// Create utility
static pointer create(Session& session, std::string_view name, TrackListType type, bool isPublic, ObjectPtr<User> user);
// Accessors
std::string_view getName() const { return _name; }
bool isPublic() const { return _isPublic; }
@@ -86,6 +84,7 @@ class TrackList : public Object<TrackList, TrackListId>
void setName(const std::string& name) { _name = name; }
void setIsPublic(bool isPublic) { _isPublic = isPublic; }
void clear() { _entries.clear(); }
void setLastModifiedDateTime(const Wt::WDateTime& dateTime) { _lastModifiedDateTime = dateTime; }
// Get tracks, ordered by position
bool isEmpty() const;
@@ -117,18 +116,26 @@ class TrackList : public Object<TrackList, TrackListId>
template<class Action>
void persist(Action& a)
{
Wt::Dbo::field(a, _name, "name");
Wt::Dbo::field(a, _type, "type");
Wt::Dbo::field(a, _isPublic, "public");
Wt::Dbo::field(a, _name, "name");
Wt::Dbo::field(a, _type, "type");
Wt::Dbo::field(a, _isPublic, "public");
Wt::Dbo::field(a, _creationDateTime, "creation_date_time");
Wt::Dbo::field(a, _lastModifiedDateTime, "last_modified_date_time");
Wt::Dbo::belongsTo(a, _user, "user", Wt::Dbo::OnDeleteCascade);
Wt::Dbo::hasMany(a, _entries, Wt::Dbo::ManyToOne, "tracklist");
}
private:
friend class Session;
TrackList(std::string_view name, TrackListType type, bool isPublic, ObjectPtr<User> user);
static pointer create(Session& session, std::string_view name, TrackListType type, bool isPublic, ObjectPtr<User> user);
std::string _name;
TrackListType _type {TrackListType::Playlist};
bool _isPublic {false};
Wt::WDateTime _creationDateTime;
Wt::WDateTime _lastModifiedDateTime;
Wt::Dbo::ptr<User> _user;
Wt::Dbo::collection<Wt::Dbo::ptr<TrackListEntry>> _entries;
@@ -138,15 +145,13 @@ class TrackListEntry : public Object<TrackListEntry, TrackListEntryId>
{
public:
TrackListEntry() = default;
TrackListEntry(ObjectPtr<Track> track, ObjectPtr<TrackList> tracklist, const Wt::WDateTime& dateTime);
TrackListEntry(ObjectPtr<Track> track, ObjectPtr<TrackList> tracklist);
void onPostCreated() override;
void onPreRemove() override;
// find utility
static pointer getById(Session& session, TrackListEntryId id);
// Create utility
static pointer create(Session& session, ObjectPtr<Track> track, ObjectPtr<TrackList> tracklist, const Wt::WDateTime& dateTime = {});
// Accessors
ObjectPtr<Track> getTrack() const { return _track; }
const Wt::WDateTime& getDateTime() const { return _dateTime; }
@@ -161,6 +166,11 @@ class TrackListEntry : public Object<TrackListEntry, TrackListEntryId>
}
private:
friend class Session;
TrackListEntry(ObjectPtr<Track> track, ObjectPtr<TrackList> tracklist, const Wt::WDateTime& dateTime);
TrackListEntry(ObjectPtr<Track> track, ObjectPtr<TrackList> tracklist);
static pointer create(Session& session, ObjectPtr<Track> track, ObjectPtr<TrackList> tracklist, const Wt::WDateTime& dateTime = {});
Wt::WDateTime _dateTime; // optional date time
Wt::Dbo::ptr<Track> _track;
Wt::Dbo::ptr<TrackList> _tracklist;
@@ -104,6 +104,13 @@ namespace Database
StarredDateDesc,
};
enum class TrackListSortMethod
{
None,
Name,
LastModifiedDesc,
};
enum class TrackSortMethod
{
None,
@@ -64,10 +64,6 @@ class User : public Object<User, UserId>
static inline const Scrobbler defaultScrobbler {Scrobbler::Internal};
User() = default;
User(std::string_view loginName);
// utility
static pointer create(Session& session, std::string_view loginName);
static std::size_t getCount(Session& session);
static pointer find(Session& session, UserId id);
@@ -137,6 +133,10 @@ class User : public Object<User, UserId>
}
private:
friend class Session;
User(std::string_view loginName);
static pointer create(Session& session, std::string_view loginName);
std::string _loginName;
std::string _passwordSalt;
std::string _passwordHash;
@@ -345,7 +345,6 @@ TEST_F(DatabaseFixture, Artist_nonReleaseTracks)
track1.get().modify()->setRelease(release.get());
}
{
auto transaction {session.createSharedTransaction()};
+11 -11
View File
@@ -211,9 +211,9 @@ TEST_F(DatabaseFixture, Cluster_multiTracksMultipleClustersTopRelease)
{
auto transaction {session.createUniqueTransaction()};
TrackListEntry::create(session, trackA.get(), trackList.get());
TrackListEntry::create(session, trackB.get(), trackList.get());
TrackListEntry::create(session, trackB.get(), trackList.get());
session.create<TrackListEntry>(trackA.get(), trackList.get());
session.create<TrackListEntry>(trackB.get(), trackList.get());
session.create<TrackListEntry>(trackB.get(), trackList.get());
}
{
@@ -249,9 +249,9 @@ TEST_F(DatabaseFixture, Cluster_multiTracksMultipleClustersTopRelease)
{
auto transaction {session.createUniqueTransaction()};
TrackListEntry::create(session, trackC.get(), trackList.get());
TrackListEntry::create(session, trackC.get(), trackList.get());
TrackListEntry::create(session, trackC.get(), trackList.get());
session.create<TrackListEntry>(trackC.get(), trackList.get());
session.create<TrackListEntry>(trackC.get(), trackList.get());
session.create<TrackListEntry>(trackC.get(), trackList.get());
}
{
@@ -630,7 +630,7 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackSingleCluster)
auto transaction {session.createUniqueTransaction()};
if (i < 5)
TrackListEntry::create(session, tracks.back().get(), trackList.get());
session.create<TrackListEntry>(tracks.back().get(), trackList.get());
if (i < 10)
cluster.get().modify()->addTrack(tracks.back().get());
@@ -663,7 +663,7 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClusters)
auto transaction {session.createUniqueTransaction()};
if (i < 5)
TrackListEntry::create(session, tracks.back().get(), trackList.get());
session.create<TrackListEntry>(tracks.back().get(), trackList.get());
if (i < 10)
{
@@ -742,7 +742,7 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClustersRecentlyPlayed)
{
auto transaction {session.createUniqueTransaction()};
TrackListEntry::create(session, track1.get(), trackList.get(), now);
session.create<TrackListEntry>(track1.get(), trackList.get(), now);
}
{
@@ -828,7 +828,7 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClustersRecentlyPlayed)
{
auto transaction {session.createUniqueTransaction()};
TrackListEntry::create(session, track2.get(), trackList.get(), now.addSecs(1));
session.create<TrackListEntry>(track2.get(), trackList.get(), now.addSecs(1));
}
{
@@ -908,7 +908,7 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClustersRecentlyPlayed)
{
auto transaction {session.createUniqueTransaction()};
TrackListEntry::create(session, track1.get(), trackList.get(), now.addSecs(2));
session.create<TrackListEntry>(track1.get(), trackList.get(), now.addSecs(2));
}
{
+5 -2
View File
@@ -29,6 +29,7 @@
#include "services/database/Db.hpp"
#include "services/database/Listen.hpp"
#include "services/database/Release.hpp"
#include "services/database/ScanSettings.hpp"
#include "services/database/Session.hpp"
#include "services/database/Track.hpp"
#include "services/database/TrackArtistLink.hpp"
@@ -50,7 +51,7 @@ class ScopedEntity
{
auto transaction {_session.createUniqueTransaction()};
auto entity {T::create(_session, std::forward<Args>(args)...)};
auto entity {_session.create<T>(std::forward<Args>(args)...)};
EXPECT_TRUE(entity);
_id = entity->getId();
}
@@ -60,7 +61,9 @@ class ScopedEntity
auto transaction {_session.createUniqueTransaction()};
auto entity {T::find(_session, _id)};
entity.remove();
// could not be here due to "on delete cascade" constraints...
if (entity)
entity.remove();
}
ScopedEntity(const ScopedEntity&) = delete;
+62 -15
View File
@@ -59,7 +59,7 @@ TEST_F(DatabaseFixture, SingleTrackListSingleTrack)
{
auto transaction {session.createUniqueTransaction()};
TrackListEntry::create(session, track.get(), trackList1.get());
session.create<TrackListEntry>(track.get(), trackList1.get());
}
{
@@ -74,6 +74,53 @@ TEST_F(DatabaseFixture, SingleTrackListSingleTrack)
}
}
TEST_F(DatabaseFixture, TrackList_SortMethod)
{
ScopedUser user {session, "MyUser"};
ScopedTrackList trackList2 {session, "MyTrackList2", TrackListType::Playlist, false, user.lockAndGet()};
ScopedTrackList trackList1 {session, "MyTrackList1", TrackListType::Playlist, false, user.lockAndGet()};
ScopedTrack track {session, "MyTrack"};
{
auto transaction {session.createSharedTransaction()};
const auto trackLists {TrackList::find(session, TrackList::FindParameters {}.setSortMethod(TrackListSortMethod::Name))};
ASSERT_EQ(trackLists.results.size(), 2);
EXPECT_EQ(trackLists.results[0], trackList1.getId());
EXPECT_EQ(trackLists.results[1], trackList2.getId());
}
{
auto transaction {session.createUniqueTransaction()};
session.create<TrackListEntry>(track.get(), trackList1.get());
}
{
auto transaction {session.createSharedTransaction()};
const auto trackLists {TrackList::find(session, TrackList::FindParameters {}.setSortMethod(TrackListSortMethod::LastModifiedDesc))};
ASSERT_EQ(trackLists.results.size(), 2);
EXPECT_EQ(trackLists.results[0], trackList1.getId());
EXPECT_EQ(trackLists.results[1], trackList2.getId());
}
{
auto transaction {session.createUniqueTransaction()};
session.create<TrackListEntry>(track.get(), trackList2.get());
}
{
auto transaction {session.createSharedTransaction()};
const auto trackLists {TrackList::find(session, TrackList::FindParameters {}.setSortMethod(TrackListSortMethod::LastModifiedDesc))};
ASSERT_EQ(trackLists.results.size(), 2);
EXPECT_EQ(trackLists.results[0], trackList2.getId());
EXPECT_EQ(trackLists.results[1], trackList1.getId());
}
}
TEST_F(DatabaseFixture, SingleTrackListMultipleTrack)
{
ScopedUser user {session, "MyUser"};
@@ -85,7 +132,7 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrack)
tracks.emplace_back(session, "MyTrack" + std::to_string(i));
auto transaction {session.createUniqueTransaction()};
TrackListEntry::create(session, tracks.back().get(), trackList.get());
session.create<TrackListEntry>(tracks.back().get(), trackList.get());
}
{
@@ -127,9 +174,9 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackDateTime)
{
Wt::WDateTime now {Wt::WDateTime::currentDateTime()};
auto transaction {session.createUniqueTransaction()};
TrackListEntry::create(session, track1.get(), trackList.get(), now);
TrackListEntry::create(session, track2.get(), trackList.get(), now.addSecs(-1));
TrackListEntry::create(session, track3.get(), trackList.get(), now.addSecs(1));
session.create<TrackListEntry>(track1.get(), trackList.get(), now);
session.create<TrackListEntry>(track2.get(), trackList.get(), now.addSecs(-1));
session.create<TrackListEntry>(track3.get(), trackList.get(), now.addSecs(1));
}
{
@@ -176,7 +223,7 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackRecentlyPlayed)
{
auto transaction {session.createUniqueTransaction()};
TrackListEntry::create(session, track1.get(), trackList.get(), now);
session.create<TrackListEntry>(track1.get(), trackList.get(), now);
}
{
@@ -198,7 +245,7 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackRecentlyPlayed)
{
auto transaction {session.createUniqueTransaction()};
TrackListEntry::create(session, track2.get(), trackList.get(), now.addSecs(1));
session.create<TrackListEntry>(track2.get(), trackList.get(), now.addSecs(1));
}
{
@@ -224,7 +271,7 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackRecentlyPlayed)
{
auto transaction {session.createUniqueTransaction()};
TrackListEntry::create(session, track1.get(), trackList.get(), now.addSecs(2));
session.create<TrackListEntry>(track1.get(), trackList.get(), now.addSecs(2));
}
{
@@ -270,7 +317,7 @@ TEST_F(DatabaseFixture, SingleTrackList_getArtists)
{
auto transaction {session.createUniqueTransaction()};
TrackArtistLink::create(session, track1.get(), artist1.get(), TrackArtistLinkType::Artist);
TrackListEntry::create(session, track1.get(), trackList.get());
session.create<TrackListEntry>(track1.get(), trackList.get());
}
{
@@ -303,7 +350,7 @@ TEST_F(DatabaseFixture, SingleTrackList_getArtists)
{
auto transaction {session.createUniqueTransaction()};
TrackArtistLink::create(session, track2.get(), artist2.get(), TrackArtistLinkType::Artist);
TrackListEntry::create(session, track2.get(), trackList.get());
session.create<TrackListEntry>(track2.get(), trackList.get());
artist1.get().modify()->setSortName("ZZZ");
artist2.get().modify()->setSortName("AAA");
}
@@ -350,7 +397,7 @@ TEST_F(DatabaseFixture, SingleTrackList_getReleases)
track.get().modify()->setRelease(release.get());
TrackListEntry::create(session, track.get(), trackList.get());
session.create<TrackListEntry>(track.get(), trackList.get());
}
{
@@ -382,9 +429,9 @@ TEST_F(DatabaseFixture, SingleTrackList_getTracks)
{
auto transaction {session.createUniqueTransaction()};
TrackListEntry::create(session, track1.get(), trackList.get());
TrackListEntry::create(session, track2.get(), trackList.get());
TrackListEntry::create(session, track1.get(), trackList.get());
session.create<TrackListEntry>(track1.get(), trackList.get());
session.create<TrackListEntry>(track2.get(), trackList.get());
session.create<TrackListEntry>(track1.get(), trackList.get());
}
{
@@ -418,7 +465,7 @@ TEST_F(DatabaseFixture, SingleTrackListSingleTrackWithCluster)
{
auto transaction {session.createUniqueTransaction()};
TrackListEntry::create(session, track.get(), trackList1.get());
session.create<TrackListEntry>(track.get(), trackList1.get());
cluster.get().modify()->addTrack(track.get());
}
@@ -100,7 +100,7 @@ static
Artist::pointer
createArtist(Session& session, const MetaData::Artist& artistInfo)
{
Artist::pointer artist {Artist::create(session, artistInfo.name)};
Artist::pointer artist {session.create<Artist>(artistInfo.name)};
if (artistInfo.musicBrainzArtistID)
artist.modify()->setMBID(*artistInfo.musicBrainzArtistID);
@@ -187,7 +187,7 @@ getOrCreateRelease(Session& session, const MetaData::Album& album)
release = Release::find(session, *album.musicBrainzAlbumID);
if (!release)
{
release = Release::create(session, album.name, album.musicBrainzAlbumID);
release = session.create<Release>(album.name, album.musicBrainzAlbumID);
}
else if (release->getName() != album.name)
{
@@ -213,7 +213,7 @@ getOrCreateRelease(Session& session, const MetaData::Album& album)
// No release found with the same name and without MBID -> creating
if (!release)
release = Release::create(session, album.name);
release = session.create<Release>(album.name);
return release;
}
@@ -236,7 +236,7 @@ getOrCreateClusters(Session& session, const MetaData::Clusters& clustersNames)
{
auto cluster = clusterType->getCluster(clusterName);
if (!cluster)
cluster = Cluster::create(session, clusterType, clusterName);
cluster = session.create<Cluster>(clusterType, clusterName);
clusters.push_back(cluster);
}
@@ -567,7 +567,7 @@ ScannerService::fetchTrackFeatures(TrackId trackId, const UUID& recordingMBID)
if (!track)
return false;
TrackFeatures::create(_dbSession, track, data);
_dbSession.create<TrackFeatures>(track, data);
}
return true;
@@ -788,7 +788,7 @@ ScannerService::scanAudioFile(const std::filesystem::path& file, bool forceScan,
if (!track)
{
// Create a new song
track = Track::create(_dbSession, file);
track = _dbSession.create<Track>(file);
LMS_LOG(DBUPDATER, INFO) << "Adding '" << file.string() << "'";
stats.additions++;
}
@@ -51,7 +51,7 @@ namespace Scrobbling
if (!user)
return;
starredObj = StarredObjType::create(session, obj, user, *scrobbler);
starredObj = session.create<StarredObjType>(obj, user, *scrobbler);
}
starredObj.modify()->setDateTime(Wt::WDateTime::currentDateTime());
starredObjId = starredObj->getId();
@@ -88,7 +88,7 @@ namespace Scrobbling
if (!track)
return;
auto dbListen {Database::Listen::create(session, user, track, Database::Scrobbler::Internal, listen.listenedAt)};
auto dbListen {session.create<Database::Listen>(user, track, Database::Scrobbler::Internal, listen.listenedAt)};
dbListen.modify()->setScrobblingState(Database::ScrobblingState::Synchronized);
}
@@ -574,7 +574,7 @@ namespace Scrobbling::ListenBrainz
if (!user)
return;
StarredTrack::pointer starredTrack {StarredTrack::create(session, track, user, Database::Scrobbler::ListenBrainz)};
StarredTrack::pointer starredTrack {session.create<StarredTrack>(track, user, Database::Scrobbler::ListenBrainz)};
starredTrack.modify()->setScrobblingState(ScrobblingState::Synchronized);
context.importedFeedbackCount++;
@@ -366,7 +366,7 @@ namespace Scrobbling::ListenBrainz
if (!track)
return false;
dbListen = Database::Listen::create(session, user, track, Database::Scrobbler::ListenBrainz, listen.listenedAt);
dbListen = session.create<Database::Listen>(user, track, Database::Scrobbler::ListenBrainz, listen.listenedAt);
dbListen.modify()->setScrobblingState(scrobblingState);
LOG(DEBUG) << "LISTEN CREATED for user " << user->getLoginName() << ", track '" << track->getName() << "' AT " << listen.listenedAt.toString();
+5 -5
View File
@@ -563,7 +563,7 @@ handleCreatePlaylistRequest(RequestContext& context)
}
else
{
tracklist = TrackList::create(context.dbSession, *name, TrackListType::Playlist, false, user);
tracklist = context.dbSession.create<TrackList>(*name, TrackListType::Playlist, false, user);
}
for (const TrackId trackId : trackIds)
@@ -572,7 +572,7 @@ handleCreatePlaylistRequest(RequestContext& context)
if (!track)
continue;
TrackListEntry::create(context.dbSession, track, tracklist);
context.dbSession.create<TrackListEntry>(track, tracklist);
}
return Response::createOkResponse(context.serverProtocolVersion);
@@ -594,7 +594,7 @@ handleCreateUserRequest(RequestContext& context)
if (user)
throw UserAlreadyExistsGenericError {};
user = User::create(context.dbSession, username);
user = context.dbSession.create<User>(username);
userId = user->getId();
}
@@ -1699,7 +1699,7 @@ handleUpdatePlaylistRequest(RequestContext& context)
if (!track)
continue;
TrackListEntry::create(context.dbSession, track, tracklist);
context.dbSession.create<TrackListEntry>(track, tracklist);
}
return Response::createOkResponse(context.serverProtocolVersion);
@@ -1754,7 +1754,7 @@ handleCreateBookmark(RequestContext& context)
// Replace any existing bookmark
auto bookmark {TrackBookmark::find(context.dbSession, user->getId(), trackId)};
if (!bookmark)
bookmark = TrackBookmark::create(context.dbSession, user, track);
bookmark = context.dbSession.create<TrackBookmark>(user, track);
bookmark.modify()->setOffset(std::chrono::milliseconds {position});
if (comment)