This commit is contained in:
Liu committed 2025-12-31 01:06:51 +08:00
1 parent ea11a77fae
commit 135c13982d
36 files changed
+13070 -9881

No files matched your search

File renamed without changes.
-289
View File
@@ -1,289 +0,0 @@
# -*- coding: utf-8 -*-
# 基于原作者 @嗷呜 版本修改
# 本资源来源于互联网公开渠道,仅可用于个人学习爬虫技术。
# 严禁将其用于任何商业用途,下载后请于 24 小时内删除,搜索结果均来自源站,本人不承担任何责任。
from base.spider import Spider
from urllib.parse import urlparse, urlencode
import re,sys,time,json,urllib3,hashlib,datetime
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
sys.path.append('..')
class Spider(Spider):
api,apisignkey,datasignkey,detail_type,search_data = '', '' , '','',''
headers = {
'User-Agent': 'okhttp/4.12.0',
}
def init(self, extend=""):
ext = extend.rstrip()
if ext.startswith('http'):
self.api = ext.rstrip('/')
else:
arr = json.loads(ext)
self.api = arr['api'].rstrip('/')
if self.api.endswith('v1.vod'):
self.detail_type = arr.get('detail_type','')
self.apisignkey = arr.get('apisignkey', '')
if self.apisignkey:
self.datasignkey = arr.get('datasignkey', '6QQNUsP3PkD2ajJCPCY8')
def homeContent(self, filter):
if self.api.endswith('v1.vod'):
path = '/types'
if self.apisignkey and self.datasignkey:
path = self.datasign(path)
data = self.fetch(f"{self.api}{path}", headers=self.headers, verify=False).text
data = json.loads(data[1:] if data.startswith('\ufeff') else data)
data = data['data']
else:
data = self.fetch(f"{self.api}/nav?token=", headers=self.headers, verify=False).json()
keys = ["class", "area", "lang", "year", "letter", "by", "sort"]
filters = {}
classes = []
for item in data.get('list',data.get('data',[])):
has_non_empty_field = False
jsontype_extend = item["type_extend"]
classes.append({"type_name": item["type_name"], "type_id": item["type_id"]})
for key in keys:
if key in jsontype_extend and jsontype_extend[key].strip() != "":
has_non_empty_field = True
break
if has_non_empty_field:
filters[str(item["type_id"])] = []
for dkey in jsontype_extend:
if dkey in keys and jsontype_extend[dkey].strip() != "":
values = jsontype_extend[dkey].split(",")
value_array = [{"n": value.strip(), "v": value.strip()} for value in values if
value.strip() != ""]
filters[str(item["type_id"])].append({"key": dkey, "name": dkey, "value": value_array})
result = {"class": classes, "filters": filters}
return result
def homeVideoContent(self):
if self.api.endswith('v1.vod'):
path = '/vodPhbAll'
if self.apisignkey and self.datasignkey:
keytime = self.keytime()
path += self.datasign(f'?apikey={self.apikey()}&keytime={keytime}',keytime)
data = self.fetch(f"{self.api}{path}", headers=self.headers, verify=False).text
data = json.loads(data[1:] if data.startswith('\ufeff') else data)
data = data['data']
else:
data = self.fetch(f"{self.api}/index_video?token=", headers=self.headers, verify=False).json()
videos = []
if self.api.endswith('v1.vod'):
for item in data['list']: videos.extend(item['vod_list'])
elif 'list' in data:
for item in data['list']: videos.extend(item['vlist'])
elif 'data' in data:
for item in data['data']: videos.extend(item['vlist'])
return {'list': videos}
def categoryContent(self, tid, pg, filter, extend):
if self.api.endswith('v1.vod'):
path = f"?type={tid}&class={extend.get('class', '')}&lang={extend.get('lang', '')}&area={extend.get('area', '')}&year={extend.get('year', '')}&by=&page={pg}&limit=9"
if self.apisignkey and self.datasignkey:
keytime = self.keytime()
path = self.datasign(f'{path}&apikey={self.apikey()}&keytime={keytime}' ,keytime)
data = self.fetch(f"{self.api}{path}", headers=self.headers, verify=False).text
data = json.loads(data[1:] if data.startswith('\ufeff') else data)
data = data['data']
else:
params = {'tid': tid, 'class': extend.get('class', ''), 'area': extend.get('area', ''), 'lang': extend.get('lang', ''), 'year': extend.get('year', ''), 'limit': '18', 'pg': pg}
data = self.fetch(f"{self.api}/video", params=params, headers=self.headers, verify=False).json()
if 'data' in data:
data = {'list':data['data']}
return data
def searchContent(self, key, quick, pg="1"):
if self.api.endswith('v1.vod'):
path = f"?page={pg}&limit=10&wd={key}"
if self.apisignkey and self.datasignkey:
keytime = self.keytime()
path = self.datasign(f'{path}&apikey={self.apikey()}&keytime={keytime}',keytime)
else:
path = f"/search?text={key}&pg={pg}"
data = self.fetch(f"{self.api}{path}", headers=self.headers, verify=False).text
data = json.loads(data[1:] if data.startswith('\ufeff') else data)
if self.api.endswith('v1.vod') and self.detail_type == 'search':
self.search_data = data
data2 = data.get('list',data.get('data',[]))
if 'type' in data2:
for item in data2:
item.pop('type', None)
if not 'list' in data2:
data2 = {'list': data2, 'page': pg}
return data2
def detailContent(self, ids):
if self.detail_type == 'search':
for i in self.search_data['data']['list']:
if str(i['vod_id']) == str(ids[0]):
data = i
break
else:
if self.api.endswith('v1.vod'):
path = f'/detail?vod_id={ids[0]}&rel_limit=10'
if self.apisignkey and self.datasignkey:
keytime = self.keytime()
path = self.datasign(f'{path}&apikey={self.apikey()}&keytime={keytime}',keytime)
else:
path = f'/video_detail?id={ids[0]}'
data = self.fetch(f"{self.api}{path}", headers=self.headers, verify=False).text
data = json.loads(data[1:] if data.startswith('\ufeff') else data)
data = data['data']
if 'vod_info' in data:
data = data['vod_info']
show = []
vod_play_url = []
if 'vod_url_with_player' in data:
for i in data['vod_url_with_player']:
if i['code'] == i['name']:
show.append(i['name'])
else:
show.append(f"{i['name']}\u2005({i['code']})")
parse_api = i.get('parse_api','')
if parse_api and parse_api.startswith('http'):
url = i.get('url','')
if url:
url2 = '#'.join([i+ '@' + parse_api for i in url.split('#')])
vod_play_url.append(url2)
else:
vod_play_url.append(i.get('url', ''))
data.pop('vod_url_with_player')
if 'vod_play_list' in data:
vod_play_list = data['vod_play_list']
for i in vod_play_list.values() if isinstance(vod_play_list, dict) else vod_play_list:
parses = ''
player_info = i['player_info']
if player_info['show'] == i['from']:
show.append(player_info['show'])
else:
show.append(f"{player_info['show']}\u2005({i['from']})")
parse = player_info.get('parse','').strip()
parse2 = player_info.get('parse2','').strip()
if 'parse' in player_info and parse.startswith('http'):
parses += parse + ','
if 'parse2' in player_info and parse2.startswith('http') and parse2 != parse:
parses += parse2
parses = parses.rstrip(',')
url = ''
urls = i['urls']
for j in urls.values() if isinstance(urls, dict) else urls:
if parse:
url += f"{j['name']}${j['url']}@{parses}#"
else:
url += f"{j['name']}${j['url']}#"
url = url.rstrip('#')
vod_play_url.append(url)
if 'vod_play_list' in data:
data.pop('vod_play_list')
if 'rel_vods' in data:
data.pop('rel_vods')
if 'type' in data:
data.pop('type')
if show:
data['vod_play_from'] = '$$$'.join(show)
if vod_play_url:
data['vod_play_url'] = '$$$'.join(vod_play_url)
return {'list': [data]}
def playerContent(self, flag, id, vipFlags):
video_pattern = re.compile(r'https?:\/\/.*\.(?:m3u8|mp4|flv)')
jx,url,ua = 0,'','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36'
if '@' in id:
rawurl, jxapi = id.split('@', 1)
jxapis = jxapi.split(',', 1) if ',' in jxapi else [jxapi]
for jxapi_ in jxapis:
try:
res = self.fetch(f"{jxapi_}{rawurl}", headers=self.headers, timeout=10, verify=False).text
res = json.loads(res[1:] if res.startswith('\ufeff') else res)
url = res.get('url', '')
if url.startswith('http'):
jxua = res.get('ua')
if jxua:
ua = jxua
except Exception:
url = ''
continue
if url.startswith('http'):
jx = 0
else:
url = rawurl
jx = 0 if video_pattern.match(rawurl) else 1
else:
url = id
jx = 0 if video_pattern.match(id) else 1
if url.startswith('NBY'):
jx, url = 0,''
return {'jx': jx,'parse': 0,'url': url,'header': {'User-Agent': ua}}
def keytime(self):
return str(int(datetime.datetime.now().timestamp()))
def md5(self, str):
hash_obj = hashlib.md5()
hash_obj.update(str.encode('utf-8'))
return hash_obj.hexdigest()
def apikey(self):
date = datetime.datetime.now()
year = str(date.year)
hour = str(date.hour)
minute = str(date.minute)
if len(hour) < 2:
hour = "0" + hour
if len(minute) < 2:
minute = "0" + minute
str_value = self.apisignkey
sign_str = f"{year}:{hour}:{year}:{minute}:{str_value}"
md5_hash = self.md5(sign_str)
return md5_hash
def datasign(self, url='', timestamp=''):
parsed_url = urlparse(url)
query_params = self._parse_query_params(parsed_url.query)
if not timestamp:
timestamp = str(time.time())
query_params["timestamp"] = timestamp
sorted_params = sorted(query_params.items(), key=lambda x: x[0])
sign = self._generate_signature(sorted_params)
query_params["datasign"] = sign
new_query = urlencode(query_params)
new_url = parsed_url._replace(query=new_query).geturl()
return new_url
def _parse_query_params(self, query_str):
params = {}
if not query_str:
return params
for param in query_str.split('&'):
if '=' not in param:
continue
key, value = param.split('=', 1)
if value:
params[key] = value
return params
def _generate_signature(self, sorted_params):
param_str = '&'.join([f"{k}={v}" for k, v in sorted_params])
raw_sign_str = f"{param_str}{self.datasignkey}"
md5_hash = hashlib.md5(raw_sign_str.encode('utf-8')).hexdigest()
return md5_hash
def localProxy(self, param):
pass
def getName(self):
pass
def isVideoFormat(self, url):
pass
def manualVideoCheck(self):
pass
def destroy(self):
pass
@@ -0,0 +1,663 @@
/*小心儿悠悠
配置说明:
{
"host": "https://xxx", // 必填,站点地址
"ver": 1, // 可选,API版本:1或2,默认1
"cms": "", // 可选,CMS地址
"from": "", // 可选,搜索来源
"category": 1, // 可选,分类模式:0-无分类 1-普通 2-搜索
"timeout": 5, // 可选,超时时间(秒),默认5
"custom_first": 0, // 可选,自定义解析优先:0-否 1-是
"source_order": ["线路A", "线路B"], // 可选,线路排序
// UA配置//可选,特殊站点需要自定义
"ua": {
"host": "UA字符串", // 主机检测UA
"config": "UA字符串", // 配置获取UA
"home": "UA字符串", // 首页UA
"category": "UA字符串", // 分类页UA
"search": "UA字符串", // 搜索页UA
"parse": "UA字符串", // 解析UA
"player": "UA字符串" // 播放UA
},
// 解析配置
"parse": {
"线路名": "解析地址或parse:前缀"
}
}
使用示例:
{
"host": "https://xxx",
"ver": 1,
"source_order": ["超清", "高清", "标清"],
"ua": {
"home": "Mozilla/5.0...",
"player": "Mozilla/5.0..."
},
"parse": {
"超清": "https://jx.xxx/?url=",
"高清": "parse:https://parse.xxx.com/"
}
}
*/
import { Crypto, _ } from 'assets://js/lib/cat.js';
const MOBILE_UA = "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36";
const TV_UA = "Dalvik/2.1.0 (Linux; U; Android 9; Pixel Build/PI)";
var def_header = {'User-Agent': TV_UA};
var cachedPlayUrls = {};
var HOST;
var foxConfig = {
host: '',
headers: {'User-Agent': TV_UA},
timeout: 5000,
ver: 1,
uas: {},
parses: {},
custom_parses: {},
froms: '',
detail: [],
custom_first: 0,
category: 1,
cms: '',
sourceOrder: []
};
var recommendCache = {
data: [],
timestamp: 0,
cacheTime: 30 * 60 * 1000
};
function decodeBase64(str) {
try {
return Crypto.enc.Utf8.stringify(Crypto.enc.Base64.parse(str));
} catch (e) {
return str;
}
}
function request(reqUrl, header, data, method) {
try {
let optObj = {
headers: header || foxConfig.headers,
method: method || 'get',
timeout: foxConfig.timeout,
};
if (method === 'post') {
optObj.data = data || {};
optObj.postType = 'form';
}
let res = req(reqUrl, optObj);
return res.content;
} catch (e) {
throw new Error('请求失败: ' + reqUrl);
}
}
function getdata(url, data, method) {
try {
url = /^http/.test(url) ? url : `${HOST}${url}`;
let res = request(url, foxConfig.headers, data, method);
return JSON.parse(res);
} catch (e) {
return {};
}
}
function sortSources(play_from, play_urls) {
if (!foxConfig.sourceOrder || foxConfig.sourceOrder.length === 0) {
return { play_from, play_urls };
}
let sourceMap = new Map();
for (let i = 0; i < play_from.length; i++) {
if (i < play_urls.length) {
sourceMap.set(play_from[i], {
originalName: play_from[i],
url: play_urls[i],
index: i
});
}
}
let sorted_play_from = [];
let sorted_play_urls = [];
for (let sourceName of foxConfig.sourceOrder) {
for (let [key, source] of sourceMap) {
if (key.includes(sourceName)) {
sorted_play_from.push(source.originalName);
sorted_play_urls.push(source.url);
sourceMap.delete(key);
break;
}
}
}
for (let [key, source] of sourceMap) {
sorted_play_from.push(source.originalName);
sorted_play_urls.push(source.url);
}
return {
play_from: sorted_play_from,
play_urls: sorted_play_urls
};
}
async function getRecommendVideos() {
try {
const now = Date.now();
if (recommendCache.data.length > 0 && now - recommendCache.timestamp < recommendCache.cacheTime) {
return recommendCache.data;
}
let headers = Object.assign({}, foxConfig.headers);
let recommend_ua = foxConfig.uas.home || foxConfig.uas.homeVideo;
if (recommend_ua) headers['User-Agent'] = recommend_ua;
let videos = [];
let res = request(HOST + '/api.php/Appfox/vodList?sort=热门&page=1&limit=12', headers);
let response = JSON.parse(res);
if (response.data && response.data.list) {
videos = response.data.list.slice(0, 12);
} else if (response.data && response.data.recommend_list) {
videos = response.data.recommend_list.slice(0, 12);
} else if (Array.isArray(response.data)) {
videos = response.data.slice(0, 12);
}
if (videos.length > 0) {
recommendCache.data = videos;
recommendCache.timestamp = now;
}
return videos;
} catch (e) {
return [];
}
}
async function init(cfg) {
try {
if (!cfg.ext) throw new Error('配置参数为空');
let extValue = cfg.ext;
if (typeof extValue === 'string') {
try {
extValue = decodeBase64(extValue);
} catch (e) {}
try {
extValue = JSON.parse(extValue);
} catch (e) {
if (extValue.startsWith('http')) {
extValue = {host: extValue};
} else {
throw new Error('配置格式错误');
}
}
}
let host = extValue.host || '';
if (!host) throw new Error('主机地址为空');
if (!host.match(/^https?:\/\/[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*(:\d+)?(\/)?$/)) {
let headers = Object.assign({}, foxConfig.headers);
let custom_ua = foxConfig.uas.host;
if (custom_ua) headers['User-Agent'] = custom_ua;
let res = request(host, headers);
let result = JSON.parse(res);
host = result.apiDomain;
}
foxConfig.host = host.replace(/\/+$/, '');
HOST = foxConfig.host;
foxConfig.ver = extValue.ver === 2 ? 2 : 1;
foxConfig.cms = extValue.cms || '';
foxConfig.froms = extValue.from || '';
foxConfig.custom_parses = extValue.parse || {};
foxConfig.custom_first = extValue.custom_first || 0;
foxConfig.category = extValue.category || 1;
foxConfig.timeout = (extValue.timeout || 5) * 1000;
foxConfig.sourceOrder = extValue.source_order || [];
let ua = extValue.ua;
if (ua) {
if (typeof ua === 'string') {
foxConfig.headers['User-Agent'] = ua;
} else if (typeof ua === 'object') {
foxConfig.uas = {
host: ua.host,
config: ua.config,
home: ua.home,
category: ua.category,
search: ua.search,
parse: ua.parse,
player: ua.player || 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1'
};
}
}
if (foxConfig.cms) {
let cms = foxConfig.cms.replace(/\/+$/, '');
if (cms.match(/^https?:\/\/.*\/vod/)) {
cms += cms.includes('?') ? '&' : '?';
foxConfig.cms = cms;
}
}
} catch (e) {
throw new Error('初始化失败: ' + e.message);
}
}
async function home(filter) {
try {
if (!HOST || foxConfig.category === 0) return '{"class":[]}';
let headers = Object.assign({}, foxConfig.headers);
let custom_ua = foxConfig.uas.home;
if (custom_ua) headers['User-Agent'] = custom_ua;
if (foxConfig.cms && foxConfig.category !== 2) {
let class_url = foxConfig.cms.replace(/&ac=videolist|ac=videolist&|ac=videolist|ac=detail/g, '');
if (!class_url.includes('ac=')) class_url += 'ac=list';
let classRes = request(class_url, headers);
let classes = JSON.parse(classRes).class;
let recommendVideos = await getRecommendVideos();
let dataRes = request(foxConfig.cms.replace(/&$/, ''), headers);
let data = JSON.parse(dataRes);
data.class = classes;
if (recommendVideos.length > 0) {
data.recommend = recommendVideos;
}
return JSON.stringify(data);
} else {
let res = request(HOST + '/api.php/Appfox/init', headers);
let response = JSON.parse(res);
let classes = [];
for (let i of response.data.type_list) {
classes.push({type_id: i.type_id, type_name: i.type_name});
}
let recommendVideos = await getRecommendVideos();
let result = {class: classes};
if (recommendVideos.length > 0) {
result.recommend = recommendVideos;
}
return JSON.stringify(result);
}
} catch (e) {
return '{"class":[]}';
}
}
async function homeVod() {
try {
if (!HOST || foxConfig.category === 0) return '{"list":[]}';
if (foxConfig.cms && foxConfig.category !== 2) {
let headers = Object.assign({}, foxConfig.headers);
let custom_ua = foxConfig.uas.homeVideo || foxConfig.uas.home;
if (custom_ua) headers['User-Agent'] = custom_ua;
let res = request(foxConfig.cms.replace(/&$/, ''), headers);
let data = JSON.parse(res);
if (!data.list || data.list.length === 0) {
let recommendVideos = await getRecommendVideos();
if (recommendVideos.length > 0) {
data.list = recommendVideos;
}
}
if (foxConfig.category === 2 && data.list) {
for (let i of data.list) {
i.vod_id = 'msearch:' + i.vod_id;
}
}
return JSON.stringify(data);
}
let headers = Object.assign({}, foxConfig.headers);
let custom_ua = foxConfig.uas.homeVideo || foxConfig.uas.home;
if (custom_ua) headers['User-Agent'] = custom_ua;
let path = foxConfig.ver === 2 ? 'nav' : 'index';
let res = request(HOST + '/api.php/Appfox/' + path, headers);
let response = JSON.parse(res);
let videos = [];
if (foxConfig.ver === 2) {
let navigationId = '';
for (let i of response.data) {
if (i.navigationId) {
navigationId = i.navigationId;
break;
}
}
if (navigationId) {
let navRes = request(HOST + '/api.php/Appfox/nav_video?id=' + navigationId, headers);
let navData = JSON.parse(navRes);
for (let item of navData.data) {
if (item.videos) {
videos = videos.concat(item.videos);
}
}
}
} else {
for (let item of response.data) {
if (item.banner) videos = videos.concat(item.banner);
if (item.categories) {
for (let cat of item.categories) {
if (cat.videos) videos = videos.concat(cat.videos);
}
}
}
}
if (videos.length === 0) {
videos = await getRecommendVideos();
}
if (foxConfig.category === 2) {
for (let i of videos) {
i.vod_id = 'msearch:' + i.vod_id;
}
}
return JSON.stringify({list: videos.slice(0, 20)});
} catch (e) {
try {
let recommendVideos = await getRecommendVideos();
return JSON.stringify({list: recommendVideos});
} catch (e2) {
return '{"list":[]}';
}
}
}
async function category(tid, pg, filter, extend) {
try {
if (!HOST) return '{"list":[]}';
pg = parseInt(pg) || 1;
let headers = Object.assign({}, foxConfig.headers);
let category_ua = foxConfig.uas.category;
if (category_ua) headers['User-Agent'] = category_ua;
if (foxConfig.cms && foxConfig.category !== 2) {
let res = request(foxConfig.cms + 'pg=' + pg + '&t=' + tid, headers);
let data = JSON.parse(res);
if (foxConfig.category === 2 && data.list) {
for (let i of data.list) {
i.vod_id = 'msearch:' + i.vod_id;
}
}
return JSON.stringify(data);
} else {
let res = request(HOST + '/api.php/Appfox/vodList?type_id=' + tid + '&page=' + pg, headers);
let response = JSON.parse(res);
let videos = response.data?.list || response.data?.recommend_list || [];
let data = {list: videos};
if (foxConfig.category === 2) {
for (let i of data.list) {
i.vod_id = 'msearch:' + i.vod_id;
}
}
return JSON.stringify(data);
}
} catch (e) {
return '{"list":[]}';
}
}
async function search(wd, quick, pg) {
try {
if (!HOST) return '{"list":[]}';
pg = parseInt(pg) || 1;
let headers = Object.assign({}, foxConfig.headers);
let search_ua = foxConfig.uas.search;
if (search_ua) headers['User-Agent'] = search_ua;
if (foxConfig.cms) {
let cms = foxConfig.cms.split('?')[0] + '?';
let res = request(cms + 'ac=detail&wd=' + encodeURIComponent(wd), headers);
let response = JSON.parse(res);
foxConfig.detail = response.list || [];
return JSON.stringify(response);
} else {
let path = HOST + '/api.php/Appfox/vod?ac=detail&wd=' + encodeURIComponent(wd);
if (foxConfig.froms) path += '&from=' + foxConfig.froms;
let res = request(path, headers);
let response = JSON.parse(res);
foxConfig.detail = response.list || [];
return JSON.stringify(response);
}
} catch (e) {
return '{"list":[]}';
}
}
async function detail(id) {
try {
let headers = Object.assign({}, foxConfig.headers);
let detail_ua = foxConfig.uas.detail || foxConfig.uas.search;
if (detail_ua) headers['User-Agent'] = detail_ua;
let video = null;
for (let i of foxConfig.detail) {
if (String(i.vod_id) === String(id)) {
video = Object.assign({}, i);
break;
}
}
if (!video) {
if (foxConfig.cms) {
let cms = foxConfig.cms.split('?')[0] + '?';
let res = request(cms + 'ac=detail&ids=' + id, headers);
let response = JSON.parse(res);
video = response.list?.[0] || null;
} else {
let res = request(HOST + '/api.php/Appfox/vod?ac=detail&ids=' + id, headers);
let response = JSON.parse(res);
video = response.list?.[0] || null;
}
}
if (!video) return '{"list":[]}';
let play_from = video.vod_play_from ? video.vod_play_from.split('$$$') : [];
let play_urls = video.vod_play_url ? video.vod_play_url.split('$$$') : [];
let sortedSources = sortSources(play_from, play_urls);
play_from = sortedSources.play_from;
play_urls = sortedSources.play_urls;
try {
let config_headers = Object.assign({}, foxConfig.headers);
let custom_ua = foxConfig.uas.config;
if (custom_ua) config_headers['User-Agent'] = custom_ua;
let res = request(HOST + '/api.php/Appfox/config', config_headers);
let config_response = JSON.parse(res);
let player_list = config_response.data?.playerList || [];
let jiexi_data_list = config_response.data?.jiexiDataList || [];
let player_map = {};
for (let player of player_list) {
player_map[player.playerCode] = player;
}
let processed_play_urls = [];
for (let idx = 0; idx < play_from.length; idx++) {
let play_code = play_from[idx];
if (player_map[play_code]) {
let player_info = player_map[play_code];
if (player_info.playerCode !== player_info.playerName) {
play_from[idx] = player_info.playerName + '\u2005(' + play_code + ')';
}
}
if (idx < play_urls.length) {
let urls = play_urls[idx].split('#');
let processed_urls = [];
for (let url of urls) {
let parts = url.split('$');
if (parts.length >= 2) {
parts[1] = play_code + '@' + parts[1];
processed_urls.push(parts.join('$'));
} else {
processed_urls.push(url);
}
}
processed_play_urls.push(processed_urls.join('#'));
}
}
video.vod_play_from = play_from.join('$$$');
video.vod_play_url = processed_play_urls.join('$$$');
foxConfig.parses = {};
for (let p of jiexi_data_list) {
if (p.url && p.url.startsWith('http')) {
foxConfig.parses[p.playerCode] = p.url;
}
}
} catch (e) {
}
return JSON.stringify({list: [video]});
} catch (e) {
return '{"list":[]}';
}
}
async function play(flag, id, flags) {
try {
let parts = id.split('@', 2);
if (parts.length < 2) {
throw new Error('ID格式错误');
}
let play_from = parts[0];
let raw_url = parts[1];
let jx = 0, parse = 0, parsed = 0;
let headers = Object.assign({}, foxConfig.headers);
let parse_ua = foxConfig.uas.parse;
if (parse_ua) headers['User-Agent'] = parse_ua;
let player_ua = foxConfig.uas.player || MOBILE_UA;
let url = raw_url;
let parses_main = [];
if (foxConfig.custom_first === 1) {
parses_main.push(foxConfig.custom_parses);
parses_main.push(foxConfig.parses);
} else {
parses_main.push(foxConfig.parses);
parses_main.push(foxConfig.custom_parses);
}
outer: for (let parses2 of parses_main) {
if (!parsed && !url.match(/https?:\/\/.*\.(m3u8|mp4|flv|mkv)/)) {
for (let key in parses2) {
if (!play_from.includes(key)) continue;
let parsers = parses2[key];
if (Array.isArray(parsers)) {
for (let parser of parsers) {
if (parser && parser.startsWith('parse:')) {
url = parser.split('parse:')[1] + raw_url;
jx = 0;
parse = 1;
break outer;
}
if (parser && parser.startsWith('http')) {
try {
let res = request(parser + raw_url, headers);
let response = JSON.parse(res);
if (response.url && response.url.startsWith('http')) {
url = response.url;
parsed = 1;
break outer;
}
} catch (e) {
continue;
}
}
}
} else {
if (parsers && parsers.startsWith('parse:')) {
url = parsers.split('parse:')[1] + raw_url;
jx = 0;
parse = 1;
break outer;
}
if (parsers && parsers.startsWith('http')) {
try {
let res = request(parsers + raw_url, headers);
let response = JSON.parse(res);
if (response.url && response.url.startsWith('http')) {
url = response.url;
parsed = 1;
break outer;
}
} catch (e) {
continue;
}
}
}
if (parsed || parse) break;
}
}
if (parsed || parse) break;
}
if (!(url.match(/https?:\/\/.*\.(m3u8|mp4|flv|mkv)/) || parsed === 1)) {
jx = 1;
}
return JSON.stringify({
jx: jx,
parse: parse,
url: url,
header: {'User-Agent': player_ua}
});
} catch (e) {
return JSON.stringify({
jx: 0,
parse: 0,
url: id,
header: {}
});
}
}
export function __jsEvalReturn() {
return {
init: init,
home: home,
homeVod: homeVod,
category: category,
search: search,
detail: detail,
play: play
};
}