Files
EricRollei-AAA_Metadata_System/documentation/node_template.md
T

9.9 KiB

  1. Node Template for Metadata Integration Let's create a template that any node can use to properly integrate with the metadata system. This template will follow best practices and provide a solid foundation for new nodes.

Template for nodes that integrate with the metadata system


import os
import datetime
from typing import Dict, Any, List, Optional

# Import the metadata service
from eric_metadata.service import MetadataService

class MetadataEnabledNode:
    """Base template for a node that integrates with the metadata system"""
    
    def __init__(self):
        """Initialize the node and metadata service"""
        self.metadata_service = MetadataService(debug=False)
        
    @classmethod
    def INPUT_TYPES(cls):
        """Define node inputs with metadata options"""
        return {
            "required": {
                "image": ("IMAGE",),
                "input_filepath": ("STRING", {"default": ""}),
            },
            "optional": {
                # Metadata writing options
                "write_metadata_to_xmp": ("BOOLEAN", {"default": True}),
                "embed_metadata": ("BOOLEAN", {"default": True}),
                "write_text_file": ("BOOLEAN", {"default": False}),
                "write_to_database": ("BOOLEAN", {"default": False}),
                "debug_logging": ("BOOLEAN", {"default": False})
            }
        }
    
    @classmethod
    def RETURN_TYPES(cls):
        """Define node outputs"""
        return ("IMAGE", "STRING")
    
    @classmethod
    def RETURN_NAMES(cls):
        """Define node output names"""
        return ("image", "message")
    
    @classmethod
    def FUNCTION(cls):
        """Define the function name called"""
        return "process"
    
    @classmethod
    def CATEGORY(cls):
        """Define node category"""
        return "Eric's Nodes/Metadata"
    
    def process(self, image, input_filepath="", 
               write_metadata_to_xmp=True, embed_metadata=True, 
               write_text_file=False, write_to_database=False,
               debug_logging=False) -> tuple:
        """
        Process image and write metadata
        
        Args:
            image: Input image
            input_filepath: Path to image file
            write_metadata_to_xmp: Whether to write to XMP sidecar
            embed_metadata: Whether to embed metadata in image
            write_text_file: Whether to write to text file
            write_to_database: Whether to write to database
            debug_logging: Whether to enable debug logging
            
        Returns:
            tuple: (image, message)
        """
        try:
            # Enable debug logging if requested
            if debug_logging:
                self.metadata_service.debug = True
                
            # Skip metadata operations if no filepath provided
            if not input_filepath:
                return (image, "No filepath provided, skipping metadata operations")
                
            # Perform your node's core functionality here
            # ------------------------------------------
            result_data = self._analyze_image(image)
            # ------------------------------------------
            
            # Create metadata structure according to standards
            metadata = self._create_metadata(result_data)
            
            # Set targets based on user preferences
            targets = []
            if write_metadata_to_xmp: targets.append('xmp')
            if embed_metadata: targets.append('embedded')
            if write_text_file: targets.append('txt') 
            if write_to_database: targets.append('db')
            
            # Skip metadata writing if no targets selected
            if not targets:
                return (image, "No metadata targets selected, skipping metadata write")
                
            # Set resource identifier (important for proper XMP handling)
            filename = os.path.basename(input_filepath)
            resource_uri = f"file:///{filename}"
            self.metadata_service.set_resource_identifier(resource_uri)
            
            # Write metadata
            write_results = self.metadata_service.write_metadata(input_filepath, metadata, targets=targets)
            
            # Prepare status message
            success_targets = [t for t, success in write_results.items() if success]
            fail_targets = [t for t, success in write_results.items() if not success]
            
            if all(write_results.values()):
                message = f"Successfully wrote metadata to all targets: {', '.join(targets)}"
            elif any(write_results.values()):
                message = f"Partially wrote metadata. Success: {', '.join(success_targets)}. Failed: {', '.join(fail_targets)}"
            else:
                message = "Failed to write metadata to any target"
                
            return (image, message)
            
        except Exception as e:
            error_msg = f"Error in process: {str(e)}"
            print(error_msg)
            return (image, error_msg)
            
        finally:
            # Always ensure cleanup happens
            self.cleanup()
    
    def _analyze_image(self, image) -> Dict[str, Any]:
        """
        Perform the core analysis on the image
        
        Args:
            image: Input image
            
        Returns:
            dict: Analysis results
        """
        # OVERRIDE THIS METHOD IN YOUR SUBCLASS
        # This is where you put your node's specific functionality
        
        # Example placeholder implementation
        return {
            'score': 0.5,
            'confidence': 0.9,
            'timestamp': datetime.datetime.now().isoformat()
        }
    
    def _create_metadata(self, result_data: Dict[str, Any]) -> Dict[str, Any]:
        """
        Create metadata structure from analysis results
        
        Args:
            result_data: Analysis results
            
        Returns:
            dict: Structured metadata
        """
        # OVERRIDE THIS METHOD IN YOUR SUBCLASS
        # This is where you convert your node's results into a standardized metadata structure
        
        # Example placeholder implementation for a technical analysis node
        metadata = {
            'analysis': {
                'technical': {
                    'my_metric': {
                        'score': result_data.get('score', 0.0),
                        'confidence': result_data.get('confidence', 1.0),
                        'higher_better': True,
                        'timestamp': result_data.get('timestamp', self.metadata_service.get_timestamp())
                    }
                }
            }
        }
        
        return metadata
    
    def cleanup(self):
        """Clean up resources"""
        if hasattr(self, 'metadata_service'):
            self.metadata_service.cleanup()
    
    def __del__(self):
        """Ensure cleanup on deletion"""
        self.cleanup()

