【剪映小助手源码精讲】13_元数据管理系统

第13章:元数据管理系统

13.1 元数据系统概述

元数据管理系统是剪映小助手中的核心基础设施,负责管理和维护各种媒体资源的元数据信息。元数据是描述数据的数据,在视频编辑软件中,元数据包含了字体、遮罩、转场、动画效果等各种配置信息。本章将详细介绍元数据管理系统的架构设计和实现细节。

13.1.1 元数据的重要性

元数据在视频编辑软件中扮演着至关重要的角色:

资源管理:通过元数据可以快速定位和检索所需的媒体资源
配置管理:保存和管理各种效果的参数配置
版本控制:跟踪元数据的变化历史,支持版本回退
性能优化:通过缓存和索引机制提高元数据的访问效率
扩展性:支持新类型元数据的动态添加和管理

13.1.2 系统架构设计

元数据管理系统采用分层架构设计,主要包括以下几个层次:

数据访问层:负责元数据的存储和检索
业务逻辑层:处理元数据的业务逻辑和规则
API接口层:提供统一的元数据访问接口
缓存层:提高元数据的访问性能
索引层:支持高效的元数据搜索和过滤

13.2 元数据基础模型设计

13.2.1 元数据基类实现

首先,我们定义元数据的抽象基类,为所有具体的元数据类型提供统一的接口:

from abc import ABC, abstractmethod
from typing import Dict, Any, Optional, List
from datetime import datetime
from enum import Enum
import json
import uuid

class MetadataType(Enum):
    """元数据类型枚举"""
    FONT = "font"
    MASK = "mask"
    TRANSITION = "transition"
    ANIMATION = "animation"
    EFFECT = "effect"
    STICKER = "sticker"
    FILTER = "filter"
    AUDIO = "audio"
    VIDEO = "video"
    IMAGE = "image"
    TEXT = "text"
    COLOR = "color"
    LAYOUT = "layout"

class MetadataStatus(Enum):
    """元数据状态枚举"""
    ACTIVE = "active"
    INACTIVE = "inactive"
    DEPRECATED = "deprecated"
    DRAFT = "draft"
    PUBLISHED = "published"

class BaseMetadata(ABC):
    """元数据基类"""
    
    def __init__(self, metadata_type: MetadataType, name: str = "", 
                 description: str = "", version: str = "1.0.0"):
        self.metadata_id = str(uuid.uuid4())
        self.metadata_type = metadata_type
        self.name = name
        self.description = description
        self.version = version
        self.status = MetadataStatus.ACTIVE
        self.created_at = datetime.now()
        self.updated_at = datetime.now()
        self.created_by = "system"
        self.updated_by = "system"
        self.tags: List[str] = []
        self.properties: Dict[str, Any] = {}
        self.dependencies: List[str] = []
        self.compatibility: Dict[str, Any] = {}
        self.performance_metrics: Dict[str, Any] = {}
        self.usage_statistics: Dict[str, Any] = {
            "usage_count": 0,
            "last_used": None,
            "average_rating": 0.0,
            "user_ratings": []
        }
    
    @abstractmethod
    def validate(self) -> bool:
        """验证元数据的有效性"""
        pass
    
    @abstractmethod
    def get_default_config(self) -> Dict[str, Any]:
        """获取默认配置"""
        pass
    
    @abstractmethod
    def apply_config(self, config: Dict[str, Any]) -> bool:
        """应用配置"""
        pass
    
    def update_usage_stats(self, rating: Optional[float] = None) -> None:
        """更新使用统计"""
        self.usage_statistics["usage_count"] += 1
        self.usage_statistics["last_used"] = datetime.now()
        
        if rating is not None:
            self.usage_statistics["user_ratings"].append(rating)
            ratings = self.usage_statistics["user_ratings"]
            self.usage_statistics["average_rating"] = sum(ratings) / len(ratings)
    
    def add_tag(self, tag: str) -> None:
        """添加标签"""
        if tag not in self.tags:
            self.tags.append(tag)
            self.updated_at = datetime.now()
    
    def remove_tag(self, tag: str) -> None:
        """移除标签"""
        if tag in self.tags:
            self.tags.remove(tag)
            self.updated_at = datetime.now()
    
    def add_dependency(self, dependency_id: str) -> None:
        """添加依赖"""
        if dependency_id not in self.dependencies:
            self.dependencies.append(dependency_id)
            self.updated_at = datetime.now()
    
    def remove_dependency(self, dependency_id: str) -> None:
        """移除依赖"""
        if dependency_id in self.dependencies:
            self.dependencies.remove(dependency_id)
            self.updated_at = datetime.now()
    
    def set_property(self, key: str, value: Any) -> None:
        """设置属性"""
        self.properties[key] = value
        self.updated_at = datetime.now()
    
    def get_property(self, key: str, default: Any = None) -> Any:
        """获取属性"""
        return self.properties.get(key, default)
    
    def set_compatibility(self, platform: str, version: str) -> None:
        """设置兼容性信息"""
        self.compatibility[platform] = version
        self.updated_at = datetime.now()
    
    def is_compatible(self, platform: str, version: str) -> bool:
        """检查兼容性"""
        if platform not in self.compatibility:
            return True  # 默认兼容
        
        required_version = self.compatibility[platform]
        return self._compare_versions(version, required_version) >= 0
    
    def _compare_versions(self, version1: str, version2: str) -> int:
        """比较版本号"""
        v1_parts = [int(x) for x in version1.split('.')]
        v2_parts = [int(x) for x in version2.split('.')]
        
        for i in range(max(len(v1_parts), len(v2_parts))):
            v1_part = v1_parts[i] if i < len(v1_parts) else 0
            v2_part = v2_parts[i] if i < len(v2_parts) else 0
            
            if v1_part < v2_part:
                return -1
            elif v1_part > v2_part:
                return 1
        
        return 0
    
    def update_performance_metrics(self, metrics: Dict[str, Any]) -> None:
        """更新性能指标"""
        self.performance_metrics.update(metrics)
        self.updated_at = datetime.now()
    
    def get_similarity_score(self, other_metadata: 'BaseMetadata') -> float:
        """计算与其他元数据的相似度分数"""
        score = 0.0
        total_weight = 0.0
        
        # 类型相似度
        if self.metadata_type == other_metadata.metadata_type:
            score += 1.0
            total_weight += 1.0
        
        # 标签相似度
        if self.tags and other_metadata.tags:
            common_tags = set(self.tags) & set(other_metadata.tags)
            tag_similarity = len(common_tags) / max(len(self.tags), len(other_metadata.tags))
            score += tag_similarity * 0.5
            total_weight += 0.5
        
        # 名称相似度(简单的字符串相似度)
        if self.name and other_metadata.name:
            name_similarity = self._calculate_string_similarity(self.name, other_metadata.name)
            score += name_similarity * 0.3
            total_weight += 0.3
        
        return score / total_weight if total_weight > 0 else 0.0
    
    def _calculate_string_similarity(self, s1: str, s2: str) -> float:
        """计算字符串相似度"""
        if not s1 or not s2:
            return 0.0
        
        # 简单的字符匹配相似度
        common_chars = set(s1.lower()) & set(s2.lower())
        total_chars = set(s1.lower()) | set(s2.lower())
        
        return len(common_chars) / len(total_chars) if total_chars else 0.0
    
    def to_dict(self) -> Dict[str, Any]:
        """转换为字典"""
        return {
            "metadata_id": self.metadata_id,
            "metadata_type": self.metadata_type.value,
            "name": self.name,
            "description": self.description,
            "version": self.version,
            "status": self.status.value,
            "created_at": self.created_at.isoformat(),
            "updated_at": self.updated_at.isoformat(),
            "created_by": self.created_by,
            "updated_by": self.updated_by,
            "tags": self.tags,
            "properties": self.properties,
            "dependencies": self.dependencies,
            "compatibility": self.compatibility,
            "performance_metrics": self.performance_metrics,
            "usage_statistics": self.usage_statistics
        }
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'BaseMetadata':
        """从字典创建"""
        # 这是一个抽象方法,子类需要实现具体的创建逻辑
        raise NotImplementedError("Subclasses must implement from_dict method")
    
    def __str__(self) -> str:
        """字符串表示"""
        return f"{self.metadata_type.value.title()}Metadata(id={self.metadata_id}, name='{self.name}')"
    
    def __repr__(self) -> str:
        """详细字符串表示"""
        return f"BaseMetadata(type={self.metadata_type.value}, id={self.metadata_id}, name='{self.name}')"

