From 604c418bd49489d9d8a44273e76f84180560a5af Mon Sep 17 00:00:00 2001 From: jxxghp Date: Thu, 4 Jul 2024 21:25:50 +0800 Subject: [PATCH] fix filter rules --- app/api/endpoints/system.py | 22 +++--- app/chain/__init__.py | 8 +- app/chain/search.py | 61 +++------------ app/chain/subscribe.py | 47 ++---------- app/helper/rule.py | 52 +++++++++++++ app/helper/torrent.py | 134 +-------------------------------- app/modules/filter/__init__.py | 88 ++++++++++++++++++++-- app/schemas/__init__.py | 2 +- app/schemas/rule.py | 33 ++++++++ app/schemas/types.py | 20 ++--- 10 files changed, 211 insertions(+), 256 deletions(-) create mode 100644 app/helper/rule.py create mode 100644 app/schemas/rule.py diff --git a/app/api/endpoints/system.py b/app/api/endpoints/system.py index 5874c895..8bf566cf 100644 --- a/app/api/endpoints/system.py +++ b/app/api/endpoints/system.py @@ -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 }) diff --git a/app/chain/__init__.py b/app/chain/__init__.py index 21a81aee..d5803929 100644 --- a/app/chain/__init__.py +++ b/app/chain/__init__.py @@ -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) diff --git a/app/chain/search.py b/app/chain/search.py index 02f9f03b..78527c17 100644 --- a/app/chain/search.py +++ b/app/chain/search.py @@ -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): """ diff --git a/app/chain/subscribe.py b/app/chain/subscribe.py index c5bb5955..94c25ecd 100644 --- a/app/chain/subscribe.py +++ b/app/chain/subscribe.py @@ -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 \ diff --git a/app/helper/rule.py b/app/helper/rule.py new file mode 100644 index 00000000..20fdb71c --- /dev/null +++ b/app/helper/rule.py @@ -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 diff --git a/app/helper/torrent.py b/app/helper/torrent.py index 9a141d4b..55eb97b2 100644 --- a/app/helper/torrent.py +++ b/app/helper/torrent.py @@ -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: """ diff --git a/app/modules/filter/__init__.py b/app/modules/filter/__init__.py index cabb92a3..75115b13 100644 --- a/app/modules/filter/__init__.py +++ b/app/modules/filter/__init__.py @@ -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 diff --git a/app/schemas/__init__.py b/app/schemas/__init__.py index 0ea1f602..f3fc1883 100644 --- a/app/schemas/__init__.py +++ b/app/schemas/__init__.py @@ -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 * diff --git a/app/schemas/rule.py b/app/schemas/rule.py new file mode 100644 index 00000000..dee67c04 --- /dev/null +++ b/app/schemas/rule.py @@ -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 diff --git a/app/schemas/types.py b/app/schemas/types.py index f3e8f8e7..aa67ac46 100644 --- a/app/schemas/types.py +++ b/app/schemas/types.py @@ -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