\flexget\plugins\ # Windows
25 | ```
26 | 再次注意`plugins`文件夹和`config.yml`处在同一级目录下,例如:
27 | ```
28 | /.flxget
29 | ┕━config.yml
30 | ┕━plugins
31 | ┕━nexusphp.py
32 | ```
33 | 3. 将插件拷贝至plugins
34 | 4. 若启用了Web-UI或守护进程,则重启flexget重新加载配置
35 |
36 | ## 使用
37 | 1. 编辑flexget配置文件,添加nexusphp选项,按照需要进行配置
38 | #### 简单配置
39 | ```yaml
40 | nexusphp:
41 | cookie: 'a=xxx; b=xxx' # 必填
42 | discount: # 优惠信息 选填
43 | - free
44 | ```
45 | #### 完整配置
46 | ```yaml
47 | nexusphp:
48 | cookie: 'a=xxx; b=xxx' # 必填
49 | discount: # 优惠信息 选填
50 | - free
51 | - 2x
52 | - 2x50%
53 | - 2xfree
54 | - 50%
55 | - 30%
56 | seeders: # 做种情况 选填(兼容性较差不建议使用)
57 | min: 1
58 | max: 2
59 | leechers: # 下载情况 选填(兼容性较差不建议使用)
60 | min: 10
61 | max: 100
62 | max_complete: 0.8
63 | left-time: 1 hours # 优惠剩余时间 选填
64 | hr: no # 是否下载HR 选填
65 | adapter: # 站点适配器,自行适配站点,参考最下方常见问题 选填
66 | free: free
67 | 2x: twoup
68 | 2xfree: twoupfree
69 | 30%: thirtypercent
70 | 50%: halfdown
71 | 2x50%: twouphalfdown
72 | comment: no # 在torrent注释中添加详情链接 选填
73 | user-agent: xxxxxx # 浏览器标识 选填
74 | remember: yes # 记住优惠信息 选填 请勿随意设置
75 | ```
76 | 2. 为rss的other_fields字段添加link属性
77 | ```yaml
78 | rss:
79 | url: https://www.example.com/rss
80 | other_fields: [link]
81 | ```
82 | 3. 启动flexget
83 | ``` bash
84 | flexget execute
85 | # 如果仅仅想要测试而不下载,可以添加 --test 参数
86 | flexget --test execute
87 | ```
88 |
89 | ## 详细配置
90 | - `cookie` **网站cookie** 必须填写
91 | - `discount` **优惠类型** 默认不限制优惠类型。
92 | 列表类型,Flexget会只下载含有列表内优惠类型的种子。
93 | 有效值:`free 2x 2x50% 2xfree 50% 30%`
94 | `注意:x为英文字母`
95 | - `seeders` **做种情况** 做种人数超出范围的,Flexget将不会下载
96 | - `注意:此选项兼容性较差`
97 | - `min` 最小做种人数。整数,默认不限制
98 | - `max` 最大做种人数。整数,默认不限制
99 | - `leechers` **下载情况** 下载人数超出范围的,Flexget将不会下载
100 | - `注意:此选项兼容性较差`
101 | - `min` 最小下载人数。整数,默认不限制
102 | - `max` 最大下载人数。整数,默认不限制
103 | - `max_complete` **下载者中最大完成度** 超过这个值将不下载。
104 | 小数,范围`0-1.0`,默认为1
105 | - `left-time` **最小剩余时间** 当实际剩余时间小于设置的值,则不下载。
106 | 时间字符串,例如 `3 hours`、`10 minutes`、`1 days`。
107 | 例如设置`1 hours`,优惠剩余59分钟,那么就不下载。默认不限制
108 | - `hr` **是否下载HR种** 默认 yes
109 | 1. `yes` 会下载HR,即不过滤HR
110 | 2. `no` 不下载HR
111 | - `adapter` **站点适配器** 站点不兼容时可自定义,具体参考
112 | [判断站点以及适配站点](https://github.com/Juszoe/flexget-nexusphp/blob/master/site.md)
113 | - `comment` **在torrent注释中添加详情链接**
114 | 1. `yes` 在torrent注释中添加详情链接,方便在BT客户端查看
115 | 2. `no` 默认值
116 | - `user-agent` **浏览器标识** 默认为Google浏览器
117 | - `remember` **记住优惠信息** 不建议设置为 no,因为会增大站点压力。默认 yes
118 |
119 |
120 | ## 完整配置示例
121 | ### 免费热种
122 | ```yaml
123 | tasks:
124 | my-free-task:
125 | rss:
126 | url: https://www.example.com/rss.xml
127 | other_fields:
128 | - link
129 | nexusphp:
130 | cookie: 'a=xxx; b=xxx'
131 | discount:
132 | - free
133 | - 2xfree
134 | download: ~/flexget/torrents/
135 | ```
136 | ### 热种
137 | ```yaml
138 | tasks:
139 | my-hot-task:
140 | rss:
141 | url: https://www.example.com/rss.xml
142 | other_fields:
143 | - link
144 | nexusphp:
145 | cookie: 'a=xxx; b=xxx'
146 | seeders:
147 | min: 1
148 | leechers:
149 | min: 20
150 | download: ~/flexget/torrents/
151 | ```
152 | ### 避免HR
153 | ```yaml
154 | tasks:
155 | no-hr-task:
156 | rss:
157 | url: https://www.example.com/rss.xml
158 | other_fields:
159 | - link
160 | nexusphp:
161 | cookie: 'a=xxx; b=xxx'
162 | hr: no
163 | download: ~/flexget/torrents/
164 | ```
165 |
166 | ## 常见问题
167 | ### 我的python版本是2.X如何使用?
168 |
169 | 本插件只支持Python 3.X或Python 2.7版本,其他版本不可用,请卸载Flexget后使用Python3重装
170 | ```bash
171 | pip uninstall flexget # 卸载
172 | pip3 install flexget # 使用pip3安装
173 | ```
174 | ### 目前支持哪些站点
175 |
176 | 如果站点禁止使用脚本爬虫,应立即停止使用本插件
177 | - 任何未修改关键结构的nexusphp站点
178 | - PTH
179 | - MT(站点安全性较高,[ip或浏览器变动](#user-agent)可能无法访问)
180 | - OB
181 | - Sky
182 | - School
183 | - U2
184 | - CHD
185 | - TJU(禁止脚本,请勿使用)
186 | - SSD
187 | - OpenCD
188 | - TTG(不支持人数筛选)
189 | - FRDS
190 | - Dream
191 | - HDC
192 | - LemonHD
193 |
194 | ### 如何判断站点是否支持
195 | [判断站点以及适配站点](https://github.com/Juszoe/flexget-nexusphp/blob/master/site.md)
196 |
197 | ### 确认cookie正确,还是提示 Can't access the site. Your cookie may be wrong!
198 |
199 | 某些站点安全性要求较高,ip或浏览器变动时无法使用cookie访问,需要重新登录。
200 | 解决办法:设置 user-agent 参数与浏览器相同,查看浏览器user-agent的方法自行搜索,并保证登录ip与使用Flexget相同。
201 |
202 | ### 站点启用了Cloudflare五秒盾无法获取信息
203 | 当触发Cloudflare五秒盾通常有以下提示:
204 | ```
205 | NexusPHP._get_info: 503 Server Error: Service Temporarily Unavailable for url
206 | ```
207 | 解决方案也很简单,可以考虑使用Flexget官方内置的插件[cfscraper](https://flexget.com/Plugins/cfscraper)
208 | 1. 首先需要安装依赖
209 | ``` bash
210 | pip install cloudscraper
211 | ```
212 | 2. 然后启用
213 | ``` yaml
214 | cfscraper: yes
215 | ```
216 | **注意!绕过站点安全机制可能有风险,自行决定是否使用**
217 |
218 |
219 | ## 支持作者
220 |
221 | 插件经常需要时间维护,如果本插件对你有用,可以请作者吃顿饭😉,给作者提供更多动力
222 |
223 |
--------------------------------------------------------------------------------
/nexusphp.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | from __future__ import unicode_literals, division, absolute_import
3 |
4 | import time
5 | from builtins import *
6 |
7 | import concurrent.futures
8 | import re
9 | import logging
10 | from datetime import datetime
11 |
12 | from requests.adapters import HTTPAdapter
13 |
14 | from flexget import plugin
15 | from flexget.config_schema import one_or_more
16 | from flexget.event import event
17 | from flexget.utils.soup import get_soup
18 | from flexget.utils.tools import parse_timedelta
19 |
20 | log = logging.getLogger('nexusphp')
21 |
22 |
23 | class NexusPHP(object):
24 | """
25 | 配置示例
26 | task_name:
27 | rss:
28 | url: https://www.example.com/rss.xml
29 | other_fields:
30 | - link
31 | nexusphp:
32 | cookie: 'my_cookie'
33 | discount:
34 | - free
35 | - 2x
36 | seeders:
37 | min: 1
38 | max: 30
39 | leechers:
40 | min: 1
41 | max: 100
42 | max_complete: 0.8
43 | hr: no
44 | """
45 |
46 | schema = {
47 | 'type': 'object',
48 | 'properties': {
49 | 'cookie': {'type': 'string'},
50 | 'discount': one_or_more({'type': 'string', 'enum': ['free', '2x', '2xfree', '30%', '50%', '2x50%']}),
51 | 'seeders': {
52 | 'type': 'object',
53 | 'properties': {
54 | 'min': {'type': 'integer', 'minimum': 0, 'default': 0},
55 | 'max': {'type': 'integer', 'minimum': 0, 'default': 100000}
56 | }
57 | },
58 | 'leechers': {
59 | 'type': 'object',
60 | 'properties': {
61 | 'min': {'type': 'integer', 'minimum': 0, 'default': 0},
62 | 'max': {'type': 'integer', 'minimum': 0, 'default': 100000},
63 | 'max_complete': {'type': 'number', 'minimum': 0, 'maximum': 1, 'default': 1}
64 | }
65 | },
66 | 'left-time': {'type': 'string', 'format': 'interval'},
67 | 'hr': {'type': 'boolean'},
68 | 'adapter': {
69 | 'type': 'object',
70 | 'properties': {
71 | 'free': {'type': 'string', 'default': 'free'},
72 | '2x': {'type': 'string', 'default': 'twoup'},
73 | '2xfree': {'type': 'string', 'default': 'twoupfree'},
74 | '30%': {'type': 'string', 'default': 'thirtypercent'},
75 | '50%': {'type': 'string', 'default': 'halfdown'},
76 | '2x50%': {'type': 'string', 'default': 'twouphalfdown'}
77 | }
78 | },
79 | 'comment': {'type': 'boolean'},
80 | 'user-agent': {'type': 'string'},
81 | 'remember': {'type': 'boolean', 'default': True}
82 | },
83 | 'required': ['cookie']
84 | }
85 |
86 | @staticmethod
87 | def build_config(config):
88 | config = dict(config)
89 | config.setdefault('discount', None)
90 | config.setdefault('seeders', None)
91 | config.setdefault('leechers', None)
92 | config.setdefault('left-time', None)
93 | config.setdefault('hr', True)
94 | config.setdefault('adapter', None)
95 | config.setdefault('user-agent',
96 | 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
97 | 'Chrome/75.0.3770.142 Safari/537.36')
98 | return config
99 |
100 | @plugin.priority(127)
101 | def on_task_modify(self, task, config):
102 | if config.get('comment', False):
103 | for entry in task.entries:
104 | if 'torrent' in entry and 'link' in entry:
105 | entry['torrent'].content['comment'] = entry['link']
106 | entry['torrent'].modified = True
107 |
108 | def on_task_filter(self, task, config):
109 | config = self.build_config(config)
110 |
111 | adapter = HTTPAdapter(max_retries=5)
112 | task.requests.mount('http://', adapter)
113 | task.requests.mount('https://', adapter)
114 | task.requests.headers.update({
115 | 'cookie': config['cookie'],
116 | 'user-agent': config['user-agent']
117 | })
118 |
119 | # 先访问一次 预防异常
120 | try:
121 | task.requests.get(task.entries[0].get('link'))
122 | except Exception:
123 | pass
124 |
125 | def consider_entry(_entry, _link):
126 | try:
127 | discount, seeders, leechers, hr, expired_time = NexusPHP._get_info(task, _link, config)
128 | except plugin.PluginError as e:
129 | raise e
130 | except Exception as e:
131 | log.info('NexusPHP._get_info: ' + str(e))
132 | return
133 |
134 | remember = config['remember']
135 |
136 | if config['discount']:
137 | if discount not in config['discount']:
138 | _entry.reject('%s does not match discount' % discount, remember=remember) # 优惠信息不匹配
139 | return
140 |
141 | if config['left-time'] and expired_time:
142 | left_time = expired_time - datetime.now()
143 | # 实际剩余时间 < 'left-time'
144 | if left_time < parse_timedelta(config['left-time']):
145 | _entry.reject('its discount time only left [%s]' % left_time, remember=remember) # 剩余时间不足
146 | return
147 |
148 | if config['hr'] is False and hr:
149 | _entry.reject('it is HR', remember=True) # 拒绝HR
150 |
151 | if config['seeders']:
152 | seeder_max = config['seeders']['max']
153 | seeder_min = config['seeders']['min']
154 | if len(seeders) not in range(seeder_min, seeder_max + 1):
155 | _entry.reject('%d is out of range of seeder' % len(seeders), remember=True) # 做种人数不匹配
156 | return
157 |
158 | if config['leechers']:
159 | leecher_max = config['leechers']['max']
160 | leecher_min = config['leechers']['min']
161 | if len(leechers) not in range(leecher_min, leecher_max + 1):
162 | _entry.reject('%d is out of range of leecher' % len(leechers), remember=True) # 下载人数不匹配
163 | return
164 |
165 | if len(leechers) != 0:
166 | max_complete = max(leechers, key=lambda x: x['completed'])['completed']
167 | else:
168 | max_complete = 0
169 | if max_complete > config['leechers']['max_complete']:
170 | _entry.reject('%f is more than max_complete' % max_complete, remember=True) # 最大完成度不匹配
171 | return
172 |
173 | _entry.accept()
174 |
175 | futures = [] # 线程任务
176 | with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
177 | for entry in task.accepted + task.undecided:
178 | link = entry.get('link')
179 | if not link:
180 | raise plugin.PluginError("The rss plugin require 'other_fields' which contain 'link'. "
181 | "For example: other_fields: - link")
182 | futures.append(executor.submit(consider_entry, entry, link))
183 | time.sleep(0.5)
184 |
185 | for f in concurrent.futures.as_completed(futures):
186 | exception = f.exception()
187 | if isinstance(exception, plugin.PluginError):
188 | log.error(exception)
189 |
190 | @staticmethod
191 | # 解析页面,获取优惠、做种者信息、下载者信息
192 | def info_from_page(detail_page, peer_page, discount_fn, hr_fn):
193 | try:
194 | discount, expired_time = discount_fn(detail_page)
195 | except plugin.PluginError as e:
196 | raise e
197 | except Exception:
198 | discount = expired_time = None # 无优惠
199 |
200 | try:
201 | if hr_fn:
202 | hr = hr_fn(detail_page)
203 | else:
204 | hr = False
205 | for item in ['hitandrun', 'hit_run.gif', 'Hit and Run', 'Hit & Run']:
206 | if item in detail_page.text:
207 | hr = True
208 | break
209 | except plugin.PluginError as e:
210 | raise e
211 | except Exception:
212 | hr = False # 无HR
213 |
214 | soup = get_soup(peer_page.replace('\n', ''), 'html5lib')
215 | seeders = leechers = []
216 | tables = soup.find_all('table', limit=2)
217 | if len(tables) == 2:
218 | # 1. seeder leecher 均有
219 | seeders = NexusPHP.get_peers(tables[0])
220 | leechers = NexusPHP.get_peers(tables[1])
221 | elif len(tables) == 1 and len(soup.body.contents) == 3:
222 | # 2. seeder leecher 有其一
223 | nodes = soup.body.contents
224 | if nodes[1].name == 'table':
225 | # 2.1 只有seeder 在第二个节点
226 | seeders = NexusPHP.get_peers(nodes[1])
227 | else:
228 | # 2.2 只有leecher 在第三个节点
229 | leechers = NexusPHP.get_peers(nodes[2])
230 | else:
231 | # 3. seeder leecher 均无
232 | seeders = leechers = []
233 | return discount, seeders, leechers, hr, expired_time
234 |
235 | @staticmethod
236 | def get_peers(table):
237 | peers = []
238 | name_index = 0
239 | connectable_index = 1
240 | uploaded_index = 2
241 | downloaded_index = 4
242 | completed_index = 7
243 | for index, tr in enumerate(table.find_all('tr')):
244 | try:
245 | if index == 0:
246 | tds = tr.find_all('td')
247 | for i, td in enumerate(tds):
248 | text = td.get_text()
249 | if text in ['用户', '用戶', '会员/IP']:
250 | name_index = i
251 | elif text in ['可连接', '可連接', '公网']:
252 | connectable_index = i
253 | elif text in ['上传', '上傳', '总上传']:
254 | uploaded_index = i
255 | elif text in ['下载', '下載', '本次下载']:
256 | downloaded_index = i
257 | elif text == '完成':
258 | completed_index = i
259 | else:
260 | tds = tr.find_all('td')
261 | peers.append({
262 | 'name': tds[name_index].get_text(),
263 | 'connectable': True if tds[connectable_index].get_text() != '是' else False,
264 | 'uploaded': tds[uploaded_index].get_text(),
265 | 'downloaded': tds[downloaded_index].get_text(),
266 | 'completed': float(tds[completed_index].get_text().strip('%')) / 100
267 | })
268 | except Exception:
269 | peers.append({
270 | 'name': '',
271 | 'connectable': False,
272 | 'uploaded': '',
273 | 'downloaded': '',
274 | 'completed': 0
275 | })
276 | return peers
277 |
278 | @staticmethod
279 | def _get_info(task, link, config):
280 | if 'open.cd' in link:
281 | link = link.replace('details.php', 'plugin_details.php')
282 | detail_page = task.requests.get(link, timeout=20) # 详情
283 | detail_page.encoding = 'utf-8'
284 |
285 | if 'login' in detail_page.url or 'portal.php' in detail_page.url:
286 | raise plugin.PluginError("Can't access the site. Your cookie may be wrong!")
287 |
288 | # 1. peer page
289 | def get_peer_page():
290 | if 'totheglory' in link:
291 | return ''
292 | if 'lemonhd' in link:
293 | # https://lemonhd.org/details_movie.php?id=xxx
294 | # https://lemonhd.org/details_music.php?id=xxx
295 | # ...
296 | peer_url = re.sub(r'details_\w+.php', 'viewpeerlist.php', link, 1)
297 | else:
298 | peer_url = link.replace('details.php', 'viewpeerlist.php', 1)
299 | try:
300 | if config['seeders'] or config['leechers']: # 配置了seeders、leechers才请求
301 | return task.requests.get(peer_url).text # peer详情
302 | except Exception:
303 | return ''
304 | return ''
305 |
306 | peer_page = get_peer_page()
307 |
308 | # 2. HR
309 | hr_fn = None
310 | if 'chdbits' in link:
311 | def chd_hr_fn(_page):
312 | if 'H&R' in _page.text:
313 | return True
314 | return False
315 |
316 | hr_fn = chd_hr_fn
317 |
318 | # 3. discount
319 | sites_discount = {
320 | 'chdbits': {
321 | 'pro_free2up.*?': '2xfree',
322 | 'pro_free.*?': 'free',
323 | 'pro_2up.*?': '2x',
324 | 'pro_50pctdown2up.*?': '2x50%',
325 | 'pro_30pctdown.*?': '30%',
326 | 'pro_50pctdown.*?': '50%'
327 | },
328 | 'u2.dmhy': {
329 | 'class=.pro_2up.*?promotion.*?': '2x',
330 | 'class=.pro_free2up.*?promotion.*?': '2xfree',
331 | 'class=.pro_free.*?promotion.*?': 'free',
332 | 'class=.pro_50pctdown2up.*?promotion.*?': '2x50%',
333 | 'class=.pro_30pctdown.*?promotion.*?': '30%',
334 | 'class=.pro_50pctdown.*?promotion.*?': '50%',
335 | r'class=.pro_custom.*?0\.00X.*?promotion.*?': '2xfree'
336 | },
337 | 'totheglory': {
338 | '本种子限时不计流量.*?': 'free',
339 | '本种子的下载流量计为实际流量的30%.*?': '30%',
340 | '本种子的下载流量会减半.*?': '50%',
341 | },
342 | 'open.cd': {
343 | 'pro_free2up': '2xfree',
344 | 'pro_free': 'free',
345 | 'pro_2up': '2x',
346 | 'pro_50pctdown2up': '2x50%',
347 | 'pro_30pctdown': '30%',
348 | 'pro_50pctdown': '50%'
349 | }
350 | }
351 |
352 | discount_fn = NexusPHP.generate_discount_fn({
353 | 'class=\'free\'.*?免.*?': 'free',
354 | 'class=\'twoup\'.*?2X.*?': '2x',
355 | 'class=\'twoupfree\'.*?2X免.*?': '2xfree',
356 | 'class=\'thirtypercent\'.*?30%.*?': '30%',
357 | 'class=\'halfdown\'.*?50%.*?': '50%',
358 | 'class=\'twouphalfdown\'.*?2X 50%.*?': '2x50%'
359 | })
360 | for site, convert in sites_discount.items():
361 | if site in link:
362 | discount_fn = NexusPHP.generate_discount_fn(convert)
363 | break
364 |
365 | if 'hdchina' in link:
366 | def _(page):
367 | return NexusPHP.get_discount_from_hdchina(page, task)
368 |
369 | discount_fn = _
370 |
371 | if config['adapter']:
372 | convert = {value: key for key, value in config['adapter'].items()}
373 | discount_fn = NexusPHP.generate_discount_fn(convert)
374 |
375 | return NexusPHP.info_from_page(detail_page, peer_page, discount_fn, hr_fn)
376 |
377 | @staticmethod
378 | def get_discount_from_hdchina(details_page, task):
379 | soup = get_soup(details_page.text, 'html5lib')
380 | csrf = soup.find('meta', attrs={'name': 'x-csrf'})['content']
381 | torrent_id = str(soup.find('div', class_='details_box').find('span', class_='sp_state_placeholder')['id'])
382 |
383 | res = task.requests.post('https://hdchina.org/ajax_promotion.php', data={
384 | 'ids[]': torrent_id,
385 | 'csrf': csrf,
386 | }, timeout=10)
387 |
388 | """ sample response
389 | {
390 | 'status': 200,
391 | 'message': {
392 | '530584': {
393 | 'sp_state': '
' in discount_info['sp_state']:
410 | # HDC cookie 仅部分错误时会直接返回free
411 | # 同时带有特征
412 | # 也许是站点的BUG
413 | raise plugin.PluginError("Can't access the site. Your cookie may be wrong!")
414 |
415 | expired_time = None
416 | match = re.search(r'(\d{4})(-\d{1,2}){2}\s\d{1,2}(:\d{1,2}){2}', discount_info['timeout'])
417 | if match:
418 | expired_time_str = match.group(0)
419 | expired_time = datetime.strptime(expired_time_str, "%Y-%m-%d %H:%M:%S")
420 |
421 | discount_mapping = {
422 | 'class="pro_free2up"': '2xfree',
423 | 'class="pro_free"': 'free',
424 | 'class="pro_2up"': '2x',
425 | 'class="pro_50pctdown2up"': '2x50%',
426 | 'class="pro_30pctdown"': '30%',
427 | 'class="pro_50pctdown"': '50%'
428 | }
429 |
430 | for key, value in discount_mapping.items():
431 | if key in discount_info['sp_state']:
432 | return value, expired_time
433 |
434 | return None, None
435 |
436 | @staticmethod
437 | def generate_discount_fn(convert):
438 | def fn(page):
439 | html = page.text.replace('\n', '')
440 | for key, value in convert.items():
441 | match = re.search(key, html)
442 | if match:
443 | discount_str = match.group(0)
444 | expired_time = None
445 | # 匹配优惠剩余时间
446 | match = re.search(r'(\d{4})(-\d{1,2}){2}\s\d{1,2}(:\d{1,2}){2}', discount_str)
447 | if match:
448 | expired_time_str = match.group(0)
449 | expired_time = datetime.strptime(expired_time_str, "%Y-%m-%d %H:%M:%S")
450 | return value, expired_time
451 | return None, None
452 |
453 | return fn
454 |
455 |
456 | @event('plugin.register')
457 | def register_plugin():
458 | plugin.register(NexusPHP, 'nexusphp', api_ver=2)
459 |
--------------------------------------------------------------------------------
/site.md:
--------------------------------------------------------------------------------
1 | # 判断站点支持程度
2 | - 若发现日志输出大量none,可能该站点不支持,请联系开发者适配
3 | ```bash
4 | REJECTED: `XXX` by nexusphp plugin because none does not match discount
5 | ```
6 | - 该站点结构和其他站点区别较大,可能不支持
7 |
8 | # 适配站点
9 | ## 自行适配
10 | 1. 在详情页面的HTML代码中寻找相关信息,如:
11 | ```html
12 |
XXX 2006 1080p[免费]
13 | XXX 2006 1080p[2X]
14 | XXX 2006 1080p[2X免费]
15 | XXX 2006 1080p[30%]
16 | XXX 2006 1080p[50%]
17 | XXX 2006 1080p[2X50%]
18 | ```
19 | 选择其中的类名作为标识,即 `free twoup twoupfree thirtypercent halfdown twouphalfdown`
20 | 2. 在配置文件添加适配选项adopter
21 | ```yaml
22 | nexusphp:
23 | adapter:
24 | free: free
25 | 2x: twoup
26 | 2xfree: twoupfree
27 | 30%: thirtypercent
28 | 50%: halfdown
29 | 2x50%: twouphalfdown
30 | ```
31 | 3. 适配成功建议将结果提交给开发者,帮助其他人
32 | `注意:支持正则表达式`
33 | ## 开发者适配
34 | 请联系开发者并提供以下信息:
35 | - 站点名
36 | - Flexget相关输出日志
37 | - 站点关键HTML结构截图,只需要free信息附近的HTML代码,
38 | 注意是详情页面的代码,而非列表里的
39 |
40 | `注意:提供信息时注意passkey`
--------------------------------------------------------------------------------