-
Notifications
You must be signed in to change notification settings - Fork 85
/
Copy pathfilter.py
389 lines (364 loc) · 15.4 KB
/
filter.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
import re
import log
from app.conf import ModuleConf
from app.helper import DbHelper
from app.media.meta import ReleaseGroupsMatcher
from app.utils import StringUtils
from app.utils.commons import SingletonMeta
from app.utils.types import MediaType
class Filter(metaclass=SingletonMeta):
rg_matcher = None
dbhelper = None
_groups = []
_rules = []
def __init__(self):
self.init_config()
def init_config(self):
self.dbhelper = DbHelper()
self.rg_matcher = ReleaseGroupsMatcher()
self._groups = self.get_filter_group()
self._rules = self.get_filter_rule()
def get_rule_groups(self, groupid=None, default=False):
"""
获取所有规则组
"""
ret_groups = []
for group in self._groups:
group_info = {
"id": group.ID,
"name": group.GROUP_NAME,
"default": group.IS_DEFAULT,
"note": group.NOTE
}
if (groupid and str(groupid) == str(group.ID)) \
or (default and group.IS_DEFAULT == "Y"):
return group_info
ret_groups.append(group_info)
if groupid or default:
return {}
return ret_groups
def get_rule_infos(self):
"""
获取所有的规则组及组内的规则
"""
groups = self.get_rule_groups()
for group in groups:
group['rules'] = self.get_rules(group.get("id"))
return groups
def get_rules(self, groupid, ruleid=None):
"""
获取过滤规则
"""
if not groupid:
return []
ret_rules = []
for rule in self._rules:
rule_info = {
"id": rule.ID,
"group": rule.GROUP_ID,
"name": rule.ROLE_NAME,
"pri": rule.PRIORITY or 0,
"include": rule.INCLUDE.split("\n") if rule.INCLUDE else [],
"exclude": rule.EXCLUDE.split("\n") if rule.EXCLUDE else [],
"size": rule.SIZE_LIMIT,
"free": rule.NOTE,
"free_text": {
"1.0 1.0": "普通",
"1.0 0.0": "免费",
"2.0 0.0": "2X免费"
}.get(rule.NOTE, "全部") if rule.NOTE else ""
}
if str(rule.GROUP_ID) == str(groupid) \
and (not ruleid or int(ruleid) == rule.ID):
ret_rules.append(rule_info)
if ruleid:
return ret_rules[0] if ret_rules else {}
return ret_rules
def get_rule_first_order(self, rulegroup):
"""
获取规则的最高优先级
"""
if not rulegroup:
rulegroup = self.get_rule_groups(default=True)
first_order = min([int(rule_info.get("pri")) for rule_info in self.get_rules(groupid=rulegroup)] or [0])
return 100 - first_order
def check_rules(self, meta_info, rulegroup=None):
"""
检查种子是否匹配站点过滤规则:排除规则、包含规则,优先规则
:param meta_info: 识别的信息
:param rulegroup: 规则组ID
:return: 是否匹配,匹配的优先值,规则名称,值越大越优先
"""
if not meta_info:
return False, 0, ""
# 为-1时不使用过滤规则
if rulegroup and int(rulegroup) == -1:
return True, 0, "不过滤"
# 过滤使用的文本
title = meta_info.rev_string
if meta_info.subtitle:
title = f"{title} {meta_info.subtitle}"
# 过滤规则组
if not rulegroup:
rulegroup = self.get_rule_groups(default=True)
if not rulegroup:
return True, 0, "未配置过滤规则"
else:
rulegroup = self.get_rule_groups(groupid=rulegroup)
filters = self.get_rules(groupid=rulegroup.get("id"))
# 命中优先级
order_seq = 0
# 当前规则组是否命中
group_match = True
for filter_info in filters:
try:
# 当前规则是否命中
rule_match = True
# 命中规则的序号
order_seq = 100 - int(filter_info.get('pri'))
# 必须包括的项
includes = filter_info.get('include')
if includes and rule_match:
include_flag = True
for include in includes:
if not include:
continue
if not re.search(r'%s' % include.strip(), title, re.IGNORECASE):
include_flag = False
break
if not include_flag:
rule_match = False
# 不能包含的项
excludes = filter_info.get('exclude')
if excludes and rule_match:
exclude_flag = False
exclude_count = 0
for exclude in excludes:
if not exclude:
continue
exclude_count += 1
if not re.search(r'%s' % exclude.strip(), title, re.IGNORECASE):
exclude_flag = True
if exclude_count > 0 and not exclude_flag:
rule_match = False
# 大小
sizes = filter_info.get('size')
if sizes and rule_match and meta_info.size:
meta_info.size = StringUtils.num_filesize(meta_info.size)
if sizes.find(',') != -1:
sizes = sizes.split(',')
if StringUtils.is_numeric(sizes[0]):
begin_size = float(sizes[0].strip())
else:
begin_size = 0
if StringUtils.is_numeric(sizes[1]):
end_size = float(sizes[1].strip())
else:
end_size = 0
else:
begin_size = 0
if StringUtils.is_numeric(sizes):
end_size = float(sizes.strip())
else:
end_size = 0
if meta_info.type == MediaType.MOVIE:
if not begin_size * 1024 ** 3 <= int(meta_info.size) <= end_size * 1024 ** 3:
rule_match = False
else:
if meta_info.total_episodes \
and not begin_size * 1024 ** 3 <= int(meta_info.size) / int(meta_info.total_episodes) <= end_size * 1024 ** 3:
rule_match = False
# 促销
free = filter_info.get("free")
if free and meta_info.upload_volume_factor is not None and meta_info.download_volume_factor is not None:
ul_factor, dl_factor = free.split()
if float(ul_factor) > meta_info.upload_volume_factor \
or float(dl_factor) < meta_info.download_volume_factor:
rule_match = False
if rule_match:
return True, order_seq, rulegroup.get("name")
else:
group_match = False
except Exception as err:
log.error(f"【Filter】过滤规则出现严重错误 {err},请检查:{filter_info}")
if not group_match:
return False, 0, rulegroup.get("name")
return True, order_seq, rulegroup.get("name")
def is_rule_free(self, rulegroup=None):
"""
判断规则中是否需要Free检测
"""
if not rulegroup:
rulegroup = self.get_rule_groups(default=True)
if not rulegroup:
return True, 0, ""
else:
rulegroup = self.get_rule_groups(groupid=rulegroup)
filters = self.get_rules(groupid=rulegroup.get("id"))
for filter_info in filters:
if filter_info.get("free"):
return True
return False
@staticmethod
def is_torrent_match_sey(media_info, s_num, e_num, year_str):
"""
种子名称关键字匹配
:param media_info: 已识别的种子信息
:param s_num: 要匹配的季号,为空则不匹配
:param e_num: 要匹配的集号,为空则不匹配
:param year_str: 要匹配的年份,为空则不匹配
:return: 是否命中
"""
if s_num:
if not media_info.get_season_list():
return False
if not isinstance(s_num, list):
s_num = [s_num]
if not set(s_num).issuperset(set(media_info.get_season_list())):
return False
if e_num:
if not isinstance(e_num, list):
e_num = [e_num]
if not set(e_num).issuperset(set(media_info.get_episode_list())):
return False
if year_str:
if str(media_info.year) != str(year_str):
return False
return True
def check_torrent_filter(self,
meta_info,
filter_args,
uploadvolumefactor=None,
downloadvolumefactor=None):
"""
对种子进行过滤
:param meta_info: 名称识别后的MetaBase对象
:param filter_args: 过滤条件的字典
:param uploadvolumefactor: 种子的上传因子 传空不过滤
:param downloadvolumefactor: 种子的下载因子 传空不过滤
:return: 是否匹配,匹配的优先值,匹配信息,值越大越优先
"""
# 过滤包含,排除,关键字使用的文本
text = meta_info.rev_string
if meta_info.subtitle:
text = f"{text} {meta_info.subtitle}"
# 过滤质量
if filter_args.get("restype"):
restype_re = ModuleConf.TORRENT_SEARCH_PARAMS["restype"].get(filter_args.get("restype"))
if not meta_info.get_edtion_string():
return False, 0, f"{meta_info.org_string} 不符合质量 {filter_args.get('restype')} 要求"
if restype_re and not re.search(r"%s" % restype_re, meta_info.get_edtion_string(), re.I):
return False, 0, f"{meta_info.org_string} 不符合质量 {filter_args.get('restype')} 要求"
# 过滤分辨率
if filter_args.get("pix"):
pix_re = ModuleConf.TORRENT_SEARCH_PARAMS["pix"].get(filter_args.get("pix"))
if not meta_info.resource_pix:
return False, 0, f"{meta_info.org_string} 不符合分辨率 {filter_args.get('pix')} 要求"
if pix_re and not re.search(r"%s" % pix_re, meta_info.resource_pix, re.I):
return False, 0, f"{meta_info.org_string} 不符合分辨率 {filter_args.get('pix')} 要求"
# 过滤制作组/字幕组
if filter_args.get("team"):
team = filter_args.get("team")
if not meta_info.resource_team:
resource_team = self.rg_matcher.match(
title=meta_info.rev_string,
groups=team)
if not resource_team:
return False, 0, f"{meta_info.org_string} 不符合制作组/字幕组 {team} 要求"
else:
meta_info.resource_team = resource_team
elif not re.search(r"%s" % team, meta_info.resource_team, re.I):
return False, 0, f"{meta_info.org_string} 不符合制作组/字幕组 {team} 要求"
# 过滤促销
if filter_args.get("sp_state"):
ul_factor, dl_factor = filter_args.get("sp_state").split()
if uploadvolumefactor and ul_factor not in ("*", str(uploadvolumefactor)):
return False, 0, f"{meta_info.org_string} 不符合促销要求"
if downloadvolumefactor and dl_factor not in ("*", str(downloadvolumefactor)):
return False, 0, f"{meta_info.org_string} 不符合促销要求"
# 过滤包含
if filter_args.get("include"):
include = filter_args.get("include")
if not re.search(r"%s" % include, text, re.I):
return False, 0, f"{meta_info.org_string} 不符合包含 {include} 要求"
# 过滤排除
if filter_args.get("exclude"):
exclude = filter_args.get("exclude")
if re.search(r"%s" % exclude, text, re.I):
return False, 0, f"{meta_info.org_string} 不符合排除 {exclude} 要求"
# 过滤关键字
if filter_args.get("key"):
key = filter_args.get("key")
if not re.search(r"%s" % key, text, re.I):
return False, 0, f"{meta_info.org_string} 不符合 {key} 要求"
# 过滤过滤规则,-1表示不使用过滤规则,空则使用默认过滤规则
if filter_args.get("rule"):
# 已设置默认规则
match_flag, order_seq, rule_name = self.check_rules(meta_info, filter_args.get("rule"))
match_msg = "%s 大小:%s 促销:%s 不符合订阅/站点过滤规则 %s 要求" % (
meta_info.org_string,
StringUtils.str_filesize(meta_info.size),
meta_info.get_volume_factor_string(),
rule_name
)
return match_flag, order_seq, match_msg
else:
# 默认过滤规则
match_flag, order_seq, rule_name = self.check_rules(meta_info)
match_msg = "%s 大小:%s 促销:%s 不符合默认过滤规则 %s 要求" % (
meta_info.org_string,
StringUtils.str_filesize(meta_info.size),
meta_info.get_volume_factor_string(),
rule_name
)
return match_flag, order_seq, match_msg
def add_group(self, name, default='N'):
"""
添加过滤规则组
"""
ret = self.dbhelper.add_filter_group(name, default)
self.init_config()
return ret
def delete_filtergroup(self, groupid):
"""
删除过滤规则组
"""
ret = self.dbhelper.delete_filtergroup(groupid)
self.init_config()
return ret
def set_default_filtergroup(self, groupid):
"""
设置默认过滤规则组
"""
ret = self.dbhelper.set_default_filtergroup(groupid)
self.init_config()
return ret
def add_filter_rule(self, item, ruleid=None):
"""
添加过滤规则
"""
ret = self.dbhelper.insert_filter_rule(item, ruleid)
self.init_config()
return ret
def delete_filterrule(self, ruleid):
"""
删除过滤规则
"""
ret = self.dbhelper.delete_filterrule(ruleid)
self.init_config()
return ret
def get_filter_group(self, gid=None):
"""
获取过滤规则组
"""
return self.dbhelper.get_config_filter_group(gid)
def get_filter_rule(self, groupid=None):
"""
获取过滤规则
"""
return self.dbhelper.get_config_filter_rule(groupid)
def get_filter_groupid_by_name(self, name):
"""
根据名称获取过滤规则组ID
"""
return self.dbhelper.get_filter_groupid_by_name(name)