diff --git a/README.md b/README.md index e864a50..8062724 100644 --- a/README.md +++ b/README.md @@ -21,9 +21,8 @@ web页面部分正在开发当中,详情请见[web仓库](https://gitee.com/de 当前服务器线路列表(项目中包含): ``` -http://sucdn.jerrywang.top/ -http://store.jerrywang.top/ -http://dcstore.spark-app.store/ +https://d.store.deepinos.org.cn/ +https://store.deepinos.org.cn/ ``` #### 调用参数(spk规则) diff --git a/spark-store-project.pro b/spark-store-project.pro index 329b096..254ea6c 100644 --- a/spark-store-project.pro +++ b/spark-store-project.pro @@ -4,4 +4,4 @@ CONFIG += ordered SUBDIRS = third-party/QtNetworkService \ src/spark-store.pro -spark-store.depends = third-party/QtNetworkService \ No newline at end of file +spark-store.depends = third-party/QtNetworkService diff --git a/src/downloadworker.cpp b/src/downloadworker.cpp new file mode 100644 index 0000000..fbece6e --- /dev/null +++ b/src/downloadworker.cpp @@ -0,0 +1,259 @@ +#include "downloadworker.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DownloadWorker::DownloadWorker(QObject *parent) +{ + +} + +void DownloadWorker::setIdentifier(int identifier) +{ + this->identifier = identifier; +} + +void DownloadWorker::setParamter(const QString &url, QPair range, QFile *file) +{ + this->url = url; + this->startPos = range.first; + this->endPos = range.second; + this->file = file; +} + +qint64 DownloadWorker::getReceivedPos() +{ + return receivedPos; +} + +void DownloadWorker::doWork() +{ + mgr = new QNetworkAccessManager(this); + QNetworkRequest request; + request.setUrl(url); + request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + request.setRawHeader("Range", QString("bytes=%1-%2").arg(startPos) + .arg(endPos).toLocal8Bit()); + reply = mgr->get(request); + qDebug() << "开始下载数据:" << QString(" %1~%2 -> writePos Start %3") + .arg(startPos).arg(endPos).arg(receivedPos); + connect(reply, static_cast(&QNetworkReply::error), + [this](QNetworkReply::NetworkError error){ + if (error != QNetworkReply::NoError) { + qDebug() << "出错了:" << reply->errorString(); + } + }); + connect(reply, &QNetworkReply::finished, mgr, &QNetworkAccessManager::deleteLater); + connect(reply, &QNetworkReply::readyRead, this, &DownloadWorker::dataReady); + connect(reply, &QNetworkReply::finished, this, &DownloadWorker::slotFinish); + connect(reply, &QNetworkReply::downloadProgress, this, &DownloadWorker::handleProcess); +// connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); + connect(reply, &QNetworkReply::finished, this, &DownloadWorker::doStop); + +} + +void DownloadWorker::doStop() +{ + if (reply) { + reply->disconnect(); + reply->aboutToClose(); + reply->deleteLater(); + reply = nullptr; + } +} + +void DownloadWorker::dataReady() +{ + QByteArray data = reply->readAll(); + file->seek(startPos + receivedPos); + file->write(data); + receivedPos += data.size(); +} + +void DownloadWorker::slotFinish() +{ + file->flush(); + qDebug() << "数据块下载完毕:" << QString(" %1~%2 -> writePos Start %3") + .arg(startPos).arg(endPos).arg(receivedPos); + emit workFinished(); +} + +void DownloadWorker::handleProcess(qint64, qint64) +{ + emit this->downloadProcess(); +} + + +DownloadController::DownloadController(QObject *parent) +{ + domains = { + "d1.store.deepinos.org.cn", + "d2.store.deepinos.org.cn", + "d3.store.deepinos.org.cn", + "d4.store.deepinos.org.cn", + "d5.store.deepinos.org.cn" + }; + this->threadNum = domains.size() > 4 ? 4 : domains.size(); +} + +DownloadController::~DownloadController() +{ + if (workers.size() > 0) { + for(int i = 0; i < workers.size(); i++) { + workers.at(i)->doStop(); + workers.at(i)->disconnect(); + workers.at(i)->deleteLater(); + } + workers.clear(); + } +} + +void DownloadController::setFilename(QString filename) +{ + this->filename = filename; +} + +void DownloadController::setThreadNum(int threadNum) +{ + this->threadNum = threadNum; +} + +/** + * @brief 开始下载 + */ +void DownloadController::startDownload(const QString &url) +{ + + + finish = 0; + + // 下载任务等分,计算每个线程的下载数据 + fileSize = getFileSize(url); + if (fileSize == 0) { + emit errorOccur("文件大小获取失败"); + return; + } + qint64 segmentSize = fileSize / threadNum; + ranges.resize(threadNum); + QVector receivedBytes; + receivedBytes.resize(threadNum); + for (int i = 0; i < threadNum; i++) { + ranges[i].first = i * segmentSize; + ranges[i].second = i * segmentSize + segmentSize - 1; + receivedBytes[i] = 0; + } + ranges[threadNum-1].second = fileSize; // 余数部分加入最后一个 + + // 打开文件 + QDir tmpdir("/tmp/spark-store"); + file = new QFile; + file->setFileName(tmpdir.absoluteFilePath(filename)); + + if (file->exists()) + file->remove(); + if (!file->open(QIODevice::WriteOnly)) { + delete file; + file = nullptr; + emit errorOccur(file->errorString()); + return; + } + file->resize(fileSize); + + // 创建下载线程 + workers.clear(); + for(int i = 0; i < ranges.size(); i++) { + qDebug() << QString("第%1个下载请求:%2-%3").arg(i).arg(ranges.at(i).first).arg(ranges.at(i).second); + auto worker = new DownloadWorker(this); + auto range = ranges.at(i); + QString chunkUrl = replaceDomain(url, domains.at(i)); + worker->setIdentifier(i); + worker->setParamter(chunkUrl, range, file); + workers.append(worker); + connect(worker, &DownloadWorker::downloadProcess, this, &DownloadController::handleProcess); + connect(worker, &DownloadWorker::workFinished, this, &DownloadController::chunkDownloadFinish); + worker->doWork(); + } +} + +/** + * @brief 停止下载 + */ +void DownloadController::stopDownload() +{ + for(int i = 0; i < workers.size(); i++) { + workers.at(i)->doStop(); + workers.at(i)->disconnect(); + workers.at(i)->deleteLater(); + } + workers.clear(); + + qDebug() << "文件下载路径:" << QFileInfo(file->fileName()).absoluteFilePath(); + file->flush(); + file->close(); + delete file; + file = nullptr; +} + + +void DownloadController::handleProcess() +{ + qint64 bytesReceived = 0; + for(int i = 0; i < workers.size(); i++) { + bytesReceived += workers.at(i)->getReceivedPos(); + } + qDebug() << QString("下载进度 %1-%2").arg(bytesReceived).arg(fileSize); + emit downloadProcess(bytesReceived, fileSize); +} + +void DownloadController::chunkDownloadFinish() +{ + finish++; + qDebug() << QString("已下载了%1块,共%2块!!!").arg(finish).arg(threadNum); + if (finish == threadNum) { + stopDownload(); + emit downloadFinished(); + } +} + +qint64 DownloadController::getFileSize(const QString& url) +{ + QEventLoop event; + QNetworkAccessManager requestManager; + QNetworkRequest request; + request.setUrl(QUrl(url)); + request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + QNetworkReply *reply = requestManager.head(request); + connect(reply, static_cast(&QNetworkReply::error), + [this, reply](QNetworkReply::NetworkError error){ + if (error != QNetworkReply::NoError) { + emit errorOccur(reply->errorString()); + } + }); + connect(reply, &QNetworkReply::finished, &event, &QEventLoop::quit); + event.exec(); + qint64 fileSize = 0; + if (reply->rawHeader("Accept-Ranges") == QByteArrayLiteral("bytes") + && reply->hasRawHeader(QString("Content-Length").toLocal8Bit())) { + fileSize = reply->header(QNetworkRequest::ContentLengthHeader).toUInt(); + } + qDebug() << "文件大小为:" << fileSize; + reply->deleteLater(); + return fileSize; +} + +QString DownloadController::replaceDomain(const QString& url, const QString domain) +{ + QRegularExpression regex(R"((?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9])"); + if (regex.match(url).hasMatch()) { + return QString(url).replace(regex.match(url).captured(), domain); + } + return url; +} + diff --git a/src/downloadworker.h b/src/downloadworker.h new file mode 100644 index 0000000..72f558b --- /dev/null +++ b/src/downloadworker.h @@ -0,0 +1,75 @@ +#ifndef DOWNLOADWORKER_H +#define DOWNLOADWORKER_H + +#include +#include +#include +#include + +class DownloadWorker : public QObject +{ + Q_OBJECT +public: + explicit DownloadWorker(QObject *parent = nullptr); + void setIdentifier(int identifier); + void setParamter(const QString &url, QPair range, QFile *flle); + qint64 getReceivedPos(); + +public slots: + void doWork(); + void doStop(); + void dataReady(); + void slotFinish(); + void handleProcess(qint64, qint64); + +signals: + void resultReady(int identifier, QByteArray data); + void testSignals(); + void workFinished(); + void downloadProcess(); + +private: + int identifier; + QString url; + qint64 startPos; + qint64 endPos; + qint64 receivedPos = 0; + QNetworkReply *reply; + QNetworkAccessManager *mgr; + QFile *file; +}; + +class DownloadController : public QObject +{ + Q_OBJECT +public: + explicit DownloadController(QObject *parent = nullptr); + ~DownloadController(); + void setFilename(QString filename); + void setThreadNum(int threadNum); + void startDownload(const QString &url); + void stopDownload(); + qint64 getFileSize(const QString& url); + QString replaceDomain(const QString& url, const QString domain); + +public slots: + void handleProcess(); + void chunkDownloadFinish(); + +signals: + void errorOccur(const QString& msg); + void downloadProcess(qint64, qint64); + void downloadFinished(); + +private: + int threadNum; + QString filename; + qint64 fileSize; + QVector> ranges; + QFile *file; + QList workers; + int finish = 0; + QVector domains; +}; + +#endif // FILEDOWNLOADWORKER_H diff --git a/src/spark-store.pro b/src/spark-store.pro index 09c2bbe..fcf5be5 100644 --- a/src/spark-store.pro +++ b/src/spark-store.pro @@ -25,8 +25,12 @@ DEFINES += QT_DEPRECATED_WARNINGS # You can also select to disable deprecated APIs only up to a certain version of Qt. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 +# 禁止输出 qWarning / qDebug 信息 +CONFIG(release, debug|release): DEFINES += QT_NO_WARNING_OUTPUT QT_NO_DEBUG_OUTPUT + SOURCES += main.cpp\ appitem.cpp \ + downloadworker.cpp \ widget.cpp \ downloadlist.cpp \ image_show.cpp \ @@ -37,6 +41,7 @@ SOURCES += main.cpp\ HEADERS += \ appitem.h \ + downloadworker.h \ widget.h \ downloadlist.h \ image_show.h \ diff --git a/src/widget.cpp b/src/widget.cpp index 796dacf..b67cc18 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -33,6 +33,7 @@ #include "HttpClient.h" #include "appitem.h" #include "flowlayout.h" +#include "downloadworker.h" DWIDGET_USE_NAMESPACE @@ -48,6 +49,8 @@ Widget::Widget(DBlurEffectWidget *parent) : m_loadweb->show(); httpClient = new AeaQt::HttpClient; + // 并发下载 + downloadController = new DownloadController(this); connect(ui->menu_main,&QPushButton::clicked,[=](){Widget::chooseLeftMenu(0);}); connect(ui->menu_network,&QPushButton::clicked,[=](){Widget::chooseLeftMenu(1);}); @@ -72,6 +75,7 @@ Widget::Widget(DBlurEffectWidget *parent) : connect(&appinfoLoadThread, &SpkAppInfoLoaderThread::finishedScreenshotLoad, this, &Widget::sltAppinfoScreenshot, Qt::ConnectionType::BlockingQueuedConnection); connect(&appinfoLoadThread, &SpkAppInfoLoaderThread::finishAllLoading, this, &Widget::sltAppinfoFinish, Qt::ConnectionType::BlockingQueuedConnection); + // 搜索事件 connect(searchEdit, &DSearchEdit::returnPressed, this, [=]() { @@ -127,6 +131,7 @@ Widget::~Widget() { notify_uninit(); +// delete httpFinished; delete ui; qDebug()<<"exit"; DApplication::quit(); @@ -231,8 +236,15 @@ void Widget::initConfig() while (getline(serverList,lineTmp)) { ui->comboBox_server->addItem(QString::fromStdString(lineTmp)); } + for(int i = 0; i < ui->comboBox_server->count(); i++) + { + if(ui->comboBox_server->itemText(i) == "开发者模式 Dev only") + { + ui->comboBox_server->model()->setData(ui->comboBox_server->model()->index(i, 0), QVariant(0), Qt::UserRole - 1); + } + } }else { - ui->comboBox_server->addItem("http://sucdn.jerrywang.top/"); + ui->comboBox_server->addItem("https://d.store.deepinos.org.cn/"); } // 读取服务器URL并初始化菜单项的链接 @@ -241,7 +253,7 @@ void Widget::initConfig() ui->comboBox_server->setCurrentText(readConfig.value("server/choose").toString()); appinfoLoadThread.setServer(serverUrl=readConfig.value("server/choose").toString()); }else { - appinfoLoadThread.setServer(serverUrl="http://sucdn.jerrywang.top/"); // 默认URL + appinfoLoadThread.setServer(serverUrl="https://d.store.deenos.org.cn/"); // 默认URL } configCanSave=true; // 防止触发保存配置信号 menuUrl[0]=serverUrl + "store/#/"; @@ -266,8 +278,8 @@ void Widget::initConfig() ui->webfoot->hide(); //初始化首页 - ui->webEngineView->setUrl(menuUrl[0]); - // ui->webEngineView->setUrl(menuUrl[1]); + chooseLeftMenu(0); + // ui->webEngineView->setUrl(menuUrl[0]); //给下载列表赋值到数组,方便调用 for (int i =0; iwebfoot->setFixedHeight(allh-foot); } -int Widget::loadappinfo(QUrl arg1) -{ - if(arg1.isEmpty()){ - return 1; - } - - // 先隐藏详情页负责显示截图的label - ui->screen_0->hide(); - ui->screen_1->hide(); - ui->screen_2->hide(); - ui->screen_3->hide(); - ui->screen_4->hide(); - ui->label_appicon->clear(); - ui->tag_community->hide(); - ui->tag_ubuntu->hide(); - ui->tag_deepin->hide(); - ui->tag_uos->hide(); - ui->tag_dtk5->hide(); - ui->tag_dwine2->hide(); - ui->tag_dwine5->hide(); - ui->tag_a2d->hide(); - - // 重置UI状态 - ui->pushButton_uninstall->hide(); - ui->pushButton_website->setEnabled(false); - ui->pushButton->setEnabled(false); - ui->pushButton_translate->setEnabled(false); - ui->label_show->setText("Loading..."); - ui->label_show->show(); - - QProcess get_json; - QDir dir("/tmp"); - dir.mkdir("spark-store"); - QDir::setCurrent("/tmp/spark-store"); - - get_json.start("curl -o app.json "+arg1.toString()); - get_json.waitForFinished(); - if(get_json.exitCode()) - { - sendNotification(tr("Failed to download app info. Please check internet connection.")); - } - - QFile app_json("app.json"); - if(app_json.open(QIODevice::ReadOnly)){ - // 成功得到json文件 - QByteArray json_array=app_json.readAll(); - // 将路径转化为相应源的下载路径 - urladdress=arg1.toString().left(arg1.toString().length()-8); - QStringList downloadurl=urladdress.split("/"); - urladdress=ui->comboBox_server->currentText(); - QString deburl=urladdress; - deburl=deburl.left(urladdress.length()-1); - urladdress="https://cdn.jsdelivr.net/gh/Jerrywang959/jsonpng@master/"; // 使用图片专用服务器请保留这行,删除后将使用源服务器 - urladdress=urladdress.left(urladdress.length()-1); - - for (int i=3;ilabel_appname->setText(appName); - system("rm -r *.png"); - ui->label_show->show(); - // 软件信息加载 - QString info; - info= tr("PkgName: ")+json["Pkgname"].toString()+"\n"; - info+=tr("Version: ")+json["Version"].toString()+"\n"; - if(json["Author"].toString()!="" && json["Author"].toString()!=" "){ - info+=tr("Author: ")+json["Author"].toString()+"\n"; - } - - if(json["Website"].toString()!="" && json["Website"].toString()!=" "){ - info+=tr("Official Site: ")+json["Website"].toString()+"\n"; - ui->pushButton_website->show(); - appweb=json["Website"].toString(); - } - info+=tr("Contributor: ")+json["Contributor"].toString()+"\n"; - info+=tr("Update Time: ")+json["Update"].toString()+"\n"; - info+=tr("Installed Size: ")+json["Size"].toString()+"\n"; - ui->label_info->setText(info); - ui->label_more->setText(json["More"].toString()); - QProcess isInstall; - pkgName=json["Pkgname"].toString(); - isInstall.start("dpkg -s "+json["Pkgname"].toString()); - isInstall.waitForFinished(); - int error=QString::fromStdString(isInstall.readAllStandardError().toStdString()).length(); - if(error==0){ - ui->pushButton_download->setText(tr("Reinstall")); - ui->pushButton_uninstall->show(); - - }else { - ui->pushButton_download->setText(tr("Install")); - } - //tag加载 - QString tags=json["Tags"].toString(); - QStringList tagList=tags.split(";"); - for (int i=0;itag_community->show(); - if(tagList[i]=="ubuntu") - ui->tag_ubuntu->show(); - if(tagList[i]=="deepin") - ui->tag_deepin->show(); - if(tagList[i]=="uos") - ui->tag_uos->show(); - if(tagList[i]=="dtk5") - ui->tag_dtk5->show(); - if(tagList[i]=="dwine2") - ui->tag_dwine2->show(); - if(tagList[i]=="dwine5") - ui->tag_dwine5->show(); - if(tagList[i]=="a2d") - ui->tag_a2d->show(); - } - // 图标加载 - get_json.start("curl -o icon.png "+urladdress+"icon.png"); - get_json.waitForFinished(); - if(!get_json.exitCode()) { - QPixmap appicon(QString::fromUtf8(TMP_PATH)+"/icon.png"); - ui->label_appicon->setPixmap(appicon); - ui->pushButton_download->setEnabled(true); - ui->pushButton->setEnabled(true); - ui->pushButton_translate->setEnabled(true); - ui->pushButton_website->setEnabled(true); - } - else - sendNotification(tr("Failed to load application icon.")); - - - // 截图展示加载 - QList label_screen; - label_screen << ui->screen_0 << ui->screen_1 << ui->screen_2 << ui->screen_3 << ui->screen_4; - for (int i = 0; i < 5; i++) { - QString cmd = "curl -o screen_"+QString::number(i+1)+".png "+urladdress+"screen_"+QString::number(i+1)+".png"; - get_json.terminate(); - get_json.start(cmd); - get_json.waitForFinished(); - bool s = screen[i].load("screen_"+QString::number(i+1)+".png"); - if(s){ - label_screen[i]->setImage(screen[i]); - label_screen[i]->show(); - /* - switch(i){ // 故意为之,为了清除多余截图 - case 0: - label_screen[1]->hide(); - case 1: - label_screen[2]->hide(); - case 2: - label_screen[3]->hide(); - case 3: - label_screen[4]->hide(); - - }*/ - }else{ - QFile::remove("screen_"+QString::number(i+1)+".png"); - break; - } - } - ui->label_show->setText(""); - ui->label_show->hide(); - } - return 0; -} - void Widget::on_pushButton_download_clicked() { chooseLeftMenu(13); @@ -705,31 +549,37 @@ void Widget::on_pushButton_download_clicked() system("cp icon.png icon_"+QString::number(allDownload-1).toUtf8()+".png"); download_list[allDownload-1].seticon(icon); if(!isBusy){ - file = new QFile(fileName); - if(!file->open(QIODevice::WriteOnly)){ - delete file; - file = nullptr; - return ; - } +// file = new QFile(fileName); +// if(!file->open(QIODevice::WriteOnly)){ +// delete file; +// file = nullptr; +// return ; +// } + nowDownload+=1; - startRequest(urList.at(nowDownload-1)); // 进行链接请求 + + startRequest(urList.at(nowDownload-1), fileName); // 进行链接请求 } if(ui->pushButton_download->text()==tr("Reinstall")){ download_list[allDownload-1].reinstall=true; } } -void Widget::startRequest(QUrl url) +void Widget::startRequest(QUrl url, QString fileName) { ui->listWidget->show(); ui->label->hide(); isBusy=true; isdownload=true; download_list[allDownload-1].free=false; - reply = manager->get(QNetworkRequest(url)); - connect(reply,SIGNAL(finished()),this,SLOT(httpFinished())); - connect(reply,SIGNAL(readyRead()),this,SLOT(httpReadyRead())); - connect(reply,SIGNAL(downloadProgress(qint64,qint64)),this,SLOT(updateDataReadProgress(qint64,qint64))); + + connect(downloadController, &DownloadController::downloadProcess, this, &Widget::updateDataReadProgress); + connect(downloadController, &DownloadController::downloadFinished, this, &Widget::httpFinished); + connect(downloadController, &DownloadController::errorOccur, [this](QString msg){ + this->sendNotification(msg); + }); + downloadController->setFilename(fileName); + downloadController->startDownload(url.toString()); } void Widget::searchApp(QString text) @@ -846,6 +696,8 @@ void Widget::updateDataReadProgress(qint64 bytesRead, qint64 totalBytes) download_list[nowDownload-1].setValue((bytesRead*10000)/totalBytes); // 当前值 download_size=bytesRead; if(download_list[nowDownload-1].close){ // 随时检测下载是否被取消 + downloadController->disconnect(); + downloadController->stopDownload(); download_list[nowDownload-1].closeDownload(); httpFinished(); } @@ -958,12 +810,6 @@ void Widget::sltAppinfoFinish() void Widget::httpFinished() // 完成下载 { - file->flush(); - file->close(); - reply->deleteLater(); - reply = nullptr; - delete file; - file = nullptr; isdownload=false; isBusy=false; download_list[nowDownload-1].readyInstall(); @@ -974,14 +820,7 @@ void Widget::httpFinished() // 完成下载 nowDownload+=1; } QString fileName=download_list[nowDownload-1].getName(); - file = new QFile(fileName); - if(!file->open(QIODevice::WriteOnly)) - { - delete file; - file = nullptr; - return ; - } - startRequest(urList.at(nowDownload-1)); + startRequest(urList.at(nowDownload-1), fileName); } } @@ -1049,7 +888,7 @@ void Widget::on_pushButton_updateServer_clicked() ui->pushButton_updateServer->setEnabled(false); ui->comboBox_server->clear(); QFile::remove(QDir::homePath().toUtf8()+"/.config/spark-store/server.list"); - system("curl -o "+QDir::homePath().toUtf8()+"/.config/spark-store/server.list http://dcstore.shenmo.tech/store/server.list"); + system("curl -o "+QDir::homePath().toUtf8()+"/.config/spark-store/server.list https://d.store.deepinos.org.cn/store/server.list"); std::fstream server; server.open(QDir::homePath().toUtf8()+"/.config/spark-store/server.list",std::ios::in); std::string lineTmp; @@ -1058,10 +897,18 @@ void Widget::on_pushButton_updateServer_clicked() ui->comboBox_server->addItem(QString::fromStdString(lineTmp)); } }else { - ui->comboBox_server->addItem("http://sucdn.jerrywang.top/"); + ui->comboBox_server->addItem("https://d.store.deepinos.org.cn/"); } ui->pushButton_updateServer->setEnabled(true); ui->comboBox_server->setCurrentIndex(0); + + for(int i = 0; i < ui->comboBox_server->count(); i++) + { + if(ui->comboBox_server->itemText(i) == "开发者模式 Dev only") + { + ui->comboBox_server->model()->setData(ui->comboBox_server->model()->index(i, 0), QVariant(0), Qt::UserRole - 1); + } + } }); } @@ -1269,6 +1116,7 @@ void Widget::on_webEngineView_urlChanged(const QUrl &arg1) ui->pushButton_download->setEnabled(false); ui->stackedWidget->setCurrentIndex(2); qDebug()<<"https://demo-one-vert.vercel.app/"+type_name+"/"+pname; + qDebug()<< "链接地址:" << arg1; /* load.cancel();//打开并发加载线程前关闭正在执行的线程 load = QtConcurrent::run([=](){ diff --git a/src/widget.h b/src/widget.h index a1a1bf7..c44355c 100644 --- a/src/widget.h +++ b/src/widget.h @@ -39,6 +39,7 @@ class Widget; class FlowLayout; +class DownloadController; namespace AeaQt { class HttpClient; @@ -51,7 +52,7 @@ class Widget : public DBlurEffectWidget public: explicit Widget(DBlurEffectWidget *parent = nullptr); ~Widget(); - void startRequest(QUrl url); + void startRequest(QUrl url, QString fileName); void searchApp(QString); int nowDownload=0; int allDownload=0; @@ -122,7 +123,6 @@ public: private: void initUI(); void initConfig(); - int loadappinfo(QUrl); void chooseLeftMenu(int index); void setfoot(int); void updatefoot(); @@ -163,6 +163,7 @@ private: AeaQt::HttpClient *httpClient; FlowLayout *applist_grid; QHBoxLayout *main; + DownloadController *downloadController; }; #endif // WIDGET_H diff --git a/src/workerthreads.cpp b/src/workerthreads.cpp index 2f6eef5..5241363 100644 --- a/src/workerthreads.cpp +++ b/src/workerthreads.cpp @@ -5,117 +5,124 @@ #include #include "workerthreads.h" #include "widget.h" +#include "HttpClient.h" void SpkAppInfoLoaderThread::run() { emit requestResetUi(); - QProcess get_json; - QString urladdress, deatils, more, packagename, appweb; - QDir dir("/tmp"); - bool isInstalled; - dir.mkdir("spark-store"); - QDir::setCurrent("/tmp/spark-store"); + httpClient = new AeaQt::HttpClient; - get_json.start("curl -o app.json " + targetUrl.toString()); - if(waitDownload(get_json) == -1) - return; - if(get_json.exitCode()) - { - Widget::sendNotification(tr("Failed to download app info. Please check internet connection.")); - } + httpClient->get(targetUrl.toString()) + .header("content-type", "application/json") + .onResponse([this](QByteArray json_array) { + qDebug() << "请求应用信息 " << json_array; + QString urladdress, deatils, more, packagename, appweb; + bool isInstalled; - QFile app_json("app.json"); - if(app_json.open(QIODevice::ReadOnly)){ - // 成功得到json文件 - QByteArray json_array = app_json.readAll(); - // 将路径转化为相应源的下载路径 - urladdress = targetUrl.toString().left(targetUrl.toString().length()-8); - QStringList downloadurl=urladdress.split("/"); + // 将路径转化为相应源的下载路径 + urladdress = targetUrl.toString().left(targetUrl.toString().length()-8); + QStringList downloadurl=urladdress.split("/"); - QString deburl = serverUrl; - deburl = deburl.left(urladdress.length()-1); - urladdress = "https://cdn.jsdelivr.net/gh/Jerrywang959/jsonpng@master/"; // 使用图片专用服务器请保留这行,删除后将使用源服务器 - urladdress = urladdress.left(urladdress.length()-1); + QString deburl = serverUrl; + deburl = deburl.left(urladdress.length()-1); + urladdress = "https://img.jerrywang.top/"; // 使用图片专用服务器请保留这行,删除后将使用源服务器 + urladdress = urladdress.left(urladdress.length()-1); - for (int i=3;ipushButton_website->show(); move to setinfo slot - appweb=json["Website"].toString(); - } - details+=tr("Contributor: ")+json["Contributor"].toString()+"\n"; - details+=tr("Update Time: ")+json["Update"].toString()+"\n"; - details+=tr("Installed Size: ")+json["Size"].toString()+"\n"; - more = json["More"].toString(); - - QProcess isInstall; - packagename = json["Pkgname"].toString(); - isInstall.start("dpkg -s "+json["Pkgname"].toString()); - isInstall.waitForFinished(); - int error=QString::fromStdString(isInstall.readAllStandardError().toStdString()).length(); - if(error==0) - isInstalled = true; - else - isInstalled = false; - - emit requestSetAppInformation(&appName, &details, &more, &appweb, &packagename, &fileUrl, isInstalled); - - //tag加载 - QString tags=json["Tags"].toString(); - QStringList tagList=tags.split(";"); - emit requestSetTags(&tagList); - - // 图标加载 - get_json.start("curl -o icon.png "+urladdress+"icon.png"); - if(waitDownload(get_json) == -1) - return; - if(!get_json.exitCode()) { - QPixmap appicon("icon.png"); - emit finishedIconLoad(&appicon); - } - else - Widget::sendNotification(tr("Failed to load application icon.")); - - - // 截图展示加载 - QPixmap screenshotCache[5]; - for (int i = 0; i < 5; i++) { - QString cmd = "curl -o screen_"+QString::number(i+1)+".png "+urladdress+"screen_"+QString::number(i+1)+".png"; - get_json.start(cmd); - if(waitDownload(get_json) == -1) - return; - bool s = screenshotCache[i].load(QString(TMP_PATH) + "/screen_"+QString::number(i+1)+".png"); - if(s){ - emit finishedScreenshotLoad(&screenshotCache[i], i); - }else{ - emit finishedScreenshotLoad(nullptr, i); - QFile::remove("screen_"+QString::number(i+1)+".png"); - break; + for (int i=3;ipushButton_website->show(); move to setinfo slot + appweb=json["Website"].toString(); + } + details+=tr("Contributor: ")+json["Contributor"].toString()+"\n"; + details+=tr("Update Time: ")+json["Update"].toString()+"\n"; + details+=tr("Installed Size: ")+json["Size"].toString()+"\n"; + more = json["More"].toString(); + + QProcess isInstall; + packagename = json["Pkgname"].toString(); + isInstall.start("dpkg -s "+json["Pkgname"].toString()); + isInstall.waitForFinished(); + int error=QString::fromStdString(isInstall.readAllStandardError().toStdString()).length(); + if(error==0) + isInstalled = true; + else + isInstalled = false; + + emit requestSetAppInformation(&appName, &details, &more, &appweb, &packagename, &fileUrl, isInstalled); + + //tag加载 + QString tags=json["Tags"].toString(); + QStringList tagList=tags.split(";"); + emit requestSetTags(&tagList); + + // 图标加载 + httpClient->get(urladdress+"icon.png") + .onResponse([this](QByteArray imgData){ + QPixmap appicon; + appicon.loadFromData(imgData); + emit finishedIconLoad(&appicon); + }) + .onError([this](QString errorStr) { + Widget::sendNotification(tr("Failed to load application icon.")); + }) + .block() + .timeout(5 * 100) + .exec(); + + + // 截图展示加载 + QPixmap screenshotCache[5]; + for (int i = 0; i < 5; i++) { + httpClient->get(urladdress+"screen_"+QString::number(i+1)+".png") + .onResponse([this, i, &screenshotCache](QByteArray imgData){ + bool s = screenshotCache[i].loadFromData(imgData); + if(s){ + emit finishedScreenshotLoad(&screenshotCache[i], i); + }else{ + emit finishedScreenshotLoad(nullptr, i); + } + }) + .onError([this](QString errorStr) { + qDebug() << "截图下载失败"; +// Widget::sendNotification(tr("Failed to load application screenshot.")); + }) + .block() + .timeout(4 * 100) + .exec(); + } + emit finishAllLoading(); + + httpClient->deleteLater(); + }) + .onError([](QString errorStr) { + Widget::sendNotification(tr("Failed to download app info. Please check internet connection.")); + }) + .timeout(5 * 100) + .block() + .exec(); } + void SpkAppInfoLoaderThread::setUrl(const QUrl &url) { targetUrl = url; diff --git a/src/workerthreads.h b/src/workerthreads.h index 399fd32..dc3535f 100644 --- a/src/workerthreads.h +++ b/src/workerthreads.h @@ -6,6 +6,11 @@ #include #include + +namespace AeaQt { + class HttpClient; +} + class SpkAppInfoLoaderThread Q_DECL_FINAL : public QThread { Q_OBJECT @@ -31,6 +36,8 @@ private: QString serverUrl; bool finishedDownload = false; int downloaderRetval = 0; + + AeaQt::HttpClient *httpClient; }; #endif // WORKERTHREADS_H diff --git a/translations/spark-store_en.qm b/translations/spark-store_en.qm new file mode 100644 index 0000000..8a774d5 Binary files /dev/null and b/translations/spark-store_en.qm differ diff --git a/translations/spark-store_en.ts b/translations/spark-store_en.ts index 24aa7bb..cdf0870 100644 --- a/translations/spark-store_en.ts +++ b/translations/spark-store_en.ts @@ -28,47 +28,47 @@ SpkAppInfoLoaderThread - + Failed to download app info. Please check internet connection. - + PkgName: - + Version: - + Author: - + Official Site: - + Contributor: - + Update Time: - + Installed Size: - + Failed to load application icon. @@ -127,8 +127,7 @@ - - + Install @@ -411,128 +410,82 @@ 0B - + Spark Store - + Search or enter spk:// - + Submit App - + Settings - + Not Exist - - - - - + + + + + Spark\ Store - - Failed to download app info. Please check internet connection. - - - - - PkgName: - - - - - Version: - - - - - Author: - - - - - Official Site: - - - - - Contributor: - - - - - Update Time: - - - - - Installed Size: - - - - - - + + Reinstall - - Failed to load application icon. - - - - + Failed to get the name to the file to be downloaded. - + Updating, please wait... - + Apt has reported an error. Please use apt update in terminal to locate the problem. - + Unknown error! - + Yes - + No - + Information for Contributors - + Currently the translation contribution is limited to English, and you will be redirected to our Gitee repository at which you are supposed to be creating pull requests to contribute app info @@ -543,27 +496,27 @@ Click yes to continue. - + Uninstall succeeded - + Relative apps Not Found! - + Request Error: %1 - + Temporary cache was cleaned - + The URL has been copied to the clipboard diff --git a/translations/spark-store_zh_CN.qm b/translations/spark-store_zh_CN.qm new file mode 100644 index 0000000..5d80191 Binary files /dev/null and b/translations/spark-store_zh_CN.qm differ diff --git a/translations/spark-store_zh_CN.ts b/translations/spark-store_zh_CN.ts index 8cb86d8..2490873 100644 --- a/translations/spark-store_zh_CN.ts +++ b/translations/spark-store_zh_CN.ts @@ -48,47 +48,47 @@ SpkAppInfoLoaderThread - + Failed to download app info. Please check internet connection. 下载应用程序详细信息失败,请检查网络连接。 - + PkgName: 包名: - + Version: 版本: - + Author: 作者: - + Official Site: 官网: - + Contributor: 投稿者: - + Update Time: 更新时间: - + Installed Size: 大小: - + Failed to load application icon. 加载应用程序图标失败。 @@ -190,8 +190,7 @@ - - + Install 安装 @@ -471,103 +470,93 @@ - + Spark Store Spark 应用商店 - + Search or enter spk:// 搜索或打开链接 - + Submit App 投递应用 - + Settings 设置 - + Not Exist 不存在 - - - - - + + + + + Spark\ Store 星火应用商店 - Failed to download app info. Please check internet connection. - 下载应用程序详细信息失败,请检查网络连接。 + 下载应用程序详细信息失败,请检查网络连接。 - PkgName: - 包名: + 包名: - Version: - 版本: + 版本: - Author: - 作者: + 作者: - Official Site: - 官网: + 官网: - Contributor: - 投稿者: + 投稿者: - Update Time: - 更新时间: + 更新时间: - Installed Size: - 大小: + 大小: - - - + + Reinstall 重新安装 - Failed to load application icon. - 加载应用程序图标失败。 + 加载应用程序图标失败。 - + Failed to get the name to the file to be downloaded. 获取安装包文件名失败。 - + Relative apps Not Found! 相关应用未找到! - + Request Error: %1 请求错误:%1 @@ -576,37 +565,37 @@ 目前仅支持商店专用链接的打开,搜索功能正在开发,请期待以后的版本! - + Updating, please wait... 正在更新,请稍候…… - + Apt has reported an error. Please use apt update in terminal to locate the problem. 更新中发生错误,请在终端使用apt update来查看错误原因。 - + Unknown error! 未知错误! - + Yes - + No - + Information for Contributors 贡献者须知 - + Currently the translation contribution is limited to English, and you will be redirected to our Gitee repository at which you are supposed to be creating pull requests to contribute app info @@ -642,17 +631,17 @@ Click yes to continue. 服务器未知错误 - + Uninstall succeeded 卸载成功 - + Temporary cache was cleaned 缓存目录已清空 - + The URL has been copied to the clipboard 链接已复制到剪贴板