mirror of
https://github.com/cluntop/tvbox.git
synced 2026-01-11 18:08:34 +01:00
Update Up
This commit is contained in:
parent
a5ebc612c0
commit
01cf29e115
7 changed files with 1174 additions and 0 deletions
30
box.json
30
box.json
|
|
@ -17,6 +17,36 @@
|
|||
"type": 3,
|
||||
"api": "./js/金牌影院.js"
|
||||
},
|
||||
{
|
||||
"key": "荐片",
|
||||
"name": "荐片",
|
||||
"type": 3,
|
||||
"api": "./js/荐片.js"
|
||||
},
|
||||
{
|
||||
"key": "海龟",
|
||||
"name": "海龟",
|
||||
"type": 3,
|
||||
"api": "./js/海龟.js"
|
||||
},
|
||||
{
|
||||
"key": "鬼片之家",
|
||||
"name": "鬼片之家",
|
||||
"type": 3,
|
||||
"api": "./js/鬼片之家.js"
|
||||
},
|
||||
{
|
||||
"key": "永乐视频",
|
||||
"name": "永乐视频",
|
||||
"type": 3,
|
||||
"api": "./js/永乐视频.js"
|
||||
},
|
||||
{
|
||||
"key": "apple",
|
||||
"name": "apple",
|
||||
"type": 3,
|
||||
"api": "./js/apple.js"
|
||||
},
|
||||
{
|
||||
"key": "tt",
|
||||
"name": "老三",
|
||||
|
|
|
|||
149
js/apple.js
Executable file
149
js/apple.js
Executable file
|
|
@ -0,0 +1,149 @@
|
|||
let host = 'http://asp.xpgtv.com';
|
||||
let headers = {
|
||||
"User-Agent": "okhttp/3.12.11"
|
||||
};
|
||||
|
||||
async function init(cfg) {}
|
||||
|
||||
function getList(data) {
|
||||
let videos = [];
|
||||
data.forEach(vod => {
|
||||
let r = vod.updateInfo ? "更新至" + vod.updateInfo : "";
|
||||
videos.push({
|
||||
"vod_id": vod.id.toString(),
|
||||
"vod_name": vod.name,
|
||||
"vod_pic": vod.pic,
|
||||
"vod_remarks": r || (vod.score ? vod.score.toString() : "")
|
||||
});
|
||||
});
|
||||
return videos;
|
||||
}
|
||||
|
||||
// ------------------- 兼容 JSON -------------------
|
||||
function parseResp(resp) {
|
||||
return typeof resp.content === "string" ? JSON.parse(resp.content) : resp.content;
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
let url = host + "/api.php/v2.vod/androidtypes";
|
||||
let resp = await req(url, { headers: headers });
|
||||
let data = parseResp(resp);
|
||||
|
||||
let dy = { "classes": "类型", "areas": "地区", "years": "年份", "sortby": "排序" };
|
||||
let demos = ['时间', '人气', '评分'];
|
||||
let classes = [];
|
||||
let filters = {};
|
||||
|
||||
data.data.forEach(item => {
|
||||
let typeId = item.type_id.toString();
|
||||
classes.push({ "type_name": item.type_name, "type_id": typeId });
|
||||
item['sortby'] = ['updatetime', 'hits', 'score'];
|
||||
let filterArray = [];
|
||||
for (let key in dy) {
|
||||
if (item[key] && item[key].length > 1) {
|
||||
let values = [];
|
||||
item[key].forEach((val, idx) => {
|
||||
let vStr = val.toString().trim();
|
||||
if (vStr !== "") {
|
||||
values.push({ "n": key === "sortby" ? demos[idx] : vStr, "v": vStr });
|
||||
}
|
||||
});
|
||||
let fKey = key === "areas" ? "areaes" : (key === "years" ? "yeares" : key);
|
||||
filterArray.push({ "key": fKey, "name": dy[key], "value": values });
|
||||
}
|
||||
}
|
||||
filters[typeId] = filterArray;
|
||||
});
|
||||
return JSON.stringify({ class: classes, filters: filters });
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
let url = host + "/api.php/v2.main/androidhome";
|
||||
let resp = await req(url, { headers: headers });
|
||||
let data = parseResp(resp);
|
||||
|
||||
let videos = [];
|
||||
data.data.list.forEach(i => { videos = videos.concat(getList(i.list)); });
|
||||
return JSON.stringify({ list: videos });
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
let params = {
|
||||
"page": pg,
|
||||
"type": tid,
|
||||
"area": extend.areaes || '',
|
||||
"year": extend.yeares || '',
|
||||
"sortby": extend.sortby || '',
|
||||
"class": extend.classes || ''
|
||||
};
|
||||
let query = Object.keys(params).filter(k => params[k] !== '').map(k => k + '=' + encodeURIComponent(params[k])).join('&');
|
||||
let url = host + '/api.php/v2.vod/androidfilter10086?' + query;
|
||||
let resp = await req(url, { headers: headers });
|
||||
let data = parseResp(resp);
|
||||
|
||||
return JSON.stringify({ list: getList(data.data), page: parseInt(pg), pagecount: 9999, limit: 90, total: 999999 });
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
let url = host + '/api.php/v3.vod/androiddetail2?vod_id=' + id;
|
||||
let resp = await req(url, { headers: headers });
|
||||
let data = parseResp(resp).data;
|
||||
|
||||
// 过滤掉包含“及时雨”的选集
|
||||
let filteredUrls = data.urls.filter(i => !i.key.includes("及时雨"));
|
||||
let playlist = filteredUrls.map(i => i.key + '$' + i.url).join('#');
|
||||
|
||||
let vod = {
|
||||
'vod_id': id,
|
||||
'vod_name': data.name,
|
||||
'vod_year': data.year,
|
||||
'vod_area': data.area,
|
||||
'vod_lang': data.lang,
|
||||
'type_name': data.className,
|
||||
'vod_actor': data.actor,
|
||||
'vod_director': data.director,
|
||||
'vod_content': data.content,
|
||||
'vod_play_from': '书生精选线路',
|
||||
'vod_play_url': playlist
|
||||
};
|
||||
|
||||
return JSON.stringify({ list: [vod] });
|
||||
}
|
||||
|
||||
async function search(wd, quick, pg) {
|
||||
let page = pg || '1';
|
||||
let url = host + '/api.php/v2.vod/androidsearch10086?page=' + page + '&wd=' + encodeURIComponent(wd);
|
||||
let resp = await req(url, { headers: headers });
|
||||
let data = parseResp(resp);
|
||||
|
||||
return JSON.stringify({ list: getList(data.data), page: page });
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
let playUrl = id;
|
||||
if (!id.startsWith('http')) {
|
||||
playUrl = "http://c.xpgtv.net/m3u8/" + id + ".m3u8";
|
||||
}
|
||||
|
||||
const playHeader = {
|
||||
'user_id': 'XPGBOX',
|
||||
'token2': 'SnAXiSW8vScXE0Z9aDOnK5xffbO75w1+uPom3WjnYfVEA1oWtUdi2Ihy1N8=',
|
||||
'version': 'XPGBOX com.phoenix.tv1.5.7',
|
||||
'hash': 'd78a',
|
||||
'screenx': '2345',
|
||||
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36',
|
||||
'token': 'ElEDlwCVgXcFHFhddiq2JKteHofExRBUrfNlmHrWetU3VVkxnzJAodl52N9EUFS+Dig2A/fBa/V9RuoOZRBjYvI+GW8kx3+xMlRecaZuECdb/3AdGkYpkjW3wCnpMQxf8vVeCz5zQLDr8l8bUChJiLLJLGsI+yiNskiJTZz9HiGBZhZuWh1mV1QgYah5CLTbSz8=',
|
||||
'timestamp': '1743060300',
|
||||
'screeny': '1065',
|
||||
'Accept': '*/*',
|
||||
'Connection': 'keep-alive'
|
||||
};
|
||||
|
||||
return JSON.stringify({
|
||||
parse: 0,
|
||||
url: playUrl,
|
||||
header: playHeader
|
||||
});
|
||||
}
|
||||
|
||||
export default { init, home, homeVod, category, detail, search, play };
|
||||
107
js/永乐视频.js
Executable file
107
js/永乐视频.js
Executable file
|
|
@ -0,0 +1,107 @@
|
|||
let host = 'https://www.ylys.tv';
|
||||
let headers = {
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
|
||||
"Referer": host + "/"
|
||||
};
|
||||
|
||||
async function init(cfg) {}
|
||||
|
||||
/**
|
||||
* 通用解析:不做过滤,不做干扰
|
||||
*/
|
||||
function getList(html) {
|
||||
let videos = [];
|
||||
let items = pdfa(html, ".module-item,.module-card-item");
|
||||
items.forEach(it => {
|
||||
let idMatch = it.match(/detail\/(\d+)/);
|
||||
let nameMatch = it.match(/title="(.*?)"/) || it.match(/<strong>(.*?)<\/strong>/);
|
||||
let picMatch = it.match(/data-original="(.*?)"/) || it.match(/src="(.*?)"/);
|
||||
|
||||
if (idMatch && nameMatch) {
|
||||
let pic = picMatch ? (picMatch[1] || picMatch[2]) : "";
|
||||
videos.push({
|
||||
"vod_id": idMatch[1],
|
||||
"vod_name": nameMatch[1].replace(/<.*?>/g, ""),
|
||||
"vod_pic": pic.startsWith('/') ? host + pic : pic,
|
||||
"vod_remarks": (it.match(/module-item-note">(.*?)<\/div>/) || ["",""])[1].replace(/<.*?>/g, "")
|
||||
});
|
||||
}
|
||||
});
|
||||
return videos;
|
||||
}
|
||||
|
||||
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":"动漫"}
|
||||
],
|
||||
"filters": {
|
||||
"1":[{"key":"class","name":"类型","value":[{"n":"全部","v":""},{"n":"动作片","v":"6"},{"n":"喜剧片","v":"7"},{"n":"爱情片","v":"8"},{"n":"科幻片","v":"9"},{"n":"恐怖片","v":"11"}]}],
|
||||
"2":[{"key":"class","name":"类型","value":[{"n":"全部","v":""},{"n":"国产剧","v":"13"},{"n":"港台剧","v":"14"},{"n":"日剧","v":"15"},{"n":"韩剧","v":"33"},{"n":"欧美剧","v":"16"}]}],
|
||||
"3":[{"key":"class","name":"类型","value":[{"n":"全部","v":""},{"n":"内地综艺","v":"27"},{"n":"港台综艺","v":"28"},{"n":"日本综艺","v":"29"},{"n":"韩国综艺","v":"36"}]}],
|
||||
"4":[{"key":"class","name":"类型","value":[{"n":"全部","v":""},{"n":"国产动漫","v":"31"},{"n":"日本动漫","v":"32"},{"n":"欧美动漫","v":"42"},{"n":"其他动漫","v":"43"}]}]
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
let resp = await req(host, { headers: headers });
|
||||
return JSON.stringify({ list: getList(resp.content) });
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
let p = pg || 1;
|
||||
let targetId = (extend && extend.class) ? extend.class : tid;
|
||||
let url = host + "/vodtype/" + targetId + "/" + (parseInt(p) > 1 ? "page/" + p + "/" : "");
|
||||
let resp = await req(url, { headers: headers });
|
||||
return JSON.stringify({
|
||||
"list": getList(resp.content),
|
||||
"page": parseInt(p)
|
||||
});
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
let url = host + '/voddetail/' + id + '/';
|
||||
let resp = await req(url, { headers: headers });
|
||||
let html = resp.content;
|
||||
|
||||
let playFrom = pdfa(html, ".module-tab-item").map(it => (it.match(/<span>(.*?)<\/span>/) || ["","线路"])[1]).join('$$$');
|
||||
let playUrl = pdfa(html, ".module-play-list-content").map(list =>
|
||||
pdfa(list, "a").map(a => {
|
||||
let n = (a.match(/<span>(.*?)<\/span>/) || ["","播放"])[1];
|
||||
let v = a.match(/href="\/play\/(.*?)\/"/);
|
||||
return n + '$' + (v ? v[1] : "");
|
||||
}).join('#')
|
||||
).join('$$$');
|
||||
|
||||
return JSON.stringify({
|
||||
list: [{
|
||||
'vod_id': id,
|
||||
'vod_name': (html.match(/<h1>(.*?)<\/h1>/) || ["", ""])[1],
|
||||
'vod_pic': (html.match(/data-original="(.*?)"/) || ["", ""])[1],
|
||||
'vod_content': (html.match(/introduction-content">.*?<p>(.*?)<\/p>/s) || ["", ""])[1].replace(/<.*?>/g, ""),
|
||||
'vod_play_from': playFrom,
|
||||
'vod_play_url': playUrl
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
async function search(wd, quick, pg) {
|
||||
let p = pg || 1;
|
||||
let url = host + "/vodsearch/" + encodeURIComponent(wd) + "-------------/" + (parseInt(p) > 1 ? "page/" + p + "/" : "");
|
||||
let resp = await req(url, { headers: headers });
|
||||
return JSON.stringify({ list: getList(resp.content) });
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
let url = host + "/play/" + id + "/";
|
||||
let resp = await req(url, { headers: headers });
|
||||
let m3u8 = resp.content.match(/"url":"([^"]+\.m3u8)"/);
|
||||
if (m3u8) return JSON.stringify({ parse: 0, url: m3u8[1].replace(/\\/g, ""), header: headers });
|
||||
return JSON.stringify({ parse: 1, url: url, header: headers });
|
||||
}
|
||||
|
||||
export default { init, home, homeVod, category, detail, search, play };
|
||||
150
js/海龟.js
Executable file
150
js/海龟.js
Executable file
|
|
@ -0,0 +1,150 @@
|
|||
const host = 'https://www.haigui.tv';
|
||||
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;
|
||||
|
||||
function getList(html) {
|
||||
return pdfa(html, ".module-item").map(it => {
|
||||
let id = m(it, /href="\/video\/(.*?)\/"/);
|
||||
let name = m(it, /title="(.*?)"/);
|
||||
if (!id || !name) return null;
|
||||
return {
|
||||
vod_id: id,
|
||||
vod_name: name,
|
||||
vod_pic: fixPic(m(it, /data-original="(.*?)"/) || m(it, /src="(.*?)"/)),
|
||||
vod_remarks: m(it, /module-item-note">(.*?)<\/div>/).replace(/<.*?>/g, "")
|
||||
};
|
||||
}).filter(Boolean);
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
const classes = [
|
||||
["dianying","电影"],["dianshiju","电视剧"],["zongyi","综艺"],
|
||||
["dongman","动漫"],["jilupian","纪录片"],["duanju","短剧"]
|
||||
].map(([id,name]) => ({type_id:id,type_name:name}));
|
||||
|
||||
const dict = {
|
||||
area: ["中国大陆","中国香港","中国台湾","美国","韩国","日本","泰国"],
|
||||
year: ["2025","2024","2023","2022","2021","2020"],
|
||||
lang: ["国语","英语","粤语","韩语","日语"]
|
||||
};
|
||||
|
||||
const filters = {};
|
||||
classes.forEach(c => {
|
||||
filters[c.type_id] = [
|
||||
{ key:"class", name:"类型", value:getSubClasses(c.type_id) },
|
||||
...Object.keys(dict).map(k => ({
|
||||
key:k,
|
||||
name:{area:"地区",year:"年份",lang:"语言"}[k],
|
||||
value:[{n:"全部",v:""}].concat(dict[k].map(v=>({n:v,v})))
|
||||
}))
|
||||
];
|
||||
});
|
||||
|
||||
return JSON.stringify({ class: classes, filters });
|
||||
}
|
||||
|
||||
function getSubClasses(tid) {
|
||||
const map = {
|
||||
dianying: [
|
||||
["dongzuopian","动作片"],["xijupian","喜剧片"],
|
||||
["aiqingpian","爱情片"],["kehuanpian","科幻片"],["kongbupian","恐怖片"]
|
||||
],
|
||||
dianshiju: [
|
||||
["guochanju","国产剧"],["gangtaiju","港台剧"],
|
||||
["rihanju","日韩剧"],["oumeiju","欧美剧"]
|
||||
]
|
||||
};
|
||||
return [{n:"全部",v:""}].concat((map[tid]||[]).map(([v,n])=>({n,v})));
|
||||
}
|
||||
|
||||
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;
|
||||
let id = extend.class || tid;
|
||||
let url = `${host}/filter/${id}`;
|
||||
["area","lang","year"].forEach(k=>{
|
||||
if (extend[k]) url += `/${k}/${encodeURIComponent(extend[k])}`;
|
||||
});
|
||||
url += `/page/${p}/`;
|
||||
const r = await req(url,{headers});
|
||||
return JSON.stringify({ page:p, list:getList(r.content) });
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
const r = await req(`${host}/video/${id}/`,{headers});
|
||||
const h = r.content;
|
||||
|
||||
const playFrom = pdfa(h,".module-tab-item")
|
||||
.map(it=>m(it,/<span>(.*?)<\/span>/)||"播放源")
|
||||
.join("$$$");
|
||||
|
||||
const playUrl = pdfa(h,".module-player-list").map(l =>
|
||||
pdfa(l,".module-blocklist a").map(it=>{
|
||||
let pid = m(it,/href=\"\/play\/(.*?)\/\"/);
|
||||
if (!pid) return null;
|
||||
return `${m(it,/<span>(.*?)<\/span>/)||"正片"}$${pid}`;
|
||||
}).filter(Boolean).join("#")
|
||||
).join("$$$");
|
||||
|
||||
return JSON.stringify({
|
||||
list:[{
|
||||
vod_id:id,
|
||||
vod_name:m(h,/page-title\">(.*?)<\/h1>/),
|
||||
vod_pic:fixPic(m(h,/video-cover.*?data-src=\"(.*?)\"/)||m(h,/video-cover.*?src=\"(.*?)\"/)),
|
||||
type_name:(
|
||||
m(h,/tag-link[^>]*>[\s\S]*?icon-cate-[^<]*<\/i>\s*([^<\n]+)/) ||
|
||||
m(h,/icon-cate-[^<]*<\/i>\s*([^<\n]+)/)
|
||||
).trim(),
|
||||
vod_year:m(h,/year\/(\d+)\//),
|
||||
vod_area:m(h,/area\/.*?\/ \">(.*?)\t/s).trim(),
|
||||
vod_director:m(h,/导演:<\/span>.*?<div.*?>(.*?)<\/div>/s).replace(/<.*?>|\//g,"").trim(),
|
||||
vod_actor:m(h,/主演:<\/span>.*?<div.*?>(.*?)<\/div>/s).replace(/<.*?>|\//g,"").trim(),
|
||||
vod_remarks:m(h,/备注:<\/span>.*?<div.*?>(.*?)<\/div>/),
|
||||
vod_content:m(h,/vod_content.*?<span>(.*?)<\/span>/s)||"暂无简介",
|
||||
vod_play_from:playFrom,
|
||||
vod_play_url:playUrl
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
async function search(wd, quick, pg=1) {
|
||||
let url = `${host}/search/${encodeURIComponent(wd)}/${pg>1?`page/${pg}/`:""}`;
|
||||
const r = await req(url,{headers});
|
||||
const h = r.content;
|
||||
|
||||
const items = pdfa(h,".module-search-item") || pdfa(h,".module-item");
|
||||
|
||||
const list = items.map(it=>{
|
||||
let id = m(it,/\/video\/(.*?)\//);
|
||||
let name = m(it,/title=\"(.*?)\"/) || m(it,/alt=\"(.*?)\"/);
|
||||
if (!id || !name) return null;
|
||||
return {
|
||||
vod_id:id,
|
||||
vod_name:name,
|
||||
vod_pic:fixPic(m(it,/data-src=\"(.*?)\"/)||m(it,/data-original=\"(.*?)\"/)||m(it,/src=\"(.*?)\"/)),
|
||||
vod_remarks:m(it,/module-item-note\">(.*?)<\/div>/)||m(it,/video-serial\">(.*?)<\/span>/)
|
||||
};
|
||||
}).filter(Boolean);
|
||||
|
||||
return JSON.stringify({ page:pg, list });
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
const r = await req(`${host}/play/${id}/`,{headers});
|
||||
let u = m(r.content,/"url":"([^"]+\.m3u8[^"]*)"/);
|
||||
if (u) return JSON.stringify({ parse:0, url:u.replace(/\\/g,""), header:headers });
|
||||
return JSON.stringify({ parse:1, url:`${host}/play/${id}/`, header:headers });
|
||||
}
|
||||
|
||||
export default { init, home, homeVod, category, detail, search, play };
|
||||
152
js/荐片.js
Executable file
152
js/荐片.js
Executable file
|
|
@ -0,0 +1,152 @@
|
|||
/**
|
||||
* 荐片 新式 JS0 接口源
|
||||
* 适配 FongMi TVBox 最新规范
|
||||
*/
|
||||
|
||||
let host = 'https://api.ztcgi.com';
|
||||
let UA = 'Mozilla/5.0 (Linux; Android 9; V2196A Build/PQ3A.190705.08211809; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/91.0.4472.114 Mobile Safari/537.36;webank/h5face;webank/1.0;netType:NETWORK_WIFI;appVersion:416;packageName:com.jp3.xg3';
|
||||
let imghost = '';
|
||||
|
||||
/**
|
||||
* 初始化配置
|
||||
*/
|
||||
async function init(cfg) {
|
||||
try {
|
||||
let res = await req(`${host}/api/appAuthConfig`, { headers: { 'User-Agent': UA } });
|
||||
let config = JSON.parse(res.content);
|
||||
imghost = `https://${config.data.imgDomain}`;
|
||||
} catch (e) {
|
||||
imghost = 'https://img.jianpian.com';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 首页分类与筛选
|
||||
*/
|
||||
async function home(filter) {
|
||||
let classes = [
|
||||
{type_id: '1', type_name: '电影'},
|
||||
{type_id: '2', type_name: '电视剧'},
|
||||
{type_id: '3', type_name: '动漫'},
|
||||
{type_id: '4', type_name: '综艺'}
|
||||
];
|
||||
|
||||
// 筛选数据模板
|
||||
const filterItem = [
|
||||
{"key": "cateId", "name": "分类", "value": [{"v": "1", "n": "剧情"}, {"v": "2", "n": "爱情"}, {"v": "3", "n": "动画"}, {"v": "4", "n": "喜剧"}, {"v": "5", "n": "战争"}, {"v": "6", "n": "歌舞"}, {"v": "7", "n": "古装"}, {"v": "8", "n": "奇幻"}, {"v": "9", "n": "冒险"}, {"v": "10", "n": "动作"}, {"v": "11", "n": "科幻"}, {"v": "12", "n": "悬疑"}, {"v": "13", "n": "犯罪"}, {"v": "14", "n": "家庭"}, {"v": "15", "n": "传记"}, {"v": "16", "n": "运动"}, {"v": "18", "n": "惊悚"}, {"v": "20", "n": "短片"}, {"v": "21", "n": "历史"}, {"v": "22", "n": "音乐"}, {"v": "23", "n": "西部"}, {"v": "24", "n": "武侠"}, {"v": "25", "n": "恐怖"}]},
|
||||
{"key": "area", "name": "地區", "value": [{"v": "1", "n": "国产"}, {"v": "3", "n": "中国香港"}, {"v": "6", "n": "中国台湾"}, {"v": "5", "n": "美国"}, {"v": "18", "n": "韩国"}, {"v": "2", "n": "日本"}]},
|
||||
{"key": "year", "name": "年代", "value": [{"v": "107", "n": "2025"}, {"v": "119", "n": "2024"}, {"v": "153", "n": "2023"}, {"v": "101", "n": "2022"}, {"v": "118", "n": "2021"}, {"v": "16", "n": "2020"}, {"v": "7", "n": "2019"}, {"v": "22", "n": "2016"}, {"v": "2015", "n": "2015以前"}]},
|
||||
{"key": "sort", "name": "排序", "value": [{"v": "update", "n": "最新"}, {"v": "hot", "n": "最热"}, {"v": "rating", "n": "评分"}]}
|
||||
];
|
||||
|
||||
let filterObj = {"1": filterItem, "2": filterItem, "3": filterItem, "4": filterItem};
|
||||
|
||||
return JSON.stringify({
|
||||
class: classes,
|
||||
filters: filterObj
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 首页推荐
|
||||
*/
|
||||
async function homeVod() {
|
||||
let html = await req(`${host}/api/slide/list?pos_id=88`, { headers: { 'User-Agent': UA, 'Referer': host } });
|
||||
let res = JSON.parse(html.content);
|
||||
let videos = res.data.map(item => ({
|
||||
vod_id: item.jump_id,
|
||||
vod_name: item.title,
|
||||
vod_pic: item.thumbnail.includes('http') ? item.thumbnail : `${imghost}${item.thumbnail}`,
|
||||
vod_remarks: ""
|
||||
}));
|
||||
return JSON.stringify({ list: videos });
|
||||
}
|
||||
|
||||
/**
|
||||
* 分类列表
|
||||
*/
|
||||
async function category(tid, pg, filter, extend) {
|
||||
let url = `${host}/api/crumb/list?fcate_pid=${tid}&category_id=&area=${extend.area || ''}&year=${extend.year || ''}&type=${extend.cateId || ''}&sort=${extend.sort || ''}&page=${pg}`;
|
||||
let html = await req(url, { headers: { 'User-Agent': UA, 'Referer': host } });
|
||||
let res = JSON.parse(html.content);
|
||||
let videos = res.data.map(item => ({
|
||||
vod_id: item.id,
|
||||
vod_name: item.title,
|
||||
vod_pic: item.path.includes('http') ? item.path : `${imghost}${item.path}`,
|
||||
vod_remarks: item.mask
|
||||
}));
|
||||
return JSON.stringify({
|
||||
page: pg,
|
||||
list: videos
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 详情页
|
||||
*/
|
||||
async function detail(id) {
|
||||
let html = await req(`${host}/api/video/detailv2?id=${id}`, { headers: { 'User-Agent': UA, 'Referer': host } });
|
||||
let data = JSON.parse(html.content).data;
|
||||
|
||||
// 线路处理:将“常规线路”显示为“边下边播”
|
||||
let play_from = data.source_list_source.map(item => item.name).join('$$$').replace(/常规线路/g, '边下边播');
|
||||
let play_url = data.source_list_source.map(play =>
|
||||
play.source_list.map(({source_name, url}) => `${source_name}$${url}`).join('#')
|
||||
).join('$$$');
|
||||
|
||||
let vod = {
|
||||
vod_id: data.id,
|
||||
vod_name: data.title,
|
||||
vod_year: data.year,
|
||||
vod_area: data.area,
|
||||
vod_remarks: data.mask,
|
||||
vod_content: data.description,
|
||||
vod_play_from: play_from,
|
||||
vod_play_url: play_url,
|
||||
vod_pic: data.thumbnail.includes('http') ? data.thumbnail : `${imghost}${data.thumbnail}`
|
||||
};
|
||||
|
||||
return JSON.stringify({ list: [vod] });
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索功能
|
||||
*/
|
||||
async function search(wd, quick) {
|
||||
let url = `${host}/api/v2/search/videoV2?key=${encodeURIComponent(wd)}&category_id=88&page=1&pageSize=20`;
|
||||
let html = await req(url, { headers: { 'User-Agent': UA, 'Referer': host } });
|
||||
let res = JSON.parse(html.content);
|
||||
let videos = res.data.map(item => ({
|
||||
vod_id: item.id,
|
||||
vod_name: item.title,
|
||||
vod_pic: item.thumbnail.includes('http') ? item.thumbnail : `${imghost}${item.thumbnail}`,
|
||||
vod_remarks: item.mask
|
||||
}));
|
||||
return JSON.stringify({ list: videos });
|
||||
}
|
||||
|
||||
/**
|
||||
* 播放解析
|
||||
*/
|
||||
async function play(flag, id, flags) {
|
||||
let playUrl = id;
|
||||
// 判断是否需要添加专用协议前缀
|
||||
if (!id.includes(".m3u8") && !id.includes(".mp4")) {
|
||||
playUrl = `tvbox-xg:${id}`;
|
||||
}
|
||||
return JSON.stringify({
|
||||
parse: 0,
|
||||
url: playUrl
|
||||
});
|
||||
}
|
||||
|
||||
// 导出标准接口对象
|
||||
export default {
|
||||
init,
|
||||
home,
|
||||
homeVod,
|
||||
category,
|
||||
detail,
|
||||
search,
|
||||
play
|
||||
};
|
||||
115
js/鬼片之家.js
Executable file
115
js/鬼片之家.js
Executable file
|
|
@ -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
|
||||
};
|
||||
471
py/瓜子.py
Executable file
471
py/瓜子.py
Executable file
|
|
@ -0,0 +1,471 @@
|
|||
# coding = utf-8
|
||||
#!/usr/bin/python
|
||||
import re
|
||||
import sys
|
||||
import json
|
||||
import time
|
||||
import base64
|
||||
import hashlib
|
||||
import urllib.parse
|
||||
from Crypto.Cipher import AES
|
||||
from Crypto.Util.Padding import pad, unpad
|
||||
from Crypto.PublicKey import RSA
|
||||
from Crypto.Cipher import PKCS1_v1_5
|
||||
from base.spider import Spider
|
||||
|
||||
sys.path.append('..')
|
||||
|
||||
class Spider(Spider):
|
||||
def __init__(self):
|
||||
self.name = "瓜子"
|
||||
self.host = 'https://api.w32z7vtd.com'
|
||||
self.token = '1be86e8e18a9fa18b2b8d5432699dad0.ac008ed650fd087bfbecf2fda9d82e9835253ef24843e6b18fcd128b10763497bcf9d53e959f5377cde038c20ccf9d17f604c9b8bb6e61041def86729b2fc7408bd241e23c213ac57f0226ee656e2bb0a583ae0e4f3bf6c6ab6c490c9a6f0d8cdfd366aacf5d83193671a8f77cd1af1ff2e9145de92ec43ec87cf4bdc563f6e919fe32861b0e93b118ec37d8035fbb3c.59dd05c5d9a8ae726528783128218f15fe6f2c0c8145eddab112b374fcfe3d79'
|
||||
self.header = {
|
||||
'Cache-Control': 'no-cache',
|
||||
'Version': '2406025',
|
||||
'PackageName': 'com.uf076bf0c246.qe439f0d5e.m8aaf56b725a.ifeb647346f',
|
||||
'Ver': '1.9.2',
|
||||
'Referer': self.host,
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'User-Agent': 'okhttp/3.12.0'
|
||||
}
|
||||
# 添加缓存机制
|
||||
self.cache = {}
|
||||
self.cache_timeout = 300 # 5分钟缓存
|
||||
|
||||
def getName(self):
|
||||
return self.name
|
||||
|
||||
def init(self, extend=''):
|
||||
pass
|
||||
|
||||
def homeContent(self, filter):
|
||||
result = {}
|
||||
classes = [
|
||||
{"type_name": "电影", "type_id": "1"},
|
||||
{"type_name": "电视剧", "type_id": "2"},
|
||||
{"type_name": "动漫", "type_id": "4"},
|
||||
{"type_name": "综艺", "type_id": "3"},
|
||||
{"type_name": "短剧", "type_id": "64"}
|
||||
]
|
||||
|
||||
result['class'] = classes
|
||||
|
||||
# 设置筛选条件 - 为所有分类添加筛选
|
||||
filters = {}
|
||||
for cate in classes:
|
||||
tid = cate['type_id']
|
||||
filters[tid] = [
|
||||
{"key": "area", "name": "地区", "value": [
|
||||
{"n": "全部", "v": "0"},
|
||||
{"n": "大陆", "v": "大陆"},
|
||||
{"n": "香港", "v": "香港"},
|
||||
{"n": "台湾", "v": "台湾"},
|
||||
{"n": "美国", "v": "美国"},
|
||||
{"n": "韩国", "v": "韩国"},
|
||||
{"n": "日本", "v": "日本"},
|
||||
{"n": "英国", "v": "英国"},
|
||||
{"n": "法国", "v": "法国"},
|
||||
{"n": "泰国", "v": "泰国"},
|
||||
{"n": "印度", "v": "印度"},
|
||||
{"n": "其他", "v": "其他"}
|
||||
]},
|
||||
{"key": "year", "name": "年份", "value": [
|
||||
{"n": "全部", "v": "0"},
|
||||
{"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": "更早", "v": "2004"}
|
||||
]},
|
||||
{"key": "sort", "name": "排序", "value": [
|
||||
{"n": "最新", "v": "d_id"},
|
||||
{"n": "最热", "v": "d_hits"},
|
||||
{"n": "推荐", "v": "d_score"}
|
||||
]}
|
||||
]
|
||||
|
||||
result['filters'] = filters
|
||||
return result
|
||||
|
||||
def homeVideoContent(self):
|
||||
# 首页推荐直接返回空列表,避免加载问题
|
||||
return {'list': []}
|
||||
|
||||
def categoryContent(self, tid, pg, filter, extend):
|
||||
videos = []
|
||||
try:
|
||||
body = {
|
||||
"area": extend.get('area', '0'),
|
||||
"year": extend.get('year', '0'),
|
||||
"pageSize": "30",
|
||||
"sort": extend.get('sort', 'd_id'),
|
||||
"page": str(pg),
|
||||
"tid": tid
|
||||
}
|
||||
|
||||
cache_key = f"category_{tid}_{pg}_{hash(str(body))}"
|
||||
data = self.get_cached_data(cache_key, body, '/App/IndexList/indexList')
|
||||
|
||||
if data and 'list' in data:
|
||||
for item in data['list']:
|
||||
vod_continu = item.get('vod_continu', 0)
|
||||
remarks = '电影' if vod_continu == 0 else f'更新至{vod_continu}集'
|
||||
|
||||
video = {
|
||||
"vod_id": f"{item.get('vod_id', '')}/{vod_continu}",
|
||||
"vod_name": item.get('vod_name', ''),
|
||||
"vod_pic": item.get('vod_pic', ''),
|
||||
"vod_remarks": remarks
|
||||
}
|
||||
videos.append(video)
|
||||
except Exception as e:
|
||||
print(f"获取分类内容失败: {e}")
|
||||
|
||||
return {
|
||||
'list': videos,
|
||||
'page': int(pg),
|
||||
'pagecount': 9999,
|
||||
'limit': 30,
|
||||
'total': 999999
|
||||
}
|
||||
|
||||
def detailContent(self, ids):
|
||||
try:
|
||||
vod_id = ids[0].split('/')[0]
|
||||
|
||||
# 获取视频详情
|
||||
t = str(int(time.time()))
|
||||
body1 = {
|
||||
"token_id": "1649412",
|
||||
"vod_id": vod_id,
|
||||
"mobile_time": t,
|
||||
"token": self.token
|
||||
}
|
||||
qdata = self.get_data(body1, '/App/IndexPlay/playInfo')
|
||||
|
||||
# 获取播放列表
|
||||
body2 = {
|
||||
"vurl_cloud_id": "2",
|
||||
"vod_d_id": vod_id
|
||||
}
|
||||
jdata = self.get_data(body2, '/App/Resource/Vurl/show')
|
||||
|
||||
if not qdata or 'vodInfo' not in qdata:
|
||||
return {'list': []}
|
||||
|
||||
vod = qdata['vodInfo']
|
||||
|
||||
# 构建视频信息
|
||||
video_detail = {
|
||||
"vod_id": vod_id,
|
||||
"vod_name": vod.get('vod_name', ''),
|
||||
"vod_pic": vod.get('vod_pic', ''),
|
||||
"vod_year": vod.get('vod_year', ''),
|
||||
"vod_area": vod.get('vod_area', ''),
|
||||
"vod_actor": vod.get('vod_actor', ''),
|
||||
"vod_director": vod.get('vod_director', ''),
|
||||
"vod_content": vod.get('vod_use_content', '').strip(),
|
||||
"vod_play_from": "嗷呜要吃瓜"
|
||||
}
|
||||
|
||||
# 构建播放列表
|
||||
play_list = []
|
||||
if jdata and 'list' in jdata:
|
||||
for index, item in enumerate(jdata['list']):
|
||||
if 'play' in item:
|
||||
n = [] # 播放源名称
|
||||
p = [] # 播放参数
|
||||
for key, value in item['play'].items():
|
||||
if 'param' in value and value['param']:
|
||||
n.append(key)
|
||||
p.append(value['param'])
|
||||
|
||||
if p:
|
||||
play_name = str(index + 1)
|
||||
if len(jdata['list']) == 1:
|
||||
play_name = vod.get('vod_name', '')
|
||||
|
||||
play_url = f"{p[-1]}||{'@'.join(n)}"
|
||||
play_list.append(f"{play_name}${play_url}")
|
||||
|
||||
video_detail["vod_play_url"] = "#".join(play_list)
|
||||
|
||||
return {'list': [video_detail]}
|
||||
|
||||
except Exception as e:
|
||||
print(f"获取详情失败: {e}")
|
||||
return {'list': []}
|
||||
|
||||
def searchContent(self, key, quick, pg=1):
|
||||
videos = []
|
||||
try:
|
||||
body = {
|
||||
"keywords": key,
|
||||
"order_val": "1",
|
||||
"page": str(pg)
|
||||
}
|
||||
|
||||
# 搜索不使用缓存,确保实时性
|
||||
start_time = time.time()
|
||||
data = self.get_data(body, '/App/Index/findMoreVod', use_cache=False)
|
||||
end_time = time.time()
|
||||
|
||||
print(f"搜索请求耗时: {end_time - start_time:.2f}秒")
|
||||
|
||||
if data and 'list' in data:
|
||||
for item in data['list']:
|
||||
vod_continu = item.get('vod_continu', 0)
|
||||
remarks = '电影' if vod_continu == 0 else f'更新至{vod_continu}集'
|
||||
|
||||
video = {
|
||||
"vod_id": f"{item.get('vod_id', '')}/{vod_continu}",
|
||||
"vod_name": item.get('vod_name', ''),
|
||||
"vod_pic": item.get('vod_pic', ''),
|
||||
"vod_remarks": remarks
|
||||
}
|
||||
videos.append(video)
|
||||
except Exception as e:
|
||||
print(f"搜索失败: {e}")
|
||||
|
||||
return {
|
||||
'list': videos,
|
||||
'page': int(pg),
|
||||
'pagecount': 9999,
|
||||
'limit': 30,
|
||||
'total': 999999
|
||||
}
|
||||
|
||||
def playerContent(self, flag, id, vipFlags):
|
||||
try:
|
||||
# 解析播放信息
|
||||
parts = id.split('||')
|
||||
if len(parts) < 2:
|
||||
return {"parse": 0, "playUrl": "", "url": ""}
|
||||
|
||||
param_str = parts[0]
|
||||
resolutions = parts[1].split('@') if len(parts) > 1 else []
|
||||
|
||||
# 解析参数
|
||||
params = {}
|
||||
for pair in param_str.split('&'):
|
||||
if '=' in pair:
|
||||
key, value = pair.split('=', 1)
|
||||
params[key] = value
|
||||
|
||||
# 获取播放链接
|
||||
if resolutions:
|
||||
# 分辨率从大到小排序
|
||||
resolutions.sort(key=lambda x: int(x) if x.isdigit() else 0, reverse=True)
|
||||
|
||||
# 使用最大分辨率
|
||||
params['resolution'] = resolutions[0]
|
||||
body = params
|
||||
|
||||
start_time = time.time()
|
||||
data = self.get_data(body, '/App/Resource/VurlDetail/showOne', use_cache=False)
|
||||
end_time = time.time()
|
||||
print(f"播放链接获取耗时: {end_time - start_time:.2f}秒")
|
||||
|
||||
if data and 'url' in data:
|
||||
return {
|
||||
"parse": 0,
|
||||
"playUrl": "",
|
||||
"url": data['url'],
|
||||
"header": json.dumps(self.header)
|
||||
}
|
||||
|
||||
return {"parse": 0, "playUrl": "", "url": ""}
|
||||
|
||||
except Exception as e:
|
||||
print(f"播放解析失败: {e}")
|
||||
return {"parse": 0, "playUrl": "", "url": ""}
|
||||
|
||||
def isVideoFormat(self, url):
|
||||
video_formats = ['.m3u8', '.mp4', '.avi', '.mkv', '.flv', '.ts']
|
||||
return any(url.lower().endswith(fmt) for fmt in video_formats)
|
||||
|
||||
def manualVideoCheck(self):
|
||||
pass
|
||||
|
||||
def localProxy(self, params):
|
||||
return None
|
||||
|
||||
def aes_encrypt(self, text, key, iv):
|
||||
"""AES加密"""
|
||||
try:
|
||||
key_bytes = key.encode('utf-8')
|
||||
iv_bytes = iv.encode('utf-8')
|
||||
cipher = AES.new(key_bytes, AES.MODE_CBC, iv_bytes)
|
||||
encrypted = cipher.encrypt(pad(text.encode('utf-8'), AES.block_size))
|
||||
return encrypted.hex().upper()
|
||||
except Exception as e:
|
||||
print(f"AES加密失败: {e}")
|
||||
return ""
|
||||
|
||||
def aes_decrypt(self, text, key, iv):
|
||||
"""AES解密"""
|
||||
try:
|
||||
key_bytes = key.encode('utf-8')
|
||||
iv_bytes = iv.encode('utf-8')
|
||||
cipher = AES.new(key_bytes, AES.MODE_CBC, iv_bytes)
|
||||
encrypted_bytes = bytes.fromhex(text)
|
||||
decrypted = unpad(cipher.decrypt(encrypted_bytes), AES.block_size)
|
||||
return decrypted.decode('utf-8')
|
||||
except Exception as e:
|
||||
print(f"AES解密失败: {e}")
|
||||
return ""
|
||||
|
||||
def rsa_decrypt(self, encrypted_data, private_key):
|
||||
"""RSA解密"""
|
||||
try:
|
||||
# 解码base64数据
|
||||
encrypted_bytes = base64.b64decode(encrypted_data)
|
||||
|
||||
# 导入私钥
|
||||
rsa_key = RSA.import_key(private_key)
|
||||
cipher = PKCS1_v1_5.new(rsa_key)
|
||||
|
||||
# 解密
|
||||
decrypted = cipher.decrypt(encrypted_bytes, None)
|
||||
return decrypted.decode('utf-8') if decrypted else ""
|
||||
except Exception as e:
|
||||
print(f"RSA解密失败: {e}")
|
||||
return ""
|
||||
|
||||
def get_cached_data(self, cache_key, data, path):
|
||||
"""带缓存的数据获取"""
|
||||
current_time = time.time()
|
||||
if cache_key in self.cache:
|
||||
cached_data, timestamp = self.cache[cache_key]
|
||||
if current_time - timestamp < self.cache_timeout:
|
||||
return cached_data
|
||||
|
||||
# 缓存不存在或已过期,重新获取
|
||||
result = self.get_data(data, path)
|
||||
if result:
|
||||
self.cache[cache_key] = (result, current_time)
|
||||
return result
|
||||
|
||||
def get_data(self, data, path, use_cache=True):
|
||||
"""获取数据的主要方法"""
|
||||
try:
|
||||
# 构建缓存键
|
||||
cache_key = f"{path}_{hash(str(data))}" if use_cache else None
|
||||
|
||||
if use_cache and cache_key in self.cache:
|
||||
cached_data, timestamp = self.cache[cache_key]
|
||||
if time.time() - timestamp < self.cache_timeout:
|
||||
return cached_data
|
||||
|
||||
start_time = time.time()
|
||||
|
||||
# AES加密请求数据
|
||||
request_key = self.aes_encrypt(json.dumps(data), 'mvXBSW7ekreItNsT', '2U3IrJL8szAKp0Fj')
|
||||
if not request_key:
|
||||
return None
|
||||
|
||||
# 生成签名
|
||||
t = str(int(time.time()))
|
||||
keys = "Qmxi5ciWXbQzkr7o+SUNiUuQxQEf8/AVyUWY4T/BGhcXBIUz4nOyHBGf9A4KbM0iKF3yp9M7WAY0rrs5PzdTAOB45plcS2zZ0wUibcXuGJ29VVGRWKGwE9zu2vLwhfgjTaaDpXo4rby+7GxXTktzJmxvneOUdYeHi+PZsThlvPI="
|
||||
sign_str = f"token_id=,token={self.token},phone_type=1,request_key={request_key},app_id=1,time={t},keys={keys}*&zvdvdvddbfikkkumtmdwqppp?|4Y!s!2br"
|
||||
signature = hashlib.md5(sign_str.encode()).hexdigest()
|
||||
|
||||
# 构建请求体
|
||||
body = {
|
||||
'token': self.token,
|
||||
'token_id': '',
|
||||
'phone_type': '1',
|
||||
'time': t,
|
||||
'phone_model': 'xiaomi-22021211rc',
|
||||
'keys': keys,
|
||||
'request_key': request_key,
|
||||
'signature': signature,
|
||||
'app_id': '1',
|
||||
'ad_version': '1'
|
||||
}
|
||||
|
||||
# 发送请求 - 设置超时时间
|
||||
url = f"{self.host}{path}"
|
||||
response = self.post(url, headers=self.header, data=body, timeout=10)
|
||||
|
||||
if response.status_code != 200:
|
||||
print(f"API请求失败: {response.status_code}, 路径: {path}")
|
||||
return None
|
||||
|
||||
response_data = response.json()
|
||||
if 'data' not in response_data:
|
||||
print(f"API返回数据格式错误, 路径: {path}")
|
||||
return None
|
||||
|
||||
data_response = response_data['data']
|
||||
|
||||
# RSA解密响应密钥
|
||||
private_key = """-----BEGIN PRIVATE KEY-----
|
||||
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGAe6hKrWLi1zQmjTT1
|
||||
ozbE4QdFeJGNxubxld6GrFGximxfMsMB6BpJhpcTouAqywAFppiKetUBBbXwYsYU
|
||||
1wNr648XVmPmCMCy4rY8vdliFnbMUj086DU6Z+/oXBdWU3/b1G0DN3E9wULRSwcK
|
||||
ZT3wj/cCI1vsCm3gj2R5SqkA9Y0CAwEAAQKBgAJH+4CxV0/zBVcLiBCHvSANm0l7
|
||||
HetybTh/j2p0Y1sTXro4ALwAaCTUeqdBjWiLSo9lNwDHFyq8zX90+gNxa7c5EqcW
|
||||
V9FmlVXr8VhfBzcZo1nXeNdXFT7tQ2yah/odtdcx+vRMSGJd1t/5k5bDd9wAvYdI
|
||||
DblMAg+wiKKZ5KcdAkEA1cCakEN4NexkF5tHPRrR6XOY/XHfkqXxEhMqmNbB9U34
|
||||
saTJnLWIHC8IXys6Qmzz30TtzCjuOqKRRy+FMM4TdwJBAJQZFPjsGC+RqcG5UvVM
|
||||
iMPhnwe/bXEehShK86yJK/g/UiKrO87h3aEu5gcJqBygTq3BBBoH2md3pr/W+hUM
|
||||
WBsCQQChfhTIrdDinKi6lRxrdBnn0Ohjg2cwuqK5zzU9p/N+S9x7Ck8wUI53DKm8
|
||||
jUJE8WAG7WLj/oCOWEh+ic6NIwTdAkEAj0X8nhx6AXsgCYRql1klbqtVmL8+95KZ
|
||||
K7PnLWG/IfjQUy3pPGoSaZ7fdquG8bq8oyf5+dzjE/oTXcByS+6XRQJAP/5ciy1b
|
||||
L3NhUhsaOVy55MHXnPjdcTX0FaLi+ybXZIfIQ2P4rb19mVq1feMbCXhz+L1rG8oa
|
||||
t5lYKfpe8k83ZA==
|
||||
-----END PRIVATE KEY-----"""
|
||||
|
||||
bodyki_json = self.rsa_decrypt(data_response['keys'], private_key)
|
||||
if not bodyki_json:
|
||||
print("RSA解密失败")
|
||||
return None
|
||||
|
||||
bodyki = json.loads(bodyki_json)
|
||||
|
||||
# AES解密响应数据
|
||||
decrypted_data = self.aes_decrypt(data_response['response_key'], bodyki['key'], bodyki['iv'])
|
||||
if not decrypted_data:
|
||||
print("AES解密失败")
|
||||
return None
|
||||
|
||||
result = json.loads(decrypted_data)
|
||||
|
||||
end_time = time.time()
|
||||
print(f"数据获取耗时: {end_time - start_time:.2f}秒, 路径: {path}")
|
||||
|
||||
# 缓存结果
|
||||
if use_cache and cache_key:
|
||||
self.cache[cache_key] = (result, time.time())
|
||||
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
print(f"获取数据失败: {e}, 路径: {path}")
|
||||
return None
|
||||
|
||||
def get_md5(self, text):
|
||||
"""计算MD5"""
|
||||
return hashlib.md5(text.encode()).hexdigest()
|
||||
|
||||
if __name__ == '__main__':
|
||||
pass
|
||||
Loading…
Reference in a new issue