Files
multi_render_blender/main.js
sorlinv d5cb63a27b v1.1.0 — chemin Blender configurable depuis l'UI
Refonte de PathResolver : auto-detection systeme (which/where + chemins courants),
persistance du chemin dans userData, validation du fichier.
Ajout d'un modal Bootstrap pour configurer le chemin manuellement.
Badge vert/rouge dans la navbar indiquant le statut Blender.
Le modal s'ouvre automatiquement si Blender n'est pas trouve au lancement.
Suppression de extraResources (plus de Blender embarque dans le build).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 16:44:04 +01:00

217 lines
7.1 KiB
JavaScript

const { app, BrowserWindow, ipcMain, dialog } = require("electron");
const path = require("path");
const fs = require("fs");
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;
const create_window = () => {
obj_main_window = new BrowserWindow({
width: 1400,
height: 900,
minWidth: 1000,
minHeight: 700,
webPreferences: {
preload: path.join(__dirname, "preload.js"),
contextIsolation: true,
nodeIntegration: false,
},
title: "Multi Render Blender",
});
obj_main_window.loadFile(path.join(__dirname, "src", "renderer", "index.html"));
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());
});
};
// ── App lifecycle ──────────────────────────────────────────────
app.whenReady().then(() => {
create_window();
app.on("activate", () => {
if (BrowserWindow.getAllWindows().length === 0) {
create_window();
}
});
});
app.on("window-all-closed", () => {
if (process.platform !== "darwin") {
app.quit();
}
});
// ── IPC Handlers ───────────────────────────────────────────────
ipcMain.handle("select-blend-file", () => {
return dialog.showOpenDialog(obj_main_window, {
title: "Selectionner un fichier Blender",
filters: [
{ name: "Blender Files", extensions: ["blend"] },
],
properties: ["openFile"],
})
.then((obj_result) => {
if (obj_result.canceled || obj_result.filePaths.length === 0) {
return null;
}
return obj_result.filePaths[0];
});
});
ipcMain.handle("get-cameras", (event, str_blend_path) => {
return CameraParser.list_cameras(str_blend_path);
});
ipcMain.handle("start-render", (event, obj_config) => {
return obj_queue_manager.start(obj_config);
});
ipcMain.handle("pause-render", () => {
return obj_queue_manager.pause();
});
ipcMain.handle("stop-render", () => {
return obj_queue_manager.stop();
});
ipcMain.handle("save-config", (event, obj_config) => {
return ConfigManager.save(obj_main_window, obj_config);
});
ipcMain.handle("load-config", () => {
return ConfigManager.load(obj_main_window);
});
ipcMain.handle("read-image", (event, str_image_path) => {
let NB_MAX_RETRIES = 10;
let NB_RETRY_DELAY = 500;
let fn_try_read = (nb_attempt) => {
return new Promise((resolve, reject) => {
if (!fs.existsSync(str_image_path)) {
if (nb_attempt < NB_MAX_RETRIES) {
setTimeout(() => {
fn_try_read(nb_attempt + 1).then(resolve).catch(reject);
}, NB_RETRY_DELAY);
return;
}
reject(new Error("Fichier introuvable apres " + NB_MAX_RETRIES + " tentatives : " + str_image_path));
return;
}
// Wait for file size to stabilize (file fully written)
let nb_size_prev = -1;
let fn_check_stable = () => {
let obj_stats = fs.statSync(str_image_path);
if (obj_stats.size === 0 || obj_stats.size !== nb_size_prev) {
nb_size_prev = obj_stats.size;
if (nb_attempt < NB_MAX_RETRIES) {
setTimeout(() => {
nb_attempt++;
fn_check_stable();
}, NB_RETRY_DELAY);
return;
}
if (obj_stats.size === 0) {
reject(new Error("Le fichier est vide apres " + NB_MAX_RETRIES + " tentatives : " + str_image_path));
return;
}
}
fs.readFile(str_image_path, (obj_err, obj_buffer) => {
if (obj_err) {
reject(new Error("Impossible de lire l'image : " + obj_err.message));
return;
}
let str_ext = path.extname(str_image_path).toLowerCase().replace(".", "");
let str_mime = "image/png";
if (str_ext === "jpg" || str_ext === "jpeg") {
str_mime = "image/jpeg";
} else if (str_ext === "bmp") {
str_mime = "image/bmp";
} else if (str_ext === "tiff" || str_ext === "tif") {
str_mime = "image/tiff";
} else if (str_ext === "exr") {
str_mime = "image/x-exr";
}
let str_base64 = obj_buffer.toString("base64");
resolve("data:" + str_mime + ";base64," + str_base64);
});
};
fn_check_stable();
});
};
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();
});
ipcMain.handle("apply-update", (event, str_tag_name) => {
return UpdateManager.download_and_apply(str_tag_name);
});
ipcMain.handle("select-output-folder", () => {
return dialog.showOpenDialog(obj_main_window, {
title: "Selectionner le dossier de sortie",
properties: ["openDirectory"],
})
.then((obj_result) => {
if (obj_result.canceled || obj_result.filePaths.length === 0) {
return null;
}
return obj_result.filePaths[0];
});
});