* 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>
144 lines
3.5 KiB
C++
144 lines
3.5 KiB
C++
#ifndef PICTURELOADER_H
|
|
#define PICTURELOADER_H
|
|
|
|
#include "carddatabase.h"
|
|
|
|
#include <QList>
|
|
#include <QMap>
|
|
#include <QMutex>
|
|
#include <QNetworkRequest>
|
|
class QNetworkAccessManager;
|
|
class QNetworkReply;
|
|
class QThread;
|
|
|
|
class PictureToLoad
|
|
{
|
|
private:
|
|
class SetDownloadPriorityComparator
|
|
{
|
|
public:
|
|
/*
|
|
* Returns true if a has higher download priority than b
|
|
* Enabled sets have priority over disabled sets
|
|
* Both groups follows the user-defined order
|
|
*/
|
|
inline bool operator()(const CardSetPtr &a, const CardSetPtr &b) const
|
|
{
|
|
if (a->getEnabled()) {
|
|
return !b->getEnabled() || a->getSortKey() < b->getSortKey();
|
|
} else {
|
|
return !b->getEnabled() && a->getSortKey() < b->getSortKey();
|
|
}
|
|
}
|
|
};
|
|
|
|
CardInfoPtr card;
|
|
QList<CardSetPtr> sortedSets;
|
|
QList<QString> urlTemplates;
|
|
QList<QString> currentSetUrls;
|
|
QString currentUrl;
|
|
CardSetPtr currentSet;
|
|
|
|
public:
|
|
explicit PictureToLoad(CardInfoPtr _card = CardInfoPtr());
|
|
|
|
CardInfoPtr getCard() const
|
|
{
|
|
return card;
|
|
}
|
|
void clear()
|
|
{
|
|
card.clear();
|
|
}
|
|
QString getCurrentUrl() const
|
|
{
|
|
return currentUrl;
|
|
}
|
|
CardSetPtr getCurrentSet() const
|
|
{
|
|
return currentSet;
|
|
}
|
|
QString getSetName() const;
|
|
QString transformUrl(const QString &urlTemplate) const;
|
|
bool nextSet();
|
|
bool nextUrl();
|
|
void populateSetUrls();
|
|
};
|
|
|
|
class PictureLoaderWorker : public QObject
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
explicit PictureLoaderWorker();
|
|
~PictureLoaderWorker() override;
|
|
|
|
void enqueueImageLoad(CardInfoPtr card);
|
|
void clearNetworkCache();
|
|
|
|
private:
|
|
static QStringList md5Blacklist;
|
|
|
|
QThread *pictureLoaderThread;
|
|
QString picsPath, customPicsPath;
|
|
QList<PictureToLoad> loadQueue;
|
|
QMutex mutex;
|
|
QNetworkAccessManager *networkManager;
|
|
QList<PictureToLoad> cardsToDownload;
|
|
PictureToLoad cardBeingLoaded;
|
|
PictureToLoad cardBeingDownloaded;
|
|
bool picDownload, downloadRunning, loadQueueRunning;
|
|
void startNextPicDownload();
|
|
bool cardImageExistsOnDisk(QString &setName, QString &correctedCardName);
|
|
bool imageIsBlackListed(const QByteArray &);
|
|
QNetworkReply *makeRequest(const QUrl &url);
|
|
private slots:
|
|
void picDownloadFinished(QNetworkReply *reply);
|
|
void picDownloadFailed();
|
|
|
|
void picDownloadChanged();
|
|
void picsPathChanged();
|
|
public slots:
|
|
void processLoadQueue();
|
|
|
|
signals:
|
|
void startLoadQueue();
|
|
void imageLoaded(CardInfoPtr card, const QImage &image);
|
|
};
|
|
|
|
class PictureLoader : public QObject
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
static PictureLoader &getInstance()
|
|
{
|
|
static PictureLoader instance;
|
|
return instance;
|
|
}
|
|
|
|
private:
|
|
explicit PictureLoader();
|
|
~PictureLoader() override;
|
|
// Singleton - Don't implement copy constructor and assign operator
|
|
PictureLoader(PictureLoader const &);
|
|
void operator=(PictureLoader const &);
|
|
|
|
PictureLoaderWorker *worker;
|
|
|
|
public:
|
|
static void getPixmap(QPixmap &pixmap, CardInfoPtr card, QSize size);
|
|
static void getCardBackPixmap(QPixmap &pixmap, QSize size);
|
|
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);
|
|
};
|
|
#endif
|