From 2bd4fb8c78b6b5482ec43099dedc2ddc4725e1a8 Mon Sep 17 00:00:00 2001 From: emeric Date: Thu, 2 Apr 2020 20:54:31 +0200 Subject: [PATCH] WIP --- src/libs/database/impl/Artist.cpp | 6 +- src/libs/database/impl/Session.cpp | 8 ++- src/libs/database/include/database/Artist.hpp | 21 ++++-- src/libs/metadata/impl/AvFormatParser.cpp | 6 +- src/libs/metadata/impl/TagLibParser.cpp | 71 ++++++++++++------- .../metadata/include/metadata/IParser.hpp | 5 ++ src/libs/scanner/impl/MediaScanner.cpp | 49 ++++++++++--- src/tools/metadata/CMakeLists.txt | 1 + src/tools/metadata/LmsMetadata.cpp | 3 + 9 files changed, 122 insertions(+), 48 deletions(-) diff --git a/src/libs/database/impl/Artist.cpp b/src/libs/database/impl/Artist.cpp index b5c6d221..d2571e91 100644 --- a/src/libs/database/impl/Artist.cpp +++ b/src/libs/database/impl/Artist.cpp @@ -171,13 +171,13 @@ getQuery(Session& session, } std::vector -Artist::getByClusters(Session& session, const std::set& clusters) +Artist::getByClusters(Session& session, const std::set& clusters, NameSortMethod sortMethod) { assert(!clusters.empty()); session.checkSharedLocked(); bool more; - return getByFilter(session, clusters, {}, {}, {}, {}, more); + return getByFilter(session, clusters, {}, {}, sortMethod, {}, {}, more); } std::vector @@ -185,6 +185,7 @@ Artist::getByFilter(Session& session, const std::set& clusters, const std::vector& keywords, std::optional linkType, + NameSortMethod sortMethod, std::optional offset, std::optional size, bool& moreResults) @@ -405,6 +406,7 @@ void Artist::setSortName(const std::string& sortName) { _sortName = std::string(sortName, 0 , _maxNameLength); + LMS_LOG(DB, DEBUG) << "SORT NAME = '" << _sortName << "'"; } } // namespace Database diff --git a/src/libs/database/impl/Session.cpp b/src/libs/database/impl/Session.cpp index 041bfc41..750fc32f 100644 --- a/src/libs/database/impl/Session.cpp +++ b/src/libs/database/impl/Session.cpp @@ -40,7 +40,7 @@ namespace Database { -#define LMS_DATABASE_VERSION 14 +#define LMS_DATABASE_VERSION 15 using Version = std::size_t; @@ -163,6 +163,12 @@ CREATE TABLE IF NOT EXISTS "track_bookmark" ( // Just increment the scan version of the settings to make the next scheduled scan rescan everything ScanSettings::get(*this).modify()->incScanVersion(); } + else if (version == 14) + { + // SortName now set from metadata + // Just increment the scan version of the settings to make the next scheduled scan rescan everything + ScanSettings::get(*this).modify()->incScanVersion(); + } else { LMS_LOG(DB, ERROR) << "Database version " << version << " cannot be handled using migration"; diff --git a/src/libs/database/include/database/Artist.hpp b/src/libs/database/include/database/Artist.hpp index 4d515c7f..44fa339c 100644 --- a/src/libs/database/include/database/Artist.hpp +++ b/src/libs/database/include/database/Artist.hpp @@ -45,6 +45,13 @@ class Artist : public Wt::Dbo::Dbo { public: + enum class NameSortMethod + { + None, + ByName, + BySortName, + }; + using pointer = Wt::Dbo::ptr; Artist() {} @@ -55,24 +62,28 @@ class Artist : public Wt::Dbo::Dbo static pointer getById(Session& session, IdType id); static std::vector getByName(Session& session, const std::string& name); static std::vector getByClusters(Session& session, - const std::set& clusters); // at least one track that belongs to these clusters + const std::set& clusters, // at least one track that belongs to these clusters + NameSortMethod sortMethod + ); static std::vector getByFilter(Session& session, const std::set& clusters, // if non empty, at least one artist that belongs to these clusters const std::vector& keywords, // if non empty, name must match all of these keywords std::optional linkType, // if set, only artists that have produced at least one track with this link type + NameSortMethod sortMethod, std::optional offset, std::optional size, bool& moreExpected); - static std::vector getAll(Session& session, std::optional offset = {}, std::optional size = {}); + static std::vector getAll(Session& session, NameSortMethod sortMethod, std::optional offset = {}, std::optional size = {}); static std::vector getAllIds(Session& session); static std::vector getAllOrphans(Session& session); // No track related static std::vector getLastAdded(Session& session, Wt::WDateTime after, std::optional size = {}); static std::vector getAllIdsWithClusters(Session& session, std::optional limit = {}); // Accessors - const std::string& getName(void) const { return _name; } - std::optional getMBID(void) const { return UUID::fromString(_MBID); } + const std::string& getName() const { return _name; } + const std::string& getSortName() const { return _sortName; } + std::optional getMBID() const { return UUID::fromString(_MBID); } std::vector> getReleases(const std::set& clusterIds = {}) const; // if non empty, get the releases that match all these clusters std::size_t getReleaseCount() const; @@ -97,7 +108,7 @@ class Artist : public Wt::Dbo::Dbo void persist(Action& a) { Wt::Dbo::field(a, _name, "name"); - Wt::Dbo::field(a, _name, "sort_name"); + Wt::Dbo::field(a, _sortName, "sort_name"); Wt::Dbo::field(a, _MBID, "mbid"); Wt::Dbo::hasMany(a, _trackArtistLinks, Wt::Dbo::ManyToOne, "artist"); diff --git a/src/libs/metadata/impl/AvFormatParser.cpp b/src/libs/metadata/impl/AvFormatParser.cpp index 7dfd8af4..ae2d9495 100644 --- a/src/libs/metadata/impl/AvFormatParser.cpp +++ b/src/libs/metadata/impl/AvFormatParser.cpp @@ -94,7 +94,7 @@ getAlbumArtists(const MetadataMap& metadataMap) auto mbid {findFirstValueOfAs(metadataMap, {"MUSICBRAINZ ALBUM ARTIST ID", "MUSICBRAINZ/ALBUM ARTIST ID"})}; - return {Artist {*name, mbid} }; + return {Artist {*name, std::nullopt, mbid} }; } static @@ -118,9 +118,9 @@ getArtists(const MetadataMap& metadataMap) for (std::size_t i {}; i < artistNames.size(); ++i) { if (artistMBIDs && artistNames.size() == artistMBIDs->size()) - artists.emplace_back(Artist {artistNames[i], (*artistMBIDs)[i]}); + artists.emplace_back(Artist {artistNames[i], std::nullopt, (*artistMBIDs)[i]}); else - artists.emplace_back(Artist {artistNames[i], {}}); + artists.emplace_back(Artist {artistNames[i], std::nullopt, {}}); } return artists; diff --git a/src/libs/metadata/impl/TagLibParser.cpp b/src/libs/metadata/impl/TagLibParser.cpp index fee48f25..2fe31d73 100644 --- a/src/libs/metadata/impl/TagLibParser.cpp +++ b/src/libs/metadata/impl/TagLibParser.cpp @@ -87,55 +87,74 @@ static std::vector getArtists(const TagLib::PropertyMap& properties) { - std::vector res; - std::vector artistNames {getPropertyValuesAs(properties, "ARTISTS")}; if (artistNames.empty()) artistNames = getPropertyValuesAs(properties, "ARTIST"); if (artistNames.empty()) - return res; + return {}; - const std::vector artistsMBID {getPropertyValuesFirstMatchAs(properties, {"MUSICBRAINZ_ARTISTID", "MUSICBRAINZ ARTIST ID"})}; + std::vector artists; + artists.reserve(artistNames.size()); + std::transform(std::cbegin(artistNames), std::cend(artistNames), std::back_inserter(artists), + [&](const std::string& name) { return Artist {name}; }); - if (artistNames.size() == artistsMBID.size()) { - std::transform(std::cbegin(artistNames), std::cend(artistNames), std::cbegin(artistsMBID), std::back_inserter(res), - [&](const std::string& name, const UUID& mbid) { return Artist {name, mbid}; }); - } - else - { - std::transform(std::cbegin(artistNames), std::cend(artistNames), std::back_inserter(res), - [&](const std::string& name) { return Artist{name, {}}; }); + const std::vector artistSortNames {getPropertyValuesAs(properties, "ARTISTSORT")}; + if (artistSortNames.size() == artists.size()) + { + for (std::size_t i {}; i < artistSortNames.size(); ++i) + artists[i].sortName = artistSortNames[i]; + } } - return res; + { + const std::vector artistsMBID {getPropertyValuesFirstMatchAs(properties, {"MUSICBRAINZ_ARTISTID", "MUSICBRAINZ ARTIST ID"})}; + + if (artistNames.size() == artistsMBID.size()) + { + for (std::size_t i {}; i < artistsMBID.size(); ++i) + artists[i].musicBrainzArtistID = artistsMBID[i]; + } + } + + + return artists; } static std::vector getAlbumArtists(const TagLib::PropertyMap& properties) { - std::vector res; - std::vector artistNames {getPropertyValuesAs(properties, "ALBUMARTIST")}; if (artistNames.empty()) - return res; + return {}; - const std::vector artistsMBID {getPropertyValuesFirstMatchAs(properties, {"MUSICBRAINZ_ALBUMARTISTID", "MUSICBRAINZ ALBUM ARTIST ID"})}; + std::vector artists; + artists.reserve(artistNames.size()); + std::transform(std::cbegin(artistNames), std::cend(artistNames), std::back_inserter(artists), + [&](const std::string& name) { return Artist {name}; }); - if (artistNames.size() == artistsMBID.size()) { - std::transform(std::cbegin(artistNames), std::cend(artistNames), std::cbegin(artistsMBID), std::back_inserter(res), - [&](const std::string& name, const UUID& mbid) { return Artist{name, mbid}; }); - } - else - { - std::transform(std::cbegin(artistNames), std::cend(artistNames), std::back_inserter(res), - [&](const std::string& name) { return Artist{name, {}}; }); + const std::vector artistSortNames {getPropertyValuesAs(properties, "ALBUMARTISTSORT")}; + if (artistSortNames.size() == artists.size()) + { + for (std::size_t i {}; i < artistSortNames.size(); ++i) + artists[i].sortName = artistSortNames[i]; + } } - return res; + { + const std::vector artistsMBID {getPropertyValuesFirstMatchAs(properties, {"MUSICBRAINZ_ALBUMARTISTID", "MUSICBRAINZ ALBUM ARTIST ID"})}; + + if (artistsMBID.size() == artists.size()) + { + for (std::size_t i {}; i < artistsMBID.size(); ++i) + artists[i].musicBrainzArtistID = artistsMBID[i]; + } + } + + return artists; } static diff --git a/src/libs/metadata/include/metadata/IParser.hpp b/src/libs/metadata/include/metadata/IParser.hpp index abb12216..014fed3e 100644 --- a/src/libs/metadata/include/metadata/IParser.hpp +++ b/src/libs/metadata/include/metadata/IParser.hpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "utils/UUID.hpp" @@ -35,7 +36,11 @@ namespace MetaData struct Artist { std::string name; + std::optional sortName; std::optional musicBrainzArtistID; + + Artist(std::string_view _name) : name {_name} {} + Artist(std::string_view _name, std::optional _sortName, std::optional _musicBrainzArtistID) : name {_name}, sortName {_sortName}, musicBrainzArtistID {_musicBrainzArtistID} {} }; struct Album diff --git a/src/libs/scanner/impl/MediaScanner.cpp b/src/libs/scanner/impl/MediaScanner.cpp index e70b1c43..ca0c4715 100644 --- a/src/libs/scanner/impl/MediaScanner.cpp +++ b/src/libs/scanner/impl/MediaScanner.cpp @@ -84,6 +84,38 @@ isPathInParentPath(const std::filesystem::path& path, const std::filesystem::pat return false; } +static +Artist::pointer +createArtist(Session& session, const MetaData::Artist& artistInfo) +{ + Artist::pointer artist {Artist::create(session, artistInfo.name)}; + + if (artistInfo.musicBrainzArtistID) + artist.modify()->setMBID(*artistInfo.musicBrainzArtistID); + if (artistInfo.sortName) + artist.modify()->setSortName(*artistInfo.sortName); + + return artist; +} + +static +void +updateArtistIfNeeded(const Artist::pointer& artist, const MetaData::Artist& artistInfo) +{ + // Name may have been updated + if (artist->getName() != artistInfo.name) + { + artist.modify()->setName(artistInfo.name); + } + + // Sortname may have been updated + if (artistInfo.sortName && *artistInfo.sortName != artist->getSortName() ) + { + LMS_LOG(DBUPDATER, INFO) << "Setting sort name = '" << *artistInfo.sortName << "'"; + artist.modify()->setSortName(*artistInfo.sortName); + } +} + std::vector getOrCreateArtists(Session& session, const std::vector& artistsInfo) { @@ -98,14 +130,9 @@ getOrCreateArtists(Session& session, const std::vector& artist { artist = Artist::getByMBID(session, *artistInfo.musicBrainzArtistID); if (!artist) - { - artist = Artist::create(session, artistInfo.name, artistInfo.musicBrainzArtistID); - } - else if (artist->getName() != artistInfo.name) - { - // Name may have been updated - artist.modify()->setName(artistInfo.name); - } + artist = createArtist(session, artistInfo); + else + updateArtistIfNeeded(artist, artistInfo); artists.emplace_back(std::move(artist)); continue; @@ -126,7 +153,9 @@ getOrCreateArtists(Session& session, const std::vector& artist // No Artist found with the same name and without MBID -> creating if (!artist) - artist = Artist::create(session, artistInfo.name); + artist = createArtist(session, artistInfo); + else + updateArtistIfNeeded(artist, artistInfo); artists.emplace_back(std::move(artist)); continue; @@ -430,9 +459,7 @@ MediaScanner::scan(boost::system::error_code err) LMS_LOG(DBUPDATER, INFO) << "Scan " << (_running ? "complete" : "aborted") << ". Changes = " << stats.nbChanges() << " (added = " << stats.additions << ", removed = " << stats.deletions << ", updated = " << stats.updates << "), Not changed = " << stats.skips << ", Scanned = " << stats.scans << " (errors = " << stats.errors.size() << "), features fetched = " << stats.featuresFetched << "/" << stats.featuresToFetch <<", duplicates = " << stats.duplicates.size(); - LMS_LOG(DBUPDATER, INFO) << "Optimizing db..."; _dbSession.optimize(); - LMS_LOG(DBUPDATER, INFO) << "Optimize db done!"; if (_running) { diff --git a/src/tools/metadata/CMakeLists.txt b/src/tools/metadata/CMakeLists.txt index 4c0706cf..0a831918 100644 --- a/src/tools/metadata/CMakeLists.txt +++ b/src/tools/metadata/CMakeLists.txt @@ -5,6 +5,7 @@ add_executable(lms-metadata target_link_libraries(lms-metadata PRIVATE lmsmetadata + lmsutils ) install(TARGETS lms-metadata DESTINATION bin) diff --git a/src/tools/metadata/LmsMetadata.cpp b/src/tools/metadata/LmsMetadata.cpp index 7a04e178..b3af8687 100644 --- a/src/tools/metadata/LmsMetadata.cpp +++ b/src/tools/metadata/LmsMetadata.cpp @@ -36,6 +36,9 @@ std::ostream& operator<<(std::ostream& os, const MetaData::Artist& artist) if (artist.musicBrainzArtistID) os << " (" << artist.musicBrainzArtistID->getAsString() << ")"; + if (artist.sortName) + os << " '" << *artist.sortName << "'"; + return os; }