* Add OrcaCloud sync platform and preset bundle sharing system

  Introduce OrcaCloud, a cloud sync platform for user presets, alongside
  a preset bundle system that enables sharing printer/filament/process
  profiles as local exportable bundles or subscribed cloud bundles.

  OrcaCloud platform:
  - Auth to Orca Cloud
  - Encrypted token storage (file-based or system keychain)
  - User preset sync with
  - Profile migration from default/bambu folders on first login
  - Homepage integration with entrance to cloud.orcaslicer.com

  Preset bundles:
  - Local bundle import/export with bundle_structure.json metadata
  - Subscribed cloud bundles with version-based update checking
  - Thread-safe concurrent bundle access with read-write mutex
  - Canonical bundle preset naming (_local/<id>/... and _subscribed/<id>/...)
  - Bundle presets are read-only; grouped under subheaders in combo boxes
  - PresetBundleDialog with auto-sync toggle, refresh, update notifications
  - Hyperlinked bundle names to cloud bundle pages

  Co-authored-by: Sabriel Koh <sabrielkcr@gmail.com>
  Co-authored-by: Derrick <derrick992110@gmail.com>
  Co-authored-by: Mykola Nahirnyi <mnahirnyi@amcbridge.com>
  Co-authored-by: Ian Chua <iancrb00@gmail.com>
  Co-authored-by: Draginraptor <draginraptor@gmail.com>
  Co-authored-by: ExPikaPaka <112851715+ExPikaPaka@users.noreply.github.com>
  Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
  Co-authored-by: Ocraftyone <Ocraftyone@users.noreply.github.com>
  Co-authored-by: yw4z <ywsyildiz@gmail.com>
  Co-authored-by: peterm-m <101202951+peterm-m@users.noreply.github.com>

