aboutsummaryrefslogtreecommitdiff
path: root/application/pages/modplatform/twitch
diff options
context:
space:
mode:
Diffstat (limited to 'application/pages/modplatform/twitch')
-rw-r--r--application/pages/modplatform/twitch/TwitchData.h38
-rw-r--r--application/pages/modplatform/twitch/TwitchModel.cpp312
-rw-r--r--application/pages/modplatform/twitch/TwitchModel.h76
-rw-r--r--application/pages/modplatform/twitch/TwitchPage.cpp111
-rw-r--r--application/pages/modplatform/twitch/TwitchPage.h77
-rw-r--r--application/pages/modplatform/twitch/TwitchPage.ui73
6 files changed, 0 insertions, 687 deletions
diff --git a/application/pages/modplatform/twitch/TwitchData.h b/application/pages/modplatform/twitch/TwitchData.h
deleted file mode 100644
index dd000b84..00000000
--- a/application/pages/modplatform/twitch/TwitchData.h
+++ /dev/null
@@ -1,38 +0,0 @@
-#pragma once
-
-#include <QString>
-#include <QList>
-
-namespace Twitch {
-
-struct ModpackAuthor {
- QString name;
- QString url;
-};
-
-struct ModpackFile {
- int addonId;
- int fileId;
- QString version;
- QString mcVersion;
- QString downloadUrl;
-};
-
-struct Modpack
-{
- bool broken = true;
- int addonId = 0;
-
- QString name;
- QString description;
- QList<ModpackAuthor> authors;
- QString mcVersion;
- QString logoName;
- QString logoUrl;
- QString websiteUrl;
-
- ModpackFile latestFile;
-};
-}
-
-Q_DECLARE_METATYPE(Twitch::Modpack)
diff --git a/application/pages/modplatform/twitch/TwitchModel.cpp b/application/pages/modplatform/twitch/TwitchModel.cpp
deleted file mode 100644
index 5c6c7858..00000000
--- a/application/pages/modplatform/twitch/TwitchModel.cpp
+++ /dev/null
@@ -1,312 +0,0 @@
-#include "TwitchModel.h"
-#include "MultiMC.h"
-
-#include <MMCStrings.h>
-#include <Version.h>
-
-#include <QtMath>
-#include <QLabel>
-
-#include <RWStorage.h>
-#include <Env.h>
-
-namespace Twitch {
-
-ListModel::ListModel(QObject *parent) : QAbstractListModel(parent)
-{
-}
-
-ListModel::~ListModel()
-{
-}
-
-int ListModel::rowCount(const QModelIndex &parent) const
-{
- return modpacks.size();
-}
-
-int ListModel::columnCount(const QModelIndex &parent) const
-{
- return 1;
-}
-
-QVariant ListModel::data(const QModelIndex &index, int role) const
-{
- int pos = index.row();
- if(pos >= modpacks.size() || pos < 0 || !index.isValid())
- {
- return QString("INVALID INDEX %1").arg(pos);
- }
-
- Modpack pack = modpacks.at(pos);
- if(role == Qt::DisplayRole)
- {
- return pack.name;
- }
- else if (role == Qt::ToolTipRole)
- {
- if(pack.description.length() > 100)
- {
- //some magic to prevent to long tooltips and replace html linebreaks
- QString edit = pack.description.left(97);
- edit = edit.left(edit.lastIndexOf("<br>")).left(edit.lastIndexOf(" ")).append("...");
- return edit;
-
- }
- return pack.description;
- }
- else if(role == Qt::DecorationRole)
- {
- if(m_logoMap.contains(pack.logoName))
- {
- return (m_logoMap.value(pack.logoName));
- }
- QIcon icon = MMC->getThemedIcon("screenshot-placeholder");
- ((ListModel *)this)->requestLogo(pack.logoName, pack.logoUrl);
- return icon;
- }
- else if(role == Qt::UserRole)
- {
- QVariant v;
- v.setValue(pack);
- return v;
- }
-
- return QVariant();
-}
-
-void ListModel::logoLoaded(QString logo, QIcon out)
-{
- m_loadingLogos.removeAll(logo);
- m_logoMap.insert(logo, out);
- for(int i = 0; i < modpacks.size(); i++) {
- if(modpacks[i].logoName == logo) {
- emit dataChanged(createIndex(i, 0), createIndex(i, 0), {Qt::DecorationRole});
- }
- }
-}
-
-void ListModel::logoFailed(QString logo)
-{
- m_failedLogos.append(logo);
- m_loadingLogos.removeAll(logo);
-}
-
-void ListModel::requestLogo(QString logo, QString url)
-{
- if(m_loadingLogos.contains(logo) || m_failedLogos.contains(logo))
- {
- return;
- }
-
- MetaEntryPtr entry = ENV.metacache()->resolveEntry("TwitchPacks", QString("logos/%1").arg(logo.section(".", 0, 0)));
- NetJob *job = new NetJob(QString("Twitch Icon Download %1").arg(logo));
- job->addNetAction(Net::Download::makeCached(QUrl(url), entry));
-
- auto fullPath = entry->getFullPath();
- QObject::connect(job, &NetJob::succeeded, this, [this, logo, fullPath]
- {
- emit logoLoaded(logo, QIcon(fullPath));
- if(waitingCallbacks.contains(logo))
- {
- waitingCallbacks.value(logo)(fullPath);
- }
- });
-
- QObject::connect(job, &NetJob::failed, this, [this, logo]
- {
- emit logoFailed(logo);
- });
-
- job->start();
-
- m_loadingLogos.append(logo);
-}
-
-void ListModel::getLogo(const QString &logo, const QString &logoUrl, LogoCallback callback)
-{
- if(m_logoMap.contains(logo))
- {
- callback(ENV.metacache()->resolveEntry("TwitchPacks", QString("logos/%1").arg(logo.section(".", 0, 0)))->getFullPath());
- }
- else
- {
- requestLogo(logo, logoUrl);
- }
-}
-
-Qt::ItemFlags ListModel::flags(const QModelIndex &index) const
-{
- return QAbstractListModel::flags(index);
-}
-
-bool ListModel::canFetchMore(const QModelIndex& parent) const
-{
- return searchState == CanPossiblyFetchMore;
-}
-
-void ListModel::fetchMore(const QModelIndex& parent)
-{
- if (parent.isValid())
- return;
- if(nextSearchOffset == 0) {
- qWarning() << "fetchMore with 0 offset is wrong...";
- return;
- }
- performPaginatedSearch();
-}
-
-void ListModel::performPaginatedSearch()
-{
- NetJob *netJob = new NetJob("Twitch::Search");
- auto searchUrl = QString(
- "https://addons-ecs.forgesvc.net/api/v2/addon/search?"
- "categoryId=0&"
- "gameId=432&"
- //"gameVersion=1.12.2&"
- "index=%1&"
- "pageSize=25&"
- "searchFilter=%2&"
- "sectionId=4471&"
- "sort=0"
- ).arg(nextSearchOffset).arg(currentSearchTerm);
- netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &response));
- jobPtr = netJob;
- jobPtr->start();
- QObject::connect(netJob, &NetJob::succeeded, this, &ListModel::searchRequestFinished);
- QObject::connect(netJob, &NetJob::failed, this, &ListModel::searchRequestFailed);
-}
-
-void ListModel::searchWithTerm(const QString& term)
-{
- if(currentSearchTerm == term) {
- return;
- }
- currentSearchTerm = term;
- if(jobPtr) {
- jobPtr->abort();
- searchState = ResetRequested;
- return;
- }
- else {
- beginResetModel();
- modpacks.clear();
- endResetModel();
- searchState = None;
- }
- nextSearchOffset = 0;
- performPaginatedSearch();
-}
-
-void Twitch::ListModel::searchRequestFinished()
-{
- jobPtr.reset();
-
- QJsonParseError parse_error;
- QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error);
- if(parse_error.error != QJsonParseError::NoError) {
- qWarning() << "Error while parsing JSON response from Twitch at " << parse_error.offset << " reason: " << parse_error.errorString();
- qWarning() << response;
- return;
- }
-
- QList<Modpack> newList;
- auto objs = doc.array();
- for(auto projectIter: objs) {
- Modpack pack;
- auto project = projectIter.toObject();
- pack.addonId = project.value("id").toInt(0);
- if (pack.addonId == 0) {
- qWarning() << "Pack without an ID, skipping: " << pack.name;
- continue;
- }
- pack.name = project.value("name").toString();
- pack.websiteUrl = project.value("websiteUrl").toString();
- pack.description = project.value("summary").toString();
- bool thumbnailFound = false;
- auto attachments = project.value("attachments").toArray();
- for(auto attachmentIter: attachments) {
- auto attachment = attachmentIter.toObject();
- bool isDefault = attachment.value("isDefault").toBool(false);
- if(isDefault) {
- thumbnailFound = true;
- pack.logoName = attachment.value("title").toString();
- pack.logoUrl = attachment.value("thumbnailUrl").toString();
- break;
- }
- }
- if(!thumbnailFound) {
- qWarning() << "Pack without an icon, skipping: " << pack.name;
- continue;
- }
- auto authors = project.value("authors").toArray();
- for(auto authorIter: authors) {
- auto author = authorIter.toObject();
- ModpackAuthor packAuthor;
- packAuthor.name = author.value("name").toString();
- packAuthor.url = author.value("url").toString();
- pack.authors.append(packAuthor);
- }
- int defaultFileId = project.value("defaultFileId").toInt(0);
- if(defaultFileId == 0) {
- qWarning() << "Pack without default file, skipping: " << pack.name;
- continue;
- }
- bool found = false;
- auto files = project.value("latestFiles").toArray();
- for(auto fileIter: files) {
- auto file = fileIter.toObject();
- int id = file.value("id").toInt(0);
- // NOTE: for now, ignore everything that's not the default...
- if(id != defaultFileId) {
- continue;
- }
- pack.latestFile.addonId = pack.addonId;
- pack.latestFile.fileId = id;
- // FIXME: what to do when there's more than one, or there's no version?
- auto versionArray = file.value("gameVersion").toArray();
- if(versionArray.size() != 1) {
- continue;
- }
- pack.latestFile.mcVersion = versionArray[0].toString();
- pack.latestFile.version = file.value("displayName").toString();
- pack.latestFile.downloadUrl = file.value("downloadUrl").toString();
- found = true;
- break;
- }
- if(!found) {
- qWarning() << "Pack with no good file, skipping: " << pack.name;
- continue;
- }
- pack.broken = false;
- newList.append(pack);
- }
- if(objs.size() < 25) {
- searchState = Finished;
- } else {
- nextSearchOffset += 25;
- searchState = CanPossiblyFetchMore;
- }
- beginInsertRows(QModelIndex(), modpacks.size(), modpacks.size() + newList.size() - 1);
- modpacks.append(newList);
- endInsertRows();
-}
-
-void Twitch::ListModel::searchRequestFailed(QString reason)
-{
- jobPtr.reset();
-
- if(searchState == ResetRequested) {
- beginResetModel();
- modpacks.clear();
- endResetModel();
-
- nextSearchOffset = 0;
- performPaginatedSearch();
- } else {
- searchState = Finished;
- }
-}
-
-}
-
diff --git a/application/pages/modplatform/twitch/TwitchModel.h b/application/pages/modplatform/twitch/TwitchModel.h
deleted file mode 100644
index ad355c64..00000000
--- a/application/pages/modplatform/twitch/TwitchModel.h
+++ /dev/null
@@ -1,76 +0,0 @@
-#pragma once
-
-#include <modplatform/legacy_ftb/PackHelpers.h>
-#include <RWStorage.h>
-
-#include <QAbstractListModel>
-#include <QSortFilterProxyModel>
-#include <QThreadPool>
-#include <QIcon>
-#include <QStyledItemDelegate>
-#include <QList>
-#include <QString>
-#include <QStringList>
-#include <QMetaType>
-
-#include <functional>
-#include <net/NetJob.h>
-
-#include "TwitchData.h"
-
-namespace Twitch {
-
-
-typedef QMap<QString, QIcon> LogoMap;
-typedef std::function<void(QString)> LogoCallback;
-
-class ListModel : public QAbstractListModel
-{
- Q_OBJECT
-
-public:
- ListModel(QObject *parent);
- virtual ~ListModel();
-
- int rowCount(const QModelIndex &parent) const override;
- int columnCount(const QModelIndex &parent) const override;
- QVariant data(const QModelIndex &index, int role) const override;
- Qt::ItemFlags flags(const QModelIndex &index) const override;
- bool canFetchMore(const QModelIndex & parent) const override;
- void fetchMore(const QModelIndex & parent) override;
-
- void getLogo(const QString &logo, const QString &logoUrl, LogoCallback callback);
- void searchWithTerm(const QString & term);
-
-private slots:
- void performPaginatedSearch();
-
- void logoFailed(QString logo);
- void logoLoaded(QString logo, QIcon out);
-
- void searchRequestFinished();
- void searchRequestFailed(QString reason);
-
-private:
- void requestLogo(QString file, QString url);
-
-private:
- QList<Modpack> modpacks;
- QStringList m_failedLogos;
- QStringList m_loadingLogos;
- LogoMap m_logoMap;
- QMap<QString, LogoCallback> waitingCallbacks;
-
- QString currentSearchTerm;
- int nextSearchOffset = 0;
- enum SearchState {
- None,
- CanPossiblyFetchMore,
- ResetRequested,
- Finished
- } searchState = None;
- NetJobPtr jobPtr;
- QByteArray response;
-};
-
-}
diff --git a/application/pages/modplatform/twitch/TwitchPage.cpp b/application/pages/modplatform/twitch/TwitchPage.cpp
deleted file mode 100644
index 1e9f9dbb..00000000
--- a/application/pages/modplatform/twitch/TwitchPage.cpp
+++ /dev/null
@@ -1,111 +0,0 @@
-#include "TwitchPage.h"
-#include "ui_TwitchPage.h"
-
-#include "MultiMC.h"
-#include "dialogs/NewInstanceDialog.h"
-#include <InstanceImportTask.h>
-#include "TwitchModel.h"
-#include <QKeyEvent>
-
-TwitchPage::TwitchPage(NewInstanceDialog* dialog, QWidget *parent)
- : QWidget(parent), ui(new Ui::TwitchPage), dialog(dialog)
-{
- ui->setupUi(this);
- connect(ui->searchButton, &QPushButton::clicked, this, &TwitchPage::triggerSearch);
- ui->searchEdit->installEventFilter(this);
- model = new Twitch::ListModel(this);
- ui->packView->setModel(model);
- connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &TwitchPage::onSelectionChanged);
-}
-
-TwitchPage::~TwitchPage()
-{
- delete ui;
-}
-
-bool TwitchPage::eventFilter(QObject* watched, QEvent* event)
-{
- if (watched == ui->searchEdit && event->type() == QEvent::KeyPress) {
- QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
- if (keyEvent->key() == Qt::Key_Return) {
- triggerSearch();
- keyEvent->accept();
- return true;
- }
- }
- return QWidget::eventFilter(watched, event);
-}
-
-bool TwitchPage::shouldDisplay() const
-{
- return true;
-}
-
-void TwitchPage::openedImpl()
-{
- suggestCurrent();
-}
-
-void TwitchPage::triggerSearch()
-{
- model->searchWithTerm(ui->searchEdit->text());
-}
-
-void TwitchPage::onSelectionChanged(QModelIndex first, QModelIndex second)
-{
- if(!first.isValid())
- {
- if(isOpened)
- {
- dialog->setSuggestedPack();
- }
- ui->frame->clear();
- return;
- }
-
- current = model->data(first, Qt::UserRole).value<Twitch::Modpack>();
- QString text = "";
- QString name = current.name;
-
- if (current.websiteUrl.isEmpty())
- text = name;
- else
- text = "<a href=\"" + current.websiteUrl + "\">" + name + "</a>";
- if (!current.authors.empty()) {
- auto authorToStr = [](Twitch::ModpackAuthor & author) {
- if(author.url.isEmpty()) {
- return author.name;
- }
- return QString("<a href=\"%1\">%2</a>").arg(author.url, author.name);
- };
- QStringList authorStrs;
- for(auto & author: current.authors) {
- authorStrs.push_back(authorToStr(author));
- }
- text += tr(" by ") + authorStrs.join(", ");
- }
-
- ui->frame->setModText(text);
- ui->frame->setModDescription(current.description);
- suggestCurrent();
-}
-
-void TwitchPage::suggestCurrent()
-{
- if(!isOpened)
- {
- return;
- }
- if(current.broken)
- {
- dialog->setSuggestedPack();
- }
-
- dialog->setSuggestedPack(current.name, new InstanceImportTask(current.latestFile.downloadUrl));
- QString editedLogoName;
- editedLogoName = "twitch_" + current.logoName.section(".", 0, 0);
- model->getLogo(current.logoName, current.logoUrl, [this, editedLogoName](QString logo)
- {
- dialog->setSuggestedIconFromFile(logo, editedLogoName);
- });
-}
diff --git a/application/pages/modplatform/twitch/TwitchPage.h b/application/pages/modplatform/twitch/TwitchPage.h
deleted file mode 100644
index 093900ff..00000000
--- a/application/pages/modplatform/twitch/TwitchPage.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QWidget>
-
-#include "pages/BasePage.h"
-#include <MultiMC.h>
-#include "tasks/Task.h"
-#include "TwitchData.h"
-
-namespace Ui
-{
-class TwitchPage;
-}
-
-class NewInstanceDialog;
-
-namespace Twitch {
- class ListModel;
-}
-
-class TwitchPage : public QWidget, public BasePage
-{
- Q_OBJECT
-
-public:
- explicit TwitchPage(NewInstanceDialog* dialog, QWidget *parent = 0);
- virtual ~TwitchPage();
- virtual QString displayName() const override
- {
- return tr("Twitch");
- }
- virtual QIcon icon() const override
- {
- return MMC->getThemedIcon("twitch");
- }
- virtual QString id() const override
- {
- return "twitch";
- }
- virtual QString helpPage() const override
- {
- return "Twitch-platform";
- }
- virtual bool shouldDisplay() const override;
-
- void openedImpl() override;
-
- bool eventFilter(QObject * watched, QEvent * event) override;
-
-private:
- void suggestCurrent();
-
-private slots:
- void triggerSearch();
- void onSelectionChanged(QModelIndex first, QModelIndex second);
-
-private:
- Ui::TwitchPage *ui = nullptr;
- NewInstanceDialog* dialog = nullptr;
- Twitch::ListModel* model = nullptr;
- Twitch::Modpack current;
-};
diff --git a/application/pages/modplatform/twitch/TwitchPage.ui b/application/pages/modplatform/twitch/TwitchPage.ui
deleted file mode 100644
index c78d8ce0..00000000
--- a/application/pages/modplatform/twitch/TwitchPage.ui
+++ /dev/null
@@ -1,73 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>TwitchPage</class>
- <widget class="QWidget" name="TwitchPage">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>875</width>
- <height>745</height>
- </rect>
- </property>
- <layout class="QGridLayout" name="gridLayout">
- <item row="0" column="0">
- <widget class="QLineEdit" name="searchEdit"/>
- </item>
- <item row="0" column="1">
- <widget class="QPushButton" name="searchButton">
- <property name="text">
- <string>Search</string>
- </property>
- </widget>
- </item>
- <item row="2" column="0" colspan="2">
- <widget class="MCModInfoFrame" name="frame">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="frameShape">
- <enum>QFrame::StyledPanel</enum>
- </property>
- <property name="frameShadow">
- <enum>QFrame::Raised</enum>
- </property>
- </widget>
- </item>
- <item row="1" column="0" colspan="2">
- <widget class="QListView" name="packView">
- <property name="horizontalScrollBarPolicy">
- <enum>Qt::ScrollBarAlwaysOff</enum>
- </property>
- <property name="alternatingRowColors">
- <bool>true</bool>
- </property>
- <property name="iconSize">
- <size>
- <width>48</width>
- <height>48</height>
- </size>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- <customwidgets>
- <customwidget>
- <class>MCModInfoFrame</class>
- <extends>QFrame</extends>
- <header>widgets/MCModInfoFrame.h</header>
- <container>1</container>
- </customwidget>
- </customwidgets>
- <tabstops>
- <tabstop>searchEdit</tabstop>
- <tabstop>searchButton</tabstop>
- <tabstop>packView</tabstop>
- </tabstops>
- <resources/>
- <connections/>
-</ui>