diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 18:07:22 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 18:07:22 +0000 |
commit | c04dcc2e7d834218ef2d4194331e383402495ae1 (patch) | |
tree | 7333e38d10d75386e60f336b80c2443c1166031d /xbmc/TextureCacheJob.cpp | |
parent | Initial commit. (diff) | |
download | kodi-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/TextureCacheJob.cpp')
-rw-r--r-- | xbmc/TextureCacheJob.cpp | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/xbmc/TextureCacheJob.cpp b/xbmc/TextureCacheJob.cpp new file mode 100644 index 0000000..02ef0f4 --- /dev/null +++ b/xbmc/TextureCacheJob.cpp @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2012-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 "TextureCacheJob.h" + +#include "FileItem.h" +#include "ServiceBroker.h" +#include "TextureCache.h" +#include "TextureDatabase.h" +#include "URL.h" +#include "addons/kodi-dev-kit/include/kodi/c-api/addon-instance/audiodecoder.h" +#include "commons/ilog.h" +#include "filesystem/File.h" +#include "guilib/Texture.h" +#include "music/MusicThumbLoader.h" +#include "pictures/Picture.h" +#include "settings/AdvancedSettings.h" +#include "settings/SettingsComponent.h" +#include "utils/EmbeddedArt.h" +#include "utils/StringUtils.h" +#include "utils/URIUtils.h" +#include "utils/log.h" +#include "video/VideoThumbLoader.h" + +#include <cstdlib> +#include <cstring> +#include <exception> +#include <utility> + +#include "PlatformDefs.h" + +CTextureCacheJob::CTextureCacheJob(const std::string &url, const std::string &oldHash): + m_url(url), + m_oldHash(oldHash), + m_cachePath(CTextureCache::GetCacheFile(m_url)) +{ +} + +CTextureCacheJob::~CTextureCacheJob() = default; + +bool CTextureCacheJob::operator==(const CJob* job) const +{ + if (strcmp(job->GetType(),GetType()) == 0) + { + const CTextureCacheJob* cacheJob = dynamic_cast<const CTextureCacheJob*>(job); + if (cacheJob && cacheJob->m_cachePath == m_cachePath) + return true; + } + return false; +} + +bool CTextureCacheJob::DoWork() +{ + if (ShouldCancel(0, 0)) + return false; + + // check whether we need cache the job anyway + bool needsRecaching = false; + std::string path(CServiceBroker::GetTextureCache()->CheckCachedImage(m_url, needsRecaching)); + if (!path.empty() && !needsRecaching) + return false; + if (CServiceBroker::GetTextureCache()->StartCacheImage(m_url)) + return CacheTexture(); + + return false; +} + +bool CTextureCacheJob::CacheTexture(std::unique_ptr<CTexture>* out_texture) +{ + // unwrap the URL as required + std::string additional_info; + unsigned int width, height; + CPictureScalingAlgorithm::Algorithm scalingAlgorithm; + std::string image = DecodeImageURL(m_url, width, height, scalingAlgorithm, additional_info); + + m_details.updateable = additional_info != "music" && UpdateableURL(image); + + // generate the hash + m_details.hash = GetImageHash(image); + if (m_details.hash.empty()) + return false; + else if (m_details.hash == m_oldHash) + return true; + + std::unique_ptr<CTexture> texture = LoadImage(image, width, height, additional_info, true); + if (texture) + { + if (texture->HasAlpha()) + m_details.file = m_cachePath + ".png"; + else + m_details.file = m_cachePath + ".jpg"; + + CLog::Log(LOGDEBUG, "{} image '{}' to '{}':", m_oldHash.empty() ? "Caching" : "Recaching", + CURL::GetRedacted(image), m_details.file); + + if (CPicture::CacheTexture(texture.get(), width, height, + CTextureCache::GetCachedPath(m_details.file), scalingAlgorithm)) + { + m_details.width = width; + m_details.height = height; + if (out_texture) // caller wants the texture + *out_texture = std::move(texture); + return true; + } + } + return false; +} + +bool CTextureCacheJob::ResizeTexture(const std::string &url, uint8_t* &result, size_t &result_size) +{ + result = NULL; + result_size = 0; + + if (url.empty()) + return false; + + // unwrap the URL as required + std::string additional_info; + unsigned int width, height; + CPictureScalingAlgorithm::Algorithm scalingAlgorithm; + std::string image = DecodeImageURL(url, width, height, scalingAlgorithm, additional_info); + if (image.empty()) + return false; + + std::unique_ptr<CTexture> texture = LoadImage(image, width, height, additional_info, true); + if (texture == NULL) + return false; + + bool success = CPicture::ResizeTexture(image, texture.get(), width, height, result, result_size, + scalingAlgorithm); + + return success; +} + +std::string CTextureCacheJob::DecodeImageURL(const std::string &url, unsigned int &width, unsigned int &height, CPictureScalingAlgorithm::Algorithm& scalingAlgorithm, std::string &additional_info) +{ + // unwrap the URL as required + std::string image(url); + additional_info.clear(); + width = height = 0; + scalingAlgorithm = CPictureScalingAlgorithm::NoAlgorithm; + if (StringUtils::StartsWith(url, "image://")) + { + // format is image://[type@]<url_encoded_path>?options + CURL thumbURL(url); + + if (!CTextureCache::CanCacheImageURL(thumbURL)) + return ""; + if (thumbURL.GetUserName() == "music") + additional_info = "music"; + if (StringUtils::StartsWith(thumbURL.GetUserName(), "video_") || + StringUtils::StartsWith(thumbURL.GetUserName(), "pvr") || + StringUtils::StartsWith(thumbURL.GetUserName(), "epg")) + additional_info = thumbURL.GetUserName(); + + image = thumbURL.GetHostName(); + + if (thumbURL.HasOption("flipped")) + additional_info = "flipped"; + + if (thumbURL.GetOption("size") == "thumb") + width = height = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_imageRes; + else + { + if (thumbURL.HasOption("width") && StringUtils::IsInteger(thumbURL.GetOption("width"))) + width = strtol(thumbURL.GetOption("width").c_str(), NULL, 0); + if (thumbURL.HasOption("height") && StringUtils::IsInteger(thumbURL.GetOption("height"))) + height = strtol(thumbURL.GetOption("height").c_str(), NULL, 0); + } + + if (thumbURL.HasOption("scaling_algorithm")) + scalingAlgorithm = CPictureScalingAlgorithm::FromString(thumbURL.GetOption("scaling_algorithm")); + } + + // Handle special case about audiodecoder addon music files, e.g. SACD + if (StringUtils::EndsWith(URIUtils::GetExtension(image), KODI_ADDON_AUDIODECODER_TRACK_EXT)) + { + std::string addonImageURL = URIUtils::GetDirectory(image); + URIUtils::RemoveSlashAtEnd(addonImageURL); + if (XFILE::CFile::Exists(addonImageURL)) + image = addonImageURL; + } + + return image; +} + +std::unique_ptr<CTexture> CTextureCacheJob::LoadImage(const std::string& image, + unsigned int width, + unsigned int height, + const std::string& additional_info, + bool requirePixels) +{ + if (additional_info == "music") + { // special case for embedded music images + EmbeddedArt art; + if (CMusicThumbLoader::GetEmbeddedThumb(image, art)) + return CTexture::LoadFromFileInMemory(art.m_data.data(), art.m_size, art.m_mime, width, + height); + } + + if (StringUtils::StartsWith(additional_info, "video_")) + { + EmbeddedArt art; + if (CVideoThumbLoader::GetEmbeddedThumb(image, additional_info.substr(6), art)) + return CTexture::LoadFromFileInMemory(art.m_data.data(), art.m_size, art.m_mime, width, + height); + } + + // Validate file URL to see if it is an image + CFileItem file(image, false); + file.FillInMimeType(); + if (!(file.IsPicture() && !(file.IsZIP() || file.IsRAR() || file.IsCBR() || file.IsCBZ() )) + && !StringUtils::StartsWithNoCase(file.GetMimeType(), "image/") && !StringUtils::EqualsNoCase(file.GetMimeType(), "application/octet-stream")) // ignore non-pictures + return NULL; + + std::unique_ptr<CTexture> texture = + CTexture::LoadFromFile(image, width, height, requirePixels, file.GetMimeType()); + if (!texture) + return NULL; + + // EXIF bits are interpreted as: <flipXY><flipY*flipX><flipX> + // where to undo the operation we apply them in reverse order <flipX>*<flipY*flipX>*<flipXY> + // When flipped we have an additional <flipX> on the left, which is equivalent to toggling the last bit + if (additional_info == "flipped") + texture->SetOrientation(texture->GetOrientation() ^ 1); + + return texture; +} + +bool CTextureCacheJob::UpdateableURL(const std::string &url) const +{ + // we don't constantly check online images + return !(StringUtils::StartsWith(url, "http://") || StringUtils::StartsWith(url, "https://")); +} + +std::string CTextureCacheJob::GetImageHash(const std::string &url) +{ + // silently ignore - we cannot stat these + // in the case of upnp thumbs are/should be provided when filling the directory list, there's no reason to stat all object ids + if (URIUtils::IsProtocol(url, "addons") || URIUtils::IsProtocol(url, "plugin") || + URIUtils::IsProtocol(url, "upnp")) + return ""; + + struct __stat64 st; + if (XFILE::CFile::Stat(url, &st) == 0) + { + int64_t time = st.st_mtime; + if (!time) + time = st.st_ctime; + if (time || st.st_size) + return StringUtils::Format("d{}s{}", time, st.st_size); + + // the image exists but we couldn't determine the mtime/ctime and/or size + // so set an obviously bad hash + return "BADHASH"; + } + + CLog::Log(LOGDEBUG, "{} - unable to stat url {}", __FUNCTION__, CURL::GetRedacted(url)); + return ""; +} + +CTextureUseCountJob::CTextureUseCountJob(const std::vector<CTextureDetails> &textures) : m_textures(textures) +{ +} + +bool CTextureUseCountJob::operator==(const CJob* job) const +{ + if (strcmp(job->GetType(),GetType()) == 0) + { + const CTextureUseCountJob* useJob = dynamic_cast<const CTextureUseCountJob*>(job); + if (useJob && useJob->m_textures == m_textures) + return true; + } + return false; +} + +bool CTextureUseCountJob::DoWork() +{ + CTextureDatabase db; + if (db.Open()) + { + db.BeginTransaction(); + for (std::vector<CTextureDetails>::const_iterator i = m_textures.begin(); i != m_textures.end(); ++i) + db.IncrementUseCount(*i); + db.CommitTransaction(); + } + return true; +} |