summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMax Kellermann <max@musicpd.org>2021-01-21 13:14:27 +0100
committerMax Kellermann <max@musicpd.org>2021-01-21 13:57:59 +0100
commit1afa33c3c766af22c35b02ba58e84693243a4f3e (patch)
treeff46da0303d69d6b5271548e03049b45bf812f50 /src
parent3a7c9c7c8443227cbc6786f2dc3498c945ca927f (diff)
db/simple/Song: Export() merges tags with "target"
Closes https://github.com/MusicPlayerDaemon/MPD/issues/1048
Diffstat (limited to 'src')
-rw-r--r--src/db/plugins/simple/SimpleDatabasePlugin.cxx2
-rw-r--r--src/db/plugins/simple/Song.cxx83
2 files changed, 79 insertions, 6 deletions
diff --git a/src/db/plugins/simple/SimpleDatabasePlugin.cxx b/src/db/plugins/simple/SimpleDatabasePlugin.cxx
index 1e126f3da..bb8230723 100644
--- a/src/db/plugins/simple/SimpleDatabasePlugin.cxx
+++ b/src/db/plugins/simple/SimpleDatabasePlugin.cxx
@@ -233,12 +233,12 @@ SimpleDatabase::GetSong(std::string_view uri) const
"No such song");
const Song *song = r.directory->FindSong(r.rest);
- protect.unlock();
if (song == nullptr)
throw DatabaseError(DatabaseErrorCode::NOT_FOUND,
"No such song");
exported_song.Construct(song->Export());
+ protect.unlock();
#ifndef NDEBUG
++borrowed_song_count;
diff --git a/src/db/plugins/simple/Song.cxx b/src/db/plugins/simple/Song.cxx
index 519a1a3d7..8db47d03f 100644
--- a/src/db/plugins/simple/Song.cxx
+++ b/src/db/plugins/simple/Song.cxx
@@ -21,9 +21,12 @@
#include "ExportedSong.hxx"
#include "Directory.hxx"
#include "tag/Tag.hxx"
+#include "tag/Builder.hxx"
#include "song/DetachedSong.hxx"
#include "song/LightSong.hxx"
#include "fs/Traits.hxx"
+#include "time/ChronoUtil.hxx"
+#include "util/IterableSplitString.hxx"
Song::Song(DetachedSong &&other, Directory &_parent) noexcept
:tag(std::move(other.WritableTag())),
@@ -54,17 +57,87 @@ Song::GetURI() const noexcept
}
}
+/**
+ * Path name traversal of a #Directory.
+ */
+gcc_pure
+static const Directory *
+FindTargetDirectory(const Directory &base, StringView path) noexcept
+{
+ const auto *directory = &base;
+ for (const StringView name : IterableSplitString(path, '/')) {
+ if (name.empty() || name.Equals("."))
+ continue;
+
+ directory = name.Equals("..")
+ ? directory->parent
+ : directory->FindChild(name);
+ if (directory == nullptr)
+ break;
+ }
+
+ return directory;
+}
+
+/**
+ * Path name traversal of a #Song.
+ */
+gcc_pure
+static const Song *
+FindTargetSong(const Directory &_directory, StringView target) noexcept
+{
+ auto [path, last] = target.SplitLast('/');
+ if (last == nullptr) {
+ last = path;
+ path = nullptr;
+ }
+
+ if (last.empty())
+ return nullptr;
+
+ const auto *directory = FindTargetDirectory(_directory, path);
+ if (directory == nullptr)
+ return nullptr;
+
+ return directory->FindSong(last);
+}
+
ExportedSong
Song::Export() const noexcept
{
- ExportedSong dest(filename.c_str(), tag);
+ const auto *target_song = !target.empty()
+ ? FindTargetSong(parent, (std::string_view)target)
+ : nullptr;
+
+ Tag merged_tag;
+ if (target_song != nullptr) {
+ /* if we found the target song (which may be the
+ underlying song file of a CUE file), merge the tags
+ from that song with this song's tags (from the CUE
+ file) */
+ TagBuilder builder(tag);
+ builder.Complement(target_song->tag);
+ merged_tag = builder.Commit();
+ }
+
+ ExportedSong dest = merged_tag.IsDefined()
+ ? ExportedSong(filename.c_str(), std::move(merged_tag))
+ : ExportedSong(filename.c_str(), tag);
if (!parent.IsRoot())
dest.directory = parent.GetPath();
if (!target.empty())
dest.real_uri = target.c_str();
- dest.mtime = mtime;
- dest.start_time = start_time;
- dest.end_time = end_time;
- dest.audio_format = audio_format;
+ dest.mtime = IsNegative(mtime) && target_song != nullptr
+ ? target_song->mtime
+ : mtime;
+ dest.start_time = start_time.IsZero() && target_song != nullptr
+ ? target_song->start_time
+ : start_time;
+ dest.end_time = end_time.IsZero() && target_song != nullptr
+ ? target_song->end_time
+ : end_time;
+ dest.audio_format = audio_format.IsDefined() || target_song == nullptr
+ ? audio_format
+ : target_song->audio_format;
return dest;
}