飞快TV.json created online with Bitbucket
This commit is contained in:
parent
1e3b88d606
commit
2355f32a0d
460
TVBoxOSC/tvbox/py/飞快TV.json
Normal file
460
TVBoxOSC/tvbox/py/飞快TV.json
Normal file
@ -0,0 +1,460 @@
|
||||
"""
|
||||
@header({
|
||||
searchable: 1,
|
||||
filterable: 1,
|
||||
quickSearch: 1,
|
||||
title: '飞快',
|
||||
author: 'EylinSir修复版-网盘推送播放',
|
||||
lang: 'hipy'
|
||||
})
|
||||
"""
|
||||
|
||||
import sys
|
||||
import json
|
||||
import re
|
||||
import base64
|
||||
import datetime
|
||||
from urllib.parse import quote_plus, unquote
|
||||
from lxml import etree
|
||||
|
||||
sys.path.append('..')
|
||||
from base.spider import Spider
|
||||
|
||||
|
||||
class Spider(Spider):
|
||||
def getName(self):
|
||||
return "飞快"
|
||||
|
||||
def init(self, extend=""):
|
||||
pass
|
||||
|
||||
def homeContent(self, filter):
|
||||
_r1 = {}
|
||||
_c1 = {
|
||||
"电影": "1",
|
||||
"剧集": "2",
|
||||
"综艺": "3",
|
||||
"动漫": "4"
|
||||
}
|
||||
_c2 = [{'type_name': _k1, 'type_id': _v1} for _k1, _v1 in _c1.items()]
|
||||
_r1['class'] = _c2
|
||||
return _r1
|
||||
|
||||
def homeVideoContent(self):
|
||||
_l1 = []
|
||||
try:
|
||||
_u1 = "".join(['h', 't', 't', 'p', 's', ':', '/', '/', 'f', 'e', 'i', 'k', 'u', 'a', 'i', '.', 't', 'v', '/'])
|
||||
_r2 = self.fetch(_u1, headers=self._get_header())
|
||||
if not _r2 or not _r2.text:
|
||||
return {'list': _l1}
|
||||
|
||||
_h1 = self._parse_dom(_r2)
|
||||
if not _h1:
|
||||
return {'list': _l1}
|
||||
|
||||
_x1 = _h1.xpath(
|
||||
'//div[contains(@class, "module-focus")]//a[contains(@href, "/voddetail/")] | '
|
||||
'//div[contains(@class, "module-hot")]//a[contains(@href, "/voddetail/")] | '
|
||||
'//div[contains(@class, "module-recommend")]//a[contains(@href, "/voddetail/")] | '
|
||||
'//div[contains(@class, "module-items") and contains(@class, "module-poster-items")]//a[contains(@href, "/voddetail/")]'
|
||||
)
|
||||
|
||||
_s1 = set()
|
||||
for _a1 in _x1:
|
||||
_v1 = self._parse_item(_a1)
|
||||
if _v1 and _v1["vod_id"] not in _s1:
|
||||
_s1.add(_v1["vod_id"])
|
||||
_l1.append(_v1)
|
||||
|
||||
_l1 = _l1[:30]
|
||||
except Exception:
|
||||
pass
|
||||
return {'list': _l1}
|
||||
|
||||
def categoryContent(self, tid, pg, filter, extend):
|
||||
if tid == '0':
|
||||
return self.homeVideoContent()
|
||||
|
||||
_r3 = {'list': [], 'page': pg, 'pagecount': 9999, 'limit': 90, 'total': 999999}
|
||||
try:
|
||||
_e1 = json.loads(extend) if extend and isinstance(extend, str) else {}
|
||||
_u2 = self._build_url(tid, pg, _e1)
|
||||
except Exception:
|
||||
_u2 = f'{"".join(["h","t","t","p","s",":","/","/","f","e","i","k","u","a","i",".","t","v","/","v","o","d","s","h","o","w","/",tid,"-","-","-","-","-","-","-","-","-",".","h","t","m","l"])}'
|
||||
|
||||
_h2 = self._get_header().copy()
|
||||
_h2['Referer'] = f'https://feikuai.tv/vodtype/{tid}.html'
|
||||
|
||||
_r4 = self.fetch(_u2, headers=_h2)
|
||||
if not _r4 or not _r4.text:
|
||||
return _r3
|
||||
|
||||
_h3 = self._parse_dom(_r4)
|
||||
_v2 = []
|
||||
_s2 = set()
|
||||
try:
|
||||
_l2 = _h3.xpath('//div[contains(@class, "module-items") and contains(@class, "module-poster-items")]//a[contains(@href, "/voddetail/")]') or \
|
||||
_h3.xpath('//a[contains(@class, "module-poster-item") and contains(@href, "/voddetail/")]') or \
|
||||
_h3.xpath('//div[contains(@class, "module-card-items")]//a[contains(@href, "/voddetail/")]')
|
||||
|
||||
for _a2 in _l2:
|
||||
_v3 = self._parse_item(_a2)
|
||||
if _v3 and _v3["vod_id"] not in _s2:
|
||||
_s2.add(_v3["vod_id"])
|
||||
_v2.append(_v3)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
_r3['list'] = _v2
|
||||
return _r3
|
||||
|
||||
def detailContent(self, ids):
|
||||
if not ids:
|
||||
return {'list': []}
|
||||
_t1 = str(ids[0]).strip()
|
||||
_u3 = f'https://feikuai.tv/voddetail/{_t1}.html'
|
||||
try:
|
||||
_r5 = self.fetch(_u3, headers=self._get_header())
|
||||
if not _r5 or not _r5.text:
|
||||
return {'list': []}
|
||||
_h4 = self._parse_dom(_r5)
|
||||
if not _h4:
|
||||
return {'list': []}
|
||||
except Exception:
|
||||
return {'list': []}
|
||||
|
||||
_t2 = _h4.xpath('//h1/text() | //div[contains(@class, "module-info-heading")]//h1/text()')
|
||||
_t3 = _t2[0].strip() if _t2 else ''
|
||||
_p1 = self._get_img(_h4)
|
||||
_d1 = self._get_desc(_h4)
|
||||
|
||||
_v4 = {
|
||||
"vod_id": _t1,
|
||||
"vod_name": _t3,
|
||||
"vod_pic": _p1,
|
||||
"type_name": "",
|
||||
"vod_year": "",
|
||||
"vod_area": "",
|
||||
"vod_remarks": "",
|
||||
"vod_actor": "",
|
||||
"vod_director": "",
|
||||
"vod_content": _d1
|
||||
}
|
||||
|
||||
_f1, _l3 = self._get_sources(_h4, _t1)
|
||||
|
||||
if not _f1 or not _l3:
|
||||
_f1 = ['飞快']
|
||||
_l3 = [f'{"".join(["暂","无","播","放","源"])}${"".join(["h","t","t","p","s",":","/","/","f","e","i","k","u","a","i",".","t","v","/","v","o","d","d","e","t","a","i","l","/",_t1,".","h","t","m","l"])}']
|
||||
|
||||
_v4['vod_play_from'] = '$$$'.join(_f1) if _f1 else ""
|
||||
_v4['vod_play_url'] = '$$$'.join(_l3)
|
||||
return {'list': [_v4]}
|
||||
|
||||
def searchContent(self, key, quick, pg='1'):
|
||||
_v5 = []
|
||||
try:
|
||||
_p2 = str(pg)
|
||||
if _p2 in ('', '1'):
|
||||
_u4 = f'https://feikuai.tv/vodsearch/-------------.html?wd={quote_plus(key)}'
|
||||
_h5 = self._get_header().copy()
|
||||
_h5['Referer'] = f'https://feikuai.tv/vodsearch/-------------.html?wd={quote_plus(key)}'
|
||||
else:
|
||||
_u4 = f'https://feikuai.tv/label/search_ajax.html?wd={quote_plus(key)}&by=time&order=desc&page={_p2}'
|
||||
_h5 = self._get_header().copy()
|
||||
_h5['X-Requested-With'] = 'XMLHttpRequest'
|
||||
_h5['Accept'] = '*/*'
|
||||
_h5['Referer'] = f'https://feikuai.tv/vodsearch/-------------.html?wd={quote_plus(key)}'
|
||||
|
||||
_r6 = self.fetch(_u4, headers=_h5)
|
||||
if not _r6 or not _r6.text:
|
||||
return {'list': []}
|
||||
|
||||
_h6 = self._parse_dom(_r6)
|
||||
_i1 = _h6.xpath('//div[@id="resultList"]//a[contains(@href, "/voddetail/")]') or \
|
||||
_h6.xpath('//div[contains(@class, "module-card-items")]//a[contains(@href, "/voddetail/")]') or \
|
||||
_h6.xpath('//div[contains(@class, "module-items") and contains(@class, "module-poster-items")]//a[contains(@href, "/voddetail/")]') or \
|
||||
_h6.xpath('//a[contains(@href, "/voddetail/")]')
|
||||
|
||||
_s3 = set()
|
||||
for _a3 in _i1:
|
||||
_v6 = self._parse_item(_a3)
|
||||
if _v6 and _v6["vod_id"] not in _s3:
|
||||
_s3.add(_v6["vod_id"])
|
||||
_v5.append(_v6)
|
||||
except Exception:
|
||||
pass
|
||||
return {'list': _v5}
|
||||
|
||||
def playerContent(self, flag, id, vipFlags):
|
||||
|
||||
if isinstance(id, str) and id.startswith(''.join(['p','u','s','h',':','/','/'])):
|
||||
return {"parse": 0, "url": id}
|
||||
|
||||
|
||||
_u5 = f'https://feikuai.tv/vodplay/{id}.html'
|
||||
_v7 = _u5
|
||||
try:
|
||||
_r7 = self.fetch(_u5, headers=self._get_header(), timeout=45)
|
||||
if not _r7 or not _r7.text:
|
||||
return {"parse": 0, "url": _v7, "header": self._get_header()}
|
||||
|
||||
_p3 = r'(?:var\s+)?player_[a-zA-Z0-9_]+\s*=\s*(\{(?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})*\})(?=\s*</script>)'
|
||||
_m1 = re.search(_p3, _r7.text, re.S)
|
||||
if _m1:
|
||||
_d2 = json.loads(_m1.group(1))
|
||||
_v7 = _d2.get('url') or ''
|
||||
_v7 = self._decode_str(_v7, str(_d2.get('encrypt', '0')))
|
||||
if _v7.startswith('//'):
|
||||
_v7 = 'https:' + _v7
|
||||
if _v7.endswith('.m3u8'):
|
||||
return {"parse": 1, "url": _v7, "header": self._get_header()}
|
||||
except Exception:
|
||||
pass
|
||||
return {"parse": 0, "url": _v7, "header": self._get_header()}
|
||||
|
||||
def _parse_item(self, a_element):
|
||||
try:
|
||||
_h7 = a_element.xpath('./@href')[0] if a_element.xpath('./@href') else ''
|
||||
_m2 = re.search(r'/voddetail/(\d+)\.html', _h7)
|
||||
if not _m2:
|
||||
return None
|
||||
_s4 = _m2.group(1)
|
||||
|
||||
_t4 = (a_element.xpath('.//div[contains(@class, "module-poster-item-title")]//text()') or
|
||||
a_element.xpath('.//div[contains(@class, "module-card-item-title")]/a//text()') or
|
||||
a_element.xpath('./@title') or a_element.xpath('.//img/@alt'))
|
||||
_n1 = _t4[0].strip() if _t4 else f"视频_{_s4}"
|
||||
|
||||
_i2 = self._get_img(a_element)
|
||||
|
||||
_r8 = a_element.xpath('.//div[contains(@class, "module-item-note")]//text()')
|
||||
_r9 = ''.join([x.strip() for x in _r8 if x.strip()]) if _r8 else ""
|
||||
|
||||
return {
|
||||
"vod_id": _s4,
|
||||
"vod_name": _n1,
|
||||
"vod_pic": _i2,
|
||||
"vod_remarks": _r9
|
||||
}
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def _get_img(self, element):
|
||||
_i3 = (element.xpath('.//img[contains(@class, "lazy")]/@data-original[contains(., ".webp") or contains(., ".jpg") or contains(., ".png")]') or
|
||||
element.xpath('.//img[contains(@class, "lazy")]/@src[contains(., ".webp") or contains(., ".jpg") or contains(., ".png")]') or
|
||||
element.xpath('.//img/@data-original[contains(., ".webp") or contains(., ".jpg") or contains(., ".png")]') or
|
||||
element.xpath('.//img/@src[contains(., ".webp") or contains(., ".jpg") or contains(., ".png")]'))
|
||||
|
||||
if _i3:
|
||||
_i4 = _i3[0]
|
||||
if _i4.startswith('/'):
|
||||
_i4 = 'https://feikuai.tv' + _i4
|
||||
return _i4
|
||||
return ''
|
||||
|
||||
def _get_desc(self, root):
|
||||
try:
|
||||
_d3 = root.xpath('//div[contains(@class, "module-info-introduction-content")]//text()')
|
||||
_d4 = '\n'.join([x.strip() for x in _d3 if x.strip()]) if _d3 else ''
|
||||
return _d4
|
||||
except Exception:
|
||||
return ''
|
||||
|
||||
def _parse_dom(self, rsp):
|
||||
try:
|
||||
_p4 = etree.HTMLParser(encoding='utf-8', recover=True, remove_blank_text=True)
|
||||
if hasattr(rsp, 'content'):
|
||||
return etree.HTML(rsp.content, parser=_p4)
|
||||
return etree.HTML(rsp.text.encode('utf-8', errors='ignore'), parser=_p4)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def _build_url(self, tid, pg, ext):
|
||||
_a1 = (ext.get('area') or ext.get('1') or '').strip()
|
||||
_c3 = (ext.get('class') or ext.get('3') or '').strip()
|
||||
_y1 = (ext.get('year') or ext.get('11') or '').strip()
|
||||
|
||||
_e2 = quote_plus(_a1) if _a1 else ''
|
||||
_e3 = quote_plus(_c3) if _c3 else ''
|
||||
_p5 = '' if str(pg) in ('', '1') else str(pg)
|
||||
return f'https://feikuai.tv/vodshow/{tid}-{_e2}--{_e3}-----{_p5}---{_y1}.html'
|
||||
|
||||
def _get_sources(self, root, tid):
|
||||
_f2 = []
|
||||
_l4 = []
|
||||
|
||||
self._get_normal_sources(root, tid, _f2, _l4)
|
||||
|
||||
self._get_pan_sources(root, tid, _f2, _l4)
|
||||
|
||||
return _f2, _l4
|
||||
|
||||
def _get_normal_sources(self, root, tid, playFrom, playList):
|
||||
try:
|
||||
_e4 = root.xpath('//div[contains(@class, "module-play-list")]//a[contains(@href, "/vodplay/")] | //ul[contains(@class, "module-play-list")]//a[contains(@href, "/vodplay/")]')
|
||||
_g1 = {}
|
||||
for _a4 in _e4:
|
||||
try:
|
||||
_h8 = _a4.xpath('./@href')[0] if _a4.xpath('./@href') else ''
|
||||
_m3 = re.search(r'/vodplay/(\d+)-(\d+)-(\d+)\.html', _h8)
|
||||
if not _m3:
|
||||
continue
|
||||
_v8, _s5, _e5 = _m3.groups()
|
||||
if _v8 != tid:
|
||||
continue
|
||||
_n2 = ''.join(_a4.xpath('string(.)')).strip()
|
||||
if not _n2 or _n2 in ('立即播放', '收藏', '追更', '分享', '报错', '下载'):
|
||||
continue
|
||||
if _s5 not in _g1:
|
||||
_g1[_s5] = []
|
||||
_g1[_s5].append(f"{_n2}${_v8}-{_s5}-{_e5}")
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
_o1 = []
|
||||
for _b1 in root.xpath('//div[contains(@class, "his-tab-list")]'):
|
||||
try:
|
||||
_f3 = _b1.xpath('.//a[contains(@href, "/vodplay/")][1]/@href')
|
||||
if _f3:
|
||||
_m4 = re.search(r'/vodplay/(\d+)-(\d+)-(\d+)\.html', _f3[0])
|
||||
if _m4:
|
||||
_s6 = _m4.group(2)
|
||||
if _s6 not in _o1:
|
||||
_o1.append(_s6)
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
_l5 = [x.strip() for x in root.xpath('//div[contains(@class, "module-tab-items-box")]//div[contains(@class, "module-tab-item")]//span/text()') if x.strip()]
|
||||
|
||||
for _i5, _s7 in enumerate(_o1):
|
||||
_s8 = _l5[_i5] if _i5 < len(_l5) else f"线路{_s7}"
|
||||
playFrom.append(_s8)
|
||||
playList.append('#'.join(_g1.get(_s7, [])))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _get_pan_sources(self, root, tid, playFrom, playList):
|
||||
try:
|
||||
_d5 = root.xpath('//div[@id="download-list"]')
|
||||
if not _d5:
|
||||
return
|
||||
|
||||
_t5 = root.xpath('//div[@id="y-downList"]//div[contains(@class, "module-tab-item")]')
|
||||
|
||||
_p6 = {
|
||||
'百度网盘': '百度网盘',
|
||||
'夸克网盘': '夸克网盘',
|
||||
'迅雷云盘': '迅雷云盘',
|
||||
'阿里云盘': '阿里云盘',
|
||||
'天翼云盘': '天翼云盘',
|
||||
'UC网盘': 'UC网盘',
|
||||
'115网盘': '115网盘',
|
||||
'移动云盘': '移动云盘'
|
||||
}
|
||||
|
||||
for _t6 in _t5:
|
||||
try:
|
||||
_s9 = ''.join(_t6.xpath('.//span/text()')).strip()
|
||||
if not _s9 or _s9 == '磁力链接':
|
||||
continue
|
||||
|
||||
_s10 = _p6.get(_s9, _s9)
|
||||
|
||||
_t7 = _t6.xpath('./@data-index')
|
||||
if not _t7:
|
||||
continue
|
||||
_t8 = _t7[0]
|
||||
|
||||
_c4 = root.xpath(f'//div[@id="tab-content-{_t8}"]//div[@class="module-row-info"]//a')
|
||||
|
||||
_e6 = []
|
||||
for _i6, _l6 in enumerate(_c4, 1):
|
||||
try:
|
||||
_u6 = _l6.xpath('./@href')
|
||||
if not _u6:
|
||||
continue
|
||||
_u7 = _u6[0].strip()
|
||||
|
||||
_h9 = _l6.xpath('.//h4/text()')
|
||||
if _h9:
|
||||
_e7 = _h9[0].strip()
|
||||
_e7 = re.sub(r'@一键搜片-\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}$', '', _e7).strip()
|
||||
else:
|
||||
_e7 = f"资源{_i6}"
|
||||
|
||||
_u8 = self._process_url(_u7)
|
||||
if _u8:
|
||||
_e6.append(f"{_e7}${_u8}")
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
if _e6:
|
||||
playFrom.append(_s10)
|
||||
playList.append('#'.join(_e6))
|
||||
except Exception:
|
||||
continue
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _process_url(self, raw_url):
|
||||
try:
|
||||
if not raw_url:
|
||||
return None
|
||||
|
||||
if not raw_url.startswith(('http://', 'https://')):
|
||||
if raw_url.startswith('//'):
|
||||
_u9 = 'https:' + raw_url
|
||||
elif raw_url.startswith('/'):
|
||||
_u9 = 'https://feikuai.tv' + raw_url
|
||||
else:
|
||||
return None
|
||||
else:
|
||||
_u9 = raw_url
|
||||
|
||||
return f"push://{_u9}"
|
||||
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def _decode_str(self, raw, encrypt):
|
||||
try:
|
||||
if not raw:
|
||||
return ''
|
||||
_e8 = str(encrypt or '0').strip()
|
||||
if _e8 == '1':
|
||||
_t9 = unquote(raw)
|
||||
elif _e8 == '2':
|
||||
try:
|
||||
_b2 = base64.b64decode(raw + '===')
|
||||
_t9 = unquote(_b2.decode('utf-8', errors='ignore'))
|
||||
except Exception:
|
||||
_t9 = unquote(raw)
|
||||
else:
|
||||
_t9 = raw
|
||||
|
||||
_t9 = re.sub(r'%u([0-9a-fA-F]{4})',
|
||||
lambda _m5: chr(int(_m5.group(1), 16)), _t9)
|
||||
return _t9
|
||||
except Exception:
|
||||
return raw
|
||||
|
||||
def _get_header(self):
|
||||
return {
|
||||
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36",
|
||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
|
||||
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
|
||||
"Connection": "keep-alive"
|
||||
}
|
||||
|
||||
def localProxy(self, params):
|
||||
pass
|
||||
|
||||
def isVideoFormat(self, url):
|
||||
return url
|
||||
|
||||
def manualVideoCheck(self):
|
||||
return []
|
||||
|
||||
def destroy(self):
|
||||
pass
|
||||
Loading…
x
Reference in New Issue
Block a user