chore:下载metalink

This commit is contained in:
2025-06-11 23:08:35 +08:00
parent c0e670f6c1
commit a2aadf44e3
9 changed files with 129 additions and 103 deletions

0
CMakeLists.txt Normal file
View File

View File

@@ -9,8 +9,8 @@ set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets) find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets Network)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets) find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Network)
set(PROJECT_SOURCES set(PROJECT_SOURCES
main.cpp main.cpp
@@ -47,7 +47,7 @@ else()
endif() endif()
endif() endif()
target_link_libraries(Spark-Update-Tool PRIVATE Qt${QT_VERSION_MAJOR}::Widgets) target_link_libraries(Spark-Update-Tool PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Network)
# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1. # Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
# If you are developing for iOS or macOS you should consider setting an # If you are developing for iOS or macOS you should consider setting an

View File

@@ -5,6 +5,7 @@
#include <QProgressBar> #include <QProgressBar>
#include <QPushButton> #include <QPushButton>
#include <QApplication> // 包含 QApplication 头文件 #include <QApplication> // 包含 QApplication 头文件
#include <QDir> // 添加 QDir 头文件
AppDelegate::AppDelegate(QObject *parent) : QStyledItemDelegate(parent), m_downloadManager(new DownloadManager(this)) AppDelegate::AppDelegate(QObject *parent) : QStyledItemDelegate(parent), m_downloadManager(new DownloadManager(this))
{ {
@@ -128,23 +129,32 @@ bool AppDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QS
// 取消按钮区域 // 取消按钮区域
QRect cancelButtonRect(rect.right() - 70, rect.top() + (rect.height() - 20) / 2, 60, 20); QRect cancelButtonRect(rect.right() - 70, rect.top() + (rect.height() - 20) / 2, 60, 20);
if (cancelButtonRect.contains(mouseEvent->pos())) { if (cancelButtonRect.contains(mouseEvent->pos())) {
// 修正方法调用,假设 DownloadManager 有 killProcess 方法 // 修正方法调用,假设 DownloadManager 有 cancelDownload 方法
if (m_downloadManager->isRunning()) { m_downloadManager->cancelDownload();
m_downloadManager->killProcess();
m_isDownloading = false; m_isDownloading = false;
emit updateDisplay(); // 触发重绘 emit updateDisplay(); // 触发重绘
}
return true; return true;
} }
} else { } else {
// 更新按钮区域 // 更新按钮区域
QRect buttonRect(rect.right() - 80, rect.top() + (rect.height() - 30) / 2, 70, 30); QRect buttonRect(rect.right() - 80, rect.top() + (rect.height() - 30) / 2, 70, 30);
if (buttonRect.contains(mouseEvent->pos())) { if (buttonRect.contains(mouseEvent->pos())) {
// 修改这里使用Qt::UserRole +1获取包名
QString packageName = index.data(Qt::UserRole + 1).toString(); QString packageName = index.data(Qt::UserRole + 1).toString();
QString downloadUrl = index.data(Qt::UserRole + 7).toString();
qDebug() << "从模型中获取的包名:" << packageName;
qDebug() << "从模型中获取的下载 URL:" << downloadUrl; // 检查模型中是否正确传递 URL
if (downloadUrl.isEmpty()) {
qWarning() << "下载 URL 为空,无法开始下载,包名:" << packageName;
return false;
}
QString outputPath = QString("%1/%2.metalink").arg(QDir::tempPath(), packageName);
m_isDownloading = true; m_isDownloading = true;
m_progress = 0; m_progress = 0;
m_downloadManager->startDownload(packageName); m_downloadManager->startDownload(downloadUrl, outputPath);
emit updateDisplay(); // 触发重绘 emit updateDisplay(); // 触发重绘
return true; return true;
} }

View File

@@ -14,29 +14,49 @@ QVariant AppListModel::data(const QModelIndex &index, int role) const
if (!index.isValid() || index.row() >= m_data.size()) if (!index.isValid() || index.row() >= m_data.size())
return QVariant(); return QVariant();
const QJsonObject obj = m_data.at(index.row()).toObject(); const QVariantMap &map = m_data.at(index.row()); // 直接访问 QVariantMap
switch (role) { switch (role) {
case Qt::DisplayRole: case Qt::DisplayRole:
return obj["name"].toString(); return map.value("name");
case Qt::UserRole + 1: // 包名 case Qt::UserRole + 1: // 包名
return obj["package"].toString(); return map.value("package");
case Qt::UserRole + 2: // 当前版本 case Qt::UserRole + 2: // 当前版本
return obj["current_version"].toString(); return map.value("current_version");
case Qt::UserRole + 3: // 新版本 case Qt::UserRole + 3: // 新版本
return obj["new_version"].toString(); return map.value("new_version");
case Qt::UserRole + 4: // 图标路径 case Qt::UserRole + 4: // 图标路径
return obj["icon"].toString(); return map.value("icon");
case Qt::UserRole + 5: // 文件大小 case Qt::UserRole + 5: // 文件大小
return obj["size"].toVariant(); return map.value("size");
case Qt::UserRole + 6: // 描述
return map.value("description");
case Qt::UserRole + 7: // 下载 URL
return map.value("download_url"); // 返回下载 URL
default: default:
return QVariant(); return QVariant();
} }
} }
void AppListModel::setUpdateData(const QJsonArray &data) void AppListModel::setUpdateData(const QJsonArray &updateInfo)
{ {
beginResetModel(); beginResetModel();
m_data = data; m_data.clear(); // 清空 QList<QVariantMap>
for (const auto &item : updateInfo) {
QJsonObject obj = item.toObject();
QVariantMap map;
map["package"] = obj["package"].toString();
map["name"] = obj["name"].toString();
map["current_version"] = obj["current_version"].toString();
map["new_version"] = obj["new_version"].toString();
map["icon"] = obj["icon"].toString();
map["size"] = obj["size"].toString();
map["download_url"] = obj["download_url"].toString(); // 确保设置下载 URL
m_data.append(map); // 添加到 QList<QVariantMap>
qDebug() << "设置到模型的包名:" << map["package"].toString();
qDebug() << "设置到模型的下载 URL:" << map["download_url"].toString(); // 检查设置的数据
}
endResetModel(); endResetModel();
} }

View File

@@ -20,7 +20,7 @@ public:
void setUpdateData(const QJsonArray &data); void setUpdateData(const QJsonArray &data);
private: private:
QJsonArray m_data; QList<QVariantMap> m_data; // 修改类型为 QList<QVariantMap>
}; };
#endif // APPLISTMODEL_H #endif // APPLISTMODEL_H

