mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-07 00:16:57 +08:00
refactor(plugin): align local repo naming
This commit is contained in:
@@ -155,12 +155,12 @@ async def all_plugins(_: User = Depends(get_current_active_superuser_async),
|
|||||||
|
|
||||||
# 未安装的本地插件
|
# 未安装的本地插件
|
||||||
not_installed_plugins = [plugin for plugin in local_plugins if not plugin.installed]
|
not_installed_plugins = [plugin for plugin in local_plugins if not plugin.installed]
|
||||||
# 本地插件来源目录中的插件
|
# 本地插件仓库目录中的插件
|
||||||
local_source_plugins = plugin_manager.get_local_source_plugins()
|
local_repo_plugins = plugin_manager.get_local_repo_plugins()
|
||||||
# 在线插件
|
# 在线插件
|
||||||
online_plugins = await plugin_manager.async_get_online_plugins(force)
|
online_plugins = await plugin_manager.async_get_online_plugins(force)
|
||||||
candidate_plugins = plugin_manager._process_plugins_list(online_plugins + local_source_plugins, []) \
|
candidate_plugins = plugin_manager._process_plugins_list(online_plugins + local_repo_plugins, []) \
|
||||||
if online_plugins or local_source_plugins else []
|
if online_plugins or local_repo_plugins else []
|
||||||
if not candidate_plugins:
|
if not candidate_plugins:
|
||||||
# 没有获取在线插件
|
# 没有获取在线插件
|
||||||
if state == "market":
|
if state == "market":
|
||||||
|
|||||||
+12
-12
@@ -312,9 +312,9 @@ class PluginManager(ConfigReloadMixin, metaclass=Singleton):
|
|||||||
"""
|
"""
|
||||||
# 监视插件目录
|
# 监视插件目录
|
||||||
plugin_paths = [str(settings.ROOT_PATH / "app" / "plugins")]
|
plugin_paths = [str(settings.ROOT_PATH / "app" / "plugins")]
|
||||||
for local_path in PluginHelper.get_local_source_paths():
|
for local_repo_path in PluginHelper.get_local_repo_paths():
|
||||||
if local_path.exists() and local_path.is_dir():
|
if local_repo_path.exists() and local_repo_path.is_dir():
|
||||||
plugin_paths.append(str(local_path))
|
plugin_paths.append(str(local_repo_path))
|
||||||
logger.info(">>> 监控线程已启动,准备进入watch循环...")
|
logger.info(">>> 监控线程已启动,准备进入watch循环...")
|
||||||
# 使用 watchfiles 监视目录变化,并响应变化事件
|
# 使用 watchfiles 监视目录变化,并响应变化事件
|
||||||
# Todo: yield_on_timeout = True 时,每秒检查停止事件,会返回空集合;后续可以考虑用来做心跳之类的功能?
|
# Todo: yield_on_timeout = True 时,每秒检查停止事件,会返回空集合;后续可以考虑用来做心跳之类的功能?
|
||||||
@@ -437,17 +437,17 @@ class PluginManager(ConfigReloadMixin, metaclass=Singleton):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_local_plugin_candidate_from_path(event_path: Path) -> Optional[dict]:
|
def _get_local_plugin_candidate_from_path(event_path: Path) -> Optional[dict]:
|
||||||
"""
|
"""
|
||||||
根据本地插件来源路径解析具体插件候选,保留 plugins/plugins.v2 来源差异。
|
根据本地插件仓库路径解析具体插件候选,保留 plugins/plugins.v2 来源差异。
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
event_path = event_path.resolve()
|
event_path = event_path.resolve()
|
||||||
for local_path in PluginHelper.get_local_source_paths():
|
for local_repo_path in PluginHelper.get_local_repo_paths():
|
||||||
if not local_path.exists() or not local_path.is_dir():
|
if not local_repo_path.exists() or not local_repo_path.is_dir():
|
||||||
continue
|
continue
|
||||||
if not event_path.is_relative_to(local_path):
|
if not event_path.is_relative_to(local_repo_path):
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
relative_parts = event_path.relative_to(local_path).parts
|
relative_parts = event_path.relative_to(local_repo_path).parts
|
||||||
except (ValueError, IndexError):
|
except (ValueError, IndexError):
|
||||||
continue
|
continue
|
||||||
if len(relative_parts) < 2:
|
if len(relative_parts) < 2:
|
||||||
@@ -462,14 +462,14 @@ class PluginManager(ConfigReloadMixin, metaclass=Singleton):
|
|||||||
candidate = PluginHelper().get_local_plugin_candidate(
|
candidate = PluginHelper().get_local_plugin_candidate(
|
||||||
pid=plugin_dir_name,
|
pid=plugin_dir_name,
|
||||||
package_version=package_version,
|
package_version=package_version,
|
||||||
source_path=local_path,
|
repo_path=local_repo_path,
|
||||||
strict_compat=False
|
strict_compat=False
|
||||||
)
|
)
|
||||||
if candidate:
|
if candidate:
|
||||||
return candidate
|
return candidate
|
||||||
return None
|
return None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"从本地来源路径解析插件候选时出错: {e}")
|
logger.error(f"从本地插件仓库路径解析插件候选时出错: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -1233,9 +1233,9 @@ class PluginManager(ConfigReloadMixin, metaclass=Singleton):
|
|||||||
plugins.sort(key=lambda x: x.plugin_order if hasattr(x, "plugin_order") else 0)
|
plugins.sort(key=lambda x: x.plugin_order if hasattr(x, "plugin_order") else 0)
|
||||||
return plugins
|
return plugins
|
||||||
|
|
||||||
def get_local_source_plugins(self) -> List[schemas.Plugin]:
|
def get_local_repo_plugins(self) -> List[schemas.Plugin]:
|
||||||
"""
|
"""
|
||||||
获取本地插件来源目录中的插件信息。
|
获取本地插件仓库目录中的插件信息。
|
||||||
"""
|
"""
|
||||||
plugins = []
|
plugins = []
|
||||||
installed_apps = SystemConfigOper().get(SystemConfigKey.UserInstalledPlugins) or []
|
installed_apps = SystemConfigOper().get(SystemConfigKey.UserInstalledPlugins) or []
|
||||||
|
|||||||
+26
-26
@@ -76,29 +76,29 @@ class PluginHelper(metaclass=WeakSingleton):
|
|||||||
return pid or None
|
return pid or None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_local_source_paths() -> List[Path]:
|
def get_local_repo_paths() -> List[Path]:
|
||||||
"""
|
"""
|
||||||
获取本地插件来源目录列表。
|
获取本地插件仓库目录列表。
|
||||||
"""
|
"""
|
||||||
if not settings.PLUGIN_LOCAL_REPO_PATHS:
|
if not settings.PLUGIN_LOCAL_REPO_PATHS:
|
||||||
return []
|
return []
|
||||||
paths = []
|
paths = []
|
||||||
for item in settings.PLUGIN_LOCAL_REPO_PATHS.split(","):
|
for item in settings.PLUGIN_LOCAL_REPO_PATHS.split(","):
|
||||||
local_path = item.strip()
|
local_repo_path = item.strip()
|
||||||
if not local_path:
|
if not local_repo_path:
|
||||||
continue
|
continue
|
||||||
path = Path(local_path).expanduser()
|
path = Path(local_repo_path).expanduser()
|
||||||
if not path.is_absolute():
|
if not path.is_absolute():
|
||||||
path = settings.ROOT_PATH / path
|
path = settings.ROOT_PATH / path
|
||||||
paths.append(path.resolve())
|
paths.append(path.resolve())
|
||||||
return paths
|
return paths
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __get_local_package(source_path: Path, package_version: Optional[str] = None) -> Optional[Dict[str, dict]]:
|
def __get_local_package(repo_path: Path, package_version: Optional[str] = None) -> Optional[Dict[str, dict]]:
|
||||||
"""
|
"""
|
||||||
从本地插件仓库读取 package.json 或 package.{version}.json。
|
从本地插件仓库读取 package.json 或 package.{version}.json。
|
||||||
"""
|
"""
|
||||||
package_file = source_path / (
|
package_file = repo_path / (
|
||||||
f"package.{package_version}.json" if package_version else "package.json"
|
f"package.{package_version}.json" if package_version else "package.json"
|
||||||
)
|
)
|
||||||
if not package_file.exists():
|
if not package_file.exists():
|
||||||
@@ -115,25 +115,25 @@ class PluginHelper(metaclass=WeakSingleton):
|
|||||||
return payload
|
return payload
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __get_local_plugin_dir(source_path: Path, pid: str, package_version: Optional[str]) -> Path:
|
def __get_local_plugin_dir(repo_path: Path, pid: str, package_version: Optional[str]) -> Path:
|
||||||
plugin_root = f"plugins.{package_version}" if package_version else "plugins"
|
plugin_root = f"plugins.{package_version}" if package_version else "plugins"
|
||||||
return source_path / plugin_root / pid.lower()
|
return repo_path / plugin_root / pid.lower()
|
||||||
|
|
||||||
def get_local_plugin_candidates(self) -> Dict[str, dict]:
|
def get_local_plugin_candidates(self) -> Dict[str, dict]:
|
||||||
"""
|
"""
|
||||||
扫描本地插件仓库,按插件ID保留版本号最高的候选。
|
扫描本地插件仓库,按插件ID保留版本号最高的候选。
|
||||||
"""
|
"""
|
||||||
candidates: Dict[str, dict] = {}
|
candidates: Dict[str, dict] = {}
|
||||||
for source_order, source_path in enumerate(self.get_local_source_paths()):
|
for source_order, repo_path in enumerate(self.get_local_repo_paths()):
|
||||||
if not source_path.exists() or not source_path.is_dir():
|
if not repo_path.exists() or not repo_path.is_dir():
|
||||||
logger.warning(f"本地插件来源目录不存在或不可读:{source_path}")
|
logger.warning(f"本地插件仓库目录不存在或不可读:{repo_path}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
package_candidates = []
|
package_candidates = []
|
||||||
if settings.VERSION_FLAG:
|
if settings.VERSION_FLAG:
|
||||||
package_candidates.append((settings.VERSION_FLAG, self.__get_local_package(source_path,
|
package_candidates.append((settings.VERSION_FLAG, self.__get_local_package(repo_path,
|
||||||
settings.VERSION_FLAG)))
|
settings.VERSION_FLAG)))
|
||||||
package_candidates.append(("", self.__get_local_package(source_path)))
|
package_candidates.append(("", self.__get_local_package(repo_path)))
|
||||||
|
|
||||||
for package_version, local_plugins in package_candidates:
|
for package_version, local_plugins in package_candidates:
|
||||||
if local_plugins is None:
|
if local_plugins is None:
|
||||||
@@ -149,7 +149,7 @@ class PluginHelper(metaclass=WeakSingleton):
|
|||||||
):
|
):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
plugin_dir = self.__get_local_plugin_dir(source_path, pid, package_version)
|
plugin_dir = self.__get_local_plugin_dir(repo_path, pid, package_version)
|
||||||
if not plugin_dir.is_dir():
|
if not plugin_dir.is_dir():
|
||||||
logger.debug(f"跳过本地插件 {pid}:插件目录不存在 {plugin_dir}")
|
logger.debug(f"跳过本地插件 {pid}:插件目录不存在 {plugin_dir}")
|
||||||
continue
|
continue
|
||||||
@@ -158,7 +158,7 @@ class PluginHelper(metaclass=WeakSingleton):
|
|||||||
candidate["id"] = pid
|
candidate["id"] = pid
|
||||||
candidate["package_version"] = package_version
|
candidate["package_version"] = package_version
|
||||||
candidate["source_order"] = source_order
|
candidate["source_order"] = source_order
|
||||||
candidate["source_path"] = source_path
|
candidate["repo_path"] = repo_path
|
||||||
candidate["path"] = plugin_dir
|
candidate["path"] = plugin_dir
|
||||||
candidate_version = str(candidate.get("version") or "0")
|
candidate_version = str(candidate.get("version") or "0")
|
||||||
|
|
||||||
@@ -174,25 +174,25 @@ class PluginHelper(metaclass=WeakSingleton):
|
|||||||
candidate_version == existing_version
|
candidate_version == existing_version
|
||||||
and source_order < int(existing.get("source_order", source_order))
|
and source_order < int(existing.get("source_order", source_order))
|
||||||
):
|
):
|
||||||
logger.info(f"本地插件 {pid} 存在同版本来源,使用靠前目录:{source_path}")
|
logger.info(f"本地插件 {pid} 存在同版本来源,使用靠前目录:{repo_path}")
|
||||||
candidates[pid] = candidate
|
candidates[pid] = candidate
|
||||||
|
|
||||||
return candidates
|
return candidates
|
||||||
|
|
||||||
def get_local_plugin_candidate(self, pid: str, package_version: Optional[str] = None,
|
def get_local_plugin_candidate(self, pid: str, package_version: Optional[str] = None,
|
||||||
source_path: Optional[Path] = None,
|
repo_path: Optional[Path] = None,
|
||||||
strict_compat: bool = True) -> Optional[dict]:
|
strict_compat: bool = True) -> Optional[dict]:
|
||||||
"""
|
"""
|
||||||
获取指定插件ID的本地插件候选。
|
获取指定插件ID的本地插件候选。
|
||||||
"""
|
"""
|
||||||
if not pid:
|
if not pid:
|
||||||
return None
|
return None
|
||||||
if package_version is not None or source_path is not None:
|
if package_version is not None or repo_path is not None:
|
||||||
source_paths = [source_path.resolve()] if source_path else self.get_local_source_paths()
|
repo_paths = [repo_path.resolve()] if repo_path else self.get_local_repo_paths()
|
||||||
for source_order, local_source_path in enumerate(self.get_local_source_paths()):
|
for source_order, local_repo_path in enumerate(self.get_local_repo_paths()):
|
||||||
if local_source_path not in source_paths:
|
if local_repo_path not in repo_paths:
|
||||||
continue
|
continue
|
||||||
local_plugins = self.__get_local_package(local_source_path, package_version or "")
|
local_plugins = self.__get_local_package(local_repo_path, package_version or "")
|
||||||
if not local_plugins:
|
if not local_plugins:
|
||||||
continue
|
continue
|
||||||
for candidate_pid, plugin_info in local_plugins.items():
|
for candidate_pid, plugin_info in local_plugins.items():
|
||||||
@@ -205,14 +205,14 @@ class PluginHelper(metaclass=WeakSingleton):
|
|||||||
)
|
)
|
||||||
if not is_compatible and strict_compat:
|
if not is_compatible and strict_compat:
|
||||||
return None
|
return None
|
||||||
plugin_dir = self.__get_local_plugin_dir(local_source_path, candidate_pid, package_version or "")
|
plugin_dir = self.__get_local_plugin_dir(local_repo_path, candidate_pid, package_version or "")
|
||||||
if not plugin_dir.is_dir():
|
if not plugin_dir.is_dir():
|
||||||
return None
|
return None
|
||||||
candidate = plugin_info.copy()
|
candidate = plugin_info.copy()
|
||||||
candidate["id"] = candidate_pid
|
candidate["id"] = candidate_pid
|
||||||
candidate["package_version"] = package_version or ""
|
candidate["package_version"] = package_version or ""
|
||||||
candidate["source_order"] = source_order
|
candidate["source_order"] = source_order
|
||||||
candidate["source_path"] = local_source_path
|
candidate["repo_path"] = local_repo_path
|
||||||
candidate["path"] = plugin_dir
|
candidate["path"] = plugin_dir
|
||||||
if not is_compatible:
|
if not is_compatible:
|
||||||
candidate["compatible"] = False
|
candidate["compatible"] = False
|
||||||
@@ -450,7 +450,7 @@ class PluginHelper(metaclass=WeakSingleton):
|
|||||||
|
|
||||||
def install_local(self, pid: str, repo_url: str = "", force_install: bool = False) -> Tuple[bool, str]:
|
def install_local(self, pid: str, repo_url: str = "", force_install: bool = False) -> Tuple[bool, str]:
|
||||||
"""
|
"""
|
||||||
从本地插件来源目录安装插件。
|
从本地插件仓库目录安装插件。
|
||||||
"""
|
"""
|
||||||
local_pid = self.parse_local_repo_url(repo_url) if repo_url else pid
|
local_pid = self.parse_local_repo_url(repo_url) if repo_url else pid
|
||||||
if not local_pid or local_pid.lower() != pid.lower():
|
if not local_pid or local_pid.lower() != pid.lower():
|
||||||
|
|||||||
Reference in New Issue
Block a user