mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-07-01 21:01:47 +08:00
fix actions
This commit is contained in:
@@ -2,9 +2,14 @@ from abc import ABC, abstractmethod
|
||||
|
||||
from pydantic.main import BaseModel
|
||||
|
||||
from app.chain import ChainBase
|
||||
from app.schemas import ActionContext, ActionParams
|
||||
|
||||
|
||||
class ActionChain(ChainBase):
|
||||
pass
|
||||
|
||||
|
||||
class BaseAction(BaseModel, ABC):
|
||||
"""
|
||||
工作流动作基类
|
||||
@@ -13,6 +18,10 @@ class BaseAction(BaseModel, ABC):
|
||||
# 完成标志
|
||||
_done_flag = False
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.chain = ActionChain()
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def name(self) -> str:
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from app.actions import BaseAction
|
||||
from app.chain.download import DownloadChain
|
||||
from app.schemas import ActionParams, ActionContext
|
||||
|
||||
|
||||
@@ -17,10 +16,6 @@ class FetchDownloadsAction(BaseAction):
|
||||
|
||||
_downloads = []
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.downloadchain = DownloadChain()
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return "获取下载任务"
|
||||
@@ -41,7 +36,7 @@ class FetchDownloadsAction(BaseAction):
|
||||
"""
|
||||
self._downloads = context.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:
|
||||
download.completed = True
|
||||
continue
|
||||
|
||||
@@ -3,7 +3,6 @@ from typing import Optional
|
||||
from pydantic import Field
|
||||
|
||||
from app.actions import BaseAction
|
||||
from app.chain.media import MediaChain
|
||||
from app.core.config import settings
|
||||
from app.core.context import Context
|
||||
from app.core.metainfo import MetaInfo
|
||||
@@ -22,7 +21,6 @@ class FetchRssParams(ActionParams):
|
||||
content_type: Optional[str] = Field(None, description="Content-Type")
|
||||
referer: Optional[str] = Field(None, description="Referer")
|
||||
ua: Optional[str] = Field(None, description="User-Agent")
|
||||
recognize: Optional[bool] = Field(False, description="是否识别")
|
||||
|
||||
|
||||
class FetchRssAction(BaseAction):
|
||||
@@ -35,7 +33,6 @@ class FetchRssAction(BaseAction):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.rsshelper = RssHelper()
|
||||
self.mediachain = MediaChain()
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
@@ -83,13 +80,11 @@ class FetchRssAction(BaseAction):
|
||||
size=item.get("size"),
|
||||
pubdate=item["pubdate"].strftime("%Y-%m-%d %H:%M:%S") if item.get("pubdate") else None,
|
||||
)
|
||||
meta, mediainfo = None, None
|
||||
if params.recognize:
|
||||
meta = MetaInfo(title=torrentinfo.title, subtitle=torrentinfo.description)
|
||||
mediainfo = self.mediachain.recognize_media(meta)
|
||||
if not mediainfo:
|
||||
logger.warning(f"{torrentinfo.title} 未识别到媒体信息")
|
||||
continue
|
||||
meta = MetaInfo(title=torrentinfo.title, subtitle=torrentinfo.description)
|
||||
mediainfo = self.chain.recognize_media(meta)
|
||||
if not mediainfo:
|
||||
logger.warning(f"{torrentinfo.title} 未识别到媒体信息")
|
||||
continue
|
||||
self._rss_torrents.append(Context(meta_info=meta, media_info=mediainfo, torrent_info=torrentinfo))
|
||||
|
||||
if self._rss_torrents:
|
||||
|
||||
69
app/actions/fetch_torrents.py
Normal file
69
app/actions/fetch_torrents.py
Normal file
@@ -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.helper.torrent import TorrentHelper
|
||||
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):
|
||||
@@ -14,6 +22,12 @@ class FilterTorrentsAction(BaseAction):
|
||||
过滤资源数据
|
||||
"""
|
||||
|
||||
_torrents = []
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.torrenthelper = TorrentHelper()
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return "过滤资源数据"
|
||||
@@ -24,7 +38,29 @@ class FilterTorrentsAction(BaseAction):
|
||||
|
||||
@property
|
||||
def success(self) -> bool:
|
||||
return True
|
||||
return self.done
|
||||
|
||||
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.schemas import ActionParams, ActionContext
|
||||
from app.schemas import ActionParams, ActionContext, MessageChannel
|
||||
|
||||
|
||||
class SendMessageParams(ActionParams):
|
||||
"""
|
||||
发送消息参数
|
||||
"""
|
||||
pass
|
||||
channel: Optional[List[str]] = Field([], description="消息渠道")
|
||||
userid: Optional[Union[str, int]] = Field(None, description="用户ID")
|
||||
|
||||
|
||||
class SendMessageAction(BaseAction):
|
||||
@@ -24,7 +29,20 @@ class SendMessageAction(BaseAction):
|
||||
|
||||
@property
|
||||
def success(self) -> bool:
|
||||
return True
|
||||
return self.done
|
||||
|
||||
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)
|
||||
def search_by_title(keyword: str = None,
|
||||
page: int = 0,
|
||||
site: int = None,
|
||||
sites: List[int] = None,
|
||||
_: 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:
|
||||
return schemas.Response(success=False, message="未搜索到任何资源")
|
||||
return schemas.Response(success=True, data=[torrent.to_dict() for torrent in torrents])
|
||||
|
||||
@@ -61,19 +61,21 @@ class SearchChain(ChainBase):
|
||||
self.save_cache(bytes_results, self.__result_temp_file)
|
||||
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 page: 页码
|
||||
:param site: 站点ID
|
||||
:param sites: 站点ID列表
|
||||
:param cache_local: 是否缓存到本地
|
||||
"""
|
||||
if title:
|
||||
logger.info(f'开始搜索资源,关键词:{title} ...')
|
||||
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:
|
||||
logger.warn(f'{title} 未搜索到资源')
|
||||
return []
|
||||
@@ -81,8 +83,9 @@ class SearchChain(ChainBase):
|
||||
contexts = [Context(meta_info=MetaInfo(title=torrent.title, subtitle=torrent.description),
|
||||
torrent_info=torrent) for torrent in torrents]
|
||||
# 保存到本地文件
|
||||
bytes_results = pickle.dumps(contexts)
|
||||
self.save_cache(bytes_results, self.__result_temp_file)
|
||||
if cache_local:
|
||||
bytes_results = pickle.dumps(contexts)
|
||||
self.save_cache(bytes_results, self.__result_temp_file)
|
||||
return contexts
|
||||
|
||||
def last_search_results(self) -> List[Context]:
|
||||
|
||||
@@ -33,7 +33,7 @@ class RuleHelper:
|
||||
return group
|
||||
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:
|
||||
if not group.media_type:
|
||||
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)
|
||||
elif group.category == media.category:
|
||||
elif media and group.category == media.category:
|
||||
ret_groups.append(group)
|
||||
return ret_groups
|
||||
|
||||
|
||||
@@ -445,6 +445,26 @@ class TorrentHelper(metaclass=Singleton):
|
||||
logger.info(f"{torrent_info.title} 不匹配特效规则 {effect}")
|
||||
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
|
||||
|
||||
@staticmethod
|
||||
|
||||
Reference in New Issue
Block a user