command/playlist: add position parameter to "playlistadd"

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1106
This commit is contained in:
Max Kellermann 2021-10-25 08:43:42 +02:00
parent 4529bb4a83
commit a6173e0eae
6 changed files with 84 additions and 4 deletions

2
NEWS
View File

@ -1,6 +1,6 @@
ver 0.23.3 (not yet released)
* protocol
- add optional position parameter to "add"
- add optional position parameter to "add" and "playlistadd"
* output
- alsa: add option "stop_dsd_silence" to work around DSD DAC noise
* macOS: fix libfmt related build failure

View File

@ -695,7 +695,7 @@ Whenever possible, ids should be used.
can also be a single file.
The position parameter is the same as in :ref:`addid
<command_addid>`.
<command_addid>`. [#since_0_23_3]_
Clients that are connected via local socket may add arbitrary
local files (URI is an absolute path). Example::
@ -933,12 +933,15 @@ remote playlists (absolute URI with a supported scheme).
.. _command_playlistadd:
:command:`playlistadd {NAME} {URI}`
:command:`playlistadd {NAME} {URI} [POSITION]`
Adds ``URI`` to the playlist
`NAME.m3u`.
`NAME.m3u` will be created if it does
not exist.
The ``POSITION`` parameter specifies where the songs will be
inserted into the playlist. [#since_0_23_3]_
.. _command_playlistclear:
:command:`playlistclear {NAME}`
@ -1649,3 +1652,4 @@ client-to-client messages are local to the current partition.
.. [#since_0_22_4] Since :program:`MPD` 0.22.4
.. [#since_0_23] Since :program:`MPD` 0.23
.. [#since_0_23_1] Since :program:`MPD` 0.23.1
.. [#since_0_23_3] Since :program:`MPD` 0.23.3

View File

@ -268,6 +268,29 @@ PlaylistFileEditor::PlaylistFileEditor(const char *name_utf8,
{
}
void
PlaylistFileEditor::Insert(std::size_t i, const char *uri)
{
if (i > size())
throw PlaylistError(PlaylistResult::BAD_RANGE, "Bad position");
if (size() >= playlist_max_length)
throw PlaylistError(PlaylistResult::TOO_LARGE,
"Stored playlist is too large");
contents.emplace(std::next(contents.begin(), i), uri);
}
void
PlaylistFileEditor::Insert(std::size_t i, const DetachedSong &song)
{
const char *uri = playlist_saveAbsolutePaths
? song.GetRealURI()
: song.GetURI();
Insert(i, uri);
}
void
PlaylistFileEditor::MoveIndex(unsigned src, unsigned dest)
{

View File

@ -51,6 +51,13 @@ public:
*/
explicit PlaylistFileEditor(const char *name_utf8, LoadMode load_mode);
auto size() const noexcept {
return contents.size();
}
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 RemoveIndex(unsigned i);

View File

@ -157,7 +157,7 @@ static constexpr struct command commands[] = {
{ "play", PERMISSION_PLAYER, 0, 1, handle_play },
{ "playid", PERMISSION_PLAYER, 0, 1, handle_playid },
{ "playlist", PERMISSION_READ, 0, 0, handle_playlist },
{ "playlistadd", PERMISSION_CONTROL, 2, 2, handle_playlistadd },
{ "playlistadd", PERMISSION_CONTROL, 2, 3, handle_playlistadd },
{ "playlistclear", PERMISSION_CONTROL, 1, 1, handle_playlistclear },
{ "playlistdelete", PERMISSION_CONTROL, 2, 2, handle_playlistdelete },
{ "playlistfind", PERMISSION_READ, 1, -1, handle_playlistfind },

View File

@ -22,8 +22,10 @@
#include "PositionArg.hxx"
#include "Request.hxx"
#include "Instance.hxx"
#include "db/Interface.hxx"
#include "db/Selection.hxx"
#include "db/DatabasePlaylist.hxx"
#include "db/DatabaseSong.hxx"
#include "PlaylistSave.hxx"
#include "PlaylistFile.hxx"
#include "PlaylistError.hxx"
@ -201,12 +203,56 @@ handle_playlistclear([[maybe_unused]] Client &client,
return CommandResult::OK;
}
static CommandResult
handle_playlistadd_position(Client &client, const char *playlist_name,
const char *uri, unsigned position,
Response &r)
{
PlaylistFileEditor editor{
playlist_name,
PlaylistFileEditor::LoadMode::TRY,
};
if (position > editor.size()) {
r.Error(ACK_ERROR_ARG, "Bad position");
return CommandResult::ERROR;
}
if (uri_has_scheme(uri)) {
editor.Insert(position, uri);
} else {
#ifdef ENABLE_DATABASE
const auto &db = client.GetDatabaseOrThrow();
const auto *storage = client.GetStorage();
const DatabaseSelection selection(uri, true, nullptr);
db.Visit(selection, [&editor, &position, storage](const auto &song){
editor.Insert(position,
DatabaseDetachSong(storage, song));
++position;
});
#else
(void)client;
r.Error(ACK_ERROR_NO_EXIST, "No database");
return CommandResult::ERROR;
#endif
}
editor.Save();
return CommandResult::OK;
}
CommandResult
handle_playlistadd(Client &client, Request args, [[maybe_unused]] Response &r)
{
const char *const playlist = args[0];
const char *const uri = args[1];
if (args.size >= 3)
return handle_playlistadd_position(client, playlist, uri,
args.ParseUnsigned(2), r);
if (uri_has_scheme(uri)) {
const SongLoader loader(client);
spl_append_uri(playlist, loader, uri);