add special modrinth header

Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
This commit is contained in:
Trial97 2026-05-07 13:29:35 +03:00
parent 18924e43da
commit ca721f9d67
18 changed files with 127 additions and 27 deletions

View file

@ -19,20 +19,49 @@
#include "ResourceDownloadTask.h"
#include <utility>
#include "Application.h"
#include "FileSystem.h"
#include "minecraft/MinecraftInstance.h"
#include "minecraft/PackProfile.h"
#include "minecraft/mod/ResourceFolderModel.h"
#include "minecraft/mod/ShaderPackFolderModel.h"
#include "modplatform/ModIndex.h"
#include "modplatform/helpers/HashUtils.h"
#include "net/ApiDownload.h"
#include "net/ChecksumValidator.h"
namespace {
Net::ModrinthDownloadMeta createModrinthMeta(BaseInstance* instance, QString reason)
{
auto* mcInstance = dynamic_cast<MinecraftInstance*>(instance);
if (!mcInstance) {
return {};
}
auto* profile = mcInstance->getPackProfile();
if (!profile) {
return {};
}
auto loaders = profile->getModLoadersList();
return {
.reason = std::move(reason),
.gameVersion = profile->getComponentVersion("net.minecraft"),
.loader = !loaders.isEmpty() ? ModPlatform::getModLoaderAsString(loaders.first()) : "",
};
}
} // namespace
ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack,
ModPlatform::IndexedVersion version,
ResourceFolderModel* packs,
bool is_indexed)
bool is_indexed,
QString downloadReason)
: m_pack(std::move(pack)), m_pack_version(std::move(version)), m_pack_model(packs)
{
if (is_indexed) {
@ -45,7 +74,9 @@ ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack,
m_filesNetJob.reset(new NetJob(tr("Resource download"), APPLICATION->network()));
m_filesNetJob->setStatus(tr("Downloading resource:\n%1").arg(m_pack_version.downloadUrl));
auto action = Net::ApiDownload::makeFile(m_pack_version.downloadUrl, m_pack_model->dir().absoluteFilePath(getFilename()));
auto action = Net::ApiDownload::makeFile(m_pack_version.downloadUrl, m_pack_model->dir().absoluteFilePath(getFilename()),
Net::Download::Option::NoOptions,
createModrinthMeta(m_pack_model->instance(), std::move(downloadReason)));
if (!m_pack_version.hash_type.isEmpty() && !m_pack_version.hash.isEmpty()) {
switch (Hashing::algorithmFromString(m_pack_version.hash_type)) {
case Hashing::Algorithm::Md4:

View file

@ -20,6 +20,7 @@
#pragma once
#include "net/NetJob.h"
#include "net/ApiHeaderProxy.h"
#include "tasks/SequentialTask.h"
#include "minecraft/mod/tasks/LocalResourceUpdateTask.h"
@ -33,7 +34,8 @@ class ResourceDownloadTask : public SequentialTask {
explicit ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack,
ModPlatform::IndexedVersion version,
ResourceFolderModel* packs,
bool is_indexed = true);
bool is_indexed = true,
QString downloadReason = "standalone");
const QString& getFilename() const { return m_pack_version.fileName; }
const QVariant& getVersionID() const { return m_pack_version.fileId; }
const ModPlatform::IndexedVersion& getVersion() const { return m_pack_version; }

View file

@ -183,6 +183,7 @@ class ResourceFolderModel : public QAbstractListModel {
};
QString instDirPath() const;
BaseInstance* instance() const { return m_instance; }
signals:
void updateFinished();

View file

@ -16,7 +16,10 @@
#include "net/ChecksumValidator.h"
#include "net/ApiDownload.h"
#include "net/ApiHeaderProxy.h"
#include "net/NetJob.h"
#include "modplatform/ModIndex.h"
#include "settings/INISettingsObject.h"
#include "ui/dialogs/CustomMessageBox.h"
@ -256,15 +259,38 @@ std::unique_ptr<MinecraftInstance> ModrinthCreationTask::createInstance()
return nullptr;
}
qDebug() << "Will try to download" << file.downloads.front() << "to" << file_path;
auto dl = Net::ApiDownload::makeFile(file.downloads.dequeue(), file_path);
QString loader;
if (m_instance.has_value()) {
auto* mcInstance = dynamic_cast<MinecraftInstance*>(m_instance.value());
if (mcInstance) {
auto* profile = mcInstance->getPackProfile();
if (profile) {
auto loaders = profile->getModLoadersList();
if (!loaders.isEmpty()) {
loader = ModPlatform::getModLoaderAsString(loaders.first());
}
}
}
}
Net::ModrinthDownloadMeta meta{
.reason = "modpack",
.gameVersion = m_minecraft_version,
.loader = loader,
};
QUrl downloadUrl = file.downloads.dequeue();
auto dl = Net::ApiDownload::makeFile(downloadUrl, file_path, Net::Download::Option::NoOptions, meta);
dl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash));
downloadMods->addNetAction(dl);
if (!file.downloads.empty()) {
// FIXME: This really needs to be put into a ConcurrentTask of
// MultipleOptionsTask's , once those exist :)
auto param = dl.toWeakRef();
connect(dl.get(), &Task::failed, [&file, file_path, param, downloadMods] {
auto ndl = Net::ApiDownload::makeFile(file.downloads.dequeue(), file_path);
connect(dl.get(), &Task::failed, [&file, file_path, param, downloadMods, meta] {
QUrl fallbackUrl = file.downloads.dequeue();
auto ndl = Net::ApiDownload::makeFile(fallbackUrl, file_path, Net::Download::Option::NoOptions, meta);
ndl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash));
downloadMods->addNetAction(ndl);
if (auto shared = param.lock())

View file

@ -36,10 +36,10 @@ std::pair<Download::Ptr, QByteArray*> ApiDownload::makeByteArray(QUrl url, Downl
return { dl, response };
}
Download::Ptr ApiDownload::makeFile(QUrl url, QString path, Download::Options options)
Download::Ptr ApiDownload::makeFile(QUrl url, QString path, Download::Options options, ModrinthDownloadMeta meta)
{
auto dl = Download::makeFile(url, path, options);
dl->addHeaderProxy(std::make_unique<ApiHeaderProxy>());
dl->addHeaderProxy(std::make_unique<ApiHeaderProxy>(std::move(meta)));
return dl;
}

View file

@ -20,13 +20,15 @@
#pragma once
#include "Download.h"
#include "net/ApiHeaderProxy.h"
namespace Net {
namespace ApiDownload {
Download::Ptr makeCached(QUrl url, MetaEntryPtr entry, Download::Options options = Download::Option::NoOptions);
std::pair<Download::Ptr, QByteArray*> makeByteArray(QUrl url, Download::Options options = Download::Option::NoOptions);
Download::Ptr makeFile(QUrl url, QString path, Download::Options options = Download::Option::NoOptions);
Download::Ptr makeFile(QUrl url, QString path, Download::Options options = Download::Option::NoOptions,
ModrinthDownloadMeta meta = ModrinthDownloadMeta());
}; // namespace ApiDownload
} // namespace Net

View file

@ -23,11 +23,35 @@
#include "BuildConfig.h"
#include "net/HeaderProxy.h"
#include <QJsonDocument>
#include <QJsonObject>
namespace Net {
struct ModrinthDownloadMeta {
QString reason;
QString gameVersion;
QString loader;
bool isEmpty() const { return reason.isEmpty(); }
QByteArray toJson() const
{
QJsonObject obj;
if (!reason.isEmpty())
obj["reason"] = reason;
if (!gameVersion.isEmpty())
obj["game_version"] = gameVersion;
if (!loader.isEmpty())
obj["loader"] = loader;
return QJsonDocument(obj).toJson(QJsonDocument::Compact);
}
};
class ApiHeaderProxy : public HeaderProxy {
public:
ApiHeaderProxy() : HeaderProxy() {}
explicit ApiHeaderProxy(ModrinthDownloadMeta meta) : m_meta(std::move(meta)) {}
virtual ~ApiHeaderProxy() = default;
public:
@ -39,11 +63,19 @@ class ApiHeaderProxy : public HeaderProxy {
} else if (request.url().host() == QUrl(BuildConfig.MODRINTH_PROD_URL).host() ||
request.url().host() == QUrl(BuildConfig.MODRINTH_STAGING_URL).host()) {
QString token = APPLICATION->getModrinthAPIToken();
if (!token.isNull())
hdrs.append({ "Authorization", token.toUtf8() });
if (!token.isNull()) {
hdrs.append({ .headerName = "Authorization", .headerValue = token.toUtf8() });
}
}
if (request.url().host() == "cdn.modrinth.com" && !m_meta.isEmpty()) {
hdrs.append({ .headerName = "modrinth-download-meta", .headerValue = m_meta.toJson() });
}
return hdrs;
};
private:
ModrinthDownloadMeta m_meta;
};
} // namespace Net

View file

@ -185,7 +185,7 @@ void ResourceDownloadDialog::confirm()
return;
}
for (const auto& dep : task->getDependecies()) {
addResource(dep->pack, dep->version);
addResource(dep->pack, dep->version, "dependency");
depNames << dep->pack->name;
}
dependencyExtraInfo = task->getExtraInfo();
@ -234,10 +234,10 @@ ResourcePage* ResourceDownloadDialog::selectedPage()
return result;
}
void ResourceDownloadDialog::addResource(ModPlatform::IndexedPack::Ptr pack, ModPlatform::IndexedVersion& ver)
void ResourceDownloadDialog::addResource(ModPlatform::IndexedPack::Ptr pack, ModPlatform::IndexedVersion& ver, QString downloadReason)
{
removeResource(pack->name);
selectedPage()->addResourceToPage(pack, ver, getBaseModel());
selectedPage()->addResourceToPage(pack, ver, getBaseModel(), downloadReason);
setButtonStatus();
}

