174 lines
7.6 KiB
Python
174 lines
7.6 KiB
Python
from concurrent.futures import ThreadPoolExecutor
|
|
from threading import Lock
|
|
import logging
|
|
import os
|
|
import gc
|
|
|
|
# Import utilities
|
|
from .utils.cache import LRUCache
|
|
from .utils.animation import AnimationType, EASING_FUNCTIONS
|
|
|
|
# Configure logging
|
|
logging.basicConfig(level=logging.INFO)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
class GeekyRemB_Animator:
|
|
"""Animation module for GeekyRemB with keyframe support"""
|
|
|
|
def __init__(self):
|
|
self.use_gpu = False # No need for GPU in parameter provider
|
|
|
|
# Thread pool for parallel processing
|
|
cpu_cores = os.cpu_count() or 4
|
|
self.max_workers = min(cpu_cores, 8)
|
|
self.executor = ThreadPoolExecutor(max_workers=self.max_workers)
|
|
|
|
# Cache for processed frames
|
|
self.frame_cache = LRUCache(maxsize=100)
|
|
|
|
def cleanup_resources(self):
|
|
"""Clean up resources"""
|
|
try:
|
|
if self.executor is not None:
|
|
try:
|
|
self.executor.shutdown(wait=False)
|
|
except Exception as e:
|
|
logger.error(f"Error shutting down executor: {str(e)}")
|
|
self.executor = None
|
|
|
|
if self.frame_cache is not None:
|
|
try:
|
|
self.frame_cache.clear()
|
|
except Exception as e:
|
|
logger.error(f"Error clearing cache: {str(e)}")
|
|
self.frame_cache = None
|
|
|
|
# Force garbage collection
|
|
gc.collect()
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error during cleanup: {str(e)}")
|
|
|
|
def __del__(self):
|
|
"""Ensure proper cleanup on deletion"""
|
|
try:
|
|
self.cleanup_resources()
|
|
except:
|
|
pass
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"output_format": (["RGBA", "RGB"],),
|
|
"animation_type": ([anim.value for anim in AnimationType],),
|
|
"animation_speed": ("FLOAT", {"default": 1.0, "min": 0.1, "max": 10.0, "step": 0.1}),
|
|
"frame_count": ("INT", {"default": 30, "min": 1, "max": 1000, "step": 1}), # Changed from animation_duration
|
|
"repeats": ("INT", {"default": 1, "min": 1, "max": 10, "step": 1}),
|
|
"reverse": ("BOOLEAN", {"default": False}),
|
|
"easing_function": (list(EASING_FUNCTIONS.keys()),),
|
|
"fps": ("INT", {"default": 30, "min": 1, "max": 120, "step": 1}),
|
|
"use_keyframes": ("BOOLEAN", {"default": False}),
|
|
"delay": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 5.0, "step": 0.1}),
|
|
"x_position": ("INT", {"default": 0, "min": -1000, "max": 1000, "step": 1}),
|
|
"y_position": ("INT", {"default": 0, "min": -1000, "max": 1000, "step": 1}),
|
|
"scale": ("FLOAT", {"default": 1.0, "min": 0.1, "max": 5.0, "step": 0.1}),
|
|
"rotation": ("FLOAT", {"default": 0.0, "min": -360.0, "max": 360.0, "step": 1.0}),
|
|
"steps": ("INT", {"default": 1, "min": 1, "max": 10, "step": 1}),
|
|
"phase_shift": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.01}),
|
|
},
|
|
"optional": {
|
|
# Keyframe inputs
|
|
"keyframe1": ("KEYFRAME",),
|
|
"keyframe2": ("KEYFRAME",),
|
|
"keyframe3": ("KEYFRAME",),
|
|
"keyframe4": ("KEYFRAME",),
|
|
"keyframe5": ("KEYFRAME",),
|
|
}
|
|
}
|
|
|
|
RETURN_TYPES = ("ANIMATOR",)
|
|
FUNCTION = "provide_animator"
|
|
CATEGORY = "image/animation"
|
|
DISPLAY_NAME = "Geeky RemB Animator"
|
|
|
|
def provide_animator(self, output_format, animation_type, animation_speed, frame_count,
|
|
repeats, reverse, easing_function, fps, use_keyframes, delay, x_position, y_position,
|
|
scale, rotation, steps, phase_shift,
|
|
keyframe1=None, keyframe2=None, keyframe3=None, keyframe4=None, keyframe5=None):
|
|
"""Provides animation parameters to the main GeekyRemB node"""
|
|
|
|
# Calculate animation duration from frame_count and fps for compatibility
|
|
animation_duration = frame_count / fps if fps > 0 else 1.0
|
|
|
|
# Check if keyframes are provided and should be used
|
|
if use_keyframes:
|
|
keyframes = []
|
|
# Add provided keyframes to the list, ignore None values
|
|
if keyframe1 is not None:
|
|
keyframes.append(keyframe1)
|
|
logger.info(f"Added keyframe1: {keyframe1}")
|
|
if keyframe2 is not None:
|
|
keyframes.append(keyframe2)
|
|
logger.info(f"Added keyframe2: {keyframe2}")
|
|
if keyframe3 is not None:
|
|
keyframes.append(keyframe3)
|
|
logger.info(f"Added keyframe3: {keyframe3}")
|
|
if keyframe4 is not None:
|
|
keyframes.append(keyframe4)
|
|
logger.info(f"Added keyframe4: {keyframe4}")
|
|
if keyframe5 is not None:
|
|
keyframes.append(keyframe5)
|
|
logger.info(f"Added keyframe5: {keyframe5}")
|
|
|
|
if keyframes:
|
|
# Sort keyframes by frame number
|
|
keyframes.sort(key=lambda k: k.get("frame", 0))
|
|
|
|
# Create keyframe animation parameters with explicit animation_type=keyframe
|
|
keyframe_params = {
|
|
"animation_type": "keyframe", # Explicitly set as keyframe
|
|
"fps": fps,
|
|
"keyframes": keyframes,
|
|
"default_easing": easing_function,
|
|
"duration": animation_duration, # Calculated from frame_count and fps
|
|
"total_frames": frame_count, # Use frame_count directly
|
|
# Add base values for interpolation reference
|
|
"base_position": (x_position, y_position),
|
|
"base_scale": scale,
|
|
"base_rotation": rotation
|
|
}
|
|
|
|
# Log keyframe details for debugging
|
|
for i, kf in enumerate(keyframes):
|
|
frame = kf.get("frame", 0)
|
|
pos = kf.get("position", (0, 0))
|
|
scl = kf.get("scale", 1.0)
|
|
rot = kf.get("rotation", 0.0)
|
|
logger.info(f"Keyframe {i}: frame={frame}, position={pos}, scale={scl}, rotation={rot}")
|
|
|
|
logger.info(f"Using keyframe animation with {len(keyframes)} keyframes for {frame_count} total frames")
|
|
return ({"animation_params": keyframe_params},)
|
|
|
|
# If no keyframes or use_keyframes is False, use standard animation
|
|
animation_params = {
|
|
"output_format": output_format,
|
|
"animation_type": animation_type,
|
|
"animation_speed": animation_speed,
|
|
"frame_count": frame_count, # Use frame_count directly
|
|
"animation_duration": animation_duration, # Include for compatibility
|
|
"repeats": repeats,
|
|
"reverse": reverse,
|
|
"easing_function": easing_function,
|
|
"fps": fps,
|
|
"delay": delay,
|
|
"x_position": x_position,
|
|
"y_position": y_position,
|
|
"scale": scale,
|
|
"rotation": rotation,
|
|
"steps": steps,
|
|
"phase_shift": phase_shift
|
|
}
|
|
|
|
return ({"animation_params": animation_params},)
|