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)
|
ver 0.23.3 (not yet released)
|
||||||
* protocol
|
* protocol
|
||||||
- add optional position parameter to "add"
|
- add optional position parameter to "add" and "playlistadd"
|
||||||
* output
|
* output
|
||||||
- alsa: add option "stop_dsd_silence" to work around DSD DAC noise
|
- alsa: add option "stop_dsd_silence" to work around DSD DAC noise
|
||||||
* macOS: fix libfmt related build failure
|
* macOS: fix libfmt related build failure
|
||||||
|
|
|
@ -695,7 +695,7 @@ Whenever possible, ids should be used.
|
||||||
can also be a single file.
|
can also be a single file.
|
||||||
|
|
||||||
The position parameter is the same as in :ref:`addid
|
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
|
Clients that are connected via local socket may add arbitrary
|
||||||
local files (URI is an absolute path). Example::
|
local files (URI is an absolute path). Example::
|
||||||
|
@ -933,12 +933,15 @@ remote playlists (absolute URI with a supported scheme).
|
||||||
|
|
||||||
.. _command_playlistadd:
|
.. _command_playlistadd:
|
||||||
|
|
||||||
:command:`playlistadd {NAME} {URI}`
|
:command:`playlistadd {NAME} {URI} [POSITION]`
|
||||||
Adds ``URI`` to the playlist
|
Adds ``URI`` to the playlist
|
||||||
`NAME.m3u`.
|
`NAME.m3u`.
|
||||||
`NAME.m3u` will be created if it does
|
`NAME.m3u` will be created if it does
|
||||||
not exist.
|
not exist.
|
||||||
|
|
||||||
|
The ``POSITION`` parameter specifies where the songs will be
|
||||||
|
inserted into the playlist. [#since_0_23_3]_
|
||||||
|
|
||||||
.. _command_playlistclear:
|
.. _command_playlistclear:
|
||||||
|
|
||||||
:command:`playlistclear {NAME}`
|
: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_22_4] Since :program:`MPD` 0.22.4
|
||||||
.. [#since_0_23] Since :program:`MPD` 0.23
|
.. [#since_0_23] Since :program:`MPD` 0.23
|
||||||
.. [#since_0_23_1] Since :program:`MPD` 0.23.1
|
.. [#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
|
void
|
||||||
PlaylistFileEditor::MoveIndex(unsigned src, unsigned dest)
|
PlaylistFileEditor::MoveIndex(unsigned src, unsigned dest)
|
||||||
{
|
{
|
||||||
|
|
|
@ -51,6 +51,13 @@ public:
|
||||||
*/
|
*/
|
||||||
explicit PlaylistFileEditor(const char *name_utf8, LoadMode load_mode);
|
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 MoveIndex(unsigned src, unsigned dest);
|
||||||
void RemoveIndex(unsigned i);
|
void RemoveIndex(unsigned i);
|
||||||
|
|
||||||
|
|
|
@ -157,7 +157,7 @@ static constexpr struct command commands[] = {
|
||||||
{ "play", PERMISSION_PLAYER, 0, 1, handle_play },
|
{ "play", PERMISSION_PLAYER, 0, 1, handle_play },
|
||||||
{ "playid", PERMISSION_PLAYER, 0, 1, handle_playid },
|
{ "playid", PERMISSION_PLAYER, 0, 1, handle_playid },
|
||||||
{ "playlist", PERMISSION_READ, 0, 0, handle_playlist },
|
{ "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 },
|
{ "playlistclear", PERMISSION_CONTROL, 1, 1, handle_playlistclear },
|
||||||
{ "playlistdelete", PERMISSION_CONTROL, 2, 2, handle_playlistdelete },
|
{ "playlistdelete", PERMISSION_CONTROL, 2, 2, handle_playlistdelete },
|
||||||
{ "playlistfind", PERMISSION_READ, 1, -1, handle_playlistfind },
|
{ "playlistfind", PERMISSION_READ, 1, -1, handle_playlistfind },
|
||||||
|
|
|
@ -22,8 +22,10 @@
|
||||||
#include "PositionArg.hxx"
|
#include "PositionArg.hxx"
|
||||||
#include "Request.hxx"
|
#include "Request.hxx"
|
||||||
#include "Instance.hxx"
|
#include "Instance.hxx"
|
||||||
|
#include "db/Interface.hxx"
|
||||||
#include "db/Selection.hxx"
|
#include "db/Selection.hxx"
|
||||||
#include "db/DatabasePlaylist.hxx"
|
#include "db/DatabasePlaylist.hxx"
|
||||||
|
#include "db/DatabaseSong.hxx"
|
||||||
#include "PlaylistSave.hxx"
|
#include "PlaylistSave.hxx"
|
||||||
#include "PlaylistFile.hxx"
|
#include "PlaylistFile.hxx"
|
||||||
#include "PlaylistError.hxx"
|
#include "PlaylistError.hxx"
|
||||||
|
@ -201,12 +203,56 @@ handle_playlistclear([[maybe_unused]] Client &client,
|
||||||
return CommandResult::OK;
|
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
|
CommandResult
|
||||||
handle_playlistadd(Client &client, Request args, [[maybe_unused]] Response &r)
|
handle_playlistadd(Client &client, Request args, [[maybe_unused]] Response &r)
|
||||||
{
|
{
|
||||||
const char *const playlist = args[0];
|
const char *const playlist = args[0];
|
||||||
const char *const uri = args[1];
|
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)) {
|
if (uri_has_scheme(uri)) {
|
||||||
const SongLoader loader(client);
|
const SongLoader loader(client);
|
||||||
spl_append_uri(playlist, loader, uri);
|
spl_append_uri(playlist, loader, uri);
|
||||||
|
|
Loading…
Reference in New Issue