summaryrefslogtreecommitdiffstats
path: root/xbmc/addons/RepositoryUpdater.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:22 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:22 +0000
commitc04dcc2e7d834218ef2d4194331e383402495ae1 (patch)
tree7333e38d10d75386e60f336b80c2443c1166031d /xbmc/addons/RepositoryUpdater.cpp
parentInitial commit. (diff)
downloadkodi-c04dcc2e7d834218ef2d4194331e383402495ae1.tar.xz
kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.zip
Adding upstream version 2:20.4+dfsg.upstream/2%20.4+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xbmc/addons/RepositoryUpdater.cpp')
-rw-r--r--xbmc/addons/RepositoryUpdater.cpp338
1 files changed, 338 insertions, 0 deletions
diff --git a/xbmc/addons/RepositoryUpdater.cpp b/xbmc/addons/RepositoryUpdater.cpp
new file mode 100644
index 0000000..3ea8b9c
--- /dev/null
+++ b/xbmc/addons/RepositoryUpdater.cpp
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2015-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 "RepositoryUpdater.h"
+
+#include "ServiceBroker.h"
+#include "TextureDatabase.h"
+#include "addons/AddonDatabase.h"
+#include "addons/AddonEvents.h"
+#include "addons/AddonInstaller.h"
+#include "addons/AddonManager.h"
+#include "addons/AddonSystemSettings.h"
+#include "addons/Repository.h"
+#include "addons/addoninfo/AddonInfo.h"
+#include "addons/addoninfo/AddonType.h"
+#include "dialogs/GUIDialogExtendedProgressBar.h"
+#include "dialogs/GUIDialogKaiToast.h"
+#include "events/AddonManagementEvent.h"
+#include "events/EventLog.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "settings/lib/Setting.h"
+#include "utils/JobManager.h"
+#include "utils/ProgressJob.h"
+#include "utils/log.h"
+
+#include <algorithm>
+#include <iterator>
+#include <mutex>
+#include <vector>
+
+using namespace std::chrono_literals;
+
+namespace ADDON
+{
+
+class CRepositoryUpdateJob : public CProgressJob
+{
+public:
+ explicit CRepositoryUpdateJob(const RepositoryPtr& repo) : m_repo(repo) {}
+ ~CRepositoryUpdateJob() override = default;
+ bool DoWork() override;
+ const RepositoryPtr& GetAddon() const { return m_repo; }
+
+private:
+ const RepositoryPtr m_repo;
+};
+
+bool CRepositoryUpdateJob::DoWork()
+{
+ CLog::Log(LOGDEBUG, "CRepositoryUpdateJob[{}] checking for updates.", m_repo->ID());
+ CAddonDatabase database;
+ database.Open();
+
+ std::string oldChecksum;
+ if (database.GetRepoChecksum(m_repo->ID(), oldChecksum) == -1)
+ oldChecksum = "";
+
+ const CAddonDatabase::RepoUpdateData updateData{database.GetRepoUpdateData(m_repo->ID())};
+ if (updateData.lastCheckedVersion != m_repo->Version())
+ oldChecksum = "";
+
+ std::string newChecksum;
+ std::vector<AddonInfoPtr> addons;
+ int recheckAfter;
+ auto status = m_repo->FetchIfChanged(oldChecksum, newChecksum, addons, recheckAfter);
+
+ database.SetRepoUpdateData(
+ m_repo->ID(), CAddonDatabase::RepoUpdateData(
+ CDateTime::GetCurrentDateTime(), m_repo->Version(),
+ CDateTime::GetCurrentDateTime() + CDateTimeSpan(0, 0, 0, recheckAfter)));
+
+ MarkFinished();
+
+ if (status == CRepository::STATUS_ERROR)
+ return false;
+
+ if (status == CRepository::STATUS_NOT_MODIFIED)
+ {
+ CLog::Log(LOGDEBUG, "CRepositoryUpdateJob[{}] checksum not changed.", m_repo->ID());
+ return true;
+ }
+
+ //Invalidate art.
+ {
+ CTextureDatabase textureDB;
+ textureDB.Open();
+ textureDB.BeginMultipleExecute();
+
+ for (const auto& addon : addons)
+ {
+ AddonPtr oldAddon;
+ if (CServiceBroker::GetAddonMgr().FindInstallableById(addon->ID(), oldAddon) && oldAddon &&
+ addon->Version() > oldAddon->Version())
+ {
+ if (!oldAddon->Icon().empty() || !oldAddon->Art().empty() ||
+ !oldAddon->Screenshots().empty())
+ CLog::Log(LOGDEBUG, "CRepository: invalidating cached art for '{}'", addon->ID());
+
+ if (!oldAddon->Icon().empty())
+ textureDB.InvalidateCachedTexture(oldAddon->Icon());
+
+ for (const auto& path : oldAddon->Screenshots())
+ textureDB.InvalidateCachedTexture(path);
+
+ for (const auto& art : oldAddon->Art())
+ textureDB.InvalidateCachedTexture(art.second);
+ }
+ }
+ textureDB.CommitMultipleExecute();
+ }
+
+ database.UpdateRepositoryContent(m_repo->ID(), m_repo->Version(), newChecksum, addons);
+ return true;
+}
+
+CRepositoryUpdater::CRepositoryUpdater(CAddonMgr& addonMgr) :
+ m_timer(this),
+ m_doneEvent(true),
+ m_addonMgr(addonMgr)
+{
+ // Register settings
+ std::set<std::string> settingSet;
+ settingSet.insert(CSettings::SETTING_ADDONS_AUTOUPDATES);
+ CServiceBroker::GetSettingsComponent()->GetSettings()->RegisterCallback(this, settingSet);
+}
+
+void CRepositoryUpdater::Start()
+{
+ m_addonMgr.Events().Subscribe(this, &CRepositoryUpdater::OnEvent);
+ ScheduleUpdate();
+}
+
+CRepositoryUpdater::~CRepositoryUpdater()
+{
+ // Unregister settings
+ CServiceBroker::GetSettingsComponent()->GetSettings()->UnregisterCallback(this);
+
+ m_addonMgr.Events().Unsubscribe(this);
+}
+
+void CRepositoryUpdater::OnEvent(const ADDON::AddonEvent& event)
+{
+ if (typeid(event) == typeid(ADDON::AddonEvents::Enabled))
+ {
+ if (m_addonMgr.HasType(event.addonId, AddonType::REPOSITORY))
+ ScheduleUpdate();
+ }
+}
+
+void CRepositoryUpdater::OnJobComplete(unsigned int jobID, bool success, CJob* job)
+{
+ std::unique_lock<CCriticalSection> lock(m_criticalSection);
+ m_jobs.erase(std::find(m_jobs.begin(), m_jobs.end(), job));
+ if (m_jobs.empty())
+ {
+ CLog::Log(LOGDEBUG, "CRepositoryUpdater: done.");
+ m_doneEvent.Set();
+
+ VECADDONS updates = m_addonMgr.GetAvailableUpdates();
+
+ if (CAddonSystemSettings::GetInstance().GetAddonAutoUpdateMode() == AUTO_UPDATES_NOTIFY)
+ {
+ if (!updates.empty())
+ {
+ if (updates.size() == 1)
+ CGUIDialogKaiToast::QueueNotification(
+ updates[0]->Icon(), updates[0]->Name(), g_localizeStrings.Get(24068),
+ TOAST_DISPLAY_TIME, false, TOAST_DISPLAY_TIME);
+ else
+ CGUIDialogKaiToast::QueueNotification(
+ "", g_localizeStrings.Get(24001), g_localizeStrings.Get(24061),
+ TOAST_DISPLAY_TIME, false, TOAST_DISPLAY_TIME);
+
+ auto eventLog = CServiceBroker::GetEventLog();
+ for (const auto &addon : updates)
+ {
+ if (eventLog)
+ eventLog->Add(EventPtr(new CAddonManagementEvent(addon, 24068)));
+ }
+ }
+ }
+
+ if (CAddonSystemSettings::GetInstance().GetAddonAutoUpdateMode() == AUTO_UPDATES_ON)
+ {
+ m_addonMgr.CheckAndInstallAddonUpdates(false);
+ }
+
+ ScheduleUpdate();
+
+ m_events.Publish(RepositoryUpdated{});
+ }
+}
+
+bool CRepositoryUpdater::CheckForUpdates(bool showProgress)
+{
+ VECADDONS addons;
+ if (m_addonMgr.GetAddons(addons, AddonType::REPOSITORY) && !addons.empty())
+ {
+ std::unique_lock<CCriticalSection> lock(m_criticalSection);
+ for (const auto& addon : addons)
+ CheckForUpdates(std::static_pointer_cast<ADDON::CRepository>(addon), showProgress);
+
+ return true;
+ }
+
+ return false;
+}
+
+static void SetProgressIndicator(CRepositoryUpdateJob* job)
+{
+ auto dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogExtendedProgressBar>(WINDOW_DIALOG_EXT_PROGRESS);
+ if (dialog)
+ job->SetProgressIndicators(dialog->GetHandle(g_localizeStrings.Get(24092)), nullptr);
+}
+
+void CRepositoryUpdater::CheckForUpdates(const ADDON::RepositoryPtr& repo, bool showProgress)
+{
+ std::unique_lock<CCriticalSection> lock(m_criticalSection);
+ auto job = std::find_if(m_jobs.begin(), m_jobs.end(),
+ [&](CRepositoryUpdateJob* job){ return job->GetAddon()->ID() == repo->ID(); });
+
+ if (job == m_jobs.end())
+ {
+ auto* job = new CRepositoryUpdateJob(repo);
+ m_jobs.push_back(job);
+ m_doneEvent.Reset();
+ if (showProgress)
+ SetProgressIndicator(job);
+ CServiceBroker::GetJobManager()->AddJob(job, this, CJob::PRIORITY_LOW);
+ }
+ else
+ {
+ if (showProgress && !(*job)->HasProgressIndicator())
+ SetProgressIndicator(*job);
+ }
+}
+
+void CRepositoryUpdater::Await()
+{
+ m_doneEvent.Wait();
+}
+
+void CRepositoryUpdater::OnTimeout()
+{
+ //workaround
+ if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_FULLSCREEN_VIDEO ||
+ CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_FULLSCREEN_GAME ||
+ CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_SLIDESHOW)
+ {
+ CLog::Log(LOGDEBUG,"CRepositoryUpdater: busy playing. postponing scheduled update");
+ m_timer.RestartAsync(2min);
+ return;
+ }
+
+ CLog::Log(LOGDEBUG,"CRepositoryUpdater: running scheduled update");
+ CheckForUpdates();
+}
+
+void CRepositoryUpdater::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
+{
+ if (setting->GetId() == CSettings::SETTING_ADDONS_AUTOUPDATES)
+ ScheduleUpdate();
+}
+
+CDateTime CRepositoryUpdater::LastUpdated() const
+{
+ VECADDONS repos;
+ if (!m_addonMgr.GetAddons(repos, AddonType::REPOSITORY) || repos.empty())
+ return CDateTime();
+
+ CAddonDatabase db;
+ db.Open();
+ std::vector<CDateTime> updateTimes;
+ std::transform(
+ repos.begin(), repos.end(), std::back_inserter(updateTimes), [&](const AddonPtr& repo) {
+ const auto updateData = db.GetRepoUpdateData(repo->ID());
+ if (updateData.lastCheckedAt.IsValid() && updateData.lastCheckedVersion == repo->Version())
+ return updateData.lastCheckedAt;
+ return CDateTime();
+ });
+
+ return *std::min_element(updateTimes.begin(), updateTimes.end());
+}
+
+CDateTime CRepositoryUpdater::ClosestNextCheck() const
+{
+ VECADDONS repos;
+ if (!m_addonMgr.GetAddons(repos, AddonType::REPOSITORY) || repos.empty())
+ return CDateTime();
+
+ CAddonDatabase db;
+ db.Open();
+ std::vector<CDateTime> nextCheckTimes;
+ std::transform(
+ repos.begin(), repos.end(), std::back_inserter(nextCheckTimes), [&](const AddonPtr& repo) {
+ const auto updateData = db.GetRepoUpdateData(repo->ID());
+ if (updateData.nextCheckAt.IsValid() && updateData.lastCheckedVersion == repo->Version())
+ return updateData.nextCheckAt;
+ return CDateTime();
+ });
+
+ return *std::min_element(nextCheckTimes.begin(), nextCheckTimes.end());
+}
+
+void CRepositoryUpdater::ScheduleUpdate()
+{
+ std::unique_lock<CCriticalSection> lock(m_criticalSection);
+ m_timer.Stop(true);
+
+ if (CAddonSystemSettings::GetInstance().GetAddonAutoUpdateMode() == AUTO_UPDATES_NEVER)
+ return;
+
+ if (!m_addonMgr.HasAddons(AddonType::REPOSITORY))
+ return;
+
+ int delta{1};
+ const auto nextCheck = ClosestNextCheck();
+ if (nextCheck.IsValid())
+ {
+ // Repos were already checked once and we know when to check next
+ delta = std::max(1, (nextCheck - CDateTime::GetCurrentDateTime()).GetSecondsTotal() * 1000);
+ CLog::Log(LOGDEBUG, "CRepositoryUpdater: closest next update check at {} (in {} s)",
+ nextCheck.GetAsLocalizedDateTime(), delta / 1000);
+ }
+
+ if (!m_timer.Start(std::chrono::milliseconds(delta)))
+ CLog::Log(LOGERROR,"CRepositoryUpdater: failed to start timer");
+}
+}