Added a way to save the current playqueue in a playlist

This commit is contained in:
emeric
2022-07-02 15:39:50 +02:00
parent 27cad7ba0e
commit 1c7ef90108
12 changed files with 314 additions and 62 deletions
+3 -3
View File
@@ -32,9 +32,9 @@
${value class="form-control"}
</div>
</form>
<div class="modal-footer">
${add-btn class="btn btn-primary me-1"}${cancel-btn class="btn btn-secondary"}
</div>
</div>
<div class="modal-footer">
${add-btn class="btn btn-primary me-1"}${cancel-btn class="btn btn-secondary"}
</div>
</div>
</div>
+6 -7
View File
@@ -26,6 +26,10 @@
<message id="Lms.passwords-dont-match">Passwords don't match</message>
<message id="Lms.quit-other-session">Another session has been open. Reopen this one?</message>
<message id="Lms.save">Save</message>
<message id="Lms.track-count">
<plural case="0">{1} track</plural>
<plural case="1">{1} tracks</plural>
</message>
<message id="Lms.user">User</message>
<!--Errors-->
@@ -157,12 +161,6 @@
<message id="Lms.Explore.Release.copyright">Copyright</message>
<message id="Lms.Explore.Release.disc">Disc {1}</message>
<!--Explore:TrackList-->
<message id="Lms.Explore.TrackList.track-count">
<plural case="0">{1} track</plural>
<plural case="1">{1} tracks</plural>
</message>
<!--Explore:TrackLists-->
<message id="Lms.Explore.TrackLists.del-tracklist-confirm">Delete playlist?</message>
@@ -174,7 +172,7 @@
<!--Playqueue-->
<message id="Lms.PlayQueue.clear">Clear</message>
<message id="Lms.PlayQueue.nb-tracks">{1} tracks</message>
<message id="Lms.PlayQueue.create-tracklist">Create new playlist</message>
<message id="Lms.PlayQueue.nb-tracks-added">
<plural case="0">Added {1} track</plural>
<plural case="1">Added {1} tracks</plural>
@@ -183,6 +181,7 @@
<message id="Lms.PlayQueue.playqueue-full">Play Queue full!</message>
<message id="Lms.PlayQueue.radio-mode">Radio mode</message>
<message id="Lms.PlayQueue.repeat">Repeat</message>
<message id="Lms.PlayQueue.replace-tracklist">Replace existing playlist</message>
<message id="Lms.PlayQueue.shuffle">Shuffle</message>
<!--PlayHistory-->
+6 -8
View File
@@ -26,6 +26,10 @@
<message id="Lms.passwords-dont-match">Les mots de passe ne correspondent pas</message>
<message id="Lms.quit-other-session">Une autre session a été ouverte. Reouvrir celle-ci ?</message>
<message id="Lms.save">Sauvegarder</message>
<message id="Lms.track-count">
<plural case="0">{1} piste</plural>
<plural case="1">{1} pistes</plural>
</message>
<message id="Lms.user">Utilisateur</message>
<!--Errors-->
@@ -157,12 +161,6 @@
<message id="Lms.Explore.Release.copyright">Copyright</message>
<message id="Lms.Explore.Release.disc">Disque {1}</message>
<!--Explore:TrackList-->
<message id="Lms.Explore.TrackList.track-count">
<plural case="0">{1} piste</plural>
<plural case="1">{1} pistes</plural>
</message>
<!--Explore:TrackLists-->
<message id="Lms.Explore.TrackLists.del-tracklist-confirm">Supprimer la liste de lecture ?</message>
@@ -174,7 +172,7 @@
<!--Playqueue-->
<message id="Lms.PlayQueue.clear">Effacer</message>
<message id="Lms.PlayQueue.nb-tracks">{1} pistes</message>
<message id="Lms.PlayQueue.create-tracklist">Créer une nouvelle liste de lecture</message>
<message id="Lms.PlayQueue.nb-tracks-added">
<plural case="0">{1} piste ajoutée</plural>
<plural case="1">{1} pistes ajoutées</plural>
@@ -183,12 +181,12 @@
<message id="Lms.PlayQueue.playqueue-full">Liste de lecture pleine!</message>
<message id="Lms.PlayQueue.radio-mode">Mode radio</message>
<message id="Lms.PlayQueue.repeat">Repéter</message>
<message id="Lms.PlayQueue.replace-tracklist">Remplacer une liste de lecture existante</message>
<message id="Lms.PlayQueue.shuffle">Mélanger</message>
<!--PlayHistory-->
<message id="Lms.PlayHistory.playhistory">Historique de lecture</message>
<!--Settings-->
<message id="Lms.Settings.audio">Audio</message>
<message id="Lms.Settings.audio-settings-are-local">Ces paramètres audio sont locaux à votre navigateur !</message>
+12 -8
View File
@@ -26,6 +26,10 @@
<message id="Lms.passwords-dont-match">Le password non corrispondono</message>
<message id="Lms.quit-other-session">Un'altra sessione è stata aperta. Vuoi riaprire questa?</message>
<message id="Lms.save">Salva</message>
<message id="Lms.user">Utente</message>
<!--Errors-->
@@ -46,6 +50,7 @@
<message id="Lms.Admin.Database.menu-database"><i class="fa fa-fw fa-database" aria-hidden="true"></i> Raccolta musicale</message>
<message id="Lms.Admin.Database.never">Mai</message>
<message id="Lms.Admin.Database.path">Cartella dei file multimediali</message>
<message id="Lms.Admin.Database.recommendation-engine-type">Modalità di raccomandazione brani consigliati</message>
<message id="Lms.Admin.Database.recommendation-engine-type.clusters">Basata sui tag</message>
<message id="Lms.Admin.Database.recommendation-engine-type.features">Basata sull'analisi acustica</message>
@@ -156,12 +161,6 @@
<message id="Lms.Explore.Release.copyright">Copyright</message>
<message id="Lms.Explore.Release.disc">Disco {1}</message>
<!--Explore:TrackList-->
<!--Explore:TrackLists-->
@@ -173,7 +172,7 @@
<!--Playqueue-->
<message id="Lms.PlayQueue.clear">Cancella</message>
<message id="Lms.PlayQueue.nb-tracks">{1} tracce</message>
<message id="Lms.PlayQueue.nb-tracks-added">
<plural case="0">Aggiunta {1} traccia</plural>
<plural case="1">Aggiunte {1} tracce</plural>
@@ -182,12 +181,12 @@
<message id="Lms.PlayQueue.playqueue-full">Coda di riproduzione piena!</message>
<message id="Lms.PlayQueue.radio-mode">Modalità radio</message>
<message id="Lms.PlayQueue.repeat">Ripeti</message>
<message id="Lms.PlayQueue.shuffle">Mischia</message>
<!--PlayHistory-->
<message id="Lms.PlayHistory.playhistory">Storico riproduzione</message>
<!--Settings-->
<message id="Lms.Settings.audio">Audio</message>
<message id="Lms.Settings.audio-settings-are-local">Queste impostazioni sono salvate localmente su questo dispositivo!</message>
@@ -203,6 +202,11 @@
<message id="Lms.Settings.replaygain-mode.release">Album</message>
<message id="Lms.Settings.replaygain-preamp">Preamplificazione ReplayGain</message>
<message id="Lms.Settings.replaygain-preamp-no-rg-info">Preamplificazione ReplayGain (se non è disponibile nessuna informazione)</message>
<message id="Lms.Settings.subsonic-artist-list-mode">Modalità di elencazione artisti</message>
<message id="Lms.Settings.subsonic-artist-list-mode.all-artists">Tutti gli artisti</message>
<message id="Lms.Settings.subsonic-artist-list-mode.release-artists">Artisti album</message>
+11 -7
View File
@@ -26,6 +26,10 @@
<message id="Lms.passwords-dont-match">密码不匹配</message>
<message id="Lms.quit-other-session">另一个会话已打开,重新打开这一个?</message>
<message id="Lms.save">保存</message>
<message id="Lms.user">用户</message>
<!--Errors-->
@@ -157,12 +161,6 @@
<message id="Lms.Explore.Release.copyright">版权所有</message>
<message id="Lms.Explore.Release.disc">唱片 {1}</message>
<!--Explore:TrackList-->
<!--Explore:TrackLists-->
@@ -174,7 +172,7 @@
<!--Playqueue-->
<message id="Lms.PlayQueue.clear">清除</message>
<message id="Lms.PlayQueue.nb-tracks">{1} 音轨</message>
<message id="Lms.PlayQueue.nb-tracks-added">
<plural case="0">添加 {1} 音轨</plural>
<plural case="1">已添加 {1} 音轨</plural>
@@ -183,6 +181,7 @@
<message id="Lms.PlayQueue.playqueue-full">播放队列已满!</message>
<message id="Lms.PlayQueue.radio-mode">广播模式</message>
<message id="Lms.PlayQueue.repeat">循环播放</message>
<message id="Lms.PlayQueue.shuffle">随机播放</message>
<!--PlayHistory-->
@@ -203,6 +202,11 @@
<message id="Lms.Settings.replaygain-mode.release">专辑</message>
<message id="Lms.Settings.replaygain-preamp">ReplayGain 放大器</message>
<message id="Lms.Settings.replaygain-preamp-no-rg-info">ReplayGain 放大器 (如果没有信息)</message>
<message id="Lms.Settings.subsonic-artist-list-mode">歌手列表模式</message>
<message id="Lms.Settings.subsonic-artist-list-mode.all-artists">所有歌手</message>
<message id="Lms.Settings.subsonic-artist-list-mode.release-artists">专辑歌手</message>
+56 -7
View File
@@ -6,22 +6,26 @@
<message id="Lms.PlayQueue.template.shuffle-btn"><i class="fa fa-fw fa-random"></i></message>
<message id="Lms.PlayQueue.template.repeat-btn"><i class="fa fa-fw fa-repeat"></i></message>
<message id="Lms.PlayQueue.template.radio-btn"><i class="fa fa-fw fa-rss"></i></message>
<message id="Lms.PlayQueue.template.save-btn"><i class="fa fa-fw fa-save"></i></message>
<message id="Lms.PlayQueue.template">
<div class="mb-3">
<div class="d-inline-block me-2" title="${tr:Lms.PlayQueue.clear}">
${clear-btn class="btn btn-secondary"}
</div>
<div class="d-inline-block me-2" title="${tr:Lms.PlayQueue.shuffle}">
${shuffle-btn class="btn btn-secondary"}
</div>
<div class="btn-group me-2">
${repeat-btn class="btn-check"}
<label class="btn btn-outline-info" title="${tr:Lms.PlayQueue.repeat}" for="${id:repeat-btn}">${tr:Lms.PlayQueue.template.repeat-btn}</label>
${radio-btn class="btn-check"}
<label class="btn btn-outline-info" title="${tr:Lms.PlayQueue.radio-mode}" for="${id:radio-btn}">${tr:Lms.PlayQueue.template.radio-btn}</label>
</div>
${nb-tracks}
<div class="d-inline-block me-2" title="${tr:Lms.save}">
${save-btn class="btn btn-secondary"}
</div>
<div class="d-inline-block me-2" title="${tr:Lms.PlayQueue.shuffle}">
${shuffle-btn class="btn btn-secondary"}
</div>
<div class="d-inline-block me-2" title="${tr:Lms.PlayQueue.clear}">
${clear-btn class="btn btn-warning"}
</div>
${track-count}
</div>
${entries}
</message>
@@ -56,4 +60,49 @@
${artist}
</message>
<message id="Lms.PlayQueue.template.save-as-tracklist">
<div class="modal fade" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">${tr:Lms.save}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form>
<div class="mb-2">
<div class="form-check form-check-inline">
${create-tracklist-btn class="form-check-input" name="action-type"}
<label class="form-check-label" for="${id:create-tracklist-btn}">${tr:Lms.PlayQueue.create-tracklist}</label>
</div>
<div class="form-check form-check-inline">
${replace-tracklist-btn class="form-check-input" name="action-type"}
<label class="form-check-label" for="${id:replace-tracklist-btn}">${tr:Lms.PlayQueue.replace-tracklist}</label>
</div>
</div>
${contents}
</form>
</div>
<div class="modal-footer">
${save-btn class="btn btn-primary me-1"}${cancel-btn class="btn btn-secondary"}
</div>
</div>
</div>
</div>
</message>
<message id="Lms.PlayQueue.template.save-as-tracklist.create-tracklist">
${name class="form-control"}
<div class="invalid-feedback">
${name-info}
</div>
</message>
<message id="Lms.PlayQueue.template.save-as-tracklist.replace-tracklist">
${name class="form-control"}
<div class="invalid-feedback">
${name-info}
</div>
</message>
</messages>
+1 -1
View File
@@ -43,7 +43,7 @@ createQuery(Session& session, const Track::FindParameters& params)
{
session.checkSharedLocked();
auto query {session.getDboSession().query<TrackId>("SELECT DISTINCT t.id from track t")};
auto query {session.getDboSession().query<TrackId>(params.distinct ? "SELECT DISTINCT t.id FROM track t" : "SELECT t.id FROM track t")};
for (std::string_view keyword : params.keywords)
query.where("t.name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'").bind("%" + Utils::escapeLikeKeyword(keyword) + "%");
@@ -71,6 +71,7 @@ class Track : public Object<Track, TrackId>
bool nonRelease {}; // only tracks that do not belong to a release
ReleaseId release; // matching this release
TrackListId trackList; // matching this trackList
bool distinct {true};
FindParameters& setClusters(const std::vector<ClusterId>& _clusters) { clusters = _clusters; return *this; }
FindParameters& setKeywords(const std::vector<std::string_view>& _keywords) { keywords = _keywords; return *this; }
@@ -82,6 +83,7 @@ class Track : public Object<Track, TrackId>
FindParameters& setNonRelease(bool _nonRelease) { nonRelease = _nonRelease; return *this; }
FindParameters& setRelease(ReleaseId _release) { release = _release; return *this; }
FindParameters& setTrackList(TrackListId _trackList) { trackList = _trackList; return *this; }
FindParameters& setDistinct(bool _distinct) { distinct = _distinct; return *this; }
};
struct PathResult
+209 -2
View File
@@ -20,7 +20,13 @@
#include "PlayQueue.hpp"
#include <Wt/WCheckBox.h>
#include <Wt/WComboBox.h>
#include <Wt/WFormModel.h>
#include <Wt/WLineEdit.h>
#include <Wt/WRadioButton.h>
#include <Wt/WPushButton.h>
#include <Wt/WStackedWidget.h>
#include <Wt/WTemplateFormView.h>
#include "services/database/Cluster.hpp"
#include "services/database/Release.hpp"
@@ -36,13 +42,79 @@
#include "utils/String.hpp"
#include "common/InfiniteScrollingContainer.hpp"
#include "common/MandatoryValidator.hpp"
#include "common/ValueStringModel.hpp"
#include "resource/DownloadResource.hpp"
#include "LmsApplication.hpp"
#include "MediaPlayer.hpp"
#include "ModalManager.hpp"
#include "Utils.hpp"
namespace UserInterface {
namespace
{
class CreateTrackListModel : public Wt::WFormModel
{
public:
static inline const Field NameField {"name"};
CreateTrackListModel()
{
addField(NameField);
setValidator(NameField, createMandatoryValidator());
}
Wt::WString getName() const { return valueText(NameField); }
};
class ReplaceTrackListModel : public Wt::WFormModel
{
public:
static inline const Field NameField {"name"};
using TrackListModel = ValueStringModel<Database::TrackListId>;
ReplaceTrackListModel()
{
addField(NameField);
setValidator(NameField, createMandatoryValidator());
}
Database::TrackListId getTrackListId() const
{
auto row {trackListModel->getRowFromString(valueText(NameField))};
return trackListModel->getValue(*row);
}
static
std::shared_ptr<TrackListModel>
createTrackListModel()
{
using namespace Database;
auto model {std::make_shared<TrackListModel>()};
auto transaction {LmsApp->getDbSession().createSharedTransaction()};
TrackList::FindParameters params;
params.setType(TrackListType::Playlist);
params.setUser(LmsApp->getUserId());
params.setSortMethod(TrackListSortMethod::Name);
auto tracklists {TrackList::find(LmsApp->getDbSession(), params)};
for (const TrackListId trackListId : tracklists.results)
{
const TrackList::pointer trackList {TrackList::find(LmsApp->getDbSession(), trackListId)};
model->add(Wt::WString::fromUTF8(std::string {trackList->getName()}), trackListId);
}
return model;
}
std::shared_ptr<TrackListModel> trackListModel {createTrackListModel()};
};
}
PlayQueue::PlayQueue()
: Template {Wt::WString::tr("Lms.PlayQueue.template")}
{
@@ -55,6 +127,12 @@ PlayQueue::PlayQueue()
clearTracks();
});
Wt::WPushButton* saveBtn {bindNew<Wt::WPushButton>("save-btn", Wt::WString::tr("Lms.PlayQueue.template.save-btn"), Wt::TextFormat::XHTML)};
saveBtn->clicked().connect([=]
{
saveAsTrackList();
});
_entriesContainer = bindNew<InfiniteScrollingContainer>("entries");
_entriesContainer->onRequestElements.connect([this]
{
@@ -108,7 +186,7 @@ PlayQueue::PlayQueue()
_radioBtn->setCheckState(Wt::CheckState::Checked);
}
_nbTracks = bindNew<Wt::WText>("nb-tracks");
_nbTracks = bindNew<Wt::WText>("track-count");
LmsApp->preQuit().connect([=]
{
@@ -287,7 +365,8 @@ PlayQueue::updateInfo()
{
auto transaction {LmsApp->getDbSession().createSharedTransaction()};
_nbTracks->setText(Wt::WString::tr("Lms.PlayQueue.nb-tracks").arg(static_cast<unsigned>(getTrackList()->getCount())));
const auto trackCount {getTrackList()->getCount()};
_nbTracks->setText(Wt::WString::trn("Lms.track-count", trackCount).arg(trackCount));
}
void
@@ -541,5 +620,133 @@ PlayQueue::getReplayGain(std::size_t pos, const Database::Track::pointer& track)
return settings->replayGain.preAmpGainIfNoInfo;
}
void
PlayQueue::saveAsTrackList()
{
auto modal {std::make_unique<Template>(Wt::WString::tr("Lms.PlayQueue.template.save-as-tracklist"))};
modal->addFunction("id", &Wt::WTemplate::Functions::id);
modal->addFunction("tr", &Wt::WTemplate::Functions::tr);
Wt::WWidget* modalPtr {modal.get()};
auto* cancelBtn {modal->bindNew<Wt::WPushButton>("cancel-btn", Wt::WString::tr("Lms.cancel"))};
cancelBtn->clicked().connect([=]
{
LmsApp->getModalManager().dispose(modalPtr);
});
Wt::WStackedWidget* contentStack {modal->bindNew<Wt::WStackedWidget>("contents")};
// Create/Replace selector
enum Index : int
{
CreateTrackList = 0,
ReplaceTrackList = 1,
};
{
modal->bindNew<Wt::WRadioButton>("replace-tracklist-btn")
->clicked().connect([=]
{
contentStack->setCurrentIndex(Index::ReplaceTrackList);
});
Wt::WRadioButton* createTrackListBtn {modal->bindNew<Wt::WRadioButton>("create-tracklist-btn")};
createTrackListBtn->setChecked();
createTrackListBtn->clicked().connect([=]
{
contentStack->setCurrentIndex(Index::CreateTrackList);
});
}
// Create TrackList
Wt::WTemplateFormView* createTrackList {contentStack->addNew<Wt::WTemplateFormView>(Wt::WString::tr("Lms.PlayQueue.template.save-as-tracklist.create-tracklist"))};
auto createTrackListModel {std::make_shared<CreateTrackListModel>()};
createTrackList->setFormWidget(CreateTrackListModel::NameField, std::make_unique<Wt::WLineEdit>());
createTrackList->updateView(createTrackListModel.get());
// Replace TrackList
Wt::WTemplateFormView* replaceTrackList {contentStack->addNew<Wt::WTemplateFormView>(Wt::WString::tr("Lms.PlayQueue.template.save-as-tracklist.replace-tracklist"))};
auto replaceTrackListModel {std::make_shared<ReplaceTrackListModel>()};
{
auto name {std::make_unique<Wt::WComboBox>()};
name->setModel(replaceTrackListModel->trackListModel);
replaceTrackList->setFormWidget(ReplaceTrackListModel::NameField, std::move(name));
}
replaceTrackList->updateView(replaceTrackListModel.get());
auto* saveBtn {modal->bindNew<Wt::WPushButton>("save-btn", Wt::WString::tr("Lms.save"))};
saveBtn->clicked().connect([=]
{
bool success {};
switch (contentStack->currentIndex())
{
case Index::CreateTrackList:
createTrackList->updateModel(createTrackListModel.get());
if (createTrackListModel->validate())
{
exportToNewTrackList(createTrackListModel->getName());
success = true;
}
createTrackList->updateView(createTrackListModel.get());
break;
case Index::ReplaceTrackList:
replaceTrackList->updateModel(replaceTrackListModel.get());
if (replaceTrackListModel->validate())
{
exportToTrackList(replaceTrackListModel->getTrackListId());
success = true;
}
replaceTrackList->updateView(replaceTrackListModel.get());
break;
}
if (success)
LmsApp->getModalManager().dispose(modalPtr);
});
LmsApp->getModalManager().show(std::move(modal));
}
void
PlayQueue::exportToNewTrackList(const Wt::WString& name)
{
using namespace Database;
Database::TrackListId trackListId;
{
Session& session {LmsApp->getDbSession()};
auto transaction {session.createUniqueTransaction()};
TrackList::pointer trackList {session.create<TrackList>(name.toUTF8(), TrackListType::Playlist, false, LmsApp->getUser())};
trackListId = trackList->getId();
}
exportToTrackList(trackListId);
}
void
PlayQueue::exportToTrackList(Database::TrackListId trackListId)
{
using namespace Database;
Session& session {LmsApp->getDbSession()};
auto transaction {session.createUniqueTransaction()};
TrackList::pointer trackList {TrackList::find(LmsApp->getDbSession(), trackListId)};
trackList.modify()->clear();
Track::FindParameters params;
params.setTrackList(_tracklistId);
params.setDistinct(false);
params.setSortMethod(TrackSortMethod::TrackList);
const auto tracks {Track::find(session, params)};
for (const TrackId trackId : tracks.results)
session.create<TrackListEntry>(Track::find(session, trackId), trackList);
}
} // namespace UserInterface
+4
View File
@@ -89,6 +89,10 @@ class PlayQueue : public Template
void addRadioTrackFromSimilarity(std::shared_ptr<Similarity::Finder> similarityFinder);
void addRadioTrackFromClusters();
std::optional<float> getReplayGain(std::size_t pos, const Database::ObjectPtr<Database::Track>& track) const;
void saveAsTrackList();
void exportToNewTrackList(const Wt::WString& name);
void exportToTrackList(Database::TrackListId trackList);
static inline constexpr std::size_t _capacity {1000};
static inline constexpr std::size_t _batchSize {12};
+1 -1
View File
@@ -223,6 +223,7 @@ getTrackListTracks(Database::Session& session, Database::TrackListId trackListId
params.setClusters(clusters);
params.setRange({0, maxTrackCount});
params.setSortMethod(TrackSortMethod::TrackList);
params.setDistinct(false);
return Database::Track::find(session, params).results;
}
@@ -252,6 +253,5 @@ Explore::handleTracksAction(PlayQueueAction action, const std::vector<Database::
tracksAction.emit(action, tracksId);
}
} // namespace UserInterface
+3 -18
View File
@@ -94,7 +94,8 @@ namespace UserInterface
bindString("name", std::string {trackList->getName()}, Wt::TextFormat::Plain);
bindString("duration", Utils::durationToString(trackList->getDuration()));
bindString("track-count", Wt::WString::trn("Lms.Explore.TrackList.track-count", trackList->getCount()).arg(trackList->getCount()));
const auto trackCount {trackList->getCount()};
bindString("track-count", Wt::WString::trn("Lms.track-count", trackCount).arg(trackCount));
Wt::WContainerWidget* clusterContainers {bindNew<Wt::WContainerWidget>("clusters")};
{
@@ -189,6 +190,7 @@ namespace UserInterface
params.setTrackList(_trackListId);
params.setSortMethod(Database::TrackSortMethod::TrackList);
params.setRange({static_cast<std::size_t>(_container->getCount()), _batchSize});
params.setDistinct(false);
const auto trackIds {Database::Track::find(LmsApp->getDbSession(), params)};
for (const TrackId trackId : trackIds.results)
@@ -199,22 +201,5 @@ namespace UserInterface
_container->setHasMore(trackIds.moreResults && _container->getCount() < _maxCount);
}
/*
void
Tracks::addSome()
{
}
std::vector<Database::TrackId>
Tracks::getAllTracks()
{
RangeResults<TrackId> trackIds {_trackCollector.get(Range {})};
return std::move(trackIds.results);
}
*/
} // namespace UserInterface