New command searchplaylist
This commit is contained in:
parent
f7790430a0
commit
d62f7cdc34
2
NEWS
2
NEWS
|
@ -10,7 +10,7 @@ ver 0.24 (not yet released)
|
||||||
- show PCRE support in "config" response
|
- show PCRE support in "config" response
|
||||||
- apply Unicode normalization to case-insensitive filter expressions
|
- apply Unicode normalization to case-insensitive filter expressions
|
||||||
- stickers on playlists and some tag types
|
- stickers on playlists and some tag types
|
||||||
- new commands "stickernames" and "playlistlength"
|
- new commands "stickernames", "playlistlength" and "searchplaylist"
|
||||||
- new "search"/"find" filter "added-since"
|
- new "search"/"find" filter "added-since"
|
||||||
- allow range in listplaylist and listplaylistinfo
|
- allow range in listplaylist and listplaylistinfo
|
||||||
- "sticker find" supports sort and window parameter and new sticker compare operators "eq", "lt" and "gt"
|
- "sticker find" supports sort and window parameter and new sticker compare operators "eq", "lt" and "gt"
|
||||||
|
|
|
@ -994,6 +994,14 @@ remote playlists (absolute URI with a supported scheme).
|
||||||
plugins are supported. A range may be specified to list
|
plugins are supported. A range may be specified to list
|
||||||
only a part of the playlist. [#since_0_24]_
|
only a part of the playlist. [#since_0_24]_
|
||||||
|
|
||||||
|
.. _command_searchplaylist:
|
||||||
|
|
||||||
|
:command:`searchplaylist {NAME} {FILTER} [{START:END}]`
|
||||||
|
Search the playlist for songs matching
|
||||||
|
``FILTER`` (see :ref:`Filters <filter_syntax>`). Playlist
|
||||||
|
plugins are supported. A range may be specified to list
|
||||||
|
only a part of the playlist.
|
||||||
|
|
||||||
.. _command_listplaylists:
|
.. _command_listplaylists:
|
||||||
|
|
||||||
:command:`listplaylists`
|
:command:`listplaylists`
|
||||||
|
|
|
@ -176,6 +176,7 @@ static constexpr struct command commands[] = {
|
||||||
{ "searchaddpl", PERMISSION_CONTROL, 2, -1, handle_searchaddpl },
|
{ "searchaddpl", PERMISSION_CONTROL, 2, -1, handle_searchaddpl },
|
||||||
{ "searchcount", PERMISSION_READ, 1, -1, handle_searchcount },
|
{ "searchcount", PERMISSION_READ, 1, -1, handle_searchcount },
|
||||||
#endif
|
#endif
|
||||||
|
{ "searchplaylist", PERMISSION_READ, 2, 3, handle_searchplaylist },
|
||||||
{ "seek", PERMISSION_PLAYER, 2, 2, handle_seek },
|
{ "seek", PERMISSION_PLAYER, 2, 2, handle_seek },
|
||||||
{ "seekcur", PERMISSION_PLAYER, 1, 1, handle_seekcur },
|
{ "seekcur", PERMISSION_PLAYER, 1, 1, handle_seekcur },
|
||||||
{ "seekid", PERMISSION_PLAYER, 2, 2, handle_seekid },
|
{ "seekid", PERMISSION_PLAYER, 2, 2, handle_seekid },
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "PlaylistError.hxx"
|
#include "PlaylistError.hxx"
|
||||||
#include "db/PlaylistVector.hxx"
|
#include "db/PlaylistVector.hxx"
|
||||||
#include "SongLoader.hxx"
|
#include "SongLoader.hxx"
|
||||||
|
#include "song/Filter.hxx"
|
||||||
#include "song/DetachedSong.hxx"
|
#include "song/DetachedSong.hxx"
|
||||||
#include "BulkEdit.hxx"
|
#include "BulkEdit.hxx"
|
||||||
#include "playlist/Length.hxx"
|
#include "playlist/Length.hxx"
|
||||||
|
@ -26,6 +27,7 @@
|
||||||
#include "Mapper.hxx"
|
#include "Mapper.hxx"
|
||||||
#include "fs/AllocatedPath.hxx"
|
#include "fs/AllocatedPath.hxx"
|
||||||
#include "time/ChronoUtil.hxx"
|
#include "time/ChronoUtil.hxx"
|
||||||
|
#include "util/Exception.hxx"
|
||||||
#include "util/UriExtract.hxx"
|
#include "util/UriExtract.hxx"
|
||||||
#include "LocateUri.hxx"
|
#include "LocateUri.hxx"
|
||||||
|
|
||||||
|
@ -129,7 +131,7 @@ handle_listplaylist(Client &client, Request args, Response &r)
|
||||||
RangeArg range = args.ParseOptional(1, RangeArg::All());
|
RangeArg range = args.ParseOptional(1, RangeArg::All());
|
||||||
|
|
||||||
playlist_file_print(r, client.GetPartition(), SongLoader(client),
|
playlist_file_print(r, client.GetPartition(), SongLoader(client),
|
||||||
name, range.start, range.end, false);
|
name, range.start, range.end, false, nullptr);
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,7 +148,39 @@ handle_listplaylistinfo(Client &client, Request args, Response &r)
|
||||||
RangeArg range = args.ParseOptional(1, RangeArg::All());
|
RangeArg range = args.ParseOptional(1, RangeArg::All());
|
||||||
|
|
||||||
playlist_file_print(r, client.GetPartition(), SongLoader(client),
|
playlist_file_print(r, client.GetPartition(), SongLoader(client),
|
||||||
name, range.start, range.end, true);
|
name, range.start, range.end, true, nullptr);
|
||||||
|
return CommandResult::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandResult
|
||||||
|
handle_searchplaylist(Client &client, Request args, Response &r)
|
||||||
|
{
|
||||||
|
const auto name = LocateUri(UriPluginKind::PLAYLIST, args.front(),
|
||||||
|
&client
|
||||||
|
#ifdef ENABLE_DATABASE
|
||||||
|
, nullptr
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
args.shift();
|
||||||
|
|
||||||
|
RangeArg window = RangeArg::All();
|
||||||
|
if (args.size() == 2) {
|
||||||
|
window = args.ParseRange(args.size() - 1);
|
||||||
|
args.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
SongFilter filter;
|
||||||
|
try {
|
||||||
|
filter.Parse(args, true);
|
||||||
|
} catch (...) {
|
||||||
|
r.Error(ACK_ERROR_ARG,
|
||||||
|
GetFullMessage(std::current_exception()).c_str());
|
||||||
|
return CommandResult::ERROR;
|
||||||
|
}
|
||||||
|
filter.Optimize();
|
||||||
|
|
||||||
|
playlist_file_print(r, client.GetPartition(), SongLoader(client),
|
||||||
|
name, window.start, window.end, true, &filter);
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,9 @@ handle_listplaylist(Client &client, Request request, Response &response);
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_listplaylistinfo(Client &client, Request request, Response &response);
|
handle_listplaylistinfo(Client &client, Request request, Response &response);
|
||||||
|
|
||||||
|
CommandResult
|
||||||
|
handle_searchplaylist(Client &client, Request request, Response &response);
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_playlistlength(Client &client, Request request, Response &response);
|
handle_playlistlength(Client &client, Request request, Response &response);
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,9 @@
|
||||||
#include "PlaylistSong.hxx"
|
#include "PlaylistSong.hxx"
|
||||||
#include "SongEnumerator.hxx"
|
#include "SongEnumerator.hxx"
|
||||||
#include "SongPrint.hxx"
|
#include "SongPrint.hxx"
|
||||||
|
#include "song/Filter.hxx"
|
||||||
#include "song/DetachedSong.hxx"
|
#include "song/DetachedSong.hxx"
|
||||||
|
#include "song/LightSong.hxx"
|
||||||
#include "input/Error.hxx"
|
#include "input/Error.hxx"
|
||||||
#include "fs/Traits.hxx"
|
#include "fs/Traits.hxx"
|
||||||
#include "thread/Mutex.hxx"
|
#include "thread/Mutex.hxx"
|
||||||
|
@ -50,13 +52,55 @@ playlist_provider_print(Response &r,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
playlist_provider_search_print(Response &r,
|
||||||
|
const SongLoader &loader,
|
||||||
|
const char *uri,
|
||||||
|
SongEnumerator &e,
|
||||||
|
unsigned start_index,
|
||||||
|
unsigned end_index,
|
||||||
|
SongFilter *filter) noexcept
|
||||||
|
{
|
||||||
|
const auto base_uri = uri != nullptr
|
||||||
|
? PathTraitsUTF8::GetParent(uri)
|
||||||
|
: ".";
|
||||||
|
|
||||||
|
std::unique_ptr<DetachedSong> song;
|
||||||
|
|
||||||
|
unsigned skip = start_index;
|
||||||
|
unsigned n = end_index - start_index;
|
||||||
|
|
||||||
|
while ((song = e.NextSong()) != nullptr) {
|
||||||
|
const bool detail = playlist_check_translate_song(*song, base_uri,
|
||||||
|
loader);
|
||||||
|
if (!filter->Match(static_cast<LightSong>(*song)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (skip > 0) {
|
||||||
|
--skip;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (detail)
|
||||||
|
song_print_info(r, *song);
|
||||||
|
else
|
||||||
|
/* fallback if no detail was requested or no
|
||||||
|
detail was available */
|
||||||
|
song_print_uri(r, *song);
|
||||||
|
|
||||||
|
if (--n == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
playlist_file_print(Response &r, Partition &partition,
|
playlist_file_print(Response &r, Partition &partition,
|
||||||
const SongLoader &loader,
|
const SongLoader &loader,
|
||||||
const LocatedUri &uri,
|
const LocatedUri &uri,
|
||||||
unsigned start_index,
|
unsigned start_index,
|
||||||
unsigned end_index,
|
unsigned end_index,
|
||||||
bool detail)
|
bool detail,
|
||||||
|
SongFilter *filter)
|
||||||
try {
|
try {
|
||||||
Mutex mutex;
|
Mutex mutex;
|
||||||
|
|
||||||
|
@ -72,8 +116,12 @@ try {
|
||||||
if (playlist == nullptr)
|
if (playlist == nullptr)
|
||||||
throw PlaylistError::NoSuchList();
|
throw PlaylistError::NoSuchList();
|
||||||
|
|
||||||
playlist_provider_print(r, loader, uri.canonical_uri, *playlist,
|
if (filter == nullptr)
|
||||||
start_index, end_index, detail);
|
playlist_provider_print(r, loader, uri.canonical_uri, *playlist,
|
||||||
|
start_index, end_index, detail);
|
||||||
|
else
|
||||||
|
playlist_provider_search_print(r, loader, uri.canonical_uri, *playlist,
|
||||||
|
start_index, end_index, filter);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
if (IsFileNotFound(std::current_exception()))
|
if (IsFileNotFound(std::current_exception()))
|
||||||
throw PlaylistError::NoSuchList();
|
throw PlaylistError::NoSuchList();
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
class Response;
|
class Response;
|
||||||
class SongLoader;
|
class SongLoader;
|
||||||
|
class SongFilter;
|
||||||
struct Partition;
|
struct Partition;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -20,4 +21,6 @@ playlist_file_print(Response &r, Partition &partition,
|
||||||
const SongLoader &loader,
|
const SongLoader &loader,
|
||||||
const LocatedUri &uri,
|
const LocatedUri &uri,
|
||||||
unsigned start_index, unsigned end_index,
|
unsigned start_index, unsigned end_index,
|
||||||
bool detail);
|
bool detail,
|
||||||
|
SongFilter *filter);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue