273 lines
7.5 KiB
C++
273 lines
7.5 KiB
C++
/*
|
|
* Copyright (C) 2018 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 "ArtistsView.hpp"
|
|
|
|
#include <optional>
|
|
|
|
#include <Wt/WMenu.h>
|
|
|
|
#include "common/ValueStringModel.hpp"
|
|
#include "database/Artist.hpp"
|
|
#include "database/Session.hpp"
|
|
#include "database/User.hpp"
|
|
#include "database/TrackArtistLink.hpp"
|
|
#include "database/TrackList.hpp"
|
|
#include "utils/Logger.hpp"
|
|
|
|
#include "common/LoadingIndicator.hpp"
|
|
#include "ArtistListHelpers.hpp"
|
|
#include "LmsApplication.hpp"
|
|
#include "Filters.hpp"
|
|
|
|
using namespace Database;
|
|
|
|
namespace UserInterface {
|
|
|
|
using ArtistLinkModel = ValueStringModel<std::optional<TrackArtistLinkType>>;
|
|
|
|
Artists::Artists(Filters* filters)
|
|
: Wt::WTemplate {Wt::WString::tr("Lms.Explore.Artists.template")},
|
|
_filters {filters}
|
|
{
|
|
addFunction("tr", &Wt::WTemplate::Functions::tr);
|
|
|
|
{
|
|
auto* menu {bindNew<Wt::WMenu>("mode")};
|
|
|
|
auto addItem = [this](Wt::WMenu& menu,const Wt::WString& str, Mode mode)
|
|
{
|
|
auto* item {menu.addItem(str)};
|
|
item->clicked().connect([this, mode] { refreshView(mode); });
|
|
|
|
if (mode == defaultMode)
|
|
item->renderSelected(true);
|
|
};
|
|
|
|
addItem(*menu, Wt::WString::tr("Lms.Explore.random"), Mode::Random);
|
|
addItem(*menu, Wt::WString::tr("Lms.Explore.starred"), Mode::Starred);
|
|
addItem(*menu, Wt::WString::tr("Lms.Explore.recently-played"), Mode::RecentlyPlayed);
|
|
addItem(*menu, Wt::WString::tr("Lms.Explore.most-played"), Mode::MostPlayed);
|
|
addItem(*menu, Wt::WString::tr("Lms.Explore.recently-added"), Mode::RecentlyAdded);
|
|
addItem(*menu, Wt::WString::tr("Lms.Explore.all"), Mode::All);
|
|
}
|
|
|
|
_linkType = bindNew<Wt::WComboBox>("link-type");
|
|
_linkType->setModel(std::make_shared<ArtistLinkModel>());
|
|
_linkType->changed().connect([this] { refreshView(); });
|
|
refreshArtistLinkTypes();
|
|
|
|
LmsApp->getEvents().dbScanned.connect(this, [this]
|
|
{
|
|
refreshArtistLinkTypes();
|
|
});
|
|
|
|
_container = bindNew<Wt::WContainerWidget>("artists");
|
|
hideLoadingIndicator();
|
|
|
|
refreshView();
|
|
|
|
filters->updated().connect([this] { refreshView(); });
|
|
}
|
|
|
|
void
|
|
Artists::refreshView()
|
|
{
|
|
_container->clear();
|
|
_randomArtists.clear();
|
|
addSome();
|
|
}
|
|
|
|
void
|
|
Artists::refreshView(Mode mode)
|
|
{
|
|
_mode = mode;
|
|
refreshView();
|
|
}
|
|
|
|
void
|
|
Artists::refreshArtistLinkTypes()
|
|
{
|
|
std::shared_ptr<ArtistLinkModel> linkTypeModel {std::static_pointer_cast<ArtistLinkModel>(_linkType->model())};
|
|
|
|
EnumSet<Database::TrackArtistLinkType> usedLinkTypes;
|
|
{
|
|
auto transaction {LmsApp->getDbSession().createSharedTransaction()};
|
|
usedLinkTypes = Database::TrackArtistLink::getUsedTypes(LmsApp->getDbSession());
|
|
}
|
|
|
|
auto addTypeIfUsed {[&](Database::TrackArtistLinkType linkType, std::string_view stringKey)
|
|
{
|
|
if (!usedLinkTypes.contains(linkType))
|
|
return;
|
|
|
|
linkTypeModel->add(Wt::WString::tr(std::string {stringKey}), linkType);
|
|
}};
|
|
|
|
linkTypeModel->clear();
|
|
|
|
linkTypeModel->add(Wt::WString::tr("Lms.Explore.Artists.linktype-all"), {});
|
|
addTypeIfUsed(TrackArtistLinkType::Artist, "Lms.Explore.Artists.linktype-artist");
|
|
addTypeIfUsed(TrackArtistLinkType::ReleaseArtist, "Lms.Explore.Artists.linktype-releaseartist");
|
|
addTypeIfUsed(TrackArtistLinkType::Composer, "Lms.Explore.Artists.linktype-composer");
|
|
addTypeIfUsed(TrackArtistLinkType::Lyricist, "Lms.Explore.Artists.linktype-lyricist");
|
|
addTypeIfUsed(TrackArtistLinkType::Mixer, "Lms.Explore.Artists.linktype-mixer");
|
|
addTypeIfUsed(TrackArtistLinkType::Producer, "Lms.Explore.Artists.linktype-producer");
|
|
addTypeIfUsed(TrackArtistLinkType::Remixer, "Lms.Explore.Artists.linktype-remixer");
|
|
}
|
|
|
|
void
|
|
Artists::displayLoadingIndicator()
|
|
{
|
|
_loadingIndicator = bindWidget<Wt::WTemplate>("loading-indicator", createLoadingIndicator());
|
|
_loadingIndicator->scrollVisibilityChanged().connect([this](bool visible)
|
|
{
|
|
if (!visible)
|
|
return;
|
|
|
|
addSome();
|
|
});
|
|
}
|
|
|
|
void
|
|
Artists::hideLoadingIndicator()
|
|
{
|
|
_loadingIndicator = nullptr;
|
|
bindEmpty("loading-indicator");
|
|
}
|
|
|
|
std::vector<Artist::pointer>
|
|
Artists::getRandomArtists(std::optional<Range> range, bool& moreResults)
|
|
{
|
|
std::vector<Artist::pointer> artists;
|
|
|
|
const std::optional<TrackArtistLinkType> linkType {static_cast<ArtistLinkModel*>(_linkType->model().get())->getValue(_linkType->currentIndex())};
|
|
|
|
if (_randomArtists.empty())
|
|
_randomArtists = Artist::getAllIdsRandom(LmsApp->getDbSession(), _filters->getClusterIds(), linkType, maxItemsPerMode[Mode::Random]);
|
|
|
|
{
|
|
auto itBegin {std::cbegin(_randomArtists) + std::min(range ? range->offset : 0, _randomArtists.size())};
|
|
auto itEnd {std::cbegin(_randomArtists) + std::min(range ? range->offset + range->limit : _randomArtists.size(), _randomArtists.size())};
|
|
|
|
for (auto it {itBegin}; it != itEnd; ++it)
|
|
{
|
|
Artist::pointer artist {Artist::getById(LmsApp->getDbSession(), *it)};
|
|
if (artist)
|
|
artists.push_back(artist);
|
|
}
|
|
|
|
moreResults = (itEnd != std::cend(_randomArtists));
|
|
}
|
|
|
|
return artists;
|
|
}
|
|
|
|
std::vector<Artist::pointer>
|
|
Artists::getArtists(std::optional<Range> range, bool& moreResults)
|
|
{
|
|
std::vector<Artist::pointer> artists;
|
|
|
|
const std::optional<TrackArtistLinkType> linkType {static_cast<ArtistLinkModel*>(_linkType->model().get())->getValue(_linkType->currentIndex())};
|
|
|
|
const std::optional<std::size_t> modeLimit{maxItemsPerMode[_mode]};
|
|
if (modeLimit)
|
|
{
|
|
if (range)
|
|
range->limit = std::min(*modeLimit - range->offset, range->limit);
|
|
else
|
|
range = Range {0, *modeLimit};
|
|
}
|
|
|
|
switch (_mode)
|
|
{
|
|
case Mode::Random:
|
|
artists = getRandomArtists(range, moreResults);
|
|
break;
|
|
|
|
case Mode::Starred:
|
|
artists = Artist::getStarred(LmsApp->getDbSession(),
|
|
LmsApp->getUser(),
|
|
_filters->getClusterIds(),
|
|
linkType,
|
|
Artist::SortMethod::BySortName,
|
|
range, moreResults);
|
|
break;
|
|
|
|
case Mode::RecentlyPlayed:
|
|
artists = LmsApp->getUser()->getPlayedTrackList(LmsApp->getDbSession())
|
|
->getArtistsReverse(_filters->getClusterIds(),
|
|
linkType,
|
|
range, moreResults);
|
|
break;
|
|
|
|
case Mode::MostPlayed:
|
|
artists = LmsApp->getUser()->getPlayedTrackList(LmsApp->getDbSession())
|
|
->getTopArtists(_filters->getClusterIds(),
|
|
linkType,
|
|
range, moreResults);
|
|
break;
|
|
|
|
case Mode::RecentlyAdded:
|
|
artists = Artist::getLastWritten(LmsApp->getDbSession(),
|
|
std::nullopt,
|
|
_filters->getClusterIds(),
|
|
linkType,
|
|
range, moreResults);
|
|
break;
|
|
|
|
case Mode::All:
|
|
artists = Artist::getByFilter(LmsApp->getDbSession(),
|
|
_filters->getClusterIds(),
|
|
{},
|
|
linkType,
|
|
Artist::SortMethod::BySortName,
|
|
range, moreResults);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (range && modeLimit && (range->offset + range->limit == *modeLimit))
|
|
moreResults = false;
|
|
|
|
return artists;
|
|
}
|
|
|
|
void
|
|
Artists::addSome()
|
|
{
|
|
auto transaction {LmsApp->getDbSession().createSharedTransaction()};
|
|
|
|
bool moreResults {};
|
|
for (const Artist::pointer& artist : getArtists(Range {static_cast<std::size_t>(_container->count()), batchSize}, moreResults))
|
|
{
|
|
_container->addWidget(ArtistListHelpers::createEntry(artist));
|
|
}
|
|
|
|
if (moreResults)
|
|
displayLoadingIndicator();
|
|
else
|
|
hideLoadingIndicator();
|
|
}
|
|
|
|
} // namespace UserInterface
|
|
|