PictureLoader: Replace downloadedPics cache with QNetworkCache (#4756)

* PictureLoader: Replace downloadedPics cache with QNetworkCache

Currently when the "Download card pictures on the fly" option is
enabled, Cockatrice stores downloaded pictures into a downloadedPics
sub-folder, keyed on set and card name. If a picture is found in that
folder, we never try to download a picture for that card ever again
(until it is reprinted in a more recent set, I guess).

This has the unfortunate consequence that if you change the URLs for
downloading card images, the changes are not applied to cards that
already have their picture downloaded. In particular, if you use
localized card pictures (through !sflang!), you get a mix of cards in
different languages depending on the currently configured language at
the time each card was downloaded.

This patch removes that mechanism in favor of setting a
QNetworkDiskCache on the QNetworkAccessManager used by the PictureLoader
to download pictures. The QNetworkDiskCache caches the picture keyed on
their URL: if the URL changes, a new request will be made. In
particular, if you use picture URLs with !sflang! and change the
language, pictures for the current language will be downloaded even for
cards that already have a picture. The QNetworkDiskCache is configured
with a maximum size of 4GB, which should be enough to hold one
high-quality JPEG for each M:TG card in existence.

Note that this does not affect the existing mechanism for defining
custom card art, either through the CUSTOM directory or the set-based
one.  Cockatrice will still read existing cards in the downloadedPics
directory as before, it will just no longer write into that directory
(since pictures are cached by the QNetworkDiskCache instead). To fully
switch to the new cache, users should use the "Delete Downloaded Images"
button in the settings: it will clear the QNetworkDiskCache but also
remove the downloadedPics directory.

* Do not use system cache dir for portable installs

* Add settings for network cache size

* Delete corrupted cache entries

* Use old-style connect() syntax (Qt5 build failure)

* Add setNetworkCacheSizeInMB to test mocks

* setTransferTimeout was added in Qt 5.15

* Improve logging messages

We now have the following messages

 - "Trying to download picture from url: URL" before loading a picture
   when picture download is enabled
 - "Trying to load picture from cache: URL" before loading a picture
   when picture download is disabled (i.e. cache-only offline mode)
 - "Removing corrupted cache file for url URL and retrying (ERR)" when
   when we fail to load a picture from the cache. Usually, this should
   be due to the timeout, in which case ERR will be "Operation
   Canceled".
 - "Download failed for url URL (ERR)" when there was an error
   downloading a picture from the network (ERR is the error message)
 - "Following redirect to URL" and "Following cached redirect to URL"
   when following a redirect (from network/from cache)
 - "Image successfully downloaded from URL" and "Image successfully
   loaded from cached url at URL" on success
 - "Possible cached/downloaded picture at URL could not be loaded" on
   ImageReader error

* Clarify that network cache is on disk

Also migrate "Delete Downloaded Image" to a "Clear" button right next to
the network cache size.

* Remove qPrintable

* Move pixmap cache settings to card sources

* qDebug().nospace()

* some formatting on debug messages

* format

* inverted condition

---------

Co-authored-by: ebbit1q <ebbit1q@gmail.com>
This commit is contained in:
Basile Clement 2023-03-03 15:55:17 +00:00 committed by GitHub
parent 42e7a8b423
commit c14936c63c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 201 additions and 100 deletions

View file

@ -3,6 +3,7 @@
#include "carddatabase.h"
#include "gettextwithmax.h"
#include "main.h"
#include "pictureloader.h"
#include "releasechannel.h"
#include "sequenceEdit/sequenceedit.h"
#include "settingscache.h"
@ -68,18 +69,9 @@ GeneralSettingsPage::GeneralSettingsPage()
updateNotificationCheckBox.setChecked(settings.getNotifyAboutUpdates());
newVersionOracleCheckBox.setChecked(settings.getNotifyAboutNewVersion());
// pixmap cache
pixmapCacheEdit.setMinimum(PIXMAPCACHE_SIZE_MIN);
// 2047 is the max value to avoid overflowing of QPixmapCache::setCacheLimit(int size)
pixmapCacheEdit.setMaximum(PIXMAPCACHE_SIZE_MAX);
pixmapCacheEdit.setSingleStep(64);
pixmapCacheEdit.setValue(settings.getPixmapCacheSize());
pixmapCacheEdit.setSuffix(" MB");
showTipsOnStartup.setChecked(settings.getShowTipsOnStartup());
connect(&languageBox, SIGNAL(currentIndexChanged(int)), this, SLOT(languageBoxChanged(int)));
connect(&pixmapCacheEdit, SIGNAL(valueChanged(int)), &settings, SLOT(setPixmapCacheSize(int)));
connect(&updateReleaseChannelBox, SIGNAL(currentIndexChanged(int)), &settings, SLOT(setUpdateReleaseChannel(int)));
connect(&updateNotificationCheckBox, SIGNAL(stateChanged(int)), &settings, SLOT(setNotifyAboutUpdate(int)));
connect(&newVersionOracleCheckBox, SIGNAL(stateChanged(int)), &settings, SLOT(setNotifyAboutNewVersion(int)));
@ -90,8 +82,6 @@ GeneralSettingsPage::GeneralSettingsPage()
personalGrid->addWidget(&languageBox, 0, 1);
personalGrid->addWidget(&updateReleaseChannelLabel, 1, 0);
personalGrid->addWidget(&updateReleaseChannelBox, 1, 1);
personalGrid->addWidget(&pixmapCacheLabel, 2, 0);
personalGrid->addWidget(&pixmapCacheEdit, 2, 1);
personalGrid->addWidget(&updateNotificationCheckBox, 3, 0, 1, 2);
personalGrid->addWidget(&newVersionOracleCheckBox, 4, 0, 1, 2);
personalGrid->addWidget(&showTipsOnStartup, 5, 0, 1, 2);
@ -301,7 +291,6 @@ void GeneralSettingsPage::retranslateUi()
cardDatabasePathLabel.setText(tr("Card database:"));
customCardDatabasePathLabel.setText(tr("Custom database directory:"));
tokenDatabasePathLabel.setText(tr("Token database:"));
pixmapCacheLabel.setText(tr("Picture cache size:"));
updateReleaseChannelLabel.setText(tr("Update channel"));
updateNotificationCheckBox.setText(tr("Notify if a feature supported by the server is missing in my client"));
newVersionOracleCheckBox.setText(tr("Automatically run Oracle when running a new version of Cockatrice"));
@ -591,12 +580,38 @@ DeckEditorSettingsPage::DeckEditorSettingsPage()
messageListLayout->addWidget(messageToolBar);
messageListLayout->addWidget(urlList);
// pixmap cache
pixmapCacheEdit.setMinimum(PIXMAPCACHE_SIZE_MIN);
// 2047 is the max value to avoid overflowing of QPixmapCache::setCacheLimit(int size)
pixmapCacheEdit.setMaximum(PIXMAPCACHE_SIZE_MAX);
pixmapCacheEdit.setSingleStep(64);
pixmapCacheEdit.setValue(SettingsCache::instance().getPixmapCacheSize());
pixmapCacheEdit.setSuffix(" MB");
networkCacheEdit.setMinimum(NETWORK_CACHE_SIZE_MIN);
networkCacheEdit.setMaximum(NETWORK_CACHE_SIZE_MAX);
networkCacheEdit.setSingleStep(1);
networkCacheEdit.setValue(SettingsCache::instance().getNetworkCacheSizeInMB());
networkCacheEdit.setSuffix(tr(" MB"));
auto networkCacheLayout = new QHBoxLayout;
networkCacheLayout->addStretch();
networkCacheLayout->addWidget(&networkCacheLabel);
networkCacheLayout->addWidget(&networkCacheEdit);
auto pixmapCacheLayout = new QHBoxLayout;
pixmapCacheLayout->addStretch();
pixmapCacheLayout->addWidget(&pixmapCacheLabel);
pixmapCacheLayout->addWidget(&pixmapCacheEdit);
// Top Layout
lpGeneralGrid->addWidget(&picDownloadCheckBox, 0, 0);
lpGeneralGrid->addWidget(&resetDownloadURLs, 0, 1);
lpGeneralGrid->addLayout(messageListLayout, 1, 0, 1, 2);
lpGeneralGrid->addWidget(&urlLinkLabel, 2, 0);
lpGeneralGrid->addWidget(&clearDownloadedPicsButton, 2, 1);
lpGeneralGrid->addLayout(networkCacheLayout, 2, 0, 1, 2);
lpGeneralGrid->addLayout(pixmapCacheLayout, 3, 0, 1, 2);
lpGeneralGrid->addWidget(&urlLinkLabel, 4, 0);
lpGeneralGrid->addWidget(&clearDownloadedPicsButton, 4, 1);
// Spoiler Layout
lpSpoilerGrid->addWidget(&mcDownloadSpoilersCheckBox, 0, 0);
@ -611,6 +626,9 @@ DeckEditorSettingsPage::DeckEditorSettingsPage()
connect(&mcDownloadSpoilersCheckBox, SIGNAL(toggled(bool)), &SettingsCache::instance(),
SLOT(setDownloadSpoilerStatus(bool)));
connect(&mcDownloadSpoilersCheckBox, SIGNAL(toggled(bool)), this, SLOT(setSpoilersEnabled(bool)));
connect(&pixmapCacheEdit, SIGNAL(valueChanged(int)), &SettingsCache::instance(), SLOT(setPixmapCacheSize(int)));
connect(&networkCacheEdit, SIGNAL(valueChanged(int)), &SettingsCache::instance(),
SLOT(setNetworkCacheSizeInMB(int)));
mpGeneralGroupBox = new QGroupBox;
mpGeneralGroupBox->setLayout(lpGeneralGrid);
@ -635,6 +653,11 @@ void DeckEditorSettingsPage::resetDownloadedURLsButtonClicked()
void DeckEditorSettingsPage::clearDownloadedPicsButtonClicked()
{
PictureLoader::clearNetworkCache();
// These are not used anymore, but we don't delete them automatically, so
// we should do it here lest we leave pictures hanging around on users'
// machines.
QString picsPath = SettingsCache::instance().getPicsPath() + "/downloadedPics/";
QStringList dirs = QDir(picsPath).entryList(QDir::AllDirs | QDir::NoDotAndDotDot);
bool outerSuccessRemove = true;
@ -662,6 +685,7 @@ void DeckEditorSettingsPage::clearDownloadedPicsButtonClicked()
}
if (outerSuccessRemove) {
QMessageBox::information(this, tr("Success"), tr("Downloaded card pictures have been reset."));
QDir(SettingsCache::instance().getPicsPath()).rmdir("downloadedPics");
} else {
QMessageBox::critical(this, tr("Error"), tr("One or more downloaded card pictures could not be cleared."));
}
@ -785,6 +809,10 @@ void DeckEditorSettingsPage::retranslateUi()
urlLinkLabel.setText(QString("<a href='%1'>%2</a>").arg(WIKI_CUSTOM_PIC_URL).arg(tr("How to add a custom URL")));
clearDownloadedPicsButton.setText(tr("Delete Downloaded Images"));
resetDownloadURLs.setText(tr("Reset Download URLs"));
networkCacheLabel.setText(tr("Downloaded images directory size:"));
networkCacheEdit.setToolTip(tr("On-disk cache for downloaded pictures"));
pixmapCacheLabel.setText(tr("Picture cache size:"));
pixmapCacheEdit.setToolTip(tr("In-memory cache for pictures not currently on screen"));
}
MessagesSettingsPage::MessagesSettingsPage()

