mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-02-06 00:40:11 +00:00
201 lines
5.3 KiB
C++
201 lines
5.3 KiB
C++
#include <QApplication>
|
|
#include <QThread>
|
|
|
|
#include "downloader.h"
|
|
#include "curl_handle.h"
|
|
#include "progress_dialog.h"
|
|
|
|
#include "util/logs.hpp"
|
|
|
|
LOG_CHANNEL(network_log, "NET");
|
|
|
|
usz curl_write_cb_compat(char* ptr, usz /*size*/, usz nmemb, void* userdata)
|
|
{
|
|
downloader* download = static_cast<downloader*>(userdata);
|
|
return download->update_buffer(ptr, nmemb);
|
|
}
|
|
|
|
downloader::downloader(QWidget* parent)
|
|
: QObject(parent)
|
|
, m_parent(parent)
|
|
, m_curl(new rpcs3::curl::curl_handle(this))
|
|
{
|
|
}
|
|
|
|
downloader::~downloader()
|
|
{
|
|
if (m_thread && m_thread->isRunning())
|
|
{
|
|
m_curl_abort = true;
|
|
m_thread->wait();
|
|
}
|
|
}
|
|
|
|
void downloader::start(const std::string& url, bool follow_location, bool show_progress_dialog, const QString& progress_dialog_title, bool keep_progress_dialog_open, int expected_size)
|
|
{
|
|
network_log.notice("Starting download from URL: %s", url);
|
|
|
|
if (m_thread)
|
|
{
|
|
if (m_thread->isRunning())
|
|
{
|
|
m_curl_abort = true;
|
|
m_thread->wait();
|
|
}
|
|
m_thread->deleteLater();
|
|
}
|
|
|
|
m_keep_progress_dialog_open = keep_progress_dialog_open;
|
|
m_curl_buf.clear();
|
|
m_curl_abort = false;
|
|
|
|
CURLcode err = curl_easy_setopt(m_curl->get_curl(), CURLOPT_URL, url.c_str());
|
|
if (err != CURLE_OK) network_log.error("curl_easy_setopt(CURLOPT_URL, %s) error: %s", url, curl_easy_strerror(err));
|
|
|
|
err = curl_easy_setopt(m_curl->get_curl(), CURLOPT_WRITEFUNCTION, curl_write_cb_compat);
|
|
if (err != CURLE_OK) network_log.error("curl_easy_setopt(CURLOPT_WRITEFUNCTION, curl_write_cb_compat) error: %s", curl_easy_strerror(err));
|
|
|
|
err = curl_easy_setopt(m_curl->get_curl(), CURLOPT_WRITEDATA, this);
|
|
if (err != CURLE_OK) network_log.error("curl_easy_setopt(CURLOPT_WRITEDATA) error: %s", curl_easy_strerror(err));
|
|
|
|
err = curl_easy_setopt(m_curl->get_curl(), CURLOPT_FOLLOWLOCATION, follow_location ? 1 : 0);
|
|
if (err != CURLE_OK) network_log.error("curl_easy_setopt(CURLOPT_FOLLOWLOCATION, %d) error: %s", follow_location, curl_easy_strerror(err));
|
|
|
|
m_thread = QThread::create([this]
|
|
{
|
|
// Reset error buffer before we call curl_easy_perform
|
|
m_curl->reset_error_buffer();
|
|
|
|
const CURLcode result = curl_easy_perform(m_curl->get_curl());
|
|
m_curl_success = result == CURLE_OK;
|
|
|
|
if (!m_curl_success && !m_curl_abort)
|
|
{
|
|
const std::string error = fmt::format("curl_easy_perform(): %s", m_curl->get_verbose_error(result));
|
|
network_log.error("%s", error);
|
|
Q_EMIT signal_download_error(QString::fromStdString(error));
|
|
}
|
|
});
|
|
|
|
connect(m_thread, &QThread::finished, this, [this]()
|
|
{
|
|
if (m_curl_abort)
|
|
{
|
|
network_log.notice("Download aborted");
|
|
return;
|
|
}
|
|
|
|
if (m_progress_dialog && (!m_keep_progress_dialog_open || !m_curl_success))
|
|
{
|
|
m_progress_dialog->close();
|
|
m_progress_dialog = nullptr;
|
|
}
|
|
|
|
if (m_curl_success)
|
|
{
|
|
network_log.notice("Download finished");
|
|
Q_EMIT signal_download_finished(m_curl_buf);
|
|
}
|
|
});
|
|
|
|
// The downloader's signals are expected to be disconnected and customized before start is called.
|
|
// Therefore we need to (re)connect its signal(s) here and not in the constructor.
|
|
connect(this, &downloader::signal_buffer_update, this, &downloader::handle_buffer_update);
|
|
|
|
if (show_progress_dialog)
|
|
{
|
|
const int maximum = expected_size > 0 ? expected_size : 100;
|
|
|
|
if (m_progress_dialog)
|
|
{
|
|
m_progress_dialog->setWindowTitle(progress_dialog_title);
|
|
m_progress_dialog->setAutoClose(!m_keep_progress_dialog_open);
|
|
m_progress_dialog->SetRange(0, maximum);
|
|
}
|
|
else
|
|
{
|
|
m_progress_dialog = new progress_dialog(progress_dialog_title, tr("Please wait..."), tr("Abort"), 0, maximum, true, m_parent);
|
|
m_progress_dialog->setAutoReset(false);
|
|
m_progress_dialog->setAutoClose(!m_keep_progress_dialog_open);
|
|
m_progress_dialog->show();
|
|
|
|
// Handle abort
|
|
connect(m_progress_dialog, &QProgressDialog::canceled, this, [this]()
|
|
{
|
|
m_curl_abort = true;
|
|
close_progress_dialog();
|
|
Q_EMIT signal_download_canceled();
|
|
});
|
|
connect(m_progress_dialog, &QProgressDialog::finished, m_progress_dialog, &QProgressDialog::deleteLater);
|
|
}
|
|
}
|
|
|
|
m_thread->setObjectName("Download Thread");
|
|
m_thread->setParent(this);
|
|
m_thread->start();
|
|
}
|
|
|
|
void downloader::update_progress_dialog(const QString& title) const
|
|
{
|
|
if (m_progress_dialog)
|
|
{
|
|
m_progress_dialog->setWindowTitle(title);
|
|
}
|
|
}
|
|
|
|
void downloader::close_progress_dialog()
|
|
{
|
|
if (m_progress_dialog)
|
|
{
|
|
m_progress_dialog->close();
|
|
m_progress_dialog = nullptr;
|
|
}
|
|
}
|
|
|
|
progress_dialog* downloader::get_progress_dialog() const
|
|
{
|
|
return m_progress_dialog;
|
|
}
|
|
|
|
usz downloader::update_buffer(char* data, usz size)
|
|
{
|
|
if (m_curl_abort)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
const auto old_size = m_curl_buf.size();
|
|
const auto new_size = old_size + size;
|
|
m_curl_buf.resize(static_cast<int>(new_size));
|
|
memcpy(m_curl_buf.data() + old_size, data, size);
|
|
|
|
int max = 0;
|
|
|
|
if (m_actual_download_size < 0)
|
|
{
|
|
if (curl_easy_getinfo(m_curl->get_curl(), CURLINFO_CONTENT_LENGTH_DOWNLOAD, &m_actual_download_size) == CURLE_OK && m_actual_download_size > 0)
|
|
{
|
|
max = static_cast<int>(m_actual_download_size);
|
|
}
|
|
}
|
|
|
|
Q_EMIT signal_buffer_update(static_cast<int>(new_size), max);
|
|
|
|
return size;
|
|
}
|
|
|
|
void downloader::handle_buffer_update(int size, int max) const
|
|
{
|
|
if (m_curl_abort)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (m_progress_dialog)
|
|
{
|
|
m_progress_dialog->SetRange(0, max > 0 ? max : m_progress_dialog->maximum());
|
|
m_progress_dialog->SetValue(size);
|
|
QApplication::processEvents();
|
|
}
|
|
}
|