13.2.2 字体元数据实现

字体元数据是视频编辑中最重要的元数据类型之一,它包含了字体的各种属性和配置信息:

class FontStyle(Enum):
    """字体样式枚举"""
    NORMAL = "normal"
    BOLD = "bold"
    ITALIC = "italic"
    BOLD_ITALIC = "bold_italic"

class FontWeight(Enum):
    """字体粗细枚举"""
    THIN = 100
    LIGHT = 300
    NORMAL = 400
    MEDIUM = 500
    SEMI_BOLD = 600
    BOLD = 700
    EXTRA_BOLD = 800
    BLACK = 900

class FontMetadata(BaseMetadata):
    """字体元数据"""
    
    def __init__(self, name: str = "", family: str = "", 
                 style: FontStyle = FontStyle.NORMAL,
                 weight: FontWeight = FontWeight.NORMAL):
        super().__init__(MetadataType.FONT, name, f"字体: {name}")
        
        # 字体基本信息
        self.family = family  # 字体族
        self.style = style    # 字体样式
        self.weight = weight  # 字体粗细
        
        # 字体文件信息
        self.font_file_path = ""
        self.font_file_size = 0
        self.font_file_hash = ""
        
        # 字体度量信息
        self.ascent = 0.0     # 上升高度
        self.descent = 0.0    # 下降高度
        self.line_gap = 0.0   # 行间距
        self.units_per_em = 0  # 每em单位数
        
        # 字符支持
        self.supported_characters: List[str] = []
        self.unicode_ranges: List[Dict[str, Any]] = []
        
        # 字体特征
        self.has_ligatures = False      # 是否支持连字
        self.has_kerning = False      # 是否支持字距调整
        self.is_monospace = False     # 是否为等宽字体
        self.is_variable_font = False   # 是否为可变字体
        
        # 字体变体
        self.available_variants: List[Dict[str, Any]] = []
        self.variable_axes: List[Dict[str, Any]] = []
        
        # 渲染优化
        self.hinting_level = "normal"   # 提示级别
        self.rendering_mode = "normal"  # 渲染模式
        self.anti_aliasing = True       # 是否抗锯齿
        
        # 许可信息
        self.license_type = "unknown"   # 许可类型
        self.license_url = ""            # 许可URL
        self.is_commercial = False       # 是否可用于商业用途
        
        # 性能特征
        self.load_time = 0.0             # 加载时间(毫秒)
        self.memory_usage = 0            # 内存使用(字节)
        self.render_performance = 0.0    # 渲染性能评分
    
    def validate(self) -> bool:
        """验证字体元数据"""
        if not self.name:
            return False
        
        if not self.family:
            return False
        
        if not self.font_file_path:
            return False
        
        # 验证字体文件是否存在
        try:
            import os
            if not os.path.exists(self.font_file_path):
                return False
        except Exception:
            return False
        
        # 验证字体样式
        if not isinstance(self.style, FontStyle):
            return False
        
        # 验证字体粗细
        if not isinstance(self.weight, FontWeight):
            return False
        
        return True
    
    def get_default_config(self) -> Dict[str, Any]:
        """获取默认配置"""
        return {
            "font_family": self.family,
            "font_style": self.style.value,
            "font_weight": self.weight.value,
            "font_size": 16,
            "color": "#000000",
            "background_color": "#FFFFFF",
            "text_align": "left",
            "line_height": 1.2,
            "letter_spacing": 0,
            "word_spacing": 0,
            "text_decoration": "none",
            "text_transform": "none"
        }
    
    def apply_config(self, config: Dict[str, Any]) -> bool:
        """应用配置"""
        try:
            # 验证配置
            if "font_family" in config and config["font_family"] != self.family:
                return False  # 字体族不匹配
            
            # 应用样式配置
            if "font_style" in config:
                style_value = config["font_style"]
                if isinstance(style_value, str):
                    try:
                        self.style = FontStyle(style_value)
                    except ValueError:
                        return False
            
            # 应用粗细配置
            if "font_weight" in config:
                weight_value = config["font_weight"]
                if isinstance(weight_value, int):
                    try:
                        # 找到最接近的权重
                        weight_values = [w.value for w in FontWeight]
                        closest_weight = min(weight_values, key=lambda x: abs(x - weight_value))
                        self.weight = FontWeight(closest_weight)
                    except ValueError:
                        return False
            
            # 更新修改时间
            self.updated_at = datetime.now()
            
            return True
        
        except Exception:
            return False
    
    def supports_character(self, char: str) -> bool:
        """检查是否支持指定字符"""
        if not char:
            return False
        
        # 检查字符是否在支持的字符列表中
        if self.supported_characters:
            return char in self.supported_characters
        
        # 检查Unicode范围
        char_code = ord(char)
        for unicode_range in self.unicode_ranges:
            start = unicode_range.get("start", 0)
            end = unicode_range.get("end", 0)
            if start <= char_code <= end:
                return True
        
        return True
    
    def supports_text(self, text: str) -> bool:
        """检查是否支持指定文本"""
        if not text:
            return True
        
        for char in text:
            if not self.supports_character(char):
                return False
        
        return True
    
    def get_font_variant(self, style: FontStyle, weight: FontWeight) -> Optional[Dict[str, Any]]:
        """获取字体变体"""
        for variant in self.available_variants:
            if (variant.get("style") == style.value and 
                variant.get("weight") == weight.value):
                return variant
        
        return None
    
    def calculate_text_bounds(self, text: str, font_size: float, 
                            max_width: Optional[float] = None) -> Dict[str, Any]:
        """计算文本边界"""
        # 这是一个简化的实现,实际应该使用字体渲染库
        
        # 估算字符宽度(基于平均字符宽度)
        avg_char_width = font_size * 0.6  # 假设平均字符宽度为字体大小的60%
        text_width = len(text) * avg_char_width
        
        # 估算文本高度
        line_height = font_size * self.get_default_config()["line_height"]
        text_height = line_height
        
        # 如果指定了最大宽度,计算需要的行数
        lines = 1
        if max_width and text_width > max_width:
            chars_per_line = int(max_width / avg_char_width)
            lines = (len(text) + chars_per_line - 1) // chars_per_line
            text_width = max_width
            text_height = lines * line_height
        
        return {
            "width": text_width,
            "height": text_height,
            "lines": lines,
            "chars_per_line": chars_per_line if max_width else len(text)
        }
    
    def to_dict(self) -> Dict[str, Any]:
        """转换为字典"""
        base_dict = super().to_dict()
        base_dict.update({
            "family": self.family,
            "style": self.style.value,
            "weight": self.weight.value,
            "font_file_path": self.font_file_path,
            "font_file_size": self.font_file_size,
            "font_file_hash": self.font_file_hash,
            "ascent": self.ascent,
            "descent": self.descent,
            "line_gap": self.line_gap,
            "units_per_em": self.units_per_em,
            "supported_characters": self.supported_characters,
            "unicode_ranges": self.unicode_ranges,
            "has_ligatures": self.has_ligatures,
            "has_kerning": self.has_kerning,
            "is_monospace": self.is_monospace,
            "is_variable_font": self.is_variable_font,
            "available_variants": self.available_variants,
            "variable_axes": self.variable_axes,
            "hinting_level": self.hinting_level,
            "rendering_mode": self.rendering_mode,
            "anti_aliasing": self.anti_aliasing,
            "license_type": self.license_type,
            "license_url": self.license_url,
            "is_commercial": self.is_commercial,
            "load_time": self.load_time,
            "memory_usage": self.memory_usage,
            "render_performance": self.render_performance
        })
        return base_dict
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'FontMetadata':
        """从字典创建"""
        metadata = cls(
            name=data.get("name", ""),
            family=data.get("family", ""),
            style=FontStyle(data.get("style", "normal")),
            weight=FontWeight(data.get("weight", 400))
        )
        
        # 设置基本属性
        metadata.metadata_id = data.get("metadata_id", str(uuid.uuid4()))
        metadata.description = data.get("description", "")
        metadata.version = data.get("version", "1.0.0")
        metadata.status = MetadataStatus(data.get("status", "active"))
        
        # 设置时间戳
        if "created_at" in data:
            metadata.created_at = datetime.fromisoformat(data["created_at"])
        if "updated_at" in data:
            metadata.updated_at = datetime.fromisoformat(data["updated_at"])
        
        # 设置字体特定属性
        metadata.font_file_path = data.get("font_file_path", "")
        metadata.font_file_size = data.get("font_file_size", 0)
        metadata.font_file_hash = data.get("font_file_hash", "")
        metadata.ascent = data.get("ascent", 0.0)
        metadata.descent = data.get("descent", 0.0)
        metadata.line_gap = data.get("line_gap", 0.0)
        metadata.units_per_em = data.get("units_per_em", 0)
        metadata.supported_characters = data.get("supported_characters", [])
        metadata.unicode_ranges = data.get("unicode_ranges", [])
        metadata.has_ligatures = data.get("has_ligatures", False)
        metadata.has_kerning = data.get("has_kerning", False)
        metadata.is_monospace = data.get("is_monospace", False)
        metadata.is_variable_font = data.get("is_variable_font", False)
        metadata.available_variants = data.get("available_variants", [])
        metadata.variable_axes = data.get("variable_axes", [])
        metadata.hinting_level = data.get("hinting_level", "normal")
        metadata.rendering_mode = data.get("rendering_mode", "normal")
        metadata.anti_aliasing = data.get("anti_aliasing", True)
        metadata.license_type = data.get("license_type", "unknown")
        metadata.license_url = data.get("license_url", "")
        metadata.is_commercial = data.get("is_commercial", False)
        metadata.load_time = data.get("load_time", 0.0)
        metadata.memory_usage = data.get("memory_usage", 0)
        metadata.render_performance = data.get("render_performance", 0.0)
        
        # 设置通用属性
        metadata.created_by = data.get("created_by", "system")
        metadata.updated_by = data.get("updated_by", "system")
        metadata.tags = data.get("tags", [])
        metadata.properties = data.get("properties", {})
        metadata.dependencies = data.get("dependencies", [])
        metadata.compatibility = data.get("compatibility", {})
        metadata.performance_metrics = data.get("performance_metrics", {})
        metadata.usage_statistics = data.get("usage_statistics", {
            "usage_count": 0,
            "last_used": None,
            "average_rating": 0.0,
            "user_ratings": []
        })
        
        return metadata

