NOISSUE connect twitch URL resolving to modpack resolving. works now.

This commit is contained in:
Petr Mrázek 2019-06-30 11:03:59 +02:00
parent f74e3db804
commit 63330bf111
6 changed files with 127 additions and 77 deletions

View File

@ -1,7 +1,9 @@
#include "FileResolvingTask.h" #include "FileResolvingTask.h"
#include "Json.h" #include "Json.h"
const char * metabase = "https://cursemeta.dries007.net"; namespace {
const char * metabase = "https://cursemeta.dries007.net";
}
Flame::FileResolvingTask::FileResolvingTask(Flame::Manifest& toProcess) Flame::FileResolvingTask::FileResolvingTask(Flame::Manifest& toProcess)
: m_toProcess(toProcess) : m_toProcess(toProcess)
@ -34,70 +36,14 @@ void Flame::FileResolvingTask::netJobFinished()
int index = 0; int index = 0;
for(auto & bytes: results) for(auto & bytes: results)
{ {
auto & out = m_toProcess.files[index];
try try
{ {
auto doc = Json::requireDocument(bytes); failed &= (!out.parseFromBytes(bytes));
auto obj = Json::requireObject(doc);
auto & out = m_toProcess.files[index];
// result code signifies true failure.
if(obj.contains("code"))
{
qCritical() << "Resolving of" << out.projectId << out.fileId << "failed because of a negative result:";
qCritical() << bytes;
failed = true;
continue;
}
out.fileName = Json::requireString(obj, "FileNameOnDisk");
QString rawUrl = Json::requireString(obj, "DownloadURL");
out.url = QUrl(rawUrl, QUrl::TolerantMode);
if(!out.url.isValid())
{
throw JSONValidationError(QString("Invalid URL: %1").arg(rawUrl));
}
// This is a piece of a Flame project JSON pulled out into the file metadata (here) for convenience
// It is also optional
QJsonObject projObj = Json::ensureObject(obj, "_Project", {});
if(!projObj.isEmpty())
{
QString strType = Json::ensureString(projObj, "PackageType", "mod").toLower();
if(strType == "singlefile")
{
out.type = File::Type::SingleFile;
}
else if(strType == "ctoc")
{
out.type = File::Type::Ctoc;
}
else if(strType == "cmod2")
{
out.type = File::Type::Cmod2;
}
else if(strType == "mod")
{
out.type = File::Type::Mod;
}
else if(strType == "folder")
{
out.type = File::Type::Folder;
}
else if(strType == "modpack")
{
out.type = File::Type::Modpack;
}
else
{
qCritical() << "Resolving of" << out.projectId << out.fileId << "failed because of unknown file type:" << strType;
out.type = File::Type::Unknown;
failed = true;
continue;
}
out.targetFolder = Json::ensureString(projObj, "Path", "mods");
}
out.resolved = true;
} }
catch (const JSONValidationError &e) catch (const JSONValidationError &e)
{ {
auto & out = m_toProcess.files[index];
qCritical() << "Resolving of" << out.projectId << out.fileId << "failed because of a parsing error:"; qCritical() << "Resolving of" << out.projectId << out.fileId << "failed because of a parsing error:";
qCritical() << e.cause(); qCritical() << e.cause();
qCritical() << "JSON:"; qCritical() << "JSON:";

View File

@ -64,3 +64,63 @@ void Flame::loadManifest(Flame::Manifest & m, const QString &filepath)
} }
loadManifestV1(m, obj); loadManifestV1(m, obj);
} }
bool Flame::File::parseFromBytes(const QByteArray& bytes)
{
auto doc = Json::requireDocument(bytes);
auto obj = Json::requireObject(doc);
// result code signifies true failure.
if(obj.contains("code"))
{
qCritical() << "Resolving of" << projectId << fileId << "failed because of a negative result:";
qCritical() << bytes;
return false;
}
fileName = Json::requireString(obj, "FileNameOnDisk");
QString rawUrl = Json::requireString(obj, "DownloadURL");
url = QUrl(rawUrl, QUrl::TolerantMode);
if(!url.isValid())
{
throw JSONValidationError(QString("Invalid URL: %1").arg(rawUrl));
}
// This is a piece of a Flame project JSON pulled out into the file metadata (here) for convenience
// It is also optional
QJsonObject projObj = Json::ensureObject(obj, "_Project", {});
if(!projObj.isEmpty())
{
QString strType = Json::ensureString(projObj, "PackageType", "mod").toLower();
if(strType == "singlefile")
{
type = File::Type::SingleFile;
}
else if(strType == "ctoc")
{
type = File::Type::Ctoc;
}
else if(strType == "cmod2")
{
type = File::Type::Cmod2;
}
else if(strType == "mod")
{
type = File::Type::Mod;
}
else if(strType == "folder")
{
type = File::Type::Folder;
}
else if(strType == "modpack")
{
type = File::Type::Modpack;
}
else
{
qCritical() << "Resolving of" << projectId << fileId << "failed because of unknown file type:" << strType;
type = File::Type::Unknown;
return false;
}
targetFolder = Json::ensureString(projObj, "Path", "mods");
}
resolved = true;
return true;
}

