Merge tag 'v0.22.10'

release v0.22.10
This commit is contained in:
Max Kellermann
2021-08-06 18:21:59 +02:00
17 changed files with 177 additions and 30 deletions

View File

@@ -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<Directory *>(this)->LookupDirectory(target);
return lr.directory->FindSong(lr.rest) != nullptr;
}
void
Directory::PruneEmpty() noexcept
{

View File

@@ -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;
}
@@ -206,11 +210,13 @@ public:
* Looks up a directory by its relative URI.
*
* @param uri the relative URI
* @return the Directory, or nullptr if none was found
*/
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() &&

View File

@@ -31,6 +31,7 @@
#include "playlist/SongEnumerator.hxx"
#include "storage/FileInfo.hxx"
#include "storage/StorageInterface.hxx"
#include "fs/Traits.hxx"
#include "util/StringFormat.hxx"
#include "Log.hxx"
@@ -71,7 +72,14 @@ UpdateWalk::UpdatePlaylistFile(Directory &parent, std::string_view name,
auto db_song = std::make_unique<Song>(std::move(*song),
*directory);
db_song->target = "../" + db_song->filename;
db_song->target =
PathTraitsUTF8::IsAbsoluteOrHasScheme(db_song->filename.c_str())
? db_song->filename
/* prepend "../" to relative paths to
go from the virtual directory
(DEVICE_PLAYLIST) to the containing
directory */
: "../" + db_song->filename;
db_song->filename = StringFormat<64>("track%04u",
++track);

View File

@@ -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;
}

View File

@@ -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, std::string_view suffix,
const StorageFileInfo &info) noexcept;