From 1985786ed25b82eddfdabe104c8698967dbb4cca Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Thu, 5 Aug 2021 18:57:11 +0200 Subject: [PATCH] db/simple: prune CUE entries from database for non-existent songs Closes https://github.com/MusicPlayerDaemon/MPD/issues/1019 --- NEWS | 1 + src/db/plugins/simple/Directory.cxx | 17 +++++++++++++++++ src/db/plugins/simple/Directory.hxx | 9 ++++++++- src/db/update/Walk.cxx | 27 +++++++++++++++++++++++++++ src/db/update/Walk.hxx | 6 ++++++ 5 files changed, 59 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 28a9acb36..3d6f0a112 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,7 @@ ver 0.22.10 (not yet released) * database - simple: fix crash bug - simple: fix absolute paths in CUE "as_directory" entries + - simple: prune CUE entries from database for non-existent songs * input - curl: fix crash bug after stream with Icy metadata was closed by peer - tidal: remove defunct unmaintained plugin diff --git a/src/db/plugins/simple/Directory.cxx b/src/db/plugins/simple/Directory.cxx index 4c7bb2873..b03994730 100644 --- a/src/db/plugins/simple/Directory.cxx +++ b/src/db/plugins/simple/Directory.cxx @@ -109,6 +109,23 @@ Directory::FindChild(std::string_view name) const noexcept return nullptr; } +bool +Directory::TargetExists(std::string_view _target) const noexcept +{ + StringView target{_target}; + + if (target.SkipPrefix("../")) { + if (parent == nullptr) + return false; + + return parent->TargetExists(target); + } + + /* sorry for the const_cast ... */ + const auto lr = const_cast(this)->LookupDirectory(target); + return lr.directory->FindSong(lr.rest) != nullptr; +} + void Directory::PruneEmpty() noexcept { diff --git a/src/db/plugins/simple/Directory.hxx b/src/db/plugins/simple/Directory.hxx index 6b8899440..87e8351e7 100644 --- a/src/db/plugins/simple/Directory.hxx +++ b/src/db/plugins/simple/Directory.hxx @@ -118,13 +118,17 @@ public: return new Directory(std::string(), nullptr); } + bool IsPlaylist() const noexcept { + return device == DEVICE_PLAYLIST; + } + /** * Is this really a regular file which is being treated like a * directory? */ bool IsReallyAFile() const noexcept { return device == DEVICE_INARCHIVE || - device == DEVICE_PLAYLIST || + IsPlaylist() || device == DEVICE_CONTAINER; } @@ -210,6 +214,9 @@ public: gcc_pure LookupResult LookupDirectory(std::string_view uri) noexcept; + [[gnu::pure]] + bool TargetExists(std::string_view target) const noexcept; + gcc_pure bool IsEmpty() const noexcept { return children.empty() && diff --git a/src/db/update/Walk.cxx b/src/db/update/Walk.cxx index b1fb675e6..c5fb8230d 100644 --- a/src/db/update/Walk.cxx +++ b/src/db/update/Walk.cxx @@ -133,6 +133,28 @@ UpdateWalk::PurgeDeletedFromDirectory(Directory &directory) noexcept } } +void +UpdateWalk::PurgeDanglingFromPlaylists(Directory &directory) noexcept +{ + /* recurse */ + for (Directory &child : directory.children) + PurgeDanglingFromPlaylists(child); + + if (!directory.IsPlaylist()) + /* this check is only for virtual directories + representing a playlist file */ + return; + + directory.ForEachSongSafe([&](Song &song){ + if (!song.target.empty() && + !PathTraitsUTF8::IsAbsoluteOrHasScheme(song.target.c_str()) && + !directory.TargetExists(song.target)) { + editor.DeleteSong(directory, &song); + modified = true; + } + }); +} + #ifndef _WIN32 static bool update_directory_stat(Storage &storage, Directory &directory) noexcept @@ -530,5 +552,10 @@ UpdateWalk::Walk(Directory &root, const char *path, bool discard) noexcept UpdateDirectory(root, exclude_list, info); } + { + const ScopeDatabaseLock protect; + PurgeDanglingFromPlaylists(root); + } + return modified; } diff --git a/src/db/update/Walk.hxx b/src/db/update/Walk.hxx index 433d1a3a1..984230ce9 100644 --- a/src/db/update/Walk.hxx +++ b/src/db/update/Walk.hxx @@ -85,6 +85,12 @@ private: void PurgeDeletedFromDirectory(Directory &directory) noexcept; + /** + * Remove all virtual songs inside playlists whose "target" + * field points to a non-existing song file. + */ + void PurgeDanglingFromPlaylists(Directory &directory) noexcept; + void UpdateSongFile2(Directory &directory, const char *name, const char *suffix, const StorageFileInfo &info) noexcept;