View File

@ -8,6 +8,9 @@ namespace Flame
{ {
struct File struct File
{ {
// NOTE: throws JSONValidationError
bool parseFromBytes(const QByteArray &bytes);
int projectId = 0; int projectId = 0;
int fileId = 0; int fileId = 0;
// NOTE: the opposite to 'optional'. This is at the time of writing unused. // NOTE: the opposite to 'optional'. This is at the time of writing unused.

View File

@ -1,11 +1,11 @@
#include "UrlResolvingTask.h" #include "UrlResolvingTask.h"
#include <QtXml> #include <QtXml>
#include <Json.h>
/*
namespace { namespace {
const char * metabase = "https://cursemeta.dries007.net"; const char * metabase = "https://cursemeta.dries007.net";
} }
*/
Flame::UrlResolvingTask::UrlResolvingTask(const QString& toProcess) Flame::UrlResolvingTask::UrlResolvingTask(const QString& toProcess)
: m_url(toProcess) : m_url(toProcess)
@ -13,12 +13,17 @@ Flame::UrlResolvingTask::UrlResolvingTask(const QString& toProcess)
} }
void Flame::UrlResolvingTask::executeTask() void Flame::UrlResolvingTask::executeTask()
{
resolveUrl();
}
void Flame::UrlResolvingTask::resolveUrl()
{ {
setStatus(tr("Resolving URL...")); setStatus(tr("Resolving URL..."));
setProgress(0, 1); setProgress(0, 1);
m_dljob.reset(new NetJob("URL resolver")); m_dljob.reset(new NetJob("URL resolver"));
weAreDigging = false; bool weAreDigging = false;
needle = QString(); needle = QString();
if(m_url.startsWith("https://")) { if(m_url.startsWith("https://")) {
@ -48,17 +53,12 @@ void Flame::UrlResolvingTask::executeTask()
} }
auto dl = Net::Download::makeByteArray(QUrl(m_url), &results); auto dl = Net::Download::makeByteArray(QUrl(m_url), &results);
m_dljob->addNetAction(dl); m_dljob->addNetAction(dl);
connect(m_dljob.get(), &NetJob::finished, this, &Flame::UrlResolvingTask::netJobFinished);
m_dljob->start();
}
void Flame::UrlResolvingTask::netJobFinished()
{
if(weAreDigging) { if(weAreDigging) {
processHTML(); connect(m_dljob.get(), &NetJob::finished, this, &Flame::UrlResolvingTask::processHTML);
} else { } else {
processCCIP(); connect(m_dljob.get(), &NetJob::finished, this, &Flame::UrlResolvingTask::processCCIP);
} }
m_dljob->start();
} }
void Flame::UrlResolvingTask::processHTML() void Flame::UrlResolvingTask::processHTML()
@ -83,7 +83,7 @@ void Flame::UrlResolvingTask::processHTML()
qDebug() << "Found needle: " << found; qDebug() << "Found needle: " << found;
// twitch://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download-client/2697088 // twitch://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download-client/2697088
m_url = found; m_url = found;
executeTask(); resolveUrl();
return; return;
} }
emitFailed(tr("Couldn't find the end of the needle in the haystack...")); emitFailed(tr("Couldn't find the end of the needle in the haystack..."));
@ -135,6 +135,36 @@ void Flame::UrlResolvingTask::processCCIP()
return; return;
} }
qDebug() << "Resolved" << m_url << "as" << m_result.projectId << "/" << m_result.fileId; qDebug() << "Resolved" << m_url << "as" << m_result.projectId << "/" << m_result.fileId;
emitSucceeded(); resolveIDs();
} }
void Flame::UrlResolvingTask::resolveIDs()
{
setStatus(tr("Resolving mod IDs..."));
m_dljob.reset(new NetJob("Mod id resolver"));
auto projectIdStr = QString::number(m_result.projectId);
auto fileIdStr = QString::number(m_result.fileId);
QString metaurl = QString("%1/%2/%3.json").arg(metabase, projectIdStr, fileIdStr);
auto dl = Net::Download::makeByteArray(QUrl(metaurl), &results);
m_dljob->addNetAction(dl);
connect(m_dljob.get(), &NetJob::finished, this, &Flame::UrlResolvingTask::processCursemeta);
m_dljob->start();
}
void Flame::UrlResolvingTask::processCursemeta()
{
try {
if(m_result.parseFromBytes(results)) {
emitSucceeded();
qDebug() << results;
return;
}
} catch (const JSONValidationError &e) {
qCritical() << "Resolving of" << m_result.projectId << m_result.fileId << "failed because of a parsing error:";
qCritical() << e.cause();
qCritical() << "JSON:";
qCritical() << results;
}
emitFailed(tr("Failed to resolve the modpack file."));
}