View File

@@ -381,9 +381,14 @@ QJsonArray aptssUpdater::getUpdateInfoAsJson()
} }
// 在构建JSON对象时添加新字段在jsonObj中添加 // 在构建JSON对象时添加新字段在jsonObj中添加
if (packageInfo[packageName].contains("url")) {
jsonObj["download_url"] = packageInfo[packageName]["url"]; jsonObj["download_url"] = packageInfo[packageName]["url"];
qDebug() << "生成的下载 URL:" << packageInfo[packageName]["url"]; // 检查生成的 URL
} else {
qWarning() << "未找到下载 URL包名:" << packageName;
jsonObj["download_url"] = ""; // 设置为空字符串以避免崩溃
}
jsonObj["sha512"] = packageInfo[packageName]["sha512"]; jsonObj["sha512"] = packageInfo[packageName]["sha512"];
jsonArray.append(jsonObj); jsonArray.append(jsonObj);
} }
qDebug()<<jsonArray; qDebug()<<jsonArray;

View File

@@ -1,80 +1,68 @@
#include "downloadmanager.h" #include "downloadmanager.h"
#include <QDebug> // 包含 QDebug 头文件 #include <QFile>
#include <QDebug>
DownloadManager::DownloadManager(QObject *parent) : QObject(parent), m_process(new QProcess(this)) DownloadManager::DownloadManager(QObject *parent) : QObject(parent) {}
void DownloadManager::startDownload(const QString &url, const QString &outputPath)
{ {
m_progressRegex = QRegularExpression(R"(\((\d+)%\))"); qDebug() << "接收到的下载 URL:" << url; // 检查 URL 是否正确传递
connect(m_process, &QProcess::readyReadStandardOutput, this, &DownloadManager::onProcessReadyRead);
connect(m_process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &DownloadManager::onProcessFinished); // 验证 URL 是否包含协议
QUrl metalinkUrl(url + ".metalink");
if (!metalinkUrl.isValid() || metalinkUrl.scheme().isEmpty()) {
qWarning() << "无效的 URL:" << metalinkUrl.toString();
emit downloadFinished(false);
return;
} }
DownloadManager::~DownloadManager() QNetworkRequest request(metalinkUrl);
m_reply = m_networkManager.get(request);
connect(m_reply, &QNetworkReply::downloadProgress, this, &DownloadManager::onDownloadProgress);
connect(m_reply, &QNetworkReply::finished, this, &DownloadManager::onDownloadFinished);
// 保存路径
m_reply->setProperty("outputPath", outputPath);
}
void DownloadManager::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{ {
if (m_process->state() == QProcess::Running) { if (bytesTotal > 0) {
m_process->kill(); int progress = static_cast<int>((bytesReceived * 100) / bytesTotal);
// 原代码
// m_process->waitForFinished();
// 修改后增加超时处理
if (!m_process->waitForFinished(5000)) {
qWarning() << "进程终止超时";
}
}
}
void DownloadManager::startDownload(const QString &appName)
{
// 输出开始下载的日志
QString program = "aptss";
QStringList arguments;
arguments << "download" << "--print-uris" << appName;
qDebug() << "执行命令:" << program << arguments.join(" ");
m_process->start(program, arguments);
}
void DownloadManager::onProcessReadyRead()
{
static QString buffer; // 缓存未处理的输出
buffer += QString::fromUtf8(m_process->readAllStandardOutput());
const QStringList lines = buffer.split(QRegularExpression("[\r\n]"), Qt::SkipEmptyParts);
for (const QString &line : lines) {
QRegularExpressionMatch match = m_progressRegex.match(line);
if (match.hasMatch()) {
int progress = match.captured(1).toInt();
qDebug() << "匹配进度:" << progress << "% => " << line;
emit downloadProgress(progress); emit downloadProgress(progress);
}
}
void DownloadManager::onDownloadFinished()
{
if (m_reply->error() == QNetworkReply::NoError) {
QString outputPath = m_reply->property("outputPath").toString();
QFile file(outputPath);
if (file.open(QIODevice::WriteOnly)) {
file.write(m_reply->readAll());
file.close();
qDebug() << "文件已保存到路径:" << outputPath; // 输出保存路径
emit downloadFinished(true);
} else { } else {
// qDebug() << "未匹配行:" << line; qWarning() << "无法保存文件到路径:" << outputPath;
emit downloadFinished(false);
} }
}
// 如果最后不是完整的一行,保留最后一部分
if (!buffer.endsWith('\n') && !buffer.endsWith('\r')) {
buffer = lines.last();
} else { } else {
buffer.clear(); qWarning() << "下载失败:" << m_reply->errorString();
} emit downloadFinished(false);
} }
m_reply->deleteLater();
m_reply = nullptr;
}
void DownloadManager::onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus) void DownloadManager::cancelDownload()
{ {
bool success = (exitStatus == QProcess::NormalExit && exitCode == 0); if (m_reply) {
emit downloadFinished(success); m_reply->abort(); // 取消当前下载
} m_reply->deleteLater();
m_reply = nullptr;
bool DownloadManager::isRunning() const emit downloadFinished(false); // 发送下载失败信号
{
return m_process->state() == QProcess::Running;
}
void DownloadManager::killProcess()
{
if (m_process->state() == QProcess::Running) {
m_process->kill();
m_process->waitForFinished();
} }
} }

