bl_info = { "name": "XMP Metadata Sidecar with Hierarchical Keywords", "blender": (4, 2, 0), "category": "File",}import bpyimport os# Function to create the XMP file with hierarchical keywordsdef create_xmp_file(filepath, keywords, description, credit): xmp_filename = filepath.replace(".blend", ".xmp") # Prepare hierarchical keywords (split by '|') keyword_list = keywords.split('|') hierarchical_keywords = ''.join([f"{kw}" for kw in keyword_list]) xmp_content = f""" {hierarchical_keywords} {description} {credit} """ # Write the XMP file with open(xmp_filename, 'w') as f: f.write(xmp_content)# Operator to handle the XMP metadata saving processclass SaveXMPMetadataOperator(bpy.types.Operator): bl_idname = "wm.save_xmp_metadata" bl_label = "Save XMP Metadata" def execute(self, context): scene = context.scene keywords = scene.xmp_metadata_keywords description = scene.xmp_metadata_description credit = scene.xmp_metadata_credit filepath = bpy.data.filepath if filepath: create_xmp_file(filepath, keywords, description, credit) self.report({'INFO'}, f"XMP Metadata saved to: {filepath.replace('.blend', '.xmp')}") else: self.report({'WARNING'}, "Please save the Blender file first.") return {'FINISHED'}# Panel for the graphical interfaceclass XMPMetadataPanel(bpy.types.Panel): bl_label = "XMP Metadata" bl_idname = "VIEW3D_PT_xmp_metadata" bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = 'XMP Metadata' def draw(self, context): layout = self.layout scene = context.scene layout.prop(scene, "xmp_metadata_keywords") layout.prop(scene, "xmp_metadata_description") layout.prop(scene, "xmp_metadata_credit") layout.operator("wm.save_xmp_metadata")# Function to automatically save XMP when the Blender file is saveddef auto_save_xmp(scene): filepath = bpy.data.filepath if filepath: keywords = scene.xmp_metadata_keywords description = scene.xmp_metadata_description credit = scene.xmp_metadata_credit create_xmp_file(filepath, keywords, description, credit)# Property definitions for metadata fieldsdef register_properties(): bpy.types.Scene.xmp_metadata_keywords = bpy.props.StringProperty( name="Hierarchical Keywords", description="Enter hierarchical keywords separated by '|'. Example: 'Keyword1|SubKeyword1|SubKeyword2'" ) bpy.types.Scene.xmp_metadata_description = bpy.props.StringProperty( name="Description", description="Enter a description for the XMP metadata" ) bpy.types.Scene.xmp_metadata_credit = bpy.props.StringProperty( name="Credit", description="Enter a credit for the XMP metadata" )def unregister_properties(): del bpy.types.Scene.xmp_metadata_keywords del bpy.types.Scene.xmp_metadata_description del bpy.types.Scene.xmp_metadata_credit# Add-on registrationdef register(): bpy.utils.register_class(SaveXMPMetadataOperator) bpy.utils.register_class(XMPMetadataPanel) register_properties() bpy.app.handlers.save_post.append(auto_save_xmp)def unregister(): bpy.utils.unregister_class(SaveXMPMetadataOperator) bpy.utils.unregister_class(XMPMetadataPanel) unregister_properties() bpy.app.handlers.save_post.remove(auto_save_xmp)if __name__ == "__main__": register()