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;
}
template <typename TrackListType>
static bool appendToPlaylist(
ILibraryPtr library,
const int64_t playlistId,
std::shared_ptr<TrackList> trackList,
TrackListType trackList,
int offset)
{
try {
std::shared_ptr<AppendPlaylistQuery> query =
std::make_shared<AppendPlaylistQuery>(
playlistId,
trackList,
offset);
library, playlistId, trackList, offset);
library->Enqueue(query, ILibrary::QuerySynchronous);
@ -447,3 +446,14 @@ bool LocalSimpleDataProvider::AppendToPlaylistWithExternalIds(
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,
int offset = -1) override;
virtual bool AppendToPlaylistWithTrackList(
const int64_t playlistId,
musik::core::sdk::ITrackList* trackList,
int offset = -1) override;
private:
musik::core::ILibraryPtr library;
};

View File

@ -34,7 +34,8 @@
#include "pch.hpp"
#include "AppendPlaylistQuery.h"
#include <core/library/track/LibraryTrack.h>
#include <core/library/query/local/TrackMetadataQuery.h>
#include <core/library/LocalLibraryConstants.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 = ?";
AppendPlaylistQuery::AppendPlaylistQuery(
musik::core::ILibraryPtr library,
const int64_t playlistId,
std::shared_ptr<musik::core::TrackList> tracks,
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)
, offset(offset) {
}
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;
}
@ -99,8 +117,20 @@ bool AppendPlaylistQuery::OnRun(musik::core::db::Connection &db) {
Statement insertTrack(INSERT_PLAYLIST_TRACK_QUERY.c_str(), db);
for (size_t i = 0; i < this->tracks->Count(); i++) {
auto track = this->tracks->Get(i);
for (size_t i = 0; i < tracks->Count(); i++) {
auto id = tracks->GetId(i);
auto target = TrackPtr(new LibraryTrack(id, this->library));
std::shared_ptr<TrackMetadataQuery> query(
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"));
@ -111,8 +141,9 @@ bool AppendPlaylistQuery::OnRun(musik::core::db::Connection &db) {
return false;
}
}
}
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 {
public:
AppendPlaylistQuery(
musik::core::ILibraryPtr library,
const int64_t playlistId,
std::shared_ptr<musik::core::TrackList> tracks,
const int offset = -1);
AppendPlaylistQuery(
musik::core::ILibraryPtr library,
const int64_t playlistId,
musik::core::sdk::ITrackList *tracks,
const int offset = -1);
virtual ~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);
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;
};

View File

@ -55,17 +55,33 @@ static const std::string ALL_METADATA_QUERY_BY_EXTERNAL_ID =
"FROM " + TABLES + " " +
"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->library = library;
this->type = type;
}
bool TrackMetadataQuery::OnRun(Connection& db) {
bool queryById = this->result->GetId() != 0;
const std::string& query = queryById
std::string query;
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);
@ -82,6 +98,7 @@ bool TrackMetadataQuery::OnRun(Connection& db) {
}
if (trackQuery.Step() == Row) {
if (this->type == Full) {
result->SetValue(constants::Track::TRACK_NUM, trackQuery.ColumnText(0));
result->SetValue(constants::Track::DISC_NUM, trackQuery.ColumnText(1));
result->SetValue(constants::Track::BPM, trackQuery.ColumnText(2));
@ -102,6 +119,12 @@ bool TrackMetadataQuery::OnRun(Connection& db) {
result->SetValue(constants::Track::ALBUM_ID, trackQuery.ColumnText(17));
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;
}

View File

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

View File

@ -99,6 +99,11 @@ namespace musik { namespace core { namespace sdk {
const char** externalTrackIds,
size_t externalTrackIdCount,
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 playlist_id = "playlist_id";
static const std::string playlist_name = "playlist_name";
static const std::string subquery = "subquery";
static const std::string type = "type";
}
namespace value {

View File

@ -419,7 +419,7 @@ void WebSocketServer::RespondWithSuccess(connection_hdl connection, const std::s
{ message::name, name },
{ message::id, id },
{ 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);
@ -430,7 +430,7 @@ void WebSocketServer::RespondWithFailure(connection_hdl connection, json& reques
{ message::name, request[message::name] },
{ message::id, request[message::id] },
{ 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);
@ -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()) {
json& options = request[message::options];
std::string filter = options.value(key::filter, "");
int limit = -1, offset = 0;
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)) {
return;
}
@ -788,13 +791,14 @@ void WebSocketServer::RespondWithCurrentTime(connection_hdl connection, json& re
void WebSocketServer::RespondWithSavePlaylist(connection_hdl connection, json& request) {
auto& options = request[message::options];
int64_t id = options.value(key::playlist_id, 0);
if (id) {
/* by int64 id (faster, but less reliable) */
if (options.find(key::ids) != options.end()) {
json& ids = options[key::ids];
if (ids.is_array()) {
int64_t id = options.value(key::playlist_id, 0);
std::string name = options.value(key::playlist_name, "");
size_t count = ids.size();
@ -813,9 +817,11 @@ void WebSocketServer::RespondWithSavePlaylist(connection_hdl connection, json& r
this->RespondWithOptions(connection, request, {
{ key::playlist_id, newPlaylistId }
});
return;
}
else {
this->RespondWithFailure(connection, request);
return;
}
}
}
@ -824,7 +830,6 @@ void WebSocketServer::RespondWithSavePlaylist(connection_hdl connection, json& r
json& externalIds = options[key::external_ids];
if (externalIds.is_array()) {
int64_t id = options.value(key::playlist_id, 0);
std::string name = options.value(key::playlist_name, "");
size_t count = externalIds.size();
@ -855,18 +860,19 @@ void WebSocketServer::RespondWithSavePlaylist(connection_hdl connection, json& r
this->RespondWithOptions(connection, request, {
{ key::playlist_id, newPlaylistId }
});
return;
}
else {
this->RespondWithFailure(connection, request);
return;
}
}
}
/* 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) {
auto& options = request[message::options];
@ -890,13 +896,14 @@ void WebSocketServer::RespondWithDeletePlaylist(connection_hdl connection, json&
void WebSocketServer::RespondWithAppendToPlaylist(connection_hdl connection, json& request) {
auto& options = request[message::options];
int offset = options.value(key::offset, -1);
int64_t id = options.value(key::playlist_id, 0);
if (id) {
/* by int64 id (faster, but less reliable) */
if (options.find(key::ids) != options.end()) {
json& ids = options[key::ids];
if (ids.is_array()) {
int64_t id = options.value(key::playlist_id, 0);
size_t count = ids.size();
int64_t* idArray = new int64_t[count];
@ -921,7 +928,6 @@ void WebSocketServer::RespondWithAppendToPlaylist(connection_hdl connection, jso
json& externalIds = options[key::external_ids];
if (externalIds.is_array()) {
int64_t id = options.value(key::playlist_id, 0);
size_t count = externalIds.size();
char** externalIdArray = (char**)malloc(count * sizeof(char*));
@ -946,14 +952,44 @@ void WebSocketServer::RespondWithAppendToPlaylist(connection_hdl connection, jso
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, "");
if (subquery.find(message::options) != subquery.end()) {
ITrackList* tracks = nullptr;
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);
}
if (tracks) {
bool result = this->context.dataProvider
->AppendToPlaylistWithTrackList(id, tracks, offset);
tracks->Release();
result
? this->RespondWithSuccess(connection, request)
: this->RespondWithFailure(connection, request);
return;
}
}
}
}
/* no id list or external id list */
else {
this->RespondWithInvalidRequest(
connection, request[message::name], request[message::id]);
}
}
void WebSocketServer::BroadcastPlaybackOverview() {
{

View File

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