@ -3,7 +3,7 @@ import re
|
|||||||
import traceback
|
import traceback
|
||||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Dict, Tuple
|
from typing import Dict
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
from app.chain import ChainBase
|
from app.chain import ChainBase
|
||||||
@ -352,102 +352,13 @@ class SearchChain(ChainBase):
|
|||||||
filter_rule = self.systemconfig.get(SystemConfigKey.DefaultSearchFilterRules)
|
filter_rule = self.systemconfig.get(SystemConfigKey.DefaultSearchFilterRules)
|
||||||
if not filter_rule:
|
if not filter_rule:
|
||||||
return torrents
|
return torrents
|
||||||
# 包含
|
|
||||||
include = filter_rule.get("include")
|
|
||||||
# 排除
|
|
||||||
exclude = filter_rule.get("exclude")
|
|
||||||
# 质量
|
|
||||||
quality = filter_rule.get("quality")
|
|
||||||
# 分辨率
|
|
||||||
resolution = filter_rule.get("resolution")
|
|
||||||
# 特效
|
|
||||||
effect = filter_rule.get("effect")
|
|
||||||
# 电影大小
|
|
||||||
movie_size = filter_rule.get("movie_size")
|
|
||||||
# 剧集单集大小
|
|
||||||
tv_size = filter_rule.get("tv_size")
|
|
||||||
|
|
||||||
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 __filter_torrent(t: TorrentInfo) -> bool:
|
|
||||||
"""
|
|
||||||
过滤种子
|
|
||||||
"""
|
|
||||||
# 包含
|
|
||||||
if include:
|
|
||||||
if not re.search(r"%s" % include,
|
|
||||||
f"{t.title} {t.description}", re.I):
|
|
||||||
logger.info(f"{t.title} 不匹配包含规则 {include}")
|
|
||||||
return False
|
|
||||||
# 排除
|
|
||||||
if exclude:
|
|
||||||
if re.search(r"%s" % exclude,
|
|
||||||
f"{t.title} {t.description}", re.I):
|
|
||||||
logger.info(f"{t.title} 匹配排除规则 {exclude}")
|
|
||||||
return False
|
|
||||||
# 质量
|
|
||||||
if quality:
|
|
||||||
if not re.search(r"%s" % quality, t.title, re.I):
|
|
||||||
logger.info(f"{t.title} 不匹配质量规则 {quality}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# 分辨率
|
|
||||||
if resolution:
|
|
||||||
if not re.search(r"%s" % resolution, t.title, re.I):
|
|
||||||
logger.info(f"{t.title} 不匹配分辨率规则 {resolution}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# 特效
|
|
||||||
if effect:
|
|
||||||
if not re.search(r"%s" % effect, t.title, re.I):
|
|
||||||
logger.info(f"{t.title} 不匹配特效规则 {effect}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# 大小
|
|
||||||
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=t.title, subtitle=t.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 <= (t.size / episodes_num) <= end_size * 1024 ** 3):
|
|
||||||
logger.info(f"{t.title} {StringUtils.str_filesize(t.size)} "
|
|
||||||
f"共{episodes_num}集,不匹配大小规则 {size}")
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
# 电影比较大小
|
|
||||||
if not (begin_size * 1024 ** 3 <= t.size <= end_size * 1024 ** 3):
|
|
||||||
logger.info(f"{t.title} {StringUtils.str_filesize(t.size)} 不匹配大小规则 {size}")
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
# 使用默认过滤规则再次过滤
|
# 使用默认过滤规则再次过滤
|
||||||
return list(filter(lambda t: __filter_torrent(t), torrents))
|
return list(filter(
|
||||||
|
lambda t: self.torrenthelper.filter_torrent(
|
||||||
|
torrent_info=t,
|
||||||
|
filter_rule=filter_rule,
|
||||||
|
mediainfo=mediainfo
|
||||||
|
),
|
||||||
|
torrents
|
||||||
|
))
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import json
|
import json
|
||||||
import random
|
import random
|
||||||
import re
|
|
||||||
import time
|
import time
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Dict, List, Optional, Union, Tuple
|
from typing import Dict, List, Optional, Union, Tuple
|
||||||
@ -18,6 +17,7 @@ from app.db.models.subscribe import Subscribe
|
|||||||
from app.db.subscribe_oper import SubscribeOper
|
from app.db.subscribe_oper import SubscribeOper
|
||||||
from app.db.systemconfig_oper import SystemConfigOper
|
from app.db.systemconfig_oper import SystemConfigOper
|
||||||
from app.helper.message import MessageHelper
|
from app.helper.message import MessageHelper
|
||||||
|
from app.helper.torrent import TorrentHelper
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.schemas import NotExistMediaInfo, Notification
|
from app.schemas import NotExistMediaInfo, Notification
|
||||||
from app.schemas.types import MediaType, SystemConfigKey, MessageChannel, NotificationType
|
from app.schemas.types import MediaType, SystemConfigKey, MessageChannel, NotificationType
|
||||||
@ -37,6 +37,7 @@ class SubscribeChain(ChainBase):
|
|||||||
self.mediachain = MediaChain()
|
self.mediachain = MediaChain()
|
||||||
self.message = MessageHelper()
|
self.message = MessageHelper()
|
||||||
self.systemconfig = SystemConfigOper()
|
self.systemconfig = SystemConfigOper()
|
||||||
|
self.torrenthelper = TorrentHelper()
|
||||||
|
|
||||||
def add(self, title: str, year: str,
|
def add(self, title: str, year: str,
|
||||||
mtype: MediaType = None,
|
mtype: MediaType = None,
|
||||||
@ -446,64 +447,19 @@ class SubscribeChain(ChainBase):
|
|||||||
|
|
||||||
def get_filter_rule(self, subscribe: Subscribe):
|
def get_filter_rule(self, subscribe: Subscribe):
|
||||||
"""
|
"""
|
||||||
获取订阅过滤规则,没有则返回默认规则
|
获取订阅过滤规则,同时组合默认规则
|
||||||
"""
|
"""
|
||||||
# 默认过滤规则
|
# 默认过滤规则
|
||||||
if (subscribe.include
|
default_rule = self.systemconfig.get(SystemConfigKey.DefaultFilterRules)
|
||||||
or subscribe.exclude
|
|
||||||
or subscribe.quality
|
|
||||||
or subscribe.resolution
|
|
||||||
or subscribe.effect):
|
|
||||||
return {
|
return {
|
||||||
"include": subscribe.include,
|
"include": subscribe.include or default_rule.get("include"),
|
||||||
"exclude": subscribe.exclude,
|
"exclude": subscribe.exclude or default_rule.get("exclude"),
|
||||||
"quality": subscribe.quality,
|
"quality": subscribe.quality or default_rule.get("quality"),
|
||||||
"resolution": subscribe.resolution,
|
"resolution": subscribe.resolution or default_rule.get("resolution"),
|
||||||
"effect": subscribe.effect,
|
"effect": subscribe.effect or default_rule.get("effect"),
|
||||||
|
"tv_size": subscribe.tv_size or default_rule.get("tv_size"),
|
||||||
|
"movie_size": subscribe.movie_size or default_rule.get("movie_size"),
|
||||||
}
|
}
|
||||||
# 订阅默认过滤规则
|
|
||||||
return self.systemconfig.get(SystemConfigKey.DefaultFilterRules) or {}
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def check_filter_rule(torrent_info: TorrentInfo, filter_rule: Dict[str, str]) -> bool:
|
|
||||||
"""
|
|
||||||
检查种子是否匹配订阅过滤规则
|
|
||||||
"""
|
|
||||||
if not filter_rule:
|
|
||||||
return True
|
|
||||||
# 包含
|
|
||||||
include = filter_rule.get("include")
|
|
||||||
if include:
|
|
||||||
if not re.search(r"%s" % include,
|
|
||||||
f"{torrent_info.title} {torrent_info.description}", re.I):
|
|
||||||
logger.info(f"{torrent_info.title} 不匹配包含规则 {include}")
|
|
||||||
return False
|
|
||||||
# 排除
|
|
||||||
exclude = filter_rule.get("exclude")
|
|
||||||
if exclude:
|
|
||||||
if re.search(r"%s" % exclude,
|
|
||||||
f"{torrent_info.title} {torrent_info.description}", re.I):
|
|
||||||
logger.info(f"{torrent_info.title} 匹配排除规则 {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
|
|
||||||
return True
|
|
||||||
|
|
||||||
def match(self, torrents: Dict[str, List[Context]]):
|
def match(self, torrents: Dict[str, List[Context]]):
|
||||||
"""
|
"""
|
||||||
@ -662,8 +618,9 @@ class SubscribeChain(ChainBase):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# 过滤规则
|
# 过滤规则
|
||||||
if not self.check_filter_rule(torrent_info=torrent_info,
|
if not self.torrenthelper.filter_torrent(torrent_info=torrent_info,
|
||||||
filter_rule=filter_rule):
|
filter_rule=filter_rule,
|
||||||
|
mediainfo=torrent_mediainfo):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 洗版时,优先级小于已下载优先级的不要
|
# 洗版时,优先级小于已下载优先级的不要
|
||||||
|
@ -1,20 +1,22 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import re
|
import re
|
||||||
|
import traceback
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Tuple, Optional, List, Union
|
from typing import Tuple, Optional, List, Union, Dict
|
||||||
from urllib.parse import unquote
|
from urllib.parse import unquote
|
||||||
|
|
||||||
from requests import Response
|
from requests import Response
|
||||||
from torrentool.api import Torrent
|
from torrentool.api import Torrent
|
||||||
|
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
from app.core.context import Context
|
from app.core.context import Context, TorrentInfo, MediaInfo
|
||||||
from app.core.metainfo import MetaInfo
|
from app.core.metainfo import MetaInfo
|
||||||
from app.db.systemconfig_oper import SystemConfigOper
|
from app.db.systemconfig_oper import SystemConfigOper
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.utils.http import RequestUtils
|
from app.utils.http import RequestUtils
|
||||||
from app.schemas.types import MediaType, SystemConfigKey
|
from app.schemas.types import MediaType, SystemConfigKey
|
||||||
from app.utils.singleton import Singleton
|
from app.utils.singleton import Singleton
|
||||||
|
from app.utils.string import StringUtils
|
||||||
|
|
||||||
|
|
||||||
class TorrentHelper(metaclass=Singleton):
|
class TorrentHelper(metaclass=Singleton):
|
||||||
@ -295,3 +297,97 @@ class TorrentHelper(metaclass=Singleton):
|
|||||||
"""
|
"""
|
||||||
if url not in self._invalid_torrents:
|
if url not in self._invalid_torrents:
|
||||||
self._invalid_torrents.append(url)
|
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
|
||||||
|
|
||||||
|
if not filter_rule:
|
||||||
|
return True
|
||||||
|
# 包含
|
||||||
|
include = filter_rule.get("include")
|
||||||
|
if include:
|
||||||
|
if not re.search(r"%s" % include,
|
||||||
|
f"{torrent_info.title} {torrent_info.description}", re.I):
|
||||||
|
logger.info(f"{torrent_info.title} 不匹配包含规则 {include}")
|
||||||
|
return False
|
||||||
|
# 排除
|
||||||
|
exclude = filter_rule.get("exclude")
|
||||||
|
if exclude:
|
||||||
|
if re.search(r"%s" % exclude,
|
||||||
|
f"{torrent_info.title} {torrent_info.description}", re.I):
|
||||||
|
logger.info(f"{torrent_info.title} 匹配排除规则 {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
|
||||||
|
Reference in New Issue
Block a user