mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-11 17:59:10 +08:00
Merge branch 'dk_thingiverse'
This commit is contained in:
commit
575824443a
@ -140,6 +140,18 @@ Downloader::Downloader()
|
||||
Bind(EVT_DWNLDR_FILE_CANCELED, &Downloader::on_canceled, this);
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool is_any_subdomain(const std::string& url, const std::vector<std::string>& subdomains)
|
||||
{
|
||||
for (const std::string& sub : subdomains)
|
||||
{
|
||||
if(FileGet::is_subdomain(url, sub))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
void Downloader::start_download(const std::string& full_url)
|
||||
{
|
||||
assert(m_initialized);
|
||||
@ -156,14 +168,14 @@ void Downloader::start_download(const std::string& full_url)
|
||||
|
||||
size_t id = get_next_id();
|
||||
|
||||
if (!boost::starts_with(escaped_url, "https://") || !FileGet::is_subdomain(escaped_url, "printables.com")) {
|
||||
std::string msg = format(_L("Download won't start. Download URL doesn't point to https://printables.com : %1%"), escaped_url);
|
||||
if (!boost::starts_with(escaped_url, "https://") || !is_any_subdomain(escaped_url, {"printables.com", "thingiverse.com"})) {
|
||||
std::string msg = format(_L("Download won't start. Download URL doesn't point to allowed subdomains : %1%"), escaped_url);
|
||||
BOOST_LOG_TRIVIAL(error) << msg;
|
||||
NotificationManager* ntf_mngr = wxGetApp().notification_manager();
|
||||
ntf_mngr->push_notification(NotificationType::CustomNotification, NotificationManager::NotificationLevel::RegularNotificationLevel, msg);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
m_downloads.emplace_back(std::make_unique<Download>(id, std::move(escaped_url), this, m_dest_folder, true));
|
||||
NotificationManager* ntf_mngr = wxGetApp().notification_manager();
|
||||
ntf_mngr->push_download_URL_progress_notification(id, m_downloads.back()->get_filename(), std::bind(&Downloader::user_action_callback, this, std::placeholders::_1, std::placeholders::_2));
|
||||
@ -202,7 +214,7 @@ void Downloader::on_progress(wxCommandEvent& event)
|
||||
float percent = (float)std::stoi(into_u8(event.GetString())) / 100.f;
|
||||
//BOOST_LOG_TRIVIAL(error) << "progress " << id << ": " << percent;
|
||||
NotificationManager* ntf_mngr = wxGetApp().notification_manager();
|
||||
BOOST_LOG_TRIVIAL(trace) << "Download "<< id << ": " << percent;
|
||||
//BOOST_LOG_TRIVIAL(trace) << "Download "<< id << ": " << percent;
|
||||
ntf_mngr->set_download_URL_progress(id, percent);
|
||||
}
|
||||
void Downloader::on_error(wxCommandEvent& event)
|
||||
@ -250,7 +262,9 @@ bool Downloader::user_action_callback(DownloaderUserAction action, int id)
|
||||
|
||||
void Downloader::on_name_change(wxCommandEvent& event)
|
||||
{
|
||||
|
||||
size_t id = event.GetInt();
|
||||
NotificationManager* ntf_mngr = wxGetApp().notification_manager();
|
||||
ntf_mngr->set_download_URL_filename(id, into_u8(event.GetString()));
|
||||
}
|
||||
|
||||
void Downloader::on_paused(wxCommandEvent& event)
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/nowide/cstdio.hpp>
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
|
||||
#include "format.hpp"
|
||||
#include "GUI.hpp"
|
||||
@ -68,6 +69,26 @@ unsigned get_current_pid()
|
||||
return ::getpid();
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string extract_filename_from_header(const std::string& headers) {
|
||||
// Split the headers into lines
|
||||
std::istringstream header_stream(headers);
|
||||
std::string line;
|
||||
|
||||
while (std::getline(header_stream, line)) {
|
||||
if (line.find("content-disposition") != std::string::npos) {
|
||||
// Apply regex to extract filename from the content-disposition line
|
||||
std::regex filename_regex("filename\\s*=\\s*\"([^\"]+)\"", std::regex::icase);
|
||||
std::smatch match;
|
||||
|
||||
if (std::regex_search(line, match, filename_regex) && match.size() > 1) {
|
||||
return match.str(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
// int = DOWNLOAD ID; string = file path
|
||||
@ -111,6 +132,8 @@ FileGet::priv::priv(int ID, std::string&& url, const std::string& filename, wxEv
|
||||
, m_dest_folder(dest_folder)
|
||||
, m_load_after(load_after)
|
||||
{
|
||||
// Prevent ':' in filename
|
||||
m_filename.erase(std::remove(m_filename.begin(), m_filename.end(), ':'), m_filename.end());
|
||||
}
|
||||
|
||||
void FileGet::priv::get_perform()
|
||||
@ -129,7 +152,7 @@ void FileGet::priv::get_perform()
|
||||
std::string extension = dest_path.extension().string();
|
||||
std::string just_filename = m_filename.substr(0, m_filename.size() - extension.size());
|
||||
std::string final_filename = just_filename;
|
||||
// Find unsed filename
|
||||
// Find unused filename
|
||||
try {
|
||||
size_t version = 0;
|
||||
while (boost::filesystem::exists(m_dest_folder / (final_filename + extension)) || boost::filesystem::exists(m_dest_folder / (final_filename + extension + "." + std::to_string(get_current_pid()) + ".download")))
|
||||
@ -154,7 +177,6 @@ void FileGet::priv::get_perform()
|
||||
}
|
||||
|
||||
m_filename = final_filename + extension;
|
||||
|
||||
m_tmp_path = m_dest_folder / (m_filename + "." + std::to_string(get_current_pid()) + ".download");
|
||||
|
||||
wxCommandEvent* evt = new wxCommandEvent(EVT_DWNLDR_FILE_NAME_CHANGE);
|
||||
@ -247,13 +269,48 @@ void FileGet::priv::get_perform()
|
||||
m_written = written_previously + written_this_session;
|
||||
}
|
||||
wxCommandEvent* evt = new wxCommandEvent(EVT_DWNLDR_FILE_PROGRESS);
|
||||
int percent_total = (written_previously + progress.dlnow) * 100 / m_absolute_size;
|
||||
int percent_total = m_absolute_size == 0 ? 0 : (written_previously + progress.dlnow) * 100 / m_absolute_size;
|
||||
evt->SetString(std::to_string(percent_total));
|
||||
evt->SetInt(m_id);
|
||||
m_evt_handler->QueueEvent(evt);
|
||||
}
|
||||
|
||||
})
|
||||
.on_headers([&](const std::string& headers) {
|
||||
// we are looking for content-disposition header in response, to use it as correct filename
|
||||
std::string new_filename = extract_filename_from_header(headers);
|
||||
if (new_filename.empty()) {
|
||||
return;
|
||||
}
|
||||
// Find unused filename
|
||||
boost::filesystem::path temp_dest_path = m_dest_folder / new_filename;
|
||||
std::string extension = temp_dest_path.extension().string();
|
||||
std::string just_filename = new_filename.substr(0, new_filename.size() - extension.size());
|
||||
std::string final_filename = just_filename;
|
||||
try {
|
||||
size_t version = 0;
|
||||
while (boost::filesystem::exists(m_dest_folder / (final_filename + extension)))
|
||||
{
|
||||
++version;
|
||||
if (version > 999) {
|
||||
BOOST_LOG_TRIVIAL(error) << GUI::format("Failed to find suitable filename. Last name: %1%." , (m_dest_folder / (final_filename + extension)).string());
|
||||
return;
|
||||
}
|
||||
final_filename = GUI::format("%1%(%2%)", just_filename, std::to_string(version));
|
||||
}
|
||||
} catch (const boost::filesystem::filesystem_error& e)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error) << "Failed to resolved filename from headers.";
|
||||
return;
|
||||
}
|
||||
m_filename = final_filename + extension;
|
||||
dest_path = m_dest_folder / m_filename;
|
||||
|
||||
wxCommandEvent* evt = new wxCommandEvent(EVT_DWNLDR_FILE_NAME_CHANGE);
|
||||
evt->SetString(from_u8(m_filename));
|
||||
evt->SetInt(m_id);
|
||||
m_evt_handler->QueueEvent(evt);
|
||||
})
|
||||
.on_error([&](std::string body, std::string error, unsigned http_status) {
|
||||
if (file != NULL)
|
||||
fclose(file);
|
||||
@ -268,19 +325,31 @@ void FileGet::priv::get_perform()
|
||||
.on_complete([&](std::string body, unsigned /* http_status */) {
|
||||
try
|
||||
{
|
||||
// If server is not sending Content-Length header, the progress function does not write all data to file.
|
||||
// We need to write it now.
|
||||
if (written_this_session < body.size()) {
|
||||
std::string part_for_write = body.substr(written_this_session);
|
||||
fwrite(part_for_write.c_str(), 1, part_for_write.size(), file);
|
||||
}
|
||||
fclose(file);
|
||||
boost::filesystem::rename(m_tmp_path, dest_path);
|
||||
}
|
||||
catch (const std::exception& /*e*/)
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
//TODO: report?
|
||||
//error_message = GUI::format("Failed to write and move %1% to %2%", tmp_path, dest_path);
|
||||
wxCommandEvent* evt = new wxCommandEvent(EVT_DWNLDR_FILE_ERROR);
|
||||
evt->SetString("Failed to write and move.");
|
||||
evt->SetString(GUI::format("Failed to write and move %1% to %2%", m_tmp_path, dest_path));
|
||||
evt->SetInt(m_id);
|
||||
m_evt_handler->QueueEvent(evt);
|
||||
return;
|
||||
}
|
||||
wxCommandEvent* evt = new wxCommandEvent(EVT_DWNLDR_FILE_PROGRESS);
|
||||
int percent_total = 100;
|
||||
evt->SetString(std::to_string(percent_total));
|
||||
evt->SetInt(m_id);
|
||||
m_evt_handler->QueueEvent(evt);
|
||||
|
||||
DownloadEventData event_data = {m_id, dest_path.wstring(), m_load_after};
|
||||
wxQueueEvent(m_evt_handler, new Event<DownloadEventData>(EVT_DWNLDR_FILE_COMPLETE, event_data));
|
||||
})
|
||||
|
@ -1119,6 +1119,11 @@ void NotificationManager::URLDownloadWithPrintablesLinkNotification::render_text
|
||||
|
||||
|
||||
//------URLDownloadNotification----------------
|
||||
void NotificationManager::URLDownloadNotification::set_filename(const std::string& filename_line)
|
||||
{
|
||||
m_text1 = filename_line;
|
||||
init();
|
||||
}
|
||||
|
||||
void NotificationManager::URLDownloadNotification::render_close_button(const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
||||
{
|
||||
@ -2624,6 +2629,19 @@ void NotificationManager::set_download_URL_error(size_t id, const std::string& t
|
||||
}
|
||||
}
|
||||
}
|
||||
void NotificationManager::set_download_URL_filename(size_t id, const std::string& filename)
|
||||
{
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::URLDownload) {
|
||||
URLDownloadNotification* ntf = dynamic_cast<URLDownloadNotification*>(notification.get());
|
||||
if (ntf->get_download_id() != id)
|
||||
continue;
|
||||
ntf->set_filename(_u8L("Download") + ": " + filename);
|
||||
wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationManager::init_slicing_progress_notification(std::function<bool()> cancel_callback)
|
||||
{
|
||||
|
@ -258,6 +258,7 @@ public:
|
||||
void set_download_URL_paused(size_t id);
|
||||
void set_download_URL_canceled(size_t id);
|
||||
void set_download_URL_error(size_t id, const std::string& text);
|
||||
void set_download_URL_filename(size_t id, const std::string& filename);
|
||||
// slicing progress
|
||||
void init_slicing_progress_notification(std::function<bool()> cancel_callback);
|
||||
void set_slicing_progress_began();
|
||||
@ -561,6 +562,7 @@ private:
|
||||
void set_paused(bool paused) { m_download_paused = paused; }
|
||||
void set_error_message(const std::string& message) { m_error_message = message; }
|
||||
bool compare_text(const std::string& text) const override { return false; };
|
||||
void set_filename(const std::string& filename_line);
|
||||
protected:
|
||||
void render_close_button(const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y) override;
|
||||
|
@ -137,12 +137,14 @@ struct Http::priv
|
||||
Http::ProgressFn progressfn;
|
||||
Http::IPResolveFn ipresolvefn;
|
||||
Http::RetryFn retryfn;
|
||||
Http::HeadersFn headersfn;
|
||||
|
||||
priv(const std::string &url);
|
||||
~priv();
|
||||
|
||||
static bool ca_file_supported(::CURL *curl);
|
||||
static size_t writecb(void *data, size_t size, size_t nmemb, void *userp);
|
||||
static size_t headercb(void *data, size_t size, size_t nmemb, void *userp);
|
||||
static int xfercb(void *userp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow);
|
||||
static int xfercb_legacy(void *userp, double dltotal, double dlnow, double ultotal, double ulnow);
|
||||
static size_t form_file_read_cb(char *buffer, size_t size, size_t nitems, void *userp);
|
||||
@ -230,6 +232,14 @@ size_t Http::priv::writecb(void *data, size_t size, size_t nmemb, void *userp)
|
||||
return realsize;
|
||||
}
|
||||
|
||||
size_t Http::priv::headercb(void *data, size_t size, size_t nmemb, void *userp)
|
||||
{
|
||||
std::string header(reinterpret_cast<char*>(data), size * nmemb);
|
||||
std::string *header_data = static_cast<std::string*>(userp);
|
||||
header_data->append(header);
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
int Http::priv::xfercb(void *userp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
|
||||
{
|
||||
auto self = static_cast<priv*>(userp);
|
||||
@ -377,6 +387,10 @@ void Http::priv::http_perform(const HttpRetryOpt& retry_opts)
|
||||
|
||||
::curl_easy_setopt(curl, CURLOPT_VERBOSE, get_logging_level() >= 5);
|
||||
|
||||
std::string header_data;
|
||||
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, headercb);
|
||||
curl_easy_setopt(curl, CURLOPT_HEADERDATA, &header_data);
|
||||
|
||||
if (headerlist != nullptr) {
|
||||
::curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
|
||||
}
|
||||
@ -444,6 +458,9 @@ void Http::priv::http_perform(const HttpRetryOpt& retry_opts)
|
||||
if (http_status >= 400) {
|
||||
if (errorfn) { errorfn(std::move(buffer), std::string(), http_status); }
|
||||
} else {
|
||||
if (headersfn && !header_data.empty()) {
|
||||
headersfn(header_data);
|
||||
}
|
||||
if (completefn) { completefn(std::move(buffer), http_status); }
|
||||
if (ipresolvefn) {
|
||||
char* ct;
|
||||
@ -657,6 +674,12 @@ Http& Http::on_retry(RetryFn fn)
|
||||
return *this;
|
||||
}
|
||||
|
||||
Http& Http::on_headers(HeadersFn fn)
|
||||
{
|
||||
if (p) { p->headersfn = std::move(fn); }
|
||||
return *this;
|
||||
}
|
||||
|
||||
Http& Http::cookie_file(const std::string& file_path)
|
||||
{
|
||||
if (p) {
|
||||
|
@ -67,6 +67,8 @@ public:
|
||||
//<bool - false if canceled(int - attempt number, unsigned - ms to next attempt, 0 if last)>
|
||||
typedef std::function<bool(int, unsigned)> RetryFn;
|
||||
|
||||
typedef std::function<void(const std::string&)> HeadersFn;
|
||||
|
||||
Http(Http &&other);
|
||||
|
||||
// Note: strings are expected to be UTF-8-encoded
|
||||
@ -147,6 +149,8 @@ public:
|
||||
|
||||
Http& on_retry(RetryFn fn);
|
||||
|
||||
Http& on_headers(HeadersFn fn);
|
||||
|
||||
Http& cookie_file(const std::string& file_path);
|
||||
Http& cookie_jar(const std::string& file_path);
|
||||
Http& set_referer(const std::string& referer);
|
||||
|
Loading…
x
Reference in New Issue
Block a user