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"); 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); UpdateManager.init(obj_main_window); obj_main_window.webContents.on("did-finish-load", () => { UpdateManager.check_for_updates(); }); }; // ── 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("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]; }); });