command/playlist: allow range in "playlistmove"

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1689
This commit is contained in:
Max Kellermann 2023-03-06 11:35:14 +01:00
parent f4adbdbe31
commit 04e60d67ae
5 changed files with 45 additions and 14 deletions

1
NEWS
View File

@ -2,6 +2,7 @@ ver 0.24 (not yet released)
* protocol
- new command "searchcount" (case-insensitive "count")
- "playlistfind"/"playlistsearch" have "sort" and "window" parameters
- allow range in "playlistmove"
- "save" can append to or replace an existing playlist
- filter "prio" (for "playlistfind"/"playlistsearch")
- limit "player" idle events to the current partition

View File

@ -994,8 +994,9 @@ remote playlists (absolute URI with a supported scheme).
.. _command_playlistmove:
:command:`playlistmove {NAME} {FROM} {TO}`
Moves the song at position ``FROM`` in
:command:`playlistmove {NAME} [{FROM} | {START:END}] {TO}`
Moves the song at position ``FROM`` or range of songs
at ``START:END`` [#since_0_24]_ in
the playlist `NAME.m3u` to the
position ``TO``.

View File

@ -296,18 +296,41 @@ PlaylistFileEditor::Insert(std::size_t i, const DetachedSong &song)
Insert(i, uri);
}
void
PlaylistFileEditor::MoveIndex(unsigned src, unsigned dest)
static PlaylistFileContents
CutRange(PlaylistFileContents &src, RangeArg range) noexcept
{
if (src >= contents.size() || dest >= contents.size())
PlaylistFileContents dest;
dest.reserve(range.Count());
const auto begin = std::next(src.begin(), range.start);
const auto end = std::next(src.begin(), range.end);
for (auto i = begin; i != end; ++i)
dest.emplace_back(std::move(*i));
src.erase(begin, end);
return dest;
}
static void
InsertRange(PlaylistFileContents &dest, PlaylistFileContents::iterator pos,
PlaylistFileContents &&src) noexcept
{
dest.reserve(dest.size() + src.size());
for (auto &i : src)
pos = std::next(dest.emplace(pos, std::move(i)));
}
void
PlaylistFileEditor::MoveIndex(RangeArg src, unsigned dest)
{
if (src.end > contents.size() || dest > contents.size() - src.Count())
throw PlaylistError(PlaylistResult::BAD_RANGE, "Bad range");
const auto src_i = std::next(contents.begin(), src);
auto value = std::move(*src_i);
contents.erase(src_i);
const auto dest_i = std::next(contents.begin(), dest);
contents.insert(dest_i, std::move(value));
auto tmp = CutRange(contents, src);
InsertRange(contents, std::next(contents.begin(), dest), std::move(tmp));
}
void

View File

@ -59,7 +59,7 @@ public:
void Insert(std::size_t i, const char *uri);
void Insert(std::size_t i, const DetachedSong &song);
void MoveIndex(unsigned src, unsigned dest);
void MoveIndex(RangeArg src, unsigned dest);
void RemoveIndex(unsigned i);
void RemoveRange(RangeArg range);

View File

@ -202,10 +202,16 @@ handle_playlistmove([[maybe_unused]] Client &client,
Request args, [[maybe_unused]] Response &r)
{
const char *const name = args.front();
unsigned from = args.ParseUnsigned(1);
RangeArg from = args.ParseRange(1);
if (from.IsOpenEnded()) {
r.Error(ACK_ERROR_ARG, "Open-ended range not supported");
return CommandResult::ERROR;
}
unsigned to = args.ParseUnsigned(2);
if (from == to)
if (from.IsEmpty() || from.start == to)
/* this doesn't check whether the playlist exists, but
what the hell.. */
return CommandResult::OK;