client/Response: new Client wrapper class for writing responses
This commit is contained in:
parent
b1480167be
commit
7652a2986b
@ -122,6 +122,7 @@ libmpd_a_SOURCES = \
|
|||||||
src/client/ClientMessage.cxx src/client/ClientMessage.hxx \
|
src/client/ClientMessage.cxx src/client/ClientMessage.hxx \
|
||||||
src/client/ClientSubscribe.cxx \
|
src/client/ClientSubscribe.cxx \
|
||||||
src/client/ClientFile.cxx \
|
src/client/ClientFile.cxx \
|
||||||
|
src/client/Response.cxx src/client/Response.hxx \
|
||||||
src/Listen.cxx src/Listen.hxx \
|
src/Listen.cxx src/Listen.hxx \
|
||||||
src/LogInit.cxx src/LogInit.hxx \
|
src/LogInit.cxx src/LogInit.hxx \
|
||||||
src/LogBackend.cxx src/LogBackend.hxx \
|
src/LogBackend.cxx src/LogBackend.hxx \
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include "Instance.hxx"
|
#include "Instance.hxx"
|
||||||
#include "db/Interface.hxx"
|
#include "db/Interface.hxx"
|
||||||
#include "client/Client.hxx"
|
#include "client/Client.hxx"
|
||||||
|
#include "client/Response.hxx"
|
||||||
#include "input/InputStream.hxx"
|
#include "input/InputStream.hxx"
|
||||||
#include "DetachedSong.hxx"
|
#include "DetachedSong.hxx"
|
||||||
#include "fs/Traits.hxx"
|
#include "fs/Traits.hxx"
|
||||||
@ -37,15 +38,17 @@
|
|||||||
#define SONG_TIME "Time: "
|
#define SONG_TIME "Time: "
|
||||||
|
|
||||||
void
|
void
|
||||||
playlist_print_uris(Client &client, const playlist &playlist)
|
playlist_print_uris(Response &r, Partition &partition,
|
||||||
|
const playlist &playlist)
|
||||||
{
|
{
|
||||||
const Queue &queue = playlist.queue;
|
const Queue &queue = playlist.queue;
|
||||||
|
|
||||||
queue_print_uris(client, queue, 0, queue.GetLength());
|
queue_print_uris(r, partition, queue, 0, queue.GetLength());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
playlist_print_info(Client &client, const playlist &playlist,
|
playlist_print_info(Response &r, Partition &partition,
|
||||||
|
const playlist &playlist,
|
||||||
unsigned start, unsigned end)
|
unsigned start, unsigned end)
|
||||||
{
|
{
|
||||||
const Queue &queue = playlist.queue;
|
const Queue &queue = playlist.queue;
|
||||||
@ -58,12 +61,12 @@ playlist_print_info(Client &client, const playlist &playlist,
|
|||||||
/* an invalid "start" offset is fatal */
|
/* an invalid "start" offset is fatal */
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
queue_print_info(client, queue, start, end);
|
queue_print_info(r, partition, queue, start, end);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
playlist_print_id(Client &client, const playlist &playlist,
|
playlist_print_id(Response &r, Partition &partition, const playlist &playlist,
|
||||||
unsigned id)
|
unsigned id)
|
||||||
{
|
{
|
||||||
int position;
|
int position;
|
||||||
@ -73,50 +76,53 @@ playlist_print_id(Client &client, const playlist &playlist,
|
|||||||
/* no such song */
|
/* no such song */
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return playlist_print_info(client, playlist, position, position + 1);
|
return playlist_print_info(r, partition,
|
||||||
|
playlist, position, position + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
playlist_print_current(Client &client, const playlist &playlist)
|
playlist_print_current(Response &r, Partition &partition,
|
||||||
|
const playlist &playlist)
|
||||||
{
|
{
|
||||||
int current_position = playlist.GetCurrentPosition();
|
int current_position = playlist.GetCurrentPosition();
|
||||||
if (current_position < 0)
|
if (current_position < 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
queue_print_info(client, playlist.queue,
|
queue_print_info(r, partition, playlist.queue,
|
||||||
current_position, current_position + 1);
|
current_position, current_position + 1);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
playlist_print_find(Client &client, const playlist &playlist,
|
playlist_print_find(Response &r, Partition &partition,
|
||||||
|
const playlist &playlist,
|
||||||
const SongFilter &filter)
|
const SongFilter &filter)
|
||||||
{
|
{
|
||||||
queue_find(client, playlist.queue, filter);
|
queue_find(r, partition, playlist.queue, filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
playlist_print_changes_info(Client &client,
|
playlist_print_changes_info(Response &r, Partition &partition,
|
||||||
const playlist &playlist,
|
const playlist &playlist,
|
||||||
uint32_t version)
|
uint32_t version)
|
||||||
{
|
{
|
||||||
queue_print_changes_info(client, playlist.queue, version);
|
queue_print_changes_info(r, partition, playlist.queue, version);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
playlist_print_changes_position(Client &client,
|
playlist_print_changes_position(Response &r,
|
||||||
const playlist &playlist,
|
const playlist &playlist,
|
||||||
uint32_t version)
|
uint32_t version)
|
||||||
{
|
{
|
||||||
queue_print_changes_position(client, playlist.queue, version);
|
queue_print_changes_position(r, playlist.queue, version);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_DATABASE
|
#ifdef ENABLE_DATABASE
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
PrintSongDetails(Client &client, const char *uri_utf8)
|
PrintSongDetails(Response &r, Partition &partition, const char *uri_utf8)
|
||||||
{
|
{
|
||||||
const Database *db = client.partition.instance.database;
|
const Database *db = partition.instance.database;
|
||||||
if (db == nullptr)
|
if (db == nullptr)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -124,7 +130,7 @@ PrintSongDetails(Client &client, const char *uri_utf8)
|
|||||||
if (song == nullptr)
|
if (song == nullptr)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
song_print_info(client, *song);
|
song_print_info(r, partition, *song);
|
||||||
db->ReturnSong(song);
|
db->ReturnSong(song);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -132,7 +138,8 @@ PrintSongDetails(Client &client, const char *uri_utf8)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool
|
bool
|
||||||
spl_print(Client &client, const char *name_utf8, bool detail,
|
spl_print(Response &r, Partition &partition,
|
||||||
|
const char *name_utf8, bool detail,
|
||||||
Error &error)
|
Error &error)
|
||||||
{
|
{
|
||||||
#ifndef ENABLE_DATABASE
|
#ifndef ENABLE_DATABASE
|
||||||
@ -145,10 +152,10 @@ spl_print(Client &client, const char *name_utf8, bool detail,
|
|||||||
|
|
||||||
for (const auto &uri_utf8 : contents) {
|
for (const auto &uri_utf8 : contents) {
|
||||||
#ifdef ENABLE_DATABASE
|
#ifdef ENABLE_DATABASE
|
||||||
if (!detail || !PrintSongDetails(client, uri_utf8.c_str()))
|
if (!detail || !PrintSongDetails(r, partition,
|
||||||
|
uri_utf8.c_str()))
|
||||||
#endif
|
#endif
|
||||||
client_printf(client, SONG_FILE "%s\n",
|
r.Format(SONG_FILE "%s\n", uri_utf8.c_str());
|
||||||
uri_utf8.c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -23,15 +23,18 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
struct playlist;
|
struct playlist;
|
||||||
|
struct Partition;
|
||||||
class SongFilter;
|
class SongFilter;
|
||||||
class Client;
|
class Client;
|
||||||
|
class Response;
|
||||||
class Error;
|
class Error;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends the whole playlist to the client, song URIs only.
|
* Sends the whole playlist to the client, song URIs only.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
playlist_print_uris(Client &client, const playlist &playlist);
|
playlist_print_uris(Response &r, Partition &partition,
|
||||||
|
const playlist &playlist);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a range of the playlist to the client, including all known
|
* Sends a range of the playlist to the client, including all known
|
||||||
@ -40,7 +43,8 @@ playlist_print_uris(Client &client, const playlist &playlist);
|
|||||||
* This function however fails when the start offset is invalid.
|
* This function however fails when the start offset is invalid.
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
playlist_print_info(Client &client, const playlist &playlist,
|
playlist_print_info(Response &r, Partition &partition,
|
||||||
|
const playlist &playlist,
|
||||||
unsigned start, unsigned end);
|
unsigned start, unsigned end);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -49,8 +53,8 @@ playlist_print_info(Client &client, const playlist &playlist,
|
|||||||
* @return true on suite, false if there is no such song
|
* @return true on suite, false if there is no such song
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
playlist_print_id(Client &client, const playlist &playlist,
|
playlist_print_id(Response &r, Partition &partition,
|
||||||
unsigned id);
|
const playlist &playlist, unsigned id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends the current song to the client.
|
* Sends the current song to the client.
|
||||||
@ -58,20 +62,22 @@ playlist_print_id(Client &client, const playlist &playlist,
|
|||||||
* @return true on success, false if there is no current song
|
* @return true on success, false if there is no current song
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
playlist_print_current(Client &client, const playlist &playlist);
|
playlist_print_current(Response &r, Partition &partition,
|
||||||
|
const playlist &playlist);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find songs in the playlist.
|
* Find songs in the playlist.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
playlist_print_find(Client &client, const playlist &playlist,
|
playlist_print_find(Response &r, Partition &partition,
|
||||||
|
const playlist &playlist,
|
||||||
const SongFilter &filter);
|
const SongFilter &filter);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Print detailed changes since the specified playlist version.
|
* Print detailed changes since the specified playlist version.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
playlist_print_changes_info(Client &client,
|
playlist_print_changes_info(Response &r, Partition &partition,
|
||||||
const playlist &playlist,
|
const playlist &playlist,
|
||||||
uint32_t version);
|
uint32_t version);
|
||||||
|
|
||||||
@ -79,7 +85,7 @@ playlist_print_changes_info(Client &client,
|
|||||||
* Print changes since the specified playlist version, position only.
|
* Print changes since the specified playlist version, position only.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
playlist_print_changes_position(Client &client,
|
playlist_print_changes_position(Response &r,
|
||||||
const playlist &playlist,
|
const playlist &playlist,
|
||||||
uint32_t version);
|
uint32_t version);
|
||||||
|
|
||||||
@ -92,7 +98,8 @@ playlist_print_changes_position(Client &client,
|
|||||||
* @return true on success, false if the playlist does not exist
|
* @return true on success, false if the playlist does not exist
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
spl_print(Client &client, const char *name_utf8, bool detail,
|
spl_print(Response &r, Partition &partition,
|
||||||
|
const char *name_utf8, bool detail,
|
||||||
Error &error);
|
Error &error);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -20,18 +20,20 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "SongPrint.hxx"
|
#include "SongPrint.hxx"
|
||||||
#include "db/LightSong.hxx"
|
#include "db/LightSong.hxx"
|
||||||
|
#include "Partition.hxx"
|
||||||
|
#include "Instance.hxx"
|
||||||
#include "storage/StorageInterface.hxx"
|
#include "storage/StorageInterface.hxx"
|
||||||
#include "DetachedSong.hxx"
|
#include "DetachedSong.hxx"
|
||||||
#include "TimePrint.hxx"
|
#include "TimePrint.hxx"
|
||||||
#include "TagPrint.hxx"
|
#include "TagPrint.hxx"
|
||||||
#include "client/Client.hxx"
|
#include "client/Response.hxx"
|
||||||
#include "fs/Traits.hxx"
|
#include "fs/Traits.hxx"
|
||||||
#include "util/UriUtil.hxx"
|
#include "util/UriUtil.hxx"
|
||||||
|
|
||||||
#define SONG_FILE "file: "
|
#define SONG_FILE "file: "
|
||||||
|
|
||||||
static void
|
static void
|
||||||
song_print_uri(Client &client, const char *uri, bool base)
|
song_print_uri(Response &r, Partition &partition, const char *uri, bool base)
|
||||||
{
|
{
|
||||||
std::string allocated;
|
std::string allocated;
|
||||||
|
|
||||||
@ -39,12 +41,14 @@ song_print_uri(Client &client, const char *uri, bool base)
|
|||||||
uri = PathTraitsUTF8::GetBase(uri);
|
uri = PathTraitsUTF8::GetBase(uri);
|
||||||
} else {
|
} else {
|
||||||
#ifdef ENABLE_DATABASE
|
#ifdef ENABLE_DATABASE
|
||||||
const Storage *storage = client.GetStorage();
|
const Storage *storage = partition.instance.storage;
|
||||||
if (storage != nullptr) {
|
if (storage != nullptr) {
|
||||||
const char *suffix = storage->MapToRelativeUTF8(uri);
|
const char *suffix = storage->MapToRelativeUTF8(uri);
|
||||||
if (suffix != nullptr)
|
if (suffix != nullptr)
|
||||||
uri = suffix;
|
uri = suffix;
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
(void)partition;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
allocated = uri_remove_auth(uri);
|
allocated = uri_remove_auth(uri);
|
||||||
@ -52,78 +56,81 @@ song_print_uri(Client &client, const char *uri, bool base)
|
|||||||
uri = allocated.c_str();
|
uri = allocated.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
client_printf(client, SONG_FILE "%s\n", uri);
|
r.Format(SONG_FILE "%s\n", uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
song_print_uri(Client &client, const LightSong &song, bool base)
|
song_print_uri(Response &r, Partition &partition,
|
||||||
|
const LightSong &song, bool base)
|
||||||
{
|
{
|
||||||
if (!base && song.directory != nullptr) {
|
if (!base && song.directory != nullptr)
|
||||||
client_printf(client, SONG_FILE "%s/%s\n",
|
r.Format(SONG_FILE "%s/%s\n", song.directory, song.uri);
|
||||||
song.directory, song.uri);
|
else
|
||||||
} else
|
song_print_uri(r, partition, song.uri, base);
|
||||||
song_print_uri(client, song.uri, base);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
song_print_uri(Client &client, const DetachedSong &song, bool base)
|
song_print_uri(Response &r, Partition &partition,
|
||||||
|
const DetachedSong &song, bool base)
|
||||||
{
|
{
|
||||||
song_print_uri(client, song.GetURI(), base);
|
song_print_uri(r, partition, song.GetURI(), base);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
song_print_info(Client &client, const LightSong &song, bool base)
|
song_print_info(Response &r, Partition &partition,
|
||||||
|
const LightSong &song, bool base)
|
||||||
{
|
{
|
||||||
song_print_uri(client, song, base);
|
song_print_uri(r, partition, song, base);
|
||||||
|
|
||||||
const unsigned start_ms = song.start_time.ToMS();
|
const unsigned start_ms = song.start_time.ToMS();
|
||||||
const unsigned end_ms = song.end_time.ToMS();
|
const unsigned end_ms = song.end_time.ToMS();
|
||||||
|
|
||||||
if (end_ms > 0)
|
if (end_ms > 0)
|
||||||
client_printf(client, "Range: %u.%03u-%u.%03u\n",
|
r.Format("Range: %u.%03u-%u.%03u\n",
|
||||||
start_ms / 1000,
|
start_ms / 1000,
|
||||||
start_ms % 1000,
|
start_ms % 1000,
|
||||||
end_ms / 1000,
|
end_ms / 1000,
|
||||||
end_ms % 1000);
|
end_ms % 1000);
|
||||||
else if (start_ms > 0)
|
else if (start_ms > 0)
|
||||||
client_printf(client, "Range: %u.%03u-\n",
|
r.Format("Range: %u.%03u-\n",
|
||||||
start_ms / 1000,
|
start_ms / 1000,
|
||||||
start_ms % 1000);
|
start_ms % 1000);
|
||||||
|
|
||||||
if (song.mtime > 0)
|
if (song.mtime > 0)
|
||||||
time_print(client, "Last-Modified", song.mtime);
|
time_print(r, "Last-Modified", song.mtime);
|
||||||
|
|
||||||
tag_print(client, *song.tag);
|
tag_print(r, *song.tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
song_print_info(Client &client, const DetachedSong &song, bool base)
|
song_print_info(Response &r, Partition &partition,
|
||||||
|
const DetachedSong &song, bool base)
|
||||||
{
|
{
|
||||||
song_print_uri(client, song, base);
|
song_print_uri(r, partition, song, base);
|
||||||
|
|
||||||
const unsigned start_ms = song.GetStartTime().ToMS();
|
const unsigned start_ms = song.GetStartTime().ToMS();
|
||||||
const unsigned end_ms = song.GetEndTime().ToMS();
|
const unsigned end_ms = song.GetEndTime().ToMS();
|
||||||
|
|
||||||
if (end_ms > 0)
|
if (end_ms > 0)
|
||||||
client_printf(client, "Range: %u.%03u-%u.%03u\n",
|
r.Format("Range: %u.%03u-%u.%03u\n",
|
||||||
start_ms / 1000,
|
start_ms / 1000,
|
||||||
start_ms % 1000,
|
start_ms % 1000,
|
||||||
end_ms / 1000,
|
end_ms / 1000,
|
||||||
end_ms % 1000);
|
end_ms % 1000);
|
||||||
else if (start_ms > 0)
|
else if (start_ms > 0)
|
||||||
client_printf(client, "Range: %u.%03u-\n",
|
r.Format("Range: %u.%03u-\n",
|
||||||
start_ms / 1000,
|
start_ms / 1000,
|
||||||
start_ms % 1000);
|
start_ms % 1000);
|
||||||
|
|
||||||
if (song.GetLastModified() > 0)
|
if (song.GetLastModified() > 0)
|
||||||
time_print(client, "Last-Modified", song.GetLastModified());
|
time_print(r, "Last-Modified", song.GetLastModified());
|
||||||
|
|
||||||
tag_print_values(client, song.GetTag());
|
tag_print_values(r, song.GetTag());
|
||||||
|
|
||||||
const auto duration = song.GetDuration();
|
const auto duration = song.GetDuration();
|
||||||
if (!duration.IsNegative())
|
if (!duration.IsNegative())
|
||||||
client_printf(client, "Time: %i\n"
|
r.Format("Time: %i\n"
|
||||||
"duration: %1.3f\n",
|
"duration: %1.3f\n",
|
||||||
duration.RoundS(),
|
duration.RoundS(),
|
||||||
duration.ToDoubleS());
|
duration.ToDoubleS());
|
||||||
}
|
}
|
||||||
|
@ -22,18 +22,23 @@
|
|||||||
|
|
||||||
struct LightSong;
|
struct LightSong;
|
||||||
class DetachedSong;
|
class DetachedSong;
|
||||||
class Client;
|
class Response;
|
||||||
|
struct Partition;
|
||||||
|
|
||||||
void
|
void
|
||||||
song_print_info(Client &client, const DetachedSong &song, bool base=false);
|
song_print_info(Response &r, Partition &partition,
|
||||||
|
const DetachedSong &song, bool base=false);
|
||||||
|
|
||||||
void
|
void
|
||||||
song_print_info(Client &client, const LightSong &song, bool base=false);
|
song_print_info(Response &r, Partition &partition,
|
||||||
|
const LightSong &song, bool base=false);
|
||||||
|
|
||||||
void
|
void
|
||||||
song_print_uri(Client &client, const LightSong &song, bool base=false);
|
song_print_uri(Response &r, Partition &partition,
|
||||||
|
const LightSong &song, bool base=false);
|
||||||
|
|
||||||
void
|
void
|
||||||
song_print_uri(Client &client, const DetachedSong &song, bool base=false);
|
song_print_uri(Response &r, Partition &partition,
|
||||||
|
const DetachedSong &song, bool base=false);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "Stats.hxx"
|
#include "Stats.hxx"
|
||||||
#include "PlayerControl.hxx"
|
#include "PlayerControl.hxx"
|
||||||
#include "client/Client.hxx"
|
#include "client/Response.hxx"
|
||||||
#include "Partition.hxx"
|
#include "Partition.hxx"
|
||||||
#include "Instance.hxx"
|
#include "Instance.hxx"
|
||||||
#include "db/Selection.hxx"
|
#include "db/Selection.hxx"
|
||||||
@ -94,7 +94,7 @@ stats_update(const Database &db)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
db_stats_print(Client &client, const Database &db)
|
db_stats_print(Response &r, const Database &db)
|
||||||
{
|
{
|
||||||
if (!stats_update(db))
|
if (!stats_update(db))
|
||||||
return;
|
return;
|
||||||
@ -102,41 +102,38 @@ db_stats_print(Client &client, const Database &db)
|
|||||||
unsigned total_duration_s =
|
unsigned total_duration_s =
|
||||||
std::chrono::duration_cast<std::chrono::seconds>(stats.total_duration).count();
|
std::chrono::duration_cast<std::chrono::seconds>(stats.total_duration).count();
|
||||||
|
|
||||||
client_printf(client,
|
r.Format("artists: %u\n"
|
||||||
"artists: %u\n"
|
"albums: %u\n"
|
||||||
"albums: %u\n"
|
"songs: %u\n"
|
||||||
"songs: %u\n"
|
"db_playtime: %u\n",
|
||||||
"db_playtime: %u\n",
|
stats.artist_count,
|
||||||
stats.artist_count,
|
stats.album_count,
|
||||||
stats.album_count,
|
stats.song_count,
|
||||||
stats.song_count,
|
total_duration_s);
|
||||||
total_duration_s);
|
|
||||||
|
|
||||||
const time_t update_stamp = db.GetUpdateStamp();
|
const time_t update_stamp = db.GetUpdateStamp();
|
||||||
if (update_stamp > 0)
|
if (update_stamp > 0)
|
||||||
client_printf(client,
|
r.Format("db_update: %lu\n",
|
||||||
"db_update: %lu\n",
|
(unsigned long)update_stamp);
|
||||||
(unsigned long)update_stamp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void
|
void
|
||||||
stats_print(Client &client)
|
stats_print(Response &r, const Partition &partition)
|
||||||
{
|
{
|
||||||
client_printf(client,
|
r.Format("uptime: %u\n"
|
||||||
"uptime: %u\n"
|
"playtime: %lu\n",
|
||||||
"playtime: %lu\n",
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
GetProcessUptimeS(),
|
GetProcessUptimeS(),
|
||||||
#else
|
#else
|
||||||
MonotonicClockS() - start_time,
|
MonotonicClockS() - start_time,
|
||||||
#endif
|
#endif
|
||||||
(unsigned long)(client.player_control.GetTotalPlayTime() + 0.5));
|
(unsigned long)(partition.pc.GetTotalPlayTime() + 0.5));
|
||||||
|
|
||||||
#ifdef ENABLE_DATABASE
|
#ifdef ENABLE_DATABASE
|
||||||
const Database *db = client.partition.instance.database;
|
const Database *db = partition.instance.database;
|
||||||
if (db != nullptr)
|
if (db != nullptr)
|
||||||
db_stats_print(client, *db);
|
db_stats_print(r, *db);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,8 @@
|
|||||||
#ifndef MPD_STATS_HXX
|
#ifndef MPD_STATS_HXX
|
||||||
#define MPD_STATS_HXX
|
#define MPD_STATS_HXX
|
||||||
|
|
||||||
class Client;
|
class Response;
|
||||||
|
struct Partition;
|
||||||
|
|
||||||
void
|
void
|
||||||
stats_global_init();
|
stats_global_init();
|
||||||
@ -29,6 +30,6 @@ void
|
|||||||
stats_invalidate();
|
stats_invalidate();
|
||||||
|
|
||||||
void
|
void
|
||||||
stats_print(Client &client);
|
stats_print(Response &r, const Partition &partition);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -21,40 +21,40 @@
|
|||||||
#include "TagPrint.hxx"
|
#include "TagPrint.hxx"
|
||||||
#include "tag/Tag.hxx"
|
#include "tag/Tag.hxx"
|
||||||
#include "tag/TagSettings.h"
|
#include "tag/TagSettings.h"
|
||||||
#include "client/Client.hxx"
|
#include "client/Response.hxx"
|
||||||
|
|
||||||
void tag_print_types(Client &client)
|
void
|
||||||
|
tag_print_types(Response &r)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < TAG_NUM_OF_ITEM_TYPES; i++) {
|
for (i = 0; i < TAG_NUM_OF_ITEM_TYPES; i++) {
|
||||||
if (!ignore_tag_items[i])
|
if (!ignore_tag_items[i])
|
||||||
client_printf(client, "tagtype: %s\n",
|
r.Format("tagtype: %s\n", tag_item_names[i]);
|
||||||
tag_item_names[i]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
tag_print(Client &client, TagType type, const char *value)
|
tag_print(Response &r, TagType type, const char *value)
|
||||||
{
|
{
|
||||||
client_printf(client, "%s: %s\n", tag_item_names[type], value);
|
r.Format("%s: %s\n", tag_item_names[type], value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
tag_print_values(Client &client, const Tag &tag)
|
tag_print_values(Response &r, const Tag &tag)
|
||||||
{
|
{
|
||||||
for (const auto &i : tag)
|
for (const auto &i : tag)
|
||||||
client_printf(client, "%s: %s\n",
|
r.Format("%s: %s\n", tag_item_names[i.type], i.value);
|
||||||
tag_item_names[i.type], i.value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void tag_print(Client &client, const Tag &tag)
|
void
|
||||||
|
tag_print(Response &r, const Tag &tag)
|
||||||
{
|
{
|
||||||
if (!tag.duration.IsNegative())
|
if (!tag.duration.IsNegative())
|
||||||
client_printf(client, "Time: %i\n"
|
r.Format("Time: %i\n"
|
||||||
"duration: %1.3f\n",
|
"duration: %1.3f\n",
|
||||||
tag.duration.RoundS(),
|
tag.duration.RoundS(),
|
||||||
tag.duration.ToDoubleS());
|
tag.duration.ToDoubleS());
|
||||||
|
|
||||||
tag_print_values(client, tag);
|
tag_print_values(r, tag);
|
||||||
}
|
}
|
||||||
|
@ -25,17 +25,18 @@
|
|||||||
enum TagType : uint8_t;
|
enum TagType : uint8_t;
|
||||||
|
|
||||||
struct Tag;
|
struct Tag;
|
||||||
class Client;
|
class Response;
|
||||||
|
|
||||||
void tag_print_types(Client &client);
|
|
||||||
|
|
||||||
void
|
void
|
||||||
tag_print(Client &client, TagType type, const char *value);
|
tag_print_types(Response &response);
|
||||||
|
|
||||||
void
|
void
|
||||||
tag_print_values(Client &client, const Tag &tag);
|
tag_print(Response &response, TagType type, const char *value);
|
||||||
|
|
||||||
void
|
void
|
||||||
tag_print(Client &client, const Tag &tag);
|
tag_print_values(Response &response, const Tag &tag);
|
||||||
|
|
||||||
|
void
|
||||||
|
tag_print(Response &response, const Tag &tag);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -19,10 +19,10 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "TimePrint.hxx"
|
#include "TimePrint.hxx"
|
||||||
#include "client/Client.hxx"
|
#include "client/Response.hxx"
|
||||||
|
|
||||||
void
|
void
|
||||||
time_print(Client &client, const char *name, time_t t)
|
time_print(Response &r, const char *name, time_t t)
|
||||||
{
|
{
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
const struct tm *tm2 = gmtime(&t);
|
const struct tm *tm2 = gmtime(&t);
|
||||||
@ -41,5 +41,5 @@ time_print(Client &client, const char *name, time_t t)
|
|||||||
"%FT%TZ",
|
"%FT%TZ",
|
||||||
#endif
|
#endif
|
||||||
tm2);
|
tm2);
|
||||||
client_printf(client, "%s: %s\n", name, buffer);
|
r.Format("%s: %s\n", name, buffer);
|
||||||
}
|
}
|
||||||
|
@ -22,12 +22,12 @@
|
|||||||
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
class Client;
|
class Response;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write a line with a time stamp to the client.
|
* Write a line with a time stamp to the client.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
time_print(Client &client, const char *name, time_t t);
|
time_print(Response &r, const char *name, time_t t);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
72
src/client/Response.cxx
Normal file
72
src/client/Response.cxx
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2003-2015 The Music Player Daemon Project
|
||||||
|
* http://www.musicpd.org
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "Response.hxx"
|
||||||
|
#include "Client.hxx"
|
||||||
|
#include "protocol/Result.hxx"
|
||||||
|
#include "util/FormatString.hxx"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
bool
|
||||||
|
Response::Write(const void *data, size_t length)
|
||||||
|
{
|
||||||
|
return client.Write(data, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Response::Write(const char *data)
|
||||||
|
{
|
||||||
|
return Write(data, strlen(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Response::FormatV(const char *fmt, va_list args)
|
||||||
|
{
|
||||||
|
char *p = FormatNewV(fmt, args);
|
||||||
|
bool success = Write(p);
|
||||||
|
delete[] p;
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Response::Format(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
bool success = FormatV(fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Response::Error(enum ack code, const char *msg)
|
||||||
|
{
|
||||||
|
command_error(client, code, "%s", msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Response::FormatError(enum ack code, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
command_error_v(client, code, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
49
src/client/Response.hxx
Normal file
49
src/client/Response.hxx
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2003-2015 The Music Player Daemon Project
|
||||||
|
* http://www.musicpd.org
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MPD_RESPONSE_HXX
|
||||||
|
#define MPD_RESPONSE_HXX
|
||||||
|
|
||||||
|
#include "check.h"
|
||||||
|
#include "protocol/Ack.hxx"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
class Client;
|
||||||
|
|
||||||
|
class Response {
|
||||||
|
Client &client;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Response(Client &_client):client(_client) {}
|
||||||
|
|
||||||
|
Response(const Response &) = delete;
|
||||||
|
Response &operator=(const Response &) = delete;
|
||||||
|
|
||||||
|
bool Write(const void *data, size_t length);
|
||||||
|
bool Write(const char *data);
|
||||||
|
bool FormatV(const char *fmt, va_list args);
|
||||||
|
bool Format(const char *fmt, ...);
|
||||||
|
|
||||||
|
void Error(enum ack code, const char *msg);
|
||||||
|
void FormatError(enum ack code, const char *fmt, ...);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -36,6 +36,7 @@
|
|||||||
#include "protocol/Result.hxx"
|
#include "protocol/Result.hxx"
|
||||||
#include "Partition.hxx"
|
#include "Partition.hxx"
|
||||||
#include "client/Client.hxx"
|
#include "client/Client.hxx"
|
||||||
|
#include "client/Response.hxx"
|
||||||
#include "util/Macros.hxx"
|
#include "util/Macros.hxx"
|
||||||
#include "util/Tokenizer.hxx"
|
#include "util/Tokenizer.hxx"
|
||||||
#include "util/Error.hxx"
|
#include "util/Error.hxx"
|
||||||
@ -226,38 +227,50 @@ command_available(gcc_unused const Partition &partition,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* don't be fooled, this is the command handler for "commands" command */
|
|
||||||
static CommandResult
|
static CommandResult
|
||||||
handle_commands(Client &client, gcc_unused Request args)
|
PrintAvailableCommands(Response &r, const Partition &partition,
|
||||||
|
unsigned permission)
|
||||||
{
|
{
|
||||||
const unsigned permission = client.GetPermission();
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < num_commands; ++i) {
|
for (unsigned i = 0; i < num_commands; ++i) {
|
||||||
const struct command *cmd = &commands[i];
|
const struct command *cmd = &commands[i];
|
||||||
|
|
||||||
if (cmd->permission == (permission & cmd->permission) &&
|
if (cmd->permission == (permission & cmd->permission) &&
|
||||||
command_available(client.partition, cmd))
|
command_available(partition, cmd))
|
||||||
client_printf(client, "command: %s\n", cmd->cmd);
|
r.Format("command: %s\n", cmd->cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static CommandResult
|
static CommandResult
|
||||||
handle_not_commands(Client &client, gcc_unused Request args)
|
PrintUnavailableCommands(Response &r, unsigned permission)
|
||||||
{
|
{
|
||||||
const unsigned permission = client.GetPermission();
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < num_commands; ++i) {
|
for (unsigned i = 0; i < num_commands; ++i) {
|
||||||
const struct command *cmd = &commands[i];
|
const struct command *cmd = &commands[i];
|
||||||
|
|
||||||
if (cmd->permission != (permission & cmd->permission))
|
if (cmd->permission != (permission & cmd->permission))
|
||||||
client_printf(client, "command: %s\n", cmd->cmd);
|
r.Format("command: %s\n", cmd->cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* don't be fooled, this is the command handler for "commands" command */
|
||||||
|
static CommandResult
|
||||||
|
handle_commands(Client &client, gcc_unused Request args)
|
||||||
|
{
|
||||||
|
Response r(client);
|
||||||
|
return PrintAvailableCommands(r, client.partition,
|
||||||
|
client.GetPermission());
|
||||||
|
}
|
||||||
|
|
||||||
|
static CommandResult
|
||||||
|
handle_not_commands(Client &client, gcc_unused Request args)
|
||||||
|
{
|
||||||
|
Response r(client);
|
||||||
|
return PrintUnavailableCommands(r, client.GetPermission());
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
command_init()
|
command_init()
|
||||||
{
|
{
|
||||||
@ -299,7 +312,8 @@ command_check_request(const struct command *cmd, Client &client,
|
|||||||
unsigned permission, Request args)
|
unsigned permission, Request args)
|
||||||
{
|
{
|
||||||
if (cmd->permission != (permission & cmd->permission)) {
|
if (cmd->permission != (permission & cmd->permission)) {
|
||||||
command_error(client, ACK_ERROR_PERMISSION,
|
Response r(client);
|
||||||
|
r.FormatError(ACK_ERROR_PERMISSION,
|
||||||
"you don't have permission for \"%s\"",
|
"you don't have permission for \"%s\"",
|
||||||
cmd->cmd);
|
cmd->cmd);
|
||||||
return false;
|
return false;
|
||||||
@ -312,16 +326,19 @@ command_check_request(const struct command *cmd, Client &client,
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (min == max && unsigned(max) != args.size) {
|
if (min == max && unsigned(max) != args.size) {
|
||||||
command_error(client, ACK_ERROR_ARG,
|
Response r(client);
|
||||||
|
r.FormatError(ACK_ERROR_ARG,
|
||||||
"wrong number of arguments for \"%s\"",
|
"wrong number of arguments for \"%s\"",
|
||||||
cmd->cmd);
|
cmd->cmd);
|
||||||
return false;
|
return false;
|
||||||
} else if (args.size < unsigned(min)) {
|
} else if (args.size < unsigned(min)) {
|
||||||
command_error(client, ACK_ERROR_ARG,
|
Response r(client);
|
||||||
|
r.FormatError(ACK_ERROR_ARG,
|
||||||
"too few arguments for \"%s\"", cmd->cmd);
|
"too few arguments for \"%s\"", cmd->cmd);
|
||||||
return false;
|
return false;
|
||||||
} else if (max >= 0 && args.size > unsigned(max)) {
|
} else if (max >= 0 && args.size > unsigned(max)) {
|
||||||
command_error(client, ACK_ERROR_ARG,
|
Response r(client);
|
||||||
|
r.FormatError(ACK_ERROR_ARG,
|
||||||
"too many arguments for \"%s\"", cmd->cmd);
|
"too many arguments for \"%s\"", cmd->cmd);
|
||||||
return false;
|
return false;
|
||||||
} else
|
} else
|
||||||
@ -336,7 +353,8 @@ command_checked_lookup(Client &client, unsigned permission,
|
|||||||
|
|
||||||
const struct command *cmd = command_lookup(cmd_name);
|
const struct command *cmd = command_lookup(cmd_name);
|
||||||
if (cmd == nullptr) {
|
if (cmd == nullptr) {
|
||||||
command_error(client, ACK_ERROR_UNKNOWN,
|
Response r(client);
|
||||||
|
r.FormatError(ACK_ERROR_UNKNOWN,
|
||||||
"unknown command \"%s\"", cmd_name);
|
"unknown command \"%s\"", cmd_name);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@ -357,7 +375,7 @@ command_process(Client &client, unsigned num, char *line)
|
|||||||
command_list_num = num;
|
command_list_num = num;
|
||||||
|
|
||||||
/* get the command name (first word on the line) */
|
/* get the command name (first word on the line) */
|
||||||
/* we have to set current_command because command_error()
|
/* we have to set current_command because Response::Error()
|
||||||
expects it to be set */
|
expects it to be set */
|
||||||
|
|
||||||
Tokenizer tokenizer(line);
|
Tokenizer tokenizer(line);
|
||||||
@ -366,12 +384,12 @@ command_process(Client &client, unsigned num, char *line)
|
|||||||
tokenizer.NextWord(error);
|
tokenizer.NextWord(error);
|
||||||
if (cmd_name == nullptr) {
|
if (cmd_name == nullptr) {
|
||||||
current_command = "";
|
current_command = "";
|
||||||
|
|
||||||
|
Response r(client);
|
||||||
if (tokenizer.IsEnd())
|
if (tokenizer.IsEnd())
|
||||||
command_error(client, ACK_ERROR_UNKNOWN,
|
r.FormatError(ACK_ERROR_UNKNOWN, "No command given");
|
||||||
"No command given");
|
|
||||||
else
|
else
|
||||||
command_error(client, ACK_ERROR_UNKNOWN,
|
r.Error(ACK_ERROR_UNKNOWN, error.GetMessage());
|
||||||
"%s", error.GetMessage());
|
|
||||||
|
|
||||||
current_command = nullptr;
|
current_command = nullptr;
|
||||||
|
|
||||||
@ -387,8 +405,8 @@ command_process(Client &client, unsigned num, char *line)
|
|||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (args.size == COMMAND_ARGV_MAX) {
|
if (args.size == COMMAND_ARGV_MAX) {
|
||||||
command_error(client, ACK_ERROR_ARG,
|
Response r(client);
|
||||||
"Too many arguments");
|
r.Error(ACK_ERROR_ARG, "Too many arguments");
|
||||||
current_command = nullptr;
|
current_command = nullptr;
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
@ -398,7 +416,8 @@ command_process(Client &client, unsigned num, char *line)
|
|||||||
if (tokenizer.IsEnd())
|
if (tokenizer.IsEnd())
|
||||||
break;
|
break;
|
||||||
|
|
||||||
command_error(client, ACK_ERROR_ARG, "%s", error.GetMessage());
|
Response r(client);
|
||||||
|
r.Error(ACK_ERROR_UNKNOWN, error.GetMessage());
|
||||||
current_command = nullptr;
|
current_command = nullptr;
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "CommandError.hxx"
|
#include "CommandError.hxx"
|
||||||
#include "db/DatabaseError.hxx"
|
#include "db/DatabaseError.hxx"
|
||||||
#include "protocol/Result.hxx"
|
#include "client/Response.hxx"
|
||||||
#include "util/Error.hxx"
|
#include "util/Error.hxx"
|
||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
|
|
||||||
@ -29,57 +29,55 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
print_playlist_result(Client &client, PlaylistResult result)
|
print_playlist_result(Response &r, PlaylistResult result)
|
||||||
{
|
{
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case PlaylistResult::SUCCESS:
|
case PlaylistResult::SUCCESS:
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
|
|
||||||
case PlaylistResult::ERRNO:
|
case PlaylistResult::ERRNO:
|
||||||
command_error(client, ACK_ERROR_SYSTEM, "%s",
|
r.Error(ACK_ERROR_SYSTEM, strerror(errno));
|
||||||
strerror(errno));
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
case PlaylistResult::DENIED:
|
case PlaylistResult::DENIED:
|
||||||
command_error(client, ACK_ERROR_PERMISSION, "Access denied");
|
r.Error(ACK_ERROR_PERMISSION, "Access denied");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
case PlaylistResult::NO_SUCH_SONG:
|
case PlaylistResult::NO_SUCH_SONG:
|
||||||
command_error(client, ACK_ERROR_NO_EXIST, "No such song");
|
r.Error(ACK_ERROR_NO_EXIST, "No such song");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
case PlaylistResult::NO_SUCH_LIST:
|
case PlaylistResult::NO_SUCH_LIST:
|
||||||
command_error(client, ACK_ERROR_NO_EXIST, "No such playlist");
|
r.Error(ACK_ERROR_NO_EXIST, "No such playlist");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
case PlaylistResult::LIST_EXISTS:
|
case PlaylistResult::LIST_EXISTS:
|
||||||
command_error(client, ACK_ERROR_EXIST,
|
r.Error(ACK_ERROR_EXIST, "Playlist already exists");
|
||||||
"Playlist already exists");
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
case PlaylistResult::BAD_NAME:
|
case PlaylistResult::BAD_NAME:
|
||||||
command_error(client, ACK_ERROR_ARG,
|
r.Error(ACK_ERROR_ARG,
|
||||||
"playlist name is invalid: "
|
"playlist name is invalid: "
|
||||||
"playlist names may not contain slashes,"
|
"playlist names may not contain slashes,"
|
||||||
" newlines or carriage returns");
|
" newlines or carriage returns");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
case PlaylistResult::BAD_RANGE:
|
case PlaylistResult::BAD_RANGE:
|
||||||
command_error(client, ACK_ERROR_ARG, "Bad song index");
|
r.Error(ACK_ERROR_ARG, "Bad song index");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
case PlaylistResult::NOT_PLAYING:
|
case PlaylistResult::NOT_PLAYING:
|
||||||
command_error(client, ACK_ERROR_PLAYER_SYNC, "Not playing");
|
r.Error(ACK_ERROR_PLAYER_SYNC, "Not playing");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
case PlaylistResult::TOO_LARGE:
|
case PlaylistResult::TOO_LARGE:
|
||||||
command_error(client, ACK_ERROR_PLAYLIST_MAX,
|
r.Error(ACK_ERROR_PLAYLIST_MAX,
|
||||||
"playlist is at the max size");
|
"playlist is at the max size");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
case PlaylistResult::DISABLED:
|
case PlaylistResult::DISABLED:
|
||||||
command_error(client, ACK_ERROR_UNKNOWN,
|
r.Error(ACK_ERROR_UNKNOWN,
|
||||||
"stored playlist support is disabled");
|
"stored playlist support is disabled");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,42 +86,39 @@ print_playlist_result(Client &client, PlaylistResult result)
|
|||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
print_error(Client &client, const Error &error)
|
print_error(Response &r, const Error &error)
|
||||||
{
|
{
|
||||||
assert(error.IsDefined());
|
assert(error.IsDefined());
|
||||||
|
|
||||||
LogError(error);
|
LogError(error);
|
||||||
|
|
||||||
if (error.IsDomain(playlist_domain)) {
|
if (error.IsDomain(playlist_domain)) {
|
||||||
return print_playlist_result(client,
|
return print_playlist_result(r,
|
||||||
PlaylistResult(error.GetCode()));
|
PlaylistResult(error.GetCode()));
|
||||||
} else if (error.IsDomain(ack_domain)) {
|
} else if (error.IsDomain(ack_domain)) {
|
||||||
command_error(client, (ack)error.GetCode(),
|
r.Error((ack)error.GetCode(), error.GetMessage());
|
||||||
"%s", error.GetMessage());
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
#ifdef ENABLE_DATABASE
|
#ifdef ENABLE_DATABASE
|
||||||
} else if (error.IsDomain(db_domain)) {
|
} else if (error.IsDomain(db_domain)) {
|
||||||
switch ((enum db_error)error.GetCode()) {
|
switch ((enum db_error)error.GetCode()) {
|
||||||
case DB_DISABLED:
|
case DB_DISABLED:
|
||||||
command_error(client, ACK_ERROR_NO_EXIST, "%s",
|
r.Error(ACK_ERROR_NO_EXIST, error.GetMessage());
|
||||||
error.GetMessage());
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
case DB_NOT_FOUND:
|
case DB_NOT_FOUND:
|
||||||
command_error(client, ACK_ERROR_NO_EXIST, "Not found");
|
r.Error(ACK_ERROR_NO_EXIST, "Not found");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
case DB_CONFLICT:
|
case DB_CONFLICT:
|
||||||
command_error(client, ACK_ERROR_ARG, "Conflict");
|
r.Error(ACK_ERROR_ARG, "Conflict");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
} else if (error.IsDomain(errno_domain)) {
|
} else if (error.IsDomain(errno_domain)) {
|
||||||
command_error(client, ACK_ERROR_SYSTEM, "%s",
|
r.Error(ACK_ERROR_SYSTEM, strerror(error.GetCode()));
|
||||||
strerror(error.GetCode()));
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
command_error(client, ACK_ERROR_UNKNOWN, "error");
|
r.Error(ACK_ERROR_UNKNOWN, "error");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
@ -23,16 +23,16 @@
|
|||||||
#include "CommandResult.hxx"
|
#include "CommandResult.hxx"
|
||||||
#include "PlaylistError.hxx"
|
#include "PlaylistError.hxx"
|
||||||
|
|
||||||
class Client;
|
class Response;
|
||||||
class Error;
|
class Error;
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
print_playlist_result(Client &client, PlaylistResult result);
|
print_playlist_result(Response &r, PlaylistResult result);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send the #Error to the client.
|
* Send the #Error to the client.
|
||||||
*/
|
*/
|
||||||
CommandResult
|
CommandResult
|
||||||
print_error(Client &client, const Error &error);
|
print_error(Response &r, const Error &error);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -28,11 +28,11 @@
|
|||||||
#include "db/Selection.hxx"
|
#include "db/Selection.hxx"
|
||||||
#include "CommandError.hxx"
|
#include "CommandError.hxx"
|
||||||
#include "client/Client.hxx"
|
#include "client/Client.hxx"
|
||||||
|
#include "client/Response.hxx"
|
||||||
#include "tag/Tag.hxx"
|
#include "tag/Tag.hxx"
|
||||||
#include "util/ConstBuffer.hxx"
|
#include "util/ConstBuffer.hxx"
|
||||||
#include "util/Error.hxx"
|
#include "util/Error.hxx"
|
||||||
#include "SongFilter.hxx"
|
#include "SongFilter.hxx"
|
||||||
#include "protocol/Result.hxx"
|
|
||||||
#include "BulkEdit.hxx"
|
#include "BulkEdit.hxx"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -40,11 +40,13 @@
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_listfiles_db(Client &client, const char *uri)
|
handle_listfiles_db(Client &client, const char *uri)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
const DatabaseSelection selection(uri, false);
|
const DatabaseSelection selection(uri, false);
|
||||||
|
|
||||||
Error error;
|
Error error;
|
||||||
if (!db_selection_print(client, selection, false, true, error))
|
if (!db_selection_print(r, client.partition,
|
||||||
return print_error(client, error);
|
selection, false, true, error))
|
||||||
|
return print_error(r, error);
|
||||||
|
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
}
|
}
|
||||||
@ -52,14 +54,17 @@ handle_listfiles_db(Client &client, const char *uri)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_lsinfo2(Client &client, Request args)
|
handle_lsinfo2(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
/* default is root directory */
|
/* default is root directory */
|
||||||
const auto uri = args.GetOptional(0, "");
|
const auto uri = args.GetOptional(0, "");
|
||||||
|
|
||||||
const DatabaseSelection selection(uri, false);
|
const DatabaseSelection selection(uri, false);
|
||||||
|
|
||||||
Error error;
|
Error error;
|
||||||
if (!db_selection_print(client, selection, true, false, error))
|
if (!db_selection_print(r, client.partition,
|
||||||
return print_error(client, error);
|
selection, true, false, error))
|
||||||
|
return print_error(r, error);
|
||||||
|
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
}
|
}
|
||||||
@ -67,9 +72,11 @@ handle_lsinfo2(Client &client, Request args)
|
|||||||
static CommandResult
|
static CommandResult
|
||||||
handle_match(Client &client, Request args, bool fold_case)
|
handle_match(Client &client, Request args, bool fold_case)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
RangeArg window;
|
RangeArg window;
|
||||||
if (args.size >= 2 && strcmp(args[args.size - 2], "window") == 0) {
|
if (args.size >= 2 && strcmp(args[args.size - 2], "window") == 0) {
|
||||||
if (!args.Parse(args.size - 1, window, client))
|
if (!args.Parse(args.size - 1, window, r))
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
args.pop_back();
|
args.pop_back();
|
||||||
@ -79,17 +86,18 @@ handle_match(Client &client, Request args, bool fold_case)
|
|||||||
|
|
||||||
SongFilter filter;
|
SongFilter filter;
|
||||||
if (!filter.Parse(args, fold_case)) {
|
if (!filter.Parse(args, fold_case)) {
|
||||||
command_error(client, ACK_ERROR_ARG, "incorrect arguments");
|
r.Error(ACK_ERROR_ARG, "incorrect arguments");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DatabaseSelection selection("", true, &filter);
|
const DatabaseSelection selection("", true, &filter);
|
||||||
|
|
||||||
Error error;
|
Error error;
|
||||||
return db_selection_print(client, selection, true, false,
|
return db_selection_print(r, client.partition,
|
||||||
|
selection, true, false,
|
||||||
window.start, window.end, error)
|
window.start, window.end, error)
|
||||||
? CommandResult::OK
|
? CommandResult::OK
|
||||||
: print_error(client, error);
|
: print_error(r, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
@ -107,9 +115,11 @@ handle_search(Client &client, Request args)
|
|||||||
static CommandResult
|
static CommandResult
|
||||||
handle_match_add(Client &client, Request args, bool fold_case)
|
handle_match_add(Client &client, Request args, bool fold_case)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
SongFilter filter;
|
SongFilter filter;
|
||||||
if (!filter.Parse(args, fold_case)) {
|
if (!filter.Parse(args, fold_case)) {
|
||||||
command_error(client, ACK_ERROR_ARG, "incorrect arguments");
|
r.Error(ACK_ERROR_ARG, "incorrect arguments");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,7 +129,7 @@ handle_match_add(Client &client, Request args, bool fold_case)
|
|||||||
Error error;
|
Error error;
|
||||||
return AddFromDatabase(client.partition, selection, error)
|
return AddFromDatabase(client.partition, selection, error)
|
||||||
? CommandResult::OK
|
? CommandResult::OK
|
||||||
: print_error(client, error);
|
: print_error(r, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
@ -137,34 +147,38 @@ handle_searchadd(Client &client, Request args)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_searchaddpl(Client &client, Request args)
|
handle_searchaddpl(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
const char *playlist = args.shift();
|
const char *playlist = args.shift();
|
||||||
|
|
||||||
SongFilter filter;
|
SongFilter filter;
|
||||||
if (!filter.Parse(args, true)) {
|
if (!filter.Parse(args, true)) {
|
||||||
command_error(client, ACK_ERROR_ARG, "incorrect arguments");
|
r.Error(ACK_ERROR_ARG, "incorrect arguments");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
Error error;
|
Error error;
|
||||||
const Database *db = client.GetDatabase(error);
|
const Database *db = client.GetDatabase(error);
|
||||||
if (db == nullptr)
|
if (db == nullptr)
|
||||||
return print_error(client, error);
|
return print_error(r, error);
|
||||||
|
|
||||||
return search_add_to_playlist(*db, *client.GetStorage(),
|
return search_add_to_playlist(*db, *client.GetStorage(),
|
||||||
"", playlist, &filter, error)
|
"", playlist, &filter, error)
|
||||||
? CommandResult::OK
|
? CommandResult::OK
|
||||||
: print_error(client, error);
|
: print_error(r, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_count(Client &client, Request args)
|
handle_count(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
TagType group = TAG_NUM_OF_ITEM_TYPES;
|
TagType group = TAG_NUM_OF_ITEM_TYPES;
|
||||||
if (args.size >= 2 && strcmp(args[args.size - 2], "group") == 0) {
|
if (args.size >= 2 && strcmp(args[args.size - 2], "group") == 0) {
|
||||||
const char *s = args[args.size - 1];
|
const char *s = args[args.size - 1];
|
||||||
group = tag_name_parse_i(s);
|
group = tag_name_parse_i(s);
|
||||||
if (group == TAG_NUM_OF_ITEM_TYPES) {
|
if (group == TAG_NUM_OF_ITEM_TYPES) {
|
||||||
command_error(client, ACK_ERROR_ARG,
|
r.FormatError(ACK_ERROR_ARG,
|
||||||
"Unknown tag type: %s", s);
|
"Unknown tag type: %s", s);
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
@ -175,38 +189,43 @@ handle_count(Client &client, Request args)
|
|||||||
|
|
||||||
SongFilter filter;
|
SongFilter filter;
|
||||||
if (!args.IsEmpty() && !filter.Parse(args, false)) {
|
if (!args.IsEmpty() && !filter.Parse(args, false)) {
|
||||||
command_error(client, ACK_ERROR_ARG, "incorrect arguments");
|
r.Error(ACK_ERROR_ARG, "incorrect arguments");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
Error error;
|
Error error;
|
||||||
return PrintSongCount(client, "", &filter, group, error)
|
return PrintSongCount(r, client.partition, "", &filter, group, error)
|
||||||
? CommandResult::OK
|
? CommandResult::OK
|
||||||
: print_error(client, error);
|
: print_error(r, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_listall(Client &client, Request args)
|
handle_listall(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
/* default is root directory */
|
/* default is root directory */
|
||||||
const auto uri = args.GetOptional(0, "");
|
const auto uri = args.GetOptional(0, "");
|
||||||
|
|
||||||
Error error;
|
Error error;
|
||||||
return db_selection_print(client, DatabaseSelection(uri, true),
|
return db_selection_print(r, client.partition,
|
||||||
|
DatabaseSelection(uri, true),
|
||||||
false, false, error)
|
false, false, error)
|
||||||
? CommandResult::OK
|
? CommandResult::OK
|
||||||
: print_error(client, error);
|
: print_error(r, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_list(Client &client, Request args)
|
handle_list(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
const char *tag_name = args.shift();
|
const char *tag_name = args.shift();
|
||||||
unsigned tagType = locate_parse_type(tag_name);
|
unsigned tagType = locate_parse_type(tag_name);
|
||||||
|
|
||||||
if (tagType >= TAG_NUM_OF_ITEM_TYPES &&
|
if (tagType >= TAG_NUM_OF_ITEM_TYPES &&
|
||||||
tagType != LOCATE_TAG_FILE_TYPE) {
|
tagType != LOCATE_TAG_FILE_TYPE) {
|
||||||
command_error(client, ACK_ERROR_ARG,
|
r.FormatError(ACK_ERROR_ARG,
|
||||||
"Unknown tag type: %s", tag_name);
|
"Unknown tag type: %s", tag_name);
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
@ -217,7 +236,7 @@ handle_list(Client &client, Request args)
|
|||||||
if (args.size == 1) {
|
if (args.size == 1) {
|
||||||
/* for compatibility with < 0.12.0 */
|
/* for compatibility with < 0.12.0 */
|
||||||
if (tagType != TAG_ALBUM) {
|
if (tagType != TAG_ALBUM) {
|
||||||
command_error(client, ACK_ERROR_ARG,
|
r.FormatError(ACK_ERROR_ARG,
|
||||||
"should be \"%s\" for 3 arguments",
|
"should be \"%s\" for 3 arguments",
|
||||||
tag_item_names[TAG_ALBUM]);
|
tag_item_names[TAG_ALBUM]);
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
@ -231,7 +250,7 @@ handle_list(Client &client, Request args)
|
|||||||
const char *s = args[args.size - 1];
|
const char *s = args[args.size - 1];
|
||||||
TagType gt = tag_name_parse_i(s);
|
TagType gt = tag_name_parse_i(s);
|
||||||
if (gt == TAG_NUM_OF_ITEM_TYPES) {
|
if (gt == TAG_NUM_OF_ITEM_TYPES) {
|
||||||
command_error(client, ACK_ERROR_ARG,
|
r.FormatError(ACK_ERROR_ARG,
|
||||||
"Unknown tag type: %s", s);
|
"Unknown tag type: %s", s);
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
@ -246,8 +265,7 @@ handle_list(Client &client, Request args)
|
|||||||
filter = new SongFilter();
|
filter = new SongFilter();
|
||||||
if (!filter->Parse(args, false)) {
|
if (!filter->Parse(args, false)) {
|
||||||
delete filter;
|
delete filter;
|
||||||
command_error(client, ACK_ERROR_ARG,
|
r.Error(ACK_ERROR_ARG, "not able to parse args");
|
||||||
"not able to parse args");
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -255,15 +273,16 @@ handle_list(Client &client, Request args)
|
|||||||
if (tagType < TAG_NUM_OF_ITEM_TYPES &&
|
if (tagType < TAG_NUM_OF_ITEM_TYPES &&
|
||||||
group_mask & (1u << tagType)) {
|
group_mask & (1u << tagType)) {
|
||||||
delete filter;
|
delete filter;
|
||||||
command_error(client, ACK_ERROR_ARG, "Conflicting group");
|
r.Error(ACK_ERROR_ARG, "Conflicting group");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
Error error;
|
Error error;
|
||||||
CommandResult ret =
|
CommandResult ret =
|
||||||
PrintUniqueTags(client, tagType, group_mask, filter, error)
|
PrintUniqueTags(r, client.partition,
|
||||||
|
tagType, group_mask, filter, error)
|
||||||
? CommandResult::OK
|
? CommandResult::OK
|
||||||
: print_error(client, error);
|
: print_error(r, error);
|
||||||
|
|
||||||
delete filter;
|
delete filter;
|
||||||
|
|
||||||
@ -273,12 +292,15 @@ handle_list(Client &client, Request args)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_listallinfo(Client &client, Request args)
|
handle_listallinfo(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
/* default is root directory */
|
/* default is root directory */
|
||||||
const auto uri = args.GetOptional(0, "");
|
const auto uri = args.GetOptional(0, "");
|
||||||
|
|
||||||
Error error;
|
Error error;
|
||||||
return db_selection_print(client, DatabaseSelection(uri, true),
|
return db_selection_print(r, client.partition,
|
||||||
|
DatabaseSelection(uri, true),
|
||||||
true, false, error)
|
true, false, error)
|
||||||
? CommandResult::OK
|
? CommandResult::OK
|
||||||
: print_error(client, error);
|
: print_error(r, error);
|
||||||
}
|
}
|
||||||
|
@ -24,8 +24,8 @@
|
|||||||
#include "Request.hxx"
|
#include "Request.hxx"
|
||||||
#include "CommandError.hxx"
|
#include "CommandError.hxx"
|
||||||
#include "protocol/Ack.hxx"
|
#include "protocol/Ack.hxx"
|
||||||
#include "protocol/Result.hxx"
|
|
||||||
#include "client/Client.hxx"
|
#include "client/Client.hxx"
|
||||||
|
#include "client/Response.hxx"
|
||||||
#include "util/ConstBuffer.hxx"
|
#include "util/ConstBuffer.hxx"
|
||||||
#include "util/CharUtil.hxx"
|
#include "util/CharUtil.hxx"
|
||||||
#include "util/UriUtil.hxx"
|
#include "util/UriUtil.hxx"
|
||||||
@ -72,21 +72,22 @@ skip_path(Path name_fs)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_listfiles_local(Client &client, const char *path_utf8)
|
handle_listfiles_local(Client &client, const char *path_utf8)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
const auto path_fs = AllocatedPath::FromUTF8(path_utf8);
|
const auto path_fs = AllocatedPath::FromUTF8(path_utf8);
|
||||||
if (path_fs.IsNull()) {
|
if (path_fs.IsNull()) {
|
||||||
command_error(client, ACK_ERROR_NO_EXIST,
|
r.Error(ACK_ERROR_NO_EXIST, "unsupported file name");
|
||||||
"unsupported file name");
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
Error error;
|
Error error;
|
||||||
if (!client.AllowFile(path_fs, error))
|
if (!client.AllowFile(path_fs, error))
|
||||||
return print_error(client, error);
|
return print_error(r, error);
|
||||||
|
|
||||||
DirectoryReader reader(path_fs);
|
DirectoryReader reader(path_fs);
|
||||||
if (reader.HasFailed()) {
|
if (reader.HasFailed()) {
|
||||||
error.FormatErrno("Failed to open '%s'", path_utf8);
|
error.FormatErrno("Failed to open '%s'", path_utf8);
|
||||||
return print_error(client, error);
|
return print_error(r, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (reader.ReadEntry()) {
|
while (reader.ReadEntry()) {
|
||||||
@ -105,17 +106,16 @@ handle_listfiles_local(Client &client, const char *path_utf8)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (fi.IsRegular())
|
if (fi.IsRegular())
|
||||||
client_printf(client, "file: %s\n"
|
r.Format("file: %s\n"
|
||||||
"size: %" PRIu64 "\n",
|
"size: %" PRIu64 "\n",
|
||||||
name_utf8.c_str(),
|
name_utf8.c_str(),
|
||||||
fi.GetSize());
|
fi.GetSize());
|
||||||
else if (fi.IsDirectory())
|
else if (fi.IsDirectory())
|
||||||
client_printf(client, "directory: %s\n",
|
r.Format("directory: %s\n", name_utf8.c_str());
|
||||||
name_utf8.c_str());
|
|
||||||
else
|
else
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
time_print(client, "Last-Modified", fi.GetModificationTime());
|
time_print(r, "Last-Modified", fi.GetModificationTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
@ -158,10 +158,10 @@ IsValidValue(const char *p)
|
|||||||
static void
|
static void
|
||||||
print_pair(const char *key, const char *value, void *ctx)
|
print_pair(const char *key, const char *value, void *ctx)
|
||||||
{
|
{
|
||||||
Client &client = *(Client *)ctx;
|
auto &r = *(Response *)ctx;
|
||||||
|
|
||||||
if (IsValidName(key) && IsValidValue(value))
|
if (IsValidName(key) && IsValidValue(value))
|
||||||
client_printf(client, "%s: %s\n", key, value);
|
r.Format("%s: %s\n", key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr tag_handler print_comment_handler = {
|
static constexpr tag_handler print_comment_handler = {
|
||||||
@ -171,17 +171,15 @@ static constexpr tag_handler print_comment_handler = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static CommandResult
|
static CommandResult
|
||||||
read_stream_comments(Client &client, const char *uri)
|
read_stream_comments(Response &r, const char *uri)
|
||||||
{
|
{
|
||||||
if (!uri_supported_scheme(uri)) {
|
if (!uri_supported_scheme(uri)) {
|
||||||
command_error(client, ACK_ERROR_NO_EXIST,
|
r.Error(ACK_ERROR_NO_EXIST, "unsupported URI scheme");
|
||||||
"unsupported URI scheme");
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tag_stream_scan(uri, print_comment_handler, &client)) {
|
if (!tag_stream_scan(uri, print_comment_handler, &r)) {
|
||||||
command_error(client, ACK_ERROR_NO_EXIST,
|
r.Error(ACK_ERROR_NO_EXIST, "Failed to load file");
|
||||||
"Failed to load file");
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,16 +188,15 @@ read_stream_comments(Client &client, const char *uri)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static CommandResult
|
static CommandResult
|
||||||
read_file_comments(Client &client, const Path path_fs)
|
read_file_comments(Response &r, const Path path_fs)
|
||||||
{
|
{
|
||||||
if (!tag_file_scan(path_fs, print_comment_handler, &client)) {
|
if (!tag_file_scan(path_fs, print_comment_handler, &r)) {
|
||||||
command_error(client, ACK_ERROR_NO_EXIST,
|
r.Error(ACK_ERROR_NO_EXIST, "Failed to load file");
|
||||||
"Failed to load file");
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
tag_ape_scan2(path_fs, &print_comment_handler, &client);
|
tag_ape_scan2(path_fs, &print_comment_handler, &r);
|
||||||
tag_id3_scan(path_fs, &print_comment_handler, &client);
|
tag_id3_scan(path_fs, &print_comment_handler, &r);
|
||||||
|
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
|
|
||||||
@ -219,6 +216,8 @@ translate_uri(const char *uri)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_read_comments(Client &client, Request args)
|
handle_read_comments(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
assert(args.size == 1);
|
assert(args.size == 1);
|
||||||
const char *const uri = translate_uri(args.front());
|
const char *const uri = translate_uri(args.front());
|
||||||
|
|
||||||
@ -227,25 +226,23 @@ handle_read_comments(Client &client, Request args)
|
|||||||
const char *path_utf8 = uri + 7;
|
const char *path_utf8 = uri + 7;
|
||||||
AllocatedPath path_fs = AllocatedPath::FromUTF8(path_utf8);
|
AllocatedPath path_fs = AllocatedPath::FromUTF8(path_utf8);
|
||||||
if (path_fs.IsNull()) {
|
if (path_fs.IsNull()) {
|
||||||
command_error(client, ACK_ERROR_NO_EXIST,
|
r.Error(ACK_ERROR_NO_EXIST, "unsupported file name");
|
||||||
"unsupported file name");
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
Error error;
|
Error error;
|
||||||
if (!client.AllowFile(path_fs, error))
|
if (!client.AllowFile(path_fs, error))
|
||||||
return print_error(client, error);
|
return print_error(r, error);
|
||||||
|
|
||||||
return read_file_comments(client, path_fs);
|
return read_file_comments(r, path_fs);
|
||||||
} else if (uri_has_scheme(uri)) {
|
} else if (uri_has_scheme(uri)) {
|
||||||
return read_stream_comments(client, uri);
|
return read_stream_comments(r, uri);
|
||||||
} else if (!PathTraitsUTF8::IsAbsolute(uri)) {
|
} else if (!PathTraitsUTF8::IsAbsolute(uri)) {
|
||||||
#ifdef ENABLE_DATABASE
|
#ifdef ENABLE_DATABASE
|
||||||
const Storage *storage = client.GetStorage();
|
const Storage *storage = client.GetStorage();
|
||||||
if (storage == nullptr) {
|
if (storage == nullptr) {
|
||||||
#endif
|
#endif
|
||||||
command_error(client, ACK_ERROR_NO_EXIST,
|
r.Error(ACK_ERROR_NO_EXIST, "No database");
|
||||||
"No database");
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
#ifdef ENABLE_DATABASE
|
#ifdef ENABLE_DATABASE
|
||||||
}
|
}
|
||||||
@ -253,21 +250,20 @@ handle_read_comments(Client &client, Request args)
|
|||||||
{
|
{
|
||||||
AllocatedPath path_fs = storage->MapFS(uri);
|
AllocatedPath path_fs = storage->MapFS(uri);
|
||||||
if (!path_fs.IsNull())
|
if (!path_fs.IsNull())
|
||||||
return read_file_comments(client, path_fs);
|
return read_file_comments(r, path_fs);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const std::string uri2 = storage->MapUTF8(uri);
|
const std::string uri2 = storage->MapUTF8(uri);
|
||||||
if (uri_has_scheme(uri2.c_str()))
|
if (uri_has_scheme(uri2.c_str()))
|
||||||
return read_stream_comments(client,
|
return read_stream_comments(r, uri2.c_str());
|
||||||
uri2.c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
command_error(client, ACK_ERROR_NO_EXIST, "No such file");
|
r.Error(ACK_ERROR_NO_EXIST, "No such file");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
command_error(client, ACK_ERROR_NO_EXIST, "No such file");
|
r.Error(ACK_ERROR_NO_EXIST, "No such file");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,9 +22,9 @@
|
|||||||
#include "Request.hxx"
|
#include "Request.hxx"
|
||||||
#include "client/Client.hxx"
|
#include "client/Client.hxx"
|
||||||
#include "client/ClientList.hxx"
|
#include "client/ClientList.hxx"
|
||||||
|
#include "client/Response.hxx"
|
||||||
#include "Instance.hxx"
|
#include "Instance.hxx"
|
||||||
#include "Partition.hxx"
|
#include "Partition.hxx"
|
||||||
#include "protocol/Result.hxx"
|
|
||||||
#include "util/ConstBuffer.hxx"
|
#include "util/ConstBuffer.hxx"
|
||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
@ -35,6 +35,8 @@
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_subscribe(Client &client, Request args)
|
handle_subscribe(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
assert(args.size == 1);
|
assert(args.size == 1);
|
||||||
const char *const channel_name = args[0];
|
const char *const channel_name = args[0];
|
||||||
|
|
||||||
@ -43,18 +45,15 @@ handle_subscribe(Client &client, Request args)
|
|||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
|
|
||||||
case Client::SubscribeResult::INVALID:
|
case Client::SubscribeResult::INVALID:
|
||||||
command_error(client, ACK_ERROR_ARG,
|
r.Error(ACK_ERROR_ARG, "invalid channel name");
|
||||||
"invalid channel name");
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
case Client::SubscribeResult::ALREADY:
|
case Client::SubscribeResult::ALREADY:
|
||||||
command_error(client, ACK_ERROR_EXIST,
|
r.Error(ACK_ERROR_EXIST, "already subscribed to this channel");
|
||||||
"already subscribed to this channel");
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
case Client::SubscribeResult::FULL:
|
case Client::SubscribeResult::FULL:
|
||||||
command_error(client, ACK_ERROR_EXIST,
|
r.Error(ACK_ERROR_EXIST, "subscription list is full");
|
||||||
"subscription list is full");
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,14 +65,15 @@ handle_subscribe(Client &client, Request args)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_unsubscribe(Client &client, Request args)
|
handle_unsubscribe(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
assert(args.size == 1);
|
assert(args.size == 1);
|
||||||
const char *const channel_name = args[0];
|
const char *const channel_name = args[0];
|
||||||
|
|
||||||
if (client.Unsubscribe(channel_name))
|
if (client.Unsubscribe(channel_name))
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
else {
|
else {
|
||||||
command_error(client, ACK_ERROR_NO_EXIST,
|
r.Error(ACK_ERROR_NO_EXIST, "not subscribed to this channel");
|
||||||
"not subscribed to this channel");
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,8 +88,9 @@ handle_channels(Client &client, gcc_unused Request args)
|
|||||||
channels.insert(c.subscriptions.begin(),
|
channels.insert(c.subscriptions.begin(),
|
||||||
c.subscriptions.end());
|
c.subscriptions.end());
|
||||||
|
|
||||||
|
Response r(client);
|
||||||
for (const auto &channel : channels)
|
for (const auto &channel : channels)
|
||||||
client_printf(client, "channel: %s\n", channel.c_str());
|
r.Format("channel: %s\n", channel.c_str());
|
||||||
|
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
}
|
}
|
||||||
@ -100,11 +101,12 @@ handle_read_messages(Client &client,
|
|||||||
{
|
{
|
||||||
assert(args.IsEmpty());
|
assert(args.IsEmpty());
|
||||||
|
|
||||||
|
Response r(client);
|
||||||
while (!client.messages.empty()) {
|
while (!client.messages.empty()) {
|
||||||
const ClientMessage &msg = client.messages.front();
|
const ClientMessage &msg = client.messages.front();
|
||||||
|
|
||||||
client_printf(client, "channel: %s\nmessage: %s\n",
|
r.Format("channel: %s\nmessage: %s\n",
|
||||||
msg.GetChannel(), msg.GetMessage());
|
msg.GetChannel(), msg.GetMessage());
|
||||||
client.messages.pop_front();
|
client.messages.pop_front();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,9 +121,9 @@ handle_send_message(Client &client, Request args)
|
|||||||
const char *const channel_name = args[0];
|
const char *const channel_name = args[0];
|
||||||
const char *const message_text = args[1];
|
const char *const message_text = args[1];
|
||||||
|
|
||||||
|
Response r(client);
|
||||||
if (!client_message_valid_channel_name(channel_name)) {
|
if (!client_message_valid_channel_name(channel_name)) {
|
||||||
command_error(client, ACK_ERROR_ARG,
|
r.Error(ACK_ERROR_ARG, "invalid channel name");
|
||||||
"invalid channel name");
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,8 +136,8 @@ handle_send_message(Client &client, Request args)
|
|||||||
if (sent)
|
if (sent)
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
else {
|
else {
|
||||||
command_error(client, ACK_ERROR_NO_EXIST,
|
r.Error(ACK_ERROR_NO_EXIST,
|
||||||
"nobody is subscribed to this channel");
|
"nobody is subscribed to this channel");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,9 +21,9 @@
|
|||||||
#include "NeighborCommands.hxx"
|
#include "NeighborCommands.hxx"
|
||||||
#include "Request.hxx"
|
#include "Request.hxx"
|
||||||
#include "client/Client.hxx"
|
#include "client/Client.hxx"
|
||||||
|
#include "client/Response.hxx"
|
||||||
#include "Instance.hxx"
|
#include "Instance.hxx"
|
||||||
#include "Partition.hxx"
|
#include "Partition.hxx"
|
||||||
#include "protocol/Result.hxx"
|
|
||||||
#include "neighbor/Glue.hxx"
|
#include "neighbor/Glue.hxx"
|
||||||
#include "neighbor/Info.hxx"
|
#include "neighbor/Info.hxx"
|
||||||
#include "util/ConstBuffer.hxx"
|
#include "util/ConstBuffer.hxx"
|
||||||
@ -42,19 +42,19 @@ neighbor_commands_available(const Instance &instance)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_listneighbors(Client &client, gcc_unused Request args)
|
handle_listneighbors(Client &client, gcc_unused Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
const NeighborGlue *const neighbors =
|
const NeighborGlue *const neighbors =
|
||||||
client.partition.instance.neighbors;
|
client.partition.instance.neighbors;
|
||||||
if (neighbors == nullptr) {
|
if (neighbors == nullptr) {
|
||||||
command_error(client, ACK_ERROR_UNKNOWN,
|
r.Error(ACK_ERROR_UNKNOWN, "No neighbor plugin configured");
|
||||||
"No neighbor plugin configured");
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto &i : neighbors->GetList())
|
for (const auto &i : neighbors->GetList())
|
||||||
client_printf(client,
|
r.Format("neighbor: %s\n"
|
||||||
"neighbor: %s\n"
|
"name: %s\n",
|
||||||
"name: %s\n",
|
i.uri.c_str(),
|
||||||
i.uri.c_str(),
|
i.display_name.c_str());
|
||||||
i.display_name.c_str());
|
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,6 @@
|
|||||||
#include "tag/TagHandler.hxx"
|
#include "tag/TagHandler.hxx"
|
||||||
#include "TimePrint.hxx"
|
#include "TimePrint.hxx"
|
||||||
#include "decoder/DecoderPrint.hxx"
|
#include "decoder/DecoderPrint.hxx"
|
||||||
#include "protocol/Result.hxx"
|
|
||||||
#include "ls.hxx"
|
#include "ls.hxx"
|
||||||
#include "mixer/Volume.hxx"
|
#include "mixer/Volume.hxx"
|
||||||
#include "util/UriUtil.hxx"
|
#include "util/UriUtil.hxx"
|
||||||
@ -44,6 +43,7 @@
|
|||||||
#include "PlaylistFile.hxx"
|
#include "PlaylistFile.hxx"
|
||||||
#include "db/PlaylistVector.hxx"
|
#include "db/PlaylistVector.hxx"
|
||||||
#include "client/Client.hxx"
|
#include "client/Client.hxx"
|
||||||
|
#include "client/Response.hxx"
|
||||||
#include "Partition.hxx"
|
#include "Partition.hxx"
|
||||||
#include "Instance.hxx"
|
#include "Instance.hxx"
|
||||||
#include "Idle.hxx"
|
#include "Idle.hxx"
|
||||||
@ -58,36 +58,39 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
static void
|
static void
|
||||||
print_spl_list(Client &client, const PlaylistVector &list)
|
print_spl_list(Response &r, const PlaylistVector &list)
|
||||||
{
|
{
|
||||||
for (const auto &i : list) {
|
for (const auto &i : list) {
|
||||||
client_printf(client, "playlist: %s\n", i.name.c_str());
|
r.Format("playlist: %s\n", i.name.c_str());
|
||||||
|
|
||||||
if (i.mtime > 0)
|
if (i.mtime > 0)
|
||||||
time_print(client, "Last-Modified", i.mtime);
|
time_print(r, "Last-Modified", i.mtime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_urlhandlers(Client &client, gcc_unused Request args)
|
handle_urlhandlers(Client &client, gcc_unused Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
if (client.IsLocal())
|
if (client.IsLocal())
|
||||||
client_puts(client, "handler: file://\n");
|
r.Format("handler: file://\n");
|
||||||
print_supported_uri_schemes(client);
|
print_supported_uri_schemes(r);
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_decoders(Client &client, gcc_unused Request args)
|
handle_decoders(Client &client, gcc_unused Request args)
|
||||||
{
|
{
|
||||||
decoder_list_print(client);
|
Response r(client);
|
||||||
|
decoder_list_print(r);
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_tagtypes(Client &client, gcc_unused Request args)
|
handle_tagtypes(Client &client, gcc_unused Request args)
|
||||||
{
|
{
|
||||||
tag_print_types(client);
|
Response r(client);
|
||||||
|
tag_print_types(r);
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,14 +109,16 @@ handle_close(gcc_unused Client &client, gcc_unused Request args)
|
|||||||
static void
|
static void
|
||||||
print_tag(TagType type, const char *value, void *ctx)
|
print_tag(TagType type, const char *value, void *ctx)
|
||||||
{
|
{
|
||||||
Client &client = *(Client *)ctx;
|
auto &r = *(Response *)ctx;
|
||||||
|
|
||||||
tag_print(client, type, value);
|
tag_print(r, type, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_listfiles(Client &client, Request args)
|
handle_listfiles(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
/* default is root directory */
|
/* default is root directory */
|
||||||
const auto uri = args.GetOptional(0, "");
|
const auto uri = args.GetOptional(0, "");
|
||||||
|
|
||||||
@ -124,7 +129,7 @@ handle_listfiles(Client &client, Request args)
|
|||||||
#ifdef ENABLE_DATABASE
|
#ifdef ENABLE_DATABASE
|
||||||
if (uri_has_scheme(uri))
|
if (uri_has_scheme(uri))
|
||||||
/* use storage plugin to list remote directory */
|
/* use storage plugin to list remote directory */
|
||||||
return handle_listfiles_storage(client, uri);
|
return handle_listfiles_storage(r, uri);
|
||||||
|
|
||||||
/* must be a path relative to the configured
|
/* must be a path relative to the configured
|
||||||
music_directory */
|
music_directory */
|
||||||
@ -132,14 +137,14 @@ handle_listfiles(Client &client, Request args)
|
|||||||
if (client.partition.instance.storage != nullptr)
|
if (client.partition.instance.storage != nullptr)
|
||||||
/* if we have a storage instance, obtain a list of
|
/* if we have a storage instance, obtain a list of
|
||||||
files from it */
|
files from it */
|
||||||
return handle_listfiles_storage(client,
|
return handle_listfiles_storage(r,
|
||||||
*client.partition.instance.storage,
|
*client.partition.instance.storage,
|
||||||
uri);
|
uri);
|
||||||
|
|
||||||
/* fall back to entries from database if we have no storage */
|
/* fall back to entries from database if we have no storage */
|
||||||
return handle_listfiles_db(client, uri);
|
return handle_listfiles_db(client, uri);
|
||||||
#else
|
#else
|
||||||
command_error(client, ACK_ERROR_NO_EXIST, "No database");
|
r.Error(ACK_ERROR_NO_EXIST, "No database");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -156,42 +161,40 @@ handle_lsinfo(Client &client, Request args)
|
|||||||
/* default is root directory */
|
/* default is root directory */
|
||||||
const auto uri = args.GetOptional(0, "");
|
const auto uri = args.GetOptional(0, "");
|
||||||
|
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
if (memcmp(uri, "file:///", 8) == 0) {
|
if (memcmp(uri, "file:///", 8) == 0) {
|
||||||
/* print information about an arbitrary local file */
|
/* print information about an arbitrary local file */
|
||||||
const char *path_utf8 = uri + 7;
|
const char *path_utf8 = uri + 7;
|
||||||
const auto path_fs = AllocatedPath::FromUTF8(path_utf8);
|
const auto path_fs = AllocatedPath::FromUTF8(path_utf8);
|
||||||
|
|
||||||
if (path_fs.IsNull()) {
|
if (path_fs.IsNull()) {
|
||||||
command_error(client, ACK_ERROR_NO_EXIST,
|
r.Error(ACK_ERROR_NO_EXIST, "unsupported file name");
|
||||||
"unsupported file name");
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
Error error;
|
Error error;
|
||||||
if (!client.AllowFile(path_fs, error))
|
if (!client.AllowFile(path_fs, error))
|
||||||
return print_error(client, error);
|
return print_error(r, error);
|
||||||
|
|
||||||
DetachedSong song(path_utf8);
|
DetachedSong song(path_utf8);
|
||||||
if (!song.Update()) {
|
if (!song.Update()) {
|
||||||
command_error(client, ACK_ERROR_NO_EXIST,
|
r.Error(ACK_ERROR_NO_EXIST, "No such file");
|
||||||
"No such file");
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
song_print_info(client, song);
|
song_print_info(r, client.partition, song);
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uri_has_scheme(uri)) {
|
if (uri_has_scheme(uri)) {
|
||||||
if (!uri_supported_scheme(uri)) {
|
if (!uri_supported_scheme(uri)) {
|
||||||
command_error(client, ACK_ERROR_NO_EXIST,
|
r.Error(ACK_ERROR_NO_EXIST, "unsupported URI scheme");
|
||||||
"unsupported URI scheme");
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tag_stream_scan(uri, print_tag_handler, &client)) {
|
if (!tag_stream_scan(uri, print_tag_handler, &r)) {
|
||||||
command_error(client, ACK_ERROR_NO_EXIST,
|
r.Error(ACK_ERROR_NO_EXIST, "No such file");
|
||||||
"No such file");
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,10 +210,10 @@ handle_lsinfo(Client &client, Request args)
|
|||||||
if (isRootDirectory(uri)) {
|
if (isRootDirectory(uri)) {
|
||||||
Error error;
|
Error error;
|
||||||
const auto &list = ListPlaylistFiles(error);
|
const auto &list = ListPlaylistFiles(error);
|
||||||
print_spl_list(client, list);
|
print_spl_list(r, list);
|
||||||
} else {
|
} else {
|
||||||
#ifndef ENABLE_DATABASE
|
#ifndef ENABLE_DATABASE
|
||||||
command_error(client, ACK_ERROR_NO_EXIST, "No database");
|
r.Error(ACK_ERROR_NO_EXIST, "No database");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -224,13 +227,14 @@ static CommandResult
|
|||||||
handle_update(Client &client, UpdateService &update,
|
handle_update(Client &client, UpdateService &update,
|
||||||
const char *uri_utf8, bool discard)
|
const char *uri_utf8, bool discard)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
unsigned ret = update.Enqueue(uri_utf8, discard);
|
unsigned ret = update.Enqueue(uri_utf8, discard);
|
||||||
if (ret > 0) {
|
if (ret > 0) {
|
||||||
client_printf(client, "updating_db: %i\n", ret);
|
r.Format("updating_db: %i\n", ret);
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
} else {
|
} else {
|
||||||
command_error(client, ACK_ERROR_UPDATE_ALREADY,
|
r.Error(ACK_ERROR_UPDATE_ALREADY, "already updating");
|
||||||
"already updating");
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -239,17 +243,19 @@ static CommandResult
|
|||||||
handle_update(Client &client, Database &db,
|
handle_update(Client &client, Database &db,
|
||||||
const char *uri_utf8, bool discard)
|
const char *uri_utf8, bool discard)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
Error error;
|
Error error;
|
||||||
unsigned id = db.Update(uri_utf8, discard, error);
|
unsigned id = db.Update(uri_utf8, discard, error);
|
||||||
if (id > 0) {
|
if (id > 0) {
|
||||||
client_printf(client, "updating_db: %i\n", id);
|
r.Format("updating_db: %i\n", id);
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
} else if (error.IsDefined()) {
|
} else if (error.IsDefined()) {
|
||||||
return print_error(client, error);
|
return print_error(r, error);
|
||||||
} else {
|
} else {
|
||||||
/* Database::Update() has returned 0 without setting
|
/* Database::Update() has returned 0 without setting
|
||||||
the Error: the method is not implemented */
|
the Error: the method is not implemented */
|
||||||
command_error(client, ACK_ERROR_NO_EXIST, "Not implemented");
|
r.Error(ACK_ERROR_NO_EXIST, "Not implemented");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -259,6 +265,8 @@ handle_update(Client &client, Database &db,
|
|||||||
static CommandResult
|
static CommandResult
|
||||||
handle_update(Client &client, Request args, bool discard)
|
handle_update(Client &client, Request args, bool discard)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
#ifdef ENABLE_DATABASE
|
#ifdef ENABLE_DATABASE
|
||||||
const char *path = "";
|
const char *path = "";
|
||||||
|
|
||||||
@ -270,8 +278,7 @@ handle_update(Client &client, Request args, bool discard)
|
|||||||
/* backwards compatibility with MPD 0.15 */
|
/* backwards compatibility with MPD 0.15 */
|
||||||
path = "";
|
path = "";
|
||||||
else if (!uri_safe_local(path)) {
|
else if (!uri_safe_local(path)) {
|
||||||
command_error(client, ACK_ERROR_ARG,
|
r.Error(ACK_ERROR_ARG, "Malformed path");
|
||||||
"Malformed path");
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -288,7 +295,7 @@ handle_update(Client &client, Request args, bool discard)
|
|||||||
(void)discard;
|
(void)discard;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
command_error(client, ACK_ERROR_NO_EXIST, "No database");
|
r.Error(ACK_ERROR_NO_EXIST, "No database");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,13 +314,14 @@ handle_rescan(Client &client, gcc_unused Request args)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_setvol(Client &client, Request args)
|
handle_setvol(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
unsigned level;
|
unsigned level;
|
||||||
if (!args.Parse(0, level, client, 100))
|
if (!args.Parse(0, level, r, 100))
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
if (!volume_level_change(client.partition.outputs, level)) {
|
if (!volume_level_change(client.partition.outputs, level)) {
|
||||||
command_error(client, ACK_ERROR_SYSTEM,
|
r.Error(ACK_ERROR_SYSTEM, "problems setting volume");
|
||||||
"problems setting volume");
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,13 +331,15 @@ handle_setvol(Client &client, Request args)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_volume(Client &client, Request args)
|
handle_volume(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
int relative;
|
int relative;
|
||||||
if (!args.Parse(0, relative, client, -100, 100))
|
if (!args.Parse(0, relative, r, -100, 100))
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
const int old_volume = volume_level_get(client.partition.outputs);
|
const int old_volume = volume_level_get(client.partition.outputs);
|
||||||
if (old_volume < 0) {
|
if (old_volume < 0) {
|
||||||
command_error(client, ACK_ERROR_SYSTEM, "No mixer");
|
r.Error(ACK_ERROR_SYSTEM, "No mixer");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -341,8 +351,7 @@ handle_volume(Client &client, Request args)
|
|||||||
|
|
||||||
if (new_volume != old_volume &&
|
if (new_volume != old_volume &&
|
||||||
!volume_level_change(client.partition.outputs, new_volume)) {
|
!volume_level_change(client.partition.outputs, new_volume)) {
|
||||||
command_error(client, ACK_ERROR_SYSTEM,
|
r.Error(ACK_ERROR_SYSTEM, "problems setting volume");
|
||||||
"problems setting volume");
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -352,7 +361,8 @@ handle_volume(Client &client, Request args)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_stats(Client &client, gcc_unused Request args)
|
handle_stats(Client &client, gcc_unused Request args)
|
||||||
{
|
{
|
||||||
stats_print(client);
|
Response r(client);
|
||||||
|
stats_print(r, client.partition);
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,10 +375,11 @@ handle_ping(gcc_unused Client &client, gcc_unused Request args)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_password(Client &client, Request args)
|
handle_password(Client &client, Request args)
|
||||||
{
|
{
|
||||||
unsigned permission = 0;
|
Response r(client);
|
||||||
|
|
||||||
|
unsigned permission = 0;
|
||||||
if (getPermissionFromPassword(args.front(), &permission) < 0) {
|
if (getPermissionFromPassword(args.front(), &permission) < 0) {
|
||||||
command_error(client, ACK_ERROR_PASSWORD, "incorrect password");
|
r.Error(ACK_ERROR_PASSWORD, "incorrect password");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -380,9 +391,11 @@ handle_password(Client &client, Request args)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_config(Client &client, gcc_unused Request args)
|
handle_config(Client &client, gcc_unused Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
if (!client.IsLocal()) {
|
if (!client.IsLocal()) {
|
||||||
command_error(client, ACK_ERROR_PERMISSION,
|
r.Error(ACK_ERROR_PERMISSION,
|
||||||
"Command only permitted to local clients");
|
"Command only permitted to local clients");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -390,7 +403,7 @@ handle_config(Client &client, gcc_unused Request args)
|
|||||||
const Storage *storage = client.GetStorage();
|
const Storage *storage = client.GetStorage();
|
||||||
if (storage != nullptr) {
|
if (storage != nullptr) {
|
||||||
const auto path = storage->MapUTF8("");
|
const auto path = storage->MapUTF8("");
|
||||||
client_printf(client, "music_directory: %s\n", path.c_str());
|
r.Format("music_directory: %s\n", path.c_str());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -400,14 +413,14 @@ handle_config(Client &client, gcc_unused Request args)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_idle(Client &client, Request args)
|
handle_idle(Client &client, Request args)
|
||||||
{
|
{
|
||||||
unsigned flags = 0;
|
Response r(client);
|
||||||
|
|
||||||
|
unsigned flags = 0;
|
||||||
for (const char *i : args) {
|
for (const char *i : args) {
|
||||||
unsigned event = idle_parse_name(i);
|
unsigned event = idle_parse_name(i);
|
||||||
if (event == 0) {
|
if (event == 0) {
|
||||||
command_error(client, ACK_ERROR_ARG,
|
r.FormatError(ACK_ERROR_ARG,
|
||||||
"Unrecognized idle event: %s",
|
"Unrecognized idle event: %s", i);
|
||||||
i);
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,23 +22,23 @@
|
|||||||
#include "Request.hxx"
|
#include "Request.hxx"
|
||||||
#include "output/OutputPrint.hxx"
|
#include "output/OutputPrint.hxx"
|
||||||
#include "output/OutputCommand.hxx"
|
#include "output/OutputCommand.hxx"
|
||||||
#include "protocol/Result.hxx"
|
|
||||||
#include "client/Client.hxx"
|
#include "client/Client.hxx"
|
||||||
|
#include "client/Response.hxx"
|
||||||
#include "Partition.hxx"
|
#include "Partition.hxx"
|
||||||
#include "util/ConstBuffer.hxx"
|
#include "util/ConstBuffer.hxx"
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_enableoutput(Client &client, Request args)
|
handle_enableoutput(Client &client, Request args)
|
||||||
{
|
{
|
||||||
assert(args.size == 1);
|
Response r(client);
|
||||||
|
|
||||||
|
assert(args.size == 1);
|
||||||
unsigned device;
|
unsigned device;
|
||||||
if (!args.Parse(0, device, client))
|
if (!args.Parse(0, device, r))
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
if (!audio_output_enable_index(client.partition.outputs, device)) {
|
if (!audio_output_enable_index(client.partition.outputs, device)) {
|
||||||
command_error(client, ACK_ERROR_NO_EXIST,
|
r.Error(ACK_ERROR_NO_EXIST, "No such audio output");
|
||||||
"No such audio output");
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,15 +48,15 @@ handle_enableoutput(Client &client, Request args)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_disableoutput(Client &client, Request args)
|
handle_disableoutput(Client &client, Request args)
|
||||||
{
|
{
|
||||||
assert(args.size == 1);
|
Response r(client);
|
||||||
|
|
||||||
|
assert(args.size == 1);
|
||||||
unsigned device;
|
unsigned device;
|
||||||
if (!args.Parse(0, device, client))
|
if (!args.Parse(0, device, r))
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
if (!audio_output_disable_index(client.partition.outputs, device)) {
|
if (!audio_output_disable_index(client.partition.outputs, device)) {
|
||||||
command_error(client, ACK_ERROR_NO_EXIST,
|
r.Error(ACK_ERROR_NO_EXIST, "No such audio output");
|
||||||
"No such audio output");
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,15 +66,15 @@ handle_disableoutput(Client &client, Request args)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_toggleoutput(Client &client, Request args)
|
handle_toggleoutput(Client &client, Request args)
|
||||||
{
|
{
|
||||||
assert(args.size == 1);
|
Response r(client);
|
||||||
|
|
||||||
|
assert(args.size == 1);
|
||||||
unsigned device;
|
unsigned device;
|
||||||
if (!args.Parse(0, device, client))
|
if (!args.Parse(0, device, r))
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
if (!audio_output_toggle_index(client.partition.outputs, device)) {
|
if (!audio_output_toggle_index(client.partition.outputs, device)) {
|
||||||
command_error(client, ACK_ERROR_NO_EXIST,
|
r.Error(ACK_ERROR_NO_EXIST, "No such audio output");
|
||||||
"No such audio output");
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,7 +86,8 @@ handle_devices(Client &client, gcc_unused Request args)
|
|||||||
{
|
{
|
||||||
assert(args.IsEmpty());
|
assert(args.IsEmpty());
|
||||||
|
|
||||||
printAudioDevices(client, client.partition.outputs);
|
Response r(client);
|
||||||
|
printAudioDevices(r, client.partition.outputs);
|
||||||
|
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
}
|
}
|
||||||
|
@ -24,10 +24,10 @@
|
|||||||
#include "queue/Playlist.hxx"
|
#include "queue/Playlist.hxx"
|
||||||
#include "PlaylistPrint.hxx"
|
#include "PlaylistPrint.hxx"
|
||||||
#include "client/Client.hxx"
|
#include "client/Client.hxx"
|
||||||
|
#include "client/Response.hxx"
|
||||||
#include "mixer/Volume.hxx"
|
#include "mixer/Volume.hxx"
|
||||||
#include "Partition.hxx"
|
#include "Partition.hxx"
|
||||||
#include "Instance.hxx"
|
#include "Instance.hxx"
|
||||||
#include "protocol/Result.hxx"
|
|
||||||
#include "AudioFormat.hxx"
|
#include "AudioFormat.hxx"
|
||||||
#include "ReplayGainConfig.hxx"
|
#include "ReplayGainConfig.hxx"
|
||||||
#include "util/ConstBuffer.hxx"
|
#include "util/ConstBuffer.hxx"
|
||||||
@ -59,23 +59,27 @@
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_play(Client &client, Request args)
|
handle_play(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
int song = -1;
|
int song = -1;
|
||||||
if (!args.ParseOptional(0, song, client))
|
if (!args.ParseOptional(0, song, r))
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
PlaylistResult result = client.partition.PlayPosition(song);
|
PlaylistResult result = client.partition.PlayPosition(song);
|
||||||
return print_playlist_result(client, result);
|
return print_playlist_result(r, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_playid(Client &client, Request args)
|
handle_playid(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
int id = -1;
|
int id = -1;
|
||||||
if (!args.ParseOptional(0, id, client))
|
if (!args.ParseOptional(0, id, r))
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
PlaylistResult result = client.partition.PlayId(id);
|
PlaylistResult result = client.partition.PlayId(id);
|
||||||
return print_playlist_result(client, result);
|
return print_playlist_result(r, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
@ -88,16 +92,19 @@ handle_stop(Client &client, gcc_unused Request args)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_currentsong(Client &client, gcc_unused Request args)
|
handle_currentsong(Client &client, gcc_unused Request args)
|
||||||
{
|
{
|
||||||
playlist_print_current(client, client.playlist);
|
Response r(client);
|
||||||
|
playlist_print_current(r, client.partition, client.playlist);
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_pause(Client &client, Request args)
|
handle_pause(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
if (!args.IsEmpty()) {
|
if (!args.IsEmpty()) {
|
||||||
bool pause_flag;
|
bool pause_flag;
|
||||||
if (!args.Parse(0, pause_flag, client))
|
if (!args.Parse(0, pause_flag, r))
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
client.player_control.SetPause(pause_flag);
|
client.player_control.SetPause(pause_flag);
|
||||||
@ -127,68 +134,64 @@ handle_status(Client &client, gcc_unused Request args)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
const playlist &playlist = client.playlist;
|
const playlist &playlist = client.playlist;
|
||||||
client_printf(client,
|
r.Format("volume: %i\n"
|
||||||
"volume: %i\n"
|
COMMAND_STATUS_REPEAT ": %i\n"
|
||||||
COMMAND_STATUS_REPEAT ": %i\n"
|
COMMAND_STATUS_RANDOM ": %i\n"
|
||||||
COMMAND_STATUS_RANDOM ": %i\n"
|
COMMAND_STATUS_SINGLE ": %i\n"
|
||||||
COMMAND_STATUS_SINGLE ": %i\n"
|
COMMAND_STATUS_CONSUME ": %i\n"
|
||||||
COMMAND_STATUS_CONSUME ": %i\n"
|
COMMAND_STATUS_PLAYLIST ": %li\n"
|
||||||
COMMAND_STATUS_PLAYLIST ": %li\n"
|
COMMAND_STATUS_PLAYLIST_LENGTH ": %i\n"
|
||||||
COMMAND_STATUS_PLAYLIST_LENGTH ": %i\n"
|
COMMAND_STATUS_MIXRAMPDB ": %f\n"
|
||||||
COMMAND_STATUS_MIXRAMPDB ": %f\n"
|
COMMAND_STATUS_STATE ": %s\n",
|
||||||
COMMAND_STATUS_STATE ": %s\n",
|
volume_level_get(client.partition.outputs),
|
||||||
volume_level_get(client.partition.outputs),
|
playlist.GetRepeat(),
|
||||||
playlist.GetRepeat(),
|
playlist.GetRandom(),
|
||||||
playlist.GetRandom(),
|
playlist.GetSingle(),
|
||||||
playlist.GetSingle(),
|
playlist.GetConsume(),
|
||||||
playlist.GetConsume(),
|
(unsigned long)playlist.GetVersion(),
|
||||||
(unsigned long)playlist.GetVersion(),
|
playlist.GetLength(),
|
||||||
playlist.GetLength(),
|
client.player_control.GetMixRampDb(),
|
||||||
client.player_control.GetMixRampDb(),
|
state);
|
||||||
state);
|
|
||||||
|
|
||||||
if (client.player_control.GetCrossFade() > 0)
|
if (client.player_control.GetCrossFade() > 0)
|
||||||
client_printf(client,
|
r.Format(COMMAND_STATUS_CROSSFADE ": %i\n",
|
||||||
COMMAND_STATUS_CROSSFADE ": %i\n",
|
int(client.player_control.GetCrossFade() + 0.5));
|
||||||
int(client.player_control.GetCrossFade() + 0.5));
|
|
||||||
|
|
||||||
if (client.player_control.GetMixRampDelay() > 0)
|
if (client.player_control.GetMixRampDelay() > 0)
|
||||||
client_printf(client,
|
r.Format(COMMAND_STATUS_MIXRAMPDELAY ": %f\n",
|
||||||
COMMAND_STATUS_MIXRAMPDELAY ": %f\n",
|
client.player_control.GetMixRampDelay());
|
||||||
client.player_control.GetMixRampDelay());
|
|
||||||
|
|
||||||
song = playlist.GetCurrentPosition();
|
song = playlist.GetCurrentPosition();
|
||||||
if (song >= 0) {
|
if (song >= 0) {
|
||||||
client_printf(client,
|
r.Format(COMMAND_STATUS_SONG ": %i\n"
|
||||||
COMMAND_STATUS_SONG ": %i\n"
|
COMMAND_STATUS_SONGID ": %u\n",
|
||||||
COMMAND_STATUS_SONGID ": %u\n",
|
song, playlist.PositionToId(song));
|
||||||
song, playlist.PositionToId(song));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (player_status.state != PlayerState::STOP) {
|
if (player_status.state != PlayerState::STOP) {
|
||||||
client_printf(client,
|
r.Format(COMMAND_STATUS_TIME ": %i:%i\n"
|
||||||
COMMAND_STATUS_TIME ": %i:%i\n"
|
"elapsed: %1.3f\n"
|
||||||
"elapsed: %1.3f\n"
|
COMMAND_STATUS_BITRATE ": %u\n",
|
||||||
COMMAND_STATUS_BITRATE ": %u\n",
|
player_status.elapsed_time.RoundS(),
|
||||||
player_status.elapsed_time.RoundS(),
|
player_status.total_time.IsNegative()
|
||||||
player_status.total_time.IsNegative()
|
? 0u
|
||||||
? 0u
|
: unsigned(player_status.total_time.RoundS()),
|
||||||
: unsigned(player_status.total_time.RoundS()),
|
player_status.elapsed_time.ToDoubleS(),
|
||||||
player_status.elapsed_time.ToDoubleS(),
|
player_status.bit_rate);
|
||||||
player_status.bit_rate);
|
|
||||||
|
|
||||||
if (!player_status.total_time.IsNegative())
|
if (!player_status.total_time.IsNegative())
|
||||||
client_printf(client, "duration: %1.3f\n",
|
r.Format("duration: %1.3f\n",
|
||||||
player_status.total_time.ToDoubleS());
|
player_status.total_time.ToDoubleS());
|
||||||
|
|
||||||
if (player_status.audio_format.IsDefined()) {
|
if (player_status.audio_format.IsDefined()) {
|
||||||
struct audio_format_string af_string;
|
struct audio_format_string af_string;
|
||||||
|
|
||||||
client_printf(client,
|
r.Format(COMMAND_STATUS_AUDIO ": %s\n",
|
||||||
COMMAND_STATUS_AUDIO ": %s\n",
|
audio_format_to_string(player_status.audio_format,
|
||||||
audio_format_to_string(player_status.audio_format,
|
&af_string));
|
||||||
&af_string));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,25 +201,21 @@ handle_status(Client &client, gcc_unused Request args)
|
|||||||
? update_service->GetId()
|
? update_service->GetId()
|
||||||
: 0;
|
: 0;
|
||||||
if (updateJobId != 0) {
|
if (updateJobId != 0) {
|
||||||
client_printf(client,
|
r.Format(COMMAND_STATUS_UPDATING_DB ": %i\n",
|
||||||
COMMAND_STATUS_UPDATING_DB ": %i\n",
|
updateJobId);
|
||||||
updateJobId);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Error error = client.player_control.LockGetError();
|
Error error = client.player_control.LockGetError();
|
||||||
if (error.IsDefined())
|
if (error.IsDefined())
|
||||||
client_printf(client,
|
r.Format(COMMAND_STATUS_ERROR ": %s\n",
|
||||||
COMMAND_STATUS_ERROR ": %s\n",
|
error.GetMessage());
|
||||||
error.GetMessage());
|
|
||||||
|
|
||||||
song = playlist.GetNextPosition();
|
song = playlist.GetNextPosition();
|
||||||
if (song >= 0) {
|
if (song >= 0)
|
||||||
client_printf(client,
|
r.Format(COMMAND_STATUS_NEXTSONG ": %i\n"
|
||||||
COMMAND_STATUS_NEXTSONG ": %i\n"
|
COMMAND_STATUS_NEXTSONGID ": %u\n",
|
||||||
COMMAND_STATUS_NEXTSONGID ": %u\n",
|
song, playlist.PositionToId(song));
|
||||||
song, playlist.PositionToId(song));
|
|
||||||
}
|
|
||||||
|
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
}
|
}
|
||||||
@ -247,8 +246,10 @@ handle_previous(Client &client, gcc_unused Request args)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_repeat(Client &client, Request args)
|
handle_repeat(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
bool status;
|
bool status;
|
||||||
if (!args.Parse(0, status, client))
|
if (!args.Parse(0, status, r))
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
client.partition.SetRepeat(status);
|
client.partition.SetRepeat(status);
|
||||||
@ -258,8 +259,10 @@ handle_repeat(Client &client, Request args)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_single(Client &client, Request args)
|
handle_single(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
bool status;
|
bool status;
|
||||||
if (!args.Parse(0, status, client))
|
if (!args.Parse(0, status, r))
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
client.partition.SetSingle(status);
|
client.partition.SetSingle(status);
|
||||||
@ -269,8 +272,10 @@ handle_single(Client &client, Request args)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_consume(Client &client, Request args)
|
handle_consume(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
bool status;
|
bool status;
|
||||||
if (!args.Parse(0, status, client))
|
if (!args.Parse(0, status, r))
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
client.partition.SetConsume(status);
|
client.partition.SetConsume(status);
|
||||||
@ -280,8 +285,10 @@ handle_consume(Client &client, Request args)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_random(Client &client, Request args)
|
handle_random(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
bool status;
|
bool status;
|
||||||
if (!args.Parse(0, status, client))
|
if (!args.Parse(0, status, r))
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
client.partition.SetRandom(status);
|
client.partition.SetRandom(status);
|
||||||
@ -299,53 +306,58 @@ handle_clearerror(gcc_unused Client &client, gcc_unused Request args)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_seek(Client &client, Request args)
|
handle_seek(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
unsigned song;
|
unsigned song;
|
||||||
SongTime seek_time;
|
SongTime seek_time;
|
||||||
|
if (!args.Parse(0, song, r) || !args.Parse(1, seek_time, r))
|
||||||
if (!args.Parse(0, song, client))
|
|
||||||
return CommandResult::ERROR;
|
|
||||||
if (!args.Parse(1, seek_time, client))
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
PlaylistResult result =
|
PlaylistResult result =
|
||||||
client.partition.SeekSongPosition(song, seek_time);
|
client.partition.SeekSongPosition(song, seek_time);
|
||||||
return print_playlist_result(client, result);
|
return print_playlist_result(r, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_seekid(Client &client, Request args)
|
handle_seekid(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
unsigned id;
|
unsigned id;
|
||||||
SongTime seek_time;
|
SongTime seek_time;
|
||||||
if (!args.Parse(0, id, client))
|
if (!args.Parse(0, id, r))
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
if (!args.Parse(1, seek_time, client))
|
if (!args.Parse(1, seek_time, r))
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
PlaylistResult result =
|
PlaylistResult result =
|
||||||
client.partition.SeekSongId(id, seek_time);
|
client.partition.SeekSongId(id, seek_time);
|
||||||
return print_playlist_result(client, result);
|
return print_playlist_result(r, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_seekcur(Client &client, Request args)
|
handle_seekcur(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
const char *p = args.front();
|
const char *p = args.front();
|
||||||
bool relative = *p == '+' || *p == '-';
|
bool relative = *p == '+' || *p == '-';
|
||||||
SignedSongTime seek_time;
|
SignedSongTime seek_time;
|
||||||
if (!ParseCommandArg(client, seek_time, p))
|
if (!ParseCommandArg(r, seek_time, p))
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
PlaylistResult result =
|
PlaylistResult result =
|
||||||
client.partition.SeekCurrent(seek_time, relative);
|
client.partition.SeekCurrent(seek_time, relative);
|
||||||
return print_playlist_result(client, result);
|
return print_playlist_result(r, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_crossfade(Client &client, Request args)
|
handle_crossfade(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
unsigned xfade_time;
|
unsigned xfade_time;
|
||||||
if (!args.Parse(0, xfade_time, client))
|
if (!args.Parse(0, xfade_time, r))
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
client.player_control.SetCrossFade(xfade_time);
|
client.player_control.SetCrossFade(xfade_time);
|
||||||
@ -355,8 +367,10 @@ handle_crossfade(Client &client, Request args)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_mixrampdb(Client &client, Request args)
|
handle_mixrampdb(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
float db;
|
float db;
|
||||||
if (!args.Parse(0, db, client))
|
if (!args.Parse(0, db, r))
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
client.player_control.SetMixRampDb(db);
|
client.player_control.SetMixRampDb(db);
|
||||||
@ -366,8 +380,10 @@ handle_mixrampdb(Client &client, Request args)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_mixrampdelay(Client &client, Request args)
|
handle_mixrampdelay(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
float delay_secs;
|
float delay_secs;
|
||||||
if (!args.Parse(0, delay_secs, client))
|
if (!args.Parse(0, delay_secs, r))
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
client.player_control.SetMixRampDelay(delay_secs);
|
client.player_control.SetMixRampDelay(delay_secs);
|
||||||
@ -378,9 +394,10 @@ handle_mixrampdelay(Client &client, Request args)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_replay_gain_mode(Client &client, Request args)
|
handle_replay_gain_mode(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
if (!replay_gain_set_mode_string(args.front())) {
|
if (!replay_gain_set_mode_string(args.front())) {
|
||||||
command_error(client, ACK_ERROR_ARG,
|
r.Error(ACK_ERROR_ARG, "Unrecognized replay gain mode");
|
||||||
"Unrecognized replay gain mode");
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -391,7 +408,7 @@ handle_replay_gain_mode(Client &client, Request args)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_replay_gain_status(Client &client, gcc_unused Request args)
|
handle_replay_gain_status(Client &client, gcc_unused Request args)
|
||||||
{
|
{
|
||||||
client_printf(client, "replay_gain_mode: %s\n",
|
Response r(client);
|
||||||
replay_gain_get_mode_string());
|
r.Format("replay_gain_mode: %s\n", replay_gain_get_mode_string());
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
#include "queue/Playlist.hxx"
|
#include "queue/Playlist.hxx"
|
||||||
#include "TimePrint.hxx"
|
#include "TimePrint.hxx"
|
||||||
#include "client/Client.hxx"
|
#include "client/Client.hxx"
|
||||||
#include "protocol/Result.hxx"
|
#include "client/Response.hxx"
|
||||||
#include "ls.hxx"
|
#include "ls.hxx"
|
||||||
#include "Mapper.hxx"
|
#include "Mapper.hxx"
|
||||||
#include "fs/AllocatedPath.hxx"
|
#include "fs/AllocatedPath.hxx"
|
||||||
@ -48,30 +48,33 @@ playlist_commands_available()
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
print_spl_list(Client &client, const PlaylistVector &list)
|
print_spl_list(Response &r, const PlaylistVector &list)
|
||||||
{
|
{
|
||||||
for (const auto &i : list) {
|
for (const auto &i : list) {
|
||||||
client_printf(client, "playlist: %s\n", i.name.c_str());
|
r.Format("playlist: %s\n", i.name.c_str());
|
||||||
|
|
||||||
if (i.mtime > 0)
|
if (i.mtime > 0)
|
||||||
time_print(client, "Last-Modified", i.mtime);
|
time_print(r, "Last-Modified", i.mtime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_save(Client &client, Request args)
|
handle_save(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
Error error;
|
Error error;
|
||||||
return spl_save_playlist(args.front(), client.playlist, error)
|
return spl_save_playlist(args.front(), client.playlist, error)
|
||||||
? CommandResult::OK
|
? CommandResult::OK
|
||||||
: print_error(client, error);
|
: print_error(r, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_load(Client &client, Request args)
|
handle_load(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
RangeArg range = RangeArg::All();
|
RangeArg range = RangeArg::All();
|
||||||
if (!args.ParseOptional(1, range, client))
|
if (!args.ParseOptional(1, range, r))
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
const ScopeBulkEdit bulk_edit(client.partition);
|
const ScopeBulkEdit bulk_edit(client.partition);
|
||||||
@ -82,7 +85,7 @@ handle_load(Client &client, Request args)
|
|||||||
range.start, range.end,
|
range.start, range.end,
|
||||||
client.playlist,
|
client.playlist,
|
||||||
client.player_control, loader, error))
|
client.player_control, loader, error))
|
||||||
return print_error(client, error);
|
return print_error(r, error);
|
||||||
|
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
}
|
}
|
||||||
@ -90,97 +93,114 @@ handle_load(Client &client, Request args)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_listplaylist(Client &client, Request args)
|
handle_listplaylist(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
const char *const name = args.front();
|
const char *const name = args.front();
|
||||||
|
|
||||||
if (playlist_file_print(client, name, false))
|
if (playlist_file_print(r, client.partition, SongLoader(client),
|
||||||
|
name, false))
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
|
|
||||||
Error error;
|
Error error;
|
||||||
return spl_print(client, name, false, error)
|
return spl_print(r, client.partition, name, false, error)
|
||||||
? CommandResult::OK
|
? CommandResult::OK
|
||||||
: print_error(client, error);
|
: print_error(r, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_listplaylistinfo(Client &client, Request args)
|
handle_listplaylistinfo(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
const char *const name = args.front();
|
const char *const name = args.front();
|
||||||
|
|
||||||
if (playlist_file_print(client, name, true))
|
if (playlist_file_print(r, client.partition, SongLoader(client),
|
||||||
|
name, true))
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
|
|
||||||
Error error;
|
Error error;
|
||||||
return spl_print(client, name, true, error)
|
return spl_print(r, client.partition, name, true, error)
|
||||||
? CommandResult::OK
|
? CommandResult::OK
|
||||||
: print_error(client, error);
|
: print_error(r, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_rm(Client &client, Request args)
|
handle_rm(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
const char *const name = args.front();
|
const char *const name = args.front();
|
||||||
|
|
||||||
Error error;
|
Error error;
|
||||||
return spl_delete(name, error)
|
return spl_delete(name, error)
|
||||||
? CommandResult::OK
|
? CommandResult::OK
|
||||||
: print_error(client, error);
|
: print_error(r, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_rename(Client &client, Request args)
|
handle_rename(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
const char *const old_name = args[0];
|
const char *const old_name = args[0];
|
||||||
const char *const new_name = args[1];
|
const char *const new_name = args[1];
|
||||||
|
|
||||||
Error error;
|
Error error;
|
||||||
return spl_rename(old_name, new_name, error)
|
return spl_rename(old_name, new_name, error)
|
||||||
? CommandResult::OK
|
? CommandResult::OK
|
||||||
: print_error(client, error);
|
: print_error(r, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_playlistdelete(Client &client, Request args)
|
handle_playlistdelete(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
const char *const name = args[0];
|
const char *const name = args[0];
|
||||||
unsigned from;
|
unsigned from;
|
||||||
if (!args.Parse(1, from, client))
|
if (!args.Parse(1, from, r))
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
Error error;
|
Error error;
|
||||||
return spl_remove_index(name, from, error)
|
return spl_remove_index(name, from, error)
|
||||||
? CommandResult::OK
|
? CommandResult::OK
|
||||||
: print_error(client, error);
|
: print_error(r, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_playlistmove(Client &client, Request args)
|
handle_playlistmove(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
const char *const name = args.front();
|
const char *const name = args.front();
|
||||||
unsigned from, to;
|
unsigned from, to;
|
||||||
if (!args.Parse(1, from, client) ||
|
if (!args.Parse(1, from, r) || !args.Parse(2, to, r))
|
||||||
!args.Parse(2, to, client))
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
Error error;
|
Error error;
|
||||||
return spl_move_index(name, from, to, error)
|
return spl_move_index(name, from, to, error)
|
||||||
? CommandResult::OK
|
? CommandResult::OK
|
||||||
: print_error(client, error);
|
: print_error(r, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_playlistclear(Client &client, Request args)
|
handle_playlistclear(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
const char *const name = args.front();
|
const char *const name = args.front();
|
||||||
|
|
||||||
Error error;
|
Error error;
|
||||||
return spl_clear(name, error)
|
return spl_clear(name, error)
|
||||||
? CommandResult::OK
|
? CommandResult::OK
|
||||||
: print_error(client, error);
|
: print_error(r, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_playlistadd(Client &client, Request args)
|
handle_playlistadd(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
const char *const playlist = args[0];
|
const char *const playlist = args[0];
|
||||||
const char *const uri = args[1];
|
const char *const uri = args[1];
|
||||||
|
|
||||||
@ -193,7 +213,7 @@ handle_playlistadd(Client &client, Request args)
|
|||||||
#ifdef ENABLE_DATABASE
|
#ifdef ENABLE_DATABASE
|
||||||
const Database *db = client.GetDatabase(error);
|
const Database *db = client.GetDatabase(error);
|
||||||
if (db == nullptr)
|
if (db == nullptr)
|
||||||
return print_error(client, error);
|
return print_error(r, error);
|
||||||
|
|
||||||
success = search_add_to_playlist(*db, *client.GetStorage(),
|
success = search_add_to_playlist(*db, *client.GetStorage(),
|
||||||
uri, playlist, nullptr,
|
uri, playlist, nullptr,
|
||||||
@ -204,22 +224,23 @@ handle_playlistadd(Client &client, Request args)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!success && !error.IsDefined()) {
|
if (!success && !error.IsDefined()) {
|
||||||
command_error(client, ACK_ERROR_NO_EXIST,
|
r.Error(ACK_ERROR_NO_EXIST, "directory or file not found");
|
||||||
"directory or file not found");
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
return success ? CommandResult::OK : print_error(client, error);
|
return success ? CommandResult::OK : print_error(r, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_listplaylists(Client &client, gcc_unused Request args)
|
handle_listplaylists(Client &client, gcc_unused Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
Error error;
|
Error error;
|
||||||
const auto list = ListPlaylistFiles(error);
|
const auto list = ListPlaylistFiles(error);
|
||||||
if (list.empty() && error.IsDefined())
|
if (list.empty() && error.IsDefined())
|
||||||
return print_error(client, error);
|
return print_error(r, error);
|
||||||
|
|
||||||
print_spl_list(client, list);
|
print_spl_list(r, list);
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
}
|
}
|
||||||
|
@ -28,9 +28,9 @@
|
|||||||
#include "queue/Playlist.hxx"
|
#include "queue/Playlist.hxx"
|
||||||
#include "PlaylistPrint.hxx"
|
#include "PlaylistPrint.hxx"
|
||||||
#include "client/Client.hxx"
|
#include "client/Client.hxx"
|
||||||
|
#include "client/Response.hxx"
|
||||||
#include "Partition.hxx"
|
#include "Partition.hxx"
|
||||||
#include "BulkEdit.hxx"
|
#include "BulkEdit.hxx"
|
||||||
#include "protocol/Result.hxx"
|
|
||||||
#include "ls.hxx"
|
#include "ls.hxx"
|
||||||
#include "util/ConstBuffer.hxx"
|
#include "util/ConstBuffer.hxx"
|
||||||
#include "util/UriUtil.hxx"
|
#include "util/UriUtil.hxx"
|
||||||
@ -56,6 +56,8 @@ translate_uri(const char *uri)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_add(Client &client, Request args)
|
handle_add(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
const char *uri = args.front();
|
const char *uri = args.front();
|
||||||
if (memcmp(uri, "/", 2) == 0)
|
if (memcmp(uri, "/", 2) == 0)
|
||||||
/* this URI is malformed, but some clients are buggy
|
/* this URI is malformed, but some clients are buggy
|
||||||
@ -72,7 +74,7 @@ handle_add(Client &client, Request args)
|
|||||||
Error error;
|
Error error;
|
||||||
unsigned id = client.partition.AppendURI(loader, uri, error);
|
unsigned id = client.partition.AppendURI(loader, uri, error);
|
||||||
if (id == 0)
|
if (id == 0)
|
||||||
return print_error(client, error);
|
return print_error(r, error);
|
||||||
|
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
}
|
}
|
||||||
@ -84,9 +86,9 @@ handle_add(Client &client, Request args)
|
|||||||
Error error;
|
Error error;
|
||||||
return AddFromDatabase(client.partition, selection, error)
|
return AddFromDatabase(client.partition, selection, error)
|
||||||
? CommandResult::OK
|
? CommandResult::OK
|
||||||
: print_error(client, error);
|
: print_error(r, error);
|
||||||
#else
|
#else
|
||||||
command_error(client, ACK_ERROR_NO_EXIST, "No database");
|
r.Error(ACK_ERROR_NO_EXIST, "No database");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -94,29 +96,31 @@ handle_add(Client &client, Request args)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_addid(Client &client, Request args)
|
handle_addid(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
const char *const uri = translate_uri(args.front());
|
const char *const uri = translate_uri(args.front());
|
||||||
|
|
||||||
const SongLoader loader(client);
|
const SongLoader loader(client);
|
||||||
Error error;
|
Error error;
|
||||||
unsigned added_id = client.partition.AppendURI(loader, uri, error);
|
unsigned added_id = client.partition.AppendURI(loader, uri, error);
|
||||||
if (added_id == 0)
|
if (added_id == 0)
|
||||||
return print_error(client, error);
|
return print_error(r, error);
|
||||||
|
|
||||||
if (args.size == 2) {
|
if (args.size == 2) {
|
||||||
unsigned to;
|
unsigned to;
|
||||||
if (!args.Parse(1, to, client))
|
if (!args.Parse(1, to, r))
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
PlaylistResult result = client.partition.MoveId(added_id, to);
|
PlaylistResult result = client.partition.MoveId(added_id, to);
|
||||||
if (result != PlaylistResult::SUCCESS) {
|
if (result != PlaylistResult::SUCCESS) {
|
||||||
CommandResult ret =
|
CommandResult ret =
|
||||||
print_playlist_result(client, result);
|
print_playlist_result(r, result);
|
||||||
client.partition.DeleteId(added_id);
|
client.partition.DeleteId(added_id);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
client_printf(client, "Id: %u\n", added_id);
|
r.Format("Id: %u\n", added_id);
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,13 +158,15 @@ parse_time_range(const char *p, SongTime &start_r, SongTime &end_r)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_rangeid(Client &client, Request args)
|
handle_rangeid(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
unsigned id;
|
unsigned id;
|
||||||
if (!args.Parse(0, id, client))
|
if (!args.Parse(0, id, r))
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
SongTime start, end;
|
SongTime start, end;
|
||||||
if (!parse_time_range(args[1], start, end)) {
|
if (!parse_time_range(args[1], start, end)) {
|
||||||
command_error(client, ACK_ERROR_ARG, "Bad range");
|
r.Error(ACK_ERROR_ARG, "Bad range");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,7 +174,7 @@ handle_rangeid(Client &client, Request args)
|
|||||||
if (!client.partition.playlist.SetSongIdRange(client.partition.pc,
|
if (!client.partition.playlist.SetSongIdRange(client.partition.pc,
|
||||||
id, start, end,
|
id, start, end,
|
||||||
error))
|
error))
|
||||||
return print_error(client, error);
|
return print_error(r, error);
|
||||||
|
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
}
|
}
|
||||||
@ -176,37 +182,44 @@ handle_rangeid(Client &client, Request args)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_delete(Client &client, Request args)
|
handle_delete(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
RangeArg range;
|
RangeArg range;
|
||||||
if (!args.Parse(0, range, client))
|
if (!args.Parse(0, range, r))
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
auto result = client.partition.DeleteRange(range.start, range.end);
|
auto result = client.partition.DeleteRange(range.start, range.end);
|
||||||
return print_playlist_result(client, result);
|
return print_playlist_result(r, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_deleteid(Client &client, Request args)
|
handle_deleteid(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
unsigned id;
|
unsigned id;
|
||||||
if (!args.Parse(0, id, client))
|
if (!args.Parse(0, id, r))
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
PlaylistResult result = client.partition.DeleteId(id);
|
PlaylistResult result = client.partition.DeleteId(id);
|
||||||
return print_playlist_result(client, result);
|
return print_playlist_result(r, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_playlist(Client &client, gcc_unused Request args)
|
handle_playlist(Client &client, gcc_unused Request args)
|
||||||
{
|
{
|
||||||
playlist_print_uris(client, client.playlist);
|
Response r(client);
|
||||||
|
playlist_print_uris(r, client.partition, client.playlist);
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_shuffle(gcc_unused Client &client, Request args)
|
handle_shuffle(gcc_unused Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
RangeArg range = RangeArg::All();
|
RangeArg range = RangeArg::All();
|
||||||
if (!args.ParseOptional(0, range, client))
|
if (!args.ParseOptional(0, range, r))
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
client.partition.Shuffle(range.start, range.end);
|
client.partition.Shuffle(range.start, range.end);
|
||||||
@ -223,37 +236,42 @@ handle_clear(gcc_unused Client &client, gcc_unused Request args)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_plchanges(Client &client, Request args)
|
handle_plchanges(Client &client, Request args)
|
||||||
{
|
{
|
||||||
uint32_t version;
|
Response r(client);
|
||||||
|
|
||||||
if (!check_uint32(client, &version, args.front()))
|
uint32_t version;
|
||||||
|
if (!ParseCommandArg32(r, version, args.front()))
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
playlist_print_changes_info(client, client.playlist, version);
|
playlist_print_changes_info(r, client.partition,
|
||||||
|
client.playlist, version);
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_plchangesposid(Client &client, Request args)
|
handle_plchangesposid(Client &client, Request args)
|
||||||
{
|
{
|
||||||
uint32_t version;
|
Response r(client);
|
||||||
|
|
||||||
if (!check_uint32(client, &version, args.front()))
|
uint32_t version;
|
||||||
|
if (!ParseCommandArg32(r, version, args.front()))
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
playlist_print_changes_position(client, client.playlist, version);
|
playlist_print_changes_position(r, client.playlist, version);
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_playlistinfo(Client &client, Request args)
|
handle_playlistinfo(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
RangeArg range = RangeArg::All();
|
RangeArg range = RangeArg::All();
|
||||||
if (!args.ParseOptional(0, range, client))
|
if (!args.ParseOptional(0, range, r))
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
if (!playlist_print_info(client, client.playlist,
|
if (!playlist_print_info(r, client.partition, client.playlist,
|
||||||
range.start, range.end))
|
range.start, range.end))
|
||||||
return print_playlist_result(client,
|
return print_playlist_result(r,
|
||||||
PlaylistResult::BAD_RANGE);
|
PlaylistResult::BAD_RANGE);
|
||||||
|
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
@ -262,17 +280,19 @@ handle_playlistinfo(Client &client, Request args)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_playlistid(Client &client, Request args)
|
handle_playlistid(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
if (!args.IsEmpty()) {
|
if (!args.IsEmpty()) {
|
||||||
unsigned id;
|
unsigned id;
|
||||||
if (!args.Parse(0, id, client))
|
if (!args.Parse(0, id, r))
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
bool ret = playlist_print_id(client, client.playlist, id);
|
bool ret = playlist_print_id(r, client.partition,
|
||||||
|
client.playlist, id);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
return print_playlist_result(client,
|
return print_playlist_result(r, PlaylistResult::NO_SUCH_SONG);
|
||||||
PlaylistResult::NO_SUCH_SONG);
|
|
||||||
} else {
|
} else {
|
||||||
playlist_print_info(client, client.playlist,
|
playlist_print_info(r, client.partition, client.playlist,
|
||||||
0, std::numeric_limits<unsigned>::max());
|
0, std::numeric_limits<unsigned>::max());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,13 +303,15 @@ static CommandResult
|
|||||||
handle_playlist_match(Client &client, Request args,
|
handle_playlist_match(Client &client, Request args,
|
||||||
bool fold_case)
|
bool fold_case)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
SongFilter filter;
|
SongFilter filter;
|
||||||
if (!filter.Parse(args, fold_case)) {
|
if (!filter.Parse(args, fold_case)) {
|
||||||
command_error(client, ACK_ERROR_ARG, "incorrect arguments");
|
r.Error(ACK_ERROR_ARG, "incorrect arguments");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
playlist_print_find(client, client.playlist, filter);
|
playlist_print_find(r, client.partition, client.playlist, filter);
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,13 +330,15 @@ handle_playlistsearch(Client &client, Request args)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_prio(Client &client, Request args)
|
handle_prio(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
unsigned priority;
|
unsigned priority;
|
||||||
if (!args.ParseShift(0, priority, client, 0xff))
|
if (!args.ParseShift(0, priority, r, 0xff))
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
for (const char *i : args) {
|
for (const char *i : args) {
|
||||||
RangeArg range;
|
RangeArg range;
|
||||||
if (!ParseCommandArg(client, range, i))
|
if (!ParseCommandArg(r, range, i))
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
PlaylistResult result =
|
PlaylistResult result =
|
||||||
@ -322,7 +346,7 @@ handle_prio(Client &client, Request args)
|
|||||||
range.end,
|
range.end,
|
||||||
priority);
|
priority);
|
||||||
if (result != PlaylistResult::SUCCESS)
|
if (result != PlaylistResult::SUCCESS)
|
||||||
return print_playlist_result(client, result);
|
return print_playlist_result(r, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
@ -331,19 +355,21 @@ handle_prio(Client &client, Request args)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_prioid(Client &client, Request args)
|
handle_prioid(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
unsigned priority;
|
unsigned priority;
|
||||||
if (!args.ParseShift(0, priority, client, 0xff))
|
if (!args.ParseShift(0, priority, r, 0xff))
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
for (const char *i : args) {
|
for (const char *i : args) {
|
||||||
unsigned song_id;
|
unsigned song_id;
|
||||||
if (!ParseCommandArg(client, song_id, i))
|
if (!ParseCommandArg(r, song_id, i))
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
PlaylistResult result =
|
PlaylistResult result =
|
||||||
client.partition.SetPriorityId(song_id, priority);
|
client.partition.SetPriorityId(song_id, priority);
|
||||||
if (result != PlaylistResult::SUCCESS)
|
if (result != PlaylistResult::SUCCESS)
|
||||||
return print_playlist_result(client, result);
|
return print_playlist_result(r, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
@ -352,52 +378,56 @@ handle_prioid(Client &client, Request args)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_move(Client &client, Request args)
|
handle_move(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
RangeArg range;
|
RangeArg range;
|
||||||
int to;
|
int to;
|
||||||
|
|
||||||
if (!args.Parse(0, range, client) ||
|
if (!args.Parse(0, range, r) || !args.Parse(1, to, r))
|
||||||
!args.Parse(1, to, client))
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
PlaylistResult result =
|
PlaylistResult result =
|
||||||
client.partition.MoveRange(range.start, range.end, to);
|
client.partition.MoveRange(range.start, range.end, to);
|
||||||
return print_playlist_result(client, result);
|
return print_playlist_result(r, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_moveid(Client &client, Request args)
|
handle_moveid(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
unsigned id;
|
unsigned id;
|
||||||
int to;
|
int to;
|
||||||
if (!args.Parse(0, id, client) ||
|
if (!args.Parse(0, id, r) || !args.Parse(1, to, r))
|
||||||
!args.Parse(1, to, client))
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
PlaylistResult result = client.partition.MoveId(id, to);
|
PlaylistResult result = client.partition.MoveId(id, to);
|
||||||
return print_playlist_result(client, result);
|
return print_playlist_result(r, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_swap(Client &client, Request args)
|
handle_swap(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
unsigned song1, song2;
|
unsigned song1, song2;
|
||||||
if (!args.Parse(0, song1, client) ||
|
if (!args.Parse(0, song1, r) || !args.Parse(1, song2, r))
|
||||||
!args.Parse(1, song2, client))
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
PlaylistResult result =
|
PlaylistResult result =
|
||||||
client.partition.SwapPositions(song1, song2);
|
client.partition.SwapPositions(song1, song2);
|
||||||
return print_playlist_result(client, result);
|
return print_playlist_result(r, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_swapid(Client &client, Request args)
|
handle_swapid(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
unsigned id1, id2;
|
unsigned id1, id2;
|
||||||
if (!args.Parse(0, id1, client) ||
|
if (!args.Parse(0, id1, r) || !args.Parse(1, id2, r))
|
||||||
!args.Parse(1, id2, client))
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
PlaylistResult result = client.partition.SwapIds(id1, id2);
|
PlaylistResult result = client.partition.SwapIds(id1, id2);
|
||||||
return print_playlist_result(client, result);
|
return print_playlist_result(r, result);
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
class Client;
|
class Response;
|
||||||
|
|
||||||
class Request : public ConstBuffer<const char *> {
|
class Request : public ConstBuffer<const char *> {
|
||||||
typedef ConstBuffer<const char *> Base;
|
typedef ConstBuffer<const char *> Base;
|
||||||
@ -45,26 +45,26 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename... Args>
|
template<typename T, typename... Args>
|
||||||
bool Parse(unsigned idx, T &value_r, Client &client,
|
bool Parse(unsigned idx, T &value_r, Response &r,
|
||||||
Args&&... args) {
|
Args&&... args) {
|
||||||
assert(idx < size);
|
assert(idx < size);
|
||||||
|
|
||||||
return ParseCommandArg(client, value_r, data[idx],
|
return ParseCommandArg(r, value_r, data[idx],
|
||||||
std::forward<Args>(args)...);
|
std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename... Args>
|
template<typename T, typename... Args>
|
||||||
bool ParseOptional(unsigned idx, T &value_r, Client &client,
|
bool ParseOptional(unsigned idx, T &value_r, Response &r,
|
||||||
Args&&... args) {
|
Args&&... args) {
|
||||||
return idx >= size ||
|
return idx >= size ||
|
||||||
Parse(idx, value_r, client,
|
Parse(idx, value_r, r,
|
||||||
std::forward<Args>(args)...);
|
std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename... Args>
|
template<typename T, typename... Args>
|
||||||
bool ParseShift(unsigned idx, T &value_r, Client &client,
|
bool ParseShift(unsigned idx, T &value_r, Response &r,
|
||||||
Args&&... args) {
|
Args&&... args) {
|
||||||
bool success = Parse(idx, value_r, client,
|
bool success = Parse(idx, value_r, r,
|
||||||
std::forward<Args>(args)...);
|
std::forward<Args>(args)...);
|
||||||
shift();
|
shift();
|
||||||
return success;
|
return success;
|
||||||
|
@ -27,8 +27,8 @@
|
|||||||
#include "sticker/StickerPrint.hxx"
|
#include "sticker/StickerPrint.hxx"
|
||||||
#include "sticker/StickerDatabase.hxx"
|
#include "sticker/StickerDatabase.hxx"
|
||||||
#include "CommandError.hxx"
|
#include "CommandError.hxx"
|
||||||
#include "protocol/Result.hxx"
|
|
||||||
#include "client/Client.hxx"
|
#include "client/Client.hxx"
|
||||||
|
#include "client/Response.hxx"
|
||||||
#include "Partition.hxx"
|
#include "Partition.hxx"
|
||||||
#include "Instance.hxx"
|
#include "Instance.hxx"
|
||||||
#include "util/Error.hxx"
|
#include "util/Error.hxx"
|
||||||
@ -37,7 +37,8 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
struct sticker_song_find_data {
|
struct sticker_song_find_data {
|
||||||
Client &client;
|
Response &r;
|
||||||
|
Partition &partition;
|
||||||
const char *name;
|
const char *name;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -48,17 +49,17 @@ sticker_song_find_print_cb(const LightSong &song, const char *value,
|
|||||||
struct sticker_song_find_data *data =
|
struct sticker_song_find_data *data =
|
||||||
(struct sticker_song_find_data *)user_data;
|
(struct sticker_song_find_data *)user_data;
|
||||||
|
|
||||||
song_print_uri(data->client, song);
|
song_print_uri(data->r, data->partition, song);
|
||||||
sticker_print_value(data->client, data->name, value);
|
sticker_print_value(data->r, data->name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static CommandResult
|
static CommandResult
|
||||||
handle_sticker_song(Client &client, Request args)
|
handle_sticker_song(Response &r, Partition &partition, Request args)
|
||||||
{
|
{
|
||||||
Error error;
|
Error error;
|
||||||
const Database *db = client.GetDatabase(error);
|
const Database *db = partition.GetDatabase(error);
|
||||||
if (db == nullptr)
|
if (db == nullptr)
|
||||||
return print_error(client, error);
|
return print_error(r, error);
|
||||||
|
|
||||||
const char *const cmd = args.front();
|
const char *const cmd = args.front();
|
||||||
|
|
||||||
@ -66,53 +67,52 @@ handle_sticker_song(Client &client, Request args)
|
|||||||
if (args.size == 4 && strcmp(cmd, "get") == 0) {
|
if (args.size == 4 && strcmp(cmd, "get") == 0) {
|
||||||
const LightSong *song = db->GetSong(args[2], error);
|
const LightSong *song = db->GetSong(args[2], error);
|
||||||
if (song == nullptr)
|
if (song == nullptr)
|
||||||
return print_error(client, error);
|
return print_error(r, error);
|
||||||
|
|
||||||
const auto value = sticker_song_get_value(*song, args[3],
|
const auto value = sticker_song_get_value(*song, args[3],
|
||||||
error);
|
error);
|
||||||
db->ReturnSong(song);
|
db->ReturnSong(song);
|
||||||
if (value.empty()) {
|
if (value.empty()) {
|
||||||
if (error.IsDefined())
|
if (error.IsDefined())
|
||||||
return print_error(client, error);
|
return print_error(r, error);
|
||||||
|
|
||||||
command_error(client, ACK_ERROR_NO_EXIST,
|
r.Error(ACK_ERROR_NO_EXIST, "no such sticker");
|
||||||
"no such sticker");
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
sticker_print_value(client, args[3], value.c_str());
|
sticker_print_value(r, args[3], value.c_str());
|
||||||
|
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
/* list song song_id */
|
/* list song song_id */
|
||||||
} else if (args.size == 3 && strcmp(cmd, "list") == 0) {
|
} else if (args.size == 3 && strcmp(cmd, "list") == 0) {
|
||||||
const LightSong *song = db->GetSong(args[2], error);
|
const LightSong *song = db->GetSong(args[2], error);
|
||||||
if (song == nullptr)
|
if (song == nullptr)
|
||||||
return print_error(client, error);
|
return print_error(r, error);
|
||||||
|
|
||||||
Sticker *sticker = sticker_song_get(*song, error);
|
Sticker *sticker = sticker_song_get(*song, error);
|
||||||
db->ReturnSong(song);
|
db->ReturnSong(song);
|
||||||
if (sticker) {
|
if (sticker) {
|
||||||
sticker_print(client, *sticker);
|
sticker_print(r, *sticker);
|
||||||
sticker_free(sticker);
|
sticker_free(sticker);
|
||||||
} else if (error.IsDefined())
|
} else if (error.IsDefined())
|
||||||
return print_error(client, error);
|
return print_error(r, error);
|
||||||
|
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
/* set song song_id id key */
|
/* set song song_id id key */
|
||||||
} else if (args.size == 5 && strcmp(cmd, "set") == 0) {
|
} else if (args.size == 5 && strcmp(cmd, "set") == 0) {
|
||||||
const LightSong *song = db->GetSong(args[2], error);
|
const LightSong *song = db->GetSong(args[2], error);
|
||||||
if (song == nullptr)
|
if (song == nullptr)
|
||||||
return print_error(client, error);
|
return print_error(r, error);
|
||||||
|
|
||||||
bool ret = sticker_song_set_value(*song, args[3], args[4],
|
bool ret = sticker_song_set_value(*song, args[3], args[4],
|
||||||
error);
|
error);
|
||||||
db->ReturnSong(song);
|
db->ReturnSong(song);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
if (error.IsDefined())
|
if (error.IsDefined())
|
||||||
return print_error(client, error);
|
return print_error(r, error);
|
||||||
|
|
||||||
command_error(client, ACK_ERROR_SYSTEM,
|
r.Error(ACK_ERROR_SYSTEM,
|
||||||
"failed to set sticker value");
|
"failed to set sticker value");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,7 +122,7 @@ handle_sticker_song(Client &client, Request args)
|
|||||||
strcmp(cmd, "delete") == 0) {
|
strcmp(cmd, "delete") == 0) {
|
||||||
const LightSong *song = db->GetSong(args[2], error);
|
const LightSong *song = db->GetSong(args[2], error);
|
||||||
if (song == nullptr)
|
if (song == nullptr)
|
||||||
return print_error(client, error);
|
return print_error(r, error);
|
||||||
|
|
||||||
bool ret = args.size == 3
|
bool ret = args.size == 3
|
||||||
? sticker_song_delete(*song, error)
|
? sticker_song_delete(*song, error)
|
||||||
@ -130,10 +130,9 @@ handle_sticker_song(Client &client, Request args)
|
|||||||
db->ReturnSong(song);
|
db->ReturnSong(song);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
if (error.IsDefined())
|
if (error.IsDefined())
|
||||||
return print_error(client, error);
|
return print_error(r, error);
|
||||||
|
|
||||||
command_error(client, ACK_ERROR_SYSTEM,
|
r.Error(ACK_ERROR_SYSTEM, "no such sticker");
|
||||||
"no such sticker");
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,14 +160,14 @@ handle_sticker_song(Client &client, Request args)
|
|||||||
else if (strcmp(op_s, ">") == 0)
|
else if (strcmp(op_s, ">") == 0)
|
||||||
op = StickerOperator::GREATER_THAN;
|
op = StickerOperator::GREATER_THAN;
|
||||||
else {
|
else {
|
||||||
command_error(client, ACK_ERROR_ARG,
|
r.Error(ACK_ERROR_ARG, "bad operator");
|
||||||
"bad operator");
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sticker_song_find_data data = {
|
struct sticker_song_find_data data = {
|
||||||
client,
|
r,
|
||||||
|
partition,
|
||||||
args[3],
|
args[3],
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -177,16 +176,16 @@ handle_sticker_song(Client &client, Request args)
|
|||||||
sticker_song_find_print_cb, &data,
|
sticker_song_find_print_cb, &data,
|
||||||
error)) {
|
error)) {
|
||||||
if (error.IsDefined())
|
if (error.IsDefined())
|
||||||
return print_error(client, error);
|
return print_error(r, error);
|
||||||
|
|
||||||
command_error(client, ACK_ERROR_SYSTEM,
|
r.Error(ACK_ERROR_SYSTEM,
|
||||||
"failed to set search sticker database");
|
"failed to set search sticker database");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
} else {
|
} else {
|
||||||
command_error(client, ACK_ERROR_ARG, "bad request");
|
r.Error(ACK_ERROR_ARG, "bad request");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -194,19 +193,19 @@ handle_sticker_song(Client &client, Request args)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_sticker(Client &client, Request args)
|
handle_sticker(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
assert(args.size >= 3);
|
assert(args.size >= 3);
|
||||||
|
|
||||||
if (!sticker_enabled()) {
|
if (!sticker_enabled()) {
|
||||||
command_error(client, ACK_ERROR_UNKNOWN,
|
r.Error(ACK_ERROR_UNKNOWN, "sticker database is disabled");
|
||||||
"sticker database is disabled");
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(args[1], "song") == 0)
|
if (strcmp(args[1], "song") == 0)
|
||||||
return handle_sticker_song(client, args);
|
return handle_sticker_song(r, client.partition, args);
|
||||||
else {
|
else {
|
||||||
command_error(client, ACK_ERROR_ARG,
|
r.Error(ACK_ERROR_ARG, "unknown sticker domain");
|
||||||
"unknown sticker domain");
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,12 +23,12 @@
|
|||||||
#include "StorageCommands.hxx"
|
#include "StorageCommands.hxx"
|
||||||
#include "Request.hxx"
|
#include "Request.hxx"
|
||||||
#include "CommandError.hxx"
|
#include "CommandError.hxx"
|
||||||
#include "protocol/Result.hxx"
|
|
||||||
#include "util/UriUtil.hxx"
|
#include "util/UriUtil.hxx"
|
||||||
#include "util/Error.hxx"
|
#include "util/Error.hxx"
|
||||||
#include "util/ConstBuffer.hxx"
|
#include "util/ConstBuffer.hxx"
|
||||||
#include "fs/Traits.hxx"
|
#include "fs/Traits.hxx"
|
||||||
#include "client/Client.hxx"
|
#include "client/Client.hxx"
|
||||||
|
#include "client/Response.hxx"
|
||||||
#include "Partition.hxx"
|
#include "Partition.hxx"
|
||||||
#include "Instance.hxx"
|
#include "Instance.hxx"
|
||||||
#include "storage/Registry.hxx"
|
#include "storage/Registry.hxx"
|
||||||
@ -57,7 +57,7 @@ skip_path(const char *name_utf8)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
handle_listfiles_storage(Client &client, StorageDirectoryReader &reader,
|
handle_listfiles_storage(Response &r, StorageDirectoryReader &reader,
|
||||||
Error &error)
|
Error &error)
|
||||||
{
|
{
|
||||||
const char *name_utf8;
|
const char *name_utf8;
|
||||||
@ -75,19 +75,19 @@ handle_listfiles_storage(Client &client, StorageDirectoryReader &reader,
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
case StorageFileInfo::Type::REGULAR:
|
case StorageFileInfo::Type::REGULAR:
|
||||||
client_printf(client, "file: %s\n"
|
r.Format("file: %s\n"
|
||||||
"size: %" PRIu64 "\n",
|
"size: %" PRIu64 "\n",
|
||||||
name_utf8,
|
name_utf8,
|
||||||
info.size);
|
info.size);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case StorageFileInfo::Type::DIRECTORY:
|
case StorageFileInfo::Type::DIRECTORY:
|
||||||
client_printf(client, "directory: %s\n", name_utf8);
|
r.Format("directory: %s\n", name_utf8);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info.mtime != 0)
|
if (info.mtime != 0)
|
||||||
time_print(client, "Last-Modified", info.mtime);
|
time_print(r, "Last-Modified", info.mtime);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -98,52 +98,51 @@ handle_listfiles_storage(Client &client, StorageDirectoryReader &reader,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
handle_listfiles_storage(Client &client, Storage &storage, const char *uri,
|
handle_listfiles_storage(Response &r, Storage &storage, const char *uri,
|
||||||
Error &error)
|
Error &error)
|
||||||
{
|
{
|
||||||
auto reader = storage.OpenDirectory(uri, error);
|
auto reader = storage.OpenDirectory(uri, error);
|
||||||
if (reader == nullptr)
|
if (reader == nullptr)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
bool success = handle_listfiles_storage(client, *reader, error);
|
bool success = handle_listfiles_storage(r, *reader, error);
|
||||||
delete reader;
|
delete reader;
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_listfiles_storage(Client &client, Storage &storage, const char *uri)
|
handle_listfiles_storage(Response &r, Storage &storage, const char *uri)
|
||||||
{
|
{
|
||||||
Error error;
|
Error error;
|
||||||
if (!handle_listfiles_storage(client, storage, uri, error))
|
if (!handle_listfiles_storage(r, storage, uri, error))
|
||||||
return print_error(client, error);
|
return print_error(r, error);
|
||||||
|
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_listfiles_storage(Client &client, const char *uri)
|
handle_listfiles_storage(Response &r, const char *uri)
|
||||||
{
|
{
|
||||||
Error error;
|
Error error;
|
||||||
Storage *storage = CreateStorageURI(io_thread_get(), uri, error);
|
Storage *storage = CreateStorageURI(io_thread_get(), uri, error);
|
||||||
if (storage == nullptr) {
|
if (storage == nullptr) {
|
||||||
if (error.IsDefined())
|
if (error.IsDefined())
|
||||||
return print_error(client, error);
|
return print_error(r, error);
|
||||||
|
|
||||||
command_error(client, ACK_ERROR_ARG,
|
r.Error(ACK_ERROR_ARG, "Unrecognized storage URI");
|
||||||
"Unrecognized storage URI");
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool success = handle_listfiles_storage(client, *storage, "", error);
|
bool success = handle_listfiles_storage(r, *storage, "", error);
|
||||||
delete storage;
|
delete storage;
|
||||||
if (!success)
|
if (!success)
|
||||||
return print_error(client, error);
|
return print_error(r, error);
|
||||||
|
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
print_storage_uri(Client &client, const Storage &storage)
|
print_storage_uri(Client &client, Response &r, const Storage &storage)
|
||||||
{
|
{
|
||||||
std::string uri = storage.MapUTF8("");
|
std::string uri = storage.MapUTF8("");
|
||||||
if (uri.empty())
|
if (uri.empty())
|
||||||
@ -165,24 +164,26 @@ print_storage_uri(Client &client, const Storage &storage)
|
|||||||
uri = std::move(allocated);
|
uri = std::move(allocated);
|
||||||
}
|
}
|
||||||
|
|
||||||
client_printf(client, "storage: %s\n", uri.c_str());
|
r.Format("storage: %s\n", uri.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_listmounts(Client &client, gcc_unused Request args)
|
handle_listmounts(Client &client, gcc_unused Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
Storage *_composite = client.partition.instance.storage;
|
Storage *_composite = client.partition.instance.storage;
|
||||||
if (_composite == nullptr) {
|
if (_composite == nullptr) {
|
||||||
command_error(client, ACK_ERROR_NO_EXIST, "No database");
|
r.Error(ACK_ERROR_NO_EXIST, "No database");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
CompositeStorage &composite = *(CompositeStorage *)_composite;
|
CompositeStorage &composite = *(CompositeStorage *)_composite;
|
||||||
|
|
||||||
const auto visitor = [&client](const char *mount_uri,
|
const auto visitor = [&client, &r](const char *mount_uri,
|
||||||
const Storage &storage){
|
const Storage &storage){
|
||||||
client_printf(client, "mount: %s\n", mount_uri);
|
r.Format("mount: %s\n", mount_uri);
|
||||||
print_storage_uri(client, storage);
|
print_storage_uri(client, r, storage);
|
||||||
};
|
};
|
||||||
|
|
||||||
composite.VisitMounts(visitor);
|
composite.VisitMounts(visitor);
|
||||||
@ -193,9 +194,11 @@ handle_listmounts(Client &client, gcc_unused Request args)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_mount(Client &client, Request args)
|
handle_mount(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
Storage *_composite = client.partition.instance.storage;
|
Storage *_composite = client.partition.instance.storage;
|
||||||
if (_composite == nullptr) {
|
if (_composite == nullptr) {
|
||||||
command_error(client, ACK_ERROR_NO_EXIST, "No database");
|
r.Error(ACK_ERROR_NO_EXIST, "No database");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,7 +208,7 @@ handle_mount(Client &client, Request args)
|
|||||||
const char *const remote_uri = args[1];
|
const char *const remote_uri = args[1];
|
||||||
|
|
||||||
if (*local_uri == 0) {
|
if (*local_uri == 0) {
|
||||||
command_error(client, ACK_ERROR_ARG, "Bad mount point");
|
r.Error(ACK_ERROR_ARG, "Bad mount point");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,7 +218,7 @@ handle_mount(Client &client, Request args)
|
|||||||
UpdateQueue::Erase() really gets called for every
|
UpdateQueue::Erase() really gets called for every
|
||||||
unmount, and no Directory disappears recursively
|
unmount, and no Directory disappears recursively
|
||||||
during database update */
|
during database update */
|
||||||
command_error(client, ACK_ERROR_ARG, "Bad mount point");
|
r.Error(ACK_ERROR_ARG, "Bad mount point");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,10 +227,9 @@ handle_mount(Client &client, Request args)
|
|||||||
error);
|
error);
|
||||||
if (storage == nullptr) {
|
if (storage == nullptr) {
|
||||||
if (error.IsDefined())
|
if (error.IsDefined())
|
||||||
return print_error(client, error);
|
return print_error(r, error);
|
||||||
|
|
||||||
command_error(client, ACK_ERROR_ARG,
|
r.Error(ACK_ERROR_ARG, "Unrecognized storage URI");
|
||||||
"Unrecognized storage URI");
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,7 +243,7 @@ handle_mount(Client &client, Request args)
|
|||||||
|
|
||||||
if (!db.Mount(local_uri, remote_uri, error)) {
|
if (!db.Mount(local_uri, remote_uri, error)) {
|
||||||
composite.Unmount(local_uri);
|
composite.Unmount(local_uri);
|
||||||
return print_error(client, error);
|
return print_error(r, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: call Instance::OnDatabaseModified()?
|
// TODO: call Instance::OnDatabaseModified()?
|
||||||
@ -256,9 +258,11 @@ handle_mount(Client &client, Request args)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_unmount(Client &client, Request args)
|
handle_unmount(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
Storage *_composite = client.partition.instance.storage;
|
Storage *_composite = client.partition.instance.storage;
|
||||||
if (_composite == nullptr) {
|
if (_composite == nullptr) {
|
||||||
command_error(client, ACK_ERROR_NO_EXIST, "No database");
|
r.Error(ACK_ERROR_NO_EXIST, "No database");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,7 +271,7 @@ handle_unmount(Client &client, Request args)
|
|||||||
const char *const local_uri = args.front();
|
const char *const local_uri = args.front();
|
||||||
|
|
||||||
if (*local_uri == 0) {
|
if (*local_uri == 0) {
|
||||||
command_error(client, ACK_ERROR_ARG, "Bad mount point");
|
r.Error(ACK_ERROR_ARG, "Bad mount point");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,7 +293,7 @@ handle_unmount(Client &client, Request args)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!composite.Unmount(local_uri)) {
|
if (!composite.Unmount(local_uri)) {
|
||||||
command_error(client, ACK_ERROR_ARG, "Not a mount point");
|
r.Error(ACK_ERROR_ARG, "Not a mount point");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,12 +25,13 @@
|
|||||||
class Client;
|
class Client;
|
||||||
class Storage;
|
class Storage;
|
||||||
class Request;
|
class Request;
|
||||||
|
class Response;
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_listfiles_storage(Client &client, Storage &storage, const char *uri);
|
handle_listfiles_storage(Response &r, Storage &storage, const char *uri);
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_listfiles_storage(Client &client, const char *uri);
|
handle_listfiles_storage(Response &r, const char *uri);
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_listmounts(Client &client, Request args);
|
handle_listmounts(Client &client, Request args);
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
#include "Request.hxx"
|
#include "Request.hxx"
|
||||||
#include "CommandError.hxx"
|
#include "CommandError.hxx"
|
||||||
#include "client/Client.hxx"
|
#include "client/Client.hxx"
|
||||||
#include "protocol/Result.hxx"
|
#include "client/Response.hxx"
|
||||||
#include "tag/Tag.hxx"
|
#include "tag/Tag.hxx"
|
||||||
#include "Partition.hxx"
|
#include "Partition.hxx"
|
||||||
#include "util/ConstBuffer.hxx"
|
#include "util/ConstBuffer.hxx"
|
||||||
@ -30,15 +30,16 @@
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_addtagid(Client &client, Request args)
|
handle_addtagid(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
unsigned song_id;
|
unsigned song_id;
|
||||||
if (!args.Parse(0, song_id, client))
|
if (!args.Parse(0, song_id, r))
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
const char *const tag_name = args[1];
|
const char *const tag_name = args[1];
|
||||||
const TagType tag_type = tag_name_parse_i(tag_name);
|
const TagType tag_type = tag_name_parse_i(tag_name);
|
||||||
if (tag_type == TAG_NUM_OF_ITEM_TYPES) {
|
if (tag_type == TAG_NUM_OF_ITEM_TYPES) {
|
||||||
command_error(client, ACK_ERROR_ARG,
|
r.FormatError(ACK_ERROR_ARG, "Unknown tag type: %s", tag_name);
|
||||||
"Unknown tag type: %s", tag_name);
|
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +48,7 @@ handle_addtagid(Client &client, Request args)
|
|||||||
Error error;
|
Error error;
|
||||||
if (!client.partition.playlist.AddSongIdTag(song_id, tag_type, value,
|
if (!client.partition.playlist.AddSongIdTag(song_id, tag_type, value,
|
||||||
error))
|
error))
|
||||||
return print_error(client, error);
|
return print_error(r, error);
|
||||||
|
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
}
|
}
|
||||||
@ -55,8 +56,10 @@ handle_addtagid(Client &client, Request args)
|
|||||||
CommandResult
|
CommandResult
|
||||||
handle_cleartagid(Client &client, Request args)
|
handle_cleartagid(Client &client, Request args)
|
||||||
{
|
{
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
unsigned song_id;
|
unsigned song_id;
|
||||||
if (!args.Parse(0, song_id, client))
|
if (!args.Parse(0, song_id, r))
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
|
|
||||||
TagType tag_type = TAG_NUM_OF_ITEM_TYPES;
|
TagType tag_type = TAG_NUM_OF_ITEM_TYPES;
|
||||||
@ -64,7 +67,7 @@ handle_cleartagid(Client &client, Request args)
|
|||||||
const char *const tag_name = args[1];
|
const char *const tag_name = args[1];
|
||||||
tag_type = tag_name_parse_i(tag_name);
|
tag_type = tag_name_parse_i(tag_name);
|
||||||
if (tag_type == TAG_NUM_OF_ITEM_TYPES) {
|
if (tag_type == TAG_NUM_OF_ITEM_TYPES) {
|
||||||
command_error(client, ACK_ERROR_ARG,
|
r.FormatError(ACK_ERROR_ARG,
|
||||||
"Unknown tag type: %s", tag_name);
|
"Unknown tag type: %s", tag_name);
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
}
|
}
|
||||||
@ -73,7 +76,7 @@ handle_cleartagid(Client &client, Request args)
|
|||||||
Error error;
|
Error error;
|
||||||
if (!client.partition.playlist.ClearSongIdTag(song_id, tag_type,
|
if (!client.partition.playlist.ClearSongIdTag(song_id, tag_type,
|
||||||
error))
|
error))
|
||||||
return print_error(client, error);
|
return print_error(r, error);
|
||||||
|
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,8 @@
|
|||||||
#include "Count.hxx"
|
#include "Count.hxx"
|
||||||
#include "Selection.hxx"
|
#include "Selection.hxx"
|
||||||
#include "Interface.hxx"
|
#include "Interface.hxx"
|
||||||
#include "client/Client.hxx"
|
#include "Partition.hxx"
|
||||||
|
#include "client/Response.hxx"
|
||||||
#include "LightSong.hxx"
|
#include "LightSong.hxx"
|
||||||
#include "tag/Tag.hxx"
|
#include "tag/Tag.hxx"
|
||||||
|
|
||||||
@ -40,26 +41,24 @@ class TagCountMap : public std::map<std::string, SearchStats> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
PrintSearchStats(Client &client, const SearchStats &stats)
|
PrintSearchStats(Response &r, const SearchStats &stats)
|
||||||
{
|
{
|
||||||
unsigned total_duration_s =
|
unsigned total_duration_s =
|
||||||
std::chrono::duration_cast<std::chrono::seconds>(stats.total_duration).count();
|
std::chrono::duration_cast<std::chrono::seconds>(stats.total_duration).count();
|
||||||
|
|
||||||
client_printf(client,
|
r.Format("songs: %u\n"
|
||||||
"songs: %u\n"
|
"playtime: %u\n",
|
||||||
"playtime: %u\n",
|
stats.n_songs, total_duration_s);
|
||||||
stats.n_songs, total_duration_s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
Print(Client &client, TagType group, const TagCountMap &m)
|
Print(Response &r, TagType group, const TagCountMap &m)
|
||||||
{
|
{
|
||||||
assert(unsigned(group) < TAG_NUM_OF_ITEM_TYPES);
|
assert(unsigned(group) < TAG_NUM_OF_ITEM_TYPES);
|
||||||
|
|
||||||
for (const auto &i : m) {
|
for (const auto &i : m) {
|
||||||
client_printf(client, "%s: %s\n",
|
r.Format("%s: %s\n", tag_item_names[group], i.first.c_str());
|
||||||
tag_item_names[group], i.first.c_str());
|
PrintSearchStats(r, i.second);
|
||||||
PrintSearchStats(client, i.second);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,12 +108,12 @@ GroupCountVisitor(TagCountMap &map, TagType group, const LightSong &song)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
PrintSongCount(Client &client, const char *name,
|
PrintSongCount(Response &r, const Partition &partition, const char *name,
|
||||||
const SongFilter *filter,
|
const SongFilter *filter,
|
||||||
TagType group,
|
TagType group,
|
||||||
Error &error)
|
Error &error)
|
||||||
{
|
{
|
||||||
const Database *db = client.GetDatabase(error);
|
const Database *db = partition.GetDatabase(error);
|
||||||
if (db == nullptr)
|
if (db == nullptr)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -131,7 +130,7 @@ PrintSongCount(Client &client, const char *name,
|
|||||||
if (!db->Visit(selection, f, error))
|
if (!db->Visit(selection, f, error))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
PrintSearchStats(client, stats);
|
PrintSearchStats(r, stats);
|
||||||
} else {
|
} else {
|
||||||
/* group by the specified tag: store counts in a
|
/* group by the specified tag: store counts in a
|
||||||
std::map */
|
std::map */
|
||||||
@ -144,7 +143,7 @@ PrintSongCount(Client &client, const char *name,
|
|||||||
if (!db->Visit(selection, f, error))
|
if (!db->Visit(selection, f, error))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Print(client, group, map);
|
Print(r, group, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -25,13 +25,14 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
enum TagType : uint8_t;
|
enum TagType : uint8_t;
|
||||||
class Client;
|
struct Partition;
|
||||||
|
class Response;
|
||||||
class SongFilter;
|
class SongFilter;
|
||||||
class Error;
|
class Error;
|
||||||
|
|
||||||
gcc_nonnull(2)
|
gcc_nonnull(3)
|
||||||
bool
|
bool
|
||||||
PrintSongCount(Client &client, const char *name,
|
PrintSongCount(Response &r, const Partition &partition, const char *name,
|
||||||
const SongFilter *filter,
|
const SongFilter *filter,
|
||||||
TagType group,
|
TagType group,
|
||||||
Error &error);
|
Error &error);
|
||||||
|
@ -24,6 +24,8 @@
|
|||||||
#include "SongPrint.hxx"
|
#include "SongPrint.hxx"
|
||||||
#include "TimePrint.hxx"
|
#include "TimePrint.hxx"
|
||||||
#include "client/Client.hxx"
|
#include "client/Client.hxx"
|
||||||
|
#include "client/Response.hxx"
|
||||||
|
#include "Partition.hxx"
|
||||||
#include "tag/Tag.hxx"
|
#include "tag/Tag.hxx"
|
||||||
#include "LightSong.hxx"
|
#include "LightSong.hxx"
|
||||||
#include "LightDirectory.hxx"
|
#include "LightDirectory.hxx"
|
||||||
@ -42,116 +44,119 @@ ApplyBaseFlag(const char *uri, bool base)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
PrintDirectoryURI(Client &client, bool base, const LightDirectory &directory)
|
PrintDirectoryURI(Response &r, bool base, const LightDirectory &directory)
|
||||||
{
|
{
|
||||||
client_printf(client, "directory: %s\n",
|
r.Format("directory: %s\n",
|
||||||
ApplyBaseFlag(directory.GetPath(), base));
|
ApplyBaseFlag(directory.GetPath(), base));
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
PrintDirectoryBrief(Client &client, bool base, const LightDirectory &directory)
|
PrintDirectoryBrief(Response &r, bool base, const LightDirectory &directory)
|
||||||
{
|
{
|
||||||
if (!directory.IsRoot())
|
if (!directory.IsRoot())
|
||||||
PrintDirectoryURI(client, base, directory);
|
PrintDirectoryURI(r, base, directory);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
PrintDirectoryFull(Client &client, bool base, const LightDirectory &directory)
|
PrintDirectoryFull(Response &r, bool base, const LightDirectory &directory)
|
||||||
{
|
{
|
||||||
if (!directory.IsRoot()) {
|
if (!directory.IsRoot()) {
|
||||||
PrintDirectoryURI(client, base, directory);
|
PrintDirectoryURI(r, base, directory);
|
||||||
|
|
||||||
if (directory.mtime > 0)
|
if (directory.mtime > 0)
|
||||||
time_print(client, "Last-Modified", directory.mtime);
|
time_print(r, "Last-Modified", directory.mtime);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
print_playlist_in_directory(Client &client, bool base,
|
print_playlist_in_directory(Response &r, bool base,
|
||||||
const char *directory,
|
const char *directory,
|
||||||
const char *name_utf8)
|
const char *name_utf8)
|
||||||
{
|
{
|
||||||
if (base || directory == nullptr)
|
if (base || directory == nullptr)
|
||||||
client_printf(client, "playlist: %s\n",
|
r.Format("playlist: %s\n",
|
||||||
ApplyBaseFlag(name_utf8, base));
|
ApplyBaseFlag(name_utf8, base));
|
||||||
else
|
else
|
||||||
client_printf(client, "playlist: %s/%s\n",
|
r.Format("playlist: %s/%s\n",
|
||||||
directory, name_utf8);
|
directory, name_utf8);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
print_playlist_in_directory(Client &client, bool base,
|
print_playlist_in_directory(Response &r, bool base,
|
||||||
const LightDirectory *directory,
|
const LightDirectory *directory,
|
||||||
const char *name_utf8)
|
const char *name_utf8)
|
||||||
{
|
{
|
||||||
if (base || directory == nullptr || directory->IsRoot())
|
if (base || directory == nullptr || directory->IsRoot())
|
||||||
client_printf(client, "playlist: %s\n", name_utf8);
|
r.Format("playlist: %s\n", name_utf8);
|
||||||
else
|
else
|
||||||
client_printf(client, "playlist: %s/%s\n",
|
r.Format("playlist: %s/%s\n",
|
||||||
directory->GetPath(), name_utf8);
|
directory->GetPath(), name_utf8);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
PrintSongBrief(Client &client, bool base, const LightSong &song)
|
PrintSongBrief(Response &r, Partition &partition,
|
||||||
|
bool base, const LightSong &song)
|
||||||
{
|
{
|
||||||
song_print_uri(client, song, base);
|
song_print_uri(r, partition, song, base);
|
||||||
|
|
||||||
if (song.tag->has_playlist)
|
if (song.tag->has_playlist)
|
||||||
/* this song file has an embedded CUE sheet */
|
/* this song file has an embedded CUE sheet */
|
||||||
print_playlist_in_directory(client, base,
|
print_playlist_in_directory(r, base,
|
||||||
song.directory, song.uri);
|
song.directory, song.uri);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
PrintSongFull(Client &client, bool base, const LightSong &song)
|
PrintSongFull(Response &r, Partition &partition,
|
||||||
|
bool base, const LightSong &song)
|
||||||
{
|
{
|
||||||
song_print_info(client, song, base);
|
song_print_info(r, partition, song, base);
|
||||||
|
|
||||||
if (song.tag->has_playlist)
|
if (song.tag->has_playlist)
|
||||||
/* this song file has an embedded CUE sheet */
|
/* this song file has an embedded CUE sheet */
|
||||||
print_playlist_in_directory(client, base,
|
print_playlist_in_directory(r, base,
|
||||||
song.directory, song.uri);
|
song.directory, song.uri);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
PrintPlaylistBrief(Client &client, bool base,
|
PrintPlaylistBrief(Response &r, bool base,
|
||||||
const PlaylistInfo &playlist,
|
const PlaylistInfo &playlist,
|
||||||
const LightDirectory &directory)
|
const LightDirectory &directory)
|
||||||
{
|
{
|
||||||
print_playlist_in_directory(client, base,
|
print_playlist_in_directory(r, base,
|
||||||
&directory, playlist.name.c_str());
|
&directory, playlist.name.c_str());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
PrintPlaylistFull(Client &client, bool base,
|
PrintPlaylistFull(Response &r, bool base,
|
||||||
const PlaylistInfo &playlist,
|
const PlaylistInfo &playlist,
|
||||||
const LightDirectory &directory)
|
const LightDirectory &directory)
|
||||||
{
|
{
|
||||||
print_playlist_in_directory(client, base,
|
print_playlist_in_directory(r, base,
|
||||||
&directory, playlist.name.c_str());
|
&directory, playlist.name.c_str());
|
||||||
|
|
||||||
if (playlist.mtime > 0)
|
if (playlist.mtime > 0)
|
||||||
time_print(client, "Last-Modified", playlist.mtime);
|
time_print(r, "Last-Modified", playlist.mtime);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
db_selection_print(Client &client, const DatabaseSelection &selection,
|
db_selection_print(Response &r, Partition &partition,
|
||||||
|
const DatabaseSelection &selection,
|
||||||
bool full, bool base,
|
bool full, bool base,
|
||||||
unsigned window_start, unsigned window_end,
|
unsigned window_start, unsigned window_end,
|
||||||
Error &error)
|
Error &error)
|
||||||
{
|
{
|
||||||
const Database *db = client.GetDatabase(error);
|
const Database *db = partition.GetDatabase(error);
|
||||||
if (db == nullptr)
|
if (db == nullptr)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -160,13 +165,13 @@ db_selection_print(Client &client, const DatabaseSelection &selection,
|
|||||||
using namespace std::placeholders;
|
using namespace std::placeholders;
|
||||||
const auto d = selection.filter == nullptr
|
const auto d = selection.filter == nullptr
|
||||||
? std::bind(full ? PrintDirectoryFull : PrintDirectoryBrief,
|
? std::bind(full ? PrintDirectoryFull : PrintDirectoryBrief,
|
||||||
std::ref(client), base, _1)
|
std::ref(r), base, _1)
|
||||||
: VisitDirectory();
|
: VisitDirectory();
|
||||||
VisitSong s = std::bind(full ? PrintSongFull : PrintSongBrief,
|
VisitSong s = std::bind(full ? PrintSongFull : PrintSongBrief,
|
||||||
std::ref(client), base, _1);
|
std::ref(r), std::ref(partition), base, _1);
|
||||||
const auto p = selection.filter == nullptr
|
const auto p = selection.filter == nullptr
|
||||||
? std::bind(full ? PrintPlaylistFull : PrintPlaylistBrief,
|
? std::bind(full ? PrintPlaylistFull : PrintPlaylistBrief,
|
||||||
std::ref(client), base, _1, _2)
|
std::ref(r), base, _1, _2)
|
||||||
: VisitPlaylist();
|
: VisitPlaylist();
|
||||||
|
|
||||||
if (window_start > 0 ||
|
if (window_start > 0 ||
|
||||||
@ -182,45 +187,47 @@ db_selection_print(Client &client, const DatabaseSelection &selection,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
db_selection_print(Client &client, const DatabaseSelection &selection,
|
db_selection_print(Response &r, Partition &partition,
|
||||||
|
const DatabaseSelection &selection,
|
||||||
bool full, bool base,
|
bool full, bool base,
|
||||||
Error &error)
|
Error &error)
|
||||||
{
|
{
|
||||||
return db_selection_print(client, selection, full, base,
|
return db_selection_print(r, partition, selection, full, base,
|
||||||
0, std::numeric_limits<int>::max(),
|
0, std::numeric_limits<int>::max(),
|
||||||
error);
|
error);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
PrintSongURIVisitor(Client &client, const LightSong &song)
|
PrintSongURIVisitor(Response &r, Partition &partition, const LightSong &song)
|
||||||
{
|
{
|
||||||
song_print_uri(client, song);
|
song_print_uri(r, partition, song);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
PrintUniqueTag(Client &client, TagType tag_type,
|
PrintUniqueTag(Response &r, TagType tag_type,
|
||||||
const Tag &tag)
|
const Tag &tag)
|
||||||
{
|
{
|
||||||
const char *value = tag.GetValue(tag_type);
|
const char *value = tag.GetValue(tag_type);
|
||||||
assert(value != nullptr);
|
assert(value != nullptr);
|
||||||
client_printf(client, "%s: %s\n", tag_item_names[tag_type], value);
|
r.Format("%s: %s\n", tag_item_names[tag_type], value);
|
||||||
|
|
||||||
for (const auto &item : tag)
|
for (const auto &item : tag)
|
||||||
if (item.type != tag_type)
|
if (item.type != tag_type)
|
||||||
client_printf(client, "%s: %s\n",
|
r.Format("%s: %s\n",
|
||||||
tag_item_names[item.type], item.value);
|
tag_item_names[item.type], item.value);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
PrintUniqueTags(Client &client, unsigned type, uint32_t group_mask,
|
PrintUniqueTags(Response &r, Partition &partition,
|
||||||
|
unsigned type, uint32_t group_mask,
|
||||||
const SongFilter *filter,
|
const SongFilter *filter,
|
||||||
Error &error)
|
Error &error)
|
||||||
{
|
{
|
||||||
const Database *db = client.GetDatabase(error);
|
const Database *db = partition.GetDatabase(error);
|
||||||
if (db == nullptr)
|
if (db == nullptr)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -229,13 +236,13 @@ PrintUniqueTags(Client &client, unsigned type, uint32_t group_mask,
|
|||||||
if (type == LOCATE_TAG_FILE_TYPE) {
|
if (type == LOCATE_TAG_FILE_TYPE) {
|
||||||
using namespace std::placeholders;
|
using namespace std::placeholders;
|
||||||
const auto f = std::bind(PrintSongURIVisitor,
|
const auto f = std::bind(PrintSongURIVisitor,
|
||||||
std::ref(client), _1);
|
std::ref(r), std::ref(partition), _1);
|
||||||
return db->Visit(selection, f, error);
|
return db->Visit(selection, f, error);
|
||||||
} else {
|
} else {
|
||||||
assert(type < TAG_NUM_OF_ITEM_TYPES);
|
assert(type < TAG_NUM_OF_ITEM_TYPES);
|
||||||
|
|
||||||
using namespace std::placeholders;
|
using namespace std::placeholders;
|
||||||
const auto f = std::bind(PrintUniqueTag, std::ref(client),
|
const auto f = std::bind(PrintUniqueTag, std::ref(r),
|
||||||
(TagType)type, _1);
|
(TagType)type, _1);
|
||||||
return db->VisitUniqueTags(selection, (TagType)type,
|
return db->VisitUniqueTags(selection, (TagType)type,
|
||||||
group_mask,
|
group_mask,
|
||||||
|
@ -26,7 +26,9 @@
|
|||||||
|
|
||||||
class SongFilter;
|
class SongFilter;
|
||||||
struct DatabaseSelection;
|
struct DatabaseSelection;
|
||||||
|
struct Partition;
|
||||||
class Client;
|
class Client;
|
||||||
|
class Response;
|
||||||
class Error;
|
class Error;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -34,17 +36,20 @@ class Error;
|
|||||||
* @param base print only base name of songs/directories?
|
* @param base print only base name of songs/directories?
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
db_selection_print(Client &client, const DatabaseSelection &selection,
|
db_selection_print(Response &r, Partition &partition,
|
||||||
|
const DatabaseSelection &selection,
|
||||||
bool full, bool base, Error &error);
|
bool full, bool base, Error &error);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
db_selection_print(Client &client, const DatabaseSelection &selection,
|
db_selection_print(Response &r, Partition &partition,
|
||||||
|
const DatabaseSelection &selection,
|
||||||
bool full, bool base,
|
bool full, bool base,
|
||||||
unsigned window_start, unsigned window_end,
|
unsigned window_start, unsigned window_end,
|
||||||
Error &error);
|
Error &error);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
PrintUniqueTags(Client &client, unsigned type, uint32_t group_mask,
|
PrintUniqueTags(Response &r, Partition &partition,
|
||||||
|
unsigned type, uint32_t group_mask,
|
||||||
const SongFilter *filter,
|
const SongFilter *filter,
|
||||||
Error &error);
|
Error &error);
|
||||||
|
|
||||||
|
@ -21,35 +21,35 @@
|
|||||||
#include "DecoderPrint.hxx"
|
#include "DecoderPrint.hxx"
|
||||||
#include "DecoderList.hxx"
|
#include "DecoderList.hxx"
|
||||||
#include "DecoderPlugin.hxx"
|
#include "DecoderPlugin.hxx"
|
||||||
#include "client/Client.hxx"
|
#include "client/Response.hxx"
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
static void
|
static void
|
||||||
decoder_plugin_print(Client &client,
|
decoder_plugin_print(Response &r,
|
||||||
const DecoderPlugin &plugin)
|
const DecoderPlugin &plugin)
|
||||||
{
|
{
|
||||||
const char *const*p;
|
const char *const*p;
|
||||||
|
|
||||||
assert(plugin.name != nullptr);
|
assert(plugin.name != nullptr);
|
||||||
|
|
||||||
client_printf(client, "plugin: %s\n", plugin.name);
|
r.Format("plugin: %s\n", plugin.name);
|
||||||
|
|
||||||
if (plugin.suffixes != nullptr)
|
if (plugin.suffixes != nullptr)
|
||||||
for (p = plugin.suffixes; *p != nullptr; ++p)
|
for (p = plugin.suffixes; *p != nullptr; ++p)
|
||||||
client_printf(client, "suffix: %s\n", *p);
|
r.Format("suffix: %s\n", *p);
|
||||||
|
|
||||||
if (plugin.mime_types != nullptr)
|
if (plugin.mime_types != nullptr)
|
||||||
for (p = plugin.mime_types; *p != nullptr; ++p)
|
for (p = plugin.mime_types; *p != nullptr; ++p)
|
||||||
client_printf(client, "mime_type: %s\n", *p);
|
r.Format("mime_type: %s\n", *p);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
decoder_list_print(Client &client)
|
decoder_list_print(Response &r)
|
||||||
{
|
{
|
||||||
using namespace std::placeholders;
|
using namespace std::placeholders;
|
||||||
const auto f = std::bind(decoder_plugin_print, std::ref(client), _1);
|
const auto f = std::bind(decoder_plugin_print, std::ref(r), _1);
|
||||||
decoder_plugins_for_each_enabled(f);
|
decoder_plugins_for_each_enabled(f);
|
||||||
}
|
}
|
||||||
|
@ -20,9 +20,9 @@
|
|||||||
#ifndef MPD_DECODER_PRINT_HXX
|
#ifndef MPD_DECODER_PRINT_HXX
|
||||||
#define MPD_DECODER_PRINT_HXX
|
#define MPD_DECODER_PRINT_HXX
|
||||||
|
|
||||||
class Client;
|
class Response;
|
||||||
|
|
||||||
void
|
void
|
||||||
decoder_list_print(Client &client);
|
decoder_list_print(Response &r);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -19,9 +19,9 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "ls.hxx"
|
#include "ls.hxx"
|
||||||
|
#include "client/Response.hxx"
|
||||||
#include "util/StringUtil.hxx"
|
#include "util/StringUtil.hxx"
|
||||||
#include "util/UriUtil.hxx"
|
#include "util/UriUtil.hxx"
|
||||||
#include "client/Client.hxx"
|
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
@ -78,12 +78,13 @@ void print_supported_uri_schemes_to_fp(FILE *fp)
|
|||||||
fprintf(fp,"\n");
|
fprintf(fp,"\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_supported_uri_schemes(Client &client)
|
void
|
||||||
|
print_supported_uri_schemes(Response &r)
|
||||||
{
|
{
|
||||||
const char *const *prefixes = remoteUrlPrefixes;
|
const char *const *prefixes = remoteUrlPrefixes;
|
||||||
|
|
||||||
while (*prefixes) {
|
while (*prefixes) {
|
||||||
client_printf(client, "handler: %s\n", *prefixes);
|
r.Format("handler: %s\n", *prefixes);
|
||||||
prefixes++;
|
prefixes++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
class Client;
|
class Response;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether the scheme of the specified URI is supported by MPD.
|
* Checks whether the scheme of the specified URI is supported by MPD.
|
||||||
@ -38,7 +38,7 @@ bool uri_supported_scheme(const char *url);
|
|||||||
* Send a list of supported URI schemes to the client. This is the
|
* Send a list of supported URI schemes to the client. This is the
|
||||||
* response to the "urlhandlers" command.
|
* response to the "urlhandlers" command.
|
||||||
*/
|
*/
|
||||||
void print_supported_uri_schemes(Client &client);
|
void print_supported_uri_schemes(Response &r);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a list of supported URI schemes to a file pointer.
|
* Send a list of supported URI schemes to a file pointer.
|
||||||
|
@ -26,18 +26,17 @@
|
|||||||
#include "OutputPrint.hxx"
|
#include "OutputPrint.hxx"
|
||||||
#include "MultipleOutputs.hxx"
|
#include "MultipleOutputs.hxx"
|
||||||
#include "Internal.hxx"
|
#include "Internal.hxx"
|
||||||
#include "client/Client.hxx"
|
#include "client/Response.hxx"
|
||||||
|
|
||||||
void
|
void
|
||||||
printAudioDevices(Client &client, const MultipleOutputs &outputs)
|
printAudioDevices(Response &r, const MultipleOutputs &outputs)
|
||||||
{
|
{
|
||||||
for (unsigned i = 0, n = outputs.Size(); i != n; ++i) {
|
for (unsigned i = 0, n = outputs.Size(); i != n; ++i) {
|
||||||
const AudioOutput &ao = outputs.Get(i);
|
const AudioOutput &ao = outputs.Get(i);
|
||||||
|
|
||||||
client_printf(client,
|
r.Format("outputid: %i\n"
|
||||||
"outputid: %i\n"
|
"outputname: %s\n"
|
||||||
"outputname: %s\n"
|
"outputenabled: %i\n",
|
||||||
"outputenabled: %i\n",
|
i, ao.name, ao.enabled);
|
||||||
i, ao.name, ao.enabled);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,10 +25,10 @@
|
|||||||
#ifndef MPD_OUTPUT_PRINT_HXX
|
#ifndef MPD_OUTPUT_PRINT_HXX
|
||||||
#define MPD_OUTPUT_PRINT_HXX
|
#define MPD_OUTPUT_PRINT_HXX
|
||||||
|
|
||||||
class Client;
|
class Response;
|
||||||
class MultipleOutputs;
|
class MultipleOutputs;
|
||||||
|
|
||||||
void
|
void
|
||||||
printAudioDevices(Client &client, const MultipleOutputs &outputs);
|
printAudioDevices(Response &r, const MultipleOutputs &outputs);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -28,48 +28,51 @@
|
|||||||
#include "fs/Traits.hxx"
|
#include "fs/Traits.hxx"
|
||||||
#include "thread/Mutex.hxx"
|
#include "thread/Mutex.hxx"
|
||||||
#include "thread/Cond.hxx"
|
#include "thread/Cond.hxx"
|
||||||
#include "client/Client.hxx"
|
#include "Partition.hxx"
|
||||||
|
#include "Instance.hxx"
|
||||||
|
|
||||||
static void
|
static void
|
||||||
playlist_provider_print(Client &client, const char *uri,
|
playlist_provider_print(Response &r, Partition &partition,
|
||||||
|
const SongLoader &loader,
|
||||||
|
const char *uri,
|
||||||
SongEnumerator &e, bool detail)
|
SongEnumerator &e, bool detail)
|
||||||
{
|
{
|
||||||
const std::string base_uri = uri != nullptr
|
const std::string base_uri = uri != nullptr
|
||||||
? PathTraitsUTF8::GetParent(uri)
|
? PathTraitsUTF8::GetParent(uri)
|
||||||
: std::string(".");
|
: std::string(".");
|
||||||
|
|
||||||
const SongLoader loader(client);
|
|
||||||
|
|
||||||
DetachedSong *song;
|
DetachedSong *song;
|
||||||
while ((song = e.NextSong()) != nullptr) {
|
while ((song = e.NextSong()) != nullptr) {
|
||||||
if (playlist_check_translate_song(*song, base_uri.c_str(),
|
if (playlist_check_translate_song(*song, base_uri.c_str(),
|
||||||
loader) &&
|
loader) &&
|
||||||
detail)
|
detail)
|
||||||
song_print_info(client, *song);
|
song_print_info(r, partition, *song);
|
||||||
else
|
else
|
||||||
/* fallback if no detail was requested or no
|
/* fallback if no detail was requested or no
|
||||||
detail was available */
|
detail was available */
|
||||||
song_print_uri(client, *song);
|
song_print_uri(r, partition, *song);
|
||||||
|
|
||||||
delete song;
|
delete song;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
playlist_file_print(Client &client, const char *uri, bool detail)
|
playlist_file_print(Response &r, Partition &partition,
|
||||||
|
const SongLoader &loader,
|
||||||
|
const char *uri, bool detail)
|
||||||
{
|
{
|
||||||
Mutex mutex;
|
Mutex mutex;
|
||||||
Cond cond;
|
Cond cond;
|
||||||
|
|
||||||
SongEnumerator *playlist = playlist_open_any(uri,
|
SongEnumerator *playlist = playlist_open_any(uri,
|
||||||
#ifdef ENABLE_DATABASE
|
#ifdef ENABLE_DATABASE
|
||||||
client.GetStorage(),
|
partition.instance.storage,
|
||||||
#endif
|
#endif
|
||||||
mutex, cond);
|
mutex, cond);
|
||||||
if (playlist == nullptr)
|
if (playlist == nullptr)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
playlist_provider_print(client, uri, *playlist, detail);
|
playlist_provider_print(r, partition, loader, uri, *playlist, detail);
|
||||||
delete playlist;
|
delete playlist;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -20,17 +20,20 @@
|
|||||||
#ifndef MPD_PLAYLIST__PRINT_HXX
|
#ifndef MPD_PLAYLIST__PRINT_HXX
|
||||||
#define MPD_PLAYLIST__PRINT_HXX
|
#define MPD_PLAYLIST__PRINT_HXX
|
||||||
|
|
||||||
class Client;
|
class Response;
|
||||||
|
class SongLoader;
|
||||||
|
struct Partition;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send the playlist file to the client.
|
* Send the playlist file to the client.
|
||||||
*
|
*
|
||||||
* @param client the client which requested the playlist
|
|
||||||
* @param uri the URI of the playlist file in UTF-8 encoding
|
* @param uri the URI of the playlist file in UTF-8 encoding
|
||||||
* @param detail true if all details should be printed
|
* @param detail true if all details should be printed
|
||||||
* @return true on success, false if the playlist does not exist
|
* @return true on success, false if the playlist does not exist
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
playlist_file_print(Client &client, const char *uri, bool detail);
|
playlist_file_print(Response &r, Partition &partition,
|
||||||
|
const SongLoader &loader,
|
||||||
|
const char *uri, bool detail);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -19,27 +19,26 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "ArgParser.hxx"
|
#include "ArgParser.hxx"
|
||||||
#include "Result.hxx"
|
|
||||||
#include "Chrono.hxx"
|
#include "Chrono.hxx"
|
||||||
|
#include "client/Response.hxx"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
bool
|
bool
|
||||||
check_uint32(Client &client, uint32_t *dst, const char *s)
|
ParseCommandArg32(Response &r, uint32_t &value_r, const char *s)
|
||||||
{
|
{
|
||||||
char *test;
|
char *test;
|
||||||
|
|
||||||
*dst = strtoul(s, &test, 10);
|
value_r = strtoul(s, &test, 10);
|
||||||
if (test == s || *test != '\0') {
|
if (test == s || *test != '\0') {
|
||||||
command_error(client, ACK_ERROR_ARG,
|
r.FormatError(ACK_ERROR_ARG, "Integer expected: %s", s);
|
||||||
"Integer expected: %s", s);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ParseCommandArg(Client &client, int &value_r, const char *s,
|
ParseCommandArg(Response &r, int &value_r, const char *s,
|
||||||
int min_value, int max_value)
|
int min_value, int max_value)
|
||||||
{
|
{
|
||||||
char *test;
|
char *test;
|
||||||
@ -47,14 +46,12 @@ ParseCommandArg(Client &client, int &value_r, const char *s,
|
|||||||
|
|
||||||
value = strtol(s, &test, 10);
|
value = strtol(s, &test, 10);
|
||||||
if (test == s || *test != '\0') {
|
if (test == s || *test != '\0') {
|
||||||
command_error(client, ACK_ERROR_ARG,
|
r.FormatError(ACK_ERROR_ARG, "Integer expected: %s", s);
|
||||||
"Integer expected: %s", s);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value < min_value || value > max_value) {
|
if (value < min_value || value > max_value) {
|
||||||
command_error(client, ACK_ERROR_ARG,
|
r.FormatError(ACK_ERROR_ARG, "Number too large: %s", s);
|
||||||
"Number too large: %s", s);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,22 +60,22 @@ ParseCommandArg(Client &client, int &value_r, const char *s,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ParseCommandArg(Client &client, int &value_r, const char *s)
|
ParseCommandArg(Response &r, int &value_r, const char *s)
|
||||||
{
|
{
|
||||||
return ParseCommandArg(client, value_r, s,
|
return ParseCommandArg(r, value_r, s,
|
||||||
std::numeric_limits<int>::min(),
|
std::numeric_limits<int>::min(),
|
||||||
std::numeric_limits<int>::max());
|
std::numeric_limits<int>::max());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ParseCommandArg(Client &client, RangeArg &value_r, const char *s)
|
ParseCommandArg(Response &r, RangeArg &value_r, const char *s)
|
||||||
{
|
{
|
||||||
char *test, *test2;
|
char *test, *test2;
|
||||||
long value;
|
long value;
|
||||||
|
|
||||||
value = strtol(s, &test, 10);
|
value = strtol(s, &test, 10);
|
||||||
if (test == s || (*test != '\0' && *test != ':')) {
|
if (test == s || (*test != '\0' && *test != ':')) {
|
||||||
command_error(client, ACK_ERROR_ARG,
|
r.FormatError(ACK_ERROR_ARG,
|
||||||
"Integer or range expected: %s", s);
|
"Integer or range expected: %s", s);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -92,14 +89,12 @@ ParseCommandArg(Client &client, RangeArg &value_r, const char *s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (value < 0) {
|
if (value < 0) {
|
||||||
command_error(client, ACK_ERROR_ARG,
|
r.FormatError(ACK_ERROR_ARG, "Number is negative: %s", s);
|
||||||
"Number is negative: %s", s);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unsigned(value) > std::numeric_limits<unsigned>::max()) {
|
if (unsigned(value) > std::numeric_limits<unsigned>::max()) {
|
||||||
command_error(client, ACK_ERROR_ARG,
|
r.FormatError(ACK_ERROR_ARG, "Number too large: %s", s);
|
||||||
"Number too large: %s", s);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,7 +103,7 @@ ParseCommandArg(Client &client, RangeArg &value_r, const char *s)
|
|||||||
if (*test == ':') {
|
if (*test == ':') {
|
||||||
value = strtol(++test, &test2, 10);
|
value = strtol(++test, &test2, 10);
|
||||||
if (*test2 != '\0') {
|
if (*test2 != '\0') {
|
||||||
command_error(client, ACK_ERROR_ARG,
|
r.FormatError(ACK_ERROR_ARG,
|
||||||
"Integer or range expected: %s", s);
|
"Integer or range expected: %s", s);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -117,13 +112,13 @@ ParseCommandArg(Client &client, RangeArg &value_r, const char *s)
|
|||||||
value = std::numeric_limits<int>::max();
|
value = std::numeric_limits<int>::max();
|
||||||
|
|
||||||
if (value < 0) {
|
if (value < 0) {
|
||||||
command_error(client, ACK_ERROR_ARG,
|
r.FormatError(ACK_ERROR_ARG,
|
||||||
"Number is negative: %s", s);
|
"Number is negative: %s", s);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unsigned(value) > std::numeric_limits<unsigned>::max()) {
|
if (unsigned(value) > std::numeric_limits<unsigned>::max()) {
|
||||||
command_error(client, ACK_ERROR_ARG,
|
r.FormatError(ACK_ERROR_ARG,
|
||||||
"Number too large: %s", s);
|
"Number too large: %s", s);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -137,7 +132,7 @@ ParseCommandArg(Client &client, RangeArg &value_r, const char *s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ParseCommandArg(Client &client, unsigned &value_r, const char *s,
|
ParseCommandArg(Response &r, unsigned &value_r, const char *s,
|
||||||
unsigned max_value)
|
unsigned max_value)
|
||||||
{
|
{
|
||||||
unsigned long value;
|
unsigned long value;
|
||||||
@ -145,13 +140,12 @@ ParseCommandArg(Client &client, unsigned &value_r, const char *s,
|
|||||||
|
|
||||||
value = strtoul(s, &endptr, 10);
|
value = strtoul(s, &endptr, 10);
|
||||||
if (endptr == s || *endptr != 0) {
|
if (endptr == s || *endptr != 0) {
|
||||||
command_error(client, ACK_ERROR_ARG,
|
r.FormatError(ACK_ERROR_ARG, "Integer expected: %s", s);
|
||||||
"Integer expected: %s", s);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value > max_value) {
|
if (value > max_value) {
|
||||||
command_error(client, ACK_ERROR_ARG,
|
r.FormatError(ACK_ERROR_ARG,
|
||||||
"Number too large: %s", s);
|
"Number too large: %s", s);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -161,21 +155,21 @@ ParseCommandArg(Client &client, unsigned &value_r, const char *s,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ParseCommandArg(Client &client, unsigned &value_r, const char *s)
|
ParseCommandArg(Response &r, unsigned &value_r, const char *s)
|
||||||
{
|
{
|
||||||
return ParseCommandArg(client, value_r, s,
|
return ParseCommandArg(r, value_r, s,
|
||||||
std::numeric_limits<unsigned>::max());
|
std::numeric_limits<unsigned>::max());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ParseCommandArg(Client &client, bool &value_r, const char *s)
|
ParseCommandArg(Response &r, bool &value_r, const char *s)
|
||||||
{
|
{
|
||||||
long value;
|
long value;
|
||||||
char *endptr;
|
char *endptr;
|
||||||
|
|
||||||
value = strtol(s, &endptr, 10);
|
value = strtol(s, &endptr, 10);
|
||||||
if (endptr == s || *endptr != 0 || (value != 0 && value != 1)) {
|
if (endptr == s || *endptr != 0 || (value != 0 && value != 1)) {
|
||||||
command_error(client, ACK_ERROR_ARG,
|
r.FormatError(ACK_ERROR_ARG,
|
||||||
"Boolean (0/1) expected: %s", s);
|
"Boolean (0/1) expected: %s", s);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -185,15 +179,14 @@ ParseCommandArg(Client &client, bool &value_r, const char *s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ParseCommandArg(Client &client, float &value_r, const char *s)
|
ParseCommandArg(Response &r, float &value_r, const char *s)
|
||||||
{
|
{
|
||||||
float value;
|
float value;
|
||||||
char *endptr;
|
char *endptr;
|
||||||
|
|
||||||
value = strtof(s, &endptr);
|
value = strtof(s, &endptr);
|
||||||
if (endptr == s || *endptr != 0) {
|
if (endptr == s || *endptr != 0) {
|
||||||
command_error(client, ACK_ERROR_ARG,
|
r.FormatError(ACK_ERROR_ARG, "Float expected: %s", s);
|
||||||
"Float expected: %s", s);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,10 +195,10 @@ ParseCommandArg(Client &client, float &value_r, const char *s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ParseCommandArg(Client &client, SongTime &value_r, const char *s)
|
ParseCommandArg(Response &r, SongTime &value_r, const char *s)
|
||||||
{
|
{
|
||||||
float value;
|
float value;
|
||||||
bool success = ParseCommandArg(client, value, s) && value >= 0;
|
bool success = ParseCommandArg(r, value, s) && value >= 0;
|
||||||
if (success)
|
if (success)
|
||||||
value_r = SongTime::FromS(value);
|
value_r = SongTime::FromS(value);
|
||||||
|
|
||||||
@ -213,10 +206,10 @@ ParseCommandArg(Client &client, SongTime &value_r, const char *s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ParseCommandArg(Client &client, SignedSongTime &value_r, const char *s)
|
ParseCommandArg(Response &r, SignedSongTime &value_r, const char *s)
|
||||||
{
|
{
|
||||||
float value;
|
float value;
|
||||||
bool success = ParseCommandArg(client, value, s);
|
bool success = ParseCommandArg(r, value, s);
|
||||||
if (success)
|
if (success)
|
||||||
value_r = SignedSongTime::FromS(value);
|
value_r = SignedSongTime::FromS(value);
|
||||||
|
|
||||||
|
@ -26,19 +26,19 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
class Client;
|
class Response;
|
||||||
class SongTime;
|
class SongTime;
|
||||||
class SignedSongTime;
|
class SignedSongTime;
|
||||||
|
|
||||||
bool
|
bool
|
||||||
check_uint32(Client &client, uint32_t *dst, const char *s);
|
ParseCommandArg32(Response &r, uint32_t &value_r, const char *s);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ParseCommandArg(Client &client, int &value_r, const char *s,
|
ParseCommandArg(Response &r, int &value_r, const char *s,
|
||||||
int min_value, int max_value);
|
int min_value, int max_value);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ParseCommandArg(Client &client, int &value_r, const char *s);
|
ParseCommandArg(Response &r, int &value_r, const char *s);
|
||||||
|
|
||||||
struct RangeArg {
|
struct RangeArg {
|
||||||
unsigned start, end;
|
unsigned start, end;
|
||||||
@ -54,25 +54,25 @@ struct RangeArg {
|
|||||||
};
|
};
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ParseCommandArg(Client &client, RangeArg &value_r, const char *s);
|
ParseCommandArg(Response &r, RangeArg &value_r, const char *s);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ParseCommandArg(Client &client, unsigned &value_r, const char *s,
|
ParseCommandArg(Response &r, unsigned &value_r, const char *s,
|
||||||
unsigned max_value);
|
unsigned max_value);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ParseCommandArg(Client &client, unsigned &value_r, const char *s);
|
ParseCommandArg(Response &r, unsigned &value_r, const char *s);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ParseCommandArg(Client &client, bool &value_r, const char *s);
|
ParseCommandArg(Response &r, bool &value_r, const char *s);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ParseCommandArg(Client &client, float &value_r, const char *s);
|
ParseCommandArg(Response &r, float &value_r, const char *s);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ParseCommandArg(Client &client, SongTime &value_r, const char *s);
|
ParseCommandArg(Response &r, SongTime &value_r, const char *s);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ParseCommandArg(Client &client, SignedSongTime &value_r, const char *s);
|
ParseCommandArg(Response &r, SignedSongTime &value_r, const char *s);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
#include "Queue.hxx"
|
#include "Queue.hxx"
|
||||||
#include "SongFilter.hxx"
|
#include "SongFilter.hxx"
|
||||||
#include "SongPrint.hxx"
|
#include "SongPrint.hxx"
|
||||||
#include "client/Client.hxx"
|
#include "client/Response.hxx"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send detailed information about a range of songs in the queue to a
|
* Send detailed information about a range of songs in the queue to a
|
||||||
@ -33,70 +33,70 @@
|
|||||||
* @param end the index of the last song (excluding)
|
* @param end the index of the last song (excluding)
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
queue_print_song_info(Client &client, const Queue &queue,
|
queue_print_song_info(Response &r, Partition &partition, const Queue &queue,
|
||||||
unsigned position)
|
unsigned position)
|
||||||
{
|
{
|
||||||
song_print_info(client, queue.Get(position));
|
song_print_info(r, partition, queue.Get(position));
|
||||||
client_printf(client, "Pos: %u\nId: %u\n",
|
r.Format("Pos: %u\nId: %u\n",
|
||||||
position, queue.PositionToId(position));
|
position, queue.PositionToId(position));
|
||||||
|
|
||||||
uint8_t priority = queue.GetPriorityAtPosition(position);
|
uint8_t priority = queue.GetPriorityAtPosition(position);
|
||||||
if (priority != 0)
|
if (priority != 0)
|
||||||
client_printf(client, "Prio: %u\n", priority);
|
r.Format("Prio: %u\n", priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
queue_print_info(Client &client, const Queue &queue,
|
queue_print_info(Response &r, Partition &partition, const Queue &queue,
|
||||||
unsigned start, unsigned end)
|
unsigned start, unsigned end)
|
||||||
{
|
{
|
||||||
assert(start <= end);
|
assert(start <= end);
|
||||||
assert(end <= queue.GetLength());
|
assert(end <= queue.GetLength());
|
||||||
|
|
||||||
for (unsigned i = start; i < end; ++i)
|
for (unsigned i = start; i < end; ++i)
|
||||||
queue_print_song_info(client, queue, i);
|
queue_print_song_info(r, partition, queue, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
queue_print_uris(Client &client, const Queue &queue,
|
queue_print_uris(Response &r, Partition &partition, const Queue &queue,
|
||||||
unsigned start, unsigned end)
|
unsigned start, unsigned end)
|
||||||
{
|
{
|
||||||
assert(start <= end);
|
assert(start <= end);
|
||||||
assert(end <= queue.GetLength());
|
assert(end <= queue.GetLength());
|
||||||
|
|
||||||
for (unsigned i = start; i < end; ++i) {
|
for (unsigned i = start; i < end; ++i) {
|
||||||
client_printf(client, "%i:", i);
|
r.Format("%i:", i);
|
||||||
song_print_uri(client, queue.Get(i));
|
song_print_uri(r, partition, queue.Get(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
queue_print_changes_info(Client &client, const Queue &queue,
|
queue_print_changes_info(Response &r, Partition &partition, const Queue &queue,
|
||||||
uint32_t version)
|
uint32_t version)
|
||||||
{
|
{
|
||||||
for (unsigned i = 0; i < queue.GetLength(); i++) {
|
for (unsigned i = 0; i < queue.GetLength(); i++) {
|
||||||
if (queue.IsNewerAtPosition(i, version))
|
if (queue.IsNewerAtPosition(i, version))
|
||||||
queue_print_song_info(client, queue, i);
|
queue_print_song_info(r, partition, queue, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
queue_print_changes_position(Client &client, const Queue &queue,
|
queue_print_changes_position(Response &r, const Queue &queue,
|
||||||
uint32_t version)
|
uint32_t version)
|
||||||
{
|
{
|
||||||
for (unsigned i = 0; i < queue.GetLength(); i++)
|
for (unsigned i = 0; i < queue.GetLength(); i++)
|
||||||
if (queue.IsNewerAtPosition(i, version))
|
if (queue.IsNewerAtPosition(i, version))
|
||||||
client_printf(client, "cpos: %i\nId: %i\n",
|
r.Format("cpos: %i\nId: %i\n",
|
||||||
i, queue.PositionToId(i));
|
i, queue.PositionToId(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
queue_find(Client &client, const Queue &queue,
|
queue_find(Response &r, Partition &partition, const Queue &queue,
|
||||||
const SongFilter &filter)
|
const SongFilter &filter)
|
||||||
{
|
{
|
||||||
for (unsigned i = 0; i < queue.GetLength(); i++) {
|
for (unsigned i = 0; i < queue.GetLength(); i++) {
|
||||||
const DetachedSong &song = queue.Get(i);
|
const DetachedSong &song = queue.Get(i);
|
||||||
|
|
||||||
if (filter.Match(song))
|
if (filter.Match(song))
|
||||||
queue_print_song_info(client, queue, i);
|
queue_print_song_info(r, partition, queue, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,27 +28,28 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
struct Queue;
|
struct Queue;
|
||||||
|
struct Partition;
|
||||||
class SongFilter;
|
class SongFilter;
|
||||||
class Client;
|
class Response;
|
||||||
|
|
||||||
void
|
void
|
||||||
queue_print_info(Client &client, const Queue &queue,
|
queue_print_info(Response &r, Partition &partition, const Queue &queue,
|
||||||
unsigned start, unsigned end);
|
unsigned start, unsigned end);
|
||||||
|
|
||||||
void
|
void
|
||||||
queue_print_uris(Client &client, const Queue &queue,
|
queue_print_uris(Response &r, Partition &partition, const Queue &queue,
|
||||||
unsigned start, unsigned end);
|
unsigned start, unsigned end);
|
||||||
|
|
||||||
void
|
void
|
||||||
queue_print_changes_info(Client &client, const Queue &queue,
|
queue_print_changes_info(Response &r, Partition &partition, const Queue &queue,
|
||||||
uint32_t version);
|
uint32_t version);
|
||||||
|
|
||||||
void
|
void
|
||||||
queue_print_changes_position(Client &client, const Queue &queue,
|
queue_print_changes_position(Response &r, const Queue &queue,
|
||||||
uint32_t version);
|
uint32_t version);
|
||||||
|
|
||||||
void
|
void
|
||||||
queue_find(Client &client, const Queue &queue,
|
queue_find(Response &response, Partition &partition, const Queue &queue,
|
||||||
const SongFilter &filter);
|
const SongFilter &filter);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -20,25 +20,25 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "StickerPrint.hxx"
|
#include "StickerPrint.hxx"
|
||||||
#include "StickerDatabase.hxx"
|
#include "StickerDatabase.hxx"
|
||||||
#include "client/Client.hxx"
|
#include "client/Response.hxx"
|
||||||
|
|
||||||
void
|
void
|
||||||
sticker_print_value(Client &client,
|
sticker_print_value(Response &r,
|
||||||
const char *name, const char *value)
|
const char *name, const char *value)
|
||||||
{
|
{
|
||||||
client_printf(client, "sticker: %s=%s\n", name, value);
|
r.Format("sticker: %s=%s\n", name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
print_sticker_cb(const char *name, const char *value, void *data)
|
print_sticker_cb(const char *name, const char *value, void *data)
|
||||||
{
|
{
|
||||||
Client &client = *(Client *)data;
|
auto &r = *(Response *)data;
|
||||||
|
|
||||||
sticker_print_value(client, name, value);
|
sticker_print_value(r, name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
sticker_print(Client &client, const Sticker &sticker)
|
sticker_print(Response &r, const Sticker &sticker)
|
||||||
{
|
{
|
||||||
sticker_foreach(sticker, print_sticker_cb, &client);
|
sticker_foreach(sticker, print_sticker_cb, &r);
|
||||||
}
|
}
|
||||||
|
@ -21,18 +21,18 @@
|
|||||||
#define MPD_STICKER_PRINT_HXX
|
#define MPD_STICKER_PRINT_HXX
|
||||||
|
|
||||||
struct Sticker;
|
struct Sticker;
|
||||||
class Client;
|
class Response;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends one sticker value to the client.
|
* Sends one sticker value to the client.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
sticker_print_value(Client &client, const char *name, const char *value);
|
sticker_print_value(Response &r, const char *name, const char *value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends all sticker values to the client.
|
* Sends all sticker values to the client.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
sticker_print(Client &client, const Sticker &sticker);
|
sticker_print(Response &r, const Sticker &sticker);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "protocol/ArgParser.hxx"
|
#include "protocol/ArgParser.hxx"
|
||||||
#include "protocol/Result.hxx"
|
#include "client/Response.hxx"
|
||||||
#include "Compiler.h"
|
#include "Compiler.h"
|
||||||
|
|
||||||
#include <cppunit/TestFixture.h>
|
#include <cppunit/TestFixture.h>
|
||||||
@ -13,10 +13,15 @@
|
|||||||
static enum ack last_error = ack(-1);
|
static enum ack last_error = ack(-1);
|
||||||
|
|
||||||
void
|
void
|
||||||
command_error(gcc_unused Client &client, enum ack error,
|
Response::Error(enum ack code, gcc_unused const char *msg)
|
||||||
gcc_unused const char *fmt, ...)
|
|
||||||
{
|
{
|
||||||
last_error = error;
|
last_error = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Response::FormatError(enum ack code, gcc_unused const char *fmt, ...)
|
||||||
|
{
|
||||||
|
last_error = code;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ArgParserTest : public CppUnit::TestFixture {
|
class ArgParserTest : public CppUnit::TestFixture {
|
||||||
@ -32,22 +37,23 @@ void
|
|||||||
ArgParserTest::TestRange()
|
ArgParserTest::TestRange()
|
||||||
{
|
{
|
||||||
Client &client = *(Client *)nullptr;
|
Client &client = *(Client *)nullptr;
|
||||||
|
Response r(client);
|
||||||
|
|
||||||
RangeArg range;
|
RangeArg range;
|
||||||
|
|
||||||
CPPUNIT_ASSERT(ParseCommandArg(client, range, "1"));
|
CPPUNIT_ASSERT(ParseCommandArg(r, range, "1"));
|
||||||
CPPUNIT_ASSERT_EQUAL(1u, range.start);
|
CPPUNIT_ASSERT_EQUAL(1u, range.start);
|
||||||
CPPUNIT_ASSERT_EQUAL(2u, range.end);
|
CPPUNIT_ASSERT_EQUAL(2u, range.end);
|
||||||
|
|
||||||
CPPUNIT_ASSERT(ParseCommandArg(client, range, "1:5"));
|
CPPUNIT_ASSERT(ParseCommandArg(r, range, "1:5"));
|
||||||
CPPUNIT_ASSERT_EQUAL(1u, range.start);
|
CPPUNIT_ASSERT_EQUAL(1u, range.start);
|
||||||
CPPUNIT_ASSERT_EQUAL(5u, range.end);
|
CPPUNIT_ASSERT_EQUAL(5u, range.end);
|
||||||
|
|
||||||
CPPUNIT_ASSERT(ParseCommandArg(client, range, "1:"));
|
CPPUNIT_ASSERT(ParseCommandArg(r, range, "1:"));
|
||||||
CPPUNIT_ASSERT_EQUAL(1u, range.start);
|
CPPUNIT_ASSERT_EQUAL(1u, range.start);
|
||||||
CPPUNIT_ASSERT(range.end >= 999999u);
|
CPPUNIT_ASSERT(range.end >= 999999u);
|
||||||
|
|
||||||
CPPUNIT_ASSERT(!ParseCommandArg(client, range, "-2"));
|
CPPUNIT_ASSERT(!ParseCommandArg(r, range, "-2"));
|
||||||
CPPUNIT_ASSERT_EQUAL(ACK_ERROR_ARG, last_error);
|
CPPUNIT_ASSERT_EQUAL(ACK_ERROR_ARG, last_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user