mirror of
https://github.com/jxxghp/MoviePilot-Plugins.git
synced 2026-05-25 07:26:52 +00:00
- 数据模型重构: 全面引入 Pydantic 模型(ClashConfig, Proxy, ProxyGroup 等)替代原有字典结构,提供更严格的数据验证与类型安全。 - 数据迁移机制: 新增 v2.1.0 数据升级脚本,支持将旧版代理、策略组及规则数据自动迁移至新架构。 - 配置补丁系统: 实现基于 JSON Patch 的细粒度配置修补机制,替代旧版覆盖逻辑,提升配置修改的灵活性。 - 服务层优化: 重写 ClashRuleProviderService 以适配新对象模型,增强代码可维护性与扩展性。 - API模型同步: 更新相关 API 数据模型以保持与内部数据结构的一致性。 - 用户界面: 批量规则管理和数据项隐藏支持
94 lines
3.1 KiB
Python
94 lines
3.1 KiB
Python
from typing import TypeVar, Generic, Iterator, Any
|
|
from pydantic import BaseModel, RootModel, Field, model_validator
|
|
from .metadata import Metadata
|
|
|
|
|
|
# Specific data payload model
|
|
T = TypeVar("T")
|
|
|
|
class ResourceItem(BaseModel, Generic[T]):
|
|
"""Generic resource item model"""
|
|
name: str = Field(..., description="Resource name")
|
|
data: T = Field(..., description="Resource data payload")
|
|
meta: Metadata = Field(default_factory=Metadata, description="Resource metadata")
|
|
|
|
|
|
# Subclasses of ResourceItem
|
|
R = TypeVar("R", bound=ResourceItem)
|
|
|
|
class ResourceList(RootModel[list[R]], Generic[R]):
|
|
"""
|
|
Generic configuration list base class
|
|
"""
|
|
root: list[R] = Field(default_factory=list)
|
|
|
|
@model_validator(mode='after')
|
|
def validate_unique_names(self) -> 'ResourceList[R]':
|
|
names = [item.name for item in self.root]
|
|
if len(names) != len(set(names)):
|
|
raise ValueError("names must be unique")
|
|
return self
|
|
|
|
def __iter__(self) -> Iterator[R]:
|
|
return iter(self.root)
|
|
|
|
def __len__(self) -> int:
|
|
return len(self.root)
|
|
|
|
def __contains__(self, name: str) -> bool:
|
|
"""Check if a configuration with the specified name exists"""
|
|
return any(item.name == name for item in self.root)
|
|
|
|
def get(self, name: str) -> R | None:
|
|
"""Get the configuration item by name"""
|
|
for item in self.root:
|
|
if item.name == name:
|
|
return item
|
|
return None
|
|
|
|
def add(self, item: R):
|
|
"""Add a configuration item, raise an exception if the name is duplicated"""
|
|
if item.name in self:
|
|
raise ValueError(f"name {item.name!r} already exists")
|
|
self.root.insert(0, item)
|
|
|
|
def remove(self, name: str):
|
|
"""Remove the configuration item by name"""
|
|
self.root = [item for item in self.root if item.name != name]
|
|
|
|
def pop(self, name: str) -> R | None:
|
|
"""Remove and return the configuration item with the specified name"""
|
|
for i, item in enumerate(self.root) :
|
|
if item.name == name:
|
|
return self.root.pop(i)
|
|
return None
|
|
|
|
def update(self, name: str, item: R):
|
|
"""Update the configuration item with the specified name"""
|
|
for i, existing_item in enumerate(self.root):
|
|
if existing_item.name == name:
|
|
item.meta = self.root[i].meta
|
|
self.root[i] = item
|
|
return
|
|
|
|
def update_data(self, name: str, data: Any) -> bool:
|
|
"""Update only the data payload of the configuration item with the specified name"""
|
|
item = self.get(name)
|
|
if item:
|
|
item.data = data
|
|
return True
|
|
return False
|
|
|
|
def set_meta(self, name: str, meta: Metadata) -> bool:
|
|
"""Set metadata for the specified configuration item"""
|
|
item = self.get(name)
|
|
if item:
|
|
item.meta = meta
|
|
return True
|
|
return False
|
|
|
|
@property
|
|
def names(self) -> list[str]:
|
|
"""Return a list of names for all configuration items"""
|
|
return [item.name for item in self.root]
|