bool CAddonDatabase::GetAddon(int id, AddonPtr& addon) { try { if (NULL == m_pDB.get()) return false; if (NULL == m_pDS2.get()) return false; CStdString sql = PrepareSQL("select * from addon where id=%i",id); m_pDS2->query(sql.c_str()); if (!m_pDS2->eof()) { AddonProps props(m_pDS2->fv("addonID" ).get_asString(), TranslateType(m_pDS2->fv("type").get_asString()), m_pDS2->fv("version").get_asString()); props.name = m_pDS2->fv("name").get_asString(); props.summary = m_pDS2->fv("summary").get_asString(); props.description = m_pDS2->fv("description").get_asString(); props.changelog = m_pDS2->fv("changelog").get_asString(); props.path = m_pDS2->fv("path").get_asString(); props.icon = m_pDS2->fv("icon").get_asString(); props.fanart = m_pDS2->fv("fanart").get_asString(); props.author = m_pDS2->fv("author").get_asString(); props.disclaimer = m_pDS2->fv("disclaimer").get_asString(); sql = PrepareSQL("select reason from broken where addonID='%s'",props.id.c_str()); m_pDS2->query(sql.c_str()); if (!m_pDS2->eof()) props.broken = m_pDS2->fv(0).get_asString(); sql = PrepareSQL("select key,value from addonextra where id=%i", id); m_pDS2->query(sql.c_str()); while (!m_pDS2->eof()) { props.extrainfo.insert(make_pair(m_pDS2->fv(0).get_asString(), m_pDS2->fv(1).get_asString())); m_pDS2->next(); } addon = CAddonMgr::AddonFromProps(props); return NULL != addon.get(); } } catch (...) { CLog::Log(LOGERROR, "%s failed on addon %i", __FUNCTION__, id); } addon.reset(); return false; }
JSONRPC_STATUS CAddonsOperations::ExecuteAddon(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) { string id = parameterObject["addonid"].asString(); AddonPtr addon; if (!CAddonMgr::GetInstance().GetAddon(id, addon) || addon.get() == NULL || addon->Type() < ADDON_SKIN || addon->Type() >= ADDON_MAX) return InvalidParams; 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.size() == 0) cmd = StringUtils::Format("RunAddon(%s)", id.c_str()); else cmd = StringUtils::Format("RunAddon(%s, %s)", id.c_str(), argv.c_str()); if (params["wait"].asBoolean()) CApplicationMessenger::GetInstance().SendMsg(TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr, cmd); else CApplicationMessenger::GetInstance().PostMsg(TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr, cmd); return ACK; }
void CAddonsOperations::FillDetails(AddonPtr 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 = CTextureCache::GetInstance().CheckCachedImage(url, needsRecaching); if (!image.empty() || CFile::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; }
bool CAddonInstaller::CheckDependencies(const AddonPtr &addon, std::vector<std::string>& preDeps, CAddonDatabase &database) { if (!addon.get()) return true; // a NULL addon has no dependencies ADDONDEPS deps = addon->GetDeps(); database.Open(); for (ADDONDEPS::const_iterator i = deps.begin(); i != deps.end(); ++i) { const CStdString &addonID = i->first; const AddonVersion &version = i->second.first; bool optional = i->second.second; AddonPtr dep; bool haveAddon = CAddonMgr::Get().GetAddon(addonID, dep); if ((haveAddon && !dep->MeetsVersion(version)) || (!haveAddon && !optional)) { // we have it but our version isn't good enough, or we don't have it and we need it if (!database.GetAddon(addonID, dep) || !dep->MeetsVersion(version)) { // we don't have it in a repo, or we have it but the version isn't good enough, so dep isn't satisfied. CLog::Log(LOGDEBUG, "Addon %s requires %s version %s which is not available", addon->ID().c_str(), addonID.c_str(), version.c_str()); database.Close(); return false; } } // at this point we have our dep, or the dep is optional (and we don't have it) so check that it's OK as well // TODO: should we assume that installed deps are OK? if (dep && std::find(preDeps.begin(), preDeps.end(), dep->ID()) == preDeps.end()) { if (!CheckDependencies(dep, preDeps, database)) { database.Close(); return false; } preDeps.push_back(dep->ID()); } } database.Close(); return true; }
bool CAddonDatabase::GetAddon(int id, AddonPtr &addon) { try { if (NULL == m_pDB.get()) return false; if (NULL == m_pDS2.get()) return false; std::string sql = "SELECT addon.*," " broken.reason," " addonextra.key, addonextra.value," " dependencies.addon, dependencies.version, dependencies.optional" " FROM addon" " LEFT JOIN broken" " ON broken.addonID = addon.addonID" " LEFT JOIN addonextra" " ON addonextra.id = addon.id" " LEFT JOIN dependencies" " ON dependencies.id = addon.id"; sql += PrepareSQL(" WHERE addon.id=%i", id); m_pDS2->query(sql.c_str()); if (!m_pDS2->eof()) { const dbiplus::query_data &data = m_pDS2->get_result_set().records; const dbiplus::sql_record* const record = data[0]; AddonProps props(record->at(addon_addonID).get_asString(), TranslateType(record->at(addon_type).get_asString()), record->at(addon_version).get_asString(), record->at(addon_minversion).get_asString()); props.name = record->at(addon_name).get_asString(); props.summary = record->at(addon_summary).get_asString(); props.description = record->at(addon_description).get_asString(); props.changelog = record->at(addon_changelog).get_asString(); props.path = record->at(addon_path).get_asString(); props.icon = record->at(addon_icon).get_asString(); props.fanart = record->at(addon_fanart).get_asString(); props.author = record->at(addon_author).get_asString(); props.disclaimer = record->at(addon_disclaimer).get_asString(); props.broken = record->at(broken_reason).get_asString(); /* while this is a cartesion join and we'll typically get multiple rows, we rely on the fact that extrainfo and dependencies are maps, so insert() will insert the first instance only */ for (dbiplus::query_data::const_iterator i = data.begin(); i != data.end(); ++i) { const dbiplus::sql_record* const record = *i; if (!record->at(addonextra_key).get_asString().empty()) props.extrainfo.insert(make_pair(record->at(addonextra_key).get_asString(), record->at(addonextra_value).get_asString())); if (!m_pDS2->fv(dependencies_addon).get_asString().empty()) props.dependencies.insert(make_pair(record->at(dependencies_addon).get_asString(), make_pair(AddonVersion(record->at(dependencies_version).get_asString()), record->at(dependencies_optional).get_asBool()))); } addon = CAddonMgr::AddonFromProps(props); return NULL != addon.get(); } } catch (...) { CLog::Log(LOGERROR, "%s failed on addon %i", __FUNCTION__, id); } addon.reset(); return false; }
bool CAddonDatabase::GetAddon(int id, AddonPtr &addon) { try { if (NULL == m_pDB.get()) return false; if (NULL == m_pDS2.get()) return false; std::string sql = "SELECT addon.*," " broken.reason," " addonextra.key, addonextra.value," " dependencies.addon, dependencies.version, dependencies.optional" " FROM addon" " LEFT JOIN broken" " ON broken.addonID = addon.addonID" " LEFT JOIN addonextra" " ON addonextra.id = addon.id" " LEFT JOIN dependencies" " ON dependencies.id = addon.id"; sql += PrepareSQL(" WHERE addon.id=%i", id); m_pDS2->query(sql); if (!m_pDS2->eof()) { const dbiplus::query_data &data = m_pDS2->get_result_set().records; const dbiplus::sql_record* const record = data[0]; CAddonBuilder builder; builder.SetId(record->at(addon_addonID).get_asString()); builder.SetType(TranslateType(record->at(addon_type).get_asString())); builder.SetVersion(AddonVersion(record->at(addon_version).get_asString())); builder.SetMinVersion(AddonVersion(record->at(addon_minversion).get_asString())); builder.SetName(record->at(addon_name).get_asString()); builder.SetSummary(record->at(addon_summary).get_asString()); builder.SetDescription(record->at(addon_description).get_asString()); builder.SetChangelog(record->at(addon_changelog).get_asString()); builder.SetDisclaimer(record->at(addon_disclaimer).get_asString()); builder.SetAuthor(record->at(addon_author).get_asString()); builder.SetBroken(record->at(broken_reason).get_asString()); builder.SetPath(record->at(addon_path).get_asString()); builder.SetIcon(record->at(addon_icon).get_asString()); builder.SetFanart(record->at(addon_fanart).get_asString()); InfoMap extrainfo; ADDONDEPS dependencies; /* while this is a cartesion join and we'll typically get multiple rows, we rely on the fact that extrainfo and dependencies are maps, so insert() will insert the first instance only */ for (dbiplus::query_data::const_iterator i = data.begin(); i != data.end(); ++i) { const dbiplus::sql_record* const record = *i; if (!record->at(addonextra_key).get_asString().empty()) extrainfo.insert(std::make_pair(record->at(addonextra_key).get_asString(), record->at(addonextra_value).get_asString())); if (!m_pDS2->fv(dependencies_addon).get_asString().empty()) dependencies.insert(std::make_pair(record->at(dependencies_addon).get_asString(), std::make_pair(AddonVersion(record->at(dependencies_version).get_asString()), record->at(dependencies_optional).get_asBool()))); } builder.SetExtrainfo(std::move(extrainfo)); builder.SetDependencies(std::move(dependencies)); addon = builder.Build(); return NULL != addon.get(); } } catch (...) { CLog::Log(LOGERROR, "%s failed on addon %i", __FUNCTION__, id); } addon.reset(); return false; }
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, ADDON::ADDON_UNKNOWN, false) || addon.get() == NULL || addon->Type() <= ADDON_UNKNOWN || addon->Type() >= ADDON_MAX) return InvalidParams; CAddonDatabase addondb; FillDetails(addon, parameterObject["properties"], result["addon"], addondb); return OK; }
bool CRetroPlayerDialogs::InstallGameClientDialog(const CFileItem &file, GameClientPtr &result) { VECADDONS addons; CGameManager::GetAllGameClients(addons); map<string, GameClientPtr> candidates; for (VECADDONS::const_iterator itRemote = addons.begin(); itRemote != addons.end(); ++itRemote) { if (!(*itRemote)->IsType(ADDON_GAMEDLL)) continue; GameClientPtr gc = boost::dynamic_pointer_cast<CGameClient>(*itRemote); // Only add game clients to the list if they provide extensions or platforms if (!gc || (gc->GetExtensions().empty() /* && gc->GetPlatforms().empty() */)) continue; if (!gc->CanOpen(file)) continue; // If the game client is already installed and enabled, exclude it from the list AddonPtr addon; if (CAddonMgr::Get().GetAddon(gc->ID(), addon, ADDON_GAMEDLL, true)) continue; // If GetAddon() returns false, but addon is non-NULL, then the add-on exists but is disabled bool bEnabled = (addon.get() == NULL); bool bBroken = !(*itRemote)->Props().broken.empty(); if (bEnabled && bBroken) continue; string strName = gc->Name(); // Append "(Disabled)" to the name if the add-on is disabled if (!bEnabled) strName = StringUtils::Format("%s (%s)", strName.c_str(), g_localizeStrings.Get(1223).c_str()); candidates[strName] = gc; } if (candidates.empty()) { CLog::Log(LOGDEBUG, "RetroPlayer: No compatible game clients for installation"); // "Playback failed" // "No compatible emulators found for file:" // "FILENAME" CGUIDialogOK::ShowAndGetInput(16026, 16023, URIUtils::GetFileName(file.GetPath()), 0); return false; } // CContextButtons doesn't support keying by string, only int, so use a // parallel array to track the string values (client name) CContextButtons choicesInt; unsigned int i = 0; // Vector to translate button IDs to game client pointers vector<string> choicesStr; for (map<string, GameClientPtr>::const_iterator it = candidates.begin(); it != candidates.end(); ++it) { const string& strName = it->first; choicesInt.Add(i++, strName); choicesStr.push_back(strName); } int btnid = CGUIDialogContextMenu::ShowAndGetChoice(choicesInt); if (btnid < 0 || btnid >= (int)candidates.size()) { CLog::Log(LOGDEBUG, "RetroPlayer: User canceled game client installation selection"); return false; } map<string, GameClientPtr>::iterator it = candidates.find(choicesStr[btnid]); if (it == candidates.end()) return false; // Shouldn't happen GameClientPtr &gc = it->second; // determining disabled status is the same test as earlier // TODO: Preserve disabled values in parallel array // TODO: Don't use parallel arrays AddonPtr addon; // Return value, will be assigned to result bool disabled = !CAddonMgr::Get().GetAddon(gc->ID(), addon, ADDON_GAMEDLL, true) && addon; if (disabled) { // TODO: Prompt the user to enable it CLog::Log(LOGDEBUG, "RetroPlayer: Game client %s installed but disabled, enabling it", gc->ID().c_str()); CGameManager::Get().ClearAutoLaunch(); // Don't auto-launch queued game when the add-on is enabled CAddonMgr::Get().DisableAddon(gc->ID(), false); // Retrieve the newly-enabled add-on if (!CAddonMgr::Get().GetAddon(gc->ID(), addon) || !addon) return false; } else { CLog::Log(LOGDEBUG, "RetroPlayer: Installing game client %s", gc->ID().c_str()); bool success = CAddonInstaller::Get().PromptForInstall(gc->ID(), addon); if (!success || !addon || addon->Type() != ADDON_GAMEDLL) { CLog::Log(LOGDEBUG, "RetroPlayer: Game client installation failed"); // "id" // "Installation failed" // "Check the log file for details." CGUIDialogOK::ShowAndGetInput(gc->ID().c_str(), 114, 16029, 0); return false; } } GameClientPtr gameClient = boost::dynamic_pointer_cast<CGameClient>(addon); if (!gameClient) { CLog::Log(LOGERROR, "RetroPlayer: Add-on was not a game client!"); return false; } result = gameClient; return true; }