[PictureLoader] Reduce downtime between load attempts (#6020)

* [PictureLoader] Reduce downtime between load attempts

* rename some stuff

* better comments

* Fix segfault from status bar

Pass just the relevant data through the signals to the status bar, instead of passing the entire Work object.
That way the data is detached from the Work object and we won't segfault when Work self-deletes before status bar tries to use that data.

* Rename method
This commit is contained in:
RickyRister 2025-07-05 19:42:54 -07:00 committed by GitHub
parent 67a3b03b07
commit 0b9b39fef7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 56 additions and 38 deletions

View file

@ -2,23 +2,22 @@
PictureLoaderRequestStatusDisplayWidget::PictureLoaderRequestStatusDisplayWidget(QWidget *parent, PictureLoaderRequestStatusDisplayWidget::PictureLoaderRequestStatusDisplayWidget(QWidget *parent,
const QUrl &_url, const QUrl &_url,
PictureLoaderWorkerWork *worker) const CardInfoPtr &card,
const QString &setName)
: QWidget(parent) : QWidget(parent)
{ {
layout = new QHBoxLayout(this); layout = new QHBoxLayout(this);
if (worker->cardToDownload.getCard()) { name = new QLabel(this);
name = new QLabel(this); name->setText(card->getName());
name->setText(worker->cardToDownload.getCard()->getName()); setShortname = new QLabel(this);
setShortname = new QLabel(this); setShortname->setText(setName);
setShortname->setText(worker->cardToDownload.getSetName()); providerId = new QLabel(this);
providerId = new QLabel(this); providerId->setText(card->getProperty("uuid"));
providerId->setText(worker->cardToDownload.getCard()->getProperty("uuid"));
layout->addWidget(name); layout->addWidget(name);
layout->addWidget(setShortname); layout->addWidget(setShortname);
layout->addWidget(providerId); layout->addWidget(providerId);
}
startTime = new QLabel(QDateTime::currentDateTime().toString(), this); startTime = new QLabel(QDateTime::currentDateTime().toString(), this);
elapsedTime = new QLabel("0", this); elapsedTime = new QLabel("0", this);

View file

@ -10,8 +10,10 @@ class PictureLoaderRequestStatusDisplayWidget : public QWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
PictureLoaderRequestStatusDisplayWidget(QWidget *parent, const QUrl &url, PictureLoaderWorkerWork *worker); PictureLoaderRequestStatusDisplayWidget(QWidget *parent,
PictureLoaderWorkerWork *worker; const QUrl &url,
const CardInfoPtr &card,
const QString &setName);
void setFinished() void setFinished()
{ {

View file

@ -38,15 +38,14 @@ void PictureLoaderStatusBar::cleanOldEntries()
} }
} }
void PictureLoaderStatusBar::addQueuedImageLoad(const QUrl &url, PictureLoaderWorkerWork *worker) void PictureLoaderStatusBar::addQueuedImageLoad(const QUrl &url, const CardInfoPtr &card, const QString &setName)
{ {
loadLog->addSettingsWidget(new PictureLoaderRequestStatusDisplayWidget(loadLog, url, worker)); loadLog->addSettingsWidget(new PictureLoaderRequestStatusDisplayWidget(loadLog, url, card, setName));
progressBar->setMaximum(progressBar->maximum() + 1); progressBar->setMaximum(progressBar->maximum() + 1);
} }
void PictureLoaderStatusBar::addSuccessfulImageLoad(const QUrl &url, PictureLoaderWorkerWork *worker) void PictureLoaderStatusBar::addSuccessfulImageLoad(const QUrl &url)
{ {
Q_UNUSED(worker)
progressBar->setValue(progressBar->value() + 1); progressBar->setValue(progressBar->value() + 1);
for (PictureLoaderRequestStatusDisplayWidget *statusDisplayWidget : for (PictureLoaderRequestStatusDisplayWidget *statusDisplayWidget :
loadLog->popup->findChildren<PictureLoaderRequestStatusDisplayWidget *>()) { loadLog->popup->findChildren<PictureLoaderRequestStatusDisplayWidget *>()) {

View file

@ -15,8 +15,8 @@ public:
explicit PictureLoaderStatusBar(QWidget *parent); explicit PictureLoaderStatusBar(QWidget *parent);
public slots: public slots:
void addQueuedImageLoad(const QUrl &url, PictureLoaderWorkerWork *worker); void addQueuedImageLoad(const QUrl &url, const CardInfoPtr &card, const QString &setName);
void addSuccessfulImageLoad(const QUrl &url, PictureLoaderWorkerWork *worker); void addSuccessfulImageLoad(const QUrl &url);
void cleanOldEntries(); void cleanOldEntries();
private: private:

View file

@ -12,7 +12,10 @@
#include <QThread> #include <QThread>
#include <utility> #include <utility>
PictureLoaderWorker::PictureLoaderWorker() : QObject(nullptr), picDownload(SettingsCache::instance().getPicDownload()) static constexpr int MAX_REQUESTS_PER_SEC = 10;
PictureLoaderWorker::PictureLoaderWorker()
: QObject(nullptr), picDownload(SettingsCache::instance().getPicDownload()), requestQuota(MAX_REQUESTS_PER_SEC)
{ {
networkManager = new QNetworkAccessManager(this); networkManager = new QNetworkAccessManager(this);
// We need a timeout to ensure requests don't hang indefinitely in case of // We need a timeout to ensure requests don't hang indefinitely in case of
@ -48,7 +51,7 @@ PictureLoaderWorker::PictureLoaderWorker() : QObject(nullptr), picDownload(Setti
connect(this, &PictureLoaderWorker::imageLoadEnqueued, this, &PictureLoaderWorker::handleImageLoadEnqueued); connect(this, &PictureLoaderWorker::imageLoadEnqueued, this, &PictureLoaderWorker::handleImageLoadEnqueued);
connect(&requestTimer, &QTimer::timeout, this, &PictureLoaderWorker::processQueuedRequests); connect(&requestTimer, &QTimer::timeout, this, &PictureLoaderWorker::resetRequestQuota);
requestTimer.setInterval(1000); requestTimer.setInterval(1000);
requestTimer.start(); requestTimer.start();
} }
@ -69,7 +72,8 @@ void PictureLoaderWorker::queueRequest(const QUrl &url, PictureLoaderWorkerWork
makeRequest(url, worker); makeRequest(url, worker);
} else { } else {
requestLoadQueue.append(qMakePair(url, worker)); requestLoadQueue.append(qMakePair(url, worker));
emit imageLoadQueued(url, worker); emit imageLoadQueued(url, worker->cardToDownload.getCard(), worker->cardToDownload.getSetName());
processQueuedRequests();
} }
} }
@ -78,7 +82,7 @@ QNetworkReply *PictureLoaderWorker::makeRequest(const QUrl &url, PictureLoaderWo
// Check for cached redirects // Check for cached redirects
QUrl cachedRedirect = getCachedRedirect(url); QUrl cachedRedirect = getCachedRedirect(url);
if (!cachedRedirect.isEmpty()) { if (!cachedRedirect.isEmpty()) {
emit imageLoadSuccessful(url, worker); emit imageLoadSuccessful(url);
return makeRequest(cachedRedirect, worker); return makeRequest(cachedRedirect, worker);
} }
@ -95,22 +99,34 @@ QNetworkReply *PictureLoaderWorker::makeRequest(const QUrl &url, PictureLoaderWo
return reply; return reply;
} }
void PictureLoaderWorker::resetRequestQuota()
{
requestQuota = MAX_REQUESTS_PER_SEC;
processQueuedRequests();
}
/**
* Keeps processing requests from the queue until it is empty or until the quota runs out.
*/
void PictureLoaderWorker::processQueuedRequests() void PictureLoaderWorker::processQueuedRequests()
{ {
for (int i = 0; i < 10; i++) { while (requestQuota > 0 && processSingleRequest()) {
processSingleRequest(); --requestQuota;
} }
} }
/** /**
* Immediately processes a single queued request * Immediately processes a single queued request. No-ops if the load queue is empty
* @return If a request was processed
*/ */
void PictureLoaderWorker::processSingleRequest() bool PictureLoaderWorker::processSingleRequest()
{ {
if (!requestLoadQueue.isEmpty()) { if (!requestLoadQueue.isEmpty()) {
auto request = requestLoadQueue.takeFirst(); auto request = requestLoadQueue.takeFirst();
makeRequest(request.first, request.second); makeRequest(request.first, request.second);
return true;
} }
return false;
} }
void PictureLoaderWorker::enqueueImageLoad(const CardInfoPtr &card) void PictureLoaderWorker::enqueueImageLoad(const CardInfoPtr &card)
@ -132,7 +148,7 @@ void PictureLoaderWorker::handleImageLoadEnqueued(const CardInfoPtr &card)
// try to load image from local first // try to load image from local first
QImage image = localLoader->tryLoad(card); QImage image = localLoader->tryLoad(card);
if (!image.isNull()) { if (!image.isNull()) {
imageLoadedSuccessfully(card, image); handleImageLoaded(card, image);
} else { } else {
// queue up to load image from remote only after local loading failed // queue up to load image from remote only after local loading failed
new PictureLoaderWorkerWork(this, card); new PictureLoaderWorkerWork(this, card);
@ -140,10 +156,9 @@ void PictureLoaderWorker::handleImageLoadEnqueued(const CardInfoPtr &card)
} }
/** /**
* Called when image loading is done * Called when image loading is done. Failures are indicated by an empty QImage.
* Contrary to the name, this is called on both success and failure. Failures are indicated by an empty QImage.
*/ */
void PictureLoaderWorker::imageLoadedSuccessfully(const CardInfoPtr &card, const QImage &image) void PictureLoaderWorker::handleImageLoaded(const CardInfoPtr &card, const QImage &image)
{ {
currentlyLoading.remove(card); currentlyLoading.remove(card);
emit imageLoaded(card, image); emit imageLoaded(card, image);

View file

@ -38,8 +38,8 @@ public:
public slots: public slots:
QNetworkReply *makeRequest(const QUrl &url, PictureLoaderWorkerWork *workThread); QNetworkReply *makeRequest(const QUrl &url, PictureLoaderWorkerWork *workThread);
void processQueuedRequests(); void processQueuedRequests();
void processSingleRequest(); bool processSingleRequest();
void imageLoadedSuccessfully(const CardInfoPtr &card, const QImage &image); void handleImageLoaded(const CardInfoPtr &card, const QImage &image);
void cacheRedirect(const QUrl &originalUrl, const QUrl &redirectUrl); void cacheRedirect(const QUrl &originalUrl, const QUrl &redirectUrl);
void removedCachedUrl(const QUrl &url); void removedCachedUrl(const QUrl &url);
@ -52,7 +52,9 @@ private:
static constexpr int CacheTTLInDays = 30; // TODO: Make user configurable static constexpr int CacheTTLInDays = 30; // TODO: Make user configurable
bool picDownload; bool picDownload;
QQueue<QPair<QUrl, PictureLoaderWorkerWork *>> requestLoadQueue; QQueue<QPair<QUrl, PictureLoaderWorkerWork *>> requestLoadQueue;
QTimer requestTimer; // Timer for processing delayed requests
int requestQuota;
QTimer requestTimer; // Timer for refreshing request quota
PictureLoaderLocal *localLoader; PictureLoaderLocal *localLoader;
QSet<CardInfoPtr> currentlyLoading; // for deduplication purposes QSet<CardInfoPtr> currentlyLoading; // for deduplication purposes
@ -63,13 +65,14 @@ private:
void cleanStaleEntries(); void cleanStaleEntries();
private slots: private slots:
void resetRequestQuota();
void handleImageLoadEnqueued(const CardInfoPtr &card); void handleImageLoadEnqueued(const CardInfoPtr &card);
signals: signals:
void imageLoadEnqueued(const CardInfoPtr &card); void imageLoadEnqueued(const CardInfoPtr &card);
void imageLoaded(CardInfoPtr card, const QImage &image); void imageLoaded(CardInfoPtr card, const QImage &image);
void imageLoadQueued(const QUrl &url, PictureLoaderWorkerWork *worker); void imageLoadQueued(const QUrl &url, const CardInfoPtr &card, const QString &setName);
void imageLoadSuccessful(const QUrl &url, PictureLoaderWorkerWork *worker); void imageLoadSuccessful(const QUrl &url);
}; };
#endif // PICTURE_LOADER_WORKER_H #endif // PICTURE_LOADER_WORKER_H

View file

@ -22,7 +22,7 @@ PictureLoaderWorkerWork::PictureLoaderWorkerWork(const PictureLoaderWorker *work
connect(this, &PictureLoaderWorkerWork::requestImageDownload, worker, &PictureLoaderWorker::queueRequest); connect(this, &PictureLoaderWorkerWork::requestImageDownload, worker, &PictureLoaderWorker::queueRequest);
connect(this, &PictureLoaderWorkerWork::urlRedirected, worker, &PictureLoaderWorker::cacheRedirect); connect(this, &PictureLoaderWorkerWork::urlRedirected, worker, &PictureLoaderWorker::cacheRedirect);
connect(this, &PictureLoaderWorkerWork::cachedUrlInvalidated, worker, &PictureLoaderWorker::removedCachedUrl); connect(this, &PictureLoaderWorkerWork::cachedUrlInvalidated, worker, &PictureLoaderWorker::removedCachedUrl);
connect(this, &PictureLoaderWorkerWork::imageLoaded, worker, &PictureLoaderWorker::imageLoadedSuccessfully); connect(this, &PictureLoaderWorkerWork::imageLoaded, worker, &PictureLoaderWorker::handleImageLoaded);
// Hook up signals to settings // Hook up signals to settings
connect(&SettingsCache::instance(), SIGNAL(picDownloadChanged()), this, SLOT(picDownloadChanged())); connect(&SettingsCache::instance(), SIGNAL(picDownloadChanged()), this, SLOT(picDownloadChanged()));