db/simple/Song: Export() merges tags with "target"
Closes https://github.com/MusicPlayerDaemon/MPD/issues/1048
This commit is contained in:
parent
3a7c9c7c84
commit
1afa33c3c7
2
NEWS
2
NEWS
|
@ -1,7 +1,7 @@
|
||||||
ver 0.22.4 (not yet released)
|
ver 0.22.4 (not yet released)
|
||||||
* protocol
|
* protocol
|
||||||
- fix "readpicture" on 32 bit machines
|
- fix "readpicture" on 32 bit machines
|
||||||
- show duration of songs in virtual playlist (CUE) folders
|
- show duration and tags of songs in virtual playlist (CUE) folders
|
||||||
* storage
|
* storage
|
||||||
- curl: fix several WebDAV protocol bugs
|
- curl: fix several WebDAV protocol bugs
|
||||||
* decoder
|
* decoder
|
||||||
|
|
|
@ -233,12 +233,12 @@ SimpleDatabase::GetSong(std::string_view uri) const
|
||||||
"No such song");
|
"No such song");
|
||||||
|
|
||||||
const Song *song = r.directory->FindSong(r.rest);
|
const Song *song = r.directory->FindSong(r.rest);
|
||||||
protect.unlock();
|
|
||||||
if (song == nullptr)
|
if (song == nullptr)
|
||||||
throw DatabaseError(DatabaseErrorCode::NOT_FOUND,
|
throw DatabaseError(DatabaseErrorCode::NOT_FOUND,
|
||||||
"No such song");
|
"No such song");
|
||||||
|
|
||||||
exported_song.Construct(song->Export());
|
exported_song.Construct(song->Export());
|
||||||
|
protect.unlock();
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
++borrowed_song_count;
|
++borrowed_song_count;
|
||||||
|
|
|
@ -21,9 +21,12 @@
|
||||||
#include "ExportedSong.hxx"
|
#include "ExportedSong.hxx"
|
||||||
#include "Directory.hxx"
|
#include "Directory.hxx"
|
||||||
#include "tag/Tag.hxx"
|
#include "tag/Tag.hxx"
|
||||||
|
#include "tag/Builder.hxx"
|
||||||
#include "song/DetachedSong.hxx"
|
#include "song/DetachedSong.hxx"
|
||||||
#include "song/LightSong.hxx"
|
#include "song/LightSong.hxx"
|
||||||
#include "fs/Traits.hxx"
|
#include "fs/Traits.hxx"
|
||||||
|
#include "time/ChronoUtil.hxx"
|
||||||
|
#include "util/IterableSplitString.hxx"
|
||||||
|
|
||||||
Song::Song(DetachedSong &&other, Directory &_parent) noexcept
|
Song::Song(DetachedSong &&other, Directory &_parent) noexcept
|
||||||
:tag(std::move(other.WritableTag())),
|
: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
|
ExportedSong
|
||||||
Song::Export() const noexcept
|
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())
|
if (!parent.IsRoot())
|
||||||
dest.directory = parent.GetPath();
|
dest.directory = parent.GetPath();
|
||||||
if (!target.empty())
|
if (!target.empty())
|
||||||
dest.real_uri = target.c_str();
|
dest.real_uri = target.c_str();
|
||||||
dest.mtime = mtime;
|
dest.mtime = IsNegative(mtime) && target_song != nullptr
|
||||||
dest.start_time = start_time;
|
? target_song->mtime
|
||||||
dest.end_time = end_time;
|
: mtime;
|
||||||
dest.audio_format = audio_format;
|
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;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue