diff --git a/approot/messages.xml b/approot/messages.xml index 36d4bedd..751053d9 100644 --- a/approot/messages.xml +++ b/approot/messages.xml @@ -68,7 +68,7 @@ Scanned {1} files in {2} on {3} ({4} errors, {5} duplicates) No audio track Duplicated file hash -Duplicated MBID +Duplicated recording MBID Scan now Scanner Status diff --git a/approot/messages_fr.xml b/approot/messages_fr.xml index bf3ba440..808cebb4 100644 --- a/approot/messages_fr.xml +++ b/approot/messages_fr.xml @@ -68,7 +68,7 @@ {1} fichiers scannés en {2} le {3} ({4} erreurs, {5} duplicatas) Pas de piste audio Hash dupliqué -MBID dupliqué +Recording MBID dupliqué Lancer un scan Scanner Statut diff --git a/approot/messages_it.xml b/approot/messages_it.xml index f285e9dc..c3a3b895 100644 --- a/approot/messages_it.xml +++ b/approot/messages_it.xml @@ -67,7 +67,7 @@ Scansionati {1} files in {2} su {3} ({4} errori, {5} duplicati) Nessuna traccia audio Hash doppio -MBID doppio +Recording MBID doppio Scansiona ora Scanner Stato diff --git a/approot/messages_zh.xml b/approot/messages_zh.xml index e7f5dab7..cd3c18be 100644 --- a/approot/messages_zh.xml +++ b/approot/messages_zh.xml @@ -68,7 +68,7 @@ 已扫描 {1} 文件 在 {2} 和 {3} ({4} 错误, {5} 副本) 没有音轨 复制文件哈希值 -复制 MBID +复制 Recording MBID 立即扫描 扫描器 状态 diff --git a/conf/lms.conf b/conf/lms.conf index ab0ba1c9..b44af624 100644 --- a/conf/lms.conf +++ b/conf/lms.conf @@ -70,3 +70,6 @@ cover-max-cache-size = 30; # JPEG quality for covers (range is 1-100) cover-jpeg-quality = 75; + +# Set to true if you want to hide duplicate tracks +scanner-skip-duplicate-recording-mbid = false; diff --git a/src/libs/services/database/impl/Track.cpp b/src/libs/services/database/impl/Track.cpp index a93f2339..6c1c2a94 100644 --- a/src/libs/services/database/impl/Track.cpp +++ b/src/libs/services/database/impl/Track.cpp @@ -186,12 +186,12 @@ Track::findPaths(Session& session, Range range) } RangeResults -Track::findMBIDDuplicates(Session& session, Range range) +Track::findRecordingMBIDDuplicates(Session& session, Range range) { session.checkSharedLocked(); - auto query {session.getDboSession().query( "SELECT track.id FROM track WHERE mbid in (SELECT mbid FROM track WHERE mbid <> '' GROUP BY mbid HAVING COUNT (*) > 1)") - .orderBy("track.release_id,track.disc_number,track.track_number,track.mbid")}; + auto query {session.getDboSession().query( "SELECT track.id FROM track WHERE recording_mbid in (SELECT recording_mbid FROM track WHERE recording_mbid <> '' GROUP BY recording_mbid HAVING COUNT (*) > 1)") + .orderBy("track.release_id,track.disc_number,track.track_number,track.recording_mbid")}; return execQuery(query, range); } diff --git a/src/libs/services/database/include/services/database/Track.hpp b/src/libs/services/database/include/services/database/Track.hpp index 3f0bf099..cc530921 100644 --- a/src/libs/services/database/include/services/database/Track.hpp +++ b/src/libs/services/database/include/services/database/Track.hpp @@ -92,7 +92,7 @@ class Track : public Object static RangeResults find(Session& session, const FindParameters& parameters); static RangeResults findByNameAndReleaseName(Session& session, std::string_view trackName, std::string_view releaseName); static RangeResults findPaths(Session& session, Range range); - static RangeResults findMBIDDuplicates(Session& session, Range range); + static RangeResults findRecordingMBIDDuplicates(Session& session, Range range); static RangeResults findWithRecordingMBIDAndMissingFeatures(Session& session, Range range); // Create utility diff --git a/src/libs/services/scanner/impl/ScannerService.cpp b/src/libs/services/scanner/impl/ScannerService.cpp index abac1180..de5c94ae 100644 --- a/src/libs/services/scanner/impl/ScannerService.cpp +++ b/src/libs/services/scanner/impl/ScannerService.cpp @@ -34,6 +34,7 @@ #include "metadata/TagLibParser.hpp" #include "services/recommendation/IRecommendationService.hpp" #include "utils/Exception.hpp" +#include "utils/IConfig.hpp" #include "utils/Logger.hpp" #include "utils/Path.hpp" #include "utils/UUID.hpp" @@ -256,8 +257,11 @@ createScannerService(Db& db, Recommendation::IRecommendationService& recommendat ScannerService::ScannerService(Db& db, Recommendation::IRecommendationService& recommendationService) : _recommendationService {recommendationService} +, _skipDuplicateRecordingMBID {Service::get()->getBool("scanner-skip-duplicate-recording-mbid", false)} , _dbSession {db} { + LMS_LOG(DBUPDATER, INFO) << "skipDuplicateRecordingMBID = " << _skipDuplicateRecordingMBID; + // For now, always use TagLib _metadataParser = std::make_unique(); @@ -719,6 +723,25 @@ ScannerService::scanAudioFile(const std::filesystem::path& file, bool forceScan, Track::pointer track {Track::findByPath(_dbSession, file) }; + // Skip duplicate recording MBID + if (trackInfo->recordingMBID && _skipDuplicateRecordingMBID) + { + for (Track::pointer otherTrack : Track::findByRecordingMBID(_dbSession, *trackInfo->recordingMBID)) + { + if (track && track->getId() == otherTrack->getId()) + continue; + + LMS_LOG(DBUPDATER, DEBUG) << "Skipped '" << file.string() << "' (similar recording MBID in '" << otherTrack->getPath().string() << "')"; + // This recording MBID already exists, just remove what we just scanned + if (track) + { + track.remove(); + stats.deletions++; + } + return; + } + } + // We estimate this is an audio file if: // - we found a least one audio stream // - the duration is not null @@ -1026,14 +1049,14 @@ ScannerService::checkDuplicatedAudioFiles(ScanStats& stats) auto transaction {_dbSession.createSharedTransaction()}; - const RangeResults tracks = Track::findMBIDDuplicates(_dbSession, Range {}); + const RangeResults tracks = Track::findRecordingMBIDDuplicates(_dbSession, Range {}); for (const TrackId trackId : tracks.results) { const Track::pointer track {Track::find(_dbSession, trackId)}; - if (auto trackMBID {track->getTrackMBID()}) + if (auto recordingMBID {track->getRecordingMBID()}) { - LMS_LOG(DBUPDATER, INFO) << "Found duplicated Track MBID [" << trackMBID->getAsString() << "], file: " << track->getPath().string() << " - " << track->getName(); - stats.duplicates.emplace_back(ScanDuplicate {track->getId(), DuplicateReason::SameMBID}); + LMS_LOG(DBUPDATER, INFO) << "Found duplicated recording MBID [" << recordingMBID->getAsString() << "], file: " << track->getPath().string() << " - " << track->getName(); + stats.duplicates.emplace_back(ScanDuplicate {track->getId(), DuplicateReason::SameRecordingMBID}); } } diff --git a/src/libs/services/scanner/impl/ScannerService.hpp b/src/libs/services/scanner/impl/ScannerService.hpp index 07adf5c6..0d8ecf91 100644 --- a/src/libs/services/scanner/impl/ScannerService.hpp +++ b/src/libs/services/scanner/impl/ScannerService.hpp @@ -98,6 +98,7 @@ namespace Scanner std::atomic _abortScan {}; Wt::WIOService _ioService; boost::asio::system_timer _scheduleTimer {_ioService}; + const bool _skipDuplicateRecordingMBID {}; Events _events; std::chrono::system_clock::time_point _lastScanInProgressEmit {}; Database::Session _dbSession; diff --git a/src/libs/services/scanner/include/services/scanner/ScannerStats.hpp b/src/libs/services/scanner/include/services/scanner/ScannerStats.hpp index 1b5e3de4..53f79d52 100644 --- a/src/libs/services/scanner/include/services/scanner/ScannerStats.hpp +++ b/src/libs/services/scanner/include/services/scanner/ScannerStats.hpp @@ -30,16 +30,16 @@ namespace Scanner { enum class ScanErrorType { - CannotReadFile, // cannot read file - CannotParseFile, // cannot parse file - NoAudioTrack, // no audio track found - BadDuration, // bad duration + CannotReadFile, // cannot read file + CannotParseFile, // cannot parse file + NoAudioTrack, // no audio track found + BadDuration, // bad duration }; enum class DuplicateReason { SameHash, - SameMBID, + SameRecordingMBID, }; struct ScanError diff --git a/src/lms/ui/admin/ScannerController.cpp b/src/lms/ui/admin/ScannerController.cpp index 764d6bb8..6b5153db 100644 --- a/src/lms/ui/admin/ScannerController.cpp +++ b/src/lms/ui/admin/ScannerController.cpp @@ -96,7 +96,7 @@ class ReportResource : public Wt::WResource response.out() << track->getPath().string(); if (auto mbid {track->getTrackMBID()}) - response.out() << " (Track MBID " << mbid->getAsString() << ")"; + response.out() << " (Recording MBID " << mbid->getAsString() << ")"; response.out() << " - " << duplicateReasonToWString(duplicate.reason).toUTF8() << '\n'; } @@ -122,7 +122,7 @@ class ReportResource : public Wt::WResource switch (reason) { case Scanner::DuplicateReason::SameHash: return Wt::WString::tr("Lms.Admin.ScannerController.same-hash"); - case Scanner::DuplicateReason::SameMBID: return Wt::WString::tr("Lms.Admin.ScannerController.same-mbid"); + case Scanner::DuplicateReason::SameRecordingMBID: return Wt::WString::tr("Lms.Admin.ScannerController.same-mbid"); } return "?"; }