参考GitHub大神的项目,不过目前大佬好像不更新了,我只用它rss皇后的免费种,不过貌似有点问题,还好有另一位大佬解决了,提了PR,我在这里备份一下,防止以后自己忘记。如果有大佬还不会用flexget,可以先参考:
首先在flexget
目录下新建plugins
目录:
1 2 |
~/.flexget/plugins/ # Linux C:\Users\<YOURUSER>\flexget\plugins\ # Windows |
然后,新建nexusphp.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 |
# coding=utf-8 from __future__ import unicode_literals, division, absolute_import import time from builtins import * import concurrent.futures import re import logging from datetime import datetime from requests.adapters import HTTPAdapter from flexget import plugin from flexget.config_schema import one_or_more from flexget.event import event from flexget.utils.soup import get_soup from flexget.utils.tools import parse_timedelta log = logging.getLogger('nexusphp') class NexusPHP(object): """ 配置示例 task_name: rss: url: https://www.example.com/rss.xml other_fields: - link nexusphp: cookie: 'my_cookie' discount: - free - 2x seeders: min: 1 max: 30 leechers: min: 1 max: 100 max_complete: 0.8 hr: no """ schema = { 'type': 'object', 'properties': { 'cookie': {'type': 'string'}, 'discount': one_or_more({'type': 'string', 'enum': ['free', '2x', '2xfree', '30%', '50%', '2x50%']}), 'seeders': { 'type': 'object', 'properties': { 'min': {'type': 'integer', 'minimum': 0, 'default': 0}, 'max': {'type': 'integer', 'minimum': 0, 'default': 100000} } }, 'leechers': { 'type': 'object', 'properties': { 'min': {'type': 'integer', 'minimum': 0, 'default': 0}, 'max': {'type': 'integer', 'minimum': 0, 'default': 100000}, 'max_complete': {'type': 'number', 'minimum': 0, 'maximum': 1, 'default': 1} } }, 'left-time': {'type': 'string', 'format': 'interval'}, 'hr': {'type': 'boolean'}, 'adapter': { 'type': 'object', 'properties': { 'free': {'type': 'string', 'default': 'free'}, '2x': {'type': 'string', 'default': 'twoup'}, '2xfree': {'type': 'string', 'default': 'twoupfree'}, '30%': {'type': 'string', 'default': 'thirtypercent'}, '50%': {'type': 'string', 'default': 'halfdown'}, '2x50%': {'type': 'string', 'default': 'twouphalfdown'} } }, 'comment': {'type': 'boolean'}, 'user-agent': {'type': 'string'}, 'remember': {'type': 'boolean', 'default': True} }, 'required': ['cookie'] } @staticmethod def build_config(config): config = dict(config) config.setdefault('discount', None) config.setdefault('seeders', None) config.setdefault('leechers', None) config.setdefault('left-time', None) config.setdefault('hr', True) config.setdefault('adapter', None) config.setdefault('user-agent', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko)' 'Chrome/89.0.4389.72 Safari/537.36 Edg/89.0.774.45') return config @plugin.priority(127) def on_task_modify(self, task, config): if config.get('comment', False): for entry in task.entries: if 'torrent' in entry and 'link' in entry: entry['torrent'].content['comment'] = entry['link'] entry['torrent'].modified = True def on_task_filter(self, task, config): config = self.build_config(config) adapter = HTTPAdapter(max_retries=5) task.requests.mount('http://', adapter) task.requests.mount('https://', adapter) # 先访问一次 预防异常 headers = { 'cookie': config['cookie'], 'user-agent': config['user-agent'] } try: task.requests.get(task.entries[0].get('link'), headers=headers) except Exception: pass def consider_entry(_entry, _link): try: discount, seeders, leechers, hr, expired_time = NexusPHP._get_info(task, _link, config) except plugin.PluginError as e: raise e except Exception as e: log.info('NexusPHP._get_info: ' + str(e)) return remember = config['remember'] if config['discount']: if discount not in config['discount']: _entry.reject('%s does not match discount' % discount, remember=remember) # 优惠信息不匹配 return if config['left-time'] and expired_time: left_time = expired_time - datetime.now() # 实际剩余时间 < 'left-time' if left_time < parse_timedelta(config['left-time']): _entry.reject('its discount time only left [%s]' % left_time, remember=remember) # 剩余时间不足 return if config['hr'] is False and hr: _entry.reject('it is HR', remember=True) # 拒绝HR if config['seeders']: seeder_max = config['seeders']['max'] seeder_min = config['seeders']['min'] if len(seeders) not in range(seeder_min, seeder_max + 1): _entry.reject('%d is out of range of seeder' % len(seeders), remember=True) # 做种人数不匹配 return if config['leechers']: leecher_max = config['leechers']['max'] leecher_min = config['leechers']['min'] if len(leechers) not in range(leecher_min, leecher_max + 1): _entry.reject('%d is out of range of leecher' % len(leechers), remember=True) # 下载人数不匹配 return if len(leechers) != 0: max_complete = max(leechers, key=lambda x: x['completed'])['completed'] else: max_complete = 0 if max_complete > config['leechers']['max_complete']: _entry.reject('%f is more than max_complete' % max_complete, remember=True) # 最大完成度不匹配 return _entry.accept() futures = [] # 线程任务 with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor: for entry in task.accepted + task.undecided: link = entry.get('link') if not link: raise plugin.PluginError("The rss plugin require 'other_fields' which contain 'link'. " "For example: other_fields: - link") futures.append(executor.submit(consider_entry, entry, link)) time.sleep(0.5) for f in concurrent.futures.as_completed(futures): exception = f.exception() if isinstance(exception, plugin.PluginError): log.error(exception) @staticmethod # 解析页面,获取优惠、做种者信息、下载者信息 def info_from_page(detail_page, peer_page, discount_fn, hr_fn=None): try: discount, expired_time = discount_fn(detail_page) except Exception: discount = expired_time = None # 无优惠 try: if hr_fn: hr = hr_fn(detail_page) else: hr = False for item in ['hitandrun', 'hit_run.gif', 'Hit and Run', 'Hit & Run']: if item in detail_page.text: hr = True break except Exception: hr = False # 无HR soup = get_soup(peer_page.replace('\n', ''), 'html5lib') seeders = leechers = [] tables = soup.find_all('table', limit=2) if len(tables) == 2: # 1. seeder leecher 均有 seeders = NexusPHP.get_peers(tables[0]) leechers = NexusPHP.get_peers(tables[1]) elif len(tables) == 1 and len(soup.body.contents) == 3: # 2. seeder leecher 有其一 nodes = soup.body.contents if nodes[1].name == 'table': # 2.1 只有seeder 在第二个节点 seeders = NexusPHP.get_peers(nodes[1]) else: # 2.2 只有leecher 在第三个节点 leechers = NexusPHP.get_peers(nodes[2]) else: # 3. seeder leecher 均无 seeders = leechers = [] return discount, seeders, leechers, hr, expired_time @staticmethod def get_peers(table): peers = [] name_index = 0 connectable_index = 1 uploaded_index = 2 downloaded_index = 4 completed_index = 7 for index, tr in enumerate(table.find_all('tr')): try: if index == 0: tds = tr.find_all('td') for i, td in enumerate(tds): text = td.get_text() if text in ['用户', '用戶', '会员/IP']: name_index = i elif text in ['可连接', '可連接', '公网']: connectable_index = i elif text in ['上传', '上傳', '总上传']: uploaded_index = i elif text in ['下载', '下載', '本次下载']: downloaded_index = i elif text == '完成': completed_index = i else: tds = tr.find_all('td') peers.append({ 'name': tds[name_index].get_text(), 'connectable': True if tds[connectable_index].get_text() != '是' else False, 'uploaded': tds[uploaded_index].get_text(), 'downloaded': tds[downloaded_index].get_text(), 'completed': float(tds[completed_index].get_text().strip('%')) / 100 }) except Exception: peers.append({ 'name': '', 'connectable': False, 'uploaded': '', 'downloaded': '', 'completed': 0 }) return peers @staticmethod def _get_info(task, link, config): headers = { 'cookie': config['cookie'], 'user-agent': config['user-agent'] } if 'open.cd' in link: link = link.replace('details.php', 'plugin_details.php') detail_page = task.requests.get(link, headers=headers, timeout=20) # 详情 detail_page.encoding = 'utf-8' def get_peer_page(): if 'totheglory' in link: return '' peer_url = link.replace('details.php', 'viewpeerlist.php', 1) try: if config['seeders'] or config['leechers']: # 配置了seeders、leechers才请求 return task.requests.get(peer_url, headers=headers).text # peer详情 except Exception: return '' return '' peer_page = get_peer_page() if 'login' in detail_page.url or 'portal.php' in detail_page.url: raise plugin.PluginError("Can't access the site. Your cookie may be wrong!") if config['adapter']: convert = {value: key for key, value in config['adapter'].items()} discount_fn = NexusPHP.generate_discount_fn(convert) return NexusPHP.info_from_page(detail_page, peer_page, discount_fn) sites_discount = { 'chdbits': { 'pro_free.*?</h1>': 'free', 'pro_2up.*?</h1>': '2x', 'pro_free2up.*?</h1>': '2xfree', 'pro_30pctdown.*?</h1>': '30%', 'pro_50pctdown.*?</h1>': '50%', 'pro_50pctdown2up.*?</h1>': '2x50%' }, 'u2.dmhy': { 'class=.pro_free.*?promotion.*?</td>': 'free', 'class=.pro_2up.*?promotion.*?</td>': '2x', 'class=.pro_free2up.*?promotion.*?</td>': '2xfree', 'class=.pro_30pctdown.*?promotion.*?</td>': '30%', 'class=.pro_50pctdown.*?promotion.*?</td>': '50%', 'class=.pro_50pctdown2up.*?promotion.*?</td>': '2x50%', 'class=.pro_custom.*?0\.00X.*?promotion.*?</td>': '2xfree' }, 'totheglory': { '本种子限时不计流量.*?</font>': 'free', '本种子的下载流量计为实际流量的30%.*?</font>': '30%', '本种子的下载流量会减半.*?</font>': '50%', }, 'hdchina': { 'pro_free.*?</h2>': 'free', 'pro_2up.*?</h2>': '2x', 'pro_free2up.*?</h2>': '2xfree', 'pro_30pctdown.*?</h2>': '30%', 'pro_50pctdown.*?</h2>': '50%', 'pro_50pctdown2up.*?</h2>': '2x50%' } } for site, convert in sites_discount.items(): if site in link: discount_fn = NexusPHP.generate_discount_fn(convert) return NexusPHP.info_from_page(detail_page, peer_page, discount_fn) discount_fn = NexusPHP.generate_discount_fn({ 'class=\'free\'.*?免.*?</h1>': 'free', 'class=\'twoup\'.*?2X.*?</h1>': '2x', 'class=\'twoupfree\'.*?2X免.*?</h1>': '2xfree', 'class=\'thirtypercent\'.*?30%.*?</h1>': '30%', 'class=\'halfdown\'.*?50%.*?</h1>': '50%', 'class=\'twouphalfdown\'.*?2X 50%.*?</h1>': '2x50%' }) return NexusPHP.info_from_page(detail_page, peer_page, discount_fn) @staticmethod def generate_discount_fn(convert): def fn(page): html = page.text.replace('\n', '') for key, value in convert.items(): match = re.search(key, html) if match: discount_str = match.group(0) expired_time = None # 匹配优惠剩余时间 match = re.search('(\d{4})(-\d{1,2}){2}\s\d{1,2}(:\d{1,2}){2}', discount_str) if match: expired_time_str = match.group(0) expired_time = datetime.strptime(expired_time_str, "%Y-%m-%d %H:%M:%S") return value, expired_time return None, None return fn @event('plugin.register') def register_plugin(): plugin.register(NexusPHP, 'nexusphp', api_ver=2) |
注意更换user-agent为自己的。
然后打开flexget配置文件,修改为以下:
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 |
tasks: # hdsky: # rss: https://hdsky # accept_all: no # content_size: #启用大小过滤 # min: 10240 # 文件小于 2048M 就不下载 # max: 999900 # 文件大于 9999M 就不下载 # strict: no #不要动 # download: /Users/mcj/Downloads/CCC不备份/flexget/hdsky opencd: rss: url: https://open other_fields: - link accept_all: yes download: /Users/mcj/Downloads/CCC不备份/flexget/opencd nexusphp: cookie: '_ga=GAxxxxxxxxxxxx208746' discount: - free - 2x adapter: 2x50%: pro_50pctdown2up 2x: pro_2up 2xfree: pro_free2up 30%: pro_30pctdown 50%: pro_50pctdown free: pro_free |
接下来就OK了。
想使用flexget过滤指定大小的种子?
本文最后更新于2021年4月15日,已超过 1 年没有更新,如果文章内容或图片资源失效,请留言反馈,我们会及时处理,谢谢!
多谢大佬,我应该怎么配置?是不是这里把自己的rss链接写在url里,cookie需要取到写进去就行了?
配置示例
task_name:
rss:
url: https://www.example.com/rss.xml
other_fields:
– link
nexusphp:
cookie: ‘my_cookie’
@noteeth 这个是有严格的缩进要求的
@马春杰杰 缩进我能处理,我想确认一下,如果是ttg的话,我在url那里是填ttg的rss订阅的链接,然后cookie那里我该填什么?我用的是cookie editor,但是里面的值很多,我也不知道该填什么
@noteeth 你按f12打开控制台,然后找到cookie:
@马春杰杰 o,是把很长的这段直接都cp过去是吧?那明白了.
另外,url那里是把我自己的rss订阅链接cp上去吗?
@noteeth 是的
@马春杰杰 貌似成了,但是貌似又报错,这个是不是cookie的问题还是rss的问题?
22-02-21 22:57 VERBOSE task ttg-free REJECTED:
Fender Bender 2016 REPACK 1080p BluRay REMUX AVC DTS-HD MA 5 1-TRiToN [车祸 Fender Bender (2016) | 导演: Mark Pavia | 主演: 卡西迪·弗里曼 ] 23.66 GB
by retry_failed plugin because waiting before retrying entry which has failed in the past. (failure reason: Unexpected html content received fromhttps://totheglory.im/details.php?id=486409
– maybe a login page?)大佬 这个瓷器站能用么,我瓷器用这个,会下载不免费的种
@Geekman 这个插件目前貌似对瓷器支持的并不是很好,很多人都不能用。
对于瓷器,我是直接用qb来R官种,没用插件。
大佬,群晖 docker下,这个plugins文件夹应该建在哪个路径下
@Anooki 按照通用配置来说,应该是放到/config目录下
请教大佬,这个user-agent应该修改成什么啊
@hbya 可以按照这个修改,按F12,然后找到你的user-agent
![]()