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")
38 QMutableMapIterator<QString, MythImage *> i(
m_imageCache);
42 i.value()->SetIsInCache(
false);
65 QMutableMapIterator<QString, MythImage *> i(
m_imageCache);
70 i.value()->SetIsInCache(
false);
92 dir.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
93 QFileInfoList list = dir.entryInfoList();
95 QMap<QDateTime, QString> dirtimes;
97 for (
const auto & fi : std::as_const(list))
99 if (fi.isDir() && !fi.isSymLink())
101 if (fi.absoluteFilePath() == themecachedir)
103 dirtimes[fi.lastModified()] = fi.absoluteFilePath();
111 while (
static_cast<size_t>(dirtimes.size()) >= 2)
113 LOG(VB_GUI | VB_FILE, LOG_INFO,
LOC + QString(
"Removing cache dir: %1")
114 .arg(dirtimes.begin().value()));
117 dirtimes.erase(dirtimes.begin());
120 for (
const auto & dirtime : std::as_const(dirtimes))
121 LOG(VB_GUI | VB_FILE, LOG_INFO,
LOC + QString(
"Keeping cache dir: %1").arg(dirtime));
128 if (!Dir.startsWith(cachedirname))
131 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Removing stale cache dir: %1").arg(Dir));
137 dir.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
138 QFileInfoList list = dir.entryInfoList();
139 for (
const auto & fi : std::as_const(list))
141 if (fi.isFile() && !fi.isSymLink())
143 QFile
file(fi.absoluteFilePath());
146 else if (fi.isDir() && !fi.isSymLink())
166 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Pruning cache directory: %1 is disabled")
171 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Pruning cache directory: %1").arg(dirname));
173 qint64 cutoffsecs = cutoff.toSecsSinceEpoch();
175 LOG(VB_GUI | VB_FILE, LOG_INFO,
LOC + QString(
"Removing files not accessed since %1")
186 dir.setFilter(QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot);
187 dir.setSorting(QDir::NoSort);
188 QFileInfoList entries = dir.entryInfoList();
192 for (
const QFileInfo & fi : std::as_const(entries))
195 QString fullname = fi.filePath();
196 if (not fullname.startsWith(
'/'))
197 fullname = dirname +
"/" + fullname;
198 int rc = stat(fullname.toLocal8Bit(), &buf);
201 if (buf.st_atime < cutoffsecs)
204 LOG(VB_GUI | VB_FILE, LOG_DEBUG,
LOC + QString(
"%1 Delete %2")
205 .arg(fi.lastRead().toLocalTime().toString(
Qt::ISODate), fi.fileName()));
206 unlink(qPrintable(fullname));
211 LOG(VB_GUI | VB_FILE, LOG_DEBUG,
LOC + QString(
"%1 Keep %2")
212 .arg(fi.lastRead().toLocalTime().toString(
Qt::ISODate), fi.fileName()));
221 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Kept %1 files, deleted %2 files, stat error on %3 files")
222 .arg(kept).arg(deleted).arg(errors));
227 static QString s_oldcachedir;
233 if (tmpcachedir != s_oldcachedir)
235 LOG(VB_GUI | VB_FILE, LOG_INFO,
LOC + QString(
"Creating cache dir: %1").arg(tmpcachedir));
237 dir.mkdir(tmpcachedir);
238 s_oldcachedir = tmpcachedir;
252 if (URL.startsWith(
"myth:") || URL.startsWith(
"-"))
261 LOG(VB_GUI | VB_FILE, LOG_INFO,
LOC +
262 QString(
"LoadCacheImage(%1,%2)").arg(File, Label));
264 if (File.isEmpty() || Label.isEmpty())
277 constexpr std::chrono::seconds kImageCacheTimeout { 60s };
301 QString cachefilepath;
303 QFileInfo cacheFileInfo(cachefilepath);
307 if (!cacheFileInfo.exists())
311 QDateTime srcLastModified;
315 if ((File.startsWith(
"http://")) ||
316 (File.startsWith(
"https://")) ||
317 (File.startsWith(
"ftp://")))
324 srcLastModified = cacheFileInfo.lastModified();
328 else if (File.startsWith(
"myth://"))
337 QFileInfo original(File);
339 if (original.exists())
340 srcLastModified = original.lastModified();
346 if (cacheFileInfo.lastModified() >= srcLastModified)
359 if (ret->
Load(cachefilepath))
367 LOG(VB_GUI | VB_FILE, LOG_WARNING,
LOC +
368 QString(
"LoadCacheImage: Could not load: %1")
369 .arg(cachefilepath));
421 LOG(VB_GUI | VB_FILE, LOG_INFO,
LOC + QString(
"Saved to Cache (%1)").arg(dstfile));
423 Image->save(dstfile,
"PNG");
429 while ((
m_cacheSize.fetchAndAddOrdered(0) + Image->sizeInBytes()) >=
432 QMap<QString, MythImage *>::iterator it =
m_imageCache.begin();
433 auto oldestTime = SystemClock::now();
434 QString oldestKey = it.key();
442 if ((2 == it.value()->IncrRef()) && (it.value() != Image))
445 oldestKey = it.key();
448 it.value()->DecrRef();
452 LOG(VB_GUI | VB_FILE, LOG_INFO,
LOC +QString(
"%1 images are eligible for expiry").arg(count));
455 LOG(VB_GUI | VB_FILE, LOG_INFO,
LOC + QString(
"Cache too big (%1), removing :%2:")
456 .arg(
m_cacheSize.fetchAndAddOrdered(0) + Image->sizeInBytes())
470 QMap<QString, MythImage *>::iterator it =
m_imageCache.find(URL);
479 LOG(VB_GUI | VB_FILE, LOG_INFO,
LOC +
480 QString(
"NOT IN RAM CACHE, Adding, and adding to size :%1: :%2:").arg(URL)
481 .arg(Image->sizeInBytes()));
484 LOG(VB_GUI | VB_FILE, LOG_INFO,
LOC + QString(
"MythUIHelper::CacheImage : Cache Count = :%1: size :%2:")
493 QMap<QString, MythImage *>::iterator it =
m_imageCache.find(URL);
504 LOG(VB_GUI | VB_FILE, LOG_INFO,
LOC + QString(
"RemoveFromCacheByURL removed :%1: from cache").arg(dstfile));
505 QFile::remove(dstfile);
510 QList<QString>::iterator it;
512 QString partialKey = File;
513 partialKey.replace(
'/',
'-');
519 for (it = m_imageCacheKeys.begin(); it != m_imageCacheKeys.end(); ++it)
521 if ((*it).contains(partialKey))
528 QFileInfoList list = dir.entryInfoList();
530 for (
const auto & fileInfo : std::as_const(list))
532 if (fileInfo.fileName().contains(partialKey))
534 LOG(VB_GUI | VB_FILE, LOG_INFO,
LOC +
535 QString(
"RemoveFromCacheByFile removed: %1: from cache")
536 .arg(fileInfo.fileName()));
538 if (!dir.remove(fileInfo.fileName()))
540 LOG(VB_GENERAL, LOG_ERR,
LOC +
541 QString(
"Failed to delete %1 from the theme cache")
542 .arg(fileInfo.fileName()));
561 m_cacheSize.fetchAndAddOrdered(Image->sizeInBytes());
567 m_cacheSize.fetchAndAddOrdered(-Image->sizeInBytes());