mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-07-23 05:27:17 +08:00
feat: add plugin history endpoint to fetch remote update details
This commit is contained in:
@@ -146,6 +146,62 @@ def register_plugin(plugin_id: str):
|
||||
register_plugin_api(plugin_id)
|
||||
|
||||
|
||||
def _merge_plugin_market_metadata(
|
||||
plugin: schemas.Plugin, market_plugin: schemas.Plugin
|
||||
) -> schemas.Plugin:
|
||||
"""
|
||||
合并插件市场中的远端元数据,供已安装插件按需展示更新说明。
|
||||
"""
|
||||
plugin.repo_url = market_plugin.repo_url or plugin.repo_url
|
||||
plugin.history = market_plugin.history or {}
|
||||
plugin.has_update = market_plugin.has_update
|
||||
plugin.system_version = market_plugin.system_version or plugin.system_version
|
||||
plugin.system_version_compatible = market_plugin.system_version_compatible
|
||||
plugin.system_version_message = (
|
||||
market_plugin.system_version_message or plugin.system_version_message
|
||||
)
|
||||
return plugin
|
||||
|
||||
|
||||
async def _get_plugin_history_detail(
|
||||
plugin_id: str, force: bool = True
|
||||
) -> Optional[schemas.Plugin]:
|
||||
"""
|
||||
按需获取插件远端元数据,避免插件列表加载时批量访问网络。
|
||||
"""
|
||||
plugin_manager = PluginManager()
|
||||
installed_plugin = next(
|
||||
(
|
||||
plugin
|
||||
for plugin in plugin_manager.get_local_plugins()
|
||||
if plugin.id == plugin_id and plugin.installed
|
||||
),
|
||||
None,
|
||||
)
|
||||
if not installed_plugin:
|
||||
return None
|
||||
|
||||
local_repo_plugin = next(
|
||||
(plugin for plugin in plugin_manager.get_local_repo_plugins() if plugin.id == plugin_id),
|
||||
None,
|
||||
)
|
||||
if local_repo_plugin:
|
||||
return _merge_plugin_market_metadata(installed_plugin, local_repo_plugin)
|
||||
|
||||
market_plugin = next(
|
||||
(
|
||||
plugin
|
||||
for plugin in await plugin_manager.async_get_online_plugins(force=force)
|
||||
if plugin.id == plugin_id
|
||||
),
|
||||
None,
|
||||
)
|
||||
if not market_plugin:
|
||||
return installed_plugin
|
||||
|
||||
return _merge_plugin_market_metadata(installed_plugin, market_plugin)
|
||||
|
||||
|
||||
@router.get("/", summary="所有插件", response_model=List[schemas.Plugin])
|
||||
async def all_plugins(
|
||||
_: User = Depends(get_current_active_superuser_async),
|
||||
@@ -213,6 +269,24 @@ async def installed(_: User = Depends(get_current_active_superuser_async)) -> An
|
||||
return SystemConfigOper().get(SystemConfigKey.UserInstalledPlugins) or []
|
||||
|
||||
|
||||
@router.get("/history/{plugin_id}", summary="获取插件更新说明", response_model=schemas.Plugin)
|
||||
async def plugin_history(
|
||||
plugin_id: str,
|
||||
_: User = Depends(get_current_active_superuser_async),
|
||||
force: bool = True,
|
||||
) -> schemas.Plugin:
|
||||
"""
|
||||
按需获取指定插件的更新说明。
|
||||
"""
|
||||
plugin = await _get_plugin_history_detail(plugin_id=plugin_id, force=force)
|
||||
if not plugin:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"插件 {plugin_id} 不存在或未安装",
|
||||
)
|
||||
return plugin
|
||||
|
||||
|
||||
@router.get("/statistic", summary="插件安装统计", response_model=dict)
|
||||
async def statistic(_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||
"""
|
||||
|
||||
@@ -74,6 +74,12 @@ MoviePilot 实现了标准的 **Model Context Protocol (MCP)**,允许 AI 智
|
||||
## 6. RESTful API
|
||||
所有工具相关的API端点都在 `/api/v1/mcp` 路径下(保持向后兼容)。
|
||||
|
||||
### 插件补充接口
|
||||
|
||||
**GET** `/api/v1/plugin/history/{plugin_id}`
|
||||
|
||||
按需读取指定已安装插件的最新远端更新说明。该接口用于前端在用户点击“查看更新说明”时再实时访问插件仓库,避免加载已安装插件列表时批量请求网络。
|
||||
|
||||
### 1. 列出所有工具
|
||||
|
||||
**GET** `/api/v1/mcp/tools`
|
||||
|
||||
71
tests/test_plugin_endpoint.py
Normal file
71
tests/test_plugin_endpoint.py
Normal file
@@ -0,0 +1,71 @@
|
||||
import asyncio
|
||||
from unittest import TestCase
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
|
||||
class PluginEndpointTest(TestCase):
|
||||
|
||||
def test_plugin_history_merges_remote_metadata(self):
|
||||
"""
|
||||
已安装插件点击更新说明时,接口会按需合并远端仓库中的更新记录。
|
||||
"""
|
||||
try:
|
||||
from app import schemas
|
||||
from app.api.endpoints.plugin import plugin_history
|
||||
except ModuleNotFoundError as exc:
|
||||
self.skipTest(f"missing dependency: {exc}")
|
||||
|
||||
installed_plugin = schemas.Plugin(
|
||||
id="DemoPlugin",
|
||||
plugin_name="Demo Plugin",
|
||||
plugin_version="1.0.0",
|
||||
installed=True,
|
||||
history={},
|
||||
)
|
||||
market_plugin = schemas.Plugin(
|
||||
id="DemoPlugin",
|
||||
repo_url="https://github.com/demo/plugins",
|
||||
history={"v1.1.0": "- 新增更新说明"},
|
||||
system_version=">=2.0.0",
|
||||
system_version_compatible=True,
|
||||
has_update=True,
|
||||
)
|
||||
plugin_manager = MagicMock()
|
||||
plugin_manager.get_local_plugins.return_value = [installed_plugin]
|
||||
plugin_manager.get_local_repo_plugins.return_value = []
|
||||
plugin_manager.async_get_online_plugins = AsyncMock(return_value=[market_plugin])
|
||||
|
||||
with patch("app.api.endpoints.plugin.PluginManager", return_value=plugin_manager):
|
||||
result = asyncio.run(plugin_history("DemoPlugin", None, True))
|
||||
|
||||
self.assertEqual("https://github.com/demo/plugins", result.repo_url)
|
||||
self.assertEqual({"v1.1.0": "- 新增更新说明"}, result.history)
|
||||
self.assertEqual(">=2.0.0", result.system_version)
|
||||
self.assertTrue(result.has_update)
|
||||
|
||||
def test_plugin_history_returns_installed_plugin_when_remote_missing(self):
|
||||
"""
|
||||
远端仓库不可用时,接口仍返回本地已安装插件信息,前端可继续展示兜底状态。
|
||||
"""
|
||||
try:
|
||||
from app import schemas
|
||||
from app.api.endpoints.plugin import plugin_history
|
||||
except ModuleNotFoundError as exc:
|
||||
self.skipTest(f"missing dependency: {exc}")
|
||||
|
||||
installed_plugin = schemas.Plugin(
|
||||
id="DemoPlugin",
|
||||
plugin_name="Demo Plugin",
|
||||
plugin_version="1.0.0",
|
||||
installed=True,
|
||||
)
|
||||
plugin_manager = MagicMock()
|
||||
plugin_manager.get_local_plugins.return_value = [installed_plugin]
|
||||
plugin_manager.get_local_repo_plugins.return_value = []
|
||||
plugin_manager.async_get_online_plugins = AsyncMock(return_value=[])
|
||||
|
||||
with patch("app.api.endpoints.plugin.PluginManager", return_value=plugin_manager):
|
||||
result = asyncio.run(plugin_history("DemoPlugin", None, True))
|
||||
|
||||
self.assertEqual("DemoPlugin", result.id)
|
||||
self.assertEqual({}, result.history)
|
||||
Reference in New Issue
Block a user