mirror of https://github.com/ziyongdaima/YWJ
Compare commits
17 Commits
d047332f8e
...
df6a7f5bf8
Author | SHA1 | Date |
---|---|---|
![]() |
df6a7f5bf8 | |
![]() |
5e9d867841 | |
![]() |
a5983df81d | |
![]() |
aa6dc99092 | |
![]() |
464e7b951b | |
![]() |
28ac7c28ec | |
![]() |
1ccce5b6b5 | |
![]() |
45460248b0 | |
![]() |
426b9125c3 | |
![]() |
4fc6321795 | |
![]() |
5db4fd8202 | |
![]() |
4f42b8e498 | |
![]() |
cf39fa1244 | |
![]() |
e1580329db | |
![]() |
9cd1ed91ab | |
![]() |
f06ce4b947 | |
![]() |
2a70b6c49d |
BIN
jar/DMx.jar
BIN
jar/DMx.jar
Binary file not shown.
BIN
jar/XM.jar
BIN
jar/XM.jar
Binary file not shown.
|
@ -0,0 +1,452 @@
|
|||
/**
|
||||
* 已知问题:
|
||||
* [推荐]页面:'雷电模拟器'播放部份影片会出错,'播放器'改成'ijk' & '解码方式'改成'软解',即可正常播放
|
||||
* 影视TV 超連結跳轉支持
|
||||
* 影视TV 弹幕支持
|
||||
* https://t.me/fongmi_offical/
|
||||
* https://github.com/FongMi/Release/tree/main/apk
|
||||
* 皮皮虾DMBox 弹幕支持
|
||||
* 设置 > 窗口预览 > 开启
|
||||
* https://t.me/pipixiawerun
|
||||
* vod_area:'bilidanmu'
|
||||
* Cookie设置
|
||||
* Cookie获取方法 https://ghproxy.net/https://raw.githubusercontent.com/UndCover/PyramidStore/main/list.md
|
||||
* Cookie设置方法1: DR-PY 后台管理界面
|
||||
* CMS后台管理 > 设置中心 > 环境变量 > {"bili_cookie":"XXXXXXX","vmid":"XXXXXX"} > 保存
|
||||
* Cookie设置方法2: 手动替换Cookie
|
||||
* 底下代码 headers的
|
||||
* "Cookie":"$bili_cookie"
|
||||
* 手动替换为
|
||||
* "Cookie":"将获取的Cookie黏贴在这"
|
||||
* 客户端长期Cookie设置教程:
|
||||
* 抓包哔哩手机端搜索access_key,取任意链接里的access_key和appkey在drpy环境变量中增加同名的环境变量即可
|
||||
* 此时哔哩.js这个解析可用于此源的解析线路用
|
||||
* 传参 ?render=1&type=url¶ms=../json/哔哩教育.json@哔哩教育[官]
|
||||
* 写法 ext: ./我的哔哩.js?type=url¶ms=./json/哔哩大全.json
|
||||
*/
|
||||
var rule = {
|
||||
title: '我的哔哩[官]',
|
||||
host: 'https://api.bilibili.com',
|
||||
homeUrl: '/x/web-interface/ranking/v2?rid=0&type=origin',
|
||||
// url:'/x/web-interface/search/type?search_type=video&keyword=fyclass&page=fypage',
|
||||
url: '/x/web-interface/search/type?search_type=video&fyfilter',
|
||||
filter_url: 'keyword=fyclass{{fl.tid}}&page=fypage&duration={{fl.duration}}&order={{fl.order}}',
|
||||
class_parse: $js.toString(() => {
|
||||
// let html = request('{{host}}/files/json/哔哩教育.json');
|
||||
log('rule.params:' + rule.params);
|
||||
let html = request(rule.params);
|
||||
let json = dealJson(html);
|
||||
input = json.classes;
|
||||
homeObj.filter = json.filter;
|
||||
// log(input);
|
||||
}),
|
||||
filterable: 1,
|
||||
detailUrl: '/x/web-interface/view/detail?aid=fyid',
|
||||
searchUrl: '/x/web-interface/search/type?search_type=video&keyword=**&page=fypage',
|
||||
searchable: 2,
|
||||
quickSearch: 0,
|
||||
params: 'http://127.0.0.1:9978/file/jars/哔哩大全.json',
|
||||
// params: '?render=1&type=url¶ms=../json/哔哩教育.json@哔哩教育[官]',
|
||||
// params: '?render=1&type=url¶ms=../json/哔哩大全.json@哔哩大全[官]',
|
||||
headers: {
|
||||
"User-Agent": "PC_UA",
|
||||
"Referer": "https://www.bilibili.com",
|
||||
"Cookie": "SESSDATA=01c79709%2C1711618905%2C244c3%2A91CjA9UUnRb_kJg03J5FqUbin76yMY90o1-ckJsM1ItQH21ns4hgcS6Le6oMR3SKtC18QSVmxSbGRhN3cyS3NReERsOHRfYVlhTWJKZjZyTF9wUWJXbjNfaF9qWklFMFpoeVg1ZUhFb0Q0MGFiN3FYd1N3UEFEY1oxX29IdmFSRVVLamcybGhhSkJnIIEC; bili_jct=e9d6e9df733afde2a03693d63a4e77dc; DedeUserID=186957646;"
|
||||
},
|
||||
timeout: 5000,
|
||||
limit: 8,
|
||||
play_parse: true,
|
||||
double: false,
|
||||
lazy: `js:
|
||||
let ids = input.split('_');
|
||||
let dan = 'https://api.bilibili.com/x/v1/dm/list.so?oid=' + ids[1];
|
||||
let result = {};
|
||||
let iurl = 'https://api.bilibili.com:443/x/player/playurl?avid=' + ids[0] + '&cid=' + ids[1] + '&qn=116';
|
||||
let html = request(iurl);
|
||||
let jRoot = JSON.parse(html);
|
||||
let jo = jRoot.data;
|
||||
let ja = jo.durl;
|
||||
let maxSize = -1;
|
||||
let position = -1;
|
||||
ja.forEach(function(tmpJo, i) {
|
||||
if (maxSize < Number(tmpJo.size)) {
|
||||
maxSize = Number(tmpJo.size);
|
||||
position = i
|
||||
}
|
||||
});
|
||||
let purl = '';
|
||||
if (ja.length > 0) {
|
||||
if (position === -1) {
|
||||
position = 0
|
||||
}
|
||||
purl = ja[position].url
|
||||
}
|
||||
result.parse = 0;
|
||||
result.playUrl = '';
|
||||
result.url = unescape(purl);
|
||||
result.header = {
|
||||
'Referer': 'https://live.bilibili.com',
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36'
|
||||
};
|
||||
if (/\\.flv/.test(purl)) {
|
||||
result.contentType = 'video/x-flv';
|
||||
} else {
|
||||
result.contentType = '';
|
||||
}
|
||||
result.danmaku = dan;
|
||||
input = result
|
||||
`,
|
||||
推荐: `js:
|
||||
function stripHtmlTag(src) {
|
||||
return src.replace(/<\\/?[^>]+(>|$)/g, '').replace(/&.{1,5};/g, '').replace(/\\s{2,}/g, ' ');
|
||||
}
|
||||
function turnDHM(duration) {
|
||||
let min = '';
|
||||
let sec = '';
|
||||
try {
|
||||
min = duration.split(':')[0];
|
||||
sec = duration.split(':')[1];
|
||||
} catch (e) {
|
||||
min = Math.floor(duration / 60);
|
||||
sec = duration % 60;
|
||||
}
|
||||
if (isNaN(parseInt(duration))) {
|
||||
return '无效输入';
|
||||
}
|
||||
if (min == 0) {
|
||||
return sec + '秒'
|
||||
} else if (0 < min && min < 60) {
|
||||
return min + '分'
|
||||
} else if (60 <= min && min < 1440) {
|
||||
if (min % 60 == 0) {
|
||||
let h = min / 60;
|
||||
return h + '小时'
|
||||
} else {
|
||||
let h = min / 60;
|
||||
h = (h + '').split('.')[0];
|
||||
let m = min % 60;
|
||||
return h + '小时' + m + '分';
|
||||
}
|
||||
} else if (min >= 1440) {
|
||||
let d = min / 60 / 24;
|
||||
d = (d + '').split('.')[0];
|
||||
let h = min / 60 % 24;
|
||||
h = (h + '').split('.')[0];
|
||||
let m = min % 60;
|
||||
let dhm = '';
|
||||
if (d > 0) {
|
||||
dhm = d + '天'
|
||||
}
|
||||
if (h >= 1) {
|
||||
dhm = dhm + h + '小时'
|
||||
}
|
||||
if (m > 0) {
|
||||
dhm = dhm + m + '分'
|
||||
}
|
||||
return dhm
|
||||
}
|
||||
return null
|
||||
}
|
||||
function ConvertNum(num) {
|
||||
let _ws = Math.pow(10, 1);
|
||||
let _b = 1e4;
|
||||
if (num < _b) {
|
||||
return num.toString();
|
||||
}
|
||||
let _r = '';
|
||||
let _strArg = ['', '万', '亿', '万亿'];
|
||||
let _i = Math.floor(Math.log(num) / Math.log(_b));
|
||||
if (_i > 3) {
|
||||
_i = 3;
|
||||
}
|
||||
_r = Math.floor(num / Math.pow(_b, _i) * _ws) / _ws + _strArg[_i];
|
||||
return _r;
|
||||
}
|
||||
let html = request(input);
|
||||
let vodList = JSON.parse(html).data.list;
|
||||
let videos = [];
|
||||
vodList.forEach(function(vod) {
|
||||
let aid = vod.aid;
|
||||
let title = stripHtmlTag(vod.title);
|
||||
let img = vod.pic;
|
||||
if (img.startsWith('//')) {
|
||||
img = 'https:' + img;
|
||||
}
|
||||
let remark = turnDHM(vod.duration) + ' ▶' + ConvertNum(vod.stat.view) + ' 🆙' + vod.owner.name;
|
||||
videos.push({
|
||||
vod_id: aid,
|
||||
vod_name: title,
|
||||
vod_pic: img,
|
||||
vod_remarks: remark
|
||||
})
|
||||
});
|
||||
VODS = videos
|
||||
`,
|
||||
一级: `js:
|
||||
if (cateObj.tid.endsWith('_clicklink')) {
|
||||
cateObj.tid = cateObj.tid.split('_')[0];
|
||||
input = HOST + '/x/web-interface/search/type?search_type=video&keyword=' + cateObj.tid + '&page=' + MY_PAGE;
|
||||
}
|
||||
function stripHtmlTag(src) {
|
||||
return src.replace(/<\\/?[^>]+(>|$)/g, '').replace(/&.{1,5};/g, '').replace(/\\s{2,}/g, ' ');
|
||||
}
|
||||
function turnDHM(duration) {
|
||||
let min = '';
|
||||
let sec = '';
|
||||
try {
|
||||
min = duration.split(':')[0];
|
||||
sec = duration.split(':')[1];
|
||||
} catch (e) {
|
||||
min = Math.floor(duration / 60);
|
||||
sec = duration % 60;
|
||||
}
|
||||
if (isNaN(parseInt(duration))) {
|
||||
return '无效输入';
|
||||
}
|
||||
if (min == 0) {
|
||||
return sec + '秒'
|
||||
} else if (0 < min && min < 60) {
|
||||
return min + '分'
|
||||
} else if (60 <= min && min < 1440) {
|
||||
if (min % 60 == 0) {
|
||||
let h = min / 60;
|
||||
return h + '小时'
|
||||
} else {
|
||||
let h = min / 60;
|
||||
h = (h + '').split('.')[0];
|
||||
let m = min % 60;
|
||||
return h + '小时' + m + '分';
|
||||
}
|
||||
} else if (min >= 1440) {
|
||||
let d = min / 60 / 24;
|
||||
d = (d + '').split('.')[0];
|
||||
let h = min / 60 % 24;
|
||||
h = (h + '').split('.')[0];
|
||||
let m = min % 60;
|
||||
let dhm = '';
|
||||
if (d > 0) {
|
||||
dhm = d + '天'
|
||||
}
|
||||
if (h >= 1) {
|
||||
dhm = dhm + h + '小时'
|
||||
}
|
||||
if (m > 0) {
|
||||
dhm = dhm + m + '分'
|
||||
}
|
||||
return dhm
|
||||
}
|
||||
return null
|
||||
}
|
||||
function ConvertNum(num) {
|
||||
let _ws = Math.pow(10, 1);
|
||||
let _b = 1e4;
|
||||
if (num < _b) {
|
||||
return num.toString();
|
||||
}
|
||||
let _r = '';
|
||||
let _strArg = ['', '万', '亿', '万亿'];
|
||||
let _i = Math.floor(Math.log(num) / Math.log(_b));
|
||||
if (_i > 3) {
|
||||
_i = 3;
|
||||
}
|
||||
_r = Math.floor(num / Math.pow(_b, _i) * _ws) / _ws + _strArg[_i];
|
||||
return _r;
|
||||
}
|
||||
let data = [];
|
||||
let vodList = [];
|
||||
if (MY_CATE === '推荐') {
|
||||
input = HOST + '/x/web-interface/index/top/rcmd?ps=14&fresh_idx=' + MY_PAGE + '&fresh_idx_1h=' + MY_PAGE;
|
||||
data = JSON.parse(request(input)).data;
|
||||
vodList = data.item;
|
||||
} else if (MY_CATE === '历史记录') {
|
||||
input = HOST + '/x/v2/history?pn=' + MY_PAGE;
|
||||
data = JSON.parse(request(input)).data;
|
||||
vodList = data;
|
||||
} else {
|
||||
data = JSON.parse(request(input)).data;
|
||||
vodList = data.result;
|
||||
}
|
||||
let videos = [];
|
||||
vodList.forEach(function(vod) {
|
||||
let aid = vod.aid?vod.aid:vod.id;
|
||||
let title = stripHtmlTag(vod.title);
|
||||
let img = vod.pic;
|
||||
if (img.startsWith('//')) {
|
||||
img = 'https:' + img;
|
||||
}
|
||||
let play = '';
|
||||
let danmaku = '';
|
||||
if (MY_CATE === '推荐') {
|
||||
play = ConvertNum(vod.stat.view);
|
||||
danmaku = vod.stat.danmaku;
|
||||
} else if (MY_CATE === '历史记录') {
|
||||
play = ConvertNum(vod.stat.view);
|
||||
danmaku = vod.stat.danmaku;
|
||||
} else {
|
||||
play = ConvertNum(vod.play);
|
||||
danmaku = vod.video_review;
|
||||
}
|
||||
let remark = turnDHM(vod.duration) + ' ▶' + play + ' 💬' + danmaku;
|
||||
videos.push({
|
||||
vod_id: aid,
|
||||
vod_name: title,
|
||||
vod_pic: img,
|
||||
vod_remarks: remark
|
||||
})
|
||||
});
|
||||
VODS = videos
|
||||
`,
|
||||
二级: `js:
|
||||
function stripHtmlTag(src) {
|
||||
return src.replace(/<\\/?[^>]+(>|$)/g, '').replace(/&.{1,5};/g, '').replace(/\\s{2,}/g, ' ');
|
||||
}
|
||||
let html = request(input);
|
||||
let jo = JSON.parse(html).data.View;
|
||||
// 历史记录
|
||||
let cookies = rule_fetch_params.headers.Cookie.split(';');
|
||||
let bili_jct = '';
|
||||
cookies.forEach(cookie => {
|
||||
if (cookie.includes('bili_jct')) {
|
||||
bili_jct = cookie.split('=')[1];
|
||||
}
|
||||
});
|
||||
if (bili_jct !== '') {
|
||||
let historyReport = 'https://api.bilibili.com/x/v2/history/report';
|
||||
let dataPost = {
|
||||
aid: jo.aid,
|
||||
cid: jo.cid,
|
||||
csrf: bili_jct,
|
||||
};
|
||||
post(historyReport, dataPost, 'form');
|
||||
}
|
||||
|
||||
let stat = jo.stat;
|
||||
let up_info = JSON.parse(html).data.Card;
|
||||
let relation = up_info.following ? '已关注' : '未关注';
|
||||
let aid = jo.aid;
|
||||
let title = stripHtmlTag(jo.title);
|
||||
let pic = jo.pic;
|
||||
let desc = jo.desc;
|
||||
|
||||
let date = new Date(jo.pubdate * 1000);
|
||||
let yy = date.getFullYear().toString();
|
||||
let mm = date.getMonth()+1;
|
||||
mm = mm < 10 ? ('0' + mm) : mm;
|
||||
let dd = date.getDate();
|
||||
dd = dd < 10 ? ('0' + dd) : dd;
|
||||
|
||||
let up_name = jo.owner.name;
|
||||
let typeName = jo.tname;
|
||||
// let remark = jo.duration;
|
||||
let vod = {
|
||||
vod_id: aid,
|
||||
vod_name: title,
|
||||
vod_pic: pic,
|
||||
type_name: typeName,
|
||||
vod_year: yy+mm+dd,
|
||||
vod_area: 'bilidanmu',
|
||||
// vod_remarks: remark,
|
||||
vod_tags: 'mv',
|
||||
// vod_director: '🆙 ' + up_name + ' 👥 ' + up_info.follower + ' ' + relation,
|
||||
vod_director: '🆙 ' + '[a=cr:' + JSON.stringify({'id':up_name + '_clicklink','name':up_name}) + '/]' + up_name + '[/a]' + ' 👥 ' + up_info.follower + ' ' + relation,
|
||||
vod_actor: '▶' + stat.view + ' ' + '💬' + stat.danmaku + ' ' + '👍' + stat.like + ' ' + '💰' + stat.coin + ' ' + '⭐' + stat.favorite,
|
||||
vod_content: desc
|
||||
};
|
||||
let ja = jo.pages;
|
||||
let treeMap = {};
|
||||
let playurls = [];
|
||||
ja.forEach(function(tmpJo) {
|
||||
let cid = tmpJo.cid;
|
||||
let part = tmpJo.part.replaceAll('#', '﹟').replaceAll('$', '﹩');
|
||||
playurls.push(
|
||||
part + '$' + aid + '_' + cid
|
||||
)
|
||||
});
|
||||
treeMap['播放'] = playurls.join('#');
|
||||
let relatedData = JSON.parse(html).data.Related;
|
||||
playurls = [];
|
||||
relatedData.forEach(function(rd) {
|
||||
let ccid = rd.cid;
|
||||
let title = rd.title.replaceAll('#', '﹟').replaceAll('$', '﹩');
|
||||
let aaid = rd.aid;
|
||||
playurls.push(
|
||||
title + '$' + aaid + '_' + ccid
|
||||
)
|
||||
});
|
||||
treeMap['哔哩推荐'] = playurls.join('#');
|
||||
vod.vod_play_from = Object.keys(treeMap).join("$$$");
|
||||
vod.vod_play_url = Object.values(treeMap).join("$$$");
|
||||
VOD = vod;
|
||||
`,
|
||||
搜索: `js:
|
||||
let html = request(input);
|
||||
function stripHtmlTag(src) {
|
||||
return src.replace(/<\\/?[^>]+(>|$)/g, '').replace(/&.{1,5};/g, '').replace(/\\s{2,}/g, ' ');
|
||||
}
|
||||
function turnDHM(duration) {
|
||||
let min = '';
|
||||
let sec = '';
|
||||
try {
|
||||
min = duration.split(':')[0];
|
||||
sec = duration.split(':')[1];
|
||||
} catch (e) {
|
||||
min = Math.floor(duration / 60);
|
||||
sec = duration % 60;
|
||||
}
|
||||
if (isNaN(parseInt(duration))) {
|
||||
return '无效输入';
|
||||
}
|
||||
if (min == 0) {
|
||||
return sec + '秒'
|
||||
} else if (0 < min && min < 60) {
|
||||
return min + '分'
|
||||
} else if (60 <= min && min < 1440) {
|
||||
if (min % 60 == 0) {
|
||||
let h = min / 60;
|
||||
return h + '小时'
|
||||
} else {
|
||||
let h = min / 60;
|
||||
h = (h + '').split('.')[0];
|
||||
let m = min % 60;
|
||||
return h + '小时' + m + '分';
|
||||
}
|
||||
} else if (min >= 1440) {
|
||||
let d = min / 60 / 24;
|
||||
d = (d + '').split('.')[0];
|
||||
let h = min / 60 % 24;
|
||||
h = (h + '').split('.')[0];
|
||||
let m = min % 60;
|
||||
let dhm = '';
|
||||
if (d > 0) {
|
||||
dhm = d + '天'
|
||||
}
|
||||
if (h >= 1) {
|
||||
dhm = dhm + h + '小时'
|
||||
}
|
||||
if (m > 0) {
|
||||
dhm = dhm + m + '分'
|
||||
}
|
||||
return dhm
|
||||
}
|
||||
return null
|
||||
}
|
||||
let videos = [];
|
||||
let vodList = JSON.parse(html).data.result;
|
||||
vodList.forEach(function(vod) {
|
||||
let aid = vod.aid;
|
||||
let title = stripHtmlTag(vod.title);
|
||||
let img = vod.pic;
|
||||
if (img.startsWith('//')) {
|
||||
img = 'https:' + img;
|
||||
}
|
||||
let remark = turnDHM(vod.duration);
|
||||
videos.push({
|
||||
vod_id: aid,
|
||||
vod_name: title,
|
||||
vod_pic: img,
|
||||
vod_remarks: remark
|
||||
})
|
||||
});
|
||||
VODS = videos
|
||||
`,
|
||||
}
|
|
@ -0,0 +1,315 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# by @嗷呜
|
||||
import json
|
||||
import random
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
from base64 import b64decode, b64encode
|
||||
import concurrent.futures
|
||||
import requests
|
||||
from Crypto.Hash import MD5
|
||||
from pyquery import PyQuery as pq
|
||||
sys.path.append('..')
|
||||
from base.spider import Spider
|
||||
|
||||
class Spider(Spider):
|
||||
|
||||
def init(self, extend=""):
|
||||
self.host=self.gethost()
|
||||
self.headers.update({
|
||||
'referer': f'{self.host}/',
|
||||
'origin': self.host,
|
||||
})
|
||||
self.session = requests.Session()
|
||||
self.session.headers.update(self.headers)
|
||||
self.session.get(self.host)
|
||||
pass
|
||||
|
||||
def getName(self):
|
||||
pass
|
||||
|
||||
def isVideoFormat(self, url):
|
||||
pass
|
||||
|
||||
def manualVideoCheck(self):
|
||||
pass
|
||||
|
||||
def destroy(self):
|
||||
pass
|
||||
|
||||
headers = {
|
||||
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.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',
|
||||
'sec-ch-ua': '"Not/A)Brand";v="8", "Chromium";v="134", "Google Chrome";v="134"',
|
||||
'sec-ch-ua-mobile': '?0',
|
||||
'sec-ch-ua-platform': '"macOS"',
|
||||
'sec-fetch-site': 'same-origin',
|
||||
'sec-fetch-mode': 'navigate',
|
||||
'sec-fetch-user': '?1',
|
||||
'sec-fetch-dest': 'document',
|
||||
'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8',
|
||||
}
|
||||
|
||||
config={
|
||||
"1":[{"key":"class","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":"文艺"},{"n":"微电影","v":"微电影"},{"n":"古装","v":"古装"},{"n":"历史","v":"历史"},{"n":"运动","v":"运动"},{"n":"农村","v":"农村"},{"n":"儿童","v":"儿童"},{"n":"网络电影","v":"网络电影"}]},{"key":"area","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":"其他"}]},{"key":"year","name":"年份","value":[{"n":"全部","v":""},{"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"},{"n":"2003","v":"2003"},{"n":"2002","v":"2002"},{"n":"2001","v":"2001"},{"n":"2000","v":"2000"}]},{"key":"by","name":"排序","value":[{"n":"时间","v":"time"},{"n":"人气","v":"hits"},{"n":"评分","v":"score"}]}],
|
||||
"2":[{"key":"class","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":"其他"}]},{"key":"area","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":"其他"}]},{"key":"year","name":"年份","value":[{"n":"全部","v":""},{"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"},{"n":"2003","v":"2003"},{"n":"2002","v":"2002"},{"n":"2001","v":"2001"},{"n":"2000","v":"2000"}]},{"key":"by","name":"排序","value":[{"n":"时间","v":"time"},{"n":"人气","v":"hits"},{"n":"评分","v":"score"}]}],
|
||||
"3":[{"key":"class","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":"area","name":"地区","value":[{"n":"全部","v":""},{"n":"内地","v":"内地"},{"n":"港台","v":"港台"},{"n":"欧美","v":"欧美"},{"n":"日韩","v":"日韩"},{"n":"其他","v":"其他"}]},{"key":"year","name":"年份","value":[{"n":"全部","v":""},{"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"},{"n":"2003","v":"2003"},{"n":"2002","v":"2002"},{"n":"2001","v":"2001"},{"n":"2000","v":"2000"}]},{"key":"by","name":"排序","value":[{"n":"时间","v":"time"},{"n":"人气","v":"hits"},{"n":"评分","v":"score"}]}],
|
||||
"4":[{"key":"class","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":"area","name":"地区","value":[{"n":"全部","v":""},{"n":"国产","v":"国产"},{"n":"欧美","v":"欧美"},{"n":"日本","v":"日本"},{"n":"其他","v":"其他"}]},{"key":"year","name":"年份","value":[{"n":"全部","v":""},{"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"},{"n":"2003","v":"2003"},{"n":"2002","v":"2002"},{"n":"2001","v":"2001"},{"n":"2000","v":"2000"}]},{"key":"by","name":"排序","value":[{"n":"时间","v":"time"},{"n":"人气","v":"hits"},{"n":"评分","v":"score"}]}],
|
||||
}
|
||||
|
||||
def homeContent(self, filter):
|
||||
data=self.getpq()
|
||||
result = {}
|
||||
classes = []
|
||||
for k in data('ul.swiper-wrapper').eq(0)('li').items():
|
||||
i=k('a').attr('href')
|
||||
if i and 'type' in i:
|
||||
classes.append({
|
||||
'type_name': k.text(),
|
||||
'type_id': re.findall(r'\d+', i)[0],
|
||||
})
|
||||
result['class'] = classes
|
||||
result['list'] = self.getlist(data('.tab-content.ewave-pannel_bd li'))
|
||||
result['filters'] = self.config
|
||||
return result
|
||||
|
||||
def homeVideoContent(self):
|
||||
pass
|
||||
|
||||
def categoryContent(self, tid, pg, filter, extend):
|
||||
path=f"/vodshow/{tid}-{extend.get('area','')}-{extend.get('by','')}-{extend.get('class','')}-----{pg}---{extend.get('year','')}.html"
|
||||
data=self.getpq(path)
|
||||
result = {}
|
||||
result['list'] = self.getlist(data('ul.ewave-vodlist.clearfix li'))
|
||||
result['page'] = pg
|
||||
result['pagecount'] = 9999
|
||||
result['limit'] = 90
|
||||
result['total'] = 999999
|
||||
return result
|
||||
|
||||
def detailContent(self, ids):
|
||||
data=self.getpq(f"/voddetail/{ids[0]}.html")
|
||||
v=data('.ewave-content__detail')
|
||||
c=data('p')
|
||||
vod = {
|
||||
'type_name':c.eq(0)('a').text(),
|
||||
'vod_year': v('.data.hidden-sm').text(),
|
||||
'vod_remarks': v('h1').text(),
|
||||
'vod_actor': c.eq(1)('a').text(),
|
||||
'vod_director': c.eq(2)('a').text(),
|
||||
'vod_content': c.eq(-1).text(),
|
||||
'vod_play_from': '',
|
||||
'vod_play_url': ''
|
||||
}
|
||||
nd=list(data('ul.nav-tabs.swiper-wrapper li').items())
|
||||
pd=list(data('ul.ewave-content__playlist').items())
|
||||
n,p=[],[]
|
||||
for i,x in enumerate(nd):
|
||||
n.append(x.text())
|
||||
p.append('#'.join([f"{j.text()}${j('a').attr('href')}" for j in pd[i]('li').items()]))
|
||||
vod['vod_play_url']='$$$'.join(p)
|
||||
vod['vod_play_from']='$$$'.join(n)
|
||||
return {'list':[vod]}
|
||||
|
||||
def searchContent(self, key, quick, pg="1"):
|
||||
if pg=="1":
|
||||
p=f"-------------.html?wd={key}"
|
||||
else:
|
||||
p=f"{key}----------{pg}---.html"
|
||||
data=self.getpq(f"/vodsearch/{p}")
|
||||
return {'list':self.getlist(data('ul.ewave-vodlist__media.clearfix li')),'page':pg}
|
||||
|
||||
def playerContent(self, flag, id, vipFlags):
|
||||
try:
|
||||
data=self.getpq(id)
|
||||
jstr = json.loads(data('.ewave-player__video script').eq(0).text().split('=', 1)[-1])
|
||||
jxpath='/bbplayer/api.php'
|
||||
data=self.session.post(f"{self.host}{jxpath}",data={'vid':jstr['url']}).json()['data']
|
||||
if re.search(r'\.m3u8|\.mp4',data['url']):
|
||||
url=data['url']
|
||||
elif data['urlmode'] == 1:
|
||||
url=self.decode1(data['url'])
|
||||
elif data['urlmode'] == 2:
|
||||
url=self.decode2(data['url'])
|
||||
elif re.search(r'\.m3u8|\.mp4',jstr['url']):
|
||||
url=jstr['url']
|
||||
else:
|
||||
url=None
|
||||
if not url:raise Exception('未找到播放地址')
|
||||
p,c=0,''
|
||||
except Exception as e:
|
||||
self.log(f"解析失败: {e}")
|
||||
p,url,c=1,f"{self.host}{id}",'document.querySelector("#playleft iframe").contentWindow.document.querySelector("#start").click()'
|
||||
return {'parse': p, 'url': url, 'header': {'User-Agent':'okhttp/3.12.1'},'click': c}
|
||||
|
||||
def localProxy(self, param):
|
||||
wdict=json.loads(self.d64(param['wdict']))
|
||||
url=f"{wdict['jx']}{wdict['id']}"
|
||||
data=pq(self.fetch(url,headers=self.headers).text)
|
||||
html=data('script').eq(-1).text()
|
||||
url = re.search(r'src="(.*?)"', html).group(1)
|
||||
return [302,'text/html',None,{'Location':url}]
|
||||
|
||||
def liveContent(self, url):
|
||||
pass
|
||||
|
||||
def gethost(self):
|
||||
data=pq(self.fetch('https://13826.kstore.space/jubba.py/jbb1.html',headers=self.headers).text)
|
||||
hlist=list(data('.content-top ul li').items())[:2]
|
||||
hsots=[j('a').attr('href') for i in hlist for j in i('a').items()]
|
||||
return self.host_late(hsots)
|
||||
|
||||
def host_late(self, urls):
|
||||
with concurrent.futures.ThreadPoolExecutor() as executor:
|
||||
future_to_url = {
|
||||
executor.submit(self.test_host, url): url
|
||||
for url in urls
|
||||
}
|
||||
results = {}
|
||||
for future in concurrent.futures.as_completed(future_to_url):
|
||||
url = future_to_url[future]
|
||||
try:
|
||||
results[url] = future.result()
|
||||
except Exception as e:
|
||||
results[url] = float('inf')
|
||||
min_url = min(results.items(), key=lambda x: x[1])[0] if results else None
|
||||
if all(delay == float('inf') for delay in results.values()) or not min_url:
|
||||
return urls[0]
|
||||
return min_url
|
||||
|
||||
def test_host(self, url):
|
||||
try:
|
||||
start_time = time.monotonic()
|
||||
response = requests.head(
|
||||
url,
|
||||
timeout=1.0,
|
||||
allow_redirects=False,
|
||||
headers=self.headers
|
||||
)
|
||||
response.raise_for_status()
|
||||
return (time.monotonic() - start_time) * 1000
|
||||
except Exception as e:
|
||||
print(f"测试{url}失败: {str(e)}")
|
||||
return float('inf')
|
||||
|
||||
def getpq(self, path='',min=0,max=3):
|
||||
data = self.session.get(f"{self.host}{path}")
|
||||
data=data.text
|
||||
try:
|
||||
if '人机验证' in data:
|
||||
print(f"第{min}次尝试人机验证")
|
||||
jstr=pq(data)('script').eq(-1).html()
|
||||
token,tpath,stt=self.extract(jstr)
|
||||
body={'value':self.encrypt(self.host,stt),'token':self.encrypt(token,stt)}
|
||||
cd=self.session.post(f"{self.host}{tpath}",data=body)
|
||||
if min>max:raise Exception('人机验证失败')
|
||||
return self.getpq(path,min+1,max)
|
||||
return pq(data)
|
||||
except:
|
||||
return pq(data.encode('utf-8'))
|
||||
|
||||
def encrypt(self, input_str,staticchars):
|
||||
encodechars = ""
|
||||
for char in input_str:
|
||||
num0 = staticchars.find(char)
|
||||
if num0 == -1:
|
||||
code = char
|
||||
else:
|
||||
code = staticchars[(num0 + 3) % 62]
|
||||
num1 = random.randint(0, 61)
|
||||
num2 = random.randint(0, 61)
|
||||
encodechars += staticchars[num1] + code + staticchars[num2]
|
||||
return self.e64(encodechars)
|
||||
|
||||
def extract(self, js_code):
|
||||
token_match = re.search(r'var token = encrypt\("([^"]+)"\);', js_code)
|
||||
token_value = token_match.group(1) if token_match else None
|
||||
url_match = re.search(r'var url = \'([^\']+)\';', js_code)
|
||||
url_value = url_match.group(1) if url_match else None
|
||||
staticchars_match = re.search(r'var\s+staticchars\s*=\s*["\']([^"\']+)["\'];', js_code)
|
||||
staticchars = staticchars_match.group(1) if staticchars_match else None
|
||||
return token_value, url_value,staticchars
|
||||
|
||||
def decode1(self, val):
|
||||
url = self._custom_str_decode(val)
|
||||
parts = url.split("/")
|
||||
result = "/".join(parts[2:])
|
||||
key1 = json.loads(self.d64(parts[1]))
|
||||
key2 = json.loads(self.d64(parts[0]))
|
||||
decoded = self.d64(result)
|
||||
return self._de_string(key1, key2, decoded)
|
||||
|
||||
def _custom_str_decode(self, val):
|
||||
decoded = self.d64(val)
|
||||
key = self.md5("test")
|
||||
result = ""
|
||||
for i in range(len(decoded)):
|
||||
result += chr(ord(decoded[i]) ^ ord(key[i % len(key)]))
|
||||
return self.d64(result)
|
||||
|
||||
def _de_string(self, key_array, value_array, input_str):
|
||||
result = ""
|
||||
for char in input_str:
|
||||
if re.match(r'^[a-zA-Z]$', char):
|
||||
if char in key_array:
|
||||
index = key_array.index(char)
|
||||
result += value_array[index]
|
||||
continue
|
||||
result += char
|
||||
return result
|
||||
|
||||
def decode2(self, url):
|
||||
key = "PXhw7UT1B0a9kQDKZsjIASmOezxYG4CHo5Jyfg2b8FLpEvRr3WtVnlqMidu6cN"
|
||||
url=self.d64(url)
|
||||
result = ""
|
||||
i = 1
|
||||
while i < len(url):
|
||||
try:
|
||||
index = key.find(url[i])
|
||||
if index == -1:
|
||||
char = url[i]
|
||||
else:
|
||||
char = key[(index + 59) % 62]
|
||||
result += char
|
||||
except IndexError:
|
||||
break
|
||||
i += 3
|
||||
return result
|
||||
|
||||
def getlist(self, data):
|
||||
videos = []
|
||||
for k in data.items():
|
||||
j = k('.ewave-vodlist__thumb')
|
||||
h=k('.text-overflow a')
|
||||
if not h.attr('href'):h=j
|
||||
videos.append({
|
||||
'vod_id': re.findall(r'\d+', h.attr('href'))[0],
|
||||
'vod_name': j.attr('title'),
|
||||
'vod_pic': j.attr('data-original'),
|
||||
'vod_remarks': k('.pic-text').text(),
|
||||
})
|
||||
return videos
|
||||
|
||||
def e64(self, text):
|
||||
try:
|
||||
text_bytes = text.encode('utf-8')
|
||||
encoded_bytes = b64encode(text_bytes)
|
||||
return encoded_bytes.decode('utf-8')
|
||||
except Exception as e:
|
||||
print(f"Base64编码错误: {str(e)}")
|
||||
return ""
|
||||
|
||||
def d64(self,encoded_text):
|
||||
try:
|
||||
encoded_bytes = encoded_text.encode('utf-8')
|
||||
decoded_bytes = b64decode(encoded_bytes)
|
||||
return decoded_bytes.decode('utf-8')
|
||||
except Exception as e:
|
||||
print(f"Base64解码错误: {str(e)}")
|
||||
return ""
|
||||
|
||||
def md5(self, text):
|
||||
h = MD5.new()
|
||||
h.update(text.encode('utf-8'))
|
||||
return h.hexdigest()
|
|
@ -0,0 +1,255 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# by @嗷呜
|
||||
import re
|
||||
import sys
|
||||
from Crypto.Hash import MD5
|
||||
sys.path.append("..")
|
||||
from Crypto.Cipher import AES
|
||||
from Crypto.Util.Padding import pad, unpad
|
||||
from urllib.parse import quote, urlparse
|
||||
from base64 import b64encode, b64decode
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
import json
|
||||
import time
|
||||
from base.spider import Spider
|
||||
|
||||
class Spider(Spider):
|
||||
|
||||
'''
|
||||
sites照常配置,
|
||||
lives配置:
|
||||
{
|
||||
"name": "xxxx",
|
||||
"type": 3,
|
||||
"api": "路径/若惜追剧APP.py",
|
||||
"ext": ""
|
||||
}
|
||||
'''
|
||||
|
||||
def init(self, extend=""):
|
||||
self.host = self.gethost()
|
||||
pass
|
||||
|
||||
def getName(self):
|
||||
pass
|
||||
|
||||
|
||||
def isVideoFormat(self, url):
|
||||
pass
|
||||
|
||||
def manualVideoCheck(self):
|
||||
pass
|
||||
|
||||
def action(self, action):
|
||||
pass
|
||||
|
||||
def destroy(self):
|
||||
pass
|
||||
|
||||
def homeContent(self, filter):
|
||||
data = self.getdata("/api.php/getappapi.index/initV119")
|
||||
dy = {"class": "类型", "area": "地区", "lang": "语言", "year": "年份", "letter": "字母", "by": "排序",
|
||||
"sort": "排序"}
|
||||
filters = {}
|
||||
classes = []
|
||||
json_data = data["type_list"]
|
||||
homedata = data["banner_list"][8:]
|
||||
for item in json_data:
|
||||
if item["type_name"] == "全部":
|
||||
continue
|
||||
has_non_empty_field = False
|
||||
jsontype_extend = json.loads(item["type_extend"])
|
||||
homedata.extend(item["recommend_list"])
|
||||
jsontype_extend["sort"] = "最新,最热,最赞"
|
||||
classes.append({"type_name": item["type_name"], "type_id": item["type_id"]})
|
||||
for key in dy:
|
||||
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 dy 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": dy[dkey], "value": value_array})
|
||||
result = {}
|
||||
result["class"] = classes
|
||||
result["filters"] = filters
|
||||
result["list"] = homedata[1:]
|
||||
return result
|
||||
|
||||
def homeVideoContent(self):
|
||||
pass
|
||||
|
||||
def categoryContent(self, tid, pg, filter, extend):
|
||||
body = {"area": extend.get('area', '全部'), "year": extend.get('year', '全部'), "type_id": tid, "page": pg,
|
||||
"sort": extend.get('sort', '最新'), "lang": extend.get('lang', '全部'),
|
||||
"class": extend.get('class', '全部')}
|
||||
result = {}
|
||||
data = self.getdata("/api.php/getappapi.index/typeFilterVodList", body)
|
||||
result["list"] = data["recommend_list"]
|
||||
result["page"] = pg
|
||||
result["pagecount"] = 9999
|
||||
result["limit"] = 90
|
||||
result["total"] = 999999
|
||||
return result
|
||||
|
||||
def detailContent(self, ids):
|
||||
body = f"vod_id={ids[0]}"
|
||||
data = self.getdata("/api.php/getappapi.index/vodDetail", body)
|
||||
vod = data["vod"]
|
||||
play = []
|
||||
names = []
|
||||
for itt in data["vod_play_list"]:
|
||||
a = []
|
||||
names.append(itt["player_info"]["show"])
|
||||
for it in itt['urls']:
|
||||
it['user_agent']=itt["player_info"].get("user_agent")
|
||||
it["parse"]=itt["player_info"].get("parse")
|
||||
a.append(f"{it['name']}${self.e64(json.dumps(it))}")
|
||||
play.append("#".join(a))
|
||||
vod["vod_play_from"] = "$$$".join(names)
|
||||
vod["vod_play_url"] = "$$$".join(play)
|
||||
result = {"list": [vod]}
|
||||
return result
|
||||
|
||||
def searchContent(self, key, quick, pg="1"):
|
||||
body = f"keywords={key}&type_id=0&page={pg}"
|
||||
data = self.getdata("/api.php/getappapi.index/searchList", body)
|
||||
result = {"list": data["search_list"], "page": pg}
|
||||
return result
|
||||
|
||||
def playerContent(self, flag, id, vipFlags):
|
||||
ids = json.loads(self.d64(id))
|
||||
h={"User-Agent": (ids['user_agent'] or "okhttp/3.14.9")}
|
||||
try:
|
||||
if re.search(r'url=', ids['parse_api_url']):
|
||||
data=self.fetch(ids['parse_api_url'], headers=h, timeout=10).json()
|
||||
url=data.get('url') or data['data'].get('url')
|
||||
else:
|
||||
body = f"parse_api={ids.get('parse') or ids['parse_api_url'].replace(ids['url'], '')}&url={quote(self.aes(ids['url'],True))}&token={ids.get('token')}"
|
||||
b = self.getdata("/api.php/getappapi.index/vodParse", body)['json']
|
||||
url = json.loads(b)['url']
|
||||
if 'error' in url:raise ValueError(f"解析失败: {url}")
|
||||
p=0
|
||||
except Exception as e:
|
||||
print('错误信息:',e)
|
||||
url, p = ids['url'], 1
|
||||
|
||||
if re.search(r'\.jpg|\.png|\.jpeg', url):
|
||||
url = self.Mproxy(url)
|
||||
result = {}
|
||||
result["parse"] = p
|
||||
result["url"] = url
|
||||
result["header"] = h
|
||||
return result
|
||||
|
||||
def liveContent(self, url):
|
||||
id=self.homeContent(True)['class'][-1]['type_id']
|
||||
vlist=self.categoryContent(id,1,False,{})['list']
|
||||
results = []
|
||||
with ThreadPoolExecutor(max_workers=len(vlist)) as executor:
|
||||
futures = [executor.submit(self.livedetailContent, item['vod_name'], item['vod_id']) for item in vlist]
|
||||
for future in futures:
|
||||
try:
|
||||
detail = future.result()
|
||||
if detail:
|
||||
results.append(detail)
|
||||
except Exception as e:
|
||||
print(f"处理详情数据失败: {str(e)}")
|
||||
return '\n'.join(results)
|
||||
|
||||
def livedetailContent(self, name,id):
|
||||
try:
|
||||
print(f"获取直播源:{name}")
|
||||
body = f"vod_id={id}"
|
||||
data = self.getdata("/api.php/getappapi.index/vodDetail", body)
|
||||
play = [f"{name},#genre#"]
|
||||
for itt in data["vod_play_list"]:
|
||||
for it in itt['urls']:
|
||||
play.append(f"{it['name']}, {it['url']}")
|
||||
except Exception as e:
|
||||
print(f"获取直播源失败:{str(e)}")
|
||||
play=[]
|
||||
return '\n'.join(play)
|
||||
|
||||
def localProxy(self, param):
|
||||
return self.Mlocal(param)
|
||||
|
||||
def gethost(self):
|
||||
headers = {
|
||||
'User-Agent': 'okhttp/3.14.9'
|
||||
}
|
||||
host = self.fetch('https://rxysyyds.oss-cn-chengdu.aliyuncs.com/getapp.txt', headers=headers).text
|
||||
return host.strip()
|
||||
|
||||
def aes(self, text,b=None):
|
||||
key = b"ebad3f1a58b13933"
|
||||
cipher = AES.new(key, AES.MODE_CBC, key)
|
||||
if b:
|
||||
ct_bytes = cipher.encrypt(pad(text.encode("utf-8"), AES.block_size))
|
||||
ct = b64encode(ct_bytes).decode("utf-8")
|
||||
return ct
|
||||
else :
|
||||
pt = unpad(cipher.decrypt(b64decode(text)), AES.block_size)
|
||||
return pt.decode("utf-8")
|
||||
|
||||
def header(self):
|
||||
t = str(int(time.time()))
|
||||
header = {"Referer":self.host,
|
||||
"User-Agent": "okhttp/3.14.9", "app-version-code": "140", "app-ui-mode": "light",
|
||||
"app-api-verify-time": t, "app-user-device-id": self.md5(t),
|
||||
"app-api-verify-sign": self.aes(t,True),
|
||||
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"}
|
||||
return header
|
||||
|
||||
def getdata(self, path, data=None):
|
||||
vdata = self.post(f"{self.host}{path}", headers=self.header(), data=data, timeout=10).json()['data']
|
||||
data1 = self.aes(vdata)
|
||||
return json.loads(data1)
|
||||
|
||||
def Mproxy(self, url):
|
||||
return f"{self.getProxyUrl()}&url={self.e64(url)}&type=m3u8"
|
||||
|
||||
def Mlocal(self, param,header=None):
|
||||
url = self.d64(param["url"])
|
||||
ydata = self.fetch(url, headers=header, allow_redirects=False)
|
||||
data = ydata.content.decode('utf-8')
|
||||
if ydata.headers.get('Location'):
|
||||
url = ydata.headers['Location']
|
||||
data = self.fetch(url, headers=header).content.decode('utf-8')
|
||||
parsed_url = urlparse(url)
|
||||
durl = parsed_url.scheme + "://" + parsed_url.netloc
|
||||
lines = data.strip().split('\n')
|
||||
for index, string in enumerate(lines):
|
||||
if '#EXT' not in string and 'http' not in string:
|
||||
last_slash_index = string.rfind('/')
|
||||
lpath = string[:last_slash_index + 1]
|
||||
lines[index] = durl + ('' if lpath.startswith('/') else '/') + lpath
|
||||
data = '\n'.join(lines)
|
||||
return [200, "application/vnd.apple.mpegur", data]
|
||||
|
||||
def e64(self, text):
|
||||
try:
|
||||
text_bytes = text.encode('utf-8')
|
||||
encoded_bytes = b64encode(text_bytes)
|
||||
return encoded_bytes.decode('utf-8')
|
||||
except Exception as e:
|
||||
print(f"Base64编码错误: {str(e)}")
|
||||
return ""
|
||||
|
||||
def d64(self,encoded_text):
|
||||
try:
|
||||
encoded_bytes = encoded_text.encode('utf-8')
|
||||
decoded_bytes = b64decode(encoded_bytes)
|
||||
return decoded_bytes.decode('utf-8')
|
||||
except Exception as e:
|
||||
print(f"Base64解码错误: {str(e)}")
|
||||
return ""
|
||||
|
||||
def md5(self, text):
|
||||
h = MD5.new()
|
||||
h.update(text.encode('utf-8'))
|
||||
return h.hexdigest()
|
|
@ -0,0 +1,388 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import requests
|
||||
import json
|
||||
import time
|
||||
import sys
|
||||
import urllib.parse
|
||||
|
||||
sys.path.append('../../../../../../')
|
||||
try:
|
||||
from base.spider import Spider
|
||||
except ImportError:
|
||||
# 定义一个基础接口类,用于本地测试
|
||||
class Spider:
|
||||
def init(self, extend=""):
|
||||
pass
|
||||
|
||||
class Spider(Spider):
|
||||
def __init__(self):
|
||||
self.siteUrl = "https://app.whjzjx.cn"
|
||||
# 分类ID映射
|
||||
self.cateManual = {
|
||||
"古装": "5",
|
||||
"穿越": "17",
|
||||
"逆袭": "7",
|
||||
"重生": "6"
|
||||
}
|
||||
# 请求头
|
||||
self.headers = {
|
||||
"Connection": "keep-alive",
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
"user-agent": "okhttp/4.10.0",
|
||||
"user_agent": "Mozilla/5.0 (Linux; Android 9; ASUS_I003DD Build/PI; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/68.0.3440.70 Mobile Safari/537.36",
|
||||
"Host": "app.whjzjx.cn",
|
||||
"Accept-Encoding": "gzip"
|
||||
}
|
||||
# token缓存
|
||||
self.token = None
|
||||
self.tokenExpireTime = 0
|
||||
|
||||
def getName(self):
|
||||
# 返回爬虫名称
|
||||
return "蓝莓短剧"
|
||||
|
||||
def init(self, extend=""):
|
||||
return
|
||||
|
||||
def isVideoFormat(self, url):
|
||||
# 检查是否为视频格式
|
||||
video_formats = ['.mp4', '.m3u8', '.ts']
|
||||
for format in video_formats:
|
||||
if format in url.lower():
|
||||
return True
|
||||
return False
|
||||
|
||||
def manualVideoCheck(self):
|
||||
# 不需要手动检查
|
||||
return False
|
||||
|
||||
def getToken(self):
|
||||
"""获取API访问Token"""
|
||||
# 如果token有效期内,直接返回
|
||||
current_time = time.time()
|
||||
if self.token and current_time < self.tokenExpireTime:
|
||||
return self.token
|
||||
|
||||
# 否则重新获取
|
||||
try:
|
||||
tkurl = 'https://app.whjzjx.cn/v1/account/login'
|
||||
body = "device=20caaae96b3443174bf4ebdbdcc253776"
|
||||
|
||||
response = requests.post(
|
||||
tkurl,
|
||||
headers=self.headers,
|
||||
data=body
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
json_data = response.json()
|
||||
if json_data.get('code') == 0 or json_data.get('code') == "ok" or json_data.get('status') == 0:
|
||||
self.token = json_data['data']['token']
|
||||
# 设置token过期时间为1小时
|
||||
self.tokenExpireTime = current_time + 3600
|
||||
return self.token
|
||||
|
||||
print(f"获取token失败: {response.text}")
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"获取token异常: {str(e)}")
|
||||
return None
|
||||
|
||||
def fetchWithToken(self, url, method="GET", body=None):
|
||||
"""带token的网络请求"""
|
||||
token = self.getToken()
|
||||
if not token:
|
||||
print("无法获取token")
|
||||
return None
|
||||
|
||||
headers = self.headers.copy()
|
||||
headers["authorization"] = token
|
||||
|
||||
try:
|
||||
if method.upper() == "GET":
|
||||
response = requests.get(url, headers=headers, timeout=10)
|
||||
else: # POST
|
||||
response = requests.post(url, headers=headers, data=body, timeout=10)
|
||||
|
||||
response.raise_for_status()
|
||||
return response
|
||||
except Exception as e:
|
||||
print(f"请求失败: {url}, 错误: {str(e)}")
|
||||
return None
|
||||
|
||||
def homeContent(self, filter):
|
||||
"""获取首页分类及筛选"""
|
||||
result = {}
|
||||
classes = []
|
||||
|
||||
# 添加分类
|
||||
for k in self.cateManual:
|
||||
classes.append({
|
||||
'type_id': self.cateManual[k],
|
||||
'type_name': k
|
||||
})
|
||||
|
||||
result['class'] = classes
|
||||
|
||||
# 获取首页推荐视频
|
||||
try:
|
||||
result['list'] = self.homeVideoContent()['list']
|
||||
except:
|
||||
result['list'] = []
|
||||
|
||||
return result
|
||||
|
||||
def homeVideoContent(self):
|
||||
"""获取首页推荐视频内容"""
|
||||
# 使用第一个分类的内容作为首页推荐
|
||||
first_cate = list(self.cateManual.values())[0]
|
||||
result = self.categoryContent(first_cate, 1, False, None)
|
||||
# 不打印错误信息,除非列表为空
|
||||
if not result.get('list'):
|
||||
print("未获取到首页推荐视频")
|
||||
return result
|
||||
|
||||
def categoryContent(self, tid, pg, filter, extend):
|
||||
"""获取分类内容"""
|
||||
result = {}
|
||||
videos = []
|
||||
|
||||
try:
|
||||
# 构建请求URL:分类页
|
||||
url = f"{self.siteUrl}/v1/theater/home_page?theater_class_id={tid}&page_num={int(pg)-1}&page_size=24"
|
||||
|
||||
response = self.fetchWithToken(url)
|
||||
if not response:
|
||||
return result
|
||||
|
||||
json_data = response.json()
|
||||
|
||||
# 服务器正常响应状态检查,返回"ok"或status=0认为是成功
|
||||
if not(json_data.get('code') == 0 or json_data.get('code') == "ok" or json_data.get('status') == 0):
|
||||
print(f"获取分类数据失败: {json_data}")
|
||||
return result
|
||||
|
||||
# 不再打印json_data,而是处理正常返回的数据
|
||||
# 解析视频列表
|
||||
data_list = json_data.get('data', {}).get('list', [])
|
||||
for item in data_list:
|
||||
theater = item.get('theater', {})
|
||||
if not theater:
|
||||
continue
|
||||
|
||||
video_id = theater.get('id')
|
||||
title = theater.get('title')
|
||||
cover = theater.get('cover_url')
|
||||
total = theater.get('total', '')
|
||||
play_amount = theater.get('play_amount_str', '')
|
||||
|
||||
videos.append({
|
||||
"vod_id": video_id,
|
||||
"vod_name": title,
|
||||
"vod_pic": cover,
|
||||
"vod_remarks": f"{total}集",
|
||||
"vod_content": f"播放量:{play_amount}"
|
||||
})
|
||||
|
||||
# 构建返回结果
|
||||
result = {
|
||||
'list': videos,
|
||||
'page': pg,
|
||||
'pagecount': 9999, # 假设有很多页
|
||||
'limit': 24,
|
||||
'total': 999999 # 设置一个较大数值
|
||||
}
|
||||
except Exception as e:
|
||||
print(f"获取分类内容异常: {str(e)}")
|
||||
|
||||
return result
|
||||
|
||||
def detailContent(self, ids):
|
||||
"""获取详情页内容"""
|
||||
video_id = ids[0]
|
||||
result = {}
|
||||
|
||||
try:
|
||||
# 构建详情页请求URL
|
||||
url = f"{self.siteUrl}/v2/theater_parent/detail?theater_parent_id={video_id}"
|
||||
|
||||
response = self.fetchWithToken(url)
|
||||
if not response:
|
||||
return {}
|
||||
|
||||
json_data = response.json()
|
||||
if not(json_data.get('code') == 0 or json_data.get('code') == "ok" or json_data.get('status') == 0):
|
||||
print(f"获取详情数据失败: {json_data}")
|
||||
return {}
|
||||
|
||||
# 解析详情数据
|
||||
data = json_data.get('data', {})
|
||||
title = data.get('title', '')
|
||||
cover = data.get('cover_url', '')
|
||||
total = data.get('total', '')
|
||||
|
||||
# 提取剧集列表
|
||||
theaters = data.get('theaters', [])
|
||||
episodes = []
|
||||
|
||||
for index, theater in enumerate(theaters):
|
||||
ep_name = f"第{theater.get('num', '')}集"
|
||||
# 生成格式为 video_id_episode_index 的ID,方便playerContent提取
|
||||
ep_url = f"{video_id}_{index}"
|
||||
episodes.append(f"{ep_name}${ep_url}")
|
||||
|
||||
# 构建VOD数据
|
||||
vod = {
|
||||
"vod_id": video_id,
|
||||
"vod_name": title,
|
||||
"vod_pic": cover,
|
||||
"vod_remarks": f"{total}集",
|
||||
"vod_content": data.get('introduction', ''),
|
||||
"vod_play_from": "蓝莓短剧",
|
||||
"vod_play_url": "#".join(episodes)
|
||||
}
|
||||
|
||||
result = {
|
||||
'list': [vod]
|
||||
}
|
||||
except Exception as e:
|
||||
print(f"获取详情内容异常: {str(e)}")
|
||||
|
||||
return result
|
||||
|
||||
def searchContent(self, key, quick, pg=1):
|
||||
"""搜索功能"""
|
||||
result = {}
|
||||
videos = []
|
||||
|
||||
try:
|
||||
# 构建搜索请求
|
||||
url = f"{self.siteUrl}/v2/search"
|
||||
body = f"text={urllib.parse.quote(key)}"
|
||||
|
||||
response = self.fetchWithToken(url, method="POST", body=body)
|
||||
if not response:
|
||||
return {}
|
||||
|
||||
json_data = response.json()
|
||||
# 修改这里,使用与detailContent相同的条件判断
|
||||
if not(json_data.get('code') == 0 or json_data.get('code') == "ok" or json_data.get('status') == 0):
|
||||
print(f"搜索数据失败: {json_data}")
|
||||
return {}
|
||||
|
||||
# 解析搜索结果
|
||||
search_data = json_data.get('data', {}).get('search_data', [])
|
||||
for item in search_data:
|
||||
video_id = item.get('id')
|
||||
title = item.get('title')
|
||||
cover = item.get('cover_url')
|
||||
score = item.get('score_str', '')
|
||||
total = item.get('total', '')
|
||||
|
||||
videos.append({
|
||||
"vod_id": video_id,
|
||||
"vod_name": title,
|
||||
"vod_pic": cover,
|
||||
"vod_remarks": f"{score}|{total}集"
|
||||
})
|
||||
|
||||
result = {
|
||||
'list': videos,
|
||||
'page': pg
|
||||
}
|
||||
except Exception as e:
|
||||
print(f"搜索内容异常: {str(e)}")
|
||||
return result
|
||||
|
||||
def searchContentPage(self, key, quick, pg=1):
|
||||
return self.searchContent(key, quick, pg)
|
||||
|
||||
def playerContent(self, flag, id, vipFlags):
|
||||
"""获取播放内容"""
|
||||
result = {}
|
||||
|
||||
# 检查是否已经是直接的视频URL
|
||||
if self.isVideoFormat(id):
|
||||
result["parse"] = 0
|
||||
result["url"] = id
|
||||
result["playUrl"] = ""
|
||||
result["header"] = json.dumps(self.headers)
|
||||
return result
|
||||
|
||||
# 如果不是直接的视频URL,需要处理一下
|
||||
try:
|
||||
# 我们需要从ID中解析出剧ID和集索引
|
||||
if id.isdigit():
|
||||
# 如果是纯数字ID,说明是剧ID,我们需要获取详情并提取第一集
|
||||
video_id = id
|
||||
ep_index = 0 # 默认获取第一集
|
||||
elif '_' in id:
|
||||
# 如果ID包含下划线,格式是 video_id_episode_index
|
||||
parts = id.split('_')
|
||||
if len(parts) >= 2:
|
||||
video_id = parts[0] # 这是纯数字的视频ID
|
||||
ep_index = int(parts[1])
|
||||
else:
|
||||
video_id = id
|
||||
ep_index = 0
|
||||
else:
|
||||
# 假设id就是视频URL
|
||||
result["parse"] = 0
|
||||
result["url"] = id
|
||||
result["playUrl"] = ""
|
||||
result["header"] = json.dumps(self.headers)
|
||||
return result
|
||||
|
||||
# 获取详情数据,通过详情接口获取剧集列表
|
||||
# 确保只使用纯数字的视频ID作为theater_parent_id参数
|
||||
detail_url = f"{self.siteUrl}/v2/theater_parent/detail?theater_parent_id={video_id}"
|
||||
print(f"请求详情URL: {detail_url}")
|
||||
detail_response = self.fetchWithToken(detail_url)
|
||||
|
||||
if not detail_response or detail_response.status_code != 200:
|
||||
print("获取详情数据失败")
|
||||
return result
|
||||
|
||||
detail_json = detail_response.json()
|
||||
# 修改这里,使用与detailContent相同的条件判断
|
||||
if not(detail_json.get('code') == 0 or detail_json.get('code') == "ok" or detail_json.get('status') == 0):
|
||||
print(f"获取详情数据错误: {detail_json}")
|
||||
return result
|
||||
|
||||
# 获取剧集列表
|
||||
theaters = detail_json.get('data', {}).get('theaters', [])
|
||||
|
||||
if not theaters or ep_index >= len(theaters):
|
||||
print(f"未找到剧集或索引超出范围: {ep_index}")
|
||||
return result
|
||||
|
||||
# 获取指定索引的剧集
|
||||
episode = theaters[ep_index]
|
||||
video_url = episode.get('son_video_url', '')
|
||||
|
||||
if not video_url:
|
||||
print(f"未找到视频URL")
|
||||
return result
|
||||
|
||||
# 添加播放所需的headers
|
||||
play_headers = {
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
|
||||
"Referer": "http://qcapp.xingya.com.cn/"
|
||||
}
|
||||
|
||||
# 返回播放信息
|
||||
result["parse"] = 0
|
||||
result["url"] = video_url
|
||||
result["playUrl"] = ""
|
||||
result["header"] = json.dumps(play_headers)
|
||||
|
||||
except Exception as e:
|
||||
print(f"获取播放内容异常: {str(e)}")
|
||||
import traceback
|
||||
print(traceback.format_exc())
|
||||
|
||||
return result
|
||||
|
||||
def localProxy(self, param):
|
||||
"""本地代理处理,此处简单返回传入的参数"""
|
||||
return [200, "video/MP2T", {}, param]
|
76
vK.json
76
vK.json
|
@ -19,50 +19,49 @@
|
|||
|
||||
|
||||
{
|
||||
"key": "csp_aiqiyi",
|
||||
"key": "csp_aqy",
|
||||
"name": "🥝爱奇异视频",
|
||||
"type": 3,
|
||||
"api": "./libs/drpy2.min.js",
|
||||
"api": "csp_AuthIQIYI",
|
||||
"searchable": 1,
|
||||
"quickSearch": 1,
|
||||
"playerType": 2,
|
||||
"filterable": 1,
|
||||
"ext": "./js/爱奇艺X.js"
|
||||
"filterable": 1
|
||||
|
||||
},
|
||||
{
|
||||
"key": "csp_优酷",
|
||||
"key": "csp_uk",
|
||||
"name": "🛡️优酷视频",
|
||||
"type": 3,
|
||||
"api": "./libs/drpy2.min.js",
|
||||
"api": "csp_AuthYOUKU",
|
||||
"searchable": 1,
|
||||
"quickSearch": 1,
|
||||
"playerType": 2,
|
||||
"filterable": 1,
|
||||
"ext": "./js/优酷x.js"
|
||||
"filterable": 1
|
||||
|
||||
},
|
||||
{
|
||||
"key": "csp_tengyun",
|
||||
"key": "csp_tx",
|
||||
"name": "🐧腾讯视频",
|
||||
"type": 3,
|
||||
"api": "./libs/drpy2.min.js",
|
||||
"api": "csp_AuthQQ",
|
||||
"searchable": 1,
|
||||
"quickSearch": 1,
|
||||
"playerType": 2,
|
||||
"filterable": 1,
|
||||
"ext": "./js/tengxunX.js"
|
||||
"filterable": 1
|
||||
|
||||
},
|
||||
{
|
||||
"key": "csp_mgtv",
|
||||
"key": "csp_mg",
|
||||
"name": "🍋芒果视频",
|
||||
"type": 3,
|
||||
"api": "./libs/drpy2.min.js",
|
||||
"api": "csp_AuthMGTV",
|
||||
"searchable": 1,
|
||||
"quickSearch": 1,
|
||||
"playerType": 2,
|
||||
"filterable": 1,
|
||||
"ext": "./js/芒果x.js"
|
||||
},
|
||||
"filterable": 1
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
"key": "玩偶",
|
||||
|
@ -88,24 +87,23 @@
|
|||
"key": "csp_XYQHiker_民影视",
|
||||
"name": "♻️农民影视",
|
||||
"type": 3,
|
||||
"api": "csp_XYQHiker",
|
||||
"searchable": 1,
|
||||
"quickSearch": 1,
|
||||
"playerType": 2,
|
||||
"filterable": 1,
|
||||
"ext": "./XYQHiker/农民影视.json"
|
||||
"api":"csp_Wwys",
|
||||
"searchable":1,
|
||||
"quickSearch":1,
|
||||
"filterable":1,
|
||||
"ext":"https://wwgz.cn/"
|
||||
},
|
||||
|
||||
{
|
||||
"key": "金牌资源",
|
||||
"name": "🥇金牌影视",
|
||||
"type": 3,
|
||||
"api": "csp_Jpys",
|
||||
"api": "./py/金牌影视.py",
|
||||
"playerType": 2,
|
||||
"searchable": 1,
|
||||
"quickSearch": 1,
|
||||
"changeable": 1
|
||||
|
||||
"changeable": 1,
|
||||
"ext": {"site":"https://www.hkybqufgh.com,https://www.sizhengxt.com,https://0996zp.com,https://9zhoukj.com/,https://www.sizhengxt.com,https://www.tjrongze.com,https://www.jiabaide.cn,https://cqzuoer.com"}
|
||||
|
||||
},
|
||||
|
||||
|
@ -124,7 +122,7 @@
|
|||
},
|
||||
|
||||
{
|
||||
"key": "虎牙直播js",
|
||||
"key": "虎牙js",
|
||||
"name": "🐯虎牙直播",
|
||||
"type": 3,
|
||||
"api": "./libs/drpy2.min.js",
|
||||
|
@ -140,9 +138,9 @@
|
|||
"key": "csp_Bili",
|
||||
"name": "💌哔哩资源",
|
||||
"type": 3,
|
||||
"api": "csp_Bili",
|
||||
"api": "./libs/drpy2.min.js",
|
||||
"playerType": 2,
|
||||
"ext": "./Bili/Bili资源.json"
|
||||
"ext": "./js/我的哔哩.js?type=url¶ms=../Bili/Bili资源.json"
|
||||
},
|
||||
|
||||
|
||||
|
@ -155,23 +153,23 @@
|
|||
|
||||
|
||||
{
|
||||
"key": "星芽短剧",
|
||||
"key": "星芽",
|
||||
"name": "📡星芽短剧",
|
||||
"type": 3,
|
||||
"api": "https://bitbucket.org/249886372/ywj/raw/d204f5d2a47f631b43c07098ce52426e1e81fea5/drpy_libs/drpy2.min.js",
|
||||
"api": "csp_AppXY",
|
||||
"searchable": 1,
|
||||
"quickSearch": 1,
|
||||
"changeable": 1,
|
||||
"ext": "https://bitbucket.org/249886372/ywj/raw/a75f9b49dfc168b86bcc12ebfc8f2007f57f1ebe/js/星芽短剧.js"
|
||||
"changeable": 1
|
||||
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
"key": "csp_tqDJ",
|
||||
"name": "🎋甜圈短剧",
|
||||
"name": "🎋蓝莓短剧",
|
||||
"type": 3,
|
||||
"api": "./py/甜圈短剧.py",
|
||||
"api": "./py/蓝莓短剧.py",
|
||||
"searchable": 1,
|
||||
"quickSearch": 1,
|
||||
"filterable": 1
|
||||
|
@ -183,7 +181,7 @@
|
|||
|
||||
|
||||
{
|
||||
"key": "csp_瓜子",
|
||||
"key": "csp_gz",
|
||||
"name": "🌰瓜子影视",
|
||||
"type": 3,
|
||||
"api": "csp_Gz360",
|
||||
|
@ -199,7 +197,7 @@
|
|||
|
||||
|
||||
{
|
||||
"key": "meowtv_ffzy",
|
||||
"key": "meowtv_zy",
|
||||
"name": "🍒采集资源",
|
||||
"type": 3,
|
||||
"api": "./libs/drpy2.min.js",
|
||||
|
@ -214,7 +212,7 @@
|
|||
|
||||
|
||||
{
|
||||
"key": "csp_SuBaiBai",
|
||||
"key": "csp_yd",
|
||||
"name": "⏮️移动影视",
|
||||
"type": 3,
|
||||
"api": "csp_YDjisu",
|
||||
|
@ -226,8 +224,6 @@
|
|||
|
||||
|
||||
|
||||
{"key":"火火影视","name":"💡火火影视","type":3,"api":"csp_SaoHuo","playerType":2,"ext":"https://shdy5.us"},
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue