diff options
Diffstat (limited to 'xbmc/interfaces/json-rpc/AddonsOperations.cpp')
-rw-r--r-- | xbmc/interfaces/json-rpc/AddonsOperations.cpp | 321 |
1 files changed, 321 insertions, 0 deletions
diff --git a/xbmc/interfaces/json-rpc/AddonsOperations.cpp b/xbmc/interfaces/json-rpc/AddonsOperations.cpp new file mode 100644 index 0000000..db6c90a --- /dev/null +++ b/xbmc/interfaces/json-rpc/AddonsOperations.cpp @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2011-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "AddonsOperations.h" + +#include "JSONUtils.h" +#include "ServiceBroker.h" +#include "TextureCache.h" +#include "addons/AddonDatabase.h" +#include "addons/AddonManager.h" +#include "addons/PluginSource.h" +#include "addons/addoninfo/AddonInfo.h" +#include "addons/addoninfo/AddonType.h" +#include "messaging/ApplicationMessenger.h" +#include "utils/FileUtils.h" +#include "utils/StringUtils.h" +#include "utils/Variant.h" + +using namespace JSONRPC; +using namespace ADDON; + +JSONRPC_STATUS CAddonsOperations::GetAddons(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + std::vector<AddonType> addonTypes; + AddonType addonType = CAddonInfo::TranslateType(parameterObject["type"].asString()); + CPluginSource::Content content = CPluginSource::Translate(parameterObject["content"].asString()); + CVariant enabled = parameterObject["enabled"]; + CVariant installed = parameterObject["installed"]; + + // ignore the "content" parameter if the type is specified but not a plugin or script + if (addonType != AddonType::UNKNOWN && addonType != AddonType::PLUGIN && + addonType != AddonType::SCRIPT) + content = CPluginSource::UNKNOWN; + + if (addonType >= AddonType::VIDEO && addonType <= AddonType::EXECUTABLE) + { + addonTypes.push_back(AddonType::PLUGIN); + addonTypes.push_back(AddonType::SCRIPT); + + switch (addonType) + { + case AddonType::VIDEO: + content = CPluginSource::VIDEO; + break; + case AddonType::AUDIO: + content = CPluginSource::AUDIO; + break; + case AddonType::IMAGE: + content = CPluginSource::IMAGE; + break; + case AddonType::GAME: + content = CPluginSource::GAME; + break; + case AddonType::EXECUTABLE: + content = CPluginSource::EXECUTABLE; + break; + default: + break; + } + } + else + addonTypes.push_back(addonType); + + VECADDONS addons; + for (const auto& typeIt : addonTypes) + { + VECADDONS typeAddons; + if (typeIt == AddonType::UNKNOWN) + { + if (!enabled.isBoolean()) //All + { + if (!installed.isBoolean() || installed.asBoolean()) + CServiceBroker::GetAddonMgr().GetInstalledAddons(typeAddons); + if (!installed.isBoolean() || (installed.isBoolean() && !installed.asBoolean())) + CServiceBroker::GetAddonMgr().GetInstallableAddons(typeAddons); + } + else if (enabled.asBoolean() && (!installed.isBoolean() || installed.asBoolean())) //Enabled + CServiceBroker::GetAddonMgr().GetAddons(typeAddons); + else if (!installed.isBoolean() || installed.asBoolean()) + CServiceBroker::GetAddonMgr().GetDisabledAddons(typeAddons); + } + else + { + if (!enabled.isBoolean()) //All + { + if (!installed.isBoolean() || installed.asBoolean()) + CServiceBroker::GetAddonMgr().GetInstalledAddons(typeAddons, typeIt); + if (!installed.isBoolean() || (installed.isBoolean() && !installed.asBoolean())) + CServiceBroker::GetAddonMgr().GetInstallableAddons(typeAddons, typeIt); + } + else if (enabled.asBoolean() && (!installed.isBoolean() || installed.asBoolean())) //Enabled + CServiceBroker::GetAddonMgr().GetAddons(typeAddons, typeIt); + else if (!installed.isBoolean() || installed.asBoolean()) + CServiceBroker::GetAddonMgr().GetDisabledAddons(typeAddons, typeIt); + } + + addons.insert(addons.end(), typeAddons.begin(), typeAddons.end()); + } + + // remove library addons + for (int index = 0; index < (int)addons.size(); index++) + { + std::shared_ptr<CPluginSource> plugin; + if (content != CPluginSource::UNKNOWN) + plugin = std::dynamic_pointer_cast<CPluginSource>(addons.at(index)); + + if ((addons.at(index)->Type() <= AddonType::UNKNOWN || + addons.at(index)->Type() >= AddonType::MAX_TYPES) || + ((content != CPluginSource::UNKNOWN && plugin == NULL) || + (plugin != NULL && !plugin->Provides(content)))) + { + addons.erase(addons.begin() + index); + index--; + } + } + + int start, end; + HandleLimits(parameterObject, result, addons.size(), start, end); + + CAddonDatabase addondb; + for (int index = start; index < end; index++) + FillDetails(addons.at(index), parameterObject["properties"], result["addons"], addondb, true); + + return OK; +} + +JSONRPC_STATUS CAddonsOperations::GetAddonDetails(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + std::string id = parameterObject["addonid"].asString(); + AddonPtr addon; + if (!CServiceBroker::GetAddonMgr().GetAddon(id, addon, OnlyEnabled::CHOICE_NO) || + addon.get() == NULL || addon->Type() <= AddonType::UNKNOWN || + addon->Type() >= AddonType::MAX_TYPES) + return InvalidParams; + + CAddonDatabase addondb; + FillDetails(addon, parameterObject["properties"], result["addon"], addondb); + + return OK; +} + +JSONRPC_STATUS CAddonsOperations::SetAddonEnabled(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + std::string id = parameterObject["addonid"].asString(); + AddonPtr addon; + if (!CServiceBroker::GetAddonMgr().GetAddon(id, addon, OnlyEnabled::CHOICE_NO) || + addon == nullptr || addon->Type() <= AddonType::UNKNOWN || + addon->Type() >= AddonType::MAX_TYPES) + return InvalidParams; + + bool disabled = false; + if (parameterObject["enabled"].isBoolean()) + { + disabled = !parameterObject["enabled"].asBoolean(); + } + // we need to toggle the current disabled state of the addon + else if (parameterObject["enabled"].isString()) + { + disabled = !CServiceBroker::GetAddonMgr().IsAddonDisabled(id); + } + else + { + return InvalidParams; + } + + bool success = disabled + ? CServiceBroker::GetAddonMgr().DisableAddon(id, AddonDisabledReason::USER) + : CServiceBroker::GetAddonMgr().EnableAddon(id); + + return success ? ACK : InvalidParams; +} + +JSONRPC_STATUS CAddonsOperations::ExecuteAddon(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + std::string id = parameterObject["addonid"].asString(); + AddonPtr addon; + if (!CServiceBroker::GetAddonMgr().GetAddon(id, addon, OnlyEnabled::CHOICE_YES) || + addon.get() == NULL || addon->Type() < AddonType::VISUALIZATION || + addon->Type() >= AddonType::MAX_TYPES) + return InvalidParams; + + std::string argv; + CVariant params = parameterObject["params"]; + if (params.isObject()) + { + for (CVariant::const_iterator_map it = params.begin_map(); it != params.end_map(); ++it) + { + if (it != params.begin_map()) + argv += ","; + argv += it->first + "=" + it->second.asString(); + } + } + else if (params.isArray()) + { + for (CVariant::const_iterator_array it = params.begin_array(); it != params.end_array(); ++it) + { + if (it != params.begin_array()) + argv += ","; + argv += StringUtils::Paramify(it->asString()); + } + } + else if (params.isString()) + { + if (!params.empty()) + argv = StringUtils::Paramify(params.asString()); + } + + std::string cmd; + if (params.empty()) + cmd = StringUtils::Format("RunAddon({})", id); + else + cmd = StringUtils::Format("RunAddon({}, {})", id, argv); + + if (params["wait"].asBoolean()) + CServiceBroker::GetAppMessenger()->SendMsg(TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr, cmd); + else + CServiceBroker::GetAppMessenger()->PostMsg(TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr, cmd); + + return ACK; +} + +static CVariant Serialize(const AddonPtr& addon) +{ + CVariant variant; + variant["addonid"] = addon->ID(); + variant["type"] = CAddonInfo::TranslateType(addon->Type(), false); + variant["name"] = addon->Name(); + variant["version"] = addon->Version().asString(); + variant["summary"] = addon->Summary(); + variant["description"] = addon->Description(); + variant["path"] = addon->Path(); + variant["author"] = addon->Author(); + variant["thumbnail"] = addon->Icon(); + variant["disclaimer"] = addon->Disclaimer(); + variant["fanart"] = addon->FanArt(); + + variant["dependencies"] = CVariant(CVariant::VariantTypeArray); + for (const auto& dep : addon->GetDependencies()) + { + CVariant info(CVariant::VariantTypeObject); + info["addonid"] = dep.id; + info["minversion"] = dep.versionMin.asString(); + info["version"] = dep.version.asString(); + info["optional"] = dep.optional; + variant["dependencies"].push_back(std::move(info)); + } + if (addon->LifecycleState() == AddonLifecycleState::BROKEN) + variant["broken"] = addon->LifecycleStateDescription(); + else + variant["broken"] = false; + if (addon->LifecycleState() == AddonLifecycleState::DEPRECATED) + variant["deprecated"] = addon->LifecycleStateDescription(); + else + variant["deprecated"] = false; + variant["extrainfo"] = CVariant(CVariant::VariantTypeArray); + for (const auto& kv : addon->ExtraInfo()) + { + CVariant info(CVariant::VariantTypeObject); + info["key"] = kv.first; + info["value"] = kv.second; + variant["extrainfo"].push_back(std::move(info)); + } + variant["rating"] = -1; + return variant; +} + +void CAddonsOperations::FillDetails(const std::shared_ptr<ADDON::IAddon>& addon, + const CVariant& fields, + CVariant& result, + CAddonDatabase& addondb, + bool append /* = false */) +{ + if (addon.get() == NULL) + return; + + CVariant addonInfo = Serialize(addon); + + CVariant object; + object["addonid"] = addonInfo["addonid"]; + object["type"] = addonInfo["type"]; + + for (unsigned int index = 0; index < fields.size(); index++) + { + std::string field = fields[index].asString(); + + // we need to manually retrieve the enabled / installed state of every addon + // from the addon database because it can't be read from addon.xml + if (field == "enabled") + { + object[field] = !CServiceBroker::GetAddonMgr().IsAddonDisabled(addon->ID()); + } + else if (field == "installed") + { + object[field] = CServiceBroker::GetAddonMgr().IsAddonInstalled(addon->ID()); + } + else if (field == "fanart" || field == "thumbnail") + { + std::string url = addonInfo[field].asString(); + // We need to check the existence of fanart and thumbnails as the addon simply + // holds where the art will be, not whether it exists. + bool needsRecaching; + std::string image = CServiceBroker::GetTextureCache()->CheckCachedImage(url, needsRecaching); + if (!image.empty() || CFileUtils::Exists(url)) + object[field] = CTextureUtils::GetWrappedImageURL(url); + else + object[field] = ""; + } + else if (addonInfo.isMember(field)) + object[field] = addonInfo[field]; + } + + if (append) + result.append(object); + else + result = object; +} |