temps restant + video + download link + left click + show next frame done
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
const BlenderProcess = require("./BlenderProcess.js");
|
||||
const BlenderDaemon = require("./BlenderDaemon.js");
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
const os = require("os");
|
||||
@@ -23,12 +24,17 @@ class QueueManager {
|
||||
this.nb_total_render_ms = 0;
|
||||
this.nb_completed_renders = 0;
|
||||
this.map_remote_avgs = {};
|
||||
this.obj_email_notifier = null;
|
||||
}
|
||||
|
||||
set_notification_config(obj_config) {
|
||||
this.obj_notification_config = obj_config || { is_notify_each_image: false, is_notify_all_done: true };
|
||||
}
|
||||
|
||||
set_email_notifier(obj_notifier) {
|
||||
this.obj_email_notifier = obj_notifier;
|
||||
}
|
||||
|
||||
start(obj_config) {
|
||||
if (this.str_status === STR_STATUS_PAUSED) {
|
||||
this.str_status = STR_STATUS_RUNNING;
|
||||
@@ -49,9 +55,24 @@ class QueueManager {
|
||||
this.list_collections = obj_config.list_collections || [];
|
||||
this.obj_render_settings = obj_config.obj_render_settings || null;
|
||||
|
||||
if (this.str_overwrite_mode === "skip") {
|
||||
this._prescan_existing();
|
||||
}
|
||||
|
||||
this._send_log("File de rendu construite : " + this.list_queue.length + " elements.");
|
||||
this._send_progress();
|
||||
this._process_next();
|
||||
|
||||
this._send_log("Demarrage du daemon Blender...");
|
||||
BlenderDaemon.start(obj_config.str_blend_file)
|
||||
.then(() => {
|
||||
this._send_log("Daemon Blender pret.");
|
||||
this._process_next();
|
||||
})
|
||||
.catch((obj_err) => {
|
||||
this._send_log("ERREUR demarrage daemon : " + (obj_err.message || String(obj_err)));
|
||||
this.str_status = STR_STATUS_IDLE;
|
||||
this._send_progress();
|
||||
});
|
||||
|
||||
return Promise.resolve({ is_success: true, nb_total: this.list_queue.length });
|
||||
}
|
||||
@@ -66,17 +87,33 @@ class QueueManager {
|
||||
}
|
||||
|
||||
stop() {
|
||||
let str_file_to_delete = null;
|
||||
|
||||
if (this.nb_current_index < this.list_queue.length) {
|
||||
let obj_item = this.list_queue[this.nb_current_index];
|
||||
if (obj_item.str_status === "rendering") {
|
||||
obj_item.str_status = "stopped";
|
||||
str_file_to_delete = obj_item.str_expected_file;
|
||||
}
|
||||
}
|
||||
|
||||
this.str_status = STR_STATUS_IDLE;
|
||||
|
||||
BlenderDaemon.stop();
|
||||
|
||||
if (this.obj_current_process) {
|
||||
this.obj_current_process.kill("SIGTERM");
|
||||
this.obj_current_process = null;
|
||||
}
|
||||
|
||||
if (this.nb_current_index < this.list_queue.length) {
|
||||
let obj_item = this.list_queue[this.nb_current_index];
|
||||
if (obj_item.str_status === "rendering") {
|
||||
obj_item.str_status = "stopped";
|
||||
if (str_file_to_delete) {
|
||||
try {
|
||||
if (fs.existsSync(str_file_to_delete)) {
|
||||
fs.unlinkSync(str_file_to_delete);
|
||||
this._send_log("Fichier partiel supprime : " + path.basename(str_file_to_delete));
|
||||
}
|
||||
} catch (obj_del_err) {
|
||||
this._send_log("Erreur suppression fichier partiel : " + obj_del_err.message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,6 +233,42 @@ class QueueManager {
|
||||
};
|
||||
}
|
||||
|
||||
_prescan_existing() {
|
||||
let nb_skipped = 0;
|
||||
let nb_remote = 0;
|
||||
|
||||
for (let obj_item of this.list_queue) {
|
||||
try {
|
||||
if (!fs.existsSync(obj_item.str_expected_file)) {
|
||||
continue;
|
||||
}
|
||||
let nb_size = fs.statSync(obj_item.str_expected_file).size;
|
||||
if (nb_size === 0) {
|
||||
continue;
|
||||
}
|
||||
if (nb_size > NB_PLACEHOLDER_MAX_SIZE) {
|
||||
obj_item.str_status = "skipped";
|
||||
try {
|
||||
obj_item.str_done_date = fs.statSync(obj_item.str_expected_file).mtime.toISOString();
|
||||
} catch (obj_date_err) {
|
||||
// ignore
|
||||
}
|
||||
nb_skipped++;
|
||||
} else {
|
||||
obj_item.str_status = "rendering_remote";
|
||||
this._read_placeholder(obj_item);
|
||||
nb_remote++;
|
||||
}
|
||||
} catch (obj_err) {
|
||||
// Fichier inaccessible, on ignore
|
||||
}
|
||||
}
|
||||
|
||||
if (nb_skipped > 0 || nb_remote > 0) {
|
||||
this._send_log("Pre-scan : " + nb_skipped + " fichier(s) existant(s), " + nb_remote + " en cours sur d'autres machines.");
|
||||
}
|
||||
}
|
||||
|
||||
_process_next() {
|
||||
if (this.str_status !== STR_STATUS_RUNNING) {
|
||||
return;
|
||||
@@ -203,10 +276,19 @@ class QueueManager {
|
||||
|
||||
this._check_remote_completions();
|
||||
|
||||
// Batch skip : boucle iterative pour eviter un stack overflow recursif
|
||||
// Batch skip : sauter les items deja marques par le prescan ou nouvellement detectes
|
||||
let nb_skip_count = 0;
|
||||
while (this.nb_current_index < this.list_queue.length && this.str_overwrite_mode === "skip") {
|
||||
let obj_check = this.list_queue[this.nb_current_index];
|
||||
|
||||
// Item deja marque par le prescan
|
||||
if (obj_check.str_status === "skipped" || obj_check.str_status === "rendering_remote") {
|
||||
this.nb_current_index++;
|
||||
nb_skip_count++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Verification fichier pour les items non pre-scannes (apparus entre-temps)
|
||||
let is_exists = false;
|
||||
let nb_size = 0;
|
||||
try {
|
||||
@@ -247,6 +329,7 @@ class QueueManager {
|
||||
|
||||
if (this.nb_current_index >= this.list_queue.length) {
|
||||
this.str_status = STR_STATUS_IDLE;
|
||||
BlenderDaemon.stop();
|
||||
this._send_log("Tous les rendus sont termines !");
|
||||
this._send_event("render-complete", { is_all_done: true });
|
||||
if (this.obj_notification_config.is_notify_all_done) {
|
||||
@@ -308,22 +391,18 @@ class QueueManager {
|
||||
|
||||
let nb_start = Date.now();
|
||||
|
||||
BlenderProcess.render_frame({
|
||||
str_blend_path: obj_item.str_blend_path,
|
||||
BlenderDaemon.render_frame({
|
||||
str_camera_name: obj_item.str_camera_name,
|
||||
nb_frame: obj_item.nb_frame,
|
||||
nb_resolution_x: obj_item.nb_resolution_x,
|
||||
nb_resolution_y: obj_item.nb_resolution_y,
|
||||
str_format: obj_item.str_format,
|
||||
str_output_path: obj_item.str_output_path,
|
||||
str_output_path: obj_item.str_expected_file,
|
||||
list_collections: this.list_collections,
|
||||
obj_render_settings: this.obj_render_settings,
|
||||
fn_on_stdout: (str_data) => {
|
||||
this._send_log(str_data.trim());
|
||||
},
|
||||
fn_on_process: (obj_process) => {
|
||||
this.obj_current_process = obj_process;
|
||||
},
|
||||
})
|
||||
.then((obj_result) => {
|
||||
this.obj_current_process = null;
|
||||
@@ -383,7 +462,33 @@ class QueueManager {
|
||||
|
||||
this.nb_current_index++;
|
||||
this._send_progress();
|
||||
this._process_next();
|
||||
|
||||
let is_gpu_error = str_err_msg.indexOf("CUDA") !== -1
|
||||
|| str_err_msg.indexOf("GPU memory") !== -1
|
||||
|| str_err_msg.indexOf("out of memory") !== -1
|
||||
|| str_err_msg.indexOf("OpenCL") !== -1
|
||||
|| str_err_msg.indexOf("HIP") !== -1;
|
||||
|
||||
if (!BlenderDaemon.is_running() || is_gpu_error) {
|
||||
if (is_gpu_error) {
|
||||
this._send_log("Erreur GPU detectee, redemarrage du daemon pour contexte CUDA neuf...");
|
||||
BlenderDaemon.stop();
|
||||
} else {
|
||||
this._send_log("Daemon crash detecte, redemarrage...");
|
||||
}
|
||||
BlenderDaemon.start(obj_item.str_blend_path)
|
||||
.then(() => {
|
||||
this._send_log("Daemon redemarre.");
|
||||
this._process_next();
|
||||
})
|
||||
.catch((obj_restart_err) => {
|
||||
this._send_log("ERREUR redemarrage daemon : " + (obj_restart_err.message || String(obj_restart_err)));
|
||||
this.str_status = STR_STATUS_IDLE;
|
||||
this._send_progress();
|
||||
});
|
||||
} else {
|
||||
this._process_next();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -440,9 +545,18 @@ class QueueManager {
|
||||
}
|
||||
}
|
||||
|
||||
let nb_remaining = 0;
|
||||
for (let nb_r = this.nb_current_index; nb_r < this.list_queue.length; nb_r++) {
|
||||
let obj_r = this.list_queue[nb_r];
|
||||
if (obj_r.str_status === "pending" || obj_r.str_status === "rendering") {
|
||||
nb_remaining++;
|
||||
}
|
||||
}
|
||||
|
||||
this._send_event("render-progress", {
|
||||
nb_current: this.nb_current_index,
|
||||
nb_total: this.list_queue.length,
|
||||
nb_remaining: nb_remaining,
|
||||
str_camera: obj_item.str_camera_name || "-",
|
||||
nb_frame: obj_item.nb_frame || 0,
|
||||
str_status: this.str_status,
|
||||
@@ -517,6 +631,20 @@ class QueueManager {
|
||||
let obj_notif = new Notification({ title: str_title, body: str_body });
|
||||
obj_notif.show();
|
||||
}
|
||||
|
||||
if (this.obj_email_notifier) {
|
||||
this.obj_email_notifier.send("[Multi Render] " + str_title, str_body)
|
||||
.then((obj_result) => {
|
||||
if (obj_result.is_success) {
|
||||
this._send_log("Email envoye : " + str_title);
|
||||
} else if (obj_result.str_error && obj_result.str_error !== "Email non configure") {
|
||||
this._send_log("Erreur email : " + obj_result.str_error);
|
||||
}
|
||||
})
|
||||
.catch((obj_err) => {
|
||||
this._send_log("Erreur email : " + obj_err.message);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user