mirror of
https://github.com/clangen/musikcube.git
synced 2024-10-02 13:02:35 +00:00
Ensure CategoryTrackListQuery is implemented successfully, and added
correct exception handling for query result parse failures in the remote library WebSocketClient.
This commit is contained in:
parent
960c9a55f4
commit
46118ab6fe
@ -185,14 +185,13 @@ void CategoryTrackListQuery::ProcessResult(musik::core::db::Statement& trackQuer
|
|||||||
|
|
||||||
runningDuration += trackQuery.ColumnInt32(1);
|
runningDuration += trackQuery.ColumnInt32(1);
|
||||||
if (this->parseHeaders && album != lastAlbum) {
|
if (this->parseHeaders && album != lastAlbum) {
|
||||||
headers->insert(index);
|
|
||||||
|
|
||||||
if (!headers->empty()) {
|
if (!headers->empty()) {
|
||||||
(*durations)[lastHeaderIndex] = runningDuration;
|
(*durations)[lastHeaderIndex] = runningDuration;
|
||||||
lastHeaderIndex = index;
|
lastHeaderIndex = index;
|
||||||
runningDuration = 0;
|
runningDuration = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
headers->insert(index);
|
||||||
lastAlbum = album;
|
lastAlbum = album;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,7 +240,7 @@ std::string CategoryTrackListQuery::SerializeResult() {
|
|||||||
void CategoryTrackListQuery::DeserializeResult(const std::string& data) {
|
void CategoryTrackListQuery::DeserializeResult(const std::string& data) {
|
||||||
this->SetStatus(IQuery::Failed);
|
this->SetStatus(IQuery::Failed);
|
||||||
nlohmann::json result = nlohmann::json::parse(data)["result"];
|
nlohmann::json result = nlohmann::json::parse(data)["result"];
|
||||||
this->DeserializeTrackListAndHeaders(result, this->library, this->result, this->headers);
|
this->DeserializeTrackListAndHeaders(result, this->library, this);
|
||||||
this->SetStatus(IQuery::Finished);
|
this->SetStatus(IQuery::Finished);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,7 +122,7 @@ std::string DirectoryTrackListQuery::SerializeResult() {
|
|||||||
void DirectoryTrackListQuery::DeserializeResult(const std::string& data) {
|
void DirectoryTrackListQuery::DeserializeResult(const std::string& data) {
|
||||||
this->SetStatus(IQuery::Failed);
|
this->SetStatus(IQuery::Failed);
|
||||||
nlohmann::json result = nlohmann::json::parse(data)["result"];
|
nlohmann::json result = nlohmann::json::parse(data)["result"];
|
||||||
this->DeserializeTrackListAndHeaders(result, this->library, this->result, this->headers);
|
this->DeserializeTrackListAndHeaders(result, this->library, this);
|
||||||
this->SetStatus(IQuery::Finished);
|
this->SetStatus(IQuery::Finished);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,7 +119,7 @@ std::string GetPlaylistQuery::SerializeResult() {
|
|||||||
void GetPlaylistQuery::DeserializeResult(const std::string& data) {
|
void GetPlaylistQuery::DeserializeResult(const std::string& data) {
|
||||||
this->SetStatus(IQuery::Failed);
|
this->SetStatus(IQuery::Failed);
|
||||||
nlohmann::json result = nlohmann::json::parse(data)["result"];
|
nlohmann::json result = nlohmann::json::parse(data)["result"];
|
||||||
this->DeserializeTrackListAndHeaders(result, this->library, this->result, this->headers);
|
this->DeserializeTrackListAndHeaders(result, this->library, this);
|
||||||
this->SetStatus(IQuery::Finished);
|
this->SetStatus(IQuery::Finished);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,7 +193,7 @@ std::string SearchTrackListQuery::SerializeResult() {
|
|||||||
void SearchTrackListQuery::DeserializeResult(const std::string& data) {
|
void SearchTrackListQuery::DeserializeResult(const std::string& data) {
|
||||||
this->SetStatus(IQuery::Failed);
|
this->SetStatus(IQuery::Failed);
|
||||||
nlohmann::json result = nlohmann::json::parse(data)["result"];
|
nlohmann::json result = nlohmann::json::parse(data)["result"];
|
||||||
this->DeserializeTrackListAndHeaders(result, this->library, this->result, this->headers);
|
this->DeserializeTrackListAndHeaders(result, this->library, this);
|
||||||
this->SetStatus(IQuery::Finished);
|
this->SetStatus(IQuery::Finished);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,6 +103,7 @@ namespace musik { namespace core { namespace library { namespace query {
|
|||||||
nlohmann::json output = {
|
nlohmann::json output = {
|
||||||
{ "result", {
|
{ "result", {
|
||||||
{ "headers", *this->GetHeaders() },
|
{ "headers", *this->GetHeaders() },
|
||||||
|
{ "durations", serialization::DurationMapToJsonMap(*this->GetDurations()) },
|
||||||
{ "trackList", serialization::TrackListToJson(*this->GetResult(), true) }
|
{ "trackList", serialization::TrackListToJson(*this->GetResult(), true) }
|
||||||
}}
|
}}
|
||||||
};
|
};
|
||||||
@ -110,13 +111,13 @@ namespace musik { namespace core { namespace library { namespace query {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DeserializeTrackListAndHeaders(
|
void DeserializeTrackListAndHeaders(
|
||||||
nlohmann::json &result,
|
nlohmann::json& result,
|
||||||
ILibraryPtr library,
|
ILibraryPtr library,
|
||||||
Result tracks,
|
TrackListQueryBase* query)
|
||||||
Headers headers)
|
|
||||||
{
|
{
|
||||||
serialization::TrackListFromJson(result["trackList"], *tracks, library, true);
|
serialization::JsonArrayToSet<std::set<size_t>, size_t>(result["headers"], *query->GetHeaders());
|
||||||
serialization::JsonArrayToSet<std::set<size_t>, size_t>(result["headers"], *headers);
|
serialization::JsonMapToDuration(result["durations"], *query->GetDurations());
|
||||||
|
serialization::TrackListFromJson(result["trackList"], *query->GetResult(), library, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -237,6 +237,20 @@ namespace musik { namespace core { namespace library { namespace query {
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nlohmann::json DurationMapToJsonMap(const std::map<size_t, size_t>& input) {
|
||||||
|
nlohmann::json output;
|
||||||
|
for (auto kv : input) {
|
||||||
|
output[std::to_string(kv.first)] = kv.second;
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonMapToDuration(const nlohmann::json& input, std::map<size_t, size_t>& output) {
|
||||||
|
for (const auto& item : input.items()) {
|
||||||
|
output[std::stoi(item.key())] = item.value().get<size_t>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} } } }
|
} } } }
|
||||||
|
@ -99,6 +99,12 @@ namespace musik { namespace core { namespace library { namespace query {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nlohmann::json DurationMapToJsonMap(
|
||||||
|
const std::map<size_t, size_t>& input);
|
||||||
|
|
||||||
|
void JsonMapToDuration(
|
||||||
|
const nlohmann::json& input,
|
||||||
|
std::map<size_t, size_t>& output);
|
||||||
}
|
}
|
||||||
|
|
||||||
} } } }
|
} } } }
|
||||||
|
@ -171,8 +171,14 @@ WebSocketClient::WebSocketClient(IMessageQueue* messageQueue, Listener* listener
|
|||||||
std::string rawResult;
|
std::string rawResult;
|
||||||
if (extractRawQueryResult(responseJson, rawResult)) {
|
if (extractRawQueryResult(responseJson, rawResult)) {
|
||||||
if (query) {
|
if (query) {
|
||||||
query->DeserializeResult(rawResult);
|
try {
|
||||||
this->listener->OnClientQuerySucceeded(this, messageId, query);
|
query->DeserializeResult(rawResult);
|
||||||
|
this->listener->OnClientQuerySucceeded(this, messageId, query);
|
||||||
|
}
|
||||||
|
catch (...) {
|
||||||
|
this->listener->OnClientQueryFailed(
|
||||||
|
this, messageId, query, QueryError::ParseFailed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this->listener->OnClientQueryFailed(
|
this->listener->OnClientQueryFailed(
|
||||||
|
@ -65,6 +65,7 @@ namespace musik { namespace core { namespace net {
|
|||||||
Disconnected = 2,
|
Disconnected = 2,
|
||||||
AuthFailed = 3,
|
AuthFailed = 3,
|
||||||
QueryNotFound = 4,
|
QueryNotFound = 4,
|
||||||
|
ParseFailed = 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class ConnectionError : int {
|
enum class ConnectionError : int {
|
||||||
|
@ -34,16 +34,26 @@
|
|||||||
|
|
||||||
#include "pch.hpp"
|
#include "pch.hpp"
|
||||||
#include "Duration.h"
|
#include "Duration.h"
|
||||||
|
#include "NarrowCast.h"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
|
template <typename N>
|
||||||
|
static std::string formatDuration(N seconds) {
|
||||||
|
N mins = (seconds / 60);
|
||||||
|
N secs = seconds - (mins * 60);
|
||||||
|
char buffer[128];
|
||||||
|
snprintf(buffer, sizeof(buffer), "%d:%02d", narrow_cast<int>(mins), narrow_cast<int>(secs));
|
||||||
|
return std::string(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
namespace musik { namespace core { namespace duration {
|
namespace musik { namespace core { namespace duration {
|
||||||
|
|
||||||
std::string Duration(int seconds) {
|
std::string Duration(int seconds) {
|
||||||
int mins = (seconds / 60);
|
return formatDuration(seconds);
|
||||||
int secs = seconds - (mins * 60);
|
}
|
||||||
char buffer[128];
|
|
||||||
snprintf(buffer, sizeof(buffer), "%d:%02d", mins, secs);
|
std::string Duration(size_t seconds) {
|
||||||
return std::string(buffer);
|
return formatDuration(seconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Duration(double seconds) {
|
std::string Duration(double seconds) {
|
||||||
|
@ -40,6 +40,7 @@ namespace musik { namespace core { namespace duration {
|
|||||||
|
|
||||||
std::string Duration(const std::string& str);
|
std::string Duration(const std::string& str);
|
||||||
std::string Duration(int seconds);
|
std::string Duration(int seconds);
|
||||||
|
std::string Duration(size_t seconds);
|
||||||
std::string Duration(double seconds);
|
std::string Duration(double seconds);
|
||||||
|
|
||||||
} } }
|
} } }
|
||||||
|
@ -131,12 +131,12 @@ void TrackListView::OnTrackListWindowCached(const musik::core::TrackList* track,
|
|||||||
void TrackListView::OnQueryCompleted(IQuery* query) {
|
void TrackListView::OnQueryCompleted(IQuery* query) {
|
||||||
if (this->query && query == this->query.get()) {
|
if (this->query && query == this->query.get()) {
|
||||||
if (this->query->GetStatus() == IQuery::Finished) {
|
if (this->query->GetStatus() == IQuery::Finished) {
|
||||||
bool hadTracks = this->tracks && this->tracks->Count() > 0;
|
const bool hadTracks = this->tracks && this->tracks->Count() > 0;
|
||||||
bool prevQuerySame = this->lastQueryHash == this->query->GetQueryHash();
|
const bool prevQuerySame = this->lastQueryHash == this->query->GetQueryHash();
|
||||||
|
|
||||||
this->SetTrackListAndUpateEventHandlers(this->query->GetResult());
|
this->SetTrackListAndUpateEventHandlers(this->query->GetResult());
|
||||||
this->AdjustTrackListCacheWindowSize();
|
this->AdjustTrackListCacheWindowSize();
|
||||||
this->headers.Set(this->query->GetHeaders());
|
this->headers.Set(this->query->GetHeaders(), this->query->GetDurations());
|
||||||
this->lastQueryHash = this->query->GetQueryHash();
|
this->lastQueryHash = this->query->GetQueryHash();
|
||||||
|
|
||||||
/* update our internal state */
|
/* update our internal state */
|
||||||
@ -389,11 +389,12 @@ TrackListView::Adapter::Adapter(TrackListView &parent)
|
|||||||
|
|
||||||
/* * * * TrackListView::HeaderCalculator * * * */
|
/* * * * TrackListView::HeaderCalculator * * * */
|
||||||
|
|
||||||
void TrackListView::HeaderCalculator::Set(Headers rawOffsets) {
|
void TrackListView::HeaderCalculator::Set(Headers rawOffsets, Durations durations) {
|
||||||
this->rawOffsets = rawOffsets;
|
this->rawOffsets = rawOffsets;
|
||||||
|
this->durations = durations;
|
||||||
this->absoluteOffsets.reset();
|
this->absoluteOffsets.reset();
|
||||||
if (rawOffsets) {
|
if (rawOffsets) {
|
||||||
this->absoluteOffsets.reset(new std::set<size_t>());
|
this->absoluteOffsets = std::make_shared<std::set<size_t>>();
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
for (auto val : (*this->rawOffsets)) {
|
for (auto val : (*this->rawOffsets)) {
|
||||||
this->absoluteOffsets->insert(val + i);
|
this->absoluteOffsets->insert(val + i);
|
||||||
@ -411,6 +412,16 @@ size_t TrackListView::HeaderCalculator::AdapterToTrackListIndex(size_t index) {
|
|||||||
return this->ApplyHeaderOffset(index, this->absoluteOffsets, -1);
|
return this->ApplyHeaderOffset(index, this->absoluteOffsets, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t TrackListView::HeaderCalculator::DurationFromAdapterIndex(size_t index) {
|
||||||
|
/* ugh, HeaderCalculator should probably be re-thought, but this is especially ugly */
|
||||||
|
const auto adjustedIndex = this->ApplyHeaderOffset(index + 1, this->absoluteOffsets, -1);
|
||||||
|
auto it = this->durations->find(adjustedIndex);
|
||||||
|
if (it != this->durations->end()) {
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
size_t TrackListView::HeaderCalculator::TrackListToAdapterIndex(size_t index) {
|
size_t TrackListView::HeaderCalculator::TrackListToAdapterIndex(size_t index) {
|
||||||
return this->ApplyHeaderOffset(index, this->rawOffsets, 1);
|
return this->ApplyHeaderOffset(index, this->rawOffsets, 1);
|
||||||
}
|
}
|
||||||
@ -490,6 +501,11 @@ IScrollAdapter::EntryPtr TrackListView::Adapter::GetEntry(cursespp::ScrollableWi
|
|||||||
album = _TSTR("tracklist_unknown_album");
|
album = _TSTR("tracklist_unknown_album");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto duration = this->parent.headers.DurationFromAdapterIndex(rawIndex);
|
||||||
|
if (duration > 0) {
|
||||||
|
album += " - " + core::duration::Duration(duration);
|
||||||
|
}
|
||||||
|
|
||||||
album = text::Ellipsize(album, this->GetWidth());
|
album = text::Ellipsize(album, this->GetWidth());
|
||||||
|
|
||||||
auto entry = std::make_shared<TrackListEntry>(
|
auto entry = std::make_shared<TrackListEntry>(
|
||||||
@ -503,7 +519,7 @@ IScrollAdapter::EntryPtr TrackListView::Adapter::GetEntry(cursespp::ScrollableWi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t trackIndex = this->parent.headers.AdapterToTrackListIndex(rawIndex);
|
const size_t trackIndex = this->parent.headers.AdapterToTrackListIndex(rawIndex);
|
||||||
TrackPtr track = parent.tracks->Get(trackIndex, sGetAsync);
|
TrackPtr track = parent.tracks->Get(trackIndex, sGetAsync);
|
||||||
|
|
||||||
Color attrs = Color::Default;
|
Color attrs = Color::Default;
|
||||||
|
@ -63,8 +63,9 @@ namespace musik {
|
|||||||
sigslot::signal1<musik::core::library::query::TrackListQueryBase*> Requeried;
|
sigslot::signal1<musik::core::library::query::TrackListQueryBase*> Requeried;
|
||||||
|
|
||||||
/* types */
|
/* types */
|
||||||
typedef std::function<cursespp::Color(TrackPtr, size_t)> RowDecorator;
|
using RowDecorator = std::function<cursespp::Color(TrackPtr, size_t)>;
|
||||||
typedef std::shared_ptr<std::set<size_t> > Headers;
|
using Headers = TrackListQueryBase::Headers;
|
||||||
|
using Durations = TrackListQueryBase::Durations;
|
||||||
|
|
||||||
/* ctor, dtor */
|
/* ctor, dtor */
|
||||||
TrackListView(
|
TrackListView(
|
||||||
@ -118,10 +119,8 @@ namespace musik {
|
|||||||
TrackListEntry(const std::string& str, int index, RowType type)
|
TrackListEntry(const std::string& str, int index, RowType type)
|
||||||
: cursespp::SingleLineEntry(str), index(index), type(type) { }
|
: cursespp::SingleLineEntry(str), index(index), type(type) { }
|
||||||
|
|
||||||
virtual ~TrackListEntry() { }
|
RowType GetType() noexcept { return type; }
|
||||||
|
int GetIndex() noexcept { return index; }
|
||||||
RowType GetType() { return type; }
|
|
||||||
int GetIndex() { return index; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RowType type;
|
RowType type;
|
||||||
@ -132,10 +131,8 @@ namespace musik {
|
|||||||
class Adapter : public cursespp::ScrollAdapterBase {
|
class Adapter : public cursespp::ScrollAdapterBase {
|
||||||
public:
|
public:
|
||||||
Adapter(TrackListView &parent);
|
Adapter(TrackListView &parent);
|
||||||
virtual ~Adapter() { }
|
size_t GetEntryCount() override;
|
||||||
|
EntryPtr GetEntry(cursespp::ScrollableWindow* window, size_t index) override;
|
||||||
virtual size_t GetEntryCount();
|
|
||||||
virtual EntryPtr GetEntry(cursespp::ScrollableWindow* window, size_t index);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TrackListView &parent;
|
TrackListView &parent;
|
||||||
@ -143,16 +140,18 @@ namespace musik {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/* class to help with header offset calculation */
|
/* class to help with header offset calculation. this thing is really gross and
|
||||||
|
should probably be refactored at some point. */
|
||||||
class HeaderCalculator {
|
class HeaderCalculator {
|
||||||
public:
|
public:
|
||||||
static const size_t NO_INDEX = (size_t) -1;
|
static const size_t NO_INDEX = (size_t) -1;
|
||||||
|
|
||||||
void Set(Headers rawOffsets);
|
void Set(Headers rawOffsets, Durations durations);
|
||||||
void Reset();
|
void Reset();
|
||||||
|
bool HeaderAt(size_t index);
|
||||||
size_t AdapterToTrackListIndex(size_t index);
|
size_t AdapterToTrackListIndex(size_t index);
|
||||||
size_t TrackListToAdapterIndex(size_t index);
|
size_t TrackListToAdapterIndex(size_t index);
|
||||||
bool HeaderAt(size_t index);
|
size_t DurationFromAdapterIndex(size_t index);
|
||||||
size_t NextHeaderIndex(size_t selectedIndex);
|
size_t NextHeaderIndex(size_t selectedIndex);
|
||||||
size_t PrevHeaderIndex(size_t selectedIndex);
|
size_t PrevHeaderIndex(size_t selectedIndex);
|
||||||
size_t Count();
|
size_t Count();
|
||||||
@ -162,6 +161,7 @@ namespace musik {
|
|||||||
|
|
||||||
Headers absoluteOffsets;
|
Headers absoluteOffsets;
|
||||||
Headers rawOffsets;
|
Headers rawOffsets;
|
||||||
|
Durations durations;
|
||||||
};
|
};
|
||||||
|
|
||||||
void OnTrackChanged(size_t index, musik::core::TrackPtr track);
|
void OnTrackChanged(size_t index, musik::core::TrackPtr track);
|
||||||
|
Loading…
Reference in New Issue
Block a user