[filesystem][SpecialProtocol] Removed assert from GetPath
[xbmc.git] / xbmc / TextureCache.cpp
blobbeca7cf9fd6e7fc1ddb8ef9c7e19aed4c6ad1115
1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "TextureCache.h"
11 #include "ServiceBroker.h"
12 #include "TextureCacheJob.h"
13 #include "URL.h"
14 #include "commons/ilog.h"
15 #include "filesystem/File.h"
16 #include "filesystem/IFileTypes.h"
17 #include "guilib/Texture.h"
18 #include "profiles/ProfileManager.h"
19 #include "settings/SettingsComponent.h"
20 #include "utils/Crc32.h"
21 #include "utils/Job.h"
22 #include "utils/StringUtils.h"
23 #include "utils/URIUtils.h"
24 #include "utils/log.h"
26 #include <chrono>
27 #include <exception>
28 #include <mutex>
29 #include <string.h>
31 using namespace XFILE;
32 using namespace std::chrono_literals;
34 CTextureCache::CTextureCache() : CJobQueue(false, 1, CJob::PRIORITY_LOW_PAUSABLE)
38 CTextureCache::~CTextureCache() = default;
40 void CTextureCache::Initialize()
42 std::unique_lock<CCriticalSection> lock(m_databaseSection);
43 if (!m_database.IsOpen())
44 m_database.Open();
47 void CTextureCache::Deinitialize()
49 CancelJobs();
51 std::unique_lock<CCriticalSection> lock(m_databaseSection);
52 m_database.Close();
55 bool CTextureCache::IsCachedImage(const std::string &url) const
57 if (url.empty())
58 return false;
60 if (!CURL::IsFullPath(url))
61 return true;
63 const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
65 return URIUtils::PathHasParent(url, "special://skin", true) ||
66 URIUtils::PathHasParent(url, "special://temp", true) ||
67 URIUtils::PathHasParent(url, "resource://", true) ||
68 URIUtils::PathHasParent(url, "androidapp://", true) ||
69 URIUtils::PathHasParent(url, profileManager->GetThumbnailsFolder(), true);
72 bool CTextureCache::HasCachedImage(const std::string &url)
74 CTextureDetails details;
75 std::string cachedImage(GetCachedImage(url, details));
76 return (!cachedImage.empty() && cachedImage != url);
79 std::string CTextureCache::GetCachedImage(const std::string &image, CTextureDetails &details, bool trackUsage)
81 std::string url = CTextureUtils::UnwrapImageURL(image);
82 if (url.empty())
83 return "";
84 if (IsCachedImage(url))
85 return url;
87 // lookup the item in the database
88 if (GetCachedTexture(url, details))
90 if (trackUsage)
91 IncrementUseCount(details);
92 return GetCachedPath(details.file);
94 return "";
97 bool CTextureCache::CanCacheImageURL(const CURL &url)
99 return url.GetUserName().empty() || url.GetUserName() == "music" ||
100 StringUtils::StartsWith(url.GetUserName(), "video_") ||
101 StringUtils::StartsWith(url.GetUserName(), "pvr") ||
102 StringUtils::StartsWith(url.GetUserName(), "epg");
105 std::string CTextureCache::CheckCachedImage(const std::string &url, bool &needsRecaching)
107 CTextureDetails details;
108 std::string path(GetCachedImage(url, details, true));
109 needsRecaching = !details.hash.empty();
110 if (!path.empty())
111 return path;
112 return "";
115 void CTextureCache::BackgroundCacheImage(const std::string &url)
117 if (url.empty())
118 return;
120 CTextureDetails details;
121 std::string path(GetCachedImage(url, details));
122 if (!path.empty() && details.hash.empty())
123 return; // image is already cached and doesn't need to be checked further
125 path = CTextureUtils::UnwrapImageURL(url);
126 if (path.empty())
127 return;
129 // needs (re)caching
130 AddJob(new CTextureCacheJob(path, details.hash));
133 bool CTextureCache::StartCacheImage(const std::string& image)
135 std::unique_lock<CCriticalSection> lock(m_processingSection);
136 std::set<std::string>::iterator i = m_processinglist.find(image);
137 if (i == m_processinglist.end())
139 m_processinglist.insert(image);
140 return true;
142 return false;
145 std::string CTextureCache::CacheImage(const std::string& image,
146 std::unique_ptr<CTexture>* texture /*= nullptr*/,
147 CTextureDetails* details /*= nullptr*/)
149 std::string url = CTextureUtils::UnwrapImageURL(image);
150 if (url.empty())
151 return "";
153 std::unique_lock<CCriticalSection> lock(m_processingSection);
154 if (m_processinglist.find(url) == m_processinglist.end())
156 m_processinglist.insert(url);
157 lock.unlock();
158 // cache the texture directly
159 CTextureCacheJob job(url);
160 bool success = job.CacheTexture(texture);
161 OnCachingComplete(success, &job);
162 if (success && details)
163 *details = job.m_details;
164 return success ? GetCachedPath(job.m_details.file) : "";
166 lock.unlock();
168 // wait for currently processing job to end.
169 while (true)
171 m_completeEvent.Wait(1000ms);
173 std::unique_lock<CCriticalSection> lock(m_processingSection);
174 if (m_processinglist.find(url) == m_processinglist.end())
175 break;
178 CTextureDetails tempDetails;
179 if (!details)
180 details = &tempDetails;
182 std::string cachedpath = GetCachedImage(url, *details, true);
183 if (!cachedpath.empty())
185 if (texture)
186 *texture = CTexture::LoadFromFile(cachedpath, 0, 0);
188 else
190 CLog::Log(LOGDEBUG, "CTextureCache::{} - Return NULL texture because cache is not ready",
191 __FUNCTION__);
194 return cachedpath;
197 bool CTextureCache::CacheImage(const std::string &image, CTextureDetails &details)
199 std::string path = GetCachedImage(image, details);
200 if (path.empty()) // not cached
201 path = CacheImage(image, NULL, &details);
203 return !path.empty();
206 void CTextureCache::ClearCachedImage(const std::string &url, bool deleteSource /*= false */)
208 //! @todo This can be removed when the texture cache covers everything.
209 std::string path = deleteSource ? url : "";
210 std::string cachedFile;
211 if (ClearCachedTexture(url, cachedFile))
212 path = GetCachedPath(cachedFile);
213 if (CFile::Exists(path))
214 CFile::Delete(path);
215 path = URIUtils::ReplaceExtension(path, ".dds");
216 if (CFile::Exists(path))
217 CFile::Delete(path);
220 bool CTextureCache::ClearCachedImage(int id)
222 std::string cachedFile;
223 if (ClearCachedTexture(id, cachedFile))
225 cachedFile = GetCachedPath(cachedFile);
226 if (CFile::Exists(cachedFile))
227 CFile::Delete(cachedFile);
228 cachedFile = URIUtils::ReplaceExtension(cachedFile, ".dds");
229 if (CFile::Exists(cachedFile))
230 CFile::Delete(cachedFile);
231 return true;
233 return false;
236 bool CTextureCache::GetCachedTexture(const std::string &url, CTextureDetails &details)
238 std::unique_lock<CCriticalSection> lock(m_databaseSection);
239 return m_database.GetCachedTexture(url, details);
242 bool CTextureCache::AddCachedTexture(const std::string &url, const CTextureDetails &details)
244 std::unique_lock<CCriticalSection> lock(m_databaseSection);
245 return m_database.AddCachedTexture(url, details);
248 void CTextureCache::IncrementUseCount(const CTextureDetails &details)
250 static const size_t count_before_update = 100;
251 std::unique_lock<CCriticalSection> lock(m_useCountSection);
252 m_useCounts.reserve(count_before_update);
253 m_useCounts.push_back(details);
254 if (m_useCounts.size() >= count_before_update)
256 AddJob(new CTextureUseCountJob(m_useCounts));
257 m_useCounts.clear();
261 bool CTextureCache::SetCachedTextureValid(const std::string &url, bool updateable)
263 std::unique_lock<CCriticalSection> lock(m_databaseSection);
264 return m_database.SetCachedTextureValid(url, updateable);
267 bool CTextureCache::ClearCachedTexture(const std::string &url, std::string &cachedURL)
269 std::unique_lock<CCriticalSection> lock(m_databaseSection);
270 return m_database.ClearCachedTexture(url, cachedURL);
273 bool CTextureCache::ClearCachedTexture(int id, std::string &cachedURL)
275 std::unique_lock<CCriticalSection> lock(m_databaseSection);
276 return m_database.ClearCachedTexture(id, cachedURL);
279 std::string CTextureCache::GetCacheFile(const std::string &url)
281 auto crc = Crc32::ComputeFromLowerCase(url);
282 std::string hex = StringUtils::Format("{:08x}", crc);
283 std::string hash = StringUtils::Format("{}/{}", hex[0], hex.c_str());
284 return hash;
287 std::string CTextureCache::GetCachedPath(const std::string &file)
289 const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
291 return URIUtils::AddFileToFolder(profileManager->GetThumbnailsFolder(), file);
294 void CTextureCache::OnCachingComplete(bool success, CTextureCacheJob *job)
296 if (success)
298 if (job->m_oldHash == job->m_details.hash)
299 SetCachedTextureValid(job->m_url, job->m_details.updateable);
300 else
301 AddCachedTexture(job->m_url, job->m_details);
304 { // remove from our processing list
305 std::unique_lock<CCriticalSection> lock(m_processingSection);
306 std::set<std::string>::iterator i = m_processinglist.find(job->m_url);
307 if (i != m_processinglist.end())
308 m_processinglist.erase(i);
311 m_completeEvent.Set();
314 void CTextureCache::OnJobComplete(unsigned int jobID, bool success, CJob *job)
316 if (strcmp(job->GetType(), kJobTypeCacheImage) == 0)
317 OnCachingComplete(success, static_cast<CTextureCacheJob*>(job));
318 return CJobQueue::OnJobComplete(jobID, success, job);
321 bool CTextureCache::Export(const std::string &image, const std::string &destination, bool overwrite)
323 CTextureDetails details;
324 std::string cachedImage(GetCachedImage(image, details));
325 if (!cachedImage.empty())
327 std::string dest = destination + URIUtils::GetExtension(cachedImage);
328 if (overwrite || !CFile::Exists(dest))
330 if (CFile::Copy(cachedImage, dest))
331 return true;
332 CLog::Log(LOGERROR, "{} failed exporting '{}' to '{}'", __FUNCTION__, cachedImage, dest);
335 return false;
338 bool CTextureCache::Export(const std::string &image, const std::string &destination)
340 CTextureDetails details;
341 std::string cachedImage(GetCachedImage(image, details));
342 if (!cachedImage.empty())
344 if (CFile::Copy(cachedImage, destination))
345 return true;
346 CLog::Log(LOGERROR, "{} failed exporting '{}' to '{}'", __FUNCTION__, cachedImage, destination);
348 return false;