Profile updates and installation:

- Refactoring and functionality change of PresetUpdater::sync_config. Zip archive now contains only index files. From index file it is decided wheter .ini file should be downloaded and where (cache for update of installed, cache/vendor for unistalled). New vendors are downloaded from set address. Fron .ini file it is decided wheter thumbnail should be downloaded. All resources for already installed vendors are checked and downloaded.  
- TemplateFilaments renamed to Templates (Warning: This might create duplicities if both files are present!).
- Various checks added to prevent crashes when dealing with broken presets, wrong files etc.
- Delayed error message when loading present finds duplicities - wait with dialog until Splash screen is gone.
- Minor changes in Config wizard when searching & loading printer thumbnails.
This commit is contained in:
David Kocik 2023-01-18 10:50:59 +01:00
parent fc65d73c2d
commit af0e312542
14 changed files with 6085 additions and 5011 deletions

View File

@ -1,2 +0,0 @@
min_slic3r_version = 2.4.0-rc
0.0.1 Initial

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,2 @@
min_slic3r_version = 2.6.0-alpha0
1.0.0 Initial

File diff suppressed because it is too large Load Diff

View File

@ -36,10 +36,10 @@ static const std::string MODEL_PREFIX = "model:";
// are phased out, then we will revert to the original name.
//static const std::string VERSION_CHECK_URL = "https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaSlicer.version";
static const std::string VERSION_CHECK_URL = "https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaSlicer.version2";
// url to folder with profile archive zip
// TODO: Uncomment and delete 2nd when we have archive online
//static const std::string PROFILE_ARCHIVE_URL = "https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/Archive/Archive.zip";
static const std::string PROFILE_ARCHIVE_URL = "https://raw.githubusercontent.com/kocikdav/PrusaSlicer-settings/master/live/Bundle/Archive.zip";
// Url to index archive zip that contains latest indicies
static const std::string INDEX_ARCHIVE_URL= "https://files.prusa3d.com/wp-content/uploads/repository/vendor_indices.zip";
// Url to folder with vendor profile files. Used when downloading new profiles that are not in resources folder.
static const std::string PROFILE_FOLDER_URL = "https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/";
const std::string AppConfig::SECTION_FILAMENTS = "filaments";
const std::string AppConfig::SECTION_MATERIALS = "sla_materials";
@ -671,9 +671,24 @@ std::string AppConfig::version_check_url() const
return from_settings.empty() ? VERSION_CHECK_URL : from_settings;
}
const std::string& AppConfig::profile_archive_url() const
std::string AppConfig::index_archive_url() const
{
return PROFILE_ARCHIVE_URL;
#if 0
// this code is for debug & testing purposes only - changed url wont get trough inner checks anyway.
auto from_settings = get("index_archive_url");
return from_settings.empty() ? INDEX_ARCHIVE_URL : from_settings;
#endif
return INDEX_ARCHIVE_URL;
}
std::string AppConfig::profile_folder_url() const
{
#if 0
// this code is for debug & testing purposes only - changed url wont get trough inner checks anyway.
auto from_settings = get("profile_folder_url");
return from_settings.empty() ? PROFILE_FOLDER_URL : from_settings;
#endif
return PROFILE_FOLDER_URL;
}
bool AppConfig::exists()

View File

@ -139,8 +139,11 @@ public:
// Get the Slic3r version check url.
// This returns a hardcoded string unless it is overriden by "version_check_url" in the ini file.
std::string version_check_url() const;
// Get the Slic3r url to vendor profile archive zip.
const std::string& profile_archive_url() const;
// Get the Slic3r url to vendor index archive zip.
std::string index_archive_url() const;
// Get the Slic3r url to folder with vendor profile files.
std::string profile_folder_url() const;
// Returns the original Slic3r version found in the ini file before it was overwritten
// by the current version

View File

@ -2146,7 +2146,7 @@ namespace PresetUtils {
if (! res.empty()
&& !fs::exists(fs::path(vendor_folder + res))
&& !fs::exists(fs::path(rsrc_folder + res))
&& (fs::exists(fs::path(cache_folder + res))))
&& !fs::exists(fs::path(cache_folder + res)))
return false;
}
}

View File

