Added a way to play tracks per disc, fixes #423
This commit is contained in:
+15
-1
@@ -50,7 +50,21 @@
|
||||
</message>
|
||||
|
||||
<message id="Lms.Explore.Release.template.entry-disc">
|
||||
<h4>${disc-title}</h4>
|
||||
<div class="d-flex">
|
||||
<h4 class="flex-fill">${disc-title}</h4>
|
||||
<div class="p-2 d-flex">
|
||||
${play-btn class="d-none d-sm-block btn btn-sm btn-outline-secondary border-0"}
|
||||
<div class="dropdown d-inline-block">
|
||||
${more-btn data-bs-toggle="dropdown" aria-expanded="false" class="btn btn-sm btn-outline-secondary border-0"}
|
||||
<ul class="dropdown-menu" aria-labelledby="${id:more-btn}">
|
||||
<li class="d-block d-sm-none">${play class="dropdown-item"}</li>
|
||||
<li>${play-next class="dropdown-item"}</li>
|
||||
<li>${play-last class="dropdown-item"}</li>
|
||||
<li>${play-shuffled class="dropdown-item"}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
${tracks class="d-grid gap-1 Lms-row-container"}
|
||||
</message>
|
||||
|
||||
|
||||
@@ -142,6 +142,9 @@ namespace Database
|
||||
if (params.trackNumber)
|
||||
query.where("t.track_number = ?").bind(*params.trackNumber);
|
||||
|
||||
if (params.discNumber)
|
||||
query.where("t.disc_number = ?").bind(*params.discNumber);
|
||||
|
||||
if (params.mediaLibrary.isValid())
|
||||
query.where("t.media_library_id = ?").bind(params.mediaLibrary);
|
||||
|
||||
|
||||
@@ -78,6 +78,7 @@ namespace Database {
|
||||
std::string releaseName; // matching this release name
|
||||
TrackListId trackList; // matching this trackList
|
||||
std::optional<int> trackNumber; // matching this track number
|
||||
std::optional<int> discNumber; // matching this disc number
|
||||
MediaLibraryId mediaLibrary; // If set, tracks in this library
|
||||
|
||||
FindParameters& setClusters(const std::vector<ClusterId>& _clusters) { clusters = _clusters; return *this; }
|
||||
@@ -94,6 +95,7 @@ namespace Database {
|
||||
FindParameters& setReleaseName(std::string_view _releaseName) { releaseName = _releaseName; return *this; }
|
||||
FindParameters& setTrackList(TrackListId _trackList) { trackList = _trackList; return *this; }
|
||||
FindParameters& setTrackNumber(int _trackNumber) { trackNumber = _trackNumber; return *this; }
|
||||
FindParameters& setDiscNumber(int _discNumber) { discNumber = _discNumber; return *this; }
|
||||
FindParameters& setMediaLibrary(MediaLibraryId _mediaLibrary) { mediaLibrary = _mediaLibrary; return *this; }
|
||||
};
|
||||
|
||||
|
||||
@@ -88,6 +88,36 @@ namespace UserInterface
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<Database::TrackId> getDiscTracks(Database::Session& session, const std::vector<PlayQueueController::Disc>& discs, const std::vector<Database::ClusterId>& clusters, std::size_t maxTrackCount)
|
||||
{
|
||||
using namespace Database;
|
||||
assert(maxTrackCount);
|
||||
|
||||
std::vector<TrackId> res;
|
||||
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
|
||||
for (const PlayQueueController::Disc& disc : discs)
|
||||
{
|
||||
Database::Track::FindParameters params;
|
||||
params.setRelease(disc.releaseId);
|
||||
params.setSortMethod(Database::TrackSortMethod::Release);
|
||||
params.setDiscNumber(disc.discNumber);
|
||||
params.setClusters(clusters);
|
||||
params.setRange(Database::Range{ 0, maxTrackCount - res.size() });
|
||||
|
||||
const auto tracks{ Database::Track::findIds(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;
|
||||
}
|
||||
|
||||
std::vector<Database::TrackId> getTrackListTracks(Database::Session& session, Database::TrackListId trackListId, const std::vector<Database::ClusterId>& clusters, std::size_t maxTrackCount)
|
||||
{
|
||||
using namespace Database;
|
||||
@@ -149,6 +179,12 @@ namespace UserInterface
|
||||
processCommand(command, tracks);
|
||||
}
|
||||
|
||||
void PlayQueueController::processCommand(Command command, const std::vector<Disc>& discs)
|
||||
{
|
||||
const std::vector<Database::TrackId> tracks{ getDiscTracks(LmsApp->getDbSession(), discs, _filters.getClusterIds(), _maxTrackCountToEnqueue) };
|
||||
processCommand(command, tracks);
|
||||
}
|
||||
|
||||
void PlayQueueController::playTrackInRelease(Database::TrackId trackId)
|
||||
{
|
||||
Database::ReleaseId releaseId;
|
||||
|
||||
@@ -45,11 +45,18 @@ namespace UserInterface
|
||||
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 processCommand(Command command, const std::vector<Database::ArtistId>& artists);
|
||||
void processCommand(Command command, const std::vector<Database::ReleaseId>& releases);
|
||||
void processCommand(Command command, const std::vector<Database::TrackId>& tracks);
|
||||
|
||||
struct Disc
|
||||
{
|
||||
Database::ReleaseId releaseId;
|
||||
size_t discNumber;
|
||||
};
|
||||
void processCommand(Command command, const std::vector<Disc>& discs);
|
||||
void processCommand(Command command, Database::TrackListId trackList);
|
||||
void playTrackInRelease(Database::TrackId track);
|
||||
|
||||
void setMaxTrackCountToEnqueue(std::size_t maxTrackCount) { _maxTrackCountToEnqueue = maxTrackCount; }
|
||||
|
||||
|
||||
@@ -340,21 +340,47 @@ namespace UserInterface
|
||||
|
||||
// Expect to be called in asc order
|
||||
std::map<std::size_t, Wt::WContainerWidget*> trackContainers;
|
||||
auto getOrAddDiscContainer = [&](std::size_t discNumber, const std::string& discSubtitle) -> Wt::WContainerWidget*
|
||||
auto getOrAddDiscContainer = [&, releaseId = _releaseId](std::size_t discNumber, const std::string& discSubtitle) -> Wt::WContainerWidget*
|
||||
{
|
||||
{
|
||||
auto it = trackContainers.find(discNumber);
|
||||
if (it != std::cend(trackContainers))
|
||||
return it->second;
|
||||
}
|
||||
if (auto it{ trackContainers.find(discNumber) }; it != std::cend(trackContainers))
|
||||
return it->second;
|
||||
|
||||
Wt::WTemplate* disc{ rootContainer->addNew<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Release.template.entry-disc")) };
|
||||
Template* disc{ rootContainer->addNew<Template>(Wt::WString::tr("Lms.Explore.Release.template.entry-disc")) };
|
||||
disc->addFunction("id", &Wt::WTemplate::Functions::id);
|
||||
|
||||
if (discSubtitle.empty())
|
||||
disc->bindNew<Wt::WText>("disc-title", Wt::WString::tr("Lms.Explore.Release.disc").arg(discNumber));
|
||||
else
|
||||
disc->bindString("disc-title", Wt::WString::fromUTF8(discSubtitle), Wt::TextFormat::Plain);
|
||||
|
||||
Wt::WPushButton* playBtn{ disc->bindNew<Wt::WPushButton>("play-btn", Wt::WString::tr("Lms.template.play-btn"), Wt::TextFormat::XHTML) };
|
||||
playBtn->clicked().connect([this, releaseId, discNumber]
|
||||
{
|
||||
_playQueueController.processCommand(PlayQueueController::Command::Play, { PlayQueueController::Disc{ releaseId, discNumber} });
|
||||
});
|
||||
disc->bindNew<Wt::WPushButton>("more-btn", Wt::WString::tr("Lms.template.more-btn"), Wt::TextFormat::XHTML);
|
||||
disc->bindNew<Wt::WPushButton>("play", Wt::WString::tr("Lms.Explore.play"))
|
||||
->clicked().connect([this, releaseId, discNumber]
|
||||
{
|
||||
_playQueueController.processCommand(PlayQueueController::Command::Play, { PlayQueueController::Disc{ releaseId, discNumber} });
|
||||
});
|
||||
disc->bindNew<Wt::WPushButton>("play-next", Wt::WString::tr("Lms.Explore.play-next"))
|
||||
->clicked().connect([this, releaseId, discNumber]
|
||||
{
|
||||
_playQueueController.processCommand(PlayQueueController::Command::PlayNext, { PlayQueueController::Disc{ releaseId, discNumber } });
|
||||
});
|
||||
|
||||
disc->bindNew<Wt::WPushButton>("play-shuffled", Wt::WString::tr("Lms.Explore.play-shuffled"), Wt::TextFormat::Plain)
|
||||
->clicked().connect([this, releaseId, discNumber]
|
||||
{
|
||||
_playQueueController.processCommand(PlayQueueController::Command::PlayShuffled, { PlayQueueController::Disc{ releaseId, discNumber } });
|
||||
});
|
||||
disc->bindNew<Wt::WPushButton>("play-last", Wt::WString::tr("Lms.Explore.play-last"))
|
||||
->clicked().connect([this, releaseId, discNumber]
|
||||
{
|
||||
_playQueueController.processCommand(PlayQueueController::Command::PlayOrAddLast, { PlayQueueController::Disc{ releaseId, discNumber } });
|
||||
});
|
||||
|
||||
Wt::WContainerWidget* tracksContainer{ disc->bindNew<Wt::WContainerWidget>("tracks") };
|
||||
trackContainers[discNumber] = tracksContainer;
|
||||
|
||||
@@ -390,6 +416,7 @@ namespace UserInterface
|
||||
container = getOrAddNoDiscContainer();
|
||||
|
||||
Template* entry{ container->addNew<Template>(Wt::WString::tr("Lms.Explore.Release.template.entry")) };
|
||||
entry->addFunction("id", &Wt::WTemplate::Functions::id);
|
||||
|
||||
entry->bindString("name", Wt::WString::fromUTF8(track->getName()), Wt::TextFormat::Plain);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user