fix actions

This commit is contained in:
jxxghp
2025-02-27 13:09:01 +08:00
parent 4a42b0d000
commit ab0008be86
16 changed files with 116 additions and 29 deletions
+6 -7
View File
@@ -1,7 +1,5 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from pydantic.main import BaseModel
from app.chain import ChainBase from app.chain import ChainBase
from app.schemas import ActionContext, ActionParams from app.schemas import ActionContext, ActionParams
@@ -10,7 +8,7 @@ class ActionChain(ChainBase):
pass pass
class BaseAction(BaseModel, ABC): class BaseAction(ABC):
""" """
工作流动作基类 工作流动作基类
""" """
@@ -18,10 +16,6 @@ class BaseAction(BaseModel, ABC):
# 完成标志 # 完成标志
_done_flag = False _done_flag = False
def __init__(self):
super().__init__()
self.chain = ActionChain()
@property @property
@abstractmethod @abstractmethod
def name(self) -> str: def name(self) -> str:
@@ -32,6 +26,11 @@ class BaseAction(BaseModel, ABC):
def description(self) -> str: def description(self) -> str:
pass pass
@property
@abstractmethod
def data(self) -> dict:
pass
@abstractmethod @abstractmethod
def execute(self, params: ActionParams, context: ActionContext) -> ActionContext: def execute(self, params: ActionParams, context: ActionContext) -> ActionContext:
""" """
+4
View File
@@ -37,6 +37,10 @@ class AddDownloadAction(BaseAction):
def description(self) -> str: def description(self) -> str:
return "根据资源列表添加下载任务" return "根据资源列表添加下载任务"
@property
def data(self) -> dict:
return AddDownloadParams().dict()
@property @property
def success(self) -> bool: def success(self) -> bool:
return True if self._added_downloads else False return True if self._added_downloads else False
+4
View File
@@ -33,6 +33,10 @@ class AddSubscribeAction(BaseAction):
def description(self) -> str: def description(self) -> str:
return "根据媒体列表添加订阅" return "根据媒体列表添加订阅"
@property
def data(self) -> dict:
return AddSubscribeParams().dict()
@property @property
def success(self) -> bool: def success(self) -> bool:
return True if self._added_subscribes else False return True if self._added_subscribes else False
+9 -1
View File
@@ -1,4 +1,4 @@
from app.actions import BaseAction from app.actions import BaseAction, ActionChain
from app.schemas import ActionParams, ActionContext from app.schemas import ActionParams, ActionContext
from app.log import logger from app.log import logger
@@ -17,6 +17,10 @@ class FetchDownloadsAction(BaseAction):
_downloads = [] _downloads = []
def __init__(self):
super().__init__()
self.chain = ActionChain()
@property @property
def name(self) -> str: def name(self) -> str:
return "获取下载任务" return "获取下载任务"
@@ -25,6 +29,10 @@ class FetchDownloadsAction(BaseAction):
def description(self) -> str: def description(self) -> str:
return "获取下载任务,更新任务状态" return "获取下载任务,更新任务状态"
@property
def data(self) -> dict:
return FetchDownloadsParams().dict()
@property @property
def success(self) -> bool: def success(self) -> bool:
if not self._downloads: if not self._downloads:
+4
View File
@@ -89,6 +89,10 @@ class FetchMediasAction(BaseAction):
def description(self) -> str: def description(self) -> str:
return "获取媒体数据" return "获取媒体数据"
@property
def data(self) -> dict:
return FetchMediasParams().dict()
@property @property
def success(self) -> bool: def success(self) -> bool:
return True if self.__medias else False return True if self.__medias else False
+6 -1
View File
@@ -2,7 +2,7 @@ from typing import Optional
from pydantic import Field from pydantic import Field
from app.actions import BaseAction from app.actions import BaseAction, ActionChain
from app.core.config import settings from app.core.config import settings
from app.core.context import Context from app.core.context import Context
from app.core.metainfo import MetaInfo from app.core.metainfo import MetaInfo
@@ -33,6 +33,7 @@ class FetchRssAction(BaseAction):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.rsshelper = RssHelper() self.rsshelper = RssHelper()
self.chain = ActionChain()
@property @property
def name(self) -> str: def name(self) -> str:
@@ -42,6 +43,10 @@ class FetchRssAction(BaseAction):
def description(self) -> str: def description(self) -> str:
return "请求RSS地址获取数据,并解析为资源列表" return "请求RSS地址获取数据,并解析为资源列表"
@property
def data(self) -> dict:
return FetchRssParams().dict()
@property @property
def success(self) -> bool: def success(self) -> bool:
return True if self._rss_torrents else False return True if self._rss_torrents else False
+5 -1
View File
@@ -38,6 +38,10 @@ class FetchTorrentsAction(BaseAction):
def description(self) -> str: def description(self) -> str:
return "根据关键字搜索站点种子资源" return "根据关键字搜索站点种子资源"
@property
def data(self) -> dict:
return FetchTorrentsParams().dict()
@property @property
def success(self) -> bool: def success(self) -> bool:
return True if self._torrents else False return True if self._torrents else False
@@ -55,7 +59,7 @@ class FetchTorrentsAction(BaseAction):
if params.season and torrent.meta_info.begin_season != params.season: if params.season and torrent.meta_info.begin_season != params.season:
continue continue
# 识别媒体信息 # 识别媒体信息
torrent.media_info = self.chain.recognize_media(torrent.meta_info) torrent.media_info = self.searchchain.recognize_media(torrent.meta_info)
if not torrent.media_info: if not torrent.media_info:
logger.warning(f"{torrent.torrent_info.title} 未识别到媒体信息") logger.warning(f"{torrent.torrent_info.title} 未识别到媒体信息")
continue continue
+4
View File
@@ -32,6 +32,10 @@ class FilterMediasAction(BaseAction):
def description(self) -> str: def description(self) -> str:
return "过滤媒体数据列表" return "过滤媒体数据列表"
@property
def data(self) -> dict:
return FilterMediasParams().dict()
@property @property
def success(self) -> bool: def success(self) -> bool:
return True if self.__medias else False return True if self.__medias else False
+6 -1
View File
@@ -2,7 +2,7 @@ from typing import Optional, List
from pydantic import Field from pydantic import Field
from app.actions import BaseAction from app.actions import BaseAction, ActionChain
from app.helper.torrent import TorrentHelper from app.helper.torrent import TorrentHelper
from app.schemas import ActionParams, ActionContext from app.schemas import ActionParams, ActionContext
@@ -30,6 +30,7 @@ class FilterTorrentsAction(BaseAction):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.torrenthelper = TorrentHelper() self.torrenthelper = TorrentHelper()
self.chain = ActionChain()
@property @property
def name(self) -> str: def name(self) -> str:
@@ -39,6 +40,10 @@ class FilterTorrentsAction(BaseAction):
def description(self) -> str: def description(self) -> str:
return "过滤资源数据列表" return "过滤资源数据列表"
@property
def data(self) -> dict:
return FilterTorrentsParams().dict()
@property @property
def success(self) -> bool: def success(self) -> bool:
return self.done return self.done
+8 -2
View File
@@ -1,3 +1,5 @@
from pathlib import Path
from app.actions import BaseAction from app.actions import BaseAction
from app.schemas import ActionParams, ActionContext from app.schemas import ActionParams, ActionContext
from app.chain.media import MediaChain from app.chain.media import MediaChain
@@ -33,6 +35,10 @@ class ScrapeFileAction(BaseAction):
def description(self) -> str: def description(self) -> str:
return "刮削媒体信息和图片" return "刮削媒体信息和图片"
@property
def data(self) -> dict:
return ScrapeFileParams().dict()
@property @property
def success(self) -> bool: def success(self) -> bool:
return True if self.__scraped_files else False return True if self.__scraped_files else False
@@ -46,8 +52,8 @@ class ScrapeFileAction(BaseAction):
continue continue
if not self.storagechain.exists(fileitem): if not self.storagechain.exists(fileitem):
continue continue
meta = MetaInfoPath(fileitem.path) meta = MetaInfoPath(Path(fileitem.path))
mediainfo = self.chain.recognize_media(meta) mediainfo = self.mediachain.recognize_media(meta)
if not mediainfo: if not mediainfo:
logger.info(f"{fileitem.path} 未识别到媒体信息,无法刮削") logger.info(f"{fileitem.path} 未识别到媒体信息,无法刮削")
continue continue
+4
View File
@@ -27,6 +27,10 @@ class SendEventAction(BaseAction):
def description(self) -> str: def description(self) -> str:
return "发送特定事件" return "发送特定事件"
@property
def data(self) -> dict:
return SendEventParams().dict()
@property @property
def success(self) -> bool: def success(self) -> bool:
return self.__success return self.__success
+9 -1
View File
@@ -2,7 +2,7 @@ from typing import List, Optional, Union
from pydantic import Field from pydantic import Field
from app.actions import BaseAction from app.actions import BaseAction, ActionChain
from app.schemas import ActionParams, ActionContext, MessageChannel from app.schemas import ActionParams, ActionContext, MessageChannel
@@ -19,6 +19,10 @@ class SendMessageAction(BaseAction):
发送消息 发送消息
""" """
def __init__(self):
super().__init__()
self.chain = ActionChain()
@property @property
def name(self) -> str: def name(self) -> str:
return "发送消息" return "发送消息"
@@ -27,6 +31,10 @@ class SendMessageAction(BaseAction):
def description(self) -> str: def description(self) -> str:
return "发送特定消息" return "发送特定消息"
@property
def data(self) -> dict:
return SendMessageParams().dict()
@property @property
def success(self) -> bool: def success(self) -> bool:
return self.done return self.done
+7 -1
View File
@@ -1,3 +1,5 @@
from pathlib import Path
from app.actions import BaseAction from app.actions import BaseAction
from app.schemas import ActionParams, ActionContext from app.schemas import ActionParams, ActionContext
from app.chain.storage import StorageChain from app.chain.storage import StorageChain
@@ -32,6 +34,10 @@ class TransferFileAction(BaseAction):
def description(self) -> str: def description(self) -> str:
return "整理和转移文件" return "整理和转移文件"
@property
def data(self) -> dict:
return TransferFileParams().dict()
@property @property
def success(self) -> bool: def success(self) -> bool:
return True if self.__fileitems else False return True if self.__fileitems else False
@@ -44,7 +50,7 @@ class TransferFileAction(BaseAction):
if not download.completed: if not download.completed:
logger.info(f"下载任务 {download.download_id} 未完成") logger.info(f"下载任务 {download.download_id} 未完成")
continue continue
fileitem = self.storagechain.get_file_item(storage="local", path=download.path) fileitem = self.storagechain.get_file_item(storage="local", path=Path(download.path))
if not fileitem: if not fileitem:
logger.info(f"文件 {download.path} 不存在") logger.info(f"文件 {download.path} 不存在")
continue continue
+9
View File
@@ -5,6 +5,7 @@ from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from app import schemas from app import schemas
from app.core.workflow import WorkFlowManager
from app.db import get_db from app.db import get_db
from app.db.models.workflow import Workflow from app.db.models.workflow import Workflow
from app.db.user_oper import get_current_active_user from app.db.user_oper import get_current_active_user
@@ -40,6 +41,14 @@ def create_workflow(workflow: schemas.Workflow,
return schemas.Response(success=True, message="创建工作流成功") return schemas.Response(success=True, message="创建工作流成功")
@router.get("/actions", summary="所有动作", response_model=List[dict])
def list_actions(_: schemas.TokenPayload = Depends(get_current_active_user)) -> Any:
"""
获取所有动作
"""
return WorkFlowManager().list_actions()
@router.get("/{workflow_id}", summary="工作流详情", response_model=schemas.Workflow) @router.get("/{workflow_id}", summary="工作流详情", response_model=schemas.Workflow)
def get_workflow(workflow_id: int, def get_workflow(workflow_id: int,
db: Session = Depends(get_db), db: Session = Depends(get_db),
+27 -8
View File
@@ -1,5 +1,5 @@
from time import sleep from time import sleep
from typing import Dict, Any, Tuple from typing import Dict, Any, Tuple, List
from app.helper.module import ModuleHelper from app.helper.module import ModuleHelper
from app.log import logger from app.log import logger
@@ -43,7 +43,10 @@ class WorkFlowManager(metaclass=Singleton):
) )
for action in actions: for action in actions:
logger.debug(f"加载动作: {action.__name__}") logger.debug(f"加载动作: {action.__name__}")
self._actions[action.__name__] = action try:
self._actions[action.__name__] = action()
except Exception as err:
logger.error(f"加载动作失败: {action.__name__} - {err}")
def stop(self): def stop(self):
""" """
@@ -59,22 +62,22 @@ class WorkFlowManager(metaclass=Singleton):
context = ActionContext() context = ActionContext()
if action.type in self._actions: if action.type in self._actions:
# 实例化 # 实例化
action_obj = self._actions[action.type]() action_obj = self._actions[action.type]
# 执行 # 执行
logger.info(f"执行动作: {action.id} - {action.name}") logger.info(f"执行动作: {action.id} - {action.name}")
result_context = action_obj.execute(action.params, context) result_context = action_obj.execute(action.data, context)
if action_obj.success: if action_obj.success:
logger.info(f"{action.name} 执行成功") logger.info(f"{action.name} 执行成功")
else: else:
logger.error(f"{action.name} 执行失败") logger.error(f"{action.name} 执行失败")
if action.loop and action.loop_interval: if action.data.loop and action.data.loop_interval:
while not action_obj.done: while not action_obj.done:
# 等待 # 等待
logger.info(f"{action.name} 等待 {action.loop_interval} 秒后继续执行 ...") logger.info(f"{action.name} 等待 {action.data.loop_interval} 秒后继续执行 ...")
sleep(action.loop_interval) sleep(action.data.loop_interval)
# 执行 # 执行
logger.info(f"继续执行动作: {action.id} - {action.name}") logger.info(f"继续执行动作: {action.id} - {action.name}")
result_context = action_obj.execute(action.params, result_context) result_context = action_obj.execute(action.data, result_context)
if action_obj.success: if action_obj.success:
logger.info(f"{action.name} 执行成功") logger.info(f"{action.name} 执行成功")
else: else:
@@ -84,3 +87,19 @@ class WorkFlowManager(metaclass=Singleton):
else: else:
logger.error(f"未找到动作: {action.type} - {action.name}") logger.error(f"未找到动作: {action.type} - {action.name}")
return False, context return False, context
def list_actions(self) -> List[dict]:
"""
获取所有动作
"""
return [
{
"type": key,
"name": action.name,
"description": action.description,
"data": {
"label": action.name,
**action.data
}
} for key, action in self._actions.items()
]
+4 -6
View File
@@ -36,7 +36,8 @@ class ActionParams(BaseModel):
""" """
动作基础参数 动作基础参数
""" """
pass loop: Optional[bool] = Field(False, description="是否需要循环")
loop_interval: Optional[int] = Field(0, description="循环间隔 (秒)")
class Action(BaseModel): class Action(BaseModel):
@@ -47,10 +48,7 @@ class Action(BaseModel):
type: Optional[str] = Field(None, description="动作类型 (类名)") type: Optional[str] = Field(None, description="动作类型 (类名)")
name: Optional[str] = Field(None, description="动作名称") name: Optional[str] = Field(None, description="动作名称")
description: Optional[str] = Field(None, description="动作描述") description: Optional[str] = Field(None, description="动作描述")
loop: Optional[bool] = Field(False, description="是否需要循环") data: Optional[ActionParams] = Field({}, description="参数")
loop_interval: Optional[int] = Field(0, description="循环间隔 (秒)")
params: Optional[ActionParams] = Field({}, description="参数")
label: Optional[str] = Field(None, description="标签")
position: Optional[dict] = Field({}, description="位置") position: Optional[dict] = Field({}, description="位置")
@@ -76,4 +74,4 @@ class ActionFlow(BaseModel):
id: Optional[str] = Field(None, description="流程ID") id: Optional[str] = Field(None, description="流程ID")
source: Optional[str] = Field(None, description="源动作") source: Optional[str] = Field(None, description="源动作")
target: Optional[str] = Field(None, description="目标动作") target: Optional[str] = Field(None, description="目标动作")
animated: Optional[bool] = Field(False, description="是否动画流程") animated: Optional[bool] = Field(True, description="是否动画流程")