mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-06 07:56:52 +08:00
feat(plugin): add centralized rating APIs
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import asyncio
|
||||
import mimetypes
|
||||
import shutil
|
||||
from typing import Annotated, Any, List, Optional
|
||||
from typing import Annotated, Any, Dict, List, Optional
|
||||
|
||||
import aiofiles
|
||||
from anyio import Path as AsyncPath
|
||||
@@ -476,6 +476,71 @@ async def statistic(_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||
return await MoviePilotServerHelper.async_get_plugin_statistic()
|
||||
|
||||
|
||||
@router.get(
|
||||
"/rating",
|
||||
summary="批量查询插件评分",
|
||||
response_model=Dict[str, schemas.PluginRating],
|
||||
)
|
||||
async def plugin_ratings(
|
||||
plugin_ids: Optional[str] = None,
|
||||
_: User = Depends(get_current_active_superuser_async),
|
||||
) -> Dict[str, schemas.PluginRating]:
|
||||
"""
|
||||
批量查询插件平均分、评分人数和当前安装实例评分。
|
||||
"""
|
||||
requested_ids = plugin_ids.split(",") if plugin_ids is not None else None
|
||||
ratings = await MoviePilotServerHelper.async_get_plugin_ratings(requested_ids)
|
||||
return {
|
||||
plugin_id: schemas.PluginRating.model_validate(rating)
|
||||
for plugin_id, rating in ratings.items()
|
||||
}
|
||||
|
||||
|
||||
@router.get(
|
||||
"/rating/{plugin_id}",
|
||||
summary="查询插件评分",
|
||||
response_model=schemas.PluginRating,
|
||||
)
|
||||
async def plugin_rating(
|
||||
plugin_id: str,
|
||||
_: User = Depends(get_current_active_superuser_async),
|
||||
) -> schemas.PluginRating:
|
||||
"""
|
||||
查询单个插件平均分、评分人数和当前安装实例评分。
|
||||
"""
|
||||
rating = await MoviePilotServerHelper.async_get_plugin_rating(plugin_id)
|
||||
return schemas.PluginRating.model_validate(rating)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/rating/{plugin_id}",
|
||||
summary="提交插件评分",
|
||||
response_model=schemas.Response,
|
||||
)
|
||||
async def rate_plugin(
|
||||
plugin_id: str,
|
||||
payload: schemas.PluginRatingRequest,
|
||||
_: User = Depends(get_current_active_superuser_async),
|
||||
) -> schemas.Response:
|
||||
"""
|
||||
为已安装插件新增或更新当前安装实例评分。
|
||||
"""
|
||||
installed_plugins = SystemConfigOper().get(SystemConfigKey.UserInstalledPlugins) or []
|
||||
if plugin_id not in installed_plugins:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=f"插件 {plugin_id} 未安装,无法评分",
|
||||
)
|
||||
|
||||
rating = await MoviePilotServerHelper.async_submit_plugin_rating(
|
||||
plugin_id,
|
||||
payload.rating,
|
||||
)
|
||||
if rating is None:
|
||||
return schemas.Response(success=False, message="连接MoviePilot服务器失败")
|
||||
return schemas.Response(success=True, data=rating)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/reload/{plugin_id}", summary="重新加载插件", response_model=schemas.Response
|
||||
)
|
||||
|
||||
@@ -29,6 +29,7 @@ class MoviePilotServerHelper:
|
||||
_USAGE_REPORT_PATH = "/usage/report"
|
||||
_USAGE_STATISTIC_PATH = "/usage/statistic"
|
||||
_PLUGIN_INSTALL_PATH = "/plugin/install"
|
||||
_PLUGIN_RATING_PATH = "/plugin/rating"
|
||||
_PLUGIN_STATISTIC_PATH = "/plugin/statistic"
|
||||
_SUBSCRIBE_ADD_PATH = "/subscribe/add"
|
||||
_SUBSCRIBE_DONE_PATH = "/subscribe/done"
|
||||
@@ -398,6 +399,39 @@ class MoviePilotServerHelper:
|
||||
"""
|
||||
return await cls._async_get(cls._server_url(cls._PLUGIN_STATISTIC_PATH), timeout=10)
|
||||
|
||||
@classmethod
|
||||
async def async_plugin_ratings(cls, plugin_ids: Optional[List[str]] = None):
|
||||
"""
|
||||
异步批量查询中心端插件评分。
|
||||
"""
|
||||
params = {"plugin_ids": ",".join(plugin_ids)} if plugin_ids is not None else None
|
||||
return await cls._async_get(
|
||||
cls._server_url(cls._PLUGIN_RATING_PATH),
|
||||
params=params,
|
||||
timeout=10,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def async_plugin_rating(cls, plugin_id: str):
|
||||
"""
|
||||
异步查询中心端单个插件评分。
|
||||
"""
|
||||
return await cls._async_get(
|
||||
f"{cls._server_url(cls._PLUGIN_RATING_PATH)}/{quote(plugin_id, safe='')}",
|
||||
timeout=10,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def async_rate_plugin(cls, plugin_id: str, rating: float):
|
||||
"""
|
||||
异步提交当前安装实例的插件评分。
|
||||
"""
|
||||
return await cls._async_post_json(
|
||||
f"{cls._server_url(cls._PLUGIN_RATING_PATH)}/{quote(plugin_id, safe='')}",
|
||||
{"rating": rating},
|
||||
timeout=10,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def plugin_install(cls, plugin_id: str, payload: Dict[str, Any]):
|
||||
"""
|
||||
@@ -459,6 +493,58 @@ class MoviePilotServerHelper:
|
||||
return res.json()
|
||||
return {}
|
||||
|
||||
@classmethod
|
||||
async def async_get_plugin_ratings(
|
||||
cls,
|
||||
plugin_ids: Optional[List[str]] = None,
|
||||
) -> Dict[str, Dict[str, Any]]:
|
||||
"""
|
||||
批量获取插件评分,中心端不可用时返回空结果。
|
||||
"""
|
||||
try:
|
||||
res = await cls.async_plugin_ratings(plugin_ids)
|
||||
if res is not None and res.status_code == 200:
|
||||
return res.json()
|
||||
except Exception as err:
|
||||
logger.debug(f"批量获取插件评分失败:{str(err)}")
|
||||
return {}
|
||||
|
||||
@classmethod
|
||||
async def async_get_plugin_rating(cls, plugin_id: str) -> Dict[str, Any]:
|
||||
"""
|
||||
获取单个插件评分,中心端不可用时返回零评分。
|
||||
"""
|
||||
empty_rating = {
|
||||
"plugin_id": plugin_id,
|
||||
"average_rating": 0.0,
|
||||
"rating_count": 0,
|
||||
"user_rating": None,
|
||||
}
|
||||
try:
|
||||
res = await cls.async_plugin_rating(plugin_id)
|
||||
if res is not None and res.status_code == 200:
|
||||
return res.json()
|
||||
except Exception as err:
|
||||
logger.debug(f"获取插件 {plugin_id} 评分失败:{str(err)}")
|
||||
return empty_rating
|
||||
|
||||
@classmethod
|
||||
async def async_submit_plugin_rating(
|
||||
cls,
|
||||
plugin_id: str,
|
||||
rating: float,
|
||||
) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
提交插件评分,成功时返回最新评分结果。
|
||||
"""
|
||||
try:
|
||||
res = await cls.async_rate_plugin(plugin_id, rating)
|
||||
if res is not None and res.status_code == 200:
|
||||
return res.json()
|
||||
except Exception as err:
|
||||
logger.debug(f"提交插件 {plugin_id} 评分失败:{str(err)}")
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def install_plugin_reg(cls, plugin_id: str, repo_url: Optional[str] = None) -> bool:
|
||||
"""
|
||||
|
||||
@@ -95,6 +95,26 @@ class PluginSidebarNavItem(BaseModel):
|
||||
order: int = Field(default=0, description="同组内排序,越小越靠前")
|
||||
|
||||
|
||||
class PluginRatingRequest(BaseModel):
|
||||
"""插件评分请求"""
|
||||
|
||||
rating: float = Field(
|
||||
ge=0.1,
|
||||
le=5.0,
|
||||
multiple_of=0.1,
|
||||
description="评分,范围 0.1 至 5.0,精确到 0.1",
|
||||
)
|
||||
|
||||
|
||||
class PluginRating(BaseModel):
|
||||
"""插件评分结果"""
|
||||
|
||||
plugin_id: str = Field(description="插件 ID")
|
||||
average_rating: float = Field(default=0.0, description="平均评分")
|
||||
rating_count: int = Field(default=0, description="评分人数")
|
||||
user_rating: Optional[float] = Field(default=None, description="当前安装实例评分")
|
||||
|
||||
|
||||
class PluginMemoryInfo(BaseModel):
|
||||
"""插件内存信息"""
|
||||
plugin_id: str = Field(description="插件ID")
|
||||
|
||||
Reference in New Issue
Block a user