From fde43c993e0a143b9dddcaff2190c91f4e5e3cb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Thu, 27 Jun 2019 03:20:11 +0200 Subject: NOISSUE add silly twitch URL and CCIP resolving page to 'add instance' It needs a few more steps and it will handle all kinds of twitch packs. --- api/logic/CMakeLists.txt | 2 + api/logic/modplatform/flame/UrlResolvingTask.cpp | 140 +++++++++++++++++++++++ api/logic/modplatform/flame/UrlResolvingTask.h | 40 +++++++ application/pages/modplatform/ImportPage.cpp | 5 + application/pages/modplatform/TwitchPage.cpp | 23 +++- application/pages/modplatform/TwitchPage.h | 6 + application/pages/modplatform/TwitchPage.ui | 33 +++++- 7 files changed, 245 insertions(+), 4 deletions(-) create mode 100644 api/logic/modplatform/flame/UrlResolvingTask.cpp create mode 100644 api/logic/modplatform/flame/UrlResolvingTask.h diff --git a/api/logic/CMakeLists.txt b/api/logic/CMakeLists.txt index 94da2682..1f795556 100644 --- a/api/logic/CMakeLists.txt +++ b/api/logic/CMakeLists.txt @@ -443,6 +443,8 @@ set(FLAME_SOURCES modplatform/flame/PackManifest.cpp modplatform/flame/FileResolvingTask.h modplatform/flame/FileResolvingTask.cpp + modplatform/flame/UrlResolvingTask.h + modplatform/flame/UrlResolvingTask.cpp ) add_unit_test(Index diff --git a/api/logic/modplatform/flame/UrlResolvingTask.cpp b/api/logic/modplatform/flame/UrlResolvingTask.cpp new file mode 100644 index 00000000..068b6a34 --- /dev/null +++ b/api/logic/modplatform/flame/UrlResolvingTask.cpp @@ -0,0 +1,140 @@ +#include "UrlResolvingTask.h" +#include + +/* +namespace { +const char * metabase = "https://cursemeta.dries007.net"; +} +*/ + +Flame::UrlResolvingTask::UrlResolvingTask(const QString& toProcess) + : m_url(toProcess) +{ +} + +void Flame::UrlResolvingTask::executeTask() +{ + setStatus(tr("Resolving URL...")); + setProgress(0, 1); + m_dljob.reset(new NetJob("URL resolver")); + + weAreDigging = false; + needle = QString(); + + if(m_url.startsWith("https://")) { + if(m_url.endsWith("?client=y")) { + // https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download?client=y + // https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download/2697088?client=y + m_url.chop(9); + // https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download + // https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download/2697088 + } + if(m_url.endsWith("/download")) { + // https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download -> need to dig inside html... + weAreDigging = true; + needle = m_url; + needle.replace("https://", "twitch://"); + needle.replace("/download", "/download-client/"); + m_url.append("?client=y"); + } else if (m_url.contains("/download/")) { + // https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download/2697088 + m_url.replace("/download/", "/download-client/"); + } + } + else if(m_url.startsWith("twitch://")) { + // twitch://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download-client/2697088 + m_url.replace(0, 9, "https://"); + // https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download-client/2697088 + } + auto dl = Net::Download::makeByteArray(QUrl(m_url), &results); + m_dljob->addNetAction(dl); + connect(m_dljob.get(), &NetJob::finished, this, &Flame::UrlResolvingTask::netJobFinished); + m_dljob->start(); +} + +void Flame::UrlResolvingTask::netJobFinished() +{ + if(weAreDigging) { + processHTML(); + } else { + processCCIP(); + } +} + +void Flame::UrlResolvingTask::processHTML() +{ + QString htmlDoc = QString::fromUtf8(results); + auto index = htmlDoc.indexOf(needle); + if(index < 0) { + emitFailed(tr("Couldn't find the needle in the haystack...")); + return; + } + auto indexStart = index; + int indexEnd = -1; + while((index + 1) < htmlDoc.size() && htmlDoc[index] != '"') { + index ++; + if(htmlDoc[index] == '"') { + indexEnd = index; + break; + } + } + if(indexEnd > 0) { + QString found = htmlDoc.mid(indexStart, indexEnd - indexStart); + qDebug() << "Found needle: " << found; + // twitch://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download-client/2697088 + m_url = found; + executeTask(); + return; + } + emitFailed(tr("Couldn't find the end of the needle in the haystack...")); + return; +} + +void Flame::UrlResolvingTask::processCCIP() +{ + QDomDocument doc; + if (!doc.setContent(results)) { + qDebug() << results; + emitFailed(tr("Resolving failed.")); + return; + } + auto packageNode = doc.namedItem("package"); + if(!packageNode.isElement()) { + emitFailed(tr("Resolving failed: missing package root element.")); + return; + } + auto projectNode = packageNode.namedItem("project"); + if(!projectNode.isElement()) { + emitFailed(tr("Resolving failed: missing project element.")); + return; + } + auto attribs = projectNode.attributes(); + + auto projectIdNode = attribs.namedItem("id"); + if(!projectIdNode.isAttr()) { + emitFailed(tr("Resolving failed: missing id attribute.")); + return; + } + auto fileIdNode = attribs.namedItem("file"); + if(!fileIdNode.isAttr()) { + emitFailed(tr("Resolving failed: missing file attribute.")); + return; + } + + auto projectId = projectIdNode.nodeValue(); + auto fileId = fileIdNode.nodeValue(); + bool success = true; + m_result.projectId = projectId.toInt(&success); + if(!success) { + emitFailed(tr("Failed to resove projectId as a number.")); + return; + } + m_result.fileId = fileId.toInt(&success); + if(!success) { + emitFailed(tr("Failed to resove fileId as a number.")); + return; + } + qDebug() << "Resolved" << m_url << "as" << m_result.projectId << "/" << m_result.fileId; + emitSucceeded(); +} + diff --git a/api/logic/modplatform/flame/UrlResolvingTask.h b/api/logic/modplatform/flame/UrlResolvingTask.h new file mode 100644 index 00000000..72f3dce1 --- /dev/null +++ b/api/logic/modplatform/flame/UrlResolvingTask.h @@ -0,0 +1,40 @@ +#pragma once + +#include "tasks/Task.h" +#include "net/NetJob.h" +#include "PackManifest.h" + +#include "multimc_logic_export.h" + +namespace Flame +{ +class MULTIMC_LOGIC_EXPORT UrlResolvingTask : public Task +{ + Q_OBJECT +public: + explicit UrlResolvingTask(const QString &toProcess); + virtual ~UrlResolvingTask() {}; + + const Flame::File &getResults() const + { + return m_result; + } + +protected: + virtual void executeTask() override; + +protected slots: + void processCCIP(); + void processHTML(); + void netJobFinished(); + +private: /* data */ + QString m_url; + QString needle; + Flame::File m_result; + QByteArray results; + NetJobPtr m_dljob; + bool weAreDigging = false; +}; +} + diff --git a/application/pages/modplatform/ImportPage.cpp b/application/pages/modplatform/ImportPage.cpp index 3cd7c2cf..3910dfda 100644 --- a/application/pages/modplatform/ImportPage.cpp +++ b/application/pages/modplatform/ImportPage.cpp @@ -75,6 +75,11 @@ void ImportPage::updateState() } else { + if(input.endsWith("?client=y")) { + input.chop(9); + input.append("/file"); + url = QUrl::fromUserInput(input); + } // hook, line and sinker. QFileInfo fi(url.fileName()); dialog->setSuggestedPack(fi.completeBaseName(), new InstanceImportTask(url)); diff --git a/application/pages/modplatform/TwitchPage.cpp b/application/pages/modplatform/TwitchPage.cpp index a984c01c..2f138b94 100644 --- a/application/pages/modplatform/TwitchPage.cpp +++ b/application/pages/modplatform/TwitchPage.cpp @@ -8,6 +8,7 @@ TwitchPage::TwitchPage(NewInstanceDialog* dialog, QWidget *parent) : QWidget(parent), ui(new Ui::TwitchPage), dialog(dialog) { ui->setupUi(this); + connect(ui->checkButton, &QPushButton::clicked, this, &TwitchPage::triggerCheck); } TwitchPage::~TwitchPage() @@ -17,10 +18,30 @@ TwitchPage::~TwitchPage() bool TwitchPage::shouldDisplay() const { - return false; + return true; } void TwitchPage::openedImpl() { dialog->setSuggestedPack(); } + +void TwitchPage::triggerCheck(bool) +{ + if(m_modIdResolver) { + qDebug() << "Click!"; + return; + } + auto task = new Flame::UrlResolvingTask(ui->lineEdit->text()); + connect(task, &Task::finished, this, &TwitchPage::checkDone); + m_modIdResolver.reset(task); + task->start(); +} + +void TwitchPage::checkDone() +{ + auto result = m_modIdResolver->getResults(); + auto formatted = QString("Project %1, File %2").arg(result.projectId).arg(result.fileId); + ui->twitchLabel->setText(formatted); + m_modIdResolver.reset(); +} diff --git a/application/pages/modplatform/TwitchPage.h b/application/pages/modplatform/TwitchPage.h index 5d959962..6d5146cc 100644 --- a/application/pages/modplatform/TwitchPage.h +++ b/application/pages/modplatform/TwitchPage.h @@ -20,6 +20,7 @@ #include "pages/BasePage.h" #include #include "tasks/Task.h" +#include "modplatform/flame/UrlResolvingTask.h" namespace Ui { @@ -55,7 +56,12 @@ public: void openedImpl() override; +private slots: + void triggerCheck(bool checked); + void checkDone(); + private: Ui::TwitchPage *ui = nullptr; NewInstanceDialog* dialog = nullptr; + shared_qobject_ptr m_modIdResolver; }; diff --git a/application/pages/modplatform/TwitchPage.ui b/application/pages/modplatform/TwitchPage.ui index 0930f541..0db2484d 100644 --- a/application/pages/modplatform/TwitchPage.ui +++ b/application/pages/modplatform/TwitchPage.ui @@ -10,9 +10,25 @@ 405 - - - + + + + + + + + Twitch URL: + + + + + + + + 0 + 0 + + 40 @@ -26,8 +42,19 @@ + + + + Check + + + + + lineEdit + checkButton + -- cgit v1.2.3