View File

@ -26,7 +26,11 @@ protected:
protected slots: protected slots:
void processCCIP(); void processCCIP();
void processHTML(); void processHTML();
void netJobFinished(); void processCursemeta();
private:
void resolveUrl();
void resolveIDs();
private: /* data */ private: /* data */
QString m_url; QString m_url;
@ -34,7 +38,6 @@ private: /* data */
Flame::File m_result; Flame::File m_result;
QByteArray results; QByteArray results;
NetJobPtr m_dljob; NetJobPtr m_dljob;
bool weAreDigging = false;
}; };
} }

View File

@ -3,6 +3,7 @@
#include "MultiMC.h" #include "MultiMC.h"
#include "dialogs/NewInstanceDialog.h" #include "dialogs/NewInstanceDialog.h"
#include <InstanceImportTask.h>
TwitchPage::TwitchPage(NewInstanceDialog* dialog, QWidget *parent) TwitchPage::TwitchPage(NewInstanceDialog* dialog, QWidget *parent)
: QWidget(parent), ui(new Ui::TwitchPage), dialog(dialog) : QWidget(parent), ui(new Ui::TwitchPage), dialog(dialog)
@ -42,6 +43,13 @@ void TwitchPage::checkDone()
{ {
auto result = m_modIdResolver->getResults(); auto result = m_modIdResolver->getResults();
auto formatted = QString("Project %1, File %2").arg(result.projectId).arg(result.fileId); auto formatted = QString("Project %1, File %2").arg(result.projectId).arg(result.fileId);
if(result.resolved && result.type == Flame::File::Type::Modpack) {
ui->twitchLabel->setText(formatted); ui->twitchLabel->setText(formatted);
QFileInfo fi(result.fileName);
dialog->setSuggestedPack(fi.completeBaseName(), new InstanceImportTask(result.url));
} else {
ui->twitchLabel->setPixmap(QPixmap(QString::fromUtf8(":/assets/deadglitch")));
dialog->setSuggestedPack();
}
m_modIdResolver.reset(); m_modIdResolver.reset();
} }