```python

Node Implementation Examples
Here are examples of how to implement specific node types using this template:
1. Technical Analysis Node Example (e.g., Blur Detection)


class BlurDetectionNode(MetadataEnabledNode):
    """Node for detecting image blur and storing results in metadata"""
    
    @classmethod
    def CATEGORY(cls):
        return "Eric's Nodes/Technical Analysis"
    
    def _analyze_image(self, image):
        # Implement blur detection algorithm here
        # This is just a placeholder
        blur_score = self._calculate_blur(image)
        
        return {
            'blur_score': blur_score,
            'confidence': 0.95,
            'timestamp': datetime.datetime.now().isoformat()
        }
    
    def _calculate_blur(self, image):
        # Actual blur detection implementation would go here
        # This is just a placeholder
        return 85.3
    
    def _create_metadata(self, result_data):
        # Structure metadata according to standards
        metadata = {
            'analysis': {
                'technical': {
                    'blur': {
                        'score': float(result_data['blur_score']),
                        'higher_better': True,  # Higher is sharper
                        'confidence': float(result_data['confidence']),
                        'timestamp': result_data['timestamp']
                    }
                }
            }
        }
        
        return metadata

2. Classification Node Example (e.g., Style Classification)

class StyleClassificationNode(MetadataEnabledNode):
    """Node for classifying image style and storing results in metadata"""
    
    @classmethod
    def CATEGORY(cls):
        return "Eric's Nodes/Classification"
    
    def _analyze_image(self, image):
        # Implement style classification here
        # This is just a placeholder
        style, confidence = self._classify_style(image)
        
        return {
            'style': style,
            'confidence': confidence,
            'timestamp': datetime.datetime.now().isoformat()
        }
    
    def _classify_style(self, image):
        # Actual style classification implementation would go here
        # This is just a placeholder
        return "minimalist", 0.87
    
    def _create_metadata(self, result_data):
        # Structure metadata according to standards
        metadata = {
            'analysis': {
                'classification': {
                    'style': result_data['style'],
                    'style_confidence': float(result_data['confidence']),
                    'timestamp': result_data['timestamp']
                }
            }
        }
        
        return metadata



This template and examples should provide a solid foundation for integrating any node with your metadata system. The template handles all the common functionality (service initialization, cleanup, resource identification, error handling), while the specific node implementations only need to focus on their core functionality and proper metadata structure.