diff --git a/main.js b/main.js index 160bc85..2f55529 100644 --- a/main.js +++ b/main.js @@ -5,6 +5,7 @@ const CameraParser = require("./src/main/CameraParser.js"); const QueueManager = require("./src/main/QueueManager.js"); const ConfigManager = require("./src/main/ConfigManager.js"); const UpdateManager = require("./src/main/UpdateManager.js"); +const PathResolver = require("./src/main/PathResolver.js"); let obj_main_window = null; let obj_queue_manager = null; @@ -27,9 +28,12 @@ const create_window = () => { obj_queue_manager = new QueueManager(obj_main_window); + PathResolver.load_saved_path(); + UpdateManager.init(obj_main_window); obj_main_window.webContents.on("did-finish-load", () => { UpdateManager.check_for_updates(); + obj_main_window.webContents.send("blender-path-status", PathResolver.get_status()); }); }; @@ -159,6 +163,37 @@ ipcMain.handle("read-image", (event, str_image_path) => { return fn_try_read(0); }); +ipcMain.handle("get-blender-path", () => { + return PathResolver.get_status(); +}); + +ipcMain.handle("set-blender-path", (event, str_path) => { + let obj_result = PathResolver.set_blender_path(str_path); + if (obj_result.is_success) { + obj_main_window.webContents.send("blender-path-status", PathResolver.get_status()); + } + return obj_result; +}); + +ipcMain.handle("select-blender-exe", () => { + let str_exe_name = process.platform === "win32" ? "blender.exe" : "blender"; + let list_filters = process.platform === "win32" + ? [{ name: "Blender", extensions: ["exe"] }] + : [{ name: "Blender", extensions: ["*"] }]; + + return dialog.showOpenDialog(obj_main_window, { + title: "Selectionner l'executable Blender", + filters: list_filters, + properties: ["openFile"], + }) + .then((obj_result) => { + if (obj_result.canceled || obj_result.filePaths.length === 0) { + return null; + } + return obj_result.filePaths[0]; + }); +}); + ipcMain.handle("check-for-updates", () => { return UpdateManager.check_for_updates(); }); diff --git a/package.json b/package.json index 70ff36a..3f4d62e 100644 --- a/package.json +++ b/package.json @@ -23,13 +23,6 @@ "src/**/*", "version.json" ], - "extraResources": [ - { - "from": "blender", - "to": "blender", - "filter": ["**/*"] - } - ], "linux": { "target": "dir" }, diff --git a/preload.js b/preload.js index 346f5e6..bfc7284 100644 --- a/preload.js +++ b/preload.js @@ -67,6 +67,24 @@ contextBridge.exposeInMainWorld("api", { }); }, + get_blender_path: () => { + return ipcRenderer.invoke("get-blender-path"); + }, + + set_blender_path: (str_path) => { + return ipcRenderer.invoke("set-blender-path", str_path); + }, + + select_blender_exe: () => { + return ipcRenderer.invoke("select-blender-exe"); + }, + + on_blender_path_status: (fn_callback) => { + ipcRenderer.on("blender-path-status", (event, obj_data) => { + fn_callback(obj_data); + }); + }, + check_for_updates: () => { return ipcRenderer.invoke("check-for-updates"); }, diff --git a/src/main/PathResolver.js b/src/main/PathResolver.js index f663141..e2ccf2f 100644 --- a/src/main/PathResolver.js +++ b/src/main/PathResolver.js @@ -1,47 +1,154 @@ const path = require("path"); const fs = require("fs"); +const { app } = require("electron"); +const { execFileSync } = require("child_process"); const STR_EXE_NAME = process.platform === "win32" ? "blender.exe" : "blender"; +const STR_CONFIG_FILE = "blender_path.json"; const PathResolver = { _str_blender_path: null, + _is_found: false, - get_blender_path: () => { - if (PathResolver._str_blender_path) { - return PathResolver._str_blender_path; + load_saved_path: () => { + let str_config_path = PathResolver._get_config_path(); + + try { + if (fs.existsSync(str_config_path)) { + let str_content = fs.readFileSync(str_config_path, "utf8"); + let obj_data = JSON.parse(str_content); + + if (obj_data.str_path && fs.existsSync(obj_data.str_path)) { + PathResolver._str_blender_path = obj_data.str_path; + PathResolver._is_found = true; + return; + } + } + } catch (obj_err) { + console.error("PathResolver: impossible de lire la config :", obj_err.message); } - // Mode package : resources/blender/ - let str_resources_dir = path.join(process.resourcesPath, "blender"); - let str_found = PathResolver._find_in_dir(str_resources_dir); - if (str_found) { - PathResolver._str_blender_path = str_found; - return str_found; + let str_detected = PathResolver.auto_detect(); + if (str_detected) { + PathResolver._str_blender_path = str_detected; + PathResolver._is_found = true; + } else { + PathResolver._str_blender_path = "blender"; + PathResolver._is_found = false; } - - // Mode dev : racine projet/blender/ - let str_dev_dir = path.join(__dirname, "..", "..", "blender"); - str_found = PathResolver._find_in_dir(str_dev_dir); - if (str_found) { - PathResolver._str_blender_path = str_found; - return str_found; - } - - // Fallback : PATH systeme - PathResolver._str_blender_path = "blender"; - return "blender"; }, - _find_in_dir: (str_dir) => { - if (!fs.existsSync(str_dir)) { - return null; + get_blender_path: () => { + if (!PathResolver._str_blender_path) { + PathResolver.load_saved_path(); + } + return PathResolver._str_blender_path; + }, + + is_found: () => { + return PathResolver._is_found; + }, + + set_blender_path: (str_path) => { + if (!str_path || !fs.existsSync(str_path)) { + return { is_success: false, str_error: "Fichier introuvable : " + str_path }; } - let list_entries = fs.readdirSync(str_dir); - for (let str_entry of list_entries) { - let str_exe = path.join(str_dir, str_entry, STR_EXE_NAME); - if (fs.existsSync(str_exe)) { - return str_exe; + PathResolver._str_blender_path = str_path; + PathResolver._is_found = true; + + let str_config_path = PathResolver._get_config_path(); + try { + let str_dir = path.dirname(str_config_path); + if (!fs.existsSync(str_dir)) { + fs.mkdirSync(str_dir, { recursive: true }); + } + fs.writeFileSync(str_config_path, JSON.stringify({ str_path: str_path }, null, 4), "utf8"); + } catch (obj_err) { + console.error("PathResolver: impossible de sauvegarder :", obj_err.message); + } + + return { is_success: true, str_path: str_path }; + }, + + auto_detect: () => { + if (process.platform === "win32") { + return PathResolver._auto_detect_windows(); + } + return PathResolver._auto_detect_linux(); + }, + + get_status: () => { + return { + str_path: PathResolver._str_blender_path || "blender", + is_found: PathResolver._is_found, + }; + }, + + // ── Private ────────────────────────────────────────────── + + _get_config_path: () => { + return path.join(app.getPath("userData"), STR_CONFIG_FILE); + }, + + _auto_detect_linux: () => { + let LIST_PATHS = [ + "/usr/bin/blender", + "/snap/bin/blender", + "/usr/local/bin/blender", + "/opt/blender/blender", + ]; + + try { + let str_result = execFileSync("which", ["blender"], { encoding: "utf8", timeout: 5000 }).trim(); + if (str_result && fs.existsSync(str_result)) { + return str_result; + } + } catch (obj_err) { + // which not found or blender not in PATH + } + + for (let str_path of LIST_PATHS) { + if (fs.existsSync(str_path)) { + return str_path; + } + } + + return null; + }, + + _auto_detect_windows: () => { + try { + let str_result = execFileSync("where", ["blender.exe"], { encoding: "utf8", timeout: 5000 }).trim(); + let str_first = str_result.split("\n")[0].trim(); + if (str_first && fs.existsSync(str_first)) { + return str_first; + } + } catch (obj_err) { + // where not found or blender not in PATH + } + + let LIST_BASES = [ + process.env.PROGRAMFILES || "C:\\Program Files", + process.env["PROGRAMFILES(X86)"] || "C:\\Program Files (x86)", + ]; + + for (let str_base of LIST_BASES) { + let str_foundation = path.join(str_base, "Blender Foundation"); + if (!fs.existsSync(str_foundation)) { + continue; + } + + try { + let list_dirs = fs.readdirSync(str_foundation); + for (let str_dir of list_dirs) { + let str_exe = path.join(str_foundation, str_dir, STR_EXE_NAME); + if (fs.existsSync(str_exe)) { + return str_exe; + } + } + } catch (obj_err) { + // permission denied or similar } } diff --git a/src/renderer/index.html b/src/renderer/index.html index ee6b093..aa317d8 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -3,7 +3,7 @@
- +