View file

@ -58,7 +58,6 @@ private:
QLineEdit *tokenDatabasePathEdit;
QPushButton *resetAllPathsButton;
QLabel *allPathsResetLabel;
QSpinBox pixmapCacheEdit;
QGroupBox *personalGroupBox;
QGroupBox *pathsGroupBox;
QComboBox languageBox;
@ -66,7 +65,6 @@ private:
QCheckBox newVersionOracleCheckBox;
QComboBox updateReleaseChannelBox;
QLabel languageLabel;
QLabel pixmapCacheLabel;
QLabel deckPathLabel;
QLabel replaysPathLabel;
QLabel picsPathLabel;
@ -168,6 +166,10 @@ private:
QLabel infoOnSpoilersLabel;
QPushButton *mpSpoilerPathButton;
QPushButton *updateNowButton;
QLabel networkCacheLabel;
QSpinBox networkCacheEdit;
QSpinBox pixmapCacheEdit;
QLabel pixmapCacheLabel;
};
class MessagesSettingsPage : public AbstractSettingsPage

View file

@ -13,6 +13,7 @@
#include <QFile>
#include <QImageReader>
#include <QNetworkAccessManager>
#include <QNetworkDiskCache>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QPainter>
@ -116,6 +117,21 @@ PictureLoaderWorker::PictureLoaderWorker()
connect(&SettingsCache::instance(), SIGNAL(picDownloadChanged()), this, SLOT(picDownloadChanged()));
networkManager = new QNetworkAccessManager(this);
// We need a timeout to ensure requests don't hang indefinitely in case of
// cache corruption, see related Qt bug: https://bugreports.qt.io/browse/QTBUG-111397
// Use Qt's default timeout (30s, as of 2023-02-22)
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
networkManager->setTransferTimeout();
#endif
auto cache = new QNetworkDiskCache(this);
cache->setCacheDirectory(SettingsCache::instance().getNetworkCachePath());
// Note: the settings is in MB, but QNetworkDiskCache uses bytes
connect(&SettingsCache::instance(), &SettingsCache::networkCacheSizeChanged, cache,
[cache](int newSizeInMB) { cache->setMaximumCacheSize(1024L * 1024L * static_cast<qint64>(newSizeInMB)); });
networkManager->setCache(cache);
// Use a ManualRedirectPolicy since we keep track of redirects in picDownloadFinished
// We can't use NoLessSafeRedirectPolicy because it is not applied with AlwaysCache
networkManager->setRedirectPolicy(QNetworkRequest::ManualRedirectPolicy);
connect(networkManager, SIGNAL(finished(QNetworkReply *)), this, SLOT(picDownloadFinished(QNetworkReply *)));
pictureLoaderThread = new QThread;
@ -149,38 +165,20 @@ void PictureLoaderWorker::processLoadQueue()
QString cardName = cardBeingLoaded.getCard()->getName();
QString correctedCardName = cardBeingLoaded.getCard()->getCorrectedName();
qDebug() << "PictureLoader: [card: " << cardName << " set: " << setName << "]: Trying to load picture";
qDebug().nospace() << "PictureLoader: [card: " << cardName << " set: " << setName
<< "]: Trying to load picture";
if (cardImageExistsOnDisk(setName, correctedCardName)) {
continue;
}
if (picDownload) {
qDebug() << "PictureLoader: [card: " << cardName << " set: " << setName
<< "]: Picture not found on disk, trying to download";
qDebug().nospace() << "PictureLoader: [card: " << cardName << " set: " << setName
<< "]: No custom picture, trying to download";
cardsToDownload.append(cardBeingLoaded);
cardBeingLoaded.clear();
if (!downloadRunning) {
startNextPicDownload();
}
} else {
if (cardBeingLoaded.nextSet()) {
qDebug() << "PictureLoader: [card: " << cardName << " set: " << setName
<< "]: Picture NOT found and download disabled, moving to next "
"set (new set: "
<< setName << " card: " << cardName << ")";
mutex.lock();
loadQueue.prepend(cardBeingLoaded);
cardBeingLoaded.clear();
mutex.unlock();
} else {
qDebug() << "PictureLoader: [card: " << cardName << " set: " << setName
<< "]: Picture NOT found, download disabled, no more sets to "
"try: BAILING OUT (old set: "
<< setName << " card: " << cardName << ")";
imageLoaded(cardBeingLoaded.getCard(), QImage());
}
}
}
}
@ -206,6 +204,8 @@ bool PictureLoaderWorker::cardImageExistsOnDisk(QString &setName, QString &corre
if (!setName.isEmpty()) {
picsPaths << picsPath + "/" + setName + "/" + correctedCardname
// We no longer store downloaded images there, but don't just ignore
// stuff that old versions have put there.
<< picsPath + "/downloadedPics/" + setName + "/" + correctedCardname;
}
@ -215,21 +215,21 @@ bool PictureLoaderWorker::cardImageExistsOnDisk(QString &setName, QString &corre
for (const auto &picsPath : picsPaths) {
imgReader.setFileName(picsPath);
if (imgReader.read(&image)) {
qDebug() << "PictureLoader: [card: " << correctedCardname << " set: " << setName
qDebug().nospace() << "PictureLoader: [card: " << correctedCardname << " set: " << setName
<< "]: Picture found on disk.";
imageLoaded(cardBeingLoaded.getCard(), image);
return true;
}
imgReader.setFileName(picsPath + ".full");
if (imgReader.read(&image)) {
qDebug() << "PictureLoader: [card: " << correctedCardname << " set: " << setName
qDebug().nospace() << "PictureLoader: [card: " << correctedCardname << " set: " << setName
<< "]: Picture.full found on disk.";
imageLoaded(cardBeingLoaded.getCard(), image);
return true;
}
imgReader.setFileName(picsPath + ".xlhq");
if (imgReader.read(&image)) {
qDebug() << "PictureLoader: [card: " << correctedCardname << " set: " << setName
qDebug().nospace() << "PictureLoader: [card: " << correctedCardname << " set: " << setName
<< "]: Picture.xlhq found on disk.";
imageLoaded(cardBeingLoaded.getCard(), image);
return true;
@ -385,11 +385,10 @@ void PictureLoaderWorker::startNextPicDownload()
picDownloadFailed();
} else {
QUrl url(picUrl);
QNetworkRequest req(url);
qDebug() << "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getCorrectedName()
<< " set: " << cardBeingDownloaded.getSetName()
<< "]: Trying to download picture from url:" << url.toDisplayString();
networkManager->get(req);
qDebug().nospace() << "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getCorrectedName()
<< " set: " << cardBeingDownloaded.getSetName() << "]: Trying to fetch picture from url "
<< url.toDisplayString();
makeRequest(url);
}
}
@ -404,10 +403,10 @@ void PictureLoaderWorker::picDownloadFailed()
loadQueue.prepend(cardBeingDownloaded);
mutex.unlock();
} else {
qDebug() << "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getCorrectedName()
<< " set: " << cardBeingDownloaded.getSetName()
<< "]: Picture NOT found, download failed, no more url combinations "
"to try: BAILING OUT";
qDebug().nospace() << "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getCorrectedName()
<< " set: " << cardBeingDownloaded.getSetName() << "]: Picture NOT found, "
<< (picDownload ? "download failed" : "downloads disabled")
<< ", no more url combinations to try: BAILING OUT";
imageLoaded(cardBeingDownloaded.getCard(), QImage());
cardBeingDownloaded.clear();
}
@ -420,20 +419,53 @@ bool PictureLoaderWorker::imageIsBlackListed(const QByteArray &picData)
return md5Blacklist.contains(md5sum);
}
void PictureLoaderWorker::picDownloadFinished(QNetworkReply *reply)
QNetworkReply *PictureLoaderWorker::makeRequest(const QUrl &url)
{
if (reply->error()) {
qDebug() << "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getName()
<< " set: " << cardBeingDownloaded.getSetName() << "]: Download failed:" << reply->errorString();
QNetworkRequest req(url);
if (!picDownload) {
req.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysCache);
}
return networkManager->get(req);
}
void PictureLoaderWorker::picDownloadFinished(QNetworkReply *reply)
{
bool isFromCache = reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool();
if (reply->error()) {
if (isFromCache) {
qDebug().nospace() << "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getName()
<< " set: " << cardBeingDownloaded.getSetName()
<< "]: Removing corrupted cache file for url " << reply->url().toDisplayString()
<< " and retrying (" << reply->errorString() << ")";
networkManager->cache()->remove(reply->url());
makeRequest(reply->url());
} else {
qDebug().nospace() << "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getName()
<< " set: " << cardBeingDownloaded.getSetName()
<< "]: " << (picDownload ? "Download" : "Cache search") << " failed for url "
<< reply->url().toDisplayString() << " (" << reply->errorString() << ")";
picDownloadFailed();
startNextPicDownload();
}
reply->deleteLater();
return;
}
// List of status codes from https://doc.qt.io/qt-6/qnetworkreply.html#redirected
int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (statusCode == 301 || statusCode == 302) {
QUrl redirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
QNetworkRequest req(redirectUrl);
qDebug() << "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getName()
<< " set: " << cardBeingDownloaded.getSetName() << "]: following redirect:" << req.url().toString();
networkManager->get(req);
if (statusCode == 301 || statusCode == 302 || statusCode == 303 || statusCode == 305 || statusCode == 307 ||
statusCode == 308) {
QUrl redirectUrl = reply->header(QNetworkRequest::LocationHeader).toUrl();
qDebug().nospace() << "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getName()
<< " set: " << cardBeingDownloaded.getSetName() << "]: following "
<< (isFromCache ? "cached redirect" : "redirect") << " to " << redirectUrl.toDisplayString();
makeRequest(redirectUrl);
reply->deleteLater();
return;
}
@ -441,10 +473,10 @@ void PictureLoaderWorker::picDownloadFinished(QNetworkReply *reply)
const QByteArray &picData = reply->peek(reply->size());
if (imageIsBlackListed(picData)) {
qDebug() << "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getName()
qDebug().nospace() << "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getName()
<< " set: " << cardBeingDownloaded.getSetName()
<< "]:Picture downloaded, but blacklisted, will consider it as "
"not found";
<< "]: Picture found, but blacklisted, will consider it as not found";
picDownloadFailed();
reply->deleteLater();
startNextPicDownload();
@ -456,40 +488,19 @@ void PictureLoaderWorker::picDownloadFinished(QNetworkReply *reply)
QImageReader imgReader;
imgReader.setDecideFormatFromContent(true);
imgReader.setDevice(reply);
// the format is determined prior to reading the QImageReader data into a QImage object, as that wipes the
// QImageReader buffer
QString extension = "." + imgReader.format();
if (extension == ".jpeg") {
extension = ".jpg";
}
if (imgReader.read(&testImage)) {
QString setName = cardBeingDownloaded.getSetName();
if (!setName.isEmpty()) {
if (!QDir().mkpath(picsPath + "/downloadedPics/" + setName)) {
qDebug() << "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getName()
<< " set: " << cardBeingDownloaded.getSetName()
<< "]: " << picsPath + "/downloadedPics/" + setName + " could not be created.";
return;
}
QFile newPic(picsPath + "/downloadedPics/" + setName + "/" +
cardBeingDownloaded.getCard()->getCorrectedName() + extension);
if (!newPic.open(QIODevice::WriteOnly)) {
return;
}
newPic.write(picData);
newPic.close();
}
imageLoaded(cardBeingDownloaded.getCard(), testImage);
qDebug() << "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getName()
<< " set: " << cardBeingDownloaded.getSetName() << "]: Image successfully downloaded from "
<< reply->request().url().toDisplayString();
qDebug().nospace() << "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getName()
<< " set: " << cardBeingDownloaded.getSetName() << "]: Image successfully "
<< (isFromCache ? "loaded from cached" : "downloaded from") << " url "
<< reply->url().toDisplayString();
} else {
qDebug() << "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getName()
<< " set: " << cardBeingDownloaded.getSetName() << "]: Possible picture at "
<< reply->request().url().toDisplayString() << " could not be loaded";
qDebug().nospace() << "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getName()
<< " set: " << cardBeingDownloaded.getSetName() << "]: Possible "
<< (isFromCache ? "cached" : "downloaded") << " picture at "
<< reply->url().toDisplayString() << " could not be loaded";
picDownloadFailed();
}
@ -533,6 +544,11 @@ void PictureLoaderWorker::picsPathChanged()
customPicsPath = SettingsCache::instance().getCustomPicsPath();
}
void PictureLoaderWorker::clearNetworkCache()
{
networkManager->cache()->clear();
}
PictureLoader::PictureLoader() : QObject(nullptr)
{
worker = new PictureLoaderWorker;
@ -613,6 +629,11 @@ void PictureLoader::clearPixmapCache()
QPixmapCache::clear();
}
void PictureLoader::clearNetworkCache()
{
getInstance().worker->clearNetworkCache();
}
void PictureLoader::cacheCardPixmaps(QList<CardInfoPtr> cards)
{
QPixmap tmp;

View file

@ -73,6 +73,7 @@ public:
~PictureLoaderWorker() override;
void enqueueImageLoad(CardInfoPtr card);
void clearNetworkCache();
private:
static QStringList md5Blacklist;
@ -87,8 +88,9 @@ private:
PictureToLoad cardBeingDownloaded;
bool picDownload, downloadRunning, loadQueueRunning;
void startNextPicDownload();
bool cardImageExistsOnDisk(QString &, QString &);
bool cardImageExistsOnDisk(QString &setName, QString &correctedCardName);
bool imageIsBlackListed(const QByteArray &);
QNetworkReply *makeRequest(const QUrl &url);
private slots:
void picDownloadFinished(QNetworkReply *reply);
void picDownloadFailed();
@ -97,6 +99,7 @@ private slots:
void picsPathChanged();
public slots:
void processLoadQueue();
signals:
void startLoadQueue();
void imageLoaded(CardInfoPtr card, const QImage &image);
@ -127,9 +130,14 @@ public:
static void clearPixmapCache(CardInfoPtr card);
static void clearPixmapCache();
static void cacheCardPixmaps(QList<CardInfoPtr> cards);
public slots:
static void clearNetworkCache();
private slots:
void picDownloadChanged();
void picsPathChanged();
public slots:
void imageLoaded(CardInfoPtr card, const QImage &image);
};

View file

@ -26,6 +26,19 @@ QString SettingsCache::getSettingsPath()
return getDataPath() + "/settings/";
}
QString SettingsCache::getCachePath() const
{
if (isPortableBuild)
return qApp->applicationDirPath() + "/cache";
else
return QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
}
QString SettingsCache::getNetworkCachePath() const
{
return getCachePath() + "/downloaded/";
}
void SettingsCache::translateLegacySettings()
{
if (isPortableBuild)
@ -206,6 +219,8 @@ SettingsCache::SettingsCache()
if (pixmapCacheSize < PIXMAPCACHE_SIZE_MIN || pixmapCacheSize > PIXMAPCACHE_SIZE_MAX)
pixmapCacheSize = PIXMAPCACHE_SIZE_DEFAULT;
networkCacheSize = settings->value("personal/networkCacheSize", NETWORK_CACHE_SIZE_DEFAULT).toInt();
picDownload = settings->value("personal/picturedownload", true).toBool();
mainWindowGeometry = settings->value("interface/main_window_geometry").toByteArray();
@ -609,6 +624,13 @@ void SettingsCache::setPixmapCacheSize(const int _pixmapCacheSize)
emit pixmapCacheSizeChanged(pixmapCacheSize);
}
void SettingsCache::setNetworkCacheSizeInMB(const int _networkCacheSize)
{
networkCacheSize = _networkCacheSize;
settings->setValue("personal/networkCacheSize", networkCacheSize);
emit networkCacheSizeChanged(networkCacheSize);
}
void SettingsCache::setClientID(const QString &_clientID)
{
clientID = _clientID;

View file

@ -20,6 +20,11 @@ class ReleaseChannel;
#define PIXMAPCACHE_SIZE_MIN 64
#define PIXMAPCACHE_SIZE_MAX 2047
// In MB
constexpr int NETWORK_CACHE_SIZE_DEFAULT = 1024 * 4; // 4 GB
constexpr int NETWORK_CACHE_SIZE_MIN = 1; // 1 MB
constexpr int NETWORK_CACHE_SIZE_MAX = 1024 * 1024; // 1 TB
#define DEFAULT_LANG_NAME "English"
#define CLIENT_INFO_NOT_SET "notset"
@ -46,6 +51,7 @@ signals:
void ignoreUnregisteredUsersChanged();
void ignoreUnregisteredUserMessagesChanged();
void pixmapCacheSizeChanged(int newSizeInMBs);
void networkCacheSizeChanged(int newSizeInMBs);
void masterVolumeChanged(int value);
void chatMentionCompleterChanged();
void downloadSpoilerTimeIndexChanged();
@ -106,6 +112,7 @@ private:
QString knownMissingFeatures;
bool useTearOffMenus;
int pixmapCacheSize;
int networkCacheSize;
bool scaleCards;
bool showMessagePopups;
bool showMentionPopups;
@ -137,6 +144,8 @@ public:
SettingsCache();
QString getDataPath();
QString getSettingsPath();
QString getCachePath() const;
QString getNetworkCachePath() const;
const QByteArray &getMainWindowGeometry() const
{
return mainWindowGeometry;
@ -330,6 +339,10 @@ public:
{
return pixmapCacheSize;
}
int getNetworkCacheSizeInMB() const
{
return networkCacheSize;
}
bool getScaleCards() const
{
return scaleCards;
@ -520,6 +533,7 @@ public slots:
void setIgnoreUnregisteredUsers(int _ignoreUnregisteredUsers);
void setIgnoreUnregisteredUserMessages(int _ignoreUnregisteredUserMessages);
void setPixmapCacheSize(const int _pixmapCacheSize);
void setNetworkCacheSizeInMB(const int _networkCacheSize);
void setCardScaling(const int _scaleCards);
void setShowMessagePopups(const int _showMessagePopups);
void setShowMentionPopups(const int _showMentionPopups);

View file

@ -202,6 +202,9 @@ void SettingsCache::setTokenDialogGeometry(const QByteArray & /* _tokenDialogGeo
void SettingsCache::setPixmapCacheSize(const int /* _pixmapCacheSize */)
{
}
void SettingsCache::setNetworkCacheSizeInMB(const int /* _networkCacheSize */)
{
}
void SettingsCache::setClientID(const QString & /* _clientID */)
{
}

View file

@ -206,6 +206,9 @@ void SettingsCache::setTokenDialogGeometry(const QByteArray & /* _tokenDialogGeo
void SettingsCache::setPixmapCacheSize(const int /* _pixmapCacheSize */)
{
}
void SettingsCache::setNetworkCacheSizeInMB(const int /* _networkCacheSize */)
{
}
void SettingsCache::setClientID(const QString & /* _clientID */)
{
}