mirror of
https://github.com/clangen/musikcube.git
synced 2025-03-14 13:21:13 +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);
|
||||
if (this->parseHeaders && album != lastAlbum) {
|
||||
headers->insert(index);
|
||||
|
||||
if (!headers->empty()) {
|
||||
(*durations)[lastHeaderIndex] = runningDuration;
|
||||
lastHeaderIndex = index;
|
||||
runningDuration = 0;
|
||||
}
|
||||
|
||||
headers->insert(index);
|
||||
lastAlbum = album;
|
||||
}
|
||||
|
||||
@ -241,7 +240,7 @@ std::string CategoryTrackListQuery::SerializeResult() {
|
||||
void CategoryTrackListQuery::DeserializeResult(const std::string& data) {
|
||||
this->SetStatus(IQuery::Failed);
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -122,7 +122,7 @@ std::string DirectoryTrackListQuery::SerializeResult() {
|
||||
void DirectoryTrackListQuery::DeserializeResult(const std::string& data) {
|
||||
this->SetStatus(IQuery::Failed);
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -119,7 +119,7 @@ std::string GetPlaylistQuery::SerializeResult() {
|
||||
void GetPlaylistQuery::DeserializeResult(const std::string& data) {
|
||||
this->SetStatus(IQuery::Failed);
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -193,7 +193,7 @@ std::string SearchTrackListQuery::SerializeResult() {
|
||||
void SearchTrackListQuery::DeserializeResult(const std::string& data) {
|
||||
this->SetStatus(IQuery::Failed);
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -103,6 +103,7 @@ namespace musik { namespace core { namespace library { namespace query {
|
||||
nlohmann::json output = {
|
||||
{ "result", {
|
||||
{ "headers", *this->GetHeaders() },
|
||||
{ "durations", serialization::DurationMapToJsonMap(*this->GetDurations()) },
|
||||
{ "trackList", serialization::TrackListToJson(*this->GetResult(), true) }
|
||||
}}
|
||||
};
|
||||
@ -110,13 +111,13 @@ namespace musik { namespace core { namespace library { namespace query {
|
||||
}
|
||||
|
||||
void DeserializeTrackListAndHeaders(
|
||||
nlohmann::json &result,
|
||||
nlohmann::json& result,
|
||||
ILibraryPtr library,
|
||||
Result tracks,
|
||||
Headers headers)
|
||||
TrackListQueryBase* query)
|
||||
{
|
||||
serialization::TrackListFromJson(result["trackList"], *tracks, library, true);
|
||||
serialization::JsonArrayToSet<std::set<size_t>, size_t>(result["headers"], *headers);
|
||||
serialization::JsonArrayToSet<std::set<size_t>, size_t>(result["headers"], *query->GetHeaders());
|
||||
serialization::JsonMapToDuration(result["durations"], *query->GetDurations());
|
||||
serialization::TrackListFromJson(result["trackList"], *query->GetResult(), library, true);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -237,6 +237,20 @@ namespace musik { namespace core { namespace library { namespace query {
|
||||
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;
|
||||
if (extractRawQueryResult(responseJson, rawResult)) {
|
||||
if (query) {
|
||||
query->DeserializeResult(rawResult);
|
||||
this->listener->OnClientQuerySucceeded(this, messageId, query);
|
||||
try {
|
||||
query->DeserializeResult(rawResult);
|
||||
this->listener->OnClientQuerySucceeded(this, messageId, query);
|
||||
}
|
||||
catch (...) {
|
||||
this->listener->OnClientQueryFailed(
|
||||
this, messageId, query, QueryError::ParseFailed);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this->listener->OnClientQueryFailed(
|
||||
|
@ -65,6 +65,7 @@ namespace musik { namespace core { namespace net {
|
||||
Disconnected = 2,
|
||||
AuthFailed = 3,
|
||||
QueryNotFound = 4,
|
||||
ParseFailed = 5,
|
||||
};
|
||||
|
||||
enum class ConnectionError : int {
|
||||
|
@ -34,16 +34,26 @@
|
||||
|
||||
#include "pch.hpp"
|
||||
#include "Duration.h"
|
||||
#include "NarrowCast.h"
|
||||
#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 {
|
||||
|
||||
std::string Duration(int seconds) {
|
||||
int mins = (seconds / 60);
|
||||
int secs = seconds - (mins * 60);
|
||||
char buffer[128];
|
||||
snprintf(buffer, sizeof(buffer), "%d:%02d", mins, secs);
|
||||
return std::string(buffer);
|
||||
return formatDuration(seconds);
|
||||
}
|
||||
|
||||
std::string Duration(size_t seconds) {
|
||||
return formatDuration(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(int seconds);
|
||||
std::string Duration(size_t seconds);
|
||||
std::string Duration(double seconds);
|
||||
|
||||
} } }
|
||||
|
@ -131,12 +131,12 @@ void TrackListView::OnTrackListWindowCached(const musik::core::TrackList* track,
|
||||
void TrackListView::OnQueryCompleted(IQuery* query) {
|
||||
if (this->query && query == this->query.get()) {
|
||||
if (this->query->GetStatus() == IQuery::Finished) {
|
||||
bool hadTracks = this->tracks && this->tracks->Count() > 0;
|
||||
bool prevQuerySame = this->lastQueryHash == this->query->GetQueryHash();
|
||||
const bool hadTracks = this->tracks && this->tracks->Count() > 0;
|
||||
const bool prevQuerySame = this->lastQueryHash == this->query->GetQueryHash();
|
||||
|
||||
this->SetTrackListAndUpateEventHandlers(this->query->GetResult());
|
||||
this->AdjustTrackListCacheWindowSize();
|
||||
this->headers.Set(this->query->GetHeaders());
|
||||
this->headers.Set(this->query->GetHeaders(), this->query->GetDurations());
|
||||
this->lastQueryHash = this->query->GetQueryHash();
|
||||
|
||||
/* update our internal state */
|
||||
@ -389,11 +389,12 @@ TrackListView::Adapter::Adapter(TrackListView &parent)
|
||||
|
||||
/* * * * TrackListView::HeaderCalculator * * * */
|
||||
|
||||
void TrackListView::HeaderCalculator::Set(Headers rawOffsets) {
|
||||
void TrackListView::HeaderCalculator::Set(Headers rawOffsets, Durations durations) {
|
||||
this->rawOffsets = rawOffsets;
|
||||
this->durations = durations;
|
||||
this->absoluteOffsets.reset();
|
||||
if (rawOffsets) {
|
||||
this->absoluteOffsets.reset(new std::set<size_t>());
|
||||
this->absoluteOffsets = std::make_shared<std::set<size_t>>();
|
||||
size_t i = 0;
|
||||
for (auto val : (*this->rawOffsets)) {
|
||||
this->absoluteOffsets->insert(val + i);
|
||||
@ -411,6 +412,16 @@ size_t TrackListView::HeaderCalculator::AdapterToTrackListIndex(size_t index) {
|
||||
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) {
|
||||
return this->ApplyHeaderOffset(index, this->rawOffsets, 1);
|
||||
}
|
||||
@ -490,6 +501,11 @@ IScrollAdapter::EntryPtr TrackListView::Adapter::GetEntry(cursespp::ScrollableWi
|
||||
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());
|
||||
|
||||
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);
|
||||
|
||||
Color attrs = Color::Default;
|
||||
|
@ -63,8 +63,9 @@ namespace musik {
|
||||
sigslot::signal1<musik::core::library::query::TrackListQueryBase*> Requeried;
|
||||
|
||||
/* types */
|
||||
typedef std::function<cursespp::Color(TrackPtr, size_t)> RowDecorator;
|
||||
typedef std::shared_ptr<std::set<size_t> > Headers;
|
||||
using RowDecorator = std::function<cursespp::Color(TrackPtr, size_t)>;
|
||||
using Headers = TrackListQueryBase::Headers;
|
||||
using Durations = TrackListQueryBase::Durations;
|
||||
|
||||
/* ctor, dtor */
|
||||
TrackListView(
|
||||
@ -118,10 +119,8 @@ namespace musik {
|
||||
TrackListEntry(const std::string& str, int index, RowType type)
|
||||
: cursespp::SingleLineEntry(str), index(index), type(type) { }
|
||||
|
||||
virtual ~TrackListEntry() { }
|
||||
|
||||
RowType GetType() { return type; }
|
||||
int GetIndex() { return index; }
|
||||
RowType GetType() noexcept { return type; }
|
||||
int GetIndex() noexcept { return index; }
|
||||
|
||||
private:
|
||||
RowType type;
|
||||
@ -132,10 +131,8 @@ namespace musik {
|
||||
class Adapter : public cursespp::ScrollAdapterBase {
|
||||
public:
|
||||
Adapter(TrackListView &parent);
|
||||
virtual ~Adapter() { }
|
||||
|
||||
virtual size_t GetEntryCount();
|
||||
virtual EntryPtr GetEntry(cursespp::ScrollableWindow* window, size_t index);
|
||||
size_t GetEntryCount() override;
|
||||
EntryPtr GetEntry(cursespp::ScrollableWindow* window, size_t index) override;
|
||||
|
||||
private:
|
||||
TrackListView &parent;
|
||||
@ -143,16 +140,18 @@ namespace musik {
|
||||
};
|
||||
|
||||
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 {
|
||||
public:
|
||||
static const size_t NO_INDEX = (size_t) -1;
|
||||
|
||||
void Set(Headers rawOffsets);
|
||||
void Set(Headers rawOffsets, Durations durations);
|
||||
void Reset();
|
||||
bool HeaderAt(size_t index);
|
||||
size_t AdapterToTrackListIndex(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 PrevHeaderIndex(size_t selectedIndex);
|
||||
size_t Count();
|
||||
@ -162,6 +161,7 @@ namespace musik {
|
||||
|
||||
Headers absoluteOffsets;
|
||||
Headers rawOffsets;
|
||||
Durations durations;
|
||||
};
|
||||
|
||||
void OnTrackChanged(size_t index, musik::core::TrackPtr track);
|
||||
|
Loading…
x
Reference in New Issue
Block a user