command/playlist: add position parameter to "playlistadd"
Closes https://github.com/MusicPlayerDaemon/MPD/issues/1106
This commit is contained in:
parent
4529bb4a83
commit
a6173e0eae
2
NEWS
2
NEWS
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue