23#define LOC QString("UICache: ")
26 : m_imageThreadPool(new
MThreadPool(
"MythUIHelper"))
29 LOG(VB_GUI, LOG_INFO,
LOC + QString(
"MythUI Image Cache size set to %1 bytes")
42 i.value()->SetIsInCache(
false);
69 i.value()->SetIsInCache(
false);
91 dir.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
92 QFileInfoList list = dir.entryInfoList();
94 QMap<QDateTime, QString> dirtimes;
96 for (
const auto & fi : std::as_const(list))
98 if (fi.isDir() && !fi.isSymLink())
100 if (fi.absoluteFilePath() == themecachedir)
102 dirtimes[fi.lastModified()] = fi.absoluteFilePath();
110 while (
static_cast<size_t>(dirtimes.size()) >= 2)
112 LOG(VB_GUI | VB_FILE, LOG_INFO,
LOC + QString(
"Removing cache dir: %1")
113 .arg(dirtimes.begin().value()));
116 dirtimes.erase(dirtimes.begin());
119 for (
const auto & dirtime : std::as_const(dirtimes))
120 LOG(VB_GUI | VB_FILE, LOG_INFO,
LOC + QString(
"Keeping cache dir: %1").arg(dirtime));
127 if (!Dir.startsWith(cachedirname))
130 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Removing stale cache dir: %1").arg(Dir));
136 dir.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
137 QFileInfoList list = dir.entryInfoList();
138 for (
const auto & fi : std::as_const(list))
140 if (fi.isFile() && !fi.isSymLink())
142 QFile
file(fi.absoluteFilePath());
145 else if (fi.isDir() && !fi.isSymLink())
165 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Pruning cache directory: %1 is disabled")
170 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Pruning cache directory: %1").arg(dirname));
172 qint64 cutoffsecs = cutoff.toSecsSinceEpoch();
174 LOG(VB_GUI | VB_FILE, LOG_INFO,
LOC + QString(
"Removing files not accessed since %1")
185 dir.setFilter(QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot);
186 dir.setSorting(QDir::NoSort);
187 QFileInfoList entries = dir.entryInfoList();
191 for (
const QFileInfo & fi : std::as_const(entries))
194 QString fullname = fi.filePath();
195 if (not fullname.startsWith(
'/'))
196 fullname = dirname +
"/" + fullname;
197 int rc = stat(fullname.toLocal8Bit().constData(), &buf);
200 if (buf.st_atime < cutoffsecs)
203 LOG(VB_GUI | VB_FILE, LOG_DEBUG,
LOC + QString(
"%1 Delete %2")
204 .arg(fi.lastRead().toLocalTime().toString(
Qt::ISODate), fi.fileName()));
205 unlink(qPrintable(fullname));
210 LOG(VB_GUI | VB_FILE, LOG_DEBUG,
LOC + QString(
"%1 Keep %2")
211 .arg(fi.lastRead().toLocalTime().toString(
Qt::ISODate), fi.fileName()));
220 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Kept %1 files, deleted %2 files, stat error on %3 files")
221 .arg(kept).arg(deleted).arg(errors));
226 static QString s_oldcachedir;
232 if (tmpcachedir != s_oldcachedir)
234 LOG(VB_GUI | VB_FILE, LOG_INFO,
LOC + QString(
"Creating cache dir: %1").arg(tmpcachedir));
236 dir.mkdir(tmpcachedir);
237 s_oldcachedir = tmpcachedir;
251 if (URL.startsWith(
"myth:") || URL.startsWith(
"-"))
260 LOG(VB_GUI | VB_FILE, LOG_INFO,
LOC +
261 QString(
"LoadCacheImage(%1,%2)").arg(File, Label));
263 if (File.isEmpty() || Label.isEmpty())
276 constexpr std::chrono::seconds kImageCacheTimeout { 60s };
300 QString cachefilepath;
302 QFileInfo cacheFileInfo(cachefilepath);
306 if (!cacheFileInfo.exists())
310 QDateTime srcLastModified;
314 if ((File.startsWith(
"http://")) ||
315 (File.startsWith(
"https://")) ||
316 (File.startsWith(
"ftp://")))
323 srcLastModified = cacheFileInfo.lastModified();
327 else if (File.startsWith(
"myth://"))
336 QFileInfo original(File);
338 if (original.exists())
339 srcLastModified = original.lastModified();
345 if (cacheFileInfo.lastModified() >= srcLastModified)
358 if (ret->
Load(cachefilepath))
366 LOG(VB_GUI | VB_FILE, LOG_WARNING,
LOC +
367 QString(
"LoadCacheImage: Could not load: %1")
368 .arg(cachefilepath));
420 LOG(VB_GUI | VB_FILE, LOG_INFO,
LOC + QString(
"Saved to Cache (%1)").arg(dstfile));
422 Image->save(dstfile,
"PNG");
428 while ((
m_cacheSize.fetchAndAddOrdered(0) + Image->sizeInBytes()) >=
431 QMap<QString, MythImage *>::iterator it =
m_imageCache.begin();
432 auto oldestTime = SystemClock::now();
433 QString oldestKey = it.key();
441 if ((2 == it.value()->IncrRef()) && (it.value() != Image))
444 oldestKey = it.key();
447 it.value()->DecrRef();
451 LOG(VB_GUI | VB_FILE, LOG_INFO,
LOC +QString(
"%1 images are eligible for expiry").arg(count));
454 LOG(VB_GUI | VB_FILE, LOG_INFO,
LOC + QString(
"Cache too big (%1), removing :%2:")
455 .arg(
m_cacheSize.fetchAndAddOrdered(0) + Image->sizeInBytes())
469 QMap<QString, MythImage *>::iterator it =
m_imageCache.find(URL);
478 LOG(VB_GUI | VB_FILE, LOG_INFO,
LOC +
479 QString(
"NOT IN RAM CACHE, Adding, and adding to size :%1: :%2:").arg(URL)
480 .arg(Image->sizeInBytes()));
483 LOG(VB_GUI | VB_FILE, LOG_INFO,
LOC + QString(
"MythUIHelper::CacheImage : Cache Count = :%1: size :%2:")
492 QMap<QString, MythImage *>::iterator it =
m_imageCache.find(URL);
503 LOG(VB_GUI | VB_FILE, LOG_INFO,
LOC + QString(
"RemoveFromCacheByURL removed :%1: from cache").arg(dstfile));
504 QFile::remove(dstfile);
509 QList<QString>::iterator it;
511 QString partialKey = File;
512 partialKey.replace(
'/',
'-');
518 for (it = m_imageCacheKeys.begin(); it != m_imageCacheKeys.end(); ++it)
520 if ((*it).contains(partialKey))
527 QFileInfoList list = dir.entryInfoList();
529 for (
const auto & fileInfo : std::as_const(list))
531 if (fileInfo.fileName().contains(partialKey))
533 LOG(VB_GUI | VB_FILE, LOG_INFO,
LOC +
534 QString(
"RemoveFromCacheByFile removed: %1: from cache")
535 .arg(fileInfo.fileName()));
537 if (!dir.remove(fileInfo.fileName()))
539 LOG(VB_GENERAL, LOG_ERR,
LOC +
540 QString(
"Failed to delete %1 from the theme cache")
541 .arg(fileInfo.fileName()));
560 m_cacheSize.fetchAndAddOrdered(Image->sizeInBytes());
566 m_cacheSize.fetchAndAddOrdered(-Image->sizeInBytes());
QDateTime GetLastModified(const QString &url)
Gets the Last Modified timestamp for a URI.
bool Load(MythImageReader *reader)
void SetIsInCache(bool bCached)
int DecrRef(void) override
Decrements reference count and deletes on 0.
int IncrRef(void) override
Increments reference count.
MythImage * GetFormatImage()
Returns a blank reference counted image in the format required for the Draw functions for this painte...
void ClearOldImageCache()
MThreadPool * GetImageThreadPool()
MThreadPool * m_imageThreadPool
void SetScreenSize(QSize Size)
void RemoveFromCacheByURL(const QString &URL)
QMap< QString, SystemTime > m_cacheTrack
MythImage * CacheImage(const QString &URL, MythImage *Image, bool NoDisk=false)
MythImage * LoadCacheImage(QString File, const QString &Label, MythPainter *Painter, ImageCacheMode cacheMode=kCacheNormal)
QMap< QString, MythImage * > m_imageCache
void RemoveCacheDir(const QString &Dir)
bool IsImageInCache(const QString &URL)
void RemoveFromCacheByFile(const QString &File)
QRecursiveMutex m_cacheLock
QString GetThemeCacheDir()
QAtomicInteger< qint64 > m_cacheSize
void ExcludeFromCacheSize(MythImage *Image)
MythImage * GetImageFromCache(const QString &URL)
void IncludeInCacheSize(MythImage *Image)
void ClearThemeCacheDir()
static void PruneCacheDir(const QString &Dir)
Remove all files in the cache that haven't been accessed in a user configurable number of days.
QAtomicInteger< qint64 > m_maxCacheSize
QString GetCacheDirByUrl(const QString &URL)
Look at the url being read and decide whether the cached version should go into the theme cache or th...
QDateTime LastModified(void) const
std::chrono::time_point< SystemClock > SystemTime
QString GetRemoteCacheDir(void)
Returns the directory for all files cached from the backend.
QString GetThumbnailDir(void)
Returns the directory where all non-theme thumbnail files should be cached.
QString GetThemeBaseCacheDir(void)
Returns the base directory where all theme related files should be cached.
MythDownloadManager * GetMythDownloadManager(void)
Gets the pointer to the MythDownloadManager singleton.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
MythUIHelper * GetMythUI()
static constexpr const char * DEFAULT_UI_THEME
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
duration< CHRONO_TYPE, ratio< 86400 > > days