Commit ·
1ada5a6
1
Parent(s): 2217223
clear queue aborts currently generating item
Browse files
wgp.py
CHANGED
|
@@ -362,24 +362,21 @@ def save_queue_action(state):
|
|
| 362 |
gen = get_gen_info(state)
|
| 363 |
queue = gen.get("queue", [])
|
| 364 |
|
| 365 |
-
if not queue or len(queue) <=1 :
|
| 366 |
gr.Info("Queue is empty. Nothing to save.")
|
| 367 |
-
return None
|
| 368 |
|
| 369 |
-
# Use an in-memory buffer for the zip file
|
| 370 |
zip_buffer = io.BytesIO()
|
| 371 |
|
| 372 |
-
# Still use a temporary directory *only* for storing images before zipping
|
| 373 |
with tempfile.TemporaryDirectory() as tmpdir:
|
| 374 |
queue_manifest = []
|
| 375 |
-
image_paths_in_zip = {}
|
| 376 |
|
| 377 |
for task_index, task in enumerate(queue):
|
| 378 |
-
# Skip the placeholder item if it exists
|
| 379 |
if task is None or not isinstance(task, dict) or task_index == 0: continue
|
| 380 |
|
| 381 |
params_copy = task.get('params', {}).copy()
|
| 382 |
-
task_id_s = task.get('id', f"task_{task_index}")
|
| 383 |
|
| 384 |
image_keys = ["image_start", "image_end", "image_refs"]
|
| 385 |
for key in image_keys:
|
|
@@ -387,95 +384,71 @@ def save_queue_action(state):
|
|
| 387 |
if images_pil is None:
|
| 388 |
continue
|
| 389 |
|
| 390 |
-
# Ensure images_pil is always a list for processing
|
| 391 |
is_originally_list = isinstance(images_pil, list)
|
| 392 |
if not is_originally_list:
|
| 393 |
images_pil = [images_pil]
|
| 394 |
|
| 395 |
image_filenames_for_json = []
|
| 396 |
for img_index, pil_image in enumerate(images_pil):
|
| 397 |
-
# Ensure it's actually a PIL Image object before proceeding
|
| 398 |
if not isinstance(pil_image, Image.Image):
|
| 399 |
print(f"Warning: Expected PIL Image for key '{key}' in task {task_id_s}, got {type(pil_image)}. Skipping image.")
|
| 400 |
continue
|
| 401 |
|
| 402 |
-
# Use object ID to check if this specific image instance is already saved
|
| 403 |
img_id = id(pil_image)
|
| 404 |
if img_id in image_paths_in_zip:
|
| 405 |
-
# If already saved, just add its filename to the list
|
| 406 |
image_filenames_for_json.append(image_paths_in_zip[img_id])
|
| 407 |
-
continue
|
| 408 |
|
| 409 |
-
# Image not saved yet, create filename and save path
|
| 410 |
img_filename_in_zip = f"task{task_id_s}_{key}_{img_index}.png"
|
| 411 |
img_save_path = os.path.join(tmpdir, img_filename_in_zip)
|
| 412 |
|
| 413 |
try:
|
| 414 |
-
# Save the image to the temporary directory
|
| 415 |
pil_image.save(img_save_path, "PNG")
|
| 416 |
image_filenames_for_json.append(img_filename_in_zip)
|
| 417 |
-
# Store the mapping from image ID to its filename in the zip
|
| 418 |
image_paths_in_zip[img_id] = img_filename_in_zip
|
| 419 |
except Exception as e:
|
| 420 |
print(f"Error saving image {img_filename_in_zip} for task {task_id_s}: {e}")
|
| 421 |
-
# Optionally decide if you want to continue or fail here
|
| 422 |
|
| 423 |
-
# Update the params_copy with the list of filenames (or single filename)
|
| 424 |
if image_filenames_for_json:
|
| 425 |
params_copy[key] = image_filenames_for_json if is_originally_list else image_filenames_for_json[0]
|
| 426 |
else:
|
| 427 |
-
# If no images were successfully processed for this key, remove it
|
| 428 |
params_copy.pop(key, None)
|
| 429 |
|
| 430 |
|
| 431 |
-
# Clean up parameters before adding to manifest
|
| 432 |
params_copy.pop('state', None)
|
| 433 |
-
params_copy.pop('start_image_data_base64', None)
|
| 434 |
params_copy.pop('end_image_data_base64', None)
|
| 435 |
-
# Also remove the actual PIL data if it somehow remained
|
| 436 |
params_copy.pop('start_image_data', None)
|
| 437 |
params_copy.pop('end_image_data', None)
|
| 438 |
|
| 439 |
manifest_entry = {
|
| 440 |
"id": task.get('id'),
|
| 441 |
"params": params_copy,
|
| 442 |
-
# Keep other necessary top-level task info if needed, like repeats etc.
|
| 443 |
-
# Example: "repeats": task.get('repeats', 1)
|
| 444 |
}
|
| 445 |
queue_manifest.append(manifest_entry)
|
| 446 |
|
| 447 |
-
# --- Create queue.json content ---
|
| 448 |
manifest_path = os.path.join(tmpdir, "queue.json")
|
| 449 |
try:
|
| 450 |
with open(manifest_path, 'w', encoding='utf-8') as f:
|
| 451 |
-
# Dump only the relevant manifest data
|
| 452 |
json.dump(queue_manifest, f, indent=4)
|
| 453 |
except Exception as e:
|
| 454 |
print(f"Error writing queue.json: {e}")
|
| 455 |
gr.Warning("Failed to create queue manifest.")
|
| 456 |
-
return None
|
| 457 |
|
| 458 |
-
# --- Create the zip file in memory ---
|
| 459 |
try:
|
| 460 |
with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zf:
|
| 461 |
-
# Add queue.json
|
| 462 |
zf.write(manifest_path, arcname="queue.json")
|
| 463 |
|
| 464 |
-
# Add all unique images that were saved to the temp dir
|
| 465 |
for saved_img_rel_path in image_paths_in_zip.values():
|
| 466 |
saved_img_abs_path = os.path.join(tmpdir, saved_img_rel_path)
|
| 467 |
if os.path.exists(saved_img_abs_path):
|
| 468 |
zf.write(saved_img_abs_path, arcname=saved_img_rel_path)
|
| 469 |
else:
|
| 470 |
-
# This shouldn't happen if saving was successful, but good to check
|
| 471 |
print(f"Warning: Image file {saved_img_rel_path} not found during zipping.")
|
| 472 |
|
| 473 |
-
# --- Prepare for return ---
|
| 474 |
-
# Move buffer position to the beginning
|
| 475 |
zip_buffer.seek(0)
|
| 476 |
-
# Read the binary content
|
| 477 |
zip_binary_content = zip_buffer.getvalue()
|
| 478 |
-
# Encode as base64 string
|
| 479 |
zip_base64 = base64.b64encode(zip_binary_content).decode('utf-8')
|
| 480 |
print(f"Queue successfully prepared as base64 string ({len(zip_base64)} chars).")
|
| 481 |
return zip_base64
|
|
@@ -483,18 +456,17 @@ def save_queue_action(state):
|
|
| 483 |
except Exception as e:
|
| 484 |
print(f"Error creating zip file in memory: {e}")
|
| 485 |
gr.Warning("Failed to create zip data for download.")
|
| 486 |
-
return None
|
| 487 |
finally:
|
| 488 |
zip_buffer.close()
|
| 489 |
|
| 490 |
def load_queue_action(filepath, state):
|
| 491 |
global task_id
|
| 492 |
gen = get_gen_info(state)
|
| 493 |
-
original_queue = gen.get("queue", [])
|
| 494 |
|
| 495 |
if not filepath or not hasattr(filepath, 'name') or not Path(filepath.name).is_file():
|
| 496 |
print("[load_queue_action] Warning: No valid file selected or file not found.")
|
| 497 |
-
# Return the current state of the DataFrame
|
| 498 |
return update_queue_data(original_queue)
|
| 499 |
|
| 500 |
newly_loaded_queue = []
|
|
@@ -518,7 +490,6 @@ def load_queue_action(filepath, state):
|
|
| 518 |
print(f"[load_queue_action] Manifest loaded. Processing {len(loaded_manifest)} tasks.")
|
| 519 |
|
| 520 |
for task_index, task_data in enumerate(loaded_manifest):
|
| 521 |
-
# (Keep the existing task processing logic here...)
|
| 522 |
if task_data is None or not isinstance(task_data, dict):
|
| 523 |
print(f"[load_queue_action] Skipping invalid task data at index {task_index}")
|
| 524 |
continue
|
|
@@ -528,7 +499,7 @@ def load_queue_action(filepath, state):
|
|
| 528 |
max_id_in_file = max(max_id_in_file, task_id_loaded)
|
| 529 |
loaded_pil_images = {}
|
| 530 |
image_keys = ["image_start", "image_end", "image_refs"]
|
| 531 |
-
params['state'] = state
|
| 532 |
|
| 533 |
for key in image_keys:
|
| 534 |
image_filenames = params.get(key)
|
|
@@ -544,26 +515,22 @@ def load_queue_action(filepath, state):
|
|
| 544 |
continue
|
| 545 |
try:
|
| 546 |
pil_image = Image.open(img_load_path)
|
| 547 |
-
# Ensure the image data is loaded into memory before the temp dir is cleaned up
|
| 548 |
pil_image.load()
|
| 549 |
-
# Convert image right after loading
|
| 550 |
converted_image = convert_image(pil_image)
|
| 551 |
loaded_pils.append(converted_image)
|
| 552 |
-
pil_image.close()
|
| 553 |
except Exception as img_e:
|
| 554 |
print(f"[load_queue_action] Error loading image {img_filename_in_zip}: {img_e}")
|
| 555 |
if loaded_pils:
|
| 556 |
params[key] = loaded_pils if is_list else loaded_pils[0]
|
| 557 |
-
loaded_pil_images[key] = params[key]
|
| 558 |
else: params.pop(key, None)
|
| 559 |
|
| 560 |
-
# Generate preview base64 strings
|
| 561 |
primary_preview_pil, secondary_preview_pil = None, None
|
| 562 |
start_prev_pil_list = loaded_pil_images.get("image_start")
|
| 563 |
end_prev_pil_list = loaded_pil_images.get("image_end")
|
| 564 |
ref_prev_pil_list = loaded_pil_images.get("image_refs")
|
| 565 |
|
| 566 |
-
# Extract first image for preview if available
|
| 567 |
if start_prev_pil_list:
|
| 568 |
primary_preview_pil = start_prev_pil_list[0] if isinstance(start_prev_pil_list, list) and start_prev_pil_list else start_prev_pil_list if not isinstance(start_prev_pil_list, list) else None
|
| 569 |
if end_prev_pil_list:
|
|
@@ -571,97 +538,102 @@ def load_queue_action(filepath, state):
|
|
| 571 |
elif ref_prev_pil_list and isinstance(ref_prev_pil_list, list) and ref_prev_pil_list:
|
| 572 |
primary_preview_pil = ref_prev_pil_list[0]
|
| 573 |
|
| 574 |
-
# Generate base64 only if PIL image exists
|
| 575 |
start_b64 = [pil_to_base64_uri(primary_preview_pil, format="jpeg", quality=70)] if primary_preview_pil else None
|
| 576 |
end_b64 = [pil_to_base64_uri(secondary_preview_pil, format="jpeg", quality=70)] if secondary_preview_pil else None
|
| 577 |
|
| 578 |
-
# Get top-level image data (PIL objects) for runtime task
|
| 579 |
top_level_start_image = loaded_pil_images.get("image_start")
|
| 580 |
top_level_end_image = loaded_pil_images.get("image_end")
|
| 581 |
|
| 582 |
-
# Construct the runtime task dictionary
|
| 583 |
runtime_task = {
|
| 584 |
"id": task_id_loaded,
|
| 585 |
-
"params": params.copy(),
|
| 586 |
-
# Extract necessary params for top level if they exist
|
| 587 |
"repeats": params.get('repeat_generation', 1),
|
| 588 |
"length": params.get('video_length'),
|
| 589 |
"steps": params.get('num_inference_steps'),
|
| 590 |
"prompt": params.get('prompt'),
|
| 591 |
-
# Store the actual loaded PIL image data here
|
| 592 |
"start_image_data": top_level_start_image,
|
| 593 |
"end_image_data": top_level_end_image,
|
| 594 |
-
# Store base64 previews generated above
|
| 595 |
"start_image_data_base64": start_b64,
|
| 596 |
"end_image_data_base64": end_b64,
|
| 597 |
}
|
| 598 |
newly_loaded_queue.append(runtime_task)
|
| 599 |
print(f"[load_queue_action] Processed task {task_index+1}/{len(loaded_manifest)}, ID: {task_id_loaded}")
|
| 600 |
|
| 601 |
-
# --- State Update ---
|
| 602 |
with lock:
|
| 603 |
print("[load_queue_action] Acquiring lock to update state...")
|
| 604 |
-
gen["queue"] = newly_loaded_queue[:]
|
| 605 |
-
local_queue_copy_for_global_ref = gen["queue"][:]
|
| 606 |
-
current_max_id_in_new_queue = max([t['id'] for t in newly_loaded_queue if 'id' in t] + [0])
|
| 607 |
|
| 608 |
-
# Update global task ID only if the loaded max ID is higher
|
| 609 |
if current_max_id_in_new_queue > task_id:
|
| 610 |
print(f"[load_queue_action] Updating global task_id from {task_id} to {current_max_id_in_new_queue + 1}")
|
| 611 |
-
task_id = current_max_id_in_new_queue + 1
|
| 612 |
else:
|
| 613 |
print(f"[load_queue_action] Global task_id ({task_id}) is >= max in file ({current_max_id_in_new_queue}). Not changing task_id.")
|
| 614 |
|
| 615 |
gen["prompts_max"] = len(newly_loaded_queue)
|
| 616 |
print("[load_queue_action] State update complete. Releasing lock.")
|
| 617 |
|
| 618 |
-
# --- Global Reference Update ---
|
| 619 |
if local_queue_copy_for_global_ref is not None:
|
| 620 |
print("[load_queue_action] Updating global queue reference...")
|
| 621 |
update_global_queue_ref(local_queue_copy_for_global_ref)
|
| 622 |
else:
|
| 623 |
-
# This case should ideally not be reached if state update happens
|
| 624 |
print("[load_queue_action] Warning: Skipping global ref update as local copy is None.")
|
| 625 |
|
| 626 |
print(f"[load_queue_action] Queue load successful. Returning DataFrame update for {len(newly_loaded_queue)} tasks.")
|
| 627 |
-
# *** Return the DataFrame update object ***
|
| 628 |
return update_queue_data(newly_loaded_queue)
|
| 629 |
|
| 630 |
except (ValueError, zipfile.BadZipFile, FileNotFoundError, Exception) as e:
|
| 631 |
error_message = f"Error during queue load: {e}"
|
| 632 |
print(f"[load_queue_action] Caught error: {error_message}")
|
| 633 |
traceback.print_exc()
|
| 634 |
-
|
| 635 |
-
gr.Warning(f"Failed to load queue: {error_message[:200]}") # Show truncated error
|
| 636 |
|
| 637 |
-
# *** Return the DataFrame update for the original queue ***
|
| 638 |
print("[load_queue_action] Load failed. Returning DataFrame update for original queue.")
|
| 639 |
return update_queue_data(original_queue)
|
| 640 |
finally:
|
| 641 |
-
# Clean up the uploaded file object if it exists and has a path
|
| 642 |
if filepath and hasattr(filepath, 'name') and filepath.name and os.path.exists(filepath.name):
|
| 643 |
try:
|
| 644 |
-
|
| 645 |
-
# os.remove(filepath.name)
|
| 646 |
-
# print(f"[load_queue_action] Cleaned up temporary upload file: {filepath.name}")
|
| 647 |
-
pass # Let Gradio manage its temp files unless specifically needed
|
| 648 |
except OSError as e:
|
| 649 |
-
# Ignore errors like "file not found" if already cleaned up
|
| 650 |
print(f"[load_queue_action] Info: Could not remove temp file {filepath.name}: {e}")
|
| 651 |
pass
|
| 652 |
|
| 653 |
def clear_queue_action(state):
|
| 654 |
gen = get_gen_info(state)
|
| 655 |
queue = gen.get("queue", [])
|
| 656 |
-
|
| 657 |
-
|
| 658 |
-
return update_queue_data([])
|
| 659 |
|
| 660 |
with lock:
|
| 661 |
-
|
| 662 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 663 |
|
| 664 |
-
gr.Info("Queue cleared.")
|
| 665 |
return update_queue_data([])
|
| 666 |
|
| 667 |
def autosave_queue():
|
|
@@ -725,7 +697,7 @@ def autosave_queue():
|
|
| 725 |
if os.path.exists(saved_img_abs_path):
|
| 726 |
zf.write(saved_img_abs_path, arcname=saved_img_rel_path)
|
| 727 |
return output_filename
|
| 728 |
-
return None
|
| 729 |
|
| 730 |
saved_path = _save_queue_to_file(global_queue_ref, AUTOSAVE_FILENAME)
|
| 731 |
|
|
@@ -740,17 +712,15 @@ def autosave_queue():
|
|
| 740 |
|
| 741 |
def autoload_queue(state):
|
| 742 |
global task_id
|
| 743 |
-
# Initial check using the original state
|
| 744 |
try:
|
| 745 |
-
gen = get_gen_info(state)
|
| 746 |
original_queue = gen.get("queue", [])
|
| 747 |
except AttributeError:
|
| 748 |
print("[autoload_queue] Error: Initial state is not a dictionary. Cannot autoload.")
|
| 749 |
-
|
| 750 |
-
return gr.update(visible=False), False, state # Return an empty DF update
|
| 751 |
|
| 752 |
loaded_flag = False
|
| 753 |
-
dataframe_update = update_queue_data(original_queue)
|
| 754 |
|
| 755 |
if not original_queue and Path(AUTOSAVE_FILENAME).is_file():
|
| 756 |
print(f"Autoloading queue from {AUTOSAVE_FILENAME}...")
|
|
@@ -758,38 +728,32 @@ def autoload_queue(state):
|
|
| 758 |
def __init__(self, name):
|
| 759 |
self.name = name
|
| 760 |
mock_filepath = MockFile(AUTOSAVE_FILENAME)
|
| 761 |
-
|
| 762 |
-
# Call load_queue_action, it modifies 'state' internally and returns a DataFrame update
|
| 763 |
dataframe_update = load_queue_action(mock_filepath, state)
|
| 764 |
|
| 765 |
-
|
| 766 |
-
gen = get_gen_info(state) # Use the (potentially) modified state dictionary
|
| 767 |
loaded_queue_after_action = gen.get("queue", [])
|
| 768 |
|
| 769 |
-
if loaded_queue_after_action:
|
| 770 |
print(f"Autoload successful. Loaded {len(loaded_queue_after_action)} tasks into state.")
|
| 771 |
loaded_flag = True
|
| 772 |
-
# Global ref update was already done inside load_queue_action if successful
|
| 773 |
else:
|
| 774 |
print("Autoload attempted but queue in state remains empty (file might be empty or invalid).")
|
| 775 |
-
# Ensure state reflects empty queue if load failed but file existed
|
| 776 |
with lock:
|
| 777 |
gen["queue"] = []
|
| 778 |
gen["prompts_max"] = 0
|
| 779 |
update_global_queue_ref([])
|
| 780 |
-
dataframe_update = update_queue_data([])
|
| 781 |
|
| 782 |
-
else:
|
| 783 |
if original_queue:
|
| 784 |
print("Autoload skipped: Queue is not empty.")
|
| 785 |
-
update_global_queue_ref(original_queue)
|
| 786 |
-
dataframe_update = update_queue_data(original_queue)
|
| 787 |
else:
|
| 788 |
print(f"Autoload skipped: {AUTOSAVE_FILENAME} not found.")
|
| 789 |
-
update_global_queue_ref([])
|
| 790 |
-
dataframe_update = update_queue_data([])
|
| 791 |
|
| 792 |
-
# Return the DataFrame update needed for the UI, the flag, and the final state dictionary
|
| 793 |
return dataframe_update, loaded_flag, state
|
| 794 |
|
| 795 |
|
|
|
|
| 362 |
gen = get_gen_info(state)
|
| 363 |
queue = gen.get("queue", [])
|
| 364 |
|
| 365 |
+
if not queue or len(queue) <=1 :
|
| 366 |
gr.Info("Queue is empty. Nothing to save.")
|
| 367 |
+
return None
|
| 368 |
|
|
|
|
| 369 |
zip_buffer = io.BytesIO()
|
| 370 |
|
|
|
|
| 371 |
with tempfile.TemporaryDirectory() as tmpdir:
|
| 372 |
queue_manifest = []
|
| 373 |
+
image_paths_in_zip = {}
|
| 374 |
|
| 375 |
for task_index, task in enumerate(queue):
|
|
|
|
| 376 |
if task is None or not isinstance(task, dict) or task_index == 0: continue
|
| 377 |
|
| 378 |
params_copy = task.get('params', {}).copy()
|
| 379 |
+
task_id_s = task.get('id', f"task_{task_index}")
|
| 380 |
|
| 381 |
image_keys = ["image_start", "image_end", "image_refs"]
|
| 382 |
for key in image_keys:
|
|
|
|
| 384 |
if images_pil is None:
|
| 385 |
continue
|
| 386 |
|
|
|
|
| 387 |
is_originally_list = isinstance(images_pil, list)
|
| 388 |
if not is_originally_list:
|
| 389 |
images_pil = [images_pil]
|
| 390 |
|
| 391 |
image_filenames_for_json = []
|
| 392 |
for img_index, pil_image in enumerate(images_pil):
|
|
|
|
| 393 |
if not isinstance(pil_image, Image.Image):
|
| 394 |
print(f"Warning: Expected PIL Image for key '{key}' in task {task_id_s}, got {type(pil_image)}. Skipping image.")
|
| 395 |
continue
|
| 396 |
|
|
|
|
| 397 |
img_id = id(pil_image)
|
| 398 |
if img_id in image_paths_in_zip:
|
|
|
|
| 399 |
image_filenames_for_json.append(image_paths_in_zip[img_id])
|
| 400 |
+
continue
|
| 401 |
|
|
|
|
| 402 |
img_filename_in_zip = f"task{task_id_s}_{key}_{img_index}.png"
|
| 403 |
img_save_path = os.path.join(tmpdir, img_filename_in_zip)
|
| 404 |
|
| 405 |
try:
|
|
|
|
| 406 |
pil_image.save(img_save_path, "PNG")
|
| 407 |
image_filenames_for_json.append(img_filename_in_zip)
|
|
|
|
| 408 |
image_paths_in_zip[img_id] = img_filename_in_zip
|
| 409 |
except Exception as e:
|
| 410 |
print(f"Error saving image {img_filename_in_zip} for task {task_id_s}: {e}")
|
|
|
|
| 411 |
|
|
|
|
| 412 |
if image_filenames_for_json:
|
| 413 |
params_copy[key] = image_filenames_for_json if is_originally_list else image_filenames_for_json[0]
|
| 414 |
else:
|
|
|
|
| 415 |
params_copy.pop(key, None)
|
| 416 |
|
| 417 |
|
|
|
|
| 418 |
params_copy.pop('state', None)
|
| 419 |
+
params_copy.pop('start_image_data_base64', None)
|
| 420 |
params_copy.pop('end_image_data_base64', None)
|
|
|
|
| 421 |
params_copy.pop('start_image_data', None)
|
| 422 |
params_copy.pop('end_image_data', None)
|
| 423 |
|
| 424 |
manifest_entry = {
|
| 425 |
"id": task.get('id'),
|
| 426 |
"params": params_copy,
|
|
|
|
|
|
|
| 427 |
}
|
| 428 |
queue_manifest.append(manifest_entry)
|
| 429 |
|
|
|
|
| 430 |
manifest_path = os.path.join(tmpdir, "queue.json")
|
| 431 |
try:
|
| 432 |
with open(manifest_path, 'w', encoding='utf-8') as f:
|
|
|
|
| 433 |
json.dump(queue_manifest, f, indent=4)
|
| 434 |
except Exception as e:
|
| 435 |
print(f"Error writing queue.json: {e}")
|
| 436 |
gr.Warning("Failed to create queue manifest.")
|
| 437 |
+
return None
|
| 438 |
|
|
|
|
| 439 |
try:
|
| 440 |
with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zf:
|
|
|
|
| 441 |
zf.write(manifest_path, arcname="queue.json")
|
| 442 |
|
|
|
|
| 443 |
for saved_img_rel_path in image_paths_in_zip.values():
|
| 444 |
saved_img_abs_path = os.path.join(tmpdir, saved_img_rel_path)
|
| 445 |
if os.path.exists(saved_img_abs_path):
|
| 446 |
zf.write(saved_img_abs_path, arcname=saved_img_rel_path)
|
| 447 |
else:
|
|
|
|
| 448 |
print(f"Warning: Image file {saved_img_rel_path} not found during zipping.")
|
| 449 |
|
|
|
|
|
|
|
| 450 |
zip_buffer.seek(0)
|
|
|
|
| 451 |
zip_binary_content = zip_buffer.getvalue()
|
|
|
|
| 452 |
zip_base64 = base64.b64encode(zip_binary_content).decode('utf-8')
|
| 453 |
print(f"Queue successfully prepared as base64 string ({len(zip_base64)} chars).")
|
| 454 |
return zip_base64
|
|
|
|
| 456 |
except Exception as e:
|
| 457 |
print(f"Error creating zip file in memory: {e}")
|
| 458 |
gr.Warning("Failed to create zip data for download.")
|
| 459 |
+
return None
|
| 460 |
finally:
|
| 461 |
zip_buffer.close()
|
| 462 |
|
| 463 |
def load_queue_action(filepath, state):
|
| 464 |
global task_id
|
| 465 |
gen = get_gen_info(state)
|
| 466 |
+
original_queue = gen.get("queue", [])
|
| 467 |
|
| 468 |
if not filepath or not hasattr(filepath, 'name') or not Path(filepath.name).is_file():
|
| 469 |
print("[load_queue_action] Warning: No valid file selected or file not found.")
|
|
|
|
| 470 |
return update_queue_data(original_queue)
|
| 471 |
|
| 472 |
newly_loaded_queue = []
|
|
|
|
| 490 |
print(f"[load_queue_action] Manifest loaded. Processing {len(loaded_manifest)} tasks.")
|
| 491 |
|
| 492 |
for task_index, task_data in enumerate(loaded_manifest):
|
|
|
|
| 493 |
if task_data is None or not isinstance(task_data, dict):
|
| 494 |
print(f"[load_queue_action] Skipping invalid task data at index {task_index}")
|
| 495 |
continue
|
|
|
|
| 499 |
max_id_in_file = max(max_id_in_file, task_id_loaded)
|
| 500 |
loaded_pil_images = {}
|
| 501 |
image_keys = ["image_start", "image_end", "image_refs"]
|
| 502 |
+
params['state'] = state
|
| 503 |
|
| 504 |
for key in image_keys:
|
| 505 |
image_filenames = params.get(key)
|
|
|
|
| 515 |
continue
|
| 516 |
try:
|
| 517 |
pil_image = Image.open(img_load_path)
|
|
|
|
| 518 |
pil_image.load()
|
|
|
|
| 519 |
converted_image = convert_image(pil_image)
|
| 520 |
loaded_pils.append(converted_image)
|
| 521 |
+
pil_image.close()
|
| 522 |
except Exception as img_e:
|
| 523 |
print(f"[load_queue_action] Error loading image {img_filename_in_zip}: {img_e}")
|
| 524 |
if loaded_pils:
|
| 525 |
params[key] = loaded_pils if is_list else loaded_pils[0]
|
| 526 |
+
loaded_pil_images[key] = params[key]
|
| 527 |
else: params.pop(key, None)
|
| 528 |
|
|
|
|
| 529 |
primary_preview_pil, secondary_preview_pil = None, None
|
| 530 |
start_prev_pil_list = loaded_pil_images.get("image_start")
|
| 531 |
end_prev_pil_list = loaded_pil_images.get("image_end")
|
| 532 |
ref_prev_pil_list = loaded_pil_images.get("image_refs")
|
| 533 |
|
|
|
|
| 534 |
if start_prev_pil_list:
|
| 535 |
primary_preview_pil = start_prev_pil_list[0] if isinstance(start_prev_pil_list, list) and start_prev_pil_list else start_prev_pil_list if not isinstance(start_prev_pil_list, list) else None
|
| 536 |
if end_prev_pil_list:
|
|
|
|
| 538 |
elif ref_prev_pil_list and isinstance(ref_prev_pil_list, list) and ref_prev_pil_list:
|
| 539 |
primary_preview_pil = ref_prev_pil_list[0]
|
| 540 |
|
|
|
|
| 541 |
start_b64 = [pil_to_base64_uri(primary_preview_pil, format="jpeg", quality=70)] if primary_preview_pil else None
|
| 542 |
end_b64 = [pil_to_base64_uri(secondary_preview_pil, format="jpeg", quality=70)] if secondary_preview_pil else None
|
| 543 |
|
|
|
|
| 544 |
top_level_start_image = loaded_pil_images.get("image_start")
|
| 545 |
top_level_end_image = loaded_pil_images.get("image_end")
|
| 546 |
|
|
|
|
| 547 |
runtime_task = {
|
| 548 |
"id": task_id_loaded,
|
| 549 |
+
"params": params.copy(),
|
|
|
|
| 550 |
"repeats": params.get('repeat_generation', 1),
|
| 551 |
"length": params.get('video_length'),
|
| 552 |
"steps": params.get('num_inference_steps'),
|
| 553 |
"prompt": params.get('prompt'),
|
|
|
|
| 554 |
"start_image_data": top_level_start_image,
|
| 555 |
"end_image_data": top_level_end_image,
|
|
|
|
| 556 |
"start_image_data_base64": start_b64,
|
| 557 |
"end_image_data_base64": end_b64,
|
| 558 |
}
|
| 559 |
newly_loaded_queue.append(runtime_task)
|
| 560 |
print(f"[load_queue_action] Processed task {task_index+1}/{len(loaded_manifest)}, ID: {task_id_loaded}")
|
| 561 |
|
|
|
|
| 562 |
with lock:
|
| 563 |
print("[load_queue_action] Acquiring lock to update state...")
|
| 564 |
+
gen["queue"] = newly_loaded_queue[:]
|
| 565 |
+
local_queue_copy_for_global_ref = gen["queue"][:]
|
| 566 |
+
current_max_id_in_new_queue = max([t['id'] for t in newly_loaded_queue if 'id' in t] + [0])
|
| 567 |
|
|
|
|
| 568 |
if current_max_id_in_new_queue > task_id:
|
| 569 |
print(f"[load_queue_action] Updating global task_id from {task_id} to {current_max_id_in_new_queue + 1}")
|
| 570 |
+
task_id = current_max_id_in_new_queue + 1
|
| 571 |
else:
|
| 572 |
print(f"[load_queue_action] Global task_id ({task_id}) is >= max in file ({current_max_id_in_new_queue}). Not changing task_id.")
|
| 573 |
|
| 574 |
gen["prompts_max"] = len(newly_loaded_queue)
|
| 575 |
print("[load_queue_action] State update complete. Releasing lock.")
|
| 576 |
|
|
|
|
| 577 |
if local_queue_copy_for_global_ref is not None:
|
| 578 |
print("[load_queue_action] Updating global queue reference...")
|
| 579 |
update_global_queue_ref(local_queue_copy_for_global_ref)
|
| 580 |
else:
|
|
|
|
| 581 |
print("[load_queue_action] Warning: Skipping global ref update as local copy is None.")
|
| 582 |
|
| 583 |
print(f"[load_queue_action] Queue load successful. Returning DataFrame update for {len(newly_loaded_queue)} tasks.")
|
|
|
|
| 584 |
return update_queue_data(newly_loaded_queue)
|
| 585 |
|
| 586 |
except (ValueError, zipfile.BadZipFile, FileNotFoundError, Exception) as e:
|
| 587 |
error_message = f"Error during queue load: {e}"
|
| 588 |
print(f"[load_queue_action] Caught error: {error_message}")
|
| 589 |
traceback.print_exc()
|
| 590 |
+
gr.Warning(f"Failed to load queue: {error_message[:200]}")
|
|
|
|
| 591 |
|
|
|
|
| 592 |
print("[load_queue_action] Load failed. Returning DataFrame update for original queue.")
|
| 593 |
return update_queue_data(original_queue)
|
| 594 |
finally:
|
|
|
|
| 595 |
if filepath and hasattr(filepath, 'name') and filepath.name and os.path.exists(filepath.name):
|
| 596 |
try:
|
| 597 |
+
pass
|
|
|
|
|
|
|
|
|
|
| 598 |
except OSError as e:
|
|
|
|
| 599 |
print(f"[load_queue_action] Info: Could not remove temp file {filepath.name}: {e}")
|
| 600 |
pass
|
| 601 |
|
| 602 |
def clear_queue_action(state):
|
| 603 |
gen = get_gen_info(state)
|
| 604 |
queue = gen.get("queue", [])
|
| 605 |
+
aborted_current = False
|
| 606 |
+
cleared_pending = False
|
|
|
|
| 607 |
|
| 608 |
with lock:
|
| 609 |
+
if "in_progress" in gen and gen["in_progress"]:
|
| 610 |
+
print("Clear Queue: Signalling abort for in-progress task.")
|
| 611 |
+
gen["abort"] = True
|
| 612 |
+
gen["extra_orders"] = 0
|
| 613 |
+
if wan_model is not None:
|
| 614 |
+
wan_model._interrupt = True
|
| 615 |
+
aborted_current = True
|
| 616 |
+
|
| 617 |
+
if queue:
|
| 618 |
+
if len(queue) > 1 or (len(queue) == 1 and queue[0] is not None and queue[0].get('id') is not None):
|
| 619 |
+
print(f"Clear Queue: Clearing {len(queue)} tasks from queue.")
|
| 620 |
+
queue.clear()
|
| 621 |
+
cleared_pending = True
|
| 622 |
+
else:
|
| 623 |
+
pass
|
| 624 |
+
|
| 625 |
+
if aborted_current or cleared_pending:
|
| 626 |
+
gen["prompts_max"] = 0
|
| 627 |
+
|
| 628 |
+
if aborted_current and cleared_pending:
|
| 629 |
+
gr.Info("Queue cleared and current generation aborted.")
|
| 630 |
+
elif aborted_current:
|
| 631 |
+
gr.Info("Current generation aborted.")
|
| 632 |
+
elif cleared_pending:
|
| 633 |
+
gr.Info("Queue cleared.")
|
| 634 |
+
else:
|
| 635 |
+
gr.Info("Queue is already empty or only contains the active task (which wasn't aborted now).")
|
| 636 |
|
|
|
|
| 637 |
return update_queue_data([])
|
| 638 |
|
| 639 |
def autosave_queue():
|
|
|
|
| 697 |
if os.path.exists(saved_img_abs_path):
|
| 698 |
zf.write(saved_img_abs_path, arcname=saved_img_rel_path)
|
| 699 |
return output_filename
|
| 700 |
+
return None
|
| 701 |
|
| 702 |
saved_path = _save_queue_to_file(global_queue_ref, AUTOSAVE_FILENAME)
|
| 703 |
|
|
|
|
| 712 |
|
| 713 |
def autoload_queue(state):
|
| 714 |
global task_id
|
|
|
|
| 715 |
try:
|
| 716 |
+
gen = get_gen_info(state)
|
| 717 |
original_queue = gen.get("queue", [])
|
| 718 |
except AttributeError:
|
| 719 |
print("[autoload_queue] Error: Initial state is not a dictionary. Cannot autoload.")
|
| 720 |
+
return gr.update(visible=False), False, state
|
|
|
|
| 721 |
|
| 722 |
loaded_flag = False
|
| 723 |
+
dataframe_update = update_queue_data(original_queue)
|
| 724 |
|
| 725 |
if not original_queue and Path(AUTOSAVE_FILENAME).is_file():
|
| 726 |
print(f"Autoloading queue from {AUTOSAVE_FILENAME}...")
|
|
|
|
| 728 |
def __init__(self, name):
|
| 729 |
self.name = name
|
| 730 |
mock_filepath = MockFile(AUTOSAVE_FILENAME)
|
|
|
|
|
|
|
| 731 |
dataframe_update = load_queue_action(mock_filepath, state)
|
| 732 |
|
| 733 |
+
gen = get_gen_info(state)
|
|
|
|
| 734 |
loaded_queue_after_action = gen.get("queue", [])
|
| 735 |
|
| 736 |
+
if loaded_queue_after_action:
|
| 737 |
print(f"Autoload successful. Loaded {len(loaded_queue_after_action)} tasks into state.")
|
| 738 |
loaded_flag = True
|
|
|
|
| 739 |
else:
|
| 740 |
print("Autoload attempted but queue in state remains empty (file might be empty or invalid).")
|
|
|
|
| 741 |
with lock:
|
| 742 |
gen["queue"] = []
|
| 743 |
gen["prompts_max"] = 0
|
| 744 |
update_global_queue_ref([])
|
| 745 |
+
dataframe_update = update_queue_data([])
|
| 746 |
|
| 747 |
+
else:
|
| 748 |
if original_queue:
|
| 749 |
print("Autoload skipped: Queue is not empty.")
|
| 750 |
+
update_global_queue_ref(original_queue)
|
| 751 |
+
dataframe_update = update_queue_data(original_queue)
|
| 752 |
else:
|
| 753 |
print(f"Autoload skipped: {AUTOSAVE_FILENAME} not found.")
|
| 754 |
+
update_global_queue_ref([])
|
| 755 |
+
dataframe_update = update_queue_data([])
|
| 756 |
|
|
|
|
| 757 |
return dataframe_update, loaded_flag, state
|
| 758 |
|
| 759 |
|