mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-06 16:07:01 +08:00
fix actions
This commit is contained in:
@@ -2,9 +2,14 @@ from abc import ABC, abstractmethod
|
|||||||
|
|
||||||
from pydantic.main import BaseModel
|
from pydantic.main import BaseModel
|
||||||
|
|
||||||
|
from app.chain import ChainBase
|
||||||
from app.schemas import ActionContext, ActionParams
|
from app.schemas import ActionContext, ActionParams
|
||||||
|
|
||||||
|
|
||||||
|
class ActionChain(ChainBase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class BaseAction(BaseModel, ABC):
|
class BaseAction(BaseModel, ABC):
|
||||||
"""
|
"""
|
||||||
工作流动作基类
|
工作流动作基类
|
||||||
@@ -13,6 +18,10 @@ 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:
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
from app.actions import BaseAction
|
from app.actions import BaseAction
|
||||||
from app.chain.download import DownloadChain
|
|
||||||
from app.schemas import ActionParams, ActionContext
|
from app.schemas import ActionParams, ActionContext
|
||||||
|
|
||||||
|
|
||||||
@@ -17,10 +16,6 @@ class FetchDownloadsAction(BaseAction):
|
|||||||
|
|
||||||
_downloads = []
|
_downloads = []
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__()
|
|
||||||
self.downloadchain = DownloadChain()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
return "获取下载任务"
|
return "获取下载任务"
|
||||||
@@ -41,7 +36,7 @@ class FetchDownloadsAction(BaseAction):
|
|||||||
"""
|
"""
|
||||||
self._downloads = context.downloads
|
self._downloads = context.downloads
|
||||||
for download in self._downloads:
|
for download in self._downloads:
|
||||||
torrents = self.downloadchain.list_torrents(hashs=[download.download_id])
|
torrents = self.chain.list_torrents(hashs=[download.download_id])
|
||||||
if not torrents:
|
if not torrents:
|
||||||
download.completed = True
|
download.completed = True
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ from typing import Optional
|
|||||||
from pydantic import Field
|
from pydantic import Field
|
||||||
|
|
||||||
from app.actions import BaseAction
|
from app.actions import BaseAction
|
||||||
from app.chain.media import MediaChain
|
|
||||||
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
|
||||||
@@ -22,7 +21,6 @@ class FetchRssParams(ActionParams):
|
|||||||
content_type: Optional[str] = Field(None, description="Content-Type")
|
content_type: Optional[str] = Field(None, description="Content-Type")
|
||||||
referer: Optional[str] = Field(None, description="Referer")
|
referer: Optional[str] = Field(None, description="Referer")
|
||||||
ua: Optional[str] = Field(None, description="User-Agent")
|
ua: Optional[str] = Field(None, description="User-Agent")
|
||||||
recognize: Optional[bool] = Field(False, description="是否识别")
|
|
||||||
|
|
||||||
|
|
||||||
class FetchRssAction(BaseAction):
|
class FetchRssAction(BaseAction):
|
||||||
@@ -35,7 +33,6 @@ class FetchRssAction(BaseAction):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.rsshelper = RssHelper()
|
self.rsshelper = RssHelper()
|
||||||
self.mediachain = MediaChain()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
@@ -83,13 +80,11 @@ class FetchRssAction(BaseAction):
|
|||||||
size=item.get("size"),
|
size=item.get("size"),
|
||||||
pubdate=item["pubdate"].strftime("%Y-%m-%d %H:%M:%S") if item.get("pubdate") else None,
|
pubdate=item["pubdate"].strftime("%Y-%m-%d %H:%M:%S") if item.get("pubdate") else None,
|
||||||
)
|
)
|
||||||
meta, mediainfo = None, None
|
meta = MetaInfo(title=torrentinfo.title, subtitle=torrentinfo.description)
|
||||||
if params.recognize:
|
mediainfo = self.chain.recognize_media(meta)
|
||||||
meta = MetaInfo(title=torrentinfo.title, subtitle=torrentinfo.description)
|
if not mediainfo:
|
||||||
mediainfo = self.mediachain.recognize_media(meta)
|
logger.warning(f"{torrentinfo.title} 未识别到媒体信息")
|
||||||
if not mediainfo:
|
continue
|
||||||
logger.warning(f"{torrentinfo.title} 未识别到媒体信息")
|
|
||||||
continue
|
|
||||||
self._rss_torrents.append(Context(meta_info=meta, media_info=mediainfo, torrent_info=torrentinfo))
|
self._rss_torrents.append(Context(meta_info=meta, media_info=mediainfo, torrent_info=torrentinfo))
|
||||||
|
|
||||||
if self._rss_torrents:
|
if self._rss_torrents:
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
from typing import Optional, List
|
||||||
|
|
||||||
|
from pydantic import Field
|
||||||
|
|
||||||
|
from app.actions import BaseAction
|
||||||
|
from app.chain.search import SearchChain
|
||||||
|
from app.log import logger
|
||||||
|
from app.schemas import ActionParams, ActionContext, MediaType
|
||||||
|
|
||||||
|
|
||||||
|
class FetchTorrentsParams(ActionParams):
|
||||||
|
"""
|
||||||
|
获取站点资源参数
|
||||||
|
"""
|
||||||
|
name: str = Field(None, description="资源名称")
|
||||||
|
year: Optional[int] = Field(None, description="年份")
|
||||||
|
type: Optional[str] = Field(None, description="资源类型 (电影/电视剧)")
|
||||||
|
season: Optional[int] = Field(None, description="季度")
|
||||||
|
sites: Optional[List[int]] = Field([], description="站点列表")
|
||||||
|
|
||||||
|
|
||||||
|
class FetchTorrentsAction(BaseAction):
|
||||||
|
"""
|
||||||
|
搜索站点资源
|
||||||
|
"""
|
||||||
|
|
||||||
|
_torrents = []
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.searchchain = SearchChain()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self) -> str:
|
||||||
|
return "获取站点资源"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def description(self) -> str:
|
||||||
|
return "根据关键字搜索站点种子资源"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def success(self) -> bool:
|
||||||
|
return True if self._torrents else False
|
||||||
|
|
||||||
|
async def execute(self, params: FetchTorrentsParams, context: ActionContext) -> ActionContext:
|
||||||
|
"""
|
||||||
|
搜索站点,获取资源列表
|
||||||
|
"""
|
||||||
|
torrents = self.searchchain.search_by_title(title=params.name, sites=params.sites)
|
||||||
|
for torrent in torrents:
|
||||||
|
if params.year and torrent.meta_info.year != params.year:
|
||||||
|
continue
|
||||||
|
if params.type and torrent.media_info and torrent.media_info.type != MediaType(params.type):
|
||||||
|
continue
|
||||||
|
if params.season and torrent.meta_info.begin_season != params.season:
|
||||||
|
continue
|
||||||
|
# 识别媒体信息
|
||||||
|
torrent.media_info = self.chain.recognize_media(torrent.meta_info)
|
||||||
|
if not torrent.media_info:
|
||||||
|
logger.warning(f"{torrent.torrent_info.title} 未识别到媒体信息")
|
||||||
|
continue
|
||||||
|
self._torrents.append(torrent)
|
||||||
|
|
||||||
|
if self._torrents:
|
||||||
|
context.torrents.extend(self._torrents)
|
||||||
|
logger.info(f"搜索到 {len(self._torrents)} 条资源")
|
||||||
|
|
||||||
|
self.job_done()
|
||||||
|
return context
|
||||||
@@ -1,4 +1,9 @@
|
|||||||
|
from typing import Optional, List
|
||||||
|
|
||||||
|
from pydantic import Field
|
||||||
|
|
||||||
from app.actions import BaseAction
|
from app.actions import BaseAction
|
||||||
|
from app.helper.torrent import TorrentHelper
|
||||||
from app.schemas import ActionParams, ActionContext
|
from app.schemas import ActionParams, ActionContext
|
||||||
|
|
||||||
|
|
||||||
@@ -6,7 +11,10 @@ class FilterTorrentsParams(ActionParams):
|
|||||||
"""
|
"""
|
||||||
过滤资源数据参数
|
过滤资源数据参数
|
||||||
"""
|
"""
|
||||||
pass
|
rule_groups: Optional[List[str]] = Field([], description="规则组")
|
||||||
|
include: Optional[str] = Field(None, description="包含规则")
|
||||||
|
exclude: Optional[str] = Field(None, description="排除规则")
|
||||||
|
size: Optional[str] = Field(None, description="资源大小范围(MB)")
|
||||||
|
|
||||||
|
|
||||||
class FilterTorrentsAction(BaseAction):
|
class FilterTorrentsAction(BaseAction):
|
||||||
@@ -14,6 +22,12 @@ class FilterTorrentsAction(BaseAction):
|
|||||||
过滤资源数据
|
过滤资源数据
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
_torrents = []
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.torrenthelper = TorrentHelper()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
return "过滤资源数据"
|
return "过滤资源数据"
|
||||||
@@ -24,7 +38,29 @@ class FilterTorrentsAction(BaseAction):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def success(self) -> bool:
|
def success(self) -> bool:
|
||||||
return True
|
return self.done
|
||||||
|
|
||||||
async def execute(self, params: FilterTorrentsParams, context: ActionContext) -> ActionContext:
|
async def execute(self, params: FilterTorrentsParams, context: ActionContext) -> ActionContext:
|
||||||
pass
|
"""
|
||||||
|
过滤torrents中的资源
|
||||||
|
"""
|
||||||
|
for torrent in context.torrents:
|
||||||
|
if self.torrenthelper.filter_torrent(
|
||||||
|
torrent_info=torrent.torrent_info,
|
||||||
|
filter_params={
|
||||||
|
"include": params.include,
|
||||||
|
"exclude": params.exclude,
|
||||||
|
"size": params.size
|
||||||
|
}
|
||||||
|
):
|
||||||
|
if self.chain.filter_torrents(
|
||||||
|
rule_groups=params.rule_groups,
|
||||||
|
torrent_list=[torrent.torrent_info],
|
||||||
|
mediainfo=torrent.media_info
|
||||||
|
):
|
||||||
|
self._torrents.append(torrent)
|
||||||
|
|
||||||
|
context.torrents = self._torrents
|
||||||
|
|
||||||
|
self.job_done()
|
||||||
|
return context
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
from typing import Optional
|
|
||||||
|
|
||||||
from pydantic import Field
|
|
||||||
|
|
||||||
from app.actions import BaseAction
|
|
||||||
from app.schemas import ActionParams, ActionContext
|
|
||||||
|
|
||||||
|
|
||||||
class SearchTorrentsParams(ActionParams):
|
|
||||||
"""
|
|
||||||
搜索站点资源参数
|
|
||||||
"""
|
|
||||||
name: str = Field(None, description="资源名称")
|
|
||||||
year: Optional[int] = Field(None, description="年份")
|
|
||||||
type: Optional[str] = Field(None, description="资源类型 (电影/电视剧)")
|
|
||||||
season: Optional[int] = Field(None, description="季度")
|
|
||||||
recognize: Optional[bool] = Field(False, description="是否识别")
|
|
||||||
|
|
||||||
|
|
||||||
class SearchTorrentsAction(BaseAction):
|
|
||||||
"""
|
|
||||||
搜索站点资源
|
|
||||||
"""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self) -> str:
|
|
||||||
return "搜索站点资源"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def description(self) -> str:
|
|
||||||
return "根据关键字搜索站点种子资源"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def success(self) -> bool:
|
|
||||||
return True
|
|
||||||
|
|
||||||
async def execute(self, params: SearchTorrentsParams, context: ActionContext) -> ActionContext:
|
|
||||||
pass
|
|
||||||
@@ -1,12 +1,17 @@
|
|||||||
|
from typing import List, Optional, Union
|
||||||
|
|
||||||
|
from pydantic import Field
|
||||||
|
|
||||||
from app.actions import BaseAction
|
from app.actions import BaseAction
|
||||||
from app.schemas import ActionParams, ActionContext
|
from app.schemas import ActionParams, ActionContext, MessageChannel
|
||||||
|
|
||||||
|
|
||||||
class SendMessageParams(ActionParams):
|
class SendMessageParams(ActionParams):
|
||||||
"""
|
"""
|
||||||
发送消息参数
|
发送消息参数
|
||||||
"""
|
"""
|
||||||
pass
|
channel: Optional[List[str]] = Field([], description="消息渠道")
|
||||||
|
userid: Optional[Union[str, int]] = Field(None, description="用户ID")
|
||||||
|
|
||||||
|
|
||||||
class SendMessageAction(BaseAction):
|
class SendMessageAction(BaseAction):
|
||||||
@@ -24,7 +29,20 @@ class SendMessageAction(BaseAction):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def success(self) -> bool:
|
def success(self) -> bool:
|
||||||
return True
|
return self.done
|
||||||
|
|
||||||
async def execute(self, params: SendMessageParams, context: ActionContext) -> ActionContext:
|
async def execute(self, params: SendMessageParams, context: ActionContext) -> ActionContext:
|
||||||
pass
|
"""
|
||||||
|
发送messages中的消息
|
||||||
|
"""
|
||||||
|
for message in context.messages:
|
||||||
|
if params.channel:
|
||||||
|
message.channel = MessageChannel(params.channel)
|
||||||
|
if params.userid:
|
||||||
|
message.userid = params.userid
|
||||||
|
self.chain.post_message(message)
|
||||||
|
|
||||||
|
context.messages = []
|
||||||
|
|
||||||
|
self.job_done()
|
||||||
|
return context
|
||||||
|
|||||||
@@ -133,12 +133,12 @@ def search_by_id(mediaid: str,
|
|||||||
@router.get("/title", summary="模糊搜索资源", response_model=schemas.Response)
|
@router.get("/title", summary="模糊搜索资源", response_model=schemas.Response)
|
||||||
def search_by_title(keyword: str = None,
|
def search_by_title(keyword: str = None,
|
||||||
page: int = 0,
|
page: int = 0,
|
||||||
site: int = None,
|
sites: List[int] = None,
|
||||||
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
"""
|
"""
|
||||||
根据名称模糊搜索站点资源,支持分页,关键词为空是返回首页资源
|
根据名称模糊搜索站点资源,支持分页,关键词为空是返回首页资源
|
||||||
"""
|
"""
|
||||||
torrents = SearchChain().search_by_title(title=keyword, page=page, site=site)
|
torrents = SearchChain().search_by_title(title=keyword, page=page, sites=sites)
|
||||||
if not torrents:
|
if not torrents:
|
||||||
return schemas.Response(success=False, message="未搜索到任何资源")
|
return schemas.Response(success=False, message="未搜索到任何资源")
|
||||||
return schemas.Response(success=True, data=[torrent.to_dict() for torrent in torrents])
|
return schemas.Response(success=True, data=[torrent.to_dict() for torrent in torrents])
|
||||||
|
|||||||
+9
-6
@@ -61,19 +61,21 @@ class SearchChain(ChainBase):
|
|||||||
self.save_cache(bytes_results, self.__result_temp_file)
|
self.save_cache(bytes_results, self.__result_temp_file)
|
||||||
return results
|
return results
|
||||||
|
|
||||||
def search_by_title(self, title: str, page: int = 0, site: int = None) -> List[Context]:
|
def search_by_title(self, title: str, page: int = 0,
|
||||||
|
sites: List[int] = None, cache_local: bool = True) -> List[Context]:
|
||||||
"""
|
"""
|
||||||
根据标题搜索资源,不识别不过滤,直接返回站点内容
|
根据标题搜索资源,不识别不过滤,直接返回站点内容
|
||||||
:param title: 标题,为空时返回所有站点首页内容
|
:param title: 标题,为空时返回所有站点首页内容
|
||||||
:param page: 页码
|
:param page: 页码
|
||||||
:param site: 站点ID
|
:param sites: 站点ID列表
|
||||||
|
:param cache_local: 是否缓存到本地
|
||||||
"""
|
"""
|
||||||
if title:
|
if title:
|
||||||
logger.info(f'开始搜索资源,关键词:{title} ...')
|
logger.info(f'开始搜索资源,关键词:{title} ...')
|
||||||
else:
|
else:
|
||||||
logger.info(f'开始浏览资源,站点:{site} ...')
|
logger.info(f'开始浏览资源,站点:{sites} ...')
|
||||||
# 搜索
|
# 搜索
|
||||||
torrents = self.__search_all_sites(keywords=[title], sites=[site] if site else None, page=page) or []
|
torrents = self.__search_all_sites(keywords=[title], sites=sites if sites else None, page=page) or []
|
||||||
if not torrents:
|
if not torrents:
|
||||||
logger.warn(f'{title} 未搜索到资源')
|
logger.warn(f'{title} 未搜索到资源')
|
||||||
return []
|
return []
|
||||||
@@ -81,8 +83,9 @@ class SearchChain(ChainBase):
|
|||||||
contexts = [Context(meta_info=MetaInfo(title=torrent.title, subtitle=torrent.description),
|
contexts = [Context(meta_info=MetaInfo(title=torrent.title, subtitle=torrent.description),
|
||||||
torrent_info=torrent) for torrent in torrents]
|
torrent_info=torrent) for torrent in torrents]
|
||||||
# 保存到本地文件
|
# 保存到本地文件
|
||||||
bytes_results = pickle.dumps(contexts)
|
if cache_local:
|
||||||
self.save_cache(bytes_results, self.__result_temp_file)
|
bytes_results = pickle.dumps(contexts)
|
||||||
|
self.save_cache(bytes_results, self.__result_temp_file)
|
||||||
return contexts
|
return contexts
|
||||||
|
|
||||||
def last_search_results(self) -> List[Context]:
|
def last_search_results(self) -> List[Context]:
|
||||||
|
|||||||
+3
-3
@@ -33,7 +33,7 @@ class RuleHelper:
|
|||||||
return group
|
return group
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_rule_group_by_media(self, media: MediaInfo, group_names: list = None) -> List[FilterRuleGroup]:
|
def get_rule_group_by_media(self, media: MediaInfo = None, group_names: list = None) -> List[FilterRuleGroup]:
|
||||||
"""
|
"""
|
||||||
根据媒体信息获取规则组
|
根据媒体信息获取规则组
|
||||||
"""
|
"""
|
||||||
@@ -44,9 +44,9 @@ class RuleHelper:
|
|||||||
for group in rule_groups:
|
for group in rule_groups:
|
||||||
if not group.media_type:
|
if not group.media_type:
|
||||||
ret_groups.append(group)
|
ret_groups.append(group)
|
||||||
elif not group.category and group.media_type == media.type.value:
|
elif media and not group.category and group.media_type == media.type.value:
|
||||||
ret_groups.append(group)
|
ret_groups.append(group)
|
||||||
elif group.category == media.category:
|
elif media and group.category == media.category:
|
||||||
ret_groups.append(group)
|
ret_groups.append(group)
|
||||||
return ret_groups
|
return ret_groups
|
||||||
|
|
||||||
|
|||||||
@@ -445,6 +445,26 @@ class TorrentHelper(metaclass=Singleton):
|
|||||||
logger.info(f"{torrent_info.title} 不匹配特效规则 {effect}")
|
logger.info(f"{torrent_info.title} 不匹配特效规则 {effect}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# 大小
|
||||||
|
size_range = filter_params.get("size")
|
||||||
|
if size_range.find("-") != -1:
|
||||||
|
# 区间
|
||||||
|
size_min, size_max = size_range.split("-")
|
||||||
|
size_min = float(size_min.strip()) * 1024 * 1024
|
||||||
|
size_max = float(size_max.strip()) * 1024 * 1024
|
||||||
|
if torrent_info.size < size_min or torrent_info.size > size_max:
|
||||||
|
return False
|
||||||
|
elif size_range.startswith(">"):
|
||||||
|
# 大于
|
||||||
|
size_min = float(size_range[1:].strip()) * 1024 * 1024
|
||||||
|
if torrent_info.size < size_min:
|
||||||
|
return False
|
||||||
|
elif size_range.startswith("<"):
|
||||||
|
# 小于
|
||||||
|
size_max = float(size_range[1:].strip()) * 1024 * 1024
|
||||||
|
if torrent_info.size > size_max:
|
||||||
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|||||||
Reference in New Issue
Block a user