A handful of additions, speed-ups, and new features:

1. Optimized AppendPlaylistQuery and TrackMetadataQuery to not query
extraneous metadata -- only the required IDs.

2. Updated the `append_to_playlist` command in the remote server to
allow for track subqueries -- `query_tracks` and
`query_tracks_by_category` are both supported.

3. Fixed bugs in `WebSocketServer::RespondWithSuccess` and
`WebSocketServer::RespondWithFailure` where the `success` flag was not
being properly returned.
This commit is contained in:
casey langen 2017-11-06 01:00:40 -08:00
parent 47c2fa3f82
commit d73178ed4e
10 changed files with 293 additions and 166 deletions

View File

@ -389,18 +389,17 @@ bool LocalSimpleDataProvider::DeletePlaylist(const int64_t playlistId) {
return false; return false;
} }
template <typename TrackListType>
static bool appendToPlaylist( static bool appendToPlaylist(
ILibraryPtr library, ILibraryPtr library,
const int64_t playlistId, const int64_t playlistId,
std::shared_ptr<TrackList> trackList, TrackListType trackList,
int offset) int offset)
{ {
try { try {
std::shared_ptr<AppendPlaylistQuery> query = std::shared_ptr<AppendPlaylistQuery> query =
std::make_shared<AppendPlaylistQuery>( std::make_shared<AppendPlaylistQuery>(
playlistId, library, playlistId, trackList, offset);
trackList,
offset);
library->Enqueue(query, ILibrary::QuerySynchronous); library->Enqueue(query, ILibrary::QuerySynchronous);
@ -447,3 +446,14 @@ bool LocalSimpleDataProvider::AppendToPlaylistWithExternalIds(
return 0; return 0;
} }
bool LocalSimpleDataProvider::AppendToPlaylistWithTrackList(
const int64_t playlistId, ITrackList* trackList, int offset)
{
static auto deleter = [](musik::core::sdk::ITrackList* trackList) {};
bool result = appendToPlaylist(
this->library, playlistId, trackList, offset);
return result;
}

View File

@ -108,6 +108,11 @@ namespace musik { namespace core { namespace db { namespace local {
size_t externalIdCount, size_t externalIdCount,
int offset = -1) override; int offset = -1) override;
virtual bool AppendToPlaylistWithTrackList(
const int64_t playlistId,
musik::core::sdk::ITrackList* trackList,
int offset = -1) override;
private: private:
musik::core::ILibraryPtr library; musik::core::ILibraryPtr library;
}; };

View File

@ -34,7 +34,8 @@
#include "pch.hpp" #include "pch.hpp"
#include "AppendPlaylistQuery.h" #include "AppendPlaylistQuery.h"
#include <core/library/track/LibraryTrack.h>
#include <core/library/query/local/TrackMetadataQuery.h>
#include <core/library/LocalLibraryConstants.h> #include <core/library/LocalLibraryConstants.h>
#include <core/db/Statement.h> #include <core/db/Statement.h>
@ -58,17 +59,34 @@ static std::string GET_MAX_SORT_ORDER_QUERY =
"SELECT MAX(sort_order) from playlist_tracks where playlist_id = ?"; "SELECT MAX(sort_order) from playlist_tracks where playlist_id = ?";
AppendPlaylistQuery::AppendPlaylistQuery( AppendPlaylistQuery::AppendPlaylistQuery(
musik::core::ILibraryPtr library,
const int64_t playlistId, const int64_t playlistId,
std::shared_ptr<musik::core::TrackList> tracks, std::shared_ptr<musik::core::TrackList> tracks,
const int offset) const int offset)
: tracks(tracks) : library(library)
, sharedTracks(tracks)
, rawTracks(nullptr)
, playlistId(playlistId)
, offset(offset) {
}
AppendPlaylistQuery::AppendPlaylistQuery(
musik::core::ILibraryPtr library,
const int64_t playlistId,
musik::core::sdk::ITrackList *tracks,
const int offset)
: library(library)
, rawTracks(tracks)
, playlistId(playlistId) , playlistId(playlistId)
, offset(offset) { , offset(offset) {
} }
bool AppendPlaylistQuery::OnRun(musik::core::db::Connection &db) { bool AppendPlaylistQuery::OnRun(musik::core::db::Connection &db) {
if (!tracks->Count() || playlistId == 0) { ITrackList* tracks = sharedTracks ? sharedTracks.get() : rawTracks;
if (!tracks || !tracks->Count() || playlistId == 0) {
return true; return true;
} }
@ -99,20 +117,33 @@ bool AppendPlaylistQuery::OnRun(musik::core::db::Connection &db) {
Statement insertTrack(INSERT_PLAYLIST_TRACK_QUERY.c_str(), db); Statement insertTrack(INSERT_PLAYLIST_TRACK_QUERY.c_str(), db);
for (size_t i = 0; i < this->tracks->Count(); i++) { for (size_t i = 0; i < tracks->Count(); i++) {
auto track = this->tracks->Get(i); auto id = tracks->GetId(i);
insertTrack.Reset(); auto target = TrackPtr(new LibraryTrack(id, this->library));
insertTrack.BindText(0, track->GetString("external_id"));
insertTrack.BindText(1, track->GetString("source_id"));
insertTrack.BindInt64(2, playlistId);
insertTrack.BindInt32(3, offset++);
if (insertTrack.Step() == db::Error) { std::shared_ptr<TrackMetadataQuery> query(
return false; new TrackMetadataQuery(
target,
this->library,
TrackMetadataQuery::IdsOnly));
this->library->Enqueue(query, ILibrary::QuerySynchronous);
if (query->GetStatus() == IQuery::Finished) {
auto track = query->Result();
insertTrack.Reset();
insertTrack.BindText(0, track->GetString("external_id"));
insertTrack.BindText(1, track->GetString("source_id"));
insertTrack.BindInt64(2, playlistId);
insertTrack.BindInt32(3, offset++);
if (insertTrack.Step() == db::Error) {
return false;
}
} }
} }
transaction.CommitAndRestart(); transaction.CommitAndRestart();
return false; return true;
} }

View File

@ -44,10 +44,17 @@ namespace musik { namespace core { namespace db { namespace local {
class AppendPlaylistQuery : public musik::core::db::LocalQueryBase { class AppendPlaylistQuery : public musik::core::db::LocalQueryBase {
public: public:
AppendPlaylistQuery( AppendPlaylistQuery(
musik::core::ILibraryPtr library,
const int64_t playlistId, const int64_t playlistId,
std::shared_ptr<musik::core::TrackList> tracks, std::shared_ptr<musik::core::TrackList> tracks,
const int offset = -1); const int offset = -1);
AppendPlaylistQuery(
musik::core::ILibraryPtr library,
const int64_t playlistId,
musik::core::sdk::ITrackList *tracks,
const int offset = -1);
virtual ~AppendPlaylistQuery() { } virtual ~AppendPlaylistQuery() { }
std::string Name() { return "AppendPlaylistQuery"; } std::string Name() { return "AppendPlaylistQuery"; }
@ -56,7 +63,10 @@ namespace musik { namespace core { namespace db { namespace local {
virtual bool OnRun(musik::core::db::Connection &db); virtual bool OnRun(musik::core::db::Connection &db);
int64_t playlistId; int64_t playlistId;
std::shared_ptr<musik::core::TrackList> tracks;
musik::core::ILibraryPtr library;
std::shared_ptr<musik::core::TrackList> sharedTracks;
musik::core::sdk::ITrackList* rawTracks;
int offset; int offset;
}; };

View File

@ -55,17 +55,33 @@ static const std::string ALL_METADATA_QUERY_BY_EXTERNAL_ID =
"FROM " + TABLES + " " + "FROM " + TABLES + " " +
"WHERE t.external_id=? AND " + PREDICATE; "WHERE t.external_id=? AND " + PREDICATE;
TrackMetadataQuery::TrackMetadataQuery(TrackPtr target, ILibraryPtr library) { static const std::string IDS_ONLY_QUERY_BY_ID =
"SELECT DISTINCT external_id, source_id FROM tracks WHERE tracks.id=?";
static const std::string IDS_ONLY_QUERY_BY_EXTERNAL_ID =
"SELECT DISTINCT external_id, source_id FROM tracks WHERE tracks.external_id=?";
TrackMetadataQuery::TrackMetadataQuery(TrackPtr target, ILibraryPtr library, Type type) {
this->result = target; this->result = target;
this->library = library; this->library = library;
this->type = type;
} }
bool TrackMetadataQuery::OnRun(Connection& db) { bool TrackMetadataQuery::OnRun(Connection& db) {
bool queryById = this->result->GetId() != 0; bool queryById = this->result->GetId() != 0;
const std::string& query = queryById std::string query;
? ALL_METADATA_QUERY_BY_ID
: ALL_METADATA_QUERY_BY_EXTERNAL_ID; if (this->type == Full) {
query = queryById
? ALL_METADATA_QUERY_BY_ID
: ALL_METADATA_QUERY_BY_EXTERNAL_ID;
}
else {
query = queryById
? IDS_ONLY_QUERY_BY_ID
: IDS_ONLY_QUERY_BY_EXTERNAL_ID;
}
Statement trackQuery(query.c_str(), db); Statement trackQuery(query.c_str(), db);
@ -82,26 +98,33 @@ bool TrackMetadataQuery::OnRun(Connection& db) {
} }
if (trackQuery.Step() == Row) { if (trackQuery.Step() == Row) {
result->SetValue(constants::Track::TRACK_NUM, trackQuery.ColumnText(0)); if (this->type == Full) {
result->SetValue(constants::Track::DISC_NUM, trackQuery.ColumnText(1)); result->SetValue(constants::Track::TRACK_NUM, trackQuery.ColumnText(0));
result->SetValue(constants::Track::BPM, trackQuery.ColumnText(2)); result->SetValue(constants::Track::DISC_NUM, trackQuery.ColumnText(1));
result->SetValue(constants::Track::DURATION, trackQuery.ColumnText(3)); result->SetValue(constants::Track::BPM, trackQuery.ColumnText(2));
result->SetValue(constants::Track::FILESIZE, trackQuery.ColumnText(4)); result->SetValue(constants::Track::DURATION, trackQuery.ColumnText(3));
result->SetValue(constants::Track::YEAR, trackQuery.ColumnText(5)); result->SetValue(constants::Track::FILESIZE, trackQuery.ColumnText(4));
result->SetValue(constants::Track::TITLE, trackQuery.ColumnText(6)); result->SetValue(constants::Track::YEAR, trackQuery.ColumnText(5));
result->SetValue(constants::Track::FILENAME, trackQuery.ColumnText(7)); result->SetValue(constants::Track::TITLE, trackQuery.ColumnText(6));
result->SetValue(constants::Track::THUMBNAIL_ID, trackQuery.ColumnText(8)); result->SetValue(constants::Track::FILENAME, trackQuery.ColumnText(7));
result->SetValue(constants::Track::ALBUM, trackQuery.ColumnText(9)); result->SetValue(constants::Track::THUMBNAIL_ID, trackQuery.ColumnText(8));
result->SetValue(constants::Track::ALBUM_ARTIST, trackQuery.ColumnText(10)); result->SetValue(constants::Track::ALBUM, trackQuery.ColumnText(9));
result->SetValue(constants::Track::GENRE, trackQuery.ColumnText(11)); result->SetValue(constants::Track::ALBUM_ARTIST, trackQuery.ColumnText(10));
result->SetValue(constants::Track::ARTIST, trackQuery.ColumnText(12)); result->SetValue(constants::Track::GENRE, trackQuery.ColumnText(11));
result->SetValue(constants::Track::FILETIME, trackQuery.ColumnText(13)); result->SetValue(constants::Track::ARTIST, trackQuery.ColumnText(12));
result->SetValue(constants::Track::GENRE_ID, trackQuery.ColumnText(14)); result->SetValue(constants::Track::FILETIME, trackQuery.ColumnText(13));
result->SetValue(constants::Track::ARTIST_ID, trackQuery.ColumnText(15)); result->SetValue(constants::Track::GENRE_ID, trackQuery.ColumnText(14));
result->SetValue(constants::Track::ALBUM_ARTIST_ID, trackQuery.ColumnText(16)); result->SetValue(constants::Track::ARTIST_ID, trackQuery.ColumnText(15));
result->SetValue(constants::Track::ALBUM_ID, trackQuery.ColumnText(17)); result->SetValue(constants::Track::ALBUM_ARTIST_ID, trackQuery.ColumnText(16));
result->SetValue(constants::Track::SOURCE_ID, trackQuery.ColumnText(18)); result->SetValue(constants::Track::ALBUM_ID, trackQuery.ColumnText(17));
result->SetValue(constants::Track::EXTERNAL_ID, trackQuery.ColumnText(19)); result->SetValue(constants::Track::SOURCE_ID, trackQuery.ColumnText(18));
result->SetValue(constants::Track::EXTERNAL_ID, trackQuery.ColumnText(19));
}
else {
result->SetValue(constants::Track::EXTERNAL_ID, trackQuery.ColumnText(0));
result->SetValue(constants::Track::SOURCE_ID, trackQuery.ColumnText(1));
}
return true; return true;
} }

View File

@ -41,9 +41,12 @@ namespace musik { namespace core { namespace db { namespace local {
class TrackMetadataQuery : public LocalQueryBase { class TrackMetadataQuery : public LocalQueryBase {
public: public:
enum Type { Full, IdsOnly };
TrackMetadataQuery( TrackMetadataQuery(
musik::core::TrackPtr target, musik::core::TrackPtr target,
musik::core::ILibraryPtr library); musik::core::ILibraryPtr library,
Type type = Full);
virtual ~TrackMetadataQuery() { } virtual ~TrackMetadataQuery() { }
@ -56,6 +59,7 @@ class TrackMetadataQuery : public LocalQueryBase {
virtual std::string Name() { return "TrackMetadataQuery"; } virtual std::string Name() { return "TrackMetadataQuery"; }
private: private:
Type type;
ILibraryPtr library; ILibraryPtr library;
TrackPtr result; TrackPtr result;
}; };

View File

@ -99,6 +99,11 @@ namespace musik { namespace core { namespace sdk {
const char** externalTrackIds, const char** externalTrackIds,
size_t externalTrackIdCount, size_t externalTrackIdCount,
int offset = -1) = 0; int offset = -1) = 0;
virtual bool AppendToPlaylistWithTrackList(
const int64_t playlistId,
ITrackList* trackList,
int offset = -1) = 0;
}; };
} } } } } }

View File

@ -113,6 +113,8 @@ namespace key {
static const std::string authenticated = "authenticated"; static const std::string authenticated = "authenticated";
static const std::string playlist_id = "playlist_id"; static const std::string playlist_id = "playlist_id";
static const std::string playlist_name = "playlist_name"; static const std::string playlist_name = "playlist_name";
static const std::string subquery = "subquery";
static const std::string type = "type";
} }
namespace value { namespace value {

View File

@ -419,7 +419,7 @@ void WebSocketServer::RespondWithSuccess(connection_hdl connection, const std::s
{ message::name, name }, { message::name, name },
{ message::id, id }, { message::id, id },
{ message::type, type::response }, { message::type, type::response },
{ message::options,{ key::success, true } } { message::options, {{ key::success, true }} }
}; };
wss->send(connection, success.dump().c_str(), websocketpp::frame::opcode::text); wss->send(connection, success.dump().c_str(), websocketpp::frame::opcode::text);
@ -430,7 +430,7 @@ void WebSocketServer::RespondWithFailure(connection_hdl connection, json& reques
{ message::name, request[message::name] }, { message::name, request[message::name] },
{ message::id, request[message::id] }, { message::id, request[message::id] },
{ message::type, type::response }, { message::type, type::response },
{ message::options,{ key::success, false } } { message::options, {{ key::success, false }} }
}; };
wss->send(connection, error.dump().c_str(), websocketpp::frame::opcode::text); wss->send(connection, error.dump().c_str(), websocketpp::frame::opcode::text);
@ -526,17 +526,20 @@ void WebSocketServer::GetLimitAndOffset(json& options, int& limit, int& offset)
} }
} }
void WebSocketServer::RespondWithQueryTracks(connection_hdl connection, json& request) { ITrackList* WebSocketServer::QueryTracks(json& request, int& limit, int& offset) {
if (request.find(message::options) != request.end()) { if (request.find(message::options) != request.end()) {
json& options = request[message::options]; json& options = request[message::options];
std::string filter = options.value(key::filter, ""); std::string filter = options.value(key::filter, "");
int limit = -1, offset = 0;
this->GetLimitAndOffset(options, limit, offset); this->GetLimitAndOffset(options, limit, offset);
return context.dataProvider->QueryTracks(filter.c_str(), limit, offset);
}
return nullptr;
}
ITrackList* tracks = context.dataProvider->QueryTracks(filter.c_str(), limit, offset); void WebSocketServer::RespondWithQueryTracks(connection_hdl connection, json& request) {
if (request.find(message::options) != request.end()) {
int limit = -1, offset = 0;
ITrackList* tracks = this->QueryTracks(request, limit, offset);
if (this->RespondWithTracks(connection, request, tracks, limit, offset)) { if (this->RespondWithTracks(connection, request, tracks, limit, offset)) {
return; return;
} }
@ -788,84 +791,87 @@ void WebSocketServer::RespondWithCurrentTime(connection_hdl connection, json& re
void WebSocketServer::RespondWithSavePlaylist(connection_hdl connection, json& request) { void WebSocketServer::RespondWithSavePlaylist(connection_hdl connection, json& request) {
auto& options = request[message::options]; auto& options = request[message::options];
int64_t id = options.value(key::playlist_id, 0);
/* by int64 id (faster, but less reliable) */ if (id) {
if (options.find(key::ids) != options.end()) { /* by int64 id (faster, but less reliable) */
json& ids = options[key::ids]; if (options.find(key::ids) != options.end()) {
json& ids = options[key::ids];
if (ids.is_array()) { if (ids.is_array()) {
int64_t id = options.value(key::playlist_id, 0); std::string name = options.value(key::playlist_name, "");
std::string name = options.value(key::playlist_name, "");
size_t count = ids.size(); size_t count = ids.size();
int64_t* idArray = new int64_t[count]; int64_t* idArray = new int64_t[count];
if (count > 0) { if (count > 0) {
std::copy(ids.begin(), ids.end(), idArray); std::copy(ids.begin(), ids.end(), idArray);
}
int64_t newPlaylistId = this->context.dataProvider
->SavePlaylistWithIds(idArray, count, name.c_str(), id);
delete[] idArray;
if (newPlaylistId != 0) {
this->RespondWithOptions(connection, request, {
{ key::playlist_id, newPlaylistId }
});
return;
}
else {
this->RespondWithFailure(connection, request);
return;
}
} }
}
/* by external id (slower, more reliable) */
else if (options.find(key::external_ids) != options.end()) {
json& externalIds = options[key::external_ids];
int64_t newPlaylistId = this->context.dataProvider if (externalIds.is_array()) {
->SavePlaylistWithIds(idArray, count, name.c_str(), id); std::string name = options.value(key::playlist_name, "");
delete[] idArray; size_t count = externalIds.size();
char** externalIdArray = (char**)malloc(count * sizeof(char*));
if (newPlaylistId != 0) { for (size_t i = 0; i < count; i++) {
this->RespondWithOptions(connection, request, { std::string externalId = externalIds[i];
{ key::playlist_id, newPlaylistId } size_t size = externalId.size();
}); externalIdArray[i] = (char*)malloc(size + 1);
} strncpy(externalIdArray[i], externalId.c_str(), size);
else { externalIdArray[i][size] = 0;
this->RespondWithFailure(connection, request); }
int64_t newPlaylistId = this->context.dataProvider
->SavePlaylistWithExternalIds(
(const char**)externalIdArray,
count,
name.c_str(),
id);
for (size_t i = 0; i < count; i++) {
free(externalIdArray[i]);
}
free(externalIdArray);
if (newPlaylistId != 0) {
this->RespondWithOptions(connection, request, {
{ key::playlist_id, newPlaylistId }
});
return;
}
else {
this->RespondWithFailure(connection, request);
return;
}
} }
} }
} }
/* by external id (slower, more reliable) */
else if (options.find(key::external_ids) != options.end()) {
json& externalIds = options[key::external_ids];
if (externalIds.is_array()) { this->RespondWithInvalidRequest(
int64_t id = options.value(key::playlist_id, 0); connection, request[message::name], request[message::id]);
std::string name = options.value(key::playlist_name, "");
size_t count = externalIds.size();
char** externalIdArray = (char**) malloc(count * sizeof(char*));
for (size_t i = 0; i < count; i++) {
std::string externalId = externalIds[i];
size_t size = externalId.size();
externalIdArray[i] = (char*) malloc(size + 1);
strncpy(externalIdArray[i], externalId.c_str(), size);
externalIdArray[i][size] = 0;
}
int64_t newPlaylistId = this->context.dataProvider
->SavePlaylistWithExternalIds(
(const char**) externalIdArray,
count,
name.c_str(),
id);
for (size_t i = 0; i < count; i++) {
free(externalIdArray[i]);
}
free(externalIdArray);
if (newPlaylistId != 0) {
this->RespondWithOptions(connection, request, {
{ key::playlist_id, newPlaylistId }
});
}
else {
this->RespondWithFailure(connection, request);
}
}
}
/* no id list or external id list */
else {
this->RespondWithInvalidRequest(
connection, request[message::name], request[message::id]);
}
} }
void WebSocketServer::RespondWithRenamePlaylist(connection_hdl connection, json& request) { void WebSocketServer::RespondWithRenamePlaylist(connection_hdl connection, json& request) {
@ -890,69 +896,99 @@ void WebSocketServer::RespondWithDeletePlaylist(connection_hdl connection, json&
void WebSocketServer::RespondWithAppendToPlaylist(connection_hdl connection, json& request) { void WebSocketServer::RespondWithAppendToPlaylist(connection_hdl connection, json& request) {
auto& options = request[message::options]; auto& options = request[message::options];
int offset = options.value(key::offset, -1); int offset = options.value(key::offset, -1);
int64_t id = options.value(key::playlist_id, 0);
/* by int64 id (faster, but less reliable) */ if (id) {
if (options.find(key::ids) != options.end()) { /* by int64 id (faster, but less reliable) */
json& ids = options[key::ids]; if (options.find(key::ids) != options.end()) {
json& ids = options[key::ids];
if (ids.is_array()) { if (ids.is_array()) {
int64_t id = options.value(key::playlist_id, 0); size_t count = ids.size();
size_t count = ids.size(); int64_t* idArray = new int64_t[count];
int64_t* idArray = new int64_t[count];
if (count > 0) { if (count > 0) {
std::copy(ids.begin(), ids.end(), idArray); std::copy(ids.begin(), ids.end(), idArray);
}
bool result = this->context.dataProvider
->AppendToPlaylistWithIds(id, idArray, count, offset);
delete[] idArray;
result
? this->RespondWithSuccess(connection, request)
: this->RespondWithFailure(connection, request);
return;
} }
bool result = this->context.dataProvider
->AppendToPlaylistWithIds(id, idArray, count, offset);
delete[] idArray;
result
? this->RespondWithSuccess(connection, request)
: this->RespondWithFailure(connection, request);
return;
} }
} /* by external id (slower, more reliable) */
/* by external id (slower, more reliable) */ else if (options.find(key::external_ids) != options.end()) {
else if (options.find(key::external_ids) != options.end()) { json& externalIds = options[key::external_ids];
json& externalIds = options[key::external_ids];
if (externalIds.is_array()) { if (externalIds.is_array()) {
int64_t id = options.value(key::playlist_id, 0); size_t count = externalIds.size();
size_t count = externalIds.size(); char** externalIdArray = (char**)malloc(count * sizeof(char*));
char** externalIdArray = (char**)malloc(count * sizeof(char*));
for (size_t i = 0; i < count; i++) { for (size_t i = 0; i < count; i++) {
std::string externalId = externalIds[i]; std::string externalId = externalIds[i];
size_t size = externalId.size(); size_t size = externalId.size();
externalIdArray[i] = (char*)malloc(size + 1); externalIdArray[i] = (char*)malloc(size + 1);
strncpy(externalIdArray[i], externalId.c_str(), size); strncpy(externalIdArray[i], externalId.c_str(), size);
externalIdArray[i][size] = 0; externalIdArray[i][size] = 0;
}
bool result = this->context.dataProvider
->AppendToPlaylistWithExternalIds(
id, (const char**)externalIdArray, count, offset);
for (size_t i = 0; i < count; i++) {
free(externalIdArray[i]);
}
free(externalIdArray);
result
? this->RespondWithSuccess(connection, request)
: this->RespondWithFailure(connection, request);
return;
} }
}
/* by subquery (query_tracks or query_tracks_by_category) */
else if (options.find(key::subquery) != options.end()) {
auto& subquery = options[key::subquery];
std::string type = subquery.value(key::type, "");
bool result = this->context.dataProvider if (subquery.find(message::options) != subquery.end()) {
->AppendToPlaylistWithExternalIds( ITrackList* tracks = nullptr;
id, (const char**)externalIdArray, count, offset); int queryLimit, queryOffset;
if (type == request::query_tracks) {
tracks = this->QueryTracks(subquery, queryLimit, queryOffset);
}
else if (type == request::query_tracks_by_category) {
tracks = this->QueryTracksByCategory(subquery, queryLimit, queryOffset);
}
for (size_t i = 0; i < count; i++) { if (tracks) {
free(externalIdArray[i]); bool result = this->context.dataProvider
->AppendToPlaylistWithTrackList(id, tracks, offset);
tracks->Release();
result
? this->RespondWithSuccess(connection, request)
: this->RespondWithFailure(connection, request);
return;
}
} }
free(externalIdArray);
result
? this->RespondWithSuccess(connection, request)
: this->RespondWithFailure(connection, request);
} }
} }
/* no id list or external id list */ /* no id list or external id list */
else { this->RespondWithInvalidRequest(
this->RespondWithInvalidRequest( connection, request[message::name], request[message::id]);
connection, request[message::name], request[message::id]);
}
} }
void WebSocketServer::BroadcastPlaybackOverview() { void WebSocketServer::BroadcastPlaybackOverview() {

View File

@ -156,6 +156,7 @@ class WebSocketServer {
void GetLimitAndOffset(json& options, int& limit, int& offset); void GetLimitAndOffset(json& options, int& limit, int& offset);
ITrackList* QueryTracksByCategory(json& request, int& limit, int& offset); ITrackList* QueryTracksByCategory(json& request, int& limit, int& offset);
ITrackList* QueryTracks(json& request, int& limit, int& offset);
json ReadTrackMetadata(ITrack* track); json ReadTrackMetadata(ITrack* track);
void BuildPlaybackOverview(json& options); void BuildPlaybackOverview(json& options);