mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-10-30 03:48:03 +08:00
120 lines
5.2 KiB
Python
120 lines
5.2 KiB
Python
"""插件分身的创建、恢复与可恢复清单接口。
|
|
|
|
这些路由并入 ``plugin`` 路由器,路径与单文件时期完全一致;单独成篇只是让分身创建与
|
|
插件目录、市场、静态资源等关注点各自分开,与 ``pluginfolder``/``pluginloglevel``/
|
|
``plugintarget`` 的切分方式一致。
|
|
"""
|
|
|
|
from typing import Any, List
|
|
|
|
from fastapi import Depends, Response
|
|
|
|
from app.api.dependencies.auth import get_current_active_superuser
|
|
from app.api.endpoints.plugin import register_plugin
|
|
from app.api.principal import ApiPrincipal
|
|
from app.api.response import (
|
|
COLLECTION_TOTAL_HEADER,
|
|
COLLECTION_TOTAL_OPENAPI_KEY,
|
|
CompatibleCountParam,
|
|
CompatiblePageParam,
|
|
ResponseAPIRouter,
|
|
resolve_compatible_pagination,
|
|
)
|
|
from app.application.plugin.folders import add_clone_to_plugin_folder
|
|
from app.application.plugin.runtime import get_plugin_manager
|
|
from app.runtime.log import logger
|
|
from app.schemas.plugin import PluginCloneOutcome as _SchemaPluginCloneOutcome
|
|
from app.schemas.plugin import PluginCloneRequest as _SchemaPluginCloneRequest
|
|
from app.schemas.plugin import PluginRestorableInstance as _SchemaPluginRestorableInstance
|
|
from app.schemas.response import Response as _SchemaResponse
|
|
|
|
router = ResponseAPIRouter()
|
|
|
|
|
|
@router.get( # type: ignore[misc]
|
|
"/clone/{plugin_id}/restorable",
|
|
summary="列出可恢复的已停用分身",
|
|
response_model=List[_SchemaPluginRestorableInstance],
|
|
openapi_extra={COLLECTION_TOTAL_OPENAPI_KEY: True},
|
|
)
|
|
def plugin_restorable_instances(
|
|
plugin_id: str,
|
|
_: ApiPrincipal = Depends(get_current_active_superuser),
|
|
page: CompatiblePageParam = None,
|
|
count: CompatibleCountParam = None,
|
|
response: Response = None,
|
|
) -> Any:
|
|
"""
|
|
列出该插件名下已停用、设置仍留存可被恢复的分身
|
|
|
|
停用只把启用位置假,业务参数与展示信息都还留在那一行上。启用中的分身不在此列
|
|
——它们的配置正被使用,摆进恢复选择器只会让人误以为能把一个活着的实例再建一遍。
|
|
|
|
未指定分页时返回完整清单:一个插件的历史分身数量有限,恢复选择器要一次看全。
|
|
"""
|
|
instances = [
|
|
_SchemaPluginRestorableInstance(**item)
|
|
for item in get_plugin_manager().get_restorable_plugin_instances(plugin_id)
|
|
]
|
|
if response is not None:
|
|
response.headers[COLLECTION_TOTAL_HEADER] = str(len(instances))
|
|
if page is not None or count is not None:
|
|
page, count = resolve_compatible_pagination(page, count)
|
|
assert page is not None and count is not None
|
|
return instances[(page - 1) * count: page * count]
|
|
return instances
|
|
|
|
|
|
@router.post( # type: ignore[misc]
|
|
"/clone/{plugin_id}",
|
|
summary="创建插件分身",
|
|
response_model=_SchemaResponse[_SchemaPluginCloneOutcome],
|
|
)
|
|
def clone_plugin(
|
|
plugin_id: str,
|
|
clone_data: _SchemaPluginCloneRequest,
|
|
_: ApiPrincipal = Depends(get_current_active_superuser),
|
|
) -> Any:
|
|
"""
|
|
创建插件分身
|
|
|
|
不填后缀时由服务端分配一个最小可用序号,因而实例 ID 只能由回执给出;该后缀名下
|
|
留有一个已停用的分身时,本次创建就是把那一行连同它的业务参数重新启用。
|
|
"""
|
|
plugin_manager = get_plugin_manager()
|
|
try:
|
|
with plugin_manager.mutation(f"创建插件 {plugin_id} 分身"):
|
|
success, message = plugin_manager.clone_plugin(
|
|
plugin_id=plugin_id,
|
|
suffix=clone_data.suffix,
|
|
name=clone_data.name,
|
|
description=clone_data.description,
|
|
version=clone_data.version,
|
|
icon=clone_data.icon,
|
|
restore_previous=clone_data.restore_previous,
|
|
)
|
|
if not success:
|
|
return _SchemaResponse(success=False, message=message)
|
|
# 分身此时已经创建并加载完成,后面只是补齐宿主注册。这一步失败不能报成
|
|
# 「创建失败」:分身确实已经存在,用户照提示重试只会撞上「分身已存在」,
|
|
# 真正的原因反而被那句话盖掉。
|
|
outcome = _SchemaPluginCloneOutcome(instance_id=message)
|
|
try:
|
|
register_plugin(message)
|
|
# 将分身插件添加到原插件所在的文件夹中
|
|
add_clone_to_plugin_folder(plugin_id, message)
|
|
except Exception as register_error: # noqa: BLE001
|
|
logger.error(f"插件分身 {message} 已创建,注册宿主能力失败:{register_error}")
|
|
return _SchemaResponse(
|
|
success=False,
|
|
message=(
|
|
f"插件分身 {message} 已创建,但注册定时任务或路由失败:"
|
|
f"{register_error};请检查该分身配置后重载插件"
|
|
),
|
|
data=outcome,
|
|
)
|
|
return _SchemaResponse(success=True, message="插件分身创建成功", data=outcome)
|
|
except Exception as e:
|
|
logger.error(f"创建插件分身失败:{str(e)}")
|
|
return _SchemaResponse(success=False, message=f"创建插件分身失败:{str(e)}")
|