diff --git a/README.md b/README.md
index c30d4062..e61d90f8 100644
--- a/README.md
+++ b/README.md
@@ -65,3 +65,6 @@ __Note__: since _LMS_ stores hashed and salted passwords, it cannot handle the _
See [INSTALL.md](INSTALL.md) file.
+## Contributing
+
+Contributions are welcome! Please prepare your pull requests on the [develop](../../tree/develop) branch.
diff --git a/approot/messages.xml b/approot/messages.xml
index 7c792916..82885ada 100644
--- a/approot/messages.xml
+++ b/approot/messages.xml
@@ -199,6 +199,7 @@
Artist list mode
All artists
Album artists
+Track artists
Subsonic API
Transcoding
Transcode bitrate
diff --git a/approot/messages_fr.xml b/approot/messages_fr.xml
index c90d20c4..8634953f 100644
--- a/approot/messages_fr.xml
+++ b/approot/messages_fr.xml
@@ -199,6 +199,7 @@
Mode de listage des artistes
Tous les artistes
Tous les artistes d'album
+Tous les artistes de piste
API Subsonic
Transcodage
Bitrate du transcodage
diff --git a/src/libs/database/impl/Track.cpp b/src/libs/database/impl/Track.cpp
index d71d410a..13ec9cf6 100644
--- a/src/libs/database/impl/Track.cpp
+++ b/src/libs/database/impl/Track.cpp
@@ -509,12 +509,13 @@ Track::getArtists(EnumSet linkTypes) const
oss << ")";
}
- Wt::Dbo::Query query {session()->query(oss.str())
- .where("t.id = ?").bind(self()->id())};
+ Wt::Dbo::Query query {session()->query(oss.str())};
for (TrackArtistLinkType type : linkTypes)
query.bind(type);
+ query.where("t.id = ?").bind(self()->id());
+
Wt::Dbo::collection res = query;
return std::vector(std::begin(res), std::end(res));
}
diff --git a/src/libs/database/include/database/User.hpp b/src/libs/database/include/database/User.hpp
index 73f0749b..576328d6 100644
--- a/src/libs/database/include/database/User.hpp
+++ b/src/libs/database/include/database/User.hpp
@@ -132,8 +132,9 @@ class User : public Wt::Dbo::Dbo
// Do not change enum values!
enum class SubsonicArtistListMode
{
- AllArtists = 0,
+ AllArtists = 0,
ReleaseArtists = 1,
+ TrackArtists = 2,
};
static inline const std::size_t MinNameLength {3};
diff --git a/src/libs/metadata/impl/TagLibParser.cpp b/src/libs/metadata/impl/TagLibParser.cpp
index 044dde49..8b7d70c3 100644
--- a/src/libs/metadata/impl/TagLibParser.cpp
+++ b/src/libs/metadata/impl/TagLibParser.cpp
@@ -397,7 +397,7 @@ TagLibParser::parse(const std::filesystem::path& p, bool debug)
}
track.album = getAlbum(properties);
- track.artists = getArtists(properties, {"ARTIST", "ARTISTS"}, {"ARTISTSORT"}, {"MUSICBRAINZ_ARTISTID", "MUSICBRAINZ ARTIST ID"});
+ track.artists = getArtists(properties, {"ARTISTS", "ARTIST"}, {"ARTISTSORT"}, {"MUSICBRAINZ_ARTISTID", "MUSICBRAINZ ARTIST ID"});
track.albumArtists = getArtists(properties, {"ALBUMARTIST"}, {"ALBUMARTISTSORT"}, {"MUSICBRAINZ_ALBUMARTISTID", "MUSICBRAINZ ALBUM ARTIST ID"});
track.conductorArtists = getArtists(properties, {"CONDUCTOR"}, {""}, {});
track.composerArtists = getArtists(properties, {"COMPOSER"}, {"COMPOSERSORT"}, {});
diff --git a/src/libs/subsonic/impl/SubsonicResource.cpp b/src/libs/subsonic/impl/SubsonicResource.cpp
index d59a5db2..65f7427e 100644
--- a/src/libs/subsonic/impl/SubsonicResource.cpp
+++ b/src/libs/subsonic/impl/SubsonicResource.cpp
@@ -316,7 +316,8 @@ trackToResponseNode(const Track::pointer& track, Session& dbSession, const User:
trackResponse.setAttribute("coverArt", IdToString({Id::Type::Track, track.id()}));
- auto artists {track->getArtists({TrackArtistLinkType::Artist})};
+ const std::vector& artists {track->getArtists({TrackArtistLinkType::Artist})};
+ LMS_LOG(API_SUBSONIC, DEBUG) << "Artists count = " << artists.size();
if (!artists.empty())
{
trackResponse.setAttribute("artist", getArtistNames(artists));
@@ -954,6 +955,9 @@ handleGetArtistsRequest(RequestContext& context)
case User::SubsonicArtistListMode::ReleaseArtists:
linkType = TrackArtistLinkType::ReleaseArtist;
break;
+ case User::SubsonicArtistListMode::TrackArtists:
+ linkType = TrackArtistLinkType::Artist;
+ break;
}
bool more {};
@@ -1098,6 +1102,9 @@ handleGetIndexesRequest(RequestContext& context)
case User::SubsonicArtistListMode::ReleaseArtists:
linkType = TrackArtistLinkType::ReleaseArtist;
break;
+ case User::SubsonicArtistListMode::TrackArtists:
+ linkType = TrackArtistLinkType::Artist;
+ break;
}
bool more {};
diff --git a/src/lms/ui/LmsApplication.cpp b/src/lms/ui/LmsApplication.cpp
index b836ebab..a38c3da1 100644
--- a/src/lms/ui/LmsApplication.cpp
+++ b/src/lms/ui/LmsApplication.cpp
@@ -541,7 +541,7 @@ LmsApplication::createHome()
{
const std::string sessionId {LmsApp->sessionId()};
- Service::get()->scanStarted().connect(this, [=] ()
+ Service::get()->scanStarted().connect(this, [=]
{
Wt::WServer::instance()->post(sessionId, [=]
{
@@ -550,9 +550,9 @@ LmsApplication::createHome()
});
});
- Service::get()->scanComplete().connect(this, [=] ()
+ Service::get()->scanComplete().connect(this, [=]
{
- Wt::WServer::instance()->post(sessionId, [=]
+ Wt::WServer::instance()->post(sessionId, [this]
{
_events.dbScanned.emit();
triggerUpdate();
diff --git a/src/lms/ui/SettingsView.cpp b/src/lms/ui/SettingsView.cpp
index 133ae59e..33f50856 100644
--- a/src/lms/ui/SettingsView.cpp
+++ b/src/lms/ui/SettingsView.cpp
@@ -333,6 +333,7 @@ class SettingsModel : public Wt::WFormModel
_subsonicArtistListModeModel = std::make_shared>();
_subsonicArtistListModeModel->add(Wt::WString::tr("Lms.Settings.subsonic-artist-list-mode.all-artists"), User::SubsonicArtistListMode::AllArtists);
_subsonicArtistListModeModel->add(Wt::WString::tr("Lms.Settings.subsonic-artist-list-mode.release-artists"), User::SubsonicArtistListMode::ReleaseArtists);
+ _subsonicArtistListModeModel->add(Wt::WString::tr("Lms.Settings.subsonic-artist-list-mode.track-artists"), User::SubsonicArtistListMode::TrackArtists);
}
bool _withOldPassword {};
@@ -346,12 +347,12 @@ class SettingsModel : public Wt::WFormModel
SettingsView::SettingsView()
{
- wApp->internalPathChanged().connect([=]
+ wApp->internalPathChanged().connect(this, [this]
{
refreshView();
});
- LmsApp->getMediaPlayer().settingsLoaded.connect([=]()
+ LmsApp->getMediaPlayer().settingsLoaded.connect([this]
{
refreshView();
});
diff --git a/src/lms/ui/admin/DatabaseSettingsView.cpp b/src/lms/ui/admin/DatabaseSettingsView.cpp
index fa0fbbc8..af11f6ea 100644
--- a/src/lms/ui/admin/DatabaseSettingsView.cpp
+++ b/src/lms/ui/admin/DatabaseSettingsView.cpp
@@ -167,10 +167,10 @@ class DatabaseSettingsModel : public Wt::WFormModel
DatabaseSettingsView::DatabaseSettingsView()
{
- wApp->internalPathChanged().connect(std::bind([=]
+ wApp->internalPathChanged().connect(this, [this]
{
refreshView();
- }));
+ });
refreshView();
}
diff --git a/src/lms/ui/admin/ScannerController.cpp b/src/lms/ui/admin/ScannerController.cpp
index 7f7d2796..87f9ae9b 100644
--- a/src/lms/ui/admin/ScannerController.cpp
+++ b/src/lms/ui/admin/ScannerController.cpp
@@ -139,7 +139,7 @@ ScannerController::ScannerController()
auto onDbEvent = [&]() { refreshContents(); };
- LmsApp->getEvents().dbScanStarted.connect(this, []()
+ LmsApp->getEvents().dbScanStarted.connect(this, []
{
LmsApp->notifyMsg(MsgType::Info, Wt::WString::tr("Lms.Admin.Database.scan-launched"));
});
diff --git a/src/lms/ui/admin/UserView.cpp b/src/lms/ui/admin/UserView.cpp
index 859519d7..33cfb6fb 100644
--- a/src/lms/ui/admin/UserView.cpp
+++ b/src/lms/ui/admin/UserView.cpp
@@ -242,7 +242,7 @@ class UserModel : public Wt::WFormModel
UserView::UserView()
{
- wApp->internalPathChanged().connect([this]()
+ wApp->internalPathChanged().connect(this, [this]()
{
refreshView();
});
diff --git a/src/lms/ui/admin/UsersView.cpp b/src/lms/ui/admin/UsersView.cpp
index af01413e..853023c8 100644
--- a/src/lms/ui/admin/UsersView.cpp
+++ b/src/lms/ui/admin/UsersView.cpp
@@ -44,7 +44,7 @@ UsersView::UsersView()
LmsApp->setInternalPath("/admin/user", true);
});
- wApp->internalPathChanged().connect([this]()
+ wApp->internalPathChanged().connect(this, [this]()
{
refreshView();
});
diff --git a/src/lms/ui/common/ValueStringModel.hpp b/src/lms/ui/common/ValueStringModel.hpp
index 333694f4..5c4dce95 100644
--- a/src/lms/ui/common/ValueStringModel.hpp
+++ b/src/lms/ui/common/ValueStringModel.hpp
@@ -73,6 +73,12 @@ class ValueStringModel : public Wt::WStringListModel
setData(rowCount() - 1, 0, str, Wt::ItemDataRole::Display);
}
+ void
+ clear()
+ {
+ removeRows(0, rowCount());
+ }
+
};
} // namespace UserInterface
diff --git a/src/lms/ui/explore/ArtistView.cpp b/src/lms/ui/explore/ArtistView.cpp
index 978c6278..70f4242d 100644
--- a/src/lms/ui/explore/ArtistView.cpp
+++ b/src/lms/ui/explore/ArtistView.cpp
@@ -50,12 +50,12 @@ Artist::Artist(Filters* filters)
{
addFunction("tr", &Wt::WTemplate::Functions::tr);
- LmsApp->internalPathChanged().connect([=]
+ LmsApp->internalPathChanged().connect(this, [this]
{
refreshView();
});
- filters->updated().connect([=]
+ filters->updated().connect([this]
{
refreshView();
});
diff --git a/src/lms/ui/explore/ArtistsView.cpp b/src/lms/ui/explore/ArtistsView.cpp
index 7cb61753..bdf7430e 100644
--- a/src/lms/ui/explore/ArtistsView.cpp
+++ b/src/lms/ui/explore/ArtistsView.cpp
@@ -69,35 +69,14 @@ Artists::Artists(Filters* filters)
}
_linkType = bindNew("link-type");
-
- {
- auto linkTypeModel {std::make_shared()};
- EnumSet 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->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");
-
- _linkType->setModel(linkTypeModel);
- }
+ _linkType->setModel(std::make_shared());
_linkType->changed().connect([this] { refreshView(); });
+ refreshArtistLinkTypes();
+
+ LmsApp->getEvents().dbScanned.connect(this, [this]
+ {
+ refreshArtistLinkTypes();
+ });
_container = bindNew("artists");
hideLoadingIndicator();
@@ -122,6 +101,37 @@ Artists::refreshView(Mode mode)
refreshView();
}
+void
+Artists::refreshArtistLinkTypes()
+{
+ std::shared_ptr linkTypeModel {std::static_pointer_cast(_linkType->model())};
+
+ EnumSet 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()
{
diff --git a/src/lms/ui/explore/ArtistsView.hpp b/src/lms/ui/explore/ArtistsView.hpp
index f1614392..1c1810fe 100644
--- a/src/lms/ui/explore/ArtistsView.hpp
+++ b/src/lms/ui/explore/ArtistsView.hpp
@@ -56,6 +56,7 @@ class Artists : public Wt::WTemplate
void refreshView();
void refreshView(Mode mode);
+ void refreshArtistLinkTypes();
void displayLoadingIndicator();
void hideLoadingIndicator();
void addSome();
diff --git a/src/lms/ui/explore/Explore.cpp b/src/lms/ui/explore/Explore.cpp
index c7444bbd..27288c05 100644
--- a/src/lms/ui/explore/Explore.cpp
+++ b/src/lms/ui/explore/Explore.cpp
@@ -113,7 +113,7 @@ Explore::Explore(Filters* filters)
tracks->tracksAction.connect(this, &Explore::handleTracksAction);
contentsStack->addWidget(std::move(tracks));
- wApp->internalPathChanged().connect([=]
+ wApp->internalPathChanged().connect(this, [=]
{
handleContentsPathChange(contentsStack);
});
diff --git a/src/lms/ui/explore/ReleaseView.cpp b/src/lms/ui/explore/ReleaseView.cpp
index f3356679..d2dcacac 100644
--- a/src/lms/ui/explore/ReleaseView.cpp
+++ b/src/lms/ui/explore/ReleaseView.cpp
@@ -54,12 +54,12 @@ Release::Release(Filters* filters)
{
addFunction("tr", &Wt::WTemplate::Functions::tr);
- wApp->internalPathChanged().connect([=]
+ wApp->internalPathChanged().connect(this, [this]
{
refreshView();
});
- filters->updated().connect([=]
+ filters->updated().connect([this]
{
refreshView();
});
diff --git a/src/test/database/DatabaseTest.cpp b/src/test/database/DatabaseTest.cpp
index f3e76efa..d12e3c52 100644
--- a/src/test/database/DatabaseTest.cpp
+++ b/src/test/database/DatabaseTest.cpp
@@ -290,7 +290,7 @@ testSingleTrackSingleArtist(Session& session)
CHECK(track->getArtists({TrackArtistLinkType::Artist}).size() == 1);
CHECK(track->getArtists({TrackArtistLinkType::ReleaseArtist}).empty());
- CHECK(track->getArtists({}).empty());
+ CHECK(track->getArtists({}).size() == 1);
}
{