command/playlist: allow range in "playlistmove"
Closes https://github.com/MusicPlayerDaemon/MPD/issues/1689
This commit is contained in:
parent
f4adbdbe31
commit
04e60d67ae
1
NEWS
1
NEWS
|
@ -2,6 +2,7 @@ ver 0.24 (not yet released)
|
||||||
* protocol
|
* protocol
|
||||||
- new command "searchcount" (case-insensitive "count")
|
- new command "searchcount" (case-insensitive "count")
|
||||||
- "playlistfind"/"playlistsearch" have "sort" and "window" parameters
|
- "playlistfind"/"playlistsearch" have "sort" and "window" parameters
|
||||||
|
- allow range in "playlistmove"
|
||||||
- "save" can append to or replace an existing playlist
|
- "save" can append to or replace an existing playlist
|
||||||
- filter "prio" (for "playlistfind"/"playlistsearch")
|
- filter "prio" (for "playlistfind"/"playlistsearch")
|
||||||
- limit "player" idle events to the current partition
|
- limit "player" idle events to the current partition
|
||||||
|
|
|
@ -994,8 +994,9 @@ remote playlists (absolute URI with a supported scheme).
|
||||||
|
|
||||||
.. _command_playlistmove:
|
.. _command_playlistmove:
|
||||||
|
|
||||||
:command:`playlistmove {NAME} {FROM} {TO}`
|
:command:`playlistmove {NAME} [{FROM} | {START:END}] {TO}`
|
||||||
Moves the song at position ``FROM`` in
|
Moves the song at position ``FROM`` or range of songs
|
||||||
|
at ``START:END`` [#since_0_24]_ in
|
||||||
the playlist `NAME.m3u` to the
|
the playlist `NAME.m3u` to the
|
||||||
position ``TO``.
|
position ``TO``.
|
||||||
|
|
||||||
|
|
|
@ -296,18 +296,41 @@ PlaylistFileEditor::Insert(std::size_t i, const DetachedSong &song)
|
||||||
Insert(i, uri);
|
Insert(i, uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static PlaylistFileContents
|
||||||
PlaylistFileEditor::MoveIndex(unsigned src, unsigned dest)
|
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");
|
throw PlaylistError(PlaylistResult::BAD_RANGE, "Bad range");
|
||||||
|
|
||||||
const auto src_i = std::next(contents.begin(), src);
|
auto tmp = CutRange(contents, src);
|
||||||
auto value = std::move(*src_i);
|
InsertRange(contents, std::next(contents.begin(), dest), std::move(tmp));
|
||||||
contents.erase(src_i);
|
|
||||||
|
|
||||||
const auto dest_i = std::next(contents.begin(), dest);
|
|
||||||
contents.insert(dest_i, std::move(value));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -59,7 +59,7 @@ public:
|
||||||
void Insert(std::size_t i, const char *uri);
|
void Insert(std::size_t i, const char *uri);
|
||||||
void Insert(std::size_t i, const DetachedSong &song);
|
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 RemoveIndex(unsigned i);
|
||||||
void RemoveRange(RangeArg range);
|
void RemoveRange(RangeArg range);
|
||||||
|
|
||||||
|
|
|
@ -202,10 +202,16 @@ handle_playlistmove([[maybe_unused]] Client &client,
|
||||||
Request args, [[maybe_unused]] Response &r)
|
Request args, [[maybe_unused]] Response &r)
|
||||||
{
|
{
|
||||||
const char *const name = args.front();
|
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);
|
unsigned to = args.ParseUnsigned(2);
|
||||||
|
|
||||||
if (from == to)
|
if (from.IsEmpty() || from.start == to)
|
||||||
/* this doesn't check whether the playlist exists, but
|
/* this doesn't check whether the playlist exists, but
|
||||||
what the hell.. */
|
what the hell.. */
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
|
|
Loading…
Reference in New Issue