queue/Print: implement sorting

This commit is contained in:
Max Kellermann
2022-02-14 06:39:39 +01:00
parent 166ce0da5a
commit c3be961ccf
5 changed files with 119 additions and 3 deletions

View File

@@ -24,6 +24,7 @@
#include "protocol/RangeArg.hxx"
#include "db/DatabaseQueue.hxx"
#include "db/Selection.hxx"
#include "tag/ParseName.hxx"
#include "song/Filter.hxx"
#include "SongLoader.hxx"
#include "song/DetachedSong.hxx"
@@ -286,6 +287,19 @@ handle_playlistid(Client &client, Request args, Response &r)
return CommandResult::OK;
}
static TagType
ParseSortTag(const char *s)
{
if (StringIsEqualIgnoreCase(s, "Last-Modified"))
return TagType(SORT_TAG_LAST_MODIFIED);
TagType tag = tag_name_parse_i(s);
if (tag == TAG_NUM_OF_ITEM_TYPES)
throw ProtocolError(ACK_ERROR_ARG, "Unknown sort tag");
return tag;
}
static CommandResult
handle_playlist_match(Client &client, Request args, Response &r,
bool fold_case)
@@ -298,6 +312,21 @@ handle_playlist_match(Client &client, Request args, Response &r,
args.pop_back();
}
TagType sort = TAG_NUM_OF_ITEM_TYPES;
bool descending = false;
if (args.size >= 2 && StringIsEqual(args[args.size - 2], "sort")) {
const char *s = args.back();
if (*s == '-') {
descending = true;
++s;
}
sort = ParseSortTag(s);
args.pop_back();
args.pop_back();
}
SongFilter filter;
try {
filter.Parse(args, fold_case);
@@ -311,6 +340,8 @@ handle_playlist_match(Client &client, Request args, Response &r,
QueueSelection selection;
selection.filter = &filter;
selection.window = window;
selection.sort = sort;
selection.descending = descending;
playlist_print_find(r, client.GetPlaylist(), selection);
return CommandResult::OK;

View File

@@ -24,11 +24,14 @@
#include "SongPrint.hxx"
#include "song/DetachedSong.hxx"
#include "song/LightSong.hxx"
#include "tag/Sort.hxx"
#include "client/Response.hxx"
#include "PlaylistError.hxx"
#include <fmt/format.h>
#include <algorithm>
/**
* Send detailed information about a range of songs in the queue to a
* client.
@@ -101,10 +104,70 @@ queue_print_changes_position(Response &r, const Queue &queue,
i, queue.PositionToId(i));
}
[[gnu::pure]]
static std::vector<unsigned>
CollectQueue(const Queue &queue, const QueueSelection &selection) noexcept
{
std::vector<unsigned> v;
for (unsigned i = 0; i < queue.GetLength(); i++)
if (selection.MatchPosition(queue, i))
v.emplace_back(i);
return v;
}
static void
PrintSortedQueue(Response &r, const Queue &queue,
const QueueSelection &selection)
{
/* collect all matching songs */
auto v = CollectQueue(queue, selection);
auto window = selection.window;
if (!window.CheckClip(v.size()))
throw PlaylistError::BadRange();
/* sort them */
const auto sort = selection.sort;
const auto descending = selection.descending;
if (sort == TagType(SORT_TAG_LAST_MODIFIED))
std::stable_sort(v.begin(), v.end(),
[&queue, descending](unsigned a_pos, unsigned b_pos){
if (descending)
std::swap(a_pos, b_pos);
const auto &a = queue.Get(a_pos);
const auto &b = queue.Get(b_pos);
return a.GetLastModified() < b.GetLastModified();
});
else
std::stable_sort(v.begin(), v.end(),
[&queue, sort, descending](unsigned a_pos,
unsigned b_pos){
const auto &a = queue.Get(a_pos);
const auto &b = queue.Get(b_pos);
return CompareTags(sort, descending,
a.GetTag(),
b.GetTag());
});
for (unsigned i = window.start; i < window.end; ++i)
queue_print_song_info(r, queue, v[i]);
}
void
PrintQueue(Response &r, const Queue &queue,
const QueueSelection &selection)
{
if (selection.sort != TAG_NUM_OF_ITEM_TYPES) {
PrintSortedQueue(r, queue, selection);
return;
}
auto window = selection.window;
if (!window.CheckClip(queue.GetLength()))

View File

@@ -20,6 +20,7 @@
#pragma once
#include "protocol/RangeArg.hxx"
#include "tag/Type.h"
struct Queue;
class SongFilter;
@@ -36,6 +37,18 @@ struct QueueSelection {
RangeArg window = RangeArg::All();
/**
* Sort the result by the given tag. #TAG_NUM_OF_ITEM_TYPES
* means don't sort. #SORT_TAG_LAST_MODIFIED sorts by
* "Last-Modified" (not technically a tag).
*/
TagType sort = TAG_NUM_OF_ITEM_TYPES;
/**
* If #sort is set, this flag can reverse the sort order.
*/
bool descending = false;
[[gnu::pure]]
bool MatchPosition(const Queue &queue,
unsigned position) const noexcept;