View file

@ -64,7 +64,7 @@ class ResourceDownloadDialog : public QDialog, public BasePageProvider {
bool selectPage(QString pageId);
ResourcePage* selectedPage();
void addResource(ModPlatform::IndexedPack::Ptr, ModPlatform::IndexedVersion&);
void addResource(ModPlatform::IndexedPack::Ptr, ModPlatform::IndexedVersion&, QString downloadReason = "standalone");
void removeResource(const QString&);
QList<DownloadTaskPtr> getTasks();

View file

@ -234,7 +234,7 @@ void ResourceUpdateDialog::checkCandidates()
auto changelog = dep->version.changelog;
if (dep->pack->provider == ModPlatform::ResourceProvider::FLAME)
changelog = api.getModFileChangelog(dep->version.addonId.toInt(), dep->version.fileId.toInt());
auto download_task = makeShared<ResourceDownloadTask>(dep->pack, dep->version, m_resourceModel);
auto download_task = makeShared<ResourceDownloadTask>(dep->pack, dep->version, m_resourceModel, true, "dependency");
auto extraInfo = dependencyExtraInfo.value(dep->version.addonId.toString());
CheckUpdateTask::Update updatable = {
dep->pack->name, dep->version.hash, tr("Not installed"), dep->version.version, dep->version.version_type,

View file

@ -112,10 +112,10 @@ QMap<QString, QString> ModPage::urlHandlers() const
/******** Make changes to the UI ********/
void ModPage::addResourceToPage(ModPlatform::IndexedPack::Ptr pack, ModPlatform::IndexedVersion& version, ResourceFolderModel* base_model)
void ModPage::addResourceToPage(ModPlatform::IndexedPack::Ptr pack, ModPlatform::IndexedVersion& version, ResourceFolderModel* base_model, QString downloadReason)
{
bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
m_model->addPack(pack, version, base_model, is_indexed);
m_model->addPack(pack, version, base_model, is_indexed, downloadReason);
}
} // namespace ResourceDownload

View file

@ -49,7 +49,7 @@ class ModPage : public ResourcePage {
QMap<QString, QString> urlHandlers() const override;
void addResourceToPage(ModPlatform::IndexedPack::Ptr, ModPlatform::IndexedVersion&, ResourceFolderModel*) override;
void addResourceToPage(ModPlatform::IndexedPack::Ptr, ModPlatform::IndexedVersion&, ResourceFolderModel*, QString downloadReason = "standalone") override;
virtual std::unique_ptr<ModFilterWidget> createFilterWidget() = 0;

View file

@ -486,10 +486,11 @@ void ResourceModel::infoRequestSucceeded(ModPlatform::IndexedPack::Ptr pack, con
void ResourceModel::addPack(ModPlatform::IndexedPack::Ptr pack,
ModPlatform::IndexedVersion& version,
ResourceFolderModel* packs,
bool is_indexed)
bool is_indexed,
QString downloadReason)
{
version.is_currently_selected = true;
m_selected.append(makeShared<ResourceDownloadTask>(pack, version, packs, is_indexed));
m_selected.append(makeShared<ResourceDownloadTask>(pack, version, packs, is_indexed, downloadReason));
}
void ResourceModel::removePack(const QString& rem)

View file

@ -94,7 +94,8 @@ class ResourceModel : public QAbstractListModel {
void addPack(ModPlatform::IndexedPack::Ptr pack,
ModPlatform::IndexedVersion& version,
ResourceFolderModel* packs,
bool is_indexed = false);
bool is_indexed = false,
QString downloadReason = "standalone");
void removePack(const QString& rem);
QList<DownloadTaskPtr> selectedPacks() { return m_selected; }

View file

@ -395,7 +395,10 @@ void ResourcePage::removeResourceFromDialog(const QString& packName)
m_parentDialog->removeResource(packName);
}
void ResourcePage::addResourceToPage(ModPlatform::IndexedPack::Ptr pack, ModPlatform::IndexedVersion& ver, ResourceFolderModel* baseModel)
void ResourcePage::addResourceToPage(ModPlatform::IndexedPack::Ptr pack,
ModPlatform::IndexedVersion& ver,
ResourceFolderModel* baseModel,
QString downloadReason)
{
bool isIndexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
m_model->addPack(std::move(pack), ver, baseModel, isIndexed);

View file

@ -78,7 +78,7 @@ class ResourcePage : public QWidget, public BasePage {
void addResourceToDialog(ModPlatform::IndexedPack::Ptr, ModPlatform::IndexedVersion&);
void removeResourceFromDialog(const QString& packName);
virtual void removeResourceFromPage(const QString& name);
virtual void addResourceToPage(ModPlatform::IndexedPack::Ptr, ModPlatform::IndexedVersion&, ResourceFolderModel*);
virtual void addResourceToPage(ModPlatform::IndexedPack::Ptr, ModPlatform::IndexedVersion&, ResourceFolderModel*, QString downloadReason = "standalone");
virtual void modelReset();

View file

@ -44,10 +44,11 @@ QMap<QString, QString> ShaderPackResourcePage::urlHandlers() const
void ShaderPackResourcePage::addResourceToPage(ModPlatform::IndexedPack::Ptr pack,
ModPlatform::IndexedVersion& version,
ResourceFolderModel* base_model)
ResourceFolderModel* base_model,
QString downloadReason)
{
bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
m_model->addPack(pack, version, base_model, is_indexed);
m_model->addPack(pack, version, base_model, is_indexed, downloadReason);
}
} // namespace ResourceDownload

View file

@ -39,7 +39,7 @@ class ShaderPackResourcePage : public ResourcePage {
bool supportsFiltering() const override { return false; };
void addResourceToPage(ModPlatform::IndexedPack::Ptr, ModPlatform::IndexedVersion&, ResourceFolderModel*) override;
void addResourceToPage(ModPlatform::IndexedPack::Ptr, ModPlatform::IndexedVersion&, ResourceFolderModel*, QString downloadReason = "standalone") override;
QMap<QString, QString> urlHandlers() const override;