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); } {