mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-07-01 11:03:54 -07:00
Refactor Picture Loader (#5457)
This commit is contained in:
parent
a417b049da
commit
ba89495dc0
17 changed files with 614 additions and 581 deletions
|
|
@ -93,7 +93,9 @@ set(cockatrice_SOURCES
|
||||||
src/server/pending_command.cpp
|
src/server/pending_command.cpp
|
||||||
src/game/phase.cpp
|
src/game/phase.cpp
|
||||||
src/client/ui/phases_toolbar.cpp
|
src/client/ui/phases_toolbar.cpp
|
||||||
src/client/ui/picture_loader.cpp
|
src/client/ui/picture_loader/picture_loader.cpp
|
||||||
|
src/client/ui/picture_loader/picture_loader_worker.cpp
|
||||||
|
src/client/ui/picture_loader/picture_to_load.cpp
|
||||||
src/game/zones/pile_zone.cpp
|
src/game/zones/pile_zone.cpp
|
||||||
src/client/ui/pixel_map_generator.cpp
|
src/client/ui/pixel_map_generator.cpp
|
||||||
src/game/player/player.cpp
|
src/game/player/player.cpp
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
#include "../../main.h"
|
#include "../../main.h"
|
||||||
#include "../../server/pending_command.h"
|
#include "../../server/pending_command.h"
|
||||||
#include "../../settings/cache_settings.h"
|
#include "../../settings/cache_settings.h"
|
||||||
#include "../ui/picture_loader.h"
|
#include "../ui/picture_loader/picture_loader.h"
|
||||||
#include "../ui/pixel_map_generator.h"
|
#include "../ui/pixel_map_generator.h"
|
||||||
#include "../ui/widgets/printing_selector/printing_selector.h"
|
#include "../ui/widgets/printing_selector/printing_selector.h"
|
||||||
#include "pb/command_deck_upload.pb.h"
|
#include "pb/command_deck_upload.pb.h"
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
#include "../network/replay_timeline_widget.h"
|
#include "../network/replay_timeline_widget.h"
|
||||||
#include "../ui/line_edit_completer.h"
|
#include "../ui/line_edit_completer.h"
|
||||||
#include "../ui/phases_toolbar.h"
|
#include "../ui/phases_toolbar.h"
|
||||||
#include "../ui/picture_loader.h"
|
#include "../ui/picture_loader/picture_loader.h"
|
||||||
#include "../ui/window_main.h"
|
#include "../ui/window_main.h"
|
||||||
#include "get_pb_extension.h"
|
#include "get_pb_extension.h"
|
||||||
#include "pb/command_concede.pb.h"
|
#include "pb/command_concede.pb.h"
|
||||||
|
|
|
||||||
|
|
@ -1,161 +0,0 @@
|
||||||
#ifndef PICTURELOADER_H
|
|
||||||
#define PICTURELOADER_H
|
|
||||||
|
|
||||||
#include "../../game/cards/card_database.h"
|
|
||||||
|
|
||||||
#include <QList>
|
|
||||||
#include <QMap>
|
|
||||||
#include <QMutex>
|
|
||||||
#include <QNetworkRequest>
|
|
||||||
class QNetworkAccessManager;
|
|
||||||
class QNetworkReply;
|
|
||||||
class QThread;
|
|
||||||
|
|
||||||
#define REDIRECT_HEADER_NAME "redirects"
|
|
||||||
#define REDIRECT_ORIGINAL_URL "original"
|
|
||||||
#define REDIRECT_URL "redirect"
|
|
||||||
#define REDIRECT_TIMESTAMP "timestamp"
|
|
||||||
#define REDIRECT_CACHE_FILENAME "cache.ini"
|
|
||||||
|
|
||||||
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;
|
|
||||||
QHash<QUrl, QPair<QUrl, QDateTime>> redirectCache; // Stores redirect and timestamp
|
|
||||||
QString cacheFilePath; // Path to persistent storage
|
|
||||||
static constexpr int CacheTTLInDays = 30; // TODO: Make user configurable
|
|
||||||
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);
|
|
||||||
void cacheRedirect(const QUrl &originalUrl, const QUrl &redirectUrl);
|
|
||||||
QUrl getCachedRedirect(const QUrl &originalUrl) const;
|
|
||||||
void loadRedirectCache();
|
|
||||||
void saveRedirectCache() const;
|
|
||||||
void cleanStaleEntries();
|
|
||||||
|
|
||||||
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 getCardBackLoadingInProgressPixmap(QPixmap &pixmap, QSize size);
|
|
||||||
static void getCardBackLoadingFailedPixmap(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
|
|
||||||
158
cockatrice/src/client/ui/picture_loader/picture_loader.cpp
Normal file
158
cockatrice/src/client/ui/picture_loader/picture_loader.cpp
Normal file
|
|
@ -0,0 +1,158 @@
|
||||||
|
#include "picture_loader.h"
|
||||||
|
|
||||||
|
#include "../../../settings/cache_settings.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QBuffer>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QDirIterator>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QMovie>
|
||||||
|
#include <QNetworkDiskCache>
|
||||||
|
#include <QNetworkRequest>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QPixmapCache>
|
||||||
|
#include <QScreen>
|
||||||
|
#include <QThread>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <qloggingcategory.h>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
Q_LOGGING_CATEGORY(PictureLoaderLog, "picture_loader")
|
||||||
|
|
||||||
|
// never cache more than 300 cards at once for a single deck
|
||||||
|
#define CACHED_CARD_PER_DECK_MAX 300
|
||||||
|
|
||||||
|
PictureLoader::PictureLoader() : QObject(nullptr)
|
||||||
|
{
|
||||||
|
worker = new PictureLoaderWorker;
|
||||||
|
connect(&SettingsCache::instance(), SIGNAL(picsPathChanged()), this, SLOT(picsPathChanged()));
|
||||||
|
connect(&SettingsCache::instance(), SIGNAL(picDownloadChanged()), this, SLOT(picDownloadChanged()));
|
||||||
|
|
||||||
|
connect(worker, SIGNAL(imageLoaded(CardInfoPtr, const QImage &)), this,
|
||||||
|
SLOT(imageLoaded(CardInfoPtr, const QImage &)));
|
||||||
|
}
|
||||||
|
|
||||||
|
PictureLoader::~PictureLoader()
|
||||||
|
{
|
||||||
|
worker->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PictureLoader::getCardBackPixmap(QPixmap &pixmap, QSize size)
|
||||||
|
{
|
||||||
|
QString backCacheKey = "_trice_card_back_" + QString::number(size.width()) + QString::number(size.height());
|
||||||
|
if (!QPixmapCache::find(backCacheKey, &pixmap)) {
|
||||||
|
qCDebug(PictureLoaderLog) << "PictureLoader: cache fail for" << backCacheKey;
|
||||||
|
pixmap = QPixmap("theme:cardback").scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||||
|
QPixmapCache::insert(backCacheKey, pixmap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PictureLoader::getCardBackLoadingInProgressPixmap(QPixmap &pixmap, QSize size)
|
||||||
|
{
|
||||||
|
QString backCacheKey = "_trice_card_back_" + QString::number(size.width()) + QString::number(size.height());
|
||||||
|
if (!QPixmapCache::find(backCacheKey, &pixmap)) {
|
||||||
|
qCDebug(PictureLoaderLog) << "PictureLoader: cache fail for" << backCacheKey;
|
||||||
|
pixmap = QPixmap("theme:cardback").scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||||
|
QPixmapCache::insert(backCacheKey, pixmap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PictureLoader::getCardBackLoadingFailedPixmap(QPixmap &pixmap, QSize size)
|
||||||
|
{
|
||||||
|
QString backCacheKey = "_trice_card_back_" + QString::number(size.width()) + QString::number(size.height());
|
||||||
|
if (!QPixmapCache::find(backCacheKey, &pixmap)) {
|
||||||
|
qCDebug(PictureLoaderLog) << "PictureLoader: cache fail for" << backCacheKey;
|
||||||
|
pixmap = QPixmap("theme:cardback").scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||||
|
QPixmapCache::insert(backCacheKey, pixmap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PictureLoader::getPixmap(QPixmap &pixmap, CardInfoPtr card, QSize size)
|
||||||
|
{
|
||||||
|
if (card == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// search for an exact size copy of the picture in cache
|
||||||
|
QString key = card->getPixmapCacheKey();
|
||||||
|
QString sizeKey = key + QLatin1Char('_') + QString::number(size.width()) + QString::number(size.height());
|
||||||
|
if (QPixmapCache::find(sizeKey, &pixmap))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// load the image and create a copy of the correct size
|
||||||
|
QPixmap bigPixmap;
|
||||||
|
if (QPixmapCache::find(key, &bigPixmap)) {
|
||||||
|
QScreen *screen = qApp->primaryScreen();
|
||||||
|
qreal dpr = screen->devicePixelRatio();
|
||||||
|
pixmap = bigPixmap.scaled(size * dpr, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||||
|
pixmap.setDevicePixelRatio(dpr);
|
||||||
|
QPixmapCache::insert(sizeKey, pixmap);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the card to the load queue
|
||||||
|
getInstance().worker->enqueueImageLoad(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PictureLoader::imageLoaded(CardInfoPtr card, const QImage &image)
|
||||||
|
{
|
||||||
|
if (image.isNull()) {
|
||||||
|
QPixmapCache::insert(card->getPixmapCacheKey(), QPixmap());
|
||||||
|
} else {
|
||||||
|
if (card->getUpsideDownArt()) {
|
||||||
|
QImage mirrorImage = image.mirrored(true, true);
|
||||||
|
QPixmapCache::insert(card->getPixmapCacheKey(), QPixmap::fromImage(mirrorImage));
|
||||||
|
} else {
|
||||||
|
QPixmapCache::insert(card->getPixmapCacheKey(), QPixmap::fromImage(image));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
card->emitPixmapUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PictureLoader::clearPixmapCache(CardInfoPtr card)
|
||||||
|
{
|
||||||
|
if (card) {
|
||||||
|
QPixmapCache::remove(card->getPixmapCacheKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PictureLoader::clearPixmapCache()
|
||||||
|
{
|
||||||
|
QPixmapCache::clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PictureLoader::clearNetworkCache()
|
||||||
|
{
|
||||||
|
getInstance().worker->clearNetworkCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PictureLoader::cacheCardPixmaps(QList<CardInfoPtr> cards)
|
||||||
|
{
|
||||||
|
QPixmap tmp;
|
||||||
|
int max = qMin(cards.size(), CACHED_CARD_PER_DECK_MAX);
|
||||||
|
for (int i = 0; i < max; ++i) {
|
||||||
|
const CardInfoPtr &card = cards.at(i);
|
||||||
|
if (!card) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString key = card->getPixmapCacheKey();
|
||||||
|
if (QPixmapCache::find(key, &tmp)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
getInstance().worker->enqueueImageLoad(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PictureLoader::picDownloadChanged()
|
||||||
|
{
|
||||||
|
QPixmapCache::clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PictureLoader::picsPathChanged()
|
||||||
|
{
|
||||||
|
QPixmapCache::clear();
|
||||||
|
}
|
||||||
45
cockatrice/src/client/ui/picture_loader/picture_loader.h
Normal file
45
cockatrice/src/client/ui/picture_loader/picture_loader.h
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
#ifndef PICTURELOADER_H
|
||||||
|
#define PICTURELOADER_H
|
||||||
|
|
||||||
|
#include "../../../game/cards/card_database.h"
|
||||||
|
#include "picture_loader_worker.h"
|
||||||
|
|
||||||
|
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 getCardBackLoadingInProgressPixmap(QPixmap &pixmap, QSize size);
|
||||||
|
static void getCardBackLoadingFailedPixmap(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
|
||||||
|
|
@ -1,126 +1,17 @@
|
||||||
#include "picture_loader.h"
|
#include "picture_loader_worker.h"
|
||||||
|
|
||||||
#include "../../game/cards/card_database_manager.h"
|
#include "../../../game/cards/card_database_manager.h"
|
||||||
#include "../../settings/cache_settings.h"
|
#include "../../../settings/cache_settings.h"
|
||||||
|
|
||||||
#include <QApplication>
|
|
||||||
#include <QBuffer>
|
#include <QBuffer>
|
||||||
#include <QCryptographicHash>
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QDirIterator>
|
#include <QDirIterator>
|
||||||
#include <QFileInfo>
|
#include <QLoggingCategory>
|
||||||
#include <QImageReader>
|
|
||||||
#include <QMovie>
|
#include <QMovie>
|
||||||
#include <QNetworkAccessManager>
|
|
||||||
#include <QNetworkDiskCache>
|
#include <QNetworkDiskCache>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
#include <QNetworkRequest>
|
|
||||||
#include <QPainter>
|
|
||||||
#include <QPixmapCache>
|
|
||||||
#include <QRegularExpression>
|
|
||||||
#include <QScreen>
|
|
||||||
#include <QSet>
|
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QUrl>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <qloggingcategory.h>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
Q_LOGGING_CATEGORY(PictureLoaderLog, "picture_loader")
|
Q_LOGGING_CATEGORY(PictureLoaderWorkerLog, "picture_loader.worker");
|
||||||
|
|
||||||
// never cache more than 300 cards at once for a single deck
|
|
||||||
#define CACHED_CARD_PER_DECK_MAX 300
|
|
||||||
|
|
||||||
PictureToLoad::PictureToLoad(CardInfoPtr _card)
|
|
||||||
: card(std::move(_card)), urlTemplates(SettingsCache::instance().downloads().getAllURLs())
|
|
||||||
{
|
|
||||||
if (card) {
|
|
||||||
for (const auto &cardInfoPerSetList : card->getSets()) {
|
|
||||||
for (const auto &set : cardInfoPerSetList) {
|
|
||||||
sortedSets << set.getPtr();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (sortedSets.empty()) {
|
|
||||||
sortedSets << CardSet::newInstance("", "", "", QDate());
|
|
||||||
}
|
|
||||||
std::sort(sortedSets.begin(), sortedSets.end(), SetDownloadPriorityComparator());
|
|
||||||
|
|
||||||
// If the user hasn't disabled arts other than their personal preference...
|
|
||||||
if (!SettingsCache::instance().getOverrideAllCardArtWithPersonalPreference()) {
|
|
||||||
// If the pixmapCacheKey corresponds to a specific set, we have to try to load it first.
|
|
||||||
for (const auto &cardInfoPerSetList : card->getSets()) {
|
|
||||||
for (const auto &set : cardInfoPerSetList) {
|
|
||||||
if (QLatin1String("card_") + card->getName() + QString("_") + QString(set.getProperty("uuid")) ==
|
|
||||||
card->getPixmapCacheKey()) {
|
|
||||||
long long setIndex = sortedSets.indexOf(set.getPtr());
|
|
||||||
CardSetPtr setForCardProviderID = sortedSets.takeAt(setIndex);
|
|
||||||
sortedSets.prepend(setForCardProviderID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// The first time called, nextSet will also populate the Urls for the first set.
|
|
||||||
nextSet();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PictureToLoad::populateSetUrls()
|
|
||||||
{
|
|
||||||
/* currentSetUrls is a list, populated each time a new set is requested for a particular card
|
|
||||||
and Urls are removed from it as a download is attempted from each one. Custom Urls for
|
|
||||||
a set are given higher priority, so should be placed first in the list. */
|
|
||||||
currentSetUrls.clear();
|
|
||||||
|
|
||||||
if (card && currentSet) {
|
|
||||||
QString setCustomURL = card->getCustomPicURL(currentSet->getShortName());
|
|
||||||
|
|
||||||
if (!setCustomURL.isEmpty()) {
|
|
||||||
currentSetUrls.append(setCustomURL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const QString &urlTemplate : urlTemplates) {
|
|
||||||
QString transformedUrl = transformUrl(urlTemplate);
|
|
||||||
|
|
||||||
if (!transformedUrl.isEmpty()) {
|
|
||||||
currentSetUrls.append(transformedUrl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Call nextUrl to make sure currentUrl is up-to-date
|
|
||||||
but we don't need the result here. */
|
|
||||||
(void)nextUrl();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PictureToLoad::nextSet()
|
|
||||||
{
|
|
||||||
if (!sortedSets.isEmpty()) {
|
|
||||||
currentSet = sortedSets.takeFirst();
|
|
||||||
populateSetUrls();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
currentSet = {};
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PictureToLoad::nextUrl()
|
|
||||||
{
|
|
||||||
if (!currentSetUrls.isEmpty()) {
|
|
||||||
currentUrl = currentSetUrls.takeFirst();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
currentUrl = QString();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString PictureToLoad::getSetName() const
|
|
||||||
{
|
|
||||||
if (currentSet) {
|
|
||||||
return currentSet->getCorrectedShortName();
|
|
||||||
} else {
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Card back returned by gatherer when card is not found
|
// Card back returned by gatherer when card is not found
|
||||||
QStringList PictureLoaderWorker::md5Blacklist = QStringList() << "db0c48db407a907c16ade38de048a441";
|
QStringList PictureLoaderWorker::md5Blacklist = QStringList() << "db0c48db407a907c16ade38de048a441";
|
||||||
|
|
@ -192,7 +83,7 @@ void PictureLoaderWorker::processLoadQueue()
|
||||||
QString cardName = cardBeingLoaded.getCard()->getName();
|
QString cardName = cardBeingLoaded.getCard()->getName();
|
||||||
QString correctedCardName = cardBeingLoaded.getCard()->getCorrectedName();
|
QString correctedCardName = cardBeingLoaded.getCard()->getCorrectedName();
|
||||||
|
|
||||||
qCDebug(PictureLoaderLog).nospace()
|
qCDebug(PictureLoaderWorkerLog).nospace()
|
||||||
<< "PictureLoader: [card: " << cardName << " set: " << setName << "]: Trying to load picture";
|
<< "PictureLoader: [card: " << cardName << " set: " << setName << "]: Trying to load picture";
|
||||||
|
|
||||||
if (CardDatabaseManager::getInstance()->isProviderIdForPreferredPrinting(
|
if (CardDatabaseManager::getInstance()->isProviderIdForPreferredPrinting(
|
||||||
|
|
@ -202,8 +93,8 @@ void PictureLoaderWorker::processLoadQueue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qCDebug(PictureLoaderLog).nospace() << "PictureLoader: [card: " << cardName << " set: " << setName
|
qCDebug(PictureLoaderWorkerLog).nospace() << "PictureLoader: [card: " << cardName << " set: " << setName
|
||||||
<< "]: No custom picture, trying to download";
|
<< "]: No custom picture, trying to download";
|
||||||
cardsToDownload.append(cardBeingLoaded);
|
cardsToDownload.append(cardBeingLoaded);
|
||||||
cardBeingLoaded.clear();
|
cardBeingLoaded.clear();
|
||||||
if (!downloadRunning) {
|
if (!downloadRunning) {
|
||||||
|
|
@ -245,22 +136,22 @@ bool PictureLoaderWorker::cardImageExistsOnDisk(QString &setName, QString &corre
|
||||||
for (const auto &_picsPath : picsPaths) {
|
for (const auto &_picsPath : picsPaths) {
|
||||||
imgReader.setFileName(_picsPath);
|
imgReader.setFileName(_picsPath);
|
||||||
if (imgReader.read(&image)) {
|
if (imgReader.read(&image)) {
|
||||||
qCDebug(PictureLoaderLog).nospace()
|
qCDebug(PictureLoaderWorkerLog).nospace()
|
||||||
<< "PictureLoader: [card: " << correctedCardname << " set: " << setName << "]: Picture found on disk.";
|
<< "PictureLoader: [card: " << correctedCardname << " set: " << setName << "]: Picture found on disk.";
|
||||||
imageLoaded(cardBeingLoaded.getCard(), image);
|
imageLoaded(cardBeingLoaded.getCard(), image);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
imgReader.setFileName(_picsPath + ".full");
|
imgReader.setFileName(_picsPath + ".full");
|
||||||
if (imgReader.read(&image)) {
|
if (imgReader.read(&image)) {
|
||||||
qCDebug(PictureLoaderLog).nospace() << "PictureLoader: [card: " << correctedCardname << " set: " << setName
|
qCDebug(PictureLoaderWorkerLog).nospace() << "PictureLoader: [card: " << correctedCardname
|
||||||
<< "]: Picture.full found on disk.";
|
<< " set: " << setName << "]: Picture.full found on disk.";
|
||||||
imageLoaded(cardBeingLoaded.getCard(), image);
|
imageLoaded(cardBeingLoaded.getCard(), image);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
imgReader.setFileName(_picsPath + ".xlhq");
|
imgReader.setFileName(_picsPath + ".xlhq");
|
||||||
if (imgReader.read(&image)) {
|
if (imgReader.read(&image)) {
|
||||||
qCDebug(PictureLoaderLog).nospace() << "PictureLoader: [card: " << correctedCardname << " set: " << setName
|
qCDebug(PictureLoaderWorkerLog).nospace() << "PictureLoader: [card: " << correctedCardname
|
||||||
<< "]: Picture.xlhq found on disk.";
|
<< " set: " << setName << "]: Picture.xlhq found on disk.";
|
||||||
imageLoaded(cardBeingLoaded.getCard(), image);
|
imageLoaded(cardBeingLoaded.getCard(), image);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -269,147 +160,6 @@ bool PictureLoaderWorker::cardImageExistsOnDisk(QString &setName, QString &corre
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse(const QString &urlTemplate,
|
|
||||||
const QString &propType,
|
|
||||||
const QString &cardName,
|
|
||||||
const QString &setName,
|
|
||||||
std::function<QString(const QString &)> getProperty,
|
|
||||||
QMap<QString, QString> &transformMap)
|
|
||||||
{
|
|
||||||
static const QRegularExpression rxFillWith("^(.+)_fill_with_(.+)$");
|
|
||||||
static const QRegularExpression rxSubStr("^(.+)_substr_(\\d+)_(\\d+)$");
|
|
||||||
|
|
||||||
const QRegularExpression rxCardProp("!" + propType + ":([^!]+)!");
|
|
||||||
|
|
||||||
auto matches = rxCardProp.globalMatch(urlTemplate);
|
|
||||||
while (matches.hasNext()) {
|
|
||||||
auto match = matches.next();
|
|
||||||
QString templatePropertyName = match.captured(1);
|
|
||||||
auto fillMatch = rxFillWith.match(templatePropertyName);
|
|
||||||
QString cardPropertyName;
|
|
||||||
QString fillWith;
|
|
||||||
int subStrPos = 0;
|
|
||||||
int subStrLen = -1;
|
|
||||||
if (fillMatch.hasMatch()) {
|
|
||||||
cardPropertyName = fillMatch.captured(1);
|
|
||||||
fillWith = fillMatch.captured(2);
|
|
||||||
} else {
|
|
||||||
fillWith = QString();
|
|
||||||
auto subStrMatch = rxSubStr.match(templatePropertyName);
|
|
||||||
if (subStrMatch.hasMatch()) {
|
|
||||||
cardPropertyName = subStrMatch.captured(1);
|
|
||||||
subStrPos = subStrMatch.captured(2).toInt();
|
|
||||||
subStrLen = subStrMatch.captured(3).toInt();
|
|
||||||
} else {
|
|
||||||
cardPropertyName = templatePropertyName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QString propertyValue = getProperty(cardPropertyName);
|
|
||||||
if (propertyValue.isEmpty()) {
|
|
||||||
qCDebug(PictureLoaderLog).nospace()
|
|
||||||
<< "PictureLoader: [card: " << cardName << " set: " << setName << "]: Requested " << propType
|
|
||||||
<< "property (" << cardPropertyName << ") for Url template (" << urlTemplate << ") is not available";
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
int propLength = propertyValue.length();
|
|
||||||
if (subStrLen > 0) {
|
|
||||||
if (subStrPos + subStrLen > propLength) {
|
|
||||||
qCDebug(PictureLoaderLog).nospace()
|
|
||||||
<< "PictureLoader: [card: " << cardName << " set: " << setName << "]: Requested " << propType
|
|
||||||
<< " property (" << cardPropertyName << ") for Url template (" << urlTemplate
|
|
||||||
<< ") is smaller than substr specification (" << subStrPos << " + " << subStrLen << " > "
|
|
||||||
<< propLength << ")";
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
propertyValue = propertyValue.mid(subStrPos, subStrLen);
|
|
||||||
propLength = subStrLen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fillWith.isEmpty()) {
|
|
||||||
int fillLength = fillWith.length();
|
|
||||||
if (fillLength < propLength) {
|
|
||||||
qCDebug(PictureLoaderLog).nospace()
|
|
||||||
<< "PictureLoader: [card: " << cardName << " set: " << setName << "]: Requested " << propType
|
|
||||||
<< " property (" << cardPropertyName << ") for Url template (" << urlTemplate
|
|
||||||
<< ") is longer than fill specification (" << fillWith << ")";
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
|
|
||||||
propertyValue = fillWith.left(fillLength - propLength) + propertyValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
transformMap["!" + propType + ":" + templatePropertyName + "!"] = propertyValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString PictureToLoad::transformUrl(const QString &urlTemplate) const
|
|
||||||
{
|
|
||||||
/* This function takes Url templates and substitutes actual card details
|
|
||||||
into the url. This is used for making Urls with follow a predictable format
|
|
||||||
for downloading images. If information is requested by the template that is
|
|
||||||
not populated for this specific card/set combination, an empty string is returned.*/
|
|
||||||
|
|
||||||
CardSetPtr set = getCurrentSet();
|
|
||||||
|
|
||||||
QMap<QString, QString> transformMap = QMap<QString, QString>();
|
|
||||||
QString setName = getSetName();
|
|
||||||
|
|
||||||
// name
|
|
||||||
QString cardName = card->getName();
|
|
||||||
transformMap["!name!"] = cardName;
|
|
||||||
transformMap["!name_lower!"] = card->getName().toLower();
|
|
||||||
transformMap["!corrected_name!"] = card->getCorrectedName();
|
|
||||||
transformMap["!corrected_name_lower!"] = card->getCorrectedName().toLower();
|
|
||||||
|
|
||||||
// card properties
|
|
||||||
if (parse(
|
|
||||||
urlTemplate, "prop", cardName, setName, [&](const QString &name) { return card->getProperty(name); },
|
|
||||||
transformMap)) {
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (set) {
|
|
||||||
transformMap["!setcode!"] = set->getShortName();
|
|
||||||
transformMap["!setcode_lower!"] = set->getShortName().toLower();
|
|
||||||
transformMap["!setname!"] = set->getLongName();
|
|
||||||
transformMap["!setname_lower!"] = set->getLongName().toLower();
|
|
||||||
|
|
||||||
if (parse(
|
|
||||||
urlTemplate, "set", cardName, setName,
|
|
||||||
[&](const QString &name) { return card->getSetProperty(set->getShortName(), name); }, transformMap)) {
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// language setting
|
|
||||||
transformMap["!sflang!"] = QString(QCoreApplication::translate(
|
|
||||||
"PictureLoader", "en", "code for scryfall's language property, not available for all languages"));
|
|
||||||
|
|
||||||
QString transformedUrl = urlTemplate;
|
|
||||||
for (const QString &prop : transformMap.keys()) {
|
|
||||||
if (transformedUrl.contains(prop)) {
|
|
||||||
if (!transformMap[prop].isEmpty()) {
|
|
||||||
transformedUrl.replace(prop, QUrl::toPercentEncoding(transformMap[prop]));
|
|
||||||
} else {
|
|
||||||
/* This means the template is requesting information that is not
|
|
||||||
* populated in this card, so it should return an empty string,
|
|
||||||
* indicating an invalid Url.
|
|
||||||
*/
|
|
||||||
qCDebug(PictureLoaderLog).nospace()
|
|
||||||
<< "PictureLoader: [card: " << cardName << " set: " << setName << "]: Requested information ("
|
|
||||||
<< prop << ") for Url template (" << urlTemplate << ") is not available";
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return transformedUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PictureLoaderWorker::startNextPicDownload()
|
void PictureLoaderWorker::startNextPicDownload()
|
||||||
{
|
{
|
||||||
if (cardsToDownload.isEmpty()) {
|
if (cardsToDownload.isEmpty()) {
|
||||||
|
|
@ -429,7 +179,7 @@ void PictureLoaderWorker::startNextPicDownload()
|
||||||
picDownloadFailed();
|
picDownloadFailed();
|
||||||
} else {
|
} else {
|
||||||
QUrl url(picUrl);
|
QUrl url(picUrl);
|
||||||
qCDebug(PictureLoaderLog).nospace()
|
qCDebug(PictureLoaderWorkerLog).nospace()
|
||||||
<< "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getCorrectedName()
|
<< "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getCorrectedName()
|
||||||
<< " set: " << cardBeingDownloaded.getSetName() << "]: Trying to fetch picture from url "
|
<< " set: " << cardBeingDownloaded.getSetName() << "]: Trying to fetch picture from url "
|
||||||
<< url.toDisplayString();
|
<< url.toDisplayString();
|
||||||
|
|
@ -448,7 +198,7 @@ void PictureLoaderWorker::picDownloadFailed()
|
||||||
loadQueue.prepend(cardBeingDownloaded);
|
loadQueue.prepend(cardBeingDownloaded);
|
||||||
mutex.unlock();
|
mutex.unlock();
|
||||||
} else {
|
} else {
|
||||||
qCDebug(PictureLoaderLog).nospace()
|
qCDebug(PictureLoaderWorkerLog).nospace()
|
||||||
<< "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getCorrectedName()
|
<< "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getCorrectedName()
|
||||||
<< " set: " << cardBeingDownloaded.getSetName() << "]: Picture NOT found, "
|
<< " set: " << cardBeingDownloaded.getSetName() << "]: Picture NOT found, "
|
||||||
<< (picDownload ? "download failed" : "downloads disabled")
|
<< (picDownload ? "download failed" : "downloads disabled")
|
||||||
|
|
@ -470,7 +220,7 @@ QNetworkReply *PictureLoaderWorker::makeRequest(const QUrl &url)
|
||||||
// Check if the redirect is cached
|
// Check if the redirect is cached
|
||||||
QUrl cachedRedirect = getCachedRedirect(url);
|
QUrl cachedRedirect = getCachedRedirect(url);
|
||||||
if (!cachedRedirect.isEmpty()) {
|
if (!cachedRedirect.isEmpty()) {
|
||||||
qCDebug(PictureLoaderLog).nospace()
|
qCDebug(PictureLoaderWorkerLog).nospace()
|
||||||
<< "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getCorrectedName()
|
<< "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getCorrectedName()
|
||||||
<< " set: " << cardBeingDownloaded.getSetName() << "]: Using cached redirect for " << url.toDisplayString()
|
<< " set: " << cardBeingDownloaded.getSetName() << "]: Using cached redirect for " << url.toDisplayString()
|
||||||
<< " to " << cachedRedirect.toDisplayString();
|
<< " to " << cachedRedirect.toDisplayString();
|
||||||
|
|
@ -495,7 +245,7 @@ QNetworkReply *PictureLoaderWorker::makeRequest(const QUrl &url)
|
||||||
}
|
}
|
||||||
|
|
||||||
cacheRedirect(url, redirectUrl);
|
cacheRedirect(url, redirectUrl);
|
||||||
qCDebug(PictureLoaderLog).nospace()
|
qCDebug(PictureLoaderWorkerLog).nospace()
|
||||||
<< "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getCorrectedName()
|
<< "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getCorrectedName()
|
||||||
<< " set: " << cardBeingDownloaded.getSetName() << "]: Caching redirect from " << url.toDisplayString()
|
<< " set: " << cardBeingDownloaded.getSetName() << "]: Caching redirect from " << url.toDisplayString()
|
||||||
<< " to " << redirectUrl.toDisplayString();
|
<< " to " << redirectUrl.toDisplayString();
|
||||||
|
|
@ -575,7 +325,7 @@ void PictureLoaderWorker::picDownloadFinished(QNetworkReply *reply)
|
||||||
|
|
||||||
if (reply->error()) {
|
if (reply->error()) {
|
||||||
if (isFromCache) {
|
if (isFromCache) {
|
||||||
qCDebug(PictureLoaderLog).nospace()
|
qCDebug(PictureLoaderWorkerLog).nospace()
|
||||||
<< "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getName()
|
<< "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getName()
|
||||||
<< " set: " << cardBeingDownloaded.getSetName() << "]: Removing corrupted cache file for url "
|
<< " set: " << cardBeingDownloaded.getSetName() << "]: Removing corrupted cache file for url "
|
||||||
<< reply->url().toDisplayString() << " and retrying (" << reply->errorString() << ")";
|
<< reply->url().toDisplayString() << " and retrying (" << reply->errorString() << ")";
|
||||||
|
|
@ -584,7 +334,7 @@ void PictureLoaderWorker::picDownloadFinished(QNetworkReply *reply)
|
||||||
|
|
||||||
makeRequest(reply->url());
|
makeRequest(reply->url());
|
||||||
} else {
|
} else {
|
||||||
qCDebug(PictureLoaderLog).nospace()
|
qCDebug(PictureLoaderWorkerLog).nospace()
|
||||||
<< "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getName()
|
<< "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getName()
|
||||||
<< " set: " << cardBeingDownloaded.getSetName() << "]: " << (picDownload ? "Download" : "Cache search")
|
<< " set: " << cardBeingDownloaded.getSetName() << "]: " << (picDownload ? "Download" : "Cache search")
|
||||||
<< " failed for url " << reply->url().toDisplayString() << " (" << reply->errorString() << ")";
|
<< " failed for url " << reply->url().toDisplayString() << " (" << reply->errorString() << ")";
|
||||||
|
|
@ -602,7 +352,7 @@ void PictureLoaderWorker::picDownloadFinished(QNetworkReply *reply)
|
||||||
if (statusCode == 301 || statusCode == 302 || statusCode == 303 || statusCode == 305 || statusCode == 307 ||
|
if (statusCode == 301 || statusCode == 302 || statusCode == 303 || statusCode == 305 || statusCode == 307 ||
|
||||||
statusCode == 308) {
|
statusCode == 308) {
|
||||||
QUrl redirectUrl = reply->header(QNetworkRequest::LocationHeader).toUrl();
|
QUrl redirectUrl = reply->header(QNetworkRequest::LocationHeader).toUrl();
|
||||||
qCDebug(PictureLoaderLog).nospace()
|
qCDebug(PictureLoaderWorkerLog).nospace()
|
||||||
<< "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getName()
|
<< "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getName()
|
||||||
<< " set: " << cardBeingDownloaded.getSetName() << "]: following "
|
<< " set: " << cardBeingDownloaded.getSetName() << "]: following "
|
||||||
<< (isFromCache ? "cached redirect" : "redirect") << " to " << redirectUrl.toDisplayString();
|
<< (isFromCache ? "cached redirect" : "redirect") << " to " << redirectUrl.toDisplayString();
|
||||||
|
|
@ -615,9 +365,10 @@ void PictureLoaderWorker::picDownloadFinished(QNetworkReply *reply)
|
||||||
const QByteArray &picData = reply->peek(reply->size());
|
const QByteArray &picData = reply->peek(reply->size());
|
||||||
|
|
||||||
if (imageIsBlackListed(picData)) {
|
if (imageIsBlackListed(picData)) {
|
||||||
qCDebug(PictureLoaderLog).nospace() << "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getName()
|
qCDebug(PictureLoaderWorkerLog).nospace()
|
||||||
<< " set: " << cardBeingDownloaded.getSetName()
|
<< "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getName()
|
||||||
<< "]: Picture found, but blacklisted, will consider it as not found";
|
<< " set: " << cardBeingDownloaded.getSetName()
|
||||||
|
<< "]: Picture found, but blacklisted, will consider it as not found";
|
||||||
|
|
||||||
picDownloadFailed();
|
picDownloadFailed();
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
|
|
@ -650,7 +401,7 @@ void PictureLoaderWorker::picDownloadFinished(QNetworkReply *reply)
|
||||||
imageLoaded(cardBeingDownloaded.getCard(), testImage);
|
imageLoaded(cardBeingDownloaded.getCard(), testImage);
|
||||||
logSuccessMessage = true;
|
logSuccessMessage = true;
|
||||||
} else {
|
} else {
|
||||||
qCDebug(PictureLoaderLog).nospace()
|
qCDebug(PictureLoaderWorkerLog).nospace()
|
||||||
<< "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getName()
|
<< "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getName()
|
||||||
<< " set: " << cardBeingDownloaded.getSetName() << "]: Possible " << (isFromCache ? "cached" : "downloaded")
|
<< " set: " << cardBeingDownloaded.getSetName() << "]: Possible " << (isFromCache ? "cached" : "downloaded")
|
||||||
<< " picture at " << reply->url().toDisplayString() << " could not be loaded: " << reply->errorString();
|
<< " picture at " << reply->url().toDisplayString() << " could not be loaded: " << reply->errorString();
|
||||||
|
|
@ -659,7 +410,7 @@ void PictureLoaderWorker::picDownloadFinished(QNetworkReply *reply)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (logSuccessMessage) {
|
if (logSuccessMessage) {
|
||||||
qCDebug(PictureLoaderLog).nospace()
|
qCDebug(PictureLoaderWorkerLog).nospace()
|
||||||
<< "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getName()
|
<< "PictureLoader: [card: " << cardBeingDownloaded.getCard()->getName()
|
||||||
<< " set: " << cardBeingDownloaded.getSetName() << "]: Image successfully "
|
<< " set: " << cardBeingDownloaded.getSetName() << "]: Image successfully "
|
||||||
<< (isFromCache ? "loaded from cached" : "downloaded from") << " url " << reply->url().toDisplayString();
|
<< (isFromCache ? "loaded from cached" : "downloaded from") << " url " << reply->url().toDisplayString();
|
||||||
|
|
@ -708,138 +459,4 @@ void PictureLoaderWorker::picsPathChanged()
|
||||||
void PictureLoaderWorker::clearNetworkCache()
|
void PictureLoaderWorker::clearNetworkCache()
|
||||||
{
|
{
|
||||||
networkManager->cache()->clear();
|
networkManager->cache()->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
PictureLoader::PictureLoader() : QObject(nullptr)
|
|
||||||
{
|
|
||||||
worker = new PictureLoaderWorker;
|
|
||||||
connect(&SettingsCache::instance(), SIGNAL(picsPathChanged()), this, SLOT(picsPathChanged()));
|
|
||||||
connect(&SettingsCache::instance(), SIGNAL(picDownloadChanged()), this, SLOT(picDownloadChanged()));
|
|
||||||
|
|
||||||
connect(worker, SIGNAL(imageLoaded(CardInfoPtr, const QImage &)), this,
|
|
||||||
SLOT(imageLoaded(CardInfoPtr, const QImage &)));
|
|
||||||
}
|
|
||||||
|
|
||||||
PictureLoader::~PictureLoader()
|
|
||||||
{
|
|
||||||
worker->deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PictureLoader::getCardBackPixmap(QPixmap &pixmap, QSize size)
|
|
||||||
{
|
|
||||||
QString backCacheKey = "_trice_card_back_" + QString::number(size.width()) + QString::number(size.height());
|
|
||||||
if (!QPixmapCache::find(backCacheKey, &pixmap)) {
|
|
||||||
qCDebug(PictureLoaderLog) << "PictureLoader: cache fail for" << backCacheKey;
|
|
||||||
pixmap = QPixmap("theme:cardback").scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
|
||||||
QPixmapCache::insert(backCacheKey, pixmap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PictureLoader::getCardBackLoadingInProgressPixmap(QPixmap &pixmap, QSize size)
|
|
||||||
{
|
|
||||||
QString backCacheKey = "_trice_card_back_" + QString::number(size.width()) + QString::number(size.height());
|
|
||||||
if (!QPixmapCache::find(backCacheKey, &pixmap)) {
|
|
||||||
qCDebug(PictureLoaderLog) << "PictureLoader: cache fail for" << backCacheKey;
|
|
||||||
pixmap = QPixmap("theme:cardback").scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
|
||||||
QPixmapCache::insert(backCacheKey, pixmap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PictureLoader::getCardBackLoadingFailedPixmap(QPixmap &pixmap, QSize size)
|
|
||||||
{
|
|
||||||
QString backCacheKey = "_trice_card_back_" + QString::number(size.width()) + QString::number(size.height());
|
|
||||||
if (!QPixmapCache::find(backCacheKey, &pixmap)) {
|
|
||||||
qCDebug(PictureLoaderLog) << "PictureLoader: cache fail for" << backCacheKey;
|
|
||||||
pixmap = QPixmap("theme:cardback").scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
|
||||||
QPixmapCache::insert(backCacheKey, pixmap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PictureLoader::getPixmap(QPixmap &pixmap, CardInfoPtr card, QSize size)
|
|
||||||
{
|
|
||||||
if (card == nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// search for an exact size copy of the picture in cache
|
|
||||||
QString key = card->getPixmapCacheKey();
|
|
||||||
QString sizeKey = key + QLatin1Char('_') + QString::number(size.width()) + QString::number(size.height());
|
|
||||||
if (QPixmapCache::find(sizeKey, &pixmap))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// load the image and create a copy of the correct size
|
|
||||||
QPixmap bigPixmap;
|
|
||||||
if (QPixmapCache::find(key, &bigPixmap)) {
|
|
||||||
QScreen *screen = qApp->primaryScreen();
|
|
||||||
qreal dpr = screen->devicePixelRatio();
|
|
||||||
pixmap = bigPixmap.scaled(size * dpr, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
|
||||||
pixmap.setDevicePixelRatio(dpr);
|
|
||||||
QPixmapCache::insert(sizeKey, pixmap);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// add the card to the load queue
|
|
||||||
getInstance().worker->enqueueImageLoad(card);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PictureLoader::imageLoaded(CardInfoPtr card, const QImage &image)
|
|
||||||
{
|
|
||||||
if (image.isNull()) {
|
|
||||||
QPixmapCache::insert(card->getPixmapCacheKey(), QPixmap());
|
|
||||||
} else {
|
|
||||||
if (card->getUpsideDownArt()) {
|
|
||||||
QImage mirrorImage = image.mirrored(true, true);
|
|
||||||
QPixmapCache::insert(card->getPixmapCacheKey(), QPixmap::fromImage(mirrorImage));
|
|
||||||
} else {
|
|
||||||
QPixmapCache::insert(card->getPixmapCacheKey(), QPixmap::fromImage(image));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
card->emitPixmapUpdated();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PictureLoader::clearPixmapCache(CardInfoPtr card)
|
|
||||||
{
|
|
||||||
if (card) {
|
|
||||||
QPixmapCache::remove(card->getPixmapCacheKey());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PictureLoader::clearPixmapCache()
|
|
||||||
{
|
|
||||||
QPixmapCache::clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PictureLoader::clearNetworkCache()
|
|
||||||
{
|
|
||||||
getInstance().worker->clearNetworkCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PictureLoader::cacheCardPixmaps(QList<CardInfoPtr> cards)
|
|
||||||
{
|
|
||||||
QPixmap tmp;
|
|
||||||
int max = qMin(cards.size(), CACHED_CARD_PER_DECK_MAX);
|
|
||||||
for (int i = 0; i < max; ++i) {
|
|
||||||
const CardInfoPtr &card = cards.at(i);
|
|
||||||
if (!card) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString key = card->getPixmapCacheKey();
|
|
||||||
if (QPixmapCache::find(key, &tmp)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
getInstance().worker->enqueueImageLoad(card);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PictureLoader::picDownloadChanged()
|
|
||||||
{
|
|
||||||
QPixmapCache::clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PictureLoader::picsPathChanged()
|
|
||||||
{
|
|
||||||
QPixmapCache::clear();
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
#ifndef PICTURE_LOADER_WORKER_H
|
||||||
|
#define PICTURE_LOADER_WORKER_H
|
||||||
|
|
||||||
|
#include "../../../game/cards/card_database.h"
|
||||||
|
#include "picture_to_load.h"
|
||||||
|
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#define REDIRECT_HEADER_NAME "redirects"
|
||||||
|
#define REDIRECT_ORIGINAL_URL "original"
|
||||||
|
#define REDIRECT_URL "redirect"
|
||||||
|
#define REDIRECT_TIMESTAMP "timestamp"
|
||||||
|
#define REDIRECT_CACHE_FILENAME "cache.ini"
|
||||||
|
|
||||||
|
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;
|
||||||
|
QHash<QUrl, QPair<QUrl, QDateTime>> redirectCache; // Stores redirect and timestamp
|
||||||
|
QString cacheFilePath; // Path to persistent storage
|
||||||
|
static constexpr int CacheTTLInDays = 30; // TODO: Make user configurable
|
||||||
|
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);
|
||||||
|
void cacheRedirect(const QUrl &originalUrl, const QUrl &redirectUrl);
|
||||||
|
QUrl getCachedRedirect(const QUrl &originalUrl) const;
|
||||||
|
void loadRedirectCache();
|
||||||
|
void saveRedirectCache() const;
|
||||||
|
void cleanStaleEntries();
|
||||||
|
|
||||||
|
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);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // PICTURE_LOADER_WORKER_H
|
||||||
244
cockatrice/src/client/ui/picture_loader/picture_to_load.cpp
Normal file
244
cockatrice/src/client/ui/picture_loader/picture_to_load.cpp
Normal file
|
|
@ -0,0 +1,244 @@
|
||||||
|
#include "picture_to_load.h"
|
||||||
|
|
||||||
|
#include "../../../settings/cache_settings.h"
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QDate>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <qloggingcategory.h>
|
||||||
|
|
||||||
|
Q_LOGGING_CATEGORY(PictureToLoadLog, "picture_loader.picture_to_load")
|
||||||
|
|
||||||
|
PictureToLoad::PictureToLoad(CardInfoPtr _card)
|
||||||
|
: card(std::move(_card)), urlTemplates(SettingsCache::instance().downloads().getAllURLs())
|
||||||
|
{
|
||||||
|
if (card) {
|
||||||
|
for (const auto &cardInfoPerSetList : card->getSets()) {
|
||||||
|
for (const auto &set : cardInfoPerSetList) {
|
||||||
|
sortedSets << set.getPtr();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sortedSets.empty()) {
|
||||||
|
sortedSets << CardSet::newInstance("", "", "", QDate());
|
||||||
|
}
|
||||||
|
std::sort(sortedSets.begin(), sortedSets.end(), SetDownloadPriorityComparator());
|
||||||
|
|
||||||
|
// If the user hasn't disabled arts other than their personal preference...
|
||||||
|
if (!SettingsCache::instance().getOverrideAllCardArtWithPersonalPreference()) {
|
||||||
|
// If the pixmapCacheKey corresponds to a specific set, we have to try to load it first.
|
||||||
|
for (const auto &cardInfoPerSetList : card->getSets()) {
|
||||||
|
for (const auto &set : cardInfoPerSetList) {
|
||||||
|
if (QLatin1String("card_") + card->getName() + QString("_") + QString(set.getProperty("uuid")) ==
|
||||||
|
card->getPixmapCacheKey()) {
|
||||||
|
long long setIndex = sortedSets.indexOf(set.getPtr());
|
||||||
|
CardSetPtr setForCardProviderID = sortedSets.takeAt(setIndex);
|
||||||
|
sortedSets.prepend(setForCardProviderID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// The first time called, nextSet will also populate the Urls for the first set.
|
||||||
|
nextSet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PictureToLoad::populateSetUrls()
|
||||||
|
{
|
||||||
|
/* currentSetUrls is a list, populated each time a new set is requested for a particular card
|
||||||
|
and Urls are removed from it as a download is attempted from each one. Custom Urls for
|
||||||
|
a set are given higher priority, so should be placed first in the list. */
|
||||||
|
currentSetUrls.clear();
|
||||||
|
|
||||||
|
if (card && currentSet) {
|
||||||
|
QString setCustomURL = card->getCustomPicURL(currentSet->getShortName());
|
||||||
|
|
||||||
|
if (!setCustomURL.isEmpty()) {
|
||||||
|
currentSetUrls.append(setCustomURL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const QString &urlTemplate : urlTemplates) {
|
||||||
|
QString transformedUrl = transformUrl(urlTemplate);
|
||||||
|
|
||||||
|
if (!transformedUrl.isEmpty()) {
|
||||||
|
currentSetUrls.append(transformedUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Call nextUrl to make sure currentUrl is up-to-date
|
||||||
|
but we don't need the result here. */
|
||||||
|
(void)nextUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PictureToLoad::nextSet()
|
||||||
|
{
|
||||||
|
if (!sortedSets.isEmpty()) {
|
||||||
|
currentSet = sortedSets.takeFirst();
|
||||||
|
populateSetUrls();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
currentSet = {};
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PictureToLoad::nextUrl()
|
||||||
|
{
|
||||||
|
if (!currentSetUrls.isEmpty()) {
|
||||||
|
currentUrl = currentSetUrls.takeFirst();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
currentUrl = QString();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PictureToLoad::getSetName() const
|
||||||
|
{
|
||||||
|
if (currentSet) {
|
||||||
|
return currentSet->getCorrectedShortName();
|
||||||
|
} else {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse(const QString &urlTemplate,
|
||||||
|
const QString &propType,
|
||||||
|
const QString &cardName,
|
||||||
|
const QString &setName,
|
||||||
|
std::function<QString(const QString &)> getProperty,
|
||||||
|
QMap<QString, QString> &transformMap)
|
||||||
|
{
|
||||||
|
static const QRegularExpression rxFillWith("^(.+)_fill_with_(.+)$");
|
||||||
|
static const QRegularExpression rxSubStr("^(.+)_substr_(\\d+)_(\\d+)$");
|
||||||
|
|
||||||
|
const QRegularExpression rxCardProp("!" + propType + ":([^!]+)!");
|
||||||
|
|
||||||
|
auto matches = rxCardProp.globalMatch(urlTemplate);
|
||||||
|
while (matches.hasNext()) {
|
||||||
|
auto match = matches.next();
|
||||||
|
QString templatePropertyName = match.captured(1);
|
||||||
|
auto fillMatch = rxFillWith.match(templatePropertyName);
|
||||||
|
QString cardPropertyName;
|
||||||
|
QString fillWith;
|
||||||
|
int subStrPos = 0;
|
||||||
|
int subStrLen = -1;
|
||||||
|
if (fillMatch.hasMatch()) {
|
||||||
|
cardPropertyName = fillMatch.captured(1);
|
||||||
|
fillWith = fillMatch.captured(2);
|
||||||
|
} else {
|
||||||
|
fillWith = QString();
|
||||||
|
auto subStrMatch = rxSubStr.match(templatePropertyName);
|
||||||
|
if (subStrMatch.hasMatch()) {
|
||||||
|
cardPropertyName = subStrMatch.captured(1);
|
||||||
|
subStrPos = subStrMatch.captured(2).toInt();
|
||||||
|
subStrLen = subStrMatch.captured(3).toInt();
|
||||||
|
} else {
|
||||||
|
cardPropertyName = templatePropertyName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QString propertyValue = getProperty(cardPropertyName);
|
||||||
|
if (propertyValue.isEmpty()) {
|
||||||
|
qCDebug(PictureToLoadLog).nospace()
|
||||||
|
<< "PictureLoader: [card: " << cardName << " set: " << setName << "]: Requested " << propType
|
||||||
|
<< "property (" << cardPropertyName << ") for Url template (" << urlTemplate << ") is not available";
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
int propLength = propertyValue.length();
|
||||||
|
if (subStrLen > 0) {
|
||||||
|
if (subStrPos + subStrLen > propLength) {
|
||||||
|
qCDebug(PictureToLoadLog).nospace()
|
||||||
|
<< "PictureLoader: [card: " << cardName << " set: " << setName << "]: Requested " << propType
|
||||||
|
<< " property (" << cardPropertyName << ") for Url template (" << urlTemplate
|
||||||
|
<< ") is smaller than substr specification (" << subStrPos << " + " << subStrLen << " > "
|
||||||
|
<< propLength << ")";
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
propertyValue = propertyValue.mid(subStrPos, subStrLen);
|
||||||
|
propLength = subStrLen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fillWith.isEmpty()) {
|
||||||
|
int fillLength = fillWith.length();
|
||||||
|
if (fillLength < propLength) {
|
||||||
|
qCDebug(PictureToLoadLog).nospace()
|
||||||
|
<< "PictureLoader: [card: " << cardName << " set: " << setName << "]: Requested " << propType
|
||||||
|
<< " property (" << cardPropertyName << ") for Url template (" << urlTemplate
|
||||||
|
<< ") is longer than fill specification (" << fillWith << ")";
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
propertyValue = fillWith.left(fillLength - propLength) + propertyValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
transformMap["!" + propType + ":" + templatePropertyName + "!"] = propertyValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PictureToLoad::transformUrl(const QString &urlTemplate) const
|
||||||
|
{
|
||||||
|
/* This function takes Url templates and substitutes actual card details
|
||||||
|
into the url. This is used for making Urls with follow a predictable format
|
||||||
|
for downloading images. If information is requested by the template that is
|
||||||
|
not populated for this specific card/set combination, an empty string is returned.*/
|
||||||
|
|
||||||
|
CardSetPtr set = getCurrentSet();
|
||||||
|
|
||||||
|
QMap<QString, QString> transformMap = QMap<QString, QString>();
|
||||||
|
QString setName = getSetName();
|
||||||
|
|
||||||
|
// name
|
||||||
|
QString cardName = card->getName();
|
||||||
|
transformMap["!name!"] = cardName;
|
||||||
|
transformMap["!name_lower!"] = card->getName().toLower();
|
||||||
|
transformMap["!corrected_name!"] = card->getCorrectedName();
|
||||||
|
transformMap["!corrected_name_lower!"] = card->getCorrectedName().toLower();
|
||||||
|
|
||||||
|
// card properties
|
||||||
|
if (parse(
|
||||||
|
urlTemplate, "prop", cardName, setName, [&](const QString &name) { return card->getProperty(name); },
|
||||||
|
transformMap)) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (set) {
|
||||||
|
transformMap["!setcode!"] = set->getShortName();
|
||||||
|
transformMap["!setcode_lower!"] = set->getShortName().toLower();
|
||||||
|
transformMap["!setname!"] = set->getLongName();
|
||||||
|
transformMap["!setname_lower!"] = set->getLongName().toLower();
|
||||||
|
|
||||||
|
if (parse(
|
||||||
|
urlTemplate, "set", cardName, setName,
|
||||||
|
[&](const QString &name) { return card->getSetProperty(set->getShortName(), name); }, transformMap)) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// language setting
|
||||||
|
transformMap["!sflang!"] = QString(QCoreApplication::translate(
|
||||||
|
"PictureLoader", "en", "code for scryfall's language property, not available for all languages"));
|
||||||
|
|
||||||
|
QString transformedUrl = urlTemplate;
|
||||||
|
for (const QString &prop : transformMap.keys()) {
|
||||||
|
if (transformedUrl.contains(prop)) {
|
||||||
|
if (!transformMap[prop].isEmpty()) {
|
||||||
|
transformedUrl.replace(prop, QUrl::toPercentEncoding(transformMap[prop]));
|
||||||
|
} else {
|
||||||
|
/* This means the template is requesting information that is not
|
||||||
|
* populated in this card, so it should return an empty string,
|
||||||
|
* indicating an invalid Url.
|
||||||
|
*/
|
||||||
|
qCDebug(PictureToLoadLog).nospace()
|
||||||
|
<< "PictureLoader: [card: " << cardName << " set: " << setName << "]: Requested information ("
|
||||||
|
<< prop << ") for Url template (" << urlTemplate << ") is not available";
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return transformedUrl;
|
||||||
|
}
|
||||||
60
cockatrice/src/client/ui/picture_loader/picture_to_load.h
Normal file
60
cockatrice/src/client/ui/picture_loader/picture_to_load.h
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
#ifndef PICTURE_TO_LOAD_H
|
||||||
|
#define PICTURE_TO_LOAD_H
|
||||||
|
|
||||||
|
#include "../../../game/cards/card_database.h"
|
||||||
|
|
||||||
|
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();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // PICTURE_TO_LOAD_H
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#include "card_info_picture_enlarged_widget.h"
|
#include "card_info_picture_enlarged_widget.h"
|
||||||
|
|
||||||
#include "../../picture_loader.h"
|
#include "../../picture_loader/picture_loader.h"
|
||||||
|
|
||||||
#include <QPainterPath>
|
#include <QPainterPath>
|
||||||
#include <QStylePainter>
|
#include <QStylePainter>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
#include "../../../../game/cards/card_database_manager.h"
|
#include "../../../../game/cards/card_database_manager.h"
|
||||||
#include "../../../../game/cards/card_item.h"
|
#include "../../../../game/cards/card_item.h"
|
||||||
#include "../../../../settings/cache_settings.h"
|
#include "../../../../settings/cache_settings.h"
|
||||||
#include "../../picture_loader.h"
|
#include "../../picture_loader/picture_loader.h"
|
||||||
|
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QMouseEvent>
|
#include <QMouseEvent>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#include "dlg_manage_sets.h"
|
#include "dlg_manage_sets.h"
|
||||||
|
|
||||||
#include "../client/network/sets_model.h"
|
#include "../client/network/sets_model.h"
|
||||||
#include "../client/ui/picture_loader.h"
|
#include "../client/ui/picture_loader/picture_loader.h"
|
||||||
#include "../deck/custom_line_edit.h"
|
#include "../deck/custom_line_edit.h"
|
||||||
#include "../game/cards/card_database_manager.h"
|
#include "../game/cards/card_database_manager.h"
|
||||||
#include "../main.h"
|
#include "../main.h"
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
#include "../client/network/release_channel.h"
|
#include "../client/network/release_channel.h"
|
||||||
#include "../client/network/spoiler_background_updater.h"
|
#include "../client/network/spoiler_background_updater.h"
|
||||||
#include "../client/sound_engine.h"
|
#include "../client/sound_engine.h"
|
||||||
#include "../client/ui/picture_loader.h"
|
#include "../client/ui/picture_loader/picture_loader.h"
|
||||||
#include "../client/ui/theme_manager.h"
|
#include "../client/ui/theme_manager.h"
|
||||||
#include "../deck/custom_line_edit.h"
|
#include "../deck/custom_line_edit.h"
|
||||||
#include "../game/cards/card_database.h"
|
#include "../game/cards/card_database.h"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#include "abstract_card_item.h"
|
#include "abstract_card_item.h"
|
||||||
|
|
||||||
#include "../../client/ui/picture_loader.h"
|
#include "../../client/ui/picture_loader/picture_loader.h"
|
||||||
#include "../../settings/cache_settings.h"
|
#include "../../settings/cache_settings.h"
|
||||||
#include "../game_scene.h"
|
#include "../game_scene.h"
|
||||||
#include "card_database.h"
|
#include "card_database.h"
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#include "card_database.h"
|
#include "card_database.h"
|
||||||
|
|
||||||
#include "../../client/network/spoiler_background_updater.h"
|
#include "../../client/network/spoiler_background_updater.h"
|
||||||
#include "../../client/ui/picture_loader.h"
|
#include "../../client/ui/picture_loader/picture_loader.h"
|
||||||
#include "../../settings/cache_settings.h"
|
#include "../../settings/cache_settings.h"
|
||||||
#include "../../utility/card_set_comparator.h"
|
#include "../../utility/card_set_comparator.h"
|
||||||
#include "../game_specific_terms.h"
|
#include "../game_specific_terms.h"
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,9 @@ set(oracle_SOURCES
|
||||||
src/qt-json/json.cpp
|
src/qt-json/json.cpp
|
||||||
../cockatrice/src/game/cards/card_database.cpp
|
../cockatrice/src/game/cards/card_database.cpp
|
||||||
../cockatrice/src/game/cards/card_database_manager.cpp
|
../cockatrice/src/game/cards/card_database_manager.cpp
|
||||||
../cockatrice/src/client/ui/picture_loader.cpp
|
../cockatrice/src/client/ui/picture_loader/picture_loader.cpp
|
||||||
|
../cockatrice/src/client/ui/picture_loader/picture_loader_worker.cpp
|
||||||
|
../cockatrice/src/client/ui/picture_loader/picture_to_load.cpp
|
||||||
../cockatrice/src/game/cards/card_database_parser/card_database_parser.cpp
|
../cockatrice/src/game/cards/card_database_parser/card_database_parser.cpp
|
||||||
../cockatrice/src/game/cards/card_database_parser/cockatrice_xml_3.cpp
|
../cockatrice/src/game/cards/card_database_parser/cockatrice_xml_3.cpp
|
||||||
../cockatrice/src/game/cards/card_database_parser/cockatrice_xml_4.cpp
|
../cockatrice/src/game/cards/card_database_parser/cockatrice_xml_4.cpp
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue