Added a way to play tracks per disc, fixes #423

This commit is contained in:
emeric
2024-03-03 17:27:40 +01:00
parent 0e8a1e946d
commit 1b8d4f2413
6 changed files with 102 additions and 13 deletions
+15 -1
View File
@@ -50,7 +50,21 @@
</message> </message>
<message id="Lms.Explore.Release.template.entry-disc"> <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"} ${tracks class="d-grid gap-1 Lms-row-container"}
</message> </message>
+3
View File
@@ -142,6 +142,9 @@ namespace Database
if (params.trackNumber) if (params.trackNumber)
query.where("t.track_number = ?").bind(*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()) if (params.mediaLibrary.isValid())
query.where("t.media_library_id = ?").bind(params.mediaLibrary); query.where("t.media_library_id = ?").bind(params.mediaLibrary);
@@ -78,6 +78,7 @@ namespace Database {
std::string releaseName; // matching this release name std::string releaseName; // matching this release name
TrackListId trackList; // matching this trackList TrackListId trackList; // matching this trackList
std::optional<int> trackNumber; // matching this track number std::optional<int> trackNumber; // matching this track number
std::optional<int> discNumber; // matching this disc number
MediaLibraryId mediaLibrary; // If set, tracks in this library MediaLibraryId mediaLibrary; // If set, tracks in this library
FindParameters& setClusters(const std::vector<ClusterId>& _clusters) { clusters = _clusters; return *this; } 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& setReleaseName(std::string_view _releaseName) { releaseName = _releaseName; return *this; }
FindParameters& setTrackList(TrackListId _trackList) { trackList = _trackList; return *this; } FindParameters& setTrackList(TrackListId _trackList) { trackList = _trackList; return *this; }
FindParameters& setTrackNumber(int _trackNumber) { trackNumber = _trackNumber; 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; } FindParameters& setMediaLibrary(MediaLibraryId _mediaLibrary) { mediaLibrary = _mediaLibrary; return *this; }
}; };
@@ -88,6 +88,36 @@ namespace UserInterface
return res; 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) std::vector<Database::TrackId> getTrackListTracks(Database::Session& session, Database::TrackListId trackListId, const std::vector<Database::ClusterId>& clusters, std::size_t maxTrackCount)
{ {
using namespace Database; using namespace Database;
@@ -149,6 +179,12 @@ namespace UserInterface
processCommand(command, tracks); 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) void PlayQueueController::playTrackInRelease(Database::TrackId trackId)
{ {
Database::ReleaseId releaseId; Database::ReleaseId releaseId;
+12 -5
View File
@@ -45,11 +45,18 @@ namespace UserInterface
PlayShuffled, PlayShuffled,
}; };
void processCommand(Command command, const std::vector<Database::ArtistId>&); void processCommand(Command command, const std::vector<Database::ArtistId>& artists);
void processCommand(Command command, const std::vector<Database::ReleaseId>&); void processCommand(Command command, const std::vector<Database::ReleaseId>& releases);
void processCommand(Command command, const std::vector<Database::TrackId>&); void processCommand(Command command, const std::vector<Database::TrackId>& tracks);
void processCommand(Command command, Database::TrackListId);
void playTrackInRelease(Database::TrackId); 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; } void setMaxTrackCountToEnqueue(std::size_t maxTrackCount) { _maxTrackCountToEnqueue = maxTrackCount; }
+34 -7
View File
@@ -340,21 +340,47 @@ namespace UserInterface
// Expect to be called in asc order // Expect to be called in asc order
std::map<std::size_t, Wt::WContainerWidget*> trackContainers; 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*
{ {
{ if (auto it{ trackContainers.find(discNumber) }; it != std::cend(trackContainers))
auto it = trackContainers.find(discNumber); return it->second;
if (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()) if (discSubtitle.empty())
disc->bindNew<Wt::WText>("disc-title", Wt::WString::tr("Lms.Explore.Release.disc").arg(discNumber)); disc->bindNew<Wt::WText>("disc-title", Wt::WString::tr("Lms.Explore.Release.disc").arg(discNumber));
else else
disc->bindString("disc-title", Wt::WString::fromUTF8(discSubtitle), Wt::TextFormat::Plain); 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") }; Wt::WContainerWidget* tracksContainer{ disc->bindNew<Wt::WContainerWidget>("tracks") };
trackContainers[discNumber] = tracksContainer; trackContainers[discNumber] = tracksContainer;
@@ -390,6 +416,7 @@ namespace UserInterface
container = getOrAddNoDiscContainer(); container = getOrAddNoDiscContainer();
Template* entry{ container->addNew<Template>(Wt::WString::tr("Lms.Explore.Release.template.entry")) }; 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); entry->bindString("name", Wt::WString::fromUTF8(track->getName()), Wt::TextFormat::Plain);