Files
MoviePilot/app/helper/workflow.py
2025-08-20 17:35:32 +08:00

277 lines
9.4 KiB
Python

import json
from typing import List, Tuple, Optional
from app.core.cache import cached
from app.core.config import settings
from app.db.models import Workflow
from app.db.workflow_oper import WorkflowOper
from app.log import logger
from app.utils.http import RequestUtils, AsyncRequestUtils
from app.utils.singleton import WeakSingleton
from app.utils.system import SystemUtils
class WorkflowHelper(metaclass=WeakSingleton):
"""
工作流分享等
"""
_workflow_share = f"{settings.MP_SERVER_HOST}/workflow/share"
_workflow_shares = f"{settings.MP_SERVER_HOST}/workflow/shares"
_workflow_fork = f"{settings.MP_SERVER_HOST}/workflow/fork/%s"
_shares_cache_region = "workflow_share"
_share_user_id = None
def __init__(self):
self.get_user_uuid()
@staticmethod
def _check_workflow_share_enabled() -> Tuple[bool, str]:
"""
检查工作流分享功能是否开启
"""
if not settings.WORKFLOW_STATISTIC_SHARE:
return False, "当前没有开启工作流数据共享功能"
return True, ""
@staticmethod
def _validate_workflow(workflow: Workflow) -> Tuple[bool, str]:
"""
验证工作流是否可以分享
"""
if not workflow:
return False, "工作流不存在"
if not workflow.actions or not workflow.flows:
return False, "请分享有动作和流程的工作流"
return True, ""
@staticmethod
def _prepare_workflow_data(workflow: Workflow) -> dict:
"""
准备工作流分享数据
"""
workflow_dict = workflow.to_dict()
workflow_dict.pop("id", None)
workflow_dict.pop("context", None)
workflow_dict['actions'] = json.dumps(workflow_dict['actions'] or [])
workflow_dict['flows'] = json.dumps(workflow_dict['flows'] or [])
return workflow_dict
def _build_share_payload(self, share_title: str, share_comment: str,
share_user: str, workflow_dict: dict) -> dict:
"""
构建分享请求载荷
"""
return {
"share_title": share_title,
"share_comment": share_comment,
"share_user": share_user,
"share_uid": self._share_user_id,
**workflow_dict
}
def _handle_response(self, res, clear_cache: bool = True) -> Tuple[bool, str]:
"""
处理HTTP响应
"""
if res is None:
return False, "连接MoviePilot服务器失败"
# 检查响应状态
success = True if res.status_code == 200 else False
if success:
# 清除缓存
if clear_cache:
self.get_shares.cache_clear()
self.async_get_shares.cache_clear()
return True, ""
else:
try:
error_msg = res.json().get("message", "未知错误")
except (json.JSONDecodeError, ValueError) as e:
logger.error(f"工作流响应JSON解析失败: {e}")
error_msg = f"响应解析失败: {res.text[:100]}..."
return False, error_msg
@staticmethod
def _handle_list_response(res) -> List[dict]:
"""
处理返回List的HTTP响应
"""
if res and res.status_code == 200:
try:
return res.json()
except (json.JSONDecodeError, ValueError) as e:
logger.error(f"工作流列表响应JSON解析失败: {e}")
return []
return []
def workflow_share(self, workflow_id: int,
share_title: str, share_comment: str, share_user: str) -> Tuple[bool, str]:
"""
分享工作流
"""
# 检查功能是否开启
enabled, message = self._check_workflow_share_enabled()
if not enabled:
return False, message
# 获取工作流信息
workflow = WorkflowOper().get(workflow_id)
# 验证工作流
valid, message = self._validate_workflow(workflow)
if not valid:
return False, message
# 准备数据
workflow_dict = self._prepare_workflow_data(workflow)
payload = self._build_share_payload(share_title, share_comment, share_user, workflow_dict)
# 发送分享请求
res = RequestUtils(proxies=settings.PROXY or {},
content_type="application/json",
timeout=10).post(self._workflow_share, json=payload)
return self._handle_response(res)
async def async_workflow_share(self, workflow_id: int,
share_title: str, share_comment: str, share_user: str) -> Tuple[bool, str]:
"""
异步分享工作流
"""
# 检查功能是否开启
enabled, message = self._check_workflow_share_enabled()
if not enabled:
return False, message
# 获取工作流信息
workflow = await WorkflowOper().async_get(workflow_id)
# 验证工作流
valid, message = self._validate_workflow(workflow)
if not valid:
return False, message
# 准备数据
workflow_dict = self._prepare_workflow_data(workflow)
payload = self._build_share_payload(share_title, share_comment, share_user, workflow_dict)
# 发送分享请求
res = await AsyncRequestUtils(proxies=settings.PROXY or {},
content_type="application/json",
timeout=10).post(self._workflow_share, json=payload)
return self._handle_response(res)
def share_delete(self, share_id: int) -> Tuple[bool, str]:
"""
删除分享
"""
# 检查功能是否开启
enabled, message = self._check_workflow_share_enabled()
if not enabled:
return False, message
res = RequestUtils(proxies=settings.PROXY or {},
timeout=5).delete_res(f"{self._workflow_share}/{share_id}",
params={"share_uid": self._share_user_id})
return self._handle_response(res)
async def async_share_delete(self, share_id: int) -> Tuple[bool, str]:
"""
异步删除分享
"""
# 检查功能是否开启
enabled, message = self._check_workflow_share_enabled()
if not enabled:
return False, message
res = await AsyncRequestUtils(proxies=settings.PROXY or {},
timeout=5).delete_res(f"{self._workflow_share}/{share_id}",
params={"share_uid": self._share_user_id})
return self._handle_response(res)
def workflow_fork(self, share_id: int) -> Tuple[bool, str]:
"""
复用分享的工作流
"""
# 检查功能是否开启
enabled, message = self._check_workflow_share_enabled()
if not enabled:
return False, message
res = RequestUtils(proxies=settings.PROXY or {}, timeout=5, headers={
"Content-Type": "application/json"
}).get_res(self._workflow_fork % share_id)
return self._handle_response(res, clear_cache=False)
async def async_workflow_fork(self, share_id: int) -> Tuple[bool, str]:
"""
异步复用分享的工作流
"""
# 检查功能是否开启
enabled, message = self._check_workflow_share_enabled()
if not enabled:
return False, message
res = await AsyncRequestUtils(proxies=settings.PROXY or {},
timeout=5,
headers={
"Content-Type": "application/json"
}).get_res(self._workflow_fork % share_id)
return self._handle_response(res, clear_cache=False)
@cached(region=_shares_cache_region, maxsize=1, skip_empty=True)
def get_shares(self, name: Optional[str] = None, page: Optional[int] = 1, count: Optional[int] = 30) -> List[dict]:
"""
获取工作流分享数据
"""
enabled, _ = self._check_workflow_share_enabled()
if not enabled:
return []
res = RequestUtils(proxies=settings.PROXY or {}, timeout=15).get_res(self._workflow_shares, params={
"name": name,
"page": page,
"count": count
})
return self._handle_list_response(res)
@cached(region=_shares_cache_region, maxsize=1, skip_empty=True)
async def async_get_shares(self, name: Optional[str] = None, page: Optional[int] = 1, count: Optional[int] = 30) -> \
List[dict]:
"""
异步获取工作流分享数据
"""
enabled, _ = self._check_workflow_share_enabled()
if not enabled:
return []
res = await AsyncRequestUtils(proxies=settings.PROXY or {}, timeout=15).get_res(self._workflow_shares, params={
"name": name,
"page": page,
"count": count
})
return self._handle_list_response(res)
def get_user_uuid(self) -> str:
"""
获取用户uuid
"""
if not self._share_user_id:
self._share_user_id = SystemUtils.generate_user_unique_id()
logger.info(f"当前用户UUID: {self._share_user_id}")
return self._share_user_id or ""