Support MTGJSONv5 format in Oracle downloader (#4162)

* Fix #4043, Support MTGJSONv5 format in Oracle downloader

* Auto redirect V4 downloads to V5, as we won't support V4 after this change

* clangify >_>

* Remove null values and account for IDs missing

* fix split cards and double faced cards somewhat

* do not consider double faced cards duplicates

* fix promo double sided cards

* typo

* fix alternative versions of cards with (letter)

* zach says this is more readable

* pre qt 5.10 compatibility

Co-authored-by: ebbit1q <ebbit1q@gmail.com>
This commit is contained in:
Zach H 2020-11-23 16:12:41 -05:00 committed by GitHub
parent f3cf1f0dde
commit 9f9581c2be
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 118 additions and 76 deletions

View file

@ -3,16 +3,15 @@
#include "carddbparser/cockatricexml4.h" #include "carddbparser/cockatricexml4.h"
#include "qt-json/json.h" #include "qt-json/json.h"
#include <QDebug>
#include <QtWidgets> #include <QtWidgets>
#include <algorithm> #include <algorithm>
#include <climits> #include <climits>
SplitCardPart::SplitCardPart(const int _index, SplitCardPart::SplitCardPart(const QString &_name,
const QString &_text, const QString &_text,
const QVariantHash &_properties, const QVariantHash &_properties,
const CardInfoPerSet _setInfo) const CardInfoPerSet _setInfo)
: index(_index), text(_text), properties(_properties), setInfo(_setInfo) : name(_name), text(_text), properties(_properties), setInfo(_setInfo)
{ {
} }
@ -25,7 +24,7 @@ bool OracleImporter::readSetsFromByteArray(const QByteArray &data)
QList<SetToDownload> newSetList; QList<SetToDownload> newSetList;
bool ok; bool ok;
setsMap = QtJson::Json::parse(QString(data), ok).toMap(); setsMap = QtJson::Json::parse(QString(data), ok).toMap().value("data").toMap();
if (!ok) { if (!ok) {
qDebug() << "error: QtJson::Json::parse()"; qDebug() << "error: QtJson::Json::parse()";
return false; return false;
@ -162,14 +161,9 @@ CardInfoPtr OracleImporter::addCard(QString name,
// upsideDown (flip cards) // upsideDown (flip cards)
bool upsideDown = false; bool upsideDown = false;
QStringList additionalNames = properties.value("names").toStringList();
QString layout = properties.value("layout").toString(); QString layout = properties.value("layout").toString();
if (layout == "flip") { if (layout == "flip") {
if (properties.value("side").toString() != "front") { upsideDown = properties.value("side").toString() != "a";
upsideDown = true;
}
// reset the side property, since the card has no back image
properties.insert("side", "front");
} }
// insert the card and its properties // insert the card and its properties
@ -179,36 +173,41 @@ CardInfoPtr OracleImporter::addCard(QString name,
CardInfoPtr newCard = CardInfo::newInstance(name, text, isToken, properties, relatedCards, reverseRelatedCards, CardInfoPtr newCard = CardInfo::newInstance(name, text, isToken, properties, relatedCards, reverseRelatedCards,
setsInfo, cipt, tableRow, upsideDown); setsInfo, cipt, tableRow, upsideDown);
if (name.isEmpty()) {
qDebug() << "warning: an empty card was added to set" << setInfo.getPtr()->getShortName();
}
cards.insert(name, newCard); cards.insert(name, newCard);
return newCard; return newCard;
} }
QString OracleImporter::getStringPropertyFromMap(QVariantMap card, QString propertyName) QString OracleImporter::getStringPropertyFromMap(const QVariantMap &card, const QString &propertyName)
{ {
return card.contains(propertyName) ? card.value(propertyName).toString() : QString(""); return card.contains(propertyName) ? card.value(propertyName).toString() : QString("");
} }
int OracleImporter::importCardsFromSet(CardSetPtr currentSet, const QList<QVariant> &cardsList, bool skipSpecialCards) int OracleImporter::importCardsFromSet(const CardSetPtr &currentSet,
const QList<QVariant> &cardsList,
bool skipSpecialCards)
{ {
// mtgjson name => xml name
static const QMap<QString, QString> cardProperties{ static const QMap<QString, QString> cardProperties{
// mtgjson name => xml name
{"manaCost", "manacost"}, {"convertedManaCost", "cmc"}, {"type", "type"}, {"manaCost", "manacost"}, {"convertedManaCost", "cmc"}, {"type", "type"},
{"loyalty", "loyalty"}, {"layout", "layout"}, {"side", "side"}, {"loyalty", "loyalty"}, {"layout", "layout"}, {"side", "side"},
}; };
static const QMap<QString, QString> setInfoProperties{// mtgjson name => xml name // mtgjson name => xml name
{"multiverseId", "muid"}, static const QMap<QString, QString> setInfoProperties{{"number", "num"}, {"rarity", "rarity"}};
{"scryfallId", "uuid"},
{"number", "num"}, // mtgjson name => xml name
{"rarity", "rarity"}}; static const QMap<QString, QString> identifierProperties{{"multiverseId", "muid"}, {"scryfallId", "uuid"}};
int numCards = 0; int numCards = 0;
QMultiMap<QString, SplitCardPart> splitCards; QMultiMap<QString, SplitCardPart> splitCards;
QString ptSeparator("/"); QString ptSeparator("/");
QVariantMap card; QVariantMap card;
QString layout, name, text, colors, colorIdentity, maintype, power, toughness; QString layout, name, text, colors, colorIdentity, maintype, power, toughness, faceName;
static const bool isToken = false; static const bool isToken = false;
QStringList additionalNames;
QVariantHash properties; QVariantHash properties;
CardInfoPerSet setInfo; CardInfoPerSet setInfo;
QList<CardRelation *> relatedCards; QList<CardRelation *> relatedCards;
@ -219,6 +218,11 @@ int OracleImporter::importCardsFromSet(CardSetPtr currentSet, const QList<QVaria
for (const QVariant &cardVar : cardsList) { for (const QVariant &cardVar : cardsList) {
card = cardVar.toMap(); card = cardVar.toMap();
// skip alternatives
if (getStringPropertyFromMap(card, "isAlternative") == "true") {
continue;
}
/* Currently used layouts are: /* Currently used layouts are:
* augment, double_faced_token, flip, host, leveler, meld, normal, planar, * augment, double_faced_token, flip, host, leveler, meld, normal, planar,
* saga, scheme, split, token, transform, vanguard * saga, scheme, split, token, transform, vanguard
@ -233,6 +237,10 @@ int OracleImporter::importCardsFromSet(CardSetPtr currentSet, const QList<QVaria
// normal cards handling // normal cards handling
name = getStringPropertyFromMap(card, "name"); name = getStringPropertyFromMap(card, "name");
text = getStringPropertyFromMap(card, "text"); text = getStringPropertyFromMap(card, "text");
faceName = getStringPropertyFromMap(card, "faceName");
if (faceName.isEmpty()) {
faceName = name;
}
// card properties // card properties
properties.clear(); properties.clear();
@ -258,21 +266,40 @@ int OracleImporter::importCardsFromSet(CardSetPtr currentSet, const QList<QVaria
setInfo.setProperty(xmlPropertyName, propertyValue); setInfo.setProperty(xmlPropertyName, propertyValue);
} }
// skip alternatives // Identifiers
if (getStringPropertyFromMap(card, "isAlternative") == "true") { QMapIterator<QString, QString> it3(identifierProperties);
continue; while (it3.hasNext()) {
it3.next();
auto mtgjsonProperty = it3.key();
auto xmlPropertyName = it3.value();
auto propertyValue = getStringPropertyFromMap(card.value("identifiers").toMap(), mtgjsonProperty);
if (!propertyValue.isEmpty()) {
setInfo.setProperty(xmlPropertyName, propertyValue);
}
} }
QString numComponent{};
if (skipSpecialCards) { if (skipSpecialCards) {
// skip promo cards if it's not the only print QString numProperty = setInfo.getProperty("num");
if (allNameProps.contains(name)) { // skip promo cards if it's not the only print, cards with two faces are different cards
continue; if (allNameProps.contains(faceName)) {
// check for alternative versions
if (layout != "normal")
continue;
// alternative versions have a letter in the end of num like abc
// note this will also catch p and s, those will get removed later anyway
QChar lastChar = numProperty.at(numProperty.size() - 1);
if (!lastChar.isLetter())
continue;
numComponent = " (" + QString(lastChar) + ")";
faceName += numComponent; // add to facename to make it unique
} }
if (getStringPropertyFromMap(card, "isPromo") == "true") { if (getStringPropertyFromMap(card, "isPromo") == "true") {
specialPromoCards.insert(name, cardVar); specialPromoCards.insert(faceName, cardVar);
continue; continue;
} }
QString numProperty = setInfo.getProperty("num");
bool skip = false; bool skip = false;
// skip cards containing special stuff in the collectors number like promo cards // skip cards containing special stuff in the collectors number like promo cards
for (const QString &specialChar : specialNumChars) { for (const QString &specialChar : specialNumChars) {
@ -282,10 +309,10 @@ int OracleImporter::importCardsFromSet(CardSetPtr currentSet, const QList<QVaria
} }
} }
if (skip) { if (skip) {
specialPromoCards.insert(name, cardVar); specialPromoCards.insert(faceName, cardVar);
continue; continue;
} else { } else {
allNameProps.append(name); allNameProps.append(faceName);
} }
} }
@ -314,8 +341,6 @@ int OracleImporter::importCardsFromSet(CardSetPtr currentSet, const QList<QVaria
properties.insert("pt", power + ptSeparator + toughness); properties.insert("pt", power + ptSeparator + toughness);
} }
additionalNames = card.value("names").toStringList();
auto legalities = card.value("legalities").toMap(); auto legalities = card.value("legalities").toMap();
for (const QString &fmtName : legalities.keys()) { for (const QString &fmtName : legalities.keys()) {
properties.insert(QString("format-%1").arg(fmtName), legalities.value(fmtName).toString().toLower()); properties.insert(QString("format-%1").arg(fmtName), legalities.value(fmtName).toString().toLower());
@ -323,23 +348,33 @@ int OracleImporter::importCardsFromSet(CardSetPtr currentSet, const QList<QVaria
// split cards are considered a single card, enqueue for later merging // split cards are considered a single card, enqueue for later merging
if (layout == "split" || layout == "aftermath" || layout == "adventure") { if (layout == "split" || layout == "aftermath" || layout == "adventure") {
// get the position of this card part auto faceName = getStringPropertyFromMap(card, "faceName");
int index = additionalNames.indexOf(name); SplitCardPart split(faceName, text, properties, setInfo);
// construct full card name
name = additionalNames.join(QString(" // "));
SplitCardPart split(index, text, properties, setInfo);
splitCards.insert(name, split); splitCards.insert(name, split);
} else { } else {
// relations // relations
relatedCards.clear(); relatedCards.clear();
if (additionalNames.size() > 1) {
for (const QString &additionalName : additionalNames) { // add other face for split cards as card relation
if (additionalName != name) if (!getStringPropertyFromMap(card, "side").isEmpty()) {
properties["cmc"] = getStringPropertyFromMap(card, "faceConvertedManaCost");
if (layout == "meld") { // meld cards don't work
QRegularExpression meldNameRegex{"then meld them into ([\\.]*)"};
QString additionalName = meldNameRegex.match(text).captured(1);
if (!additionalName.isNull()) {
relatedCards.append(new CardRelation(additionalName, true)); relatedCards.append(new CardRelation(additionalName, true));
}
} else {
for (const QString &additionalName : name.split(" // ")) {
if (additionalName != faceName) {
relatedCards.append(new CardRelation(additionalName, true));
}
}
} }
name = faceName;
} }
CardInfoPtr newCard = addCard(name, text, isToken, properties, relatedCards, setInfo); CardInfoPtr newCard = addCard(name + numComponent, text, isToken, properties, relatedCards, setInfo);
numCards++; numCards++;
} }
} }
@ -350,21 +385,22 @@ int OracleImporter::importCardsFromSet(CardSetPtr currentSet, const QList<QVaria
for (const QString &nameSplit : splitCards.uniqueKeys()) { for (const QString &nameSplit : splitCards.uniqueKeys()) {
// get all parts for this specific card // get all parts for this specific card
QList<SplitCardPart> splitCardParts = splitCards.values(nameSplit); QList<SplitCardPart> splitCardParts = splitCards.values(nameSplit);
// sort them by index (aka position) // sort them by face name
std::sort(splitCardParts.begin(), splitCardParts.end(), std::sort(splitCardParts.begin(), splitCardParts.end(),
[](const SplitCardPart &a, const SplitCardPart &b) -> bool { return a.getIndex() < b.getIndex(); }); [](const SplitCardPart &a, const SplitCardPart &b) -> bool { return a.getName() < b.getName(); });
text = QString(""); text = QString("");
properties.clear(); properties.clear();
relatedCards.clear(); relatedCards.clear();
int lastIndex = -1; QString lastName{};
for (const SplitCardPart &tmp : splitCardParts) { for (const SplitCardPart &tmp : splitCardParts) {
// some sets have 2 different variations of the same split card, // some sets have 2 different variations of the same split card,
// eg. Fire // Ice in WC02. Avoid adding duplicates. // eg. Fire // Ice in WC02. Avoid adding duplicates.
if (lastIndex == tmp.getIndex()) if (lastName == tmp.getName())
continue; continue;
lastIndex = tmp.getIndex();
lastName = tmp.getName();
if (!text.isEmpty()) if (!text.isEmpty())
text.append(splitCardTextSeparator); text.append(splitCardTextSeparator);
@ -375,7 +411,6 @@ int OracleImporter::importCardsFromSet(CardSetPtr currentSet, const QList<QVaria
setInfo = tmp.getSetInfo(); setInfo = tmp.getSetInfo();
} else { } else {
const QVariantHash &props = tmp.getProperties(); const QVariantHash &props = tmp.getProperties();
layout = properties.value("layout").toString();
for (const QString &prop : props.keys()) { for (const QString &prop : props.keys()) {
QString originalPropertyValue = properties.value(prop).toString(); QString originalPropertyValue = properties.value(prop).toString();
QString thisCardPropertyValue = props.value(prop).toString(); QString thisCardPropertyValue = props.value(prop).toString();

View file

@ -60,10 +60,10 @@ public:
class SplitCardPart class SplitCardPart
{ {
public: public:
SplitCardPart(int _index, const QString &_text, const QVariantHash &_properties, CardInfoPerSet setInfo); SplitCardPart(const QString &_name, const QString &_text, const QVariantHash &_properties, CardInfoPerSet setInfo);
inline const int &getIndex() const inline const QString &getName() const
{ {
return index; return name;
} }
inline const QString &getText() const inline const QString &getText() const
{ {
@ -79,7 +79,7 @@ public:
} }
private: private:
int index; QString name;
QString text; QString text;
QVariantHash properties; QVariantHash properties;
CardInfoPerSet setInfo; CardInfoPerSet setInfo;
@ -111,7 +111,7 @@ public:
bool readSetsFromByteArray(const QByteArray &data); bool readSetsFromByteArray(const QByteArray &data);
int startImport(); int startImport();
bool saveToFile(const QString &fileName, const QString &sourceUrl, const QString &sourceVersion); bool saveToFile(const QString &fileName, const QString &sourceUrl, const QString &sourceVersion);
int importCardsFromSet(CardSetPtr currentSet, const QList<QVariant> &cards, bool skipSpecialNums = true); int importCardsFromSet(const CardSetPtr &currentSet, const QList<QVariant> &cards, bool skipSpecialNums = true);
QList<SetToDownload> &getSets() QList<SetToDownload> &getSets()
{ {
return allSets; return allSets;
@ -123,7 +123,7 @@ public:
void clear(); void clear();
protected: protected:
inline QString getStringPropertyFromMap(QVariantMap card, QString propertyName); inline QString getStringPropertyFromMap(const QVariantMap &card, const QString &propertyName);
void sortAndReduceColors(QString &colors); void sortAndReduceColors(QString &colors);
}; };

View file

@ -20,7 +20,6 @@
#include <QProgressBar> #include <QProgressBar>
#include <QPushButton> #include <QPushButton>
#include <QRadioButton> #include <QRadioButton>
#include <QScrollArea>
#include <QScrollBar> #include <QScrollBar>
#include <QStandardPaths> #include <QStandardPaths>
#include <QTextEdit> #include <QTextEdit>
@ -38,15 +37,16 @@
#define ZIP_SIGNATURE "PK" #define ZIP_SIGNATURE "PK"
// Xz stream header: 0xFD + "7zXZ" // Xz stream header: 0xFD + "7zXZ"
#define XZ_SIGNATURE "\xFD\x37\x7A\x58\x5A" #define XZ_SIGNATURE "\xFD\x37\x7A\x58\x5A"
#define ALLSETS_URL_FALLBACK "https://www.mtgjson.com/files/AllPrintings.json" #define MTGJSON_V4_URL_COMPONENT "mtgjson.com/files/"
#define MTGJSON_VERSION_URL "https://www.mtgjson.com/files/version.json" #define ALLSETS_URL_FALLBACK "https://www.mtgjson.com/api/v5/AllPrintings.json"
#define MTGJSON_VERSION_URL "https://www.mtgjson.com/api/v5/Meta.json"
#ifdef HAS_LZMA #ifdef HAS_LZMA
#define ALLSETS_URL "https://www.mtgjson.com/files/AllPrintings.json.xz" #define ALLSETS_URL "https://www.mtgjson.com/api/v5/AllPrintings.json.xz"
#elif defined(HAS_ZLIB) #elif defined(HAS_ZLIB)
#define ALLSETS_URL "https://www.mtgjson.com/files/AllPrintings.json.zip" #define ALLSETS_URL "https://www.mtgjson.com/api/v5/AllPrintings.json.zip"
#else #else
#define ALLSETS_URL "https://www.mtgjson.com/files/AllPrintings.json" #define ALLSETS_URL "https://www.mtgjson.com/api/v5/AllPrintings.json"
#endif #endif
#define TOKENS_URL "https://raw.githubusercontent.com/Cockatrice/Magic-Token/master/tokens.xml" #define TOKENS_URL "https://raw.githubusercontent.com/Cockatrice/Magic-Token/master/tokens.xml"
@ -299,7 +299,13 @@ bool LoadSetsPage::validatePage()
// else, try to import sets // else, try to import sets
if (urlRadioButton->isChecked()) { if (urlRadioButton->isChecked()) {
QUrl url = QUrl::fromUserInput(urlLineEdit->text()); // If a user attempts to download from V4, redirect them to V5
if (urlLineEdit->text().contains(MTGJSON_V4_URL_COMPONENT)) {
actRestoreDefaultUrl();
}
const auto url = QUrl::fromUserInput(urlLineEdit->text());
if (!url.isValid()) { if (!url.isValid()) {
QMessageBox::critical(this, tr("Error"), tr("The provided URL is not valid.")); QMessageBox::critical(this, tr("Error"), tr("The provided URL is not valid."));
return false; return false;
@ -342,23 +348,24 @@ bool LoadSetsPage::validatePage()
} }
#include <iostream> #include <iostream>
void LoadSetsPage::downloadSetsFile(QUrl url) void LoadSetsPage::downloadSetsFile(const QUrl &url)
{ {
wizard()->setCardSourceVersion("unknown"); wizard()->setCardSourceVersion("unknown");
QString urlString = url.toString(); const auto urlString = url.toString();
if (urlString == ALLSETS_URL || urlString == ALLSETS_URL_FALLBACK) { if (urlString == ALLSETS_URL || urlString == ALLSETS_URL_FALLBACK) {
QUrl versionUrl = QUrl::fromUserInput(MTGJSON_VERSION_URL); const auto versionUrl = QUrl::fromUserInput(MTGJSON_VERSION_URL);
QNetworkReply *versionReply = wizard()->nam->get(QNetworkRequest(versionUrl)); auto *versionReply = wizard()->nam->get(QNetworkRequest(versionUrl));
connect(versionReply, &QNetworkReply::finished, [this, versionReply]() { connect(versionReply, &QNetworkReply::finished, [this, versionReply]() {
if (versionReply->error() == QNetworkReply::NoError) { if (versionReply->error() == QNetworkReply::NoError) {
QByteArray jsonData = versionReply->readAll(); auto jsonData = versionReply->readAll();
QJsonParseError jsonError; QJsonParseError jsonError{};
QJsonDocument jsonResponse = QJsonDocument::fromJson(jsonData, &jsonError); auto jsonResponse = QJsonDocument::fromJson(jsonData, &jsonError);
if (jsonError.error == QJsonParseError::NoError) { if (jsonError.error == QJsonParseError::NoError) {
QVariantMap jsonMap = jsonResponse.toVariant().toMap(); const auto jsonMap = jsonResponse.toVariant().toMap();
QString versionString = jsonMap["version"].toString();
auto versionString = jsonMap.value("meta").toMap().value("version").toString();
if (versionString.isEmpty()) { if (versionString.isEmpty()) {
versionString = "unknown"; versionString = "unknown";
} }
@ -372,7 +379,7 @@ void LoadSetsPage::downloadSetsFile(QUrl url)
wizard()->setCardSourceUrl(url.toString()); wizard()->setCardSourceUrl(url.toString());
QNetworkReply *reply = wizard()->nam->get(QNetworkRequest(url)); auto *reply = wizard()->nam->get(QNetworkRequest(url));
connect(reply, SIGNAL(finished()), this, SLOT(actDownloadFinishedSetsFile())); connect(reply, SIGNAL(finished()), this, SLOT(actDownloadFinishedSetsFile()));
connect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(actDownloadProgressSetsFile(qint64, qint64))); connect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(actDownloadProgressSetsFile(qint64, qint64)));
@ -391,7 +398,7 @@ void LoadSetsPage::actDownloadFinishedSetsFile()
{ {
// check for a reply // check for a reply
auto *reply = dynamic_cast<QNetworkReply *>(sender()); auto *reply = dynamic_cast<QNetworkReply *>(sender());
QNetworkReply::NetworkError errorCode = reply->error(); auto errorCode = reply->error();
if (errorCode != QNetworkReply::NoError) { if (errorCode != QNetworkReply::NoError) {
QMessageBox::critical(this, tr("Error"), tr("Network error: %1.").arg(reply->errorString())); QMessageBox::critical(this, tr("Error"), tr("Network error: %1.").arg(reply->errorString()));
@ -402,9 +409,9 @@ void LoadSetsPage::actDownloadFinishedSetsFile()
return; return;
} }
int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); auto statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (statusCode == 301 || statusCode == 302) { if (statusCode == 301 || statusCode == 302) {
QUrl redirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); const auto redirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
qDebug() << "following redirect url:" << redirectUrl.toString(); qDebug() << "following redirect url:" << redirectUrl.toString();
downloadSetsFile(redirectUrl); downloadSetsFile(redirectUrl);
reply->deleteLater(); reply->deleteLater();
@ -414,7 +421,7 @@ void LoadSetsPage::actDownloadFinishedSetsFile()
progressLabel->hide(); progressLabel->hide();
progressBar->hide(); progressBar->hide();
// save allsets.json url, but only if the user customized it and download was successfull // save AllPrintings.json url, but only if the user customized it and download was successful
if (urlLineEdit->text() != QString(ALLSETS_URL)) { if (urlLineEdit->text() != QString(ALLSETS_URL)) {
wizard()->settings->setValue("allsetsurl", urlLineEdit->text()); wizard()->settings->setValue("allsetsurl", urlLineEdit->text());
} else { } else {

View file

@ -113,7 +113,7 @@ protected:
void initializePage() override; void initializePage() override;
bool validatePage() override; bool validatePage() override;
void readSetsFromByteArray(QByteArray data); void readSetsFromByteArray(QByteArray data);
void downloadSetsFile(QUrl url); void downloadSetsFile(const QUrl &url);
private: private:
QRadioButton *urlRadioButton; QRadioButton *urlRadioButton;

View file

@ -125,7 +125,7 @@ void SimpleDownloadFilePage::actDownloadFinished()
return; return;
} }
// save downlaoded file url, but only if the user customized it and download was successfull // save downloaded file url, but only if the user customized it and download was successful
if (urlLineEdit->text() != getDefaultUrl()) { if (urlLineEdit->text() != getDefaultUrl()) {
wizard()->settings->setValue(getCustomUrlSettingsKey(), urlLineEdit->text()); wizard()->settings->setValue(getCustomUrlSettingsKey(), urlLineEdit->text());
} else { } else {