aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhowie-f <rftc@gmx.de>2020-08-26 17:04:11 +0200
committerhowie-f <rftc@gmx.de>2020-08-31 15:47:10 +0200
commit744d223b95a18b074331e1ed86d8eb3a79f4e3fc (patch)
tree0b82c014a77dd37cf37594f31739363f33d4c984
parent78322b5516d8f442f7651c3fe8d67e0268bcf841 (diff)
[addons] show latest addon version from each repo for 'install from all repos'
-rw-r--r--addons/resource.language.en_gb/resources/strings.po8
-rw-r--r--xbmc/addons/AddonManager.cpp40
-rw-r--r--xbmc/addons/AddonManager.h26
-rw-r--r--xbmc/addons/AddonRepos.cpp61
-rw-r--r--xbmc/addons/AddonRepos.h28
-rw-r--r--xbmc/addons/gui/GUIDialogAddonInfo.cpp31
-rw-r--r--xbmc/addons/gui/GUIDialogAddonInfo.h6
-rw-r--r--xbmc/filesystem/AddonsDirectory.cpp41
-rw-r--r--xbmc/filesystem/AddonsDirectory.h9
9 files changed, 227 insertions, 23 deletions
diff --git a/addons/resource.language.en_gb/resources/strings.po b/addons/resource.language.en_gb/resources/strings.po
index d725435f93..c684e01095 100644
--- a/addons/resource.language.en_gb/resources/strings.po
+++ b/addons/resource.language.en_gb/resources/strings.po
@@ -22011,7 +22011,13 @@ msgctxt "#39027"
msgid "Sortname"
msgstr ""
-#empty strings from id 39028 to 39029
+#. Text for yes/no dialog when silently uninstalling an addon
+#: xbmc/addons/gui/GUIDialogAddonInfo.cpp
+msgctxt "#39028"
+msgid "Addon \"{0:s}\"[CR]Origin \"{1:s}\"[CR]Version \"{2:s}\"[CR]- will uninstall and be replaced. Would you like to proceed?"
+msgstr ""
+
+#empty string id 39029
#. Media source, a filter and smart playlist rule option
#: xbmc/dialogs/GUIDialogMediaFilter.cpp
diff --git a/xbmc/addons/AddonManager.cpp b/xbmc/addons/AddonManager.cpp
index 2f6201e45f..f9a133474c 100644
--- a/xbmc/addons/AddonManager.cpp
+++ b/xbmc/addons/AddonManager.cpp
@@ -209,23 +209,42 @@ bool CAddonMgr::ReloadSettings(const std::string &id)
return false;
}
-VECADDONS CAddonMgr::GetAvailableUpdates() const
+std::vector<std::shared_ptr<IAddon>> CAddonMgr::GetAvailableUpdates() const
+{
+ return GetAvailableUpdatesOrOutdatedAddons(false);
+}
+
+std::vector<std::shared_ptr<IAddon>> CAddonMgr::GetOutdatedAddons() const
+{
+ return GetAvailableUpdatesOrOutdatedAddons(true);
+}
+
+std::vector<std::shared_ptr<IAddon>> CAddonMgr::GetAvailableUpdatesOrOutdatedAddons(
+ bool returnOutdatedAddons) const
{
CSingleLock lock(m_critSection);
auto start = XbmcThreads::SystemClockMillis();
- VECADDONS updates;
- VECADDONS installed;
+ std::vector<std::shared_ptr<IAddon>> result;
+ std::vector<std::shared_ptr<IAddon>> installed;
CAddonRepos addonRepos(*this);
addonRepos.LoadAddonsFromDatabase(m_database);
GetAddonsForUpdate(installed);
- addonRepos.BuildUpdateList(installed, updates);
+ if (returnOutdatedAddons)
+ {
+ addonRepos.BuildOutdatedList(installed, result);
+ }
+ else
+ {
+ addonRepos.BuildUpdateList(installed, result);
+ }
- CLog::Log(LOGDEBUG, "CAddonMgr::GetAvailableUpdates took %i ms", XbmcThreads::SystemClockMillis() - start);
- return updates;
+ CLog::Log(LOGDEBUG, "CAddonMgr::GetAvailableUpdatesOrOutdatedAddons took %i ms",
+ XbmcThreads::SystemClockMillis() - start);
+ return result;
}
bool CAddonMgr::HasAvailableUpdates()
@@ -814,6 +833,15 @@ bool CAddonMgr::IsAddonInstalled(const std::string& ID)
return GetAddon(ID, tmp, ADDON_UNKNOWN, false);
}
+bool CAddonMgr::IsAddonInstalled(const std::string& ID,
+ const std::string& origin,
+ const AddonVersion& version)
+{
+ AddonPtr tmp;
+ return (GetAddon(ID, tmp, ADDON_UNKNOWN, false) && tmp && tmp->Origin() == origin &&
+ tmp->Version() == version);
+}
+
bool CAddonMgr::CanAddonBeInstalled(const AddonPtr& addon)
{
return addon != nullptr && !addon->IsBroken() && !IsAddonInstalled(addon->ID());
diff --git a/xbmc/addons/AddonManager.h b/xbmc/addons/AddonManager.h
index 9ab35a32dc..c5c9f28ba6 100644
--- a/xbmc/addons/AddonManager.h
+++ b/xbmc/addons/AddonManager.h
@@ -123,7 +123,10 @@ namespace ADDON
bool ReloadSettings(const std::string &id);
/*! Get addons with available updates */
- VECADDONS GetAvailableUpdates() const;
+ std::vector<std::shared_ptr<IAddon>> GetAvailableUpdates() const;
+
+ /*! Get addons that are outdated */
+ std::vector<std::shared_ptr<IAddon>> GetOutdatedAddons() const;
/*! Returns true if there is any addon with available updates, otherwise false */
bool HasAvailableUpdates();
@@ -222,6 +225,16 @@ namespace ADDON
*/
bool IsAddonInstalled(const std::string& ID);
+ /* \brief Checks whether an addon is installed from a
+ * particular origin repo and version
+ * \param ID id of the addon
+ * \param origin origin repository id
+ * \param version the version of the addon
+ */
+ bool IsAddonInstalled(const std::string& ID,
+ const std::string& origin,
+ const AddonVersion& version);
+
/* \brief Checks whether an addon can be installed. Broken addons can't be installed.
\param addon addon to be checked
*/
@@ -380,10 +393,21 @@ namespace ADDON
VECADDONS m_updateableAddons;
+ /*!
+ * \brief returns a vector with either available updates or outdated addons.
+ * usually called by its wrappers GetAvailableUpdates() or
+ * GetOutdatedAddons()
+ * \param[in] true to return outdated addons, false to return available updates
+ * \return vector filled with either available updates or outdated addons
+ */
+ std::vector<std::shared_ptr<IAddon>> GetAvailableUpdatesOrOutdatedAddons(
+ bool returnOutdatedAddons) const;
+
bool GetAddonsInternal(const TYPE& type,
VECADDONS& addons,
bool enabledOnly,
bool checkIncompatible = false) const;
+
bool EnableSingle(const std::string& id);
void FindAddons(ADDON_INFO_LIST& addonmap, const std::string& path);
diff --git a/xbmc/addons/AddonRepos.cpp b/xbmc/addons/AddonRepos.cpp
index 86f6766341..919d304329 100644
--- a/xbmc/addons/AddonRepos.cpp
+++ b/xbmc/addons/AddonRepos.cpp
@@ -181,15 +181,29 @@ void CAddonRepos::AddAddonIfLatest(
void CAddonRepos::BuildUpdateList(const std::vector<std::shared_ptr<IAddon>>& installed,
std::vector<std::shared_ptr<IAddon>>& updates) const
{
+ BuildUpdateOrOutdatedList(installed, updates, false);
+}
+
+void CAddonRepos::BuildOutdatedList(const std::vector<std::shared_ptr<IAddon>>& installed,
+ std::vector<std::shared_ptr<IAddon>>& outdated) const
+{
+ BuildUpdateOrOutdatedList(installed, outdated, true);
+}
+
+void CAddonRepos::BuildUpdateOrOutdatedList(const std::vector<std::shared_ptr<IAddon>>& installed,
+ std::vector<std::shared_ptr<IAddon>>& result,
+ bool returnOutdatedAddons) const
+{
std::shared_ptr<IAddon> update;
- CLog::Log(LOGDEBUG, "ADDONS: *** building update list (installed add-ons) ***");
+ CLog::Log(LOGDEBUG, "CAddonRepos::{}: Building {} list from installed add-ons", __func__,
+ returnOutdatedAddons ? "outdated" : "update");
for (const auto& addon : installed)
{
if (DoAddonUpdateCheck(addon, update))
{
- updates.emplace_back(update);
+ result.emplace_back(returnOutdatedAddons ? addon : update);
}
}
}
@@ -348,6 +362,49 @@ void CAddonRepos::GetLatestAddonVersions(std::vector<std::shared_ptr<IAddon>>& a
}
}
+void CAddonRepos::GetLatestAddonVersionsFromAllRepos(
+ std::vector<std::shared_ptr<IAddon>>& addonList) const
+{
+ const AddonRepoUpdateMode updateMode =
+ CAddonSystemSettings::GetInstance().GetAddonRepoUpdateMode();
+
+ addonList.clear();
+
+ // first we insert all official addon versions into the resulting vector
+
+ std::transform(m_latestOfficialVersions.begin(), m_latestOfficialVersions.end(),
+ back_inserter(addonList),
+ [](const std::pair<std::string, std::shared_ptr<IAddon>>& officialVersion) {
+ return officialVersion.second;
+ });
+
+ // then we insert latest version per addon and repository if they don't exist in the official map
+ // or installation from ANY_REPOSITORY is allowed and the private version is higher
+
+ for (const auto& repo : m_latestVersionsByRepo)
+ {
+ // content of official repos is stored in m_latestVersionsByRepo too
+ // so we need to filter them out
+
+ if (std::none_of(officialRepoInfos.begin(), officialRepoInfos.end(),
+ [&](const ADDON::RepoInfo& officialRepo) {
+ return repo.first == officialRepo.m_repoId;
+ }))
+ {
+ for (const auto& latestAddon : repo.second)
+ {
+ const auto& officialVersion = m_latestOfficialVersions.find(latestAddon.first);
+ if (officialVersion == m_latestOfficialVersions.end() ||
+ (updateMode == AddonRepoUpdateMode::ANY_REPOSITORY &&
+ latestAddon.second->Version() > officialVersion->second->Version()))
+ {
+ addonList.emplace_back(latestAddon.second);
+ }
+ }
+ }
+ }
+}
+
bool CAddonRepos::FindDependency(const std::string& dependsId,
const std::shared_ptr<IAddon>& parent,
std::shared_ptr<IAddon>& dependencyToInstall,
diff --git a/xbmc/addons/AddonRepos.h b/xbmc/addons/AddonRepos.h
index 2d49477b26..4c9aae26cf 100644
--- a/xbmc/addons/AddonRepos.h
+++ b/xbmc/addons/AddonRepos.h
@@ -67,6 +67,16 @@ public:
std::vector<std::shared_ptr<IAddon>>& updates) const;
/*!
+ * \brief Build the list of addons that are outdated and have an update
+ * available depending on defined rules
+ * \param installed vector of all addons installed on the system that are
+ * checked for an update
+ * \param[out] outdated list of addon versions that have an update available
+ */
+ void BuildOutdatedList(const std::vector<std::shared_ptr<IAddon>>& installed,
+ std::vector<std::shared_ptr<IAddon>>& outdated) const;
+
+ /*!
* \brief Checks if the origin-repository of a given addon is defined as official repo
* but does not check the origin path (e.g. https://mirrors.kodi.tv ...)
* \param addon pointer to addon to be checked
@@ -105,13 +115,21 @@ public:
/*!
* \brief Retrieves the latest official versions of addons to vector.
- * Private versions are added obeying the set updateMode.
+ * Private versions are added obeying updateMode.
* (either OFFICIAL_ONLY or ANY_REPOSITORY)
* \param[out] addonList retrieved addon list in a vector
*/
void GetLatestAddonVersions(std::vector<std::shared_ptr<IAddon>>& addonList) const;
/*!
+ * \brief Retrieves the latest official versions of addons to vector.
+ * Private versions (latest per repository) are added obeying updateMode.
+ * (either OFFICIAL_ONLY or ANY_REPOSITORY)
+ * \param[out] addonList retrieved addon list in a vector
+ */
+ void GetLatestAddonVersionsFromAllRepos(std::vector<std::shared_ptr<IAddon>>& addonList) const;
+
+ /*!
* \brief Find a dependency to install during an addon install or update
* If the dependency cannot be found in official versions we look in the
* installing/updating addon's (the parent's) origin repository
@@ -141,6 +159,14 @@ public:
private:
/*!
+ * \brief Executor for BuildUpdateList() and BuildOutdatedList()
+ * \sa BuildUpdateList() BuildOutdatedList()
+ */
+ void BuildUpdateOrOutdatedList(const std::vector<std::shared_ptr<IAddon>>& installed,
+ std::vector<std::shared_ptr<IAddon>>& result,
+ bool returnOutdatedAddons) const;
+
+ /*!
* \brief Load the map of addons
* \note this function should only by called from publicly exposed wrappers
* \return true on success, false otherwise
diff --git a/xbmc/addons/gui/GUIDialogAddonInfo.cpp b/xbmc/addons/gui/GUIDialogAddonInfo.cpp
index 4690c2cbc1..e923547083 100644
--- a/xbmc/addons/gui/GUIDialogAddonInfo.cpp
+++ b/xbmc/addons/gui/GUIDialogAddonInfo.cpp
@@ -79,13 +79,16 @@ bool CGUIDialogAddonInfo::OnMessage(CGUIMessage& message)
}
if (iControl == CONTROL_BTN_INSTALL)
{
- if (!m_localAddon)
+ const auto& itemAddonInfo = m_item->GetAddonInfo();
+ if (!CServiceBroker::GetAddonMgr().IsAddonInstalled(
+ itemAddonInfo->ID(), itemAddonInfo->Origin(), itemAddonInfo->Version()))
{
OnInstall();
return true;
}
else
{
+ m_silentUninstall = false;
OnUninstall();
return true;
}
@@ -158,7 +161,9 @@ void CGUIDialogAddonInfo::UpdateControls()
if (!m_item)
return;
- bool isInstalled = NULL != m_localAddon.get();
+ const auto& itemAddonInfo = m_item->GetAddonInfo();
+ bool isInstalled = CServiceBroker::GetAddonMgr().IsAddonInstalled(
+ itemAddonInfo->ID(), itemAddonInfo->Origin(), itemAddonInfo->Version());
m_addonEnabled =
m_localAddon && !CServiceBroker::GetAddonMgr().IsAddonDisabled(m_localAddon->ID());
bool canDisable =
@@ -329,9 +334,27 @@ void CGUIDialogAddonInfo::OnInstall()
if (!g_passwordManager.CheckMenuLock(WINDOW_ADDON_BROWSER))
return;
- if (m_localAddon || !m_item->HasAddonInfo())
+ if (!m_item->HasAddonInfo())
return;
+ if (m_localAddon)
+ {
+ const std::string& header = g_localizeStrings.Get(19098); // Warning!
+ const std::string text =
+ StringUtils::Format(g_localizeStrings.Get(39028), m_localAddon->ID(),
+ m_localAddon->Origin(), m_localAddon->Version().asString());
+
+ if (CGUIDialogYesNo::ShowAndGetInput(header, text))
+ {
+ m_silentUninstall = true;
+ OnUninstall();
+ }
+ else
+ {
+ return;
+ }
+ }
+
const auto& itemAddonInfo = m_item->GetAddonInfo();
const std::string& addonId = itemAddonInfo->ID();
@@ -429,7 +452,7 @@ void CGUIDialogAddonInfo::OnUninstall()
return;
// prompt user to be sure
- if (!CGUIDialogYesNo::ShowAndGetInput(CVariant{24037}, CVariant{750}))
+ if (!m_silentUninstall && !CGUIDialogYesNo::ShowAndGetInput(CVariant{24037}, CVariant{750}))
return;
bool removeData = false;
diff --git a/xbmc/addons/gui/GUIDialogAddonInfo.h b/xbmc/addons/gui/GUIDialogAddonInfo.h
index a817f1aa79..9a66c4c794 100644
--- a/xbmc/addons/gui/GUIDialogAddonInfo.h
+++ b/xbmc/addons/gui/GUIDialogAddonInfo.h
@@ -86,4 +86,10 @@ private:
CFileItemPtr m_item;
ADDON::AddonPtr m_localAddon;
bool m_addonEnabled = false;
+
+ /*!< a switch to force @ref OnUninstall() to proceed without user interaction.
+ * useful for cases like where another repo’s version of an addon must
+ * be removed before installing a new version.
+ */
+ bool m_silentUninstall = false;
};
diff --git a/xbmc/filesystem/AddonsDirectory.cpp b/xbmc/filesystem/AddonsDirectory.cpp
index efbe6c1932..3d78efa39c 100644
--- a/xbmc/filesystem/AddonsDirectory.cpp
+++ b/xbmc/filesystem/AddonsDirectory.cpp
@@ -498,7 +498,7 @@ static void DependencyAddons(const CURL& path, CFileItemList &items)
static void OutdatedAddons(const CURL& path, CFileItemList &items)
{
VECADDONS addons = CServiceBroker::GetAddonMgr().GetAvailableUpdates();
- CAddonsDirectory::GenerateAddonListing(path, addons, items, g_localizeStrings.Get(24043));
+ CAddonsDirectory::GenerateAddonListingUpdates(path, addons, items, g_localizeStrings.Get(24043));
if (!items.IsEmpty())
{
@@ -537,8 +537,8 @@ static bool Browse(const CURL& path, CFileItemList &items)
}
database.Close();
- // get all addons
- addonRepos.GetLatestAddonVersions(addons);
+ // get all latest addon versions by repo
+ addonRepos.GetLatestAddonVersionsFromAllRepos(addons);
items.SetProperty("reponame", g_localizeStrings.Get(24087));
items.SetLabel(g_localizeStrings.Get(24087));
@@ -781,9 +781,26 @@ bool CAddonsDirectory::IsRepoDirectory(const CURL& url)
void CAddonsDirectory::GenerateAddonListing(const CURL &path,
const VECADDONS& addons, CFileItemList &items, const std::string label)
{
- std::set<std::string> outdated;
- for (const auto& addon : CServiceBroker::GetAddonMgr().GetAvailableUpdates())
- outdated.insert(addon->ID());
+ GenerateAddonListing(path, addons, items, label, false);
+}
+
+void CAddonsDirectory::GenerateAddonListingUpdates(const CURL& path,
+ const VECADDONS& addons,
+ CFileItemList& items,
+ const std::string label)
+{
+ GenerateAddonListing(path, addons, items, label, true);
+}
+
+void CAddonsDirectory::GenerateAddonListing(const CURL& path,
+ const VECADDONS& addons,
+ CFileItemList& items,
+ const std::string label,
+ bool alwaysShowUpdateIcon)
+{
+ std::set<std::shared_ptr<IAddon>> outdated;
+ for (const auto& addon : CServiceBroker::GetAddonMgr().GetOutdatedAddons())
+ outdated.insert(addon);
items.ClearItems();
items.SetContent("addons");
@@ -794,9 +811,17 @@ void CAddonsDirectory::GenerateAddonListing(const CURL &path,
itemPath.SetFileName(addon->ID());
CFileItemPtr pItem = FileItemFromAddon(addon, itemPath.Get(), false);
- bool installed = CServiceBroker::GetAddonMgr().IsAddonInstalled(addon->ID());
+ bool installed = CServiceBroker::GetAddonMgr().IsAddonInstalled(addon->ID(), addon->Origin(),
+ addon->Version());
bool disabled = CServiceBroker::GetAddonMgr().IsAddonDisabled(addon->ID());
- bool hasUpdate = outdated.find(addon->ID()) != outdated.end();
+
+ bool hasUpdate =
+ alwaysShowUpdateIcon ||
+ std::any_of(outdated.begin(), outdated.end(), [&](const std::shared_ptr<IAddon>& i) {
+ return i->ID() == addon->ID() && i->Origin() == addon->Origin() &&
+ i->Version() == addon->Version();
+ });
+
bool fromOfficialRepo = CAddonRepos::IsFromOfficialRepo(addon);
pItem->SetProperty("Addon.IsInstalled", installed);
diff --git a/xbmc/filesystem/AddonsDirectory.h b/xbmc/filesystem/AddonsDirectory.h
index 06802cb3e5..0e154af6fd 100644
--- a/xbmc/filesystem/AddonsDirectory.h
+++ b/xbmc/filesystem/AddonsDirectory.h
@@ -46,6 +46,10 @@ namespace XFILE
static bool GetScriptsAndPlugins(const std::string &content, CFileItemList &items);
static void GenerateAddonListing(const CURL &path, const ADDON::VECADDONS& addons, CFileItemList &items, const std::string label);
+ static void GenerateAddonListingUpdates(const CURL& path,
+ const ADDON::VECADDONS& addons,
+ CFileItemList& items,
+ const std::string label);
static CFileItemPtr FileItemFromAddon(const ADDON::AddonPtr &addon, const std::string& path, bool folder = false);
/*! \brief Returns true if `path` is a path or subpath of the repository directory, otherwise false */
@@ -53,5 +57,10 @@ namespace XFILE
private:
bool GetSearchResults(const CURL& path, CFileItemList &items);
+ static void GenerateAddonListing(const CURL& path,
+ const ADDON::VECADDONS& addons,
+ CFileItemList& items,
+ const std::string label,
+ bool alwaysShowUpdateIcon);
};
}