Dedicated view for tracklist
This commit is contained in:
@@ -33,6 +33,7 @@
|
|||||||
<message id="Lms.Error.error-occurred">Error occured!</message>
|
<message id="Lms.Error.error-occurred">Error occured!</message>
|
||||||
<message id="Lms.Error.go-home">Go home</message>
|
<message id="Lms.Error.go-home">Go home</message>
|
||||||
<message id="Lms.Error.release-not-found">Release not found</message>
|
<message id="Lms.Error.release-not-found">Release not found</message>
|
||||||
|
<message id="Lms.Error.tracklist-not-found">Playlist not found</message>
|
||||||
<message id="Lms.Error.user-not-allowed">You are not allowed to perform this operation</message>
|
<message id="Lms.Error.user-not-allowed">You are not allowed to perform this operation</message>
|
||||||
<message id="Lms.Error.user-not-found">User not found</message>
|
<message id="Lms.Error.user-not-found">User not found</message>
|
||||||
|
|
||||||
@@ -125,6 +126,7 @@
|
|||||||
<message id="Lms.Explore.play-shuffled">Play shuffled</message>
|
<message id="Lms.Explore.play-shuffled">Play shuffled</message>
|
||||||
<message id="Lms.Explore.random">Random</message>
|
<message id="Lms.Explore.random">Random</message>
|
||||||
<message id="Lms.Explore.recently-added">Recently added</message>
|
<message id="Lms.Explore.recently-added">Recently added</message>
|
||||||
|
<message id="Lms.Explore.recently-modified">Recently modified</message>
|
||||||
<message id="Lms.Explore.recently-played">Recently played</message>
|
<message id="Lms.Explore.recently-played">Recently played</message>
|
||||||
<message id="Lms.Explore.releases">Albums</message>
|
<message id="Lms.Explore.releases">Albums</message>
|
||||||
<message id="Lms.Explore.star">Star</message>
|
<message id="Lms.Explore.star">Star</message>
|
||||||
@@ -155,6 +157,12 @@
|
|||||||
<message id="Lms.Explore.Release.copyright">Copyright</message>
|
<message id="Lms.Explore.Release.copyright">Copyright</message>
|
||||||
<message id="Lms.Explore.Release.disc">Disc {1}</message>
|
<message id="Lms.Explore.Release.disc">Disc {1}</message>
|
||||||
|
|
||||||
|
<!--Explore:TrackList-->
|
||||||
|
<message id="Lms.Explore.TrackList.track-count">
|
||||||
|
<plural case="0">{1} track</plural>
|
||||||
|
<plural case="1">{1} tracks</plural>
|
||||||
|
</message>
|
||||||
|
|
||||||
<!--Explore:TrackLists-->
|
<!--Explore:TrackLists-->
|
||||||
<message id="Lms.Explore.TrackLists.del-tracklist-confirm">Delete playlist?</message>
|
<message id="Lms.Explore.TrackLists.del-tracklist-confirm">Delete playlist?</message>
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
<message id="Lms.Error.error-occurred">Une erreur est survenue!</message>
|
<message id="Lms.Error.error-occurred">Une erreur est survenue!</message>
|
||||||
<message id="Lms.Error.go-home">Retour à l'accueil</message>
|
<message id="Lms.Error.go-home">Retour à l'accueil</message>
|
||||||
<message id="Lms.Error.release-not-found">Cet album n'existe pas</message>
|
<message id="Lms.Error.release-not-found">Cet album n'existe pas</message>
|
||||||
|
<message id="Lms.Error.tracklist-not-found">Cette liste de lecture n'existe pas</message>
|
||||||
<message id="Lms.Error.user-not-allowed">Vous n'avez pas les droits pour effectuer cette opération</message>
|
<message id="Lms.Error.user-not-allowed">Vous n'avez pas les droits pour effectuer cette opération</message>
|
||||||
<message id="Lms.Error.user-not-found">L'utilisateur n'existe pas</message>
|
<message id="Lms.Error.user-not-found">L'utilisateur n'existe pas</message>
|
||||||
|
|
||||||
@@ -125,6 +126,7 @@
|
|||||||
<message id="Lms.Explore.play-shuffled">Jouer aléatoirement</message>
|
<message id="Lms.Explore.play-shuffled">Jouer aléatoirement</message>
|
||||||
<message id="Lms.Explore.random">Aléatoire</message>
|
<message id="Lms.Explore.random">Aléatoire</message>
|
||||||
<message id="Lms.Explore.recently-added">Ajoutés récemment</message>
|
<message id="Lms.Explore.recently-added">Ajoutés récemment</message>
|
||||||
|
<message id="Lms.Explore.recently-modified">Modifiés récemment</message>
|
||||||
<message id="Lms.Explore.recently-played">Joués récemment</message>
|
<message id="Lms.Explore.recently-played">Joués récemment</message>
|
||||||
<message id="Lms.Explore.releases">Albums</message>
|
<message id="Lms.Explore.releases">Albums</message>
|
||||||
<message id="Lms.Explore.star">Ajouter aux favoris</message>
|
<message id="Lms.Explore.star">Ajouter aux favoris</message>
|
||||||
@@ -155,6 +157,12 @@
|
|||||||
<message id="Lms.Explore.Release.copyright">Copyright</message>
|
<message id="Lms.Explore.Release.copyright">Copyright</message>
|
||||||
<message id="Lms.Explore.Release.disc">Disque {1}</message>
|
<message id="Lms.Explore.Release.disc">Disque {1}</message>
|
||||||
|
|
||||||
|
<!--Explore:TrackList-->
|
||||||
|
<message id="Lms.Explore.TrackList.track-count">
|
||||||
|
<plural case="0">{1} piste</plural>
|
||||||
|
<plural case="1">{1} pistes</plural>
|
||||||
|
</message>
|
||||||
|
|
||||||
<!--Explore:TrackLists-->
|
<!--Explore:TrackLists-->
|
||||||
<message id="Lms.Explore.TrackLists.del-tracklist-confirm">Supprimer la liste de lecture ?</message>
|
<message id="Lms.Explore.TrackLists.del-tracklist-confirm">Supprimer la liste de lecture ?</message>
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
<message id="Lms.Error.error-occurred">C'è stato un errore!</message>
|
<message id="Lms.Error.error-occurred">C'è stato un errore!</message>
|
||||||
<message id="Lms.Error.go-home">Ritorna alla home</message>
|
<message id="Lms.Error.go-home">Ritorna alla home</message>
|
||||||
<message id="Lms.Error.release-not-found">Album non trovato</message>
|
<message id="Lms.Error.release-not-found">Album non trovato</message>
|
||||||
|
|
||||||
<message id="Lms.Error.user-not-allowed">Non sei autorizzato ad eseguire questa operazione</message>
|
<message id="Lms.Error.user-not-allowed">Non sei autorizzato ad eseguire questa operazione</message>
|
||||||
<message id="Lms.Error.user-not-found">Utente non trovato</message>
|
<message id="Lms.Error.user-not-found">Utente non trovato</message>
|
||||||
|
|
||||||
@@ -124,6 +125,7 @@
|
|||||||
<message id="Lms.Explore.play-shuffled">Aggiungi alla coda di riproduzione mischiando</message>
|
<message id="Lms.Explore.play-shuffled">Aggiungi alla coda di riproduzione mischiando</message>
|
||||||
<message id="Lms.Explore.random">Casuali</message>
|
<message id="Lms.Explore.random">Casuali</message>
|
||||||
<message id="Lms.Explore.recently-added">Aggiunti di recente</message>
|
<message id="Lms.Explore.recently-added">Aggiunti di recente</message>
|
||||||
|
|
||||||
<message id="Lms.Explore.recently-played">Riprodotti di recente</message>
|
<message id="Lms.Explore.recently-played">Riprodotti di recente</message>
|
||||||
<message id="Lms.Explore.releases">Album</message>
|
<message id="Lms.Explore.releases">Album</message>
|
||||||
<message id="Lms.Explore.star">Aggiungi ai preferiti</message>
|
<message id="Lms.Explore.star">Aggiungi ai preferiti</message>
|
||||||
@@ -154,8 +156,15 @@
|
|||||||
<message id="Lms.Explore.Release.copyright">Copyright</message>
|
<message id="Lms.Explore.Release.copyright">Copyright</message>
|
||||||
<message id="Lms.Explore.Release.disc">Disco {1}</message>
|
<message id="Lms.Explore.Release.disc">Disco {1}</message>
|
||||||
|
|
||||||
|
<!--Explore:TrackList-->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!--Explore:TrackLists-->
|
<!--Explore:TrackLists-->
|
||||||
|
|
||||||
|
|
||||||
<!--Explore:Search-->
|
<!--Explore:Search-->
|
||||||
<message id="Lms.Explore.Search.search-placeholder">Ricerca...</message>
|
<message id="Lms.Explore.Search.search-placeholder">Ricerca...</message>
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
<message id="Lms.Error.error-occurred">发生错误!</message>
|
<message id="Lms.Error.error-occurred">发生错误!</message>
|
||||||
<message id="Lms.Error.go-home">返回主页</message>
|
<message id="Lms.Error.go-home">返回主页</message>
|
||||||
<message id="Lms.Error.release-not-found">发布未找到</message>
|
<message id="Lms.Error.release-not-found">发布未找到</message>
|
||||||
|
|
||||||
<message id="Lms.Error.user-not-allowed">不允许执行此操作</message>
|
<message id="Lms.Error.user-not-allowed">不允许执行此操作</message>
|
||||||
<message id="Lms.Error.user-not-found">未找到此用户</message>
|
<message id="Lms.Error.user-not-found">未找到此用户</message>
|
||||||
|
|
||||||
@@ -125,6 +126,7 @@
|
|||||||
<message id="Lms.Explore.play-shuffled">随机播放</message>
|
<message id="Lms.Explore.play-shuffled">随机播放</message>
|
||||||
<message id="Lms.Explore.random">随机</message>
|
<message id="Lms.Explore.random">随机</message>
|
||||||
<message id="Lms.Explore.recently-added">最近添加</message>
|
<message id="Lms.Explore.recently-added">最近添加</message>
|
||||||
|
|
||||||
<message id="Lms.Explore.recently-played">最近播放</message>
|
<message id="Lms.Explore.recently-played">最近播放</message>
|
||||||
<message id="Lms.Explore.releases">专辑</message>
|
<message id="Lms.Explore.releases">专辑</message>
|
||||||
<message id="Lms.Explore.star">收藏</message>
|
<message id="Lms.Explore.star">收藏</message>
|
||||||
@@ -155,7 +157,13 @@
|
|||||||
<message id="Lms.Explore.Release.copyright">版权所有</message>
|
<message id="Lms.Explore.Release.copyright">版权所有</message>
|
||||||
<message id="Lms.Explore.Release.disc">唱片 {1}</message>
|
<message id="Lms.Explore.Release.disc">唱片 {1}</message>
|
||||||
|
|
||||||
<!--Explore:Search-->
|
<!--Explore:TrackList-->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!--Explore:TrackLists-->
|
||||||
|
|
||||||
<!--Explore:Search-->
|
<!--Explore:Search-->
|
||||||
<message id="Lms.Explore.Search.search-placeholder">搜索中...</message>
|
<message id="Lms.Explore.Search.search-placeholder">搜索中...</message>
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<messages xmlns:if="Wt.WTemplate.conditions">
|
||||||
|
|
||||||
|
<message id="Lms.Explore.TrackList.template">
|
||||||
|
<div class="mb-3">
|
||||||
|
<h2>${name}</h2>
|
||||||
|
<div class="small text-muted">${track-count} · ${duration}</div>
|
||||||
|
<div class="btn-group">
|
||||||
|
${play-btn class="btn btn-primary"}
|
||||||
|
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false"/>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li>${play-shuffled class="dropdown-item"}</li>
|
||||||
|
<li>${play-last class="dropdown-item"}</li>
|
||||||
|
<li>${download class="dropdown-item"}</li>
|
||||||
|
<li>${delete class="dropdown-item"}</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
${tracks}
|
||||||
|
</message>
|
||||||
|
|
||||||
|
<message id="Lms.Explore.TrackList.template.delete-tracklist">
|
||||||
|
<div class="modal fade" tabindex="-1">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">${tr:Lms.Explore.TrackLists.del-tracklist-confirm}</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
${del-btn class="btn btn-danger me-1"}${cancel-btn class="btn btn-secondary"}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</message>
|
||||||
|
|
||||||
|
</messages>
|
||||||
+12
-37
@@ -3,51 +3,26 @@
|
|||||||
<messages xmlns:if="Wt.WTemplate.conditions">
|
<messages xmlns:if="Wt.WTemplate.conditions">
|
||||||
|
|
||||||
<message id="Lms.Explore.TrackLists.template">
|
<message id="Lms.Explore.TrackLists.template">
|
||||||
|
<div class="mb-3">
|
||||||
|
<ul class="nav nav-pills">
|
||||||
|
<li class="nav-item">
|
||||||
|
${recently-modified class="nav-link"}
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
${all class="nav-link"}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
${tracklists}
|
${tracklists}
|
||||||
</message>
|
</message>
|
||||||
|
|
||||||
<message id="Lms.Explore.TrackLists.template.entry">
|
<message id="Lms.Explore.TrackLists.template.entry">
|
||||||
<div class="d-flex align-items-center rounded Lms-bg-dark-hover mb-2">
|
${name class="text-decoration-none link-secondary"}
|
||||||
<div class="p-2 flex-fill text-truncate">
|
|
||||||
<div class="d-block text-truncate">${name}</div>
|
|
||||||
</div>
|
|
||||||
<div class="p-2 d-none d-sm-block text-muted">
|
|
||||||
${duration}
|
|
||||||
</div>
|
|
||||||
<div class="p-2 d-flex">
|
|
||||||
${play-btn class="btn btn-sm btn-outline-secondary border-0"}
|
|
||||||
<div class="dropdown d-inline-block">
|
|
||||||
${more-btn data-bs-toggle="dropdown" aria-expanded="false" class="btn btn-sm btn-outline-secondary border-0"}
|
|
||||||
<ul class="dropdown-menu" aria-labelledby="${id:more-btn}">
|
|
||||||
<li>${play-shuffled class="dropdown-item"}</li>
|
|
||||||
<li>${play-last class="dropdown-item"}</li>
|
|
||||||
<li>${download class="dropdown-item"}</li>
|
|
||||||
<li>${delete class="dropdown-item"}</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</message>
|
</message>
|
||||||
|
|
||||||
<message id="Lms.Explore.TrackLists.template.container">
|
<message id="Lms.Explore.TrackLists.template.container">
|
||||||
${elements}
|
${elements class="d-grid gap-3"}
|
||||||
${loading-indicator}
|
${loading-indicator}
|
||||||
</message>
|
</message>
|
||||||
|
|
||||||
<message id="Lms.Explore.TrackLists.template.delete-tracklist">
|
|
||||||
<div class="modal fade" tabindex="-1">
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h5 class="modal-title">${tr:Lms.Explore.TrackLists.del-tracklist-confirm}</h5>
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
${del-btn class="btn btn-danger me-1"}${cancel-btn class="btn btn-secondary"}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</message>
|
|
||||||
|
|
||||||
</messages>
|
</messages>
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ namespace Auth
|
|||||||
|
|
||||||
LMS_LOG(AUTH, DEBUG) << "Creating user '" << loginName << "', admin = " << (type == UserType::ADMIN);
|
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);
|
user.modify()->setType(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ namespace Auth
|
|||||||
if (!user)
|
if (!user)
|
||||||
throw Exception {"User deleted"};
|
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();
|
LMS_LOG(UI, DEBUG) << "Created auth token for user '" << user->getLoginName() << "', expiry = " << expiry.toString();
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
std::size_t
|
||||||
Artist::getCount(Session& session)
|
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;
|
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
|
static
|
||||||
Wt::Dbo::Query<ArtistId>
|
Wt::Dbo::Query<ArtistId>
|
||||||
createQuery(Session& session, const Artist::FindParameters& params)
|
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)
|
for (std::string_view keyword : params.keywords)
|
||||||
{
|
{
|
||||||
clauses.push_back("a.name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'");
|
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)
|
for (std::string_view keyword : params.keywords)
|
||||||
{
|
{
|
||||||
sortClauses.push_back("a.sort_name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'");
|
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 ") + ")");
|
query.where("(" + StringUtils::joinStrings(clauses, " AND ") + ") OR (" + StringUtils::joinStrings(sortClauses, " AND ") + ")");
|
||||||
@@ -193,7 +188,7 @@ Artist::findAllOrphans(Session& session, Range range)
|
|||||||
session.checkSharedLocked();
|
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)")};
|
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>
|
RangeResults<ArtistId>
|
||||||
@@ -202,7 +197,7 @@ Artist::find(Session& session, const FindParameters& params)
|
|||||||
session.checkSharedLocked();
|
session.checkSharedLocked();
|
||||||
|
|
||||||
auto query {createQuery(session, params)};
|
auto query {createQuery(session, params)};
|
||||||
return execQuery(query, params.range);
|
return Utils::execQuery(query, params.range);
|
||||||
}
|
}
|
||||||
|
|
||||||
RangeResults<ArtistId>
|
RangeResults<ArtistId>
|
||||||
@@ -246,10 +241,11 @@ Artist::findSimilarArtists(EnumSet<TrackArtistLinkType> artistLinkTypes, Range r
|
|||||||
.bind(getId())
|
.bind(getId())
|
||||||
.groupBy("a.id")
|
.groupBy("a.id")
|
||||||
.orderBy("COUNT(*) DESC, RANDOM()")};
|
.orderBy("COUNT(*) DESC, RANDOM()")};
|
||||||
|
|
||||||
for (TrackArtistLinkType type : artistLinkTypes)
|
for (TrackArtistLinkType type : artistLinkTypes)
|
||||||
query.bind(type);
|
query.bind(type);
|
||||||
|
|
||||||
return execQuery(query, range);
|
return Utils::execQuery(query, range);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::vector<Cluster::pointer>>
|
std::vector<std::vector<Cluster::pointer>>
|
||||||
|
|||||||
@@ -38,12 +38,7 @@ namespace Database
|
|||||||
AuthToken::pointer
|
AuthToken::pointer
|
||||||
AuthToken::create(Session& session, std::string_view value, const Wt::WDateTime& expiry, ObjectPtr<User> user)
|
AuthToken::create(Session& session, std::string_view value, const Wt::WDateTime& expiry, ObjectPtr<User> user)
|
||||||
{
|
{
|
||||||
session.checkUniqueLocked();
|
return session.getDboSession().add(std::unique_ptr<AuthToken> {new AuthToken {value, expiry, user}});
|
||||||
|
|
||||||
AuthToken::pointer res {session.getDboSession().add(std::make_unique<AuthToken>(value, expiry, user))};
|
|
||||||
session.getDboSession().flush();
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
@@ -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
|
std::size_t
|
||||||
Cluster::getCount(Session& session)
|
Cluster::getCount(Session& session)
|
||||||
{
|
{
|
||||||
@@ -44,24 +50,13 @@ Cluster::getCount(Session& session)
|
|||||||
return session.getDboSession().query<int>("SELECT COUNT(*) FROM cluster");
|
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>
|
RangeResults<ClusterId>
|
||||||
Cluster::find(Session& session, Range range)
|
Cluster::find(Session& session, Range range)
|
||||||
{
|
{
|
||||||
session.checkSharedLocked();
|
session.checkSharedLocked();
|
||||||
auto query {session.getDboSession().query<ClusterId>("SELECT id FROM cluster")};
|
auto query {session.getDboSession().query<ClusterId>("SELECT id FROM cluster")};
|
||||||
|
|
||||||
return execQuery(query, range);
|
return Utils::execQuery(query, range);
|
||||||
}
|
}
|
||||||
|
|
||||||
RangeResults<ClusterId>
|
RangeResults<ClusterId>
|
||||||
@@ -70,7 +65,7 @@ Cluster::findOrphans(Session& session, Range range)
|
|||||||
session.checkSharedLocked();
|
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)")};
|
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
|
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")
|
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())};
|
.where("c.id = ?").bind(getId())};
|
||||||
|
|
||||||
return execQuery(query, range);
|
return Utils::execQuery(query, range);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t
|
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
|
std::size_t
|
||||||
ClusterType::getCount(Session& session)
|
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")
|
" LEFT OUTER JOIN cluster c ON c_t.id = c.cluster_type_id")
|
||||||
.where("c.id IS NULL")};
|
.where("c.id IS NULL")};
|
||||||
|
|
||||||
return execQuery(query, range);
|
return Utils::execQuery(query, range);
|
||||||
}
|
}
|
||||||
|
|
||||||
RangeResults<ClusterTypeId>
|
RangeResults<ClusterTypeId>
|
||||||
@@ -144,7 +145,7 @@ ClusterType::findUsed(Session& session, Range range)
|
|||||||
"SELECT DISTINCT c_t.id from cluster_type c_t")
|
"SELECT DISTINCT c_t.id from cluster_type c_t")
|
||||||
.join("cluster c ON c_t.id = c.cluster_type_id")};
|
.join("cluster c ON c_t.id = c.cluster_type_id")};
|
||||||
|
|
||||||
return execQuery(query, range);
|
return Utils::execQuery(query, range);
|
||||||
}
|
}
|
||||||
|
|
||||||
ClusterType::pointer
|
ClusterType::pointer
|
||||||
@@ -170,18 +171,7 @@ ClusterType::find(Session& session, Range range)
|
|||||||
|
|
||||||
auto query {session.getDboSession().query<ClusterTypeId>("SELECT id from cluster_type")};
|
auto query {session.getDboSession().query<ClusterTypeId>("SELECT id from cluster_type")};
|
||||||
|
|
||||||
return execQuery(query, range);
|
return Utils::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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Cluster::pointer
|
Cluster::pointer
|
||||||
|
|||||||
@@ -141,6 +141,12 @@ namespace Database
|
|||||||
, _track {getDboPtr(track)}
|
, _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
|
std::size_t
|
||||||
Listen::getCount(Session& session)
|
Listen::getCount(Session& session)
|
||||||
{
|
{
|
||||||
@@ -172,7 +178,7 @@ namespace Database
|
|||||||
if (parameters.scrobblingState)
|
if (parameters.scrobblingState)
|
||||||
query.where("scrobbling_state = ?").bind(*parameters.scrobblingState);
|
query.where("scrobbling_state = ?").bind(*parameters.scrobblingState);
|
||||||
|
|
||||||
return execQuery(query, parameters.range);
|
return Utils::execQuery(query, parameters.range);
|
||||||
}
|
}
|
||||||
|
|
||||||
Listen::pointer
|
Listen::pointer
|
||||||
@@ -188,17 +194,6 @@ namespace Database
|
|||||||
.resultValue();
|
.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>
|
RangeResults<ArtistId>
|
||||||
Listen::getTopArtists(Session& session,
|
Listen::getTopArtists(Session& session,
|
||||||
UserId userId,
|
UserId userId,
|
||||||
@@ -213,7 +208,7 @@ namespace Database
|
|||||||
.orderBy("COUNT(a.id) DESC")
|
.orderBy("COUNT(a.id) DESC")
|
||||||
.groupBy("a.id")};
|
.groupBy("a.id")};
|
||||||
|
|
||||||
return execQuery(query, range);
|
return Utils::execQuery(query, range);
|
||||||
}
|
}
|
||||||
|
|
||||||
RangeResults<ReleaseId>
|
RangeResults<ReleaseId>
|
||||||
@@ -227,7 +222,7 @@ namespace Database
|
|||||||
.orderBy("COUNT(r.id) DESC")
|
.orderBy("COUNT(r.id) DESC")
|
||||||
.groupBy("r.id")};
|
.groupBy("r.id")};
|
||||||
|
|
||||||
return execQuery(query, range);
|
return Utils::execQuery(query, range);
|
||||||
}
|
}
|
||||||
|
|
||||||
RangeResults<TrackId>
|
RangeResults<TrackId>
|
||||||
@@ -241,7 +236,7 @@ namespace Database
|
|||||||
.orderBy("COUNT(t.id) DESC")
|
.orderBy("COUNT(t.id) DESC")
|
||||||
.groupBy("t.id")};
|
.groupBy("t.id")};
|
||||||
|
|
||||||
return execQuery(query, range);
|
return Utils::execQuery(query, range);
|
||||||
}
|
}
|
||||||
|
|
||||||
RangeResults<ArtistId>
|
RangeResults<ArtistId>
|
||||||
@@ -256,7 +251,7 @@ namespace Database
|
|||||||
.groupBy("a.id").having("l.date_time = MAX(l.date_time)")
|
.groupBy("a.id").having("l.date_time = MAX(l.date_time)")
|
||||||
.orderBy("l.date_time DESC")};
|
.orderBy("l.date_time DESC")};
|
||||||
|
|
||||||
return execQuery(query, range);
|
return Utils::execQuery(query, range);
|
||||||
}
|
}
|
||||||
|
|
||||||
RangeResults<ReleaseId>
|
RangeResults<ReleaseId>
|
||||||
@@ -270,7 +265,7 @@ namespace Database
|
|||||||
.groupBy("r.id").having("l.date_time = MAX(l.date_time)")
|
.groupBy("r.id").having("l.date_time = MAX(l.date_time)")
|
||||||
.orderBy("l.date_time DESC")};
|
.orderBy("l.date_time DESC")};
|
||||||
|
|
||||||
return execQuery(query, range);
|
return Utils::execQuery(query, range);
|
||||||
}
|
}
|
||||||
|
|
||||||
RangeResults<TrackId>
|
RangeResults<TrackId>
|
||||||
@@ -284,7 +279,7 @@ namespace Database
|
|||||||
.groupBy("t.id").having("l.date_time = MAX(l.date_time)")
|
.groupBy("t.id").having("l.date_time = MAX(l.date_time)")
|
||||||
.orderBy("l.date_time DESC")};
|
.orderBy("l.date_time DESC")};
|
||||||
|
|
||||||
return execQuery(query, range);
|
return Utils::execQuery(query, range);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -576,12 +576,21 @@ CREATE TABLE IF NOT EXISTS "track_artist_link_backup" (
|
|||||||
migrateFromV34(Session& session)
|
migrateFromV34(Session& session)
|
||||||
{
|
{
|
||||||
// Add scrobbling state
|
// 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_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_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)) + ")");
|
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
|
void
|
||||||
doDbMigration(Session& session)
|
doDbMigration(Session& session)
|
||||||
{
|
{
|
||||||
@@ -623,6 +632,7 @@ CREATE TABLE IF NOT EXISTS "track_artist_link_backup" (
|
|||||||
{32, migrateFromV32},
|
{32, migrateFromV32},
|
||||||
{33, migrateFromV33},
|
{33, migrateFromV33},
|
||||||
{34, migrateFromV34},
|
{34, migrateFromV34},
|
||||||
|
{35, migrateFromV35},
|
||||||
};
|
};
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ namespace Database
|
|||||||
class Session;
|
class Session;
|
||||||
|
|
||||||
using Version = std::size_t;
|
using Version = std::size_t;
|
||||||
static constexpr Version LMS_DATABASE_VERSION {35};
|
static constexpr Version LMS_DATABASE_VERSION {36};
|
||||||
class VersionInfo
|
class VersionInfo
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ createQuery(Session& session, const Release::FindParameters& params)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (std::string_view keyword : params.keywords)
|
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())
|
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>
|
std::vector<Release::pointer>
|
||||||
Release::find(Session& session, const std::string& name)
|
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;
|
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
|
std::size_t
|
||||||
Release::getCount(Session& session)
|
Release::getCount(Session& session)
|
||||||
{
|
{
|
||||||
@@ -238,7 +233,7 @@ Release::findOrderedByArtist(Session& session, Range range)
|
|||||||
{
|
{
|
||||||
session.checkSharedLocked();
|
session.checkSharedLocked();
|
||||||
|
|
||||||
// TODO merge with execQuery
|
// TODO merge with find
|
||||||
auto query {session.getDboSession().query<ReleaseId>(
|
auto query {session.getDboSession().query<ReleaseId>(
|
||||||
"SELECT DISTINCT r.id FROM release r"
|
"SELECT DISTINCT r.id FROM release r"
|
||||||
" INNER JOIN track t ON r.id = t.release_id"
|
" 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")
|
" INNER JOIN artist a ON t_a_l.artist_id = a.id")
|
||||||
.orderBy("a.name COLLATE NOCASE, r.name COLLATE NOCASE")};
|
.orderBy("a.name COLLATE NOCASE, r.name COLLATE NOCASE")};
|
||||||
|
|
||||||
return execQuery(query, range);
|
return Utils::execQuery(query, range);
|
||||||
}
|
}
|
||||||
|
|
||||||
RangeResults<ReleaseId>
|
RangeResults<ReleaseId>
|
||||||
@@ -255,7 +250,7 @@ Release::findOrphans(Session& session, Range range)
|
|||||||
session.checkSharedLocked();
|
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")};
|
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>
|
RangeResults<ReleaseId>
|
||||||
@@ -265,7 +260,7 @@ Release::find(Session& session, const FindParameters& params)
|
|||||||
|
|
||||||
auto query {createQuery(session, params)};
|
auto query {createQuery(session, params)};
|
||||||
|
|
||||||
return execQuery(query, params.range);
|
return Utils::execQuery(query, params.range);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::size_t>
|
std::optional<std::size_t>
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ ScanSettings::setClusterTypes(Session& session, const std::set<std::string>& clu
|
|||||||
if (!clusterType)
|
if (!clusterType)
|
||||||
{
|
{
|
||||||
LMS_LOG(DB, INFO) << "Creating cluster type " << clusterTypeName;
|
LMS_LOG(DB, INFO) << "Creating cluster type " << clusterTypeName;
|
||||||
clusterType = ClusterType::create(session, clusterTypeName);
|
clusterType = session.create<ClusterType>(clusterTypeName);
|
||||||
_clusterTypes.insert(getDboPtr(clusterType));
|
_clusterTypes.insert(getDboPtr(clusterType));
|
||||||
|
|
||||||
needRescan = true;
|
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
|
std::size_t
|
||||||
StarredArtist::getCount(Session& session)
|
StarredArtist::getCount(Session& session)
|
||||||
{
|
{
|
||||||
@@ -61,20 +67,9 @@ namespace Database
|
|||||||
.resultValue();
|
.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
|
void
|
||||||
StarredArtist::setDateTime(const Wt::WDateTime& dateTime)
|
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
|
std::size_t
|
||||||
StarredRelease::getCount(Session& session)
|
StarredRelease::getCount(Session& session)
|
||||||
{
|
{
|
||||||
@@ -61,20 +67,9 @@ namespace Database
|
|||||||
.resultValue();
|
.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
|
void
|
||||||
StarredRelease::setDateTime(const Wt::WDateTime& dateTime)
|
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
|
std::size_t
|
||||||
StarredTrack::getCount(Session& session)
|
StarredTrack::getCount(Session& session)
|
||||||
{
|
{
|
||||||
@@ -75,23 +81,12 @@ namespace Database
|
|||||||
if (params.user.isValid())
|
if (params.user.isValid())
|
||||||
query.where("s_t.user_id = ?").bind(params.user);
|
query.where("s_t.user_id = ?").bind(params.user);
|
||||||
|
|
||||||
return execQuery(query, params.range);
|
return Utils::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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
StarredTrack::setDateTime(const Wt::WDateTime& dateTime)
|
StarredTrack::setDateTime(const Wt::WDateTime& dateTime)
|
||||||
{
|
{
|
||||||
_dateTime = normalizeDateTime(dateTime);
|
_dateTime = Utils::normalizeDateTime(dateTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ createQuery(Session& session, const Track::FindParameters& params)
|
|||||||
auto query {session.getDboSession().query<TrackId>("SELECT DISTINCT t.id from track t")};
|
auto query {session.getDboSession().query<TrackId>("SELECT DISTINCT t.id from track t")};
|
||||||
|
|
||||||
for (std::string_view keyword : params.keywords)
|
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())
|
if (params.writtenAfter.isValid())
|
||||||
query.where("t.file_last_write > ?").bind(params.writtenAfter);
|
query.where("t.file_last_write > ?").bind(params.writtenAfter);
|
||||||
@@ -156,12 +156,7 @@ Track::Track(const std::filesystem::path& p)
|
|||||||
Track::pointer
|
Track::pointer
|
||||||
Track::create(Session& session, const std::filesystem::path& p)
|
Track::create(Session& session, const std::filesystem::path& p)
|
||||||
{
|
{
|
||||||
session.checkUniqueLocked();
|
return session.getDboSession().add(std::unique_ptr<Track> {new Track {p}});
|
||||||
|
|
||||||
Track::pointer res {session.getDboSession().add(std::make_unique<Track>(p))};
|
|
||||||
session.getDboSession().flush();
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t
|
std::size_t
|
||||||
@@ -219,7 +214,7 @@ Track::findPaths(Session& session, Range range)
|
|||||||
// TODO Dbo traits on filesystem
|
// TODO Dbo traits on filesystem
|
||||||
auto query {session.getDboSession().query<QueryResultType>("SELECT id, file_path FROM track")};
|
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;
|
RangeResults<PathResult> res;
|
||||||
res.range = queryResults.range;
|
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)")
|
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")};
|
.orderBy("track.release_id,track.disc_number,track.track_number,track.recording_mbid")};
|
||||||
|
|
||||||
return execQuery(query, range);
|
return Utils::execQuery(query, range);
|
||||||
}
|
}
|
||||||
|
|
||||||
RangeResults<TrackId>
|
RangeResults<TrackId>
|
||||||
@@ -255,7 +250,7 @@ Track::findWithRecordingMBIDAndMissingFeatures(Session& session, Range range)
|
|||||||
.where("LENGTH(t.recording_mbid) > 0")
|
.where("LENGTH(t.recording_mbid) > 0")
|
||||||
.where("NOT EXISTS (SELECT * FROM track_features t_f WHERE t_f.track_id = t.id)")};
|
.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>
|
std::vector<Cluster::pointer>
|
||||||
@@ -284,7 +279,7 @@ Track::find(Session& session, const FindParameters& parameters)
|
|||||||
|
|
||||||
auto query {createQuery(session, parameters)};
|
auto query {createQuery(session, parameters)};
|
||||||
|
|
||||||
return execQuery(query, parameters.range);
|
return Utils::execQuery(query, parameters.range);
|
||||||
}
|
}
|
||||||
|
|
||||||
RangeResults<TrackId>
|
RangeResults<TrackId>
|
||||||
@@ -297,7 +292,7 @@ Track::findByNameAndReleaseName(Session& session, std::string_view trackName, st
|
|||||||
.where("t.name = ?").bind(trackName)
|
.where("t.name = ?").bind(trackName)
|
||||||
.where("r.name = ?").bind(releaseName)};
|
.where("r.name = ?").bind(releaseName)};
|
||||||
|
|
||||||
return execQuery(query, Range {});
|
return Utils::execQuery(query, Range {});
|
||||||
}
|
}
|
||||||
|
|
||||||
RangeResults<TrackId>
|
RangeResults<TrackId>
|
||||||
@@ -328,7 +323,7 @@ Track::findSimilarTracks(Session& session, const std::vector<TrackId>& tracks, R
|
|||||||
for (TrackId trackId : tracks)
|
for (TrackId trackId : tracks)
|
||||||
query.bind(trackId);
|
query.bind(trackId);
|
||||||
|
|
||||||
return execQuery(query, range);
|
return Utils::execQuery(query, range);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
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
|
std::size_t
|
||||||
TrackBookmark::getCount(Session& session)
|
TrackBookmark::getCount(Session& session)
|
||||||
{
|
{
|
||||||
@@ -41,18 +47,6 @@ TrackBookmark::getCount(Session& session)
|
|||||||
return session.getDboSession().query<int>("SELECT COUNT(*) FROM track_bookmark");
|
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>
|
RangeResults<TrackBookmarkId>
|
||||||
TrackBookmark::find(Session& session, UserId userId, Range range)
|
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")
|
auto query {session.getDboSession().query<TrackBookmarkId>("SELECT id from track_bookmark")
|
||||||
.where("user_id = ?").bind(userId)};
|
.where("user_id = ?").bind(userId)};
|
||||||
|
|
||||||
return execQuery(query, range);
|
return Utils::execQuery(query, range);
|
||||||
}
|
}
|
||||||
|
|
||||||
TrackBookmark::pointer
|
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
|
std::size_t
|
||||||
TrackFeatures::getCount(Session& session)
|
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")};
|
auto query {session.getDboSession().query<TrackFeaturesId>("SELECT id from track_features")};
|
||||||
|
|
||||||
return execQuery(query, range);
|
return Utils::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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FeatureValues
|
FeatureValues
|
||||||
|
|||||||
@@ -36,23 +36,20 @@
|
|||||||
namespace Database {
|
namespace Database {
|
||||||
|
|
||||||
TrackList::TrackList(std::string_view name, TrackListType type, bool isPublic, ObjectPtr<User> user)
|
TrackList::TrackList(std::string_view name, TrackListType type, bool isPublic, ObjectPtr<User> user)
|
||||||
: _name {name},
|
: _name {name}
|
||||||
_type {type},
|
, _type {type}
|
||||||
_isPublic {isPublic},
|
, _isPublic {isPublic}
|
||||||
_user {getDboPtr(user)}
|
, _creationDateTime {Wt::WDateTime::currentDateTime()}
|
||||||
|
, _lastModifiedDateTime {Wt::WDateTime::currentDateTime()}
|
||||||
|
, _user {getDboPtr(user)}
|
||||||
{
|
{
|
||||||
|
assert(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
TrackList::pointer
|
TrackList::pointer
|
||||||
TrackList::create(Session& session, std::string_view name, TrackListType type, bool isPublic, ObjectPtr<User> user)
|
TrackList::create(Session& session, std::string_view name, TrackListType type, bool isPublic, ObjectPtr<User> user)
|
||||||
{
|
{
|
||||||
session.checkUniqueLocked();
|
return session.getDboSession().add(std::unique_ptr<TrackList> {new TrackList {name, type, isPublic, user}});
|
||||||
assert(user);
|
|
||||||
|
|
||||||
TrackList::pointer res {session.getDboSession().add( std::make_unique<TrackList>(name, type, isPublic, user) )};
|
|
||||||
session.getDboSession().flush();
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t
|
std::size_t
|
||||||
@@ -112,9 +109,19 @@ TrackList::find(Session& session, const FindParameters& params)
|
|||||||
query.where(oss.str());
|
query.where(oss.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (params.sortMethod)
|
||||||
|
{
|
||||||
|
case TrackListSortMethod::None:
|
||||||
|
break;
|
||||||
|
case TrackListSortMethod::Name:
|
||||||
query.orderBy("t_l.name COLLATE NOCASE");
|
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
|
TrackList::pointer
|
||||||
@@ -173,7 +180,7 @@ TrackList::getEntryByTrackAndDateTime(ObjectPtr<Track> track, const Wt::WDateTim
|
|||||||
return session()->find<TrackListEntry>()
|
return session()->find<TrackListEntry>()
|
||||||
.where("tracklist_id = ?").bind(getId())
|
.where("tracklist_id = ?").bind(getId())
|
||||||
.where("track_id = ?").bind(track->getId())
|
.where("track_id = ?").bind(track->getId())
|
||||||
.where("date_time = ?").bind(normalizeDateTime(dateTime))
|
.where("date_time = ?").bind(Utils::normalizeDateTime(dateTime))
|
||||||
.resultValue();
|
.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)
|
TrackListEntry::TrackListEntry(ObjectPtr<Track> track, ObjectPtr<TrackList> tracklist, const Wt::WDateTime& dateTime)
|
||||||
: _dateTime {normalizeDateTime(dateTime)}
|
: _dateTime {Utils::normalizeDateTime(dateTime)}
|
||||||
, _track {getDboPtr(track)}
|
, _track {getDboPtr(track)}
|
||||||
, _tracklist {getDboPtr(tracklist)}
|
, _tracklist {getDboPtr(tracklist)}
|
||||||
{
|
{
|
||||||
|
assert(track);
|
||||||
|
assert(tracklist);
|
||||||
}
|
}
|
||||||
|
|
||||||
TrackListEntry::pointer
|
TrackListEntry::pointer
|
||||||
TrackListEntry::create(Session& session, ObjectPtr<Track> track, ObjectPtr<TrackList> tracklist, const Wt::WDateTime& dateTime)
|
TrackListEntry::create(Session& session, ObjectPtr<Track> track, ObjectPtr<TrackList> tracklist, const Wt::WDateTime& dateTime)
|
||||||
{
|
{
|
||||||
session.checkUniqueLocked();
|
return session.getDboSession().add(std::unique_ptr<TrackListEntry>( new TrackListEntry {track, tracklist, dateTime}));
|
||||||
assert(track);
|
}
|
||||||
assert(tracklist);
|
|
||||||
|
|
||||||
auto res = session.getDboSession().add(std::make_unique<TrackListEntry>( track, tracklist, dateTime));
|
void
|
||||||
session.getDboSession().flush();
|
TrackListEntry::onPostCreated()
|
||||||
|
{
|
||||||
|
_tracklist.modify()->setLastModifiedDateTime(Wt::WDateTime::currentDateTime());
|
||||||
|
}
|
||||||
|
|
||||||
return res;
|
void
|
||||||
|
TrackListEntry::onPreRemove()
|
||||||
|
{
|
||||||
|
_tracklist.modify()->setLastModifiedDateTime(Wt::WDateTime::currentDateTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
TrackListEntry::pointer
|
TrackListEntry::pointer
|
||||||
|
|||||||
@@ -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
|
std::size_t
|
||||||
User::getCount(Session& session)
|
User::getCount(Session& session)
|
||||||
{
|
{
|
||||||
@@ -53,7 +59,7 @@ User::find(Session& session, const FindParameters& params)
|
|||||||
if (params.scrobbler)
|
if (params.scrobbler)
|
||||||
query.where("scrobbler = ?").bind(*params.scrobbler);
|
query.where("scrobbler = ?").bind(*params.scrobbler);
|
||||||
|
|
||||||
return execQuery(query, params.range);
|
return Utils::execQuery(query, params.range);
|
||||||
}
|
}
|
||||||
|
|
||||||
User::pointer
|
User::pointer
|
||||||
@@ -64,17 +70,6 @@ User::findDemoUser(Session& session)
|
|||||||
return session.getDboSession().find<User>().where("type = ?").bind(UserType::DEMO).resultValue();
|
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::pointer
|
||||||
User::find(Session& session, UserId id)
|
User::find(Session& session, UserId id)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
#include "utils/String.hpp"
|
#include "utils/String.hpp"
|
||||||
|
|
||||||
namespace Database
|
namespace Database::Utils
|
||||||
{
|
{
|
||||||
std::string
|
std::string
|
||||||
escapeLikeKeyword(std::string_view keyword)
|
escapeLikeKeyword(std::string_view keyword)
|
||||||
@@ -35,6 +35,5 @@ namespace Database
|
|||||||
// force second resolution
|
// force second resolution
|
||||||
return Wt::WDateTime::fromTime_t(dateTime.toTime_t());
|
return Wt::WDateTime::fromTime_t(dateTime.toTime_t());
|
||||||
}
|
}
|
||||||
|
} // namespace Database::Utils
|
||||||
} // namespace Database
|
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
#include "services/database/Types.hpp"
|
#include "services/database/Types.hpp"
|
||||||
|
|
||||||
namespace Database
|
namespace Database::Utils
|
||||||
{
|
{
|
||||||
#define ESCAPE_CHAR_STR "\\"
|
#define ESCAPE_CHAR_STR "\\"
|
||||||
static inline constexpr char escapeChar {'\\'};
|
static inline constexpr char escapeChar {'\\'};
|
||||||
@@ -82,5 +82,5 @@ namespace Database
|
|||||||
}
|
}
|
||||||
|
|
||||||
Wt::WDateTime normalizeDateTime(const Wt::WDateTime& dateTime);
|
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() = default;
|
||||||
Artist(const std::string& name, const std::optional<UUID>& MBID = {});
|
|
||||||
|
|
||||||
// Accessors
|
// Accessors
|
||||||
static std::size_t getCount(Session& session);
|
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 setMBID(const std::optional<UUID>& mbid) { _MBID = mbid ? mbid->getAsString() : ""; }
|
||||||
void setSortName(const std::string& sortName);
|
void setSortName(const std::string& sortName);
|
||||||
|
|
||||||
// Create
|
|
||||||
static pointer create(Session& session, const std::string& name, const std::optional<UUID>& UUID = {});
|
|
||||||
|
|
||||||
template<class Action>
|
template<class Action>
|
||||||
void persist(Action& a)
|
void persist(Action& a)
|
||||||
{
|
{
|
||||||
@@ -118,6 +114,11 @@ class Artist : public Object<Artist, ArtistId>
|
|||||||
private:
|
private:
|
||||||
static const std::size_t _maxNameLength = 128;
|
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 _name;
|
||||||
std::string _sortName;
|
std::string _sortName;
|
||||||
std::string _MBID; // Musicbrainz Identifier
|
std::string _MBID; // Musicbrainz Identifier
|
||||||
|
|||||||
@@ -36,10 +36,8 @@ namespace Database
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AuthToken() = default;
|
AuthToken() = default;
|
||||||
AuthToken(std::string_view value, const Wt::WDateTime& expiry, ObjectPtr<User> user);
|
|
||||||
|
|
||||||
// Utility
|
// 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 void removeExpiredTokens(Session& session, const Wt::WDateTime& now);
|
||||||
static pointer find(Session& session, std::string_view value);
|
static pointer find(Session& session, std::string_view value);
|
||||||
|
|
||||||
@@ -57,6 +55,10 @@ namespace Database
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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;
|
std::string _value;
|
||||||
Wt::WDateTime _expiry;
|
Wt::WDateTime _expiry;
|
||||||
Wt::Dbo::ptr<User> _user;
|
Wt::Dbo::ptr<User> _user;
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ class Cluster : public Object<Cluster, ClusterId>
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Cluster() = default;
|
Cluster() = default;
|
||||||
Cluster(ObjectPtr<ClusterType> type, std::string_view name);
|
|
||||||
|
|
||||||
// Find utility
|
// Find utility
|
||||||
static std::size_t getCount(Session& session);
|
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 pointer find(Session& session, ClusterId id);
|
||||||
static RangeResults<ClusterId> findOrphans(Session& session, Range range);
|
static RangeResults<ClusterId> findOrphans(Session& session, Range range);
|
||||||
|
|
||||||
// Create utility
|
|
||||||
static pointer create(Session& session, ObjectPtr<ClusterType> type, std::string_view name);
|
|
||||||
|
|
||||||
// Accessors
|
// Accessors
|
||||||
const std::string& getName() const { return _name; }
|
const std::string& getName() const { return _name; }
|
||||||
ObjectPtr<ClusterType> getType() const { return _clusterType; }
|
ObjectPtr<ClusterType> getType() const { return _clusterType; }
|
||||||
@@ -72,6 +68,10 @@ class Cluster : public Object<Cluster, ClusterId>
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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;
|
static const std::size_t _maxNameLength = 128;
|
||||||
|
|
||||||
std::string _name;
|
std::string _name;
|
||||||
@@ -85,7 +85,6 @@ class ClusterType : public Object<ClusterType, ClusterTypeId>
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ClusterType() = default;
|
ClusterType() = default;
|
||||||
ClusterType(std::string_view name);
|
|
||||||
|
|
||||||
// Getters
|
// Getters
|
||||||
static std::size_t getCount(Session& session);
|
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> findOrphans(Session& session, Range range);
|
||||||
static RangeResults<ClusterTypeId> findUsed(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);
|
static void remove(Session& session, const std::string& name);
|
||||||
|
|
||||||
// Accessors
|
// Accessors
|
||||||
@@ -112,6 +110,10 @@ class ClusterType : public Object<ClusterType, ClusterTypeId>
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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;
|
static const std::size_t _maxNameLength = 128;
|
||||||
|
|
||||||
std::string _name;
|
std::string _name;
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ class Listen : public Object<Listen, ListenId>
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Listen() = default;
|
Listen() = default;
|
||||||
Listen(ObjectPtr<User> user, ObjectPtr<Track> track, Scrobbler scrobbler, const Wt::WDateTime& dateTime);
|
|
||||||
|
|
||||||
struct FindParameters
|
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 pointer find(Session& session, UserId userId, TrackId trackId, Scrobbler scrobbler, const Wt::WDateTime& dateTime);
|
||||||
static RangeResults<ListenId> find(Session& session, const FindParameters& parameters);
|
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
|
// Stats
|
||||||
static RangeResults<ArtistId> getTopArtists(Session& session,
|
static RangeResults<ArtistId> getTopArtists(Session& session,
|
||||||
UserId userId,
|
UserId userId,
|
||||||
@@ -120,6 +116,10 @@ class Listen : public Object<Listen, ListenId>
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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;
|
Wt::WDateTime _dateTime;
|
||||||
Scrobbler _scrobbler;
|
Scrobbler _scrobbler;
|
||||||
ScrobblingState _scrobblingState {ScrobblingState::PendingAdd};
|
ScrobblingState _scrobblingState {ScrobblingState::PendingAdd};
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <Wt/WSignal.h>
|
||||||
#include <Wt/Dbo/ptr.h>
|
#include <Wt/Dbo/ptr.h>
|
||||||
#include "services/database/IdType.hpp"
|
#include "services/database/IdType.hpp"
|
||||||
|
|
||||||
@@ -38,7 +39,11 @@ namespace Database
|
|||||||
bool operator!=(const ObjectPtr& other) const { return other._obj != _obj; }
|
bool operator!=(const ObjectPtr& other) const { return other._obj != _obj; }
|
||||||
|
|
||||||
auto modify() { return _obj.modify(); }
|
auto modify() { return _obj.modify(); }
|
||||||
void remove() { _obj.remove(); }
|
void remove()
|
||||||
|
{
|
||||||
|
_obj.modify()->onPreRemove();
|
||||||
|
_obj.remove();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template <typename, typename> friend class Object;
|
template <typename, typename> friend class Object;
|
||||||
@@ -61,6 +66,10 @@ namespace Database
|
|||||||
typename Wt::Dbo::dbo_traits<T>::IdType id() const = delete;
|
typename Wt::Dbo::dbo_traits<T>::IdType id() const = delete;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
template <typename> friend class ObjectPtr;
|
||||||
|
virtual void onPostCreated() {}
|
||||||
|
virtual void onPreRemove() {}
|
||||||
|
|
||||||
// Can get raw dbo ptr only from Objects
|
// Can get raw dbo ptr only from Objects
|
||||||
template <typename SomeObject>
|
template <typename SomeObject>
|
||||||
static
|
static
|
||||||
|
|||||||
@@ -79,7 +79,6 @@ class Release : public Object<Release, ReleaseId>
|
|||||||
};
|
};
|
||||||
|
|
||||||
Release() = default;
|
Release() = default;
|
||||||
Release(const std::string& name, const std::optional<UUID>& MBID = {});
|
|
||||||
|
|
||||||
// Accessors
|
// Accessors
|
||||||
static std::size_t getCount(Session& session);
|
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
|
// 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;
|
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
|
// Utility functions
|
||||||
std::optional<int> getReleaseYear(bool originalDate = false) const;
|
std::optional<int> getReleaseYear(bool originalDate = false) const;
|
||||||
std::optional<std::string> getCopyright() const;
|
std::optional<std::string> getCopyright() const;
|
||||||
@@ -133,6 +129,10 @@ class Release : public Object<Release, ReleaseId>
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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};
|
static const std::size_t _maxNameLength {128};
|
||||||
|
|
||||||
std::string _name;
|
std::string _name;
|
||||||
|
|||||||
@@ -39,7 +39,8 @@ class ScanSettings : public Object<ScanSettings, ScanSettingsId>
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// Do not modify values (just add)
|
// Do not modify values (just add)
|
||||||
enum class UpdatePeriod {
|
enum class UpdatePeriod
|
||||||
|
{
|
||||||
Never = 0,
|
Never = 0,
|
||||||
Daily,
|
Daily,
|
||||||
Weekly,
|
Weekly,
|
||||||
|
|||||||
@@ -19,16 +19,15 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
#include <Wt/Dbo/Dbo.h>
|
#include <Wt/Dbo/Dbo.h>
|
||||||
#include <Wt/Dbo/SqlConnectionPool.h>
|
#include <Wt/Dbo/SqlConnectionPool.h>
|
||||||
|
|
||||||
#include "utils/RecursiveSharedMutex.hpp"
|
#include "utils/RecursiveSharedMutex.hpp"
|
||||||
|
#include "services/database/Object.hpp"
|
||||||
|
|
||||||
|
|
||||||
namespace Database
|
namespace Database
|
||||||
{
|
{
|
||||||
|
|
||||||
class UniqueTransaction
|
class UniqueTransaction
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
@@ -73,11 +72,22 @@ namespace Database
|
|||||||
Wt::Dbo::Session& getDboSession() { return _session; }
|
Wt::Dbo::Session& getDboSession() { return _session; }
|
||||||
Db& getDb() { return _db; }
|
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:
|
private:
|
||||||
Db& _db;
|
Db& _db;
|
||||||
Wt::Dbo::Session _session;
|
Wt::Dbo::Session _session;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Database
|
} // namespace Database
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -38,16 +38,12 @@ namespace Database
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
StarredArtist() = default;
|
StarredArtist() = default;
|
||||||
StarredArtist(ObjectPtr<Artist> artist, ObjectPtr<User> user, Scrobbler scrobbler);
|
|
||||||
|
|
||||||
// Search utility
|
// Search utility
|
||||||
static std::size_t getCount(Session& session);
|
static std::size_t getCount(Session& session);
|
||||||
static pointer find(Session& session, StarredArtistId id);
|
static pointer find(Session& session, StarredArtistId id);
|
||||||
static pointer find(Session& session, ArtistId artistId, UserId userId, Scrobbler scrobbler);
|
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
|
// Accessors
|
||||||
ObjectPtr<Artist> getArtist() const { return _artist; }
|
ObjectPtr<Artist> getArtist() const { return _artist; }
|
||||||
ObjectPtr<User> getUser() const { return _user; }
|
ObjectPtr<User> getUser() const { return _user; }
|
||||||
@@ -71,6 +67,10 @@ namespace Database
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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
|
Scrobbler _scrobbler; // for which scrobbler
|
||||||
ScrobblingState _scrobblingState {ScrobblingState::PendingAdd};
|
ScrobblingState _scrobblingState {ScrobblingState::PendingAdd};
|
||||||
Wt::WDateTime _dateTime; // when it was starred
|
Wt::WDateTime _dateTime; // when it was starred
|
||||||
|
|||||||
@@ -38,16 +38,12 @@ namespace Database
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
StarredRelease() = default;
|
StarredRelease() = default;
|
||||||
StarredRelease(ObjectPtr<Release> release, ObjectPtr<User> user, Scrobbler scrobbler);
|
|
||||||
|
|
||||||
// Search utility
|
// Search utility
|
||||||
static std::size_t getCount(Session& session);
|
static std::size_t getCount(Session& session);
|
||||||
static pointer find(Session& session, StarredReleaseId id);
|
static pointer find(Session& session, StarredReleaseId id);
|
||||||
static pointer find(Session& session, ReleaseId releaseId, UserId userId, Scrobbler scrobbler);
|
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
|
// Accessors
|
||||||
ObjectPtr<Release> getRelease() const { return _release; }
|
ObjectPtr<Release> getRelease() const { return _release; }
|
||||||
ObjectPtr<User> getUser() const { return _user; }
|
ObjectPtr<User> getUser() const { return _user; }
|
||||||
@@ -71,6 +67,10 @@ namespace Database
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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
|
Scrobbler _scrobbler; // for which scrobbler
|
||||||
ScrobblingState _scrobblingState {ScrobblingState::PendingAdd};
|
ScrobblingState _scrobblingState {ScrobblingState::PendingAdd};
|
||||||
Wt::WDateTime _dateTime; // when it was starred
|
Wt::WDateTime _dateTime; // when it was starred
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ namespace Database
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
StarredTrack() = default;
|
StarredTrack() = default;
|
||||||
StarredTrack(ObjectPtr<Track> track, ObjectPtr<User> user, Scrobbler scrobbler);
|
|
||||||
|
|
||||||
struct FindParameters
|
struct FindParameters
|
||||||
{
|
{
|
||||||
@@ -59,9 +58,6 @@ namespace Database
|
|||||||
static pointer find(Session& session, TrackId trackId, UserId userId, Scrobbler scrobbler);
|
static pointer find(Session& session, TrackId trackId, UserId userId, Scrobbler scrobbler);
|
||||||
static RangeResults<StarredTrackId> find(Session& session, const FindParameters& findParams);
|
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
|
// Accessors
|
||||||
ObjectPtr<Track> getTrack() const { return _track; }
|
ObjectPtr<Track> getTrack() const { return _track; }
|
||||||
ObjectPtr<User> getUser() const { return _user; }
|
ObjectPtr<User> getUser() const { return _user; }
|
||||||
@@ -85,6 +81,10 @@ namespace Database
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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
|
Scrobbler _scrobbler; // for which scrobbler
|
||||||
ScrobblingState _scrobblingState {ScrobblingState::PendingAdd};
|
ScrobblingState _scrobblingState {ScrobblingState::PendingAdd};
|
||||||
Wt::WDateTime _dateTime; // when it was starred
|
Wt::WDateTime _dateTime; // when it was starred
|
||||||
|
|||||||
@@ -91,7 +91,6 @@ class Track : public Object<Track, TrackId>
|
|||||||
};
|
};
|
||||||
|
|
||||||
Track() = default;
|
Track() = default;
|
||||||
Track(const std::filesystem::path& p);
|
|
||||||
|
|
||||||
// Find utility functions
|
// Find utility functions
|
||||||
static std::size_t getCount(Session& session);
|
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> findRecordingMBIDDuplicates(Session& session, Range range);
|
||||||
static RangeResults<TrackId> findWithRecordingMBIDAndMissingFeatures(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
|
// Accessors
|
||||||
void setScanVersion(std::size_t version) { _scanVersion = version; }
|
void setScanVersion(std::size_t version) { _scanVersion = version; }
|
||||||
void setTrackNumber(int num) { _trackNumber = num; }
|
void setTrackNumber(int num) { _trackNumber = num; }
|
||||||
@@ -196,6 +192,10 @@ class Track : public Object<Track, TrackId>
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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 _maxNameLength = 128;
|
||||||
static const std::size_t _maxCopyrightLength = 128;
|
static const std::size_t _maxCopyrightLength = 128;
|
||||||
static const std::size_t _maxCopyrightURLLength = 128;
|
static const std::size_t _maxCopyrightURLLength = 128;
|
||||||
|
|||||||
@@ -41,10 +41,6 @@ class TrackBookmark : public Object<TrackBookmark, TrackBookmarkId>
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TrackBookmark () = default;
|
TrackBookmark () = default;
|
||||||
TrackBookmark(ObjectPtr<User> user, ObjectPtr<Track> track);
|
|
||||||
|
|
||||||
// utility
|
|
||||||
static pointer create(Session& session, ObjectPtr<User> user, ObjectPtr<Track> track);
|
|
||||||
|
|
||||||
// Find utility functions
|
// Find utility functions
|
||||||
static std::size_t getCount(Session& session);
|
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, _track, "track", Wt::Dbo::OnDeleteCascade);
|
||||||
Wt::Dbo::belongsTo(a, _user, "user", Wt::Dbo::OnDeleteCascade);
|
Wt::Dbo::belongsTo(a, _user, "user", Wt::Dbo::OnDeleteCascade);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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;
|
static const std::size_t _maxCommentLength = 128;
|
||||||
|
|
||||||
std::chrono::duration<int, std::milli> _offset;
|
std::chrono::duration<int, std::milli> _offset;
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ class TrackFeatures : public Object<TrackFeatures, TrackFeaturesId>
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TrackFeatures() = default;
|
TrackFeatures() = default;
|
||||||
TrackFeatures(ObjectPtr<Track> track, const std::string& jsonEncodedFeatures);
|
|
||||||
|
|
||||||
// Find utilities
|
// Find utilities
|
||||||
static std::size_t getCount(Session& session);
|
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 pointer find(Session& session, TrackId trackId);
|
||||||
static RangeResults<TrackFeaturesId> find(Session& session, Range range);
|
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;
|
FeatureValues getFeatureValues(const FeatureName& feature) const;
|
||||||
FeatureValuesMap getFeatureValuesMap(const std::unordered_set<FeatureName>& featureNames) const;
|
FeatureValuesMap getFeatureValuesMap(const std::unordered_set<FeatureName>& featureNames) const;
|
||||||
|
|
||||||
@@ -71,6 +67,10 @@ class TrackFeatures : public Object<TrackFeatures, TrackFeaturesId>
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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;
|
std::string _data;
|
||||||
Wt::Dbo::ptr<Track> _track;
|
Wt::Dbo::ptr<Track> _track;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ class TrackList : public Object<TrackList, TrackListId>
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TrackList() = default;
|
TrackList() = default;
|
||||||
TrackList(std::string_view name, TrackListType type, bool isPublic, ObjectPtr<User> user);
|
|
||||||
|
|
||||||
// Stats utility
|
// Stats utility
|
||||||
std::vector<ObjectPtr<Artist>> getTopArtists(const std::vector<ClusterId>& clusterIds, std::optional<TrackArtistLinkType> linkType, std::optional<Range> range, bool& moreResults) const;
|
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;
|
Range range;
|
||||||
std::optional<TrackListType> type;
|
std::optional<TrackListType> type;
|
||||||
UserId user; // only tracklists owned by this user
|
UserId user; // only tracklists owned by this user
|
||||||
|
TrackListSortMethod sortMethod {TrackListSortMethod::None};
|
||||||
|
|
||||||
FindParameters& setClusters(const std::vector<ClusterId>& _clusters) { clusters = _clusters; return *this; }
|
FindParameters& setClusters(const std::vector<ClusterId>& _clusters) { clusters = _clusters; return *this; }
|
||||||
FindParameters& setRange(Range _range) { range = _range; return *this; }
|
FindParameters& setRange(Range _range) { range = _range; return *this; }
|
||||||
FindParameters& setType(TrackListType _type) { type = _type; return *this; }
|
FindParameters& setType(TrackListType _type) { type = _type; return *this; }
|
||||||
FindParameters& setUser(UserId _user) { user = _user; 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 std::size_t getCount(Session& session);
|
||||||
static pointer find(Session& session, std::string_view name, TrackListType type, UserId userId);
|
static pointer find(Session& session, std::string_view name, TrackListType type, UserId userId);
|
||||||
static pointer find(Session& session, TrackListId tracklistId);
|
static pointer find(Session& session, TrackListId tracklistId);
|
||||||
static RangeResults<TrackListId> find(Session& session, const FindParameters& params);
|
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
|
// Accessors
|
||||||
std::string_view getName() const { return _name; }
|
std::string_view getName() const { return _name; }
|
||||||
bool isPublic() const { return _isPublic; }
|
bool isPublic() const { return _isPublic; }
|
||||||
@@ -86,6 +84,7 @@ class TrackList : public Object<TrackList, TrackListId>
|
|||||||
void setName(const std::string& name) { _name = name; }
|
void setName(const std::string& name) { _name = name; }
|
||||||
void setIsPublic(bool isPublic) { _isPublic = isPublic; }
|
void setIsPublic(bool isPublic) { _isPublic = isPublic; }
|
||||||
void clear() { _entries.clear(); }
|
void clear() { _entries.clear(); }
|
||||||
|
void setLastModifiedDateTime(const Wt::WDateTime& dateTime) { _lastModifiedDateTime = dateTime; }
|
||||||
|
|
||||||
// Get tracks, ordered by position
|
// Get tracks, ordered by position
|
||||||
bool isEmpty() const;
|
bool isEmpty() const;
|
||||||
@@ -120,15 +119,23 @@ class TrackList : public Object<TrackList, TrackListId>
|
|||||||
Wt::Dbo::field(a, _name, "name");
|
Wt::Dbo::field(a, _name, "name");
|
||||||
Wt::Dbo::field(a, _type, "type");
|
Wt::Dbo::field(a, _type, "type");
|
||||||
Wt::Dbo::field(a, _isPublic, "public");
|
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::belongsTo(a, _user, "user", Wt::Dbo::OnDeleteCascade);
|
||||||
Wt::Dbo::hasMany(a, _entries, Wt::Dbo::ManyToOne, "tracklist");
|
Wt::Dbo::hasMany(a, _entries, Wt::Dbo::ManyToOne, "tracklist");
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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;
|
std::string _name;
|
||||||
TrackListType _type {TrackListType::Playlist};
|
TrackListType _type {TrackListType::Playlist};
|
||||||
bool _isPublic {false};
|
bool _isPublic {false};
|
||||||
|
Wt::WDateTime _creationDateTime;
|
||||||
|
Wt::WDateTime _lastModifiedDateTime;
|
||||||
|
|
||||||
Wt::Dbo::ptr<User> _user;
|
Wt::Dbo::ptr<User> _user;
|
||||||
Wt::Dbo::collection<Wt::Dbo::ptr<TrackListEntry>> _entries;
|
Wt::Dbo::collection<Wt::Dbo::ptr<TrackListEntry>> _entries;
|
||||||
@@ -138,15 +145,13 @@ class TrackListEntry : public Object<TrackListEntry, TrackListEntryId>
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TrackListEntry() = default;
|
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
|
// find utility
|
||||||
static pointer getById(Session& session, TrackListEntryId id);
|
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
|
// Accessors
|
||||||
ObjectPtr<Track> getTrack() const { return _track; }
|
ObjectPtr<Track> getTrack() const { return _track; }
|
||||||
const Wt::WDateTime& getDateTime() const { return _dateTime; }
|
const Wt::WDateTime& getDateTime() const { return _dateTime; }
|
||||||
@@ -161,6 +166,11 @@ class TrackListEntry : public Object<TrackListEntry, TrackListEntryId>
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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::WDateTime _dateTime; // optional date time
|
||||||
Wt::Dbo::ptr<Track> _track;
|
Wt::Dbo::ptr<Track> _track;
|
||||||
Wt::Dbo::ptr<TrackList> _tracklist;
|
Wt::Dbo::ptr<TrackList> _tracklist;
|
||||||
|
|||||||
@@ -104,6 +104,13 @@ namespace Database
|
|||||||
StarredDateDesc,
|
StarredDateDesc,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class TrackListSortMethod
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Name,
|
||||||
|
LastModifiedDesc,
|
||||||
|
};
|
||||||
|
|
||||||
enum class TrackSortMethod
|
enum class TrackSortMethod
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
|
|||||||
@@ -64,10 +64,6 @@ class User : public Object<User, UserId>
|
|||||||
static inline const Scrobbler defaultScrobbler {Scrobbler::Internal};
|
static inline const Scrobbler defaultScrobbler {Scrobbler::Internal};
|
||||||
|
|
||||||
User() = default;
|
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 std::size_t getCount(Session& session);
|
||||||
static pointer find(Session& session, UserId id);
|
static pointer find(Session& session, UserId id);
|
||||||
@@ -137,6 +133,10 @@ class User : public Object<User, UserId>
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
friend class Session;
|
||||||
|
User(std::string_view loginName);
|
||||||
|
static pointer create(Session& session, std::string_view loginName);
|
||||||
|
|
||||||
std::string _loginName;
|
std::string _loginName;
|
||||||
std::string _passwordSalt;
|
std::string _passwordSalt;
|
||||||
std::string _passwordHash;
|
std::string _passwordHash;
|
||||||
|
|||||||
@@ -345,7 +345,6 @@ TEST_F(DatabaseFixture, Artist_nonReleaseTracks)
|
|||||||
track1.get().modify()->setRelease(release.get());
|
track1.get().modify()->setRelease(release.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
auto transaction {session.createSharedTransaction()};
|
auto transaction {session.createSharedTransaction()};
|
||||||
|
|
||||||
|
|||||||
@@ -211,9 +211,9 @@ TEST_F(DatabaseFixture, Cluster_multiTracksMultipleClustersTopRelease)
|
|||||||
{
|
{
|
||||||
auto transaction {session.createUniqueTransaction()};
|
auto transaction {session.createUniqueTransaction()};
|
||||||
|
|
||||||
TrackListEntry::create(session, trackA.get(), trackList.get());
|
session.create<TrackListEntry>(trackA.get(), trackList.get());
|
||||||
TrackListEntry::create(session, trackB.get(), trackList.get());
|
session.create<TrackListEntry>(trackB.get(), trackList.get());
|
||||||
TrackListEntry::create(session, trackB.get(), trackList.get());
|
session.create<TrackListEntry>(trackB.get(), trackList.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -249,9 +249,9 @@ TEST_F(DatabaseFixture, Cluster_multiTracksMultipleClustersTopRelease)
|
|||||||
{
|
{
|
||||||
auto transaction {session.createUniqueTransaction()};
|
auto transaction {session.createUniqueTransaction()};
|
||||||
|
|
||||||
TrackListEntry::create(session, trackC.get(), trackList.get());
|
session.create<TrackListEntry>(trackC.get(), trackList.get());
|
||||||
TrackListEntry::create(session, trackC.get(), trackList.get());
|
session.create<TrackListEntry>(trackC.get(), trackList.get());
|
||||||
TrackListEntry::create(session, trackC.get(), trackList.get());
|
session.create<TrackListEntry>(trackC.get(), trackList.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -630,7 +630,7 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackSingleCluster)
|
|||||||
auto transaction {session.createUniqueTransaction()};
|
auto transaction {session.createUniqueTransaction()};
|
||||||
|
|
||||||
if (i < 5)
|
if (i < 5)
|
||||||
TrackListEntry::create(session, tracks.back().get(), trackList.get());
|
session.create<TrackListEntry>(tracks.back().get(), trackList.get());
|
||||||
|
|
||||||
if (i < 10)
|
if (i < 10)
|
||||||
cluster.get().modify()->addTrack(tracks.back().get());
|
cluster.get().modify()->addTrack(tracks.back().get());
|
||||||
@@ -663,7 +663,7 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClusters)
|
|||||||
auto transaction {session.createUniqueTransaction()};
|
auto transaction {session.createUniqueTransaction()};
|
||||||
|
|
||||||
if (i < 5)
|
if (i < 5)
|
||||||
TrackListEntry::create(session, tracks.back().get(), trackList.get());
|
session.create<TrackListEntry>(tracks.back().get(), trackList.get());
|
||||||
|
|
||||||
if (i < 10)
|
if (i < 10)
|
||||||
{
|
{
|
||||||
@@ -742,7 +742,7 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClustersRecentlyPlayed)
|
|||||||
{
|
{
|
||||||
auto transaction {session.createUniqueTransaction()};
|
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()};
|
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()};
|
auto transaction {session.createUniqueTransaction()};
|
||||||
|
|
||||||
TrackListEntry::create(session, track1.get(), trackList.get(), now.addSecs(2));
|
session.create<TrackListEntry>(track1.get(), trackList.get(), now.addSecs(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#include "services/database/Db.hpp"
|
#include "services/database/Db.hpp"
|
||||||
#include "services/database/Listen.hpp"
|
#include "services/database/Listen.hpp"
|
||||||
#include "services/database/Release.hpp"
|
#include "services/database/Release.hpp"
|
||||||
|
#include "services/database/ScanSettings.hpp"
|
||||||
#include "services/database/Session.hpp"
|
#include "services/database/Session.hpp"
|
||||||
#include "services/database/Track.hpp"
|
#include "services/database/Track.hpp"
|
||||||
#include "services/database/TrackArtistLink.hpp"
|
#include "services/database/TrackArtistLink.hpp"
|
||||||
@@ -50,7 +51,7 @@ class ScopedEntity
|
|||||||
{
|
{
|
||||||
auto transaction {_session.createUniqueTransaction()};
|
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);
|
EXPECT_TRUE(entity);
|
||||||
_id = entity->getId();
|
_id = entity->getId();
|
||||||
}
|
}
|
||||||
@@ -60,6 +61,8 @@ class ScopedEntity
|
|||||||
auto transaction {_session.createUniqueTransaction()};
|
auto transaction {_session.createUniqueTransaction()};
|
||||||
|
|
||||||
auto entity {T::find(_session, _id)};
|
auto entity {T::find(_session, _id)};
|
||||||
|
// could not be here due to "on delete cascade" constraints...
|
||||||
|
if (entity)
|
||||||
entity.remove();
|
entity.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ TEST_F(DatabaseFixture, SingleTrackListSingleTrack)
|
|||||||
{
|
{
|
||||||
auto transaction {session.createUniqueTransaction()};
|
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)
|
TEST_F(DatabaseFixture, SingleTrackListMultipleTrack)
|
||||||
{
|
{
|
||||||
ScopedUser user {session, "MyUser"};
|
ScopedUser user {session, "MyUser"};
|
||||||
@@ -85,7 +132,7 @@ TEST_F(DatabaseFixture, SingleTrackListMultipleTrack)
|
|||||||
tracks.emplace_back(session, "MyTrack" + std::to_string(i));
|
tracks.emplace_back(session, "MyTrack" + std::to_string(i));
|
||||||
|
|
||||||
auto transaction {session.createUniqueTransaction()};
|
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()};
|
Wt::WDateTime now {Wt::WDateTime::currentDateTime()};
|
||||||
auto transaction {session.createUniqueTransaction()};
|
auto transaction {session.createUniqueTransaction()};
|
||||||
TrackListEntry::create(session, track1.get(), trackList.get(), now);
|
session.create<TrackListEntry>(track1.get(), trackList.get(), now);
|
||||||
TrackListEntry::create(session, track2.get(), trackList.get(), now.addSecs(-1));
|
session.create<TrackListEntry>(track2.get(), trackList.get(), now.addSecs(-1));
|
||||||
TrackListEntry::create(session, track3.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()};
|
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()};
|
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()};
|
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()};
|
auto transaction {session.createUniqueTransaction()};
|
||||||
TrackArtistLink::create(session, track1.get(), artist1.get(), TrackArtistLinkType::Artist);
|
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()};
|
auto transaction {session.createUniqueTransaction()};
|
||||||
TrackArtistLink::create(session, track2.get(), artist2.get(), TrackArtistLinkType::Artist);
|
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");
|
artist1.get().modify()->setSortName("ZZZ");
|
||||||
artist2.get().modify()->setSortName("AAA");
|
artist2.get().modify()->setSortName("AAA");
|
||||||
}
|
}
|
||||||
@@ -350,7 +397,7 @@ TEST_F(DatabaseFixture, SingleTrackList_getReleases)
|
|||||||
|
|
||||||
track.get().modify()->setRelease(release.get());
|
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()};
|
auto transaction {session.createUniqueTransaction()};
|
||||||
TrackListEntry::create(session, track1.get(), trackList.get());
|
session.create<TrackListEntry>(track1.get(), trackList.get());
|
||||||
TrackListEntry::create(session, track2.get(), trackList.get());
|
session.create<TrackListEntry>(track2.get(), trackList.get());
|
||||||
TrackListEntry::create(session, track1.get(), trackList.get());
|
session.create<TrackListEntry>(track1.get(), trackList.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -418,7 +465,7 @@ TEST_F(DatabaseFixture, SingleTrackListSingleTrackWithCluster)
|
|||||||
{
|
{
|
||||||
auto transaction {session.createUniqueTransaction()};
|
auto transaction {session.createUniqueTransaction()};
|
||||||
|
|
||||||
TrackListEntry::create(session, track.get(), trackList1.get());
|
session.create<TrackListEntry>(track.get(), trackList1.get());
|
||||||
cluster.get().modify()->addTrack(track.get());
|
cluster.get().modify()->addTrack(track.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ static
|
|||||||
Artist::pointer
|
Artist::pointer
|
||||||
createArtist(Session& session, const MetaData::Artist& artistInfo)
|
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)
|
if (artistInfo.musicBrainzArtistID)
|
||||||
artist.modify()->setMBID(*artistInfo.musicBrainzArtistID);
|
artist.modify()->setMBID(*artistInfo.musicBrainzArtistID);
|
||||||
@@ -187,7 +187,7 @@ getOrCreateRelease(Session& session, const MetaData::Album& album)
|
|||||||
release = Release::find(session, *album.musicBrainzAlbumID);
|
release = Release::find(session, *album.musicBrainzAlbumID);
|
||||||
if (!release)
|
if (!release)
|
||||||
{
|
{
|
||||||
release = Release::create(session, album.name, album.musicBrainzAlbumID);
|
release = session.create<Release>(album.name, album.musicBrainzAlbumID);
|
||||||
}
|
}
|
||||||
else if (release->getName() != album.name)
|
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
|
// No release found with the same name and without MBID -> creating
|
||||||
if (!release)
|
if (!release)
|
||||||
release = Release::create(session, album.name);
|
release = session.create<Release>(album.name);
|
||||||
|
|
||||||
return release;
|
return release;
|
||||||
}
|
}
|
||||||
@@ -236,7 +236,7 @@ getOrCreateClusters(Session& session, const MetaData::Clusters& clustersNames)
|
|||||||
{
|
{
|
||||||
auto cluster = clusterType->getCluster(clusterName);
|
auto cluster = clusterType->getCluster(clusterName);
|
||||||
if (!cluster)
|
if (!cluster)
|
||||||
cluster = Cluster::create(session, clusterType, clusterName);
|
cluster = session.create<Cluster>(clusterType, clusterName);
|
||||||
|
|
||||||
clusters.push_back(cluster);
|
clusters.push_back(cluster);
|
||||||
}
|
}
|
||||||
@@ -567,7 +567,7 @@ ScannerService::fetchTrackFeatures(TrackId trackId, const UUID& recordingMBID)
|
|||||||
if (!track)
|
if (!track)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
TrackFeatures::create(_dbSession, track, data);
|
_dbSession.create<TrackFeatures>(track, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -788,7 +788,7 @@ ScannerService::scanAudioFile(const std::filesystem::path& file, bool forceScan,
|
|||||||
if (!track)
|
if (!track)
|
||||||
{
|
{
|
||||||
// Create a new song
|
// Create a new song
|
||||||
track = Track::create(_dbSession, file);
|
track = _dbSession.create<Track>(file);
|
||||||
LMS_LOG(DBUPDATER, INFO) << "Adding '" << file.string() << "'";
|
LMS_LOG(DBUPDATER, INFO) << "Adding '" << file.string() << "'";
|
||||||
stats.additions++;
|
stats.additions++;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ namespace Scrobbling
|
|||||||
if (!user)
|
if (!user)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
starredObj = StarredObjType::create(session, obj, user, *scrobbler);
|
starredObj = session.create<StarredObjType>(obj, user, *scrobbler);
|
||||||
}
|
}
|
||||||
starredObj.modify()->setDateTime(Wt::WDateTime::currentDateTime());
|
starredObj.modify()->setDateTime(Wt::WDateTime::currentDateTime());
|
||||||
starredObjId = starredObj->getId();
|
starredObjId = starredObj->getId();
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ namespace Scrobbling
|
|||||||
if (!track)
|
if (!track)
|
||||||
return;
|
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);
|
dbListen.modify()->setScrobblingState(Database::ScrobblingState::Synchronized);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -574,7 +574,7 @@ namespace Scrobbling::ListenBrainz
|
|||||||
if (!user)
|
if (!user)
|
||||||
return;
|
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);
|
starredTrack.modify()->setScrobblingState(ScrobblingState::Synchronized);
|
||||||
|
|
||||||
context.importedFeedbackCount++;
|
context.importedFeedbackCount++;
|
||||||
|
|||||||
@@ -366,7 +366,7 @@ namespace Scrobbling::ListenBrainz
|
|||||||
if (!track)
|
if (!track)
|
||||||
return false;
|
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);
|
dbListen.modify()->setScrobblingState(scrobblingState);
|
||||||
|
|
||||||
LOG(DEBUG) << "LISTEN CREATED for user " << user->getLoginName() << ", track '" << track->getName() << "' AT " << listen.listenedAt.toString();
|
LOG(DEBUG) << "LISTEN CREATED for user " << user->getLoginName() << ", track '" << track->getName() << "' AT " << listen.listenedAt.toString();
|
||||||
|
|||||||
@@ -563,7 +563,7 @@ handleCreatePlaylistRequest(RequestContext& context)
|
|||||||
}
|
}
|
||||||
else
|
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)
|
for (const TrackId trackId : trackIds)
|
||||||
@@ -572,7 +572,7 @@ handleCreatePlaylistRequest(RequestContext& context)
|
|||||||
if (!track)
|
if (!track)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
TrackListEntry::create(context.dbSession, track, tracklist);
|
context.dbSession.create<TrackListEntry>(track, tracklist);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Response::createOkResponse(context.serverProtocolVersion);
|
return Response::createOkResponse(context.serverProtocolVersion);
|
||||||
@@ -594,7 +594,7 @@ handleCreateUserRequest(RequestContext& context)
|
|||||||
if (user)
|
if (user)
|
||||||
throw UserAlreadyExistsGenericError {};
|
throw UserAlreadyExistsGenericError {};
|
||||||
|
|
||||||
user = User::create(context.dbSession, username);
|
user = context.dbSession.create<User>(username);
|
||||||
userId = user->getId();
|
userId = user->getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1699,7 +1699,7 @@ handleUpdatePlaylistRequest(RequestContext& context)
|
|||||||
if (!track)
|
if (!track)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
TrackListEntry::create(context.dbSession, track, tracklist);
|
context.dbSession.create<TrackListEntry>(track, tracklist);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Response::createOkResponse(context.serverProtocolVersion);
|
return Response::createOkResponse(context.serverProtocolVersion);
|
||||||
@@ -1754,7 +1754,7 @@ handleCreateBookmark(RequestContext& context)
|
|||||||
// Replace any existing bookmark
|
// Replace any existing bookmark
|
||||||
auto bookmark {TrackBookmark::find(context.dbSession, user->getId(), trackId)};
|
auto bookmark {TrackBookmark::find(context.dbSession, user->getId(), trackId)};
|
||||||
if (!bookmark)
|
if (!bookmark)
|
||||||
bookmark = TrackBookmark::create(context.dbSession, user, track);
|
bookmark = context.dbSession.create<TrackBookmark>(user, track);
|
||||||
|
|
||||||
bookmark.modify()->setOffset(std::chrono::milliseconds {position});
|
bookmark.modify()->setOffset(std::chrono::milliseconds {position});
|
||||||
if (comment)
|
if (comment)
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ add_executable(lms
|
|||||||
ui/explore/SearchView.cpp
|
ui/explore/SearchView.cpp
|
||||||
ui/explore/TrackCollector.cpp
|
ui/explore/TrackCollector.cpp
|
||||||
ui/explore/TrackListHelpers.cpp
|
ui/explore/TrackListHelpers.cpp
|
||||||
|
ui/explore/TrackListView.cpp
|
||||||
ui/explore/TrackListsView.cpp
|
ui/explore/TrackListsView.cpp
|
||||||
ui/explore/TracksView.cpp
|
ui/explore/TracksView.cpp
|
||||||
ui/resource/AudioFileResource.cpp
|
ui/resource/AudioFileResource.cpp
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
#include "services/database/Db.hpp"
|
#include "services/database/Db.hpp"
|
||||||
#include "services/database/Release.hpp"
|
#include "services/database/Release.hpp"
|
||||||
#include "services/database/Session.hpp"
|
#include "services/database/Session.hpp"
|
||||||
|
#include "services/database/TrackList.hpp"
|
||||||
#include "services/database/User.hpp"
|
#include "services/database/User.hpp"
|
||||||
#include "services/scrobbling/IScrobblingService.hpp"
|
#include "services/scrobbling/IScrobblingService.hpp"
|
||||||
#include "utils/Logger.hpp"
|
#include "utils/Logger.hpp"
|
||||||
@@ -191,6 +192,7 @@ LmsApplication::init()
|
|||||||
messageResourceBundle().use(appRoot() + "releases");
|
messageResourceBundle().use(appRoot() + "releases");
|
||||||
messageResourceBundle().use(appRoot() + "search");
|
messageResourceBundle().use(appRoot() + "search");
|
||||||
messageResourceBundle().use(appRoot() + "settings");
|
messageResourceBundle().use(appRoot() + "settings");
|
||||||
|
messageResourceBundle().use(appRoot() + "tracklist");
|
||||||
messageResourceBundle().use(appRoot() + "tracklists");
|
messageResourceBundle().use(appRoot() + "tracklists");
|
||||||
messageResourceBundle().use(appRoot() + "tracks");
|
messageResourceBundle().use(appRoot() + "tracks");
|
||||||
|
|
||||||
@@ -300,6 +302,23 @@ LmsApplication::createReleaseAnchor(Database::Release::pointer release, bool add
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Wt::WAnchor>
|
||||||
|
LmsApplication::createTrackListAnchor(Database::TrackList::pointer trackList, bool addText)
|
||||||
|
{
|
||||||
|
Wt::WLink link {Wt::LinkType::InternalPath, "/tracklist/" + trackList->getId().toString()};
|
||||||
|
auto res {std::make_unique<Wt::WAnchor>(link)};
|
||||||
|
|
||||||
|
if (addText)
|
||||||
|
{
|
||||||
|
const Wt::WString name {Wt::WString::fromUTF8(std::string {trackList->getName()})};
|
||||||
|
res->setTextFormat(Wt::TextFormat::Plain);
|
||||||
|
res->setText(name);
|
||||||
|
res->setToolTip(name, Wt::TextFormat::Plain);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
LmsApplication::handleException(LmsApplicationException& e)
|
LmsApplication::handleException(LmsApplicationException& e)
|
||||||
{
|
{
|
||||||
@@ -350,6 +369,7 @@ handlePathChange(Wt::WStackedWidget& stack, bool isAdmin)
|
|||||||
{ "/search", IdxExplore, false },
|
{ "/search", IdxExplore, false },
|
||||||
{ "/tracks", IdxExplore, false },
|
{ "/tracks", IdxExplore, false },
|
||||||
{ "/tracklists", IdxExplore, false },
|
{ "/tracklists", IdxExplore, false },
|
||||||
|
{ "/tracklist", IdxExplore, false },
|
||||||
{ "/playqueue", IdxPlayQueue, false },
|
{ "/playqueue", IdxPlayQueue, false },
|
||||||
{ "/settings", IdxSettings, false },
|
{ "/settings", IdxSettings, false },
|
||||||
{ "/admin/database", IdxAdminDatabase, true },
|
{ "/admin/database", IdxAdminDatabase, true },
|
||||||
@@ -546,7 +566,7 @@ LmsApplication::createHome()
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
internalPathChanged().connect([=]
|
internalPathChanged().connect(mainStack, [=]
|
||||||
{
|
{
|
||||||
handlePathChange(*mainStack, isAdmin);
|
handlePathChange(*mainStack, isAdmin);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ namespace Database
|
|||||||
class Db;
|
class Db;
|
||||||
class Release;
|
class Release;
|
||||||
class Session;
|
class Session;
|
||||||
|
class TrackList;
|
||||||
class User;
|
class User;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,6 +83,7 @@ class LmsApplication : public Wt::WApplication
|
|||||||
static std::unique_ptr<Wt::WAnchor> createArtistAnchor(Database::ObjectPtr<Database::Artist> artist, bool addText = true);
|
static std::unique_ptr<Wt::WAnchor> createArtistAnchor(Database::ObjectPtr<Database::Artist> artist, bool addText = true);
|
||||||
static Wt::WLink createReleaseLink(Database::ObjectPtr<Database::Release> release);
|
static Wt::WLink createReleaseLink(Database::ObjectPtr<Database::Release> release);
|
||||||
static std::unique_ptr<Wt::WAnchor> createReleaseAnchor(Database::ObjectPtr<Database::Release> release, bool addText = true);
|
static std::unique_ptr<Wt::WAnchor> createReleaseAnchor(Database::ObjectPtr<Database::Release> release, bool addText = true);
|
||||||
|
static std::unique_ptr<Wt::WAnchor> createTrackListAnchor(Database::ObjectPtr<Database::TrackList> trackList, bool addText = true);
|
||||||
|
|
||||||
MediaPlayer& getMediaPlayer() const { return *_mediaPlayer; }
|
MediaPlayer& getMediaPlayer() const { return *_mediaPlayer; }
|
||||||
PlayQueue& getPlayQueue() const { return *_playQueue; }
|
PlayQueue& getPlayQueue() const { return *_playQueue; }
|
||||||
|
|||||||
@@ -42,6 +42,12 @@ namespace UserInterface
|
|||||||
ReleaseNotFoundException() : LmsApplicationException {Wt::WString::tr("Lms.Error.release-not-found")} {}
|
ReleaseNotFoundException() : LmsApplicationException {Wt::WString::tr("Lms.Error.release-not-found")} {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class TrackListNotFoundException : public LmsApplicationException
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TrackListNotFoundException() : LmsApplicationException {Wt::WString::tr("Lms.Error.tracklist-not-found")} {}
|
||||||
|
};
|
||||||
|
|
||||||
class UserNotFoundException : public LmsApplicationException
|
class UserNotFoundException : public LmsApplicationException
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ PlayQueue::PlayQueue()
|
|||||||
|
|
||||||
getTrackList().modify()->clear();
|
getTrackList().modify()->clear();
|
||||||
for (const auto& entry : entries)
|
for (const auto& entry : entries)
|
||||||
Database::TrackListEntry::create(LmsApp->getDbSession(), entry->getTrack(), trackList);
|
LmsApp->getDbSession().create<Database::TrackListEntry>(entry->getTrack(), trackList);
|
||||||
}
|
}
|
||||||
_entriesContainer->clear();
|
_entriesContainer->clear();
|
||||||
addSome();
|
addSome();
|
||||||
@@ -150,12 +150,12 @@ PlayQueue::PlayQueue()
|
|||||||
static const std::string queuedListName {"__queued_tracks__"};
|
static const std::string queuedListName {"__queued_tracks__"};
|
||||||
trackList = Database::TrackList::find(LmsApp->getDbSession(), queuedListName, Database::TrackListType::Internal, LmsApp->getUserId());
|
trackList = Database::TrackList::find(LmsApp->getDbSession(), queuedListName, Database::TrackListType::Internal, LmsApp->getUserId());
|
||||||
if (!trackList)
|
if (!trackList)
|
||||||
trackList = Database::TrackList::create(LmsApp->getDbSession(), queuedListName, Database::TrackListType::Internal, false, LmsApp->getUser());
|
trackList = LmsApp->getDbSession().create<Database::TrackList>(queuedListName, Database::TrackListType::Internal, false, LmsApp->getUser());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
static const std::string currentPlayQueueName {"__current__playqueue__"};
|
static const std::string currentPlayQueueName {"__current__playqueue__"};
|
||||||
trackList = Database::TrackList::create(LmsApp->getDbSession(), currentPlayQueueName, Database::TrackListType::Internal, false, LmsApp->getUser());
|
trackList = LmsApp->getDbSession().create<Database::TrackList>(currentPlayQueueName, Database::TrackListType::Internal, false, LmsApp->getUser());
|
||||||
}
|
}
|
||||||
|
|
||||||
_tracklistId = trackList->getId();
|
_tracklistId = trackList->getId();
|
||||||
@@ -323,7 +323,7 @@ PlayQueue::enqueueTracks(const std::vector<Database::TrackId>& trackIds)
|
|||||||
if (nbTracksQueued == nbTracksToEnqueue)
|
if (nbTracksQueued == nbTracksToEnqueue)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
Database::TrackListEntry::create(LmsApp->getDbSession(), track, tracklist);
|
LmsApp->getDbSession().create<Database::TrackListEntry>(track, tracklist);
|
||||||
nbTracksQueued++;
|
nbTracksQueued++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ class InitWizardModel : public Wt::WFormModel
|
|||||||
if (Database::User::getCount(LmsApp->getDbSession()) > 0)
|
if (Database::User::getCount(LmsApp->getDbSession()) > 0)
|
||||||
throw LmsException {"Admin user already created"};
|
throw LmsException {"Admin user already created"};
|
||||||
|
|
||||||
Database::User::pointer user {Database::User::create(LmsApp->getDbSession(), valueText(AdminLoginField).toUTF8())};
|
Database::User::pointer user {LmsApp->getDbSession().create<Database::User>(valueText(AdminLoginField).toUTF8())};
|
||||||
user.modify()->setType(Database::UserType::ADMIN);
|
user.modify()->setType(Database::UserType::ADMIN);
|
||||||
Service<::Auth::IPasswordService>::get()->setPassword(user->getId(), valueText(PasswordField).toUTF8());
|
Service<::Auth::IPasswordService>::get()->setPassword(user->getId(), valueText(PasswordField).toUTF8());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ class UserModel : public Wt::WFormModel
|
|||||||
throw UserNotAllowedException {};
|
throw UserNotAllowedException {};
|
||||||
|
|
||||||
// Create user
|
// Create user
|
||||||
user = User::create(LmsApp->getDbSession(), valueText(LoginField).toUTF8());
|
user = LmsApp->getDbSession().create<User>(valueText(LoginField).toUTF8());
|
||||||
|
|
||||||
if (Wt::asNumber(value(DemoField)))
|
if (Wt::asNumber(value(DemoField)))
|
||||||
user.modify()->setType(UserType::DEMO);
|
user.modify()->setType(UserType::DEMO);
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
#include "ReleasesView.hpp"
|
#include "ReleasesView.hpp"
|
||||||
#include "ReleaseView.hpp"
|
#include "ReleaseView.hpp"
|
||||||
#include "SearchView.hpp"
|
#include "SearchView.hpp"
|
||||||
|
#include "TrackListView.hpp"
|
||||||
#include "TrackListsView.hpp"
|
#include "TrackListsView.hpp"
|
||||||
#include "TracksView.hpp"
|
#include "TracksView.hpp"
|
||||||
|
|
||||||
@@ -52,6 +53,7 @@ handleContentsPathChange(Wt::WStackedWidget* stack)
|
|||||||
IdxArtists = 0,
|
IdxArtists = 0,
|
||||||
IdxArtist,
|
IdxArtist,
|
||||||
IdxTracklists,
|
IdxTracklists,
|
||||||
|
IdxTracklist,
|
||||||
IdxReleases,
|
IdxReleases,
|
||||||
IdxRelease,
|
IdxRelease,
|
||||||
IdxSearch,
|
IdxSearch,
|
||||||
@@ -63,6 +65,7 @@ handleContentsPathChange(Wt::WStackedWidget* stack)
|
|||||||
{ "/artists", IdxArtists },
|
{ "/artists", IdxArtists },
|
||||||
{ "/artist", IdxArtist },
|
{ "/artist", IdxArtist },
|
||||||
{ "/tracklists", IdxTracklists },
|
{ "/tracklists", IdxTracklists },
|
||||||
|
{ "/tracklist", IdxTracklist },
|
||||||
{ "/releases", IdxReleases },
|
{ "/releases", IdxReleases },
|
||||||
{ "/release", IdxRelease },
|
{ "/release", IdxRelease },
|
||||||
{ "/search", IdxSearch },
|
{ "/search", IdxSearch },
|
||||||
@@ -104,6 +107,11 @@ Explore::Explore(Filters* filters)
|
|||||||
tracklists->trackListAction.connect(this, &Explore::handleTrackListAction);
|
tracklists->trackListAction.connect(this, &Explore::handleTrackListAction);
|
||||||
contentsStack->addWidget(std::move(tracklists));
|
contentsStack->addWidget(std::move(tracklists));
|
||||||
|
|
||||||
|
auto tracklist {std::make_unique<TrackList>(*_filters)};
|
||||||
|
tracklist->trackListAction.connect(this, &Explore::handleTrackListAction);
|
||||||
|
tracklist->tracksAction.connect(this, &Explore::handleTracksAction);
|
||||||
|
contentsStack->addWidget(std::move(tracklist));
|
||||||
|
|
||||||
auto releases = std::make_unique<Releases>(*_filters);
|
auto releases = std::make_unique<Releases>(*_filters);
|
||||||
releases->releasesAction.connect(this, &Explore::handleReleasesAction);
|
releases->releasesAction.connect(this, &Explore::handleReleasesAction);
|
||||||
contentsStack->addWidget(std::move(releases));
|
contentsStack->addWidget(std::move(releases));
|
||||||
|
|||||||
@@ -0,0 +1,196 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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 "TrackListView.hpp"
|
||||||
|
|
||||||
|
#include <Wt/WPushButton.h>
|
||||||
|
|
||||||
|
#include "services/database/Session.hpp"
|
||||||
|
#include "services/database/Track.hpp"
|
||||||
|
#include "services/database/TrackList.hpp"
|
||||||
|
#include "utils/Logger.hpp"
|
||||||
|
#include "utils/String.hpp"
|
||||||
|
|
||||||
|
#include "common/InfiniteScrollingContainer.hpp"
|
||||||
|
#include "resource/DownloadResource.hpp"
|
||||||
|
#include "Filters.hpp"
|
||||||
|
#include "LmsApplication.hpp"
|
||||||
|
#include "LmsApplicationException.hpp"
|
||||||
|
#include "ModalManager.hpp"
|
||||||
|
#include "TrackListHelpers.hpp"
|
||||||
|
#include "Utils.hpp"
|
||||||
|
|
||||||
|
using namespace Database;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
static
|
||||||
|
std::optional<TrackListId>
|
||||||
|
extractTrackListIdFromInternalPath()
|
||||||
|
{
|
||||||
|
return StringUtils::readAs<TrackListId::ValueType>(wApp->internalPathNextPart("/tracklist/"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace UserInterface
|
||||||
|
{
|
||||||
|
TrackList::TrackList(Filters& filters)
|
||||||
|
: Template {Wt::WString::tr("Lms.Explore.TrackList.template")}
|
||||||
|
{
|
||||||
|
addFunction("tr", &Wt::WTemplate::Functions::tr);
|
||||||
|
addFunction("id", &Wt::WTemplate::Functions::id);
|
||||||
|
|
||||||
|
wApp->internalPathChanged().connect(this, [this]
|
||||||
|
{
|
||||||
|
refreshView();
|
||||||
|
});
|
||||||
|
|
||||||
|
filters.updated().connect([this]
|
||||||
|
{
|
||||||
|
refreshView();
|
||||||
|
});
|
||||||
|
|
||||||
|
refreshView();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TrackList::refreshView()
|
||||||
|
{
|
||||||
|
if (!wApp->internalPathMatches("/tracklist/"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const std::optional<TrackListId> trackListId {extractTrackListIdFromInternalPath()};
|
||||||
|
if (!trackListId)
|
||||||
|
throw TrackListNotFoundException {};
|
||||||
|
|
||||||
|
auto transaction {LmsApp->getDbSession().createSharedTransaction()};
|
||||||
|
|
||||||
|
const Database::TrackList::pointer trackList {Database::TrackList::find(LmsApp->getDbSession(), *trackListId)};
|
||||||
|
if (!trackList)
|
||||||
|
throw TrackListNotFoundException {};
|
||||||
|
|
||||||
|
_trackListId = *trackListId;
|
||||||
|
|
||||||
|
clear();
|
||||||
|
|
||||||
|
bindString("name", std::string {trackList->getName()}, Wt::TextFormat::Plain);
|
||||||
|
bindString("duration", Utils::durationToString(trackList->getDuration()));
|
||||||
|
bindString("track-count", Wt::WString::trn("Lms.Explore.TrackList.track-count", trackList->getCount()).arg(trackList->getCount()));
|
||||||
|
|
||||||
|
bindNew<Wt::WPushButton>("play-btn", Wt::WString::tr("Lms.Explore.play"), Wt::TextFormat::XHTML)
|
||||||
|
->clicked().connect([=]
|
||||||
|
{
|
||||||
|
trackListAction.emit(PlayQueueAction::Play, *trackListId);
|
||||||
|
});
|
||||||
|
|
||||||
|
bindNew<Wt::WPushButton>("play-shuffled", Wt::WString::tr("Lms.Explore.play-shuffled"), Wt::TextFormat::Plain)
|
||||||
|
->clicked().connect([=]
|
||||||
|
{
|
||||||
|
trackListAction.emit(PlayQueueAction::PlayShuffled, *trackListId);
|
||||||
|
});
|
||||||
|
|
||||||
|
bindNew<Wt::WPushButton>("play-last", Wt::WString::tr("Lms.Explore.play-last"), Wt::TextFormat::Plain)
|
||||||
|
->clicked().connect([=]
|
||||||
|
{
|
||||||
|
trackListAction.emit(PlayQueueAction::PlayLast, *trackListId);
|
||||||
|
});
|
||||||
|
|
||||||
|
bindNew<Wt::WPushButton>("download", Wt::WString::tr("Lms.Explore.download"))
|
||||||
|
->setLink(Wt::WLink {std::make_unique<DownloadTrackListResource>(*trackListId)});
|
||||||
|
|
||||||
|
bindNew<Wt::WPushButton>("delete", Wt::WString::tr("Lms.delete"))
|
||||||
|
->clicked().connect([=]
|
||||||
|
{
|
||||||
|
auto modal {std::make_unique<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.TrackList.template.delete-tracklist"))};
|
||||||
|
modal->addFunction("tr", &Wt::WTemplate::Functions::tr);
|
||||||
|
Wt::WWidget* modalPtr {modal.get()};
|
||||||
|
|
||||||
|
auto* delBtn {modal->bindNew<Wt::WPushButton>("del-btn", Wt::WString::tr("Lms.delete"))};
|
||||||
|
delBtn->clicked().connect([=]
|
||||||
|
{
|
||||||
|
{
|
||||||
|
auto transaction {LmsApp->getDbSession().createUniqueTransaction()};
|
||||||
|
|
||||||
|
Database::TrackList::pointer trackList {Database::TrackList::find(LmsApp->getDbSession(), *trackListId)};
|
||||||
|
if (trackList)
|
||||||
|
trackList.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
clear();
|
||||||
|
LmsApp->setInternalPath("/tracklists", true);
|
||||||
|
|
||||||
|
LmsApp->getModalManager().dispose(modalPtr);
|
||||||
|
});
|
||||||
|
|
||||||
|
auto* cancelBtn {modal->bindNew<Wt::WPushButton>("cancel-btn", Wt::WString::tr("Lms.cancel"))};
|
||||||
|
cancelBtn->clicked().connect([=]
|
||||||
|
{
|
||||||
|
LmsApp->getModalManager().dispose(modalPtr);
|
||||||
|
});
|
||||||
|
|
||||||
|
LmsApp->getModalManager().show(std::move(modal));
|
||||||
|
});
|
||||||
|
|
||||||
|
_container = bindNew<InfiniteScrollingContainer>("tracks");
|
||||||
|
_container->onRequestElements.connect([this]
|
||||||
|
{
|
||||||
|
addSome();
|
||||||
|
});
|
||||||
|
|
||||||
|
addSome();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TrackList::addSome()
|
||||||
|
{
|
||||||
|
auto transaction {LmsApp->getDbSession().createSharedTransaction()};
|
||||||
|
|
||||||
|
Database::Track::FindParameters params;
|
||||||
|
params.setTrackList(_trackListId);
|
||||||
|
params.setSortMethod(Database::TrackSortMethod::TrackList);
|
||||||
|
params.setRange({static_cast<std::size_t>(_container->getCount()), _batchSize});
|
||||||
|
|
||||||
|
const auto trackIds {Database::Track::find(LmsApp->getDbSession(), params)};
|
||||||
|
for (const TrackId trackId : trackIds.results)
|
||||||
|
{
|
||||||
|
if (const Track::pointer track {Track::find(LmsApp->getDbSession(), trackId)})
|
||||||
|
_container->add(TrackListHelpers::createEntry(track, tracksAction));
|
||||||
|
}
|
||||||
|
|
||||||
|
_container->setHasMore(trackIds.moreResults && _container->getCount() < _maxCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
void
|
||||||
|
Tracks::addSome()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Database::TrackId>
|
||||||
|
Tracks::getAllTracks()
|
||||||
|
{
|
||||||
|
RangeResults<TrackId> trackIds {_trackCollector.get(Range {})};
|
||||||
|
|
||||||
|
return std::move(trackIds.results);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
} // namespace UserInterface
|
||||||
|
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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 "services/database/TrackListId.hpp"
|
||||||
|
#include "services/database/Types.hpp"
|
||||||
|
|
||||||
|
#include "common/Template.hpp"
|
||||||
|
#include "PlayQueueAction.hpp"
|
||||||
|
|
||||||
|
namespace UserInterface
|
||||||
|
{
|
||||||
|
class Filters;
|
||||||
|
class InfiniteScrollingContainer;
|
||||||
|
|
||||||
|
class TrackList : public Template
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TrackList(Filters& filters);
|
||||||
|
|
||||||
|
PlayQueueActionTrackListSignal trackListAction;
|
||||||
|
PlayQueueActionTrackSignal tracksAction;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void refreshView();
|
||||||
|
void addSome();
|
||||||
|
|
||||||
|
static constexpr std::size_t _batchSize {6};
|
||||||
|
static constexpr std::size_t _maxCount {8000};
|
||||||
|
|
||||||
|
Database::TrackListId _trackListId;
|
||||||
|
InfiniteScrollingContainer* _container {};
|
||||||
|
};
|
||||||
|
} // namespace UserInterface
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2018 Emeric Poupon
|
* Copyright (C) 2022 Emeric Poupon
|
||||||
*
|
*
|
||||||
* This file is part of LMS.
|
* This file is part of LMS.
|
||||||
*
|
*
|
||||||
@@ -26,11 +26,8 @@
|
|||||||
|
|
||||||
#include "common/InfiniteScrollingContainer.hpp"
|
#include "common/InfiniteScrollingContainer.hpp"
|
||||||
#include "common/Template.hpp"
|
#include "common/Template.hpp"
|
||||||
#include "resource/DownloadResource.hpp"
|
|
||||||
#include "ReleaseListHelpers.hpp"
|
|
||||||
#include "Filters.hpp"
|
#include "Filters.hpp"
|
||||||
#include "LmsApplication.hpp"
|
#include "LmsApplication.hpp"
|
||||||
#include "ModalManager.hpp"
|
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
|
|
||||||
using namespace Database;
|
using namespace Database;
|
||||||
@@ -45,6 +42,28 @@ namespace UserInterface
|
|||||||
addFunction("tr", &Wt::WTemplate::Functions::tr);
|
addFunction("tr", &Wt::WTemplate::Functions::tr);
|
||||||
addFunction("id", &Wt::WTemplate::Functions::id);
|
addFunction("id", &Wt::WTemplate::Functions::id);
|
||||||
|
|
||||||
|
auto bindMenuItem {[this](const std::string& var, const Wt::WString& title, Mode mode)
|
||||||
|
{
|
||||||
|
auto *menuItem {bindNew<Wt::WPushButton>(var, title)};
|
||||||
|
menuItem->clicked().connect([=]
|
||||||
|
{
|
||||||
|
_mode = mode;
|
||||||
|
refreshView();
|
||||||
|
_currentActiveItem->removeStyleClass("active");
|
||||||
|
menuItem->addStyleClass("active");
|
||||||
|
_currentActiveItem = menuItem;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (mode == _mode)
|
||||||
|
{
|
||||||
|
_currentActiveItem = menuItem;
|
||||||
|
_currentActiveItem->addStyleClass("active");
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
|
||||||
|
bindMenuItem("recently-modified", Wt::WString::tr("Lms.Explore.recently-modified"), Mode::RecentlyModified);
|
||||||
|
bindMenuItem("all", Wt::WString::tr("Lms.Explore.all"), Mode::All);
|
||||||
|
|
||||||
_container = bindNew<InfiniteScrollingContainer>("tracklists", Wt::WString::tr("Lms.Explore.TrackLists.template.container"));
|
_container = bindNew<InfiniteScrollingContainer>("tracklists", Wt::WString::tr("Lms.Explore.TrackLists.template.container"));
|
||||||
_container->onRequestElements.connect([this]
|
_container->onRequestElements.connect([this]
|
||||||
{
|
{
|
||||||
@@ -79,6 +98,15 @@ namespace UserInterface
|
|||||||
params.setUser(LmsApp->getUserId());
|
params.setUser(LmsApp->getUserId());
|
||||||
params.setType(TrackListType::Playlist);
|
params.setType(TrackListType::Playlist);
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
|
switch (_mode)
|
||||||
|
{
|
||||||
|
case Mode::All:
|
||||||
|
params.setSortMethod(TrackListSortMethod::Name);
|
||||||
|
break;
|
||||||
|
case Mode::RecentlyModified:
|
||||||
|
params.setSortMethod(TrackListSortMethod::LastModifiedDesc);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
const auto trackListIds {TrackList::find(session, params)};
|
const auto trackListIds {TrackList::find(session, params)};
|
||||||
for (const TrackListId trackListId : trackListIds.results)
|
for (const TrackListId trackListId : trackListIds.results)
|
||||||
@@ -94,66 +122,9 @@ namespace UserInterface
|
|||||||
TrackLists::addTracklist(const ObjectPtr<TrackList>& trackList)
|
TrackLists::addTracklist(const ObjectPtr<TrackList>& trackList)
|
||||||
{
|
{
|
||||||
const TrackListId trackListId {trackList->getId()};
|
const TrackListId trackListId {trackList->getId()};
|
||||||
Template* entry {_container->addNew<Template>(Wt::WString::tr("Lms.Explore.TrackLists.template.entry"))};
|
|
||||||
entry->addFunction("id", &Wt::WTemplate::Functions::id);
|
|
||||||
|
|
||||||
entry->bindString("name", std::string {trackList->getName()}, Wt::TextFormat::Plain);
|
WTemplate* entry {_container->addNew<Template>(Wt::WString::tr("Lms.Explore.TrackLists.template.entry"))};
|
||||||
entry->bindString("duration", Utils::durationToString(trackList->getDuration()));
|
entry->bindWidget("name", LmsApplication::createTrackListAnchor(trackList));
|
||||||
|
|
||||||
entry->bindNew<Wt::WPushButton>("play-btn", Wt::WString::tr("Lms.template.play-btn"), Wt::TextFormat::XHTML)
|
|
||||||
->clicked().connect([=]
|
|
||||||
{
|
|
||||||
trackListAction.emit(PlayQueueAction::Play, trackListId);
|
|
||||||
});
|
|
||||||
|
|
||||||
entry->bindNew<Wt::WPushButton>("more-btn", Wt::WString::tr("Lms.template.more-btn"), Wt::TextFormat::XHTML);
|
|
||||||
|
|
||||||
entry->bindNew<Wt::WPushButton>("play-shuffled", Wt::WString::tr("Lms.Explore.play-shuffled"))
|
|
||||||
->clicked().connect([=]
|
|
||||||
{
|
|
||||||
trackListAction.emit(PlayQueueAction::PlayShuffled, trackListId);
|
|
||||||
});
|
|
||||||
|
|
||||||
entry->bindNew<Wt::WPushButton>("play-last", Wt::WString::tr("Lms.Explore.play-last"))
|
|
||||||
->clicked().connect([=]
|
|
||||||
{
|
|
||||||
trackListAction.emit(PlayQueueAction::PlayLast, trackListId);
|
|
||||||
});
|
|
||||||
|
|
||||||
entry->bindNew<Wt::WPushButton>("download", Wt::WString::tr("Lms.Explore.download"))
|
|
||||||
->setLink(Wt::WLink {std::make_unique<DownloadTrackListResource>(trackListId)});
|
|
||||||
|
|
||||||
entry->bindNew<Wt::WPushButton>("delete", Wt::WString::tr("Lms.delete"))
|
|
||||||
->clicked().connect([=]
|
|
||||||
{
|
|
||||||
auto modal {std::make_unique<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.TrackLists.template.delete-tracklist"))};
|
|
||||||
modal->addFunction("tr", &Wt::WTemplate::Functions::tr);
|
|
||||||
Wt::WWidget* modalPtr {modal.get()};
|
|
||||||
|
|
||||||
auto* delBtn {modal->bindNew<Wt::WPushButton>("del-btn", Wt::WString::tr("Lms.delete"))};
|
|
||||||
delBtn->clicked().connect([=]
|
|
||||||
{
|
|
||||||
{
|
|
||||||
auto transaction {LmsApp->getDbSession().createUniqueTransaction()};
|
|
||||||
|
|
||||||
TrackList::pointer trackList {TrackList::find(LmsApp->getDbSession(), trackListId)};
|
|
||||||
if (trackList)
|
|
||||||
trackList.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
_container->remove(*entry);
|
|
||||||
|
|
||||||
LmsApp->getModalManager().dispose(modalPtr);
|
|
||||||
});
|
|
||||||
|
|
||||||
auto* cancelBtn {modal->bindNew<Wt::WPushButton>("cancel-btn", Wt::WString::tr("Lms.cancel"))};
|
|
||||||
cancelBtn->clicked().connect([=]
|
|
||||||
{
|
|
||||||
LmsApp->getModalManager().dispose(modalPtr);
|
|
||||||
});
|
|
||||||
|
|
||||||
LmsApp->getModalManager().show(std::move(modal));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace UserInterface
|
} // namespace UserInterface
|
||||||
|
|||||||
@@ -45,6 +45,12 @@ namespace UserInterface
|
|||||||
PlayQueueActionTrackListSignal trackListAction;
|
PlayQueueActionTrackListSignal trackListAction;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
enum class Mode
|
||||||
|
{
|
||||||
|
RecentlyModified,
|
||||||
|
All,
|
||||||
|
};
|
||||||
|
|
||||||
void refreshView();
|
void refreshView();
|
||||||
void addSome();
|
void addSome();
|
||||||
void addTracklist(const Database::ObjectPtr<Database::TrackList>& trackList);
|
void addTracklist(const Database::ObjectPtr<Database::TrackList>& trackList);
|
||||||
@@ -52,7 +58,9 @@ namespace UserInterface
|
|||||||
static constexpr std::size_t _batchSize {30};
|
static constexpr std::size_t _batchSize {30};
|
||||||
static constexpr std::size_t _maxCount {500};
|
static constexpr std::size_t _maxCount {500};
|
||||||
|
|
||||||
|
Mode _mode {Mode::RecentlyModified};
|
||||||
Filters& _filters;
|
Filters& _filters;
|
||||||
|
Wt::WWidget* _currentActiveItem {};
|
||||||
InfiniteScrollingContainer* _container {};
|
InfiniteScrollingContainer* _container {};
|
||||||
};
|
};
|
||||||
} // namespace UserInterface
|
} // namespace UserInterface
|
||||||
|
|||||||
Reference in New Issue
Block a user