上传文件至「api」
This commit is contained in:
@@ -0,0 +1,552 @@
|
||||
# coding=utf-8
|
||||
# !/usr/bin/python
|
||||
|
||||
"""
|
||||
|
||||
作者 精彩一瞬间 内容均从互联网收集而来 仅供交流学习使用 严禁用于商业用途 请于24小时内删除
|
||||
====================Diudiumiao====================
|
||||
|
||||
"""
|
||||
|
||||
from Crypto.Util.Padding import unpad
|
||||
from Crypto.Util.Padding import pad
|
||||
from urllib.parse import urlparse
|
||||
from urllib.parse import unquote
|
||||
from Crypto.Cipher import ARC4
|
||||
from urllib.parse import quote
|
||||
from base.spider import Spider
|
||||
from Crypto.Cipher import AES
|
||||
from datetime import datetime
|
||||
from bs4 import BeautifulSoup
|
||||
from base64 import b64decode
|
||||
import concurrent.futures
|
||||
import urllib.request
|
||||
import urllib.parse
|
||||
import datetime
|
||||
import binascii
|
||||
import requests
|
||||
import hashlib
|
||||
import base64
|
||||
import json
|
||||
import time
|
||||
import hmac
|
||||
import sys
|
||||
import re
|
||||
import os
|
||||
|
||||
sys.path.append('..')
|
||||
|
||||
xurl = "https://www.wasu.cn"
|
||||
|
||||
headerx = {
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.87 Safari/537.36'
|
||||
}
|
||||
|
||||
headerz = {
|
||||
'accept': '*/*',
|
||||
'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
|
||||
'cache-control': 'no-cache',
|
||||
'origin': xurl,
|
||||
'pragma': 'no-cache',
|
||||
'priority': 'u=1, i',
|
||||
'referer': xurl,
|
||||
'sec-ch-ua': '"Microsoft Edge";v="143", "Chromium";v="143", "Not A(Brand";v="24"',
|
||||
'sec-ch-ua-mobile': '?0',
|
||||
'sec-ch-ua-platform': '"Windows"',
|
||||
'sec-fetch-dest': 'empty',
|
||||
'sec-fetch-mode': 'cors',
|
||||
'sec-fetch-site': 'cross-site',
|
||||
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36 Edg/143.0.0.0',
|
||||
}
|
||||
|
||||
class Spider(Spider):
|
||||
|
||||
def getName(self):
|
||||
return "精彩"
|
||||
|
||||
def init(self, extend):
|
||||
pass
|
||||
|
||||
def isVideoFormat(self, url):
|
||||
pass
|
||||
|
||||
def manualVideoCheck(self):
|
||||
pass
|
||||
|
||||
def homeVideoContent(self):
|
||||
pass
|
||||
|
||||
def homeContent(self, filter):
|
||||
result = {}
|
||||
result = {"class": [{"type_id": "961", "type_name": "电影"},
|
||||
{"type_id": "962", "type_name": "剧集"},
|
||||
{"type_id": "963", "type_name": "少儿"},
|
||||
{"type_id": "965", "type_name": "栏目"},
|
||||
{"type_id": "966", "type_name": "新闻"}],
|
||||
"list": [],
|
||||
"filters": {"961": [{"key": "地区",
|
||||
"name": "地区",
|
||||
"value": [{"n": "全部", "v": ""},
|
||||
{"n": "内地", "v": "内地"},
|
||||
{"n": "港台", "v": "港台"},
|
||||
{"n": "欧美", "v": "欧美"},
|
||||
{"n": "日韩", "v": "日韩"},
|
||||
{"n": "泰国", "v": "泰国"},
|
||||
{"n": "其他", "v": "其他"}]},
|
||||
{"key": "类型",
|
||||
"name": "类型",
|
||||
"value": [{"n": "全部", "v": ""},
|
||||
{"n": "动作", "v": "动作"},
|
||||
{"n": "科幻", "v": "科幻"},
|
||||
{"n": "惊悚", "v": "惊悚"},
|
||||
{"n": "冒险", "v": "冒险"},
|
||||
{"n": "剧情", "v": "剧情"},
|
||||
{"n": "励志", "v": "励志"},
|
||||
{"n": "爱情", "v": "爱情"},
|
||||
{"n": "喜剧", "v": "喜剧"},
|
||||
{"n": "家庭", "v": "家庭"},
|
||||
{"n": "历史", "v": "历史"},
|
||||
{"n": "魔幻", "v": "魔幻"},
|
||||
{"n": "恐怖", "v": "恐怖"},
|
||||
{"n": "战争", "v": "战争"},
|
||||
{"n": "武侠", "v": "武侠"}]},
|
||||
{"key": "年代",
|
||||
"name": "年代",
|
||||
"value": [{"n": "全部", "v": ""},
|
||||
|
||||
{"n": "2026", "v": "2026"},
|
||||
{"n": "2025", "v": "2025"},
|
||||
{"n": "2024", "v": "2024"},
|
||||
{"n": "2023", "v": "2023"},
|
||||
{"n": "2022", "v": "2022"},
|
||||
{"n": "2021", "v": "2021"},
|
||||
{"n": "2020", "v": "2020"},
|
||||
{"n": "2019", "v": "2019"},
|
||||
{"n": "2018", "v": "2018"},
|
||||
{"n": "2017", "v": "2017"},
|
||||
{"n": "2016", "v": "2016"},
|
||||
{"n": "2015", "v": "2015"},
|
||||
{"n": "2014", "v": "2014"},
|
||||
{"n": "2013", "v": "2013"},
|
||||
{"n": "2012", "v": "2012"},
|
||||
{"n": "2011", "v": "2011"},
|
||||
{"n": "2010", "v": "2010"}]}],
|
||||
"962": [{"key": "地区",
|
||||
"name": "地区",
|
||||
"value": [{"n": "全部", "v": ""},
|
||||
{"n": "内地", "v": "内地"},
|
||||
{"n": "港台", "v": "港台"},
|
||||
{"n": "日韩", "v": "日韩"},
|
||||
{"n": "欧美", "v": "欧美"},
|
||||
{"n": "泰国", "v": "泰国"},
|
||||
{"n": "其他", "v": "其他"}]},
|
||||
{"key": "类型",
|
||||
"name": "类型",
|
||||
"value": [{"n": "全部", "v": ""},
|
||||
{"n": "都市", "v": "都市"},
|
||||
{"n": "爱情", "v": "爱情"},
|
||||
|
||||
{"n": "短剧", "v": "短剧"}, {"n": "战争", "v": "战争"},
|
||||
{"n": "家庭", "v": "家庭"},
|
||||
{"n": "悬疑", "v": "悬疑"},
|
||||
{"n": "古装", "v": "古装"}, {"n": "谍战", "v": "谍战"},
|
||||
{"n": "喜剧", "v": "喜剧"},
|
||||
{"n": "农村", "v": "农村"},
|
||||
{"n": "刑侦", "v": "刑侦"},
|
||||
{"n": "武侠", "v": "武侠"},
|
||||
{"n": "历史", "v": "历史"}]},
|
||||
{"key": "年代",
|
||||
"name": "年代",
|
||||
"value": [{"n": "全部", "v": ""},
|
||||
|
||||
{"n": "2026", "v": "2026"},
|
||||
{"n": "2025", "v": "2025"},
|
||||
{"n": "2024", "v": "2024"},
|
||||
{"n": "2023", "v": "2023"},
|
||||
{"n": "2022", "v": "2022"},
|
||||
{"n": "2021", "v": "2021"},
|
||||
{"n": "2020", "v": "2020"},
|
||||
{"n": "2019", "v": "2019"},
|
||||
{"n": "2018", "v": "2018"},
|
||||
{"n": "2017", "v": "2017"},
|
||||
{"n": "2016", "v": "2016"},
|
||||
{"n": "2015", "v": "2015"},
|
||||
{"n": "2014", "v": "2014"},
|
||||
{"n": "2013", "v": "2013"},
|
||||
{"n": "2012", "v": "2012"},
|
||||
{"n": "2011", "v": "2011"},
|
||||
{"n": "2010", "v": "2010"}]}],
|
||||
"963": [{"key": "地区",
|
||||
"name": "地区",
|
||||
"value": [{"n": "全部", "v": ""},
|
||||
{"n": "内地", "v": "内地"},
|
||||
{"n": "日韩", "v": "日韩"},
|
||||
{"n": "欧美", "v": "欧美"},
|
||||
{"n": "港台", "v": "港台"},
|
||||
{"n": "其他", "v": "其他"}]},
|
||||
{"key": "类型",
|
||||
"name": "类型",
|
||||
"value": [{"n": "全部", "v": ""},
|
||||
{"n": "动作", "v": "动作"},
|
||||
{"n": "冒险", "v": "冒险"},
|
||||
{"n": "益智", "v": "益智"},
|
||||
{"n": "亲子", "v": "亲子"},
|
||||
{"n": "热血", "v": "热血"},
|
||||
{"n": "剧情", "v": "剧情"},
|
||||
{"n": "魔幻", "v": "魔幻"},
|
||||
{"n": "励志", "v": "励志"},
|
||||
{"n": "机战", "v": "机战"},
|
||||
{"n": "搞笑", "v": "搞笑"},
|
||||
{"n": "科幻", "v": "科幻"},
|
||||
{"n": "治愈", "v": "治愈"},
|
||||
{"n": "儿歌", "v": "儿歌"},
|
||||
{"n": "教育", "v": "教育"},
|
||||
{"n": "校园", "v": "校园"},
|
||||
{"n": "童话", "v": "童话"},
|
||||
{"n": "推理", "v": "推理"},
|
||||
{"n": "怀旧", "v": "怀旧"},
|
||||
{"n": "宠物", "v": "宠物"},
|
||||
{"n": "舞蹈", "v": "舞蹈"}]},
|
||||
{"key": "年代",
|
||||
"name": "年代",
|
||||
"value": [{"n": "全部", "v": ""},
|
||||
|
||||
{"n": "2026", "v": "2026"},
|
||||
{"n": "2025", "v": "2025"},
|
||||
{"n": "2024", "v": "2024"},
|
||||
{"n": "2023", "v": "2023"},
|
||||
{"n": "2022", "v": "2022"},
|
||||
{"n": "2021", "v": "2021"},
|
||||
{"n": "2020", "v": "2020"},
|
||||
{"n": "2019", "v": "2019"},
|
||||
{"n": "2018", "v": "2018"},
|
||||
{"n": "2017", "v": "2017"},
|
||||
{"n": "2016", "v": "2016"},
|
||||
{"n": "2015", "v": "2015"},
|
||||
{"n": "2014", "v": "2014"},
|
||||
{"n": "2013", "v": "2013"},
|
||||
{"n": "2012", "v": "2012"},
|
||||
{"n": "2011", "v": "2011"},
|
||||
{"n": "2010", "v": "2010"}]}],
|
||||
"965": [{"key": "地区",
|
||||
"name": "地区",
|
||||
"value": [{"n": "全部", "v": ""},
|
||||
{"n": "内地", "v": "内地"},
|
||||
{"n": "欧美", "v": "欧美"}]},
|
||||
{"key": "类型",
|
||||
"name": "类型",
|
||||
"value": [{"n": "全部", "v": ""},
|
||||
{"n": "文化", "v": "文化"},
|
||||
{"n": "纪实", "v": "纪实"},
|
||||
{"n": "访谈", "v": "访谈"},
|
||||
{"n": "历史", "v": "历史"},
|
||||
{"n": "美食", "v": "美食"},
|
||||
{"n": "旅游", "v": "旅游"},
|
||||
{"n": "时尚", "v": "时尚"},
|
||||
{"n": "情感", "v": "情感"},
|
||||
{"n": "生活", "v": "生活"},
|
||||
{"n": "真人秀", "v": "真人秀"}]},
|
||||
{"key": "年代",
|
||||
"name": "年代",
|
||||
"value": [{"n": "全部", "v": ""},
|
||||
{"n": "2026", "v": "2026"},
|
||||
{"n": "2025", "v": "2025"},
|
||||
{"n": "2024", "v": "2024"},
|
||||
{"n": "2023", "v": "2023"},
|
||||
{"n": "2022", "v": "2022"},
|
||||
{"n": "2021", "v": "2021"},
|
||||
{"n": "2020", "v": "2020"},
|
||||
{"n": "2019", "v": "2019"},
|
||||
{"n": "2018", "v": "2018"},
|
||||
{"n": "2017", "v": "2017"},
|
||||
{"n": "2016", "v": "2016"},
|
||||
{"n": "2015", "v": "2015"},
|
||||
{"n": "2014", "v": "2014"},
|
||||
{"n": "2013", "v": "2013"},
|
||||
{"n": "2012", "v": "2012"},
|
||||
{"n": "2011", "v": "2011"},
|
||||
{"n": "2010", "v": "2010"}]}],
|
||||
"966": [{"key": "类型",
|
||||
"name": "类型",
|
||||
"value": [{"n": "全部", "v": ""},
|
||||
{"n": "国内视野", "v": "国内视野"},
|
||||
{"n": "国际纵览", "v": "国际纵览"},
|
||||
{"n": "军事话题", "v": "军事话题"},
|
||||
{"n": "社会百态", "v": "社会百态"},
|
||||
{"n": "央视频", "v": "央视频"}]}]}}
|
||||
return result
|
||||
|
||||
def generate_x_sign(self, secret_b64):
|
||||
secret_key = self.decode_secret_key(secret_b64)
|
||||
data = self.get_data_string()
|
||||
signature = self.compute_signature(secret_key, data)
|
||||
x_sign = self.encode_signature(signature)
|
||||
return x_sign
|
||||
|
||||
def decode_secret_key(self, secret_b64):
|
||||
return base64.b64decode(secret_b64)
|
||||
|
||||
def get_data_string(self):
|
||||
return "{}"
|
||||
|
||||
def compute_signature(self, secret_key, data):
|
||||
return hmac.new(secret_key, data.encode('utf-8'), hashlib.sha256).digest()
|
||||
|
||||
def encode_signature(self, signature):
|
||||
return base64.b64encode(signature).decode('utf-8')
|
||||
|
||||
def get_current_app_key(self, xurl, headerx):
|
||||
resp = self.fetch_index_page(xurl, headerx)
|
||||
js_path = self.extract_js_path(resp.text)
|
||||
js_url = f"{xurl}{js_path}"
|
||||
js_content = self.fetch_js_content(js_url, headerx)
|
||||
target_key_b64 = self.extract_target_key(js_content)
|
||||
return target_key_b64
|
||||
|
||||
def fetch_index_page(self, xurl, headerx):
|
||||
resp = requests.get(xurl, headers=headerx)
|
||||
resp.raise_for_status()
|
||||
return resp
|
||||
|
||||
def extract_js_path(self, html_content):
|
||||
js_path_pattern = r'src="(/[\d\.]+/assets/js/index-[\w\.-]+\.js)"'
|
||||
match = re.search(js_path_pattern, html_content)
|
||||
return match.group(1)
|
||||
|
||||
def fetch_js_content(self, js_url, headerx):
|
||||
js_resp = requests.get(js_url, headers=headerx)
|
||||
js_resp.raise_for_status()
|
||||
return js_resp.text
|
||||
|
||||
def extract_target_key(self, js_content):
|
||||
key_pattern = r'const \w+="([^"]+)",\w+="([^"]+)",\w+="([^"]+)";'
|
||||
key_match = re.search(key_pattern, js_content)
|
||||
return key_match.group(2)
|
||||
|
||||
def get_headers(self, sign1, xurl):
|
||||
return {
|
||||
'accept': 'application/json, text/plain, */*',
|
||||
'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
|
||||
'cache-control': 'no-cache',
|
||||
'launchchannel': 'web_channel',
|
||||
'origin': xurl,
|
||||
'pragma': 'no-cache',
|
||||
'priority': 'u=1, i',
|
||||
'referer': xurl,
|
||||
'sec-ch-ua': '"Microsoft Edge";v="143", "Chromium";v="143", "Not A(Brand";v="24"',
|
||||
'sec-ch-ua-mobile': '?0',
|
||||
'sec-ch-ua-platform': '"Windows"',
|
||||
'sec-fetch-dest': 'empty',
|
||||
'sec-fetch-mode': 'cors',
|
||||
'sec-fetch-site': 'cross-site',
|
||||
'siteid': '1000101',
|
||||
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36 Edg/143.0.0.0',
|
||||
'x-sign': sign1,
|
||||
}
|
||||
|
||||
def categoryContent(self, cid, pg, filter, ext):
|
||||
secret_b64 = self.get_current_app_key(xurl, headerx)
|
||||
sign1 = self.generate_x_sign(secret_b64)
|
||||
page = self.get_page_number(pg)
|
||||
NdType, DqType, LxType = self.extract_filter_types(ext)
|
||||
headers = self.get_headers(sign1, xurl)
|
||||
params = self.build_category_params(cid, page, NdType, DqType, LxType)
|
||||
data = self.fetch_category_data(params, headers)
|
||||
videos = self.parse_category_videos(data)
|
||||
return self.build_category_result(videos, pg)
|
||||
|
||||
def get_page_number(self, pg):
|
||||
return int(pg) if pg else 1
|
||||
|
||||
def extract_filter_types(self, ext):
|
||||
NdType = ext.get('年代', '全部')
|
||||
DqType = ext.get('地区', '全部')
|
||||
LxType = ext.get('类型', '全部')
|
||||
return NdType, DqType, LxType
|
||||
|
||||
def build_category_params(self, cid, page, NdType, DqType, LxType):
|
||||
return {'functionName': 'getNewsSearchedByCondition','nodeId': cid,'nodeTag': LxType,'yearTag': NdType,'countryTag': DqType,'orderType': '0','pageSize': '40','page': page,'keyword': '','siteId': '1000101',}
|
||||
|
||||
def fetch_category_data(self, params, headers):
|
||||
detail = requests.get('https://ups.5g.wasu.tv/rmp-user-suggest/1000101/hzhs/searchServlet', params=params,headers=headers)
|
||||
detail.encoding = "utf-8"
|
||||
return detail.json()
|
||||
|
||||
def parse_category_videos(self, data):
|
||||
videos = []
|
||||
for vod in data['data']:
|
||||
videos.append(self.parse_category_video(vod))
|
||||
return videos
|
||||
|
||||
def parse_category_video(self, vod):
|
||||
return {"vod_id": f"{vod['nodeId']}@{vod['newsId']}","vod_name": vod['title'],"vod_pic": vod['hPic'],"vod_year": vod.get('pubTime', '暂无日期'),"vod_remarks": vod.get('episodeDesc', '暂无备注')}
|
||||
|
||||
def build_category_result(self, videos, pg):
|
||||
result = {'list': videos}
|
||||
result['page'] = pg
|
||||
result['pagecount'] = 9999
|
||||
result['limit'] = 90
|
||||
result['total'] = 999999
|
||||
return result
|
||||
|
||||
def detailContent(self, ids):
|
||||
did = ids[0]
|
||||
secret_b64 = self.get_current_app_key(xurl, headerx)
|
||||
sign1 = self.generate_x_sign(secret_b64)
|
||||
fenge = did.split("@")
|
||||
headers = self.get_headers(sign1, xurl)
|
||||
params = self.build_detail_params(fenge)
|
||||
data = self.fetch_detail_data(params, headers)
|
||||
content = self.build_content(data)
|
||||
director = self.extract_detail_field(data, 'director')
|
||||
actor = self.extract_detail_field(data, 'actor')
|
||||
remarks = self.extract_detail_field(data, 'episodeDesc')
|
||||
year = self.extract_detail_field(data, 'pubTime')
|
||||
area = self.extract_detail_field(data, 'countryTag')
|
||||
bofang = self.build_play_url(data)
|
||||
videos = [self.build_video_data(did, director, actor, remarks, year, area, content, bofang)]
|
||||
return self.build_result(videos)
|
||||
|
||||
def build_detail_params(self, fenge):
|
||||
return {'siteId': '1000101', 'functionName': 'getCurrentNews', 'nodeId': fenge[0], 'newsId': fenge[1],'platform': 'web',}
|
||||
|
||||
def fetch_detail_data(self, params, headers):
|
||||
detail = requests.get('https://mcspapp.5g.wasu.tv/bvradio_app/hzhs/newsServlet', params=params, headers=headers)
|
||||
detail.encoding = "utf-8"
|
||||
return detail.json()
|
||||
|
||||
def build_content(self, data):
|
||||
return '介绍剧情👉' + data.get('data', {}).get('newsAbstract', '')
|
||||
|
||||
def extract_detail_field(self, data, field_name):
|
||||
return data.get('data', {}).get(field_name, '')
|
||||
|
||||
def build_play_url(self, data):
|
||||
bofang = ''
|
||||
for vod in data['data']['vodList']:
|
||||
name = vod['title']
|
||||
if len(vod['fileList']) > 1:
|
||||
id = vod['fileList'][1]['playUrl']
|
||||
else:
|
||||
id = vod['fileList'][0]['playUrl']
|
||||
bofang += name + '$' + id + '#'
|
||||
return bofang[:-1]
|
||||
|
||||
def build_video_data(self, did, director, actor, remarks, year, area, content, bofang):
|
||||
return {"vod_id": did, "vod_director": director, "vod_actor": actor, "vod_remarks": remarks, "vod_year": year, "vod_area": area, "vod_content": content, "vod_play_from": "华数", "vod_play_url": bofang}
|
||||
|
||||
def build_result(self, videos):
|
||||
result = {}
|
||||
result['list'] = videos
|
||||
return result
|
||||
|
||||
def get_x_sign_for_post(self, secret_b64, playUrl):
|
||||
secret_key = self.decode_secret_key(secret_b64)
|
||||
payload_dict = self.build_payload_dict(playUrl)
|
||||
data_string = self.serialize_payload(payload_dict)
|
||||
signature = self.compute_hmac_signature(secret_key, data_string)
|
||||
x_sign = self.encode_signature(signature)
|
||||
return x_sign
|
||||
|
||||
def decode_secret_key(self, secret_b64):
|
||||
return base64.b64decode(secret_b64)
|
||||
|
||||
def build_payload_dict(self, playUrl):
|
||||
return {"playUrl": playUrl, "platform": "web"}
|
||||
|
||||
def serialize_payload(self, payload_dict):
|
||||
return json.dumps(payload_dict, separators=(',', ':'), ensure_ascii=False)
|
||||
|
||||
def compute_hmac_signature(self, secret_key, data_string):
|
||||
return hmac.new(secret_key, data_string.encode('utf-8'), hashlib.sha256).digest()
|
||||
|
||||
def encode_signature(self, signature):
|
||||
return base64.b64encode(signature).decode('utf-8')
|
||||
|
||||
def playerContent(self, flag, id, vipFlags):
|
||||
secret_b64 = self.get_current_app_key(xurl, headerx)
|
||||
id = self.normalize_play_url(id)
|
||||
sign2 = self.get_x_sign_for_post(secret_b64, id)
|
||||
headers = self.get_headers(sign2, xurl)
|
||||
json_data = self.build_request_json(id)
|
||||
result = self.post_play_url_request(headers, json_data)
|
||||
play_url = result['data']['playUrl']
|
||||
return self.build_player_result(play_url)
|
||||
|
||||
def normalize_play_url(self, id):
|
||||
return id.replace('.mp4', '/playlist.m3u8')
|
||||
|
||||
def build_request_json(self, playUrl):
|
||||
return {'playUrl': playUrl, 'platform': 'web', }
|
||||
|
||||
def post_play_url_request(self, headers, json_data):
|
||||
response = requests.post('https://mcspapp.5g.wasu.tv/thirdApiFile/file/getPlayUrl', headers=headers, json=json_data)
|
||||
return response.json()
|
||||
|
||||
def build_player_result(self, play_url):
|
||||
result = {}
|
||||
result["parse"] = 0
|
||||
result["playUrl"] = ''
|
||||
result["url"] = play_url
|
||||
result["header"] = headerz
|
||||
return result
|
||||
|
||||
def searchContentPage(self, key, quick, pg):
|
||||
page = self.get_page_number(pg)
|
||||
secret_b64 = self.get_current_app_key(xurl, headerx)
|
||||
sign1 = self.generate_x_sign(secret_b64)
|
||||
headers = self.get_headers(sign1, xurl)
|
||||
params = self.build_search_params(key, page)
|
||||
data = self.fetch_search_data(params, headers)
|
||||
videos = self.parse_search_videos(data)
|
||||
return self.build_search_result(videos, pg)
|
||||
|
||||
def get_page_number(self, pg):
|
||||
return int(pg) if pg else 1
|
||||
|
||||
def build_search_params(self, key, page):
|
||||
return {'functionName': 'getNewsSearched', 'searchNewsType': '3,4,5', 'keyword': key, 'pageSize': 10, 'page': page, 'siteId': 1000101}
|
||||
|
||||
def fetch_search_data(self, params, headers):
|
||||
detail = requests.get("https://ups.5g.wasu.tv/rmp-user-suggest/1000101/hzhs/searchServlet", params=params, headers=headers)
|
||||
detail.encoding = "utf-8"
|
||||
return detail.json()
|
||||
|
||||
def parse_search_videos(self, data):
|
||||
videos = []
|
||||
for vod in data['data']:
|
||||
videos.append(self.parse_search_video(vod))
|
||||
return videos
|
||||
|
||||
def parse_search_video(self, vod):
|
||||
return {"vod_id": f"{vod['nodeId']}@{vod['newsId']}", "vod_name": vod['title'], "vod_pic": vod['hPic'], "vod_year": vod.get('pubTime', '暂无日期'), "vod_remarks": vod.get('episodeDesc', '暂无备注')}
|
||||
|
||||
def build_search_result(self, videos, pg):
|
||||
result = {'list': videos}
|
||||
result['page'] = pg
|
||||
result['pagecount'] = 9999
|
||||
result['limit'] = 90
|
||||
result['total'] = 999999
|
||||
return result
|
||||
|
||||
def searchContent(self, key, quick, pg="1"):
|
||||
return self.searchContentPage(key, quick, '1')
|
||||
|
||||
def localProxy(self, params):
|
||||
if params['type'] == "m3u8":
|
||||
return self.proxyM3u8(params)
|
||||
elif params['type'] == "media":
|
||||
return self.proxyMedia(params)
|
||||
elif params['type'] == "ts":
|
||||
return self.proxyTs(params)
|
||||
return None
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,445 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import re, urllib.parse
|
||||
import json
|
||||
import time
|
||||
from bs4 import BeautifulSoup
|
||||
import requests
|
||||
from base.spider import Spider as BaseSpider
|
||||
|
||||
|
||||
class Spider(BaseSpider):
|
||||
# 缓存变量,避免频繁请求发布页
|
||||
_cache_host = ""
|
||||
_cache_time = 0
|
||||
CACHE_DURATION = 300 # 缓存5分钟
|
||||
|
||||
def init(self, extend=""):
|
||||
self.headers = {
|
||||
"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
||||
"Accept-Language": "zh-CN,zh;q=0.9",
|
||||
}
|
||||
|
||||
# 优先读取缓存,没有或过期则重新获取
|
||||
now = time.time()
|
||||
if self._cache_host and now - self._cache_time < self.CACHE_DURATION:
|
||||
self.host = self._cache_host
|
||||
else:
|
||||
publish_url = "https://www.vip1949.com/"
|
||||
self.host = self.get_online_host(publish_url)
|
||||
self._cache_host = self.host
|
||||
self._cache_time = now
|
||||
|
||||
def get_online_host(self, publish_url):
|
||||
"""从发布页解析JS域名列表,返回第一个在线可用主站"""
|
||||
try:
|
||||
resp = requests.get(publish_url, headers=self.headers, timeout=15)
|
||||
resp.raise_for_status()
|
||||
html = resp.text
|
||||
|
||||
# 正则提取JS中的domains数组
|
||||
pattern = r'const domains = (\[.*?\]);'
|
||||
match = re.search(pattern, html, re.S)
|
||||
if not match:
|
||||
return "https://www.cd-zj.com"
|
||||
|
||||
js_str = match.group(1)
|
||||
# 修复JS对象格式为标准JSON
|
||||
js_str = re.sub(r'(\w+)\s*:', r'"\1":', js_str)
|
||||
js_str = re.sub(r'"+', '"', js_str)
|
||||
domains = json.loads(js_str)
|
||||
|
||||
# 逐个检测连通性,返回第一个可用
|
||||
for item in domains:
|
||||
url = item["url"]
|
||||
if self.check_url_online(url):
|
||||
return url.rstrip("/")
|
||||
|
||||
return "https://www.cd-zj.com"
|
||||
except Exception as e:
|
||||
print(f"获取发布页异常: {e}")
|
||||
return "https://www.cd-zj.com"
|
||||
|
||||
def check_url_online(self, url, timeout=3):
|
||||
"""检测网址是否可正常访问"""
|
||||
try:
|
||||
r = requests.head(url, headers=self.headers, timeout=timeout, allow_redirects=True)
|
||||
return 200 <= r.status_code < 400
|
||||
except:
|
||||
return False
|
||||
|
||||
def getName(self):
|
||||
return '枫叶影院'
|
||||
|
||||
def homeContent(self, filter):
|
||||
return {"class": [
|
||||
{'type_id': "/label/qq", 'type_name': "腾讯VIP精选"},
|
||||
{'type_id': "/label/bli", 'type_name': "B站VIP精选"},
|
||||
{'type_id': "/label/youku", 'type_name': "优酷VIP精选"},
|
||||
{"type_id": "2", "type_name": "电视剧"},
|
||||
{"type_id": "1", "type_name": "电影"},
|
||||
{"type_id": "4", "type_name": "动漫"},
|
||||
{"type_id": "3", "type_name": "综艺"},
|
||||
{"type_id": "5", "type_name": "热门短剧"},
|
||||
], "filters": self._build_filters()}
|
||||
|
||||
def _build_filters(self):
|
||||
area = [{"n": "全部", "v": ""}, {"n": "大陆", "v": "大陆"}, {"n": "香港", "v": "香港"},
|
||||
{"n": "台湾", "v": "台湾"}, {"n": "美国", "v": "美国"}, {"n": "韩国", "v": "韩国"},
|
||||
{"n": "日本", "v": "日本"}, {"n": "泰国", "v": "泰国"}, {"n": "新加坡", "v": "新加坡"},
|
||||
{"n": "马来西亚", "v": "马来西亚"}, {"n": "印度", "v": "印度"}, {"n": "英国", "v": "英国"},
|
||||
{"n": "法国", "v": "法国"}, {"n": "加拿大", "v": "加拿大"}, {"n": "西班牙", "v": "西班牙"},
|
||||
{"n": "俄罗斯", "v": "俄罗斯"}, {"n": "其它", "v": "其它"}]
|
||||
year = [{"n": "全部", "v": ""}, {"n": "2026", "v": "2026"}, {"n": "2025", "v": "2025"},
|
||||
{"n": "2024", "v": "2024"}, {"n": "2023", "v": "2023"}, {"n": "2022", "v": "2022"},
|
||||
{"n": "2021", "v": "2021"}, {"n": "2020", "v": "2020"}, {"n": "2019", "v": "2019"},
|
||||
{"n": "2018", "v": "2018"}, {"n": "2017", "v": "2017"}, {"n": "2016", "v": "2016"},
|
||||
{"n": "2015", "v": "2015"}, {"n": "2014", "v": "2014"}, {"n": "2013", "v": "2013"},
|
||||
{"n": "2012", "v": "2012"}, {"n": "2011", "v": "2011"}, {"n": "2010", "v": "2010"},
|
||||
{"n": "2009", "v": "2009"}, {"n": "2008", "v": "2008"}, {"n": "2007", "v": "2007"},
|
||||
{"n": "2006", "v": "2006"}, {"n": "2005", "v": "2005"}, {"n": "2004", "v": "2004"}]
|
||||
lang = [{"n": "全部", "v": ""}, {"n": "国语", "v": "国语"}, {"n": "英语", "v": "英语"},
|
||||
{"n": "粤语", "v": "粤语"}, {"n": "闽南语", "v": "闽南语"}, {"n": "韩语", "v": "韩语"},
|
||||
{"n": "日语", "v": "日语"}, {"n": "法语", "v": "法语"}, {"n": "德语", "v": "德语"},
|
||||
{"n": "其它", "v": "其它"}]
|
||||
sort = [{"n": "时间", "v": "time"}, {"n": "人气", "v": "hits"}, {"n": "评分", "v": "score"}]
|
||||
letter = [{"n": "全部", "v": ""}, {"n": "A", "v": "A"}, {"n": "B", "v": "B"}, {"n": "C", "v": "C"},
|
||||
{"n": "D", "v": "D"}, {"n": "E", "v": "E"}, {"n": "F", "v": "F"}, {"n": "G", "v": "G"},
|
||||
{"n": "H", "v": "H"}, {"n": "I", "v": "I"}, {"n": "J", "v": "J"}, {"n": "K", "v": "K"},
|
||||
{"n": "L", "v": "L"}, {"n": "M", "v": "M"}, {"n": "N", "v": "N"}, {"n": "O", "v": "O"},
|
||||
{"n": "P", "v": "P"}, {"n": "Q", "v": "Q"}, {"n": "R", "v": "R"}, {"n": "S", "v": "S"},
|
||||
{"n": "T", "v": "T"}, {"n": "U", "v": "U"}, {"n": "V", "v": "V"}, {"n": "W", "v": "W"},
|
||||
{"n": "X", "v": "X"}, {"n": "Y", "v": "Y"}, {"n": "Z", "v": "Z"}, {"n": "0-9", "v": "0-9"}]
|
||||
return {
|
||||
"2": [
|
||||
{"key": "class", "name": "类型",
|
||||
"value": [{"n": "全部", "v": "2"}, {"n": "国产剧", "v": "13"}, {"n": "日韩剧", "v": "15"},
|
||||
{"n": "海外剧", "v": "16"}]},
|
||||
{"key": "area", "name": "地区", "value": area},
|
||||
{"key": "genre", "name": "剧情", "value": [{"n": v[0], "v": v[1]} for v in
|
||||
[("全部", ""), ("古装", "古装"), ("战争", "战争"),
|
||||
("青春偶像", "青春偶像"), ("喜剧", "喜剧"),
|
||||
("家庭", "家庭"), ("犯罪", "犯罪"), ("动作", "动作"),
|
||||
("奇幻", "奇幻"), ("剧情", "剧情"), ("历史", "历史"),
|
||||
("经典", "经典"), ("乡村", "乡村"), ("情景", "情景"),
|
||||
("商战", "商战"), ("网剧", "网剧"), ("其他", "其他")]]},
|
||||
{"key": "year", "name": "年份", "value": year},
|
||||
{"key": "lang", "name": "语言", "value": lang},
|
||||
{"key": "letter", "name": "字母", "value": letter},
|
||||
{"key": "sort", "name": "排序", "value": sort},
|
||||
],
|
||||
"1": [
|
||||
{"key": "class", "name": "类型",
|
||||
"value": [{"n": "全部", "v": "1"}, {"n": "动作片", "v": "6"}, {"n": "喜剧片", "v": "7"},
|
||||
{"n": "恐怖片", "v": "8"}, {"n": "科幻片", "v": "9"}, {"n": "爱情片", "v": "10"},
|
||||
{"n": "剧情片", "v": "11"}, {"n": "战争片", "v": "12"}, {"n": "纪录片", "v": "20"}]},
|
||||
{"key": "area", "name": "地区", "value": area},
|
||||
{"key": "genre", "name": "剧情", "value": [{"n": v[0], "v": v[1]} for v in
|
||||
[("全部", ""), ("喜剧", "喜剧"), ("爱情", "爱情"),
|
||||
("恐怖", "恐怖"), ("动作", "动作"), ("科幻", "科幻"),
|
||||
("剧情", "剧情"), ("战争", "战争"), ("警匪", "警匪"),
|
||||
("犯罪", "犯罪"), ("动画", "动画"), ("奇幻", "奇幻"),
|
||||
("武侠", "武侠"), ("冒险", "冒险"), ("枪战", "枪战"),
|
||||
("悬疑", "悬疑"), ("惊悚", "惊悚"), ("经典", "经典"),
|
||||
("青春", "青春"), ("文艺", "文艺"), ("微电影", "微电影"),
|
||||
("古装", "古装"), ("历史", "历史"), ("运动", "运动"),
|
||||
("农村", "农村"), ("儿童", "儿童"),
|
||||
("网络电影", "网络电影")]]},
|
||||
{"key": "year", "name": "年份", "value": year},
|
||||
{"key": "lang", "name": "语言", "value": lang},
|
||||
{"key": "letter", "name": "字母", "value": letter},
|
||||
{"key": "sort", "name": "排序", "value": sort},
|
||||
],
|
||||
"4": [
|
||||
{"key": "class", "name": "类型",
|
||||
"value": [{"n": "全部", "v": "4"}, {"n": "国产动漫", "v": "25"}, {"n": "日韩动漫", "v": "26"}]},
|
||||
{"key": "genre", "name": "剧情", "value": [{"n": v[0], "v": v[1]} for v in
|
||||
[("全部", ""), ("情感", "情感"), ("科幻", "科幻"),
|
||||
("热血", "热血"), ("推理", "推理"), ("搞笑", "搞笑"),
|
||||
("冒险", "冒险"), ("奇幻", "奇幻"), ("战斗", "战斗"),
|
||||
("校园", "校园"), ("萝莉", "萝莉"), ("治愈", "治愈"),
|
||||
("原创", "原创"), ("亲子", "亲子"), ("益智", "益智"),
|
||||
("励志", "励志"), ("其他", "其他")]]},
|
||||
{"key": "area", "name": "地区",
|
||||
"value": [{"n": "全部", "v": ""}, {"n": "大陆", "v": "大陆"}, {"n": "香港", "v": "香港"},
|
||||
{"n": "台湾", "v": "台湾"}, {"n": "美国", "v": "美国"}, {"n": "韩国", "v": "韩国"},
|
||||
{"n": "日本", "v": "日本"}, {"n": "法国", "v": "法国"}, {"n": "英国", "v": "英国"},
|
||||
{"n": "其它", "v": "其它"}]},
|
||||
{"key": "year", "name": "年份", "value": year},
|
||||
{"key": "lang", "name": "语言", "value": lang},
|
||||
{"key": "letter", "name": "字母", "value": letter},
|
||||
{"key": "sort", "name": "排序", "value": sort},
|
||||
],
|
||||
"3": [
|
||||
{"key": "class", "name": "类型",
|
||||
"value": [{"n": "全部", "v": "3"}, {"n": "大陆综艺", "v": "21"}, {"n": "日韩综艺", "v": "22"}]},
|
||||
{"key": "genre", "name": "剧情", "value": [{"n": v[0], "v": v[1]} for v in
|
||||
[("全部", ""), ("选秀", "选秀"), ("情感", "情感"),
|
||||
("访谈", "访谈"), ("播报", "播报"), ("音乐", "音乐"),
|
||||
("美食", "美食"), ("旅游", "旅游"), ("搞笑", "搞笑"),
|
||||
("游戏", "游戏"), ("亲子", "亲子"), ("其它", "其它")]]},
|
||||
{"key": "area", "name": "地区",
|
||||
"value": [{"n": "全部", "v": ""}, {"n": "大陆", "v": "大陆"}, {"n": "香港", "v": "香港"},
|
||||
{"n": "台湾", "v": "台湾"}, {"n": "美国", "v": "美国"}, {"n": "韩国", "v": "韩国"},
|
||||
{"n": "日本", "v": "日本"}, {"n": "英国", "v": "英国"}, {"n": "其它", "v": "其它"}]},
|
||||
{"key": "year", "name": "年份", "value": year},
|
||||
{"key": "lang", "name": "语言", "value": lang},
|
||||
{"key": "letter", "name": "字母", "value": letter},
|
||||
{"key": "sort", "name": "排序", "value": sort},
|
||||
],
|
||||
}
|
||||
|
||||
def homeVideoContent(self):
|
||||
html = self._fetch('/')
|
||||
return {"list": self._parse_video_list(html)}
|
||||
|
||||
def categoryContent(self, tid, pg, filter, extend):
|
||||
# 构建筛选参数:参照歪比巴卜,直接取extend里的值,fallback到filter
|
||||
if tid.startswith('/label'):
|
||||
url = f'{tid}/page/{pg}.html'
|
||||
html = self._fetch(url)
|
||||
items = self._parse_video_list(html)
|
||||
page = int(pg)
|
||||
page_count = page if len(items) < 24 else page + 2
|
||||
return {"list": items, "page": page, "pagecount": page_count, "limit": 24, "total": page_count * 24}
|
||||
|
||||
args = {}
|
||||
if extend and isinstance(extend, dict):
|
||||
for k, v in extend.items():
|
||||
if v:
|
||||
args[k] = str(v)
|
||||
if isinstance(filter, dict):
|
||||
for k, v in filter.items():
|
||||
if v and k not in args:
|
||||
args[k] = str(v)
|
||||
route_tid = args.get('class', args.get('tid', str(tid)))
|
||||
area = args.get('area', '')
|
||||
genre = args.get('genre', '')
|
||||
year = args.get('year', '')
|
||||
lang = args.get('lang', '')
|
||||
letter = args.get('letter', '')
|
||||
sort = args.get('sort', '')
|
||||
# 无筛选走正常分页
|
||||
if not area and not genre and not year and not lang and not letter and not sort:
|
||||
url = f'/cupfox-list/{route_tid}--------{pg}---.html'
|
||||
html = self._fetch(url)
|
||||
items = self._parse_video_list(html)
|
||||
page = int(pg)
|
||||
soup = BeautifulSoup(html, 'html.parser')
|
||||
pagecount = page
|
||||
for a in soup.select('a.page-link'):
|
||||
if a.text == '尾页':
|
||||
m = re.search(r'---(\d+)---', a.get('href', ''))
|
||||
if m:
|
||||
pagecount = int(m.group(1))
|
||||
break
|
||||
if not items:
|
||||
pagecount = 0
|
||||
return {"list": items, "page": page, "pagecount": pagecount, "limit": 36, "total": 9999}
|
||||
# 有筛选:{tid}-{area}-{sort}-{genre}-{lang}-{letter}------{year}.html
|
||||
segs = [route_tid, area, sort, genre, lang, letter, '', '', year]
|
||||
url = '/cupfox-list/' + '-'.join(segs) + '.html'
|
||||
html = self._fetch(url)
|
||||
items = self._parse_video_list(html)
|
||||
return {"list": items, "page": 1, "pagecount": 1, "limit": 36, "total": 9999}
|
||||
|
||||
def detailContent(self, ids):
|
||||
result = {"list": []}
|
||||
vid = ids[0].split(',')[0].strip()
|
||||
try:
|
||||
html = self._fetch(f'/detail/{vid}.html')
|
||||
if not html: return result
|
||||
soup = BeautifulSoup(html, 'html.parser')
|
||||
vod_name = soup.select_one('h3.slide-info-title')
|
||||
vod_name = vod_name.text.strip() if vod_name else ''
|
||||
vod_pic = soup.select_one('img.lazy')
|
||||
vod_pic = self._fix_pic(vod_pic.get('data-src', '')) if vod_pic else ''
|
||||
vod_director = ''
|
||||
vod_actor = ''
|
||||
for el in soup.select('.slide-info'):
|
||||
text = el.get_text(' ').strip()
|
||||
if text.startswith('导演:'):
|
||||
vod_director = text.replace('导演:', '').strip()
|
||||
elif text.startswith('演员:'):
|
||||
vod_actor = text.replace('演员:', '').strip()
|
||||
vod_content = soup.select_one('#height_limit')
|
||||
vod_content = vod_content.get_text(' ', strip=True) if vod_content else ''
|
||||
play_from, play_url = [], []
|
||||
for tab in soup.select('.anthology-tab a.swiper-slide'):
|
||||
src_name = re.sub(r'<[^>]+>', '', str(tab)).strip() or tab.get_text(' ', strip=True).strip()
|
||||
if src_name:
|
||||
play_from.append(src_name)
|
||||
tab_blocks = soup.select('.anthology-list-box')
|
||||
for i, block in enumerate(tab_blocks):
|
||||
ep_list = []
|
||||
for a in block.select('li a'):
|
||||
href = a.get('href', '')
|
||||
m = re.search(r'/play/(.*?)\.html', href)
|
||||
if m:
|
||||
ep_list.append(f'{a.text.strip()}${vid}-{m.group(1)}')
|
||||
ep_list.reverse()
|
||||
if ep_list and i < len(play_from):
|
||||
play_url.append('#'.join(ep_list))
|
||||
valid_from = [pf for i, pf in enumerate(play_from) if i < len(play_url)]
|
||||
result["list"].append({
|
||||
"vod_id": vid, "vod_name": vod_name, "vod_pic": vod_pic,
|
||||
"vod_director": vod_director, "vod_actor": vod_actor,
|
||||
"vod_content": vod_content,
|
||||
"vod_play_from": "$$$".join(valid_from),
|
||||
"vod_play_url": "$$$".join(play_url),
|
||||
})
|
||||
except:
|
||||
pass
|
||||
return result
|
||||
|
||||
def searchContent(self, key, quick, pg="1"):
|
||||
try:
|
||||
decoded = urllib.parse.unquote(key)
|
||||
except:
|
||||
decoded = key
|
||||
html = self._fetch(f'/cupfox-search/{urllib.parse.quote(decoded)}----------{pg}---.html')
|
||||
items = self._parse_search_list(html)
|
||||
return {"list": items, "page": int(pg), "pagecount": 1, "limit": 36, "total": len(items)}
|
||||
|
||||
def playerContent(self, flag, id, vipFlags):
|
||||
url = ''
|
||||
try:
|
||||
url = id if id.startswith('http') else f'{self.host}/play/{id}.html'
|
||||
html = self._fetch(url)
|
||||
if html:
|
||||
m = re.search(r'player_aaaa=(.*?)</script>', html, re.S)
|
||||
if m:
|
||||
|
||||
try:
|
||||
pd = json.loads(m.group(1))
|
||||
except Exception as e:
|
||||
print(e)
|
||||
pd = {}
|
||||
# print('pd:', pd)
|
||||
play_url = pd.get('url')
|
||||
play_id = pd.get('from')
|
||||
|
||||
api_map = {
|
||||
'YYNB': 'https://zzrs.mfdyvip.com/player/mplayer.php',
|
||||
'JD4K': 'https://fgsrg.hzqingshan.com/player/mplayer.php',
|
||||
}
|
||||
if not play_url:
|
||||
return {"parse": 0, "url": 'https://php.doube.eu.org/error.m3u8',
|
||||
"header": {'User-Agent': 'Mozilla/5.0'}}
|
||||
if play_url.startswith('http') and (play_url.endswith('.m3u8') or play_url.endswith('.mp4')):
|
||||
return {"parse": 0, "url": play_url, "header": {'User-Agent': 'Mozilla/5.0'}}
|
||||
|
||||
else:
|
||||
headers = {
|
||||
'User-Agent': "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.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,application/signed-exchange;v=b3;q=0.7",
|
||||
'accept-language': "zh-CN,zh;q=0.9",
|
||||
'cache-control': "no-cache",
|
||||
'pragma': "no-cache",
|
||||
'priority': "u=0, i",
|
||||
'referer': "https://www.ht10010.com/",
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
}
|
||||
response = requests.get(f"https://fgsrg.hzqingshan.com/player/?url={play_url}", headers=headers)
|
||||
token = re.search(r'data-te="(.*?)"', response.text)
|
||||
if token:
|
||||
token = token.group(1)
|
||||
payload = {
|
||||
'url': play_url,
|
||||
'token': token
|
||||
}
|
||||
# print('payload', payload)
|
||||
try:
|
||||
response = self.post(api_map[play_id], data=payload, headers=headers)
|
||||
|
||||
response.raise_for_status()
|
||||
result = response.json()
|
||||
# print('result:', result)
|
||||
if result['code'] == 200 and 'url' in result:
|
||||
play_url = result['url']
|
||||
return {"parse": 0, "url": play_url, "header": {
|
||||
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1'}}
|
||||
except Exception as e:
|
||||
print(e)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return {"parse": 1, "url": url}
|
||||
|
||||
def localProxy(self, param=''):
|
||||
return {}
|
||||
|
||||
def isVideoFormat(self, url):
|
||||
return False
|
||||
|
||||
def manualVideoCheck(self):
|
||||
return False
|
||||
|
||||
def _fetch(self, url):
|
||||
try:
|
||||
if not url.startswith('http'):
|
||||
url = self.host + url
|
||||
rsp = self.fetch(url, headers=self.headers)
|
||||
return rsp.text if rsp else ''
|
||||
except:
|
||||
return ''
|
||||
|
||||
def _fix_pic(self, u):
|
||||
if not u: return ''
|
||||
if u.startswith('//'): return 'https:' + u
|
||||
return u.replace('&', '&')
|
||||
|
||||
def _parse_video_list(self, html):
|
||||
videos, seen = [], set()
|
||||
soup = BeautifulSoup(html, 'html.parser')
|
||||
cards = soup.select('a.public-list-exp')
|
||||
for a in cards:
|
||||
href = a.get('href', '')
|
||||
m = re.search(r'/detail/(\d+)\.html', href)
|
||||
if not m: continue
|
||||
vod_id = m.group(1)
|
||||
if vod_id in seen: continue
|
||||
seen.add(vod_id)
|
||||
span = ','.join([span.text for span in a.select('span.public-prt')])
|
||||
# print('span', span)
|
||||
vod_name = a.get('title', '') or (a.select_one('img') and a.select_one('img').get('alt', '')) or ''
|
||||
pic_el = a.select_one('img')
|
||||
vod_pic = self._fix_pic(pic_el.get('data-src', '')) if pic_el else ''
|
||||
remark_el = a.select_one('.ft2') or a.select_one('.public-list-prb')
|
||||
vod_remarks = remark_el.text.strip() if remark_el else ''
|
||||
videos.append(
|
||||
{"vod_id": vod_id, "vod_name": vod_name.strip(), "vod_pic": vod_pic, "vod_remarks": vod_remarks, "vod_year": span})
|
||||
return videos
|
||||
|
||||
def _parse_search_list(self, html):
|
||||
videos, seen = [], set()
|
||||
soup = BeautifulSoup(html, 'html.parser')
|
||||
cards = soup.select('a.public-list-exp')
|
||||
for a in cards:
|
||||
href = a.get('href', '')
|
||||
m = re.search(r'/detail/(\d+)\.html', href)
|
||||
if not m: continue
|
||||
vod_id = m.group(1)
|
||||
if vod_id in seen: continue
|
||||
seen.add(vod_id)
|
||||
pic_el = a.select_one('img')
|
||||
vod_pic = self._fix_pic(pic_el.get('data-src', '')) if pic_el else ''
|
||||
title_el = soup.select_one(f'a.thumb-txt[href="/detail/{vod_id}.html"]')
|
||||
if title_el:
|
||||
vod_name = title_el.text.strip()
|
||||
else:
|
||||
vod_name = a.select_one('img') and a.select_one('img').get('alt', '') or ''
|
||||
remark_el = a.select_one('.public-list-prb') or a.select_one('.ft2')
|
||||
vod_remarks = remark_el.text.strip() if remark_el else ''
|
||||
videos.append(
|
||||
{"vod_id": vod_id, "vod_name": vod_name.strip(), "vod_pic": vod_pic, "vod_remarks": vod_remarks})
|
||||
return videos
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sp = Spider()
|
||||
sp.init()
|
||||
# 20067-5-189
|
||||
print(sp.categoryContent('/label/qq','1',True, {}))
|
||||
# print(sp.playerContent('', '20067-6-189', []))
|
||||
# print(sp.playerContent('', '20067-5-189', []))
|
||||
pass
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
/**
|
||||
* 毒舌影视 - 2026/01/22 源码定点适配版 (移除console.log)
|
||||
* 适配路径: /dsshiyidt/
|
||||
* 适配图片: data-original
|
||||
*/
|
||||
|
||||
const host = 'https://www.xnhrsb.com';
|
||||
const headers = {
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
||||
'Referer': host + '/'
|
||||
};
|
||||
|
||||
async function init(cfg) {}
|
||||
|
||||
const m = (s, r, i = 1) => (s.match(r) || [])[i] || "";
|
||||
const fixPic = p => p && p.startsWith('/') ? host + p : p;
|
||||
|
||||
/**
|
||||
* 核心解析:针对 <li> 结构进行精准提取
|
||||
*/
|
||||
function getList(html) {
|
||||
let list = [];
|
||||
// 源码特征:<li class="..."><a href="/dsshiyidt/ID.html">...<img data-original="PIC" alt="NAME">...</h3>
|
||||
// 使用 pdfa 提取所有 li 元素
|
||||
const items = pdfa(html, ".bt_img ul li");
|
||||
|
||||
items.forEach(it => {
|
||||
// 提取 ID (匹配 dsshiyidt 或 dsshiyixq 或 dsshiyipy 以防万一)
|
||||
let id = m(it, /href="\/dsshiyi(?:dt|xq|py)\/(.*?)\.html"/);
|
||||
if (!id) return;
|
||||
|
||||
// 提取图片:源码明确使用 data-original
|
||||
let pic = m(it, /data-original="([^"]+)"/) || m(it, /src="([^"]+)"/);
|
||||
|
||||
// 提取标题:源码在 alt 属性里,也在 dytit 类的 a 标签里
|
||||
let name = m(it, /alt="([^"]+)"/) || m(it, /class="dytit"[\s\S]*?>(.*?)<\/a>/);
|
||||
name = name ? name.replace(/<.*?>/g, "").trim() : "";
|
||||
|
||||
// 提取备注:源码在 hdinfo 类里
|
||||
let remarks = m(it, /class="hdinfo">[\s\S]*?<span>(.*?)<\/span>/);
|
||||
|
||||
if (id && name) {
|
||||
list.push({
|
||||
vod_id: id,
|
||||
vod_name: name,
|
||||
vod_pic: fixPic(pic),
|
||||
vod_remarks: remarks.trim()
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
return JSON.stringify({
|
||||
class: [
|
||||
{ type_id: "1", type_name: "电影" },
|
||||
{ type_id: "2", type_name: "电视剧" },
|
||||
{ type_id: "3", type_name: "综艺" },
|
||||
{ type_id: "4", type_name: "动漫" },
|
||||
{ type_id: "5", type_name: "短剧" }
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
const r = await req(host, { headers });
|
||||
return JSON.stringify({ list: getList(r.content) });
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend = {}) {
|
||||
let p = pg || 1;
|
||||
// 适配源码中的 11 横杠分页逻辑
|
||||
// 第一页: /dsshiyisw/1-----------.html
|
||||
// 第二页: /dsshiyisw/1--------2---.html
|
||||
let url = `${host}/dsshiyisw/${tid}-----------.html`;
|
||||
if (p > 1) {
|
||||
url = `${host}/dsshiyisw/${tid}--------${p}---.html`;
|
||||
}
|
||||
|
||||
const r = await req(url, { headers });
|
||||
const list = getList(r.content);
|
||||
|
||||
// 已移除 console.log
|
||||
return JSON.stringify({ page: p, list: list });
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
// 详情页路径统一尝试 dt (源码中新发现的路径)
|
||||
const r = await req(`${host}/dsshiyidt/${id}.html`, { headers });
|
||||
const h = r.content;
|
||||
|
||||
// 详情页解析:标题在 h1,图片在 dyimg 类的 img 里
|
||||
const name = m(h, /<h1[^>]*>(.*?)<\/h1>/);
|
||||
const pic = fixPic(m(h, /class="dyimg"[\s\S]*?src="(.*?)"/));
|
||||
const content = m(h, /id="p_content"[\s\S]*?>(.*?)<\/div>/s).replace(/<.*?>/g, "").trim();
|
||||
|
||||
// 播放源解析
|
||||
const playFrom = pdfa(h, ".mi_paly_box .ypxingq_t").map(it => it.replace(/<.*?>/g, "").trim()).join("$$$") || "播放源";
|
||||
const playUrl = pdfa(h, ".paly_list_btn").map(l =>
|
||||
pdfa(l, "a").map(it => {
|
||||
let aName = it.replace(/<.*?>/g, "").trim();
|
||||
let aId = m(it, /href="\/dsshiyipy\/(.*?)\.html"/);
|
||||
if (!aId || aName.includes('立即播放')) return null;
|
||||
return `${aName}$${aId}`;
|
||||
}).filter(Boolean).join("#")
|
||||
).join("$$$");
|
||||
|
||||
return JSON.stringify({
|
||||
list: [{
|
||||
vod_id: id,
|
||||
vod_name: name,
|
||||
vod_pic: pic,
|
||||
vod_content: content || "暂无简介",
|
||||
vod_play_from: playFrom,
|
||||
vod_play_url: playUrl
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
async function search(wd, quick, pg = 1) {
|
||||
let url = `${host}/dsshiyisc/${encodeURIComponent(wd)}----------${pg}---.html`;
|
||||
const r = await req(url, { headers });
|
||||
return JSON.stringify({ list: getList(r.content) });
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
const r = await req(`${host}/dsshiyipy/${id}.html`, { headers });
|
||||
const html = r.content;
|
||||
// 提取播放器配置中的 m3u8 地址
|
||||
let u = m(html, /"url"\s*:\s*"([^"]+\.m3u8[^"]*)"/);
|
||||
if (u) return JSON.stringify({ parse: 0, url: u.replace(/\\/g, ""), header: headers });
|
||||
// 如果没有直链,调用 webview 解析
|
||||
return JSON.stringify({ parse: 1, url: `${host}/dsshiyipy/${id}.html` });
|
||||
}
|
||||
|
||||
export default { init, home, homeVod, category, detail, search, play };
|
||||
+271
@@ -0,0 +1,271 @@
|
||||
import { Crypto, load, _ } from 'assets://js/lib/cat.js';
|
||||
|
||||
let siteUrl = 'https://gaoqingju.com';
|
||||
let siteKey = '';
|
||||
let siteType = 0;
|
||||
|
||||
let headers = {
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.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,application/signed-exchange;v=b3;q=0.7',
|
||||
'Accept-Language': 'zh-CN,zh;q=0.9',
|
||||
'Referer': 'https://gaoqingju.com/'
|
||||
};
|
||||
|
||||
async function request(reqUrl, postData, agentSp, get) {
|
||||
let res = await req(reqUrl, {
|
||||
method: get ? 'get' : 'post',
|
||||
headers: headers,
|
||||
data: postData || {},
|
||||
postType: get ? '' : 'form',
|
||||
});
|
||||
return res.content;
|
||||
}
|
||||
|
||||
async function init(cfg) {
|
||||
siteKey = cfg.skey;
|
||||
siteType = cfg.stype;
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
let classes = [
|
||||
{ type_id: 'movie', type_name: '电影' },
|
||||
{ type_id: 'tv', type_name: '电视剧' },
|
||||
{ type_id: 'manga', type_name: '动漫' },
|
||||
{ type_id: 'jilu', type_name: '纪录片' }
|
||||
];
|
||||
|
||||
let filterObj = genFilterObj();
|
||||
return JSON.stringify({
|
||||
class: classes,
|
||||
filters: filterObj
|
||||
});
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
let html = await request(siteUrl, null, null, true);
|
||||
const $ = load(html);
|
||||
let videos = [];
|
||||
|
||||
$('.grid > a.group').each((i, n) => {
|
||||
let pic = $(n).find('img').attr('src') || '';
|
||||
if (pic.startsWith('/')) {
|
||||
pic = siteUrl + pic;
|
||||
}
|
||||
videos.push({
|
||||
vod_id: $(n).attr('href'),
|
||||
vod_name: $(n).find('h3').text().trim(),
|
||||
vod_pic: pic,
|
||||
vod_remarks: $(n).find('.absolute.top-2.right-2').text().trim() || $(n).find('.absolute.bottom-0 p').text().trim()
|
||||
});
|
||||
});
|
||||
|
||||
return JSON.stringify({
|
||||
list: videos,
|
||||
});
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
let url = `${siteUrl}/index/${tid}.html`;
|
||||
|
||||
if (extend && Object.keys(extend).length > 0) {
|
||||
let id = extend['id'] || 't';
|
||||
let area = extend['area'] || 'a';
|
||||
let year = extend['year'] || 'y';
|
||||
let clazz = extend['class'] || 's';
|
||||
|
||||
if (tid === 'tv') {
|
||||
let status = extend['status'] || 'n';
|
||||
url = `${siteUrl}/index/${tid}-${id}-${area}-${year}-${clazz}-${status}.html`;
|
||||
} else {
|
||||
url = `${siteUrl}/index/${tid}-${id}-${area}-${year}-${clazz}.html`;
|
||||
}
|
||||
}
|
||||
|
||||
if (pg && pg > 1) {
|
||||
url += `?page=${pg}`;
|
||||
}
|
||||
|
||||
let videos = await getVideos(url);
|
||||
return JSON.stringify({
|
||||
list: videos,
|
||||
});
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
try {
|
||||
let url = id.startsWith('http') ? id : siteUrl + id;
|
||||
const html = await request(url, null, null, true);
|
||||
const $ = load(html);
|
||||
|
||||
let title = $('h1.text-4xl').text().trim();
|
||||
let pic = $('.aspect-poster img').attr('src') || '';
|
||||
if (pic.startsWith('/')) {
|
||||
pic = siteUrl + pic;
|
||||
}
|
||||
|
||||
let director = '';
|
||||
let actor = '';
|
||||
|
||||
$('div.mb-6.space-y-2 > p').each((i, el) => {
|
||||
let text = $(el).text();
|
||||
if (text.includes('导演:')) director = text.replace('导演:', '').trim();
|
||||
if (text.includes('主演:')) actor = text.replace('主演:', '').trim();
|
||||
});
|
||||
|
||||
let content = $('h3:contains("剧情简介")').next('p').text().trim();
|
||||
|
||||
let fromList = [];
|
||||
let urlList = [];
|
||||
|
||||
let containers = $('[x-ref="episodesContainer"], [x-ref="backupEpisodesContainer"]');
|
||||
|
||||
if (containers.length > 0) {
|
||||
containers.each((idx, container) => {
|
||||
let sourceName = idx === 0 ? '在线播放' : '在线播放' + (idx + 1);
|
||||
let currentUrls = [];
|
||||
|
||||
$(container).find('a').each((i, n) => {
|
||||
let name = $(n).text().replace(/\s+/g, '').trim();
|
||||
let link = $(n).attr('href');
|
||||
if (link) {
|
||||
currentUrls.push(`${name}$${link}`);
|
||||
}
|
||||
});
|
||||
|
||||
if (currentUrls.length > 0) {
|
||||
fromList.push(sourceName);
|
||||
urlList.push(currentUrls.join('#'));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
let backupUrls = [];
|
||||
$('.bg-gray-900\\/50 a').each((i, n) => {
|
||||
let name = $(n).text().replace(/\s+/g, '').trim();
|
||||
let link = $(n).attr('href');
|
||||
if (link) {
|
||||
backupUrls.push(`${name}$${link}`);
|
||||
}
|
||||
});
|
||||
if (backupUrls.length > 0) {
|
||||
fromList.push('在线播放');
|
||||
urlList.push(backupUrls.join('#'));
|
||||
}
|
||||
}
|
||||
|
||||
const video = {
|
||||
vod_id: id,
|
||||
vod_name: title,
|
||||
vod_pic: pic,
|
||||
vod_actor: actor,
|
||||
vod_director: director,
|
||||
vod_content: content,
|
||||
vod_play_from: fromList.join('$$$'),
|
||||
vod_play_url: urlList.join('$$$'),
|
||||
};
|
||||
|
||||
return JSON.stringify({ list: [video] });
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async function search(wd, quick, pg) {
|
||||
let url = `${siteUrl}/index/search/get_search_data.html?keyword=${encodeURIComponent(wd)}`;
|
||||
if (pg && pg > 1) url += `&page=${pg}`;
|
||||
|
||||
let videos = await getVideos(url);
|
||||
return JSON.stringify({
|
||||
list: videos,
|
||||
});
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
let url = id.startsWith('http') ? id : siteUrl + id;
|
||||
const html = await request(url, null, null, true);
|
||||
|
||||
let playUrlMatch = html.match(/let\s+playUrl\s*=\s*['"](.*?)['"]/);
|
||||
let rawUrl = playUrlMatch ? playUrlMatch[1] : '';
|
||||
let finalUrl = rawUrl;
|
||||
|
||||
if (rawUrl.includes('target=')) {
|
||||
try {
|
||||
let urlObj = new URL(rawUrl);
|
||||
let targetBase64 = urlObj.searchParams.get('target');
|
||||
if (targetBase64) {
|
||||
finalUrl = base64Decode(targetBase64);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
return JSON.stringify({
|
||||
parse: 0,
|
||||
url: finalUrl,
|
||||
});
|
||||
}
|
||||
|
||||
async function getVideos(url) {
|
||||
const html = await request(url, null, null, true);
|
||||
const $ = load(html);
|
||||
let videos = [];
|
||||
|
||||
$('.grid > div.group a').each((i, n) => {
|
||||
let id = $(n).attr('href');
|
||||
let name = $(n).find('h3').text().trim();
|
||||
let pic = $(n).find('img').attr('src') || '';
|
||||
if (pic.startsWith('/')) {
|
||||
pic = siteUrl + pic;
|
||||
}
|
||||
let remark = $(n).find('.absolute.top-2.right-2').text().trim();
|
||||
|
||||
videos.push({
|
||||
vod_id: id,
|
||||
vod_name: name,
|
||||
vod_pic: pic,
|
||||
vod_remarks: remark,
|
||||
});
|
||||
});
|
||||
return videos;
|
||||
}
|
||||
|
||||
function base64Encode(text) {
|
||||
return Crypto.enc.Base64.stringify(Crypto.enc.Utf8.parse(text));
|
||||
}
|
||||
|
||||
function base64Decode(text) {
|
||||
return Crypto.enc.Utf8.stringify(Crypto.enc.Base64.parse(text));
|
||||
}
|
||||
|
||||
function genFilterObj() {
|
||||
return {
|
||||
'movie': [
|
||||
{ key: 'id', name: '类型', value: [{n:'全部',v:'t'},{n:'动作片',v:'3'},{n:'恐怖片',v:'4'},{n:'剧情片',v:'5'},{n:'战争片',v:'6'},{n:'爱情片',v:'9'},{n:'科幻片',v:'10'},{n:'惊悚片',v:'11'},{n:'犯罪片',v:'12'},{n:'悬疑片',v:'13'},{n:'喜剧片',v:'29'}] },
|
||||
{ key: 'year', name: '年份', value: [{n:'全部',v:'y'},{n:'2026',v:'2026'},{n:'2025',v:'2025'},{n:'2024',v:'2024'},{n:'2023',v:'2023'},{n:'2022',v:'2022'},{n:'2021',v:'2021'}] }
|
||||
],
|
||||
'tv': [
|
||||
{ key: 'id', name: '地区', value: [{n:'全部',v:'t'},{n:'国产剧',v:'18'},{n:'港台剧',v:'19'},{n:'欧美剧',v:'20'},{n:'韩剧',v:'21'},{n:'日剧',v:'40'}] },
|
||||
{ key: 'year', name: '年份', value: [{n:'全部',v:'y'},{n:'2026',v:'2026'},{n:'2025',v:'2025'},{n:'2024',v:'2024'},{n:'2023',v:'2023'},{n:'2022',v:'2022'}] },
|
||||
{ key: 'status', name: '状态', value: [{n:'全部',v:'n'},{n:'连载中',v:'2'},{n:'已完结',v:'1'}] }
|
||||
],
|
||||
'manga': [
|
||||
{ key: 'id', name: '分类', value: [{n:'全部',v:'t'},{n:'中国',v:'8'},{n:'日本',v:'34'},{n:'韩国',v:'35'},{n:'欧美',v:'37'}] }
|
||||
],
|
||||
'jilu': [
|
||||
{ key: 'id', name: '分类', value: [{n:'全部',v:'t'},{n:'中国',v:'8'},{n:'日本',v:'34'},{n:'韩国',v:'35'},{n:'欧美',v:'37'}] }
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init,
|
||||
home: home,
|
||||
homeVod: homeVod,
|
||||
category: category,
|
||||
detail: detail,
|
||||
play: play,
|
||||
search: search,
|
||||
};
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
let host = 'https://www.guipian360.com';
|
||||
let headers = {
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
|
||||
"Referer": host + "/"
|
||||
};
|
||||
|
||||
const extractVideos = html => {
|
||||
if (!html) return [];
|
||||
return pdfa(html, '.m-movies .u-movie').map(it => {
|
||||
const url = it.match(/href="([^"]+)"/)?.[1] || "";
|
||||
const id = url.match(/\/(\d+)\.html/)?.[1];
|
||||
let name = it.match(/title="([^"]+)"/)?.[1] || "";
|
||||
name = name.split('/')[0].trim().replace(/.*《([^》]+)》.*/, "$1");
|
||||
const pic = it.match(/src="([^"]+)"/)?.[1] || "";
|
||||
const remarks = it.match(/class="zhuangtai"[^>]*>([^<]+)/)?.[1] || "";
|
||||
return id && name ? {
|
||||
vod_id: id,
|
||||
vod_name: name,
|
||||
vod_pic: pic.startsWith('/') ? host + pic : pic,
|
||||
vod_remarks: remarks.trim()
|
||||
} : null;
|
||||
}).filter(Boolean);
|
||||
};
|
||||
|
||||
const home = async () => {
|
||||
const list = extractVideos((await req(host, { headers }))?.content || '');
|
||||
return JSON.stringify({
|
||||
class: [
|
||||
{type_id:"1",type_name:"鬼片大全"},{type_id:"6",type_name:"大陆鬼片"},{type_id:"9",type_name:"港台鬼片"},
|
||||
{type_id:"8",type_name:"林正英鬼片"},{type_id:"7",type_name:"日韩鬼片"},{type_id:"11",type_name:"欧美鬼片"},
|
||||
{type_id:"10",type_name:"泰国鬼片"},{type_id:"3",type_name:"恐怖片"},{type_id:"2",type_name:"电视剧"},
|
||||
{type_id:"12",type_name:"国产剧"},{type_id:"20",type_name:"港台剧"},{type_id:"13",type_name:"美剧"},
|
||||
{type_id:"14",type_name:"韩剧"},{type_id:"15",type_name:"日剧"},{type_id:"16",type_name:"泰剧"},
|
||||
{type_id:"22",type_name:"其它剧"},{type_id:"4",type_name:"动漫"}
|
||||
],
|
||||
filters: {},
|
||||
list
|
||||
});
|
||||
};
|
||||
|
||||
const homeVod = async () => {
|
||||
const list = extractVideos((await req(host, { headers }))?.content || '');
|
||||
return JSON.stringify({ list });
|
||||
};
|
||||
|
||||
const category = async (tid, pg, _, extend) => {
|
||||
const url = pg > 1 ? `${host}/list/${tid}_${pg}.html` : `${host}/list/${tid}.html`;
|
||||
const html = (await req(url, { headers }))?.content || '';
|
||||
const list = extractVideos(html);
|
||||
const m = html.match(/href="\/list\/\d+_(\d+)\.html"[^>]*>\.\.(\d+)<\/a>/);
|
||||
return JSON.stringify({ list, page: +pg, pagecount: m ? +m[1] : 999, limit: 20 });
|
||||
};
|
||||
|
||||
const detail = async id => {
|
||||
const html = (await req(`${host}/nv/${id}.html`, { headers }))?.content || '';
|
||||
if (!html) return JSON.stringify({ list: [] });
|
||||
|
||||
const tabs = pdfa(html, '#tv_tab li a').map(a => (a.match(/>([^<]+)/)?.[1] || "线路").trim());
|
||||
const urls = pdfa(html, '#tv_tab .list').map(list =>
|
||||
pdfa(list, 'ul.abc li a').map(a => {
|
||||
const name = (a.match(/>([^<]+)/)?.[1] || "播放").trim();
|
||||
const vid = (a.match(/href="\/play\/(.*?)\.html"/)?.[1] || "");
|
||||
return `${name}$${vid}`;
|
||||
}).join('#')
|
||||
);
|
||||
|
||||
if (!tabs.length || !urls.length) return JSON.stringify({ list: [] });
|
||||
|
||||
let vod_name = (html.match(/<h1>(.*?)<\/h1>/)?.[1] || "").split('/')[0].trim().replace(/.*《([^》]+)】?.*/, "$1");
|
||||
const m = s => (html.match(s) || ["", ""])[1].trim();
|
||||
const vod_pic = m(/<img[^>]+src="([^"]+)"[^>]*class="lazy"/);
|
||||
const vod_content = (html.match(/<p class="jianjie-p"[^>]*>([\s\S]*?)<\/p>/)?.[1] || "").replace(/<.*?>/g, "").trim() || "暂无简介";
|
||||
|
||||
return JSON.stringify({
|
||||
list: [{
|
||||
vod_id: id,
|
||||
vod_name,
|
||||
vod_pic: vod_pic.startsWith('/') ? host + vod_pic : vod_pic,
|
||||
vod_content,
|
||||
vod_director: m(/<li class="hidden-xs"><span>导演:<\/span>(.*?)<\/li>/),
|
||||
vod_actor: m(/<li class="hidden-xs"><span>主演:<\/span>(.*?)<\/li>/),
|
||||
vod_year: m(/<strong>(\d{4})年<\/strong>/),
|
||||
vod_area: m(/地区:<\/span><a[^>]*>(.*?)<\/a>/),
|
||||
vod_lang: m(/语言:<\/span>(.*?)<\/li>/),
|
||||
vod_play_from: tabs.join('$$$'),
|
||||
vod_play_url: urls.join('$$$')
|
||||
}]
|
||||
});
|
||||
};
|
||||
|
||||
const search = async (wd, _, pg = 1) => {
|
||||
const url = `${host}/vodsearch/${encodeURIComponent(wd)}----------${pg}---.html`;
|
||||
const list = extractVideos((await req(url, { headers }))?.content || '');
|
||||
return JSON.stringify({ list });
|
||||
};
|
||||
|
||||
const play = async (_, id) => {
|
||||
const html = (await req(`${host}/play/${id}.html`, { headers }))?.content || '';
|
||||
const m3u8 = html.match(/var now="([^"]+)";/);
|
||||
return JSON.stringify({
|
||||
parse: m3u8 ? 0 : 1,
|
||||
url: m3u8 ? m3u8[1] : `${host}/play/${id}.html`,
|
||||
header: headers
|
||||
});
|
||||
};
|
||||
|
||||
export default {
|
||||
init: async () => {},
|
||||
home,
|
||||
homeVod,
|
||||
category,
|
||||
detail,
|
||||
search,
|
||||
play
|
||||
};
|
||||
Reference in New Issue
Block a user