mirror of
https://github.com/TriM-Organization/Musicreater.git
synced 2026-04-29 04:35:50 +00:00
完成插件系统的基本设计,接着需要优化一些内容,标记了 TODO
This commit is contained in:
@@ -93,24 +93,76 @@ from .data import SingleMusic, SingleTrack
|
|||||||
# ]
|
# ]
|
||||||
|
|
||||||
|
|
||||||
|
# ========================
|
||||||
|
# 枚举类
|
||||||
|
# ========================
|
||||||
|
|
||||||
|
class PluginTypes(str, Enum):
|
||||||
|
"""插件类型枚举"""
|
||||||
|
|
||||||
|
FUNCTION_MUSIC_IMPORT = "import_music_data"
|
||||||
|
FUNCTION_TRACK_IMPORT = "import_track_data"
|
||||||
|
FUNCTION_MUSIC_OPERATE = "music_data_operating"
|
||||||
|
FUNCTION_TRACK_OPERATE = "track_data_operating"
|
||||||
|
FUNCTION_MUSIC_EXPORT = "export_music_data"
|
||||||
|
FUNCTION_TRACK_EXPORT = "export_track_data"
|
||||||
|
SERVICE = "service"
|
||||||
|
LIBRARY = "library"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ========================
|
||||||
|
# 数据类
|
||||||
|
# ========================
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class PluginConfig(ABC):
|
class PluginConfig(ABC):
|
||||||
"""插件配置基类"""
|
"""插件配置基类"""
|
||||||
|
|
||||||
def to_dict(self) -> Dict[str, Any]:
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
"""字典化配置文件"""
|
"""将配置内容转换为字典
|
||||||
|
|
||||||
|
返回
|
||||||
|
====
|
||||||
|
Dict[str, Any]
|
||||||
|
配置项的字典表示,不包含以下划线开头的私有属性
|
||||||
|
"""
|
||||||
return {k: v for k, v in self.__dict__.items() if not k.startswith("_")}
|
return {k: v for k, v in self.__dict__.items() if not k.startswith("_")}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, data: Dict[str, Any]) -> "PluginConfig":
|
def from_dict(cls, data: Dict[str, Any]) -> "PluginConfig":
|
||||||
"""从字典创建配置实例"""
|
"""从字典创建配置实例
|
||||||
|
|
||||||
|
参数
|
||||||
|
====
|
||||||
|
data: Dict[str, Any]
|
||||||
|
包含配置字段的字典
|
||||||
|
|
||||||
|
返回
|
||||||
|
====
|
||||||
|
PluginConfig
|
||||||
|
配置类的实例
|
||||||
|
"""
|
||||||
|
|
||||||
# 只保留类中定义的字段
|
# 只保留类中定义的字段
|
||||||
field_names = {f.name for f in cls.__dataclass_fields__.values()}
|
field_names = {f.name for f in cls.__dataclass_fields__.values()}
|
||||||
filtered_data = {k: v for k, v in data.items() if k in field_names}
|
filtered_data = {k: v for k, v in data.items() if k in field_names}
|
||||||
return cls(**filtered_data)
|
return cls(**filtered_data)
|
||||||
|
|
||||||
def save_to_file(self, file_path: Path) -> None:
|
def save_to_file(self, file_path: Path) -> None:
|
||||||
"""保存配置到文件"""
|
"""保存配置到 TOML 文件
|
||||||
|
|
||||||
|
参数
|
||||||
|
====
|
||||||
|
file_path: Path
|
||||||
|
目标文件路径;必须以 .toml 为后缀
|
||||||
|
|
||||||
|
异常
|
||||||
|
====
|
||||||
|
PluginConfigDumpError
|
||||||
|
当文件后缀不是 .toml 或写入失败时抛出
|
||||||
|
"""
|
||||||
if file_path.suffix.upper() == ".TOML":
|
if file_path.suffix.upper() == ".TOML":
|
||||||
file_path.parent.mkdir(parents=True, exist_ok=True)
|
file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
else:
|
else:
|
||||||
@@ -126,7 +178,23 @@ class PluginConfig(ABC):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load_from_file(cls, file_path: Path) -> "PluginConfig":
|
def load_from_file(cls, file_path: Path) -> "PluginConfig":
|
||||||
"""从文件加载配置"""
|
"""从 TOML 文件加载配置
|
||||||
|
|
||||||
|
参数
|
||||||
|
====
|
||||||
|
file_path: Path
|
||||||
|
源文件路径
|
||||||
|
|
||||||
|
返回
|
||||||
|
====
|
||||||
|
PluginConfig
|
||||||
|
加载后的配置实例
|
||||||
|
|
||||||
|
异常
|
||||||
|
====
|
||||||
|
PluginConfigLoadError
|
||||||
|
当读取或解析失败时抛出
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
with file_path.open("rb") as f:
|
with file_path.open("rb") as f:
|
||||||
return cls.from_dict(tomllib.load(f))
|
return cls.from_dict(tomllib.load(f))
|
||||||
@@ -134,19 +202,6 @@ class PluginConfig(ABC):
|
|||||||
raise PluginConfigLoadError(e)
|
raise PluginConfigLoadError(e)
|
||||||
|
|
||||||
|
|
||||||
class PluginType(str, Enum):
|
|
||||||
"""插件类型枚举"""
|
|
||||||
|
|
||||||
FUNCTION_MUSIC_IMPORT = "import_music_data"
|
|
||||||
FUNCTION_TRACK_IMPORT = "import_track_data"
|
|
||||||
FUNCTION_MUSIC_OPERATE = "music_data_operating"
|
|
||||||
FUNCTION_TRACK_OPERATE = "track_data_operating"
|
|
||||||
FUNCTION_MUSIC_EXPORT = "export_music_data"
|
|
||||||
FUNCTION_TRACK_EXPORT = "export_track_data"
|
|
||||||
SERVICE = "service"
|
|
||||||
LIBRARY = "library"
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class PluginMetaInformation(ABC):
|
class PluginMetaInformation(ABC):
|
||||||
"""插件元信息"""
|
"""插件元信息"""
|
||||||
@@ -159,7 +214,7 @@ class PluginMetaInformation(ABC):
|
|||||||
"""插件简介"""
|
"""插件简介"""
|
||||||
version: Tuple[int, ...]
|
version: Tuple[int, ...]
|
||||||
"""插件版本号"""
|
"""插件版本号"""
|
||||||
type: PluginType
|
type: PluginTypes
|
||||||
"""插件类型"""
|
"""插件类型"""
|
||||||
license: str = "MIT License"
|
license: str = "MIT License"
|
||||||
"""插件发布时采用的许可协议"""
|
"""插件发布时采用的许可协议"""
|
||||||
@@ -167,6 +222,11 @@ class PluginMetaInformation(ABC):
|
|||||||
"""插件是否对其他插件存在依赖"""
|
"""插件是否对其他插件存在依赖"""
|
||||||
|
|
||||||
|
|
||||||
|
# ========================
|
||||||
|
# 抽象基类
|
||||||
|
# ========================
|
||||||
|
|
||||||
|
|
||||||
class TopPluginBase(ABC):
|
class TopPluginBase(ABC):
|
||||||
"""所有插件的抽象基类"""
|
"""所有插件的抽象基类"""
|
||||||
|
|
||||||
@@ -195,7 +255,7 @@ class TopInOutPluginBase(TopPluginBase, ABC):
|
|||||||
"""导入导出用抽象基类"""
|
"""导入导出用抽象基类"""
|
||||||
|
|
||||||
supported_formats: Tuple[str, ...] = tuple()
|
supported_formats: Tuple[str, ...] = tuple()
|
||||||
"""支持的格式"""
|
"""支持的格式(定义后会自动转大写)"""
|
||||||
|
|
||||||
def __init_subclass__(cls) -> None:
|
def __init_subclass__(cls) -> None:
|
||||||
super().__init_subclass__()
|
super().__init_subclass__()
|
||||||
@@ -214,11 +274,33 @@ class TopInOutPluginBase(TopPluginBase, ABC):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def can_handle_file(self, file_path: Path) -> bool:
|
def can_handle_file(self, file_path: Path) -> bool:
|
||||||
"""判断是否可处理某个文件"""
|
"""判断是否可处理某个文件
|
||||||
|
|
||||||
|
参数
|
||||||
|
====
|
||||||
|
file_path: Path
|
||||||
|
待检测的文件路径
|
||||||
|
|
||||||
|
返回
|
||||||
|
====
|
||||||
|
bool
|
||||||
|
若文件后缀已在本类中定义,则返回 True
|
||||||
|
"""
|
||||||
return file_path.suffix.upper().endswith(self.supported_formats)
|
return file_path.suffix.upper().endswith(self.supported_formats)
|
||||||
|
|
||||||
def can_handle_format(self, format_name: str) -> bool:
|
def can_handle_format(self, format_name: str) -> bool:
|
||||||
"""判断是否可处理某个格式"""
|
"""判断是否可处理某个格式
|
||||||
|
|
||||||
|
参数
|
||||||
|
====
|
||||||
|
format_name: str
|
||||||
|
格式名称(如 'MIDI', 'WAV')
|
||||||
|
|
||||||
|
返回
|
||||||
|
====
|
||||||
|
bool
|
||||||
|
若格式名本类中已经定义,则返回 True
|
||||||
|
"""
|
||||||
return format_name.upper().endswith(self.supported_formats)
|
return format_name.upper().endswith(self.supported_formats)
|
||||||
|
|
||||||
|
|
||||||
@@ -228,7 +310,7 @@ class MusicInputPluginBase(TopInOutPluginBase, ABC):
|
|||||||
def __init_subclass__(cls) -> None:
|
def __init_subclass__(cls) -> None:
|
||||||
super().__init_subclass__()
|
super().__init_subclass__()
|
||||||
|
|
||||||
if cls.metainfo.type != PluginType.FUNCTION_MUSIC_IMPORT:
|
if cls.metainfo.type != PluginTypes.FUNCTION_MUSIC_IMPORT:
|
||||||
raise PluginMetainfoValueError(
|
raise PluginMetainfoValueError(
|
||||||
"插件类`{cls_name}`是从`MusicInputPlugin`继承的,该类的子类应当为一个`PluginType.FUNCTION_MUSIC_IMPORT`类型的插件,而不是`PluginType.{cls_type}`".format(
|
"插件类`{cls_name}`是从`MusicInputPlugin`继承的,该类的子类应当为一个`PluginType.FUNCTION_MUSIC_IMPORT`类型的插件,而不是`PluginType.{cls_type}`".format(
|
||||||
cls_name=cls.__name__,
|
cls_name=cls.__name__,
|
||||||
@@ -240,11 +322,38 @@ class MusicInputPluginBase(TopInOutPluginBase, ABC):
|
|||||||
def loadbytes(
|
def loadbytes(
|
||||||
self, bytes_buffer_in: BinaryIO, config: Optional[PluginConfig]
|
self, bytes_buffer_in: BinaryIO, config: Optional[PluginConfig]
|
||||||
) -> "SingleMusic":
|
) -> "SingleMusic":
|
||||||
"""从字节流加载数据到完整曲目"""
|
"""从字节流加载数据到完整曲目
|
||||||
|
|
||||||
|
参数
|
||||||
|
====
|
||||||
|
bytes_buffer_in: BinaryIO
|
||||||
|
输入的二进制字节流
|
||||||
|
config: Optional[PluginConfig]
|
||||||
|
插件配置;**可选**
|
||||||
|
|
||||||
|
返回
|
||||||
|
====
|
||||||
|
SingleMusic
|
||||||
|
解析得到的完整曲目对象
|
||||||
|
"""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def load(self, file_path: Path, config: Optional[PluginConfig]) -> "SingleMusic":
|
def load(self, file_path: Path, config: Optional[PluginConfig]) -> "SingleMusic":
|
||||||
"""从文件加载数据到完整曲目"""
|
"""从文件加载数据到完整曲目
|
||||||
|
|
||||||
|
参数
|
||||||
|
====
|
||||||
|
file_path: Path
|
||||||
|
输入文件路径
|
||||||
|
config: Optional[PluginConfig]
|
||||||
|
插件配置;**可选**
|
||||||
|
|
||||||
|
返回
|
||||||
|
====
|
||||||
|
SingleMusic
|
||||||
|
解析得到的完整曲目对象
|
||||||
|
"""
|
||||||
with file_path.open("rb") as f:
|
with file_path.open("rb") as f:
|
||||||
return self.loadbytes(f, config)
|
return self.loadbytes(f, config)
|
||||||
|
|
||||||
@@ -255,7 +364,7 @@ class TrackInputPluginBase(TopInOutPluginBase, ABC):
|
|||||||
def __init_subclass__(cls) -> None:
|
def __init_subclass__(cls) -> None:
|
||||||
super().__init_subclass__()
|
super().__init_subclass__()
|
||||||
|
|
||||||
if cls.metainfo.type != PluginType.FUNCTION_TRACK_IMPORT:
|
if cls.metainfo.type != PluginTypes.FUNCTION_TRACK_IMPORT:
|
||||||
raise PluginMetainfoValueError(
|
raise PluginMetainfoValueError(
|
||||||
"插件类`{cls_name}`是从`TrackInputPlugin`继承的,该类的子类应当为一个`PluginType.FUNCTION_TRACK_IMPORT`类型的插件,而不是`PluginType.{cls_type}`".format(
|
"插件类`{cls_name}`是从`TrackInputPlugin`继承的,该类的子类应当为一个`PluginType.FUNCTION_TRACK_IMPORT`类型的插件,而不是`PluginType.{cls_type}`".format(
|
||||||
cls_name=cls.__name__,
|
cls_name=cls.__name__,
|
||||||
@@ -267,11 +376,37 @@ class TrackInputPluginBase(TopInOutPluginBase, ABC):
|
|||||||
def loadbytes(
|
def loadbytes(
|
||||||
self, bytes_buffer_in: BinaryIO, config: Optional[PluginConfig]
|
self, bytes_buffer_in: BinaryIO, config: Optional[PluginConfig]
|
||||||
) -> "SingleTrack":
|
) -> "SingleTrack":
|
||||||
"""从字节流加载音符数据到单个音轨"""
|
"""从字节流加载音符数据到单个音轨
|
||||||
|
|
||||||
|
参数
|
||||||
|
====
|
||||||
|
bytes_buffer_in: BinaryIO
|
||||||
|
输入的二进制字节流
|
||||||
|
config: Optional[PluginConfig]
|
||||||
|
插件配置;**可选**
|
||||||
|
|
||||||
|
返回
|
||||||
|
====
|
||||||
|
SingleTrack
|
||||||
|
解析得到的单个音轨对象
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def load(self, file_path: Path, config: Optional[PluginConfig]) -> "SingleTrack":
|
def load(self, file_path: Path, config: Optional[PluginConfig]) -> "SingleTrack":
|
||||||
"""从文件加载音符数据到单个音轨"""
|
"""从文件加载音符数据到单个音轨
|
||||||
|
|
||||||
|
参数
|
||||||
|
====
|
||||||
|
file_path: Path
|
||||||
|
输入文件路径
|
||||||
|
config: Optional[PluginConfig]
|
||||||
|
插件配置;**可选**
|
||||||
|
|
||||||
|
返回
|
||||||
|
====
|
||||||
|
SingleTrack
|
||||||
|
解析得到的单个音轨对象
|
||||||
|
"""
|
||||||
with file_path.open("rb") as f:
|
with file_path.open("rb") as f:
|
||||||
return self.loadbytes(f, config)
|
return self.loadbytes(f, config)
|
||||||
|
|
||||||
@@ -282,7 +417,7 @@ class MusicOperatePluginBase(TopPluginBase, ABC):
|
|||||||
def __init_subclass__(cls) -> None:
|
def __init_subclass__(cls) -> None:
|
||||||
super().__init_subclass__()
|
super().__init_subclass__()
|
||||||
|
|
||||||
if cls.metainfo.type != PluginType.FUNCTION_MUSIC_OPERATE:
|
if cls.metainfo.type != PluginTypes.FUNCTION_MUSIC_OPERATE:
|
||||||
raise PluginMetainfoValueError(
|
raise PluginMetainfoValueError(
|
||||||
"插件类`{cls_name}`是从`MusicOperatePlugin`继承的,该类的子类应当为一个`PluginType.FUNCTION_MUSIC_OPERATE`类型的插件,而不是`PluginType.{cls_type}`".format(
|
"插件类`{cls_name}`是从`MusicOperatePlugin`继承的,该类的子类应当为一个`PluginType.FUNCTION_MUSIC_OPERATE`类型的插件,而不是`PluginType.{cls_type}`".format(
|
||||||
cls_name=cls.__name__,
|
cls_name=cls.__name__,
|
||||||
@@ -294,7 +429,20 @@ class MusicOperatePluginBase(TopPluginBase, ABC):
|
|||||||
def process(
|
def process(
|
||||||
self, data: "SingleMusic", config: Optional[PluginConfig]
|
self, data: "SingleMusic", config: Optional[PluginConfig]
|
||||||
) -> "SingleMusic":
|
) -> "SingleMusic":
|
||||||
"""处理完整曲目的数据"""
|
"""处理完整曲目的数据
|
||||||
|
|
||||||
|
参数
|
||||||
|
====
|
||||||
|
data: SingleMusic
|
||||||
|
待处理的完整曲目
|
||||||
|
config: Optional[PluginConfig]
|
||||||
|
插件配置;**可选**
|
||||||
|
|
||||||
|
返回
|
||||||
|
====
|
||||||
|
SingleMusic
|
||||||
|
处理后的完整曲目
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@@ -304,7 +452,7 @@ class TrackOperatePluginBase(TopPluginBase, ABC):
|
|||||||
def __init_subclass__(cls) -> None:
|
def __init_subclass__(cls) -> None:
|
||||||
super().__init_subclass__()
|
super().__init_subclass__()
|
||||||
|
|
||||||
if cls.metainfo.type != PluginType.FUNCTION_TRACK_OPERATE:
|
if cls.metainfo.type != PluginTypes.FUNCTION_TRACK_OPERATE:
|
||||||
raise PluginMetainfoValueError(
|
raise PluginMetainfoValueError(
|
||||||
"插件类`{cls_name}`是从`TrackOperatePlugin`继承的,该类的子类应当为一个`PluginType.FUNCTION_TRACK_OPERATE`类型的插件,而不是`PluginType.{cls_type}`".format(
|
"插件类`{cls_name}`是从`TrackOperatePlugin`继承的,该类的子类应当为一个`PluginType.FUNCTION_TRACK_OPERATE`类型的插件,而不是`PluginType.{cls_type}`".format(
|
||||||
cls_name=cls.__name__,
|
cls_name=cls.__name__,
|
||||||
@@ -316,7 +464,20 @@ class TrackOperatePluginBase(TopPluginBase, ABC):
|
|||||||
def process(
|
def process(
|
||||||
self, data: "SingleTrack", config: Optional[PluginConfig]
|
self, data: "SingleTrack", config: Optional[PluginConfig]
|
||||||
) -> "SingleTrack":
|
) -> "SingleTrack":
|
||||||
"""处理单个音轨的音符数据"""
|
"""处理单个音轨的音符数据
|
||||||
|
|
||||||
|
参数
|
||||||
|
====
|
||||||
|
data: SingleTrack
|
||||||
|
待处理的单个音轨
|
||||||
|
config: Optional[PluginConfig]
|
||||||
|
插件配置;**可选**
|
||||||
|
|
||||||
|
返回
|
||||||
|
====
|
||||||
|
SingleTrack
|
||||||
|
处理后的单个音轨
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@@ -326,7 +487,7 @@ class MusicOutputPluginBase(TopInOutPluginBase, ABC):
|
|||||||
def __init_subclass__(cls) -> None:
|
def __init_subclass__(cls) -> None:
|
||||||
super().__init_subclass__()
|
super().__init_subclass__()
|
||||||
|
|
||||||
if cls.metainfo.type != PluginType.FUNCTION_MUSIC_EXPORT:
|
if cls.metainfo.type != PluginTypes.FUNCTION_MUSIC_EXPORT:
|
||||||
raise PluginMetainfoValueError(
|
raise PluginMetainfoValueError(
|
||||||
"插件类`{cls_name}`是从`MusicOutputPlugin`继承的,该类的子类应当为一个`PluginType.FUNCTION_MUSIC_EXPORT`类型的插件,而不是`PluginType.{cls_type}`".format(
|
"插件类`{cls_name}`是从`MusicOutputPlugin`继承的,该类的子类应当为一个`PluginType.FUNCTION_MUSIC_EXPORT`类型的插件,而不是`PluginType.{cls_type}`".format(
|
||||||
cls_name=cls.__name__,
|
cls_name=cls.__name__,
|
||||||
@@ -338,14 +499,38 @@ class MusicOutputPluginBase(TopInOutPluginBase, ABC):
|
|||||||
def dumpbytes(
|
def dumpbytes(
|
||||||
self, data: "SingleMusic", config: Optional[PluginConfig]
|
self, data: "SingleMusic", config: Optional[PluginConfig]
|
||||||
) -> BinaryIO:
|
) -> BinaryIO:
|
||||||
"""将完整曲目导出为对应格式的字节流"""
|
"""将完整曲目导出为对应格式的字节流
|
||||||
|
|
||||||
|
参数
|
||||||
|
====
|
||||||
|
data: SingleMusic
|
||||||
|
待导出的完整曲目
|
||||||
|
config: Optional[PluginConfig]
|
||||||
|
插件配置;**可选**
|
||||||
|
|
||||||
|
返回
|
||||||
|
====
|
||||||
|
BinaryIO
|
||||||
|
导出后的二进制字节流
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def dump(
|
def dump(
|
||||||
self, data: "SingleMusic", file_path: Path, config: Optional[PluginConfig]
|
self, data: "SingleMusic", file_path: Path, config: Optional[PluginConfig]
|
||||||
):
|
):
|
||||||
"""将完整曲目导出为对应格式的文件"""
|
"""将完整曲目导出为对应格式的文件
|
||||||
|
|
||||||
|
参数
|
||||||
|
====
|
||||||
|
data: SingleMusic
|
||||||
|
待导出的完整曲目
|
||||||
|
file_path: Path
|
||||||
|
输出文件路径
|
||||||
|
config: Optional[PluginConfig]
|
||||||
|
插件配置;**可选**
|
||||||
|
"""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@@ -355,7 +540,7 @@ class TrackOutputPluginBase(TopInOutPluginBase, ABC):
|
|||||||
def __init_subclass__(cls) -> None:
|
def __init_subclass__(cls) -> None:
|
||||||
super().__init_subclass__()
|
super().__init_subclass__()
|
||||||
|
|
||||||
if cls.metainfo.type != PluginType.FUNCTION_TRACK_EXPORT:
|
if cls.metainfo.type != PluginTypes.FUNCTION_TRACK_EXPORT:
|
||||||
raise PluginMetainfoValueError(
|
raise PluginMetainfoValueError(
|
||||||
"插件类`{cls_name}`是从`TrackOutputPlugin`继承的,该类的子类应当为一个`PluginType.FUNCTION_TRACK_EXPORT`类型的插件,而不是`PluginType.{cls_type}`".format(
|
"插件类`{cls_name}`是从`TrackOutputPlugin`继承的,该类的子类应当为一个`PluginType.FUNCTION_TRACK_EXPORT`类型的插件,而不是`PluginType.{cls_type}`".format(
|
||||||
cls_name=cls.__name__,
|
cls_name=cls.__name__,
|
||||||
@@ -367,14 +552,37 @@ class TrackOutputPluginBase(TopInOutPluginBase, ABC):
|
|||||||
def dumpbytes(
|
def dumpbytes(
|
||||||
self, data: "SingleTrack", config: Optional[PluginConfig]
|
self, data: "SingleTrack", config: Optional[PluginConfig]
|
||||||
) -> BinaryIO:
|
) -> BinaryIO:
|
||||||
"""将单个音轨导出为对应格式的字节流"""
|
"""将单个音轨导出为对应格式的字节流
|
||||||
|
|
||||||
|
参数
|
||||||
|
====
|
||||||
|
data: SingleTrack
|
||||||
|
待导出的单个音轨
|
||||||
|
config: Optional[PluginConfig]
|
||||||
|
插件配置;**可选**
|
||||||
|
|
||||||
|
返回
|
||||||
|
====
|
||||||
|
BinaryIO
|
||||||
|
导出后的二进制字节流
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def dump(
|
def dump(
|
||||||
self, data: "SingleTrack", file_path: Path, config: Optional[PluginConfig]
|
self, data: "SingleTrack", file_path: Path, config: Optional[PluginConfig]
|
||||||
):
|
):
|
||||||
"""将单个音轨导出为对应格式的文件"""
|
"""将单个音轨导出为对应格式的文件
|
||||||
|
|
||||||
|
参数
|
||||||
|
====
|
||||||
|
data: SingleTrack
|
||||||
|
待导出的单个音轨
|
||||||
|
file_path: Path
|
||||||
|
输出文件路径
|
||||||
|
config: Optional[PluginConfig]
|
||||||
|
插件配置;**可选**
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@@ -384,7 +592,7 @@ class ServicePluginBase(TopPluginBase, ABC):
|
|||||||
def __init_subclass__(cls) -> None:
|
def __init_subclass__(cls) -> None:
|
||||||
super().__init_subclass__()
|
super().__init_subclass__()
|
||||||
|
|
||||||
if cls.metainfo.type != PluginType.SERVICE:
|
if cls.metainfo.type != PluginTypes.SERVICE:
|
||||||
raise PluginMetainfoValueError(
|
raise PluginMetainfoValueError(
|
||||||
"插件类`{cls_name}`是从`ServicePlugin`继承的,该类的子类应当为一个`PluginType.SERVICE`类型的插件,而不是`PluginType.{cls_type}`".format(
|
"插件类`{cls_name}`是从`ServicePlugin`继承的,该类的子类应当为一个`PluginType.SERVICE`类型的插件,而不是`PluginType.{cls_type}`".format(
|
||||||
cls_name=cls.__name__,
|
cls_name=cls.__name__,
|
||||||
@@ -394,7 +602,15 @@ class ServicePluginBase(TopPluginBase, ABC):
|
|||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def serve(self, config: Optional[PluginConfig], *args) -> None:
|
def serve(self, config: Optional[PluginConfig], *args) -> None:
|
||||||
"""服务插件的运行逻辑"""
|
"""服务插件的运行逻辑
|
||||||
|
|
||||||
|
参数
|
||||||
|
====
|
||||||
|
config: Optional[PluginConfig]
|
||||||
|
插件配置;**可选**
|
||||||
|
*args: Any
|
||||||
|
其他运行时参数
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@@ -404,7 +620,7 @@ class LibraryPluginBase(TopPluginBase, ABC):
|
|||||||
def __init_subclass__(cls) -> None:
|
def __init_subclass__(cls) -> None:
|
||||||
super().__init_subclass__()
|
super().__init_subclass__()
|
||||||
|
|
||||||
if cls.metainfo.type != PluginType.LIBRARY:
|
if cls.metainfo.type != PluginTypes.LIBRARY:
|
||||||
raise PluginMetainfoValueError(
|
raise PluginMetainfoValueError(
|
||||||
"插件类`{cls_name}`是从`LibraryPlugin`继承的,该类的子类应当为一个`PluginType.LIBRARY`类型的插件,而不是`PluginType.{cls_type}`".format(
|
"插件类`{cls_name}`是从`LibraryPlugin`继承的,该类的子类应当为一个`PluginType.LIBRARY`类型的插件,而不是`PluginType.{cls_type}`".format(
|
||||||
cls_name=cls.__name__,
|
cls_name=cls.__name__,
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ from Musicreater.plugins import (
|
|||||||
music_input_plugin,
|
music_input_plugin,
|
||||||
PluginConfig,
|
PluginConfig,
|
||||||
PluginMetaInformation,
|
PluginMetaInformation,
|
||||||
PluginType,
|
PluginTypes,
|
||||||
MusicInputPluginBase,
|
MusicInputPluginBase,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -37,11 +37,11 @@ class MidiImport2MusicPlugin(MusicInputPluginBase):
|
|||||||
"""Midi 音乐数据导入插件"""
|
"""Midi 音乐数据导入插件"""
|
||||||
|
|
||||||
metainfo = PluginMetaInformation(
|
metainfo = PluginMetaInformation(
|
||||||
name="midi_2_music_plugin",
|
name="Midi 导入插件",
|
||||||
author="金羿、玉衡Alioth",
|
author="金羿、玉衡Alioth",
|
||||||
description="从 Midi 文件导入音乐数据",
|
description="从 Midi 文件导入音乐数据",
|
||||||
version=(0, 0, 1),
|
version=(0, 0, 1),
|
||||||
type=PluginType.FUNCTION_MUSIC_IMPORT,
|
type=PluginTypes.FUNCTION_MUSIC_IMPORT,
|
||||||
license="Same as Musicreater",
|
license="Same as Musicreater",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -476,14 +476,7 @@ class SingleTrack(List[SingleNote]):
|
|||||||
|
|
||||||
self.extra_info = extra_information if extra_information else {}
|
self.extra_info = extra_information if extra_information else {}
|
||||||
|
|
||||||
self.argument_curves = {
|
self.argument_curves = {item: None for item in CurvableParam}
|
||||||
CurvableParam.PITCH: None,
|
|
||||||
CurvableParam.VELOCITY: None,
|
|
||||||
CurvableParam.VOLUME: None,
|
|
||||||
CurvableParam.DISTANCE: None,
|
|
||||||
CurvableParam.LR_PANNING: None,
|
|
||||||
CurvableParam.UD_PANNING: None,
|
|
||||||
}
|
|
||||||
|
|
||||||
super().__init__(*args)
|
super().__init__(*args)
|
||||||
super().sort()
|
super().sort()
|
||||||
@@ -572,9 +565,9 @@ class SingleTrack(List[SingleNote]):
|
|||||||
is_percussive_note=self.is_percussive,
|
is_percussive_note=self.is_percussive,
|
||||||
sound_position=self.sound_position,
|
sound_position=self.sound_position,
|
||||||
**{
|
**{
|
||||||
item.value: self.argument_curves[item].value_at(_note.start_time) # type: ignore
|
item.value: argcrv.value_at(_note.start_time)
|
||||||
for item in CurvableParam
|
for item in CurvableParam
|
||||||
if self.argument_curves[item]
|
if (argcrv := self.argument_curves[item])
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -225,6 +225,23 @@ class PluginLoadError(MusicreaterOuterlyError):
|
|||||||
super().__init__("插件加载错误 - ", *args)
|
super().__init__("插件加载错误 - ", *args)
|
||||||
|
|
||||||
|
|
||||||
|
class PluginNotFoundError(PluginLoadError):
|
||||||
|
"""插件未找到"""
|
||||||
|
|
||||||
|
def __init__(self, *args):
|
||||||
|
"""插件未找到"""
|
||||||
|
super().__init__("插件未找到:", *args)
|
||||||
|
|
||||||
|
|
||||||
|
class PluginRegisteredError(PluginLoadError):
|
||||||
|
"""插件重复注册"""
|
||||||
|
|
||||||
|
def __init__(self, *args):
|
||||||
|
"""插件已被注册注册"""
|
||||||
|
super().__init__("插件重复注册:", *args)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class PluginConfigRelatedError(MusicreaterOuterlyError):
|
class PluginConfigRelatedError(MusicreaterOuterlyError):
|
||||||
"""插件配置相关错误"""
|
"""插件配置相关错误"""
|
||||||
|
|
||||||
|
|||||||
@@ -47,19 +47,25 @@ import re
|
|||||||
|
|
||||||
|
|
||||||
from difflib import get_close_matches
|
from difflib import get_close_matches
|
||||||
from typing import Dict, Generator, List, Optional, Tuple, Union
|
from typing import Dict, Generator, List, Optional, Tuple, Union, Mapping, Callable
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
from .data import SingleMusic, SingleTrack
|
from .data import SingleMusic, SingleTrack
|
||||||
from .exceptions import FileFormatNotSupportedError, PluginNotSpecifiedError
|
from .exceptions import (
|
||||||
|
FileFormatNotSupportedError,
|
||||||
|
PluginNotSpecifiedError,
|
||||||
|
PluginNotFoundError,
|
||||||
|
)
|
||||||
from ._plugin_abc import TopPluginBase
|
from ._plugin_abc import TopPluginBase
|
||||||
from .plugins import (
|
from .plugins import (
|
||||||
_global_plugin_registry,
|
_global_plugin_registry,
|
||||||
PluginRegistry,
|
PluginRegistry,
|
||||||
PluginConfig,
|
PluginConfig,
|
||||||
PluginType,
|
PluginTypes,
|
||||||
load_plugin_module,
|
load_plugin_module,
|
||||||
|
T_IOPlugin,
|
||||||
|
T_Plugin,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -72,7 +78,7 @@ class MusiCreater:
|
|||||||
__plugin_registry: PluginRegistry
|
__plugin_registry: PluginRegistry
|
||||||
"""插件注册表实例"""
|
"""插件注册表实例"""
|
||||||
_plugin_cache: Dict[str, TopPluginBase]
|
_plugin_cache: Dict[str, TopPluginBase]
|
||||||
"""插件缓存字典,插件名为键、插件实例为值"""
|
"""插件缓存字典,插件id为键、插件实例为值"""
|
||||||
music: SingleMusic
|
music: SingleMusic
|
||||||
"""当前曲目实例"""
|
"""当前曲目实例"""
|
||||||
|
|
||||||
@@ -86,149 +92,119 @@ class MusiCreater:
|
|||||||
|
|
||||||
self.music = whole_music
|
self.music = whole_music
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_plugin_within_iousage(
|
||||||
|
get_func: Callable[[Union[Path, str]], Generator[T_IOPlugin, None, None]],
|
||||||
|
fpath: Path,
|
||||||
|
plg_regdict: Dict[str, T_IOPlugin],
|
||||||
|
plg_id: Optional[str],
|
||||||
|
) -> T_IOPlugin:
|
||||||
|
|
||||||
|
__plugin: Optional[T_IOPlugin] = None
|
||||||
|
if plg_id:
|
||||||
|
__plugin = plg_regdict.get(plg_id)
|
||||||
|
|
||||||
|
else:
|
||||||
|
for __plg in get_func(fpath):
|
||||||
|
if __plugin:
|
||||||
|
raise PluginNotSpecifiedError(
|
||||||
|
"文件类型`{}`可被多个插件处理,请在调用函数的参数中指定插件名称".format(
|
||||||
|
fpath.suffix.upper()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
__plugin = __plg
|
||||||
|
if __plugin:
|
||||||
|
return __plugin
|
||||||
|
else:
|
||||||
|
raise FileFormatNotSupportedError(
|
||||||
|
"无法找到处理`{}`类型文件的插件".format(fpath.suffix.upper())
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def import_music(
|
def import_music(
|
||||||
cls,
|
cls,
|
||||||
file_path: Path,
|
file_path: Path,
|
||||||
plugin_name: Optional[str] = None,
|
plugin_id: Optional[str] = None,
|
||||||
plugin_config: Optional[PluginConfig] = None,
|
plugin_config: Optional[PluginConfig] = None,
|
||||||
):
|
) -> "MusiCreater":
|
||||||
__music = None
|
return cls(
|
||||||
if plugin_name:
|
whole_music=cls._get_plugin_within_iousage(
|
||||||
__music = _global_plugin_registry.get_music_input_plugin(plugin_name).load(
|
_global_plugin_registry.get_music_input_plugin_by_format,
|
||||||
file_path, plugin_config
|
file_path,
|
||||||
)
|
_global_plugin_registry._music_input_plugins,
|
||||||
else:
|
plugin_id,
|
||||||
for plugin in _global_plugin_registry.get_music_input_plugin_by_format(
|
).load(file_path, plugin_config)
|
||||||
file_path
|
)
|
||||||
):
|
|
||||||
if __music is not None:
|
|
||||||
raise PluginNotSpecifiedError(
|
|
||||||
"文件类型`{}`可被多个插件处理,请在导入函数的参数中指定插件名称".format(
|
|
||||||
file_path.suffix.upper()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
__music = plugin.load(file_path, plugin_config)
|
|
||||||
if __music is None:
|
|
||||||
raise FileFormatNotSupportedError(
|
|
||||||
"无法找到处理`{}`类型文件的插件".format(file_path.suffix.upper())
|
|
||||||
)
|
|
||||||
return cls(whole_music=__music)
|
|
||||||
|
|
||||||
def import_track(
|
def import_track(
|
||||||
self,
|
self,
|
||||||
file_path: Path,
|
file_path: Path,
|
||||||
plugin_name: Optional[str] = None,
|
plugin_id: Optional[str] = None,
|
||||||
plugin_config: Optional[PluginConfig] = None,
|
plugin_config: Optional[PluginConfig] = None,
|
||||||
) -> SingleTrack:
|
) -> SingleTrack:
|
||||||
__track = None
|
self.music.append(
|
||||||
if plugin_name:
|
self._get_plugin_within_iousage(
|
||||||
__track = self.get_plugin_by_name(
|
self.__plugin_registry.get_track_input_plugin_by_format,
|
||||||
plugin_name
|
file_path,
|
||||||
).load( # pyright: ignore[reportAttributeAccessIssue]
|
self.__plugin_registry._track_input_plugins,
|
||||||
file_path, plugin_config
|
plugin_id,
|
||||||
)
|
).load(file_path, plugin_config)
|
||||||
else:
|
|
||||||
for plugin in self.__plugin_registry.get_track_input_plugin_by_format(
|
|
||||||
file_path
|
|
||||||
):
|
|
||||||
if __track:
|
|
||||||
raise PluginNotSpecifiedError(
|
|
||||||
"文件类型`{}`可被多个插件处理,请在导入函数的参数中指定插件名称".format(
|
|
||||||
file_path.suffix.upper()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
__track = plugin.load(file_path, plugin_config)
|
|
||||||
if __track:
|
|
||||||
self.music.append(__track)
|
|
||||||
return __track
|
|
||||||
raise FileFormatNotSupportedError(
|
|
||||||
"无法找到处理`{}`类型文件的插件".format(file_path.suffix.upper())
|
|
||||||
)
|
)
|
||||||
|
return self.music[-1]
|
||||||
|
|
||||||
def export_music(
|
def export_music(
|
||||||
self,
|
self,
|
||||||
file_path: Path,
|
file_path: Path,
|
||||||
plugin_name: Optional[str] = None,
|
plugin_id: Optional[str] = None,
|
||||||
plugin_config: Optional[PluginConfig] = None,
|
plugin_config: Optional[PluginConfig] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
__plugin = None
|
self._get_plugin_within_iousage(
|
||||||
if plugin_name:
|
self.__plugin_registry.get_music_output_plugin_by_format,
|
||||||
__plugin = self.get_plugin_by_name(plugin_name)
|
file_path,
|
||||||
else:
|
self.__plugin_registry._music_output_plugins,
|
||||||
for plugin in self.__plugin_registry.get_music_output_plugin_by_format(
|
plugin_id,
|
||||||
file_path
|
).dump(self.music, file_path, plugin_config)
|
||||||
):
|
|
||||||
if __plugin:
|
|
||||||
raise PluginNotSpecifiedError(
|
|
||||||
"文件类型`{}`可被多个插件处理,请在导出函数的参数中指定插件名称".format(
|
|
||||||
file_path.suffix.upper()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
__plugin = plugin
|
|
||||||
|
|
||||||
if __plugin:
|
|
||||||
__plugin.dump( # pyright: ignore[reportAttributeAccessIssue]
|
|
||||||
self.music, file_path, plugin_config
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
raise FileFormatNotSupportedError(
|
|
||||||
"无法找到处理`{}`类型文件的插件".format(file_path.suffix.upper())
|
|
||||||
)
|
|
||||||
|
|
||||||
def export_track(
|
def export_track(
|
||||||
self,
|
self,
|
||||||
track_index: int,
|
track_index: int,
|
||||||
file_path: Path,
|
file_path: Path,
|
||||||
plugin_name: Optional[str] = None,
|
plugin_id: Optional[str] = None,
|
||||||
plugin_config: Optional[PluginConfig] = None,
|
plugin_config: Optional[PluginConfig] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
__plugin = None
|
self._get_plugin_within_iousage(
|
||||||
if plugin_name:
|
self.__plugin_registry.get_track_output_plugin_by_format,
|
||||||
__plugin = self.get_plugin_by_name(plugin_name)
|
file_path,
|
||||||
else:
|
self.__plugin_registry._track_output_plugins,
|
||||||
for plugin in self.__plugin_registry.get_track_output_plugin_by_format(
|
plugin_id,
|
||||||
file_path
|
).dump(self.music[track_index], file_path, plugin_config)
|
||||||
):
|
|
||||||
if __plugin:
|
|
||||||
raise PluginNotSpecifiedError(
|
|
||||||
"文件类型`{}`可被多个插件处理,请在导出函数的参数中指定插件名称".format(
|
|
||||||
file_path.suffix.upper()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
__plugin = plugin
|
|
||||||
|
|
||||||
if __plugin:
|
|
||||||
__plugin.dump( # pyright: ignore[reportAttributeAccessIssue]
|
|
||||||
self.music[track_index], file_path, plugin_config
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
raise FileFormatNotSupportedError(
|
|
||||||
"无法找到处理`{}`类型文件的插件".format(file_path.suffix.upper())
|
|
||||||
)
|
|
||||||
|
|
||||||
def perform_operation_on_music(
|
def perform_operation_on_music(
|
||||||
self, plugin_name: str, plugin_config: Optional[PluginConfig] = None
|
self, plugin_id: str, plugin_config: Optional[PluginConfig] = None
|
||||||
):
|
):
|
||||||
# 这样做是为了兼容以后的*撤回/重做*功能
|
if __plugin := self.__plugin_registry._music_operate_plugins.get(plugin_id):
|
||||||
self.music = self.get_plugin_by_name(
|
# 这样做是为了兼容以后的*撤回/重做*功能
|
||||||
plugin_name
|
self.music = __plugin.process(self.music, plugin_config)
|
||||||
).process( # pyright: ignore[reportAttributeAccessIssue]
|
else:
|
||||||
self.music, plugin_config
|
raise PluginNotFoundError(
|
||||||
)
|
"无法找到惟一识别码为`{}`的插件".format(plugin_id)
|
||||||
|
)
|
||||||
|
|
||||||
def perform_operation_on_track(
|
def perform_operation_on_track(
|
||||||
self,
|
self,
|
||||||
track_index: int,
|
track_index: int,
|
||||||
plugin_name: str,
|
plugin_id: str,
|
||||||
plugin_config: Optional[PluginConfig] = None,
|
plugin_config: Optional[PluginConfig] = None,
|
||||||
):
|
):
|
||||||
# 这样做是为了兼容以后的*撤回/重做*功能
|
if __plugin := self.__plugin_registry._track_operate_plugins.get(plugin_id):
|
||||||
self.music[track_index] = self.get_plugin_by_name(
|
# 这样做是为了兼容以后的*撤回/重做*功能
|
||||||
plugin_name
|
self.music[track_index] = __plugin.process(
|
||||||
).process( # pyright: ignore[reportAttributeAccessIssue]
|
self.music[track_index], plugin_config
|
||||||
self.music[track_index], plugin_config
|
)
|
||||||
)
|
else:
|
||||||
|
raise PluginNotFoundError(
|
||||||
|
"无法找到惟一识别码为`{}`的插件".format(plugin_id)
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _camel_to_snake(name: str) -> str:
|
def _camel_to_snake(name: str) -> str:
|
||||||
@@ -240,8 +216,8 @@ class MusiCreater:
|
|||||||
"([a-z0-9])([A-Z])", r"\1_\2", re.sub("(.)([A-Z][a-z]+)", r"\1_\2", name)
|
"([a-z0-9])([A-Z])", r"\1_\2", re.sub("(.)([A-Z][a-z]+)", r"\1_\2", name)
|
||||||
).lower()
|
).lower()
|
||||||
|
|
||||||
def _parse_plugin_name(self, attr_name: str) -> Optional[str]:
|
def _parse_plugin_id(self, attr_name: str) -> Optional[str]:
|
||||||
"""解析属性名称为插件名称"""
|
"""解析属性名称为插件惟一识别码"""
|
||||||
|
|
||||||
# 尝试去除 _plugin 后缀
|
# 尝试去除 _plugin 后缀
|
||||||
if attr_name.endswith("_plugin"):
|
if attr_name.endswith("_plugin"):
|
||||||
@@ -256,35 +232,35 @@ class MusiCreater:
|
|||||||
if snake_case_name in self._plugin_cache: # 尝试转换后的插件名
|
if snake_case_name in self._plugin_cache: # 尝试转换后的插件名
|
||||||
return snake_case_name
|
return snake_case_name
|
||||||
else:
|
else:
|
||||||
return self._parse_plugin_name(snake_case_name)
|
return self._parse_plugin_id(snake_case_name)
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _get_closest_plugin_name(self, requested_name: str) -> Optional[str]:
|
def _get_closest_plugin_id(self, requested_id: str) -> Optional[str]:
|
||||||
"""找到最接近的插件名称(用于更好的错误提示)"""
|
"""找到最接近的插件识别码(用于更好的错误提示)"""
|
||||||
|
|
||||||
matches = get_close_matches(
|
matches = get_close_matches(
|
||||||
requested_name, self._plugin_cache.keys(), n=1, cutoff=0.6
|
requested_id, self._plugin_cache.keys(), n=1, cutoff=0.6
|
||||||
)
|
)
|
||||||
return matches[0] if matches else None
|
return matches[0] if matches else None
|
||||||
|
|
||||||
def get_plugin_by_name(self, name: str) -> TopPluginBase:
|
def get_plugin_by_id(self, plg_id: str):
|
||||||
"""获取插件实例,并缓存起来,提高性能"""
|
"""获取插件实例,并缓存起来,提高性能"""
|
||||||
if name.startswith("_"):
|
if plg_id.startswith("_"):
|
||||||
raise AttributeError("属性`{}`不存在,不应访问类的私有属性".format(name))
|
raise AttributeError("属性`{}`不存在,不应访问类的私有属性".format(plg_id))
|
||||||
|
|
||||||
if name in self._plugin_cache:
|
if plg_id in self._plugin_cache:
|
||||||
return self._plugin_cache[name]
|
return self._plugin_cache[plg_id]
|
||||||
else:
|
else:
|
||||||
plugin_name = self._parse_plugin_name(name)
|
plugin_name = self._parse_plugin_id(plg_id)
|
||||||
if plugin_name:
|
if plugin_name:
|
||||||
self._plugin_cache[name] = self._plugin_cache[plugin_name]
|
self._plugin_cache[plg_id] = self._plugin_cache[plugin_name]
|
||||||
return self._plugin_cache[name]
|
return self._plugin_cache[plg_id]
|
||||||
|
|
||||||
closest = self._get_closest_plugin_name(name)
|
closest = self._get_closest_plugin_id(plg_id)
|
||||||
|
|
||||||
raise AttributeError(
|
raise AttributeError(
|
||||||
"插件`{}`不存在,请检查插件名称是否正确".format(name)
|
"插件`{}`不存在,请检查插件的惟一识别码是否正确".format(plg_id)
|
||||||
+ (
|
+ (
|
||||||
";或者阁下可能想要使用的是`{}`插件?".format(closest)
|
";或者阁下可能想要使用的是`{}`插件?".format(closest)
|
||||||
if closest
|
if closest
|
||||||
@@ -292,18 +268,18 @@ class MusiCreater:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def __getattr__(self, name: str):
|
def __getattr__(self, plugin_id: str):
|
||||||
"""动态属性访问,允许直接 实例.插件名 来访问插件"""
|
"""动态属性访问,允许直接 实例.插件名 来访问插件"""
|
||||||
return self.get_plugin_by_name(name)
|
return self.get_plugin_by_id(plugin_id)
|
||||||
|
|
||||||
def _cache_all_plugins(self):
|
def _cache_all_plugins(self):
|
||||||
"""获取所有已注册插件的名称"""
|
"""获取所有已注册插件的名称"""
|
||||||
for __plugin_type, __plugins_set in self.__plugin_registry:
|
for __plugin_type, __plugins_map in self.__plugin_registry:
|
||||||
for __plugin in __plugins_set:
|
for __plugin_id, __plugin in __plugins_map.items():
|
||||||
if __plugin.metainfo.name in self._plugin_cache: # 避免重复缓存
|
if __plugin_id in self._plugin_cache: # 避免重复缓存
|
||||||
if (
|
if (
|
||||||
__plugin.metainfo.version
|
__plugin.metainfo.version
|
||||||
<= self._plugin_cache[__plugin.metainfo.name].metainfo.version
|
<= self._plugin_cache[__plugin_id].metainfo.version
|
||||||
): # 优先使用版本号最大的插件
|
): # 优先使用版本号最大的插件
|
||||||
continue
|
continue
|
||||||
self._plugin_cache[__plugin.metainfo.name] = __plugin
|
self._plugin_cache[__plugin_id] = __plugin
|
||||||
|
|||||||
@@ -30,13 +30,16 @@ from typing import (
|
|||||||
Set,
|
Set,
|
||||||
Iterable,
|
Iterable,
|
||||||
Iterator,
|
Iterator,
|
||||||
|
TypeVar,
|
||||||
|
Mapping,
|
||||||
|
Callable,
|
||||||
)
|
)
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
|
|
||||||
|
|
||||||
from ._plugin_abc import (
|
from ._plugin_abc import (
|
||||||
# 枚举类
|
# 枚举类
|
||||||
PluginType,
|
PluginTypes,
|
||||||
# 抽象基类/数据类(插件参数定义)
|
# 抽象基类/数据类(插件参数定义)
|
||||||
PluginConfig,
|
PluginConfig,
|
||||||
PluginMetaInformation,
|
PluginMetaInformation,
|
||||||
@@ -56,12 +59,13 @@ from .exceptions import (
|
|||||||
PluginMetainfoNotFoundError,
|
PluginMetainfoNotFoundError,
|
||||||
ParameterTypeError,
|
ParameterTypeError,
|
||||||
PluginInstanceNotFoundError,
|
PluginInstanceNotFoundError,
|
||||||
|
PluginRegisteredError,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
# 枚举类
|
# 枚举类
|
||||||
"PluginType",
|
"PluginTypes",
|
||||||
# 抽象基类/数据类(插件参数定义)
|
# 抽象基类/数据类(插件参数定义)
|
||||||
"PluginConfig",
|
"PluginConfig",
|
||||||
"PluginMetaInformation",
|
"PluginMetaInformation",
|
||||||
@@ -86,6 +90,26 @@ __all__ = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
T_IOPlugin = TypeVar(
|
||||||
|
"T_IOPlugin",
|
||||||
|
MusicInputPluginBase,
|
||||||
|
TrackInputPluginBase,
|
||||||
|
MusicOutputPluginBase,
|
||||||
|
TrackOutputPluginBase,
|
||||||
|
)
|
||||||
|
T_Plugin = TypeVar(
|
||||||
|
"T_Plugin",
|
||||||
|
MusicInputPluginBase,
|
||||||
|
TrackInputPluginBase,
|
||||||
|
MusicOperatePluginBase,
|
||||||
|
TrackOperatePluginBase,
|
||||||
|
MusicOutputPluginBase,
|
||||||
|
TrackOutputPluginBase,
|
||||||
|
ServicePluginBase,
|
||||||
|
LibraryPluginBase,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def load_plugin_module(package: Union[Path, str]):
|
def load_plugin_module(package: Union[Path, str]):
|
||||||
"""自动发现并加载插件包中的插件
|
"""自动发现并加载插件包中的插件
|
||||||
|
|
||||||
@@ -111,290 +135,199 @@ class PluginRegistry:
|
|||||||
"""插件注册管理器(注册表)"""
|
"""插件注册管理器(注册表)"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
# 实际上在纵容那些有着同样名称的插件……
|
self._music_input_plugins: Dict[str, MusicInputPluginBase] = {}
|
||||||
# (不用 Dict[str`plugin name`, PluginClass`] 的形式)
|
self._track_input_plugins: Dict[str, TrackInputPluginBase] = {}
|
||||||
# 啊,我真的很高尚
|
self._music_operate_plugins: Dict[str, MusicOperatePluginBase] = {}
|
||||||
# 你真的不会把插件注册两遍吧……对吧?
|
self._track_operate_plugins: Dict[str, TrackOperatePluginBase] = {}
|
||||||
|
self._music_output_plugins: Dict[str, MusicOutputPluginBase] = {}
|
||||||
|
self._track_output_plugins: Dict[str, TrackOutputPluginBase] = {}
|
||||||
|
self._service_plugins: Dict[str, ServicePluginBase] = {}
|
||||||
|
self._library_plugins: Dict[str, LibraryPluginBase] = {}
|
||||||
|
|
||||||
# EMERGENCY TODO ================================================ CRITICAL 紧急更改
|
def __iter__(self) -> Iterator[Tuple[PluginTypes, Mapping[str, TopPluginBase]]]:
|
||||||
# 改成 Dict[str`plugin id`, PluginClass`]] 的形式吧
|
|
||||||
# 刚刚才想起来,这个 name 是显示名称啊!草
|
|
||||||
# 现在测试情况下就将错就错吧,先把 name 当成 id 来写吧
|
|
||||||
self._music_input_plugins: Set[MusicInputPluginBase] = set()
|
|
||||||
self._track_input_plugins: Set[TrackInputPluginBase] = set()
|
|
||||||
self._music_operate_plugins: Set[MusicOperatePluginBase] = set()
|
|
||||||
self._track_operate_plugins: Set[TrackOperatePluginBase] = set()
|
|
||||||
self._music_output_plugins: Set[MusicOutputPluginBase] = set()
|
|
||||||
self._track_output_plugins: Set[TrackOutputPluginBase] = set()
|
|
||||||
self._service_plugins: Set[ServicePluginBase] = set()
|
|
||||||
self._library_plugins: Set[LibraryPluginBase] = set()
|
|
||||||
|
|
||||||
def __iter__(self) -> Iterator[Tuple[PluginType, Set[TopPluginBase]]]:
|
|
||||||
"""迭代器,返回所有插件"""
|
"""迭代器,返回所有插件"""
|
||||||
return iter(
|
return iter(
|
||||||
(
|
(
|
||||||
(PluginType.FUNCTION_MUSIC_IMPORT, self._music_input_plugins),
|
(PluginTypes.FUNCTION_MUSIC_IMPORT, self._music_input_plugins),
|
||||||
(PluginType.FUNCTION_TRACK_IMPORT, self._track_input_plugins),
|
(PluginTypes.FUNCTION_TRACK_IMPORT, self._track_input_plugins),
|
||||||
(PluginType.FUNCTION_MUSIC_OPERATE, self._music_operate_plugins),
|
(PluginTypes.FUNCTION_MUSIC_OPERATE, self._music_operate_plugins),
|
||||||
(PluginType.FUNCTION_TRACK_OPERATE, self._track_operate_plugins),
|
(PluginTypes.FUNCTION_TRACK_OPERATE, self._track_operate_plugins),
|
||||||
(PluginType.FUNCTION_MUSIC_EXPORT, self._music_output_plugins),
|
(PluginTypes.FUNCTION_MUSIC_EXPORT, self._music_output_plugins),
|
||||||
(PluginType.FUNCTION_TRACK_EXPORT, self._track_output_plugins),
|
(PluginTypes.FUNCTION_TRACK_EXPORT, self._track_output_plugins),
|
||||||
(PluginType.SERVICE, self._service_plugins),
|
(PluginTypes.SERVICE, self._service_plugins),
|
||||||
(PluginType.LIBRARY, self._library_plugins),
|
(PluginTypes.LIBRARY, self._library_plugins),
|
||||||
)
|
)
|
||||||
) # pyright: ignore[reportReturnType]
|
)
|
||||||
|
|
||||||
def register_music_input_plugin(self, plugin_class: type) -> None:
|
@staticmethod
|
||||||
|
def _register_plugin(cls_dict: dict, plg_class: type, plg_id: str) -> None:
|
||||||
|
"""注册插件"""
|
||||||
|
if plg_id in cls_dict:
|
||||||
|
if cls_dict[plg_id].metainfo.version >= plg_class.metainfo.version:
|
||||||
|
raise PluginRegisteredError(
|
||||||
|
"插件惟一识别码`{}`所对应的插件已存在更高版本`{}`,请勿重复注册同一插件!".format(
|
||||||
|
plg_id, plg_class.metainfo
|
||||||
|
)
|
||||||
|
)
|
||||||
|
cls_dict[plg_id] = plg_class()
|
||||||
|
|
||||||
|
def register_music_input_plugin(
|
||||||
|
self,
|
||||||
|
plugin_class: type,
|
||||||
|
plugin_id: str,
|
||||||
|
) -> None:
|
||||||
"""注册输入插件-整首曲目"""
|
"""注册输入插件-整首曲目"""
|
||||||
self._music_input_plugins.add(plugin_class())
|
self._register_plugin(self._music_input_plugins, plugin_class, plugin_id)
|
||||||
|
|
||||||
def register_track_input_plugin(self, plugin_class: type) -> None:
|
def register_track_input_plugin(self, plugin_class: type, plugin_id: str) -> None:
|
||||||
"""注册输入插件-单个音轨"""
|
"""注册输入插件-单个音轨"""
|
||||||
self._track_input_plugins.add(plugin_class())
|
self._register_plugin(self._track_input_plugins, plugin_class, plugin_id)
|
||||||
|
|
||||||
def register_music_operate_plugin(self, plugin_class: type) -> None:
|
def register_music_operate_plugin(self, plugin_class: type, plugin_id: str) -> None:
|
||||||
"""注册曲目处理插件"""
|
"""注册曲目处理插件"""
|
||||||
self._music_operate_plugins.add(plugin_class())
|
self._register_plugin(self._music_operate_plugins, plugin_class, plugin_id)
|
||||||
|
|
||||||
def register_track_operate_plugin(self, plugin_class: type) -> None:
|
def register_track_operate_plugin(self, plugin_class: type, plugin_id: str) -> None:
|
||||||
"""注册音轨处理插件"""
|
"""注册音轨处理插件"""
|
||||||
self._track_operate_plugins.add(plugin_class())
|
self._register_plugin(self._track_operate_plugins, plugin_class, plugin_id)
|
||||||
|
|
||||||
def register_music_output_plugin(self, plugin_class: type) -> None:
|
def register_music_output_plugin(self, plugin_class: type, plugin_id: str) -> None:
|
||||||
"""注册输出插件-整首曲目"""
|
"""注册输出插件-整首曲目"""
|
||||||
self._music_output_plugins.add(plugin_class())
|
self._register_plugin(self._music_output_plugins, plugin_class, plugin_id)
|
||||||
|
|
||||||
def register_track_output_plugin(self, plugin_class: type) -> None:
|
def register_track_output_plugin(self, plugin_class: type, plugin_id: str) -> None:
|
||||||
"""注册输出插件-单个音轨"""
|
"""注册输出插件-单个音轨"""
|
||||||
self._track_output_plugins.add(plugin_class())
|
self._register_plugin(self._track_output_plugins, plugin_class, plugin_id)
|
||||||
|
|
||||||
def register_service_plugin(self, plugin_class: type) -> None:
|
def register_service_plugin(self, plugin_class: type, plugin_id: str) -> None:
|
||||||
"""注册服务插件"""
|
"""注册服务插件"""
|
||||||
self._service_plugins.add(plugin_class())
|
self._register_plugin(self._service_plugins, plugin_class, plugin_id)
|
||||||
|
|
||||||
def register_library_plugin(self, plugin_class: type) -> None:
|
def register_library_plugin(self, plugin_class: type, plugin_id: str) -> None:
|
||||||
"""注册支持库插件"""
|
"""注册支持库插件"""
|
||||||
self._library_plugins.add(plugin_class())
|
self._register_plugin(self._library_plugins, plugin_class, plugin_id)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_io_plugin_by_format(
|
||||||
|
plugin_regdict: Dict[str, T_IOPlugin], fpath_or_format: Union[Path, str]
|
||||||
|
) -> Generator[T_IOPlugin, None, None]:
|
||||||
|
if isinstance(fpath_or_format, str):
|
||||||
|
return (
|
||||||
|
plugin
|
||||||
|
for plugin in plugin_regdict.values()
|
||||||
|
if plugin.can_handle_format(fpath_or_format)
|
||||||
|
)
|
||||||
|
elif isinstance(fpath_or_format, Path):
|
||||||
|
return (
|
||||||
|
plugin
|
||||||
|
for plugin in plugin_regdict.values()
|
||||||
|
if plugin.can_handle_file(fpath_or_format)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise ParameterTypeError(
|
||||||
|
"用于指定“导入全曲的数据之类型”的参数,其类型须为`Path`路径或字符串,而非`{}`类型的`{}`值".format(
|
||||||
|
type(fpath_or_format), fpath_or_format
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def get_music_input_plugin_by_format(
|
def get_music_input_plugin_by_format(
|
||||||
self, filepath_or_format: Union[Path, str]
|
self, filepath_or_format: Union[Path, str]
|
||||||
) -> Generator[MusicInputPluginBase, None, None]:
|
) -> Generator[MusicInputPluginBase, None, None]:
|
||||||
"""通过指定输入的文件或格式,以获取对应的全曲导入用插件"""
|
"""通过指定输入的文件或格式,以获取对应的全曲导入用插件"""
|
||||||
if isinstance(filepath_or_format, str):
|
return self._get_io_plugin_by_format(
|
||||||
return (
|
self._music_input_plugins, filepath_or_format
|
||||||
plugin
|
)
|
||||||
for plugin in self._music_input_plugins
|
|
||||||
if plugin.can_handle_format(filepath_or_format)
|
|
||||||
)
|
|
||||||
elif isinstance(filepath_or_format, Path):
|
|
||||||
return (
|
|
||||||
plugin
|
|
||||||
for plugin in self._music_input_plugins
|
|
||||||
if plugin.can_handle_file(filepath_or_format)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
raise ParameterTypeError(
|
|
||||||
"用于指定“导入全曲的数据之类型”的参数,其类型须为`Path`路径或字符串,而非`{}`类型的`{}`值".format(
|
|
||||||
type(filepath_or_format), filepath_or_format
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_track_input_plugin_by_format(
|
def get_track_input_plugin_by_format(
|
||||||
self, filepath_or_format: Union[Path, str]
|
self, filepath_or_format: Union[Path, str]
|
||||||
) -> Generator[TrackInputPluginBase, None, None]:
|
) -> Generator[TrackInputPluginBase, None, None]:
|
||||||
"""通过指定输入的文件或格式,以获取对应的单音轨导入用插件"""
|
"""通过指定输入的文件或格式,以获取对应的单音轨导入用插件"""
|
||||||
if isinstance(filepath_or_format, str):
|
return self._get_io_plugin_by_format(
|
||||||
return (
|
self._track_input_plugins, filepath_or_format
|
||||||
plugin
|
)
|
||||||
for plugin in self._track_input_plugins
|
|
||||||
if plugin.can_handle_format(filepath_or_format)
|
|
||||||
)
|
|
||||||
elif isinstance(filepath_or_format, Path):
|
|
||||||
return (
|
|
||||||
plugin
|
|
||||||
for plugin in self._track_input_plugins
|
|
||||||
if plugin.can_handle_file(filepath_or_format)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
raise ParameterTypeError(
|
|
||||||
"用于指定“导入单个音轨的数据之类型”的参数,其类型须为`Path`路径或字符串,而非`{}`类型的`{}`值".format(
|
|
||||||
type(filepath_or_format), filepath_or_format
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_music_output_plugin_by_format(
|
def get_music_output_plugin_by_format(
|
||||||
self, filepath_or_format: Union[Path, str]
|
self, filepath_or_format: Union[Path, str]
|
||||||
) -> Generator[MusicOutputPluginBase, None, None]:
|
) -> Generator[MusicOutputPluginBase, None, None]:
|
||||||
"""通过指定输出的文件或格式,以获取对应的导出全曲用插件"""
|
"""通过指定输出的文件或格式,以获取对应的导出全曲用插件"""
|
||||||
if isinstance(filepath_or_format, str):
|
return self._get_io_plugin_by_format(
|
||||||
return (
|
self._music_output_plugins, filepath_or_format
|
||||||
plugin
|
)
|
||||||
for plugin in self._music_output_plugins
|
|
||||||
if plugin.can_handle_format(filepath_or_format)
|
|
||||||
)
|
|
||||||
elif isinstance(filepath_or_format, Path):
|
|
||||||
return (
|
|
||||||
plugin
|
|
||||||
for plugin in self._music_output_plugins
|
|
||||||
if plugin.can_handle_file(filepath_or_format)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
raise ParameterTypeError(
|
|
||||||
"用于指定“全曲数据导出的类型”的参数,其类型须为`Path`路径或字符串,而非`{}`类型的`{}`值".format(
|
|
||||||
type(filepath_or_format), filepath_or_format
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_track_output_plugin_by_format(
|
def get_track_output_plugin_by_format(
|
||||||
self, filepath_or_format: Union[Path, str]
|
self, filepath_or_format: Union[Path, str]
|
||||||
) -> Generator[TrackOutputPluginBase, None, None]:
|
) -> Generator[TrackOutputPluginBase, None, None]:
|
||||||
"""通过指定输出的文件或格式,以获取对应的导出单个音轨用插件"""
|
"""通过指定输出的文件或格式,以获取对应的导出单个音轨用插件"""
|
||||||
if isinstance(filepath_or_format, str):
|
return self._get_io_plugin_by_format(
|
||||||
return (
|
self._track_output_plugins, filepath_or_format
|
||||||
plugin
|
)
|
||||||
for plugin in self._track_output_plugins
|
|
||||||
if plugin.can_handle_format(filepath_or_format)
|
def _get_plugin_by_name(
|
||||||
|
self,
|
||||||
|
plugin_regdict: Mapping[str, T_Plugin],
|
||||||
|
plugin_name: str,
|
||||||
|
plugin_usage: str = "",
|
||||||
|
) -> T_Plugin:
|
||||||
|
"""通过指定名称,以获取对应的插件,当名称重叠时,取版本号最大的"""
|
||||||
|
try:
|
||||||
|
return max(
|
||||||
|
[
|
||||||
|
plugin
|
||||||
|
for plugin in plugin_regdict.values()
|
||||||
|
if plugin.metainfo.name == plugin_name
|
||||||
|
],
|
||||||
|
key=lambda plugin: plugin.metainfo.version,
|
||||||
)
|
)
|
||||||
elif isinstance(filepath_or_format, Path):
|
except ValueError:
|
||||||
return (
|
raise PluginInstanceNotFoundError(
|
||||||
plugin
|
"未找到“用于{}、名为`{}`”的插件".format(plugin_usage, plugin_name)
|
||||||
for plugin in self._track_output_plugins
|
|
||||||
if plugin.can_handle_file(filepath_or_format)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
raise ParameterTypeError(
|
|
||||||
"用于指定“单音轨数据导出的类型”的参数,其类型须为`Path`路径或字符串,而非`{}`类型的`{}`值".format(
|
|
||||||
type(filepath_or_format), filepath_or_format
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_music_input_plugin(self, plugin_name: str) -> MusicInputPluginBase:
|
def get_music_input_plugin(self, plugin_name: str) -> MusicInputPluginBase:
|
||||||
"""获取指定名称的全曲导入用插件,当名称重叠时,取版本号最大的"""
|
"""获取指定名称的全曲导入用插件,当名称重叠时,取版本号最大的"""
|
||||||
try:
|
return self._get_plugin_by_name(
|
||||||
return max(
|
self._music_input_plugins, plugin_name, "导入全曲"
|
||||||
[
|
)
|
||||||
plugin
|
|
||||||
for plugin in self._music_input_plugins
|
|
||||||
if plugin.metainfo.name == plugin_name
|
|
||||||
],
|
|
||||||
key=lambda plugin: plugin.metainfo.version,
|
|
||||||
)
|
|
||||||
except ValueError:
|
|
||||||
raise PluginInstanceNotFoundError(
|
|
||||||
"未找到“用于导入曲目、名为`{}`”的插件".format(plugin_name)
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_track_input_plugin(self, plugin_name: str) -> TrackInputPluginBase:
|
def get_track_input_plugin(self, plugin_name: str) -> TrackInputPluginBase:
|
||||||
"""获取指定名称的单音轨导入用插件,当名称重叠时,取版本号最大的"""
|
"""获取指定名称的单音轨导入用插件,当名称重叠时,取版本号最大的"""
|
||||||
try:
|
return self._get_plugin_by_name(
|
||||||
return max(
|
self._track_input_plugins, plugin_name, "导入单轨"
|
||||||
[
|
)
|
||||||
plugin
|
|
||||||
for plugin in self._track_input_plugins
|
|
||||||
if plugin.metainfo.name == plugin_name
|
|
||||||
],
|
|
||||||
key=lambda plugin: plugin.metainfo.version,
|
|
||||||
)
|
|
||||||
except ValueError:
|
|
||||||
raise PluginInstanceNotFoundError(
|
|
||||||
"未找到“用于导入单个音轨、名为`{}`”的插件".format(plugin_name)
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_music_operate_plugin(self, plugin_name: str) -> MusicOperatePluginBase:
|
def get_music_operate_plugin(self, plugin_name: str) -> MusicOperatePluginBase:
|
||||||
"""获取指定名称的全曲处理用插件,当名称重叠时,取版本号最大的"""
|
"""获取指定名称的全曲处理用插件,当名称重叠时,取版本号最大的"""
|
||||||
try:
|
return self._get_plugin_by_name(
|
||||||
return max(
|
self._music_operate_plugins, plugin_name, "处理整个曲目"
|
||||||
[
|
)
|
||||||
plugin
|
|
||||||
for plugin in self._music_operate_plugins
|
|
||||||
if plugin.metainfo.name == plugin_name
|
|
||||||
],
|
|
||||||
key=lambda plugin: plugin.metainfo.version,
|
|
||||||
)
|
|
||||||
except ValueError:
|
|
||||||
raise PluginInstanceNotFoundError(
|
|
||||||
"未找到“用于处理整个曲目、名为`{}`”的插件".format(plugin_name)
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_track_operate_plugin(self, plugin_name: str) -> TrackOperatePluginBase:
|
def get_track_operate_plugin(self, plugin_name: str) -> TrackOperatePluginBase:
|
||||||
"""获取指定名称的单音轨处理用插件,当名称重叠时,取版本号最大的"""
|
"""获取指定名称的单音轨处理用插件,当名称重叠时,取版本号最大的"""
|
||||||
try:
|
return self._get_plugin_by_name(
|
||||||
return max(
|
self._track_operate_plugins, plugin_name, "处理单个音轨"
|
||||||
[
|
)
|
||||||
plugin
|
|
||||||
for plugin in self._track_operate_plugins
|
|
||||||
if plugin.metainfo.name == plugin_name
|
|
||||||
],
|
|
||||||
key=lambda plugin: plugin.metainfo.version,
|
|
||||||
)
|
|
||||||
except ValueError:
|
|
||||||
raise PluginInstanceNotFoundError(
|
|
||||||
"未找到“用于处理单个音轨、名为`{}`”的插件".format(plugin_name)
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_music_output_plugin(self, plugin_name: str) -> MusicOutputPluginBase:
|
def get_music_output_plugin(self, plugin_name: str) -> MusicOutputPluginBase:
|
||||||
"""获取指定名称的导出全曲用插件,当名称重叠时,取版本号最大的"""
|
"""获取指定名称的导出全曲用插件,当名称重叠时,取版本号最大的"""
|
||||||
try:
|
return self._get_plugin_by_name(
|
||||||
return max(
|
self._music_output_plugins, plugin_name, "导出完整曲目"
|
||||||
[
|
)
|
||||||
plugin
|
|
||||||
for plugin in self._music_output_plugins
|
|
||||||
if plugin.metainfo.name == plugin_name
|
|
||||||
],
|
|
||||||
key=lambda plugin: plugin.metainfo.version,
|
|
||||||
)
|
|
||||||
except ValueError:
|
|
||||||
raise PluginMetainfoNotFoundError(
|
|
||||||
"未找到“用于导出完整曲目、名为`{}`”的插件".format(plugin_name)
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_track_output_plugin(self, plugin_name: str) -> TrackOutputPluginBase:
|
def get_track_output_plugin(self, plugin_name: str) -> TrackOutputPluginBase:
|
||||||
"""获取指定名称的导出单音轨用插件,当名称重叠时,取版本号最大的"""
|
"""获取指定名称的导出单音轨用插件,当名称重叠时,取版本号最大的"""
|
||||||
try:
|
return self._get_plugin_by_name(
|
||||||
return max(
|
self._track_output_plugins, plugin_name, "导出单个音轨"
|
||||||
[
|
)
|
||||||
plugin
|
|
||||||
for plugin in self._track_output_plugins
|
|
||||||
if plugin.metainfo.name == plugin_name
|
|
||||||
],
|
|
||||||
key=lambda plugin: plugin.metainfo.version,
|
|
||||||
)
|
|
||||||
except ValueError:
|
|
||||||
raise PluginMetainfoNotFoundError(
|
|
||||||
"未找到“用于导出单个音轨、名为`{}`”的插件".format(plugin_name)
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_service_plugin(self, plugin_name: str) -> ServicePluginBase:
|
def get_service_plugin(self, plugin_name: str) -> ServicePluginBase:
|
||||||
"""获取服务用插件,当名称重叠时,取版本号最大的"""
|
"""获取服务用插件,当名称重叠时,取版本号最大的"""
|
||||||
try:
|
return self._get_plugin_by_name(self._service_plugins, plugin_name, "提供服务")
|
||||||
return max(
|
|
||||||
[
|
|
||||||
plugin
|
|
||||||
for plugin in self._service_plugins
|
|
||||||
if plugin.metainfo.name == plugin_name
|
|
||||||
],
|
|
||||||
key=lambda plugin: plugin.metainfo.version,
|
|
||||||
)
|
|
||||||
except ValueError:
|
|
||||||
raise PluginInstanceNotFoundError(
|
|
||||||
"未找到名为`{}`的服务用插件".format(plugin_name)
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_library_plugin(self, plugin_name: str) -> LibraryPluginBase:
|
def get_library_plugin(self, plugin_name: str) -> LibraryPluginBase:
|
||||||
"""获取依赖库类插件,当名称重叠时,取版本号最高的"""
|
"""获取依赖库类插件,当名称重叠时,取版本号最高的"""
|
||||||
try:
|
return self._get_plugin_by_name(
|
||||||
return max(
|
self._library_plugins, plugin_name, "作为依赖库"
|
||||||
[
|
)
|
||||||
plugin
|
|
||||||
for plugin in self._library_plugins
|
|
||||||
if plugin.metainfo.name == plugin_name
|
|
||||||
],
|
|
||||||
key=lambda plugin: plugin.metainfo.version,
|
|
||||||
)
|
|
||||||
except ValueError:
|
|
||||||
raise PluginInstanceNotFoundError(
|
|
||||||
"未找到名为`{}`的依赖库插件".format(plugin_name)
|
|
||||||
)
|
|
||||||
|
|
||||||
def supported_input_formats(self) -> Set[str]:
|
def supported_input_formats(self) -> Set[str]:
|
||||||
"""所有支持的导入格式"""
|
"""所有支持的导入格式"""
|
||||||
@@ -402,7 +335,8 @@ class PluginRegistry:
|
|||||||
chain.from_iterable(
|
chain.from_iterable(
|
||||||
plugin.supported_formats
|
plugin.supported_formats
|
||||||
for plugin in chain(
|
for plugin in chain(
|
||||||
self._music_input_plugins, self._track_input_plugins
|
self._music_input_plugins.values(),
|
||||||
|
self._track_input_plugins.values(),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -413,7 +347,8 @@ class PluginRegistry:
|
|||||||
chain.from_iterable(
|
chain.from_iterable(
|
||||||
plugin.supported_formats
|
plugin.supported_formats
|
||||||
for plugin in chain(
|
for plugin in chain(
|
||||||
self._music_output_plugins, self._track_output_plugins
|
self._music_output_plugins.values(),
|
||||||
|
self._track_output_plugins.values(),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -423,97 +358,67 @@ _global_plugin_registry = PluginRegistry()
|
|||||||
"""全局插件注册表实例"""
|
"""全局插件注册表实例"""
|
||||||
|
|
||||||
|
|
||||||
def music_input_plugin(plugin_id: str):
|
def __plugin_regist_decorator(plg_id: str, rgst_func: Callable[[type, str], None]):
|
||||||
"""全曲输入用插件装饰器"""
|
|
||||||
|
|
||||||
def decorator(cls):
|
def decorator(cls):
|
||||||
global _global_plugin_registry
|
global _global_plugin_registry
|
||||||
cls.id = plugin_id
|
cls.id = plg_id
|
||||||
_global_plugin_registry.register_music_input_plugin(cls)
|
rgst_func(cls, plg_id)
|
||||||
return cls
|
return cls
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
def music_input_plugin(plugin_id: str):
|
||||||
|
"""全曲输入用插件装饰器"""
|
||||||
|
return __plugin_regist_decorator(
|
||||||
|
plugin_id, _global_plugin_registry.register_music_input_plugin
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def track_input_plugin(plugin_id: str):
|
def track_input_plugin(plugin_id: str):
|
||||||
"""单轨输入用插件装饰器"""
|
"""单轨输入用插件装饰器"""
|
||||||
|
return __plugin_regist_decorator(
|
||||||
def decorator(cls):
|
plugin_id, _global_plugin_registry.register_track_input_plugin
|
||||||
global _global_plugin_registry
|
)
|
||||||
cls.id = plugin_id
|
|
||||||
_global_plugin_registry.register_track_input_plugin(cls)
|
|
||||||
return cls
|
|
||||||
|
|
||||||
return decorator
|
|
||||||
|
|
||||||
|
|
||||||
def music_operate_plugin(plugin_id: str):
|
def music_operate_plugin(plugin_id: str):
|
||||||
"""全曲处理用插件装饰器"""
|
"""全曲处理用插件装饰器"""
|
||||||
|
return __plugin_regist_decorator(
|
||||||
def decorator(cls):
|
plugin_id, _global_plugin_registry.register_music_operate_plugin
|
||||||
global _global_plugin_registry
|
)
|
||||||
cls.id = plugin_id
|
|
||||||
_global_plugin_registry.register_music_operate_plugin(cls)
|
|
||||||
return cls
|
|
||||||
|
|
||||||
return decorator
|
|
||||||
|
|
||||||
|
|
||||||
def track_operate_plugin(plugin_id: str):
|
def track_operate_plugin(plugin_id: str):
|
||||||
"""音轨处理插件装饰器"""
|
"""音轨处理插件装饰器"""
|
||||||
|
return __plugin_regist_decorator(
|
||||||
def decorator(cls):
|
plugin_id, _global_plugin_registry.register_track_operate_plugin
|
||||||
global _global_plugin_registry
|
)
|
||||||
cls.id = plugin_id
|
|
||||||
_global_plugin_registry.register_track_operate_plugin(cls)
|
|
||||||
return cls
|
|
||||||
|
|
||||||
return decorator
|
|
||||||
|
|
||||||
|
|
||||||
def music_output_plugin(plugin_id: str):
|
def music_output_plugin(plugin_id: str):
|
||||||
"""乐曲输出用插件装饰器"""
|
"""乐曲输出用插件装饰器"""
|
||||||
|
return __plugin_regist_decorator(
|
||||||
def decorator(cls):
|
plugin_id, _global_plugin_registry.register_music_output_plugin
|
||||||
global _global_plugin_registry
|
)
|
||||||
cls.id = plugin_id
|
|
||||||
_global_plugin_registry.register_music_output_plugin(cls)
|
|
||||||
return cls
|
|
||||||
|
|
||||||
return decorator
|
|
||||||
|
|
||||||
|
|
||||||
def track_output_plugin(plugin_id: str):
|
def track_output_plugin(plugin_id: str):
|
||||||
"""音轨输出用插件装饰器"""
|
"""音轨输出用插件装饰器"""
|
||||||
|
return __plugin_regist_decorator(
|
||||||
def decorator(cls):
|
plugin_id, _global_plugin_registry.register_track_output_plugin
|
||||||
global _global_plugin_registry
|
)
|
||||||
cls.id = plugin_id
|
|
||||||
_global_plugin_registry.register_track_output_plugin(cls)
|
|
||||||
return cls
|
|
||||||
|
|
||||||
return decorator
|
|
||||||
|
|
||||||
|
|
||||||
def service_plugin(plugin_id: str):
|
def service_plugin(plugin_id: str):
|
||||||
"""服务插件装饰器"""
|
"""服务插件装饰器"""
|
||||||
|
return __plugin_regist_decorator(
|
||||||
def decorator(cls):
|
plugin_id, _global_plugin_registry.register_service_plugin
|
||||||
global _global_plugin_registry
|
)
|
||||||
cls.id = plugin_id
|
|
||||||
_global_plugin_registry.register_service_plugin(cls)
|
|
||||||
return cls
|
|
||||||
|
|
||||||
return decorator
|
|
||||||
|
|
||||||
|
|
||||||
def library_plugin(plugin_id: str):
|
def library_plugin(plugin_id: str):
|
||||||
"""支持库插件装饰器"""
|
"""支持库插件装饰器"""
|
||||||
|
|
||||||
def decorator(cls):
|
return __plugin_regist_decorator(
|
||||||
global _global_plugin_registry
|
plugin_id, _global_plugin_registry.register_library_plugin
|
||||||
cls.id = plugin_id
|
)
|
||||||
_global_plugin_registry.register_library_plugin(cls)
|
|
||||||
return cls
|
|
||||||
|
|
||||||
return decorator
|
|
||||||
|
|||||||
@@ -14,5 +14,11 @@ print(msct:=MusiCreater.import_music(Path("./resources/测试片段.mid")))
|
|||||||
print(msct.music)
|
print(msct.music)
|
||||||
|
|
||||||
|
|
||||||
|
# 为了让类型检查器满意,以下方法不建议使用,因为这本质上是越过了 MusiCreater 类而直接执行插件的函数
|
||||||
|
print(t := msct.midi_2_music_plugin.load(Path("./resources/测试片段.mid"), None))
|
||||||
|
# 我们建议用这种方式来代替
|
||||||
|
t = _global_plugin_registry._music_input_plugins["midi_2_music_plugin"].load(Path("./resources/测试片段.mid"), None)
|
||||||
|
|
||||||
|
print(_global_plugin_registry)
|
||||||
|
print(msct._plugin_cache)
|
||||||
|
|
||||||
print(msct.midi_2_music_plugin.load(Path("./resources/测试片段.mid"), None))
|
|
||||||
Reference in New Issue
Block a user