aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--xbmc/TextureCache.cpp44
-rw-r--r--xbmc/TextureCache.h7
-rw-r--r--xbmc/imagefiles/CMakeLists.txt6
-rw-r--r--xbmc/imagefiles/ImageCacheCleaner.cpp112
-rw-r--r--xbmc/imagefiles/ImageCacheCleaner.h58
5 files changed, 224 insertions, 3 deletions
diff --git a/xbmc/TextureCache.cpp b/xbmc/TextureCache.cpp
index c2e1c37af2..4d8fb2c047 100644
--- a/xbmc/TextureCache.cpp
+++ b/xbmc/TextureCache.cpp
@@ -15,11 +15,13 @@
#include "filesystem/File.h"
#include "filesystem/IFileTypes.h"
#include "guilib/Texture.h"
+#include "imagefiles/ImageCacheCleaner.h"
#include "imagefiles/ImageFileURL.h"
#include "profiles/ProfileManager.h"
#include "settings/SettingsComponent.h"
#include "utils/Crc32.h"
#include "utils/Job.h"
+#include "utils/JobManager.h"
#include "utils/StringUtils.h"
#include "utils/URIUtils.h"
#include "utils/log.h"
@@ -27,12 +29,14 @@
#include <chrono>
#include <exception>
#include <mutex>
+#include <optional>
#include <string.h>
using namespace XFILE;
using namespace std::chrono_literals;
-CTextureCache::CTextureCache() : CJobQueue(false, 1, CJob::PRIORITY_LOW_PAUSABLE)
+CTextureCache::CTextureCache()
+ : CJobQueue(false, 1, CJob::PRIORITY_LOW_PAUSABLE), m_cleanTimer{[this]() { CleanTimer(); }}
{
}
@@ -40,6 +44,7 @@ CTextureCache::~CTextureCache() = default;
void CTextureCache::Initialize()
{
+ m_cleanTimer.Start(60s);
std::unique_lock<CCriticalSection> lock(m_databaseSection);
if (!m_database.IsOpen())
m_database.Open();
@@ -344,3 +349,40 @@ bool CTextureCache::Export(const std::string &image, const std::string &destinat
}
return false;
}
+
+void CTextureCache::CleanTimer()
+{
+ CServiceBroker::GetJobManager()->Submit(
+ [this]()
+ {
+ auto next = ScanOldestCache();
+ m_cleanTimer.Start(next);
+ },
+ CJob::PRIORITY_LOW_PAUSABLE);
+}
+
+std::chrono::milliseconds CTextureCache::ScanOldestCache()
+{
+ auto cleaner = IMAGE_FILES::CImageCacheCleaner::Create();
+ if (!cleaner)
+ return std::chrono::hours(1);
+
+ const unsigned int cleanAmount = 1000;
+ const auto result = cleaner->ScanOldestCache(cleanAmount);
+ for (const auto& image : result.imagesToClean)
+ {
+ ClearCachedImage(image);
+ }
+
+ // update in the next 6 - 48 hours depending on number of items processed
+ const auto minTime = 6;
+ const auto maxTime = 24;
+ const auto zeroItemTime = 48;
+ const unsigned int next =
+ result.processedCount == 0
+ ? zeroItemTime
+ : minTime + (maxTime - minTime) *
+ (1 - (static_cast<float>(result.processedCount) / cleanAmount));
+ CLog::LogF(LOGDEBUG, "scheduling the next image cache cleaning in {} hours", next);
+ return std::chrono::hours(next);
+}
diff --git a/xbmc/TextureCache.h b/xbmc/TextureCache.h
index 3c56db1f5f..6b0bf44d21 100644
--- a/xbmc/TextureCache.h
+++ b/xbmc/TextureCache.h
@@ -12,8 +12,10 @@
#include "TextureDatabase.h"
#include "threads/CriticalSection.h"
#include "threads/Event.h"
+#include "threads/Timer.h"
#include "utils/JobManager.h"
+#include <chrono>
#include <memory>
#include <set>
#include <string>
@@ -152,6 +154,7 @@ public:
*/
bool Export(const std::string &image, const std::string &destination, bool overwrite);
bool Export(const std::string &image, const std::string &destination); //! @todo BACKWARD COMPATIBILITY FOR MUSIC THUMBS
+
private:
// private construction, and no assignments; use the provided singleton methods
CTextureCache(const CTextureCache&) = delete;
@@ -213,6 +216,10 @@ private:
*/
void OnCachingComplete(bool success, CTextureCacheJob *job);
+ void CleanTimer();
+ std::chrono::milliseconds ScanOldestCache();
+
+ CTimer m_cleanTimer;
CCriticalSection m_databaseSection;
CTextureDatabase m_database;
std::set<std::string> m_processinglist; ///< currently processing list to avoid 2 jobs being processed at once
diff --git a/xbmc/imagefiles/CMakeLists.txt b/xbmc/imagefiles/CMakeLists.txt
index 66d1b45aa8..69b4551944 100644
--- a/xbmc/imagefiles/CMakeLists.txt
+++ b/xbmc/imagefiles/CMakeLists.txt
@@ -1,7 +1,9 @@
-set(SOURCES ImageFileURL.cpp
+set(SOURCES ImageCacheCleaner.cpp
+ ImageFileURL.cpp
SpecialImageLoaderFactory.cpp)
-set(HEADERS ImageFileURL.h
+set(HEADERS ImageCacheCleaner.h
+ ImageFileURL.h
SpecialImageFileLoader.h
SpecialImageLoaderFactory.h)
diff --git a/xbmc/imagefiles/ImageCacheCleaner.cpp b/xbmc/imagefiles/ImageCacheCleaner.cpp
new file mode 100644
index 0000000000..6821b8dc43
--- /dev/null
+++ b/xbmc/imagefiles/ImageCacheCleaner.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2024 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 "ImageCacheCleaner.h"
+
+#include "ServiceBroker.h"
+#include "TextureCache.h"
+#include "TextureDatabase.h"
+#include "addons/AddonDatabase.h"
+#include "music/MusicDatabase.h"
+#include "utils/log.h"
+#include "video/VideoDatabase.h"
+
+namespace IMAGE_FILES
+{
+std::optional<IMAGE_FILES::CImageCacheCleaner> CImageCacheCleaner::Create()
+{
+ auto result = CImageCacheCleaner();
+ if (result.m_valid)
+ return std::move(result);
+ return {};
+}
+
+CImageCacheCleaner::CImageCacheCleaner()
+{
+ bool valid = true;
+ m_textureDB = std::make_unique<CTextureDatabase>();
+ if (!m_textureDB->Open())
+ {
+ valid = false;
+ CLog::LogF(LOGWARNING, "failed to initialize image cache cleaner: failed to open texture DB");
+ }
+
+ m_videoDB = std::make_unique<CVideoDatabase>();
+ if (!m_videoDB->Open())
+ {
+ valid = false;
+ CLog::LogF(LOGWARNING, "failed to initialize image cache cleaner: failed to open video DB");
+ }
+
+ m_musicDB = std::make_unique<CMusicDatabase>();
+ if (!m_musicDB->Open())
+ {
+ valid = false;
+ CLog::LogF(LOGWARNING, "failed to initialize image cache cleaner: failed to open music DB");
+ }
+
+ m_addonDB = std::make_unique<ADDON::CAddonDatabase>();
+ if (!m_addonDB->Open())
+ {
+ valid = false;
+ CLog::LogF(LOGWARNING, "failed to initialize image cache cleaner: failed to open add-on DB");
+ }
+ m_valid = valid;
+}
+
+CImageCacheCleaner::~CImageCacheCleaner()
+{
+ if (m_addonDB)
+ m_addonDB->Close();
+ if (m_musicDB)
+ m_musicDB->Close();
+ if (m_videoDB)
+ m_videoDB->Close();
+ if (m_textureDB)
+ m_textureDB->Close();
+}
+
+CleanerResult CImageCacheCleaner::ScanOldestCache(unsigned int imageLimit)
+{
+ CLog::LogF(LOGDEBUG, "begin process to clean image cache");
+
+ auto images = m_textureDB->GetOldestCachedImages(imageLimit);
+ if (images.empty())
+ {
+ CLog::LogF(LOGDEBUG, "found no old cached images to process");
+ return CleanerResult{0, {}, {}};
+ }
+ unsigned int processedCount = images.size();
+ CLog::LogF(LOGDEBUG, "found {} old cached images to process", processedCount);
+
+ auto usedImages = m_videoDB->GetUsedImages(images);
+
+ auto nextUsedImages = m_musicDB->GetUsedImages(images);
+ usedImages.insert(usedImages.end(), std::make_move_iterator(nextUsedImages.begin()),
+ std::make_move_iterator(nextUsedImages.end()));
+
+ nextUsedImages = m_addonDB->GetUsedImages(images);
+ usedImages.insert(usedImages.end(), std::make_move_iterator(nextUsedImages.begin()),
+ std::make_move_iterator(nextUsedImages.end()));
+
+ images.erase(std::remove_if(images.begin(), images.end(),
+ [&usedImages](const std::string& image) {
+ return std::find(usedImages.cbegin(), usedImages.cend(), image) !=
+ usedImages.cend();
+ }),
+ images.end());
+
+ m_textureDB->SetKeepCachedImages(usedImages);
+
+ unsigned int keptCount = usedImages.size();
+ CLog::LogF(LOGDEBUG, "cleaning {} unused images from cache, keeping {}", images.size(),
+ keptCount);
+
+ return CleanerResult{processedCount, keptCount, std::move(images)};
+}
+} // namespace IMAGE_FILES
diff --git a/xbmc/imagefiles/ImageCacheCleaner.h b/xbmc/imagefiles/ImageCacheCleaner.h
new file mode 100644
index 0000000000..d975ab9972
--- /dev/null
+++ b/xbmc/imagefiles/ImageCacheCleaner.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+#pragma once
+
+#include <map>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+class CTextureDatabase;
+class CVideoDatabase;
+class CMusicDatabase;
+namespace ADDON
+{
+class CAddonDatabase;
+}
+
+namespace IMAGE_FILES
+{
+struct CleanerResult
+{
+ unsigned int processedCount;
+ unsigned int keptCount;
+ std::vector<std::string> imagesToClean;
+};
+
+/*!
+ * @brief Clean old unused images from the image cache.
+ */
+class CImageCacheCleaner
+{
+public:
+ static std::optional<IMAGE_FILES::CImageCacheCleaner> Create();
+ ~CImageCacheCleaner();
+ CImageCacheCleaner(const CImageCacheCleaner&) = delete;
+ CImageCacheCleaner& operator=(const CImageCacheCleaner&) = delete;
+ CImageCacheCleaner(CImageCacheCleaner&&) = default;
+ CImageCacheCleaner& operator=(CImageCacheCleaner&&) = default;
+
+ CleanerResult ScanOldestCache(unsigned int imageLimit);
+
+private:
+ CImageCacheCleaner();
+ bool m_valid;
+
+ std::unique_ptr<CTextureDatabase> m_textureDB;
+ std::unique_ptr<CVideoDatabase> m_videoDB;
+ std::unique_ptr<CMusicDatabase> m_musicDB;
+ std::unique_ptr<ADDON::CAddonDatabase> m_addonDB;
+};
+} // namespace IMAGE_FILES