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/DatabaseCollectorBase.cpp
|
||||||
ui/explore/Explore.cpp
|
ui/explore/Explore.cpp
|
||||||
ui/explore/Filters.cpp
|
ui/explore/Filters.cpp
|
||||||
|
ui/explore/PlayQueueController.cpp
|
||||||
ui/explore/ReleaseCollector.cpp
|
ui/explore/ReleaseCollector.cpp
|
||||||
ui/explore/ReleaseListHelpers.cpp
|
ui/explore/ReleaseListHelpers.cpp
|
||||||
ui/explore/ReleasesView.cpp
|
ui/explore/ReleasesView.cpp
|
||||||
|
|||||||
@@ -502,8 +502,9 @@ LmsApplication::createHome()
|
|||||||
Wt::WStackedWidget* mainStack {main->bindNew<Wt::WStackedWidget>("contents")};
|
Wt::WStackedWidget* mainStack {main->bindNew<Wt::WStackedWidget>("contents")};
|
||||||
mainStack->setOverflow(Wt::Overflow::Visible); // wt makes it hidden by default
|
mainStack->setOverflow(Wt::Overflow::Visible); // wt makes it hidden by default
|
||||||
|
|
||||||
Explore* explore {mainStack->addNew<Explore>(filters)};
|
std::unique_ptr<PlayQueue> playQueue {std::make_unique<PlayQueue>()};
|
||||||
_playQueue = mainStack->addNew<PlayQueue>();
|
Explore* explore {mainStack->addNew<Explore>(*filters, *playQueue)};
|
||||||
|
_playQueue = mainStack->addWidget(std::move(playQueue));
|
||||||
mainStack->addNew<SettingsView>();
|
mainStack->addNew<SettingsView>();
|
||||||
|
|
||||||
searchEdit->enterPressed().connect([=]
|
searchEdit->enterPressed().connect([=]
|
||||||
@@ -525,11 +526,7 @@ LmsApplication::createHome()
|
|||||||
mainStack->addNew<UserView>();
|
mainStack->addNew<UserView>();
|
||||||
}
|
}
|
||||||
|
|
||||||
explore->setMaxTrackCountForAction(_playQueue->getCapacity());
|
explore->getPlayQueueController().setMaxTrackCountToEnqueue(_playQueue->getCapacity());
|
||||||
explore->tracksAction.connect([this] (PlayQueueAction action, const std::vector<Database::TrackId>& trackIds)
|
|
||||||
{
|
|
||||||
_playQueue->processTracks(action, trackIds);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Events from MediaPlayer
|
// Events from MediaPlayer
|
||||||
_mediaPlayer->playNext.connect([this]
|
_mediaPlayer->playNext.connect([this]
|
||||||
|
|||||||
+34
-26
@@ -416,38 +416,46 @@ PlayQueue::enqueueTracks(const std::vector<Database::TrackId>& trackIds)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
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)
|
void
|
||||||
{
|
PlayQueue::playShuffled(const std::vector<Database::TrackId>& trackIds)
|
||||||
case PlayQueueAction::PlayLast:
|
{
|
||||||
nbAddedTracks = enqueueTracks(trackIds);
|
clearTracks();
|
||||||
if (!_trackPos)
|
std::vector<Database::TrackId> shuffledTrackIds {trackIds};
|
||||||
loadTrack(0, true);
|
Random::shuffleContainer(shuffledTrackIds);
|
||||||
break;
|
const std::size_t nbAddedTracks {enqueueTracks(shuffledTrackIds)};
|
||||||
|
loadTrack(0, true);
|
||||||
|
|
||||||
case PlayQueueAction::Play:
|
notifyAddedTracks(nbAddedTracks);
|
||||||
clearTracks();
|
}
|
||||||
nbAddedTracks = enqueueTracks(trackIds);
|
|
||||||
loadTrack(0, true);
|
|
||||||
|
|
||||||
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:
|
notifyAddedTracks(nbAddedTracks);
|
||||||
{
|
}
|
||||||
clearTracks();
|
|
||||||
{
|
|
||||||
std::vector<Database::TrackId> shuffledTrackIds {trackIds};
|
|
||||||
Random::shuffleContainer(shuffledTrackIds);
|
|
||||||
nbAddedTracks = enqueueTracks(shuffledTrackIds);
|
|
||||||
}
|
|
||||||
loadTrack(0, true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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)
|
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});
|
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 <Wt/WText.h>
|
||||||
|
|
||||||
#include "services/database/Object.hpp"
|
#include "services/database/Object.hpp"
|
||||||
|
#include "services/database/TrackId.hpp"
|
||||||
#include "services/database/TrackListId.hpp"
|
#include "services/database/TrackListId.hpp"
|
||||||
#include "PlayQueueAction.hpp"
|
|
||||||
|
|
||||||
#include "common/Template.hpp"
|
#include "common/Template.hpp"
|
||||||
|
|
||||||
@@ -53,7 +53,10 @@ class PlayQueue : public Template
|
|||||||
public:
|
public:
|
||||||
PlayQueue();
|
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
|
// play the next track in the queue
|
||||||
void playNext();
|
void playNext();
|
||||||
@@ -70,6 +73,7 @@ class PlayQueue : public Template
|
|||||||
constexpr std::size_t getCapacity() const { return _capacity; }
|
constexpr std::size_t getCapacity() const { return _capacity; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void notifyAddedTracks(std::size_t nbAddedTracks) const;
|
||||||
Database::ObjectPtr<Database::TrackList> getTrackList() const;
|
Database::ObjectPtr<Database::TrackList> getTrackList() const;
|
||||||
bool isFull() const;
|
bool isFull() const;
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
#include "Filters.hpp"
|
#include "Filters.hpp"
|
||||||
#include "LmsApplication.hpp"
|
#include "LmsApplication.hpp"
|
||||||
#include "LmsApplicationException.hpp"
|
#include "LmsApplicationException.hpp"
|
||||||
|
#include "PlayQueueController.hpp"
|
||||||
#include "ReleaseListHelpers.hpp"
|
#include "ReleaseListHelpers.hpp"
|
||||||
#include "TrackListHelpers.hpp"
|
#include "TrackListHelpers.hpp"
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
@@ -47,9 +48,10 @@ using namespace Database;
|
|||||||
|
|
||||||
namespace UserInterface {
|
namespace UserInterface {
|
||||||
|
|
||||||
Artist::Artist(Filters* filters)
|
Artist::Artist(Filters& filters, PlayQueueController& controller)
|
||||||
: Template {Wt::WString::tr("Lms.Explore.Artist.template")}
|
: Template {Wt::WString::tr("Lms.Explore.Artist.template")}
|
||||||
, _filters {filters}
|
, _filters {filters}
|
||||||
|
, _playQueueController {controller}
|
||||||
{
|
{
|
||||||
addFunction("tr", &Wt::WTemplate::Functions::tr);
|
addFunction("tr", &Wt::WTemplate::Functions::tr);
|
||||||
addFunction("id", &Wt::WTemplate::Functions::id);
|
addFunction("id", &Wt::WTemplate::Functions::id);
|
||||||
@@ -59,7 +61,7 @@ Artist::Artist(Filters* filters)
|
|||||||
refreshView();
|
refreshView();
|
||||||
});
|
});
|
||||||
|
|
||||||
filters->updated().connect([this]
|
filters.updated().connect([this]
|
||||||
{
|
{
|
||||||
refreshView();
|
refreshView();
|
||||||
});
|
});
|
||||||
@@ -140,7 +142,7 @@ Artist::refreshView()
|
|||||||
Wt::WInteractWidget* entry {clusterContainers->addWidget(Utils::createCluster(clusterId))};
|
Wt::WInteractWidget* entry {clusterContainers->addWidget(Utils::createCluster(clusterId))};
|
||||||
entry->clicked().connect([=]
|
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)
|
bindNew<Wt::WPushButton>("play-btn", Wt::WString::tr("Lms.Explore.play"), Wt::TextFormat::XHTML)
|
||||||
->clicked().connect([=]
|
->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)
|
bindNew<Wt::WPushButton>("play-shuffled", Wt::WString::tr("Lms.Explore.play-shuffled"), Wt::TextFormat::Plain)
|
||||||
->clicked().connect([=]
|
->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)
|
bindNew<Wt::WPushButton>("play-last", Wt::WString::tr("Lms.Explore.play-last"), Wt::TextFormat::Plain)
|
||||||
->clicked().connect([=]
|
->clicked().connect([=]
|
||||||
{
|
{
|
||||||
artistsAction.emit(PlayQueueAction::PlayLast, {_artistId});
|
_playQueueController.processCommand(PlayQueueController::Command::PlayOrAddLast, {_artistId});
|
||||||
});
|
});
|
||||||
bindNew<Wt::WPushButton>("download", Wt::WString::tr("Lms.Explore.download"))
|
bindNew<Wt::WPushButton>("download", Wt::WString::tr("Lms.Explore.download"))
|
||||||
->setLink(Wt::WLink {std::make_unique<DownloadArtistResource>(*artistId)});
|
->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};
|
const Range range {static_cast<std::size_t>(releaseContainer.getCount()), _releasesBatchSize};
|
||||||
|
|
||||||
Release::FindParameters params;
|
Release::FindParameters params;
|
||||||
params.setClusters(_filters->getClusterIds());
|
params.setClusters(_filters.getClusterIds());
|
||||||
params.setArtist(_artistId, linkTypes, excludedLinkTypes);
|
params.setArtist(_artistId, linkTypes, excludedLinkTypes);
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
params.setSortMethod(ReleaseSortMethod::DateDesc);
|
params.setSortMethod(ReleaseSortMethod::DateDesc);
|
||||||
@@ -315,7 +317,7 @@ Artist::addSomeNonReleaseTracks()
|
|||||||
const Range range {static_cast<std::size_t>(_trackContainer->getCount()), _tracksBatchSize};
|
const Range range {static_cast<std::size_t>(_trackContainer->getCount()), _tracksBatchSize};
|
||||||
|
|
||||||
Track::FindParameters params;
|
Track::FindParameters params;
|
||||||
params.setClusters(_filters->getClusterIds());
|
params.setClusters(_filters.getClusterIds());
|
||||||
params.setArtist(_artistId);
|
params.setArtist(_artistId);
|
||||||
params.setRange(range);
|
params.setRange(range);
|
||||||
params.setSortMethod(TrackSortMethod::Name);
|
params.setSortMethod(TrackSortMethod::Name);
|
||||||
@@ -333,7 +335,7 @@ Artist::addSomeNonReleaseTracks()
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Track::pointer track {Track::find(LmsApp->getDbSession(), trackId)};
|
const Track::pointer track {Track::find(LmsApp->getDbSession(), trackId)};
|
||||||
_trackContainer->add(TrackListHelpers::createEntry(track, tracksAction));
|
_trackContainer->add(TrackListHelpers::createEntry(track, _playQueueController));
|
||||||
|
|
||||||
areTracksAdded = true;
|
areTracksAdded = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,13 +19,12 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "services/database/ArtistId.hpp"
|
||||||
#include "services/database/Object.hpp"
|
#include "services/database/Object.hpp"
|
||||||
#include "services/database/Types.hpp"
|
#include "services/database/Types.hpp"
|
||||||
#include "utils/EnumSet.hpp"
|
#include "utils/EnumSet.hpp"
|
||||||
#include "common/Template.hpp"
|
#include "common/Template.hpp"
|
||||||
|
|
||||||
#include "PlayQueueAction.hpp"
|
|
||||||
|
|
||||||
namespace Database
|
namespace Database
|
||||||
{
|
{
|
||||||
class Artist;
|
class Artist;
|
||||||
@@ -34,17 +33,14 @@ namespace Database
|
|||||||
|
|
||||||
namespace UserInterface
|
namespace UserInterface
|
||||||
{
|
{
|
||||||
|
|
||||||
class Filters;
|
class Filters;
|
||||||
|
class PlayQueueController;
|
||||||
class InfiniteScrollingContainer;
|
class InfiniteScrollingContainer;
|
||||||
|
|
||||||
class Artist : public Template
|
class Artist : public Template
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Artist(Filters* filters);
|
Artist(Filters& filters, PlayQueueController& controller);
|
||||||
|
|
||||||
PlayQueueActionArtistSignal artistsAction;
|
|
||||||
PlayQueueActionTrackSignal tracksAction;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void refreshView();
|
void refreshView();
|
||||||
@@ -60,7 +56,8 @@ namespace UserInterface
|
|||||||
static constexpr std::size_t _tracksBatchSize {6};
|
static constexpr std::size_t _tracksBatchSize {6};
|
||||||
static constexpr std::size_t _tracksMaxCount {160};
|
static constexpr std::size_t _tracksMaxCount {160};
|
||||||
|
|
||||||
Filters* _filters {};
|
Filters& _filters;
|
||||||
|
PlayQueueController& _playQueueController;
|
||||||
InfiniteScrollingContainer* _releaseContainer {};
|
InfiniteScrollingContainer* _releaseContainer {};
|
||||||
InfiniteScrollingContainer* _appearsOnReleaseContainer {};
|
InfiniteScrollingContainer* _appearsOnReleaseContainer {};
|
||||||
InfiniteScrollingContainer* _trackContainer {};
|
InfiniteScrollingContainer* _trackContainer {};
|
||||||
|
|||||||
+81
-209
@@ -19,17 +19,8 @@
|
|||||||
|
|
||||||
#include "Explore.hpp"
|
#include "Explore.hpp"
|
||||||
|
|
||||||
|
#include <Wt/WApplication.h>
|
||||||
#include <Wt/WStackedWidget.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 "ArtistsView.hpp"
|
||||||
#include "ArtistView.hpp"
|
#include "ArtistView.hpp"
|
||||||
@@ -43,215 +34,96 @@
|
|||||||
|
|
||||||
namespace UserInterface {
|
namespace UserInterface {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void
|
void
|
||||||
handleContentsPathChange(Wt::WStackedWidget* stack)
|
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))
|
|
||||||
{
|
{
|
||||||
stack->setCurrentIndex(index.second);
|
enum Idx
|
||||||
return;
|
{
|
||||||
|
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)
|
Explore::Explore(Filters& filters, PlayQueue& playQueue)
|
||||||
: Wt::WTemplate {Wt::WString::tr("Lms.Explore.template")}
|
: Wt::WTemplate {Wt::WString::tr("Lms.Explore.template")}
|
||||||
, _filters {filters}
|
, _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);
|
|
||||||
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, [=]
|
|
||||||
{
|
{
|
||||||
|
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);
|
||||||
});
|
|
||||||
|
|
||||||
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;
|
void
|
||||||
}
|
Explore::search(const Wt::WString& searchText)
|
||||||
|
|
||||||
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;
|
_search->refreshView(searchText);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
} // namespace UserInterface
|
||||||
|
|
||||||
|
|||||||
@@ -20,34 +20,25 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Wt/WTemplate.h>
|
#include <Wt/WTemplate.h>
|
||||||
|
#include "PlayQueueController.hpp"
|
||||||
#include "services/database/Types.hpp"
|
|
||||||
#include "PlayQueueAction.hpp"
|
|
||||||
|
|
||||||
namespace UserInterface
|
namespace UserInterface
|
||||||
{
|
{
|
||||||
class Filters;
|
class Filters;
|
||||||
class SearchView;
|
class SearchView;
|
||||||
|
class PlayQueue;
|
||||||
|
|
||||||
class Explore : public Wt::WTemplate
|
class Explore : public Wt::WTemplate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Explore(Filters* filters);
|
Explore(Filters& filters, PlayQueue& playQueue);
|
||||||
|
|
||||||
void search(const Wt::WString& searchText);
|
void search(const Wt::WString& searchText);
|
||||||
|
PlayQueueController& getPlayQueueController() { return _playQueueController; }
|
||||||
PlayQueueActionTrackSignal tracksAction;
|
|
||||||
void setMaxTrackCountForAction(std::size_t maxTrackCount) { _maxTrackCount = maxTrackCount; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void handleArtistsAction(PlayQueueAction action, const std::vector<Database::ArtistId>& artistsId);
|
PlayQueueController _playQueueController;
|
||||||
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 {};
|
|
||||||
SearchView* _search {};
|
SearchView* _search {};
|
||||||
std::size_t _maxTrackCount {};
|
|
||||||
};
|
};
|
||||||
} // namespace UserInterface
|
} // 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.
|
* This file is part of LMS.
|
||||||
*
|
*
|
||||||
@@ -20,7 +20,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <Wt/WSignal.h>
|
|
||||||
|
|
||||||
#include "services/database/ArtistId.hpp"
|
#include "services/database/ArtistId.hpp"
|
||||||
#include "services/database/ReleaseId.hpp"
|
#include "services/database/ReleaseId.hpp"
|
||||||
@@ -29,16 +28,34 @@
|
|||||||
|
|
||||||
namespace UserInterface
|
namespace UserInterface
|
||||||
{
|
{
|
||||||
enum class PlayQueueAction
|
class Filters;
|
||||||
{
|
class PlayQueue;
|
||||||
Play,
|
|
||||||
PlayLast,
|
|
||||||
PlayShuffled,
|
|
||||||
};
|
|
||||||
|
|
||||||
using PlayQueueActionArtistSignal = Wt::Signal<PlayQueueAction, const std::vector<Database::ArtistId>&>;
|
// Used to interact with the play queue, using the current exploration filters
|
||||||
using PlayQueueActionReleaseSignal = Wt::Signal<PlayQueueAction, const std::vector<Database::ReleaseId>&>;
|
class PlayQueueController
|
||||||
using PlayQueueActionTrackSignal = Wt::Signal<PlayQueueAction, const std::vector<Database::TrackId>&>;
|
{
|
||||||
using PlayQueueActionTrackListSignal = Wt::Signal<PlayQueueAction, Database::TrackListId>;
|
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 "common/Template.hpp"
|
||||||
#include "resource/DownloadResource.hpp"
|
#include "resource/DownloadResource.hpp"
|
||||||
#include "Filters.hpp"
|
#include "explore/Filters.hpp"
|
||||||
|
#include "explore/PlayQueueController.hpp"
|
||||||
|
#include "explore/ReleaseListHelpers.hpp"
|
||||||
#include "LmsApplication.hpp"
|
#include "LmsApplication.hpp"
|
||||||
#include "LmsApplicationException.hpp"
|
#include "LmsApplicationException.hpp"
|
||||||
#include "MediaPlayer.hpp"
|
#include "MediaPlayer.hpp"
|
||||||
#include "ReleaseListHelpers.hpp"
|
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
|
|
||||||
using namespace Database;
|
using namespace Database;
|
||||||
|
|
||||||
namespace UserInterface {
|
namespace UserInterface {
|
||||||
|
|
||||||
Release::Release(Filters* filters)
|
Release::Release(Filters& filters, PlayQueueController& playQueueController)
|
||||||
: Template {Wt::WString::tr("Lms.Explore.Release.template")}
|
: Template {Wt::WString::tr("Lms.Explore.Release.template")}
|
||||||
, _filters {filters}
|
, _filters {filters}
|
||||||
|
, _playQueueController {playQueueController}
|
||||||
{
|
{
|
||||||
addFunction("tr", &Wt::WTemplate::Functions::tr);
|
addFunction("tr", &Wt::WTemplate::Functions::tr);
|
||||||
addFunction("id", &Wt::WTemplate::Functions::id);
|
addFunction("id", &Wt::WTemplate::Functions::id);
|
||||||
@@ -57,7 +59,7 @@ Release::Release(Filters* filters)
|
|||||||
refreshView();
|
refreshView();
|
||||||
});
|
});
|
||||||
|
|
||||||
filters->updated().connect([this]
|
_filters.updated().connect([this]
|
||||||
{
|
{
|
||||||
refreshView();
|
refreshView();
|
||||||
});
|
});
|
||||||
@@ -147,7 +149,7 @@ Release::refreshView()
|
|||||||
Wt::WInteractWidget* entry {clusterContainers->addWidget(Utils::createCluster(clusterId))};
|
Wt::WInteractWidget* entry {clusterContainers->addWidget(Utils::createCluster(clusterId))};
|
||||||
entry->clicked().connect([=]
|
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)
|
bindNew<Wt::WPushButton>("play-btn", Wt::WString::tr("Lms.Explore.play"), Wt::TextFormat::XHTML)
|
||||||
->clicked().connect([=]
|
->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)
|
bindNew<Wt::WPushButton>("play-shuffled", Wt::WString::tr("Lms.Explore.play-shuffled"), Wt::TextFormat::Plain)
|
||||||
->clicked().connect([=]
|
->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)
|
bindNew<Wt::WPushButton>("play-last", Wt::WString::tr("Lms.Explore.play-last"), Wt::TextFormat::Plain)
|
||||||
->clicked().connect([=]
|
->clicked().connect([=]
|
||||||
{
|
{
|
||||||
releasesAction.emit(PlayQueueAction::PlayLast, {*releaseId});
|
_playQueueController.processCommand(PlayQueueController::Command::PlayOrAddLast, {*releaseId});
|
||||||
});
|
});
|
||||||
|
|
||||||
bindNew<Wt::WPushButton>("download", Wt::WString::tr("Lms.Explore.download"))
|
bindNew<Wt::WPushButton>("download", Wt::WString::tr("Lms.Explore.download"))
|
||||||
@@ -238,7 +240,7 @@ Release::refreshView()
|
|||||||
Database::Track::FindParameters params;
|
Database::Track::FindParameters params;
|
||||||
params.setRelease(*releaseId);
|
params.setRelease(*releaseId);
|
||||||
params.setSortMethod(Database::TrackSortMethod::Release);
|
params.setSortMethod(Database::TrackSortMethod::Release);
|
||||||
params.setClusters(_filters->getClusterIds());
|
params.setClusters(_filters.getClusterIds());
|
||||||
|
|
||||||
const auto tracks {Database::Track::find(LmsApp->getDbSession(), params)};
|
const auto tracks {Database::Track::find(LmsApp->getDbSession(), params)};
|
||||||
for (const Database::TrackId trackId : tracks.results)
|
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)};
|
Wt::WPushButton* playBtn {entry->bindNew<Wt::WPushButton>("play-btn", Wt::WString::tr("Lms.template.play-btn"), Wt::TextFormat::XHTML)};
|
||||||
playBtn->clicked().connect([=]
|
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"))
|
entry->bindNew<Wt::WPushButton>("play-last", Wt::WString::tr("Lms.Explore.play-last"))
|
||||||
->clicked().connect([=]
|
->clicked().connect([=]
|
||||||
{
|
{
|
||||||
tracksAction.emit(PlayQueueAction::PlayLast, {trackId});
|
_playQueueController.processCommand(PlayQueueController::Command::PlayOrAddLast, {trackId});
|
||||||
});
|
});
|
||||||
|
|
||||||
auto isStarred {[=] { return Service<Scrobbling::IScrobblingService>::get()->isStarred(LmsApp->getUserId(), trackId); }};
|
auto isStarred {[=] { return Service<Scrobbling::IScrobblingService>::get()->isStarred(LmsApp->getUserId(), trackId); }};
|
||||||
|
|||||||
@@ -20,8 +20,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "services/database/Object.hpp"
|
#include "services/database/Object.hpp"
|
||||||
|
#include "services/database/ReleaseId.hpp"
|
||||||
#include "common/Template.hpp"
|
#include "common/Template.hpp"
|
||||||
#include "PlayQueueAction.hpp"
|
|
||||||
|
|
||||||
namespace Database
|
namespace Database
|
||||||
{
|
{
|
||||||
@@ -31,15 +31,12 @@ namespace Database
|
|||||||
namespace UserInterface
|
namespace UserInterface
|
||||||
{
|
{
|
||||||
class Filters;
|
class Filters;
|
||||||
|
class PlayQueueController;
|
||||||
|
|
||||||
class Release : public Template
|
class Release : public Template
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Release(Filters* filters);
|
Release(Filters& filters, PlayQueueController& playQueueController);
|
||||||
|
|
||||||
PlayQueueActionReleaseSignal releasesAction;
|
|
||||||
PlayQueueActionTrackSignal tracksAction;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void refreshView();
|
void refreshView();
|
||||||
@@ -48,7 +45,8 @@ namespace UserInterface
|
|||||||
void refreshLinks(const Database::ObjectPtr<Database::Release>& release);
|
void refreshLinks(const Database::ObjectPtr<Database::Release>& release);
|
||||||
void refreshSimilarReleases(const std::vector<Database::ReleaseId>& similarReleasesId);
|
void refreshSimilarReleases(const std::vector<Database::ReleaseId>& similarReleasesId);
|
||||||
|
|
||||||
Filters* _filters {};
|
Filters& _filters;
|
||||||
|
PlayQueueController& _playQueueController;
|
||||||
};
|
};
|
||||||
} // namespace UserInterface
|
} // namespace UserInterface
|
||||||
|
|
||||||
|
|||||||
@@ -26,16 +26,18 @@
|
|||||||
|
|
||||||
#include "common/InfiniteScrollingContainer.hpp"
|
#include "common/InfiniteScrollingContainer.hpp"
|
||||||
#include "common/Template.hpp"
|
#include "common/Template.hpp"
|
||||||
#include "ReleaseListHelpers.hpp"
|
#include "explore/Filters.hpp"
|
||||||
#include "Filters.hpp"
|
#include "explore/PlayQueueController.hpp"
|
||||||
|
#include "explore/ReleaseListHelpers.hpp"
|
||||||
#include "LmsApplication.hpp"
|
#include "LmsApplication.hpp"
|
||||||
|
|
||||||
using namespace Database;
|
using namespace Database;
|
||||||
|
|
||||||
namespace UserInterface {
|
namespace UserInterface {
|
||||||
|
|
||||||
Releases::Releases(Filters& filters)
|
Releases::Releases(Filters& filters, PlayQueueController& playQueueController)
|
||||||
: Template {Wt::WString::tr("Lms.Explore.Releases.template")}
|
: Template {Wt::WString::tr("Lms.Explore.Releases.template")}
|
||||||
|
, _playQueueController {playQueueController}
|
||||||
, _releaseCollector {filters, _defaultMode, _maxCount}
|
, _releaseCollector {filters, _defaultMode, _maxCount}
|
||||||
{
|
{
|
||||||
addFunction("tr", &Wt::WTemplate::Functions::tr);
|
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)};
|
Wt::WPushButton* playBtn {bindNew<Wt::WPushButton>("play-btn", Wt::WString::tr("Lms.Explore.play"), Wt::TextFormat::XHTML)};
|
||||||
playBtn->clicked().connect([this]
|
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)
|
bindNew<Wt::WPushButton>("play-shuffled", Wt::WString::tr("Lms.Explore.play-shuffled"), Wt::TextFormat::Plain)
|
||||||
->clicked().connect([=]
|
->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)
|
bindNew<Wt::WPushButton>("play-last", Wt::WString::tr("Lms.Explore.play-last"), Wt::TextFormat::Plain)
|
||||||
->clicked().connect([=]
|
->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"));
|
_container = bindNew<InfiniteScrollingContainer>("releases", Wt::WString::tr("Lms.Explore.Releases.template.container"));
|
||||||
|
|||||||
@@ -22,20 +22,18 @@
|
|||||||
#include "services/database/Types.hpp"
|
#include "services/database/Types.hpp"
|
||||||
|
|
||||||
#include "common/Template.hpp"
|
#include "common/Template.hpp"
|
||||||
#include "PlayQueueAction.hpp"
|
|
||||||
#include "ReleaseCollector.hpp"
|
#include "ReleaseCollector.hpp"
|
||||||
|
|
||||||
namespace UserInterface
|
namespace UserInterface
|
||||||
{
|
{
|
||||||
class Filters;
|
class Filters;
|
||||||
class InfiniteScrollingContainer;
|
class InfiniteScrollingContainer;
|
||||||
|
class PlayQueueController;
|
||||||
|
|
||||||
class Releases : public Template
|
class Releases : public Template
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Releases(Filters& filters);
|
Releases(Filters& filters, PlayQueueController& playQueueController);
|
||||||
|
|
||||||
PlayQueueActionReleaseSignal releasesAction;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void refreshView();
|
void refreshView();
|
||||||
@@ -48,6 +46,7 @@ namespace UserInterface
|
|||||||
static constexpr std::size_t _batchSize {_maxItemsPerLine};
|
static constexpr std::size_t _batchSize {_maxItemsPerLine};
|
||||||
static constexpr std::size_t _maxCount {_maxItemsPerLine * 500};
|
static constexpr std::size_t _maxCount {_maxItemsPerLine * 500};
|
||||||
|
|
||||||
|
PlayQueueController& _playQueueController;
|
||||||
Wt::WWidget* _currentActiveItem {};
|
Wt::WWidget* _currentActiveItem {};
|
||||||
InfiniteScrollingContainer* _container {};
|
InfiniteScrollingContainer* _container {};
|
||||||
ReleaseCollector _releaseCollector;
|
ReleaseCollector _releaseCollector;
|
||||||
|
|||||||
@@ -35,12 +35,12 @@ using namespace Database;
|
|||||||
|
|
||||||
namespace UserInterface
|
namespace UserInterface
|
||||||
{
|
{
|
||||||
SearchView::SearchView(Filters* filters)
|
SearchView::SearchView(Filters& filters, PlayQueueController& playQueueController)
|
||||||
: Wt::WTemplate {Wt::WString::tr("Lms.Explore.Search.template")}
|
: Wt::WTemplate {Wt::WString::tr("Lms.Explore.Search.template")}
|
||||||
, _filters {filters}
|
, _playQueueController {playQueueController}
|
||||||
, _artistCollector {*filters, ArtistCollector::Mode::Search, getMaxCount(Mode::Artist)}
|
, _artistCollector {filters, ArtistCollector::Mode::Search, getMaxCount(Mode::Artist)}
|
||||||
, _releaseCollector {*filters, ReleaseCollector::Mode::Search, getMaxCount(Mode::Release)}
|
, _releaseCollector {filters, ReleaseCollector::Mode::Search, getMaxCount(Mode::Release)}
|
||||||
, _trackCollector {*filters, TrackCollector::Mode::Search, getMaxCount(Mode::Track)}
|
, _trackCollector {filters, TrackCollector::Mode::Search, getMaxCount(Mode::Track)}
|
||||||
{
|
{
|
||||||
addFunction("tr", &Wt::WTemplate::Functions::tr);
|
addFunction("tr", &Wt::WTemplate::Functions::tr);
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ namespace UserInterface
|
|||||||
_tracks = bindNew<InfiniteScrollingContainer>("tracks");
|
_tracks = bindNew<InfiniteScrollingContainer>("tracks");
|
||||||
_tracks->onRequestElements.connect([this] { addSomeTracks(); });
|
_tracks->onRequestElements.connect([this] { addSomeTracks(); });
|
||||||
|
|
||||||
_filters->updated().connect([=]
|
filters.updated().connect([=]
|
||||||
{
|
{
|
||||||
refreshView();
|
refreshView();
|
||||||
});
|
});
|
||||||
@@ -152,7 +152,7 @@ namespace UserInterface
|
|||||||
for (const TrackId trackId : trackIds.results)
|
for (const TrackId trackId : trackIds.results)
|
||||||
{
|
{
|
||||||
const Track::pointer track {Track::find(LmsApp->getDbSession(), trackId)};
|
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 "ArtistCollector.hpp"
|
||||||
#include "ReleaseCollector.hpp"
|
#include "ReleaseCollector.hpp"
|
||||||
#include "TrackCollector.hpp"
|
#include "TrackCollector.hpp"
|
||||||
#include "PlayQueueAction.hpp"
|
|
||||||
|
|
||||||
namespace UserInterface
|
namespace UserInterface
|
||||||
{
|
{
|
||||||
|
|
||||||
class InfiniteScrollingContainer;
|
class InfiniteScrollingContainer;
|
||||||
class Filters;
|
class Filters;
|
||||||
|
class PlayQueueController;
|
||||||
|
|
||||||
class SearchView : public Wt::WTemplate
|
class SearchView : public Wt::WTemplate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SearchView(Filters* filters);
|
SearchView(Filters& filters, PlayQueueController& playQueueController);
|
||||||
|
|
||||||
PlayQueueActionTrackSignal tracksAction;
|
|
||||||
|
|
||||||
void refreshView(const Wt::WString& searchText);
|
void refreshView(const Wt::WString& searchText);
|
||||||
|
|
||||||
@@ -74,7 +71,7 @@ namespace UserInterface
|
|||||||
void addSomeReleases();
|
void addSomeReleases();
|
||||||
void addSomeTracks();
|
void addSomeTracks();
|
||||||
|
|
||||||
Filters* _filters {};
|
PlayQueueController& _playQueueController;
|
||||||
ArtistCollector _artistCollector;
|
ArtistCollector _artistCollector;
|
||||||
ReleaseCollector _releaseCollector;
|
ReleaseCollector _releaseCollector;
|
||||||
TrackCollector _trackCollector;
|
TrackCollector _trackCollector;
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
#include "TrackListHelpers.hpp"
|
#include "TrackListHelpers.hpp"
|
||||||
|
|
||||||
#include <Wt/WAnchor.h>
|
#include <Wt/WAnchor.h>
|
||||||
#include <Wt/WContainerWidget.h>
|
|
||||||
#include <Wt/WImage.h>
|
#include <Wt/WImage.h>
|
||||||
#include <Wt/WPushButton.h>
|
#include <Wt/WPushButton.h>
|
||||||
|
|
||||||
@@ -29,10 +28,10 @@
|
|||||||
#include "services/scrobbling/IScrobblingService.hpp"
|
#include "services/scrobbling/IScrobblingService.hpp"
|
||||||
#include "services/database/Session.hpp"
|
#include "services/database/Session.hpp"
|
||||||
#include "services/database/Track.hpp"
|
#include "services/database/Track.hpp"
|
||||||
#include "utils/Logger.hpp"
|
|
||||||
#include "utils/Service.hpp"
|
#include "utils/Service.hpp"
|
||||||
|
|
||||||
#include "common/Template.hpp"
|
#include "common/Template.hpp"
|
||||||
|
#include "explore/PlayQueueController.hpp"
|
||||||
#include "resource/DownloadResource.hpp"
|
#include "resource/DownloadResource.hpp"
|
||||||
#include "resource/CoverResource.hpp"
|
#include "resource/CoverResource.hpp"
|
||||||
#include "LmsApplication.hpp"
|
#include "LmsApplication.hpp"
|
||||||
@@ -44,7 +43,7 @@ using namespace Database;
|
|||||||
namespace UserInterface::TrackListHelpers
|
namespace UserInterface::TrackListHelpers
|
||||||
{
|
{
|
||||||
std::unique_ptr<Wt::WWidget>
|
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 entry {std::make_unique<Template>(Wt::WString::tr("Lms.Explore.Tracks.template.entry"))};
|
||||||
auto* entryPtr {entry.get()};
|
auto* entryPtr {entry.get()};
|
||||||
@@ -74,16 +73,16 @@ namespace UserInterface::TrackListHelpers
|
|||||||
entry->bindString("duration", Utils::durationToString(track->getDuration()), Wt::TextFormat::Plain);
|
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)};
|
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>("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"))
|
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 <Wt/WWidget.h>
|
||||||
#include "services/database/Object.hpp"
|
#include "services/database/Object.hpp"
|
||||||
#include "PlayQueueAction.hpp"
|
|
||||||
|
|
||||||
namespace Database
|
namespace Database
|
||||||
{
|
{
|
||||||
class Track;
|
class Track;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace UserInterface
|
||||||
|
{
|
||||||
|
class PlayQueueController;
|
||||||
|
}
|
||||||
|
|
||||||
namespace UserInterface::TrackListHelpers
|
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
|
} // namespace UserInterface
|
||||||
|
|
||||||
|
|||||||
@@ -30,12 +30,13 @@
|
|||||||
#include "utils/String.hpp"
|
#include "utils/String.hpp"
|
||||||
|
|
||||||
#include "common/InfiniteScrollingContainer.hpp"
|
#include "common/InfiniteScrollingContainer.hpp"
|
||||||
|
#include "explore/Filters.hpp"
|
||||||
|
#include "explore/PlayQueueController.hpp"
|
||||||
|
#include "explore/TrackListHelpers.hpp"
|
||||||
#include "resource/DownloadResource.hpp"
|
#include "resource/DownloadResource.hpp"
|
||||||
#include "Filters.hpp"
|
|
||||||
#include "LmsApplication.hpp"
|
#include "LmsApplication.hpp"
|
||||||
#include "LmsApplicationException.hpp"
|
#include "LmsApplicationException.hpp"
|
||||||
#include "ModalManager.hpp"
|
#include "ModalManager.hpp"
|
||||||
#include "TrackListHelpers.hpp"
|
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
|
|
||||||
using namespace Database;
|
using namespace Database;
|
||||||
@@ -52,9 +53,10 @@ namespace
|
|||||||
|
|
||||||
namespace UserInterface
|
namespace UserInterface
|
||||||
{
|
{
|
||||||
TrackList::TrackList(Filters& filters)
|
TrackList::TrackList(Filters& filters, PlayQueueController& playQueueController)
|
||||||
: Template {Wt::WString::tr("Lms.Explore.TrackList.template")}
|
: Template {Wt::WString::tr("Lms.Explore.TrackList.template")}
|
||||||
, _filters {filters}
|
, _filters {_filters}
|
||||||
|
, _playQueueController {playQueueController}
|
||||||
{
|
{
|
||||||
addFunction("tr", &Wt::WTemplate::Functions::tr);
|
addFunction("tr", &Wt::WTemplate::Functions::tr);
|
||||||
addFunction("id", &Wt::WTemplate::Functions::id);
|
addFunction("id", &Wt::WTemplate::Functions::id);
|
||||||
@@ -64,7 +66,7 @@ namespace UserInterface
|
|||||||
refreshView();
|
refreshView();
|
||||||
});
|
});
|
||||||
|
|
||||||
_filters.updated().connect([this]
|
filters.updated().connect([this]
|
||||||
{
|
{
|
||||||
refreshView();
|
refreshView();
|
||||||
});
|
});
|
||||||
@@ -120,19 +122,19 @@ namespace UserInterface
|
|||||||
bindNew<Wt::WPushButton>("play-btn", Wt::WString::tr("Lms.Explore.play"), Wt::TextFormat::XHTML)
|
bindNew<Wt::WPushButton>("play-btn", Wt::WString::tr("Lms.Explore.play"), Wt::TextFormat::XHTML)
|
||||||
->clicked().connect([=]
|
->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)
|
bindNew<Wt::WPushButton>("play-shuffled", Wt::WString::tr("Lms.Explore.play-shuffled"), Wt::TextFormat::Plain)
|
||||||
->clicked().connect([=]
|
->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)
|
bindNew<Wt::WPushButton>("play-last", Wt::WString::tr("Lms.Explore.play-last"), Wt::TextFormat::Plain)
|
||||||
->clicked().connect([=]
|
->clicked().connect([=]
|
||||||
{
|
{
|
||||||
trackListAction.emit(PlayQueueAction::PlayLast, *trackListId);
|
_playQueueController.processCommand(PlayQueueController::Command::PlayOrAddLast, *trackListId);
|
||||||
});
|
});
|
||||||
|
|
||||||
bindNew<Wt::WPushButton>("download", Wt::WString::tr("Lms.Explore.download"))
|
bindNew<Wt::WPushButton>("download", Wt::WString::tr("Lms.Explore.download"))
|
||||||
@@ -197,7 +199,7 @@ namespace UserInterface
|
|||||||
for (const TrackId trackId : trackIds.results)
|
for (const TrackId trackId : trackIds.results)
|
||||||
{
|
{
|
||||||
if (const Track::pointer track {Track::find(LmsApp->getDbSession(), trackId)})
|
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);
|
_container->setHasMore(trackIds.moreResults && _container->getCount() < _maxCount);
|
||||||
|
|||||||
@@ -23,20 +23,17 @@
|
|||||||
#include "services/database/Types.hpp"
|
#include "services/database/Types.hpp"
|
||||||
|
|
||||||
#include "common/Template.hpp"
|
#include "common/Template.hpp"
|
||||||
#include "PlayQueueAction.hpp"
|
|
||||||
|
|
||||||
namespace UserInterface
|
namespace UserInterface
|
||||||
{
|
{
|
||||||
class Filters;
|
class Filters;
|
||||||
class InfiniteScrollingContainer;
|
class InfiniteScrollingContainer;
|
||||||
|
class PlayQueueController;
|
||||||
|
|
||||||
class TrackList : public Template
|
class TrackList : public Template
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TrackList(Filters& filters);
|
TrackList(Filters& filters, PlayQueueController& playQueueController);
|
||||||
|
|
||||||
PlayQueueActionTrackListSignal trackListAction;
|
|
||||||
PlayQueueActionTrackSignal tracksAction;
|
|
||||||
|
|
||||||
Wt::Signal<Database::TrackListId> trackListDeleted;
|
Wt::Signal<Database::TrackListId> trackListDeleted;
|
||||||
|
|
||||||
@@ -48,6 +45,7 @@ namespace UserInterface
|
|||||||
static constexpr std::size_t _maxCount {8000};
|
static constexpr std::size_t _maxCount {8000};
|
||||||
|
|
||||||
Filters& _filters;
|
Filters& _filters;
|
||||||
|
PlayQueueController& _playQueueController;
|
||||||
Database::TrackListId _trackListId;
|
Database::TrackListId _trackListId;
|
||||||
InfiniteScrollingContainer* _container {};
|
InfiniteScrollingContainer* _container {};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -27,7 +27,6 @@
|
|||||||
#include "services/database/Types.hpp"
|
#include "services/database/Types.hpp"
|
||||||
|
|
||||||
#include "common/Template.hpp"
|
#include "common/Template.hpp"
|
||||||
#include "PlayQueueAction.hpp"
|
|
||||||
|
|
||||||
namespace Database
|
namespace Database
|
||||||
{
|
{
|
||||||
@@ -44,8 +43,6 @@ namespace UserInterface
|
|||||||
public:
|
public:
|
||||||
TrackLists(Filters& filters);
|
TrackLists(Filters& filters);
|
||||||
|
|
||||||
PlayQueueActionTrackListSignal trackListAction;
|
|
||||||
|
|
||||||
void onTrackListDeleted(Database::TrackListId trackListId);
|
void onTrackListDeleted(Database::TrackListId trackListId);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -26,17 +26,19 @@
|
|||||||
#include "utils/Logger.hpp"
|
#include "utils/Logger.hpp"
|
||||||
|
|
||||||
#include "common/InfiniteScrollingContainer.hpp"
|
#include "common/InfiniteScrollingContainer.hpp"
|
||||||
#include "Filters.hpp"
|
#include "explore/Filters.hpp"
|
||||||
|
#include "explore/PlayQueueController.hpp"
|
||||||
|
#include "explore/TrackListHelpers.hpp"
|
||||||
#include "LmsApplication.hpp"
|
#include "LmsApplication.hpp"
|
||||||
#include "TrackListHelpers.hpp"
|
|
||||||
|
|
||||||
using namespace Database;
|
using namespace Database;
|
||||||
|
|
||||||
namespace UserInterface {
|
namespace UserInterface {
|
||||||
|
|
||||||
Tracks::Tracks(Filters& filters)
|
Tracks::Tracks(Filters& filters, PlayQueueController& playQueueController)
|
||||||
: Template {Wt::WString::tr("Lms.Explore.Tracks.template")},
|
: Template {Wt::WString::tr("Lms.Explore.Tracks.template")}
|
||||||
_trackCollector {filters, _defaultMode, _maxCount}
|
, _playQueueController {playQueueController}
|
||||||
|
, _trackCollector {filters, _defaultMode, _maxCount}
|
||||||
{
|
{
|
||||||
addFunction("tr", &Wt::WTemplate::Functions::tr);
|
addFunction("tr", &Wt::WTemplate::Functions::tr);
|
||||||
addFunction("id", &Wt::WTemplate::Functions::id);
|
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)
|
bindNew<Wt::WPushButton>("play-btn", Wt::WString::tr("Lms.Explore.play"), Wt::TextFormat::XHTML)
|
||||||
->clicked().connect([=]
|
->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)
|
bindNew<Wt::WPushButton>("play-shuffled", Wt::WString::tr("Lms.Explore.play-shuffled"), Wt::TextFormat::Plain)
|
||||||
->clicked().connect([=]
|
->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)
|
bindNew<Wt::WPushButton>("play-last", Wt::WString::tr("Lms.Explore.play-last"), Wt::TextFormat::Plain)
|
||||||
->clicked().connect([=]
|
->clicked().connect([=]
|
||||||
{
|
{
|
||||||
tracksAction.emit(PlayQueueAction::PlayLast, getAllTracks());
|
_playQueueController.processCommand(PlayQueueController::Command::PlayOrAddLast, getAllTracks());
|
||||||
});
|
});
|
||||||
|
|
||||||
_container = bindNew<InfiniteScrollingContainer>("tracks");
|
_container = bindNew<InfiniteScrollingContainer>("tracks");
|
||||||
@@ -122,7 +124,7 @@ Tracks::addSome()
|
|||||||
for (const TrackId trackId : trackIds.results)
|
for (const TrackId trackId : trackIds.results)
|
||||||
{
|
{
|
||||||
if (const Track::pointer track {Track::find(LmsApp->getDbSession(), trackId)})
|
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->setHasMore(trackIds.moreResults);
|
||||||
|
|||||||
@@ -22,20 +22,18 @@
|
|||||||
#include "services/database/Types.hpp"
|
#include "services/database/Types.hpp"
|
||||||
|
|
||||||
#include "common/Template.hpp"
|
#include "common/Template.hpp"
|
||||||
#include "PlayQueueAction.hpp"
|
|
||||||
#include "TrackCollector.hpp"
|
#include "TrackCollector.hpp"
|
||||||
|
|
||||||
namespace UserInterface
|
namespace UserInterface
|
||||||
{
|
{
|
||||||
class Filters;
|
class Filters;
|
||||||
class InfiniteScrollingContainer;
|
class InfiniteScrollingContainer;
|
||||||
|
class PlayQueueController;
|
||||||
|
|
||||||
class Tracks : public Template
|
class Tracks : public Template
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Tracks(Filters& filters);
|
Tracks(Filters& filters, PlayQueueController& playQueueController);
|
||||||
|
|
||||||
PlayQueueActionTrackSignal tracksAction;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void refreshView();
|
void refreshView();
|
||||||
@@ -48,6 +46,7 @@ namespace UserInterface
|
|||||||
static constexpr std::size_t _batchSize {6};
|
static constexpr std::size_t _batchSize {6};
|
||||||
static constexpr std::size_t _maxCount {8000};
|
static constexpr std::size_t _maxCount {8000};
|
||||||
|
|
||||||
|
PlayQueueController& _playQueueController;
|
||||||
Wt::WWidget* _currentActiveItem {};
|
Wt::WWidget* _currentActiveItem {};
|
||||||
InfiniteScrollingContainer* _container {};
|
InfiniteScrollingContainer* _container {};
|
||||||
TrackCollector _trackCollector;
|
TrackCollector _trackCollector;
|
||||||
|
|||||||
Reference in New Issue
Block a user