Now diplaying albums in grid mode
This commit is contained in:
+16
-18
@@ -3,15 +3,21 @@
|
||||
<!--FORMS message blocks-->
|
||||
|
||||
<message id="Lms.Explore.Artist.template">
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="Lms-header">
|
||||
<h2><div class="Lms-artistname text-primary">${name}</div></h2>
|
||||
${clusters}
|
||||
${play-btn}${add-btn}
|
||||
</div>
|
||||
<div class="row">
|
||||
${releases class="Lms-explore-artist-entries-container"}
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="well well-sm">
|
||||
<h3>${tr:Lms.Explore.Artist.similar-artists}</h3>
|
||||
${similar-artists}
|
||||
</div>
|
||||
</div>
|
||||
${<if-has-mbid>}
|
||||
<div class="col-md-4">
|
||||
<div class="well well-sm">
|
||||
@@ -20,29 +26,21 @@
|
||||
</div>
|
||||
</div>
|
||||
${</if-has-mbid>}
|
||||
<div class="col-md-4">
|
||||
<div class="well well-sm">
|
||||
<h3>${tr:Lms.Explore.Artist.similar-artists}</h3>
|
||||
${similar-artists}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</message>
|
||||
|
||||
<message id="Lms.Explore.Artist.template.entry">
|
||||
<div class="Lms-explore-artist-entry Lms-entry-hoverable">
|
||||
<div class="media">
|
||||
<div class="media-left media-middle">
|
||||
<div class="media-object Lms-cover-container-small">
|
||||
<div class="Lms-explore-entry-list">
|
||||
<div class="row Lms-vertical-align">
|
||||
<div class="col-xs-4 col-sm-3 col-md-2">
|
||||
<div class="Lms-responsive-square Lms-cover-container">
|
||||
${cover}
|
||||
</div>
|
||||
</div>
|
||||
<div class="media-body media-middle">
|
||||
<h4 class="media-heading">
|
||||
${name class="Lms-releasename text-primary"}
|
||||
</h4>
|
||||
${<if-has-artist>}<h4><small>${artist class="Lms-artistname text-muted"}</small></h4>${</if-has-artist>}
|
||||
<h4><small><div class="text-muted">${<if-has-year>}${year}${</if-has-year>}${<if-has-orig-year>} (${orig-year})${</if-has-orig-year>}</div></small></h4>
|
||||
<div class="col-xs-8 col-sm-9 col-md-10 Lms-explore-list-entry-text-container">
|
||||
<h5>${name class="Lms-releasename text-primary"}</h5>
|
||||
${<if-has-artist>}<h6>${artist class="Lms-artistname text-muted"}</h6>${</if-has-artist>}
|
||||
<h6><small><div class="text-muted">${<if-has-year>}${year}${</if-has-year>}${<if-has-orig-year>} (${orig-year})${</if-has-orig-year>}</div></small></h6>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+2
-2
@@ -5,8 +5,8 @@
|
||||
${contents class="Lms-explore-contents"}
|
||||
</message>
|
||||
|
||||
<message id="Lms.Explore.template.add-btn"><i class="fa fa-fw fa-plus-circle Lms-explore-btn Lms-control-btn"></i></message>
|
||||
<message id="Lms.Explore.template.play-btn"><i class="fa fa-fw fa-play-circle Lms-explore-btn Lms-control-btn"></i></message>
|
||||
<message id="Lms.Explore.template.add-btn"><i class="fa fa-plus-circle Lms-explore-btn Lms-control-btn"></i></message>
|
||||
<message id="Lms.Explore.template.play-btn"><i class="fa fa-play-circle Lms-explore-btn Lms-control-btn"></i></message>
|
||||
|
||||
<message id="Lms.Explore.template.filters">
|
||||
${add-filter class="btn-sm btn-info"} ${clusters class="Lms-explore-clusters"}
|
||||
|
||||
+6
-10
@@ -27,24 +27,20 @@
|
||||
<message id="Lms.PlayQueue.template.delete-btn"><i class="fa fa-fw fa-times-circle Lms-playqueue-btn Lms-control-btn"></i></message>
|
||||
|
||||
<message id="Lms.PlayQueue.template.entry">
|
||||
<div class="row Lms-playqueue-entry Lms-entry-hoverable Lms-vertical-align ${is-selected}">
|
||||
<div class="col-xs-9 Lms-playqueue-entry-name">
|
||||
<div class="media">
|
||||
<div class="media-left media-middle hidden-xs">
|
||||
<div class="media-object Lms-cover-container-small">
|
||||
<div class="row Lms-vertical-align Lms-explore-entry-list Lms-playqueue-entry ${is-selected}">
|
||||
<div class="col-xs-3 col-sm-2 col-md-2">
|
||||
<div class="Lms-responsive-square Lms-cover-container">
|
||||
${cover}
|
||||
</div>
|
||||
</div>
|
||||
<div class="media-body media-middle">
|
||||
<h4><div class="Lms-trackname text-primary">${name}</div></h4>
|
||||
<div class="col-xs-7 col-sm-8 col-md-8 Lms-explore-entry-list-text-container">
|
||||
<div class="Lms-trackname text-primary"><h5>${name}</h5></div>
|
||||
${<if-has-artists-or-release>}
|
||||
${<if-has-release>}<small>${release class="Lms-releasename text-primary"}</small>${</if-has-release>}
|
||||
${<if-has-artists>}${artists}${</if-has-artists>}
|
||||
${</if-has-artists-or-release>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-3">
|
||||
<div class="col-xs-2 col-sm-2 col-md-2">
|
||||
<div class="Lms-playqueue-entry-controls Lms-vertical-align">
|
||||
${play-btn}${del-btn}
|
||||
</div>
|
||||
|
||||
+16
-12
@@ -7,11 +7,11 @@
|
||||
</message>
|
||||
|
||||
<message id="Lms.Explore.Release.template">
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<div class="row Lms-explore-release">
|
||||
<div class="col-md-4">
|
||||
${cover class="Lms-explore-release-cover"}
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<div class="col-md-8">
|
||||
<div class="Lms-header">
|
||||
<h3><div class="Lms-releasename text-primary">${name}</div></h3>
|
||||
${<if-has-artist>}<h4>${artist class="Lms-artistname text-muted"}</h4>${</if-has-artist>}
|
||||
@@ -19,10 +19,20 @@
|
||||
${clusters}
|
||||
${play-btn}${add-btn}
|
||||
</div>
|
||||
${container class="Lms-explore-release-entries-container"}
|
||||
${container}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-8">
|
||||
<div class="well well-sm">
|
||||
<h3>${tr:Lms.Explore.Release.similar-releases}</h3>
|
||||
<div class="row">
|
||||
${similar-releases}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
${<if-has-copyright>}
|
||||
<div class="col-md-4">
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<div class="well well-sm">
|
||||
<h3>${tr:Lms.Explore.Release.copyright}</h3>
|
||||
${copyright class="text-muted"}
|
||||
@@ -30,19 +40,13 @@
|
||||
</div>
|
||||
${</if-has-copyright>}
|
||||
${<if-has-mbid>}
|
||||
<div class="col-md-4">
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<div class="well well-sm">
|
||||
<h3>${tr:Lms.Explore.links}</h3>
|
||||
${mbid-link class="text-muted"}
|
||||
</div>
|
||||
</div>
|
||||
${</if-has-mbid>}
|
||||
<div class="col-md-4">
|
||||
<div class="well well-sm">
|
||||
<h3>${tr:Lms.Explore.Release.similar-releases}</h3>
|
||||
${similar-releases}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</message>
|
||||
|
||||
|
||||
+18
-22
@@ -8,47 +8,43 @@
|
||||
${mode class="nav nav-pills Lms-explore-mode-container"}
|
||||
${play-btn}${add-btn}
|
||||
</div>
|
||||
<div class="row">
|
||||
${releases}
|
||||
</div>
|
||||
<div class="Lms-horizontal-center">
|
||||
${show-more class="btn-primary Lms-show-more"}
|
||||
</div>
|
||||
</message>
|
||||
|
||||
<message id="Lms.Explore.Releases.template.entry">
|
||||
<div class="Lms-explore-releases-entry Lms-entry-hoverable">
|
||||
<div class="media">
|
||||
<div class="media-left media-middle">
|
||||
<div class="media-object Lms-cover-container-small">
|
||||
<message id="Lms.Explore.Releases.template.entry-grid">
|
||||
<div class="col-xs-6 col-sm-3 col-md-2 col-lg-2">
|
||||
<div class="Lms-explore-entry-grid">
|
||||
<div class="Lms-responsive-square Lms-cover-container">
|
||||
${cover}
|
||||
</div>
|
||||
</div>
|
||||
<div class="media-body media-middle">
|
||||
<h4 class="media-heading">
|
||||
${release-name class="Lms-releasename text-primary"}
|
||||
</h4>
|
||||
${<if-has-artist>}<h4><small>${artist-name class="Lms-artistname text-muted"}</small></h4>${</if-has-artist>}
|
||||
<div class="Lms-explore-entry-grid-text-container">
|
||||
<h5>${release-name class="Lms-releasename text-primary"}</h5>
|
||||
<div class="Lms-explore-entry-separator"><h6>${<if-has-year>}<small><span class="text-muted">${year}</span></small>${</if-has-year>}${<if-has-artist>}${artist-name class="Lms-artistname text-muted"}${</if-has-artist>}</h6></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</message>
|
||||
|
||||
<message id="Lms.Explore.Releases.template.entry-small">
|
||||
<div class="Lms-explore-releaselink Lms-entry-hoverable">
|
||||
<div class="media">
|
||||
<div class="media-left media-middle">
|
||||
<div class="media-object Lms-cover-container-smaller">
|
||||
<message id="Lms.Explore.Releases.template.entry-list">
|
||||
<div class="Lms-explore-entry-list">
|
||||
<div class="row Lms-vertical-align">
|
||||
<div class="col-xs-4 col-sm-3 col-md-2">
|
||||
<div class="Lms-responsive-square Lms-cover-container">
|
||||
${cover}
|
||||
</div>
|
||||
</div>
|
||||
<div class="media-body media-middle">
|
||||
<div class="media-heading">
|
||||
${release-name class="Lms-releasename text-primary"}
|
||||
</div>
|
||||
${<if-has-artist>}<small>${artist-name class="Lms-artistname text-muted"}</small>${</if-has-artist>}
|
||||
<div class="col-xs-8 col-sm-9 col-md-10 Lms-explore-entry-list-text-container">
|
||||
<h5>${release-name class="Lms-releasename text-primary"}</h5>
|
||||
${<if-has-artist>}<h6>${artist-name class="Lms-artistname text-muted"}</h6>${</if-has-artist>}
|
||||
<h6><small><div class="text-muted">${<if-has-year>}${year}${</if-has-year>}${<if-has-orig-year>} (${orig-year})${</if-has-orig-year>}</div></small></h6>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</message>
|
||||
|
||||
|
||||
</messages>
|
||||
|
||||
+9
-24
@@ -3,52 +3,37 @@
|
||||
<!--FORMS message blocks-->
|
||||
|
||||
<message id="Lms.Explore.Search.template">
|
||||
<div class="row">
|
||||
<div class="col-lg-8">
|
||||
<div class="Lms-header">
|
||||
<h2>${tr:Lms.Explore.Search.search}</h2>
|
||||
</div>
|
||||
<div class="row">
|
||||
${<if-artists>}
|
||||
<div class="col-lg-8">
|
||||
<div class="well well-sm">
|
||||
<h3>${tr:Lms.Explore.artists}</h3>
|
||||
${artists}
|
||||
</div>
|
||||
</div>
|
||||
${</if-artists>}
|
||||
${<if-releases>}
|
||||
<div class="col-lg-8">
|
||||
<div class="well well-sm">
|
||||
<h3>${tr:Lms.Explore.releases}</h3>
|
||||
<div class="row">
|
||||
${releases}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
${</if-releases>}
|
||||
${<if-tracks>}
|
||||
<div class="col-lg-8">
|
||||
<div class="well well-sm">
|
||||
<h3>${tr:Lms.Explore.tracks}</h3>
|
||||
${tracks}
|
||||
</div>
|
||||
</div>
|
||||
${</if-tracks>}
|
||||
</div>
|
||||
</div>
|
||||
</message>
|
||||
|
||||
<message id="Lms.Explore.Search.template.track-entry">
|
||||
<div class="Lms-explore-tracks-entry Lms-entry-hoverable Lms-vertical-align">
|
||||
<div class="Lms-explore-tracks-entry-name">
|
||||
<div class="media">
|
||||
<div class="media-left media-middle hidden-xs">
|
||||
<div class="media-object Lms-cover-container-small">
|
||||
${cover}
|
||||
</div>
|
||||
</div>
|
||||
<div class="media-body media-middle">
|
||||
<h4><div class="Lms-trackname text-primary">${name}</div></h4>
|
||||
${<if-has-artists-or-release>}
|
||||
${<if-has-release>}<small>${release class="Lms-releasename text-primary"}</small>${</if-has-release>}
|
||||
${<if-has-artists>}${artists}${</if-has-artists>}
|
||||
${</if-has-artists-or-release>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</message>
|
||||
|
||||
</messages>
|
||||
|
||||
+6
-10
@@ -20,24 +20,20 @@
|
||||
</message>
|
||||
|
||||
<message id="Lms.Explore.Tracks.template.entry">
|
||||
<div class="row Lms-explore-tracks-entry Lms-entry-hoverable Lms-vertical-align ${is-playing}">
|
||||
<div class="col-xs-9 Lms-explore-tracks-entry-name">
|
||||
<div class="media">
|
||||
<div class="media-left media-middle hidden-xs">
|
||||
<div class="media-object Lms-cover-container-small">
|
||||
<div class="row Lms-vertical-align Lms-explore-entry-list Lms-explore-tracks-entry ${is-playing}">
|
||||
<div class="col-xs-3 col-sm-2 col-md-2">
|
||||
<div class="Lms-responsive-square Lms-cover-container">
|
||||
${cover}
|
||||
</div>
|
||||
</div>
|
||||
<div class="media-body media-middle">
|
||||
<h4><div class="Lms-trackname text-primary">${name}</div></h4>
|
||||
<div class="col-xs-7 col-sm-8 col-md-8 Lms-explore-entry-list-text-container">
|
||||
<div class="Lms-trackname text-primary"><h5>${name}</h5></div>
|
||||
${<if-has-artists-or-release>}
|
||||
${<if-has-release>}<small>${release class="Lms-releasename text-primary"}</small>${</if-has-release>}
|
||||
${<if-has-artists>}${artists}${</if-has-artists>}
|
||||
${</if-has-artists-or-release>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-3">
|
||||
<div class="col-xs-2 col-sm-2 col-md-2">
|
||||
<div class="Lms-explore-tracks-entry-controls">
|
||||
${play-btn}${add-btn}
|
||||
</div>
|
||||
|
||||
+47
-25
@@ -81,21 +81,7 @@ a.Lms-artistname:hover, a.Lms-artistname:focus {
|
||||
filter: brightness(130%);
|
||||
}
|
||||
|
||||
.Lms-cover-container-smaller {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.Lms-cover-container-small {
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.Lms-cover-container-medium {
|
||||
width: 128px;
|
||||
height: 128px;
|
||||
.Lms-cover-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@@ -124,17 +110,48 @@ a.Lms-artistname:hover, a.Lms-artistname:focus {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.Lms-explore-releases-entry {
|
||||
.Lms-explore-entry-list {
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.Lms-explore-release-cover {
|
||||
max-width: 100%;
|
||||
.Lms-explore-entry-list-text-container {
|
||||
margin-left: -15px;
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.Lms-explore-release-entries-container {
|
||||
margin-bottom: 15px;
|
||||
.Lms-explore-entry-list-text-container h5 {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.Lms-explore-entry-grid {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.Lms-explore-entry-grid-text-container h5, h6 {
|
||||
margin-top: 6px;
|
||||
margin-bottom: 6px;
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.Lms-explore-entry-separator :nth-child(2) {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.Lms-explore-release {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.Lms-explore-release-cover {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.Lms-explore-release-entry-tracknumber {
|
||||
@@ -207,11 +224,6 @@ a.Lms-artistname:hover, a.Lms-artistname:focus {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.Lms-explore-tracks-entry-name {
|
||||
margin-top: 4px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.Lms-explore-tracks-entry-controls {
|
||||
text-align: right;
|
||||
display: none;
|
||||
@@ -443,6 +455,16 @@ a.Lms-releasename:hover, a.Lms-releasename:focus {
|
||||
color: var(--hover-color);
|
||||
}
|
||||
|
||||
.Lms-responsive-square {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.Lms-responsive-square:after {
|
||||
content: "";
|
||||
display: block;
|
||||
padding-bottom: 100%;
|
||||
}
|
||||
|
||||
.Lms-show-more {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
|
||||
@@ -362,7 +362,7 @@ Artist::getReleases(const std::set<IdType>& clusterIds) const
|
||||
if (!clusterIds.empty())
|
||||
oss << " GROUP BY t.id HAVING COUNT(DISTINCT c.id) = " << clusterIds.size();
|
||||
|
||||
oss << " ORDER BY t.year,r.name";
|
||||
oss << " ORDER BY t.year DESC, r.name COLLATE NOCASE";
|
||||
|
||||
Wt::Dbo::Query<Release::pointer> query = session()->query<Release::pointer>( oss.str() );
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ add_executable(lms
|
||||
ui/explore/ReleasesView.cpp
|
||||
ui/explore/ReleaseView.cpp
|
||||
ui/explore/SearchView.cpp
|
||||
ui/explore/TrackListHelpers.cpp
|
||||
ui/explore/TracksView.cpp
|
||||
ui/resource/AudioFileResource.cpp
|
||||
ui/resource/AudioTranscodeResource.cpp
|
||||
|
||||
@@ -251,6 +251,7 @@ LmsApplication::createArtistAnchor(Database::Artist::pointer artist, bool addTex
|
||||
{
|
||||
res->setTextFormat(Wt::TextFormat::Plain);
|
||||
res->setText(Wt::WString::fromUTF8(artist->getName()));
|
||||
res->setToolTip(Wt::WString::fromUTF8(artist->getName()), Wt::TextFormat::Plain);
|
||||
}
|
||||
|
||||
return res;
|
||||
@@ -269,8 +270,10 @@ LmsApplication::createReleaseAnchor(Database::Release::pointer release, bool add
|
||||
|
||||
if (addText)
|
||||
{
|
||||
res->setWordWrap(false);
|
||||
res->setTextFormat(Wt::TextFormat::Plain);
|
||||
res->setText(Wt::WString::fromUTF8(release->getName()));
|
||||
res->setToolTip(Wt::WString::fromUTF8(release->getName()), Wt::TextFormat::Plain);
|
||||
}
|
||||
|
||||
return res;
|
||||
@@ -462,7 +465,7 @@ LmsApplication::createHome()
|
||||
mainStack->setAttributeValue("style", "overflow-x:visible;overflow-y:visible;");
|
||||
|
||||
Explore* explore = mainStack->addNew<Explore>(filters);
|
||||
PlayQueue* playqueue = mainStack->addNew<PlayQueue>();
|
||||
_playQueue = mainStack->addNew<PlayQueue>();
|
||||
auto* search {mainStack->addNew<SearchView>(filters)};
|
||||
mainStack->addNew<SettingsView>();
|
||||
|
||||
@@ -485,31 +488,31 @@ LmsApplication::createHome()
|
||||
mainStack->addNew<UserView>();
|
||||
}
|
||||
|
||||
explore->tracksAction.connect([playqueue] (PlayQueueAction action, const std::vector<Database::IdType>& trackIds)
|
||||
explore->tracksAction.connect([this] (PlayQueueAction action, const std::vector<Database::IdType>& trackIds)
|
||||
{
|
||||
playqueue->processTracks(action, trackIds);
|
||||
_playQueue->processTracks(action, trackIds);
|
||||
});
|
||||
|
||||
// Events from MediaPlayer
|
||||
_mediaPlayer->playNext.connect([=]
|
||||
_mediaPlayer->playNext.connect([this]
|
||||
{
|
||||
playqueue->playNext();
|
||||
_playQueue->playNext();
|
||||
});
|
||||
_mediaPlayer->playPrevious.connect([=]
|
||||
_mediaPlayer->playPrevious.connect([this]
|
||||
{
|
||||
playqueue->playPrevious();
|
||||
_playQueue->playPrevious();
|
||||
});
|
||||
_mediaPlayer->playbackEnded.connect([=]
|
||||
_mediaPlayer->playbackEnded.connect([this]
|
||||
{
|
||||
playqueue->playNext();
|
||||
_playQueue->playNext();
|
||||
});
|
||||
|
||||
playqueue->trackSelected.connect([=] (Database::IdType trackId, bool play, float replayGain)
|
||||
_playQueue->trackSelected.connect([this] (Database::IdType trackId, bool play, float replayGain)
|
||||
{
|
||||
_mediaPlayer->loadTrack(trackId, play, replayGain);
|
||||
});
|
||||
|
||||
playqueue->trackUnselected.connect([=] ()
|
||||
_playQueue->trackUnselected.connect([this] ()
|
||||
{
|
||||
_mediaPlayer->stop();
|
||||
});
|
||||
|
||||
@@ -45,6 +45,7 @@ class Auth;
|
||||
class ImageResource;
|
||||
class LmsApplicationException;
|
||||
class MediaPlayer;
|
||||
class PlayQueue;
|
||||
|
||||
// Events that can be listen from anywhere in the application
|
||||
struct Events
|
||||
@@ -100,7 +101,8 @@ class LmsApplication : public Wt::WApplication
|
||||
static std::unique_ptr<Wt::WAnchor> createReleaseAnchor(Wt::Dbo::ptr<Database::Release> release, bool addText = true);
|
||||
static std::unique_ptr<Wt::WText> createCluster(Wt::Dbo::ptr<Database::Cluster> cluster, bool canDelete = false);
|
||||
|
||||
MediaPlayer* getMediaPlayer() const { return _mediaPlayer; }
|
||||
MediaPlayer& getMediaPlayer() const { return *_mediaPlayer; }
|
||||
PlayQueue& getPlayQueue() const { return *_playQueue; }
|
||||
|
||||
// Signal emitted just before the session ends (user may already be logged out)
|
||||
Wt::Signal<>& preQuit() { return _preQuit; }
|
||||
@@ -131,6 +133,7 @@ class LmsApplication : public Wt::WApplication
|
||||
std::shared_ptr<AudioFileResource> _audioFileResource;
|
||||
std::shared_ptr<ImageResource> _imageResource;
|
||||
MediaPlayer* _mediaPlayer {};
|
||||
PlayQueue* _playQueue {};
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ PlayQueue::PlayQueue()
|
||||
|
||||
if (!LmsApp->getUser()->isDemo())
|
||||
{
|
||||
LmsApp->getMediaPlayer()->settingsLoaded.connect([=]
|
||||
LmsApp->getMediaPlayer().settingsLoaded.connect([=]
|
||||
{
|
||||
if (_mediaPlayerSettingsLoaded)
|
||||
return;
|
||||
@@ -451,7 +451,7 @@ PlayQueue::enqueueRadioTrack()
|
||||
std::optional<float>
|
||||
PlayQueue::getReplayGain(std::size_t pos, const Database::Track::pointer& track) const
|
||||
{
|
||||
const auto& settings {LmsApp->getMediaPlayer()->getSettings()};
|
||||
const auto& settings {LmsApp->getMediaPlayer().getSettings()};
|
||||
if (!settings)
|
||||
return std::nullopt;
|
||||
|
||||
|
||||
@@ -156,7 +156,7 @@ class SettingsModel : public Wt::WFormModel
|
||||
settings.replayGain.preAmpGain = Wt::asNumber(value(ReplayGainPreAmpGainField));
|
||||
settings.replayGain.preAmpGainIfNoInfo = Wt::asNumber(value(ReplayGainPreAmpGainIfNoInfoField));
|
||||
|
||||
LmsApp->getMediaPlayer()->setSettings(settings);
|
||||
LmsApp->getMediaPlayer().setSettings(settings);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -191,7 +191,7 @@ class SettingsModel : public Wt::WFormModel
|
||||
setValue(DarkModeField, user->getUITheme() == User::UITheme::Dark);
|
||||
|
||||
{
|
||||
const auto settings {*LmsApp->getMediaPlayer()->getSettings()};
|
||||
const auto settings {*LmsApp->getMediaPlayer().getSettings()};
|
||||
|
||||
auto transcodeModeRow {_transcodeModeModel->getRowFromValue(settings.transcode.mode)};
|
||||
if (transcodeModeRow)
|
||||
@@ -350,7 +350,7 @@ SettingsView::SettingsView()
|
||||
refreshView();
|
||||
});
|
||||
|
||||
LmsApp->getMediaPlayer()->settingsLoaded.connect([=]()
|
||||
LmsApp->getMediaPlayer().settingsLoaded.connect([=]()
|
||||
{
|
||||
refreshView();
|
||||
});
|
||||
@@ -367,7 +367,7 @@ SettingsView::refreshView()
|
||||
clear();
|
||||
|
||||
// Hack to wait for the audio player know the settings applied
|
||||
if (!LmsApp->getMediaPlayer()->getSettings())
|
||||
if (!LmsApp->getMediaPlayer().getSettings())
|
||||
return;
|
||||
|
||||
auto t {addNew<Wt::WTemplateFormView>(Wt::WString::tr("Lms.Settings.template"))};
|
||||
@@ -426,7 +426,7 @@ SettingsView::refreshView()
|
||||
t->updateModel(model.get());
|
||||
t->updateView(model.get());
|
||||
});
|
||||
if (LmsApp->getMediaPlayer()->getSettings()->transcode.mode == MediaPlayer::Settings::Transcode::Mode::Never)
|
||||
if (LmsApp->getMediaPlayer().getSettings()->transcode.mode == MediaPlayer::Settings::Transcode::Mode::Never)
|
||||
{
|
||||
model->setReadOnly(SettingsModel::TranscodeFormatField, true);
|
||||
model->setReadOnly(SettingsModel::TranscodeBitrateField, true);
|
||||
@@ -456,7 +456,7 @@ SettingsView::refreshView()
|
||||
t->updateModel(model.get());
|
||||
t->updateView(model.get());
|
||||
});
|
||||
if (LmsApp->getMediaPlayer()->getSettings()->replayGain.mode == MediaPlayer::Settings::ReplayGain::Mode::None)
|
||||
if (LmsApp->getMediaPlayer().getSettings()->replayGain.mode == MediaPlayer::Settings::ReplayGain::Mode::None)
|
||||
{
|
||||
model->setReadOnly(SettingsModel::SettingsModel::ReplayGainPreAmpGainField, true);
|
||||
model->setReadOnly(SettingsModel::SettingsModel::ReplayGainPreAmpGainIfNoInfoField, true);
|
||||
|
||||
@@ -32,11 +32,11 @@
|
||||
#include "utils/String.hpp"
|
||||
|
||||
#include "resource/ImageResource.hpp"
|
||||
|
||||
#include "ArtistListHelpers.hpp"
|
||||
#include "Filters.hpp"
|
||||
#include "LmsApplication.hpp"
|
||||
#include "LmsApplicationException.hpp"
|
||||
#include "Filters.hpp"
|
||||
#include "ReleaseListHelpers.hpp"
|
||||
|
||||
using namespace Database;
|
||||
|
||||
@@ -128,61 +128,10 @@ Artist::refreshView()
|
||||
auto releases = artist->getReleases(_filters->getClusterIds());
|
||||
for (const auto& release : releases)
|
||||
{
|
||||
releasesContainer->addWidget(createRelease(artist, release));
|
||||
releasesContainer->addWidget(ReleaseListHelpers::createEntryForArtist(release, artist));
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Wt::WTemplate>
|
||||
Artist::createRelease(const Database::Artist::pointer& artist, const Release::pointer& release)
|
||||
{
|
||||
auto entry = std::make_unique<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Artist.template.entry"));
|
||||
entry->addFunction("tr", Wt::WTemplate::Functions::tr);
|
||||
|
||||
{
|
||||
Wt::WAnchor* anchor = entry->bindWidget("cover", LmsApplication::createReleaseAnchor(release, false));
|
||||
|
||||
auto cover = std::make_unique<Wt::WImage>();
|
||||
cover->setImageLink(LmsApp->getImageResource()->getReleaseUrl(release.id(), ImageResource::Size::Small));
|
||||
cover->setStyleClass("Lms-cover");
|
||||
anchor->setImage(std::move(cover));
|
||||
}
|
||||
|
||||
entry->bindWidget("name", LmsApplication::createReleaseAnchor(release));
|
||||
|
||||
auto artists {release->getReleaseArtists()};
|
||||
if (artists.empty())
|
||||
artists = release->getArtists();
|
||||
|
||||
bool isSameArtist {(std::find(std::cbegin(artists), std::cend(artists), artist) != artists.end())};
|
||||
|
||||
if (artists.size() > 1)
|
||||
{
|
||||
entry->setCondition("if-has-artist", true);
|
||||
entry->bindNew<Wt::WText>("artist", Wt::WString::tr("Lms.Explore.various-artists"));
|
||||
}
|
||||
else if (artists.size() == 1 && !isSameArtist)
|
||||
{
|
||||
entry->setCondition("if-has-artist", true);
|
||||
entry->bindWidget("artist", LmsApplication::createArtistAnchor(artists.front()));
|
||||
}
|
||||
|
||||
std::optional<int> year {release->getReleaseYear()};
|
||||
if (year)
|
||||
{
|
||||
entry->setCondition("if-has-year", true);
|
||||
entry->bindInt("year", *year);
|
||||
|
||||
std::optional<int> originalYear {release->getReleaseYear(true)};
|
||||
if (originalYear && *originalYear != *year)
|
||||
{
|
||||
entry->setCondition("if-has-orig-year", true);
|
||||
entry->bindInt("orig-year", *originalYear);
|
||||
}
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
void
|
||||
Artist::refreshSimilarArtists(const std::vector<Database::IdType>& similarArtistsId)
|
||||
{
|
||||
|
||||
@@ -140,7 +140,7 @@ Artists::getArtists(std::optional<Range> range, bool& moreResults)
|
||||
if (modeLimit)
|
||||
{
|
||||
if (range)
|
||||
range->limit = std::min(*modeLimit - range->offset, range->offset + range->limit);
|
||||
range->limit = std::min(*modeLimit - range->offset, range->limit);
|
||||
else
|
||||
range = Range {0, *modeLimit};
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <Wt/WImage.h>
|
||||
#include <Wt/WText.h>
|
||||
|
||||
#include "database/Artist.hpp"
|
||||
#include "database/Release.hpp"
|
||||
#include "resource/ImageResource.hpp"
|
||||
|
||||
@@ -35,7 +36,7 @@ namespace UserInterface::ReleaseListHelpers
|
||||
|
||||
static
|
||||
std::unique_ptr<Wt::WTemplate>
|
||||
createEntryInternal(const Database::Release::pointer& release, const std::string& templateKey)
|
||||
createEntryInternal(const Release::pointer& release, const std::string& templateKey, const Artist::pointer& artist, const bool showYear)
|
||||
{
|
||||
auto entry {std::make_unique<Wt::WTemplate>(Wt::WString::tr(templateKey))};
|
||||
|
||||
@@ -43,7 +44,7 @@ namespace UserInterface::ReleaseListHelpers
|
||||
|
||||
Wt::WAnchor* anchor = entry->bindWidget("cover", LmsApplication::createReleaseAnchor(release, false));
|
||||
auto cover = std::make_unique<Wt::WImage>();
|
||||
cover->setImageLink(LmsApp->getImageResource()->getReleaseUrl(release.id(), ImageResource::Size::Small));
|
||||
cover->setImageLink(LmsApp->getImageResource()->getReleaseUrl(release.id(), ImageResource::Size::Large));
|
||||
cover->setStyleClass("Lms-cover");
|
||||
anchor->setImage(std::move(cover));
|
||||
|
||||
@@ -51,30 +52,56 @@ namespace UserInterface::ReleaseListHelpers
|
||||
if (artists.empty())
|
||||
artists = release->getArtists();
|
||||
|
||||
bool isSameArtist {(std::find(std::cbegin(artists), std::cend(artists), artist) != artists.end())};
|
||||
|
||||
if (artists.size() > 1)
|
||||
{
|
||||
entry->setCondition("if-has-artist", true);
|
||||
entry->bindNew<Wt::WText>("artist-name", Wt::WString::tr("Lms.Explore.various-artists"));
|
||||
}
|
||||
else if (artists.size() == 1)
|
||||
else if (artists.size() == 1 && !isSameArtist)
|
||||
{
|
||||
entry->setCondition("if-has-artist", true);
|
||||
entry->bindWidget("artist-name", LmsApplication::createArtistAnchor(artists.front()));
|
||||
}
|
||||
|
||||
if (showYear)
|
||||
{
|
||||
if (std::optional<int> year {release->getReleaseYear()})
|
||||
{
|
||||
entry->setCondition("if-has-year", true);
|
||||
|
||||
std::string strYear {std::to_string(*year)};
|
||||
|
||||
std::optional<int> originalYear {release->getReleaseYear(true)};
|
||||
if (originalYear && *originalYear != *year)
|
||||
{
|
||||
strYear += " (" + std::to_string(*originalYear) + ")";
|
||||
}
|
||||
|
||||
entry->bindString("year", strYear);
|
||||
}
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
std::unique_ptr<Wt::WTemplate>
|
||||
createEntry(const Release::pointer& release)
|
||||
createEntry(const Release::pointer& release, const Artist::pointer& artist, bool showYear)
|
||||
{
|
||||
return createEntryInternal(release, "Lms.Explore.Releases.template.entry");
|
||||
return createEntryInternal(release, "Lms.Explore.Releases.template.entry-grid", artist, showYear);
|
||||
}
|
||||
|
||||
std::unique_ptr<Wt::WTemplate>
|
||||
createEntrySmall(const Release::pointer& release)
|
||||
createEntry(const Release::pointer& release)
|
||||
{
|
||||
return createEntryInternal(release, "Lms.Explore.Releases.template.entry-small");
|
||||
return createEntry(release, Artist::pointer {}, false /*year*/);
|
||||
}
|
||||
|
||||
std::unique_ptr<Wt::WTemplate>
|
||||
createEntryForArtist(const Wt::Dbo::ptr<Database::Release>& release, const Wt::Dbo::ptr<Database::Artist>& artist)
|
||||
{
|
||||
return createEntry(release, artist, true);
|
||||
}
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
@@ -25,13 +25,13 @@
|
||||
|
||||
namespace Database
|
||||
{
|
||||
class Artist;
|
||||
class Release;
|
||||
}
|
||||
|
||||
namespace UserInterface::ReleaseListHelpers
|
||||
{
|
||||
|
||||
std::unique_ptr<Wt::WTemplate> createEntrySmall(const Wt::Dbo::ptr<Database::Release>& release);
|
||||
std::unique_ptr<Wt::WTemplate> createEntry(const Wt::Dbo::ptr<Database::Release>& release);
|
||||
std::unique_ptr<Wt::WTemplate> createEntryForArtist(const Wt::Dbo::ptr<Database::Release>& release, const Wt::Dbo::ptr<Database::Artist>& artist);
|
||||
} // namespace UserInterface
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ Release::refreshView()
|
||||
if (!releaseId)
|
||||
return;
|
||||
|
||||
const std::vector<Database::IdType> similarReleasesIds {ServiceProvider<Recommendation::IEngine>::get()->getSimilarReleases(LmsApp->getDbSession(), *releaseId, 5)};
|
||||
const std::vector<Database::IdType> similarReleasesIds {ServiceProvider<Recommendation::IEngine>::get()->getSimilarReleases(LmsApp->getDbSession(), *releaseId, 6)};
|
||||
|
||||
auto transaction {LmsApp->getDbSession().createSharedTransaction()};
|
||||
|
||||
@@ -244,12 +244,12 @@ Release::refreshView()
|
||||
|
||||
entry->bindString("duration", trackDurationToString(track->getDuration()), Wt::TextFormat::Plain);
|
||||
|
||||
LmsApp->getMediaPlayer()->trackLoaded.connect(entry, [=] (Database::IdType loadedTrackId)
|
||||
LmsApp->getMediaPlayer().trackLoaded.connect(entry, [=] (Database::IdType loadedTrackId)
|
||||
{
|
||||
entry->bindString("is-playing", loadedTrackId == trackId ? "Lms-entry-playing" : "");
|
||||
});
|
||||
|
||||
if (auto trackIdLoaded {LmsApp->getMediaPlayer()->getTrackLoaded()})
|
||||
if (auto trackIdLoaded {LmsApp->getMediaPlayer().getTrackLoaded()})
|
||||
{
|
||||
if (*trackIdLoaded == trackId)
|
||||
entry->bindString("is-playing", "Lms-entry-playing");
|
||||
@@ -313,7 +313,7 @@ Release::refreshSimilarReleases(const std::vector<Database::IdType>& similarRele
|
||||
if (!similarRelease)
|
||||
continue;
|
||||
|
||||
similarReleasesContainer->addWidget(ReleaseListHelpers::createEntrySmall(similarRelease));
|
||||
similarReleasesContainer->addWidget(ReleaseListHelpers::createEntry(similarRelease));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -111,8 +111,9 @@ Releases::addSome()
|
||||
bool moreResults {};
|
||||
|
||||
auto transaction {LmsApp->getDbSession().createSharedTransaction()};
|
||||
const auto releases {getReleases(Range {static_cast<std::size_t>(_container->count()), batchSize}, moreResults)};
|
||||
|
||||
for (const Release::pointer& release : getReleases(Range {static_cast<std::size_t>(_container->count()), batchSize}, moreResults))
|
||||
for (const Release::pointer& release : releases)
|
||||
{
|
||||
_container->addWidget(ReleaseListHelpers::createEntry(release));
|
||||
}
|
||||
@@ -154,7 +155,7 @@ Releases::getReleases(std::optional<Range> range, bool& moreResults)
|
||||
if (modeLimit)
|
||||
{
|
||||
if (range)
|
||||
range->limit = std::min(*modeLimit - range->offset, range->offset + range->limit);
|
||||
range->limit = std::min(*modeLimit - range->offset, range->limit);
|
||||
else
|
||||
range = Range {0, *modeLimit};
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ class Releases : public Wt::WTemplate
|
||||
std::vector<Database::IdType> getAllReleases();
|
||||
|
||||
static constexpr Mode defaultMode {Mode::Random};
|
||||
static constexpr std::size_t batchSize {20};
|
||||
static constexpr std::size_t batchSize {18};
|
||||
static inline std::unordered_map<Mode, std::optional<std::size_t>> maxItemsPerMode
|
||||
{
|
||||
{Mode::Random, batchSize * 6},
|
||||
|
||||
@@ -29,63 +29,15 @@
|
||||
#include "resource/ImageResource.hpp"
|
||||
#include "ArtistListHelpers.hpp"
|
||||
#include "Filters.hpp"
|
||||
#include "ReleaseListHelpers.hpp"
|
||||
#include "LmsApplication.hpp"
|
||||
#include "ReleaseListHelpers.hpp"
|
||||
#include "TrackListHelpers.hpp"
|
||||
|
||||
static constexpr std::size_t maxEntries {5};
|
||||
static constexpr std::size_t maxEntries {6};
|
||||
|
||||
namespace UserInterface
|
||||
{
|
||||
|
||||
static
|
||||
std::unique_ptr<Wt::WTemplate>
|
||||
createTrack(const Database::Track::pointer& track)
|
||||
{
|
||||
auto entry {std::make_unique<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Search.template.track-entry"))};
|
||||
|
||||
entry->bindString("name", Wt::WString::fromUTF8(track->getName()), Wt::TextFormat::Plain);
|
||||
|
||||
auto artists {track->getArtists()};
|
||||
auto release {track->getRelease()};
|
||||
const auto trackId {track.id()};
|
||||
|
||||
if (!artists.empty() || release)
|
||||
entry->setCondition("if-has-artists-or-release", true);
|
||||
|
||||
if (!artists.empty())
|
||||
{
|
||||
entry->setCondition("if-has-artists", true);
|
||||
|
||||
Wt::WContainerWidget* artistContainer {entry->bindNew<Wt::WContainerWidget>("artists")};
|
||||
for (const auto& artist : artists)
|
||||
{
|
||||
Wt::WTemplate* a {artistContainer->addNew<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Tracks.template.entry-artist"))};
|
||||
a->bindWidget("artist", LmsApplication::createArtistAnchor(artist));
|
||||
}
|
||||
}
|
||||
|
||||
if (track->getRelease())
|
||||
{
|
||||
entry->setCondition("if-has-release", true);
|
||||
entry->bindWidget("release", LmsApplication::createReleaseAnchor(track->getRelease()));
|
||||
{
|
||||
Wt::WAnchor* anchor = entry->bindWidget("cover", LmsApplication::createReleaseAnchor(release, false));
|
||||
auto cover = std::make_unique<Wt::WImage>();
|
||||
cover->setImageLink(LmsApp->getImageResource()->getReleaseUrl(release.id(), ImageResource::Size::Small));
|
||||
cover->setStyleClass("Lms-cover");
|
||||
anchor->setImage(std::move(cover));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto cover = entry->bindNew<Wt::WImage>("cover");
|
||||
cover->setImageLink(LmsApp->getImageResource()->getTrackUrl(trackId, ImageResource::Size::Small));
|
||||
cover->setStyleClass("Lms-cover");
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
SearchView::SearchView(Filters* filters)
|
||||
: Wt::WTemplate {Wt::WString::tr("Lms.Explore.Search.template")}
|
||||
, _filters {filters}
|
||||
@@ -175,7 +127,7 @@ namespace UserInterface
|
||||
auto* container {bindNew<Wt::WContainerWidget>("tracks")};
|
||||
|
||||
for (const Database::Track::pointer& track : tracks)
|
||||
container->addWidget(createTrack(track));
|
||||
container->addWidget(TrackListHelpers::createEntry(track));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 "TrackListHelpers.hpp"
|
||||
|
||||
#include <Wt/WAnchor.h>
|
||||
#include <Wt/WContainerWidget.h>
|
||||
#include <Wt/WImage.h>
|
||||
#include <Wt/WText.h>
|
||||
|
||||
#include "database/Artist.hpp"
|
||||
#include "database/Release.hpp"
|
||||
#include "database/Track.hpp"
|
||||
#include "resource/ImageResource.hpp"
|
||||
#include "LmsApplication.hpp"
|
||||
#include "MediaPlayer.hpp"
|
||||
#include "PlayQueue.hpp"
|
||||
#include "TrackStringUtils.hpp"
|
||||
|
||||
using namespace Database;
|
||||
|
||||
namespace UserInterface::TrackListHelpers
|
||||
{
|
||||
std::unique_ptr<Wt::WTemplate>
|
||||
createEntry(const Wt::Dbo::ptr<Database::Track>& track)
|
||||
{
|
||||
auto entry {std::make_unique<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Tracks.template.entry"))};
|
||||
auto* entryPtr {entry.get()};
|
||||
|
||||
entry->bindString("name", Wt::WString::fromUTF8(track->getName()), Wt::TextFormat::Plain);
|
||||
|
||||
const auto artists {track->getArtists()};
|
||||
const Release::pointer release {track->getRelease()};
|
||||
const IdType trackId {track.id()};
|
||||
|
||||
if (!artists.empty() || release)
|
||||
entry->setCondition("if-has-artists-or-release", true);
|
||||
|
||||
if (!artists.empty())
|
||||
{
|
||||
entry->setCondition("if-has-artists", true);
|
||||
|
||||
Wt::WContainerWidget* artistContainer {entry->bindNew<Wt::WContainerWidget>("artists")};
|
||||
for (const Artist::pointer& artist : artists)
|
||||
{
|
||||
Wt::WTemplate* a {artistContainer->addNew<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Tracks.template.entry-artist"))};
|
||||
a->bindWidget("artist", LmsApplication::createArtistAnchor(artist));
|
||||
}
|
||||
}
|
||||
|
||||
if (track->getRelease())
|
||||
{
|
||||
entry->setCondition("if-has-release", true);
|
||||
entry->bindWidget("release", LmsApplication::createReleaseAnchor(track->getRelease()));
|
||||
{
|
||||
Wt::WAnchor* anchor = entry->bindWidget("cover", LmsApplication::createReleaseAnchor(release, false));
|
||||
auto cover = std::make_unique<Wt::WImage>();
|
||||
cover->setImageLink(LmsApp->getImageResource()->getReleaseUrl(release.id(), ImageResource::Size::Large));
|
||||
cover->setStyleClass("Lms-cover");
|
||||
anchor->setImage(std::move(cover));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto cover = entry->bindNew<Wt::WImage>("cover");
|
||||
cover->setImageLink(LmsApp->getImageResource()->getTrackUrl(trackId, ImageResource::Size::Large));
|
||||
cover->setStyleClass("Lms-cover");
|
||||
}
|
||||
|
||||
entry->bindString("duration", trackDurationToString(track->getDuration()), Wt::TextFormat::Plain);
|
||||
|
||||
Wt::WText* playBtn = entry->bindNew<Wt::WText>("play-btn", Wt::WString::tr("Lms.Explore.template.play-btn"), Wt::TextFormat::XHTML);
|
||||
playBtn->clicked().connect([=]
|
||||
{
|
||||
LmsApp->getPlayQueue().processTracks(PlayQueueAction::Play, {trackId});
|
||||
});
|
||||
|
||||
Wt::WText* addBtn = entry->bindNew<Wt::WText>("add-btn", Wt::WString::tr("Lms.Explore.template.add-btn"), Wt::TextFormat::XHTML);
|
||||
addBtn->clicked().connect([=]
|
||||
{
|
||||
LmsApp->getPlayQueue().processTracks(PlayQueueAction::AddLast, {trackId});
|
||||
});
|
||||
|
||||
LmsApp->getMediaPlayer().trackLoaded.connect(entryPtr, [=] (Database::IdType loadedTrackId)
|
||||
{
|
||||
entryPtr->bindString("is-playing", loadedTrackId == trackId ? "Lms-entry-playing" : "");
|
||||
});
|
||||
|
||||
if (auto trackIdLoaded {LmsApp->getMediaPlayer().getTrackLoaded()})
|
||||
{
|
||||
if (*trackIdLoaded == trackId)
|
||||
entry->bindString("is-playing", "Lms-entry-playing");
|
||||
}
|
||||
else
|
||||
entry->bindString("is-playing", "");
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 <memory>
|
||||
|
||||
#include <Wt/WTemplate.h>
|
||||
|
||||
namespace Database
|
||||
{
|
||||
class Track;
|
||||
}
|
||||
|
||||
namespace UserInterface::TrackListHelpers
|
||||
{
|
||||
std::unique_ptr<Wt::WTemplate> createEntry(const Wt::Dbo::ptr<Database::Track>& track);
|
||||
} // namespace UserInterface
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "Filters.hpp"
|
||||
#include "LmsApplication.hpp"
|
||||
#include "MediaPlayer.hpp"
|
||||
#include "TrackListHelpers.hpp"
|
||||
#include "TrackStringUtils.hpp"
|
||||
|
||||
using namespace Database;
|
||||
@@ -145,7 +146,7 @@ Tracks::getTracks(std::optional<Range> range, bool& moreResults)
|
||||
if (modeLimit)
|
||||
{
|
||||
if (range)
|
||||
range->limit = std::min(*modeLimit - range->offset, range->offset + range->limit);
|
||||
range->limit = std::min(*modeLimit - range->offset, range->limit);
|
||||
else
|
||||
range = Range {0, *modeLimit};
|
||||
}
|
||||
@@ -194,82 +195,6 @@ Tracks::getAllTracks()
|
||||
return res;
|
||||
}
|
||||
|
||||
std::unique_ptr<Wt::WTemplate>
|
||||
Tracks::createEntry(const Track::pointer& track)
|
||||
{
|
||||
auto entry {std::make_unique<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Tracks.template.entry"))};
|
||||
auto* entryPtr {entry.get()};
|
||||
|
||||
entry->bindString("name", Wt::WString::fromUTF8(track->getName()), Wt::TextFormat::Plain);
|
||||
|
||||
auto artists {track->getArtists()};
|
||||
auto release {track->getRelease()};
|
||||
const auto trackId {track.id()};
|
||||
|
||||
if (!artists.empty() || release)
|
||||
entry->setCondition("if-has-artists-or-release", true);
|
||||
|
||||
if (!artists.empty())
|
||||
{
|
||||
entry->setCondition("if-has-artists", true);
|
||||
|
||||
Wt::WContainerWidget* artistContainer {entry->bindNew<Wt::WContainerWidget>("artists")};
|
||||
for (const auto& artist : artists)
|
||||
{
|
||||
Wt::WTemplate* a {artistContainer->addNew<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Tracks.template.entry-artist"))};
|
||||
a->bindWidget("artist", LmsApplication::createArtistAnchor(artist));
|
||||
}
|
||||
}
|
||||
|
||||
if (track->getRelease())
|
||||
{
|
||||
entry->setCondition("if-has-release", true);
|
||||
entry->bindWidget("release", LmsApplication::createReleaseAnchor(track->getRelease()));
|
||||
{
|
||||
Wt::WAnchor* anchor = entry->bindWidget("cover", LmsApplication::createReleaseAnchor(release, false));
|
||||
auto cover = std::make_unique<Wt::WImage>();
|
||||
cover->setImageLink(LmsApp->getImageResource()->getReleaseUrl(release.id(), ImageResource::Size::Small));
|
||||
cover->setStyleClass("Lms-cover");
|
||||
anchor->setImage(std::move(cover));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto cover = entry->bindNew<Wt::WImage>("cover");
|
||||
cover->setImageLink(LmsApp->getImageResource()->getTrackUrl(trackId, ImageResource::Size::Small));
|
||||
cover->setStyleClass("Lms-cover");
|
||||
}
|
||||
|
||||
entry->bindString("duration", trackDurationToString(track->getDuration()), Wt::TextFormat::Plain);
|
||||
|
||||
Wt::WText* playBtn = entry->bindNew<Wt::WText>("play-btn", Wt::WString::tr("Lms.Explore.template.play-btn"), Wt::TextFormat::XHTML);
|
||||
playBtn->clicked().connect([=]
|
||||
{
|
||||
tracksAction.emit(PlayQueueAction::Play, {trackId});
|
||||
});
|
||||
|
||||
Wt::WText* addBtn = entry->bindNew<Wt::WText>("add-btn", Wt::WString::tr("Lms.Explore.template.add-btn"), Wt::TextFormat::XHTML);
|
||||
addBtn->clicked().connect([=]
|
||||
{
|
||||
tracksAction.emit(PlayQueueAction::AddLast, {trackId});
|
||||
});
|
||||
|
||||
LmsApp->getMediaPlayer()->trackLoaded.connect(entryPtr, [=] (Database::IdType loadedTrackId)
|
||||
{
|
||||
entryPtr->bindString("is-playing", loadedTrackId == trackId ? "Lms-entry-playing" : "");
|
||||
});
|
||||
|
||||
if (auto trackIdLoaded {LmsApp->getMediaPlayer()->getTrackLoaded()})
|
||||
{
|
||||
if (*trackIdLoaded == trackId)
|
||||
entry->bindString("is-playing", "Lms-entry-playing");
|
||||
}
|
||||
else
|
||||
entry->bindString("is-playing", "");
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
void
|
||||
Tracks::addSome()
|
||||
{
|
||||
@@ -278,7 +203,7 @@ Tracks::addSome()
|
||||
bool moreResults;
|
||||
for (const Track::pointer& track : getTracks(Range {static_cast<std::size_t>(_tracksContainer->count()), batchSize}, moreResults))
|
||||
{
|
||||
_tracksContainer->addWidget(createEntry(track));
|
||||
_tracksContainer->addWidget(TrackListHelpers::createEntry(track));
|
||||
}
|
||||
|
||||
_showMore->setHidden(!moreResults);
|
||||
|
||||
@@ -59,7 +59,6 @@ class Tracks : public Wt::WTemplate
|
||||
void refreshView(Mode mode);
|
||||
void addSome();
|
||||
|
||||
std::unique_ptr<Wt::WTemplate> createEntry(const Wt::Dbo::ptr<Database::Track>& track);
|
||||
std::vector<Wt::Dbo::ptr<Database::Track>> getRandomTracks(std::optional<Database::Range> range, bool& moreResults);
|
||||
std::vector<Wt::Dbo::ptr<Database::Track>> getTracks(std::optional<Database::Range> range, bool& moreResults);
|
||||
std::vector<Database::IdType> getAllTracks();
|
||||
|
||||
Reference in New Issue
Block a user