mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-07 08:26:53 +08:00
refactor: 收口 V3 分层架构与插件兼容边界
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
"""插件目录条目的运行态元数据映射。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from typing import Any, Optional
|
||||
|
||||
from app.runtime.extensions.plugin.contracts import supports_plugin_hook
|
||||
from app.schemas.plugin import Plugin
|
||||
|
||||
|
||||
class PluginMetadataMapper:
|
||||
"""把市场或本地仓条目映射为包含运行态状态的插件 DTO。"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
plugin_instance: Callable[[str], Optional[Any]],
|
||||
plugin_class: Callable[[str], Optional[Any]],
|
||||
annotate_system_version: Callable[[dict], dict],
|
||||
is_package_compatible: Callable[[dict, str], bool],
|
||||
auth_checker: Callable[[Plugin, dict], bool],
|
||||
version_compare: Callable[[str, str, str], bool],
|
||||
log: Any,
|
||||
) -> None:
|
||||
"""保存注册表、兼容判断和权限判断端口。"""
|
||||
self._plugin_instance = plugin_instance
|
||||
self._plugin_class = plugin_class
|
||||
self._annotate_system_version = annotate_system_version
|
||||
self._is_package_compatible = is_package_compatible
|
||||
self._auth_checker = auth_checker
|
||||
self._version_compare = version_compare
|
||||
self._logger = log
|
||||
|
||||
def map(
|
||||
self,
|
||||
plugin_id: str,
|
||||
plugin_info: dict,
|
||||
market: str,
|
||||
installed_plugins: list[str],
|
||||
add_time: int,
|
||||
package_version: Optional[str] = None,
|
||||
) -> Optional[Plugin]:
|
||||
"""映射一个插件索引条目,不兼容或无权限时返回空。"""
|
||||
if not isinstance(plugin_info, dict):
|
||||
return None
|
||||
info = self._annotate_system_version(plugin_info.copy())
|
||||
if not self._is_package_compatible(info, package_version or ""):
|
||||
return None
|
||||
|
||||
instance = self._plugin_instance(plugin_id)
|
||||
plugin_class = self._plugin_class(plugin_id)
|
||||
plugin = Plugin(id=plugin_id)
|
||||
plugin.installed = plugin_id in installed_plugins and plugin_class is not None
|
||||
plugin.has_update = False
|
||||
if plugin_class:
|
||||
installed_version = getattr(plugin_class, "plugin_version", None)
|
||||
online_version = info.get("version")
|
||||
if installed_version and online_version:
|
||||
plugin.has_update = self._version_compare(
|
||||
installed_version,
|
||||
"<",
|
||||
online_version,
|
||||
)
|
||||
|
||||
plugin.system_version = info.get("system_version")
|
||||
if info.get("system_version_compatible") is False:
|
||||
plugin.system_version_compatible = False
|
||||
plugin.system_version_message = info.get("system_version_message")
|
||||
|
||||
plugin.state = self._state(plugin_id, instance)
|
||||
plugin.has_page = bool(
|
||||
instance and supports_plugin_hook(instance, "get_page")
|
||||
)
|
||||
if info.get("key"):
|
||||
plugin.plugin_public_key = info["key"]
|
||||
if not self._auth_checker(plugin, info):
|
||||
return None
|
||||
|
||||
plugin.plugin_name = info.get("name")
|
||||
plugin.plugin_desc = info.get("description")
|
||||
plugin.plugin_version = info.get("version")
|
||||
plugin.plugin_icon = info.get("icon")
|
||||
plugin.plugin_label = self.normalize_label(info.get("labels"))
|
||||
plugin.plugin_author = info.get("author")
|
||||
plugin.history = info.get("history") or {}
|
||||
plugin.release = bool(info.get("release"))
|
||||
plugin.repo_url = market
|
||||
plugin.is_local = False
|
||||
plugin.add_time = add_time
|
||||
return plugin
|
||||
|
||||
def _state(self, plugin_id: str, instance: Optional[Any]) -> bool:
|
||||
"""安全读取插件运行状态,插件异常时降级为未启用。"""
|
||||
if not instance or not hasattr(instance, "get_state"):
|
||||
return False
|
||||
try:
|
||||
return bool(instance.get_state())
|
||||
except Exception as error: # noqa: BLE001
|
||||
self._logger.error(f"获取插件 {plugin_id} 状态出错:{error}")
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def normalize_label(labels: Any) -> Optional[str]:
|
||||
"""兼容市场标签的旧字符串和新列表格式。"""
|
||||
if isinstance(labels, str):
|
||||
label = labels.strip()
|
||||
return label or None
|
||||
if isinstance(labels, list):
|
||||
normalized = [
|
||||
str(item).strip()
|
||||
for item in labels
|
||||
if str(item).strip()
|
||||
]
|
||||
return " ".join(normalized) or None
|
||||
return None
|
||||
Reference in New Issue
Block a user