Added MasterLibrary in the same vein as MasterTransport to easily

support dynamically switching between underlying implementations.
This commit is contained in:
casey langen 2020-10-10 01:36:31 -07:00
parent 5f0bb39a49
commit 3ae8aed29a
16 changed files with 283 additions and 42 deletions

View File

@ -172,7 +172,7 @@ mcsdk_export void mcsdk_context_init(mcsdk_context** context) {
auto internal = new mcsdk_context_internal();
LibraryFactory::Initialize(internal->message_queue);
internal->library = LibraryFactory::Instance().Default();
internal->library = LibraryFactory::Instance().DefaultLocalLibrary();
internal->playback = new PlaybackService(internal->message_queue, internal->library);
internal->metadata = new LocalMetadataProxy(internal->library);
internal->preferences = Preferences::ForComponent(prefs::components::Settings);

View File

@ -498,6 +498,7 @@
<ClCompile Include="library\LocalLibrary.cpp" />
<ClCompile Include="library\LibraryFactory.cpp" />
<ClCompile Include="library\LocalMetadataProxy.cpp" />
<ClCompile Include="library\MasterLibrary.cpp" />
<ClCompile Include="library\metadata\MetadataMap.cpp" />
<ClCompile Include="library\metadata\MetadataMapList.cpp" />
<ClCompile Include="library\QueryRegistry.cpp" />
@ -581,6 +582,7 @@
<ClInclude Include="library\LibraryFactory.h" />
<ClInclude Include="library\LocalLibraryConstants.h" />
<ClInclude Include="library\LocalMetadataProxy.h" />
<ClInclude Include="library\MasterLibrary.h" />
<ClInclude Include="library\metadata\MetadataMap.h" />
<ClInclude Include="library\metadata\MetadataMapList.h" />
<ClInclude Include="library\QueryBase.h" />

View File

@ -256,6 +256,9 @@
<ClCompile Include="library\query\TrackMetadataBatchQuery.cpp">
<Filter>src\library\query</Filter>
</ClCompile>
<ClCompile Include="library\MasterLibrary.cpp">
<Filter>src\library</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.hpp">
@ -627,5 +630,8 @@
<ClInclude Include="library\query\TrackMetadataBatchQuery.h">
<Filter>src\library\query</Filter>
</ClInclude>
<ClInclude Include="library\MasterLibrary.h">
<Filter>src\library</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -79,11 +79,7 @@ namespace musik { namespace core {
virtual ~ILibrary() { }
virtual int Enqueue(
QueryPtr query,
unsigned int options = 0,
Callback = Callback()) = 0;
virtual int Enqueue(QueryPtr query, unsigned int options = 0, Callback = Callback()) = 0;
virtual IIndexer *Indexer() = 0;
virtual int Id() = 0;
virtual const std::string& Name() = 0;

View File

@ -914,7 +914,7 @@ void Indexer::RunAnalyzers() {
getNextTrack.ResetAndUnbind();
auto track = std::make_shared<IndexerTrack>(trackId);
TrackMetadataQuery query(track, LibraryFactory::Instance().Default());
TrackMetadataQuery query(track, LibraryFactory::Instance().DefaultLocalLibrary());
query.Run(this->dbConnection);
if (query.GetStatus() == IQuery::Finished) {

View File

@ -58,7 +58,8 @@ LibraryFactory& LibraryFactory::Instance() {
};
LibraryFactory::LibraryFactory() {
this->CreateLibrary("Local Library", ILibrary::Type::Local);
this->CreateLibrary("default-local-library", ILibrary::Type::Local);
this->CreateLibrary("default-remote-library", ILibrary::Type::Remote);
}
LibraryFactory::~LibraryFactory() {
@ -133,10 +134,19 @@ LibraryFactory::LibraryVector LibraryFactory::Libraries() {
return LibraryFactory::Instance().libraries;
}
ILibraryPtr LibraryFactory::Default() {
ILibraryPtr LibraryFactory::DefaultLocalLibrary() {
return LibraryFactory::Instance().libraries.at(0);
}
ILibraryPtr LibraryFactory::DefaultRemoteLibrary() {
return LibraryFactory::Instance().libraries.at(1);
}
ILibraryPtr LibraryFactory::DefaultLibrary(ILibrary::Type type) {
return type == ILibrary::Type::Local
? DefaultLocalLibrary() : DefaultRemoteLibrary();
}
ILibraryPtr LibraryFactory::GetLibrary(int identifier) {
if (identifier) {
LibraryMap::iterator lib = this->libraryMap.find(identifier);

View File

@ -58,7 +58,10 @@ namespace musik { namespace core {
static LibraryFactory& Instance();
static void Shutdown();
ILibraryPtr Default();
ILibraryPtr DefaultLocalLibrary();
ILibraryPtr DefaultRemoteLibrary();
ILibraryPtr DefaultLibrary(ILibrary::Type type);
LibraryVector Libraries();
ILibraryPtr CreateLibrary(const std::string& name, ILibrary::Type type);

View File

@ -785,7 +785,7 @@ bool LocalMetadataProxy::SendRawQuery(
{
try {
nlohmann::json json = nlohmann::json::parse(query);
auto localLibrary = LibraryFactory::Instance().Default();
auto localLibrary = LibraryFactory::Instance().DefaultLocalLibrary();
std::string name = json["name"];
auto libraryQuery = QueryRegistry::CreateLocalQueryFor(name, query, localLibrary);
if (libraryQuery) {

View File

@ -0,0 +1,133 @@
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2004-2019 musikcube team
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// * Neither the name of the author nor the names of other contributors may
// be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////////
#include "pch.hpp"
#include "MasterLibrary.h"
#include "LibraryFactory.h"
#include <core/support/Preferences.h>
#include <core/support/PreferenceKeys.h>
using namespace musik::core;
using namespace musik::core::library;
using namespace musik::core::runtime;
MasterLibrary::MasterLibrary() {
this->LoadDefaultLibrary();
}
MasterLibrary::~MasterLibrary() {
}
ILibraryPtr MasterLibrary::Get() const {
std::unique_lock<decltype(this->libraryMutex)> lock(this->libraryMutex);
return this->wrappedLibrary;
}
int MasterLibrary::Enqueue(QueryPtr query, unsigned int options, Callback callback) {
return Get()->Enqueue(query, options, callback);
}
IIndexer* MasterLibrary::Indexer() {
return Get()->Indexer();
}
int MasterLibrary::Id() {
return Get()->Id();
}
const std::string& MasterLibrary::Name() {
return Get()->Name();
}
void MasterLibrary::SetMessageQueue(IMessageQueue& queue) {
this->Get()->SetMessageQueue(queue);
}
IMessageQueue& MasterLibrary::GetMessageQueue() {
return Get()->GetMessageQueue();
}
MasterLibrary::IResourceLocator& MasterLibrary::GetResourceLocator() {
return Get()->GetResourceLocator();
}
bool MasterLibrary::IsConfigured() {
return Get()->IsConfigured();
}
MasterLibrary::ConnectionState MasterLibrary::GetConnectionState() const {
return Get()->GetConnectionState();
}
MasterLibrary::Type MasterLibrary::GetType() const {
return Get()->GetType();
}
void MasterLibrary::Close() {
Get()->Close();
}
void MasterLibrary::LoadDefaultLibrary() {
std::unique_lock<decltype(this->libraryMutex)> lock(this->libraryMutex);
auto prevWrappedLibrary = this->wrappedLibrary;
auto prefs = Preferences::ForComponent(prefs::components::Settings);
auto libraryType = (ILibrary::Type) prefs->GetInt(
prefs::keys::LibraryType, (int) ILibrary::Type::Local);
this->wrappedLibrary = LibraryFactory::Instance().DefaultLibrary(libraryType);
if (prevWrappedLibrary != wrappedLibrary) {
if (prevWrappedLibrary) {
prevWrappedLibrary->QueryCompleted.disconnect(this);
prevWrappedLibrary->ConnectionStateChanged.disconnect(this);
}
if (this->wrappedLibrary) {
this->wrappedLibrary->QueryCompleted.connect(this, &MasterLibrary::OnQueryCompleted);
this->wrappedLibrary->ConnectionStateChanged.connect(this, &MasterLibrary::OnConectionStateChanged);
}
}
}
void MasterLibrary::OnQueryCompleted(musik::core::db::IQuery* query) {
this->QueryCompleted(query);
}
void MasterLibrary::OnConectionStateChanged(ConnectionState state) {
this->ConnectionStateChanged(state);
}

View File

@ -0,0 +1,83 @@
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2004-2019 musikcube team
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// * Neither the name of the author nor the names of other contributors may
// be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////////
#pragma once
#include <core/config.h>
#include <core/db/Connection.h>
#include <core/library/ILibrary.h>
#include <core/library/IIndexer.h>
#include <core/library/IQuery.h>
#include <core/library/QueryBase.h>
#include <mutex>
#include <sigslot/sigslot.h>
namespace musik { namespace core { namespace library {
class MasterLibrary: public ILibrary, public sigslot::has_slots<> {
public:
MasterLibrary();
virtual ~MasterLibrary();
virtual int Enqueue(QueryPtr query, unsigned int options = 0, Callback = Callback()) override;
virtual musik::core::IIndexer *Indexer() override;
virtual int Id() override;
virtual const std::string& Name() override;
virtual void SetMessageQueue(musik::core::runtime::IMessageQueue& queue) override;
virtual musik::core::runtime::IMessageQueue& GetMessageQueue() override;
virtual IResourceLocator& GetResourceLocator() override;
virtual bool IsConfigured() override;
virtual ConnectionState GetConnectionState() const override;
virtual Type GetType() const override;
virtual void Close() override;
ILibraryPtr Wrapped() const { return Get(); }
void LoadDefaultLibrary();
private:
ILibraryPtr Get() const;
void OnQueryCompleted(musik::core::db::IQuery* query);
void OnConectionStateChanged(ConnectionState state);
ILibraryPtr wrappedLibrary;
mutable std::recursive_mutex libraryMutex;
};
} } }

View File

@ -142,7 +142,7 @@ void RemoteLibrary::Close() {
}
bool RemoteLibrary::IsConfigured() {
return LibraryFactory::Instance().Default()->IsConfigured(); /* CAL TODO FIXME */
return LibraryFactory::Instance().DefaultLocalLibrary()->IsConfigured(); /* CAL TODO FIXME */
}
static inline bool isQueryDone(RemoteLibrary::Query query) {
@ -166,7 +166,7 @@ bool RemoteLibrary::IsQueryInFlight(Query query) {
int RemoteLibrary::Enqueue(QueryPtr query, unsigned int options, Callback callback) {
if (QueryRegistry::IsLocalOnlyQuery(query->Name())) {
auto defaultLocalLibrary = LibraryFactory::Instance().Default();
auto defaultLocalLibrary = LibraryFactory::Instance().DefaultLocalLibrary();
return defaultLocalLibrary->Enqueue(query, options, callback);
}
@ -275,7 +275,7 @@ void RemoteLibrary::RunQueryOnLoopback(QueryContextPtr context) {
locally, serialize the result, then deserialize it again to emulate the entire
flow. */
auto localLibrary = LibraryFactory::Instance().Default();
auto localLibrary = LibraryFactory::Instance().DefaultLocalLibrary();
localLibrary->SetMessageQueue(*this->messageQueue);
auto localQuery = QueryRegistry::CreateLocalQueryFor(

View File

@ -53,7 +53,7 @@
#include <core/debug.h>
#include <core/i18n/Locale.h>
#include <core/library/LibraryFactory.h>
#include <core/library/RemoteLibrary.h>
#include <core/library/MasterLibrary.h>
#include <core/plugin/Plugins.h>
#include <core/support/PreferenceKeys.h>
#include <core/sdk/constants.h>
@ -118,8 +118,7 @@ int main(int argc, char* argv[]) {
musik::debug::Start({ fileLogger, consoleLogger });
LibraryFactory::Initialize(Window::MessageQueue());
//ILibraryPtr library = LibraryFactory::Instance().Default();
ILibraryPtr library = LibraryFactory::Instance().CreateLibrary("remote", ILibrary::Type::Remote);
auto library = std::make_shared<musik::core::library::MasterLibrary>();
{
auto prefs = Preferences::ForComponent(

View File

@ -50,15 +50,17 @@ using namespace musik::core::library;
using namespace musik::core::net;
using namespace cursespp;
static inline std::string resolveErrorMessage(const ILibrary* library) {
using MasterLibraryPtr = LibraryNotConnectedLayout::MasterLibraryPtr;
static inline std::string resolveErrorMessage(MasterLibraryPtr library) {
static const std::map<WebSocketClient::ConnectionError, std::string> kStateToErrorString = {
{ WebSocketClient::ConnectionError::ClosedByServer, "library_error_closed_by_server" },
{ WebSocketClient::ConnectionError::ConnectionFailed, "library_error_connection_failed" },
{ WebSocketClient::ConnectionError::InvalidPassword, "library_error_invalid_password" },
};
if (library->GetType() == ILibrary::Type::Remote) {
const RemoteLibrary* remoteLibrary = static_cast<const RemoteLibrary*>(library);
auto remoteLibrary = dynamic_cast<const RemoteLibrary*>(library->Wrapped().get());
if (remoteLibrary) {
auto error = remoteLibrary->WebSocketClient().LastConnectionError();
auto it = kStateToErrorString.find(error);
if (it != kStateToErrorString.end()) {
@ -74,9 +76,10 @@ static inline std::string resolveErrorMessage(const ILibrary* library) {
return _TSTR("library_error_unknown");
}
static inline std::string resolveMessageText(const ILibrary* library) {
if (library->GetType() == ILibrary::Type::Remote) {
auto host = dynamic_cast<const RemoteLibrary*>(library)->WebSocketClient().Uri();
static inline std::string resolveMessageText(MasterLibraryPtr library) {
auto remoteLibrary = dynamic_cast<const RemoteLibrary*>(library->Wrapped().get());
if (remoteLibrary) {
auto host = remoteLibrary->WebSocketClient().Uri();
if (host.find("ws://") == 0) { host = host.substr(5); }
if (host.size()) {
return u8fmt(_TSTR("library_not_connected_with_hostname"), host.c_str());
@ -85,7 +88,7 @@ static inline std::string resolveMessageText(const ILibrary* library) {
return _TSTR("library_not_connected");
}
LibraryNotConnectedLayout::LibraryNotConnectedLayout(ILibraryPtr library)
LibraryNotConnectedLayout::LibraryNotConnectedLayout(MasterLibraryPtr library)
: LayoutBase()
, library(library) {
this->library->ConnectionStateChanged.connect(this, &LibraryNotConnectedLayout::OnLibraryStateChanged);
@ -122,10 +125,9 @@ void LibraryNotConnectedLayout::OnLibraryStateChanged(ILibrary::ConnectionState
}
void LibraryNotConnectedLayout::UpdateErrorText() {
auto library = this->library.get();
auto error = u8fmt(_TSTR("library_error_format"), resolveErrorMessage(library).c_str());
auto error = u8fmt(_TSTR("library_error_format"), resolveErrorMessage(this->library).c_str());
this->errorText->SetText(error);
this->messageText->SetText(resolveMessageText(library));
this->messageText->SetText(resolveMessageText(this->library));
}
void LibraryNotConnectedLayout::SetShortcutsWindow(cursespp::ShortcutsWindow* shortcuts) {

View File

@ -35,7 +35,7 @@
#pragma once
#include <cursespp/LayoutBase.h>
#include <core/library/ILibrary.h>
#include <core/library/MasterLibrary.h>
#include <cursespp/ITopLevelLayout.h>
#include <cursespp/TextLabel.h>
#include <cursespp/ShortcutsWindow.h>
@ -48,7 +48,9 @@ namespace musik { namespace cube {
public sigslot::has_slots<>
{
public:
LibraryNotConnectedLayout(musik::core::ILibraryPtr library);
using MasterLibraryPtr = std::shared_ptr<musik::core::library::MasterLibrary>;
LibraryNotConnectedLayout(MasterLibraryPtr library);
virtual void OnLayout() override;
virtual bool KeyPress(const std::string& kn) override;
@ -61,7 +63,7 @@ namespace musik { namespace cube {
private:
void UpdateErrorText();
musik::core::ILibraryPtr library;
MasterLibraryPtr library;
std::shared_ptr<cursespp::TextLabel> messageText;
std::shared_ptr<cursespp::TextLabel> errorText;
std::shared_ptr<cursespp::TextLabel> helpText;

View File

@ -66,6 +66,8 @@ using namespace musik::core::library;
using namespace musik::core::runtime;
using namespace cursespp;
using MasterLibraryPtr = MainLayout::MasterLibraryPtr;
static UpdateCheck updateCheck;
static inline void updateSyncingText(TextLabel* label, int updates) {
@ -86,18 +88,21 @@ static inline void updateSyncingText(TextLabel* label, int updates) {
}
}
static inline void updateRemoteLibraryConnectedText(TextLabel* label, const ILibrary* library) {
auto host = static_cast<const RemoteLibrary*>(library)->WebSocketClient().Uri();
if (host.find("ws://") == 0) { host = host.substr(5); }
auto value = u8fmt(_TSTR("library_remote_connected_banner"), host.c_str());
label->SetText(value, cursespp::text::AlignCenter);
static inline void updateRemoteLibraryConnectedText(TextLabel* label, MasterLibraryPtr library) {
RemoteLibrary* remoteLibrary = dynamic_cast<RemoteLibrary*>(library->Wrapped().get());
if (remoteLibrary) {
std::string host = remoteLibrary->WebSocketClient().Uri();
if (host.find("ws://") == 0) { host = host.substr(5); }
auto value = u8fmt(_TSTR("library_remote_connected_banner"), host.c_str());
label->SetText(value, cursespp::text::AlignCenter);
}
}
MainLayout::MainLayout(
cursespp::App& app,
musik::cube::ConsoleLogger* logger,
musik::core::audio::PlaybackService& playback,
ILibraryPtr library)
MasterLibraryPtr library)
: shortcutsFocused(false)
, syncUpdateCount(0)
, library(library)
@ -153,7 +158,7 @@ void MainLayout::UpdateTopBannerText() {
updateSyncingText(this->topBanner.get(), this->syncUpdateCount);
}
else if (libraryType == ILibrary::Type::Remote) {
updateRemoteLibraryConnectedText(this->topBanner.get(), this->library.get());
updateRemoteLibraryConnectedText(this->topBanner.get(), this->library);
}
}

View File

@ -41,10 +41,8 @@
#include <core/audio/PlaybackService.h>
#include <core/support/Preferences.h>
#include <core/library/ILibrary.h>
#include <core/runtime/IMessageTarget.h>
#include <core/audio/MasterTransport.h>
#include <core/library/MasterLibrary.h>
#include <app/util/ConsoleLogger.h>
@ -54,11 +52,13 @@ namespace musik {
namespace cube {
class MainLayout : public cursespp::AppLayout {
public:
using MasterLibraryPtr = std::shared_ptr<musik::core::library::MasterLibrary>;
MainLayout(
cursespp::App& app,
ConsoleLogger* logger,
musik::core::audio::PlaybackService& playback,
musik::core::ILibraryPtr library);
MasterLibraryPtr library);
virtual ~MainLayout();
@ -95,7 +95,7 @@ namespace musik {
std::shared_ptr<cursespp::LayoutBase> hotkeysLayout;
std::shared_ptr<cursespp::LayoutBase> lyricsLayout;
musik::core::audio::PlaybackService& playback;
musik::core::ILibraryPtr library;
MasterLibraryPtr library;
bool shortcutsFocused;
int syncUpdateCount;
};