From a3dfc082ea3e5e17be64fb7561a19834022d43fe Mon Sep 17 00:00:00 2001 From: emeric Date: Sun, 16 Feb 2025 15:52:05 +0100 Subject: [PATCH] Try to reuse the db track entry if the track file just moved, fixes #612 --- src/libs/database/impl/Session.cpp | 1 + src/libs/database/impl/Track.cpp | 3 ++ src/libs/database/include/database/Track.hpp | 6 +++ .../impl/scanners/AudioFileScanner.cpp | 50 +++++++++++++++++++ 4 files changed, 60 insertions(+) diff --git a/src/libs/database/impl/Session.cpp b/src/libs/database/impl/Session.cpp index 0340736b..a7263d9e 100644 --- a/src/libs/database/impl/Session.cpp +++ b/src/libs/database/impl/Session.cpp @@ -256,6 +256,7 @@ namespace lms::db utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_media_library_release_idx ON track(media_library_id, release_id)"); utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_mbid_idx ON track(mbid)"); utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_name_idx ON track(name)"); + utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_name_file_size_idx ON track(name, file_size)"); utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_name_nocase_idx ON track(name COLLATE NOCASE)"); utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_original_date_idx ON track(original_date)"); utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_recording_mbid_idx ON track(recording_mbid)"); diff --git a/src/libs/database/impl/Track.cpp b/src/libs/database/impl/Track.cpp index c5c32290..55bc04be 100644 --- a/src/libs/database/impl/Track.cpp +++ b/src/libs/database/impl/Track.cpp @@ -180,6 +180,9 @@ namespace lms::db if (params.hasEmbeddedImage.has_value()) query.where("t.has_cover = ?").bind(params.hasEmbeddedImage.value()); + if (params.fileSize.has_value()) + query.where("t.file_size = ?").bind(static_cast(params.fileSize.value())); + switch (params.sortMethod) { case TrackSortMethod::None: diff --git a/src/libs/database/include/database/Track.hpp b/src/libs/database/include/database/Track.hpp index 3c0c978b..6258da9c 100644 --- a/src/libs/database/include/database/Track.hpp +++ b/src/libs/database/include/database/Track.hpp @@ -86,6 +86,7 @@ namespace lms::db std::optional discNumber; // matching this disc number DirectoryId directory; // if set, tracks in this directory std::optional hasEmbeddedImage; // if set, tracks that have or not embedded images + std::optional fileSize; // if set, tracks that match this file size FindParameters& setFilters(const Filters& _filters) { @@ -186,6 +187,11 @@ namespace lms::db hasEmbeddedImage = _hasEmbeddedImage; return *this; } + FindParameters& setFileSize(std::optional _fileSize) + { + fileSize = _fileSize; + return *this; + } }; Track() = default; diff --git a/src/libs/services/scanner/impl/scanners/AudioFileScanner.cpp b/src/libs/services/scanner/impl/scanners/AudioFileScanner.cpp index b83470a5..21c7c7e9 100644 --- a/src/libs/services/scanner/impl/scanners/AudioFileScanner.cpp +++ b/src/libs/services/scanner/impl/scanners/AudioFileScanner.cpp @@ -345,6 +345,45 @@ namespace lms::scanner return db::Advisory::UnSet; } + db::Track::pointer findMovedTrackBySizeAndMetaData(db::Session& session, const metadata::Track& parsedTrack, const FileInfo& fileInfo) + { + db::Track::FindParameters params; + // Add as many fields as possible to limit errors + params.setName(parsedTrack.title); + if (parsedTrack.medium) + { + if (parsedTrack.medium->position) + params.setDiscNumber(*parsedTrack.medium->position); + if (parsedTrack.medium->release) + params.setReleaseName(parsedTrack.medium->release->name); + } + if (parsedTrack.position) + params.setTrackNumber(*parsedTrack.position); + params.setHasEmbeddedImage(parsedTrack.hasCover); + params.setFileSize(fileInfo.fileSize); + + bool error{}; + db::Track::pointer res; + db::Track::find(session, params, [&](const db::Track::pointer& track) { + // Check that the track is truly no longer where it was during the last scan + std::error_code ec; + if (std::filesystem::exists(track->getAbsoluteFilePath(), ec)) + return; + + if (res) + { + LMS_LOG(DBUPDATER, DEBUG, "Found too many candidates for file move. New file = " << fileInfo.relativePath << ", candidate = " << track->getAbsoluteFilePath() << ", previous candidate = " << res->getAbsoluteFilePath()); + error = true; + } + res = track; + }); + + if (error) + res = db::Track::pointer{}; + + return res; + } + class AudioFileScanOperation : public IFileScanOperation { public: @@ -463,6 +502,17 @@ namespace lms::scanner } } + if (!track) + { + // maybe the file just moved? + track = findMovedTrackBySizeAndMetaData(dbSession, *_parsedTrack, *fileInfo); + if (track) + { + LMS_LOG(DBUPDATER, DEBUG, "Considering track " << _file << " moved from " << track->getAbsoluteFilePath()); + track.modify()->setAbsoluteFilePath(_file); + } + } + // We estimate this is an audio file if the duration is not null if (_parsedTrack->audioProperties.duration == std::chrono::milliseconds::zero()) {