Skip to content

Commit f25c674

Browse files
committed
speed up
1 parent 7b288e6 commit f25c674

File tree

1 file changed

+56
-63
lines changed

1 file changed

+56
-63
lines changed

preview_history.py

Lines changed: 56 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,24 @@ def tensor2pil(image):
1919
image = image[0]
2020
return Image.fromarray(np.clip(255. * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8))
2121

22-
# Placeholder generation function (creates a base image, will be resized later if needed by UI)
22+
# Placeholder generation function (remains unused in the optimized path, keep for robustness?)
23+
# Note: Placeholder generation is removed from the main optimized logic below.
24+
# If an error occurs during copy, the image is simply skipped in the preview.
2325
def create_placeholder(size=(128, 128), text="?"):
2426
"""Creates a simple placeholder PIL image."""
2527
img = Image.new('RGB', size, color = (40, 40, 40))
2628
d = ImageDraw.Draw(img)
2729
font = ImageFont.load_default() # Keep it simple for placeholders
2830
try:
2931
# Basic centering for default font
30-
tw, th = d.textsize(text, font=font) if hasattr(d, 'textsize') else (10, 10)
32+
tw, th = d.textbbox((0,0), text, font=font)[2:] if hasattr(d, 'textbbox') else (10, 10) # Use textbbox if available
3133
d.text(((size[0]-tw)/2, (size[1]-th)/2), text, font=font, fill=(180, 180, 180))
3234
except Exception as e:
3335
print(f"[PreviewHistory] Error drawing placeholder text: {e}")
3436
d.text((10, 10), text, fill=(180, 180, 180)) # Fallback position
3537
return img
3638

39+
3740
class PreviewHistory:
3841
# Lock for file system operations in the target directory
3942
_dir_lock = threading.Lock()
@@ -66,7 +69,6 @@ def INPUT_TYPES(s):
6669

6770
def execute(self, image, history_size):
6871

69-
# Use the default history folder path directly
7072
history_folder = DEFAULT_HISTORY_FOLDER
7173

7274
# Ensure target directory exists
@@ -76,36 +78,42 @@ def execute(self, image, history_size):
7678
print(f"[PreviewHistory] Created history directory: {history_folder}")
7779
except OSError as e:
7880
print(f"[PreviewHistory] Error creating directory {history_folder}: {e}. Cannot proceed.")
79-
return {"ui": {"images": []}}
81+
# Return empty previews if directory creation fails
82+
return {"ui": {"images": []}} # Return empty list
8083

8184
# --- Save New Image (if provided) ---
85+
new_image_saved_path = None
8286
if image is not None and image.nelement() > 0:
8387
new_pil_image = tensor2pil(image)
8488
with PreviewHistory._dir_lock: # Lock specifically for saving
8589
try:
8690
now = datetime.now()
8791
timestamp_final = now.strftime("%d-%m-%Y_%H-%M-%S")
92+
# Include milliseconds for higher uniqueness, preventing rare collisions
93+
timestamp_final += f"_{now.microsecond // 1000:03d}"
8894
new_filename = f"history_{timestamp_final}.png"
8995
new_path = os.path.join(history_folder, new_filename)
90-
new_pil_image.save(new_path, "PNG", compress_level=1)
96+
new_pil_image.save(new_path, "PNG", compress_level=1) # Faster compression
97+
new_image_saved_path = new_path # Keep track of the path if saved
9198
# print(f"[PreviewHistory] Saved new file: {new_path}") # Debug
9299
except Exception as e:
93100
print(f"[PreviewHistory] Error saving new image to '{history_folder}': {e}")
94101
# Continue even if saving fails, try to show existing history
95102

96-
97-
# --- Cleanup Old Files & Load Images for Preview ---
98-
preview_images_pil = []
99-
sorted_history_files = []
100-
with PreviewHistory._dir_lock: # Lock for listing, cleanup, and getting paths
103+
# --- Cleanup Old Files & Get List for Preview ---
104+
sorted_history_files = [] # List to hold full paths of files to preview
105+
with PreviewHistory._dir_lock: # Lock for listing, cleanup
101106
try:
102-
# Get all .png files with modification times for cleanup and loading
107+
# Get all .png files with modification times for cleanup
103108
all_files = []
104109
for filename in os.listdir(history_folder):
105110
if filename.lower().endswith(".png"):
106111
full_path = os.path.join(history_folder, filename)
107112
try:
108113
if os.path.isfile(full_path):
114+
# Use creation time if available and potentially more stable, else modification time
115+
# Note: ctime might be platform-dependent (inode change on Unix, creation on Win)
116+
# Stick to mtime for broader consistency unless ctime is specifically desired
109117
mod_time = os.path.getmtime(full_path)
110118
all_files.append((mod_time, full_path))
111119
except OSError:
@@ -115,89 +123,74 @@ def execute(self, image, history_size):
115123
# Sort by modification time, newest first
116124
all_files.sort(key=lambda x: x[0], reverse=True)
117125