13.2.3 遮罩元数据实现

遮罩元数据用于管理视频遮罩效果的各种配置信息:

class MaskType(Enum):
    """遮罩类型枚举"""
    SHAPE = "shape"
    ALPHA = "alpha"
    LUMINANCE = "luminance"
    COLOR = "color"
    CUSTOM = "custom"

class MaskShape(Enum):
    """遮罩形状枚举"""
    RECTANGLE = "rectangle"
    CIRCLE = "circle"
    ELLIPSE = "ellipse"
    POLYGON = "polygon"
    STAR = "star"
    HEART = "heart"
    ARROW = "arrow"
    CUSTOM_PATH = "custom_path"

class MaskMetadata(BaseMetadata):
    """遮罩元数据"""
    
    def __init__(self, name: str = "", mask_type: MaskType = MaskType.SHAPE):
        super().__init__(MetadataType.MASK, name, f"遮罩: {name}")
        
        # 遮罩基本信息
        self.mask_type = mask_type
        self.mask_shape = MaskShape.RECTANGLE
        
        # 几何属性
        self.width = 100.0      # 宽度(百分比)
        self.height = 100.0     # 高度(百分比)
        self.x_position = 0.0   # X位置(百分比)
        self.y_position = 0.0   # Y位置(百分比)
        self.rotation = 0.0     # 旋转角度(度)
        self.scale = 1.0        # 缩放比例
        
        # 形状特定属性
        self.corner_radius = 0.0  # 圆角半径(仅矩形)
        self.sides = 4            # 边数(仅多边形)
        self.star_points = 5      # 星形点数(仅星形)
        
        # 羽化和边缘
        self.feather_amount = 0.0  # 羽化程度
        self.edge_softness = 0.0   # 边缘柔和度
        self.invert_mask = False   # 是否反转遮罩
        
        # 动画属性
        self.animatable_properties: List[str] = [
            "x_position", "y_position", "width", "height", 
            "rotation", "scale", "feather_amount", "edge_softness"
        ]
        
        # 颜色属性(仅颜色遮罩)
        self.color_tolerance = 0.1      # 颜色容差
        self.color_range: List[str] = []  # 颜色范围
        self.key_color = "#000000"       # 关键颜色
        
        # 自定义路径(仅自定义路径遮罩)
        self.custom_path = ""            # SVG路径字符串
        self.path_points: List[Dict[str, float]] = []
        
        # 性能特征
        self.render_complexity = 1.0     # 渲染复杂度(1-10)
        self.gpu_accelerated = True      # 是否支持GPU加速
        self.real_time_capable = True    # 是否支持实时处理
        
        # 兼容性信息
        self.supported_formats: List[str] = ["mp4", "mov", "avi", "mkv"]
        self.minimum_resolution: Dict[str, int] = {"width": 240, "height": 240}
        self.maximum_resolution: Dict[str, int] = {"width": 7680, "height": 4320}
    
    def validate(self) -> bool:
        """验证遮罩元数据"""
        if not self.name:
            return False
        
        # 验证几何属性范围
        if not (0 <= self.width <= 100):
            return False
        
        if not (0 <= self.height <= 100):
            return False
        
        if not (-50 <= self.x_position <= 150):  # 允许稍微超出边界
            return False
        
        if not (-50 <= self.y_position <= 150):
            return False
        
        if not (0 <= self.rotation <= 360):
            return False
        
        if not (0.1 <= self.scale <= 10.0):
            return False
        
        # 验证形状特定属性
        if self.mask_shape == MaskShape.POLYGON and self.sides < 3:
            return False
        
        if self.mask_shape == MaskShape.STAR and self.star_points < 3:
            return False
        
        # 验证羽化和边缘属性
        if not (0 <= self.feather_amount <= 1.0):
            return False
        
        if not (0 <= self.edge_softness <= 1.0):
            return False
        
        # 验证颜色属性(仅颜色遮罩)
        if self.mask_type == MaskType.COLOR:
            if not (0 <= self.color_tolerance <= 1.0):
                return False
            
            if not self.key_color:
                return False
        
        # 验证自定义路径(仅自定义路径遮罩)
        if self.mask_shape == MaskShape.CUSTOM_PATH:
            if not self.custom_path and not self.path_points:
                return False
        
        return True
    
    def get_default_config(self) -> Dict[str, Any]:
        """获取默认配置"""
        config = {
            "mask_type": self.mask_type.value,
            "mask_shape": self.mask_shape.value,
            "width": self.width,
            "height": self.height,
            "x_position": self.x_position,
            "y_position": self.y_position,
            "rotation": self.rotation,
            "scale": self.scale,
            "feather_amount": self.feather_amount,
            "edge_softness": self.edge_softness,
            "invert_mask": self.invert_mask,
            "animatable_properties": self.animatable_properties
        }
        
        # 添加形状特定配置
        if self.mask_shape == MaskShape.RECTANGLE:
            config["corner_radius"] = self.corner_radius
        elif self.mask_shape == MaskShape.POLYGON:
            config["sides"] = self.sides
        elif self.mask_shape == MaskShape.STAR:
            config["star_points"] = self.star_points
        
        # 添加颜色遮罩配置
        if self.mask_type == MaskType.COLOR:
            config["color_tolerance"] = self.color_tolerance
            config["color_range"] = self.color_range
            config["key_color"] = self.key_color
        
        # 添加自定义路径配置
        if self.mask_shape == MaskShape.CUSTOM_PATH:
            config["custom_path"] = self.custom_path
            config["path_points"] = self.path_points
        
        return config
    
    def apply_config(self, config: Dict[str, Any]) -> bool:
        """应用配置"""
        try:
            # 验证遮罩类型
            if "mask_type" in config:
                mask_type_value = config["mask_type"]
                if isinstance(mask_type_value, str):
                    try:
                        self.mask_type = MaskType(mask_type_value)
                    except ValueError:
                        return False
            
            # 验证遮罩形状
            if "mask_shape" in config:
                shape_value = config["mask_shape"]
                if isinstance(shape_value, str):
                    try:
                        self.mask_shape = MaskShape(shape_value)
                    except ValueError:
                        return False
            
            # 应用几何属性
            numeric_fields = [
                "width", "height", "x_position", "y_position", 
                "rotation", "scale", "feather_amount", "edge_softness"
            ]
            
            for field in numeric_fields:
                if field in config:
                    value = config[field]
                    if isinstance(value, (int, float)):
                        setattr(self, field, float(value))
                    else:
                        return False
            
            # 应用布尔属性
            if "invert_mask" in config:
                self.invert_mask = bool(config["invert_mask"])
            
            # 应用形状特定属性
            if self.mask_shape == MaskShape.RECTANGLE and "corner_radius" in config:
                self.corner_radius = float(config["corner_radius"])
            
            if self.mask_shape == MaskShape.POLYGON and "sides" in config:
                self.sides = int(config["sides"])
            
            if self.mask_shape == MaskShape.STAR and "star_points" in config:
                self.star_points = int(config["star_points"])
            
            # 应用颜色遮罩属性
            if self.mask_type == MaskType.COLOR:
                if "color_tolerance" in config:
                    self.color_tolerance = float(config["color_tolerance"])
                
                if "color_range" in config:
                    self.color_range = config["color_range"]
                
                if "key_color" in config:
                    self.key_color = config["key_color"]
            
            # 应用自定义路径
            if self.mask_shape == MaskShape.CUSTOM_PATH:
                if "custom_path" in config:
                    self.custom_path = config["custom_path"]
                
                if "path_points" in config:
                    self.path_points = config["path_points"]
            
            # 更新修改时间
            self.updated_at = datetime.now()
            
            # 验证配置
            return self.validate()
        
        except (ValueError, TypeError):
            return False
    
    def create_mask_at_time(self, time: float, 
                          keyframe_data: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
        """在指定时间创建遮罩"""
        # 获取基础配置
        mask_config = self.get_default_config()
        
        # 应用关键帧数据(如果有)
        if keyframe_data:
            for property_name, value_data in keyframe_data.items():
                if property_name in self.animatable_properties:
                    # 简单的线性插值(实际应该使用更复杂的插值算法)
                    if isinstance(value_data, dict) and "value" in value_data:
                        mask_config[property_name] = value_data["value"]
                    else:
                        mask_config[property_name] = value_data
        
        # 计算遮罩几何
        return self._calculate_mask_geometry(mask_config)
    
    def _calculate_mask_geometry(self, config: Dict[str, Any]) -> Dict[str, Any]:
        """计算遮罩几何"""
        geometry = {
            "type": config["mask_type"],
            "shape": config["mask_shape"],
            "bounds": {
                "x": config["x_position"],
                "y": config["y_position"],
                "width": config["width"],
                "height": config["height"]
            },
            "transform": {
                "rotation": config["rotation"],
                "scale": config["scale"]
            },
            "feather": config["feather_amount"],
            "edge_softness": config["edge_softness"],
            "invert": config["invert_mask"]
        }
        
        # 添加形状特定几何
        if config["mask_shape"] == "rectangle":
            geometry["corner_radius"] = config.get("corner_radius", 0)
        elif config["mask_shape"] == "polygon":
            geometry["sides"] = config.get("sides", 4)
        elif config["mask_shape"] == "star":
            geometry["points"] = config.get("star_points", 5)
        
        # 添加颜色遮罩信息
        if config["mask_type"] == "color":
            geometry["color_tolerance"] = config.get("color_tolerance", 0.1)
            geometry["key_color"] = config.get("key_color", "#000000")
        
        # 添加自定义路径
        if config["mask_shape"] == "custom_path":
            geometry["path"] = config.get("custom_path", "")
            geometry["path_points"] = config.get("path_points", [])
        
        return geometry
    
    def get_render_complexity_score(self) -> float:
        """获取渲染复杂度评分"""
        base_complexity = self.render_complexity
        
        # 根据遮罩类型调整复杂度
        type_multiplier = {
            MaskType.SHAPE: 1.0,
            MaskType.ALPHA: 1.2,
            MaskType.LUMINANCE: 1.3,
            MaskType.COLOR: 1.5,
            MaskType.CUSTOM: 2.0
        }
        
        complexity = base_complexity * type_multiplier.get(self.mask_type, 1.0)
        
        # 根据形状复杂度调整
        shape_multiplier = {
            MaskShape.RECTANGLE: 1.0,
            MaskShape.CIRCLE: 1.1,
            MaskShape.ELLIPSE: 1.2,
            MaskShape.POLYGON: 1.3,
            MaskShape.STAR: 1.4,
            MaskShape.HEART: 1.5,
            MaskShape.ARROW: 1.3,
            MaskShape.CUSTOM_PATH: 2.0
        }
        
        complexity *= shape_multiplier.get(self.mask_shape, 1.0)
        
        # 根据动画属性数量调整
        complexity *= (1.0 + len(self.animatable_properties) * 0.1)
        
        # 根据羽化和边缘调整
        if self.feather_amount > 0:
            complexity *= (1.0 + self.feather_amount * 0.3)
        
        if self.edge_softness > 0:
            complexity *= (1.0 + self.edge_softness * 0.2)
        
        return min(complexity, 10.0)  # 最大复杂度为10
    
    def to_dict(self) -> Dict[str, Any]:
        """转换为字典"""
        base_dict = super().to_dict()
        base_dict.update({
            "mask_type": self.mask_type.value,
            "mask_shape": self.mask_shape.value,
            "width": self.width,
            "height": self.height,
            "x_position": self.x_position,
            "y_position": self.y_position,
            "rotation": self.rotation,
            "scale": self.scale,
            "corner_radius": self.corner_radius,
            "sides": self.sides,
            "star_points": self.star_points,
            "feather_amount": self.feather_amount,
            "edge_softness": self.edge_softness,
            "invert_mask": self.invert_mask,
            "animatable_properties": self.animatable_properties,
            "color_tolerance": self.color_tolerance,
            "color_range": self.color_range,
            "key_color": self.key_color,
            "custom_path": self.custom_path,
            "path_points": self.path_points,
            "render_complexity": self.render_complexity,
            "gpu_accelerated": self.gpu_accelerated,
            "real_time_capable": self.real_time_capable,
            "supported_formats": self.supported_formats,
            "minimum_resolution": self.minimum_resolution,
            "maximum_resolution": self.maximum_resolution
        })
        return base_dict
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'MaskMetadata':
        """从字典创建"""
        metadata = cls(
            name=data.get("name", ""),
            mask_type=MaskType(data.get("mask_type", "shape"))
        )
        
        # 设置基本属性
        metadata.metadata_id = data.get("metadata_id", str(uuid.uuid4()))
        metadata.description = data.get("description", "")
        metadata.version = data.get("version", "1.0.0")
        metadata.status = MetadataStatus(data.get("status", "active"))
        
        # 设置时间戳
        if "created_at" in data:
            metadata.created_at = datetime.fromisoformat(data["created_at"])
        if "updated_at" in data:
            metadata.updated_at = datetime.fromisoformat(data["updated_at"])
        
        # 设置遮罩特定属性
        if "mask_shape" in data:
            metadata.mask_shape = MaskShape(data["mask_shape"])
        
        numeric_fields = [
            "width", "height", "x_position", "y_position", "rotation", "scale",
            "corner_radius", "sides", "star_points", "feather_amount", "edge_softness",
            "color_tolerance", "render_complexity"
        ]
        
        for field in numeric_fields:
            if field in data:
                setattr(metadata, field, float(data[field]))
        
        # 设置布尔属性
        metadata.invert_mask = data.get("invert_mask", False)
        metadata.gpu_accelerated = data.get("gpu_accelerated", True)
        metadata.real_time_capable = data.get("real_time_capable", True)
        metadata.anti_aliasing = data.get("anti_aliasing", True)
        
        # 设置列表属性
        metadata.animatable_properties = data.get("animatable_properties", [])
        metadata.color_range = data.get("color_range", [])
        metadata.path_points = data.get("path_points", [])
        metadata.supported_formats = data.get("supported_formats", ["mp4", "mov", "avi", "mkv"])
        
        # 设置字典属性
        metadata.minimum_resolution = data.get("minimum_resolution", {"width": 240, "height": 240})
        metadata.maximum_resolution = data.get("maximum_resolution", {"width": 7680, "height": 4320})
        
        # 设置通用属性
        metadata.created_by = data.get("created_by", "system")
        metadata.updated_by = data.get("updated_by", "system")
        metadata.tags = data.get("tags", [])
        metadata.properties = data.get("properties", {})
        metadata.dependencies = data.get("dependencies", [])
        metadata.compatibility = data.get("compatibility", {})
        metadata.performance_metrics = data.get("performance_metrics", {})
        metadata.usage_statistics = data.get("usage_statistics", {
            "usage_count": 0,
            "last_used": None,
            "average_rating": 0.0,
            "user_ratings": []
        })
        
        return metadata

13.3 元数据管理器实现

13.3.1 元数据管理器核心类

元数据管理器是系统的核心组件,负责元数据的存储、检索、缓存和管理:

import threading
import sqlite3
import os
from pathlib import Path
from typing import List, Optional, Dict, Any, Set
from concurrent.futures import ThreadPoolExecutor
import logging

class MetadataManager:
    """元数据管理器"""
    
    def __init__(self, db_path: str = "metadata.db", cache_size: int = 1000):
        self.db_path = db_path
        self.cache_size = cache_size
        
        # 数据库连接
        self.db_connection: Optional[sqlite3.Connection] = None
        self.db_lock = threading.Lock()
        
        # 缓存机制
        self.metadata_cache: Dict[str, BaseMetadata] = {}
        self.cache_access_order: List[str] = []  # LRU缓存顺序
        self.cache_lock = threading.RLock()
        
        # 索引机制
        self.type_index: Dict[MetadataType, Set[str]] = {}
        self.tag_index: Dict[str, Set[str]] = {}
        self.name_index: Dict[str, Set[str]] = {}
        self.index_lock = threading.RLock()
        
        # 线程池
        self.executor = ThreadPoolExecutor(max_workers=4)
        
        # 统计信息
        self.stats = {
            "total_operations": 0,
            "cache_hits": 0,
            "cache_misses": 0,
            "db_queries": 0,
            "index_updates": 0,
            "avg_operation_time": 0.0
        }
        
        # 日志
        self.logger = logging.getLogger(__name__)
        
        # 初始化
        self._initialize_database()
        self._load_indexes()
    
    def _initialize_database(self) -> None:
        """初始化数据库"""
        try:
            with self.db_lock:
                self.db_connection = sqlite3.connect(self.db_path, check_same_thread=False)
                self.db_connection.row_factory = sqlite3.Row
                
                # 创建元数据表
                self.db_connection.execute("""
                    CREATE TABLE IF NOT EXISTS metadata (
                        metadata_id TEXT PRIMARY KEY,
                        metadata_type TEXT NOT NULL,
                        name TEXT NOT NULL,
                        description TEXT,
                        version TEXT DEFAULT '1.0.0',
                        status TEXT DEFAULT 'active',
                        created_at TEXT NOT NULL,
                        updated_at TEXT NOT NULL,
                        created_by TEXT DEFAULT 'system',
                        updated_by TEXT DEFAULT 'system',
                        tags TEXT,  -- JSON数组
                        properties TEXT,  -- JSON对象
                        dependencies TEXT,  -- JSON数组
                        compatibility TEXT,  -- JSON对象
                        performance_metrics TEXT,  -- JSON对象
                        usage_statistics TEXT,  -- JSON对象
                        data TEXT NOT NULL  -- 完整的元数据JSON
                    )
                """)
                
                # 创建索引
                self.db_connection.execute("CREATE INDEX IF NOT EXISTS idx_metadata_type ON metadata(metadata_type)")
                self.db_connection.execute("CREATE INDEX IF NOT EXISTS idx_metadata_name ON metadata(name)")
                self.db_connection.execute("CREATE INDEX IF NOT EXISTS idx_metadata_status ON metadata(status)")
                self.db_connection.execute("CREATE INDEX IF NOT EXISTS idx_metadata_created_at ON metadata(created_at)")
                
                self.db_connection.commit()
                self.logger.info("数据库初始化完成")
        
        except Exception as e:
            self.logger.error(f"数据库初始化失败: {e}")
            raise
    
    def _load_indexes(self) -> None:
        """加载索引"""
        try:
            with self.db_lock:
                cursor = self.db_connection.cursor()
                
                # 加载所有元数据ID
                cursor.execute("SELECT metadata_id, metadata_type, name, tags FROM metadata WHERE status = 'active'")
                
                for row in cursor.fetchall():
                    metadata_id = row["metadata_id"]
                    metadata_type = MetadataType(row["metadata_type"])
                    name = row["name"]
                    tags_json = row["tags"] or "[]"
                    
                    # 更新类型索引
                    if metadata_type not in self.type_index:
                        self.type_index[metadata_type] = set()
                    self.type_index[metadata_type].add(metadata_id)
                    
                    # 更新名称索引
                    if name not in self.name_index:
                        self.name_index[name] = set()
                    self.name_index[name].add(metadata_id)
                    
                    # 更新标签索引
                    try:
                        tags = json.loads(tags_json)
                        for tag in tags:
                            if tag not in self.tag_index:
                                self.tag_index[tag] = set()
                            self.tag_index[tag].add(metadata_id)
                    except json.JSONDecodeError:
                        pass
                
                self.stats["index_updates"] += 1
                self.logger.info(f"索引加载完成,共加载 {cursor.rowcount} 条元数据")
        
        except Exception as e:
            self.logger.error(f"索引加载失败: {e}")
    
    def add_metadata(self, metadata: BaseMetadata) -> bool:
        """添加元数据"""
        try:
            start_time = time.time()
            
            # 验证元数据
            if not metadata.validate():
                self.logger.warning(f"元数据验证失败: {metadata}")
                return False
            
            with self.db_lock:
                # 检查是否已存在
                cursor = self.db_connection.cursor()
                cursor.execute("SELECT COUNT(*) FROM metadata WHERE metadata_id = ?", (metadata.metadata_id,))
                if cursor.fetchone()[0] > 0:
                    self.logger.warning(f"元数据已存在: {metadata.metadata_id}")
                    return False
                
                # 插入元数据
                metadata_dict = metadata.to_dict()
                self.db_connection.execute("""
                    INSERT INTO metadata (
                        metadata_id, metadata_type, name, description, version, status,
                        created_at, updated_at, created_by, updated_by,
                        tags, properties, dependencies, compatibility,
                        performance_metrics, usage_statistics, data
                    ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                """, (
                    metadata.metadata_id,
                    metadata.metadata_type.value,
                    metadata.name,
                    metadata.description,
                    metadata.version,
                    metadata.status.value,
                    metadata.created_at.isoformat(),
                    metadata.updated_at.isoformat(),
                    metadata.created_by,
                    metadata.updated_by,
                    json.dumps(metadata.tags),
                    json.dumps(metadata.properties),
                    json.dumps(metadata.dependencies),
                    json.dumps(metadata.compatibility),
                    json.dumps(metadata.performance_metrics),
                    json.dumps(metadata.usage_statistics),
                    json.dumps(metadata_dict)
                ))
                
                self.db_connection.commit()
                self.stats["db_queries"] += 1
            
            # 更新索引
            with self.index_lock:
                self._update_indexes(metadata)
            
            # 添加到缓存
            with self.cache_lock:
                self._add_to_cache(metadata)
            
            # 更新统计
            self._update_stats("add", time.time() - start_time)
            
            self.logger.info(f"元数据添加成功: {metadata.metadata_id}")
            return True
        
        except Exception as e:
            self.logger.error(f"元数据添加失败: {e}")
            return False
    
    def get_metadata(self, metadata_id: str) -> Optional[BaseMetadata]:
        """获取元数据"""
        try:
            start_time = time.time()
            self.stats["total_operations"] += 1
            
            # 检查缓存
            with self.cache_lock:
                if metadata_id in self.metadata_cache:
                    self.stats["cache_hits"] += 1
                    self._update_cache_order(metadata_id)
                    self._update_stats("get", time.time() - start_time)
                    return self.metadata_cache[metadata_id]
            
            self.stats["cache_misses"] += 1
            
            # 从数据库获取
            with self.db_lock:
                cursor = self.db_connection.cursor()
                cursor.execute("SELECT data FROM metadata WHERE metadata_id = ?", (metadata_id,))
                row = cursor.fetchone()
                
                if not row:
                    self.logger.warning(f"元数据不存在: {metadata_id}")
                    return None
                
                metadata_data = json.loads(row["data"])
                self.stats["db_queries"] += 1
            
            # 根据类型创建具体的元数据对象
            metadata_type = MetadataType(metadata_data["metadata_type"])
            metadata = self._create_metadata_from_dict(metadata_type, metadata_data)
            
            if metadata:
                # 添加到缓存
                with self.cache_lock:
                    self._add_to_cache(metadata)
            
            self._update_stats("get", time.time() - start_time)
            return metadata
        
        except Exception as e:
            self.logger.error(f"元数据获取失败: {e}")
            return None
    
    def update_metadata(self, metadata: BaseMetadata) -> bool:
        """更新元数据"""
        try:
            start_time = time.time()
            
            # 验证元数据
            if not metadata.validate():
                self.logger.warning(f"元数据验证失败: {metadata}")
                return False
            
            with self.db_lock:
                # 检查是否存在
                cursor = self.db_connection.cursor()
                cursor.execute("SELECT COUNT(*) FROM metadata WHERE metadata_id = ?", (metadata.metadata_id,))
                if cursor.fetchone()[0] == 0:
                    self.logger.warning(f"元数据不存在: {metadata.metadata_id}")
                    return False
                
                # 更新元数据
                metadata.updated_at = datetime.now()
                metadata_dict = metadata.to_dict()
                
                self.db_connection.execute("""
                    UPDATE metadata SET
                        name = ?, description = ?, version = ?, status = ?,
                        updated_at = ?, updated_by = ?, tags = ?, properties = ?,
                        dependencies = ?, compatibility = ?, performance_metrics = ?,
                        usage_statistics = ?, data = ?
                    WHERE metadata_id = ?
                """, (
                    metadata.name,
                    metadata.description,
                    metadata.version,
                    metadata.status.value,
                    metadata.updated_at.isoformat(),
                    metadata.updated_by,
                    json.dumps(metadata.tags),
                    json.dumps(metadata.properties),
                    json.dumps(metadata.dependencies),
                    json.dumps(metadata.compatibility),
                    json.dumps(metadata.performance_metrics),
                    json.dumps(metadata.usage_statistics),
                    json.dumps(metadata_dict),
                    metadata.metadata_id
                ))
                
                self.db_connection.commit()
                self.stats["db_queries"] += 1
            
            # 更新索引
            with self.index_lock:
                self._remove_from_indexes(metadata.metadata_id)
                self._update_indexes(metadata)
            
            # 更新缓存
            with self.cache_lock:
                if metadata.metadata_id in self.metadata_cache:
                    self.metadata_cache[metadata.metadata_id] = metadata
            
            self._update_stats("update", time.time() - start_time)
            
            self.logger.info(f"元数据更新成功: {metadata.metadata_id}")
            return True
        
        except Exception as e:
            self.logger.error(f"元数据更新失败: {e}")
            return False
    
    def delete_metadata(self, metadata_id: str) -> bool:
        """删除元数据"""
        try:
            start_time = time.time()
            
            with self.db_lock:
                # 检查是否存在
                cursor = self.db_connection.cursor()
                cursor.execute("SELECT COUNT(*) FROM metadata WHERE metadata_id = ?", (metadata_id,))
                if cursor.fetchone()[0] == 0:
                    self.logger.warning(f"元数据不存在: {metadata_id}")
                    return False
                
                # 软删除(更新状态)
                self.db_connection.execute(
                    "UPDATE metadata SET status = 'inactive' WHERE metadata_id = ?",
                    (metadata_id,)
                )
                self.db_connection.commit()
                self.stats["db_queries"] += 1
            
            # 从索引中移除
            with self.index_lock:
                self._remove_from_indexes(metadata_id)
            
            # 从缓存中移除
            with self.cache_lock:
                if metadata_id in self.metadata_cache:
                    del self.metadata_cache[metadata_id]
                    self.cache_access_order.remove(metadata_id)
            
            self._update_stats("delete", time.time() - start_time)
            
            self.logger.info(f"元数据删除成功: {metadata_id}")
            return True
        
        except Exception as e:
            self.logger.error(f"元数据删除失败: {e}")
            return False
    
    def search_metadata(self, query: str, metadata_type: Optional[MetadataType] = None,
                       tags: Optional[List[str]] = None, limit: int = 50) -> List[BaseMetadata]:
        """搜索元数据"""
        try:
            start_time = time.time()
            
            # 构建搜索条件
            conditions = []
            params = []
            
            # 文本搜索条件
            if query:
                conditions.append("(name LIKE ? OR description LIKE ?)")
                params.extend([f"%{query}%", f"%{query}%"])
            
            # 类型条件
            if metadata_type:
                conditions.append("metadata_type = ?")
                params.append(metadata_type.value)
            
            # 标签条件
            if tags:
                tag_conditions = []
                for tag in tags:
                    tag_conditions.append("tags LIKE ?")
                    params.append(f"%{tag}%")
                conditions.append(f"({' OR '.join(tag_conditions)})")
            
            # 状态条件
            conditions.append("status = 'active'")
            
            # 构建SQL查询
            where_clause = " AND ".join(conditions) if conditions else "1=1"
            sql = f"SELECT data FROM metadata WHERE {where_clause} ORDER BY name LIMIT ?"
            params.append(limit)
            
            # 执行查询
            with self.db_lock:
                cursor = self.db_connection.cursor()
                cursor.execute(sql, params)
                results = []
                
                for row in cursor.fetchall():
                    metadata_data = json.loads(row["data"])
                    metadata_type = MetadataType(metadata_data["metadata_type"])
                    metadata = self._create_metadata_from_dict(metadata_type, metadata_data)
                    if metadata:
                        results.append(metadata)
                
                self.stats["db_queries"] += 1
            
            self._update_stats("search", time.time() - start_time)
            return results
        
        except Exception as e:
            self.logger.error(f"元数据搜索失败: {e}")
            return []
    
    def get_metadata_by_type(self, metadata_type: MetadataType, limit: int = 100) -> List[BaseMetadata]:
        """按类型获取元数据"""
        try:
            # 检查索引
            with self.index_lock:
                if metadata_type in self.type_index:
                    metadata_ids = list(self.type_index[metadata_type])[:limit]
                else:
                    metadata_ids = []
            
            # 获取元数据
            results = []
            for metadata_id in metadata_ids:
                metadata = self.get_metadata(metadata_id)
                if metadata:
                    results.append(metadata)
            
            return results
        
        except Exception as e:
            self.logger.error(f"按类型获取元数据失败: {e}")
            return []
    
    def get_metadata_by_tag(self, tag: str, limit: int = 50) -> List[BaseMetadata]:
        """按标签获取元数据"""
        try:
            # 检查索引
            with self.index_lock:
                if tag in self.tag_index:
                    metadata_ids = list(self.tag_index[tag])[:limit]
                else:
                    metadata_ids = []
            
            # 获取元数据
            results = []
            for metadata_id in metadata_ids:
                metadata = self.get_metadata(metadata_id)
                if metadata:
                    results.append(metadata)
            
            return results
        
        except Exception as e:
            self.logger.error(f"按标签获取元数据失败: {e}")
            return []
    
    def _add_to_cache(self, metadata: BaseMetadata) -> None:
        """添加到缓存"""
        # 检查缓存大小
        if len(self.metadata_cache) >= self.cache_size:
            # 移除最久未使用的项
            oldest_id = self.cache_access_order.pop(0)
            if oldest_id in self.metadata_cache:
                del self.metadata_cache[oldest_id]
        
        # 添加新项
        self.metadata_cache[metadata.metadata_id] = metadata
        self.cache_access_order.append(metadata.metadata_id)
    
    def _update_cache_order(self, metadata_id: str) -> None:
        """更新缓存访问顺序"""
        if metadata_id in self.cache_access_order:
            self.cache_access_order.remove(metadata_id)
        self.cache_access_order.append(metadata_id)
    
    def _update_indexes(self, metadata: BaseMetadata) -> None:
        """更新索引"""
        metadata_id = metadata.metadata_id
        
        # 更新类型索引
        if metadata.metadata_type not in self.type_index:
            self.type_index[metadata.metadata_type] = set()
        self.type_index[metadata.metadata_type].add(metadata_id)
        
        # 更新名称索引
        if metadata.name not in self.name_index:
            self.name_index[metadata.name] = set()
        self.name_index[metadata.name].add(metadata_id)
        
        # 更新标签索引
        for tag in metadata.tags:
            if tag not in self.tag_index:
                self.tag_index[tag] = set()
            self.tag_index[tag].add(metadata_id)
        
        self.stats["index_updates"] += 1
    
    def _remove_from_indexes(self, metadata_id: str) -> None:
        """从索引中移除"""
        # 从类型索引中移除
        for metadata_type, id_set in self.type_index.items():
            if metadata_id in id_set:
                id_set.remove(metadata_id)
                if not id_set:
                    del self.type_index[metadata_type]
                break
        
        # 从名称索引中移除
        for name, id_set in self.name_index.items():
            if metadata_id in id_set:
                id_set.remove(metadata_id)
                if not id_set:
                    del self.name_index[name]
                break
        
        # 从标签索引中移除
        tags_to_remove = []
        for tag, id_set in self.tag_index.items():
            if metadata_id in id_set:
                id_set.remove(metadata_id)
                if not id_set:
                    tags_to_remove.append(tag)
        
        for tag in tags_to_remove:
            del self.tag_index[tag]
    
    def _create_metadata_from_dict(self, metadata_type: MetadataType, 
                                 data: Dict[str, Any]) -> Optional[BaseMetadata]:
        """从字典创建具体的元数据对象"""
        try:
            if metadata_type == MetadataType.FONT:
                return FontMetadata.from_dict(data)
            elif metadata_type == MetadataType.MASK:
                return MaskMetadata.from_dict(data)
            # 可以添加更多类型的支持
            else:
                # 对于未实现的类型,返回基础的元数据
                base_metadata = BaseMetadata(metadata_type)
                base_metadata.metadata_id = data.get("metadata_id", "")
                base_metadata.name = data.get("name", "")
                base_metadata.description = data.get("description", "")
                return base_metadata
        
        except Exception as e:
            self.logger.error(f"创建元数据对象失败: {e}")
            return None
    
    def _update_stats(self, operation: str, duration: float) -> None:
        """更新统计信息"""
        self.stats["total_operations"] += 1
        
        # 更新平均操作时间
        current_avg = self.stats["avg_operation_time"]
        total_ops = self.stats["total_operations"]
        self.stats["avg_operation_time"] = (current_avg * (total_ops - 1) + duration) / total_ops
    
    def get_stats(self) -> Dict[str, Any]:
        """获取统计信息"""
        cache_stats = {
            "cache_size": len(self.metadata_cache),
            "cache_usage": len(self.metadata_cache) / self.cache_size,
            "cache_hit_rate": self.stats["cache_hits"] / max(1, self.stats["cache_hits"] + self.stats["cache_misses"])
        }
        
        return {
            "stats": self.stats.copy(),
            "cache_stats": cache_stats,
            "index_stats": {
                "type_index_size": len(self.type_index),
                "tag_index_size": len(self.tag_index),
                "name_index_size": len(self.name_index)
            }
        }
    
    def clear_cache(self) -> None:
        """清空缓存"""
        with self.cache_lock:
            self.metadata_cache.clear()
            self.cache_access_order.clear()
        
        self.logger.info("缓存已清空")
    
    def rebuild_indexes(self) -> bool:
        """重建索引"""
        try:
            with self.index_lock:
                self.type_index.clear()
                self.tag_index.clear()
                self.name_index.clear()
            
            self._load_indexes()
            self.logger.info("索引重建完成")
            return True
        
        except Exception as e:
            self.logger.error(f"索引重建失败: {e}")
            return False
    
    def close(self) -> None:
        """关闭管理器"""
        try:
            # 关闭线程池
            self.executor.shutdown(wait=True)
            
            # 关闭数据库连接
            if self.db_connection:
                with self.db_lock:
                    self.db_connection.close()
            
            self.logger.info("元数据管理器已关闭")
        
        except Exception as e:
            self.logger.error(f"关闭元数据管理器失败: {e}")
    
    def __enter__(self):
        """上下文管理器进入"""
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        """上下文管理器退出"""
        self.close()

13.4 元数据系统总结

元数据管理系统是剪映小助手中的核心基础设施,它为整个应用提供了统一的数据管理和配置管理机制。通过本章的实现,我们构建了一个功能完整、性能优异的元数据管理系统。

13.4.1 核心功能回顾

元数据模型设计

  • 提供了统一的元数据基类,定义了所有元数据类型的基本接口
  • 实现了具体的元数据类型,如字体元数据和遮罩元数据
  • 支持元数据的验证、配置和应用功能
  • 提供了丰富的元数据属性,包括兼容性、性能指标和使用统计

元数据管理功能

  • 实现了完整的CRUD操作,支持元数据的增删改查
  • 提供了强大的搜索和过滤功能,支持按类型、标签、名称等条件搜索
  • 实现了高效的缓存机制,使用LRU算法管理缓存
  • 提供了完善的索引系统,支持快速的数据检索

性能和可靠性

  • 使用SQLite数据库存储元数据,确保数据的持久性和一致性
  • 实现了多线程安全的操作,支持并发访问
  • 提供了详细的统计信息和监控功能
  • 支持缓存和索引的重建,确保系统的可靠性

13.4.2 技术特点

分层架构设计

  • 采用分层架构,将数据访问、业务逻辑和API接口分离
  • 提供了清晰的抽象接口,支持不同类型的元数据扩展
  • 实现了模块化的设计,各个组件职责明确
  • 支持插件式的架构,便于功能的扩展和维护

高性能优化

  • 使用缓存机制减少数据库访问,提高查询性能
  • 实现了多层次的索引系统,支持快速的数据检索
  • 使用线程池处理并发操作,提高系统的吞吐量
  • 提供了智能的缓存管理,自动处理缓存的淘汰和更新

数据一致性

  • 使用事务确保数据库操作的一致性
  • 实现了缓存和数据库的同步机制
  • 提供了数据验证和错误处理机制
  • 支持数据的备份和恢复功能

13.4.3 应用场景

元数据管理系统在剪映小助手中有着广泛的应用:

字体管理:管理各种字体的元数据信息,支持字体的预览、选择和应用
遮罩效果:管理各种遮罩效果的配置参数,支持遮罩的创建和编辑
特效管理:管理视频特效的元数据,支持特效的分类和搜索
模板管理:管理视频模板的元数据,支持模板的推荐和使用
资源配置:管理各种媒体资源的配置信息,支持资源的优化和调度

这个元数据管理系统为剪映小助手提供了强大的数据管理能力,使得应用能够高效地处理各种类型的配置信息。通过统一的接口和灵活的架构,系统能够适应不同的应用场景和业务需求,为用户提供流畅的视频编辑体验。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值