如何下载旧版本的IPA文件?如何下载IOS旧版本的APP?
推荐随时备份IPA文件,可以一劳永逸的解决旧版本应用的问题。下面是详细步骤: 首先下载旧版iTunes,下载地址:Mac、Windows 64 位、Windows 32 位。 备份链接:Mac、Windows 64 位、Windows 32 […]
Tracker: [Could not parse bencoded data: dbconn: mysql_connect: Connection refused]
使用rtorrent的时候出现这个问题: Tracker: [Could not parse bencoded data: dbconn: mysql_connect: Connection refused] 不要担心,这是源站暂时出现问题 […]
使用nexusPHP插件过滤frds中性种子
默认的nexusPHP插件虽然支持frds,但是不支持Neutral种子。只需要稍微修改下,即可。 下面是完整文件:
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 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 |
# 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%', 'Neutral']}), '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'}, 'Neutral': {'type': 'string', 'default': 'nl'} } }, '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) task.requests.headers.update({ 'cookie': config['cookie'], 'user-agent': config['user-agent'] }) # 先访问一次 预防异常 try: task.requests.get(task.entries[0].get('link')) 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): try: discount, expired_time = discount_fn(detail_page) except plugin.PluginError as e: raise e 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 plugin.PluginError as e: raise e 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): if 'open.cd' in link: link = link.replace('details.php', 'plugin_details.php') detail_page = task.requests.get(link, timeout=20) # 详情 detail_page.encoding = 'utf-8' 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!") # 1. peer page def get_peer_page(): if 'totheglory' in link: return '' if 'lemonhd' in link: # https://lemonhd.org/details_movie.php?id=xxx # https://lemonhd.org/details_music.php?id=xxx # ... peer_url = re.sub(r'details_\w+.php', 'viewpeerlist.php', link, 1) else: peer_url = link.replace('details.php', 'viewpeerlist.php', 1) try: if config['seeders'] or config['leechers']: # 配置了seeders、leechers才请求 return task.requests.get(peer_url).text # peer详情 except Exception: return '' return '' peer_page = get_peer_page() # 2. HR hr_fn = None if 'chdbits' in link: def chd_hr_fn(_page): if '<b>H&R' in _page.text: return True return False hr_fn = chd_hr_fn # 3. discount sites_discount = { 'chdbits': { 'pro_free2up.*?</h1>': '2xfree', 'pro_free.*?</h1>': 'free', 'pro_2up.*?</h1>': '2x', 'pro_50pctdown2up.*?</h1>': '2x50%', 'pro_30pctdown.*?</h1>': '30%', 'pro_50pctdown.*?</h1>': '50%' }, 'u2.dmhy': { 'class=.pro_2up.*?promotion.*?</td>': '2x', 'class=.pro_free2up.*?promotion.*?</td>': '2xfree', 'class=.pro_free.*?promotion.*?</td>': 'free', 'class=.pro_50pctdown2up.*?promotion.*?</td>': '2x50%', 'class=.pro_30pctdown.*?promotion.*?</td>': '30%', 'class=.pro_50pctdown.*?promotion.*?</td>': '50%', r'class=.pro_custom.*?0\.00X.*?promotion.*?</td>': '2xfree' }, 'totheglory': { '本种子限时不计流量.*?</font>': 'free', '本种子的下载流量计为实际流量的30%.*?</font>': '30%', '本种子的下载流量会减半.*?</font>': '50%', }, 'open.cd': { 'pro_free2up': '2xfree', 'pro_free': 'free', 'pro_2up': '2x', 'pro_50pctdown2up': '2x50%', 'pro_30pctdown': '30%', 'pro_50pctdown': '50%' } } 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%', 'class=\'nl\'.*?中性.*?</h1>': 'Neutral', }) for site, convert in sites_discount.items(): if site in link: discount_fn = NexusPHP.generate_discount_fn(convert) break if 'hdchina' in link: def _(page): return NexusPHP.get_discount_from_hdchina(page, task) discount_fn = _ 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, hr_fn) @staticmethod def get_discount_from_hdchina(details_page, task): soup = get_soup(details_page.text, 'html5lib') csrf = soup.find('meta', attrs={'name': 'x-csrf'})['content'] torrent_id = str(soup.find('div', class_='details_box').find('span', class_='sp_state_placeholder')['id']) res = task.requests.post('https://hdchina.org/ajax_promotion.php', data={ 'ids[]': torrent_id, 'csrf': csrf, }, timeout=10) """ sample response { 'status': 200, 'message': { '530584': { 'sp_state': '<p style="display: none"> <img class="pro_free" src="pic/trans.gif" alt="Free" onmouseover="domTT_activate(this, event, \'content\', \'<b><font class="free">免费</font></b>', 'timeout': '' } } } """ if res.status_code != 200: return None, None res = res.json() if res['status'] != 200: return None, None discount_info = res['message'][torrent_id] if 'sp_state' not in discount_info or not discount_info['sp_state']: return None, None if '<p style="display: none">' in discount_info['sp_state']: # HDC cookie 仅部分错误时会直接返回free # 同时带有特征 <p style="display: none"> # 也许是站点的BUG raise plugin.PluginError("Can't access the site. Your cookie may be wrong!") expired_time = None match = re.search(r'(\d{4})(-\d{1,2}){2}\s\d{1,2}(:\d{1,2}){2}', discount_info['timeout']) if match: expired_time_str = match.group(0) expired_time = datetime.strptime(expired_time_str, "%Y-%m-%d %H:%M:%S") discount_mapping = { 'class="pro_free2up"': '2xfree', 'class="pro_free"': 'free', 'class="pro_2up"': '2x', 'class="pro_50pctdown2up"': '2x50%', 'class="pro_30pctdown"': '30%', 'class="pro_50pctdown"': '50%' } for key, value in discount_mapping.items(): if key in discount_info['sp_state']: return value, expired_time return None, None @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(r'(\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) |
只需要在配置文件中加上: [crayon-6637e2d […]
关于Python中字典的赋值、浅拷贝、深拷贝
竟然把这个搞错了。。备份一下: 直接赋值: 两者直接赋值,相当于b是a的引用,所以当a发生了变化时,b也相应变化。 浅拷贝 浅拷贝只是复制了父目录的数据,如果a字典里的值都是非容器类型数据,那么浅拷贝就等于拷贝了一份a,如果a里面的值包含容 […]
/usr/bin/mv 参数列表过长
一次在移动文件的时候提示:/usr/bin/mv 参数列表过长 或者:bash: /bin/mv: Argument list too long 网上大部分人都让我们使用find命令替换,基本都是这样: find 源文件路径 -type f […]
Excel快速找到两列中不同的数据
今天需要对比Excel中两列数据,找到不同的单元格。由于数据量比较大,大概50万行,偶然发现一个快捷键,非常好用。 选中想要对比的两列数据,然后按: Ctrl+\ 即可快速定位到不同的位置,同时选中这些位置。是不是很棒~~ 本文最后更新于2 […]
macOS下利用快捷键快速插入当前日期
转自:Nozomi’s Blog,作者写的很棒哦~~在这里备份一下~ 今天看到一篇讲述命名文件方法的文章,讲述在文件创建之初,我们应采用系统化命名,为以后搜索节省时间。里面提到,文件名开头添加日期时间,有不少好处。 我就想,有没 […]
评测macOS下几款视频压缩/转格式软件
其他系列: 评测macOS下几款屏幕录制软件 评测macOS下几款视频剪辑软件 其实视频压缩主要是看你能接受的最低码率是多少,根据这个码率来压缩。码率下来了,体积自然下来了。如果输入视频是我们剪辑后输出的,体积一般是比较大的,不过我们可以查 […]
评测macOS下几款视频剪辑软件
其他系列: 评测macOS下几款视频压缩/转格式软件 评测macOS下几款屏幕录制软件 上次评测了macOS下几款屏幕录制软件,这次正好评测下几款视频剪辑软件。 项目原因,需要把几段视频简单去掉片头片尾,然后合并一下。所以这次 […]
如何拓展macOS内置词典
1 首先下载转换器: https://cloud.189.cn/t/rYvuUfAzEfiq(访问码:7gxs) 2 下载想要的词典: zh_CN 简体中文词典 (huzheng.org)或者从我备份的网盘下载:https://cloud. […]