View File

@@ -2,30 +2,28 @@
#define DOWNLOADMANAGER_H #define DOWNLOADMANAGER_H
#include <QObject> #include <QObject>
#include <QProcess> #include <QNetworkAccessManager>
#include <QRegularExpression> #include <QNetworkReply>
class DownloadManager : public QObject class DownloadManager : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit DownloadManager(QObject *parent = nullptr); explicit DownloadManager(QObject *parent = nullptr);
~DownloadManager(); void startDownload(const QString &url, const QString &outputPath);
void cancelDownload(); // 添加取消下载的方法
void startDownload(const QString &appName);
bool isRunning() const; // 声明 isRunning 方法
void killProcess(); // 声明 killProcess 方法
signals: signals:
void downloadProgress(int progress); void downloadProgress(int progress); // 下载进度信号
void downloadFinished(bool success); void downloadFinished(bool success); // 下载完成信号
private slots: private slots:
void onProcessReadyRead(); void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
void onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus); void onDownloadFinished();
private: private:
QProcess *m_process; QNetworkAccessManager m_networkManager;
QRegularExpression m_progressRegex; QNetworkReply *m_reply = nullptr;
}; };
#endif // DOWNLOADMANAGER_H #endif // DOWNLOADMANAGER_H

View File

@@ -152,6 +152,11 @@ void MainWindow::checkUpdates()
QJsonArray updateInfo = updater.getUpdateInfoAsJson(); QJsonArray updateInfo = updater.getUpdateInfoAsJson();
m_model->setUpdateData(updateInfo); m_model->setUpdateData(updateInfo);
for (const auto &item : updateInfo) {
QJsonObject obj = item.toObject();
qDebug() << "模型设置的包名:" << obj["package"].toString();
qDebug() << "模型设置的下载 URL:" << obj["download_url"].toString(); // 检查模型数据
}
} }
MainWindow::~MainWindow() MainWindow::~MainWindow()