参考GitHub大神的项目,不过目前大佬好像不更新了,我只用它rss皇后的免费种,不过貌似有点问题,还好有另一位大佬解决了,提了PR,我在这里备份一下,防止以后自己忘记。如果有大佬还不会用flexget,可以先参考:
首先在flexget
目录下新建plugins
目录:
~/.flexget/plugins/ # Linux C:\Users\<YOURUSER>\flexget\plugins\ # Windows
然后,新建nexusphp.py
文件,填入以下内容:
# 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配置文件,修改为以下:
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 年没有更新,如果文章内容或图片资源失效,请留言反馈,我们会及时处理,谢谢!