* Fixed an issue on Windows it failed to login Orca Cloud with Google account
This commit is contained in:
SoftFever
2026-05-01 18:01:29 +08:00
committed by GitHub
parent e54e7a61c0
commit c04be9ab37
113 changed files with 8690 additions and 3466 deletions
+48 -7
View File
@@ -29,6 +29,11 @@
#define PRESET_TEMPLATE_DIR "Template"
#define PRESET_CUSTOM_VENDOR "Custom"
// Orca: bundle import directories
#define PRESET_LOCAL_DIR "_local"
#define PRESET_SUBSCRIBED_DIR "_subscribed"
#define PRESET_BUNDLE_METADATA "bundle_metadata.json"
//BBS: iot preset type strings
#define PRESET_IOT_PRINTER_TYPE "printer"
#define PRESET_IOT_FILAMENT_TYPE "filament"
@@ -228,7 +233,8 @@ public:
//BBS: add type for project-embedded
bool is_project_embedded = false;
ConfigSubstitutions *loading_substitutions{nullptr};
bool is_user() const { return ! this->is_default && ! this->is_system && ! this->is_project_embedded; }
bool is_user() const { return ! this->is_default && ! this->is_system && ! this->is_project_embedded && ! this->is_from_bundle(); }
bool can_overwrite() const { return ! this->is_default && ! this->is_system && ! this->is_from_bundle(); }
//bool is_user() const { return ! this->is_default && ! this->is_system; }
// Name of the preset, usually derived form the file name.
@@ -261,6 +267,11 @@ public:
// Orca: flag to indicate if this preset is from Orca Filament Library
bool m_from_orca_filament_lib = false;
// Orca: bundle tracking - imported preset bundles. Bundle ID: UUID (OrcaCloud) or name+timestamp (external).
// Presence of bundle_id is the source of truth for "came from a bundle".
std::string bundle_id;
bool is_from_bundle() const { return ! bundle_id.empty(); }
//BBS
Semver version; // version of preset
std::string ini_str; // ini string of preset
@@ -279,7 +290,7 @@ public:
static Preset::Type get_type_from_string(std::string type_str);
void load_info(const std::string& file);
void save_info(std::string file = "");
void remove_files();
void remove_files(bool cloud_already_deleted = false);
//BBS: add logic for only difference save
//if parent_config is null, save all keys, otherwise, only save difference
@@ -395,6 +406,28 @@ bool is_compatible_with_print (const PresetWithVendorProfile &preset, const Pre
bool is_compatible_with_printer(const PresetWithVendorProfile &preset, const PresetWithVendorProfile &active_printer, const DynamicPrintConfig *extra_config);
bool is_compatible_with_printer(const PresetWithVendorProfile &preset, const PresetWithVendorProfile &active_printer);
// Where a preset is being loaded from. `Auto` lets load_presets() infer from the directory path.
struct PresetOrigin {
enum class Kind { Auto, User, LocalBundle, SubscribedBundle };
Kind kind { Kind::Auto };
std::string bundle_id;
PresetOrigin() = default;
PresetOrigin(Kind kind, std::string bundle_id = {}) : kind(kind), bundle_id(std::move(bundle_id)) {}
bool is_bundle() const { return kind == Kind::LocalBundle || kind == Kind::SubscribedBundle; }
};
// Prepend the bundle folder to `preset_bare_name` based on `origin`. No-op for non-bundle origins.
std::string get_preset_canonical_name(const std::string &preset_bare_name, const PresetOrigin &origin);
// Tail segment of a canonical name — what's written to the bundle's .json filename and JSON "name" field.
std::string get_preset_bare_name(const std::string &canonical_name);
// Resolve an origin from a directory path when the caller passes Kind::Auto.
PresetOrigin detect_origin_from_path(const boost::filesystem::path &path, const PresetOrigin &explicit_origin = PresetOrigin());
enum class PresetSelectCompatibleType {
// Never select a compatible preset if the newly selected profile is not compatible.
Never,
@@ -471,12 +504,12 @@ public:
void add_default_preset(const std::vector<std::string> &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &preset_name);
// Load ini files of the particular type from the provided directory path.
void load_presets(const std::string &dir_path, const std::string &subdir, PresetsConfigSubstitutions& substitutions, ForwardCompatibilitySubstitutionRule rule);
void load_presets(const std::string &dir_path, const std::string &subdir, PresetsConfigSubstitutions& substitutions, ForwardCompatibilitySubstitutionRule rule, std::function<void(Preset&)> preset_loaded_fn = nullptr, const PresetOrigin &load_origin = PresetOrigin());
//BBS: update user presets directory
void update_user_presets_directory(const std::string& dir_path, const std::string& type);
void save_user_presets(const std::string& dir_path, const std::string& type, std::vector<std::string>& need_to_delete_list);
bool load_user_preset(std::string name, std::map<std::string, std::string> preset_values, PresetsConfigSubstitutions& substitutions, ForwardCompatibilitySubstitutionRule rule);
void save_user_presets(const std::string& dir_path, const std::string& type, std::map<std::string, std::string>& need_to_delete_list);
bool load_user_preset(std::string name, std::map<std::string, std::string> preset_values, PresetsConfigSubstitutions& substitutions, ForwardCompatibilitySubstitutionRule rule, const PresetOrigin &load_origin = PresetOrigin(PresetOrigin::Kind::User));
void update_after_user_presets_loaded();
//BBS: get user presets
int get_user_presets(PresetBundle *preset_bundle, std::vector<Preset> &result_presets);
@@ -550,7 +583,11 @@ public:
bool delete_current_preset();
// Delete the current preset, activate the first visible preset.
// returns true if the preset was deleted successfully.
bool delete_preset(const std::string& name);
// When force=true, bypasses the can_overwrite() check (used for bundle preset cleanup).
bool delete_preset(const std::string& name, bool force = false);
// Verify and correct the sync metadata for the preset to ensure proper cloud synchronization.
void check_and_fix_syncinfo(Preset& preset, const std::string& user_id);
// Enable / disable the "- default -" preset.
void set_default_suppressed(bool default_suppressed);
@@ -637,6 +674,7 @@ public:
{
return const_cast<PresetCollection*>(this)->find_preset2(name, auto_match);
}
size_t first_visible_idx() const;
// Return the index of the first visible, compatible, system base preset
// matching the given filament_type. Falls back to base type, then any visible.
@@ -774,6 +812,8 @@ protected:
void set_custom_preset_alias(Preset &preset);
private:
std::string canonical_preset_name(const std::string &name, const PresetOrigin &load_origin = PresetOrigin()) const;
// Comparator that sorts "Generic " prefixed presets before others, then alphabetically within each group.
static bool filament_preset_less(const Preset &a, const Preset &b) {
bool a_generic = boost::starts_with(a.name, GENERIC_PREFIX);
@@ -795,6 +835,7 @@ private:
// The "-- default -- " preset is always the first, so it needs
// to be handled differently.
// If a preset does not exist, an iterator is returned indicating where to insert a preset with the same name.
// `name` must already be canonical — callers canonicalize via find_preset / canonical_preset_name.
std::deque<Preset>::iterator find_preset_internal(const std::string &name, bool from_orca_lib_only = false)
{
auto it = Slic3r::lower_bound_by_predicate(m_presets.begin() + m_num_default_presets, m_presets.end(),
@@ -864,7 +905,7 @@ private:
friend class PresetBundle;
//BBS: mutex
std::mutex m_mutex;
std::recursive_mutex m_mutex;
// Orca: used for validation only
int m_errors = 0;