Compare commits

..

3 Commits

Author SHA1 Message Date
Ian Chua
70a6403ab0 fix: update API response shape 2026-07-22 18:20:45 +08:00
Ian Chua
e7936297a1 feat: draft endpoint for fetching new vendor profiles 2026-07-22 18:20:45 +08:00
Ian Chua
b1a66da542 feat: make updater url app-configurable 2026-07-22 18:20:44 +08:00
2 changed files with 107 additions and 13 deletions

View File

@@ -42,6 +42,9 @@ namespace Slic3r {
static const std::string VERSION_CHECK_URL = "https://check-version.orcaslicer.com/latest";
static const std::string PROFILE_UPDATE_URL = "https://check-version.orcaslicer.com/profile";
constexpr const char* CONFIG_ORCA_UPDATER_URL = "orca_updater_url";
static const std::string MODELS_STR = "models";
const std::string AppConfig::SECTION_FILAMENTS = "filaments";
@@ -1747,7 +1750,10 @@ std::string AppConfig::version_check_url() const
std::string AppConfig::profile_update_url() const
{
return PROFILE_UPDATE_URL;
std::string orca_updater_url = get(CONFIG_ORCA_UPDATER_URL);
if (orca_updater_url.empty())
return PROFILE_UPDATE_URL;
return orca_updater_url;
}
bool AppConfig::exists()

View File

@@ -1,12 +1,15 @@
#include "PresetUpdater.hpp"
#include <algorithm>
#include <boost/filesystem/directory.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/nowide/fstream.hpp>
#include <functional>
#include <atomic>
#include <mutex>
#include <set>
#include <string>
#include <thread>
#include <unordered_map>
#include <ostream>
@@ -19,6 +22,7 @@
#include <boost/lexical_cast.hpp>
#include <boost/log/trivial.hpp>
#include <vector>
#include <wx/app.h>
#include <wx/msgdlg.h>
@@ -204,6 +208,8 @@ struct PresetUpdater::priv
// Per-vendor update checking
std::set<std::string> checked_vendors;
std::unordered_map<std::string, std::string> vendor_changelogs;
mutable std::mutex vendor_changelogs_mutex;
std::mutex vendor_check_mutex;
std::vector<std::thread> vendor_check_threads;
std::atomic<bool> vendor_check_cancel{false};
@@ -229,6 +235,8 @@ struct PresetUpdater::priv
void parse_version_string(const std::string& body) const;
void sync_resources(std::string http_url, std::map<std::string, Resource> &resources, bool check_patch = false, std::string current_version="", std::string changelog_file="");
void sync_vendor_config(const std::string& vendor_id);
std::vector<std::string> check_new_vendors();
std::set<std::string> get_all_system_vendors();
void sync_tooltip(std::string http_url, std::string language);
void sync_plugins(std::string http_url, std::string plugin_version);
void sync_printer_config(std::string http_url);
@@ -669,6 +677,9 @@ void PresetUpdater::priv::sync_vendor_config(const std::string& vendor_id)
std::string online_version_str; // this represents the PROFILE VERSION, not ORCA VERSION
std::string download_url_str;
std::string changelog;
BOOST_LOG_TRIVIAL(info) << "[Orca Updater] fetching vendor update status from " << url;
Http::get(url)
.timeout_connect(5)
@@ -681,9 +692,13 @@ void PresetUpdater::priv::sync_vendor_config(const std::string& vendor_id)
if (http_status != 200) return;
try {
json j = json::parse(body);
if (j.contains("vendor_version") && j.contains("download_url")) {
online_version_str = j["vendor_version"].get<std::string>();
BOOST_LOG_TRIVIAL(info) << "[Orca Updater] url: " << url << " returned:" << body;
if ((j.contains("version") || j.contains("vendor_version")) && j.contains("download_url")) {
online_version_str = j.contains("version") ? j["version"].get<std::string>()
: j["vendor_version"].get<std::string>();
download_url_str = j["download_url"].get<std::string>();
changelog = j.value("changelog", std::string());
}
} catch (const std::exception& e) {
BOOST_LOG_TRIVIAL(warning) << "[Orca Updater] vendor check JSON parse failed: " << e.what();
@@ -705,7 +720,6 @@ void PresetUpdater::priv::sync_vendor_config(const std::string& vendor_id)
boost::system::error_code ec;
fs::remove_all(cache_profile_path / vendor_id, ec);
fs::remove(cache_profile_path / (vendor_id + ".json"), ec);
fs::remove(cache_profile_path / (vendor_id + ".changelog"), ec);
// Download the zip
BOOST_LOG_TRIVIAL(info) << "[Orca Updater] downloading update for " << vendor_id
@@ -743,6 +757,11 @@ void PresetUpdater::priv::sync_vendor_config(const std::string& vendor_id)
if (cancel || vendor_check_cancel) return;
{
std::lock_guard<std::mutex> lock(vendor_changelogs_mutex);
vendor_changelogs[vendor_id] = std::move(changelog);
}
BOOST_LOG_TRIVIAL(info) << "[Orca Updater] vendor " << vendor_id << " update cached, notifying UI";
GUI::wxGetApp().CallAfter([] {
GUI::wxGetApp().check_config_updates_from_updater();
@@ -1184,6 +1203,12 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version
if (!fs::exists(cache_profile_path))
return updates;
std::unordered_map<std::string, std::string> changelogs;
{
std::lock_guard<std::mutex> lock(vendor_changelogs_mutex);
changelogs = vendor_changelogs;
}
for (auto &dir_entry : boost::filesystem::directory_iterator(cache_profile_path)) {
const auto &path = dir_entry.path();
std::string file_path = path.string();
@@ -1217,15 +1242,8 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version
if (config_version)
cache_ver = *config_version;
std::string changelog;
std::string changelog_file = (cache_profile_path / (vendor_name + ".changelog")).string();
boost::nowide::ifstream ifs(changelog_file);
if (ifs) {
std::ostringstream oss;
oss<< ifs.rdbuf();
changelog = oss.str();
ifs.close();
}
const auto changelog_it = changelogs.find(vendor_name);
std::string changelog = changelog_it != changelogs.end() ? changelog_it->second : std::string();
if (vendor_ver < cache_ver) {
BOOST_LOG_TRIVIAL(info) << "[Orca Updater]:need to update settings from " << vendor_ver.to_string()
@@ -1404,6 +1422,76 @@ void PresetUpdater::check_vendor_update(const std::string& vendor_id)
});
}
// TBD the actual shape of the reponse before using this.
std::vector<std::string> PresetUpdater::priv::check_new_vendors()
{
AppConfig* app_config = GUI::wxGetApp().app_config;
std::string url = app_config->profile_update_url() + "/new?orca_version=" + Http::url_encode(SoftFever_VERSION);
auto check_cancel = [this](Http::Progress, bool& cancel_http) {
if (cancel || vendor_check_cancel)
cancel_http = true;
};
std::vector<std::string> vendor_ids;
const auto system_vendors = get_all_system_vendors();
const std::vector<std::string> system_vendor_list(system_vendors.begin(), system_vendors.end());
auto post = Http::post(url);
post.timeout_connect(5);
post.on_progress(check_cancel);
post.on_error([](std::string body, std::string error, unsigned http_status) {
BOOST_LOG_TRIVIAL(warning) << "[Orca Updater] new vendor check HTTP error: " << error;
})
.on_complete([&vendor_ids](std::string body, unsigned http_status) {
if (http_status != 200)
return;
try {
json j = json::parse(body);
if (j.is_array()) {
vendor_ids = j.get<std::vector<std::string>>();
}
} catch (const std::exception& e) {
BOOST_LOG_TRIVIAL(warning) << "[Orca Updater] vendor check JSON parse failed: " << e.what();
}
});
post.set_post_body(json(system_vendor_list).dump());
post.perform_sync();
return vendor_ids;
}
std::set<std::string> PresetUpdater::priv::get_all_system_vendors() {
std::set<std::string> vendors;
PresetBundle temp_bundle;
boost::filesystem::path dir = boost::filesystem::path(resources_dir()) / "profiles";
for (auto& entry : boost::filesystem::directory_iterator(dir)) {
if (!Slic3r::is_json_file(entry.path().string()))
continue;
std::string vendor_name = entry.path().filename().stem().string();
temp_bundle.load_vendor_configs_from_json(dir.string(), vendor_name, PresetBundle::LoadSystem,
ForwardCompatibilitySubstitutionRule::EnableSystemSilent);
}
for (auto& preset : temp_bundle.printers)
if (preset.is_system)
vendors.insert(preset.vendor->name);
for (auto& preset : temp_bundle.prints)
if (preset.is_system)
vendors.insert(preset.vendor->name);
for (auto& preset : temp_bundle.filaments)
if (preset.is_system)
vendors.insert(preset.vendor->name);
return vendors;
}
void PresetUpdater::slic3r_update_notify()
{
if (! p->enabled_version_check)