126+
# Determine which files to keep based on history_size
127+
files_to_keep_info = all_files[:history_size]
128+
files_to_remove_info = all_files[history_size:]
129+
118130
# Cleanup: Remove files exceeding the current history_size
119-
if len(all_files) > history_size:
120-
files_to_remove = all_files[history_size:] # Get the oldest ones
121-
# print(f"[PreviewHistory] Found {len(all_files)} files, keeping {history_size}, removing {len(files_to_remove)}.") # Debug
122-
for mod_time, path_to_remove in files_to_remove:
131+
if files_to_remove_info:
132+
# print(f"[PreviewHistory] Found {len(all_files)} files, keeping {history_size}, removing {len(files_to_remove_info)}.") # Debug
133+
for mod_time, path_to_remove in files_to_remove_info:
123134
try:
124135
# print(f"[PreviewHistory] Removing old file: {path_to_remove}") # Debug
125136
os.remove(path_to_remove)
126137
except OSError as e:
127138
print(f"[PreviewHistory] Error removing old file {path_to_remove}: {e}")
128-
# Keep only the files that were not removed
129-
files_to_keep = all_files[:history_size]
130-
else:
131-
files_to_keep = all_files # Keep all if within limit
132139

133-
# Get the paths for the files we are keeping for the preview
134-
sorted_history_files = [f[1] for f in files_to_keep]
140+
# Get the final list of full paths for the files we are keeping for the preview
141+
sorted_history_files = [f[1] for f in files_to_keep_info]
135142

136143
except Exception as e:
137-
print(f"[PreviewHistory] Error listing/cleaning files in {history_folder} for preview: {e}")
144+
print(f"[PreviewHistory] Error listing/cleaning files in {history_folder}: {e}")
138145
# Fall through with empty list if error during file operations
139146

140147

141-
# Determine a consistent size for placeholders if needed
142-
placeholder_size = (128, 128) # Default
143-
if sorted_history_files: # Check if we have any files paths left after potential cleanup
144-
try:
145-
# Try loading the first actual image (newest one)
146-
first_image_path = sorted_history_files[0]
147-
temp_img = Image.open(first_image_path)
148-
placeholder_size = temp_img.size
149-
temp_img.close()
150-
except Exception:
151-
pass # Ignore if it fails, keep default size
152-
153-
# Load images from the potentially cleaned-up list
154-
for i, fp in enumerate(sorted_history_files): # Iterate through the paths we collected
155-
try:
156-
img = Image.open(fp).convert('RGB')
157-
preview_images_pil.append(img)
158-
except Exception as e:
159-
print(f"[PreviewHistory] Error loading history image '{fp}' for preview: {e}")
160-
# Keep error placeholder for load failures, use index 'i' for label
161-
preview_images_pil.append(create_placeholder(placeholder_size, f"Err {i:02d}"))
162-
163-
164-
# --- Generate Preview Data for the UI ---
148+
# --- Generate Preview Data for the UI by Copying Files ---
165149
previews = []
166150
preview_server = server.PromptServer.instance # Get server instance
167151

168-
for i, pil_img in enumerate(preview_images_pil):
169-
if pil_img is None: continue # Should not happen with placeholder logic
152+
# Process the sorted list of history files to generate previews
153+
for i, history_file_path in enumerate(sorted_history_files):
154+
if not os.path.exists(history_file_path):
155+
print(f"[PreviewHistory] Warning: File {history_file_path} not found during preview generation (maybe removed?). Skipping.")
156+
continue
170157

171158
try:
172-
# Use numpy array for saving temporary preview file
173-
img_array = np.array(pil_img).astype(np.uint8)
174-
# Define a unique prefix for each temp preview file in the batch
175-
# Simplify prefix - just use the index 'i' from the preview loop
159+
# 1. Get image dimensions efficiently
160+
# Use a context manager to ensure the file handle is closed
161+
# PIL often reads headers without loading the full image data
162+
with Image.open(history_file_path) as img:
163+
width, height = img.size
164+
165+
# 2. Determine temporary path information using dimensions
166+
# Use a distinct prefix based on the original filename hash or index to avoid collisions in temp dir
167+
# Using index 'i' is simple and effective here.
176168
filename_prefix = f"PreviewHistory_Item_{i:02d}_"
177-
178-
# Get path for temporary preview file
179-
# Note: using dimensions from pil_img directly
180169
full_output_folder, fname, count, subfolder, _ = folder_paths.get_save_image_path(
181-
filename_prefix, self.output_dir, pil_img.width, pil_img.height
170+
filename_prefix, self.output_dir, width, height
182171
)
183-
file = f"{fname}_{count:05}_.png" # Temp preview is always png
172+
temp_filename = f"{fname}_{count:05}_.png" # Previews are always PNG
173+
temp_file_path = os.path.join(full_output_folder, temp_filename)
184174

185-
# Save the image (from history dir or placeholder) to the temporary location for UI preview
186-
pil_img.save(os.path.join(full_output_folder, file), quality=95) # Good quality for preview
175+
# 3. Copy the file from history folder to temp folder
176+
# shutil.copy2 preserves more metadata (like mtime), copy is slightly faster if metadata isn't needed.
177+
# Using copy should be sufficient here.
178+
shutil.copy(history_file_path, temp_file_path)
187179

188-
# Append preview info for this image
180+
# 4. Append preview info for this image
189181
previews.append({
190-
"filename": file,
182+
"filename": temp_filename,
191183
"subfolder": subfolder,
192184
"type": self.type
193185
})
194186

195187
except Exception as e:
196-
print(f"[PreviewHistory] Error generating UI preview for image {i}: {e}")
197-
# Optionally skip this preview or add an error indicator? For now, just skip.
188+
print(f"[PreviewHistory] Error processing history image '{history_file_path}' for preview: {e}. Skipping this image.")
189+
# Optionally, could create and copy a placeholder image here, but skipping is simpler/faster.
198190

199191

200192
# Return the list of preview data dictionaries
193+
# print(f"[PreviewHistory] Generated {len(previews)} previews.") # Debug
201194
return {"ui": {"images": previews}}
202195

203196

0 commit comments

Comments
 (0)