@ -1308,13 +1308,22 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_configbundle(
try {
pt::read_ini(ifs, tree);
} catch (const boost::property_tree::ini_parser::ini_parser_error &err) {
throw Slic3r::RuntimeError(format("Failed loading config bundle \"%1%\"\nError: \"%2%\" at line %3%", path, err.message(), err.line()).c_str());
// This throw was uncatched. While other similar problems later are just returning empty pair.
//throw Slic3r::RuntimeError(format("Failed loading config bundle \"%1%\"\nError: \"%2%\" at line %3%", path, err.message(), err.line()).c_str());
BOOST_LOG_TRIVIAL(error) << format("Failed loading config bundle \"%1%\"\nError: \"%2%\" at line %3%", path, err.message(), err.line()).c_str();
return std::make_pair(PresetsConfigSubstitutions{}, 0);
}
}
const VendorProfile *vendor_profile = nullptr;
if (flags.has(LoadConfigBundleAttribute::LoadSystem) || flags.has(LoadConfigBundleAttribute::LoadVendorOnly)) {
auto vp = VendorProfile::from_ini(tree, path);
VendorProfile vp;
try {
vp = VendorProfile::from_ini(tree, path);
} catch (const std::exception& e) {
BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: Failed to open profile file.") % path;
return std::make_pair(PresetsConfigSubstitutions{}, 0);
}
if (vp.models.size() == 0 && !vp.templates_profile) {
BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: No printer model defined.") % path;
return std::make_pair(PresetsConfigSubstitutions{}, 0);
@ -1360,7 +1369,7 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_configbundle(
} else if (boost::starts_with(section.first, "filament:")) {
presets = &this->filaments;
preset_name = section.first.substr(9);
if (vendor_profile->templates_profile) {
if (vendor_profile && vendor_profile->templates_profile) {
preset_name += " @Template";
}
} else if (boost::starts_with(section.first, "sla_print:")) {

View File

@ -141,6 +141,8 @@ BundleMap BundleMap::load()
typedef std::pair<const fs::path&, BundleLocation> DirData;
std::vector<DirData> dir_list { {vendor_dir, BundleLocation::IN_VENDOR}, {archive_dir, BundleLocation::IN_ARCHIVE}, {rsrc_vendor_dir, BundleLocation::IN_RESOURCES} };
for ( auto dir : dir_list) {
if (!fs::exists(dir.first))
continue;
for (const auto &dir_entry : boost::filesystem::directory_iterator(dir.first)) {
if (Slic3r::is_ini_file(dir_entry)) {
std::string id = dir_entry.path().stem().string(); // stem() = filename() without the trailing ".ini" part
@ -226,26 +228,30 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxSt
bool is_variants = false;
const fs::path vendor_dir_path = (fs::path(Slic3r::data_dir()) / "vendor").make_preferred();
const fs::path cache_dir_path = (fs::path(Slic3r::data_dir()) / "cache").make_preferred();
const fs::path rsrc_dir_path = (fs::path(resources_dir()) / "profiles").make_preferred();
for (const auto &model : models) {
if (! filter(model)) { continue; }
wxBitmap bitmap;
int bitmap_width = 0;
auto load_bitmap = [](const wxString& bitmap_file, wxBitmap& bitmap, int& bitmap_width)->bool {
if (wxFileExists(bitmap_file)) {
bitmap.LoadFile(bitmap_file, wxBITMAP_TYPE_PNG);
bitmap_width = bitmap.GetWidth();
return true;
}
return false;
auto load_bitmap = [](const wxString& bitmap_file, wxBitmap& bitmap, int& bitmap_width) {
bitmap.LoadFile(bitmap_file, wxBITMAP_TYPE_PNG);
bitmap_width = bitmap.GetWidth();
};
bool found = false;
for (const std::string& res : { Slic3r::data_dir() + "/vendor/" + vendor.id + "/", Slic3r::resources_dir() + "/profiles/" + vendor.id + "/", Slic3r::data_dir() + "/cache/" + vendor.id + "/" } ) {
if (load_bitmap(GUI::from_u8(res + "/" + model.thumbnail), bitmap, bitmap_width)) {
found = true;
break;
}
for (const fs::path& res : { rsrc_dir_path / vendor.id / model.thumbnail
, vendor_dir_path / vendor.id / model.thumbnail
, cache_dir_path / vendor.id / model.thumbnail })
{
if (!fs::exists(res))
continue;
load_bitmap(GUI::from_u8(res.string()), bitmap, bitmap_width);
found = true;
break;
}
if (!found) {
@ -256,7 +262,7 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxSt
load_bitmap(Slic3r::var(PRINTER_PLACEHOLDER), bitmap, bitmap_width);
}
auto *title = new wxStaticText(this, wxID_ANY, from_u8(model.name), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
wxStaticText* title = new wxStaticText(this, wxID_ANY, from_u8(model.name), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
title->SetFont(font_name);
const int wrap_width = std::max((int)MODEL_MIN_WRAP, bitmap_width);
title->Wrap(wrap_width);
@ -269,7 +275,7 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxSt
titles.push_back(title);
auto *bitmap_widget = new wxStaticBitmap(this, wxID_ANY, bitmap);
wxStaticBitmap* bitmap_widget = new wxStaticBitmap(this, wxID_ANY, bitmap);
bitmaps.push_back(bitmap_widget);
auto *variants_panel = new wxPanel(this);
@ -292,7 +298,7 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxSt
is_variants = true;
}
auto *cbox = new Checkbox(variants_panel, label, model_id, variant.name);
Checkbox* cbox = new Checkbox(variants_panel, label, model_id, variant.name);
i == 0 ? cboxes.push_back(cbox) : cboxes_alt.push_back(cbox);
const bool enabled = appconfig.get_variant(vendor.id, model_id, variant.name);
@ -1616,7 +1622,7 @@ PageVendors::PageVendors(ConfigWizard *parent)
for (const auto &pair : wizard_p()->bundles) {
const VendorProfile *vendor = pair.second.vendor_profile;
if (vendor->id == PresetBundle::PRUSA_BUNDLE) { continue; }
if (vendor->templates_profile)
if (vendor && vendor->templates_profile)
continue;
auto *cbox = new wxCheckBox(this, wxID_ANY, vendor->name);
@ -2498,7 +2504,7 @@ void ConfigWizard::priv::update_materials(Technology technology)
}
}
// template filament bundle has no printers - filament would be never added
if(pair.second.vendor_profile->templates_profile && pair.second.preset_bundle->printers.begin() == pair.second.preset_bundle->printers.end())
if(pair.second.vendor_profile&& pair.second.vendor_profile->templates_profile && pair.second.preset_bundle->printers.begin() == pair.second.preset_bundle->printers.end())
{
if (!filaments.containts(&filament)) {
filaments.push(&filament);

View File

@ -369,11 +369,11 @@ struct Materials
if (((printer == nullptr && printer_name == PageMaterials::EMPTY) || (printer != nullptr && is_compatible_with_printer(PresetWithVendorProfile(prst, prst.vendor), PresetWithVendorProfile(prntr, prntr.vendor)))) &&
(type.empty() || get_type(preset) == type) &&
(vendor.empty() || get_vendor(preset) == vendor) &&
!prst.vendor->templates_profile) {
prst.vendor && !prst.vendor->templates_profile) {
cb(preset);
}
else if ((printer == nullptr && printer_name == PageMaterials::TEMPLATES) && prst.vendor->templates_profile &&
else if ((printer == nullptr && printer_name == PageMaterials::TEMPLATES) && prst.vendor && prst.vendor->templates_profile &&
(type.empty() || get_type(preset) == type) &&
(vendor.empty() || get_vendor(preset) == vendor)) {
cb(preset);

File diff suppressed because it is too large Load Diff

View File

@ -798,7 +798,7 @@ void PlaterPresetComboBox::show_edit_menu()
wxString PlaterPresetComboBox::get_preset_name(const Preset& preset)
{
std::string name = preset.alias.empty() ? preset.name : preset.alias;
std::string name = preset.alias.empty() ? preset.name : (preset.vendor && preset.vendor->templates_profile ? preset.name : preset.alias);
return from_u8(name + suffix(preset));
}
@ -909,14 +909,7 @@ void PlaterPresetComboBox::update()
set_label_marker(Append(separator(L("System presets")), NullBitmapBndl()));
}
const AppConfig* app_config = wxGetApp().app_config;
if (!template_presets.empty() && app_config->get("no_templates") == "0") {
set_label_marker(Append(separator(L("Template presets")), wxNullBitmap));
for (std::map<wxString, wxBitmapBundle*>::iterator it = template_presets.begin(); it != template_presets.end(); ++it) {
Append(it->first, *it->second);
validate_selection(it->first == selected_user_preset);
}
}
if (!nonsys_presets.empty())
{
set_label_marker(Append(separator(L("User presets")), NullBitmapBndl()));
@ -926,6 +919,15 @@ void PlaterPresetComboBox::update()
}
}
const AppConfig* app_config = wxGetApp().app_config;
if (!template_presets.empty() && app_config->get("no_templates") == "0") {
set_label_marker(Append(separator(L("Template presets")), wxNullBitmap));
for (std::map<wxString, wxBitmapBundle*>::iterator it = template_presets.begin(); it != template_presets.end(); ++it) {
Append(it->first, *it->second);
validate_selection(it->first == selected_user_preset);
}
}
if (m_type == Preset::TYPE_PRINTER)
{
// add Physical printers, if any exists
@ -1122,17 +1124,7 @@ void TabPresetComboBox::update()
if (i + 1 == m_collection->num_default_presets())
set_label_marker(Append(separator(L("System presets")), NullBitmapBndl()));
}
const AppConfig* app_config = wxGetApp().app_config;
if (!template_presets.empty() && app_config->get("no_templates") == "0") {
set_label_marker(Append(separator(L("Template presets")), wxNullBitmap));
for (std::map<wxString, std::pair<wxBitmapBundle*, bool>>::iterator it = template_presets.begin(); it != template_presets.end(); ++it) {
int item_id = Append(it->first, *it->second.first);
bool is_enabled = it->second.second;
if (!is_enabled)
set_label_marker(item_id, LABEL_ITEM_DISABLED);
validate_selection(it->first == selected);
}
}
if (!nonsys_presets.empty())
{
set_label_marker(Append(separator(L("User presets")), NullBitmapBndl()));
@ -1144,7 +1136,19 @@ void TabPresetComboBox::update()
validate_selection(it->first == selected);
}
}
const AppConfig* app_config = wxGetApp().app_config;
if (!template_presets.empty() && app_config->get("no_templates") == "0") {
set_label_marker(Append(separator(L("Template presets")), wxNullBitmap));
for (std::map<wxString, std::pair<wxBitmapBundle*, bool>>::iterator it = template_presets.begin(); it != template_presets.end(); ++it) {
int item_id = Append(it->first, *it->second.first);
bool is_enabled = it->second.second;
if (!is_enabled)
set_label_marker(item_id, LABEL_ITEM_DISABLED);
validate_selection(it->first == selected);
}
}
if (m_type == Preset::TYPE_PRINTER)
{
// add Physical printers, if any exists

View File

@ -12,9 +12,11 @@
#include <boost/filesystem/fstream.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/log/trivial.hpp>
#include <curl/curl.h>
#include <wx/app.h>
#include <wx/msgdlg.h>
#include <wx/progdlg.h>
#include "libslic3r/libslic3r.h"
#include "libslic3r/format.hpp"
@ -50,7 +52,7 @@ namespace Slic3r {
static const char *INDEX_FILENAME = "index.idx";
static const char *TMP_EXTENSION = ".download";
namespace {
void copy_file_fix(const fs::path &source, const fs::path &target)
{
BOOST_LOG_TRIVIAL(debug) << format("PresetUpdater: Copying %1% -> %2%", source, target);
@ -67,7 +69,21 @@ void copy_file_fix(const fs::path &source, const fs::path &target)
static constexpr const auto perms = fs::owner_read | fs::owner_write | fs::group_read | fs::others_read;
fs::permissions(target, perms);
}
std::string escape_string_url(const std::string& unescaped)
{
std::string ret_val;
CURL* curl = curl_easy_init();
if (curl) {
char* decoded = curl_easy_escape(curl, unescaped.c_str(), unescaped.size());
if (decoded) {
ret_val = std::string(decoded);
curl_free(decoded);
}
curl_easy_cleanup(curl);
}
return ret_val;
}
}
struct Update
{
fs::path source;
@ -160,12 +176,17 @@ struct PresetUpdater::priv
void set_download_prefs(const AppConfig *app_config);
bool get_file(const std::string &url, const fs::path &target_path) const;
void prune_tmps() const;
void sync_config(const VendorMap vendors, const std::string& profile_archive_url);
void sync_config(const VendorMap vendors, const std::string& index_archive_url);
void check_install_indices() const;
Updates get_config_updates(const Semver& old_slic3r_version) const;
bool perform_updates(Updates &&updates, bool snapshot = true) const;
void set_waiting_updates(Updates u);
// checks existence and downloads resource to cache
void get_missing_resource(const std::string& vendor, const std::string& filename, const std::string& url) const;
// checks existence and downloads resource to vendor or copy from cache to vendor
void get_or_copy_missing_resource(const std::string& vendor, const std::string& filename, const std::string& url) const;
void update_index_db();
};
PresetUpdater::priv::priv()
@ -182,6 +203,11 @@ PresetUpdater::priv::priv()
index_db = Index::load_db();
}
void PresetUpdater::priv::update_index_db()
{
index_db = Index::load_db();
}
// Pull relevant preferences from AppConfig
void PresetUpdater::priv::set_download_prefs(const AppConfig *app_config)
{
@ -235,9 +261,91 @@ void PresetUpdater::priv::prune_tmps() const
}
}
void PresetUpdater::priv::get_missing_resource(const std::string& vendor, const std::string& filename, const std::string& url) const
{
if (filename.empty() || vendor.empty())
return;
if (!boost::starts_with(url, "http://files.prusa3d.com/wp-content/uploads/repository/") &&
!boost::starts_with(url, "https://files.prusa3d.com/wp-content/uploads/repository/"))
{
throw Slic3r::CriticalException(GUI::format("URL outside prusa3d.com network: %1%", url));
}
std::string escaped_filename = escape_string_url(filename);
const fs::path file_in_vendor(vendor_path / (vendor + "/" + filename));
const fs::path file_in_rsrc(rsrc_path / (vendor + "/" + filename));
const fs::path file_in_cache(cache_path / (vendor + "/" + filename));
if (fs::exists(file_in_vendor)) { // Already in vendor. No need to do anything.
BOOST_LOG_TRIVIAL(info) << "Resource " << vendor << " / " << filename << " found in vendor folder. No need to download.";
return;
}
if (fs::exists(file_in_rsrc)) { // In resources dir since installation. No need to do anything.
BOOST_LOG_TRIVIAL(info) << "Resource " << vendor << " / " << filename << " found in resources folder. No need to download.";
return;
}
if (fs::exists(file_in_cache)) { // In cache/venodr_name/ dir. No need to do anything.
BOOST_LOG_TRIVIAL(info) << "Resource " << vendor << " / " << filename << " found in cache folder. No need to download.";
return;
}
BOOST_LOG_TRIVIAL(info) << "Resources check could not find " << vendor << " / " << filename << " bed texture. Downloading.";
const auto resource_url = format("%1%%2%%3%", url, url.back() == '/' ? "" : "/", escaped_filename); // vendor should already be in url
if (!fs::exists(file_in_cache.parent_path()))
fs::create_directory(file_in_cache.parent_path());
get_file(resource_url, file_in_cache);
return;
}
void PresetUpdater::priv::get_or_copy_missing_resource(const std::string& vendor, const std::string& filename, const std::string& url) const
{
if (filename.empty() || vendor.empty())
return;
std::string escaped_filename = escape_string_url(filename);
const fs::path file_in_vendor(vendor_path / (vendor + "/" + filename));
const fs::path file_in_rsrc(rsrc_path / (vendor + "/" + filename));
const fs::path file_in_cache(cache_path / (vendor + "/" + filename));
if (fs::exists(file_in_vendor)) { // Already in vendor. No need to do anything.
BOOST_LOG_TRIVIAL(info) << "Resource " << vendor << " / " << filename << " found in vendor folder. No need to download.";
return;
}
if (fs::exists(file_in_rsrc)) { // In resources dir since installation. No need to do anything.
BOOST_LOG_TRIVIAL(info) << "Resource " << vendor << " / " << filename << " found in resources folder. No need to download.";
return;
}
if (!fs::exists(file_in_cache)) { // No file to copy. Download it to straight to the vendor dir.
if (!boost::starts_with(url, "http://files.prusa3d.com/wp-content/uploads/repository/") &&
!boost::starts_with(url, "https://files.prusa3d.com/wp-content/uploads/repository/"))
{
throw Slic3r::CriticalException(GUI::format("URL outside prusa3d.com network: %1%", url));
}
BOOST_LOG_TRIVIAL(info) << "Downloading resources missing in cache directory: " << vendor << " / " << filename;
const auto resource_url = format("%1%%2%%3%", url, url.back() == '/' ? "" : "/", escaped_filename); // vendor should already be in url
if (!fs::exists(file_in_vendor.parent_path()))
fs::create_directory(file_in_vendor.parent_path());
get_file(resource_url, file_in_vendor);
return;
}
if (!fs::exists(file_in_vendor.parent_path())) // create vendor_name dir in vendor
fs::create_directory(file_in_vendor.parent_path());
BOOST_LOG_TRIVIAL(debug) << "Copiing: " << file_in_cache << " to " << file_in_vendor;
copy_file_fix(file_in_cache, file_in_vendor);
}
// Download vendor indices. Also download new bundles if an index indicates there's a new one available.
// Both are saved in cache.
void PresetUpdater::priv::sync_config(const VendorMap vendors, const std::string& profile_archive_url)
void PresetUpdater::priv::sync_config(const VendorMap vendors, const std::string& index_archive_url)
{
BOOST_LOG_TRIVIAL(info) << "Syncing configuration cache";
@ -246,21 +354,20 @@ void PresetUpdater::priv::sync_config(const VendorMap vendors, const std::string
// Download profiles archive zip
// dk: Do we want to return here on error? Or skip archive dwnld and unzip and work with previous run state cache / vendor? I think return.
// Any error here also doesnt show any info in UI. Do we want maybe notification?
fs::path archive_path(cache_path / "Archive.zip");
if (profile_archive_url.empty()) {
fs::path archive_path(cache_path / "vendor_indices.zip");
if (index_archive_url.empty()) {
BOOST_LOG_TRIVIAL(error) << "Downloading profile archive failed - url has no value.";
return;
}
BOOST_LOG_TRIVIAL(info) << "Downloading vedor profiles archive zip.";
BOOST_LOG_TRIVIAL(info) << "Downloading vedor profiles archive zip from " << index_archive_url;
//check if idx_url is leading to our site
if (!boost::starts_with(profile_archive_url, "http://files.prusa3d.com/wp-content/uploads/repository/") &&
!boost::starts_with(profile_archive_url, "https://files.prusa3d.com/wp-content/uploads/repository/"))
if (!boost::starts_with(index_archive_url, "http://files.prusa3d.com/wp-content/uploads/repository/") &&
!boost::starts_with(index_archive_url, "https://files.prusa3d.com/wp-content/uploads/repository/"))
{
BOOST_LOG_TRIVIAL(error) << "Unsafe url path for vedor profiles archive zip. Download is rejected.";
// TODO: this return must be uncommented when correct address is in use
//return;
return;
}
if (!get_file(profile_archive_url, archive_path)) {
if (!get_file(index_archive_url, archive_path)) {
BOOST_LOG_TRIVIAL(error) << "Download of vedor profiles archive zip failed.";
return;
}
@ -268,8 +375,16 @@ void PresetUpdater::priv::sync_config(const VendorMap vendors, const std::string
return;
}
enum class VendorStatus
{
IN_ARCHIVE,
IN_CACHE,
NEW_VERSION,
INSTALLED
};
std::vector<std::pair<std::string, VendorStatus>> vendors_with_status;
// Unzip archive to cache / vendor
std::vector<std::string> vendors_only_in_archive;
mz_zip_archive archive;
mz_zip_zero_struct(&archive);
if (!open_zip_reader(&archive, archive_path.string())) {
@ -291,69 +406,59 @@ void PresetUpdater::priv::sync_config(const VendorMap vendors, const std::string
}
// create file from buffer
fs::path tmp_path(cache_vendor_path / (name + ".tmp"));
if (!fs::exists(tmp_path.parent_path())) {
BOOST_LOG_TRIVIAL(error) << "Failed to unzip file " << name << ". Directories are not supported. Skipping file.";
continue;
}
fs::path target_path(cache_vendor_path / name);
fs::fstream file(tmp_path, std::ios::out | std::ios::binary | std::ios::trunc);
file.write(buffer.c_str(), buffer.size());
file.close();
fs::rename(tmp_path, target_path);
if (name.substr(name.size() - 3) == "ini")
vendors_only_in_archive.push_back(name);
boost::system::error_code ec;
bool exists = fs::exists(tmp_path, ec);
if(!exists || ec) {
BOOST_LOG_TRIVIAL(error) << "Failed to find unzipped file at " << tmp_path << ". Terminating Preset updater synchorinzation." ;
close_zip_reader(&archive);
return;
}
fs::rename(tmp_path, target_path, ec);
if (ec) {
BOOST_LOG_TRIVIAL(error) << "Failed to rename unzipped file at " << tmp_path << ". Terminating Preset updater synchorinzation. Error message: " << ec.message();
close_zip_reader(&archive);
return;
}
// TODO: what if unexpected happens here (folder inside zip) - crash!
if (name.substr(name.size() - 3) == "idx")
vendors_with_status.emplace_back(name.substr(0, name.size() - 4), VendorStatus::IN_ARCHIVE); // asume for now its only in archive - if not, it will change later.
}
}
}
close_zip_reader(&archive);
}
auto get_missing_resource = [&self = std::as_const(*this)](const std::string& vendor, const std::string& filename,
const std::string& url, const fs::path& vendor_path,
const fs::path& rsrc_path, const fs::path& cache_path)
{
if (filename.empty() || vendor.empty())
return;
if (!boost::starts_with(url, "http://files.prusa3d.com/wp-content/uploads/repository/") &&
!boost::starts_with(url, "https://files.prusa3d.com/wp-content/uploads/repository/"))
{
throw Slic3r::CriticalException(GUI::format("URL outside prusa3d.com network: %1%", url));
}
const fs::path file_in_vendor(vendor_path / (vendor + "/" + filename));
const fs::path file_in_rsrc(rsrc_path / (vendor + "/" + filename));
const fs::path file_in_cache(cache_path / (vendor + "/" + filename));
if (fs::exists(file_in_vendor)) { // Already in vendor. No need to do anything.
return;
}
if (fs::exists(file_in_rsrc)) { // In resources dir since installation. No need to do anything.
return;
}
BOOST_LOG_TRIVIAL(info) << "Resources check could not find " << vendor << " / " << filename << " bed texture. Downloading.";
const auto resource_url = format("%1%%2%%3%", url, url.back() == '/' ? "" : "/", filename); // vendor should already be in url
if (!fs::exists(file_in_cache.parent_path()))
fs::create_directory(file_in_cache.parent_path());
self.get_file(resource_url, file_in_cache);
return;
};
// Update vendor preset bundles if in Vendor
// Over all indices from the cache directory:
for (auto &index : index_db) {
if (cancel) {
return;
}
auto archive_it = std::find_if(vendors_with_status.begin(), vendors_with_status.end(),
[&index](const std::pair<std::string, VendorStatus>& element) { return element.first == index.vendor(); });
//assert(archive_it != vendors_with_status.end()); // this would mean there is a index for vendor that is missing in recently downloaded archive
const auto vendor_it = vendors.find(index.vendor());
if (vendor_it == vendors.end()) {
// Not installed vendor yet we need to check missing thumbnails (of new printers)
BOOST_LOG_TRIVIAL(debug) << "No such vendor: " << index.vendor();
if (archive_it != vendors_with_status.end())
archive_it->second = VendorStatus::IN_CACHE;
continue;
}
if (archive_it != vendors_with_status.end())
archive_it->second = VendorStatus::INSTALLED;
const VendorProfile &vendor = vendor_it->second;
const std::string idx_path = (cache_path / (vendor.id + ".idx")).string();
const std::string idx_path_temp = (cache_vendor_path / (vendor.id + ".idx")).string();
@ -368,7 +473,7 @@ void PresetUpdater::priv::sync_config(const VendorMap vendors, const std::string
continue;
}
if (new_index.version() < index.version()) {
BOOST_LOG_TRIVIAL(warning) << format("The downloaded index %1% for vendor %2% is older than the active one. Ignoring the downloaded index.", idx_path_temp, vendor.name);
BOOST_LOG_TRIVIAL(info) << format("The downloaded index %1% for vendor %2% is older than the active one. Ignoring the downloaded index.", idx_path_temp, vendor.name);
continue;
}
copy_file_fix(idx_path_temp, idx_path);
@ -401,23 +506,28 @@ void PresetUpdater::priv::sync_config(const VendorMap vendors, const std::string
recommended.to_string());
if (vendor.config_version >= recommended) { continue; }
const auto path_in_archive = cache_vendor_path / (vendor.id + ".ini");
const auto path_in_cache = cache_path / (vendor.id + ".ini");
// Check version
if (!boost::filesystem::exists(path_in_archive))
continue;
// vp is fully loaded to get all resources
auto vp = VendorProfile::from_ini(path_in_archive, true);
if (vp.config_version != recommended)
continue;
copy_file_fix(path_in_archive, path_in_cache);
// vendors that are checked here, doesnt need to be checked again later
const auto archive_it = std::find(vendors_only_in_archive.begin(), vendors_only_in_archive.end(), index.vendor() + ".ini");
if (archive_it != vendors_only_in_archive.end()) {
vendors_only_in_archive.erase(archive_it);
}
// vendors that are checked here, doesnt need to be checked again later
if (archive_it != vendors_with_status.end())
archive_it->second = VendorStatus::NEW_VERSION;
// Download recomended ini to cache
const auto path_in_cache = cache_path / (vendor.id + ".ini");
BOOST_LOG_TRIVIAL(info) << "Downloading new bundle for vendor: " << vendor.name;
const auto bundle_url = format("%1%/%2%.ini", vendor.config_update_url, recommended.to_string());
const auto bundle_path = cache_path / (vendor.id + ".ini");
if (!get_file(bundle_url, bundle_path))
continue;
if (cancel)
return;
// vp is fully loaded to get all resources
VendorProfile vp;
try {
vp = VendorProfile::from_ini(bundle_path, true);
} catch (const std::exception& e) {
BOOST_LOG_TRIVIAL(error) << format("Corrupted profile file for vendor %1% at %2%, message: %3%", vendor.id, bundle_path, e.what());
continue;
}
// check the fresh bundle for missing resources
// for that, the ini file must be parsed (done above)
for (const auto& model : vp.models) {
@ -425,9 +535,7 @@ void PresetUpdater::priv::sync_config(const VendorMap vendors, const std::string
if (! res.empty()) {
try
{
// for debug (wont pass check inside function)
//std::string fake_url = "https://github.com/kocikdav/PrusaSlicer-settings/raw/master/resources/" + vp.id;
get_missing_resource(vp.id, res, vendor.config_update_url, vendor_path, rsrc_path, cache_path);
get_missing_resource(vp.id, res, vendor.config_update_url);
}
catch (const std::exception& e)
{
@ -441,28 +549,222 @@ void PresetUpdater::priv::sync_config(const VendorMap vendors, const std::string
}
}
// Download missing thumbnails for not-installed vendors.
for (const std::string& vendor : vendors_only_in_archive)
{
BOOST_LOG_TRIVIAL(error) << vendor;
const auto path_in_archive = cache_vendor_path / vendor;
assert(boost::filesystem::exists(path_in_archive));
auto vp = VendorProfile::from_ini(path_in_archive, true);
for (const auto& model : vp.models) {
if (!model.thumbnail.empty()) {
try
{
// for debug (wont pass check inside function)
//std::string fake_url = "https://github.com/kocikdav/PrusaSlicer-settings/raw/master/resources/" + vp.id;
get_missing_resource(vp.id, model.thumbnail, vp.config_update_url, vendor_path, rsrc_path, cache_path);
//for (const std::string& vendor : vendors_only_in_archive)
for (const std::pair<std::string, VendorStatus >& vendor : vendors_with_status) {
if (vendor.second == VendorStatus::IN_ARCHIVE) {
// index in archive and not in cache and not installed vendor
const auto idx_path_in_archive = cache_vendor_path / (vendor.first + ".idx");
const auto ini_path_in_archive = cache_vendor_path / (vendor.first + ".ini");
if (!fs::exists(idx_path_in_archive))
continue;
Index index;
try {
index.load(idx_path_in_archive);
}
catch (const std::exception& /* err */) {
BOOST_LOG_TRIVIAL(error) << format("Could not load downloaded index %1% for vendor %2%: invalid index?", idx_path_in_archive, vendor.first);
continue;
}
const auto recommended_it = index.recommended();
if (recommended_it == index.end()) {
BOOST_LOG_TRIVIAL(error) << format("No recommended version for vendor: %1%, invalid index? (%2%)", vendor.first, idx_path_in_archive);
continue;
}
const auto recommended = recommended_it->config_version;
if (!fs::exists(ini_path_in_archive)){
// Download recommneded to vendor - we do not have any existing ini file so we have to use hardcoded url.
const std::string fixed_url = GUI::wxGetApp().app_config->profile_folder_url();
const auto bundle_url = format("%1%/%2%/%3%.ini", fixed_url, vendor.first, recommended.to_string());
if (!get_file(bundle_url, ini_path_in_archive))
continue;
} else {
// check existing ini version
// then download recommneded to vendor if needed
VendorProfile vp;
try {
vp = VendorProfile::from_ini(ini_path_in_archive, true);
} catch (const std::exception& e) {
BOOST_LOG_TRIVIAL(error) << format("Corrupted profile file for vendor %1% at %2%, message: %3%", vendor.first, ini_path_in_archive, e.what());
continue;
}
catch (const std::exception& e)
{
BOOST_LOG_TRIVIAL(error) << "Failed to get " << model.thumbnail << " for " << vp.id << " " << model.id << ": " << e.what();
if (vp.config_version != recommended) {
const std::string fixed_url = GUI::wxGetApp().app_config->profile_folder_url();
const auto bundle_url = format("%1%/%2%/%3%.ini", fixed_url, vendor.first, recommended.to_string());
if (!get_file(bundle_url, ini_path_in_archive))
continue;
}
}
if (cancel)
return;
// check missing thumbnails
VendorProfile vp;
try {
vp = VendorProfile::from_ini(ini_path_in_archive, true);
}
catch (const std::exception& e) {
BOOST_LOG_TRIVIAL(error) << format("Corrupted profile file for vendor %1% at %2%, message: %3%", vendor.first, ini_path_in_archive, e.what());
continue;
}
for (const auto& model : vp.models) {
if (!model.thumbnail.empty()) {
try
{
get_missing_resource(vp.id, model.thumbnail, vp.config_update_url);
}
catch (const std::exception& e)
{
BOOST_LOG_TRIVIAL(error) << "Failed to get " << model.thumbnail << " for " << vp.id << " " << model.id << ": " << e.what();
}
}
if (cancel)
return;
}
} else if (vendor.second == VendorStatus::IN_CACHE) {
// find those where archive index recommends other version than index in cache and get it if not present
const auto idx_path_in_archive = cache_vendor_path / (vendor.first + ".idx");
const auto ini_path_in_archive = cache_vendor_path / (vendor.first + ".ini");
const auto idx_path_in_cache = cache_path / (vendor.first + ".idx");
if (!fs::exists(idx_path_in_archive) || !fs::exists(idx_path_in_cache))
continue;
// Compare index in cache and recetly downloaded one as part of zip archive
Index index_cache;
try {
index_cache.load(idx_path_in_cache);
}
catch (const std::exception& /* err */) {
BOOST_LOG_TRIVIAL(error) << format("Could not load downloaded index %1% for vendor %2%: invalid index?", idx_path_in_cache, vendor.first);
continue;
}
const auto recommended_it_cache = index_cache.recommended();
if (recommended_it_cache == index_cache.end()) {
BOOST_LOG_TRIVIAL(error) << format("No recommended version for vendor: %1%, invalid index? (%2%)", vendor.first, idx_path_in_cache);
continue;
}
const auto recommended_cache = recommended_it_cache->config_version;
Index index_archive;
try {
index_archive.load(idx_path_in_archive);
}
catch (const std::exception& /* err */) {
BOOST_LOG_TRIVIAL(error) << format("Could not load downloaded index %1% for vendor %2%: invalid index?", idx_path_in_archive, vendor.first);
continue;
}
const auto recommended_it_archive = index_archive.recommended();
if (recommended_it_archive == index_archive.end()) {
BOOST_LOG_TRIVIAL(error) << format("No recommended version for vendor: %1%, invalid index? (%2%)", vendor.first, idx_path_in_archive);
continue;
}
const auto recommended_archive = recommended_it_archive->config_version;
if (recommended_archive <= recommended_cache) {
// There isn't more recent recomended version online. This vendor is also not istalled.
// Thus only .ini is in resources and came with installation.
// And we expect all resources are present.
continue;
}
// Download new .ini if needed. So next time user runs Wizard, most recent profiles are shown & installed.
if (!fs::exists(ini_path_in_archive) || fs::is_empty(ini_path_in_archive)) {
// download recommneded to vendor
const fs::path ini_path_in_rsrc = rsrc_path / (vendor.first + ".ini");
if (!fs::exists(ini_path_in_rsrc)) {
// THIS SHOULD NOT HAPPEN
continue;
}
// Get download path from existing ini.
VendorProfile vp;
try {
vp = VendorProfile::from_ini(ini_path_in_rsrc, false);
}
catch (const std::exception& e) {
BOOST_LOG_TRIVIAL(error) << format("Corrupted profile file for vendor %1% at %2%, message: %3%", vendor.first, ini_path_in_rsrc, e.what());
continue;
}
const auto bundle_url = format("%1%/%2%.ini", vp.config_update_url, recommended_archive.to_string());
if (!get_file(bundle_url, ini_path_in_archive)) {
BOOST_LOG_TRIVIAL(error) << format("Failed to open vendor .ini file when checking missing resources: %1%", ini_path_in_rsrc);
continue;
}
} else {
// Check existing ini version.
// Then download recommneded to vendor if needed.
VendorProfile vp;
try {
vp = VendorProfile::from_ini(ini_path_in_archive, false);
}
catch (const std::exception& e) {
BOOST_LOG_TRIVIAL(error) << format("Corrupted profile file for vendor %1% at %2%, message: %3%", vendor.first, ini_path_in_archive, e.what());
continue;
}
if (vp.config_version != recommended_archive) {
const auto bundle_url = format("%1%/%2%.ini", vp.config_update_url, recommended_archive.to_string());
if (!get_file(bundle_url, ini_path_in_archive)) {
BOOST_LOG_TRIVIAL(error) << format("Failed to open vendor .ini file when checking missing resources: %1%", ini_path_in_archive);
continue;
}
}
}
if (!fs::exists(ini_path_in_archive)) {
BOOST_LOG_TRIVIAL(error) << "Resources check failed to find ini file for vendor: " << vendor.first;
continue;
}
// check missing thumbnails
VendorProfile vp;
try {
vp = VendorProfile::from_ini(ini_path_in_archive, true);
}
catch (const std::exception& e) {
BOOST_LOG_TRIVIAL(error) << format("Corrupted profile file for vendor %1% at %2%, message: %3%", vendor.first, ini_path_in_archive, e.what());
continue;
}
for (const auto& model : vp.models) {
if (!model.thumbnail.empty()) {
try
{
get_missing_resource(vp.id, model.thumbnail, vp.config_update_url);
}
catch (const std::exception& e)
{
BOOST_LOG_TRIVIAL(error) << "Failed to get " << model.thumbnail << " for " << vp.id << " " << model.id << ": " << e.what();
}
}
if (cancel)
return;
}
} else if (vendor.second == VendorStatus::INSTALLED || vendor.second == VendorStatus::NEW_VERSION) {
// Installed vendors need to check that no resource is missing. Do this only for files in vendor folder (not in resorces)
// VendorStatus::NEW_VERSION might seem like a mistake here since files are downloaded when preparing update higher in this function.
// But this is a check for ini file in vendor where resources might be still missing since last update.
const auto path_in_vendor = vendor_path / (vendor.first + ".ini");
if(!fs::exists(path_in_vendor))
continue;
VendorProfile vp;
try {
vp = VendorProfile::from_ini(path_in_vendor, true);
}
catch (const std::exception& e) {
BOOST_LOG_TRIVIAL(error) << format("Corrupted profile file for vendor %1% at %2%, message: %3%", vendor.first, path_in_vendor, e.what());
continue;
}
for (const auto& model : vp.models) {
for (const std::string& res : { model.bed_texture, model.bed_model, model.thumbnail }) {
if (!model.thumbnail.empty()) {
try
{
get_or_copy_missing_resource(vp.id, res, vp.config_update_url);
}
catch (const std::exception& e)
{
BOOST_LOG_TRIVIAL(error) << "Failed to get " << model.thumbnail << " for " << vp.id << " " << model.id << ": " << e.what();
}
}
if (cancel)
return;
}
}
}
}
}
@ -513,8 +815,14 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version
}
// Perform a basic load and check the version of the installed preset bundle.
auto vp = VendorProfile::from_ini(bundle_path, false);
VendorProfile vp;
try {
vp = VendorProfile::from_ini(bundle_path, false);
}
catch (const std::exception& e) {
BOOST_LOG_TRIVIAL(error) << format("Corrupted profile file for vendor %1% at %2%, message: %3%", idx.vendor(), bundle_path, e.what());
continue;
}
// Getting a recommended version from the latest index, wich may have been downloaded
// from the internet, or installed / updated from the installation resources.
auto recommended = idx.recommended();
@ -590,18 +898,13 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version
if (new_vp.config_version == recommended->config_version) {
// The config bundle from the cache directory matches the recommended version of the index from the cache directory.
// This is the newest known recommended config. Use it.
if (PresetUtils::vendor_profile_has_all_resources(new_vp)) {
// All resources for the profile in cache dir are existing (either in resources or data_dir/vendor or waiting to be copied to vendor from cache)
// This final check is not performed for updates from resources dir below.
new_update = Update(std::move(path_in_cache), std::move(bundle_path), *recommended, vp.name, vp.changelog_url, current_not_supported);
// and install the config index from the cache into vendor's directory.
bundle_path_idx_to_install = idx.path();
found = true;
} else {
// Resource missing - treat as if the INI file was corrupted.
throw Slic3r::CriticalException("Some resources are missing.");
if (!PresetUtils::vendor_profile_has_all_resources(new_vp)) {
BOOST_LOG_TRIVIAL(warning) << "Some resources are missing for update of vedor " << new_vp.id;
}
new_update = Update(std::move(path_in_cache), std::move(bundle_path), *recommended, vp.name, vp.changelog_url, current_not_supported);
// and install the config index from the cache into vendor's directory.
bundle_path_idx_to_install = idx.path();
found = true;
}
} catch (const std::exception &ex) {
BOOST_LOG_TRIVIAL(info) << format("Failed to load the config bundle `%1%`: %2%", path_in_cache.string(), ex.what());
@ -715,7 +1018,7 @@ bool PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) cons
BOOST_LOG_TRIVIAL(info) << format("Performing %1% updates", updates.updates.size());
wxProgressDialog progress_dialog(_L("Installing profiles"), _L("Installing profiles"));
wxProgressDialog progress_dialog(_L("Installing profiles"), _L("Installing profiles") , 100, nullptr, wxPD_AUTO_HIDE);
progress_dialog.Pulse();
for (const auto &update : updates.updates) {
@ -755,50 +1058,16 @@ bool PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) cons
for (const auto &name : bundle.obsolete_presets.sla_prints) { obsolete_remover("sla_print", name); }
for (const auto &name : bundle.obsolete_presets.sla_materials/*filaments*/) { obsolete_remover("sla_material", name); }
for (const auto &name : bundle.obsolete_presets.printers) { obsolete_remover("printer", name); }
auto get_and_copy_missing_resource = [&self = std::as_const(*this)](const std::string& vendor, const std::string& filename,
const fs::path& vendor_path, const fs::path& rsrc_path,
const fs::path& cache_path, const std::string& url)
{
if (filename.empty() || vendor.empty())
return;
const fs::path file_in_vendor(vendor_path / (vendor + "/" + filename));
const fs::path file_in_rsrc(rsrc_path / (vendor + "/" + filename));
const fs::path file_in_cache(cache_path / (vendor + "/" + filename));
if (fs::exists(file_in_vendor)) { // Already in vendor. No need to do anything.
return;
}
if (fs::exists(file_in_rsrc)) { // In resources dir since installation. No need to do anything.
return;
}
if (!fs::exists(file_in_cache)) { // No file to copy. Download it to straight to the vendor dir.
if (!boost::starts_with(url, "http://files.prusa3d.com/wp-content/uploads/repository/") &&
!boost::starts_with(url, "https://files.prusa3d.com/wp-content/uploads/repository/"))
{
throw Slic3r::CriticalException(GUI::format("URL outside prusa3d.com network: %1%", url));
}
BOOST_LOG_TRIVIAL(info) << "Downloading resources missing in cache directory: " << vendor << " / " << filename;
const auto resource_url = format("%1%%2%%3%", url, url.back() == '/' ? "" : "/", filename); // vendor should already be in url
if (!fs::exists(file_in_vendor.parent_path()))
fs::create_directory(file_in_vendor.parent_path());
self.get_file(resource_url, file_in_vendor);
return;
}
if (!fs::exists(file_in_vendor.parent_path())) // create vendor_name dir in vendor
fs::create_directory(file_in_vendor.parent_path());
BOOST_LOG_TRIVIAL(debug) << "Copiing: " << file_in_cache << " to " << file_in_vendor;
copy_file_fix(file_in_cache, file_in_vendor);
};
// check if any resorces of installed bundle are missing. If so, new ones should be already downloaded at cache/vendor_id/
auto vp = VendorProfile::from_ini(update.target, true);
VendorProfile vp;
try {
vp = VendorProfile::from_ini(update.target, true);
}
catch (const std::exception& e) {
BOOST_LOG_TRIVIAL(error) << format("Corrupted profile file for vendor %1%, message: %2%", update.target, e.what());
continue;
}
progress_dialog.Update(1, GUI::format_wxstr(_L("Downloading resources for %1%."),vp.id));
progress_dialog.Pulse();
for (const auto& model : vp.models) {
@ -807,17 +1076,14 @@ bool PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) cons
continue;
try
{
// for debug (wont pass check inside function)
//std::string fake_url = "https://github.com/kocikdav/PrusaSlicer-settings/raw/master/resources/" + vp.id;
get_and_copy_missing_resource(vp.id, resource, vendor_path, rsrc_path, cache_path, vp.config_update_url);
get_or_copy_missing_resource(vp.id, resource, vp.config_update_url);
}
catch (const std::exception& e)
{
BOOST_LOG_TRIVIAL(error) << "Failed to prepare " << resource << " for " << vp.id << " " << model.id << ": " << e.what();
}
}
}
}
}
progress_dialog.Destroy();
@ -825,7 +1091,7 @@ bool PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) cons
return true;
}
void PresetUpdater::priv::set_waiting_updates(Updates u)
{
waiting_updates = u;
@ -858,11 +1124,11 @@ void PresetUpdater::sync(const PresetBundle *preset_bundle)
// Unfortunatelly as of C++11, it needs to be copied again
// into the closure (but perhaps the compiler can elide this).
VendorMap vendors = preset_bundle->vendors;
std::string profile_archive_url =GUI::wxGetApp().app_config->profile_archive_url();
std::string index_archive_url = GUI::wxGetApp().app_config->index_archive_url();
p->thread = std::thread([this, vendors, profile_archive_url]() {
p->thread = std::thread([this, vendors, index_archive_url]() {
this->p->prune_tmps();
this->p->sync_config(std::move(vendors), profile_archive_url);
this->p->sync_config(std::move(vendors), index_archive_url);
});
}
@ -1052,16 +1318,40 @@ bool PresetUpdater::install_bundles_rsrc_or_cache_vendor(std::vector<std::string
auto path_in_vendors = (p->vendor_path / bundle).replace_extension(".ini");
bool is_in_rsrc = fs::exists(path_in_rsrc);
bool is_in_cache_vendor = fs::exists(path_in_cache_vendor);
bool is_in_cache_vendor = fs::exists(path_in_cache_vendor) && !fs::is_empty(path_in_cache_vendor);
// find if in cache vendor is newer version than in resources
if (is_in_cache_vendor) {
auto version_cache = VendorProfile::from_ini(path_in_cache_vendor, false).config_version;
auto version_rsrc = !is_in_rsrc ? Semver::zero() : VendorProfile::from_ini(path_in_rsrc, false).config_version;
Semver version_cache = Semver::zero();
try {
auto vp_cache = VendorProfile::from_ini(path_in_cache_vendor, false);
version_cache = vp_cache.config_version;
}
catch (const std::exception& e) {
BOOST_LOG_TRIVIAL(error) << format("Corrupted profile file for vendor %1%, message: %2%", path_in_cache_vendor, e.what());
// lets use file in resources
if (is_in_rsrc) {
updates.updates.emplace_back(std::move(path_in_rsrc), std::move(path_in_vendors), Version(), "", "");
}
continue;
}
Semver version_rsrc = Semver::zero();
try {
if (is_in_rsrc) {
auto vp = VendorProfile::from_ini(path_in_rsrc, false);
version_rsrc = vp.config_version;
}
}
catch (const std::exception& e) {
BOOST_LOG_TRIVIAL(error) << format("Corrupted profile file for vendor %1%, message: %2%", path_in_rsrc, e.what());
continue;
}
if (!is_in_rsrc || version_cache > version_rsrc) {
// in case we are installing from cache / vendor. we should also copy index to cache
// This needs to be done now bcs the current one would be missing this version on next start
// dk: Should we copy it to vendor dir too?
auto path_idx_cache_vendor(path_in_cache_vendor);
path_idx_cache_vendor.replace_extension(".idx");
auto path_idx_cache = (p->cache_path / bundle).replace_extension(".idx");
@ -1121,4 +1411,9 @@ bool PresetUpdater::version_check_enabled() const
return p->enabled_version_check;
}
void PresetUpdater::update_index_db()
{
p->update_index_db();
}
}

View File

@ -53,6 +53,8 @@ public:
// Providing old slic3r version upgrade profiles on upgrade of an application even in case
// that the config index installed from the Internet is equal to the index contained in the installation package.
UpdateResult config_update(const Semver &old_slic3r_version, UpdateParams params) const;
void update_index_db();
// "Update" a list of bundles from resources or cache/vendor (behaves like an online update).
bool install_bundles_rsrc_or_cache_vendor(std::vector<std::string> bundles, bool snapshot = true) const;