const App = {
str_blend_path: null,
str_output_path: null,
init: () => {
ConsoleLog.init();
CameraList.init(App._on_camera_select);
CameraConfig.init();
RenderQueue.init();
PreviewPanel.init();
ProgressBar.init();
UpdateBanner.init();
BlenderPath.init();
NotificationConfig.init();
App._bind_events();
App._bind_render_events();
ConsoleLog.add("Application prete.");
},
_bind_events: () => {
let obj_btn_blend = document.getElementById("btn_select_blend");
obj_btn_blend.addEventListener("click", () => {
App._select_blend_file();
});
let obj_btn_output = document.getElementById("btn_select_output");
obj_btn_output.addEventListener("click", () => {
App._select_output_folder();
});
let obj_btn_start = document.getElementById("btn_start");
obj_btn_start.addEventListener("click", () => {
App._start_render();
});
let obj_btn_pause = document.getElementById("btn_pause");
obj_btn_pause.addEventListener("click", () => {
App._pause_render();
});
let obj_btn_stop = document.getElementById("btn_stop");
obj_btn_stop.addEventListener("click", () => {
App._stop_render();
});
let obj_btn_check = document.getElementById("btn_check_queue");
obj_btn_check.addEventListener("click", () => {
App._check_queue();
});
let obj_btn_save = document.getElementById("btn_save_config");
obj_btn_save.addEventListener("click", () => {
App._save_config();
});
let obj_btn_load = document.getElementById("btn_load_config");
obj_btn_load.addEventListener("click", () => {
App._load_config();
});
let obj_radio_subfolder = document.getElementById("radio_output_subfolder");
let obj_radio_prefix = document.getElementById("radio_output_prefix");
let obj_radio_both = document.getElementById("radio_output_both");
let obj_input_frame_prefix = document.getElementById("input_frame_prefix");
let obj_input_frame_padding = document.getElementById("input_frame_padding");
obj_radio_subfolder.addEventListener("change", () => { App._update_output_example(); });
obj_radio_prefix.addEventListener("change", () => { App._update_output_example(); });
obj_radio_both.addEventListener("change", () => { App._update_output_example(); });
obj_input_frame_prefix.addEventListener("input", () => { App._update_output_example(); });
obj_input_frame_padding.addEventListener("input", () => { App._update_output_example(); });
},
_bind_render_events: () => {
window.api.on_render_complete((obj_data) => {
if (obj_data.is_all_done) {
ConsoleLog.add("Tous les rendus sont termines !");
App._set_controls_state("idle");
}
});
window.api.on_render_error((obj_data) => {
ConsoleLog.add("ERREUR sur " + obj_data.str_camera + " frame " + obj_data.nb_frame + " : " + obj_data.str_error);
});
},
_update_output_example: () => {
let str_mode = document.querySelector('input[name="output_mode"]:checked').value;
let str_prefix = document.getElementById("input_frame_prefix").value || "";
let nb_padding = parseInt(document.getElementById("input_frame_padding").value, 10) || 5;
let str_padded = String(1).padStart(nb_padding, "0");
let obj_label = document.getElementById("label_output_example");
if (str_mode === "subfolder") {
obj_label.innerHTML = 'Ex: /sortie/Camera.001/' + str_prefix + str_padded + '.png';
} else if (str_mode === "prefix") {
obj_label.innerHTML = 'Ex: /sortie/Camera.001_' + str_prefix + str_padded + '.png';
} else {
obj_label.innerHTML = 'Ex: /sortie/Camera.001/Camera.001_' + str_prefix + str_padded + '.png';
}
},
// ── Actions ────────────────────────────────────────────
_select_blend_file: () => {
let obj_btn_blend = document.getElementById("btn_select_blend");
window.api.select_blend_file()
.then((str_path) => {
if (!str_path) {
return;
}
App.str_blend_path = str_path;
document.getElementById("input_blend_path").value = str_path;
ConsoleLog.add("Fichier charge : " + str_path);
obj_btn_blend.disabled = true;
CameraList.show_loading();
return window.api.get_cameras(str_path);
})
.then((obj_result) => {
obj_btn_blend.disabled = false;
if (!obj_result) {
return;
}
CameraList.set_cameras(obj_result.list_cameras, obj_result.obj_scene);
CameraConfig.clear();
ConsoleLog.add(obj_result.list_cameras.length + " camera(s) trouvee(s).");
App._update_start_button();
})
.catch((obj_err) => {
obj_btn_blend.disabled = false;
ConsoleLog.add("Erreur chargement : " + obj_err.message);
});
},
_select_output_folder: () => {
window.api.select_output_folder()
.then((str_path) => {
if (!str_path) {
return;
}
App.str_output_path = str_path;
document.getElementById("input_output_path").value = str_path;
ConsoleLog.add("Dossier de sortie : " + str_path);
App._update_start_button();
})
.catch((obj_err) => {
ConsoleLog.add("Erreur selection dossier : " + obj_err.message);
});
},
_on_camera_select: (obj_camera) => {
CameraConfig.show(obj_camera);
},
_start_render: () => {
let obj_btn_start = document.getElementById("btn_start");
if (obj_btn_start.disabled) {
return;
}
obj_btn_start.disabled = true;
if (!App.str_output_path) {
ConsoleLog.add("Veuillez selectionner un dossier de sortie.");
App._set_controls_state("idle");
return;
}
let str_mode = document.querySelector('input[name="render_mode"]:checked').value;
let str_output_mode = document.querySelector('input[name="output_mode"]:checked').value;
let str_overwrite_mode = document.querySelector('input[name="overwrite_mode"]:checked').value;
let list_cameras = CameraList.list_cameras;
let str_frame_prefix = document.getElementById("input_frame_prefix").value || "";
let nb_frame_padding = parseInt(document.getElementById("input_frame_padding").value, 10) || 5;
let obj_config = {
str_blend_file: App.str_blend_path,
str_render_mode: str_mode,
str_output_mode: str_output_mode,
str_overwrite_mode: str_overwrite_mode,
str_frame_prefix: str_frame_prefix,
nb_frame_padding: nb_frame_padding,
str_output_path: App.str_output_path,
list_cameras: list_cameras,
};
RenderQueue.build_display(str_mode, list_cameras);
ProgressBar.reset();
window.api.start_render(obj_config)
.then(() => {
App._set_controls_state("running");
ConsoleLog.add("Rendu lance en mode : " + str_mode);
})
.catch((obj_err) => {
App._set_controls_state("idle");
ConsoleLog.add("Erreur lancement rendu : " + obj_err.message);
});
},
_pause_render: () => {
window.api.pause_render()
.then(() => {
App._set_controls_state("paused");
})
.catch((obj_err) => {
ConsoleLog.add("Erreur pause : " + obj_err.message);
});
},
_stop_render: () => {
window.api.stop_render()
.then(() => {
App._set_controls_state("idle");
})
.catch((obj_err) => {
ConsoleLog.add("Erreur arret : " + obj_err.message);
});
},
_check_queue: () => {
let obj_btn_check = document.getElementById("btn_check_queue");
if (obj_btn_check.disabled) {
return;
}
if (!App.str_output_path) {
ConsoleLog.add("Veuillez selectionner un dossier de sortie.");
return;
}
let str_mode = document.querySelector('input[name="render_mode"]:checked').value;
let str_output_mode = document.querySelector('input[name="output_mode"]:checked').value;
let list_cameras = CameraList.list_cameras;
let str_frame_prefix = document.getElementById("input_frame_prefix").value || "";
let nb_frame_padding = parseInt(document.getElementById("input_frame_padding").value, 10) || 5;
let obj_config = {
str_blend_file: App.str_blend_path,
str_render_mode: str_mode,
str_output_mode: str_output_mode,
str_frame_prefix: str_frame_prefix,
nb_frame_padding: nb_frame_padding,
str_output_path: App.str_output_path,
list_cameras: list_cameras,
};
RenderQueue.build_display(str_mode, list_cameras);
obj_btn_check.disabled = true;
ConsoleLog.add("Verification des fichiers existants...");
window.api.check_queue(obj_config)
.then((obj_result) => {
obj_btn_check.disabled = false;
RenderQueue.mark_existing(obj_result.list_existing);
let nb_existing = obj_result.list_existing.length;
let nb_to_render = obj_result.nb_total - nb_existing;
ConsoleLog.add("Verification terminee : " + nb_existing + " fichier(s) existant(s), " + nb_to_render + " a rendre.");
})
.catch((obj_err) => {
obj_btn_check.disabled = false;
ConsoleLog.add("Erreur verification : " + obj_err.message);
});
},
_save_config: () => {
let str_mode = document.querySelector('input[name="render_mode"]:checked').value;
let str_output_mode = document.querySelector('input[name="output_mode"]:checked').value;
let str_overwrite_mode = document.querySelector('input[name="overwrite_mode"]:checked').value;
let str_frame_prefix = document.getElementById("input_frame_prefix").value || "";
let nb_frame_padding = parseInt(document.getElementById("input_frame_padding").value, 10) || 5;
let obj_config = {
str_blend_file: App.str_blend_path,
str_render_mode: str_mode,
str_output_mode: str_output_mode,
str_overwrite_mode: str_overwrite_mode,
str_frame_prefix: str_frame_prefix,
nb_frame_padding: nb_frame_padding,
str_output_path: App.str_output_path,
list_cameras: CameraList.list_cameras,
};
window.api.save_config(obj_config)
.then((obj_result) => {
if (obj_result && obj_result.is_success) {
ConsoleLog.add("Configuration exportee : " + obj_result.str_path);
}
})
.catch((obj_err) => {
ConsoleLog.add("Erreur sauvegarde : " + obj_err.message);
});
},
_load_config: () => {
window.api.load_config()
.then((obj_config) => {
if (!obj_config) {
return;
}
App.str_blend_path = obj_config.str_blend_file;
App.str_output_path = obj_config.str_output_path || null;
document.getElementById("input_blend_path").value = obj_config.str_blend_file || "";
document.getElementById("input_output_path").value = obj_config.str_output_path || "";
if (obj_config.str_render_mode === "frame_by_frame") {
document.getElementById("radio_frame_by_frame").checked = true;
} else {
document.getElementById("radio_camera_by_camera").checked = true;
}
if (obj_config.str_output_mode === "prefix") {
document.getElementById("radio_output_prefix").checked = true;
} else if (obj_config.str_output_mode === "both") {
document.getElementById("radio_output_both").checked = true;
} else {
document.getElementById("radio_output_subfolder").checked = true;
}
if (obj_config.str_overwrite_mode === "skip") {
document.getElementById("radio_skip").checked = true;
} else {
document.getElementById("radio_overwrite").checked = true;
}
if (obj_config.str_frame_prefix !== undefined) {
document.getElementById("input_frame_prefix").value = obj_config.str_frame_prefix;
}
if (obj_config.nb_frame_padding !== undefined) {
document.getElementById("input_frame_padding").value = obj_config.nb_frame_padding;
}
App._update_output_example();
if (obj_config.list_cameras && obj_config.list_cameras.length > 0) {
CameraList.list_cameras = obj_config.list_cameras;
CameraList.str_selected_camera = null;
CameraList.render();
let obj_badge = document.getElementById("badge_camera_count");
obj_badge.textContent = String(obj_config.list_cameras.length);
}
CameraConfig.clear();
App._update_start_button();
ConsoleLog.add("Configuration importee.");
})
.catch((obj_err) => {
ConsoleLog.add("Erreur chargement config : " + obj_err.message);
});
},
// ── UI State ───────────────────────────────────────────
_set_controls_state: (str_state) => {
let obj_btn_start = document.getElementById("btn_start");
let obj_btn_pause = document.getElementById("btn_pause");
let obj_btn_stop = document.getElementById("btn_stop");
let obj_btn_check = document.getElementById("btn_check_queue");
if (str_state === "running") {
obj_btn_start.disabled = true;
obj_btn_pause.disabled = false;
obj_btn_stop.disabled = false;
obj_btn_check.disabled = true;
} else if (str_state === "paused") {
obj_btn_start.disabled = false;
obj_btn_pause.disabled = true;
obj_btn_stop.disabled = false;
obj_btn_check.disabled = true;
} else {
App._update_start_button();
obj_btn_pause.disabled = true;
obj_btn_stop.disabled = true;
}
},
_update_start_button: () => {
let obj_btn_start = document.getElementById("btn_start");
let obj_btn_check = document.getElementById("btn_check_queue");
let is_ready = App.str_blend_path
&& App.str_output_path
&& CameraList.get_enabled_cameras().length > 0;
obj_btn_start.disabled = !is_ready;
obj_btn_check.disabled = !is_ready;
},
};
document.addEventListener("DOMContentLoaded", () => {
App.init();
});