Playing a track in the album view will now play the whole album at the selected track index (+ reworked play queue controller)
This commit is contained in:
@@ -32,6 +32,7 @@ add_executable(lms
|
||||
ui/explore/DatabaseCollectorBase.cpp
|
||||
ui/explore/Explore.cpp
|
||||
ui/explore/Filters.cpp
|
||||
ui/explore/PlayQueueController.cpp
|
||||
ui/explore/ReleaseCollector.cpp
|
||||
ui/explore/ReleaseListHelpers.cpp
|
||||
ui/explore/ReleasesView.cpp
|
||||
|
||||
@@ -502,8 +502,9 @@ LmsApplication::createHome()
|
||||
Wt::WStackedWidget* mainStack {main->bindNew<Wt::WStackedWidget>("contents")};
|
||||
mainStack->setOverflow(Wt::Overflow::Visible); // wt makes it hidden by default
|
||||
|
||||
Explore* explore {mainStack->addNew<Explore>(filters)};
|
||||
_playQueue = mainStack->addNew<PlayQueue>();
|
||||
std::unique_ptr<PlayQueue> playQueue {std::make_unique<PlayQueue>()};
|
||||
Explore* explore {mainStack->addNew<Explore>(*filters, *playQueue)};
|
||||
_playQueue = mainStack->addWidget(std::move(playQueue));
|
||||
mainStack->addNew<SettingsView>();
|
||||
|
||||
searchEdit->enterPressed().connect([=]
|
||||
@@ -525,11 +526,7 @@ LmsApplication::createHome()
|
||||
mainStack->addNew<UserView>();
|
||||
}
|
||||
|
||||
explore->setMaxTrackCountForAction(_playQueue->getCapacity());
|
||||
explore->tracksAction.connect([this] (PlayQueueAction action, const std::vector<Database::TrackId>& trackIds)
|
||||
{
|
||||
_playQueue->processTracks(action, trackIds);
|
||||
});
|
||||
explore->getPlayQueueController().setMaxTrackCountToEnqueue(_playQueue->getCapacity());
|
||||
|
||||
// Events from MediaPlayer
|
||||
_mediaPlayer->playNext.connect([this]
|
||||
|
||||
+34
-26
@@ -416,38 +416,46 @@ PlayQueue::enqueueTracks(const std::vector<Database::TrackId>& trackIds)
|
||||
}
|
||||
|
||||
void
|
||||
PlayQueue::processTracks(PlayQueueAction action, const std::vector<Database::TrackId>& trackIds)
|
||||
PlayQueue::play(const std::vector<Database::TrackId>& trackIds)
|
||||
{
|
||||
std::size_t nbAddedTracks {};
|
||||
playAtIndex(trackIds, 0);
|
||||
}
|
||||
|
||||
switch (action)
|
||||
{
|
||||
case PlayQueueAction::PlayLast:
|
||||
nbAddedTracks = enqueueTracks(trackIds);
|
||||
if (!_trackPos)
|
||||
loadTrack(0, true);
|
||||
break;
|
||||
void
|
||||
PlayQueue::playShuffled(const std::vector<Database::TrackId>& trackIds)
|
||||
{
|
||||
clearTracks();
|
||||
std::vector<Database::TrackId> shuffledTrackIds {trackIds};
|
||||
Random::shuffleContainer(shuffledTrackIds);
|
||||
const std::size_t nbAddedTracks {enqueueTracks(shuffledTrackIds)};
|
||||
loadTrack(0, true);
|
||||
|
||||
case PlayQueueAction::Play:
|
||||
clearTracks();
|
||||
nbAddedTracks = enqueueTracks(trackIds);
|
||||
loadTrack(0, true);
|
||||
notifyAddedTracks(nbAddedTracks);
|
||||
}
|
||||
|
||||
break;
|
||||
void
|
||||
PlayQueue::playOrAddLast(const std::vector<Database::TrackId>& trackIds)
|
||||
{
|
||||
const std::size_t nbAddedTracks {enqueueTracks(trackIds)};
|
||||
if (!_trackPos)
|
||||
loadTrack(0, true);
|
||||
|
||||
case PlayQueueAction::PlayShuffled:
|
||||
{
|
||||
clearTracks();
|
||||
{
|
||||
std::vector<Database::TrackId> shuffledTrackIds {trackIds};
|
||||
Random::shuffleContainer(shuffledTrackIds);
|
||||
nbAddedTracks = enqueueTracks(shuffledTrackIds);
|
||||
}
|
||||
loadTrack(0, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
notifyAddedTracks(nbAddedTracks);
|
||||
}
|
||||
|
||||
void
|
||||
PlayQueue::playAtIndex(const std::vector<Database::TrackId>& trackIds, std::size_t index)
|
||||
{
|
||||
clearTracks();
|
||||
const std::size_t nbAddedTracks {enqueueTracks(trackIds)};
|
||||
loadTrack(index, true);
|
||||
|
||||
notifyAddedTracks(nbAddedTracks);
|
||||
}
|
||||
|
||||
void
|
||||
PlayQueue::notifyAddedTracks(std::size_t nbAddedTracks) const
|
||||
{
|
||||
if (nbAddedTracks > 0)
|
||||
LmsApp->notifyMsg(Notification::Type::Info, Wt::WString::tr("Lms.PlayQueue.playqueue"), Wt::WString::trn("Lms.PlayQueue.nb-tracks-added", nbAddedTracks).arg(nbAddedTracks), std::chrono::seconds {2});
|
||||
|
||||
|
||||
@@ -27,8 +27,8 @@
|
||||
#include <Wt/WText.h>
|
||||
|
||||
#include "services/database/Object.hpp"
|
||||
#include "services/database/TrackId.hpp"
|
||||
#include "services/database/TrackListId.hpp"
|
||||
#include "PlayQueueAction.hpp"
|
||||
|
||||
#include "common/Template.hpp"
|
||||
|
||||
@@ -53,7 +53,10 @@ class PlayQueue : public Template
|
||||
public:
|
||||
PlayQueue();
|
||||
|
||||
void processTracks(PlayQueueAction action, const std::vector<Database::TrackId>& trackIds);
|
||||
void play(const std::vector<Database::TrackId>& trackIds);
|
||||
void playShuffled(const std::vector<Database::TrackId>& trackIds);
|
||||
void playOrAddLast(const std::vector<Database::TrackId>& trackIds); // play if queue empty, otherwise just add last
|
||||
void playAtIndex(const std::vector<Database::TrackId>& trackIds, std::size_t index);
|
||||
|
||||
// play the next track in the queue
|
||||
void playNext();
|
||||
@@ -70,6 +73,7 @@ class PlayQueue : public Template
|
||||
constexpr std::size_t getCapacity() const { return _capacity; }
|
||||
|
||||
private:
|
||||
void notifyAddedTracks(std::size_t nbAddedTracks) const;
|
||||
Database::ObjectPtr<Database::TrackList> getTrackList() const;
|
||||
bool isFull() const;
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "Filters.hpp"
|
||||
#include "LmsApplication.hpp"
|
||||
#include "LmsApplicationException.hpp"
|
||||
#include "PlayQueueController.hpp"
|
||||
#include "ReleaseListHelpers.hpp"
|
||||
#include "TrackListHelpers.hpp"
|
||||
#include "Utils.hpp"
|
||||
@@ -47,9 +48,10 @@ using namespace Database;
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
Artist::Artist(Filters* filters)
|
||||
Artist::Artist(Filters& filters, PlayQueueController& controller)
|
||||
: Template {Wt::WString::tr("Lms.Explore.Artist.template")}
|
||||
, _filters {filters}
|
||||
, _playQueueController {controller}
|
||||
{
|
||||
addFunction("tr", &Wt::WTemplate::Functions::tr);
|
||||
addFunction("id", &Wt::WTemplate::Functions::id);
|
||||
@@ -59,7 +61,7 @@ Artist::Artist(Filters* filters)
|
||||
refreshView();
|
||||
});
|
||||
|
||||
filters->updated().connect([this]
|
||||
filters.updated().connect([this]
|
||||
{
|
||||
refreshView();
|
||||
});
|
||||
@@ -140,7 +142,7 @@ Artist::refreshView()
|
||||
Wt::WInteractWidget* entry {clusterContainers->addWidget(Utils::createCluster(clusterId))};
|
||||
entry->clicked().connect([=]
|
||||
{
|
||||
_filters->add(clusterId);
|
||||
_filters.add(clusterId);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -151,18 +153,18 @@ Artist::refreshView()
|
||||
bindNew<Wt::WPushButton>("play-btn", Wt::WString::tr("Lms.Explore.play"), Wt::TextFormat::XHTML)
|
||||
->clicked().connect([=]
|
||||
{
|
||||
artistsAction.emit(PlayQueueAction::Play, {_artistId});
|
||||
_playQueueController.processCommand(PlayQueueController::Command::Play, {_artistId});
|
||||
});
|
||||
|
||||
bindNew<Wt::WPushButton>("play-shuffled", Wt::WString::tr("Lms.Explore.play-shuffled"), Wt::TextFormat::Plain)
|
||||
->clicked().connect([=]
|
||||
{
|
||||
artistsAction.emit(PlayQueueAction::PlayShuffled, {_artistId});
|
||||
_playQueueController.processCommand(PlayQueueController::Command::PlayShuffled, {_artistId});
|
||||
});
|
||||
bindNew<Wt::WPushButton>("play-last", Wt::WString::tr("Lms.Explore.play-last"), Wt::TextFormat::Plain)
|
||||
->clicked().connect([=]
|
||||
{
|
||||
artistsAction.emit(PlayQueueAction::PlayLast, {_artistId});
|
||||
_playQueueController.processCommand(PlayQueueController::Command::PlayOrAddLast, {_artistId});
|
||||
});
|
||||
bindNew<Wt::WPushButton>("download", Wt::WString::tr("Lms.Explore.download"))
|
||||
->setLink(Wt::WLink {std::make_unique<DownloadArtistResource>(*artistId)});
|
||||
@@ -288,7 +290,7 @@ Artist::addSomeReleases(InfiniteScrollingContainer& releaseContainer, EnumSet<Da
|
||||
const Range range {static_cast<std::size_t>(releaseContainer.getCount()), _releasesBatchSize};
|
||||
|
||||
Release::FindParameters params;
|
||||
params.setClusters(_filters->getClusterIds());
|
||||
params.setClusters(_filters.getClusterIds());
|
||||
params.setArtist(_artistId, linkTypes, excludedLinkTypes);
|
||||
params.setRange(range);
|
||||
params.setSortMethod(ReleaseSortMethod::DateDesc);
|
||||
@@ -315,7 +317,7 @@ Artist::addSomeNonReleaseTracks()
|
||||
const Range range {static_cast<std::size_t>(_trackContainer->getCount()), _tracksBatchSize};
|
||||
|
||||
Track::FindParameters params;
|
||||
params.setClusters(_filters->getClusterIds());
|
||||
params.setClusters(_filters.getClusterIds());
|
||||
params.setArtist(_artistId);
|
||||
params.setRange(range);
|
||||
params.setSortMethod(TrackSortMethod::Name);
|
||||
@@ -333,7 +335,7 @@ Artist::addSomeNonReleaseTracks()
|
||||
}
|
||||
|
||||
const Track::pointer track {Track::find(LmsApp->getDbSession(), trackId)};
|
||||
_trackContainer->add(TrackListHelpers::createEntry(track, tracksAction));
|
||||
_trackContainer->add(TrackListHelpers::createEntry(track, _playQueueController));
|
||||
|
||||
areTracksAdded = true;
|
||||
}
|
||||
|
||||
@@ -19,13 +19,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "services/database/ArtistId.hpp"
|
||||
#include "services/database/Object.hpp"
|
||||
#include "services/database/Types.hpp"
|
||||
#include "utils/EnumSet.hpp"
|
||||
#include "common/Template.hpp"
|
||||
|
||||
#include "PlayQueueAction.hpp"
|
||||
|
||||
namespace Database
|
||||
{
|
||||
class Artist;
|
||||
@@ -34,17 +33,14 @@ namespace Database
|
||||
|
||||
namespace UserInterface
|
||||
{
|
||||
|
||||
class Filters;
|
||||
class PlayQueueController;
|
||||
class InfiniteScrollingContainer;
|
||||
|
||||
class Artist : public Template
|
||||
{
|
||||
public:
|
||||
Artist(Filters* filters);
|
||||
|
||||
PlayQueueActionArtistSignal artistsAction;
|
||||
PlayQueueActionTrackSignal tracksAction;
|
||||
Artist(Filters& filters, PlayQueueController& controller);
|
||||
|
||||
private:
|
||||
void refreshView();
|
||||
@@ -60,7 +56,8 @@ namespace UserInterface
|
||||
static constexpr std::size_t _tracksBatchSize {6};
|
||||
static constexpr std::size_t _tracksMaxCount {160};
|
||||
|
||||
Filters* _filters {};
|
||||
Filters& _filters;
|
||||
PlayQueueController& _playQueueController;
|
||||
InfiniteScrollingContainer* _releaseContainer {};
|
||||
InfiniteScrollingContainer* _appearsOnReleaseContainer {};
|
||||
InfiniteScrollingContainer* _trackContainer {};
|
||||
|
||||
+81
-209
@@ -19,17 +19,8 @@
|
||||
|
||||
#include "Explore.hpp"
|
||||
|
||||
#include <Wt/WApplication.h>
|
||||
#include <Wt/WStackedWidget.h>
|
||||
#include <Wt/WTemplate.h>
|
||||
#include <Wt/WText.h>
|
||||
|
||||
#include "services/database/Artist.hpp"
|
||||
#include "services/database/Release.hpp"
|
||||
#include "services/database/Session.hpp"
|
||||
#include "services/database/Track.hpp"
|
||||
#include "utils/Logger.hpp"
|
||||
|
||||
#include "LmsApplication.hpp"
|
||||
|
||||
#include "ArtistsView.hpp"
|
||||
#include "ArtistView.hpp"
|
||||
@@ -43,215 +34,96 @@
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
namespace {
|
||||
namespace {
|
||||
|
||||
void
|
||||
handleContentsPathChange(Wt::WStackedWidget* stack)
|
||||
{
|
||||
enum Idx
|
||||
{
|
||||
IdxArtists = 0,
|
||||
IdxArtist,
|
||||
IdxTrackLists,
|
||||
IdxTrackList,
|
||||
IdxReleases,
|
||||
IdxRelease,
|
||||
IdxSearch,
|
||||
IdxTracks,
|
||||
};
|
||||
|
||||
static const std::map<std::string, int> indexes =
|
||||
{
|
||||
{ "/artists", IdxArtists },
|
||||
{ "/artist", IdxArtist },
|
||||
{ "/tracklists", IdxTrackLists },
|
||||
{ "/tracklist", IdxTrackList },
|
||||
{ "/releases", IdxReleases },
|
||||
{ "/release", IdxRelease },
|
||||
{ "/search", IdxSearch },
|
||||
{ "/tracks", IdxTracks },
|
||||
};
|
||||
|
||||
for (const auto& index : indexes)
|
||||
{
|
||||
if (wApp->internalPathMatches(index.first))
|
||||
void
|
||||
handleContentsPathChange(Wt::WStackedWidget* stack)
|
||||
{
|
||||
stack->setCurrentIndex(index.second);
|
||||
return;
|
||||
enum Idx
|
||||
{
|
||||
IdxArtists = 0,
|
||||
IdxArtist,
|
||||
IdxTrackLists,
|
||||
IdxTrackList,
|
||||
IdxReleases,
|
||||
IdxRelease,
|
||||
IdxSearch,
|
||||
IdxTracks,
|
||||
};
|
||||
|
||||
static const std::map<std::string, int> indexes =
|
||||
{
|
||||
{ "/artists", IdxArtists },
|
||||
{ "/artist", IdxArtist },
|
||||
{ "/tracklists", IdxTrackLists },
|
||||
{ "/tracklist", IdxTrackList },
|
||||
{ "/releases", IdxReleases },
|
||||
{ "/release", IdxRelease },
|
||||
{ "/search", IdxSearch },
|
||||
{ "/tracks", IdxTracks },
|
||||
};
|
||||
|
||||
for (const auto& index : indexes)
|
||||
{
|
||||
if (wApp->internalPathMatches(index.first))
|
||||
{
|
||||
stack->setCurrentIndex(index.second);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
Explore::Explore(Filters* filters)
|
||||
: Wt::WTemplate {Wt::WString::tr("Lms.Explore.template")}
|
||||
, _filters {filters}
|
||||
{
|
||||
addFunction("tr", &Functions::tr);
|
||||
|
||||
// Contents
|
||||
Wt::WStackedWidget* contentsStack {bindNew<Wt::WStackedWidget>("contents")};
|
||||
contentsStack->setOverflow(Wt::Overflow::Visible); // wt makes it hidden by default
|
||||
|
||||
// same order as enum Idx
|
||||
auto artists = std::make_unique<Artists>(*_filters);
|
||||
contentsStack->addWidget(std::move(artists));
|
||||
|
||||
auto artist = std::make_unique<Artist>(_filters);
|
||||
artist->artistsAction.connect(this, &Explore::handleArtistsAction);
|
||||
artist->tracksAction.connect(this, &Explore::handleTracksAction);
|
||||
contentsStack->addWidget(std::move(artist));
|
||||
|
||||
auto trackLists {std::make_unique<TrackLists>(*_filters)};
|
||||
TrackLists* trackListsPtr {trackLists.get()};
|
||||
trackLists->trackListAction.connect(this, &Explore::handleTrackListAction);
|
||||
contentsStack->addWidget(std::move(trackLists));
|
||||
|
||||
auto trackList {std::make_unique<TrackList>(*_filters)};
|
||||
TrackList* trackListPtr {trackList.get()};
|
||||
trackList->trackListAction.connect(this, &Explore::handleTrackListAction);
|
||||
trackList->tracksAction.connect(this, &Explore::handleTracksAction);
|
||||
contentsStack->addWidget(std::move(trackList));
|
||||
|
||||
trackListPtr->trackListDeleted.connect(trackListsPtr, &TrackLists::onTrackListDeleted);
|
||||
|
||||
auto releases = std::make_unique<Releases>(*_filters);
|
||||
releases->releasesAction.connect(this, &Explore::handleReleasesAction);
|
||||
contentsStack->addWidget(std::move(releases));
|
||||
|
||||
auto release = std::make_unique<Release>(_filters);
|
||||
release->releasesAction.connect(this, &Explore::handleReleasesAction);
|
||||
release->tracksAction.connect(this, &Explore::handleTracksAction);
|
||||
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);
|
||||
tracks->tracksAction.connect(this, &Explore::handleTracksAction);
|
||||
contentsStack->addWidget(std::move(tracks));
|
||||
|
||||
wApp->internalPathChanged().connect(this, [=]
|
||||
Explore::Explore(Filters& filters, PlayQueue& playQueue)
|
||||
: Wt::WTemplate {Wt::WString::tr("Lms.Explore.template")}
|
||||
, _playQueueController {filters, playQueue}
|
||||
{
|
||||
addFunction("tr", &Functions::tr);
|
||||
|
||||
// Contents
|
||||
Wt::WStackedWidget* contentsStack {bindNew<Wt::WStackedWidget>("contents")};
|
||||
contentsStack->setOverflow(Wt::Overflow::Visible); // wt makes it hidden by default
|
||||
|
||||
// same order as enum Idx
|
||||
auto artists = std::make_unique<Artists>(filters);
|
||||
contentsStack->addWidget(std::move(artists));
|
||||
|
||||
auto artist = std::make_unique<Artist>(filters, _playQueueController);
|
||||
contentsStack->addWidget(std::move(artist));
|
||||
|
||||
auto trackLists {std::make_unique<TrackLists>(filters)};
|
||||
contentsStack->addWidget(std::move(trackLists));
|
||||
|
||||
auto trackList {std::make_unique<TrackList>(filters, _playQueueController)};
|
||||
trackList->trackListDeleted.connect(trackLists.get(), &TrackLists::onTrackListDeleted);
|
||||
contentsStack->addWidget(std::move(trackList));
|
||||
|
||||
auto releases = std::make_unique<Releases>(filters, _playQueueController);
|
||||
contentsStack->addWidget(std::move(releases));
|
||||
|
||||
auto release = std::make_unique<Release>(filters, _playQueueController);
|
||||
contentsStack->addWidget(std::move(release));
|
||||
|
||||
auto search = std::make_unique<SearchView>(filters, _playQueueController);
|
||||
_search = search.get();
|
||||
contentsStack->addWidget(std::move(search));
|
||||
|
||||
auto tracks = std::make_unique<Tracks>(filters, _playQueueController);
|
||||
contentsStack->addWidget(std::move(tracks));
|
||||
|
||||
wApp->internalPathChanged().connect(this, [=]
|
||||
{
|
||||
handleContentsPathChange(contentsStack);
|
||||
});
|
||||
|
||||
handleContentsPathChange(contentsStack);
|
||||
});
|
||||
|
||||
handleContentsPathChange(contentsStack);
|
||||
}
|
||||
|
||||
void
|
||||
Explore::search(const Wt::WString& searchText)
|
||||
{
|
||||
_search->refreshView(searchText);
|
||||
}
|
||||
|
||||
static
|
||||
std::vector<Database::TrackId>
|
||||
getArtistsTracks(Database::Session& session, const std::vector<Database::ArtistId>& artistsId, const std::vector<Database::ClusterId>& clusters, std::size_t maxTrackCount)
|
||||
{
|
||||
assert(maxTrackCount);
|
||||
|
||||
std::vector<Database::TrackId> res;
|
||||
|
||||
auto transaction {session.createSharedTransaction()};
|
||||
|
||||
for (const Database::ArtistId artistId : artistsId)
|
||||
{
|
||||
Database::Track::FindParameters params;
|
||||
params.setArtist(artistId);
|
||||
params.setSortMethod(Database::TrackSortMethod::DateDescAndRelease);
|
||||
params.setClusters(clusters);
|
||||
params.setRange({0, maxTrackCount - res.size()});
|
||||
|
||||
const auto tracks {Database::Track::find(session, params)};
|
||||
|
||||
res.reserve(res.size() + tracks.results.size());
|
||||
res.insert(std::end(res), std::cbegin(tracks.results), std::cend(tracks.results));
|
||||
|
||||
if (res.size() == maxTrackCount)
|
||||
break;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static
|
||||
std::vector<Database::TrackId>
|
||||
getReleasesTracks(Database::Session& session, const std::vector<Database::ReleaseId>& releasesId, const std::vector<Database::ClusterId>& clusters, std::size_t maxTrackCount)
|
||||
{
|
||||
using namespace Database;
|
||||
assert(maxTrackCount);
|
||||
|
||||
std::vector<TrackId> res;
|
||||
|
||||
auto transaction {session.createSharedTransaction()};
|
||||
|
||||
for (const ReleaseId releaseId : releasesId)
|
||||
void
|
||||
Explore::search(const Wt::WString& searchText)
|
||||
{
|
||||
Database::Track::FindParameters params;
|
||||
params.setRelease(releaseId);
|
||||
params.setSortMethod(Database::TrackSortMethod::Release);
|
||||
params.setClusters(clusters);
|
||||
params.setRange({0, maxTrackCount - res.size()});
|
||||
|
||||
const auto tracks {Database::Track::find(session, params)};
|
||||
|
||||
res.reserve(res.size() + tracks.results.size());
|
||||
res.insert(std::end(res), std::cbegin(tracks.results), std::cend(tracks.results));
|
||||
|
||||
if (res.size() == maxTrackCount)
|
||||
break;
|
||||
_search->refreshView(searchText);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static
|
||||
std::vector<Database::TrackId>
|
||||
getTrackListTracks(Database::Session& session, Database::TrackListId trackListId, const std::vector<Database::ClusterId>& clusters, std::size_t maxTrackCount)
|
||||
{
|
||||
using namespace Database;
|
||||
assert(maxTrackCount);
|
||||
|
||||
auto transaction {session.createSharedTransaction()};
|
||||
|
||||
Database::Track::FindParameters params;
|
||||
params.setTrackList(trackListId);
|
||||
params.setClusters(clusters);
|
||||
params.setRange({0, maxTrackCount});
|
||||
params.setSortMethod(TrackSortMethod::TrackList);
|
||||
params.setDistinct(false);
|
||||
|
||||
return Database::Track::find(session, params).results;
|
||||
}
|
||||
|
||||
void
|
||||
Explore::handleArtistsAction(PlayQueueAction action, const std::vector<Database::ArtistId>& artistsId)
|
||||
{
|
||||
tracksAction.emit(action, getArtistsTracks(LmsApp->getDbSession(), artistsId, _filters->getClusterIds(), _maxTrackCount));
|
||||
}
|
||||
|
||||
void
|
||||
Explore::handleReleasesAction(PlayQueueAction action, const std::vector<Database::ReleaseId>& releasesId)
|
||||
{
|
||||
tracksAction.emit(action, getReleasesTracks(LmsApp->getDbSession(), releasesId, _filters->getClusterIds(), _maxTrackCount));
|
||||
}
|
||||
|
||||
void
|
||||
Explore::handleTrackListAction(PlayQueueAction action, Database::TrackListId trackListId)
|
||||
{
|
||||
tracksAction.emit(action, getTrackListTracks(LmsApp->getDbSession(), trackListId, _filters->getClusterIds(), _maxTrackCount));
|
||||
}
|
||||
|
||||
void
|
||||
Explore::handleTracksAction(PlayQueueAction action, const std::vector<Database::TrackId>& tracksId)
|
||||
{
|
||||
// consider things are already filtered here, and _maxTrackCount honored playqueue side...
|
||||
tracksAction.emit(action, tracksId);
|
||||
}
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
|
||||
@@ -20,34 +20,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <Wt/WTemplate.h>
|
||||
|
||||
#include "services/database/Types.hpp"
|
||||
#include "PlayQueueAction.hpp"
|
||||
#include "PlayQueueController.hpp"
|
||||
|
||||
namespace UserInterface
|
||||
{
|
||||
class Filters;
|
||||
class SearchView;
|
||||
class PlayQueue;
|
||||
|
||||
class Explore : public Wt::WTemplate
|
||||
{
|
||||
public:
|
||||
Explore(Filters* filters);
|
||||
Explore(Filters& filters, PlayQueue& playQueue);
|
||||
|
||||
void search(const Wt::WString& searchText);
|
||||
|
||||
PlayQueueActionTrackSignal tracksAction;
|
||||
void setMaxTrackCountForAction(std::size_t maxTrackCount) { _maxTrackCount = maxTrackCount; }
|
||||
PlayQueueController& getPlayQueueController() { return _playQueueController; }
|
||||
|
||||
private:
|
||||
void handleArtistsAction(PlayQueueAction action, const std::vector<Database::ArtistId>& artistsId);
|
||||
void handleReleasesAction(PlayQueueAction action, const std::vector<Database::ReleaseId>& releasesId);
|
||||
void handleTrackListAction(PlayQueueAction action, Database::TrackListId trackiListId);
|
||||
void handleTracksAction(PlayQueueAction action, const std::vector<Database::TrackId>& tracksId);
|
||||
|
||||
Filters* _filters {};
|
||||
PlayQueueController _playQueueController;
|
||||
SearchView* _search {};
|
||||
std::size_t _maxTrackCount {};
|
||||
};
|
||||
} // namespace UserInterface
|
||||
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "services/database/ClusterId.hpp"
|
||||
#include "services/database/Release.hpp"
|
||||
#include "services/database/Session.hpp"
|
||||
#include "services/database/Track.hpp"
|
||||
#include "explore/Filters.hpp"
|
||||
#include "explore/PlayQueueController.hpp"
|
||||
#include "PlayQueue.hpp"
|
||||
#include "LmsApplication.hpp"
|
||||
|
||||
namespace UserInterface
|
||||
{
|
||||
|
||||
static
|
||||
std::vector<Database::TrackId>
|
||||
getArtistsTracks(Database::Session& session, const std::vector<Database::ArtistId>& artistsId, const std::vector<Database::ClusterId>& clusters, std::size_t maxTrackCount)
|
||||
{
|
||||
assert(maxTrackCount);
|
||||
|
||||
std::vector<Database::TrackId> res;
|
||||
|
||||
auto transaction {session.createSharedTransaction()};
|
||||
|
||||
for (const Database::ArtistId artistId : artistsId)
|
||||
{
|
||||
Database::Track::FindParameters params;
|
||||
params.setArtist(artistId);
|
||||
params.setSortMethod(Database::TrackSortMethod::DateDescAndRelease);
|
||||
params.setClusters(clusters);
|
||||
params.setRange({0, maxTrackCount - res.size()});
|
||||
|
||||
const auto tracks {Database::Track::find(session, params)};
|
||||
|
||||
res.reserve(res.size() + tracks.results.size());
|
||||
res.insert(std::end(res), std::cbegin(tracks.results), std::cend(tracks.results));
|
||||
|
||||
if (res.size() == maxTrackCount)
|
||||
break;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static
|
||||
std::vector<Database::TrackId>
|
||||
getReleasesTracks(Database::Session& session, const std::vector<Database::ReleaseId>& releasesId, const std::vector<Database::ClusterId>& clusters, std::size_t maxTrackCount)
|
||||
{
|
||||
using namespace Database;
|
||||
assert(maxTrackCount);
|
||||
|
||||
std::vector<TrackId> res;
|
||||
|
||||
auto transaction {session.createSharedTransaction()};
|
||||
|
||||
for (const ReleaseId releaseId : releasesId)
|
||||
{
|
||||
Database::Track::FindParameters params;
|
||||
params.setRelease(releaseId);
|
||||
params.setSortMethod(Database::TrackSortMethod::Release);
|
||||
params.setClusters(clusters);
|
||||
params.setRange({0, maxTrackCount - res.size()});
|
||||
|
||||
const auto tracks {Database::Track::find(session, params)};
|
||||
|
||||
res.reserve(res.size() + tracks.results.size());
|
||||
res.insert(std::end(res), std::cbegin(tracks.results), std::cend(tracks.results));
|
||||
|
||||
if (res.size() == maxTrackCount)
|
||||
break;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static
|
||||
std::vector<Database::TrackId>
|
||||
getTrackListTracks(Database::Session& session, Database::TrackListId trackListId, const std::vector<Database::ClusterId>& clusters, std::size_t maxTrackCount)
|
||||
{
|
||||
using namespace Database;
|
||||
assert(maxTrackCount);
|
||||
|
||||
auto transaction {session.createSharedTransaction()};
|
||||
|
||||
Database::Track::FindParameters params;
|
||||
params.setTrackList(trackListId);
|
||||
params.setClusters(clusters);
|
||||
params.setRange({0, maxTrackCount});
|
||||
params.setSortMethod(TrackSortMethod::TrackList);
|
||||
params.setDistinct(false);
|
||||
|
||||
return Database::Track::find(session, params).results;
|
||||
}
|
||||
|
||||
PlayQueueController::PlayQueueController(Filters& filters, PlayQueue& playQueue)
|
||||
: _filters {filters}
|
||||
, _playQueue {playQueue}
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
PlayQueueController::processCommand(Command command, const std::vector<Database::ArtistId>& artistIds)
|
||||
{
|
||||
const std::vector<Database::TrackId> tracks {getArtistsTracks(LmsApp->getDbSession(), artistIds, _filters.getClusterIds(), _maxTrackCountToEnqueue)};
|
||||
processCommand(command, tracks);
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
PlayQueueController::processCommand(Command command, const std::vector<Database::ReleaseId>& releaseIds)
|
||||
{
|
||||
const std::vector<Database::TrackId> tracks {getReleasesTracks(LmsApp->getDbSession(), releaseIds, _filters.getClusterIds(), _maxTrackCountToEnqueue)};
|
||||
processCommand(command, tracks);
|
||||
}
|
||||
|
||||
void
|
||||
PlayQueueController::processCommand(Command command, const std::vector<Database::TrackId>& trackIds)
|
||||
{
|
||||
// consider things are already filtered here, and _maxTrackCount honored playqueue side...
|
||||
switch (command)
|
||||
{
|
||||
case Command::Play:
|
||||
_playQueue.play(trackIds);
|
||||
break;
|
||||
case Command::PlayShuffled:
|
||||
_playQueue.playShuffled(trackIds);
|
||||
break;
|
||||
case Command::PlayOrAddLast:
|
||||
_playQueue.playOrAddLast(trackIds);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PlayQueueController::processCommand(Command command, Database::TrackListId trackListId)
|
||||
{
|
||||
const std::vector<Database::TrackId> tracks {getTrackListTracks(LmsApp->getDbSession(), trackListId, _filters.getClusterIds(), _maxTrackCountToEnqueue)};
|
||||
processCommand(command, tracks);
|
||||
}
|
||||
|
||||
void
|
||||
PlayQueueController::playTrackInRelease(Database::TrackId trackId)
|
||||
{
|
||||
Database::ReleaseId releaseId;
|
||||
{
|
||||
auto transaction {LmsApp->getDbSession().createSharedTransaction()};
|
||||
const Database::Track::pointer track {Database::Track::find(LmsApp->getDbSession(), trackId)};
|
||||
if (!track || !track->getRelease())
|
||||
return;
|
||||
|
||||
releaseId = track->getRelease()->getId();
|
||||
}
|
||||
|
||||
const std::vector<Database::TrackId> tracks {getReleasesTracks(LmsApp->getDbSession(), {releaseId}, _filters.getClusterIds(), _maxTrackCountToEnqueue)};
|
||||
auto itTrack {std::find(std::cbegin(tracks), std::cend(tracks), trackId)};
|
||||
if (itTrack == std::cend(tracks))
|
||||
return;
|
||||
|
||||
const std::size_t index {static_cast<std::size_t>(std::distance(std::cbegin(tracks), itTrack))};
|
||||
_playQueue.playAtIndex(tracks, index);
|
||||
}
|
||||
} // namespace UserInterface
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Emeric Poupon
|
||||
* Copyright (C) 2022 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
@@ -20,7 +20,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <Wt/WSignal.h>
|
||||
|
||||
#include "services/database/ArtistId.hpp"
|
||||
#include "services/database/ReleaseId.hpp"
|
||||
@@ -29,16 +28,34 @@
|
||||
|
||||
namespace UserInterface
|
||||
{
|
||||
enum class PlayQueueAction
|
||||
{
|
||||
Play,
|
||||
PlayLast,
|
||||
PlayShuffled,
|
||||
};
|
||||
class Filters;
|
||||
class PlayQueue;
|
||||
|
||||
using PlayQueueActionArtistSignal = Wt::Signal<PlayQueueAction, const std::vector<Database::ArtistId>&>;
|
||||
using PlayQueueActionReleaseSignal = Wt::Signal<PlayQueueAction, const std::vector<Database::ReleaseId>&>;
|
||||
using PlayQueueActionTrackSignal = Wt::Signal<PlayQueueAction, const std::vector<Database::TrackId>&>;
|
||||
using PlayQueueActionTrackListSignal = Wt::Signal<PlayQueueAction, Database::TrackListId>;
|
||||
// Used to interact with the play queue, using the current exploration filters
|
||||
class PlayQueueController
|
||||
{
|
||||
public:
|
||||
PlayQueueController(Filters& filters, PlayQueue& playQueue);
|
||||
|
||||
enum class Command
|
||||
{
|
||||
Play,
|
||||
PlayOrAddLast,
|
||||
PlayShuffled,
|
||||
};
|
||||
|
||||
void processCommand(Command command, const std::vector<Database::ArtistId>&);
|
||||
void processCommand(Command command, const std::vector<Database::ReleaseId>&);
|
||||
void processCommand(Command command, const std::vector<Database::TrackId>&);
|
||||
void processCommand(Command command, Database::TrackListId);
|
||||
void playTrackInRelease(Database::TrackId);
|
||||
|
||||
void setMaxTrackCountToEnqueue(std::size_t maxTrackCount) { _maxTrackCountToEnqueue = maxTrackCount; }
|
||||
|
||||
private:
|
||||
Filters& _filters;
|
||||
PlayQueue& _playQueue;
|
||||
std::size_t _maxTrackCountToEnqueue {};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -34,20 +34,22 @@
|
||||
|
||||
#include "common/Template.hpp"
|
||||
#include "resource/DownloadResource.hpp"
|
||||
#include "Filters.hpp"
|
||||
#include "explore/Filters.hpp"
|
||||
#include "explore/PlayQueueController.hpp"
|
||||
#include "explore/ReleaseListHelpers.hpp"
|
||||
#include "LmsApplication.hpp"
|
||||
#include "LmsApplicationException.hpp"
|
||||
#include "MediaPlayer.hpp"
|
||||
#include "ReleaseListHelpers.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
using namespace Database;
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
Release::Release(Filters* filters)
|
||||
Release::Release(Filters& filters, PlayQueueController& playQueueController)
|
||||
: Template {Wt::WString::tr("Lms.Explore.Release.template")}
|
||||
, _filters {filters}
|
||||
, _playQueueController {playQueueController}
|
||||
{
|
||||
addFunction("tr", &Wt::WTemplate::Functions::tr);
|
||||
addFunction("id", &Wt::WTemplate::Functions::id);
|
||||
@@ -57,7 +59,7 @@ Release::Release(Filters* filters)
|
||||
refreshView();
|
||||
});
|
||||
|
||||
filters->updated().connect([this]
|
||||
_filters.updated().connect([this]
|
||||
{
|
||||
refreshView();
|
||||
});
|
||||
@@ -147,7 +149,7 @@ Release::refreshView()
|
||||
Wt::WInteractWidget* entry {clusterContainers->addWidget(Utils::createCluster(clusterId))};
|
||||
entry->clicked().connect([=]
|
||||
{
|
||||
_filters->add(clusterId);
|
||||
_filters.add(clusterId);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -156,19 +158,19 @@ Release::refreshView()
|
||||
bindNew<Wt::WPushButton>("play-btn", Wt::WString::tr("Lms.Explore.play"), Wt::TextFormat::XHTML)
|
||||
->clicked().connect([=]
|
||||
{
|
||||
releasesAction.emit(PlayQueueAction::Play, {*releaseId});
|
||||
_playQueueController.processCommand(PlayQueueController::Command::Play, {*releaseId});
|
||||
});
|
||||
|
||||
bindNew<Wt::WPushButton>("play-shuffled", Wt::WString::tr("Lms.Explore.play-shuffled"), Wt::TextFormat::Plain)
|
||||
->clicked().connect([=]
|
||||
{
|
||||
releasesAction.emit(PlayQueueAction::PlayShuffled, {*releaseId});
|
||||
_playQueueController.processCommand(PlayQueueController::Command::PlayShuffled, {*releaseId});
|
||||
});
|
||||
|
||||
bindNew<Wt::WPushButton>("play-last", Wt::WString::tr("Lms.Explore.play-last"), Wt::TextFormat::Plain)
|
||||
->clicked().connect([=]
|
||||
{
|
||||
releasesAction.emit(PlayQueueAction::PlayLast, {*releaseId});
|
||||
_playQueueController.processCommand(PlayQueueController::Command::PlayOrAddLast, {*releaseId});
|
||||
});
|
||||
|
||||
bindNew<Wt::WPushButton>("download", Wt::WString::tr("Lms.Explore.download"))
|
||||
@@ -238,7 +240,7 @@ Release::refreshView()
|
||||
Database::Track::FindParameters params;
|
||||
params.setRelease(*releaseId);
|
||||
params.setSortMethod(Database::TrackSortMethod::Release);
|
||||
params.setClusters(_filters->getClusterIds());
|
||||
params.setClusters(_filters.getClusterIds());
|
||||
|
||||
const auto tracks {Database::Track::find(LmsApp->getDbSession(), params)};
|
||||
for (const Database::TrackId trackId : tracks.results)
|
||||
@@ -285,7 +287,7 @@ Release::refreshView()
|
||||
Wt::WPushButton* playBtn {entry->bindNew<Wt::WPushButton>("play-btn", Wt::WString::tr("Lms.template.play-btn"), Wt::TextFormat::XHTML)};
|
||||
playBtn->clicked().connect([=]
|
||||
{
|
||||
tracksAction.emit(PlayQueueAction::Play, {trackId});
|
||||
_playQueueController.playTrackInRelease(trackId);
|
||||
});
|
||||
|
||||
{
|
||||
@@ -293,7 +295,7 @@ Release::refreshView()
|
||||
entry->bindNew<Wt::WPushButton>("play-last", Wt::WString::tr("Lms.Explore.play-last"))
|
||||
->clicked().connect([=]
|
||||
{
|
||||
tracksAction.emit(PlayQueueAction::PlayLast, {trackId});
|
||||
_playQueueController.processCommand(PlayQueueController::Command::PlayOrAddLast, {trackId});
|
||||
});
|
||||
|
||||
auto isStarred {[=] { return Service<Scrobbling::IScrobblingService>::get()->isStarred(LmsApp->getUserId(), trackId); }};
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "services/database/Object.hpp"
|
||||
#include "services/database/ReleaseId.hpp"
|
||||
#include "common/Template.hpp"
|
||||
#include "PlayQueueAction.hpp"
|
||||
|
||||
namespace Database
|
||||
{
|
||||
@@ -31,15 +31,12 @@ namespace Database
|
||||
namespace UserInterface
|
||||
{
|
||||
class Filters;
|
||||
|
||||
class PlayQueueController;
|
||||
|
||||
class Release : public Template
|
||||
{
|
||||
public:
|
||||
Release(Filters* filters);
|
||||
|
||||
PlayQueueActionReleaseSignal releasesAction;
|
||||
PlayQueueActionTrackSignal tracksAction;
|
||||
Release(Filters& filters, PlayQueueController& playQueueController);
|
||||
|
||||
private:
|
||||
void refreshView();
|
||||
@@ -48,7 +45,8 @@ namespace UserInterface
|
||||
void refreshLinks(const Database::ObjectPtr<Database::Release>& release);
|
||||
void refreshSimilarReleases(const std::vector<Database::ReleaseId>& similarReleasesId);
|
||||
|
||||
Filters* _filters {};
|
||||
Filters& _filters;
|
||||
PlayQueueController& _playQueueController;
|
||||
};
|
||||
} // namespace UserInterface
|
||||
|
||||
|
||||
@@ -26,16 +26,18 @@
|
||||
|
||||
#include "common/InfiniteScrollingContainer.hpp"
|
||||
#include "common/Template.hpp"
|
||||
#include "ReleaseListHelpers.hpp"
|
||||
#include "Filters.hpp"
|
||||
#include "explore/Filters.hpp"
|
||||
#include "explore/PlayQueueController.hpp"
|
||||
#include "explore/ReleaseListHelpers.hpp"
|
||||
#include "LmsApplication.hpp"
|
||||
|
||||
using namespace Database;
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
Releases::Releases(Filters& filters)
|
||||
Releases::Releases(Filters& filters, PlayQueueController& playQueueController)
|
||||
: Template {Wt::WString::tr("Lms.Explore.Releases.template")}
|
||||
, _playQueueController {playQueueController}
|
||||
, _releaseCollector {filters, _defaultMode, _maxCount}
|
||||
{
|
||||
addFunction("tr", &Wt::WTemplate::Functions::tr);
|
||||
@@ -69,18 +71,18 @@ Releases::Releases(Filters& filters)
|
||||
Wt::WPushButton* playBtn {bindNew<Wt::WPushButton>("play-btn", Wt::WString::tr("Lms.Explore.play"), Wt::TextFormat::XHTML)};
|
||||
playBtn->clicked().connect([this]
|
||||
{
|
||||
releasesAction.emit(PlayQueueAction::Play, getAllReleases());
|
||||
_playQueueController.processCommand(PlayQueueController::Command::Play, getAllReleases());
|
||||
});
|
||||
|
||||
bindNew<Wt::WPushButton>("play-shuffled", Wt::WString::tr("Lms.Explore.play-shuffled"), Wt::TextFormat::Plain)
|
||||
->clicked().connect([=]
|
||||
{
|
||||
releasesAction.emit(PlayQueueAction::PlayShuffled, getAllReleases());
|
||||
_playQueueController.processCommand(PlayQueueController::Command::PlayShuffled, getAllReleases());
|
||||
});
|
||||
bindNew<Wt::WPushButton>("play-last", Wt::WString::tr("Lms.Explore.play-last"), Wt::TextFormat::Plain)
|
||||
->clicked().connect([=]
|
||||
{
|
||||
releasesAction.emit(PlayQueueAction::PlayLast, getAllReleases());
|
||||
_playQueueController.processCommand(PlayQueueController::Command::PlayOrAddLast, getAllReleases());
|
||||
});
|
||||
|
||||
_container = bindNew<InfiniteScrollingContainer>("releases", Wt::WString::tr("Lms.Explore.Releases.template.container"));
|
||||
|
||||
@@ -22,20 +22,18 @@
|
||||
#include "services/database/Types.hpp"
|
||||
|
||||
#include "common/Template.hpp"
|
||||
#include "PlayQueueAction.hpp"
|
||||
#include "ReleaseCollector.hpp"
|
||||
|
||||
namespace UserInterface
|
||||
{
|
||||
class Filters;
|
||||
class InfiniteScrollingContainer;
|
||||
class PlayQueueController;
|
||||
|
||||
class Releases : public Template
|
||||
{
|
||||
public:
|
||||
Releases(Filters& filters);
|
||||
|
||||
PlayQueueActionReleaseSignal releasesAction;
|
||||
Releases(Filters& filters, PlayQueueController& playQueueController);
|
||||
|
||||
private:
|
||||
void refreshView();
|
||||
@@ -48,6 +46,7 @@ namespace UserInterface
|
||||
static constexpr std::size_t _batchSize {_maxItemsPerLine};
|
||||
static constexpr std::size_t _maxCount {_maxItemsPerLine * 500};
|
||||
|
||||
PlayQueueController& _playQueueController;
|
||||
Wt::WWidget* _currentActiveItem {};
|
||||
InfiniteScrollingContainer* _container {};
|
||||
ReleaseCollector _releaseCollector;
|
||||
|
||||
@@ -35,12 +35,12 @@ using namespace Database;
|
||||
|
||||
namespace UserInterface
|
||||
{
|
||||
SearchView::SearchView(Filters* filters)
|
||||
SearchView::SearchView(Filters& filters, PlayQueueController& playQueueController)
|
||||
: Wt::WTemplate {Wt::WString::tr("Lms.Explore.Search.template")}
|
||||
, _filters {filters}
|
||||
, _artistCollector {*filters, ArtistCollector::Mode::Search, getMaxCount(Mode::Artist)}
|
||||
, _releaseCollector {*filters, ReleaseCollector::Mode::Search, getMaxCount(Mode::Release)}
|
||||
, _trackCollector {*filters, TrackCollector::Mode::Search, getMaxCount(Mode::Track)}
|
||||
, _playQueueController {playQueueController}
|
||||
, _artistCollector {filters, ArtistCollector::Mode::Search, getMaxCount(Mode::Artist)}
|
||||
, _releaseCollector {filters, ReleaseCollector::Mode::Search, getMaxCount(Mode::Release)}
|
||||
, _trackCollector {filters, TrackCollector::Mode::Search, getMaxCount(Mode::Track)}
|
||||
{
|
||||
addFunction("tr", &Wt::WTemplate::Functions::tr);
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace UserInterface
|
||||
_tracks = bindNew<InfiniteScrollingContainer>("tracks");
|
||||
_tracks->onRequestElements.connect([this] { addSomeTracks(); });
|
||||
|
||||
_filters->updated().connect([=]
|
||||
filters.updated().connect([=]
|
||||
{
|
||||
refreshView();
|
||||
});
|
||||
@@ -152,7 +152,7 @@ namespace UserInterface
|
||||
for (const TrackId trackId : trackIds.results)
|
||||
{
|
||||
const Track::pointer track {Track::find(LmsApp->getDbSession(), trackId)};
|
||||
_tracks->add(TrackListHelpers::createEntry(track, tracksAction));
|
||||
_tracks->add(TrackListHelpers::createEntry(track, _playQueueController));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,20 +27,17 @@
|
||||
#include "ArtistCollector.hpp"
|
||||
#include "ReleaseCollector.hpp"
|
||||
#include "TrackCollector.hpp"
|
||||
#include "PlayQueueAction.hpp"
|
||||
|
||||
namespace UserInterface
|
||||
{
|
||||
|
||||
class InfiniteScrollingContainer;
|
||||
class Filters;
|
||||
class PlayQueueController;
|
||||
|
||||
class SearchView : public Wt::WTemplate
|
||||
{
|
||||
public:
|
||||
SearchView(Filters* filters);
|
||||
|
||||
PlayQueueActionTrackSignal tracksAction;
|
||||
SearchView(Filters& filters, PlayQueueController& playQueueController);
|
||||
|
||||
void refreshView(const Wt::WString& searchText);
|
||||
|
||||
@@ -74,7 +71,7 @@ namespace UserInterface
|
||||
void addSomeReleases();
|
||||
void addSomeTracks();
|
||||
|
||||
Filters* _filters {};
|
||||
PlayQueueController& _playQueueController;
|
||||
ArtistCollector _artistCollector;
|
||||
ReleaseCollector _releaseCollector;
|
||||
TrackCollector _trackCollector;
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include "TrackListHelpers.hpp"
|
||||
|
||||
#include <Wt/WAnchor.h>
|
||||
#include <Wt/WContainerWidget.h>
|
||||
#include <Wt/WImage.h>
|
||||
#include <Wt/WPushButton.h>
|
||||
|
||||
@@ -29,10 +28,10 @@
|
||||
#include "services/scrobbling/IScrobblingService.hpp"
|
||||
#include "services/database/Session.hpp"
|
||||
#include "services/database/Track.hpp"
|
||||
#include "utils/Logger.hpp"
|
||||
#include "utils/Service.hpp"
|
||||
|
||||
#include "common/Template.hpp"
|
||||
#include "explore/PlayQueueController.hpp"
|
||||
#include "resource/DownloadResource.hpp"
|
||||
#include "resource/CoverResource.hpp"
|
||||
#include "LmsApplication.hpp"
|
||||
@@ -44,7 +43,7 @@ using namespace Database;
|
||||
namespace UserInterface::TrackListHelpers
|
||||
{
|
||||
std::unique_ptr<Wt::WWidget>
|
||||
createEntry(const Database::ObjectPtr<Database::Track>& track, PlayQueueActionTrackSignal& tracksAction)
|
||||
createEntry(const Database::ObjectPtr<Database::Track>& track, PlayQueueController& playQueueController)
|
||||
{
|
||||
auto entry {std::make_unique<Template>(Wt::WString::tr("Lms.Explore.Tracks.template.entry"))};
|
||||
auto* entryPtr {entry.get()};
|
||||
@@ -74,16 +73,16 @@ namespace UserInterface::TrackListHelpers
|
||||
entry->bindString("duration", Utils::durationToString(track->getDuration()), Wt::TextFormat::Plain);
|
||||
|
||||
Wt::WPushButton* playBtn {entry->bindNew<Wt::WPushButton>("play-btn", Wt::WString::tr("Lms.template.play-btn"), Wt::TextFormat::XHTML)};
|
||||
playBtn->clicked().connect([trackId, &tracksAction]
|
||||
playBtn->clicked().connect([trackId, &playQueueController]
|
||||
{
|
||||
tracksAction.emit(PlayQueueAction::Play, {trackId});
|
||||
playQueueController.processCommand(PlayQueueController::Command::Play, {trackId});
|
||||
});
|
||||
|
||||
entry->bindNew<Wt::WPushButton>("more-btn", Wt::WString::tr("Lms.template.more-btn"), Wt::TextFormat::XHTML);
|
||||
entry->bindNew<Wt::WPushButton>("play-last", Wt::WString::tr("Lms.Explore.play-last"))
|
||||
->clicked().connect([=, &tracksAction]
|
||||
->clicked().connect([=, &playQueueController]
|
||||
{
|
||||
tracksAction.emit(PlayQueueAction::PlayLast, {trackId});
|
||||
playQueueController.processCommand(PlayQueueController::Command::PlayOrAddLast, {trackId});
|
||||
});
|
||||
|
||||
{
|
||||
|
||||
@@ -23,15 +23,19 @@
|
||||
|
||||
#include <Wt/WWidget.h>
|
||||
#include "services/database/Object.hpp"
|
||||
#include "PlayQueueAction.hpp"
|
||||
|
||||
namespace Database
|
||||
{
|
||||
class Track;
|
||||
}
|
||||
|
||||
namespace UserInterface
|
||||
{
|
||||
class PlayQueueController;
|
||||
}
|
||||
|
||||
namespace UserInterface::TrackListHelpers
|
||||
{
|
||||
std::unique_ptr<Wt::WWidget> createEntry(const Database::ObjectPtr<Database::Track>& track, PlayQueueActionTrackSignal& tracksAction);
|
||||
std::unique_ptr<Wt::WWidget> createEntry(const Database::ObjectPtr<Database::Track>& track, PlayQueueController& playQueueController);
|
||||
} // namespace UserInterface
|
||||
|
||||
|
||||
@@ -30,12 +30,13 @@
|
||||
#include "utils/String.hpp"
|
||||
|
||||
#include "common/InfiniteScrollingContainer.hpp"
|
||||
#include "explore/Filters.hpp"
|
||||
#include "explore/PlayQueueController.hpp"
|
||||
#include "explore/TrackListHelpers.hpp"
|
||||
#include "resource/DownloadResource.hpp"
|
||||
#include "Filters.hpp"
|
||||
#include "LmsApplication.hpp"
|
||||
#include "LmsApplicationException.hpp"
|
||||
#include "ModalManager.hpp"
|
||||
#include "TrackListHelpers.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
using namespace Database;
|
||||
@@ -52,9 +53,10 @@ namespace
|
||||
|
||||
namespace UserInterface
|
||||
{
|
||||
TrackList::TrackList(Filters& filters)
|
||||
TrackList::TrackList(Filters& filters, PlayQueueController& playQueueController)
|
||||
: Template {Wt::WString::tr("Lms.Explore.TrackList.template")}
|
||||
, _filters {filters}
|
||||
, _filters {_filters}
|
||||
, _playQueueController {playQueueController}
|
||||
{
|
||||
addFunction("tr", &Wt::WTemplate::Functions::tr);
|
||||
addFunction("id", &Wt::WTemplate::Functions::id);
|
||||
@@ -64,7 +66,7 @@ namespace UserInterface
|
||||
refreshView();
|
||||
});
|
||||
|
||||
_filters.updated().connect([this]
|
||||
filters.updated().connect([this]
|
||||
{
|
||||
refreshView();
|
||||
});
|
||||
@@ -120,19 +122,19 @@ namespace UserInterface
|
||||
bindNew<Wt::WPushButton>("play-btn", Wt::WString::tr("Lms.Explore.play"), Wt::TextFormat::XHTML)
|
||||
->clicked().connect([=]
|
||||
{
|
||||
trackListAction.emit(PlayQueueAction::Play, *trackListId);
|
||||
_playQueueController.processCommand(PlayQueueController::Command::Play, *trackListId);
|
||||
});
|
||||
|
||||
bindNew<Wt::WPushButton>("play-shuffled", Wt::WString::tr("Lms.Explore.play-shuffled"), Wt::TextFormat::Plain)
|
||||
->clicked().connect([=]
|
||||
{
|
||||
trackListAction.emit(PlayQueueAction::PlayShuffled, *trackListId);
|
||||
_playQueueController.processCommand(PlayQueueController::Command::PlayShuffled, *trackListId);
|
||||
});
|
||||
|
||||
bindNew<Wt::WPushButton>("play-last", Wt::WString::tr("Lms.Explore.play-last"), Wt::TextFormat::Plain)
|
||||
->clicked().connect([=]
|
||||
{
|
||||
trackListAction.emit(PlayQueueAction::PlayLast, *trackListId);
|
||||
_playQueueController.processCommand(PlayQueueController::Command::PlayOrAddLast, *trackListId);
|
||||
});
|
||||
|
||||
bindNew<Wt::WPushButton>("download", Wt::WString::tr("Lms.Explore.download"))
|
||||
@@ -197,7 +199,7 @@ namespace UserInterface
|
||||
for (const TrackId trackId : trackIds.results)
|
||||
{
|
||||
if (const Track::pointer track {Track::find(LmsApp->getDbSession(), trackId)})
|
||||
_container->add(TrackListHelpers::createEntry(track, tracksAction));
|
||||
_container->add(TrackListHelpers::createEntry(track, _playQueueController));
|
||||
}
|
||||
|
||||
_container->setHasMore(trackIds.moreResults && _container->getCount() < _maxCount);
|
||||
|
||||
@@ -23,20 +23,17 @@
|
||||
#include "services/database/Types.hpp"
|
||||
|
||||
#include "common/Template.hpp"
|
||||
#include "PlayQueueAction.hpp"
|
||||
|
||||
namespace UserInterface
|
||||
{
|
||||
class Filters;
|
||||
class InfiniteScrollingContainer;
|
||||
class PlayQueueController;
|
||||
|
||||
class TrackList : public Template
|
||||
{
|
||||
public:
|
||||
TrackList(Filters& filters);
|
||||
|
||||
PlayQueueActionTrackListSignal trackListAction;
|
||||
PlayQueueActionTrackSignal tracksAction;
|
||||
TrackList(Filters& filters, PlayQueueController& playQueueController);
|
||||
|
||||
Wt::Signal<Database::TrackListId> trackListDeleted;
|
||||
|
||||
@@ -48,6 +45,7 @@ namespace UserInterface
|
||||
static constexpr std::size_t _maxCount {8000};
|
||||
|
||||
Filters& _filters;
|
||||
PlayQueueController& _playQueueController;
|
||||
Database::TrackListId _trackListId;
|
||||
InfiniteScrollingContainer* _container {};
|
||||
};
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include "services/database/Types.hpp"
|
||||
|
||||
#include "common/Template.hpp"
|
||||
#include "PlayQueueAction.hpp"
|
||||
|
||||
namespace Database
|
||||
{
|
||||
@@ -44,8 +43,6 @@ namespace UserInterface
|
||||
public:
|
||||
TrackLists(Filters& filters);
|
||||
|
||||
PlayQueueActionTrackListSignal trackListAction;
|
||||
|
||||
void onTrackListDeleted(Database::TrackListId trackListId);
|
||||
|
||||
private:
|
||||
|
||||
@@ -26,17 +26,19 @@
|
||||
#include "utils/Logger.hpp"
|
||||
|
||||
#include "common/InfiniteScrollingContainer.hpp"
|
||||
#include "Filters.hpp"
|
||||
#include "explore/Filters.hpp"
|
||||
#include "explore/PlayQueueController.hpp"
|
||||
#include "explore/TrackListHelpers.hpp"
|
||||
#include "LmsApplication.hpp"
|
||||
#include "TrackListHelpers.hpp"
|
||||
|
||||
using namespace Database;
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
Tracks::Tracks(Filters& filters)
|
||||
: Template {Wt::WString::tr("Lms.Explore.Tracks.template")},
|
||||
_trackCollector {filters, _defaultMode, _maxCount}
|
||||
Tracks::Tracks(Filters& filters, PlayQueueController& playQueueController)
|
||||
: Template {Wt::WString::tr("Lms.Explore.Tracks.template")}
|
||||
, _playQueueController {playQueueController}
|
||||
, _trackCollector {filters, _defaultMode, _maxCount}
|
||||
{
|
||||
addFunction("tr", &Wt::WTemplate::Functions::tr);
|
||||
addFunction("id", &Wt::WTemplate::Functions::id);
|
||||
@@ -69,18 +71,18 @@ _trackCollector {filters, _defaultMode, _maxCount}
|
||||
bindNew<Wt::WPushButton>("play-btn", Wt::WString::tr("Lms.Explore.play"), Wt::TextFormat::XHTML)
|
||||
->clicked().connect([=]
|
||||
{
|
||||
tracksAction.emit(PlayQueueAction::Play, getAllTracks());
|
||||
_playQueueController.processCommand(PlayQueueController::Command::Play, getAllTracks());
|
||||
});
|
||||
|
||||
bindNew<Wt::WPushButton>("play-shuffled", Wt::WString::tr("Lms.Explore.play-shuffled"), Wt::TextFormat::Plain)
|
||||
->clicked().connect([=]
|
||||
{
|
||||
tracksAction.emit(PlayQueueAction::PlayShuffled, getAllTracks());
|
||||
_playQueueController.processCommand(PlayQueueController::Command::PlayShuffled, getAllTracks());
|
||||
});
|
||||
bindNew<Wt::WPushButton>("play-last", Wt::WString::tr("Lms.Explore.play-last"), Wt::TextFormat::Plain)
|
||||
->clicked().connect([=]
|
||||
{
|
||||
tracksAction.emit(PlayQueueAction::PlayLast, getAllTracks());
|
||||
_playQueueController.processCommand(PlayQueueController::Command::PlayOrAddLast, getAllTracks());
|
||||
});
|
||||
|
||||
_container = bindNew<InfiniteScrollingContainer>("tracks");
|
||||
@@ -122,7 +124,7 @@ Tracks::addSome()
|
||||
for (const TrackId trackId : trackIds.results)
|
||||
{
|
||||
if (const Track::pointer track {Track::find(LmsApp->getDbSession(), trackId)})
|
||||
_container->add(TrackListHelpers::createEntry(track, tracksAction));
|
||||
_container->add(TrackListHelpers::createEntry(track, _playQueueController));
|
||||
}
|
||||
|
||||
_container->setHasMore(trackIds.moreResults);
|
||||
|
||||
@@ -22,20 +22,18 @@
|
||||
#include "services/database/Types.hpp"
|
||||
|
||||
#include "common/Template.hpp"
|
||||
#include "PlayQueueAction.hpp"
|
||||
#include "TrackCollector.hpp"
|
||||
|
||||
namespace UserInterface
|
||||
{
|
||||
class Filters;
|
||||
class InfiniteScrollingContainer;
|
||||
class PlayQueueController;
|
||||
|
||||
class Tracks : public Template
|
||||
{
|
||||
public:
|
||||
Tracks(Filters& filters);
|
||||
|
||||
PlayQueueActionTrackSignal tracksAction;
|
||||
Tracks(Filters& filters, PlayQueueController& playQueueController);
|
||||
|
||||
private:
|
||||
void refreshView();
|
||||
@@ -48,6 +46,7 @@ namespace UserInterface
|
||||
static constexpr std::size_t _batchSize {6};
|
||||
static constexpr std::size_t _maxCount {8000};
|
||||
|
||||
PlayQueueController& _playQueueController;
|
||||
Wt::WWidget* _currentActiveItem {};
|
||||
InfiniteScrollingContainer* _container {};
|
||||
TrackCollector _trackCollector;
|
||||
|
||||
Reference in New Issue
Block a user