Changed the add button to a contextual menu
This commit is contained in:
@@ -61,9 +61,9 @@ The Subsonic API is enabled by default.
|
|||||||
__Note__: since _LMS_ stores hashed and salted passwords, it cannot handle the __token authentication__ method defined from version 1.13.0.
|
__Note__: since _LMS_ stores hashed and salted passwords, it cannot handle the __token authentication__ method defined from version 1.13.0.
|
||||||
|
|
||||||
## Keyboard shortcuts
|
## Keyboard shortcuts
|
||||||
* Play/pause: Space
|
* Play/pause: <kbd>Space</bbd>
|
||||||
* Previous track: Ctrl + Left
|
* Previous track: <kbd>Ctrl</kbd> + <kbd>Left</kbd>
|
||||||
* Next track: Ctrl + Right
|
* Next track: <kbd>Ctrl</kbd> + <kbd>Right</kbd>
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -6,18 +6,20 @@
|
|||||||
<div class="Lms-header">
|
<div class="Lms-header">
|
||||||
<h2><div class="Lms-artistname text-primary">${name}</div></h2>
|
<h2><div class="Lms-artistname text-primary">${name}</div></h2>
|
||||||
${clusters}
|
${clusters}
|
||||||
${play-btn}${add-btn}
|
${play-btn}${more-btn}
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
${releases class="Lms-explore-artist-entries-container"}
|
${releases class="Lms-explore-artist-entries-container"}
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
${<if-has-similar-artists>}
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<div class="well well-sm">
|
<div class="well well-sm">
|
||||||
<h3>${tr:Lms.Explore.Artist.similar-artists}</h3>
|
<h3>${tr:Lms.Explore.Artist.similar-artists}</h3>
|
||||||
${similar-artists}
|
${similar-artists}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
${</if-has-similar-artists>}
|
||||||
${<if-has-mbid>}
|
${<if-has-mbid>}
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<div class="well well-sm">
|
<div class="well well-sm">
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
${contents class="Lms-explore-contents"}
|
${contents class="Lms-explore-contents"}
|
||||||
</message>
|
</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.more-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.play-btn"><i class="fa fa-play-circle Lms-explore-btn Lms-control-btn"></i></message>
|
||||||
|
|
||||||
<message id="Lms.Explore.template.filters">
|
<message id="Lms.Explore.template.filters">
|
||||||
|
|||||||
@@ -121,6 +121,8 @@
|
|||||||
<message id="Lms.Explore.most-played">Most played</message>
|
<message id="Lms.Explore.most-played">Most played</message>
|
||||||
<message id="Lms.Explore.musicbrainz-artist">MusicBrainz Artist</message>
|
<message id="Lms.Explore.musicbrainz-artist">MusicBrainz Artist</message>
|
||||||
<message id="Lms.Explore.musicbrainz-release">MusicBrainz Release</message>
|
<message id="Lms.Explore.musicbrainz-release">MusicBrainz Release</message>
|
||||||
|
<message id="Lms.Explore.play-last">Play last</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-played">Recently played</message>
|
<message id="Lms.Explore.recently-played">Recently played</message>
|
||||||
@@ -157,11 +159,8 @@
|
|||||||
<plural case="0">Added {1} track</plural>
|
<plural case="0">Added {1} track</plural>
|
||||||
<plural case="1">Added {1} tracks</plural>
|
<plural case="1">Added {1} tracks</plural>
|
||||||
</message>
|
</message>
|
||||||
<message id="Lms.PlayQueue.nb-tracks-playing">
|
|
||||||
<plural case="0">Playing {1} track</plural>
|
|
||||||
<plural case="1">Playing {1} tracks</plural>
|
|
||||||
</message>
|
|
||||||
<message id="Lms.PlayQueue.playqueue">Play Queue</message>
|
<message id="Lms.PlayQueue.playqueue">Play Queue</message>
|
||||||
|
<message id="Lms.PlayQueue.playqueue-full">Play Queue full!</message>
|
||||||
<message id="Lms.PlayQueue.radio-mode">Radio mode</message>
|
<message id="Lms.PlayQueue.radio-mode">Radio mode</message>
|
||||||
<message id="Lms.PlayQueue.repeat">Repeat</message>
|
<message id="Lms.PlayQueue.repeat">Repeat</message>
|
||||||
<message id="Lms.PlayQueue.shuffle">Shuffle</message>
|
<message id="Lms.PlayQueue.shuffle">Shuffle</message>
|
||||||
|
|||||||
@@ -121,6 +121,8 @@
|
|||||||
<message id="Lms.Explore.most-played">Plus joués</message>
|
<message id="Lms.Explore.most-played">Plus joués</message>
|
||||||
<message id="Lms.Explore.musicbrainz-artist">MusicBrainz Artist</message>
|
<message id="Lms.Explore.musicbrainz-artist">MusicBrainz Artist</message>
|
||||||
<message id="Lms.Explore.musicbrainz-release">MusicBrainz Release</message>
|
<message id="Lms.Explore.musicbrainz-release">MusicBrainz Release</message>
|
||||||
|
<message id="Lms.Explore.play-last">Jouer en dernier</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-played">Joués récemment</message>
|
<message id="Lms.Explore.recently-played">Joués récemment</message>
|
||||||
@@ -157,11 +159,8 @@
|
|||||||
<plural case="0">{1} piste ajoutée</plural>
|
<plural case="0">{1} piste ajoutée</plural>
|
||||||
<plural case="1">{1} pistes ajoutées</plural>
|
<plural case="1">{1} pistes ajoutées</plural>
|
||||||
</message>
|
</message>
|
||||||
<message id="Lms.PlayQueue.nb-tracks-playing">
|
|
||||||
<plural case="0">{1} piste ajoutée</plural>
|
|
||||||
<plural case="1">{1} pistes ajoutées</plural>
|
|
||||||
</message>
|
|
||||||
<message id="Lms.PlayQueue.playqueue">Liste de lecture</message>
|
<message id="Lms.PlayQueue.playqueue">Liste de lecture</message>
|
||||||
|
<message id="Lms.PlayQueue.playqueue-full">Liste de lecture pleine!</message>
|
||||||
<message id="Lms.PlayQueue.radio-mode">Mode radio</message>
|
<message id="Lms.PlayQueue.radio-mode">Mode radio</message>
|
||||||
<message id="Lms.PlayQueue.repeat">Repéter</message>
|
<message id="Lms.PlayQueue.repeat">Repéter</message>
|
||||||
<message id="Lms.PlayQueue.shuffle">Mélanger</message>
|
<message id="Lms.PlayQueue.shuffle">Mélanger</message>
|
||||||
|
|||||||
+4
-2
@@ -17,12 +17,13 @@
|
|||||||
${<if-has-artist>}<h4>${artist class="Lms-artistname text-muted"}</h4>${</if-has-artist>}
|
${<if-has-artist>}<h4>${artist class="Lms-artistname text-muted"}</h4>${</if-has-artist>}
|
||||||
<div class="text-muted"><small>${<if-has-year>}${year}${</if-has-year>}${<if-has-orig-year>} (${orig-year})${</if-has-orig-year>}</small></div>
|
<div class="text-muted"><small>${<if-has-year>}${year}${</if-has-year>}${<if-has-orig-year>} (${orig-year})${</if-has-orig-year>}</small></div>
|
||||||
${clusters}
|
${clusters}
|
||||||
${play-btn}${add-btn}
|
${play-btn}${more-btn}
|
||||||
</div>
|
</div>
|
||||||
${container}
|
${container}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
${<if-has-similar-releases>}
|
||||||
<div class="col-xs-12 col-md-8">
|
<div class="col-xs-12 col-md-8">
|
||||||
<div class="well well-sm">
|
<div class="well well-sm">
|
||||||
<h3>${tr:Lms.Explore.Release.similar-releases}</h3>
|
<h3>${tr:Lms.Explore.Release.similar-releases}</h3>
|
||||||
@@ -31,6 +32,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
${</if-has-similar-releases>}
|
||||||
${<if-has-copyright>}
|
${<if-has-copyright>}
|
||||||
<div class="col-xs-12 col-md-4">
|
<div class="col-xs-12 col-md-4">
|
||||||
<div class="well well-sm">
|
<div class="well well-sm">
|
||||||
@@ -66,7 +68,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-xs-3">
|
<div class="col-xs-3">
|
||||||
<div class="Lms-explore-release-entry-controls">
|
<div class="Lms-explore-release-entry-controls">
|
||||||
${play-btn}${add-btn}
|
${play-btn}${more-btn}
|
||||||
</div>
|
</div>
|
||||||
<div class="Lms-explore-release-entry-track-duration text-muted">
|
<div class="Lms-explore-release-entry-track-duration text-muted">
|
||||||
${duration}
|
${duration}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<div class="Lms-header">
|
<div class="Lms-header">
|
||||||
<h2>${tr:Lms.Explore.releases}</h2>
|
<h2>${tr:Lms.Explore.releases}</h2>
|
||||||
${mode class="nav nav-pills Lms-explore-mode-container"}
|
${mode class="nav nav-pills Lms-explore-mode-container"}
|
||||||
${play-btn}${add-btn}
|
${play-btn}${more-btn}
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
${releases}
|
${releases}
|
||||||
|
|||||||
+2
-2
@@ -9,7 +9,7 @@
|
|||||||
<div class="Lms-header">
|
<div class="Lms-header">
|
||||||
<h2>${tr:Lms.Explore.tracks}</h2>
|
<h2>${tr:Lms.Explore.tracks}</h2>
|
||||||
${mode class="nav nav-pills Lms-explore-mode-container"}
|
${mode class="nav nav-pills Lms-explore-mode-container"}
|
||||||
${play-btn}${add-btn}
|
${play-btn}${more-btn}
|
||||||
</div>
|
</div>
|
||||||
${tracks}
|
${tracks}
|
||||||
${loading-indicator class="Lms-horizontal-center Lms-loading-indicator"}
|
${loading-indicator class="Lms-horizontal-center Lms-loading-indicator"}
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-xs-2 col-sm-2 col-md-2">
|
<div class="col-xs-2 col-sm-2 col-md-2">
|
||||||
<div class="Lms-explore-tracks-entry-controls">
|
<div class="Lms-explore-tracks-entry-controls">
|
||||||
${play-btn}${add-btn}
|
${play-btn}${more-btn}
|
||||||
</div>
|
</div>
|
||||||
<div class="Lms-explore-tracks-entry-duration text-muted">
|
<div class="Lms-explore-tracks-entry-duration text-muted">
|
||||||
${duration}
|
${duration}
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ namespace Av
|
|||||||
static constexpr std::size_t _chunkSize {262144};
|
static constexpr std::size_t _chunkSize {262144};
|
||||||
const std::filesystem::path _trackPath;
|
const std::filesystem::path _trackPath;
|
||||||
Transcoder _transcoder;
|
Transcoder _transcoder;
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ add_executable(lms
|
|||||||
ui/explore/Explore.cpp
|
ui/explore/Explore.cpp
|
||||||
ui/explore/Filters.cpp
|
ui/explore/Filters.cpp
|
||||||
ui/explore/ReleaseListHelpers.cpp
|
ui/explore/ReleaseListHelpers.cpp
|
||||||
|
ui/explore/ReleasePopup.cpp
|
||||||
ui/explore/ReleasesView.cpp
|
ui/explore/ReleasesView.cpp
|
||||||
ui/explore/ReleaseView.cpp
|
ui/explore/ReleaseView.cpp
|
||||||
ui/explore/SearchView.cpp
|
ui/explore/SearchView.cpp
|
||||||
|
|||||||
@@ -35,7 +35,6 @@
|
|||||||
#include "database/User.hpp"
|
#include "database/User.hpp"
|
||||||
#include "explore/Explore.hpp"
|
#include "explore/Explore.hpp"
|
||||||
#include "explore/Filters.hpp"
|
#include "explore/Filters.hpp"
|
||||||
#include "explore/SearchView.hpp"
|
|
||||||
#include "utils/Logger.hpp"
|
#include "utils/Logger.hpp"
|
||||||
#include "utils/Service.hpp"
|
#include "utils/Service.hpp"
|
||||||
#include "utils/String.hpp"
|
#include "utils/String.hpp"
|
||||||
@@ -308,6 +307,13 @@ LmsApplication::createCluster(Database::Cluster::pointer cluster, bool canDelete
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Wt::WPopupMenu*
|
||||||
|
LmsApplication::createPopupMenu()
|
||||||
|
{
|
||||||
|
_popupMenu = std::make_unique<Wt::WPopupMenu>();
|
||||||
|
return _popupMenu.get();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
LmsApplication::handleException(LmsApplicationException& e)
|
LmsApplication::handleException(LmsApplicationException& e)
|
||||||
{
|
{
|
||||||
@@ -334,7 +340,6 @@ enum IdxRoot
|
|||||||
{
|
{
|
||||||
IdxExplore = 0,
|
IdxExplore = 0,
|
||||||
IdxPlayQueue,
|
IdxPlayQueue,
|
||||||
IdxSearch,
|
|
||||||
IdxSettings,
|
IdxSettings,
|
||||||
IdxAdminDatabase,
|
IdxAdminDatabase,
|
||||||
IdxAdminUsers,
|
IdxAdminUsers,
|
||||||
@@ -355,9 +360,9 @@ handlePathChange(Wt::WStackedWidget* stack, bool isAdmin)
|
|||||||
{ "/artist", IdxExplore, false },
|
{ "/artist", IdxExplore, false },
|
||||||
{ "/releases", IdxExplore, false },
|
{ "/releases", IdxExplore, false },
|
||||||
{ "/release", IdxExplore, false },
|
{ "/release", IdxExplore, false },
|
||||||
|
{ "/search", IdxExplore, false },
|
||||||
{ "/tracks", IdxExplore, false },
|
{ "/tracks", IdxExplore, false },
|
||||||
{ "/playqueue", IdxPlayQueue, false },
|
{ "/playqueue", IdxPlayQueue, false },
|
||||||
{ "/search", IdxSearch, false },
|
|
||||||
{ "/settings", IdxSettings, false },
|
{ "/settings", IdxSettings, false },
|
||||||
{ "/admin/database", IdxAdminDatabase, true },
|
{ "/admin/database", IdxAdminDatabase, true },
|
||||||
{ "/admin/users", IdxAdminUsers, true },
|
{ "/admin/users", IdxAdminUsers, true },
|
||||||
@@ -471,7 +476,6 @@ LmsApplication::createHome()
|
|||||||
|
|
||||||
Explore* explore = mainStack->addNew<Explore>(filters);
|
Explore* explore = mainStack->addNew<Explore>(filters);
|
||||||
_playQueue = mainStack->addNew<PlayQueue>();
|
_playQueue = mainStack->addNew<PlayQueue>();
|
||||||
auto* search {mainStack->addNew<SearchView>(filters)};
|
|
||||||
mainStack->addNew<SettingsView>();
|
mainStack->addNew<SettingsView>();
|
||||||
|
|
||||||
searchEdit->enterPressed().connect([=]
|
searchEdit->enterPressed().connect([=]
|
||||||
@@ -482,7 +486,7 @@ LmsApplication::createHome()
|
|||||||
searchEdit->textInput().connect([=]
|
searchEdit->textInput().connect([=]
|
||||||
{
|
{
|
||||||
setInternalPath("/search", true);
|
setInternalPath("/search", true);
|
||||||
search->refreshView(searchEdit->text().toUTF8());
|
explore->search(searchEdit->text());
|
||||||
});
|
});
|
||||||
|
|
||||||
// Admin stuff
|
// Admin stuff
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
#include <Wt/WApplication.h>
|
#include <Wt/WApplication.h>
|
||||||
|
#include <Wt/WPopupMenu.h>
|
||||||
|
|
||||||
#include "database/Db.hpp"
|
#include "database/Db.hpp"
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
@@ -101,6 +102,7 @@ class LmsApplication : public Wt::WApplication
|
|||||||
static Wt::WLink createReleaseLink(Wt::Dbo::ptr<Database::Release> release);
|
static Wt::WLink createReleaseLink(Wt::Dbo::ptr<Database::Release> release);
|
||||||
static std::unique_ptr<Wt::WAnchor> createReleaseAnchor(Wt::Dbo::ptr<Database::Release> release, bool addText = true);
|
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);
|
static std::unique_ptr<Wt::WText> createCluster(Wt::Dbo::ptr<Database::Cluster> cluster, bool canDelete = false);
|
||||||
|
Wt::WPopupMenu* createPopupMenu();
|
||||||
|
|
||||||
MediaPlayer& getMediaPlayer() const { return *_mediaPlayer; }
|
MediaPlayer& getMediaPlayer() const { return *_mediaPlayer; }
|
||||||
PlayQueue& getPlayQueue() const { return *_playQueue; }
|
PlayQueue& getPlayQueue() const { return *_playQueue; }
|
||||||
@@ -135,6 +137,7 @@ class LmsApplication : public Wt::WApplication
|
|||||||
std::shared_ptr<ImageResource> _imageResource;
|
std::shared_ptr<ImageResource> _imageResource;
|
||||||
MediaPlayer* _mediaPlayer {};
|
MediaPlayer* _mediaPlayer {};
|
||||||
PlayQueue* _playQueue {};
|
PlayQueue* _playQueue {};
|
||||||
|
std::unique_ptr<Wt::WPopupMenu> _popupMenu {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+44
-15
@@ -209,6 +209,13 @@ PlayQueue::getTrackList() const
|
|||||||
return Database::TrackList::getById(LmsApp->getDbSession(), _tracklistId);
|
return Database::TrackList::getById(LmsApp->getDbSession(), _tracklistId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
PlayQueue::isFull() const
|
||||||
|
{
|
||||||
|
auto transaction {LmsApp->getDbSession().createSharedTransaction()};
|
||||||
|
return getTrackList()->getCount() == _nbMaxEntries;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
PlayQueue::clearTracks()
|
PlayQueue::clearTracks()
|
||||||
{
|
{
|
||||||
@@ -321,55 +328,77 @@ PlayQueue::updateCurrentTrack(bool selected)
|
|||||||
entry->bindString("is-selected", selected ? "Lms-playqueue-selected" : "");
|
entry->bindString("is-selected", selected ? "Lms-playqueue-selected" : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
std::size_t
|
||||||
PlayQueue::enqueueTracks(const std::vector<Database::IdType>& trackIds)
|
PlayQueue::enqueueTracks(const std::vector<Database::IdType>& trackIds)
|
||||||
{
|
{
|
||||||
|
std::size_t nbTracksQueued {};
|
||||||
|
|
||||||
{
|
{
|
||||||
auto transaction {LmsApp->getDbSession().createUniqueTransaction()};
|
auto transaction {LmsApp->getDbSession().createUniqueTransaction()};
|
||||||
|
|
||||||
auto tracklist = getTrackList();
|
auto tracklist {getTrackList()};
|
||||||
|
|
||||||
|
std::size_t nbTracksToEnqueue {tracklist->getCount() + trackIds.size() > _nbMaxEntries ? _nbMaxEntries - tracklist->getCount() : trackIds.size()};
|
||||||
for (Database::IdType trackId : trackIds)
|
for (Database::IdType trackId : trackIds)
|
||||||
{
|
{
|
||||||
Database::Track::pointer track {Database::Track::getById(LmsApp->getDbSession(), trackId)};
|
Database::Track::pointer track {Database::Track::getById(LmsApp->getDbSession(), trackId)};
|
||||||
if (!track)
|
if (!track)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (nbTracksQueued == nbTracksToEnqueue)
|
||||||
|
break;
|
||||||
|
|
||||||
Database::TrackListEntry::create(LmsApp->getDbSession(), track, tracklist);
|
Database::TrackListEntry::create(LmsApp->getDbSession(), track, tracklist);
|
||||||
|
nbTracksQueued++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateInfo();
|
updateInfo();
|
||||||
addSome();
|
addSome();
|
||||||
}
|
|
||||||
|
|
||||||
void
|
return nbTracksQueued;
|
||||||
PlayQueue::enqueueTrack(Database::IdType trackId)
|
|
||||||
{
|
|
||||||
enqueueTracks({trackId});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
PlayQueue::processTracks(PlayQueueAction action, const std::vector<Database::IdType>& trackIds)
|
PlayQueue::processTracks(PlayQueueAction action, const std::vector<Database::IdType>& trackIds)
|
||||||
{
|
{
|
||||||
|
std::size_t nbAddedTracks {};
|
||||||
|
|
||||||
switch (action)
|
switch (action)
|
||||||
{
|
{
|
||||||
case PlayQueueAction::AddLast:
|
case PlayQueueAction::PlayLast:
|
||||||
enqueueTracks(trackIds);
|
nbAddedTracks = enqueueTracks(trackIds);
|
||||||
LmsApp->notifyMsg(MsgType::Info, Wt::WString::trn("Lms.PlayQueue.nb-tracks-added", trackIds.size()).arg(trackIds.size()), std::chrono::milliseconds(2000));
|
if (!_trackPos)
|
||||||
break;
|
loadTrack(0, true);
|
||||||
|
|
||||||
case PlayQueueAction::AddNext:
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PlayQueueAction::Play:
|
case PlayQueueAction::Play:
|
||||||
clearTracks();
|
clearTracks();
|
||||||
enqueueTracks(trackIds);
|
nbAddedTracks = enqueueTracks(trackIds);
|
||||||
loadTrack(0, true);
|
loadTrack(0, true);
|
||||||
|
|
||||||
LmsApp->notifyMsg(MsgType::Info, Wt::WString::trn("Lms.PlayQueue.nb-tracks-playing", trackIds.size()).arg(trackIds.size()), std::chrono::milliseconds(2000));
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case PlayQueueAction::PlayShuffled:
|
||||||
|
{
|
||||||
|
clearTracks();
|
||||||
|
{
|
||||||
|
std::vector<Database::IdType> shuffledTrackIds {trackIds};
|
||||||
|
Random::shuffleContainer(shuffledTrackIds);
|
||||||
|
nbAddedTracks = enqueueTracks(shuffledTrackIds);
|
||||||
}
|
}
|
||||||
|
loadTrack(0, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nbAddedTracks > 0)
|
||||||
|
LmsApp->notifyMsg(MsgType::Info, Wt::WString::trn("Lms.PlayQueue.nb-tracks-added", nbAddedTracks).arg(nbAddedTracks), std::chrono::milliseconds(2000));
|
||||||
|
|
||||||
|
if (isFull())
|
||||||
|
LmsApp->notifyMsg(MsgType::Warning, Wt::WString::tr("Lms.PlayQueue.playqueue-full"), std::chrono::milliseconds(2000));
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
@@ -60,10 +60,10 @@ class PlayQueue : public Wt::WTemplate
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
Wt::Dbo::ptr<Database::TrackList> getTrackList() const;
|
Wt::Dbo::ptr<Database::TrackList> getTrackList() const;
|
||||||
|
bool isFull() const;
|
||||||
|
|
||||||
void clearTracks();
|
void clearTracks();
|
||||||
void enqueueTracks(const std::vector<Database::IdType>& trackIds);
|
std::size_t enqueueTracks(const std::vector<Database::IdType>& trackIds);
|
||||||
void enqueueTrack(Database::IdType trackId);
|
|
||||||
void addSome();
|
void addSome();
|
||||||
void enqueueRadioTrack();
|
void enqueueRadioTrack();
|
||||||
void updateInfo();
|
void updateInfo();
|
||||||
@@ -80,6 +80,8 @@ class PlayQueue : public Wt::WTemplate
|
|||||||
void addRadioTrackFromClusters();
|
void addRadioTrackFromClusters();
|
||||||
std::optional<float> getReplayGain(std::size_t pos, const Wt::Dbo::ptr<Database::Track>& track) const;
|
std::optional<float> getReplayGain(std::size_t pos, const Wt::Dbo::ptr<Database::Track>& track) const;
|
||||||
|
|
||||||
|
static inline constexpr std::size_t _nbMaxEntries {1000};
|
||||||
|
|
||||||
bool _repeatAll {};
|
bool _repeatAll {};
|
||||||
bool _radioMode {};
|
bool _radioMode {};
|
||||||
bool _mediaPlayerSettingsLoaded {};
|
bool _mediaPlayerSettingsLoaded {};
|
||||||
|
|||||||
@@ -19,15 +19,22 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <Wt/WSignal.h>
|
||||||
|
|
||||||
|
#include "database/Types.hpp"
|
||||||
|
|
||||||
namespace UserInterface
|
namespace UserInterface
|
||||||
{
|
{
|
||||||
|
|
||||||
enum class PlayQueueAction
|
enum class PlayQueueAction
|
||||||
{
|
{
|
||||||
Play,
|
Play,
|
||||||
AddNext,
|
PlayLast,
|
||||||
AddLast,
|
PlayShuffled,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using PlayQueueActionSignal = Wt::Signal<PlayQueueAction, const std::vector<Database::IdType>&>;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -115,11 +115,24 @@ Artist::refreshView()
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
Wt::WText* addBtn = bindNew<Wt::WText>("add-btn", Wt::WString::tr("Lms.Explore.template.add-btn"), Wt::TextFormat::XHTML);
|
Wt::WText* moreBtn = bindNew<Wt::WText>("more-btn", Wt::WString::tr("Lms.Explore.template.more-btn"), Wt::TextFormat::XHTML);
|
||||||
|
|
||||||
addBtn->clicked().connect([=]
|
moreBtn->clicked().connect([=]
|
||||||
{
|
{
|
||||||
artistsAction.emit(PlayQueueAction::AddLast, {*artistId});
|
Wt::WPopupMenu* popup {LmsApp->createPopupMenu()};
|
||||||
|
|
||||||
|
popup->addItem(Wt::WString::tr("Lms.Explore.play-shuffled"))
|
||||||
|
->triggered().connect(this, [=]
|
||||||
|
{
|
||||||
|
artistsAction.emit(PlayQueueAction::PlayShuffled, {*artistId});
|
||||||
|
});
|
||||||
|
popup->addItem(Wt::WString::tr("Lms.Explore.play-last"))
|
||||||
|
->triggered().connect(this, [=]
|
||||||
|
{
|
||||||
|
artistsAction.emit(PlayQueueAction::PlayLast, {*artistId});
|
||||||
|
});
|
||||||
|
|
||||||
|
popup->exec(moreBtn);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,6 +148,10 @@ Artist::refreshView()
|
|||||||
void
|
void
|
||||||
Artist::refreshSimilarArtists(const std::vector<Database::IdType>& similarArtistsId)
|
Artist::refreshSimilarArtists(const std::vector<Database::IdType>& similarArtistsId)
|
||||||
{
|
{
|
||||||
|
if (similarArtistsId.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
setCondition("if-has-similar-artists", true);
|
||||||
Wt::WContainerWidget* similarArtistsContainer {bindNew<Wt::WContainerWidget>("similar-artists")};
|
Wt::WContainerWidget* similarArtistsContainer {bindNew<Wt::WContainerWidget>("similar-artists")};
|
||||||
|
|
||||||
for (Database::IdType artistId : similarArtistsId)
|
for (Database::IdType artistId : similarArtistsId)
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class Artist : public Wt::WTemplate
|
|||||||
public:
|
public:
|
||||||
Artist(Filters* filters);
|
Artist(Filters* filters);
|
||||||
|
|
||||||
Wt::Signal<PlayQueueAction, const std::vector<Database::IdType>&> artistsAction;
|
PlayQueueActionSignal artistsAction;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void refreshView();
|
void refreshView();
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
#include "Filters.hpp"
|
#include "Filters.hpp"
|
||||||
#include "ReleasesView.hpp"
|
#include "ReleasesView.hpp"
|
||||||
#include "ReleaseView.hpp"
|
#include "ReleaseView.hpp"
|
||||||
|
#include "SearchView.hpp"
|
||||||
#include "TracksView.hpp"
|
#include "TracksView.hpp"
|
||||||
|
|
||||||
namespace UserInterface {
|
namespace UserInterface {
|
||||||
@@ -50,6 +51,7 @@ handleContentsPathChange(Wt::WStackedWidget* stack)
|
|||||||
IdxArtist,
|
IdxArtist,
|
||||||
IdxReleases,
|
IdxReleases,
|
||||||
IdxRelease,
|
IdxRelease,
|
||||||
|
IdxSearch,
|
||||||
IdxTracks,
|
IdxTracks,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -59,6 +61,7 @@ handleContentsPathChange(Wt::WStackedWidget* stack)
|
|||||||
{ "/artist", IdxArtist },
|
{ "/artist", IdxArtist },
|
||||||
{ "/releases", IdxReleases },
|
{ "/releases", IdxReleases },
|
||||||
{ "/release", IdxRelease },
|
{ "/release", IdxRelease },
|
||||||
|
{ "/search", IdxSearch },
|
||||||
{ "/tracks", IdxTracks },
|
{ "/tracks", IdxTracks },
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -100,6 +103,11 @@ Explore::Explore(Filters* filters)
|
|||||||
release->tracksAction.connect(this, &Explore::handleTracksAction);
|
release->tracksAction.connect(this, &Explore::handleTracksAction);
|
||||||
contentsStack->addWidget(std::move(release));
|
contentsStack->addWidget(std::move(release));
|
||||||
|
|
||||||
|
auto search = std::make_unique<SearchView>(_filters);
|
||||||
|
search->tracksAction.connect(this, &Explore::handleTracksAction);
|
||||||
|
_search = search.get();
|
||||||
|
contentsStack->addWidget(std::move(search));
|
||||||
|
|
||||||
auto tracks = std::make_unique<Tracks>(_filters);
|
auto tracks = std::make_unique<Tracks>(_filters);
|
||||||
tracks->tracksAction.connect(this, &Explore::handleTracksAction);
|
tracks->tracksAction.connect(this, &Explore::handleTracksAction);
|
||||||
contentsStack->addWidget(std::move(tracks));
|
contentsStack->addWidget(std::move(tracks));
|
||||||
@@ -112,6 +120,12 @@ Explore::Explore(Filters* filters)
|
|||||||
handleContentsPathChange(contentsStack);
|
handleContentsPathChange(contentsStack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Explore::search(const Wt::WString& searchText)
|
||||||
|
{
|
||||||
|
_search->refreshView(searchText);
|
||||||
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
std::vector<Database::IdType>
|
std::vector<Database::IdType>
|
||||||
getArtistsTracks(Database::Session& session, const std::vector<Database::IdType>& artistsId, const std::set<Database::IdType>&)
|
getArtistsTracks(Database::Session& session, const std::vector<Database::IdType>& artistsId, const std::set<Database::IdType>&)
|
||||||
|
|||||||
@@ -27,13 +27,16 @@
|
|||||||
namespace UserInterface {
|
namespace UserInterface {
|
||||||
|
|
||||||
class Filters;
|
class Filters;
|
||||||
|
class SearchView;
|
||||||
|
|
||||||
class Explore : public Wt::WTemplate
|
class Explore : public Wt::WTemplate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Explore(Filters* filters);
|
Explore(Filters* filters);
|
||||||
|
|
||||||
Wt::Signal<PlayQueueAction, const std::vector<Database::IdType>&> tracksAction;
|
void search(const Wt::WString& searchText);
|
||||||
|
|
||||||
|
PlayQueueActionSignal tracksAction;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@@ -42,6 +45,7 @@ class Explore : public Wt::WTemplate
|
|||||||
void handleTracksAction(PlayQueueAction action, const std::vector<Database::IdType>& tracksId);
|
void handleTracksAction(PlayQueueAction action, const std::vector<Database::IdType>& tracksId);
|
||||||
|
|
||||||
Filters* _filters {};
|
Filters* _filters {};
|
||||||
|
SearchView* _search {};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace UserInterface
|
} // namespace UserInterface
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* 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 "ReleasePopup.hpp"
|
||||||
|
|
||||||
|
#include "LmsApplication.hpp"
|
||||||
|
|
||||||
|
namespace UserInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
void
|
||||||
|
displayReleasePopupMenu(Wt::WInteractWidget& target,
|
||||||
|
Database::IdType releaseId,
|
||||||
|
PlayQueueActionSignal& releasesAction)
|
||||||
|
{
|
||||||
|
Wt::WPopupMenu* popup {LmsApp->createPopupMenu()};
|
||||||
|
|
||||||
|
popup->addItem(Wt::WString::tr("Lms.Explore.play-shuffled"))
|
||||||
|
->triggered().connect(&target, [&releasesAction, releaseId]
|
||||||
|
{
|
||||||
|
releasesAction.emit(PlayQueueAction::PlayShuffled, {releaseId});
|
||||||
|
});
|
||||||
|
popup->addItem(Wt::WString::tr("Lms.Explore.play-last"))
|
||||||
|
->triggered().connect(&target, [&releasesAction, releaseId]
|
||||||
|
{
|
||||||
|
releasesAction.emit(PlayQueueAction::PlayLast, {releaseId});
|
||||||
|
});
|
||||||
|
|
||||||
|
popup->popup(&target);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace UserInterface
|
||||||
|
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* 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 <Wt/WWidget.h>
|
||||||
|
#include <Wt/WSignal.h>
|
||||||
|
|
||||||
|
#include "database/Types.hpp"
|
||||||
|
#include "PlayQueueAction.hpp"
|
||||||
|
|
||||||
|
namespace UserInterface
|
||||||
|
{
|
||||||
|
void displayReleasePopupMenu(Wt::WInteractWidget& target,
|
||||||
|
Database::IdType releaseId,
|
||||||
|
PlayQueueActionSignal& releasesAction);
|
||||||
|
} // namespace UserInterface
|
||||||
|
|
||||||
@@ -22,7 +22,6 @@
|
|||||||
#include <Wt/WAnchor.h>
|
#include <Wt/WAnchor.h>
|
||||||
#include <Wt/WApplication.h>
|
#include <Wt/WApplication.h>
|
||||||
#include <Wt/WImage.h>
|
#include <Wt/WImage.h>
|
||||||
#include <Wt/WPopupMenu.h>
|
|
||||||
#include <Wt/WTemplate.h>
|
#include <Wt/WTemplate.h>
|
||||||
#include <Wt/WText.h>
|
#include <Wt/WText.h>
|
||||||
|
|
||||||
@@ -39,6 +38,7 @@
|
|||||||
#include "LmsApplicationException.hpp"
|
#include "LmsApplicationException.hpp"
|
||||||
#include "MediaPlayer.hpp"
|
#include "MediaPlayer.hpp"
|
||||||
#include "ReleaseListHelpers.hpp"
|
#include "ReleaseListHelpers.hpp"
|
||||||
|
#include "ReleasePopup.hpp"
|
||||||
#include "TrackStringUtils.hpp"
|
#include "TrackStringUtils.hpp"
|
||||||
|
|
||||||
using namespace Database;
|
using namespace Database;
|
||||||
@@ -157,10 +157,10 @@ Release::refreshView()
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
Wt::WText* addBtn {bindNew<Wt::WText>("add-btn", Wt::WString::tr("Lms.Explore.template.add-btn"), Wt::TextFormat::XHTML)};
|
Wt::WText* moreBtn {bindNew<Wt::WText>("more-btn", Wt::WString::tr("Lms.Explore.template.more-btn"), Wt::TextFormat::XHTML)};
|
||||||
addBtn->clicked().connect([=]
|
moreBtn->clicked().connect([=]
|
||||||
{
|
{
|
||||||
releasesAction.emit(PlayQueueAction::AddLast, {*releaseId});
|
displayReleasePopupMenu(*moreBtn, *releaseId, releasesAction);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,10 +237,18 @@ Release::refreshView()
|
|||||||
tracksAction.emit(PlayQueueAction::Play, {trackId});
|
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)};
|
Wt::WText* moreBtn {entry->bindNew<Wt::WText>("more-btn", Wt::WString::tr("Lms.Explore.template.more-btn"), Wt::TextFormat::XHTML)};
|
||||||
addBtn->clicked().connect([=]()
|
moreBtn->clicked().connect([=]()
|
||||||
{
|
{
|
||||||
tracksAction.emit(PlayQueueAction::AddLast, {trackId});
|
Wt::WPopupMenu* popup {LmsApp->createPopupMenu()};
|
||||||
|
|
||||||
|
popup->addItem(Wt::WString::tr("Lms.Explore.play-last"))
|
||||||
|
->triggered().connect(moreBtn, [=]
|
||||||
|
{
|
||||||
|
tracksAction.emit(PlayQueueAction::PlayLast, {trackId});
|
||||||
|
});
|
||||||
|
|
||||||
|
popup->popup(moreBtn);
|
||||||
});
|
});
|
||||||
|
|
||||||
entry->bindString("duration", trackDurationToString(track->getDuration()), Wt::TextFormat::Plain);
|
entry->bindString("duration", trackDurationToString(track->getDuration()), Wt::TextFormat::Plain);
|
||||||
@@ -306,6 +314,10 @@ Release::refreshLinks(const Database::Release::pointer& release)
|
|||||||
void
|
void
|
||||||
Release::refreshSimilarReleases(const std::vector<Database::IdType>& similarReleasesId)
|
Release::refreshSimilarReleases(const std::vector<Database::IdType>& similarReleasesId)
|
||||||
{
|
{
|
||||||
|
if (similarReleasesId.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
setCondition("if-has-similar-releases", true);
|
||||||
auto* similarReleasesContainer {bindNew<Wt::WContainerWidget>("similar-releases")};
|
auto* similarReleasesContainer {bindNew<Wt::WContainerWidget>("similar-releases")};
|
||||||
|
|
||||||
for (Database::IdType id : similarReleasesId)
|
for (Database::IdType id : similarReleasesId)
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Wt/WSignal.h>
|
|
||||||
#include <Wt/WTemplate.h>
|
#include <Wt/WTemplate.h>
|
||||||
|
|
||||||
#include "database/Types.hpp"
|
#include "database/Types.hpp"
|
||||||
@@ -39,11 +38,8 @@ namespace UserInterface
|
|||||||
public:
|
public:
|
||||||
Release(Filters* filters);
|
Release(Filters* filters);
|
||||||
|
|
||||||
Wt::Signal<PlayQueueAction, const std::vector<Database::IdType>&> releasesAction;
|
PlayQueueActionSignal releasesAction;
|
||||||
Wt::Signal<PlayQueueAction, const std::vector<Database::IdType>&> tracksAction;
|
PlayQueueActionSignal tracksAction;
|
||||||
|
|
||||||
Wt::Signal<Wt::WWidget&, Database::IdType> createReleaseActionMenu;
|
|
||||||
Wt::Signal<Wt::WWidget&, Database::IdType> createTrackActionMenu;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void refreshView();
|
void refreshView();
|
||||||
|
|||||||
@@ -72,10 +72,23 @@ _filters {filters}
|
|||||||
{
|
{
|
||||||
releasesAction.emit(PlayQueueAction::Play, getAllReleases());
|
releasesAction.emit(PlayQueueAction::Play, getAllReleases());
|
||||||
});
|
});
|
||||||
Wt::WText* addBtn {bindNew<Wt::WText>("add-btn", Wt::WString::tr("Lms.Explore.template.add-btn"), Wt::TextFormat::XHTML)};
|
Wt::WText* moreBtn {bindNew<Wt::WText>("more-btn", Wt::WString::tr("Lms.Explore.template.more-btn"), Wt::TextFormat::XHTML)};
|
||||||
addBtn->clicked().connect([this]
|
moreBtn->clicked().connect([=]
|
||||||
{
|
{
|
||||||
releasesAction.emit(PlayQueueAction::AddLast, getAllReleases());
|
Wt::WPopupMenu* popup {LmsApp->createPopupMenu()};
|
||||||
|
|
||||||
|
popup->addItem(Wt::WString::tr("Lms.Explore.play-shuffled"))
|
||||||
|
->triggered().connect([this]
|
||||||
|
{
|
||||||
|
releasesAction.emit(PlayQueueAction::PlayShuffled, getAllReleases());
|
||||||
|
});
|
||||||
|
popup->addItem(Wt::WString::tr("Lms.Explore.play-last"))
|
||||||
|
->triggered().connect([this]
|
||||||
|
{
|
||||||
|
releasesAction.emit(PlayQueueAction::PlayLast, getAllReleases());
|
||||||
|
});
|
||||||
|
|
||||||
|
popup->popup(moreBtn);
|
||||||
});
|
});
|
||||||
|
|
||||||
_container = bindNew<Wt::WContainerWidget>("releases");
|
_container = bindNew<Wt::WContainerWidget>("releases");
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ class Releases : public Wt::WTemplate
|
|||||||
public:
|
public:
|
||||||
Releases(Filters* filters);
|
Releases(Filters* filters);
|
||||||
|
|
||||||
Wt::Signal<PlayQueueAction, const std::vector<Database::IdType>&> releasesAction;
|
PlayQueueActionSignal releasesAction;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|||||||
@@ -51,9 +51,9 @@ namespace UserInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
SearchView::refreshView(const std::string& searchText)
|
SearchView::refreshView(const Wt::WString& searchText)
|
||||||
{
|
{
|
||||||
_keywords = StringUtils::splitString(searchText, " ");
|
_keywords = StringUtils::splitString(searchText.toUTF8(), " ");
|
||||||
refreshView();
|
refreshView();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,7 +127,7 @@ namespace UserInterface
|
|||||||
auto* container {bindNew<Wt::WContainerWidget>("tracks")};
|
auto* container {bindNew<Wt::WContainerWidget>("tracks")};
|
||||||
|
|
||||||
for (const Database::Track::pointer& track : tracks)
|
for (const Database::Track::pointer& track : tracks)
|
||||||
container->addWidget(TrackListHelpers::createEntry(track));
|
container->addWidget(TrackListHelpers::createEntry(track, tracksAction));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
|
|
||||||
#include <Wt/WTemplate.h>
|
#include <Wt/WTemplate.h>
|
||||||
|
|
||||||
|
#include "PlayQueueAction.hpp"
|
||||||
|
|
||||||
namespace UserInterface {
|
namespace UserInterface {
|
||||||
|
|
||||||
class Filters;
|
class Filters;
|
||||||
@@ -33,7 +35,9 @@ class SearchView : public Wt::WTemplate
|
|||||||
public:
|
public:
|
||||||
SearchView(Filters* filters);
|
SearchView(Filters* filters);
|
||||||
|
|
||||||
void refreshView(const std::string& searchText);
|
PlayQueueActionSignal tracksAction;
|
||||||
|
|
||||||
|
void refreshView(const Wt::WString& searchText);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void refreshView();
|
void refreshView();
|
||||||
|
|||||||
@@ -30,7 +30,6 @@
|
|||||||
#include "resource/ImageResource.hpp"
|
#include "resource/ImageResource.hpp"
|
||||||
#include "LmsApplication.hpp"
|
#include "LmsApplication.hpp"
|
||||||
#include "MediaPlayer.hpp"
|
#include "MediaPlayer.hpp"
|
||||||
#include "PlayQueue.hpp"
|
|
||||||
#include "TrackStringUtils.hpp"
|
#include "TrackStringUtils.hpp"
|
||||||
|
|
||||||
using namespace Database;
|
using namespace Database;
|
||||||
@@ -38,7 +37,7 @@ using namespace Database;
|
|||||||
namespace UserInterface::TrackListHelpers
|
namespace UserInterface::TrackListHelpers
|
||||||
{
|
{
|
||||||
std::unique_ptr<Wt::WTemplate>
|
std::unique_ptr<Wt::WTemplate>
|
||||||
createEntry(const Wt::Dbo::ptr<Database::Track>& track)
|
createEntry(const Wt::Dbo::ptr<Database::Track>& track, PlayQueueActionSignal& tracksAction)
|
||||||
{
|
{
|
||||||
auto entry {std::make_unique<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Tracks.template.entry"))};
|
auto entry {std::make_unique<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Tracks.template.entry"))};
|
||||||
auto* entryPtr {entry.get()};
|
auto* entryPtr {entry.get()};
|
||||||
@@ -89,15 +88,23 @@ namespace UserInterface::TrackListHelpers
|
|||||||
entry->bindString("duration", trackDurationToString(track->getDuration()), Wt::TextFormat::Plain);
|
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);
|
Wt::WText* playBtn = entry->bindNew<Wt::WText>("play-btn", Wt::WString::tr("Lms.Explore.template.play-btn"), Wt::TextFormat::XHTML);
|
||||||
playBtn->clicked().connect([=]
|
playBtn->clicked().connect([trackId, &tracksAction]
|
||||||
{
|
{
|
||||||
LmsApp->getPlayQueue().processTracks(PlayQueueAction::Play, {trackId});
|
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);
|
Wt::WText* moreBtn = entry->bindNew<Wt::WText>("more-btn", Wt::WString::tr("Lms.Explore.template.more-btn"), Wt::TextFormat::XHTML);
|
||||||
addBtn->clicked().connect([=]
|
moreBtn->clicked().connect([=, &tracksAction]
|
||||||
{
|
{
|
||||||
LmsApp->getPlayQueue().processTracks(PlayQueueAction::AddLast, {trackId});
|
Wt::WPopupMenu* popup {LmsApp->createPopupMenu()};
|
||||||
|
|
||||||
|
popup->addItem(Wt::WString::tr("Lms.Explore.play-last"))
|
||||||
|
->triggered().connect(moreBtn, [=, &tracksAction]
|
||||||
|
{
|
||||||
|
tracksAction.emit(PlayQueueAction::PlayLast, {trackId});
|
||||||
|
});
|
||||||
|
|
||||||
|
popup->popup(moreBtn);
|
||||||
});
|
});
|
||||||
|
|
||||||
LmsApp->getMediaPlayer().trackLoaded.connect(entryPtr, [=] (Database::IdType loadedTrackId)
|
LmsApp->getMediaPlayer().trackLoaded.connect(entryPtr, [=] (Database::IdType loadedTrackId)
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include <Wt/WTemplate.h>
|
#include <Wt/WTemplate.h>
|
||||||
|
#include "PlayQueueAction.hpp"
|
||||||
|
|
||||||
namespace Database
|
namespace Database
|
||||||
{
|
{
|
||||||
@@ -30,6 +31,6 @@ namespace Database
|
|||||||
|
|
||||||
namespace UserInterface::TrackListHelpers
|
namespace UserInterface::TrackListHelpers
|
||||||
{
|
{
|
||||||
std::unique_ptr<Wt::WTemplate> createEntry(const Wt::Dbo::ptr<Database::Track>& track);
|
std::unique_ptr<Wt::WTemplate> createEntry(const Wt::Dbo::ptr<Database::Track>& track, PlayQueueActionSignal& tracksAction);
|
||||||
} // namespace UserInterface
|
} // namespace UserInterface
|
||||||
|
|
||||||
|
|||||||
@@ -32,9 +32,8 @@
|
|||||||
#include "utils/Logger.hpp"
|
#include "utils/Logger.hpp"
|
||||||
#include "utils/String.hpp"
|
#include "utils/String.hpp"
|
||||||
|
|
||||||
#include "resource/ImageResource.hpp"
|
|
||||||
|
|
||||||
#include "common/LoadingIndicator.hpp"
|
#include "common/LoadingIndicator.hpp"
|
||||||
|
#include "resource/ImageResource.hpp"
|
||||||
#include "Filters.hpp"
|
#include "Filters.hpp"
|
||||||
#include "LmsApplication.hpp"
|
#include "LmsApplication.hpp"
|
||||||
#include "MediaPlayer.hpp"
|
#include "MediaPlayer.hpp"
|
||||||
@@ -76,10 +75,23 @@ _filters {filters}
|
|||||||
tracksAction.emit(PlayQueueAction::Play, getAllTracks());
|
tracksAction.emit(PlayQueueAction::Play, getAllTracks());
|
||||||
});
|
});
|
||||||
|
|
||||||
Wt::WText* addBtn = bindNew<Wt::WText>("add-btn", Wt::WString::tr("Lms.Explore.template.add-btn"), Wt::TextFormat::XHTML);
|
Wt::WText* moreBtn = bindNew<Wt::WText>("more-btn", Wt::WString::tr("Lms.Explore.template.more-btn"), Wt::TextFormat::XHTML);
|
||||||
addBtn->clicked().connect([=]
|
moreBtn->clicked().connect([=]
|
||||||
{
|
{
|
||||||
tracksAction.emit(PlayQueueAction::AddLast, getAllTracks());
|
Wt::WPopupMenu* popup {LmsApp->createPopupMenu()};
|
||||||
|
|
||||||
|
popup->addItem(Wt::WString::tr("Lms.Explore.play-shuffled"))
|
||||||
|
->triggered().connect(this, [this]
|
||||||
|
{
|
||||||
|
tracksAction.emit(PlayQueueAction::PlayShuffled, getAllTracks());
|
||||||
|
});
|
||||||
|
popup->addItem(Wt::WString::tr("Lms.Explore.play-last"))
|
||||||
|
->triggered().connect(this, [this]
|
||||||
|
{
|
||||||
|
tracksAction.emit(PlayQueueAction::PlayLast, getAllTracks());
|
||||||
|
});
|
||||||
|
|
||||||
|
popup->popup(moreBtn);
|
||||||
});
|
});
|
||||||
|
|
||||||
_tracksContainer = bindNew<Wt::WContainerWidget>("tracks");
|
_tracksContainer = bindNew<Wt::WContainerWidget>("tracks");
|
||||||
@@ -219,7 +231,7 @@ Tracks::addSome()
|
|||||||
bool moreResults;
|
bool moreResults;
|
||||||
for (const Track::pointer& track : getTracks(Range {static_cast<std::size_t>(_tracksContainer->count()), batchSize}, moreResults))
|
for (const Track::pointer& track : getTracks(Range {static_cast<std::size_t>(_tracksContainer->count()), batchSize}, moreResults))
|
||||||
{
|
{
|
||||||
_tracksContainer->addWidget(TrackListHelpers::createEntry(track));
|
_tracksContainer->addWidget(TrackListHelpers::createEntry(track, tracksAction));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (moreResults)
|
if (moreResults)
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ class Tracks : public Wt::WTemplate
|
|||||||
public:
|
public:
|
||||||
Tracks(Filters* filters);
|
Tracks(Filters* filters);
|
||||||
|
|
||||||
Wt::Signal<PlayQueueAction, const std::vector<Database::IdType>&> tracksAction;
|
PlayQueueActionSignal tracksAction;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user