Compare commits

...

17 Commits

Author SHA1 Message Date
ziyongdaima df6a7f5bf8
Update vK.json 2025-07-13 15:10:23 +08:00
ziyongdaima 5e9d867841
Create 剧霸.py 2025-07-13 14:41:40 +08:00
ziyongdaima a5983df81d
Add files via upload 2025-07-13 14:26:05 +08:00
ziyongdaima aa6dc99092
Delete jar/XM.jar 2025-07-13 14:25:43 +08:00
ziyongdaima 464e7b951b
Add files via upload 2025-07-13 13:00:04 +08:00
ziyongdaima 28ac7c28ec
Delete jar/DMx.jar 2025-07-13 12:59:12 +08:00
ziyongdaima 1ccce5b6b5
Update vK.json 2025-07-12 20:56:47 +08:00
ziyongdaima 45460248b0
Rename 蓝猫短剧.py to 蓝莓短剧.py 2025-07-12 20:55:19 +08:00
ziyongdaima 426b9125c3
Update vK.json 2025-07-12 18:52:47 +08:00
ziyongdaima 4fc6321795
Update vK.json 2025-07-11 23:33:44 +08:00
ziyongdaima 5db4fd8202
Create 蓝猫短剧.py 2025-07-11 23:32:19 +08:00
ziyongdaima 4f42b8e498
Update vK.json 2025-07-03 00:13:40 +08:00
ziyongdaima cf39fa1244
Update vK.json 2025-07-03 00:02:21 +08:00
ziyongdaima e1580329db
Update vK.json 2025-07-02 23:50:54 +08:00
ziyongdaima 9cd1ed91ab
Update 我的哔哩.js 2025-07-02 23:40:52 +08:00
ziyongdaima f06ce4b947
Create 我的哔哩.js 2025-07-02 23:20:23 +08:00
ziyongdaima 2a70b6c49d
Create 若惜追剧.py 2025-07-02 23:05:36 +08:00
7 changed files with 1448 additions and 42 deletions

Binary file not shown.

Binary file not shown.

452
js/我的哔哩.js Normal file
View File

@ -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&params=../json/哔哩教育.json@哔哩教育[]
* 写法 ext: ./我的哔哩.js?type=url&params=./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&params=../json/哔哩教育.json@哔哩教育[官]',
// params: '?render=1&type=url&params=../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
`,
}

315
py/剧霸.py Normal file
View File

@ -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()

255
py/若惜追剧.py Normal file
View File

@ -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()

388
py/蓝莓短剧.py Normal file
View File

@ -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
View File

@ -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&params=../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"},