mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-04-05 11:47:50 +08:00
fix filter rules
This commit is contained in:
@@ -19,9 +19,9 @@ from app.db.systemconfig_oper import SystemConfigOper
|
||||
from app.db.userauth import get_current_active_superuser
|
||||
from app.helper.message import MessageHelper
|
||||
from app.helper.progress import ProgressHelper
|
||||
from app.helper.rule import RuleHelper
|
||||
from app.helper.sites import SitesHelper
|
||||
from app.scheduler import Scheduler
|
||||
from app.schemas.types import SystemConfigKey
|
||||
from app.utils.http import RequestUtils
|
||||
from app.utils.system import SystemUtils
|
||||
from version import APP_VERSION
|
||||
@@ -223,10 +223,10 @@ def latest_version(_: schemas.TokenPayload = Depends(verify_token)):
|
||||
return schemas.Response(success=False)
|
||||
|
||||
|
||||
@router.get("/ruletest", summary="优先级规则测试", response_model=schemas.Response)
|
||||
@router.get("/ruletest", summary="过滤规则测试", response_model=schemas.Response)
|
||||
def ruletest(title: str,
|
||||
rulegroup_name: str,
|
||||
subtitle: str = None,
|
||||
ruletype: str = None,
|
||||
_: schemas.TokenPayload = Depends(verify_token)):
|
||||
"""
|
||||
过滤规则测试,规则类型 1-订阅,2-洗版,3-搜索
|
||||
@@ -235,20 +235,16 @@ def ruletest(title: str,
|
||||
title=title,
|
||||
description=subtitle,
|
||||
)
|
||||
if ruletype == "2":
|
||||
rule_string = SystemConfigOper().get(SystemConfigKey.BestVersionFilterRules)
|
||||
elif ruletype == "3":
|
||||
rule_string = SystemConfigOper().get(SystemConfigKey.SearchFilterRules)
|
||||
else:
|
||||
rule_string = SystemConfigOper().get(SystemConfigKey.SubscribeFilterRules)
|
||||
if not rule_string:
|
||||
return schemas.Response(success=False, message="优先级规则未设置!")
|
||||
# 查询规则组详情
|
||||
rulegroup = RuleHelper().get_rule_group(rulegroup_name)
|
||||
if not rulegroup:
|
||||
return schemas.Response(success=False, message=f"过滤规则组 {rulegroup_name} 不存在!")
|
||||
|
||||
# 过滤
|
||||
result = SearchChain().filter_torrents(rule_string=rule_string,
|
||||
result = SearchChain().filter_torrents(rule_groups=[rulegroup.name],
|
||||
torrent_list=[torrent])
|
||||
if not result:
|
||||
return schemas.Response(success=False, message="不符合优先级规则!")
|
||||
return schemas.Response(success=False, message="不符合过滤规则!")
|
||||
return schemas.Response(success=True, data={
|
||||
"priority": 100 - result[0].pri_order + 1
|
||||
})
|
||||
|
||||
@@ -22,7 +22,7 @@ from app.helper.notification import NotificationHelper
|
||||
from app.log import logger
|
||||
from app.schemas import TransferInfo, TransferTorrent, ExistMediaInfo, DownloadingTorrent, CommingMessage, Notification, \
|
||||
WebhookEventInfo, TmdbEpisode, MediaPerson, FileItem
|
||||
from app.schemas.types import TorrentStatus, MediaType, MediaImageType, EventType, NotificationType
|
||||
from app.schemas.types import TorrentStatus, MediaType, MediaImageType, EventType
|
||||
from app.utils.object import ObjectUtils
|
||||
|
||||
|
||||
@@ -316,19 +316,19 @@ class ChainBase(metaclass=ABCMeta):
|
||||
"""
|
||||
return self.run_module("refresh_torrents", site=site)
|
||||
|
||||
def filter_torrents(self, rule_string: str,
|
||||
def filter_torrents(self, rule_groups: List[str],
|
||||
torrent_list: List[TorrentInfo],
|
||||
season_episodes: Dict[int, list] = None,
|
||||
mediainfo: MediaInfo = None) -> List[TorrentInfo]:
|
||||
"""
|
||||
过滤种子资源
|
||||
:param rule_string: 过滤规则
|
||||
:param rule_groups: 过滤规则组名称列表
|
||||
:param torrent_list: 资源列表
|
||||
:param season_episodes: 季集数过滤 {season:[episodes]}
|
||||
:param mediainfo: 识别的媒体信息
|
||||
:return: 过滤后的资源列表,添加资源优先级
|
||||
"""
|
||||
return self.run_module("filter_torrents", rule_string=rule_string,
|
||||
return self.run_module("filter_torrents", rule_groups=rule_groups,
|
||||
torrent_list=torrent_list, season_episodes=season_episodes,
|
||||
mediainfo=mediainfo)
|
||||
|
||||
|
||||
@@ -97,8 +97,7 @@ class SearchChain(ChainBase):
|
||||
keyword: str = None,
|
||||
no_exists: Dict[int, Dict[int, NotExistMediaInfo]] = None,
|
||||
sites: List[int] = None,
|
||||
priority_rule: str = None,
|
||||
filter_rule: Dict[str, str] = None,
|
||||
rule_groups: List[str] = None,
|
||||
area: str = "title") -> List[Context]:
|
||||
"""
|
||||
根据媒体信息搜索种子资源,精确匹配,应用过滤规则,同时根据no_exists过滤本地已存在的资源
|
||||
@@ -106,8 +105,7 @@ class SearchChain(ChainBase):
|
||||
:param keyword: 搜索关键词
|
||||
:param no_exists: 缺失的媒体信息
|
||||
:param sites: 站点ID列表,为空时搜索所有站点
|
||||
:param priority_rule: 优先级规则,为空时使用搜索优先级规则
|
||||
:param filter_rule: 过滤规则,为空是使用默认过滤规则
|
||||
:param rule_groups: 过滤规则组名称列表
|
||||
:param area: 搜索范围,title or imdbid
|
||||
"""
|
||||
|
||||
@@ -115,7 +113,7 @@ class SearchChain(ChainBase):
|
||||
"""
|
||||
执行优先级过滤
|
||||
"""
|
||||
return self.filter_torrents(rule_string=priority_rule,
|
||||
return self.filter_torrents(rule_groups=rule_groups,
|
||||
torrent_list=torrent_list,
|
||||
season_episodes=season_episodes,
|
||||
mediainfo=mediainfo) or []
|
||||
@@ -220,27 +218,16 @@ class SearchChain(ChainBase):
|
||||
key=ProgressKey.Search)
|
||||
|
||||
# 开始过滤规则过滤
|
||||
if _match_torrents:
|
||||
logger.info(f'开始过滤规则过滤,当前规则:{filter_rule} ...')
|
||||
_match_torrents = self.filter_torrents_by_rule(torrents=_match_torrents,
|
||||
mediainfo=mediainfo,
|
||||
filter_rule=filter_rule)
|
||||
if not _match_torrents:
|
||||
logger.warn(f'{keyword or mediainfo.title} 没有符合过滤规则的资源')
|
||||
return []
|
||||
logger.info(f"过滤规则过滤完成,剩余 {len(_match_torrents)} 个资源")
|
||||
|
||||
# 开始优先级规则/剧集过滤
|
||||
if priority_rule is None:
|
||||
# 取搜索优先级规则
|
||||
priority_rule = self.systemconfig.get(SystemConfigKey.SearchFilterRules)
|
||||
if priority_rule:
|
||||
logger.info(f'开始优先级规则/剧集过滤,当前规则:{priority_rule} ...')
|
||||
if rule_groups is None:
|
||||
# 取搜索过滤规则
|
||||
rule_groups: List[str] = self.systemconfig.get(SystemConfigKey.SearchFilterRuleGroups)
|
||||
if rule_groups:
|
||||
logger.info(f'开始过滤规则/剧集过滤,使用规则组:{rule_groups} ...')
|
||||
_match_torrents = __do_filter(_match_torrents)
|
||||
if not _match_torrents:
|
||||
logger.warn(f'{keyword or mediainfo.title} 没有符合优先级规则的资源')
|
||||
logger.warn(f'{keyword or mediainfo.title} 没有符合过滤规则的资源')
|
||||
return []
|
||||
logger.info(f"优先级规则/剧集过滤完成,剩余 {len(_match_torrents)} 个资源")
|
||||
logger.info(f"过滤规则/剧集过滤完成,剩余 {len(_match_torrents)} 个资源")
|
||||
|
||||
# 去掉mediainfo中多余的数据
|
||||
mediainfo.clear()
|
||||
@@ -354,34 +341,6 @@ class SearchChain(ChainBase):
|
||||
# 返回
|
||||
return results
|
||||
|
||||
def filter_torrents_by_rule(self,
|
||||
torrents: List[TorrentInfo],
|
||||
mediainfo: MediaInfo,
|
||||
filter_rule: Dict[str, str] = None,
|
||||
) -> List[TorrentInfo]:
|
||||
"""
|
||||
使用过滤规则过滤种子
|
||||
:param torrents: 种子列表
|
||||
:param filter_rule: 过滤规则
|
||||
:param mediainfo: 媒体信息
|
||||
"""
|
||||
|
||||
if not filter_rule:
|
||||
# 没有则取搜索默认过滤规则
|
||||
filter_rule = self.systemconfig.get(SystemConfigKey.DefaultSearchFilterRules)
|
||||
if not filter_rule:
|
||||
return torrents
|
||||
|
||||
# 使用默认过滤规则再次过滤
|
||||
return list(filter(
|
||||
lambda t: self.torrenthelper.filter_torrent(
|
||||
torrent_info=t,
|
||||
filter_rule=filter_rule,
|
||||
mediainfo=mediainfo
|
||||
),
|
||||
torrents
|
||||
))
|
||||
|
||||
@eventmanager.register(EventType.SiteDeleted)
|
||||
def remove_site(self, event: Event):
|
||||
"""
|
||||
|
||||
@@ -329,20 +329,16 @@ class SubscribeChain(ChainBase):
|
||||
|
||||
# 优先级过滤规则
|
||||
if subscribe.best_version:
|
||||
priority_rule = self.systemconfig.get(SystemConfigKey.BestVersionFilterRules)
|
||||
rule_groups = self.systemconfig.get(SystemConfigKey.BeseVersionFilterRuleGroups)
|
||||
else:
|
||||
priority_rule = self.systemconfig.get(SystemConfigKey.SubscribeFilterRules)
|
||||
|
||||
# 过滤规则
|
||||
filter_rule = self.get_filter_rule(subscribe)
|
||||
rule_groups = self.systemconfig.get(SystemConfigKey.SubscribeFilterRuleGroups)
|
||||
|
||||
# 搜索,同时电视剧会过滤掉不需要的剧集
|
||||
contexts = self.searchchain.process(mediainfo=mediainfo,
|
||||
keyword=subscribe.keyword,
|
||||
no_exists=no_exists,
|
||||
sites=sites,
|
||||
priority_rule=priority_rule,
|
||||
filter_rule=filter_rule,
|
||||
rule_groups=rule_groups,
|
||||
area="imdbid" if subscribe.search_imdbid else "title")
|
||||
if not contexts:
|
||||
logger.warn(f'订阅 {subscribe.keyword or subscribe.name} 未搜索到资源')
|
||||
@@ -515,24 +511,6 @@ class SubscribeChain(ChainBase):
|
||||
|
||||
return ret_sites
|
||||
|
||||
def get_filter_rule(self, subscribe: Subscribe):
|
||||
"""
|
||||
获取订阅过滤规则,同时组合默认规则
|
||||
"""
|
||||
# 默认过滤规则
|
||||
default_rule = self.systemconfig.get(SystemConfigKey.DefaultFilterRules) or {}
|
||||
return {
|
||||
"include": subscribe.include or default_rule.get("include"),
|
||||
"exclude": subscribe.exclude or default_rule.get("exclude"),
|
||||
"quality": subscribe.quality or default_rule.get("quality"),
|
||||
"resolution": subscribe.resolution or default_rule.get("resolution"),
|
||||
"effect": subscribe.effect or default_rule.get("effect"),
|
||||
"tv_size": default_rule.get("tv_size"),
|
||||
"movie_size": default_rule.get("movie_size"),
|
||||
"min_seeders": default_rule.get("min_seeders"),
|
||||
"min_seeders_time": default_rule.get("min_seeders_time"),
|
||||
}
|
||||
|
||||
def match(self, torrents: Dict[str, List[Context]]):
|
||||
"""
|
||||
从缓存中匹配订阅,并自动下载
|
||||
@@ -624,9 +602,6 @@ class SubscribeChain(ChainBase):
|
||||
downloaded_episodes=self.__get_downloaded_episodes(subscribe)
|
||||
)
|
||||
|
||||
# 过滤规则
|
||||
filter_rule = self.get_filter_rule(subscribe)
|
||||
|
||||
# 遍历缓存种子
|
||||
_match_context = []
|
||||
for domain, contexts in torrents.items():
|
||||
@@ -678,18 +653,18 @@ class SubscribeChain(ChainBase):
|
||||
else:
|
||||
continue
|
||||
|
||||
# 优先级过滤规则
|
||||
# 过滤规则
|
||||
if subscribe.best_version:
|
||||
priority_rule = self.systemconfig.get(SystemConfigKey.BestVersionFilterRules)
|
||||
rule_groups = self.systemconfig.get(SystemConfigKey.BeseVersionFilterRuleGroups)
|
||||
else:
|
||||
priority_rule = self.systemconfig.get(SystemConfigKey.SubscribeFilterRules)
|
||||
rule_groups = self.systemconfig.get(SystemConfigKey.SubscribeFilterRuleGroups)
|
||||
result: List[TorrentInfo] = self.filter_torrents(
|
||||
rule_string=priority_rule,
|
||||
rule_groups=rule_groups,
|
||||
torrent_list=[torrent_info],
|
||||
mediainfo=torrent_mediainfo)
|
||||
if result is not None and not result:
|
||||
# 不符合过滤规则
|
||||
logger.debug(f"{torrent_info.title} 不匹配当前过滤规则")
|
||||
logger.debug(f"{torrent_info.title} 不匹配过滤规则")
|
||||
continue
|
||||
|
||||
# 不在订阅站点范围的不处理
|
||||
@@ -736,12 +711,6 @@ class SubscribeChain(ChainBase):
|
||||
logger.debug(f'{subscribe.name} 正在洗版,{torrent_info.title} 不是整季')
|
||||
continue
|
||||
|
||||
# 过滤规则
|
||||
if not self.torrenthelper.filter_torrent(torrent_info=torrent_info,
|
||||
filter_rule=filter_rule,
|
||||
mediainfo=torrent_mediainfo):
|
||||
continue
|
||||
|
||||
# 洗版时,优先级小于已下载优先级的不要
|
||||
if subscribe.best_version:
|
||||
if subscribe.current_priority \
|
||||
|
||||
52
app/helper/rule.py
Normal file
52
app/helper/rule.py
Normal file
@@ -0,0 +1,52 @@
|
||||
from typing import List, Optional
|
||||
|
||||
from app.db.systemconfig_oper import SystemConfigOper
|
||||
from app.schemas import FilterRuleGroup, CustomRule
|
||||
from app.schemas.types import SystemConfigKey
|
||||
|
||||
|
||||
class RuleHelper:
|
||||
"""
|
||||
规划帮助类
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.systemconfig = SystemConfigOper()
|
||||
|
||||
def get_rule_groups(self) -> List[FilterRuleGroup]:
|
||||
"""
|
||||
获取用户所有规则组
|
||||
"""
|
||||
rule_groups: List[dict] = self.systemconfig.get(SystemConfigKey.UserRuleGroups)
|
||||
if not rule_groups:
|
||||
return []
|
||||
return [FilterRuleGroup(**group) for group in rule_groups]
|
||||
|
||||
def get_rule_group(self, group_name: str) -> Optional[FilterRuleGroup]:
|
||||
"""
|
||||
获取规则组
|
||||
"""
|
||||
rule_groups = self.get_rule_groups()
|
||||
for group in rule_groups:
|
||||
if group.name == group_name:
|
||||
return group
|
||||
return None
|
||||
|
||||
def get_custom_rules(self) -> List[CustomRule]:
|
||||
"""
|
||||
获取用户所有自定义规则
|
||||
"""
|
||||
rules: List[dict] = self.systemconfig.get(SystemConfigKey.CustomFilterRules)
|
||||
if not rules:
|
||||
return []
|
||||
return [CustomRule(**rule) for rule in rules]
|
||||
|
||||
def get_custom_rule(self, rule_id: str) -> Optional[CustomRule]:
|
||||
"""
|
||||
获取自定义规则
|
||||
"""
|
||||
rules = self.get_custom_rules()
|
||||
for rule in rules:
|
||||
if rule.id == rule_id:
|
||||
return rule
|
||||
return None
|
||||
@@ -1,8 +1,7 @@
|
||||
import datetime
|
||||
import re
|
||||
import traceback
|
||||
from pathlib import Path
|
||||
from typing import Tuple, Optional, List, Union, Dict
|
||||
from typing import Tuple, Optional, List, Union
|
||||
from urllib.parse import unquote
|
||||
|
||||
from requests import Response
|
||||
@@ -13,8 +12,8 @@ from app.core.context import Context, TorrentInfo, MediaInfo
|
||||
from app.core.metainfo import MetaInfo
|
||||
from app.db.systemconfig_oper import SystemConfigOper
|
||||
from app.log import logger
|
||||
from app.utils.http import RequestUtils
|
||||
from app.schemas.types import MediaType, SystemConfigKey
|
||||
from app.utils.http import RequestUtils
|
||||
from app.utils.singleton import Singleton
|
||||
from app.utils.string import StringUtils
|
||||
|
||||
@@ -298,135 +297,6 @@ class TorrentHelper(metaclass=Singleton):
|
||||
if url not in self._invalid_torrents:
|
||||
self._invalid_torrents.append(url)
|
||||
|
||||
@staticmethod
|
||||
def filter_torrent(torrent_info: TorrentInfo,
|
||||
filter_rule: Dict[str, str],
|
||||
mediainfo: MediaInfo) -> bool:
|
||||
"""
|
||||
检查种子是否匹配订阅过滤规则
|
||||
"""
|
||||
|
||||
def __get_size_range(size_str: str) -> Tuple[float, float]:
|
||||
"""
|
||||
获取大小范围
|
||||
"""
|
||||
if not size_str:
|
||||
return 0, 0
|
||||
try:
|
||||
size_range = size_str.split("-")
|
||||
if len(size_range) == 1:
|
||||
return 0, float(size_range[0])
|
||||
elif len(size_range) == 2:
|
||||
return float(size_range[0]), float(size_range[1])
|
||||
except Exception as e:
|
||||
logger.error(f"解析大小范围失败:{str(e)} - {traceback.format_exc()}")
|
||||
return 0, 0
|
||||
|
||||
def __get_pubminutes(pubdate: str) -> float:
|
||||
"""
|
||||
将字符串转换为时间,并计算与当前时间差)(分钟)
|
||||
"""
|
||||
try:
|
||||
if not pubdate:
|
||||
return 0
|
||||
pubdate = pubdate.replace("T", " ").replace("Z", "")
|
||||
pubdate = datetime.datetime.strptime(pubdate, "%Y-%m-%d %H:%M:%S")
|
||||
now = datetime.datetime.now()
|
||||
return (now - pubdate).total_seconds() // 60
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
return 0
|
||||
|
||||
if not filter_rule:
|
||||
return True
|
||||
|
||||
# 匹配内容
|
||||
content = (f"{torrent_info.title} "
|
||||
f"{torrent_info.description} "
|
||||
f"{' '.join(torrent_info.labels or [])} "
|
||||
f"{torrent_info.volume_factor}")
|
||||
|
||||
# 最少做种人数
|
||||
min_seeders = filter_rule.get("min_seeders")
|
||||
if min_seeders and torrent_info.seeders < int(min_seeders):
|
||||
# 最少做种人数生效发布时间(分钟)(在设置发布时间之外的最少做种人数生效)
|
||||
min_seeders_time = filter_rule.get("min_seeders_time") or 0
|
||||
if min_seeders_time:
|
||||
# 发布时间与当前时间差(分钟)
|
||||
pubdate_minutes = __get_pubminutes(torrent_info.pubdate)
|
||||
if pubdate_minutes > int(min_seeders_time):
|
||||
logger.info(f"{torrent_info.title} 发布时间大于 {min_seeders_time} 分钟,做种人数不足 {min_seeders}")
|
||||
return False
|
||||
else:
|
||||
logger.info(f"{torrent_info.title} 做种人数不足 {min_seeders}")
|
||||
return False
|
||||
|
||||
# 包含
|
||||
include = filter_rule.get("include")
|
||||
if include:
|
||||
if not re.search(r"%s" % include, content, re.I):
|
||||
logger.info(f"{content} 不匹配包含规则 {include}")
|
||||
return False
|
||||
# 排除
|
||||
exclude = filter_rule.get("exclude")
|
||||
if exclude:
|
||||
if re.search(r"%s" % exclude, content, re.I):
|
||||
logger.info(f"{content} 匹配排除规则 {exclude}")
|
||||
return False
|
||||
# 质量
|
||||
quality = filter_rule.get("quality")
|
||||
if quality:
|
||||
if not re.search(r"%s" % quality, torrent_info.title, re.I):
|
||||
logger.info(f"{torrent_info.title} 不匹配质量规则 {quality}")
|
||||
return False
|
||||
# 分辨率
|
||||
resolution = filter_rule.get("resolution")
|
||||
if resolution:
|
||||
if not re.search(r"%s" % resolution, torrent_info.title, re.I):
|
||||
logger.info(f"{torrent_info.title} 不匹配分辨率规则 {resolution}")
|
||||
return False
|
||||
# 特效
|
||||
effect = filter_rule.get("effect")
|
||||
if effect:
|
||||
if not re.search(r"%s" % effect, torrent_info.title, re.I):
|
||||
logger.info(f"{torrent_info.title} 不匹配特效规则 {effect}")
|
||||
return False
|
||||
|
||||
# 大小
|
||||
tv_size = filter_rule.get("tv_size")
|
||||
movie_size = filter_rule.get("movie_size")
|
||||
if movie_size or tv_size:
|
||||
if mediainfo.type == MediaType.TV:
|
||||
size = tv_size
|
||||
else:
|
||||
size = movie_size
|
||||
# 大小范围
|
||||
begin_size, end_size = __get_size_range(size)
|
||||
if begin_size or end_size:
|
||||
meta = MetaInfo(title=torrent_info.title, subtitle=torrent_info.description)
|
||||
# 集数
|
||||
if mediainfo.type == MediaType.TV:
|
||||
# 电视剧
|
||||
season = meta.begin_season or 1
|
||||
if meta.total_episode:
|
||||
# 识别的总集数
|
||||
episodes_num = meta.total_episode
|
||||
else:
|
||||
# 整季集数
|
||||
episodes_num = len(mediainfo.seasons.get(season) or [1])
|
||||
# 比较大小
|
||||
if not (begin_size * 1024 ** 3 <= (torrent_info.size / episodes_num) <= end_size * 1024 ** 3):
|
||||
logger.info(f"{torrent_info.title} {StringUtils.str_filesize(torrent_info.size)} "
|
||||
f"共{episodes_num}集,不匹配大小规则 {size}")
|
||||
return False
|
||||
else:
|
||||
# 电影比较大小
|
||||
if not (begin_size * 1024 ** 3 <= torrent_info.size <= end_size * 1024 ** 3):
|
||||
logger.info(
|
||||
f"{torrent_info.title} {StringUtils.str_filesize(torrent_info.size)} 不匹配大小规则 {size}")
|
||||
return False
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def match_torrent(mediainfo: MediaInfo, torrent_meta: MetaInfo, torrent: TorrentInfo) -> bool:
|
||||
"""
|
||||
|
||||
@@ -3,6 +3,7 @@ from typing import List, Tuple, Union, Dict, Optional
|
||||
|
||||
from app.core.context import TorrentInfo, MediaInfo
|
||||
from app.core.metainfo import MetaInfo
|
||||
from app.helper.rule import RuleHelper
|
||||
from app.log import logger
|
||||
from app.modules import _ModuleBase
|
||||
from app.modules.filter.RuleParser import RuleParser
|
||||
@@ -133,8 +134,17 @@ class FilterModule(_ModuleBase):
|
||||
},
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.rulehelper = RuleHelper()
|
||||
|
||||
def init_module(self) -> None:
|
||||
self.parser = RuleParser()
|
||||
# 加载用户自定义规则,如跟内置规则冲突,以用户自定义规则为准
|
||||
custom_rules = self.rulehelper.get_custom_rules()
|
||||
for rule in custom_rules:
|
||||
logger.info(f"加载自定义规则 {rule.id} - {rule.name}")
|
||||
self.rule_set[rule.id] = rule.dict()
|
||||
|
||||
@staticmethod
|
||||
def get_name() -> str:
|
||||
@@ -149,21 +159,45 @@ class FilterModule(_ModuleBase):
|
||||
def init_setting(self) -> Tuple[str, Union[str, bool]]:
|
||||
pass
|
||||
|
||||
def filter_torrents(self, rule_string: str,
|
||||
def filter_torrents(self, rule_groups: List[str],
|
||||
torrent_list: List[TorrentInfo],
|
||||
season_episodes: Dict[int, list] = None,
|
||||
mediainfo: MediaInfo = None) -> List[TorrentInfo]:
|
||||
"""
|
||||
过滤种子资源
|
||||
:param rule_string: 过滤规则
|
||||
:param rule_groups: 过滤规则组名称列表
|
||||
:param torrent_list: 资源列表
|
||||
:param season_episodes: 季集数过滤 {season:[episodes]}
|
||||
:param mediainfo: 媒体信息
|
||||
:return: 过滤后的资源列表,添加资源优先级
|
||||
"""
|
||||
if not rule_string:
|
||||
if not rule_groups:
|
||||
return torrent_list
|
||||
self.media = mediainfo
|
||||
# 查询规则表详情
|
||||
for group_name in rule_groups:
|
||||
rule_group = self.rulehelper.get_rule_group(group_name)
|
||||
if not rule_group:
|
||||
logger.error(f"规则组 {group_name} 不存在")
|
||||
continue
|
||||
if rule_group.media_type and rule_group.media_type != mediainfo.type.value:
|
||||
# 规则组不适用当前媒体类型
|
||||
continue
|
||||
# 过滤种子
|
||||
torrent_list = self.__filter_torrents(
|
||||
rule_string=rule_group.rule_string,
|
||||
rule_name=rule_group.name,
|
||||
torrent_list=torrent_list,
|
||||
season_episodes=season_episodes
|
||||
)
|
||||
return torrent_list
|
||||
|
||||
def __filter_torrents(self, rule_string: str, rule_name: str,
|
||||
torrent_list: List[TorrentInfo],
|
||||
season_episodes: Dict[int, list]) -> List[TorrentInfo]:
|
||||
"""
|
||||
过滤种子
|
||||
"""
|
||||
# 返回种子列表
|
||||
ret_torrents = []
|
||||
for torrent in torrent_list:
|
||||
@@ -173,7 +207,8 @@ class FilterModule(_ModuleBase):
|
||||
continue
|
||||
# 能命中优先级的才返回
|
||||
if not self.__get_order(torrent, rule_string):
|
||||
logger.debug(f"种子 {torrent.site_name} - {torrent.title} {torrent.description} 不匹配优先级规则")
|
||||
logger.debug(f"种子 {torrent.site_name} - {torrent.title} {torrent.description} "
|
||||
f"不匹配 {rule_name} 过滤规则")
|
||||
continue
|
||||
ret_torrents.append(torrent)
|
||||
|
||||
@@ -196,7 +231,8 @@ class FilterModule(_ModuleBase):
|
||||
torrent_episodes = meta.episode_list
|
||||
if not set(torrent_seasons).issubset(set(seasons)):
|
||||
# 种子季不在过滤季中
|
||||
logger.debug(f"种子 {torrent.site_name} - {torrent.title} 包含季 {torrent_seasons} 不是需要的季 {list(seasons)}")
|
||||
logger.debug(
|
||||
f"种子 {torrent.site_name} - {torrent.title} 包含季 {torrent_seasons} 不是需要的季 {list(seasons)}")
|
||||
return False
|
||||
if not torrent_episodes:
|
||||
# 整季按匹配处理
|
||||
@@ -207,7 +243,7 @@ class FilterModule(_ModuleBase):
|
||||
and not set(torrent_episodes).intersection(set(need_episodes)):
|
||||
# 单季集没有交集的不要
|
||||
logger.debug(f"种子 {torrent.site_name} - {torrent.title} "
|
||||
f"集 {torrent_episodes} 没有需要的集:{need_episodes}")
|
||||
f"集 {torrent_episodes} 没有需要的集:{need_episodes}")
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -290,6 +326,10 @@ class FilterModule(_ModuleBase):
|
||||
includes = self.rule_set[rule_name].get("include") or []
|
||||
# 排除规则项
|
||||
excludes = self.rule_set[rule_name].get("exclude") or []
|
||||
# 大小范围规则项
|
||||
size_range = self.rule_set[rule_name].get("size_range")
|
||||
# 做种人数规则项
|
||||
seeders = self.rule_set[rule_name].get("seeders")
|
||||
# FREE规则
|
||||
downloadvolumefactor = self.rule_set[rule_name].get("downloadvolumefactor")
|
||||
for include in includes:
|
||||
@@ -300,6 +340,14 @@ class FilterModule(_ModuleBase):
|
||||
if re.search(r"%s" % exclude, content, re.IGNORECASE):
|
||||
# 发现排除项
|
||||
return False
|
||||
if size_range:
|
||||
if not self.__match_size(torrent, size_range):
|
||||
# 大小范围不匹配
|
||||
return False
|
||||
if seeders:
|
||||
if torrent.seeders < int(seeders):
|
||||
# 做种人数不匹配
|
||||
return False
|
||||
if downloadvolumefactor is not None:
|
||||
if torrent.downloadvolumefactor != downloadvolumefactor:
|
||||
# FREE规则不匹配
|
||||
@@ -310,6 +358,7 @@ class FilterModule(_ModuleBase):
|
||||
"""
|
||||
判断种子是否匹配TMDB规则
|
||||
"""
|
||||
|
||||
def __get_media_value(key: str):
|
||||
try:
|
||||
return getattr(self.media, key)
|
||||
@@ -346,3 +395,30 @@ class FilterModule(_ModuleBase):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def __match_size(torrent: TorrentInfo, size_range: str) -> bool:
|
||||
"""
|
||||
判断种子是否匹配大小范围(MB)
|
||||
"""
|
||||
if not size_range:
|
||||
return True
|
||||
size_range = size_range.strip()
|
||||
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 size_min <= torrent.size <= size_max:
|
||||
return True
|
||||
elif size_range.startswith(">"):
|
||||
# 大于
|
||||
size_min = float(size_range[1:].strip()) * 1024 * 1024
|
||||
if torrent.size >= size_min:
|
||||
return True
|
||||
elif size_range.startswith("<"):
|
||||
# 小于
|
||||
size_max = float(size_range[1:].strip()) * 1024 * 1024
|
||||
if torrent.size <= size_max:
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -13,5 +13,5 @@ from .mediaserver import *
|
||||
from .message import *
|
||||
from .tmdb import *
|
||||
from .transfer import *
|
||||
from .filetransfer import *
|
||||
from .rule import *
|
||||
from .system import *
|
||||
|
||||
33
app/schemas/rule.py
Normal file
33
app/schemas/rule.py
Normal file
@@ -0,0 +1,33 @@
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class CustomRule(BaseModel):
|
||||
"""
|
||||
自定义规则项
|
||||
"""
|
||||
# 规则ID
|
||||
id: Optional[str] = None
|
||||
# 名称
|
||||
name: Optional[str] = None
|
||||
# 包含
|
||||
include: Optional[list] = []
|
||||
# 排除
|
||||
exclude: Optional[list] = []
|
||||
# 大小范围
|
||||
size_range: Optional[str] = None
|
||||
# 最少做种人数
|
||||
seeders: Optional[str] = None
|
||||
|
||||
|
||||
class FilterRuleGroup(BaseModel):
|
||||
"""
|
||||
过滤规则组
|
||||
"""
|
||||
# 名称
|
||||
name: Optional[str] = None
|
||||
# 规则串
|
||||
rule_string: Optional[str] = None
|
||||
# 适用类媒体类别 None-全部 电影/电视剧
|
||||
media_type: Optional[str] = None
|
||||
@@ -84,16 +84,16 @@ class SystemConfigKey(Enum):
|
||||
TransferExcludeWords = "TransferExcludeWords"
|
||||
# 种子优先级规则
|
||||
TorrentsPriority = "TorrentsPriority"
|
||||
# 搜索优先级规则
|
||||
SearchFilterRules = "SearchFilterRules"
|
||||
# 订阅优先级规则
|
||||
SubscribeFilterRules = "SubscribeFilterRules"
|
||||
# 洗版规则
|
||||
BestVersionFilterRules = "BestVersionFilterRules"
|
||||
# 默认订阅过滤规则
|
||||
DefaultFilterRules = "DefaultFilterRules"
|
||||
# 默认搜索过滤规则
|
||||
DefaultSearchFilterRules = "DefaultSearchFilterRules"
|
||||
# 用户自定义规则
|
||||
CustomFilterRules = "CustomFilterRules"
|
||||
# 用户规则组
|
||||
UserRuleGroups = "UserRuleGroups"
|
||||
# 搜索默认过滤规则
|
||||
SearchFilterRuleGroups = "SearchFilterRuleGroups"
|
||||
# 订阅默认过滤规则
|
||||
SubscribeFilterRuleGroups = "SubscribeFilterRuleGroups"
|
||||
# 洗版默认过滤规则
|
||||
BeseVersionFilterRuleGroups = "BeseVersionFilterRuleGroups"
|
||||
# 订阅统计
|
||||
SubscribeReport = "SubscribeReport"
|
||||
# 用户自定义CSS
|
||||
|
||||
Reference in New Issue
Block a user