Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5cb63a27b |
35
main.js
35
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();
|
||||
});
|
||||
|
||||
@@ -23,13 +23,6 @@
|
||||
"src/**/*",
|
||||
"version.json"
|
||||
],
|
||||
"extraResources": [
|
||||
{
|
||||
"from": "blender",
|
||||
"to": "blender",
|
||||
"filter": ["**/*"]
|
||||
}
|
||||
],
|
||||
"linux": {
|
||||
"target": "dir"
|
||||
},
|
||||
|
||||
18
preload.js
18
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");
|
||||
},
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; style-src 'self' https://cdn.jsdelivr.net 'unsafe-inline'; font-src https://cdn.jsdelivr.net; img-src 'self' file: data:;">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' https://cdn.jsdelivr.net; style-src 'self' https://cdn.jsdelivr.net 'unsafe-inline'; font-src https://cdn.jsdelivr.net; img-src 'self' file: data:;">
|
||||
<title>Multi Render Blender</title>
|
||||
|
||||
<!-- Bootstrap 5 -->
|
||||
@@ -253,6 +253,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
<!-- Scripts -->
|
||||
<script src="scripts/ConsoleLog.js"></script>
|
||||
<script src="scripts/CameraList.js"></script>
|
||||
@@ -261,6 +264,7 @@
|
||||
<script src="scripts/PreviewPanel.js"></script>
|
||||
<script src="scripts/ProgressBar.js"></script>
|
||||
<script src="scripts/UpdateBanner.js"></script>
|
||||
<script src="scripts/BlenderPath.js"></script>
|
||||
<script src="scripts/App.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -10,6 +10,7 @@ const App = {
|
||||
PreviewPanel.init();
|
||||
ProgressBar.init();
|
||||
UpdateBanner.init();
|
||||
BlenderPath.init();
|
||||
|
||||
App._bind_events();
|
||||
App._bind_render_events();
|
||||
|
||||
197
src/renderer/scripts/BlenderPath.js
Normal file
197
src/renderer/scripts/BlenderPath.js
Normal file
@@ -0,0 +1,197 @@
|
||||
const BlenderPath = {
|
||||
str_current_path: null,
|
||||
is_found: false,
|
||||
obj_modal: null,
|
||||
|
||||
init: () => {
|
||||
BlenderPath._create_badge();
|
||||
BlenderPath._create_modal();
|
||||
BlenderPath._bind_events();
|
||||
},
|
||||
|
||||
_create_badge: () => {
|
||||
let obj_nav_right = document.querySelector("nav .d-flex.gap-2");
|
||||
let obj_badge = document.createElement("button");
|
||||
obj_badge.id = "btn_blender_status";
|
||||
obj_badge.className = "btn btn-sm btn-outline-secondary";
|
||||
obj_badge.title = "Chemin Blender";
|
||||
obj_badge.innerHTML = '<i class="mdi mdi-blender-software"></i>';
|
||||
obj_nav_right.insertBefore(obj_badge, obj_nav_right.firstChild);
|
||||
|
||||
obj_badge.addEventListener("click", () => {
|
||||
BlenderPath._open_modal();
|
||||
});
|
||||
},
|
||||
|
||||
_create_modal: () => {
|
||||
let obj_modal_el = document.createElement("div");
|
||||
obj_modal_el.id = "modal_blender_path";
|
||||
obj_modal_el.className = "modal fade";
|
||||
obj_modal_el.tabIndex = -1;
|
||||
obj_modal_el.innerHTML =
|
||||
'<div class="modal-dialog modal-dialog-centered">' +
|
||||
'<div class="modal-content bg-dark text-light border-secondary">' +
|
||||
'<div class="modal-header border-secondary">' +
|
||||
'<h6 class="modal-title"><i class="mdi mdi-blender-software me-2"></i>Chemin Blender</h6>' +
|
||||
'<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>' +
|
||||
'</div>' +
|
||||
'<div class="modal-body">' +
|
||||
'<p class="text-light-emphasis mb-3" style="font-size: 0.85rem;">' +
|
||||
'Selectionnez l\'executable Blender sur votre machine.' +
|
||||
'</p>' +
|
||||
'<div class="input-group input-group-sm mb-3">' +
|
||||
'<input type="text" id="input_blender_path" class="form-control bg-dark text-light border-secondary" placeholder="Aucun chemin configure" readonly>' +
|
||||
'<button id="btn_browse_blender" class="btn btn-outline-primary" type="button">' +
|
||||
'<i class="mdi mdi-folder-search-outline"></i> Parcourir' +
|
||||
'</button>' +
|
||||
'</div>' +
|
||||
'<div class="d-flex justify-content-between align-items-center">' +
|
||||
'<button id="btn_detect_blender" class="btn btn-sm btn-outline-secondary">' +
|
||||
'<i class="mdi mdi-magnify me-1"></i>Detecter automatiquement' +
|
||||
'</button>' +
|
||||
'<span id="label_blender_status" class="badge bg-danger">' +
|
||||
'<i class="mdi mdi-close-circle me-1"></i>Non trouve' +
|
||||
'</span>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="modal-footer border-secondary">' +
|
||||
'<button type="button" class="btn btn-sm btn-secondary" data-bs-dismiss="modal">Annuler</button>' +
|
||||
'<button id="btn_validate_blender" type="button" class="btn btn-sm btn-primary" disabled>' +
|
||||
'<i class="mdi mdi-check me-1"></i>Valider' +
|
||||
'</button>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
|
||||
document.body.appendChild(obj_modal_el);
|
||||
BlenderPath.obj_modal = new bootstrap.Modal(obj_modal_el);
|
||||
},
|
||||
|
||||
_bind_events: () => {
|
||||
let obj_btn_browse = document.getElementById("btn_browse_blender");
|
||||
obj_btn_browse.addEventListener("click", () => {
|
||||
BlenderPath._browse();
|
||||
});
|
||||
|
||||
let obj_btn_detect = document.getElementById("btn_detect_blender");
|
||||
obj_btn_detect.addEventListener("click", () => {
|
||||
BlenderPath._detect();
|
||||
});
|
||||
|
||||
let obj_btn_validate = document.getElementById("btn_validate_blender");
|
||||
obj_btn_validate.addEventListener("click", () => {
|
||||
BlenderPath._validate();
|
||||
});
|
||||
|
||||
window.api.on_blender_path_status((obj_data) => {
|
||||
BlenderPath.str_current_path = obj_data.str_path;
|
||||
BlenderPath.is_found = obj_data.is_found;
|
||||
BlenderPath._update_badge();
|
||||
|
||||
if (!obj_data.is_found) {
|
||||
BlenderPath._open_modal();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_open_modal: () => {
|
||||
let obj_input = document.getElementById("input_blender_path");
|
||||
obj_input.value = BlenderPath.is_found ? BlenderPath.str_current_path : "";
|
||||
BlenderPath._update_modal_status(BlenderPath.is_found, BlenderPath.str_current_path);
|
||||
BlenderPath.obj_modal.show();
|
||||
|
||||
if (!BlenderPath.is_found) {
|
||||
BlenderPath._detect();
|
||||
}
|
||||
},
|
||||
|
||||
_browse: () => {
|
||||
window.api.select_blender_exe()
|
||||
.then((str_path) => {
|
||||
if (!str_path) {
|
||||
return;
|
||||
}
|
||||
let obj_input = document.getElementById("input_blender_path");
|
||||
obj_input.value = str_path;
|
||||
BlenderPath._update_modal_status(true, str_path);
|
||||
})
|
||||
.catch((obj_err) => {
|
||||
ConsoleLog.add("Erreur selection Blender : " + obj_err.message);
|
||||
});
|
||||
},
|
||||
|
||||
_detect: () => {
|
||||
let obj_btn = document.getElementById("btn_detect_blender");
|
||||
obj_btn.disabled = true;
|
||||
obj_btn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span>Detection...';
|
||||
|
||||
window.api.get_blender_path()
|
||||
.then((obj_data) => {
|
||||
obj_btn.disabled = false;
|
||||
obj_btn.innerHTML = '<i class="mdi mdi-magnify me-1"></i>Detecter automatiquement';
|
||||
|
||||
if (obj_data.is_found) {
|
||||
let obj_input = document.getElementById("input_blender_path");
|
||||
obj_input.value = obj_data.str_path;
|
||||
BlenderPath._update_modal_status(true, obj_data.str_path);
|
||||
} else {
|
||||
BlenderPath._update_modal_status(false, null);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
obj_btn.disabled = false;
|
||||
obj_btn.innerHTML = '<i class="mdi mdi-magnify me-1"></i>Detecter automatiquement';
|
||||
BlenderPath._update_modal_status(false, null);
|
||||
});
|
||||
},
|
||||
|
||||
_validate: () => {
|
||||
let str_path = document.getElementById("input_blender_path").value;
|
||||
if (!str_path) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.api.set_blender_path(str_path)
|
||||
.then((obj_result) => {
|
||||
if (obj_result.is_success) {
|
||||
BlenderPath.str_current_path = obj_result.str_path;
|
||||
BlenderPath.is_found = true;
|
||||
BlenderPath._update_badge();
|
||||
BlenderPath.obj_modal.hide();
|
||||
ConsoleLog.add("Chemin Blender configure : " + obj_result.str_path);
|
||||
} else {
|
||||
BlenderPath._update_modal_status(false, null);
|
||||
ConsoleLog.add("Chemin Blender invalide : " + (obj_result.str_error || ""));
|
||||
}
|
||||
})
|
||||
.catch((obj_err) => {
|
||||
ConsoleLog.add("Erreur configuration Blender : " + obj_err.message);
|
||||
});
|
||||
},
|
||||
|
||||
_update_badge: () => {
|
||||
let obj_badge = document.getElementById("btn_blender_status");
|
||||
if (BlenderPath.is_found) {
|
||||
obj_badge.className = "btn btn-sm btn-outline-success";
|
||||
obj_badge.title = "Blender : " + BlenderPath.str_current_path;
|
||||
} else {
|
||||
obj_badge.className = "btn btn-sm btn-outline-danger";
|
||||
obj_badge.title = "Blender non trouve";
|
||||
}
|
||||
},
|
||||
|
||||
_update_modal_status: (is_valid, str_path) => {
|
||||
let obj_label = document.getElementById("label_blender_status");
|
||||
let obj_btn_validate = document.getElementById("btn_validate_blender");
|
||||
|
||||
if (is_valid && str_path) {
|
||||
obj_label.className = "badge bg-success";
|
||||
obj_label.innerHTML = '<i class="mdi mdi-check-circle me-1"></i>Trouve';
|
||||
obj_btn_validate.disabled = false;
|
||||
} else {
|
||||
obj_label.className = "badge bg-danger";
|
||||
obj_label.innerHTML = '<i class="mdi mdi-close-circle me-1"></i>Non trouve';
|
||||
obj_btn_validate.disabled = true;
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"str_version": "1.0.0"
|
||||
"str_version": "1.1.0"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user