mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-09-23 01:55:10 -07:00
[PictureLoader] Store per-host limits readably and show them per URL
This commit is contained in:
parent
21ca53962d
commit
b3b930587b
3 changed files with 137 additions and 22 deletions
|
|
@ -10,6 +10,7 @@
|
||||||
#include <QInputDialog>
|
#include <QInputDialog>
|
||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
#include <QSet>
|
||||||
#include <QToolBar>
|
#include <QToolBar>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <libcockatrice/settings/download_settings.h>
|
#include <libcockatrice/settings/download_settings.h>
|
||||||
|
|
@ -17,8 +18,6 @@
|
||||||
#include <libcockatrice/settings/personal_settings.h>
|
#include <libcockatrice/settings/personal_settings.h>
|
||||||
#include <libcockatrice/utility/macros.h>
|
#include <libcockatrice/utility/macros.h>
|
||||||
|
|
||||||
static constexpr int UNLOCKED_HOST_LIMIT_MAX = 50; ///< Upper bound for rate limits on hosts unlocked by the developer
|
|
||||||
|
|
||||||
DeckEditorSettingsPage::DeckEditorSettingsPage()
|
DeckEditorSettingsPage::DeckEditorSettingsPage()
|
||||||
{
|
{
|
||||||
picDownloadCheckBox.setChecked(SettingsCache::instance().downloads().getPicDownload());
|
picDownloadCheckBox.setChecked(SettingsCache::instance().downloads().getPicDownload());
|
||||||
|
|
@ -54,7 +53,9 @@ DeckEditorSettingsPage::DeckEditorSettingsPage()
|
||||||
urlList->setDragDropMode(QAbstractItemView::InternalMove);
|
urlList->setDragDropMode(QAbstractItemView::InternalMove);
|
||||||
connect(urlList->model(), &QAbstractItemModel::rowsMoved, this, &DeckEditorSettingsPage::urlListChanged);
|
connect(urlList->model(), &QAbstractItemModel::rowsMoved, this, &DeckEditorSettingsPage::urlListChanged);
|
||||||
|
|
||||||
urlList->addItems(SettingsCache::instance().downloads().getAllURLs());
|
for (const QString &url : SettingsCache::instance().downloads().getAllURLs()) {
|
||||||
|
addUrlItem(url);
|
||||||
|
}
|
||||||
|
|
||||||
aAdd = new QAction(this);
|
aAdd = new QAction(this);
|
||||||
aAdd->setIcon(themePixmap(QStringLiteral("icons/increment")));
|
aAdd->setIcon(themePixmap(QStringLiteral("icons/increment")));
|
||||||
|
|
@ -125,7 +126,9 @@ void DeckEditorSettingsPage::resetDownloadedURLsButtonClicked()
|
||||||
{
|
{
|
||||||
SettingsCache::instance().downloads().resetToDefaultURLs();
|
SettingsCache::instance().downloads().resetToDefaultURLs();
|
||||||
urlList->clear();
|
urlList->clear();
|
||||||
urlList->addItems(SettingsCache::instance().downloads().getAllURLs());
|
for (const QString &url : SettingsCache::instance().downloads().getAllURLs()) {
|
||||||
|
addUrlItem(url);
|
||||||
|
}
|
||||||
QMessageBox::information(this, tr("Success"), tr("Download URLs have been reset."));
|
QMessageBox::information(this, tr("Success"), tr("Download URLs have been reset."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -134,7 +137,7 @@ void DeckEditorSettingsPage::actAddURL()
|
||||||
bool ok;
|
bool ok;
|
||||||
QString msg = QInputDialog::getText(this, tr("Add URL"), tr("URL:"), QLineEdit::Normal, QString(), &ok);
|
QString msg = QInputDialog::getText(this, tr("Add URL"), tr("URL:"), QLineEdit::Normal, QString(), &ok);
|
||||||
if (ok) {
|
if (ok) {
|
||||||
urlList->addItem(msg);
|
addUrlItem(msg);
|
||||||
storeSettings();
|
storeSettings();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -149,12 +152,14 @@ void DeckEditorSettingsPage::actRemoveURL()
|
||||||
|
|
||||||
void DeckEditorSettingsPage::actEditURL()
|
void DeckEditorSettingsPage::actEditURL()
|
||||||
{
|
{
|
||||||
if (urlList->currentItem()) {
|
QListWidgetItem *item = urlList->currentItem();
|
||||||
QString oldText = urlList->currentItem()->text();
|
if (item) {
|
||||||
|
const QString oldText = urlForItem(item);
|
||||||
bool ok;
|
bool ok;
|
||||||
QString msg = QInputDialog::getText(this, tr("Edit URL"), tr("URL:"), QLineEdit::Normal, oldText, &ok);
|
QString msg = QInputDialog::getText(this, tr("Edit URL"), tr("URL:"), QLineEdit::Normal, oldText, &ok);
|
||||||
if (ok) {
|
if (ok) {
|
||||||
urlList->currentItem()->setText(msg);
|
item->setData(Qt::UserRole, msg);
|
||||||
|
item->setText(urlLabel(msg));
|
||||||
storeSettings();
|
storeSettings();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -166,10 +171,77 @@ void DeckEditorSettingsPage::storeSettings()
|
||||||
|
|
||||||
QStringList downloadUrls;
|
QStringList downloadUrls;
|
||||||
for (int i = 0; i < urlList->count(); i++) {
|
for (int i = 0; i < urlList->count(); i++) {
|
||||||
qInfo() << "Priority" << i << ":" << urlList->item(i)->text();
|
const QString url = urlForItem(urlList->item(i));
|
||||||
downloadUrls << urlList->item(i)->text();
|
qInfo() << "Priority" << i << ":" << url;
|
||||||
|
downloadUrls << url;
|
||||||
}
|
}
|
||||||
SettingsCache::instance().downloads().setDownloadUrls(downloadUrls);
|
SettingsCache::instance().downloads().setDownloadUrls(downloadUrls);
|
||||||
|
|
||||||
|
// Drop per-host limits whose host is no longer referenced by any configured URL, so removing
|
||||||
|
// a URL doesn't leave a stale throttle behind that reactivates if the host is re-added.
|
||||||
|
QSet<QString> usedHosts;
|
||||||
|
for (const QString &url : downloadUrls) {
|
||||||
|
const QString host = QUrl(url).host();
|
||||||
|
if (!host.isEmpty()) {
|
||||||
|
usedHosts.insert(host);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QHash<QString, int> limits = SettingsCache::instance().downloads().getHostRequestLimits();
|
||||||
|
bool limitsChanged = false;
|
||||||
|
for (auto it = limits.begin(); it != limits.end();) {
|
||||||
|
if (!usedHosts.contains(it.key())) {
|
||||||
|
it = limits.erase(it);
|
||||||
|
limitsChanged = true;
|
||||||
|
} else {
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (limitsChanged) {
|
||||||
|
SettingsCache::instance().downloads().setHostRequestLimits(limits);
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshUrlItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
QListWidgetItem *DeckEditorSettingsPage::addUrlItem(const QString &url)
|
||||||
|
{
|
||||||
|
auto *item = new QListWidgetItem(urlLabel(url));
|
||||||
|
item->setData(Qt::UserRole, url);
|
||||||
|
urlList->addItem(item);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DeckEditorSettingsPage::urlForItem(const QListWidgetItem *item) const
|
||||||
|
{
|
||||||
|
return item->data(Qt::UserRole).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DeckEditorSettingsPage::urlLabel(const QString &url) const
|
||||||
|
{
|
||||||
|
const QString host = QUrl(url).host();
|
||||||
|
if (host.isEmpty()) {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QHash<QString, int> limits = SettingsCache::instance().downloads().getHostRequestLimits();
|
||||||
|
const int devCap =
|
||||||
|
DownloadSettings::getDeveloperHostCaps().value(host, DownloadSettings::DEFAULT_HOST_REQUEST_LIMIT);
|
||||||
|
if (devCap == DownloadSettings::UNLIMITED_HOST_QUOTA && !limits.contains(host)) {
|
||||||
|
return tr("%1 (unlimited)").arg(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
const int requested = limits.value(
|
||||||
|
host, devCap == DownloadSettings::UNLIMITED_HOST_QUOTA ? DownloadSettings::DEFAULT_HOST_REQUEST_LIMIT : devCap);
|
||||||
|
const int effective = SettingsCache::instance().downloads().clampHostRequestLimit(host, requested);
|
||||||
|
return tr("%1 (%2/s)").arg(url).arg(effective);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeckEditorSettingsPage::refreshUrlItems()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < urlList->count(); ++i) {
|
||||||
|
QListWidgetItem *item = urlList->item(i);
|
||||||
|
item->setText(urlLabel(urlForItem(item)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeckEditorSettingsPage::actAdjustRateLimit()
|
void DeckEditorSettingsPage::actAdjustRateLimit()
|
||||||
|
|
@ -179,7 +251,7 @@ void DeckEditorSettingsPage::actAdjustRateLimit()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString host = QUrl(urlList->currentItem()->text()).host();
|
const QString host = QUrl(urlForItem(urlList->currentItem())).host();
|
||||||
if (host.isEmpty()) {
|
if (host.isEmpty()) {
|
||||||
QMessageBox::information(this, tr("Adjust Rate Limit"), tr("The selected URL does not have a valid host."));
|
QMessageBox::information(this, tr("Adjust Rate Limit"), tr("The selected URL does not have a valid host."));
|
||||||
return;
|
return;
|
||||||
|
|
@ -194,19 +266,21 @@ void DeckEditorSettingsPage::actAdjustRateLimit()
|
||||||
int minimum;
|
int minimum;
|
||||||
int maximum;
|
int maximum;
|
||||||
int defaultValue;
|
int defaultValue;
|
||||||
|
QString prompt;
|
||||||
if (unlocked) {
|
if (unlocked) {
|
||||||
minimum = 0; // 0 means "unlimited"
|
minimum = 0; // 0 means "unlimited"
|
||||||
maximum = UNLOCKED_HOST_LIMIT_MAX;
|
maximum = DownloadSettings::DEFAULT_HOST_REQUEST_LIMIT;
|
||||||
defaultValue = currentLimits.value(host, 0);
|
defaultValue = currentLimits.value(host, 0);
|
||||||
|
prompt = tr("Requests per second (0 = unlimited, fastest; up to %1):").arg(maximum);
|
||||||
} else {
|
} else {
|
||||||
minimum = DownloadSettings::MIN_HOST_REQUEST_LIMIT;
|
minimum = DownloadSettings::MIN_HOST_REQUEST_LIMIT;
|
||||||
maximum = devCap;
|
maximum = devCap;
|
||||||
defaultValue = currentLimits.value(host, devCap);
|
defaultValue = currentLimits.value(host, devCap);
|
||||||
|
prompt = tr("Requests per second (developer maximum is %1):").arg(maximum);
|
||||||
}
|
}
|
||||||
|
|
||||||
const int value = QInputDialog::getInt(this, tr("Adjust Rate Limit for %1").arg(host),
|
const int value = QInputDialog::getInt(this, tr("Adjust Rate Limit for %1").arg(host), prompt, defaultValue,
|
||||||
tr("Requests per second (developer maximum is %1):").arg(maximum),
|
minimum, maximum, 1, &ok);
|
||||||
defaultValue, minimum, maximum, 1, &ok);
|
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -218,6 +292,7 @@ void DeckEditorSettingsPage::actAdjustRateLimit()
|
||||||
limits.insert(host, value);
|
limits.insert(host, value);
|
||||||
}
|
}
|
||||||
SettingsCache::instance().downloads().setHostRequestLimits(limits);
|
SettingsCache::instance().downloads().setHostRequestLimits(limits);
|
||||||
|
refreshUrlItems();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeckEditorSettingsPage::urlListChanged(const QModelIndex &, int, int, const QModelIndex &, int)
|
void DeckEditorSettingsPage::urlListChanged(const QModelIndex &, int, int, const QModelIndex &, int)
|
||||||
|
|
@ -301,4 +376,7 @@ void DeckEditorSettingsPage::retranslateUi()
|
||||||
aEdit->setText(tr("Edit URL"));
|
aEdit->setText(tr("Edit URL"));
|
||||||
aRemove->setText(tr("Remove URL"));
|
aRemove->setText(tr("Remove URL"));
|
||||||
aRateLimit->setText(tr("Adjust Rate Limit"));
|
aRateLimit->setText(tr("Adjust Rate Limit"));
|
||||||
|
|
||||||
|
// The per-URL rate limit suffixes are translated, so refresh them when the language changes.
|
||||||
|
refreshUrlItems();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,18 @@ private:
|
||||||
QLabel infoOnSpoilersLabel;
|
QLabel infoOnSpoilersLabel;
|
||||||
QPushButton *mpSpoilerPathButton;
|
QPushButton *mpSpoilerPathButton;
|
||||||
QPushButton *updateNowButton;
|
QPushButton *updateNowButton;
|
||||||
|
|
||||||
|
/** @brief Adds a list item for the given URL, storing the raw URL alongside its displayed label. */
|
||||||
|
QListWidgetItem *addUrlItem(const QString &url);
|
||||||
|
|
||||||
|
/** @brief Returns the raw URL stored on a list item. */
|
||||||
|
[[nodiscard]] QString urlForItem(const QListWidgetItem *item) const;
|
||||||
|
|
||||||
|
/** @brief Returns the display label for a URL, including its current effective rate limit. */
|
||||||
|
[[nodiscard]] QString urlLabel(const QString &url) const;
|
||||||
|
|
||||||
|
/** @brief Refreshes the displayed label of every URL item after limits or settings change. */
|
||||||
|
void refreshUrlItems();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // COCKATRICE_DECK_EDITOR_SETTINGS_PAGE_H
|
#endif // COCKATRICE_DECK_EDITOR_SETTINGS_PAGE_H
|
||||||
|
|
|
||||||
|
|
@ -69,21 +69,46 @@ void DownloadSettings::setDownloadSpoilerStatus(bool _spoilerStatus)
|
||||||
|
|
||||||
QHash<QString, int> DownloadSettings::getHostRequestLimits() const
|
QHash<QString, int> DownloadSettings::getHostRequestLimits() const
|
||||||
{
|
{
|
||||||
const QVariantMap stored = getValue("hostRequestLimits").toMap();
|
auto settings = getSettings();
|
||||||
|
if (!defaultGroup.isEmpty()) {
|
||||||
|
settings.beginGroup(defaultGroup);
|
||||||
|
}
|
||||||
|
settings.beginGroup("hostRequestLimits");
|
||||||
|
|
||||||
QHash<QString, int> hostRequestLimits;
|
QHash<QString, int> hostRequestLimits;
|
||||||
for (auto it = stored.cbegin(); it != stored.cend(); ++it) {
|
const QStringList hosts = settings.childKeys();
|
||||||
hostRequestLimits.insert(it.key(), it.value().toInt());
|
for (const QString &host : hosts) {
|
||||||
|
hostRequestLimits.insert(host, settings.value(host).toInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.endGroup();
|
||||||
|
if (!defaultGroup.isEmpty()) {
|
||||||
|
settings.endGroup();
|
||||||
}
|
}
|
||||||
return hostRequestLimits;
|
return hostRequestLimits;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DownloadSettings::setHostRequestLimits(const QHash<QString, int> &hostRequestLimits)
|
void DownloadSettings::setHostRequestLimits(const QHash<QString, int> &hostRequestLimits)
|
||||||
{
|
{
|
||||||
QVariantMap stored;
|
auto settings = getSettings();
|
||||||
for (auto it = hostRequestLimits.cbegin(); it != hostRequestLimits.cend(); ++it) {
|
if (!defaultGroup.isEmpty()) {
|
||||||
stored.insert(it.key(), it.value());
|
settings.beginGroup(defaultGroup);
|
||||||
}
|
}
|
||||||
setValue(stored, "hostRequestLimits");
|
|
||||||
|
// Drop the legacy single-key form (an opaque @Variant blob) written by earlier builds so each
|
||||||
|
// host is stored as a plain, hand-editable key in its own subgroup.
|
||||||
|
settings.remove("hostRequestLimits");
|
||||||
|
settings.beginGroup("hostRequestLimits");
|
||||||
|
settings.remove(QString());
|
||||||
|
for (auto it = hostRequestLimits.cbegin(); it != hostRequestLimits.cend(); ++it) {
|
||||||
|
settings.setValue(it.key(), it.value());
|
||||||
|
}
|
||||||
|
settings.endGroup();
|
||||||
|
|
||||||
|
if (!defaultGroup.isEmpty()) {
|
||||||
|
settings.endGroup();
|
||||||
|
}
|
||||||
|
settings.sync();
|
||||||
emit hostRequestLimitsChanged();
|
emit hostRequestLimitsChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue