mirror of https://github.com/qist/tvbox.git
Compare commits
5 Commits
3aa7a57bb8
...
31111d5e7e
| Author | SHA1 | Date |
|---|---|---|
|
|
31111d5e7e | 1 day ago |
|
|
68666ec603 | 1 day ago |
|
|
72d7499b09 | 1 day ago |
|
|
f5df84cd9a | 1 day ago |
|
|
d95555f1b1 | 1 day ago |
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"首页": "0",
|
||||||
|
// "播放链接前缀": "https://www.gyf.lol",
|
||||||
|
"数组": "data\":{\"tit&&s_log",
|
||||||
|
"图片": "https://p0.ssl.cdn.btime.com/+ssl.cdn.btime.com/&&\"",
|
||||||
|
"副标题": "le\":\"&&\"",
|
||||||
|
"标题": "text\":\"&&\"",
|
||||||
|
"链接": "item.btime.com/&&\"",
|
||||||
|
"链接前缀": "https://app.api.btime.com/video/play?callback=jQuery36009651202523243325_1747927554988&id=",
|
||||||
|
// "线路标题": "</i>&&<span",
|
||||||
|
"播放数组": "video_streams&&audio_streams",
|
||||||
|
"播放列表": "stream&&duration",
|
||||||
|
"播放标题": "stream_vbt\":\"&&\"",
|
||||||
|
"播放链接": "url\":\"&&\"",
|
||||||
|
"分类url": "https://pc.api.btime.com/btimeweb/infoFlow?callback=jQuery36002244958011106073_1747920210579&list_id={cateId}&refresh={catePg}&count=2000&expands=pageinfo&_=1747920210580",
|
||||||
|
"分类": "2025$btv_08da67cea600bf3c78973427bfaba12d_s0_2025#2024$btv_08da67cea600bf3c78973427bfaba12d_s0_2024#2023$btv_08da67cea600bf3c78973427bfaba12d_s0_2023#2022$btv_08da67cea600bf3c78973427bfaba12d_s0_2022#2021$btv_08da67cea600bf3c78973427bfaba12d_s0_2021#2020$btv_08da67cea600bf3c78973427bfaba12d_s0_2020#2019$btv_08da67cea600bf3c78973427bfaba12d_s0_2019#2018$btv_08da67cea600bf3c78973427bfaba12d_s0_2018"
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"请求头": "User-Agent$MOBILE_UA",
|
||||||
|
"编码": "UTF-8",
|
||||||
|
"主页url": "https://dora.xiaoxinbk.com/",
|
||||||
|
"数组": "class=\"card-img-bili\"&&</a>",
|
||||||
|
"标题": "alt=\"&&\"",
|
||||||
|
"图片": "data-url=\"&&\"",
|
||||||
|
"链接": "href=\"&&\"",
|
||||||
|
"线路标题": "mt-0\">&&</h3>",
|
||||||
|
"播放数组": "class=\"card-body button-list\"&&</div>",
|
||||||
|
"播放列表": "<a&&a>",
|
||||||
|
"播放链接": "href=\"&&\"",
|
||||||
|
"播放标题": ">&&</",
|
||||||
|
"分类url": "https://www.dora-video.cn/search/sy/?niandai={year}&cat={class}&tag={cateId}&gaojijiansuo=1&zhuangtai={by}",
|
||||||
|
"分类": "全部$0#动画$20#剧场版$21#MV$22#预告片$23#直播$27#同人$25#民间$26#目录$28#中篇$30",
|
||||||
|
"剧情": "哆啦A梦新番$1#新哆啦A梦 台湾配音$2#哆啦A梦剧场版$3#最新预告片$6#剧场版集合$7#哆啦A梦MV$4#哆啦A梦七小子集合$11#生日特别篇$19#哆啦A梦生日特别篇$12#哆啦美生日特别篇$14#大雄生日特别篇$13#静香生日特别篇$15#胖虎生日特别篇$16#小夫生日特别篇$17#24小时直播$24#哆啦A梦中篇附映$29#哆啦A梦大山版 - 哆啦a梦1979版国语旧版 - 旧版$31#哆啦A梦大山修复&补档区$32#藤子·F·不二雄【其它动画】$33",
|
||||||
|
"排序": "全部$0#完结$2#连载$1#待定$-1"
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"线路数组": "<a data-toggle=\"tab\"&&/a>",
|
||||||
|
"线路标题": ">&&<",
|
||||||
|
"分类url": "http://dyxz.tv/list/{cateId}_{catePg}.html",
|
||||||
|
"分类": "电影$1#电视剧$2#动漫$3#综艺$4"
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"简介": "jianjie\">&&</p>",
|
||||||
|
"副标题": "zhuangtai\">&&</div>",
|
||||||
|
"数组": "u-movie\">&&</article>",
|
||||||
|
"线路标题": "ctitle\">&&在线播放",
|
||||||
|
"播放数组": "vlink&&</div>",
|
||||||
|
"分类url": "https://www.yinghuadongman.com.cn/sxvodshow/{cateId}-{year}/page/{catePg}/;;d0",
|
||||||
|
"分类": "国漫$2#日漫$1#美漫$3"
|
||||||
|
}
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"请求头": "User-Agent$MOBILE_UA",
|
||||||
|
"编码": "UTF-8",
|
||||||
|
"分类": "电影$1#电视剧$2#综艺$3#动漫$4",
|
||||||
|
"类型": "动作片$6#喜剧片$7#爱情片$8#科幻片$9#奇幻片$10#恐怖片$11#剧情片$12#战争片$20#动画片$26#悬疑片$22#冒险片$23#犯罪片$24#惊悚片$45#歌舞片$46#灾难片$47#网络片$48||国产剧$13#港台剧$14#日剧$15#韩剧$33#欧美剧$16#泰剧$34#新马剧$35#其他剧$25||内地综艺$27#港台综艺$28#日本综艺$29#韩国综艺$36#欧美综艺$30#新马泰综艺$37#其他综艺$38||国产动漫$31#日本动漫$32#韩国动漫$39#港台动漫$40#新马泰动漫$41#欧美动漫$42#其他动漫$43",
|
||||||
|
"分类url": "https://www.ylys.tv/vodshow/{cateId}-{area}-{by}-{class}-{lang}-{letter}---{catePg}---{year}.html"
|
||||||
|
}
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"站名": "雪花",
|
||||||
|
"主页url": "https://n3300.com/",
|
||||||
|
"分类url": "https://n3300.com/ekOtxEBafND5Chtml/{cateId}/list_{catePg}.html",
|
||||||
|
"分类": "喜剧片$xiju#爱情片$aiqing#科幻片$kehuan#剧情片$juqing#悬疑片$xuannian#战争片$zhanzheng#恐怖片$kongbu#灾难片$zainan#连续剧$lianxuju#动漫$dongman#综艺片$zongyijiemu",
|
||||||
|
"二次截取": "",
|
||||||
|
"数组": "默认--pic\">&&</li>||首页--col-sm&&</li>",
|
||||||
|
"图片": "src=\"&&\"",
|
||||||
|
"标题": "FF0000'>&&<",
|
||||||
|
"链接": "href=\"&&\"",
|
||||||
|
"副标题": "4K",
|
||||||
|
"影片年代": "年 代&&<br>",
|
||||||
|
"影片地区": "产 地&&<br>",
|
||||||
|
"影片类型": "类 别&&<br>",
|
||||||
|
"状态": "片 长 &&<br>",
|
||||||
|
"导演": "导 演&&<br>",
|
||||||
|
"主演": "主 演&&<br>",
|
||||||
|
"简介": "简 介&&</div>",
|
||||||
|
"线路数组": "下载地址列表&&<h3>",
|
||||||
|
"线路标题": "磁力",
|
||||||
|
"播放二次截取": "本站专用电影&&</body",
|
||||||
|
"播放数组": "<ul>&&</div>",
|
||||||
|
"播放列表": "<a&&/a>",
|
||||||
|
"播放链接": "href=\"&&\\&",
|
||||||
|
"播放标题": "dn=&&<",
|
||||||
|
"搜索url": "https://n3300.com/plus/search.php?kwtype=0&keyword={wd}"
|
||||||
|
}
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"请求头": "User-Agent$MOBILE_UA",
|
||||||
|
"编码": "UTF-8",
|
||||||
|
"分类url": "https://v.aiwule.com/vodshow/{cateId}-{area}-{by}-{class}-{lang}-{letter}---{catePg}---{year}.html",
|
||||||
|
"分类": "电影$20#电视剧$21#动漫$23#综艺$22#短剧$47",
|
||||||
|
"简介": "简介:&&"
|
||||||
|
}
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
{
|
||||||
|
"规则名": "动漫巴士",
|
||||||
|
"规则作者": "",
|
||||||
|
"请求头参数": "User-Agent$MOBILE_UA#Accept$text/html#accept-language$zh-CN,zh;q=0.8",
|
||||||
|
"网页编码格式": "UTF-8",
|
||||||
|
"图片是否需要代理": "0",
|
||||||
|
"是否开启获取首页数据": "1",
|
||||||
|
"首页推荐链接": "https://dm84.net",
|
||||||
|
"首页列表数组规则": "body&&.v_list",
|
||||||
|
"首页片单列表数组规则": "li",
|
||||||
|
"首页片单是否Jsoup写法": "1",
|
||||||
|
"分类起始页码": "1",
|
||||||
|
"分类链接": "https://dm84.net/list-{cateId}-{catePg}.html[firstPage=https://dm84.net/list-{cateId}.html]",
|
||||||
|
"分类名称": "国产动漫&日本动漫&欧美动漫&动漫电影",
|
||||||
|
"分类名称替换词": "1&2&3&4",
|
||||||
|
"筛选数据": {},
|
||||||
|
//"筛选数据": "ext",
|
||||||
|
//{cateId}
|
||||||
|
"筛选子分类名称": "",
|
||||||
|
"筛选子分类替换词": "",
|
||||||
|
//{class}
|
||||||
|
"筛选类型名称": "",
|
||||||
|
"筛选类型替换词": "*",
|
||||||
|
//{area}
|
||||||
|
"筛选地区名称": "",
|
||||||
|
"筛选地区替换词": "*",
|
||||||
|
//{year}
|
||||||
|
"筛选年份名称": "",
|
||||||
|
"筛选年份替换词": "*",
|
||||||
|
//{lang}
|
||||||
|
"筛选语言名称": "",
|
||||||
|
"筛选语言替换词": "*",
|
||||||
|
//{by}
|
||||||
|
"筛选排序名称": "时间&人气&评分",
|
||||||
|
"筛选排序替换词": "time&hits&score",
|
||||||
|
"分类截取模式": "1",
|
||||||
|
"分类列表数组规则": ".v_list&&li",
|
||||||
|
"分类片单是否Jsoup写法": "1",
|
||||||
|
"分类片单标题": "a&&title!在线观看",
|
||||||
|
"分类片单链接": "a&&href",
|
||||||
|
"分类片单图片": ".lazy&&data-bg",
|
||||||
|
"分类片单副标题": ".desc&&Text",
|
||||||
|
"分类片单链接加前缀": "https://dm84.net",
|
||||||
|
"分类片单链接加后缀": "",
|
||||||
|
"搜索请求头参数": "User-Agent$手机#Accept$text/html#accept-language$zh-CN,zh;q=0.8",
|
||||||
|
"搜索链接": "https://dm84.net/s-{wd}---------{SearchPg}.html",
|
||||||
|
"POST请求数据": "",
|
||||||
|
"搜索截取模式": "1",
|
||||||
|
"搜索列表数组规则": ".v_list&&li",
|
||||||
|
"搜索片单是否Jsoup写法": "1",
|
||||||
|
"搜索片单图片": ".lazy&&data-bg",
|
||||||
|
"搜索片单标题": "a&&title!在线观看",
|
||||||
|
"搜索片单链接": "a&&href",
|
||||||
|
"搜索片单副标题": ".desc&&Text",
|
||||||
|
"搜索片单链接加前缀": "https://dm84.net",
|
||||||
|
"搜索片单链接加后缀": "",
|
||||||
|
"链接是否直接播放": "0",
|
||||||
|
"直接播放链接加前缀": "",
|
||||||
|
"直接播放链接加后缀": "",
|
||||||
|
"直接播放直链视频请求头": "",
|
||||||
|
"详情是否Jsoup写法": "1",
|
||||||
|
"类型详情": "",
|
||||||
|
"年代详情": "",
|
||||||
|
"地区详情": "",
|
||||||
|
"演员详情": "",
|
||||||
|
"简介详情": ".intro&&-p&&Text",
|
||||||
|
"线路列表数组规则": ".play_from&&li",
|
||||||
|
"线路标题": "Text",
|
||||||
|
"播放列表数组规则": ".tab_content&&.play_list",
|
||||||
|
"选集列表数组规则": "a",
|
||||||
|
"选集标题链接是否Jsoup写法": "1",
|
||||||
|
"选集标题": "a&&Text",
|
||||||
|
"选集链接": "a&&href",
|
||||||
|
"是否反转选集序列": "1",
|
||||||
|
"选集链接加前缀": "https://dm84.net",
|
||||||
|
"选集链接加后缀": "",
|
||||||
|
"分析MacPlayer": "0",
|
||||||
|
"是否开启手动嗅探": "0",
|
||||||
|
"手动嗅探视频链接关键词": ".mp4#.m3u8#.flv#video/tos",
|
||||||
|
"手动嗅探视频链接过滤词": ".html#=http"
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
@ -0,0 +1,169 @@
|
|||||||
|
// 注意事项:此源仅支持 影视TV 及 爱佬版tvbox最新版
|
||||||
|
// 注意事项:此源仅支持 影视TV 及 爱佬版tvbox最新版
|
||||||
|
// 注意事项:此源仅支持 影视TV 及 爱佬版tvbox最新版
|
||||||
|
// 3个set-Cookie
|
||||||
|
|
||||||
|
var rule = {
|
||||||
|
title:'Anime1动畫',
|
||||||
|
host:'https://anime1.me',
|
||||||
|
url: '/fyclass',
|
||||||
|
detailUrl:'/?cat=fyid',
|
||||||
|
searchUrl: '/page/fypage?s=**',
|
||||||
|
searchable:2,
|
||||||
|
quickSearch:0,
|
||||||
|
headers:{'User-Agent': 'PC_UA'},
|
||||||
|
timeout:5000,
|
||||||
|
class_name:'連載中&2025&2024&2023&2022&2021&2020&2019&2018&更早',
|
||||||
|
class_url:'連載中&2025&2024&2023&2022&2021&2020&2019&2018&2017',
|
||||||
|
play_parse:true,
|
||||||
|
lazy:`js:
|
||||||
|
var apiurl = 'https://v.anime1.me/api';
|
||||||
|
var html = request(apiurl, {
|
||||||
|
headers: {
|
||||||
|
'Referer': HOST,
|
||||||
|
},
|
||||||
|
body: 'd=' + input,
|
||||||
|
method: 'POST',
|
||||||
|
withHeaders: true
|
||||||
|
});
|
||||||
|
let json = JSON.parse(html);
|
||||||
|
print(json);
|
||||||
|
log(Object.keys(json));
|
||||||
|
let setCk = Object.keys(json).filter(it => it.toLowerCase() === "set-cookie");
|
||||||
|
let cookie = setCk ? json[setCk] : "";
|
||||||
|
// 3个set-Cookie
|
||||||
|
if (Array.isArray(cookie)) {
|
||||||
|
cookie = cookie.join(';');
|
||||||
|
}
|
||||||
|
cookie = cookie.split(';').filter(function(it) {
|
||||||
|
return ['e', 'p', 'h'].includes(it.split('=')[0])
|
||||||
|
}).join(';');
|
||||||
|
log(cookie);
|
||||||
|
var purl = JSON.parse(json.body).s[0].src;
|
||||||
|
if (purl.startsWith('/')) {
|
||||||
|
purl = 'https:' + purl
|
||||||
|
}
|
||||||
|
input = {
|
||||||
|
jx: 0,
|
||||||
|
url: purl,
|
||||||
|
parse: 0,
|
||||||
|
header: JSON.stringify({
|
||||||
|
'referer': HOST,
|
||||||
|
'Cookie': cookie,
|
||||||
|
'user-agent': PC_UA
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
limit:6,
|
||||||
|
推荐: `js:
|
||||||
|
var d = [];
|
||||||
|
function stripHtmlTag(src) {
|
||||||
|
return src.replace(/<\\/?[^>]+(>|$)/g, '').replace(/&.{1,5};/g, '').replace(/\\s{2,}/g, ' ');
|
||||||
|
}
|
||||||
|
var timestamp = new Date().getTime();
|
||||||
|
var json = request('https://d1zquzjgwo9yb.cloudfront.net/?_=' + timestamp);
|
||||||
|
var list = JSON.parse(json);
|
||||||
|
let playKeys = Object.values(list).filter(function(x) {
|
||||||
|
return x[2].includes('連載中');
|
||||||
|
});
|
||||||
|
playKeys.forEach(function(it) {
|
||||||
|
d.push({
|
||||||
|
title: stripHtmlTag(it[1]),
|
||||||
|
img: 'https://sta.anicdn.com/playerImg/8.jpg',
|
||||||
|
desc: it[2],
|
||||||
|
url: it[0],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
setResult(d);
|
||||||
|
`,
|
||||||
|
一级: `js:
|
||||||
|
var d = [];
|
||||||
|
function stripHtmlTag(src) {
|
||||||
|
return src.replace(/<\\/?[^>]+(>|$)/g, '').replace(/&.{1,5};/g, '').replace(/\\s{2,}/g, ' ');
|
||||||
|
}
|
||||||
|
var timestamp = new Date().getTime();
|
||||||
|
var json = request('https://d1zquzjgwo9yb.cloudfront.net/?_=' + timestamp);
|
||||||
|
var list = JSON.parse(json);
|
||||||
|
let playKeys = Object.values(list).filter(function(x) {
|
||||||
|
if (MY_CATE === '連載中') return x[2].includes(MY_CATE);
|
||||||
|
else if (MY_CATE === '2017') return x[3] <= MY_CATE;
|
||||||
|
else return x[3] == MY_CATE;
|
||||||
|
});
|
||||||
|
playKeys.forEach(function(it) {
|
||||||
|
d.push({
|
||||||
|
title: stripHtmlTag(it[1]),
|
||||||
|
img: 'https://sta.anicdn.com/playerImg/8.jpg',
|
||||||
|
desc: it[2],
|
||||||
|
url: it[0],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
setResult(d);
|
||||||
|
`,
|
||||||
|
二级: `js:
|
||||||
|
pdfh = jsp.pdfh; pdfa = jsp.pdfa; pd = jsp.pd;
|
||||||
|
var html = request(input);
|
||||||
|
var timestamp = new Date().getTime();
|
||||||
|
var json = request('https://d1zquzjgwo9yb.cloudfront.net/?_=' + timestamp);
|
||||||
|
var list = JSON.parse(json);
|
||||||
|
var vid = input.split('=')[1];
|
||||||
|
let playKeys = Object.values(list).find(function(x) {
|
||||||
|
return x[0] === parseInt(vid);
|
||||||
|
});
|
||||||
|
VOD = {
|
||||||
|
vod_pic: 'https://sta.anicdn.com/playerImg/8.jpg',
|
||||||
|
vod_id: playKeys[0],
|
||||||
|
vod_name: playKeys[1],
|
||||||
|
vod_content: playKeys[2],
|
||||||
|
vod_year: playKeys[3],
|
||||||
|
type_name: playKeys[4],
|
||||||
|
vod_actor: playKeys[5],
|
||||||
|
};
|
||||||
|
var pageurl = pd(html, '.cat-links&&a&&href');
|
||||||
|
var pagenum = 1;
|
||||||
|
let vod_tab_list = [];
|
||||||
|
let vlist = [];
|
||||||
|
for (let p = 1; p < parseInt(pagenum) + 1; p++) {
|
||||||
|
let phtml = request(pageurl + '/page/' + pagenum);
|
||||||
|
let new_vod_list = [];
|
||||||
|
let vodList = [];
|
||||||
|
vodList = pdfa(phtml, '.site-main&&article');
|
||||||
|
for (let i = 0; i < vodList.length; i++) {
|
||||||
|
let it = vodList[i];
|
||||||
|
let ptitle = pdfh(it, '.entry-title&&Text').replace(/\\[(.*)\\]/, '$1');
|
||||||
|
let purl = pd(it, '.video-js&&data-apireq');
|
||||||
|
new_vod_list.push(ptitle + '$' + purl);
|
||||||
|
}
|
||||||
|
vlist = vlist.concat(new_vod_list);
|
||||||
|
try {
|
||||||
|
pagenum = pd(phtml, '.nav-previous&&a&&href').split('/page/')[1];
|
||||||
|
} catch(e) {}
|
||||||
|
}
|
||||||
|
let vlist2 = vlist.reverse().join("#");
|
||||||
|
vod_tab_list.push(vlist2);
|
||||||
|
VOD.vod_play_from = '在线播放';
|
||||||
|
VOD.vod_play_url = vod_tab_list.join("$$$");
|
||||||
|
`,
|
||||||
|
搜索: `js:
|
||||||
|
var d = [];
|
||||||
|
function stripHtmlTag(src) {
|
||||||
|
return src.replace(/<\\/?[^>]+(>|$)/g, '').replace(/&.{1,5};/g, '').replace(/\\s{2,}/g, ' ');
|
||||||
|
}
|
||||||
|
var timestamp = new Date().getTime();
|
||||||
|
var json = request('https://d1zquzjgwo9yb.cloudfront.net/?_=' + timestamp);
|
||||||
|
var list = JSON.parse(json);
|
||||||
|
var wd = input.split('=')[1];
|
||||||
|
let playKeys = Object.values(list).filter(function(x) {
|
||||||
|
return x[1].includes(wd);
|
||||||
|
});
|
||||||
|
log(playKeys);
|
||||||
|
playKeys.forEach(function(it) {
|
||||||
|
d.push({
|
||||||
|
title: stripHtmlTag(it[1]),
|
||||||
|
img: 'https://sta.anicdn.com/playerImg/8.jpg',
|
||||||
|
desc: it[2],
|
||||||
|
url: it[0],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
setResult(d);
|
||||||
|
`,
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,827 @@
|
|||||||
|
muban.短视2.二级.img = '.detail-pic&&img&&data-src';
|
||||||
|
var rule = {
|
||||||
|
title: '爱弹幕',
|
||||||
|
模板: '短视2',
|
||||||
|
host: 'https://bgm.girigirilove.com',
|
||||||
|
homeUrl: '/map/',
|
||||||
|
// url:'/show/fyclass--------fypage---/'
|
||||||
|
url: '/show/fyclassfyfilter/',
|
||||||
|
filterable: 1, //是否启用分类筛选,
|
||||||
|
filter_url: '-{{fl.area}}-{{fl.by}}-{{fl.class}}-{{fl.lang}}-{{fl.letter}}---fypage---{{fl.year}}',
|
||||||
|
filter: {
|
||||||
|
"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": "乙女"
|
||||||
|
}, {
|
||||||
|
"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": "2026",
|
||||||
|
"v": "2026"
|
||||||
|
}, {
|
||||||
|
"n": "2025",
|
||||||
|
"v": "2025"
|
||||||
|
}, {
|
||||||
|
"n": "2024",
|
||||||
|
"v": "2024"
|
||||||
|
}, {
|
||||||
|
"n": "2023",
|
||||||
|
"v": "2023"
|
||||||
|
}, {
|
||||||
|
"n": "2022",
|
||||||
|
"v": "2022"
|
||||||
|
}, {
|
||||||
|
"n": "2021",
|
||||||
|
"v": "2021"
|
||||||
|
}, {
|
||||||
|
"n": "2020",
|
||||||
|
"v": "2020"
|
||||||
|
}, {
|
||||||
|
"n": "2019",
|
||||||
|
"v": "2019"
|
||||||
|
}, {
|
||||||
|
"n": "2018",
|
||||||
|
"v": "2018"
|
||||||
|
}, {
|
||||||
|
"n": "2017",
|
||||||
|
"v": "2017"
|
||||||
|
}, {
|
||||||
|
"n": "2016",
|
||||||
|
"v": "2016"
|
||||||
|
}, {
|
||||||
|
"n": "2015",
|
||||||
|
"v": "2015"
|
||||||
|
}, {
|
||||||
|
"n": "2014",
|
||||||
|
"v": "2014"
|
||||||
|
}, {
|
||||||
|
"n": "2013",
|
||||||
|
"v": "2013"
|
||||||
|
}, {
|
||||||
|
"n": "2012",
|
||||||
|
"v": "2012"
|
||||||
|
}, {
|
||||||
|
"n": "2011",
|
||||||
|
"v": "2011"
|
||||||
|
}, {
|
||||||
|
"n": "2010",
|
||||||
|
"v": "2010"
|
||||||
|
}, {
|
||||||
|
"n": "2009",
|
||||||
|
"v": "2009"
|
||||||
|
}, {
|
||||||
|
"n": "2008",
|
||||||
|
"v": "2008"
|
||||||
|
}, {
|
||||||
|
"n": "2007",
|
||||||
|
"v": "2007"
|
||||||
|
}, {
|
||||||
|
"n": "2006",
|
||||||
|
"v": "2006"
|
||||||
|
}, {
|
||||||
|
"n": "2005",
|
||||||
|
"v": "2005"
|
||||||
|
}, {
|
||||||
|
"n": "2004",
|
||||||
|
"v": "2004"
|
||||||
|
}, {
|
||||||
|
"n": "2003",
|
||||||
|
"v": "2003"
|
||||||
|
}, {
|
||||||
|
"n": "2002",
|
||||||
|
"v": "2002"
|
||||||
|
}, {
|
||||||
|
"n": "2001",
|
||||||
|
"v": "2001"
|
||||||
|
}, {
|
||||||
|
"n": "2000",
|
||||||
|
"v": "2000"
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
"key": "lang",
|
||||||
|
"name": "语言",
|
||||||
|
"value": [{
|
||||||
|
"n": "全部",
|
||||||
|
"v": ""
|
||||||
|
}, {
|
||||||
|
"n": "日语",
|
||||||
|
"v": "日语"
|
||||||
|
}, {
|
||||||
|
"n": "国语",
|
||||||
|
"v": "国语"
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
"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": "热血"
|
||||||
|
}, {
|
||||||
|
"n": "运动",
|
||||||
|
"v": "运动"
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
"key": "area",
|
||||||
|
"name": "地区",
|
||||||
|
"value": [{
|
||||||
|
"n": "全部",
|
||||||
|
"v": ""
|
||||||
|
}, {
|
||||||
|
"n": "内地",
|
||||||
|
"v": "内地"
|
||||||
|
}, {
|
||||||
|
"n": "港台",
|
||||||
|
"v": "港台"
|
||||||
|
}, {
|
||||||
|
"n": "日韩",
|
||||||
|
"v": "日韩"
|
||||||
|
}, {
|
||||||
|
"n": "欧美",
|
||||||
|
"v": "欧美"
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
"key": "year",
|
||||||
|
"name": "年份",
|
||||||
|
"value": [{
|
||||||
|
"n": "全部",
|
||||||
|
"v": ""
|
||||||
|
}, {
|
||||||
|
"n": "2026",
|
||||||
|
"v": "2026"
|
||||||
|
}, {
|
||||||
|
"n": "2025",
|
||||||
|
"v": "2025"
|
||||||
|
}, {
|
||||||
|
"n": "2024",
|
||||||
|
"v": "2024"
|
||||||
|
}, {
|
||||||
|
"n": "2023",
|
||||||
|
"v": "2023"
|
||||||
|
}, {
|
||||||
|
"n": "2022",
|
||||||
|
"v": "2022"
|
||||||
|
}, {
|
||||||
|
"n": "2021",
|
||||||
|
"v": "2021"
|
||||||
|
}, {
|
||||||
|
"n": "2020",
|
||||||
|
"v": "2020"
|
||||||
|
}, {
|
||||||
|
"n": "2019",
|
||||||
|
"v": "2019"
|
||||||
|
}, {
|
||||||
|
"n": "2018",
|
||||||
|
"v": "2018"
|
||||||
|
}, {
|
||||||
|
"n": "2017",
|
||||||
|
"v": "2017"
|
||||||
|
}, {
|
||||||
|
"n": "2016",
|
||||||
|
"v": "2016"
|
||||||
|
}, {
|
||||||
|
"n": "2015",
|
||||||
|
"v": "2015"
|
||||||
|
}, {
|
||||||
|
"n": "2014",
|
||||||
|
"v": "2014"
|
||||||
|
}, {
|
||||||
|
"n": "2013",
|
||||||
|
"v": "2013"
|
||||||
|
}, {
|
||||||
|
"n": "2012",
|
||||||
|
"v": "2012"
|
||||||
|
}, {
|
||||||
|
"n": "2011",
|
||||||
|
"v": "2011"
|
||||||
|
}, {
|
||||||
|
"n": "2010",
|
||||||
|
"v": "2010"
|
||||||
|
}, {
|
||||||
|
"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"
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
"key": "lang",
|
||||||
|
"name": "语言",
|
||||||
|
"value": [{
|
||||||
|
"n": "全部",
|
||||||
|
"v": ""
|
||||||
|
}, {
|
||||||
|
"n": "国语",
|
||||||
|
"v": "国语"
|
||||||
|
}, {
|
||||||
|
"n": "英语",
|
||||||
|
"v": "英语"
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
"key": "by",
|
||||||
|
"name": "排序",
|
||||||
|
"value": [{
|
||||||
|
"n": "最新",
|
||||||
|
"v": "time"
|
||||||
|
}, {
|
||||||
|
"n": "最热",
|
||||||
|
"v": "hits"
|
||||||
|
}, {
|
||||||
|
"n": "评分",
|
||||||
|
"v": "score"
|
||||||
|
}]
|
||||||
|
}],
|
||||||
|
"21": [{
|
||||||
|
"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": "爱抖露"
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
"key": "year",
|
||||||
|
"name": "年份",
|
||||||
|
"value": [{
|
||||||
|
"n": "全部",
|
||||||
|
"v": ""
|
||||||
|
}, {
|
||||||
|
"n": "2026",
|
||||||
|
"v": "2026"
|
||||||
|
}, {
|
||||||
|
"n": "2025",
|
||||||
|
"v": "2025"
|
||||||
|
}, {
|
||||||
|
"n": "2024",
|
||||||
|
"v": "2024"
|
||||||
|
}, {
|
||||||
|
"n": "2023",
|
||||||
|
"v": "2023"
|
||||||
|
}, {
|
||||||
|
"n": "2022",
|
||||||
|
"v": "2022"
|
||||||
|
}, {
|
||||||
|
"n": "2021",
|
||||||
|
"v": "2021"
|
||||||
|
}, {
|
||||||
|
"n": "2020",
|
||||||
|
"v": "2020"
|
||||||
|
}, {
|
||||||
|
"n": "2019",
|
||||||
|
"v": "2019"
|
||||||
|
}, {
|
||||||
|
"n": "2018",
|
||||||
|
"v": "2018"
|
||||||
|
}, {
|
||||||
|
"n": "2017",
|
||||||
|
"v": "2017"
|
||||||
|
}, {
|
||||||
|
"n": "2016",
|
||||||
|
"v": "2016"
|
||||||
|
}, {
|
||||||
|
"n": "2015",
|
||||||
|
"v": "2015"
|
||||||
|
}, {
|
||||||
|
"n": "2014",
|
||||||
|
"v": "2014"
|
||||||
|
}, {
|
||||||
|
"n": "2013",
|
||||||
|
"v": "2013"
|
||||||
|
}, {
|
||||||
|
"n": "2012",
|
||||||
|
"v": "2012"
|
||||||
|
}, {
|
||||||
|
"n": "2011",
|
||||||
|
"v": "2011"
|
||||||
|
}, {
|
||||||
|
"n": "2010",
|
||||||
|
"v": "2010"
|
||||||
|
}, {
|
||||||
|
"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"
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
"key": "lang",
|
||||||
|
"name": "语言",
|
||||||
|
"value": [{
|
||||||
|
"n": "全部",
|
||||||
|
"v": ""
|
||||||
|
}, {
|
||||||
|
"n": "日语",
|
||||||
|
"v": "日语"
|
||||||
|
}, {
|
||||||
|
"n": "中文",
|
||||||
|
"v": "中文"
|
||||||
|
}, {
|
||||||
|
"n": "英语",
|
||||||
|
"v": "英语"
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
"key": "by",
|
||||||
|
"name": "排序",
|
||||||
|
"value": [{
|
||||||
|
"n": "最新",
|
||||||
|
"v": "time"
|
||||||
|
}, {
|
||||||
|
"n": "最热",
|
||||||
|
"v": "hits"
|
||||||
|
}, {
|
||||||
|
"n": "评分",
|
||||||
|
"v": "score"
|
||||||
|
}]
|
||||||
|
}],
|
||||||
|
"20": [{
|
||||||
|
"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": "烧脑"
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
"key": "area",
|
||||||
|
"name": "地区",
|
||||||
|
"value": [{
|
||||||
|
"n": "全部",
|
||||||
|
"v": ""
|
||||||
|
}, {
|
||||||
|
"n": "日本",
|
||||||
|
"v": "日本"
|
||||||
|
}, {
|
||||||
|
"n": "欧美",
|
||||||
|
"v": "欧美"
|
||||||
|
}, {
|
||||||
|
"n": "泰国",
|
||||||
|
"v": "泰国"
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
"key": "year",
|
||||||
|
"name": "年份",
|
||||||
|
"value": [{
|
||||||
|
"n": "全部",
|
||||||
|
"v": ""
|
||||||
|
}, {
|
||||||
|
"n": "2026",
|
||||||
|
"v": "2026"
|
||||||
|
}, {
|
||||||
|
"n": "2025",
|
||||||
|
"v": "2025"
|
||||||
|
}, {
|
||||||
|
"n": "2024",
|
||||||
|
"v": "2024"
|
||||||
|
}, {
|
||||||
|
"n": "2023",
|
||||||
|
"v": "2023"
|
||||||
|
}, {
|
||||||
|
"n": "2022",
|
||||||
|
"v": "2022"
|
||||||
|
}, {
|
||||||
|
"n": "2021",
|
||||||
|
"v": "2021"
|
||||||
|
}, {
|
||||||
|
"n": "2020",
|
||||||
|
"v": "2020"
|
||||||
|
}, {
|
||||||
|
"n": "2019",
|
||||||
|
"v": "2019"
|
||||||
|
}, {
|
||||||
|
"n": "2018",
|
||||||
|
"v": "2018"
|
||||||
|
}, {
|
||||||
|
"n": "2017",
|
||||||
|
"v": "2017"
|
||||||
|
}, {
|
||||||
|
"n": "2016",
|
||||||
|
"v": "2016"
|
||||||
|
}, {
|
||||||
|
"n": "2015",
|
||||||
|
"v": "2015"
|
||||||
|
}, {
|
||||||
|
"n": "2014",
|
||||||
|
"v": "2014"
|
||||||
|
}, {
|
||||||
|
"n": "2013",
|
||||||
|
"v": "2013"
|
||||||
|
}, {
|
||||||
|
"n": "2012",
|
||||||
|
"v": "2012"
|
||||||
|
}, {
|
||||||
|
"n": "2011",
|
||||||
|
"v": "2011"
|
||||||
|
}, {
|
||||||
|
"n": "2010",
|
||||||
|
"v": "2010"
|
||||||
|
}, {
|
||||||
|
"n": "2009",
|
||||||
|
"v": "2009"
|
||||||
|
}, {
|
||||||
|
"n": "2008",
|
||||||
|
"v": "2008"
|
||||||
|
}, {
|
||||||
|
"n": "2007",
|
||||||
|
"v": "2007"
|
||||||
|
}, {
|
||||||
|
"n": "2006",
|
||||||
|
"v": "2006"
|
||||||
|
}, {
|
||||||
|
"n": "2005",
|
||||||
|
"v": "2005"
|
||||||
|
}, {
|
||||||
|
"n": "2004.2003",
|
||||||
|
"v": "2004.2003"
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
"key": "lang",
|
||||||
|
"name": "语言",
|
||||||
|
"value": [{
|
||||||
|
"n": "全部",
|
||||||
|
"v": ""
|
||||||
|
}, {
|
||||||
|
"n": "日语",
|
||||||
|
"v": "日语"
|
||||||
|
}, {
|
||||||
|
"n": "英语",
|
||||||
|
"v": "英语"
|
||||||
|
}, {
|
||||||
|
"n": "泰语",
|
||||||
|
"v": "泰语"
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
"key": "by",
|
||||||
|
"name": "排序",
|
||||||
|
"value": [{
|
||||||
|
"n": "最新",
|
||||||
|
"v": "time"
|
||||||
|
}, {
|
||||||
|
"n": "最热",
|
||||||
|
"v": "hits"
|
||||||
|
}, {
|
||||||
|
"n": "评分",
|
||||||
|
"v": "score"
|
||||||
|
}]
|
||||||
|
}],
|
||||||
|
"24": [{
|
||||||
|
"key": "by",
|
||||||
|
"name": "排序",
|
||||||
|
"value": [{
|
||||||
|
"n": "最新",
|
||||||
|
"v": "time"
|
||||||
|
}, {
|
||||||
|
"n": "最热",
|
||||||
|
"v": "hits"
|
||||||
|
}, {
|
||||||
|
"n": "评分",
|
||||||
|
"v": "score"
|
||||||
|
}]
|
||||||
|
}],
|
||||||
|
"26": [{
|
||||||
|
"key": "by",
|
||||||
|
"name": "排序",
|
||||||
|
"value": [{
|
||||||
|
"n": "最新",
|
||||||
|
"v": "time"
|
||||||
|
}, {
|
||||||
|
"n": "最热",
|
||||||
|
"v": "hits"
|
||||||
|
}, {
|
||||||
|
"n": "评分",
|
||||||
|
"v": "score"
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
searchUrl: '/search/**----------fypage---/',
|
||||||
|
class_name: '日番&美番&劇場版&真人番劇&BD副音軌&其他',
|
||||||
|
class_url: '2&3&21&20&24&26',
|
||||||
|
play_parse: true,
|
||||||
|
lazy: `js:
|
||||||
|
var html = JSON.parse(request(input).match(/r player_.*?=(.*?)</)[1]);
|
||||||
|
var url = html.url;
|
||||||
|
var from = html.from;
|
||||||
|
var next = html.link_next;
|
||||||
|
if (html.encrypt == '1') {
|
||||||
|
url = unescape(url)
|
||||||
|
} else if (html.encrypt == '2') {
|
||||||
|
url = unescape(base64Decode(url))
|
||||||
|
} else if (html.encrypt == '3') {
|
||||||
|
url = url.substring(8, url.length);
|
||||||
|
url = base64Decode(url);
|
||||||
|
url = url.substring(8, (url.length) - 8)
|
||||||
|
}
|
||||||
|
if (/\\.m3u8|\\.mp4/.test(url)) {
|
||||||
|
input = {
|
||||||
|
jx: 0,
|
||||||
|
url: url,
|
||||||
|
parse: 0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var paurl = request(HOST + '/static/player/' + from + '.js').match(/ src="(.*?)'/)[1];
|
||||||
|
if (/https/.test(paurl)) {
|
||||||
|
var purl = paurl + url + '&next=' + next + '&title=';
|
||||||
|
input = {
|
||||||
|
jx: 0,
|
||||||
|
url: purl,
|
||||||
|
parse: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
推荐: '.border-box&&.public-list-box;a&&title;.lazy&&data-src;.public-list-prb&&Text;a&&href',
|
||||||
|
double: false, // 推荐内容是否双层定位
|
||||||
|
一级: '.border-box .public-list-box;a&&title;.lazy&&data-src;.public-list-prb&&Text;a&&href',
|
||||||
|
搜索: '.row-right&&.search-box;.thumb-txt&&Text;.lazy&&data-src;.public-list-prb&&Text;a&&href',
|
||||||
|
}
|
||||||
@ -0,0 +1,979 @@
|
|||||||
|
var rule = {
|
||||||
|
title: '花子动漫[漫]',
|
||||||
|
host: 'https://www.huazidm.com',
|
||||||
|
class_name: 'TV动漫&剧场&特摄',
|
||||||
|
class_url: '1&2&3',
|
||||||
|
searchUrl: '/index.php/ajax/suggest?mid=1&wd=**&limit=50',
|
||||||
|
searchUrl: '/vodsearch/**----------fypage---.html',
|
||||||
|
searchable: 2,
|
||||||
|
quickSearch: 0,
|
||||||
|
headers: {
|
||||||
|
'User-Agent': 'MOBILE_UA',
|
||||||
|
},
|
||||||
|
url: '/index.php/api/vod#type=fyclassfyfilter&page=fypage',
|
||||||
|
filterable: 0,
|
||||||
|
filter_url: '&class={{fl.class}}&year={{fl.year}}&letter={{fl.letter}}&by={{fl.by}}',
|
||||||
|
filter: {
|
||||||
|
"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": "year",
|
||||||
|
"name": "年份",
|
||||||
|
"value": [{
|
||||||
|
"n": "全部",
|
||||||
|
"v": ""
|
||||||
|
}, {
|
||||||
|
"n": "2026",
|
||||||
|
"v": "2026"
|
||||||
|
}, {
|
||||||
|
"n": "2025",
|
||||||
|
"v": "2025"
|
||||||
|
}, {
|
||||||
|
"n": "2024",
|
||||||
|
"v": "2024"
|
||||||
|
}, {
|
||||||
|
"n": "2023",
|
||||||
|
"v": "2023"
|
||||||
|
}, {
|
||||||
|
"n": "2022",
|
||||||
|
"v": "2022"
|
||||||
|
}, {
|
||||||
|
"n": "2021",
|
||||||
|
"v": "2021"
|
||||||
|
}, {
|
||||||
|
"n": "2020",
|
||||||
|
"v": "2020"
|
||||||
|
}, {
|
||||||
|
"n": "2019",
|
||||||
|
"v": "2019"
|
||||||
|
}, {
|
||||||
|
"n": "2018",
|
||||||
|
"v": "2018"
|
||||||
|
}, {
|
||||||
|
"n": "2017",
|
||||||
|
"v": "2017"
|
||||||
|
}, {
|
||||||
|
"n": "2016",
|
||||||
|
"v": "2016"
|
||||||
|
}, {
|
||||||
|
"n": "2015",
|
||||||
|
"v": "2015"
|
||||||
|
}, {
|
||||||
|
"n": "2014",
|
||||||
|
"v": "2014"
|
||||||
|
}, {
|
||||||
|
"n": "2013",
|
||||||
|
"v": "2013"
|
||||||
|
}, {
|
||||||
|
"n": "2012",
|
||||||
|
"v": "2012"
|
||||||
|
}, {
|
||||||
|
"n": "2011",
|
||||||
|
"v": "2011"
|
||||||
|
}, {
|
||||||
|
"n": "2010",
|
||||||
|
"v": "2010"
|
||||||
|
}, {
|
||||||
|
"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"
|
||||||
|
}, {
|
||||||
|
"n": "1999",
|
||||||
|
"v": "1999"
|
||||||
|
}, {
|
||||||
|
"n": "1998",
|
||||||
|
"v": "1998"
|
||||||
|
}, {
|
||||||
|
"n": "1997",
|
||||||
|
"v": "1997"
|
||||||
|
}, {
|
||||||
|
"n": "1996",
|
||||||
|
"v": "1996"
|
||||||
|
}, {
|
||||||
|
"n": "1995",
|
||||||
|
"v": "1995"
|
||||||
|
}, {
|
||||||
|
"n": "1994",
|
||||||
|
"v": "1994"
|
||||||
|
}, {
|
||||||
|
"n": "1993",
|
||||||
|
"v": "1993"
|
||||||
|
}, {
|
||||||
|
"n": "1992",
|
||||||
|
"v": "1992"
|
||||||
|
}, {
|
||||||
|
"n": "1991",
|
||||||
|
"v": "1991"
|
||||||
|
}, {
|
||||||
|
"n": "1990",
|
||||||
|
"v": "1990"
|
||||||
|
}, {
|
||||||
|
"n": "1989",
|
||||||
|
"v": "1989"
|
||||||
|
}, {
|
||||||
|
"n": "1988",
|
||||||
|
"v": "1988"
|
||||||
|
}, {
|
||||||
|
"n": "1987",
|
||||||
|
"v": "1987"
|
||||||
|
}, {
|
||||||
|
"n": "1986",
|
||||||
|
"v": "1986"
|
||||||
|
}, {
|
||||||
|
"n": "1985",
|
||||||
|
"v": "1985"
|
||||||
|
}, {
|
||||||
|
"n": "1984",
|
||||||
|
"v": "1984"
|
||||||
|
}, {
|
||||||
|
"n": "1983",
|
||||||
|
"v": "1983"
|
||||||
|
}, {
|
||||||
|
"n": "1982",
|
||||||
|
"v": "1982"
|
||||||
|
}, {
|
||||||
|
"n": "1981",
|
||||||
|
"v": "1981"
|
||||||
|
}, {
|
||||||
|
"n": "1980",
|
||||||
|
"v": "1980"
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
"key": "letter",
|
||||||
|
"name": "字母",
|
||||||
|
"value": [{
|
||||||
|
"n": "全部",
|
||||||
|
"v": ""
|
||||||
|
}, {
|
||||||
|
"n": "A",
|
||||||
|
"v": "A"
|
||||||
|
}, {
|
||||||
|
"n": "B",
|
||||||
|
"v": "B"
|
||||||
|
}, {
|
||||||
|
"n": "C",
|
||||||
|
"v": "C"
|
||||||
|
}, {
|
||||||
|
"n": "D",
|
||||||
|
"v": "D"
|
||||||
|
}, {
|
||||||
|
"n": "E",
|
||||||
|
"v": "E"
|
||||||
|
}, {
|
||||||
|
"n": "F",
|
||||||
|
"v": "F"
|
||||||
|
}, {
|
||||||
|
"n": "G",
|
||||||
|
"v": "G"
|
||||||
|
}, {
|
||||||
|
"n": "H",
|
||||||
|
"v": "H"
|
||||||
|
}, {
|
||||||
|
"n": "I",
|
||||||
|
"v": "I"
|
||||||
|
}, {
|
||||||
|
"n": "J",
|
||||||
|
"v": "J"
|
||||||
|
}, {
|
||||||
|
"n": "K",
|
||||||
|
"v": "K"
|
||||||
|
}, {
|
||||||
|
"n": "L",
|
||||||
|
"v": "L"
|
||||||
|
}, {
|
||||||
|
"n": "M",
|
||||||
|
"v": "M"
|
||||||
|
}, {
|
||||||
|
"n": "N",
|
||||||
|
"v": "N"
|
||||||
|
}, {
|
||||||
|
"n": "O",
|
||||||
|
"v": "O"
|
||||||
|
}, {
|
||||||
|
"n": "P",
|
||||||
|
"v": "P"
|
||||||
|
}, {
|
||||||
|
"n": "Q",
|
||||||
|
"v": "Q"
|
||||||
|
}, {
|
||||||
|
"n": "R",
|
||||||
|
"v": "R"
|
||||||
|
}, {
|
||||||
|
"n": "S",
|
||||||
|
"v": "S"
|
||||||
|
}, {
|
||||||
|
"n": "T",
|
||||||
|
"v": "T"
|
||||||
|
}, {
|
||||||
|
"n": "U",
|
||||||
|
"v": "U"
|
||||||
|
}, {
|
||||||
|
"n": "V",
|
||||||
|
"v": "V"
|
||||||
|
}, {
|
||||||
|
"n": "W",
|
||||||
|
"v": "W"
|
||||||
|
}, {
|
||||||
|
"n": "X",
|
||||||
|
"v": "X"
|
||||||
|
}, {
|
||||||
|
"n": "Y",
|
||||||
|
"v": "Y"
|
||||||
|
}, {
|
||||||
|
"n": "Z",
|
||||||
|
"v": "Z"
|
||||||
|
}, {
|
||||||
|
"n": "0-9",
|
||||||
|
"v": "0-9"
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
"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": "音乐"
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
"key": "year",
|
||||||
|
"name": "年份",
|
||||||
|
"value": [{
|
||||||
|
"n": "全部",
|
||||||
|
"v": ""
|
||||||
|
}, {
|
||||||
|
"n": "2026",
|
||||||
|
"v": "2026"
|
||||||
|
}, {
|
||||||
|
"n": "2025",
|
||||||
|
"v": "2025"
|
||||||
|
}, {
|
||||||
|
"n": "2024",
|
||||||
|
"v": "2024"
|
||||||
|
}, {
|
||||||
|
"n": "2023",
|
||||||
|
"v": "2023"
|
||||||
|
}, {
|
||||||
|
"n": "2022",
|
||||||
|
"v": "2022"
|
||||||
|
}, {
|
||||||
|
"n": "2021",
|
||||||
|
"v": "2021"
|
||||||
|
}, {
|
||||||
|
"n": "2020",
|
||||||
|
"v": "2020"
|
||||||
|
}, {
|
||||||
|
"n": "2019",
|
||||||
|
"v": "2019"
|
||||||
|
}, {
|
||||||
|
"n": "2018",
|
||||||
|
"v": "2018"
|
||||||
|
}, {
|
||||||
|
"n": "2017",
|
||||||
|
"v": "2017"
|
||||||
|
}, {
|
||||||
|
"n": "2016",
|
||||||
|
"v": "2016"
|
||||||
|
}, {
|
||||||
|
"n": "2015",
|
||||||
|
"v": "2015"
|
||||||
|
}, {
|
||||||
|
"n": "2014",
|
||||||
|
"v": "2014"
|
||||||
|
}, {
|
||||||
|
"n": "2013",
|
||||||
|
"v": "2013"
|
||||||
|
}, {
|
||||||
|
"n": "2012",
|
||||||
|
"v": "2012"
|
||||||
|
}, {
|
||||||
|
"n": "2011",
|
||||||
|
"v": "2011"
|
||||||
|
}, {
|
||||||
|
"n": "2010",
|
||||||
|
"v": "2010"
|
||||||
|
}, {
|
||||||
|
"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"
|
||||||
|
}, {
|
||||||
|
"n": "1999",
|
||||||
|
"v": "1999"
|
||||||
|
}, {
|
||||||
|
"n": "1998",
|
||||||
|
"v": "1998"
|
||||||
|
}, {
|
||||||
|
"n": "1997",
|
||||||
|
"v": "1997"
|
||||||
|
}, {
|
||||||
|
"n": "1996",
|
||||||
|
"v": "1996"
|
||||||
|
}, {
|
||||||
|
"n": "1995",
|
||||||
|
"v": "1995"
|
||||||
|
}, {
|
||||||
|
"n": "1994",
|
||||||
|
"v": "1994"
|
||||||
|
}, {
|
||||||
|
"n": "1993",
|
||||||
|
"v": "1993"
|
||||||
|
}, {
|
||||||
|
"n": "1992",
|
||||||
|
"v": "1992"
|
||||||
|
}, {
|
||||||
|
"n": "1991",
|
||||||
|
"v": "1991"
|
||||||
|
}, {
|
||||||
|
"n": "1990",
|
||||||
|
"v": "1990"
|
||||||
|
}, {
|
||||||
|
"n": "1989",
|
||||||
|
"v": "1989"
|
||||||
|
}, {
|
||||||
|
"n": "1988",
|
||||||
|
"v": "1988"
|
||||||
|
}, {
|
||||||
|
"n": "1987",
|
||||||
|
"v": "1987"
|
||||||
|
}, {
|
||||||
|
"n": "1986",
|
||||||
|
"v": "1986"
|
||||||
|
}, {
|
||||||
|
"n": "1985",
|
||||||
|
"v": "1985"
|
||||||
|
}, {
|
||||||
|
"n": "1984",
|
||||||
|
"v": "1984"
|
||||||
|
}, {
|
||||||
|
"n": "1983",
|
||||||
|
"v": "1983"
|
||||||
|
}, {
|
||||||
|
"n": "1982",
|
||||||
|
"v": "1982"
|
||||||
|
}, {
|
||||||
|
"n": "1981",
|
||||||
|
"v": "1981"
|
||||||
|
}, {
|
||||||
|
"n": "1980",
|
||||||
|
"v": "1980"
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
"key": "letter",
|
||||||
|
"name": "字母",
|
||||||
|
"value": [{
|
||||||
|
"n": "全部",
|
||||||
|
"v": ""
|
||||||
|
}, {
|
||||||
|
"n": "A",
|
||||||
|
"v": "A"
|
||||||
|
}, {
|
||||||
|
"n": "B",
|
||||||
|
"v": "B"
|
||||||
|
}, {
|
||||||
|
"n": "C",
|
||||||
|
"v": "C"
|
||||||
|
}, {
|
||||||
|
"n": "D",
|
||||||
|
"v": "D"
|
||||||
|
}, {
|
||||||
|
"n": "E",
|
||||||
|
"v": "E"
|
||||||
|
}, {
|
||||||
|
"n": "F",
|
||||||
|
"v": "F"
|
||||||
|
}, {
|
||||||
|
"n": "G",
|
||||||
|
"v": "G"
|
||||||
|
}, {
|
||||||
|
"n": "H",
|
||||||
|
"v": "H"
|
||||||
|
}, {
|
||||||
|
"n": "I",
|
||||||
|
"v": "I"
|
||||||
|
}, {
|
||||||
|
"n": "J",
|
||||||
|
"v": "J"
|
||||||
|
}, {
|
||||||
|
"n": "K",
|
||||||
|
"v": "K"
|
||||||
|
}, {
|
||||||
|
"n": "L",
|
||||||
|
"v": "L"
|
||||||
|
}, {
|
||||||
|
"n": "M",
|
||||||
|
"v": "M"
|
||||||
|
}, {
|
||||||
|
"n": "N",
|
||||||
|
"v": "N"
|
||||||
|
}, {
|
||||||
|
"n": "O",
|
||||||
|
"v": "O"
|
||||||
|
}, {
|
||||||
|
"n": "P",
|
||||||
|
"v": "P"
|
||||||
|
}, {
|
||||||
|
"n": "Q",
|
||||||
|
"v": "Q"
|
||||||
|
}, {
|
||||||
|
"n": "R",
|
||||||
|
"v": "R"
|
||||||
|
}, {
|
||||||
|
"n": "S",
|
||||||
|
"v": "S"
|
||||||
|
}, {
|
||||||
|
"n": "T",
|
||||||
|
"v": "T"
|
||||||
|
}, {
|
||||||
|
"n": "U",
|
||||||
|
"v": "U"
|
||||||
|
}, {
|
||||||
|
"n": "V",
|
||||||
|
"v": "V"
|
||||||
|
}, {
|
||||||
|
"n": "W",
|
||||||
|
"v": "W"
|
||||||
|
}, {
|
||||||
|
"n": "X",
|
||||||
|
"v": "X"
|
||||||
|
}, {
|
||||||
|
"n": "Y",
|
||||||
|
"v": "Y"
|
||||||
|
}, {
|
||||||
|
"n": "Z",
|
||||||
|
"v": "Z"
|
||||||
|
}, {
|
||||||
|
"n": "0-9",
|
||||||
|
"v": "0-9"
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
"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": "科幻"
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
"key": "year",
|
||||||
|
"name": "年份",
|
||||||
|
"value": [{
|
||||||
|
"n": "全部",
|
||||||
|
"v": ""
|
||||||
|
}, {
|
||||||
|
"n": "2026",
|
||||||
|
"v": "2026"
|
||||||
|
}, {
|
||||||
|
"n": "2025",
|
||||||
|
"v": "2025"
|
||||||
|
}, {
|
||||||
|
"n": "2024",
|
||||||
|
"v": "2024"
|
||||||
|
}, {
|
||||||
|
"n": "2023",
|
||||||
|
"v": "2023"
|
||||||
|
}, {
|
||||||
|
"n": "2022",
|
||||||
|
"v": "2022"
|
||||||
|
}, {
|
||||||
|
"n": "2021",
|
||||||
|
"v": "2021"
|
||||||
|
}, {
|
||||||
|
"n": "2020",
|
||||||
|
"v": "2020"
|
||||||
|
}, {
|
||||||
|
"n": "2019",
|
||||||
|
"v": "2019"
|
||||||
|
}, {
|
||||||
|
"n": "2018",
|
||||||
|
"v": "2018"
|
||||||
|
}, {
|
||||||
|
"n": "2017",
|
||||||
|
"v": "2017"
|
||||||
|
}, {
|
||||||
|
"n": "2016",
|
||||||
|
"v": "2016"
|
||||||
|
}, {
|
||||||
|
"n": "2015",
|
||||||
|
"v": "2015"
|
||||||
|
}, {
|
||||||
|
"n": "2014",
|
||||||
|
"v": "2014"
|
||||||
|
}, {
|
||||||
|
"n": "2013",
|
||||||
|
"v": "2013"
|
||||||
|
}, {
|
||||||
|
"n": "2012",
|
||||||
|
"v": "2012"
|
||||||
|
}, {
|
||||||
|
"n": "2011",
|
||||||
|
"v": "2011"
|
||||||
|
}, {
|
||||||
|
"n": "2010",
|
||||||
|
"v": "2010"
|
||||||
|
}, {
|
||||||
|
"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"
|
||||||
|
}, {
|
||||||
|
"n": "1999",
|
||||||
|
"v": "1999"
|
||||||
|
}, {
|
||||||
|
"n": "1998",
|
||||||
|
"v": "1998"
|
||||||
|
}, {
|
||||||
|
"n": "1997",
|
||||||
|
"v": "1997"
|
||||||
|
}, {
|
||||||
|
"n": "1996",
|
||||||
|
"v": "1996"
|
||||||
|
}, {
|
||||||
|
"n": "1995",
|
||||||
|
"v": "1995"
|
||||||
|
}, {
|
||||||
|
"n": "1994",
|
||||||
|
"v": "1994"
|
||||||
|
}, {
|
||||||
|
"n": "1993",
|
||||||
|
"v": "1993"
|
||||||
|
}, {
|
||||||
|
"n": "1992",
|
||||||
|
"v": "1992"
|
||||||
|
}, {
|
||||||
|
"n": "1991",
|
||||||
|
"v": "1991"
|
||||||
|
}, {
|
||||||
|
"n": "1990",
|
||||||
|
"v": "1990"
|
||||||
|
}, {
|
||||||
|
"n": "1989",
|
||||||
|
"v": "1989"
|
||||||
|
}, {
|
||||||
|
"n": "1988",
|
||||||
|
"v": "1988"
|
||||||
|
}, {
|
||||||
|
"n": "1987",
|
||||||
|
"v": "1987"
|
||||||
|
}, {
|
||||||
|
"n": "1986",
|
||||||
|
"v": "1986"
|
||||||
|
}, {
|
||||||
|
"n": "1985",
|
||||||
|
"v": "1985"
|
||||||
|
}, {
|
||||||
|
"n": "1984",
|
||||||
|
"v": "1984"
|
||||||
|
}, {
|
||||||
|
"n": "1983",
|
||||||
|
"v": "1983"
|
||||||
|
}, {
|
||||||
|
"n": "1982",
|
||||||
|
"v": "1982"
|
||||||
|
}, {
|
||||||
|
"n": "1981",
|
||||||
|
"v": "1981"
|
||||||
|
}, {
|
||||||
|
"n": "1980",
|
||||||
|
"v": "1980"
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
"key": "letter",
|
||||||
|
"name": "字母",
|
||||||
|
"value": [{
|
||||||
|
"n": "全部",
|
||||||
|
"v": ""
|
||||||
|
}, {
|
||||||
|
"n": "A",
|
||||||
|
"v": "A"
|
||||||
|
}, {
|
||||||
|
"n": "B",
|
||||||
|
"v": "B"
|
||||||
|
}, {
|
||||||
|
"n": "C",
|
||||||
|
"v": "C"
|
||||||
|
}, {
|
||||||
|
"n": "D",
|
||||||
|
"v": "D"
|
||||||
|
}, {
|
||||||
|
"n": "E",
|
||||||
|
"v": "E"
|
||||||
|
}, {
|
||||||
|
"n": "F",
|
||||||
|
"v": "F"
|
||||||
|
}, {
|
||||||
|
"n": "G",
|
||||||
|
"v": "G"
|
||||||
|
}, {
|
||||||
|
"n": "H",
|
||||||
|
"v": "H"
|
||||||
|
}, {
|
||||||
|
"n": "I",
|
||||||
|
"v": "I"
|
||||||
|
}, {
|
||||||
|
"n": "J",
|
||||||
|
"v": "J"
|
||||||
|
}, {
|
||||||
|
"n": "K",
|
||||||
|
"v": "K"
|
||||||
|
}, {
|
||||||
|
"n": "L",
|
||||||
|
"v": "L"
|
||||||
|
}, {
|
||||||
|
"n": "M",
|
||||||
|
"v": "M"
|
||||||
|
}, {
|
||||||
|
"n": "N",
|
||||||
|
"v": "N"
|
||||||
|
}, {
|
||||||
|
"n": "O",
|
||||||
|
"v": "O"
|
||||||
|
}, {
|
||||||
|
"n": "P",
|
||||||
|
"v": "P"
|
||||||
|
}, {
|
||||||
|
"n": "Q",
|
||||||
|
"v": "Q"
|
||||||
|
}, {
|
||||||
|
"n": "R",
|
||||||
|
"v": "R"
|
||||||
|
}, {
|
||||||
|
"n": "S",
|
||||||
|
"v": "S"
|
||||||
|
}, {
|
||||||
|
"n": "T",
|
||||||
|
"v": "T"
|
||||||
|
}, {
|
||||||
|
"n": "U",
|
||||||
|
"v": "U"
|
||||||
|
}, {
|
||||||
|
"n": "V",
|
||||||
|
"v": "V"
|
||||||
|
}, {
|
||||||
|
"n": "W",
|
||||||
|
"v": "W"
|
||||||
|
}, {
|
||||||
|
"n": "X",
|
||||||
|
"v": "X"
|
||||||
|
}, {
|
||||||
|
"n": "Y",
|
||||||
|
"v": "Y"
|
||||||
|
}, {
|
||||||
|
"n": "Z",
|
||||||
|
"v": "Z"
|
||||||
|
}, {
|
||||||
|
"n": "0-9",
|
||||||
|
"v": "0-9"
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
"key": "by",
|
||||||
|
"name": "时间",
|
||||||
|
"value": [{
|
||||||
|
"n": "按最新",
|
||||||
|
"v": "time"
|
||||||
|
}, {
|
||||||
|
"n": "按最热",
|
||||||
|
"v": "hits"
|
||||||
|
}, {
|
||||||
|
"n": "按评分",
|
||||||
|
"v": "score"
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
filter_def: {},
|
||||||
|
detailUrl: '/voddetail/fyid.html',
|
||||||
|
play_parse: true,
|
||||||
|
sniffer: 1,
|
||||||
|
is_video: 'obj/tos|bd.xhscdn|/ugc/',
|
||||||
|
lazy: $js.toString(() => {
|
||||||
|
input = {
|
||||||
|
parse: 1,
|
||||||
|
url: input,
|
||||||
|
//js:'try{let urls=Array.from(document.querySelectorAll("iframe")).filter(x=>x.src.includes("?url="));if(urls){location.href=urls[0].src}}catch{}document.querySelector("button").click()',
|
||||||
|
js: 'try{location.href=document.querySelector("#playleft iframe").src}catch{}document.querySelector("button.swal-button--confirm").click()',
|
||||||
|
parse_extra: '&is_pc=1&custom_regex=' + rule.is_video,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
limit: 6,
|
||||||
|
推荐: '.list-vod.flex .public-list-box;a&&title;.lazy&&data-original;.public-list-prb&&Text;a&&href',
|
||||||
|
一级: $js.toString(() => {
|
||||||
|
let body = input.split("#")[1];
|
||||||
|
let t = Math.round(new Date / 1e3).toString();
|
||||||
|
let key = md5("DS" + t + "DCC147D11943AF75");
|
||||||
|
let url = input.split("#")[0];
|
||||||
|
body = body + "&time=" + t + "&key=" + key;
|
||||||
|
print(body);
|
||||||
|
fetch_params.body = body;
|
||||||
|
let html = post(url, fetch_params);
|
||||||
|
let data = JSON.parse(html);
|
||||||
|
VODS = data.list.map(function(it) {
|
||||||
|
it.vod_pic = urljoin2(input.split("/i")[0], it.vod_pic);
|
||||||
|
return it
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
二级: {
|
||||||
|
title: '.slide-info-title&&Text;.slide-info:eq(3)--strong&&Text',
|
||||||
|
img: '.detail-pic&&data-original',
|
||||||
|
desc: '.fraction&&Text;.slide-info-remarks:eq(1)&&Text;.slide-info-remarks:eq(2)&&Text;.slide-info:eq(2)--strong&&Text;.slide-info:eq(1)--strong&&Text',
|
||||||
|
content: '#height_limit&&Text',
|
||||||
|
tabs: '.anthology.wow.fadeInUp.animated&&.swiper-wrapper&&a',
|
||||||
|
tab_text: '.swiper-slide&&Text',
|
||||||
|
lists: '.anthology-list-box:eq(#id) li',
|
||||||
|
},
|
||||||
|
搜索: 'json:list;name;pic;;id',
|
||||||
|
搜索: $js.toString(() => {
|
||||||
|
let html = fetch(input);
|
||||||
|
let list = pdfa(html, ".public-list-box");
|
||||||
|
VODS = list.map(x => {
|
||||||
|
return {
|
||||||
|
vod_name: pdfh(x, ".thumb-txt&&Text"),
|
||||||
|
vod_pic: pdfh(x, ".lazy&&data-src"),
|
||||||
|
vod_remarks: pdfh(x, ".public-list-prb&&Text"),
|
||||||
|
vod_content: pdfh(x, ".thumb-blurb&&Text"),
|
||||||
|
vod_id: pdfh(x, "a&&href")
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
图片替换: '&=>&'
|
||||||
|
}
|
||||||
@ -1,47 +1,567 @@
|
|||||||
var rule = {
|
var rule = {
|
||||||
title: '路漫漫',
|
title: "路漫漫",
|
||||||
// host:'https://www.96ba.com',
|
host: "https://www.lmm50.com",
|
||||||
host: 'https://www.wzwt369.com',
|
url: "/vod/show/id/fyclassfyfilter.html",
|
||||||
// url:'/vod/show/id/fyclass/page/fypage.html',
|
searchUrl: '/vod/search/page/fypage/wd/**.html',
|
||||||
url: '/vod/show/id/fyclassfyfilter.html',
|
searchable: 2,
|
||||||
filterable: 1,//是否启用分类筛选,
|
quickSearch: 0,
|
||||||
filter_url: '{{fl.by}}/page/fypage{{fl.year}}',
|
filterable: 1,
|
||||||
filter: {
|
filter: {
|
||||||
"ribendongman": [{ "key": "year", "name": "年份", "value": [{ "n": "全部", "v": "" }, { "n": "2024", "v": "/year/2024" }, { "n": "2023", "v": "/year/2023" }, { "n": "2022", "v": "/year/2022" }, { "n": "2021", "v": "/year/2021" }, { "n": "2020", "v": "/year/2020" }, { "n": "2019", "v": "/year/2019" }, { "n": "2018", "v": "/year/2018" }, { "n": "2017", "v": "/year/2017" }, { "n": "2016", "v": "/year/2016" }, { "n": "2015", "v": "/year/2015" }, { "n": "2014", "v": "/year/2014" }, { "n": "更早", "v": "/year/2013-1980" }] }, { "key": "by", "name": "排序", "value": [{ "n": "更新", "v": "/by/time" }, { "n": "人气", "v": "/by/hits" }, { "n": "评分", "v": "/by/score" }, { "n": "点赞", "v": "/by/up" }] }],
|
"3": [{
|
||||||
"guochandongman": [{ "key": "year", "name": "年份", "value": [{ "n": "全部", "v": "" }, { "n": "2024", "v": "/year/2024" }, { "n": "2023", "v": "/year/2023" }, { "n": "2022", "v": "/year/2022" }, { "n": "2021", "v": "/year/2021" }, { "n": "2020", "v": "/year/2020" }, { "n": "2019", "v": "/year/2019" }, { "n": "2018", "v": "/year/2018" }, { "n": "2017", "v": "/year/2017" }, { "n": "2016", "v": "/year/2016" }, { "n": "2015", "v": "/year/2015" }, { "n": "2014", "v": "/year/2014" }, { "n": "更早", "v": "/year/2013-1980" }] }, { "key": "by", "name": "排序", "value": [{ "n": "更新", "v": "/by/time" }, { "n": "人气", "v": "/by/hits" }, { "n": "评分", "v": "/by/score" }, { "n": "点赞", "v": "/by/up" }] }],
|
"key": "年代",
|
||||||
"oumeidongman": [{ "key": "year", "name": "年份", "value": [{ "n": "全部", "v": "" }, { "n": "2024", "v": "/year/2024" }, { "n": "2023", "v": "/year/2023" }, { "n": "2022", "v": "/year/2022" }, { "n": "2021", "v": "/year/2021" }, { "n": "2020", "v": "/year/2020" }, { "n": "2019", "v": "/year/2019" }, { "n": "2018", "v": "/year/2018" }, { "n": "2017", "v": "/year/2017" }, { "n": "2016", "v": "/year/2016" }, { "n": "2015", "v": "/year/2015" }, { "n": "2014", "v": "/year/2014" }, { "n": "更早", "v": "/year/2013-1980" }] }, { "key": "by", "name": "排序", "value": [{ "n": "更新", "v": "/by/time" }, { "n": "人气", "v": "/by/hits" }, { "n": "评分", "v": "/by/score" }, { "n": "点赞", "v": "/by/up" }] }],
|
"name": "年代",
|
||||||
"ribendonghuadianying": [{ "key": "year", "name": "年份", "value": [{ "n": "全部", "v": "" }, { "n": "2024", "v": "/year/2024" }, { "n": "2023", "v": "/year/2023" }, { "n": "2022", "v": "/year/2022" }, { "n": "2021", "v": "/year/2021" }, { "n": "2020", "v": "/year/2020" }, { "n": "2019", "v": "/year/2019" }, { "n": "2018", "v": "/year/2018" }, { "n": "2017", "v": "/year/2017" }, { "n": "2016", "v": "/year/2016" }, { "n": "2015", "v": "/year/2015" }, { "n": "2014", "v": "/year/2014" }, { "n": "更早", "v": "/year/2013-1980" }] }, { "key": "by", "name": "排序", "value": [{ "n": "更新", "v": "/by/time" }, { "n": "人气", "v": "/by/hits" }, { "n": "评分", "v": "/by/score" }, { "n": "点赞", "v": "/by/up" }] }],
|
"value": [{
|
||||||
"guochandonghuadianying": [{ "key": "year", "name": "年份", "value": [{ "n": "全部", "v": "" }, { "n": "2024", "v": "/year/2024" }, { "n": "2023", "v": "/year/2023" }, { "n": "2022", "v": "/year/2022" }, { "n": "2021", "v": "/year/2021" }, { "n": "2020", "v": "/year/2020" }, { "n": "2019", "v": "/year/2019" }, { "n": "2018", "v": "/year/2018" }, { "n": "2017", "v": "/year/2017" }, { "n": "2016", "v": "/year/2016" }, { "n": "2015", "v": "/year/2015" }, { "n": "2014", "v": "/year/2014" }, { "n": "更早", "v": "/year/2013-1980" }] }, { "key": "by", "name": "排序", "value": [{ "n": "更新", "v": "/by/time" }, { "n": "人气", "v": "/by/hits" }, { "n": "评分", "v": "/by/score" }, { "n": "点赞", "v": "/by/up" }] }],
|
"n": "全部",
|
||||||
"oumeidonghuadianying": [{ "key": "year", "name": "年份", "value": [{ "n": "全部", "v": "" }, { "n": "2024", "v": "/year/2024" }, { "n": "2023", "v": "/year/2023" }, { "n": "2022", "v": "/year/2022" }, { "n": "2021", "v": "/year/2021" }, { "n": "2020", "v": "/year/2020" }, { "n": "2019", "v": "/year/2019" }, { "n": "2018", "v": "/year/2018" }, { "n": "2017", "v": "/year/2017" }, { "n": "2016", "v": "/year/2016" }, { "n": "2015", "v": "/year/2015" }, { "n": "2014", "v": "/year/2014" }, { "n": "更早", "v": "/year/2013-1980" }] }, { "key": "by", "name": "排序", "value": [{ "n": "更新", "v": "/by/time" }, { "n": "人气", "v": "/by/hits" }, { "n": "评分", "v": "/by/score" }, { "n": "点赞", "v": "/by/up" }] }]
|
"v": ""
|
||||||
},
|
}, {
|
||||||
searchUrl: '/vod/search/page/fypage/wd/**.html',
|
"n": "2026",
|
||||||
searchable: 2,//是否启用全局搜索,
|
"v": "/year/2026"
|
||||||
quickSearch: 0,//是否启用快速搜索,
|
}, {
|
||||||
headers: {
|
"n": "2025",
|
||||||
'User-Agent': 'MOBILE_UA',
|
"v": "/year/2025"
|
||||||
},
|
}, {
|
||||||
class_parse: '.container&&.tag.text-light;a&&Text;a&&href;.*/(.*?).html',
|
"n": "2024",
|
||||||
play_parse: true,
|
"v": "/year/2024"
|
||||||
lazy: `js:
|
}, {
|
||||||
var html = JSON.parse(request(input).match(/r player_.*?=(.*?)</)[1]);
|
"n": "2023",
|
||||||
var url = html.url;
|
"v": "/year/2023"
|
||||||
var from = html.from;
|
}, {
|
||||||
if (/m3u8/.test(url)) {
|
"n": "2022",
|
||||||
input = url.split("&")[0]
|
"v": "/year/2022"
|
||||||
} else {
|
}, {
|
||||||
input
|
"n": "2021",
|
||||||
}
|
"v": "/year/2021"
|
||||||
`,
|
}, {
|
||||||
limit: 6,
|
"n": "2020",
|
||||||
推荐: '.owl-theme-jable .item;*;*;*;*',
|
"v": "/year/2020"
|
||||||
一级: '#mdym .col-6;h6&&Text;.lazyload&&data-src;.label&&Text;a&&href',
|
}, {
|
||||||
二级: {
|
"n": "2019",
|
||||||
"title": "h1&&Text;.video-info-aux&&Text",
|
"v": "/year/2019"
|
||||||
"img": ".url_img&&src",
|
}, {
|
||||||
"desc": ".video-info-items--span:eq(3)&&Text;;;.video-info-actor:eq(1)&&Text;.video-info-actor:eq(0)&&Text",
|
"n": "2018",
|
||||||
"content": ".content-text&&Text",
|
"v": "/year/2018"
|
||||||
"tabs": ".module-tab-content .tab-item",
|
}, {
|
||||||
"lists": ".scroll-content:eq(#id) a"
|
"n": "2017",
|
||||||
},
|
"v": "/year/2017"
|
||||||
搜索: '*',
|
}, {
|
||||||
|
"n": "2016",
|
||||||
|
"v": "/year/2016"
|
||||||
|
}, {
|
||||||
|
"n": "2015",
|
||||||
|
"v": "/year/2015"
|
||||||
|
}, {
|
||||||
|
"n": "更早",
|
||||||
|
"v": "/year/2014-1980"
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
"key": "排序",
|
||||||
|
"name": "排序",
|
||||||
|
"value": [{
|
||||||
|
"n": "最近更新",
|
||||||
|
"v": "/by/time"
|
||||||
|
}, {
|
||||||
|
"n": "最高人气",
|
||||||
|
"v": "/by/hits"
|
||||||
|
}, {
|
||||||
|
"n": "最高评分",
|
||||||
|
"v": "/by/score"
|
||||||
|
}, {
|
||||||
|
"n": "最多点赞",
|
||||||
|
"v": "/by/up"
|
||||||
|
}]
|
||||||
|
}],
|
||||||
|
"4": [{
|
||||||
|
"key": "年代",
|
||||||
|
"name": "年代",
|
||||||
|
"value": [{
|
||||||
|
"n": "全部",
|
||||||
|
"v": ""
|
||||||
|
}, {
|
||||||
|
"n": "2026",
|
||||||
|
"v": "/year/2026"
|
||||||
|
}, {
|
||||||
|
"n": "2025",
|
||||||
|
"v": "/year/2025"
|
||||||
|
}, {
|
||||||
|
"n": "2024",
|
||||||
|
"v": "/year/2024"
|
||||||
|
}, {
|
||||||
|
"n": "2023",
|
||||||
|
"v": "/year/2023"
|
||||||
|
}, {
|
||||||
|
"n": "2022",
|
||||||
|
"v": "/year/2022"
|
||||||
|
}, {
|
||||||
|
"n": "2021",
|
||||||
|
"v": "/year/2021"
|
||||||
|
}, {
|
||||||
|
"n": "2020",
|
||||||
|
"v": "/year/2020"
|
||||||
|
}, {
|
||||||
|
"n": "2019",
|
||||||
|
"v": "/year/2019"
|
||||||
|
}, {
|
||||||
|
"n": "2018",
|
||||||
|
"v": "/year/2018"
|
||||||
|
}, {
|
||||||
|
"n": "2017",
|
||||||
|
"v": "/year/2017"
|
||||||
|
}, {
|
||||||
|
"n": "2016",
|
||||||
|
"v": "/year/2016"
|
||||||
|
}, {
|
||||||
|
"n": "2015",
|
||||||
|
"v": "/year/2015"
|
||||||
|
}, {
|
||||||
|
"n": "更早",
|
||||||
|
"v": "/year/2014-1980"
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
"key": "排序",
|
||||||
|
"name": "排序",
|
||||||
|
"value": [{
|
||||||
|
"n": "最近更新",
|
||||||
|
"v": "/by/time"
|
||||||
|
}, {
|
||||||
|
"n": "最高人气",
|
||||||
|
"v": "/by/hits"
|
||||||
|
}, {
|
||||||
|
"n": "最高评分",
|
||||||
|
"v": "/by/score"
|
||||||
|
}, {
|
||||||
|
"n": "最多点赞",
|
||||||
|
"v": "/by/up"
|
||||||
|
}]
|
||||||
|
}],
|
||||||
|
"5": [{
|
||||||
|
"key": "年代",
|
||||||
|
"name": "年代",
|
||||||
|
"value": [{
|
||||||
|
"n": "全部",
|
||||||
|
"v": ""
|
||||||
|
}, {
|
||||||
|
"n": "2026",
|
||||||
|
"v": "/year/2026"
|
||||||
|
}, {
|
||||||
|
"n": "2025",
|
||||||
|
"v": "/year/2025"
|
||||||
|
}, {
|
||||||
|
"n": "2024",
|
||||||
|
"v": "/year/2024"
|
||||||
|
}, {
|
||||||
|
"n": "2023",
|
||||||
|
"v": "/year/2023"
|
||||||
|
}, {
|
||||||
|
"n": "2022",
|
||||||
|
"v": "/year/2022"
|
||||||
|
}, {
|
||||||
|
"n": "2021",
|
||||||
|
"v": "/year/2021"
|
||||||
|
}, {
|
||||||
|
"n": "2020",
|
||||||
|
"v": "/year/2020"
|
||||||
|
}, {
|
||||||
|
"n": "2019",
|
||||||
|
"v": "/year/2019"
|
||||||
|
}, {
|
||||||
|
"n": "2018",
|
||||||
|
"v": "/year/2018"
|
||||||
|
}, {
|
||||||
|
"n": "2017",
|
||||||
|
"v": "/year/2017"
|
||||||
|
}, {
|
||||||
|
"n": "2016",
|
||||||
|
"v": "/year/2016"
|
||||||
|
}, {
|
||||||
|
"n": "2015",
|
||||||
|
"v": "/year/2015"
|
||||||
|
}, {
|
||||||
|
"n": "更早",
|
||||||
|
"v": "/year/2014-1980"
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
"key": "排序",
|
||||||
|
"name": "排序",
|
||||||
|
"value": [{
|
||||||
|
"n": "最近更新",
|
||||||
|
"v": "/by/time"
|
||||||
|
}, {
|
||||||
|
"n": "最高人气",
|
||||||
|
"v": "/by/hits"
|
||||||
|
}, {
|
||||||
|
"n": "最高评分",
|
||||||
|
"v": "/by/score"
|
||||||
|
}, {
|
||||||
|
"n": "最多点赞",
|
||||||
|
"v": "/by/up"
|
||||||
|
}]
|
||||||
|
}],
|
||||||
|
"6": [{
|
||||||
|
"key": "年代",
|
||||||
|
"name": "年代",
|
||||||
|
"value": [{
|
||||||
|
"n": "全部",
|
||||||
|
"v": ""
|
||||||
|
}, {
|
||||||
|
"n": "2026",
|
||||||
|
"v": "/year/2026"
|
||||||
|
}, {
|
||||||
|
"n": "2025",
|
||||||
|
"v": "/year/2025"
|
||||||
|
}, {
|
||||||
|
"n": "2024",
|
||||||
|
"v": "/year/2024"
|
||||||
|
}, {
|
||||||
|
"n": "2023",
|
||||||
|
"v": "/year/2023"
|
||||||
|
}, {
|
||||||
|
"n": "2022",
|
||||||
|
"v": "/year/2022"
|
||||||
|
}, {
|
||||||
|
"n": "2021",
|
||||||
|
"v": "/year/2021"
|
||||||
|
}, {
|
||||||
|
"n": "2020",
|
||||||
|
"v": "/year/2020"
|
||||||
|
}, {
|
||||||
|
"n": "2019",
|
||||||
|
"v": "/year/2019"
|
||||||
|
}, {
|
||||||
|
"n": "2018",
|
||||||
|
"v": "/year/2018"
|
||||||
|
}, {
|
||||||
|
"n": "2017",
|
||||||
|
"v": "/year/2017"
|
||||||
|
}, {
|
||||||
|
"n": "2016",
|
||||||
|
"v": "/year/2016"
|
||||||
|
}, {
|
||||||
|
"n": "2015",
|
||||||
|
"v": "/year/2015"
|
||||||
|
}, {
|
||||||
|
"n": "更早",
|
||||||
|
"v": "/year/2014-1980"
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
"key": "排序",
|
||||||
|
"name": "排序",
|
||||||
|
"value": [{
|
||||||
|
"n": "最近更新",
|
||||||
|
"v": "/by/time"
|
||||||
|
}, {
|
||||||
|
"n": "最高人气",
|
||||||
|
"v": "/by/hits"
|
||||||
|
}, {
|
||||||
|
"n": "最高评分",
|
||||||
|
"v": "/by/score"
|
||||||
|
}, {
|
||||||
|
"n": "最多点赞",
|
||||||
|
"v": "/by/up"
|
||||||
|
}]
|
||||||
|
}],
|
||||||
|
"7": [{
|
||||||
|
"key": "年代",
|
||||||
|
"name": "年代",
|
||||||
|
"value": [{
|
||||||
|
"n": "全部",
|
||||||
|
"v": ""
|
||||||
|
}, {
|
||||||
|
"n": "2026",
|
||||||
|
"v": "/year/2026"
|
||||||
|
}, {
|
||||||
|
"n": "2025",
|
||||||
|
"v": "/year/2025"
|
||||||
|
}, {
|
||||||
|
"n": "2024",
|
||||||
|
"v": "/year/2024"
|
||||||
|
}, {
|
||||||
|
"n": "2023",
|
||||||
|
"v": "/year/2023"
|
||||||
|
}, {
|
||||||
|
"n": "2022",
|
||||||
|
"v": "/year/2022"
|
||||||
|
}, {
|
||||||
|
"n": "2021",
|
||||||
|
"v": "/year/2021"
|
||||||
|
}, {
|
||||||
|
"n": "2020",
|
||||||
|
"v": "/year/2020"
|
||||||
|
}, {
|
||||||
|
"n": "2019",
|
||||||
|
"v": "/year/2019"
|
||||||
|
}, {
|
||||||
|
"n": "2018",
|
||||||
|
"v": "/year/2018"
|
||||||
|
}, {
|
||||||
|
"n": "2017",
|
||||||
|
"v": "/year/2017"
|
||||||
|
}, {
|
||||||
|
"n": "2016",
|
||||||
|
"v": "/year/2016"
|
||||||
|
}, {
|
||||||
|
"n": "2015",
|
||||||
|
"v": "/year/2015"
|
||||||
|
}, {
|
||||||
|
"n": "更早",
|
||||||
|
"v": "/year/2014-1980"
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
"key": "排序",
|
||||||
|
"name": "排序",
|
||||||
|
"value": [{
|
||||||
|
"n": "最近更新",
|
||||||
|
"v": "/by/time"
|
||||||
|
}, {
|
||||||
|
"n": "最高人气",
|
||||||
|
"v": "/by/hits"
|
||||||
|
}, {
|
||||||
|
"n": "最高评分",
|
||||||
|
"v": "/by/score"
|
||||||
|
}, {
|
||||||
|
"n": "最多点赞",
|
||||||
|
"v": "/by/up"
|
||||||
|
}]
|
||||||
|
}],
|
||||||
|
"8": [{
|
||||||
|
"key": "年代",
|
||||||
|
"name": "年代",
|
||||||
|
"value": [{
|
||||||
|
"n": "全部",
|
||||||
|
"v": ""
|
||||||
|
}, {
|
||||||
|
"n": "2026",
|
||||||
|
"v": "/year/2026"
|
||||||
|
}, {
|
||||||
|
"n": "2025",
|
||||||
|
"v": "/year/2025"
|
||||||
|
}, {
|
||||||
|
"n": "2024",
|
||||||
|
"v": "/year/2024"
|
||||||
|
}, {
|
||||||
|
"n": "2023",
|
||||||
|
"v": "/year/2023"
|
||||||
|
}, {
|
||||||
|
"n": "2022",
|
||||||
|
"v": "/year/2022"
|
||||||
|
}, {
|
||||||
|
"n": "2021",
|
||||||
|
"v": "/year/2021"
|
||||||
|
}, {
|
||||||
|
"n": "2020",
|
||||||
|
"v": "/year/2020"
|
||||||
|
}, {
|
||||||
|
"n": "2019",
|
||||||
|
"v": "/year/2019"
|
||||||
|
}, {
|
||||||
|
"n": "2018",
|
||||||
|
"v": "/year/2018"
|
||||||
|
}, {
|
||||||
|
"n": "2017",
|
||||||
|
"v": "/year/2017"
|
||||||
|
}, {
|
||||||
|
"n": "2016",
|
||||||
|
"v": "/year/2016"
|
||||||
|
}, {
|
||||||
|
"n": "2015",
|
||||||
|
"v": "/year/2015"
|
||||||
|
}, {
|
||||||
|
"n": "更早",
|
||||||
|
"v": "/year/2014-1980"
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
"key": "排序",
|
||||||
|
"name": "排序",
|
||||||
|
"value": [{
|
||||||
|
"n": "最近更新",
|
||||||
|
"v": "/by/time"
|
||||||
|
}, {
|
||||||
|
"n": "最高人气",
|
||||||
|
"v": "/by/hits"
|
||||||
|
}, {
|
||||||
|
"n": "最高评分",
|
||||||
|
"v": "/by/score"
|
||||||
|
}, {
|
||||||
|
"n": "最多点赞",
|
||||||
|
"v": "/by/up"
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
filter_url: "{{fl.排序}}{{fl.年代}}/page/fypage",
|
||||||
|
filter_def: "",
|
||||||
|
headers: {
|
||||||
|
"User-Agent": "MOBILE_UA"
|
||||||
|
},
|
||||||
|
timeout: 5000,
|
||||||
|
class_name: "日本动漫&国产动漫&欧美动漫&日本动画电影&国产动画电影&欧美动画电影",
|
||||||
|
class_url: "6&7&8&3&4&5",
|
||||||
|
class_parse: "",
|
||||||
|
cate_exclude: "",
|
||||||
|
play_parse: true,
|
||||||
|
lazy: $js.toString(() => {
|
||||||
|
function getDAesString(token) {
|
||||||
|
eval(getCryptoJS());
|
||||||
|
var key = CryptoJS.enc.Utf8.parse("ejjooopppqqqrwww");
|
||||||
|
|
||||||
|
var iv = CryptoJS.enc.Utf8.parse("1348987635684651");
|
||||||
|
|
||||||
|
var token = CryptoJS.AES.decrypt(token, key, {
|
||||||
|
iv: iv,
|
||||||
|
mode: CryptoJS.mode.CBC,
|
||||||
|
padding: CryptoJS.pad.Pkcs7,
|
||||||
|
});
|
||||||
|
|
||||||
|
return token.toString(CryptoJS.enc.Utf8);
|
||||||
|
}
|
||||||
|
|
||||||
|
var html = JSON.parse(request(input).match(/r player_.*?=(.*?)</)[1]);
|
||||||
|
var url = html.url;
|
||||||
|
var from = html.from;
|
||||||
|
if (html.encrypt == "1") {
|
||||||
|
url = unescape(url);
|
||||||
|
} else if (html.encrypt == "2") {
|
||||||
|
url = unescape(base64Decode(url));
|
||||||
|
}
|
||||||
|
if (/\.mp4|\.m3u8|\.flv/.test(url)) {
|
||||||
|
input = {
|
||||||
|
parse: 0,
|
||||||
|
url: url.split("&")[0],
|
||||||
|
js: ''
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
var jsh = request(HOST + "/static/player/" + from + ".js", {
|
||||||
|
headers: {
|
||||||
|
Referer: input
|
||||||
|
},
|
||||||
|
}).match(/\.src\s*=\s*(.*?);/)[1];
|
||||||
|
//log(MY_HOME);
|
||||||
|
if (/type=/.test(jsh)) {
|
||||||
|
jsh = jsh
|
||||||
|
.replace(/[\+\s']/g, "")
|
||||||
|
.replace(/MacPlayer.Parse/, "")
|
||||||
|
.replace(/MacPlayer.PlayUrl/, url)
|
||||||
|
.replace(/window.location.href/, input);
|
||||||
|
var playht = fetch(jsh, {
|
||||||
|
headers: {
|
||||||
|
Referer: HOST
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
jsh = jsh
|
||||||
|
.replace(/[\+\s']/g, "")
|
||||||
|
.replace(/MacPlayer.Parse/, "")
|
||||||
|
.replace(/MacPlayer.PlayUrl/, url)
|
||||||
|
.replace(/window.location.href/, input);
|
||||||
|
|
||||||
|
jsh = JSON.parse(
|
||||||
|
fetch(jsh, {
|
||||||
|
headers: {
|
||||||
|
Referer: HOST
|
||||||
|
},
|
||||||
|
onlyHeaders: true
|
||||||
|
})
|
||||||
|
).url;
|
||||||
|
var playht = fetch(jsh, {
|
||||||
|
headers: {
|
||||||
|
Referer: HOST
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var postapi = jsh.match(/^(.*?\/\/.*?\/.*?\/)/)[1];
|
||||||
|
|
||||||
|
var posturl = postapi + playht.match(/post\("(.*?)"/)[1];
|
||||||
|
if (/act\s*=/.test(playht)) {
|
||||||
|
var vid = playht.match(/vid\s*=\s*"(.*?)"/)[1];
|
||||||
|
var t = playht.match(/var\s*t\s*=\s*"(.*?)"/)[1];
|
||||||
|
var token = playht.match(/token\s*=\s*"(.*?)"/)[1];
|
||||||
|
var act = playht.match(/act\s*=\s*"(.*?)"/)[1];
|
||||||
|
var play = playht.match(/play\s*=\s*"(.*?)"/)[1];
|
||||||
|
token = getDAesString(token);
|
||||||
|
|
||||||
|
var data = JSON.parse(
|
||||||
|
post(posturl, {
|
||||||
|
headers: {
|
||||||
|
Referer: HOST
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
vid: vid,
|
||||||
|
t: t,
|
||||||
|
token: token,
|
||||||
|
act: act,
|
||||||
|
play: play,
|
||||||
|
},
|
||||||
|
timeout: 5000
|
||||||
|
})
|
||||||
|
);
|
||||||
|
input = {
|
||||||
|
parse: 0,
|
||||||
|
url: data.url,
|
||||||
|
js: ''
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
var key = "";
|
||||||
|
|
||||||
|
playht.match(/var (\w+)="(.*?)";/g).forEach(function(list) {
|
||||||
|
key += list.match(/"(.*?)"/)[1];
|
||||||
|
});
|
||||||
|
const bodys = JSON.parse(
|
||||||
|
playht
|
||||||
|
.match(/post\(.*?,(.*?),\n/)[1]
|
||||||
|
.replace(/"keyyy"\s*:\s*''.*?''/, '"keyyy" : "' + key + '"')
|
||||||
|
);
|
||||||
|
var data = JSON.parse(
|
||||||
|
post(posturl, {
|
||||||
|
headers: {
|
||||||
|
Referer: HOST
|
||||||
|
},
|
||||||
|
body: bodys
|
||||||
|
})
|
||||||
|
);
|
||||||
|
if (data.ext == "xgplayer") {
|
||||||
|
var dataurl =
|
||||||
|
"https://yun.366day.site/mp4hls/xgplayer.php?vid=" + data.url;
|
||||||
|
var video = fetch(dataurl, {
|
||||||
|
headers: {
|
||||||
|
Referer: jsh
|
||||||
|
}
|
||||||
|
}).match(
|
||||||
|
/"url": "(.*?)"/
|
||||||
|
)[1];
|
||||||
|
input = {
|
||||||
|
parse: 0,
|
||||||
|
url: video,
|
||||||
|
js: ''
|
||||||
|
};
|
||||||
|
} else if (data.ext == "hls" || data.ext == "hls_list") {
|
||||||
|
input = {
|
||||||
|
parse: 0,
|
||||||
|
url: decodeURIComponent(data.url),
|
||||||
|
js: ''
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
input = {
|
||||||
|
parse: 0,
|
||||||
|
url: data.url,
|
||||||
|
js: ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
double: false,
|
||||||
|
推荐: "*",
|
||||||
|
一级: ".video-img-box;h6.title&&Text;.lazyload&&data-src;.label&&Text;a&&href",
|
||||||
|
二级: {
|
||||||
|
title: ".page-title&&Text;.tag-link&&Text",
|
||||||
|
img: ".module-item-pic&&.lazyload&&src",
|
||||||
|
desc: ".video-info-items:eq(3)&&Text;.video-info-items:eq(2)&&Text;;.video-info-items:eq(1)&&Text;.video-info-items:eq(0)&&Text",
|
||||||
|
content: ".video-info-content&&Text",
|
||||||
|
tabs: ".module-tab-item.tab-item",
|
||||||
|
lists: ".module-player-list:eq(#id) a",
|
||||||
|
tab_text: "body&&Text",
|
||||||
|
list_text: "body&&Text",
|
||||||
|
list_url: "a&&href"
|
||||||
|
},
|
||||||
|
detailUrl: "",
|
||||||
|
搜索: "*"
|
||||||
}
|
}
|
||||||
@ -0,0 +1,378 @@
|
|||||||
|
globalThis.getRandomItem = function(items) {
|
||||||
|
return items[Math.random() * items.length | 0];
|
||||||
|
}
|
||||||
|
var rule = {
|
||||||
|
title: '采集之王[合]',
|
||||||
|
author: '道长',
|
||||||
|
version: '20240706 beta17',
|
||||||
|
update_info: ``.trim(),
|
||||||
|
host: '',
|
||||||
|
homeTid: '',
|
||||||
|
homeUrl: '/api.php/provide/vod/?ac=detail&t={{rule.homeTid}}',
|
||||||
|
detailUrl: '/api.php/provide/vod/?ac=detail&ids=fyid',
|
||||||
|
searchUrl: '/api.php/provide/vod/?wd=**&pg=#TruePage##page=fypage',
|
||||||
|
classUrl: '/api.php/provide/vod/',
|
||||||
|
url: '/api.php/provide/vod/?ac=detail&pg=fypage&t=fyfilter',
|
||||||
|
filter_url: '{{fl.类型}}',
|
||||||
|
headers: {
|
||||||
|
'User-Agent': 'MOBILE_UA'
|
||||||
|
},
|
||||||
|
timeout: 5000,
|
||||||
|
limit: 20,
|
||||||
|
search_limit: 10,
|
||||||
|
searchable: 1,
|
||||||
|
quickSearch: 0,
|
||||||
|
filterable: 1,
|
||||||
|
play_parse: true,
|
||||||
|
parse_url: '',
|
||||||
|
search_match: false,
|
||||||
|
search_pic: true,
|
||||||
|
预处理: $js.toString(() => {
|
||||||
|
function getClasses(item) {
|
||||||
|
let classes = [];
|
||||||
|
if (item.class_name && item.class_url) {
|
||||||
|
if (!/&|电影|电视剧|综艺|动漫[\u4E00-\u9FA5]+/.test(item.class_name)) {
|
||||||
|
try {
|
||||||
|
item.class_name = ungzip(item.class_name)
|
||||||
|
} catch (e) {
|
||||||
|
log(`不识别的class_name导致gzip解码失败:${e}`)
|
||||||
|
return classes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let names = item.class_name.split('&');
|
||||||
|
let urls = item.class_url.split('&');
|
||||||
|
let cnt = Math.min(names.length, urls.length);
|
||||||
|
for (let i = 0; i < cnt; i++) {
|
||||||
|
classes.push({
|
||||||
|
'type_id': urls[i],
|
||||||
|
'type_name': names[i]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return classes
|
||||||
|
}
|
||||||
|
if (typeof(batchFetch) === 'function') {
|
||||||
|
rule.search_limit = 16;
|
||||||
|
log('当前程序支持批量请求[batchFetch],搜索限制已设置为16');
|
||||||
|
}
|
||||||
|
let _url = rule.params;
|
||||||
|
log(`传入参数:${_url}`);
|
||||||
|
if (_url && typeof(_url) === 'string' && /^(http|file)/.test(_url)) {
|
||||||
|
if (_url.includes('$')) {
|
||||||
|
let _url_params = _url.split('$');
|
||||||
|
_url = _url_params[0];
|
||||||
|
rule.search_match = !!(_url_params[1]);
|
||||||
|
if (_url_params.length > 2) {
|
||||||
|
rule.search_pic = !!(_url_params[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let html = request(_url);
|
||||||
|
let json = JSON.parse(html);
|
||||||
|
let _classes = [];
|
||||||
|
rule.filter = {};
|
||||||
|
rule.filter_def = {};
|
||||||
|
json.forEach(it => {
|
||||||
|
let _obj = {
|
||||||
|
type_name: it.name,
|
||||||
|
type_id: it.url,
|
||||||
|
parse_url: it.parse_url || '',
|
||||||
|
searchable: it.searchable !== 0,
|
||||||
|
api: it.api || '',
|
||||||
|
cate_exclude: it.cate_exclude || '',
|
||||||
|
cate_excludes: it.cate_excludes || [],
|
||||||
|
};
|
||||||
|
_classes.push(_obj);
|
||||||
|
try {
|
||||||
|
let json1 = [];
|
||||||
|
if (it.class_name && it.class_url) {
|
||||||
|
json1 = getClasses(it);
|
||||||
|
} else {
|
||||||
|
json1 = JSON.parse(request(urljoin(_obj.type_id, _obj.api || rule.classUrl))).class;
|
||||||
|
}
|
||||||
|
if (_obj.cate_excludes && Array.isArray(_obj.cate_excludes) && _obj.cate_excludes.length > 0) {
|
||||||
|
json1 = json1.filter(cl => !_obj.cate_excludes.includes(cl.type_name));
|
||||||
|
} else if (_obj.cate_exclude) {
|
||||||
|
json1 = json1.filter(cl => !new RegExp(_obj.cate_exclude, 'i').test(cl.type_name));
|
||||||
|
}
|
||||||
|
rule.filter[_obj.type_id] = [{
|
||||||
|
"key": "类型",
|
||||||
|
"name": "类型",
|
||||||
|
"value": json1.map(i => {
|
||||||
|
return {
|
||||||
|
"n": i.type_name,
|
||||||
|
'v': i.type_id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}];
|
||||||
|
if (json1.length > 0) {
|
||||||
|
rule.filter_def[it.url] = {
|
||||||
|
"类型": json1[0].type_id
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
rule.filter[it.url] = [{
|
||||||
|
"key": "类型",
|
||||||
|
"name": "类型",
|
||||||
|
"value": [{
|
||||||
|
"n": "全部",
|
||||||
|
"v": ""
|
||||||
|
}]
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
rule.classes = _classes;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
class_parse: $js.toString(() => {
|
||||||
|
input = rule.classes;
|
||||||
|
}),
|
||||||
|
推荐: $js.toString(() => {
|
||||||
|
VODS = [];
|
||||||
|
if (rule.classes) {
|
||||||
|
let randomClass = getRandomItem(rule.classes);
|
||||||
|
let _url = urljoin(randomClass.type_id, input);
|
||||||
|
if (randomClass.api) {
|
||||||
|
_url = _url.replace('/api.php/provide/vod/', randomClass.api)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
let html = request(_url, {
|
||||||
|
timeout: rule.timeout
|
||||||
|
});
|
||||||
|
let json = JSON.parse(html);
|
||||||
|
VODS = json.list;
|
||||||
|
VODS.forEach(it => {
|
||||||
|
it.vod_id = randomClass.type_id + '$' + it.vod_id;
|
||||||
|
it.vod_remarks = it.vod_remarks + '|' + randomClass.type_name;
|
||||||
|
});
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
一级: $js.toString(() => {
|
||||||
|
VODS = [];
|
||||||
|
if (rule.classes) {
|
||||||
|
let _url = urljoin(MY_CATE, input);
|
||||||
|
let current_vod = rule.classes.find(item => item.type_id === MY_CATE);
|
||||||
|
if (current_vod && current_vod.api) {
|
||||||
|
_url = _url.replace('/api.php/provide/vod/', current_vod.api)
|
||||||
|
}
|
||||||
|
let html = request(_url);
|
||||||
|
let json = JSON.parse(html);
|
||||||
|
VODS = json.list;
|
||||||
|
VODS.forEach(it => {
|
||||||
|
it.vod_id = MY_CATE + '$' + it.vod_id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
二级: $js.toString(() => {
|
||||||
|
VOD = {};
|
||||||
|
if (orId === 'update_info') {
|
||||||
|
VOD = {
|
||||||
|
vod_content: rule.update_info.trim(),
|
||||||
|
vod_name: '更新日志',
|
||||||
|
type_name: '更新日志',
|
||||||
|
vod_pic: 'https://resource-cdn.tuxiaobei.com/video/FtWhs2mewX_7nEuE51_k6zvg6awl.png',
|
||||||
|
vod_remarks: `版本:${rule.version}`,
|
||||||
|
vod_play_from: '道长在线',
|
||||||
|
vod_play_url: '随机小视频$http://api.yujn.cn/api/zzxjj.php',
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
if (rule.classes) {
|
||||||
|
let _url = urljoin(fyclass, input);
|
||||||
|
let current_vod = rule.classes.find(item => item.type_id === fyclass);
|
||||||
|
if (current_vod && current_vod.api) {
|
||||||
|
_url = _url.replace('/api.php/provide/vod/', current_vod.api)
|
||||||
|
}
|
||||||
|
let html = request(_url);
|
||||||
|
let json = JSON.parse(html);
|
||||||
|
let data = json.list;
|
||||||
|
VOD = data[0];
|
||||||
|
if (current_vod && current_vod.type_name) {
|
||||||
|
VOD.vod_play_from = VOD.vod_play_from.split('$$$').map(it => current_vod.type_name + '|' + it).join('$$$')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
搜索: $js.toString(() => {
|
||||||
|
VODS = [];
|
||||||
|
if (rule.classes) {
|
||||||
|
let canSearch = rule.classes.filter(it => it.searchable);
|
||||||
|
let page = Number(MY_PAGE);
|
||||||
|
page = (MY_PAGE - 1) % Math.ceil(canSearch.length / rule.search_limit) + 1;
|
||||||
|
let truePage = Math.ceil(MY_PAGE / Math.ceil(canSearch.length / rule.search_limit));
|
||||||
|
if (rule.search_limit) {
|
||||||
|
let start = (page - 1) * rule.search_limit;
|
||||||
|
let end = page * rule.search_limit;
|
||||||
|
let t1 = new Date().getTime();
|
||||||
|
let searchMode = typeof(batchFetch) === 'function' ? '批量' : '单个';
|
||||||
|
log('start:' + start);
|
||||||
|
log('end:' + end);
|
||||||
|
log('搜索模式:' + searchMode);
|
||||||
|
log('精准搜索:' + rule.search_match);
|
||||||
|
log('强制获取图片:' + rule.search_pic);
|
||||||
|
if (start < canSearch.length) {
|
||||||
|
let search_classes = canSearch.slice(start, end);
|
||||||
|
let urls = [];
|
||||||
|
search_classes.forEach(it => {
|
||||||
|
let _url = urljoin(it.type_id, input);
|
||||||
|
if (it.api) {
|
||||||
|
_url = _url.replace('/api.php/provide/vod/', it.api)
|
||||||
|
}
|
||||||
|
_url = _url.replace("#TruePage#", "" + truePage);
|
||||||
|
urls.push(_url);
|
||||||
|
});
|
||||||
|
let results_list = [];
|
||||||
|
let results = [];
|
||||||
|
if (typeof(batchFetch) === 'function') {
|
||||||
|
let reqUrls = urls.map(it => {
|
||||||
|
return {
|
||||||
|
url: it,
|
||||||
|
options: {
|
||||||
|
timeout: rule.timeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let rets = batchFetch(reqUrls);
|
||||||
|
let detailUrls = [];
|
||||||
|
let detailUrlCount = 0;
|
||||||
|
rets.forEach((ret, idx) => {
|
||||||
|
let it = search_classes[idx];
|
||||||
|
if (ret) {
|
||||||
|
try {
|
||||||
|
let json = JSON.parse(ret);
|
||||||
|
let data = json.list;
|
||||||
|
data.forEach(i => {
|
||||||
|
i.site_name = it.type_name;
|
||||||
|
i.vod_id = it.type_id + '$' + i.vod_id;
|
||||||
|
i.vod_remarks = i.vod_remarks + '|' + it.type_name;
|
||||||
|
});
|
||||||
|
if (rule.search_match) {
|
||||||
|
data = data.filter(item => item.vod_name && (new RegExp(KEY, 'i')).test(item.vod_name))
|
||||||
|
}
|
||||||
|
if (data.length > 0) {
|
||||||
|
if (rule.search_pic && !data[0].vod_pic) {
|
||||||
|
log(`当前搜索站点【${it.type_name}】没图片,尝试访问二级去获取图片`);
|
||||||
|
let detailUrl = urls[idx].split('wd=')[0] + 'ac=detail&ids=' + data.map(k => k.vod_id.split('$')[1]).join(',');
|
||||||
|
detailUrls.push(detailUrl);
|
||||||
|
results_list.push({
|
||||||
|
data: data,
|
||||||
|
has_pic: false,
|
||||||
|
detailUrlCount: detailUrlCount
|
||||||
|
});
|
||||||
|
detailUrlCount++;
|
||||||
|
} else {
|
||||||
|
results_list.push({
|
||||||
|
data: data,
|
||||||
|
has_pic: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
log(`请求:${it.type_id}发生错误:${e.message}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let reqUrls2 = detailUrls.map(it => {
|
||||||
|
return {
|
||||||
|
url: it,
|
||||||
|
options: {
|
||||||
|
timeout: rule.timeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let rets2 = reqUrls2.length > 0 ? batchFetch(reqUrls2) : [];
|
||||||
|
for (let k = 0; k < results_list.length; k++) {
|
||||||
|
let result_data = results_list[k].data;
|
||||||
|
if (!results_list[k].has_pic) {
|
||||||
|
try {
|
||||||
|
let detailJson = JSON.parse(rets2[results_list[k].detailUrlCount]);
|
||||||
|
log('二级数据列表元素数:' + detailJson.list.length);
|
||||||
|
result_data.forEach((d, _seq) => {
|
||||||
|
let detailVodPic = detailJson.list.find(vod => vod.vod_id.toString() === d.vod_id.split('$')[1]);
|
||||||
|
if (detailVodPic) {
|
||||||
|
Object.assign(d, {
|
||||||
|
vod_pic: detailVodPic.vod_pic
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
log(`强制获取网站${result_data[0].site_name}的搜索图片失败:${e.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
results = results.concat(result_data);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
urls.forEach((_url, idx) => {
|
||||||
|
let it = search_classes[idx];
|
||||||
|
try {
|
||||||
|
let html = request(_url);
|
||||||
|
let json = JSON.parse(html);
|
||||||
|
let data = json.list;
|
||||||
|
data.forEach(i => {
|
||||||
|
i.vod_id = it.type_id + '$' + i.vod_id;
|
||||||
|
i.vod_remarks = i.vod_remarks + '|' + it.type_name;
|
||||||
|
});
|
||||||
|
if (rule.search_match) {
|
||||||
|
data = data.filter(item => item.vod_name && (new RegExp(KEY, 'i')).test(item.vod_name))
|
||||||
|
}
|
||||||
|
if (data.length > 0) {
|
||||||
|
if (rule.search_pic && !data[0].vod_pic) {
|
||||||
|
log(`当前搜索站点【${it.type_name}】没图片,尝试访问二级去获取图片`);
|
||||||
|
let detailUrl = urls[idx].split('wd=')[0] + 'ac=detail&ids=' + data.map(k => k.vod_id.split('$')[1]).join(',');
|
||||||
|
try {
|
||||||
|
let detailJson = JSON.parse(request(detailUrl));
|
||||||
|
log('二级数据列表元素数:' + detailJson.list.length);
|
||||||
|
data.forEach((d, _seq) => {
|
||||||
|
let detailVodPic = detailJson.list.find(vod => vod.vod_id.toString() === d.vod_id.split('$')[1]);
|
||||||
|
if (detailVodPic) {
|
||||||
|
Object.assign(d, {
|
||||||
|
vod_pic: detailVodPic.vod_pic
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
log(`强制获取网站${it.type_id}的搜索图片失败:${e.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
results = results.concat(data);
|
||||||
|
}
|
||||||
|
results = results.concat(data);
|
||||||
|
} catch (e) {
|
||||||
|
log(`请求:${it.type_id}发生错误:${e.message}`)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
VODS = results;
|
||||||
|
let t2 = new Date().getTime();
|
||||||
|
log(`${searchMode}搜索:${urls.length}个站耗时:${(Number(t2) - Number(t1))}ms`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
lazy: $js.toString(() => {
|
||||||
|
let parse_url = '';
|
||||||
|
if (flag && flag.includes('|')) {
|
||||||
|
let type_name = flag.split('|')[0];
|
||||||
|
let current_vod = rule.classes.find(item => item.type_name === type_name);
|
||||||
|
if (current_vod && current_vod.parse_url) {
|
||||||
|
parse_url = current_vod.parse_url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (/\.(m3u8|mp4)/.test(input)) {
|
||||||
|
input = {
|
||||||
|
parse: 0,
|
||||||
|
url: input
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (parse_url.startsWith('json:')) {
|
||||||
|
let purl = parse_url.replace('json:', '') + input;
|
||||||
|
let html = request(purl);
|
||||||
|
input = {
|
||||||
|
parse: 0,
|
||||||
|
url: JSON.parse(html).url
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
input = parse_url + input;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
@ -0,0 +1,67 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "我的网盘",
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"shareId": "",
|
||||||
|
"folder": "root"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "短剧合集",
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"shareId": "Y5wMKfVDD6K",
|
||||||
|
"folder": "6616613853041ba53f2842918e02346943d980b4"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "动漫合集",
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"shareId": "LEaepiYfxcw",
|
||||||
|
"folder": "61019a966975bfef583449a39b36da512384b2d7"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "电影合集",
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"shareId": "ZHNChQfiPfk",
|
||||||
|
"folder": "root"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"shareId": "e27BPgDwxeA",
|
||||||
|
"folder": "root"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"shareId": "XUH7r6BZuML",
|
||||||
|
"folder": "root"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "电视剧合集",
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"shareId": "ftMhRaKUfYp",
|
||||||
|
"folder": "root"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"shareId": "8Fg4TNsd2A2",
|
||||||
|
"folder": "root"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"shareId": "ar8Kg9azw1S",
|
||||||
|
"folder": "root"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"shareId": "GMYSz3AHFaA",
|
||||||
|
"folder": "root"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
@ -1,57 +1,21 @@
|
|||||||
{
|
{
|
||||||
"vodPic": "https://x.imgs.ovh/x/2023/09/05/64f680bb030b4.png",
|
"vodPic": "https://rogsoft.ddnsto.com/softcenter/softcenter/res/icon-alist.png",
|
||||||
"drives": [
|
"drives": [
|
||||||
{
|
{
|
||||||
"name": "弱水",
|
"name": "小雅",
|
||||||
"server": "http://shicheng.wang:555/"
|
"server": "http://alist.xiaoya.pro"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "NICS",
|
"name": "触光",
|
||||||
"server": "https://nics.eu.org"
|
"server": "https://pan.ichuguang.com"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "ECVE",
|
"name": "魔都云",
|
||||||
"server": "https://pan.ecve.cn"
|
"server": "https://cdn.modupan.com"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "小雅",
|
"name": "七米蓝",
|
||||||
"server": "http://alist.xiaoya.pro"
|
"server": "https://al.chirmyram.com"
|
||||||
},
|
}
|
||||||
{
|
]
|
||||||
"name": "觸光",
|
}
|
||||||
"server": "https://pan.ichuguang.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "一只魚",
|
|
||||||
"server": "https://vtok.pp.ua/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "七米藍",
|
|
||||||
"server": "https://al.chirmyram.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "神族九帝",
|
|
||||||
"server": "https://alist.shenzjd.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "梓澪",
|
|
||||||
"server": "https://zi0.cc"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "网盘1",
|
|
||||||
"server": "http://223.167.75.227:5678/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "网盘2",
|
|
||||||
"server": "http://123.249.94.196:5678/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "网盘3",
|
|
||||||
"server": "http://101.204.33.244:5678/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "网盘4",
|
|
||||||
"server": "http://175.152.57.102:5678/"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -0,0 +1,111 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "我的网盘",
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"shareId": "",
|
||||||
|
"folder": "0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "幼儿教育",
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"shareId": "a08f66152533",
|
||||||
|
"folder": "0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "短剧合集",
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"shareId": "885fd4ba2d92",
|
||||||
|
"folder": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"shareId": "a1cda418984f",
|
||||||
|
"folder": "0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "电影合集",
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"shareId": "49ab75d52e00",
|
||||||
|
"folder": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"shareId": "a632967760cf",
|
||||||
|
"folder": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"shareId": "2f59bb5d96b9",
|
||||||
|
"folder": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"shareId": "50828c368def",
|
||||||
|
"folder": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"shareId": "e07e26aecc08",
|
||||||
|
"folder": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"shareId": "0536a38a356e",
|
||||||
|
"folder": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"shareId": "e273ef697403",
|
||||||
|
"folder": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"shareId": "c8ac6c88e5d8",
|
||||||
|
"folder": "0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "电视剧合集",
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"shareId": "d19c4ebe1ff7",
|
||||||
|
"folder": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"shareId": "cd4c5ac7e830",
|
||||||
|
"folder": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"shareId": "e1b2ba8b6d6c",
|
||||||
|
"folder": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"shareId": "166fa0a7ca6f",
|
||||||
|
"folder": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"shareId": "37a92c0b7f10",
|
||||||
|
"folder": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"shareId": "fb3386e42af2",
|
||||||
|
"folder": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"shareId": "46ce214f4ed7",
|
||||||
|
"folder": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"shareId": "fe4681d7fb43",
|
||||||
|
"folder": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"shareId": "8d65e885b059",
|
||||||
|
"folder": "0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "我的网盘",
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"shareId": "",
|
||||||
|
"folder": "0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "豆瓣TOP250部",
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"shareId": "c0503fdee6644",
|
||||||
|
"folder": "0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "日剧",
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"shareId": "391b86c09cd24",
|
||||||
|
"folder": "9df4921d548841199e625765413f8bd6"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "韩剧",
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"shareId": "21f04a22052f4",
|
||||||
|
"folder": "950f5ca7d5e54d528c0dae1fddec5c78"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "音乐",
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"shareId": "369e30038dae4",
|
||||||
|
"folder": "1b8a55dc18a54fb7aea8ed2b6fd6ad53"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
@ -0,0 +1,70 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"share_name": "优品阁",
|
||||||
|
"share_id": "uWa9gbM3RJ7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"share_name": "阿里1T",
|
||||||
|
"share_id": "mxAfB6eRgY4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"share_name": "平凡中的",
|
||||||
|
"share_id": "4ydLxf7VgH7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"share_name": "tacit0924",
|
||||||
|
"share_id": "DNgnCudf4cD?pwd=6666"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"share_name": "黄妈",
|
||||||
|
"share_id": "4bGRVUdUtct"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"share_name": "YYDSVIP",
|
||||||
|
"share_id": "dieULBdYP3D"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"share_name": "优源阁",
|
||||||
|
"share_id": "RnjUi1urdb2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"share_name": "风流动漫",
|
||||||
|
"share_id": "WdaaeX7HK44"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"share_name": "风流剧集",
|
||||||
|
"share_id": "kgxWjZsK6bq"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"share_name": "xiaaluo",
|
||||||
|
"share_id": "sg8CdGUwmUr"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"share_name": "4K影视",
|
||||||
|
"share_id": "wHPKUENKFsS"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"share_name": "诺兰全集",
|
||||||
|
"share_id": "gf2GebXnZHh"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"share_name": "纪录片",
|
||||||
|
"share_id": "fSNHaYST47s"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"share_name": "掌灯者|港",
|
||||||
|
"share_id": "wHPKUENKFsS"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"share_name": "掌灯者|一",
|
||||||
|
"share_id": "wHPKUENKFsS"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"share_name": "掌灯者|二",
|
||||||
|
"share_id": "wHPKUENKFsS"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"share_name": "掌灯者|三",
|
||||||
|
"share_id": "wHPKUENKFsS"
|
||||||
|
}
|
||||||
|
]
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,607 @@
|
|||||||
|
/*!
|
||||||
|
* Jinja Templating for JavaScript v0.1.8
|
||||||
|
* https://github.com/sstur/jinja-js
|
||||||
|
*
|
||||||
|
* This is a slimmed-down Jinja2 implementation [http://jinja.pocoo.org/]
|
||||||
|
*
|
||||||
|
* In the interest of simplicity, it deviates from Jinja2 as follows:
|
||||||
|
* - Line statements, cycle, super, macro tags and block nesting are not implemented
|
||||||
|
* - auto escapes html by default (the filter is "html" not "e")
|
||||||
|
* - Only "html" and "safe" filters are built in
|
||||||
|
* - Filters are not valid in expressions; `foo|length > 1` is not valid
|
||||||
|
* - Expression Tests (`if num is odd`) not implemented (`is` translates to `==` and `isnot` to `!=`)
|
||||||
|
*
|
||||||
|
* Notes:
|
||||||
|
* - if property is not found, but method '_get' exists, it will be called with the property name (and cached)
|
||||||
|
* - `{% for n in obj %}` iterates the object's keys; get the value with `{% for n in obj %}{{ obj[n] }}{% endfor %}`
|
||||||
|
* - subscript notation `a[0]` takes literals or simple variables but not `a[item.key]`
|
||||||
|
* - `.2` is not a valid number literal; use `0.2`
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/*global require, exports, module, define */
|
||||||
|
|
||||||
|
(function(global, factory) {
|
||||||
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
||||||
|
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
||||||
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.jinja = {}));
|
||||||
|
})(this, (function(jinja) {
|
||||||
|
"use strict";
|
||||||
|
var STRINGS = /'(\\.|[^'])*'|"(\\.|[^"'"])*"/g;
|
||||||
|
var IDENTS_AND_NUMS = /([$_a-z][$\w]*)|([+-]?\d+(\.\d+)?)/g;
|
||||||
|
var NUMBER = /^[+-]?\d+(\.\d+)?$/;
|
||||||
|
//non-primitive literals (array and object literals)
|
||||||
|
var NON_PRIMITIVES = /\[[@#~](,[@#~])*\]|\[\]|\{([@i]:[@#~])(,[@i]:[@#~])*\}|\{\}/g;
|
||||||
|
//bare identifiers such as variables and in object literals: {foo: 'value'}
|
||||||
|
var IDENTIFIERS = /[$_a-z][$\w]*/ig;
|
||||||
|
var VARIABLES = /i(\.i|\[[@#i]\])*/g;
|
||||||
|
var ACCESSOR = /(\.i|\[[@#i]\])/g;
|
||||||
|
var OPERATORS = /(===?|!==?|>=?|<=?|&&|\|\||[+\-\*\/%])/g;
|
||||||
|
//extended (english) operators
|
||||||
|
var EOPS = /(^|[^$\w])(and|or|not|is|isnot)([^$\w]|$)/g;
|
||||||
|
var LEADING_SPACE = /^\s+/;
|
||||||
|
var TRAILING_SPACE = /\s+$/;
|
||||||
|
|
||||||
|
var START_TOKEN = /\{\{\{|\{\{|\{%|\{#/;
|
||||||
|
var TAGS = {
|
||||||
|
'{{{': /^('(\\.|[^'])*'|"(\\.|[^"'"])*"|.)+?\}\}\}/,
|
||||||
|
'{{': /^('(\\.|[^'])*'|"(\\.|[^"'"])*"|.)+?\}\}/,
|
||||||
|
'{%': /^('(\\.|[^'])*'|"(\\.|[^"'"])*"|.)+?%\}/,
|
||||||
|
'{#': /^('(\\.|[^'])*'|"(\\.|[^"'"])*"|.)+?#\}/
|
||||||
|
};
|
||||||
|
|
||||||
|
var delimeters = {
|
||||||
|
'{%': 'directive',
|
||||||
|
'{{': 'output',
|
||||||
|
'{#': 'comment'
|
||||||
|
};
|
||||||
|
|
||||||
|
var operators = {
|
||||||
|
and: '&&',
|
||||||
|
or: '||',
|
||||||
|
not: '!',
|
||||||
|
is: '==',
|
||||||
|
isnot: '!='
|
||||||
|
};
|
||||||
|
|
||||||
|
var constants = {
|
||||||
|
'true': true,
|
||||||
|
'false': false,
|
||||||
|
'null': null
|
||||||
|
};
|
||||||
|
|
||||||
|
function Parser() {
|
||||||
|
this.nest = [];
|
||||||
|
this.compiled = [];
|
||||||
|
this.childBlocks = 0;
|
||||||
|
this.parentBlocks = 0;
|
||||||
|
this.isSilent = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Parser.prototype.push = function(line) {
|
||||||
|
if (!this.isSilent) {
|
||||||
|
this.compiled.push(line);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Parser.prototype.parse = function(src) {
|
||||||
|
this.tokenize(src);
|
||||||
|
return this.compiled;
|
||||||
|
};
|
||||||
|
|
||||||
|
Parser.prototype.tokenize = function(src) {
|
||||||
|
var lastEnd = 0,
|
||||||
|
parser = this,
|
||||||
|
trimLeading = false;
|
||||||
|
matchAll(src, START_TOKEN, function(open, index, src) {
|
||||||
|
//here we match the rest of the src against a regex for this tag
|
||||||
|
var match = src.slice(index + open.length).match(TAGS[open]);
|
||||||
|
match = (match ? match[0] : '');
|
||||||
|
//here we sub out strings so we don't get false matches
|
||||||
|
var simplified = match.replace(STRINGS, '@');
|
||||||
|
//if we don't have a close tag or there is a nested open tag
|
||||||
|
if (!match || ~simplified.indexOf(open)) {
|
||||||
|
return index + 1;
|
||||||
|
}
|
||||||
|
var inner = match.slice(0, 0 - open.length);
|
||||||
|
//check for white-space collapse syntax
|
||||||
|
if (inner.charAt(0) === '-') var wsCollapseLeft = true;
|
||||||
|
if (inner.slice(-1) === '-') var wsCollapseRight = true;
|
||||||
|
inner = inner.replace(/^-|-$/g, '').trim();
|
||||||
|
//if we're in raw mode and we are not looking at an "endraw" tag, move along
|
||||||
|
if (parser.rawMode && (open + inner) !== '{%endraw') {
|
||||||
|
return index + 1;
|
||||||
|
}
|
||||||
|
var text = src.slice(lastEnd, index);
|
||||||
|
lastEnd = index + open.length + match.length;
|
||||||
|
if (trimLeading) text = trimLeft(text);
|
||||||
|
if (wsCollapseLeft) text = trimRight(text);
|
||||||
|
if (wsCollapseRight) trimLeading = true;
|
||||||
|
if (open === '{{{') {
|
||||||
|
//liquid-style: make {{{x}}} => {{x|safe}}
|
||||||
|
open = '{{';
|
||||||
|
inner += '|safe';
|
||||||
|
}
|
||||||
|
parser.textHandler(text);
|
||||||
|
parser.tokenHandler(open, inner);
|
||||||
|
});
|
||||||
|
var text = src.slice(lastEnd);
|
||||||
|
if (trimLeading) text = trimLeft(text);
|
||||||
|
this.textHandler(text);
|
||||||
|
};
|
||||||
|
|
||||||
|
Parser.prototype.textHandler = function(text) {
|
||||||
|
this.push('write(' + JSON.stringify(text) + ');');
|
||||||
|
};
|
||||||
|
|
||||||
|
Parser.prototype.tokenHandler = function(open, inner) {
|
||||||
|
var type = delimeters[open];
|
||||||
|
if (type === 'directive') {
|
||||||
|
this.compileTag(inner);
|
||||||
|
} else if (type === 'output') {
|
||||||
|
var extracted = this.extractEnt(inner, STRINGS, '@');
|
||||||
|
//replace || operators with ~
|
||||||
|
extracted.src = extracted.src.replace(/\|\|/g, '~').split('|');
|
||||||
|
//put back || operators
|
||||||
|
extracted.src = extracted.src.map(function(part) {
|
||||||
|
return part.split('~').join('||');
|
||||||
|
});
|
||||||
|
var parts = this.injectEnt(extracted, '@');
|
||||||
|
if (parts.length > 1) {
|
||||||
|
var filters = parts.slice(1).map(this.parseFilter.bind(this));
|
||||||
|
this.push('filter(' + this.parseExpr(parts[0]) + ',' + filters.join(',') + ');');
|
||||||
|
} else {
|
||||||
|
this.push('filter(' + this.parseExpr(parts[0]) + ');');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Parser.prototype.compileTag = function(str) {
|
||||||
|
var directive = str.split(' ')[0];
|
||||||
|
var handler = tagHandlers[directive];
|
||||||
|
if (!handler) {
|
||||||
|
throw new Error('Invalid tag: ' + str);
|
||||||
|
}
|
||||||
|
handler.call(this, str.slice(directive.length).trim());
|
||||||
|
};
|
||||||
|
|
||||||
|
Parser.prototype.parseFilter = function(src) {
|
||||||
|
src = src.trim();
|
||||||
|
var match = src.match(/[:(]/);
|
||||||
|
var i = match ? match.index : -1;
|
||||||
|
if (i < 0) return JSON.stringify([src]);
|
||||||
|
var name = src.slice(0, i);
|
||||||
|
var args = src.charAt(i) === ':' ? src.slice(i + 1) : src.slice(i + 1, -1);
|
||||||
|
args = this.parseExpr(args, {
|
||||||
|
terms: true
|
||||||
|
});
|
||||||
|
return '[' + JSON.stringify(name) + ',' + args + ']';
|
||||||
|
};
|
||||||
|
|
||||||
|
Parser.prototype.extractEnt = function(src, regex, placeholder) {
|
||||||
|
var subs = [],
|
||||||
|
isFunc = typeof placeholder == 'function';
|
||||||
|
src = src.replace(regex, function(str) {
|
||||||
|
var replacement = isFunc ? placeholder(str) : placeholder;
|
||||||
|
if (replacement) {
|
||||||
|
subs.push(str);
|
||||||
|
return replacement;
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
src: src,
|
||||||
|
subs: subs
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
Parser.prototype.injectEnt = function(extracted, placeholder) {
|
||||||
|
var src = extracted.src,
|
||||||
|
subs = extracted.subs,
|
||||||
|
isArr = Array.isArray(src);
|
||||||
|
var arr = (isArr) ? src : [src];
|
||||||
|
var re = new RegExp('[' + placeholder + ']', 'g'),
|
||||||
|
i = 0;
|
||||||
|
arr.forEach(function(src, index) {
|
||||||
|
arr[index] = src.replace(re, function() {
|
||||||
|
return subs[i++];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return isArr ? arr : arr[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
//replace complex literals without mistaking subscript notation with array literals
|
||||||
|
Parser.prototype.replaceComplex = function(s) {
|
||||||
|
var parsed = this.extractEnt(s, /i(\.i|\[[@#i]\])+/g, 'v');
|
||||||
|
parsed.src = parsed.src.replace(NON_PRIMITIVES, '~');
|
||||||
|
return this.injectEnt(parsed, 'v');
|
||||||
|
};
|
||||||
|
|
||||||
|
//parse expression containing literals (including objects/arrays) and variables (including dot and subscript notation)
|
||||||
|
//valid expressions: `a + 1 > b.c or c == null`, `a and b[1] != c`, `(a < b) or (c < d and e)`, 'a || [1]`
|
||||||
|
Parser.prototype.parseExpr = function(src, opts) {
|
||||||
|
opts = opts || {};
|
||||||
|
//extract string literals -> @
|
||||||
|
var parsed1 = this.extractEnt(src, STRINGS, '@');
|
||||||
|
//note: this will catch {not: 1} and a.is; could we replace temporarily and then check adjacent chars?
|
||||||
|
parsed1.src = parsed1.src.replace(EOPS, function(s, before, op, after) {
|
||||||
|
return (op in operators) ? before + operators[op] + after : s;
|
||||||
|
});
|
||||||
|
//sub out non-string literals (numbers/true/false/null) -> #
|
||||||
|
// the distinction is necessary because @ can be object identifiers, # cannot
|
||||||
|
var parsed2 = this.extractEnt(parsed1.src, IDENTS_AND_NUMS, function(s) {
|
||||||
|
return (s in constants || NUMBER.test(s)) ? '#' : null;
|
||||||
|
});
|
||||||
|
//sub out object/variable identifiers -> i
|
||||||
|
var parsed3 = this.extractEnt(parsed2.src, IDENTIFIERS, 'i');
|
||||||
|
//remove white-space
|
||||||
|
parsed3.src = parsed3.src.replace(/\s+/g, '');
|
||||||
|
|
||||||
|
//the rest of this is simply to boil the expression down and check validity
|
||||||
|
var simplified = parsed3.src;
|
||||||
|
//sub out complex literals (objects/arrays) -> ~
|
||||||
|
// the distinction is necessary because @ and # can be subscripts but ~ cannot
|
||||||
|
while (simplified !== (simplified = this.replaceComplex(simplified)));
|
||||||
|
//now @ represents strings, # represents other primitives and ~ represents non-primitives
|
||||||
|
//replace complex variables (those with dot/subscript accessors) -> v
|
||||||
|
while (simplified !== (simplified = simplified.replace(/i(\.i|\[[@#i]\])+/, 'v')));
|
||||||
|
//empty subscript or complex variables in subscript, are not permitted
|
||||||
|
simplified = simplified.replace(/[iv]\[v?\]/g, 'x');
|
||||||
|
//sub in "i" for @ and # and ~ and v (now "i" represents all literals, variables and identifiers)
|
||||||
|
simplified = simplified.replace(/[@#~v]/g, 'i');
|
||||||
|
//sub out operators
|
||||||
|
simplified = simplified.replace(OPERATORS, '%');
|
||||||
|
//allow 'not' unary operator
|
||||||
|
simplified = simplified.replace(/!+[i]/g, 'i');
|
||||||
|
var terms = opts.terms ? simplified.split(',') : [simplified];
|
||||||
|
terms.forEach(function(term) {
|
||||||
|
//simplify logical grouping
|
||||||
|
while (term !== (term = term.replace(/\(i(%i)*\)/g, 'i')));
|
||||||
|
if (!term.match(/^i(%i)*/)) {
|
||||||
|
throw new Error('Invalid expression: ' + src + " " + term);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
parsed3.src = parsed3.src.replace(VARIABLES, this.parseVar.bind(this));
|
||||||
|
parsed2.src = this.injectEnt(parsed3, 'i');
|
||||||
|
parsed1.src = this.injectEnt(parsed2, '#');
|
||||||
|
return this.injectEnt(parsed1, '@');
|
||||||
|
};
|
||||||
|
|
||||||
|
Parser.prototype.parseVar = function(src) {
|
||||||
|
var args = Array.prototype.slice.call(arguments);
|
||||||
|
var str = args.pop(),
|
||||||
|
index = args.pop();
|
||||||
|
//quote bare object identifiers (might be a reserved word like {while: 1})
|
||||||
|
if (src === 'i' && str.charAt(index + 1) === ':') {
|
||||||
|
return '"i"';
|
||||||
|
}
|
||||||
|
var parts = ['"i"'];
|
||||||
|
src.replace(ACCESSOR, function(part) {
|
||||||
|
if (part === '.i') {
|
||||||
|
parts.push('"i"');
|
||||||
|
} else if (part === '[i]') {
|
||||||
|
parts.push('get("i")');
|
||||||
|
} else {
|
||||||
|
parts.push(part.slice(1, -1));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return 'get(' + parts.join(',') + ')';
|
||||||
|
};
|
||||||
|
|
||||||
|
//escapes a name to be used as a javascript identifier
|
||||||
|
Parser.prototype.escName = function(str) {
|
||||||
|
return str.replace(/\W/g, function(s) {
|
||||||
|
return '$' + s.charCodeAt(0).toString(16);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Parser.prototype.parseQuoted = function(str) {
|
||||||
|
if (str.charAt(0) === "'") {
|
||||||
|
str = str.slice(1, -1).replace(/\\.|"/, function(s) {
|
||||||
|
if (s === "\\'") return "'";
|
||||||
|
return s.charAt(0) === '\\' ? s : ('\\' + s);
|
||||||
|
});
|
||||||
|
str = '"' + str + '"';
|
||||||
|
}
|
||||||
|
//todo: try/catch or deal with invalid characters (linebreaks, control characters)
|
||||||
|
return JSON.parse(str);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//the context 'this' inside tagHandlers is the parser instance
|
||||||
|
var tagHandlers = {
|
||||||
|
'if': function(expr) {
|
||||||
|
this.push('if (' + this.parseExpr(expr) + ') {');
|
||||||
|
this.nest.unshift('if');
|
||||||
|
},
|
||||||
|
'else': function() {
|
||||||
|
if (this.nest[0] === 'for') {
|
||||||
|
this.push('}, function() {');
|
||||||
|
} else {
|
||||||
|
this.push('} else {');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'elseif': function(expr) {
|
||||||
|
this.push('} else if (' + this.parseExpr(expr) + ') {');
|
||||||
|
},
|
||||||
|
'endif': function() {
|
||||||
|
this.nest.shift();
|
||||||
|
this.push('}');
|
||||||
|
},
|
||||||
|
'for': function(str) {
|
||||||
|
var i = str.indexOf(' in ');
|
||||||
|
var name = str.slice(0, i).trim();
|
||||||
|
var expr = str.slice(i + 4).trim();
|
||||||
|
this.push('each(' + this.parseExpr(expr) + ',' + JSON.stringify(name) + ',function() {');
|
||||||
|
this.nest.unshift('for');
|
||||||
|
},
|
||||||
|
'endfor': function() {
|
||||||
|
this.nest.shift();
|
||||||
|
this.push('});');
|
||||||
|
},
|
||||||
|
'raw': function() {
|
||||||
|
this.rawMode = true;
|
||||||
|
},
|
||||||
|
'endraw': function() {
|
||||||
|
this.rawMode = false;
|
||||||
|
},
|
||||||
|
'set': function(stmt) {
|
||||||
|
var i = stmt.indexOf('=');
|
||||||
|
var name = stmt.slice(0, i).trim();
|
||||||
|
var expr = stmt.slice(i + 1).trim();
|
||||||
|
this.push('set(' + JSON.stringify(name) + ',' + this.parseExpr(expr) + ');');
|
||||||
|
},
|
||||||
|
'block': function(name) {
|
||||||
|
if (this.isParent) {
|
||||||
|
++this.parentBlocks;
|
||||||
|
var blockName = 'block_' + (this.escName(name) || this.parentBlocks);
|
||||||
|
this.push('block(typeof ' + blockName + ' == "function" ? ' + blockName + ' : function() {');
|
||||||
|
} else if (this.hasParent) {
|
||||||
|
this.isSilent = false;
|
||||||
|
++this.childBlocks;
|
||||||
|
blockName = 'block_' + (this.escName(name) || this.childBlocks);
|
||||||
|
this.push('function ' + blockName + '() {');
|
||||||
|
}
|
||||||
|
this.nest.unshift('block');
|
||||||
|
},
|
||||||
|
'endblock': function() {
|
||||||
|
this.nest.shift();
|
||||||
|
if (this.isParent) {
|
||||||
|
this.push('});');
|
||||||
|
} else if (this.hasParent) {
|
||||||
|
this.push('}');
|
||||||
|
this.isSilent = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'extends': function(name) {
|
||||||
|
name = this.parseQuoted(name);
|
||||||
|
var parentSrc = this.readTemplateFile(name);
|
||||||
|
this.isParent = true;
|
||||||
|
this.tokenize(parentSrc);
|
||||||
|
this.isParent = false;
|
||||||
|
this.hasParent = true;
|
||||||
|
//silence output until we enter a child block
|
||||||
|
this.isSilent = true;
|
||||||
|
},
|
||||||
|
'include': function(name) {
|
||||||
|
name = this.parseQuoted(name);
|
||||||
|
var incSrc = this.readTemplateFile(name);
|
||||||
|
this.isInclude = true;
|
||||||
|
this.tokenize(incSrc);
|
||||||
|
this.isInclude = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//liquid style
|
||||||
|
tagHandlers.assign = tagHandlers.set;
|
||||||
|
//python/django style
|
||||||
|
tagHandlers.elif = tagHandlers.elseif;
|
||||||
|
|
||||||
|
var getRuntime = function runtime(data, opts) {
|
||||||
|
var defaults = {
|
||||||
|
autoEscape: 'toJson'
|
||||||
|
};
|
||||||
|
var _toString = Object.prototype.toString;
|
||||||
|
var _hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||||
|
var getKeys = Object.keys || function(obj) {
|
||||||
|
var keys = [];
|
||||||
|
for (var n in obj)
|
||||||
|
if (_hasOwnProperty.call(obj, n)) keys.push(n);
|
||||||
|
return keys;
|
||||||
|
};
|
||||||
|
var isArray = Array.isArray || function(obj) {
|
||||||
|
return _toString.call(obj) === '[object Array]';
|
||||||
|
};
|
||||||
|
var create = Object.create || function(obj) {
|
||||||
|
function F() {}
|
||||||
|
|
||||||
|
F.prototype = obj;
|
||||||
|
return new F();
|
||||||
|
};
|
||||||
|
var toString = function(val) {
|
||||||
|
if (val == null) return '';
|
||||||
|
return (typeof val.toString == 'function') ? val.toString() : _toString.call(val);
|
||||||
|
};
|
||||||
|
var extend = function(dest, src) {
|
||||||
|
var keys = getKeys(src);
|
||||||
|
for (var i = 0, len = keys.length; i < len; i++) {
|
||||||
|
var key = keys[i];
|
||||||
|
dest[key] = src[key];
|
||||||
|
}
|
||||||
|
return dest;
|
||||||
|
};
|
||||||
|
//get a value, lexically, starting in current context; a.b -> get("a","b")
|
||||||
|
var get = function() {
|
||||||
|
var val, n = arguments[0],
|
||||||
|
c = stack.length;
|
||||||
|
while (c--) {
|
||||||
|
val = stack[c][n];
|
||||||
|
if (typeof val != 'undefined') break;
|
||||||
|
}
|
||||||
|
for (var i = 1, len = arguments.length; i < len; i++) {
|
||||||
|
if (val == null) continue;
|
||||||
|
n = arguments[i];
|
||||||
|
val = (_hasOwnProperty.call(val, n)) ? val[n] : (typeof val._get == 'function' ? (val[n] = val._get(n)) : null);
|
||||||
|
}
|
||||||
|
return (val == null) ? '' : val;
|
||||||
|
};
|
||||||
|
var set = function(n, val) {
|
||||||
|
stack[stack.length - 1][n] = val;
|
||||||
|
};
|
||||||
|
var push = function(ctx) {
|
||||||
|
stack.push(ctx || {});
|
||||||
|
};
|
||||||
|
var pop = function() {
|
||||||
|
stack.pop();
|
||||||
|
};
|
||||||
|
var write = function(str) {
|
||||||
|
output.push(str);
|
||||||
|
};
|
||||||
|
var filter = function(val) {
|
||||||
|
for (var i = 1, len = arguments.length; i < len; i++) {
|
||||||
|
var arr = arguments[i],
|
||||||
|
name = arr[0],
|
||||||
|
filter = filters[name];
|
||||||
|
if (filter) {
|
||||||
|
arr[0] = val;
|
||||||
|
//now arr looks like [val, arg1, arg2]
|
||||||
|
val = filter.apply(data, arr);
|
||||||
|
} else {
|
||||||
|
throw new Error('Invalid filter: ' + name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (opts.autoEscape && name !== opts.autoEscape && name !== 'safe') {
|
||||||
|
//auto escape if not explicitly safe or already escaped
|
||||||
|
val = filters[opts.autoEscape].call(data, val);
|
||||||
|
}
|
||||||
|
output.push(val);
|
||||||
|
};
|
||||||
|
var each = function(obj, loopvar, fn1, fn2) {
|
||||||
|
if (obj == null) return;
|
||||||
|
var arr = isArray(obj) ? obj : getKeys(obj),
|
||||||
|
len = arr.length;
|
||||||
|
var ctx = {
|
||||||
|
loop: {
|
||||||
|
length: len,
|
||||||
|
first: arr[0],
|
||||||
|
last: arr[len - 1]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
push(ctx);
|
||||||
|
for (var i = 0; i < len; i++) {
|
||||||
|
extend(ctx.loop, {
|
||||||
|
index: i + 1,
|
||||||
|
index0: i
|
||||||
|
});
|
||||||
|
fn1(ctx[loopvar] = arr[i]);
|
||||||
|
}
|
||||||
|
if (len === 0 && fn2) fn2();
|
||||||
|
pop();
|
||||||
|
};
|
||||||
|
var block = function(fn) {
|
||||||
|
push();
|
||||||
|
fn();
|
||||||
|
pop();
|
||||||
|
};
|
||||||
|
var render = function() {
|
||||||
|
return output.join('');
|
||||||
|
};
|
||||||
|
data = data || {};
|
||||||
|
opts = extend(defaults, opts || {});
|
||||||
|
var filters = extend({
|
||||||
|
html: function(val) {
|
||||||
|
return toString(val)
|
||||||
|
.split('&').join('&')
|
||||||
|
.split('<').join('<')
|
||||||
|
.split('>').join('>')
|
||||||
|
.split('"').join('"');
|
||||||
|
},
|
||||||
|
safe: function(val) {
|
||||||
|
return val;
|
||||||
|
},
|
||||||
|
toJson: function(val) {
|
||||||
|
if (typeof val === 'object') {
|
||||||
|
return JSON.stringify(val);
|
||||||
|
}
|
||||||
|
return toString(val);
|
||||||
|
}
|
||||||
|
}, opts.filters || {});
|
||||||
|
var stack = [create(data || {})],
|
||||||
|
output = [];
|
||||||
|
return {
|
||||||
|
get: get,
|
||||||
|
set: set,
|
||||||
|
push: push,
|
||||||
|
pop: pop,
|
||||||
|
write: write,
|
||||||
|
filter: filter,
|
||||||
|
each: each,
|
||||||
|
block: block,
|
||||||
|
render: render
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
var runtime;
|
||||||
|
|
||||||
|
jinja.compile = function(markup, opts) {
|
||||||
|
opts = opts || {};
|
||||||
|
var parser = new Parser();
|
||||||
|
parser.readTemplateFile = this.readTemplateFile;
|
||||||
|
var code = [];
|
||||||
|
code.push('function render($) {');
|
||||||
|
code.push('var get = $.get, set = $.set, push = $.push, pop = $.pop, write = $.write, filter = $.filter, each = $.each, block = $.block;');
|
||||||
|
code.push.apply(code, parser.parse(markup));
|
||||||
|
code.push('return $.render();');
|
||||||
|
code.push('}');
|
||||||
|
code = code.join('\n');
|
||||||
|
if (opts.runtime === false) {
|
||||||
|
var fn = new Function('data', 'options', 'return (' + code + ')(runtime(data, options))');
|
||||||
|
} else {
|
||||||
|
runtime = runtime || (runtime = getRuntime.toString());
|
||||||
|
fn = new Function('data', 'options', 'return (' + code + ')((' + runtime + ')(data, options))');
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
render: fn
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
jinja.render = function(markup, data, opts) {
|
||||||
|
var tmpl = jinja.compile(markup);
|
||||||
|
return tmpl.render(data, opts);
|
||||||
|
};
|
||||||
|
|
||||||
|
jinja.templateFiles = [];
|
||||||
|
|
||||||
|
jinja.readTemplateFile = function(name) {
|
||||||
|
var templateFiles = this.templateFiles || [];
|
||||||
|
var templateFile = templateFiles[name];
|
||||||
|
if (templateFile == null) {
|
||||||
|
throw new Error('Template file not found: ' + name);
|
||||||
|
}
|
||||||
|
return templateFile;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Helpers
|
||||||
|
*/
|
||||||
|
|
||||||
|
function trimLeft(str) {
|
||||||
|
return str.replace(LEADING_SPACE, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function trimRight(str) {
|
||||||
|
return str.replace(TRAILING_SPACE, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function matchAll(str, reg, fn) {
|
||||||
|
//copy as global
|
||||||
|
reg = new RegExp(reg.source, 'g' + (reg.ignoreCase ? 'i' : '') + (reg.multiline ? 'm' : ''));
|
||||||
|
var match;
|
||||||
|
while ((match = reg.exec(str))) {
|
||||||
|
var result = fn(match[0], match.index, str);
|
||||||
|
if (typeof result == 'number') {
|
||||||
|
reg.lastIndex = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
@ -0,0 +1,504 @@
|
|||||||
|
(function(global, factory) {
|
||||||
|
typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.jinja = {}))
|
||||||
|
})(this, function(jinja) {
|
||||||
|
"use strict";
|
||||||
|
var STRINGS = /'(\\.|[^'])*'|"(\\.|[^"'"])*"/g;
|
||||||
|
var IDENTS_AND_NUMS = /([$_a-z][$\w]*)|([+-]?\d+(\.\d+)?)/g;
|
||||||
|
var NUMBER = /^[+-]?\d+(\.\d+)?$/;
|
||||||
|
var NON_PRIMITIVES = /\[[@#~](,[@#~])*\]|\[\]|\{([@i]:[@#~])(,[@i]:[@#~])*\}|\{\}/g;
|
||||||
|
var IDENTIFIERS = /[$_a-z][$\w]*/gi;
|
||||||
|
var VARIABLES = /i(\.i|\[[@#i]\])*/g;
|
||||||
|
var ACCESSOR = /(\.i|\[[@#i]\])/g;
|
||||||
|
var OPERATORS = /(===?|!==?|>=?|<=?|&&|\|\||[+\-\*\/%])/g;
|
||||||
|
var EOPS = /(^|[^$\w])(and|or|not|is|isnot)([^$\w]|$)/g;
|
||||||
|
var LEADING_SPACE = /^\s+/;
|
||||||
|
var TRAILING_SPACE = /\s+$/;
|
||||||
|
var START_TOKEN = /\{\{\{|\{\{|\{%|\{#/;
|
||||||
|
var TAGS = {
|
||||||
|
"{{{": /^('(\\.|[^'])*'|"(\\.|[^"'"])*"|.)+?\}\}\}/,
|
||||||
|
"{{": /^('(\\.|[^'])*'|"(\\.|[^"'"])*"|.)+?\}\}/,
|
||||||
|
"{%": /^('(\\.|[^'])*'|"(\\.|[^"'"])*"|.)+?%\}/,
|
||||||
|
"{#": /^('(\\.|[^'])*'|"(\\.|[^"'"])*"|.)+?#\}/
|
||||||
|
};
|
||||||
|
var delimeters = {
|
||||||
|
"{%": "directive",
|
||||||
|
"{{": "output",
|
||||||
|
"{#": "comment"
|
||||||
|
};
|
||||||
|
var operators = {
|
||||||
|
and: "&&",
|
||||||
|
or: "||",
|
||||||
|
not: "!",
|
||||||
|
is: "==",
|
||||||
|
isnot: "!="
|
||||||
|
};
|
||||||
|
var constants = {
|
||||||
|
true: true,
|
||||||
|
false: false,
|
||||||
|
null: null
|
||||||
|
};
|
||||||
|
|
||||||
|
function Parser() {
|
||||||
|
this.nest = [];
|
||||||
|
this.compiled = [];
|
||||||
|
this.childBlocks = 0;
|
||||||
|
this.parentBlocks = 0;
|
||||||
|
this.isSilent = false
|
||||||
|
}
|
||||||
|
Parser.prototype.push = function(line) {
|
||||||
|
if (!this.isSilent) {
|
||||||
|
this.compiled.push(line)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Parser.prototype.parse = function(src) {
|
||||||
|
this.tokenize(src);
|
||||||
|
return this.compiled
|
||||||
|
};
|
||||||
|
Parser.prototype.tokenize = function(src) {
|
||||||
|
var lastEnd = 0,
|
||||||
|
parser = this,
|
||||||
|
trimLeading = false;
|
||||||
|
matchAll(src, START_TOKEN, function(open, index, src) {
|
||||||
|
var match = src.slice(index + open.length).match(TAGS[open]);
|
||||||
|
match = match ? match[0] : "";
|
||||||
|
var simplified = match.replace(STRINGS, "@");
|
||||||
|
if (!match || ~simplified.indexOf(open)) {
|
||||||
|
return index + 1
|
||||||
|
}
|
||||||
|
var inner = match.slice(0, 0 - open.length);
|
||||||
|
if (inner.charAt(0) === "-") var wsCollapseLeft = true;
|
||||||
|
if (inner.slice(-1) === "-") var wsCollapseRight = true;
|
||||||
|
inner = inner.replace(/^-|-$/g, "").trim();
|
||||||
|
if (parser.rawMode && open + inner !== "{%endraw") {
|
||||||
|
return index + 1
|
||||||
|
}
|
||||||
|
var text = src.slice(lastEnd, index);
|
||||||
|
lastEnd = index + open.length + match.length;
|
||||||
|
if (trimLeading) text = trimLeft(text);
|
||||||
|
if (wsCollapseLeft) text = trimRight(text);
|
||||||
|
if (wsCollapseRight) trimLeading = true;
|
||||||
|
if (open === "{{{") {
|
||||||
|
open = "{{";
|
||||||
|
inner += "|safe"
|
||||||
|
}
|
||||||
|
parser.textHandler(text);
|
||||||
|
parser.tokenHandler(open, inner)
|
||||||
|
});
|
||||||
|
var text = src.slice(lastEnd);
|
||||||
|
if (trimLeading) text = trimLeft(text);
|
||||||
|
this.textHandler(text)
|
||||||
|
};
|
||||||
|
Parser.prototype.textHandler = function(text) {
|
||||||
|
this.push("write(" + JSON.stringify(text) + ");")
|
||||||
|
};
|
||||||
|
Parser.prototype.tokenHandler = function(open, inner) {
|
||||||
|
var type = delimeters[open];
|
||||||
|
if (type === "directive") {
|
||||||
|
this.compileTag(inner)
|
||||||
|
} else if (type === "output") {
|
||||||
|
var extracted = this.extractEnt(inner, STRINGS, "@");
|
||||||
|
extracted.src = extracted.src.replace(/\|\|/g, "~").split("|");
|
||||||
|
extracted.src = extracted.src.map(function(part) {
|
||||||
|
return part.split("~").join("||")
|
||||||
|
});
|
||||||
|
var parts = this.injectEnt(extracted, "@");
|
||||||
|
if (parts.length > 1) {
|
||||||
|
var filters = parts.slice(1).map(this.parseFilter.bind(this));
|
||||||
|
this.push("filter(" + this.parseExpr(parts[0]) + "," + filters.join(",") + ");")
|
||||||
|
} else {
|
||||||
|
this.push("filter(" + this.parseExpr(parts[0]) + ");")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Parser.prototype.compileTag = function(str) {
|
||||||
|
var directive = str.split(" ")[0];
|
||||||
|
var handler = tagHandlers[directive];
|
||||||
|
if (!handler) {
|
||||||
|
throw new Error("Invalid tag: " + str)
|
||||||
|
}
|
||||||
|
handler.call(this, str.slice(directive.length).trim())
|
||||||
|
};
|
||||||
|
Parser.prototype.parseFilter = function(src) {
|
||||||
|
src = src.trim();
|
||||||
|
var match = src.match(/[:(]/);
|
||||||
|
var i = match ? match.index : -1;
|
||||||
|
if (i < 0) return JSON.stringify([src]);
|
||||||
|
var name = src.slice(0, i);
|
||||||
|
var args = src.charAt(i) === ":" ? src.slice(i + 1) : src.slice(i + 1, -1);
|
||||||
|
args = this.parseExpr(args, {
|
||||||
|
terms: true
|
||||||
|
});
|
||||||
|
return "[" + JSON.stringify(name) + "," + args + "]"
|
||||||
|
};
|
||||||
|
Parser.prototype.extractEnt = function(src, regex, placeholder) {
|
||||||
|
var subs = [],
|
||||||
|
isFunc = typeof placeholder == "function";
|
||||||
|
src = src.replace(regex, function(str) {
|
||||||
|
var replacement = isFunc ? placeholder(str) : placeholder;
|
||||||
|
if (replacement) {
|
||||||
|
subs.push(str);
|
||||||
|
return replacement
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
src: src,
|
||||||
|
subs: subs
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Parser.prototype.injectEnt = function(extracted, placeholder) {
|
||||||
|
var src = extracted.src,
|
||||||
|
subs = extracted.subs,
|
||||||
|
isArr = Array.isArray(src);
|
||||||
|
var arr = isArr ? src : [src];
|
||||||
|
var re = new RegExp("[" + placeholder + "]", "g"),
|
||||||
|
i = 0;
|
||||||
|
arr.forEach(function(src, index) {
|
||||||
|
arr[index] = src.replace(re, function() {
|
||||||
|
return subs[i++]
|
||||||
|
})
|
||||||
|
});
|
||||||
|
return isArr ? arr : arr[0]
|
||||||
|
};
|
||||||
|
Parser.prototype.replaceComplex = function(s) {
|
||||||
|
var parsed = this.extractEnt(s, /i(\.i|\[[@#i]\])+/g, "v");
|
||||||
|
parsed.src = parsed.src.replace(NON_PRIMITIVES, "~");
|
||||||
|
return this.injectEnt(parsed, "v")
|
||||||
|
};
|
||||||
|
Parser.prototype.parseExpr = function(src, opts) {
|
||||||
|
opts = opts || {};
|
||||||
|
var parsed1 = this.extractEnt(src, STRINGS, "@");
|
||||||
|
parsed1.src = parsed1.src.replace(EOPS, function(s, before, op, after) {
|
||||||
|
return op in operators ? before + operators[op] + after : s
|
||||||
|
});
|
||||||
|
var parsed2 = this.extractEnt(parsed1.src, IDENTS_AND_NUMS, function(s) {
|
||||||
|
return s in constants || NUMBER.test(s) ? "#" : null
|
||||||
|
});
|
||||||
|
var parsed3 = this.extractEnt(parsed2.src, IDENTIFIERS, "i");
|
||||||
|
parsed3.src = parsed3.src.replace(/\s+/g, "");
|
||||||
|
var simplified = parsed3.src;
|
||||||
|
while (simplified !== (simplified = this.replaceComplex(simplified)));
|
||||||
|
while (simplified !== (simplified = simplified.replace(/i(\.i|\[[@#i]\])+/, "v")));
|
||||||
|
simplified = simplified.replace(/[iv]\[v?\]/g, "x");
|
||||||
|
simplified = simplified.replace(/[@#~v]/g, "i");
|
||||||
|
simplified = simplified.replace(OPERATORS, "%");
|
||||||
|
simplified = simplified.replace(/!+[i]/g, "i");
|
||||||
|
var terms = opts.terms ? simplified.split(",") : [simplified];
|
||||||
|
terms.forEach(function(term) {
|
||||||
|
while (term !== (term = term.replace(/\(i(%i)*\)/g, "i")));
|
||||||
|
if (!term.match(/^i(%i)*/)) {
|
||||||
|
throw new Error("Invalid expression: " + src + " " + term)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
parsed3.src = parsed3.src.replace(VARIABLES, this.parseVar.bind(this));
|
||||||
|
parsed2.src = this.injectEnt(parsed3, "i");
|
||||||
|
parsed1.src = this.injectEnt(parsed2, "#");
|
||||||
|
return this.injectEnt(parsed1, "@")
|
||||||
|
};
|
||||||
|
Parser.prototype.parseVar = function(src) {
|
||||||
|
var args = Array.prototype.slice.call(arguments);
|
||||||
|
var str = args.pop(),
|
||||||
|
index = args.pop();
|
||||||
|
if (src === "i" && str.charAt(index + 1) === ":") {
|
||||||
|
return '"i"'
|
||||||
|
}
|
||||||
|
var parts = ['"i"'];
|
||||||
|
src.replace(ACCESSOR, function(part) {
|
||||||
|
if (part === ".i") {
|
||||||
|
parts.push('"i"')
|
||||||
|
} else if (part === "[i]") {
|
||||||
|
parts.push('get("i")')
|
||||||
|
} else {
|
||||||
|
parts.push(part.slice(1, -1))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return "get(" + parts.join(",") + ")"
|
||||||
|
};
|
||||||
|
Parser.prototype.escName = function(str) {
|
||||||
|
return str.replace(/\W/g, function(s) {
|
||||||
|
return "$" + s.charCodeAt(0).toString(16)
|
||||||
|
})
|
||||||
|
};
|
||||||
|
Parser.prototype.parseQuoted = function(str) {
|
||||||
|
if (str.charAt(0) === "'") {
|
||||||
|
str = str.slice(1, -1).replace(/\\.|"/, function(s) {
|
||||||
|
if (s === "\\'") return "'";
|
||||||
|
return s.charAt(0) === "\\" ? s : "\\" + s
|
||||||
|
});
|
||||||
|
str = '"' + str + '"'
|
||||||
|
}
|
||||||
|
return JSON.parse(str)
|
||||||
|
};
|
||||||
|
var tagHandlers = {
|
||||||
|
if: function(expr) {
|
||||||
|
this.push("if (" + this.parseExpr(expr) + ") {");
|
||||||
|
this.nest.unshift("if")
|
||||||
|
},
|
||||||
|
else: function() {
|
||||||
|
if (this.nest[0] === "for") {
|
||||||
|
this.push("}, function() {")
|
||||||
|
} else {
|
||||||
|
this.push("} else {")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
elseif: function(expr) {
|
||||||
|
this.push("} else if (" + this.parseExpr(expr) + ") {")
|
||||||
|
},
|
||||||
|
endif: function() {
|
||||||
|
this.nest.shift();
|
||||||
|
this.push("}")
|
||||||
|
},
|
||||||
|
for: function(str) {
|
||||||
|
var i = str.indexOf(" in ");
|
||||||
|
var name = str.slice(0, i).trim();
|
||||||
|
var expr = str.slice(i + 4).trim();
|
||||||
|
this.push("each(" + this.parseExpr(expr) + "," + JSON.stringify(name) + ",function() {");
|
||||||
|
this.nest.unshift("for")
|
||||||
|
},
|
||||||
|
endfor: function() {
|
||||||
|
this.nest.shift();
|
||||||
|
this.push("});")
|
||||||
|
},
|
||||||
|
raw: function() {
|
||||||
|
this.rawMode = true
|
||||||
|
},
|
||||||
|
endraw: function() {
|
||||||
|
this.rawMode = false
|
||||||
|
},
|
||||||
|
set: function(stmt) {
|
||||||
|
var i = stmt.indexOf("=");
|
||||||
|
var name = stmt.slice(0, i).trim();
|
||||||
|
var expr = stmt.slice(i + 1).trim();
|
||||||
|
this.push("set(" + JSON.stringify(name) + "," + this.parseExpr(expr) + ");")
|
||||||
|
},
|
||||||
|
block: function(name) {
|
||||||
|
if (this.isParent) {
|
||||||
|
++this.parentBlocks;
|
||||||
|
var blockName = "block_" + (this.escName(name) || this.parentBlocks);
|
||||||
|
this.push("block(typeof " + blockName + ' == "function" ? ' + blockName + " : function() {")
|
||||||
|
} else if (this.hasParent) {
|
||||||
|
this.isSilent = false;
|
||||||
|
++this.childBlocks;
|
||||||
|
blockName = "block_" + (this.escName(name) || this.childBlocks);
|
||||||
|
this.push("function " + blockName + "() {")
|
||||||
|
}
|
||||||
|
this.nest.unshift("block")
|
||||||
|
},
|
||||||
|
endblock: function() {
|
||||||
|
this.nest.shift();
|
||||||
|
if (this.isParent) {
|
||||||
|
this.push("});")
|
||||||
|
} else if (this.hasParent) {
|
||||||
|
this.push("}");
|
||||||
|
this.isSilent = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
extends: function(name) {
|
||||||
|
name = this.parseQuoted(name);
|
||||||
|
var parentSrc = this.readTemplateFile(name);
|
||||||
|
this.isParent = true;
|
||||||
|
this.tokenize(parentSrc);
|
||||||
|
this.isParent = false;
|
||||||
|
this.hasParent = true;
|
||||||
|
this.isSilent = true
|
||||||
|
},
|
||||||
|
include: function(name) {
|
||||||
|
name = this.parseQuoted(name);
|
||||||
|
var incSrc = this.readTemplateFile(name);
|
||||||
|
this.isInclude = true;
|
||||||
|
this.tokenize(incSrc);
|
||||||
|
this.isInclude = false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
tagHandlers.assign = tagHandlers.set;
|
||||||
|
tagHandlers.elif = tagHandlers.elseif;
|
||||||
|
var getRuntime = function runtime(data, opts) {
|
||||||
|
var defaults = {
|
||||||
|
autoEscape: "toJson"
|
||||||
|
};
|
||||||
|
var _toString = Object.prototype.toString;
|
||||||
|
var _hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||||
|
var getKeys = Object.keys || function(obj) {
|
||||||
|
var keys = [];
|
||||||
|
for (var n in obj)
|
||||||
|
if (_hasOwnProperty.call(obj, n)) keys.push(n);
|
||||||
|
return keys
|
||||||
|
};
|
||||||
|
var isArray = Array.isArray || function(obj) {
|
||||||
|
return _toString.call(obj) === "[object Array]"
|
||||||
|
};
|
||||||
|
var create = Object.create || function(obj) {
|
||||||
|
function F() {}
|
||||||
|
F.prototype = obj;
|
||||||
|
return new F
|
||||||
|
};
|
||||||
|
var toString = function(val) {
|
||||||
|
if (val == null) return "";
|
||||||
|
return typeof val.toString == "function" ? val.toString() : _toString.call(val)
|
||||||
|
};
|
||||||
|
var extend = function(dest, src) {
|
||||||
|
var keys = getKeys(src);
|
||||||
|
for (var i = 0, len = keys.length; i < len; i++) {
|
||||||
|
var key = keys[i];
|
||||||
|
dest[key] = src[key]
|
||||||
|
}
|
||||||
|
return dest
|
||||||
|
};
|
||||||
|
var get = function() {
|
||||||
|
var val, n = arguments[0],
|
||||||
|
c = stack.length;
|
||||||
|
while (c--) {
|
||||||
|
val = stack[c][n];
|
||||||
|
if (typeof val != "undefined") break
|
||||||
|
}
|
||||||
|
for (var i = 1, len = arguments.length; i < len; i++) {
|
||||||
|
if (val == null) continue;
|
||||||
|
n = arguments[i];
|
||||||
|
val = _hasOwnProperty.call(val, n) ? val[n] : typeof val._get == "function" ? val[n] = val._get(n) : null
|
||||||
|
}
|
||||||
|
return val == null ? "" : val
|
||||||
|
};
|
||||||
|
var set = function(n, val) {
|
||||||
|
stack[stack.length - 1][n] = val
|
||||||
|
};
|
||||||
|
var push = function(ctx) {
|
||||||
|
stack.push(ctx || {})
|
||||||
|
};
|
||||||
|
var pop = function() {
|
||||||
|
stack.pop()
|
||||||
|
};
|
||||||
|
var write = function(str) {
|
||||||
|
output.push(str)
|
||||||
|
};
|
||||||
|
var filter = function(val) {
|
||||||
|
for (var i = 1, len = arguments.length; i < len; i++) {
|
||||||
|
var arr = arguments[i],
|
||||||
|
name = arr[0],
|
||||||
|
filter = filters[name];
|
||||||
|
if (filter) {
|
||||||
|
arr[0] = val;
|
||||||
|
val = filter.apply(data, arr)
|
||||||
|
} else {
|
||||||
|
throw new Error("Invalid filter: " + name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (opts.autoEscape && name !== opts.autoEscape && name !== "safe") {
|
||||||
|
val = filters[opts.autoEscape].call(data, val)
|
||||||
|
}
|
||||||
|
output.push(val)
|
||||||
|
};
|
||||||
|
var each = function(obj, loopvar, fn1, fn2) {
|
||||||
|
if (obj == null) return;
|
||||||
|
var arr = isArray(obj) ? obj : getKeys(obj),
|
||||||
|
len = arr.length;
|
||||||
|
var ctx = {
|
||||||
|
loop: {
|
||||||
|
length: len,
|
||||||
|
first: arr[0],
|
||||||
|
last: arr[len - 1]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
push(ctx);
|
||||||
|
for (var i = 0; i < len; i++) {
|
||||||
|
extend(ctx.loop, {
|
||||||
|
index: i + 1,
|
||||||
|
index0: i
|
||||||
|
});
|
||||||
|
fn1(ctx[loopvar] = arr[i])
|
||||||
|
}
|
||||||
|
if (len === 0 && fn2) fn2();
|
||||||
|
pop()
|
||||||
|
};
|
||||||
|
var block = function(fn) {
|
||||||
|
push();
|
||||||
|
fn();
|
||||||
|
pop()
|
||||||
|
};
|
||||||
|
var render = function() {
|
||||||
|
return output.join("")
|
||||||
|
};
|
||||||
|
data = data || {};
|
||||||
|
opts = extend(defaults, opts || {});
|
||||||
|
var filters = extend({
|
||||||
|
html: function(val) {
|
||||||
|
return toString(val).split("&").join("&").split("<").join("<").split(">").join(">").split('"').join(""")
|
||||||
|
},
|
||||||
|
safe: function(val) {
|
||||||
|
return val
|
||||||
|
},
|
||||||
|
toJson: function(val) {
|
||||||
|
if (typeof val === "object") {
|
||||||
|
return JSON.stringify(val)
|
||||||
|
}
|
||||||
|
return toString(val)
|
||||||
|
}
|
||||||
|
}, opts.filters || {});
|
||||||
|
var stack = [create(data || {})],
|
||||||
|
output = [];
|
||||||
|
return {
|
||||||
|
get: get,
|
||||||
|
set: set,
|
||||||
|
push: push,
|
||||||
|
pop: pop,
|
||||||
|
write: write,
|
||||||
|
filter: filter,
|
||||||
|
each: each,
|
||||||
|
block: block,
|
||||||
|
render: render
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var runtime;
|
||||||
|
jinja.compile = function(markup, opts) {
|
||||||
|
opts = opts || {};
|
||||||
|
var parser = new Parser;
|
||||||
|
parser.readTemplateFile = this.readTemplateFile;
|
||||||
|
var code = [];
|
||||||
|
code.push("function render($) {");
|
||||||
|
code.push("var get = $.get, set = $.set, push = $.push, pop = $.pop, write = $.write, filter = $.filter, each = $.each, block = $.block;");
|
||||||
|
code.push.apply(code, parser.parse(markup));
|
||||||
|
code.push("return $.render();");
|
||||||
|
code.push("}");
|
||||||
|
code = code.join("\n");
|
||||||
|
if (opts.runtime === false) {
|
||||||
|
var fn = new Function("data", "options", "return (" + code + ")(runtime(data, options))")
|
||||||
|
} else {
|
||||||
|
runtime = runtime || (runtime = getRuntime.toString());
|
||||||
|
fn = new Function("data", "options", "return (" + code + ")((" + runtime + ")(data, options))")
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
render: fn
|
||||||
|
}
|
||||||
|
};
|
||||||
|
jinja.render = function(markup, data, opts) {
|
||||||
|
var tmpl = jinja.compile(markup);
|
||||||
|
return tmpl.render(data, opts)
|
||||||
|
};
|
||||||
|
jinja.templateFiles = [];
|
||||||
|
jinja.readTemplateFile = function(name) {
|
||||||
|
var templateFiles = this.templateFiles || [];
|
||||||
|
var templateFile = templateFiles[name];
|
||||||
|
if (templateFile == null) {
|
||||||
|
throw new Error("Template file not found: " + name)
|
||||||
|
}
|
||||||
|
return templateFile
|
||||||
|
};
|
||||||
|
|
||||||
|
function trimLeft(str) {
|
||||||
|
return str.replace(LEADING_SPACE, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
function trimRight(str) {
|
||||||
|
return str.replace(TRAILING_SPACE, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
function matchAll(str, reg, fn) {
|
||||||
|
reg = new RegExp(reg.source, "g" + (reg.ignoreCase ? "i" : "") + (reg.multiline ? "m" : ""));
|
||||||
|
var match;
|
||||||
|
while (match = reg.exec(str)) {
|
||||||
|
var result = fn(match[0], match.index, str);
|
||||||
|
if (typeof result == "number") {
|
||||||
|
reg.lastIndex = result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@ -0,0 +1,329 @@
|
|||||||
|
# coding=utf-8
|
||||||
|
# !/usr/bin/python
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
作者 丢丢喵推荐 🚓 内容均从互联网收集而来 仅供交流学习使用 版权归原创者所有 如侵犯了您的权益 请通知作者 将及时删除侵权内容
|
||||||
|
====================Diudiumiao====================
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from Crypto.Util.Padding import unpad
|
||||||
|
from Crypto.Util.Padding import pad
|
||||||
|
from urllib.parse import unquote
|
||||||
|
from Crypto.Cipher import ARC4
|
||||||
|
from urllib.parse import quote
|
||||||
|
from base.spider import Spider
|
||||||
|
from Crypto.Cipher import AES
|
||||||
|
from datetime import datetime
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
from base64 import b64decode
|
||||||
|
import urllib.request
|
||||||
|
import urllib.parse
|
||||||
|
import datetime
|
||||||
|
import binascii
|
||||||
|
import requests
|
||||||
|
import base64
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
import os
|
||||||
|
|
||||||
|
sys.path.append('..')
|
||||||
|
|
||||||
|
xurl = "https://djw1.com"
|
||||||
|
|
||||||
|
headerx = {
|
||||||
|
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.87 Safari/537.36'
|
||||||
|
}
|
||||||
|
|
||||||
|
class Spider(Spider):
|
||||||
|
global xurl
|
||||||
|
global headerx
|
||||||
|
|
||||||
|
def getName(self):
|
||||||
|
return "首页"
|
||||||
|
|
||||||
|
def init(self, extend):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def isVideoFormat(self, url):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def manualVideoCheck(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def extract_middle_text(self, text, start_str, end_str, pl, start_index1: str = '', end_index2: str = ''):
|
||||||
|
if pl == 3:
|
||||||
|
plx = []
|
||||||
|
while True:
|
||||||
|
start_index = text.find(start_str)
|
||||||
|
if start_index == -1:
|
||||||
|
break
|
||||||
|
end_index = text.find(end_str, start_index + len(start_str))
|
||||||
|
if end_index == -1:
|
||||||
|
break
|
||||||
|
middle_text = text[start_index + len(start_str):end_index]
|
||||||
|
plx.append(middle_text)
|
||||||
|
text = text.replace(start_str + middle_text + end_str, '')
|
||||||
|
if len(plx) > 0:
|
||||||
|
purl = ''
|
||||||
|
for i in range(len(plx)):
|
||||||
|
matches = re.findall(start_index1, plx[i])
|
||||||
|
output = ""
|
||||||
|
for match in matches:
|
||||||
|
match3 = re.search(r'(?:^|[^0-9])(\d+)(?:[^0-9]|$)', match[1])
|
||||||
|
if match3:
|
||||||
|
number = match3.group(1)
|
||||||
|
else:
|
||||||
|
number = 0
|
||||||
|
if 'http' not in match[0]:
|
||||||
|
output += f"#{match[1]}${number}{xurl}{match[0]}"
|
||||||
|
else:
|
||||||
|
output += f"#{match[1]}${number}{match[0]}"
|
||||||
|
output = output[1:]
|
||||||
|
purl = purl + output + "$$$"
|
||||||
|
purl = purl[:-3]
|
||||||
|
return purl
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
|
else:
|
||||||
|
start_index = text.find(start_str)
|
||||||
|
if start_index == -1:
|
||||||
|
return ""
|
||||||
|
end_index = text.find(end_str, start_index + len(start_str))
|
||||||
|
if end_index == -1:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
if pl == 0:
|
||||||
|
middle_text = text[start_index + len(start_str):end_index]
|
||||||
|
return middle_text.replace("\\", "")
|
||||||
|
|
||||||
|
if pl == 1:
|
||||||
|
middle_text = text[start_index + len(start_str):end_index]
|
||||||
|
matches = re.findall(start_index1, middle_text)
|
||||||
|
if matches:
|
||||||
|
jg = ' '.join(matches)
|
||||||
|
return jg
|
||||||
|
|
||||||
|
if pl == 2:
|
||||||
|
middle_text = text[start_index + len(start_str):end_index]
|
||||||
|
matches = re.findall(start_index1, middle_text)
|
||||||
|
if matches:
|
||||||
|
new_list = [f'{item}' for item in matches]
|
||||||
|
jg = '$$$'.join(new_list)
|
||||||
|
return jg
|
||||||
|
|
||||||
|
def homeContent(self, filter):
|
||||||
|
result = {"class": []}
|
||||||
|
|
||||||
|
detail = requests.get(url=xurl + "/all/", headers=headerx)
|
||||||
|
detail.encoding = "utf-8"
|
||||||
|
res = detail.text
|
||||||
|
|
||||||
|
doc = BeautifulSoup(res, "lxml")
|
||||||
|
|
||||||
|
soups = doc.find_all('section', class_="container items")
|
||||||
|
|
||||||
|
for soup in soups:
|
||||||
|
vods = soup.find_all('li')
|
||||||
|
|
||||||
|
for vod in vods:
|
||||||
|
|
||||||
|
id = vod.find('a')['href']
|
||||||
|
|
||||||
|
name = vod.text.strip()
|
||||||
|
|
||||||
|
result["class"].append({"type_id": id, "type_name": "" + name})
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def homeVideoContent(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def categoryContent(self, cid, pg, filter, ext):
|
||||||
|
result = {}
|
||||||
|
videos = []
|
||||||
|
|
||||||
|
if pg:
|
||||||
|
page = int(pg)
|
||||||
|
else:
|
||||||
|
page = 1
|
||||||
|
|
||||||
|
url = f'{cid}page/{str(page)}/'
|
||||||
|
detail = requests.get(url=url, headers=headerx)
|
||||||
|
detail.encoding = "utf-8"
|
||||||
|
res = detail.text
|
||||||
|
doc = BeautifulSoup(res, "lxml")
|
||||||
|
|
||||||
|
soups = doc.find_all('section', class_="container items")
|
||||||
|
|
||||||
|
for soup in soups:
|
||||||
|
vods = soup.find_all('li')
|
||||||
|
|
||||||
|
for vod in vods:
|
||||||
|
|
||||||
|
name = vod.find('img')['alt']
|
||||||
|
|
||||||
|
ids = vod.find('a', class_="image-line")
|
||||||
|
id = ids['href']
|
||||||
|
|
||||||
|
pic = vod.find('img')['src']
|
||||||
|
|
||||||
|
remark = self.extract_middle_text(str(vod), 'class="remarks light">', '<', 0)
|
||||||
|
|
||||||
|
video = {
|
||||||
|
"vod_id": id,
|
||||||
|
"vod_name": name,
|
||||||
|
"vod_pic": pic,
|
||||||
|
"vod_remarks": '▶️' + remark
|
||||||
|
}
|
||||||
|
videos.append(video)
|
||||||
|
|
||||||
|
result = {'list': videos}
|
||||||
|
result['page'] = pg
|
||||||
|
result['pagecount'] = 9999
|
||||||
|
result['limit'] = 90
|
||||||
|
result['total'] = 999999
|
||||||
|
return result
|
||||||
|
|
||||||
|
def detailContent(self, ids):
|
||||||
|
did = ids[0]
|
||||||
|
result = {}
|
||||||
|
videos = []
|
||||||
|
xianlu = ''
|
||||||
|
bofang = ''
|
||||||
|
|
||||||
|
if 'http' not in did:
|
||||||
|
did = xurl + did
|
||||||
|
|
||||||
|
res = requests.get(url=did, headers=headerx)
|
||||||
|
res.encoding = "utf-8"
|
||||||
|
res = res.text
|
||||||
|
doc = BeautifulSoup(res, "lxml")
|
||||||
|
|
||||||
|
url = 'https://fs-im-kefu.7moor-fs1.com/ly/4d2c3f00-7d4c-11e5-af15-41bf63ae4ea0/1732707176882/jiduo.txt'
|
||||||
|
response = requests.get(url)
|
||||||
|
response.encoding = 'utf-8'
|
||||||
|
code = response.text
|
||||||
|
name = self.extract_middle_text(code, "s1='", "'", 0)
|
||||||
|
Jumps = self.extract_middle_text(code, "s2='", "'", 0)
|
||||||
|
|
||||||
|
content = self.extract_middle_text(res,'class="info-detail">','<', 0)
|
||||||
|
|
||||||
|
remarks = self.extract_middle_text(res, 'class="info-mark">', '<', 0)
|
||||||
|
|
||||||
|
year = self.extract_middle_text(res, 'class="info-addtime">', '<', 0)
|
||||||
|
|
||||||
|
if name not in content:
|
||||||
|
bofang = Jumps
|
||||||
|
xianlu = '1'
|
||||||
|
else:
|
||||||
|
soups = doc.find('div', class_="ep-list-items")
|
||||||
|
|
||||||
|
soup = soups.find_all('a')
|
||||||
|
|
||||||
|
for sou in soup:
|
||||||
|
|
||||||
|
id = sou['href']
|
||||||
|
|
||||||
|
name = sou.text.strip()
|
||||||
|
|
||||||
|
bofang = bofang + name + '$' + id + '#'
|
||||||
|
|
||||||
|
bofang = bofang[:-1]
|
||||||
|
|
||||||
|
xianlu = '专线'
|
||||||
|
|
||||||
|
videos.append({
|
||||||
|
"vod_id": did,
|
||||||
|
"vod_remarks": remarks,
|
||||||
|
"vod_year": year,
|
||||||
|
"vod_content": content,
|
||||||
|
"vod_play_from": xianlu,
|
||||||
|
"vod_play_url": bofang
|
||||||
|
})
|
||||||
|
|
||||||
|
result['list'] = videos
|
||||||
|
return result
|
||||||
|
|
||||||
|
def playerContent(self, flag, id, vipFlags):
|
||||||
|
|
||||||
|
res = requests.get(url=id, headers=headerx)
|
||||||
|
res.encoding = "utf-8"
|
||||||
|
res = res.text
|
||||||
|
|
||||||
|
url = self.extract_middle_text(res, '"wwm3u8":"', '"', 0).replace('\\', '')
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
result["parse"] = 0
|
||||||
|
result["playUrl"] = ''
|
||||||
|
result["url"] = url
|
||||||
|
result["header"] = headerx
|
||||||
|
return result
|
||||||
|
|
||||||
|
def searchContentPage(self, key, quick, pg):
|
||||||
|
result = {}
|
||||||
|
videos = []
|
||||||
|
|
||||||
|
if pg:
|
||||||
|
page = int(pg)
|
||||||
|
else:
|
||||||
|
page = 1
|
||||||
|
|
||||||
|
url = f'{xurl}/search/{key}/page/{str(page)}/'
|
||||||
|
detail = requests.get(url=url, headers=headerx)
|
||||||
|
detail.encoding = "utf-8"
|
||||||
|
res = detail.text
|
||||||
|
doc = BeautifulSoup(res, "lxml")
|
||||||
|
|
||||||
|
soups = doc.find_all('section', class_="container items")
|
||||||
|
|
||||||
|
for soup in soups:
|
||||||
|
vods = soup.find_all('li')
|
||||||
|
|
||||||
|
for vod in vods:
|
||||||
|
|
||||||
|
name = vod.find('img')['alt']
|
||||||
|
|
||||||
|
ids = vod.find('a', class_="image-line")
|
||||||
|
id = ids['href']
|
||||||
|
|
||||||
|
pic = vod.find('img')['src']
|
||||||
|
|
||||||
|
remark = self.extract_middle_text(str(vod), 'class="remarks light">', '<', 0)
|
||||||
|
|
||||||
|
video = {
|
||||||
|
"vod_id": id,
|
||||||
|
"vod_name": name,
|
||||||
|
"vod_pic": pic,
|
||||||
|
"vod_remarks": '▶️' + remark
|
||||||
|
}
|
||||||
|
videos.append(video)
|
||||||
|
|
||||||
|
result['list'] = videos
|
||||||
|
result['page'] = pg
|
||||||
|
result['pagecount'] = 9999
|
||||||
|
result['limit'] = 90
|
||||||
|
result['total'] = 999999
|
||||||
|
return result
|
||||||
|
|
||||||
|
def searchContent(self, key, quick, pg="1"):
|
||||||
|
return self.searchContentPage(key, quick, '1')
|
||||||
|
|
||||||
|
def localProxy(self, params):
|
||||||
|
if params['type'] == "m3u8":
|
||||||
|
return self.proxyM3u8(params)
|
||||||
|
elif params['type'] == "media":
|
||||||
|
return self.proxyMedia(params)
|
||||||
|
elif params['type'] == "ts":
|
||||||
|
return self.proxyTs(params)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -0,0 +1,314 @@
|
|||||||
|
# coding=utf-8
|
||||||
|
# !/usr/bin/python
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
作者 丢丢喵 🚓 内容均从互联网收集而来 仅供交流学习使用 版权归原创者所有 如侵犯了您的权益 请通知作者 将及时删除侵权内容
|
||||||
|
====================Diudiumiao====================
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from Crypto.Util.Padding import unpad
|
||||||
|
from Crypto.Util.Padding import pad
|
||||||
|
from urllib.parse import unquote
|
||||||
|
from Crypto.Cipher import ARC4
|
||||||
|
from urllib.parse import quote
|
||||||
|
from base.spider import Spider
|
||||||
|
from Crypto.Cipher import AES
|
||||||
|
from datetime import datetime
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
from base64 import b64decode
|
||||||
|
import urllib.request
|
||||||
|
import urllib.parse
|
||||||
|
import datetime
|
||||||
|
import binascii
|
||||||
|
import requests
|
||||||
|
import base64
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
import os
|
||||||
|
|
||||||
|
sys.path.append('..')
|
||||||
|
|
||||||
|
xurl = "https://search.bilibili.com"
|
||||||
|
|
||||||
|
xurl1 = "https://api.live.bilibili.com"
|
||||||
|
|
||||||
|
headerx = {
|
||||||
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0'
|
||||||
|
}
|
||||||
|
|
||||||
|
class Spider(Spider):
|
||||||
|
global xurl
|
||||||
|
global xurl1
|
||||||
|
global headerx
|
||||||
|
|
||||||
|
def getName(self):
|
||||||
|
return "首页"
|
||||||
|
|
||||||
|
def init(self, extend):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def isVideoFormat(self, url):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def manualVideoCheck(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def extract_middle_text(self, text, start_str, end_str, pl, start_index1: str = '', end_index2: str = ''):
|
||||||
|
if pl == 3:
|
||||||
|
plx = []
|
||||||
|
while True:
|
||||||
|
start_index = text.find(start_str)
|
||||||
|
if start_index == -1:
|
||||||
|
break
|
||||||
|
end_index = text.find(end_str, start_index + len(start_str))
|
||||||
|
if end_index == -1:
|
||||||
|
break
|
||||||
|
middle_text = text[start_index + len(start_str):end_index]
|
||||||
|
plx.append(middle_text)
|
||||||
|
text = text.replace(start_str + middle_text + end_str, '')
|
||||||
|
if len(plx) > 0:
|
||||||
|
purl = ''
|
||||||
|
for i in range(len(plx)):
|
||||||
|
matches = re.findall(start_index1, plx[i])
|
||||||
|
output = ""
|
||||||
|
for match in matches:
|
||||||
|
match3 = re.search(r'(?:^|[^0-9])(\d+)(?:[^0-9]|$)', match[1])
|
||||||
|
if match3:
|
||||||
|
number = match3.group(1)
|
||||||
|
else:
|
||||||
|
number = 0
|
||||||
|
if 'http' not in match[0]:
|
||||||
|
output += f"#{match[1]}${number}{xurl}{match[0]}"
|
||||||
|
else:
|
||||||
|
output += f"#{match[1]}${number}{match[0]}"
|
||||||
|
output = output[1:]
|
||||||
|
purl = purl + output + "$$$"
|
||||||
|
purl = purl[:-3]
|
||||||
|
return purl
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
|
else:
|
||||||
|
start_index = text.find(start_str)
|
||||||
|
if start_index == -1:
|
||||||
|
return ""
|
||||||
|
end_index = text.find(end_str, start_index + len(start_str))
|
||||||
|
if end_index == -1:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
if pl == 0:
|
||||||
|
middle_text = text[start_index + len(start_str):end_index]
|
||||||
|
return middle_text.replace("\\", "")
|
||||||
|
|
||||||
|
if pl == 1:
|
||||||
|
middle_text = text[start_index + len(start_str):end_index]
|
||||||
|
matches = re.findall(start_index1, middle_text)
|
||||||
|
if matches:
|
||||||
|
jg = ' '.join(matches)
|
||||||
|
return jg
|
||||||
|
|
||||||
|
if pl == 2:
|
||||||
|
middle_text = text[start_index + len(start_str):end_index]
|
||||||
|
matches = re.findall(start_index1, middle_text)
|
||||||
|
if matches:
|
||||||
|
new_list = [f'{item}' for item in matches]
|
||||||
|
jg = '$$$'.join(new_list)
|
||||||
|
return jg
|
||||||
|
|
||||||
|
def homeContent(self, filter):
|
||||||
|
result = {}
|
||||||
|
result = {"class": [{"type_id": "舞", "type_name": "舞蹈"},
|
||||||
|
{"type_id": "音乐", "type_name": "音乐"},
|
||||||
|
{"type_id": "手游", "type_name": "手游"},
|
||||||
|
{"type_id": "网游", "type_name": "网游"},
|
||||||
|
{"type_id": "单机游戏", "type_name": "单机游戏"},
|
||||||
|
{"type_id": "虚拟主播", "type_name": "虚拟主播"},
|
||||||
|
{"type_id": "电台", "type_name": "电台"},
|
||||||
|
{"type_id": "体育", "type_name": "体育"},
|
||||||
|
{"type_id": "聊天", "type_name": "聊天"},
|
||||||
|
{"type_id": "娱乐", "type_name": "娱乐"},
|
||||||
|
{"type_id": "电影", "type_name": "影视"},
|
||||||
|
{"type_id": "新闻", "type_name": "新闻"}]
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def homeVideoContent(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def categoryContent(self, cid, pg, filter, ext):
|
||||||
|
result = {}
|
||||||
|
videos = []
|
||||||
|
|
||||||
|
if pg:
|
||||||
|
page = int(pg)
|
||||||
|
else:
|
||||||
|
page = 1
|
||||||
|
|
||||||
|
url = f'{xurl}/live?keyword={cid}&page={str(page)}'
|
||||||
|
detail = requests.get(url=url, headers=headerx)
|
||||||
|
detail.encoding = "utf-8"
|
||||||
|
res = detail.text
|
||||||
|
doc = BeautifulSoup(res, "lxml")
|
||||||
|
|
||||||
|
soups = doc.find_all('div', class_="video-list-item")
|
||||||
|
|
||||||
|
for vod in soups:
|
||||||
|
|
||||||
|
names = vod.find('h3', class_="bili-live-card__info--tit")
|
||||||
|
name = names.text.strip().replace('直播中', '')
|
||||||
|
|
||||||
|
id = names.find('a')['href']
|
||||||
|
id = self.extract_middle_text(id, 'bilibili.com/', '?', 0)
|
||||||
|
|
||||||
|
pic = vod.find('img')['src']
|
||||||
|
if 'http' not in pic:
|
||||||
|
pic = "https:" + pic
|
||||||
|
|
||||||
|
remarks = vod.find('a', class_="bili-live-card__info--uname")
|
||||||
|
remark = remarks.text.strip()
|
||||||
|
|
||||||
|
video = {
|
||||||
|
"vod_id": id,
|
||||||
|
"vod_name": name,
|
||||||
|
"vod_pic": pic,
|
||||||
|
"vod_remarks": remark
|
||||||
|
}
|
||||||
|
videos.append(video)
|
||||||
|
|
||||||
|
result = {'list': videos}
|
||||||
|
result['page'] = pg
|
||||||
|
result['pagecount'] = 9999
|
||||||
|
result['limit'] = 90
|
||||||
|
result['total'] = 999999
|
||||||
|
return result
|
||||||
|
|
||||||
|
def detailContent(self, ids):
|
||||||
|
did = ids[0]
|
||||||
|
result = {}
|
||||||
|
videos = []
|
||||||
|
xianlu = ''
|
||||||
|
bofang = ''
|
||||||
|
|
||||||
|
url = f'{xurl1}/xlive/web-room/v2/index/getRoomPlayInfo?room_id={did}&platform=web&protocol=0,1&format=0,1,2&codec=0,1'
|
||||||
|
detail = requests.get(url=url, headers=headerx)
|
||||||
|
detail.encoding = "utf-8"
|
||||||
|
data = detail.json()
|
||||||
|
|
||||||
|
content = '欢迎观看哔哩直播'
|
||||||
|
|
||||||
|
setup = data['data']['playurl_info']['playurl']['stream']
|
||||||
|
|
||||||
|
nam = 0
|
||||||
|
|
||||||
|
for vod in setup:
|
||||||
|
|
||||||
|
try:
|
||||||
|
host = vod['format'][nam]['codec'][0]['url_info'][1]['host']
|
||||||
|
except (KeyError, IndexError):
|
||||||
|
continue
|
||||||
|
|
||||||
|
base = vod['format'][nam]['codec'][0]['base_url']
|
||||||
|
|
||||||
|
extra = vod['format'][nam]['codec'][0]['url_info'][1]['extra']
|
||||||
|
|
||||||
|
id = host + base + extra
|
||||||
|
|
||||||
|
nam = nam + 1
|
||||||
|
|
||||||
|
namc = f"{nam}号线路"
|
||||||
|
|
||||||
|
bofang = bofang + namc + '$' + id + '#'
|
||||||
|
|
||||||
|
bofang = bofang[:-1]
|
||||||
|
|
||||||
|
xianlu = '哔哩专线'
|
||||||
|
|
||||||
|
videos.append({
|
||||||
|
"vod_id": did,
|
||||||
|
"vod_content": content,
|
||||||
|
"vod_play_from": xianlu,
|
||||||
|
"vod_play_url": bofang
|
||||||
|
})
|
||||||
|
|
||||||
|
result['list'] = videos
|
||||||
|
return result
|
||||||
|
|
||||||
|
def playerContent(self, flag, id, vipFlags):
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
result["parse"] = 0
|
||||||
|
result["playUrl"] = ''
|
||||||
|
result["url"] = id
|
||||||
|
result["header"] = headerx
|
||||||
|
return result
|
||||||
|
|
||||||
|
def searchContentPage(self, key, quick, pg):
|
||||||
|
result = {}
|
||||||
|
videos = []
|
||||||
|
|
||||||
|
if pg:
|
||||||
|
page = int(pg)
|
||||||
|
else:
|
||||||
|
page = 1
|
||||||
|
|
||||||
|
url = f'{xurl}/live?keyword={key}&page={str(page)}'
|
||||||
|
detail = requests.get(url=url, headers=headerx)
|
||||||
|
detail.encoding = "utf-8"
|
||||||
|
res = detail.text
|
||||||
|
doc = BeautifulSoup(res, "lxml")
|
||||||
|
|
||||||
|
soups = doc.find_all('div', class_="video-list-item")
|
||||||
|
|
||||||
|
for vod in soups:
|
||||||
|
|
||||||
|
names = vod.find('h3', class_="bili-live-card__info--tit")
|
||||||
|
name = names.text.strip().replace('直播中', '')
|
||||||
|
|
||||||
|
id = names.find('a')['href']
|
||||||
|
id = self.extract_middle_text(id, 'bilibili.com/', '?', 0)
|
||||||
|
|
||||||
|
pic = vod.find('img')['src']
|
||||||
|
if 'http' not in pic:
|
||||||
|
pic = "https:" + pic
|
||||||
|
|
||||||
|
remarks = vod.find('a', class_="bili-live-card__info--uname")
|
||||||
|
remark = remarks.text.strip()
|
||||||
|
|
||||||
|
video = {
|
||||||
|
"vod_id": id,
|
||||||
|
"vod_name": name,
|
||||||
|
"vod_pic": pic,
|
||||||
|
"vod_remarks": remark
|
||||||
|
}
|
||||||
|
videos.append(video)
|
||||||
|
|
||||||
|
result['list'] = videos
|
||||||
|
result['page'] = pg
|
||||||
|
result['pagecount'] = 9999
|
||||||
|
result['limit'] = 90
|
||||||
|
result['total'] = 999999
|
||||||
|
return result
|
||||||
|
|
||||||
|
def searchContent(self, key, quick, pg="1"):
|
||||||
|
return self.searchContentPage(key, quick, '1')
|
||||||
|
|
||||||
|
def localProxy(self, params):
|
||||||
|
if params['type'] == "m3u8":
|
||||||
|
return self.proxyM3u8(params)
|
||||||
|
elif params['type'] == "media":
|
||||||
|
return self.proxyMedia(params)
|
||||||
|
elif params['type'] == "ts":
|
||||||
|
return self.proxyTs(params)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -0,0 +1,240 @@
|
|||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from base64 import b64encode, b64decode
|
||||||
|
from urllib.parse import quote, unquote
|
||||||
|
from pyquery import PyQuery as pq
|
||||||
|
from requests import Session, adapters
|
||||||
|
from urllib3.util.retry import Retry
|
||||||
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||||
|
sys.path.append('..')
|
||||||
|
from base.spider import Spider
|
||||||
|
|
||||||
|
class Spider(Spider):
|
||||||
|
def init(self, extend=""):
|
||||||
|
self.host = "https://www.22a5.com"
|
||||||
|
self.session = Session()
|
||||||
|
adapter = adapters.HTTPAdapter(max_retries=Retry(total=3, backoff_factor=0.5, status_forcelist=[429, 500, 502, 503, 504]), pool_connections=20, pool_maxsize=50)
|
||||||
|
self.session.mount("http://", adapter)
|
||||||
|
self.session.mount("https://", adapter)
|
||||||
|
self.headers = {"User-Agent": "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36"}
|
||||||
|
self.session.headers.update(self.headers)
|
||||||
|
|
||||||
|
def getName(self): return "爱听音乐"
|
||||||
|
def isVideoFormat(self, url): return bool(re.search(r'\.(m3u8|mp4|mp3|m4a|flv)(\?|$)', url or "", re.I))
|
||||||
|
def manualVideoCheck(self): return False
|
||||||
|
def destroy(self): self.session.close()
|
||||||
|
|
||||||
|
def homeContent(self, filter):
|
||||||
|
classes = [{"type_name": n, "type_id": i} for n, i in [("歌手","/singerlist/index/index/index/index.html"), ("TOP榜单","/list/top.html"), ("新歌榜","/list/new.html"), ("电台","/radiolist/index.html"), ("高清MV","/mvlist/oumei.html"), ("专辑","/albumlist/index.html"), ("歌单","/playtype/index.html")]]
|
||||||
|
filters = {p: d for p in [c["type_id"] for c in classes if "singer" not in c["type_id"]] if (d := self._fetch_filters(p))}
|
||||||
|
|
||||||
|
if "/radiolist/index.html" not in filters:
|
||||||
|
filters["/radiolist/index.html"] = [{"key": "id", "name": "分类", "value": [{"n": n, "v": v} for n,v in zip(["最新","最热","有声小说","相声","音乐","情感","国漫","影视","脱口秀","历史","儿童","教育","八卦","推理","头条"], ["index","hot","novel","xiangyi","music","emotion","game","yingshi","talkshow","history","children","education","gossip","tuili","headline"])]}]
|
||||||
|
|
||||||
|
filters["/singerlist/index/index/index/index.html"] = [
|
||||||
|
{"key": "area", "name": "地区", "value": [{"n": n, "v": v} for n,v in [("全部","index"),("华语","huayu"),("欧美","oumei"),("韩国","hanguo"),("日本","ribrn")]]},
|
||||||
|
{"key": "sex", "name": "性别", "value": [{"n": n, "v": v} for n,v in [("全部","index"),("男","male"),("女","girl"),("组合","band")]]},
|
||||||
|
{"key": "genre", "name": "流派", "value": [{"n": n, "v": v} for n,v in [("全部","index"),("流行","liuxing"),("电子","dianzi"),("摇滚","yaogun"),("嘻哈","xiha"),("R&B","rb"),("民谣","minyao"),("爵士","jueshi"),("古典","gudian")]]},
|
||||||
|
{"key": "char", "name": "字母", "value": [{"n": n, "v": v} for n,v in [("全部","index")] + [{"n": chr(i), "v": chr(i).lower()} for i in range(65, 91)]]}
|
||||||
|
]
|
||||||
|
return {"class": classes, "filters": filters, "list": []}
|
||||||
|
|
||||||
|
def homeVideoContent(self): return {"list": []}
|
||||||
|
|
||||||
|
def categoryContent(self, tid, pg, filter, extend):
|
||||||
|
pg = int(pg or 1)
|
||||||
|
url = tid
|
||||||
|
if "/singerlist/" in tid:
|
||||||
|
p = tid.split('/')
|
||||||
|
if len(p) >= 6:
|
||||||
|
url = "/".join(p[:2] + [extend.get(k, p[i]) for i, k in enumerate(["area", "sex", "genre"], 2)] + [f"{extend.get('char', 'index')}.html"])
|
||||||
|
elif "id" in extend and extend["id"] not in ["index", "top"]:
|
||||||
|
url = tid.replace("index.html", f"{extend['id']}.html").replace("top.html", f"{extend['id']}.html")
|
||||||
|
if url == tid: url = f"{tid.rsplit('/', 1)[0]}/{extend['id']}.html"
|
||||||
|
|
||||||
|
if pg > 1:
|
||||||
|
sep = "/" if any(x in url for x in ["/singerlist/", "/radiolist/", "/mvlist/", "/playtype/", "/list/"]) else "_"
|
||||||
|
url = re.sub(r'(_\d+|/\d+)?\.html$', f'{sep}{pg}.html', url)
|
||||||
|
|
||||||
|
doc = self.getpq(url)
|
||||||
|
return {"list": self._parse_list(doc(".play_list li, .video_list li, .pic_list li, .singer_list li, .ali li, .layui-row li, .base_l li"), tid), "page": pg, "pagecount": 9999, "limit": 90, "total": 999999}
|
||||||
|
|
||||||
|
def searchContent(self, key, quick, pg="1"):
|
||||||
|
return {"list": self._parse_list(self.getpq(f"/so/{quote(key)}/{pg}.html")(".base_l li, .play_list li"), "search"), "page": int(pg)}
|
||||||
|
|
||||||
|
def detailContent(self, ids):
|
||||||
|
url = self._abs(ids[0])
|
||||||
|
doc = self.getpq(url)
|
||||||
|
vod = {"vod_id": url, "vod_name": self._clean(doc("h1").text() or doc("title").text()), "vod_pic": self._abs(doc(".djpg img, .pic img, .djpic img").attr("src")), "vod_play_from": "爱听音乐", "vod_content": ""}
|
||||||
|
|
||||||
|
if any(x in url for x in ["/playlist/", "/album/", "/list/", "/singer/", "/special/", "/radio/", "/radiolist/"]):
|
||||||
|
eps = self._get_eps(doc)
|
||||||
|
page_urls = {self._abs(a.attr("href")) for a in doc(".page a, .dede_pages a, .pagelist a").items() if a.attr("href") and "javascript" not in a.attr("href")} - {url}
|
||||||
|
if page_urls:
|
||||||
|
with ThreadPoolExecutor(max_workers=5) as ex:
|
||||||
|
for r in as_completed([ex.submit(lambda u: self._get_eps(self.getpq(u)), u) for u in sorted(page_urls, key=lambda x: int(re.search(r'[_\/](\d+)\.html', x).group(1)) if re.search(r'[_\/](\d+)\.html', x) else 0)]):
|
||||||
|
eps.extend(r.result() or [])
|
||||||
|
if eps:
|
||||||
|
vod.update({"vod_play_from": "播放列表", "vod_play_url": "#".join(eps)})
|
||||||
|
return {"list": [vod]}
|
||||||
|
|
||||||
|
play_list = []
|
||||||
|
if mid := re.search(r'/(song|mp3|radio|radiolist|radioplay)/([^/]+)\.html', url):
|
||||||
|
lrc_url = f"{self.host}/plug/down.php?ac=music&lk=lrc&id={mid.group(2)}"
|
||||||
|
play_list = [f"播放${self.e64('0@@@@' + url + '|||' + lrc_url)}"]
|
||||||
|
|
||||||
|
elif vid := re.search(r'/(video|mp4)/([^/]+)\.html', url):
|
||||||
|
with ThreadPoolExecutor(max_workers=3) as ex:
|
||||||
|
fs = {ex.submit(self._api, "/plug/down.php", {"ac": "vplay", "id": vid.group(2), "q": q}): n for n, q in [("蓝光", 1080), ("超清", 720), ("高清", 480)]}
|
||||||
|
play_list = [f"{fs[f]}${self.e64('0@@@@'+u)}" for f in as_completed(fs) if (u := f.result())]
|
||||||
|
play_list.sort(key=lambda x: {"蓝":0, "超":1, "高":2}.get(x[0], 3))
|
||||||
|
|
||||||
|
vod["vod_play_url"] = "#".join(play_list) if play_list else f"解析失败${self.e64('1@@@@'+url)}"
|
||||||
|
return {"list": [vod]}
|
||||||
|
|
||||||
|
def playerContent(self, flag, id, vipFlags):
|
||||||
|
raw = self.d64(id).split("@@@@")[-1]
|
||||||
|
url, subt = raw.split("|||") if "|||" in raw else (raw, "")
|
||||||
|
url = url.replace(r"\/", "/")
|
||||||
|
|
||||||
|
if ".html" in url and not self.isVideoFormat(url):
|
||||||
|
if mid := re.search(r'/(song|mp3|radio|radiolist|radioplay)/([^/]+)\.html', url):
|
||||||
|
if r_url := self._api("/js/play.php", method="POST", data={"id": mid.group(2), "type": "music"}, headers={"Referer": url.replace("http://","https://"), "X-Requested-With": "XMLHttpRequest"}):
|
||||||
|
url = r_url if ".php" not in r_url else url
|
||||||
|
elif vid := re.search(r'/(video|mp4)/([^/]+)\.html', url):
|
||||||
|
with ThreadPoolExecutor(max_workers=3) as ex:
|
||||||
|
for f in as_completed([ex.submit(self._api, "/plug/down.php", {"ac": "vplay", "id": vid.group(2), "q": q}) for q in [1080, 720, 480]]):
|
||||||
|
if v_url := f.result():
|
||||||
|
url = v_url; break
|
||||||
|
|
||||||
|
result = {"parse": 0, "url": url, "header": {"User-Agent": self.headers["User-Agent"]}}
|
||||||
|
if "22a5.com" in url: result["header"]["Referer"] = self.host + "/"
|
||||||
|
|
||||||
|
# OK影视3.6.5+支持LRC格式滚动歌词
|
||||||
|
if subt:
|
||||||
|
try:
|
||||||
|
r = self.session.get(subt, headers={"Referer": self.host + "/"}, timeout=5)
|
||||||
|
lrc_content = r.text
|
||||||
|
if lrc_content:
|
||||||
|
# 过滤广告内容
|
||||||
|
lrc_content = self._filter_lrc_ads(lrc_content)
|
||||||
|
result["lrc"] = lrc_content
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _filter_lrc_ads(self, lrc_text):
|
||||||
|
"""过滤LRC歌词中的广告内容"""
|
||||||
|
lines = lrc_text.splitlines()
|
||||||
|
filtered_lines = []
|
||||||
|
|
||||||
|
# 广告关键词模式
|
||||||
|
ad_patterns = [
|
||||||
|
r'欢迎来访.*',
|
||||||
|
r'本站.*',
|
||||||
|
r'.*广告.*',
|
||||||
|
r'QQ群.*',
|
||||||
|
r'.*www\..*',
|
||||||
|
r'.*http.*',
|
||||||
|
r'.*\.com.*',
|
||||||
|
r'.*\.cn.*',
|
||||||
|
r'.*\.net.*',
|
||||||
|
r'.*音乐网.*',
|
||||||
|
r'.*提供.*',
|
||||||
|
r'.*下载.*',
|
||||||
|
]
|
||||||
|
|
||||||
|
for line in lines:
|
||||||
|
# 保留时间标签行,但过滤掉广告文本
|
||||||
|
if re.match(r'\[\d{2}:\d{2}', line):
|
||||||
|
# 检查是否包含广告
|
||||||
|
is_ad = False
|
||||||
|
for pattern in ad_patterns:
|
||||||
|
if re.search(pattern, line, re.IGNORECASE):
|
||||||
|
is_ad = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if not is_ad:
|
||||||
|
filtered_lines.append(line)
|
||||||
|
else:
|
||||||
|
# 非时间标签行(可能是元数据),保留
|
||||||
|
filtered_lines.append(line)
|
||||||
|
|
||||||
|
return '\n'.join(filtered_lines)
|
||||||
|
|
||||||
|
def localProxy(self, param):
|
||||||
|
url = unquote(param.get("url", ""))
|
||||||
|
type_ = param.get("type")
|
||||||
|
|
||||||
|
if type_ == "img":
|
||||||
|
return [200, "image/jpeg", self.session.get(url, headers={"Referer": self.host + "/"}, timeout=5).content, {}]
|
||||||
|
|
||||||
|
elif type_ == "lrc":
|
||||||
|
try:
|
||||||
|
r = self.session.get(url, headers={"Referer": self.host + "/"}, timeout=5)
|
||||||
|
# 同时过滤代理中的广告
|
||||||
|
lrc_content = r.text
|
||||||
|
lrc_content = self._filter_lrc_ads(lrc_content)
|
||||||
|
return [200, "application/octet-stream", lrc_content.encode('utf-8'), {}]
|
||||||
|
except:
|
||||||
|
return [404, "text/plain", "Error", {}]
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _parse_list(self, items, tid=""):
|
||||||
|
res = []
|
||||||
|
for li in items.items():
|
||||||
|
a = li("a").eq(0)
|
||||||
|
if not (href := a.attr("href")) or href == "/" or any(x in href for x in ["/user/", "/login/", "javascript"]): continue
|
||||||
|
if not (name := self._clean(li(".name").text() or a.attr("title") or a.text())): continue
|
||||||
|
pic = self._abs((li("img").attr("src") or "").replace('120', '500'))
|
||||||
|
res.append({"vod_id": self._abs(href), "vod_name": name, "vod_pic": f"{self.getProxyUrl()}&url={pic}&type=img" if pic else "", "style": {"type": "oval" if "/singer/" in href else ("list" if any(x in tid for x in ["/list/", "/playtype/", "/albumlist/"]) else "rect"), "ratio": 1 if "/singer/" in href else 1.33}})
|
||||||
|
return res
|
||||||
|
|
||||||
|
def _get_eps(self, doc):
|
||||||
|
eps = []
|
||||||
|
for li in doc(".play_list li, .song_list li, .music_list li").items():
|
||||||
|
if not (a := li("a").eq(0)).attr("href") or not re.search(r'/(song|mp3|radio|radiolist|radioplay)/([^/]+)\.html', a.attr("href")): continue
|
||||||
|
full_url = self._abs(a.attr("href"))
|
||||||
|
|
||||||
|
lrc_part = ""
|
||||||
|
mid = re.search(r'/(song|mp3|radio|radiolist|radioplay)/([^/]+)\.html', full_url)
|
||||||
|
if mid:
|
||||||
|
lrc_url = f"{self.host}/plug/down.php?ac=music&lk=lrc&id={mid.group(2)}"
|
||||||
|
lrc_part = f"|||{lrc_url}"
|
||||||
|
|
||||||
|
eps.append(f"{self._clean(a.text() or li('.name').text())}${self.e64('0@@@@' + full_url + lrc_part)}")
|
||||||
|
return eps
|
||||||
|
|
||||||
|
def _clean(self, text): return re.sub(r'(爱玩音乐网|视频下载说明|视频下载地址|www\.2t58\.com|MP3免费下载|LRC歌词下载|全部歌曲|\[第\d+页\]|刷新|每日推荐|最新|热门|推荐|MV|高清|无损)', '', text or "", flags=re.I).strip()
|
||||||
|
|
||||||
|
def _fetch_filters(self, url):
|
||||||
|
doc, filters = self.getpq(url), []
|
||||||
|
for i, group in enumerate([doc(s) for s in [".ilingku_fl", ".class_list", ".screen_list", ".box_list", ".nav_list"] if doc(s)]):
|
||||||
|
opts, seen = [{"n": "全部", "v": "top" if "top" in url else "index"}], set()
|
||||||
|
for a in group("a").items():
|
||||||
|
if (v := (a.attr("href") or "").split("?")[0].rstrip('/').split('/')[-1].replace('.html','')) and v not in seen:
|
||||||
|
opts.append({"n": a.text().strip(), "v": v}); seen.add(v)
|
||||||
|
if len(opts) > 1: filters.append({"key": f"id{i}" if i else "id", "name": "分类", "value": opts})
|
||||||
|
return filters
|
||||||
|
|
||||||
|
def _api(self, path, params=None, method="GET", headers=None, data=None):
|
||||||
|
try:
|
||||||
|
h = self.headers.copy()
|
||||||
|
if headers: h.update(headers)
|
||||||
|
r = (self.session.post if method == "POST" else self.session.get)(f"{self.host}{path}", params=params, data=data, headers=h, timeout=10, allow_redirects=False)
|
||||||
|
if loc := r.headers.get("Location"): return self._abs(loc.strip())
|
||||||
|
return self._abs(r.json().get("url", "").replace(r"\/", "/")) or (r.text.strip() if r.text.strip().startswith("http") else "")
|
||||||
|
except: return ""
|
||||||
|
|
||||||
|
def getpq(self, url):
|
||||||
|
import time
|
||||||
|
for _ in range(2):
|
||||||
|
try: return pq(self.session.get(self._abs(url), timeout=5).text)
|
||||||
|
except: time.sleep(0.1)
|
||||||
|
return pq("<html></html>")
|
||||||
|
|
||||||
|
def _abs(self, url): return url if url.startswith("http") else (f"{self.host}{'/' if not url.startswith('/') else ''}{url}" if url else "")
|
||||||
|
def e64(self, text): return b64encode(text.encode("utf-8")).decode("utf-8")
|
||||||
|
def d64(self, text): return b64decode(text.encode("utf-8")).decode("utf-8")
|
||||||
@ -0,0 +1,767 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# by @嗷呜
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
from base64 import b64decode, b64encode
|
||||||
|
from urllib.parse import parse_qs
|
||||||
|
import requests
|
||||||
|
from pyquery import PyQuery as pq
|
||||||
|
sys.path.append('..')
|
||||||
|
from base.spider import Spider
|
||||||
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
|
||||||
|
|
||||||
|
class Spider(Spider):
|
||||||
|
|
||||||
|
def init(self, extend=""):
|
||||||
|
tid = 'douyin'
|
||||||
|
headers = self.gethr(0, tid)
|
||||||
|
response = requests.head(self.hosts[tid], headers=headers)
|
||||||
|
ttwid = response.cookies.get('ttwid')
|
||||||
|
headers.update({
|
||||||
|
'authority': self.hosts[tid].split('//')[-1],
|
||||||
|
'cookie': f'ttwid={ttwid}' if ttwid else ''
|
||||||
|
})
|
||||||
|
self.dyheaders = headers
|
||||||
|
pass
|
||||||
|
|
||||||
|
def getName(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def isVideoFormat(self, url):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def manualVideoCheck(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def destroy(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
headers = [
|
||||||
|
{
|
||||||
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"User-Agent": "Dart/3.4 (dart:io)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
excepturl = 'https://www.baidu.com'
|
||||||
|
|
||||||
|
hosts = {
|
||||||
|
"huya": ["https://www.huya.com","https://mp.huya.com"],
|
||||||
|
"douyin": "https://live.douyin.com",
|
||||||
|
"douyu": "https://www.douyu.com",
|
||||||
|
"wangyi": "https://cc.163.com",
|
||||||
|
"bili": ["https://api.live.bilibili.com", "https://api.bilibili.com"]
|
||||||
|
}
|
||||||
|
|
||||||
|
referers = {
|
||||||
|
"huya": "https://live.cdn.huya.com",
|
||||||
|
"douyin": "https://live.douyin.com",
|
||||||
|
"douyu": "https://m.douyu.com",
|
||||||
|
"bili": "https://live.bilibili.com"
|
||||||
|
}
|
||||||
|
|
||||||
|
playheaders = {
|
||||||
|
"wangyi": {
|
||||||
|
"User-Agent": "ExoPlayer",
|
||||||
|
"Connection": "Keep-Alive",
|
||||||
|
"Icy-MetaData": "1"
|
||||||
|
},
|
||||||
|
"bili": {
|
||||||
|
'Accept': '*/*',
|
||||||
|
'Icy-MetaData': '1',
|
||||||
|
'referer': referers['bili'],
|
||||||
|
'user-agent': headers[0]['User-Agent']
|
||||||
|
},
|
||||||
|
'douyin': {
|
||||||
|
'User-Agent': 'libmpv',
|
||||||
|
'Icy-MetaData': '1'
|
||||||
|
},
|
||||||
|
'huya': {
|
||||||
|
'User-Agent': 'ExoPlayer',
|
||||||
|
'Connection': 'Keep-Alive',
|
||||||
|
'Icy-MetaData': '1'
|
||||||
|
},
|
||||||
|
'douyu': {
|
||||||
|
'User-Agent': 'libmpv',
|
||||||
|
'Icy-MetaData': '1'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def process_bili(self):
|
||||||
|
try:
|
||||||
|
self.blfdata = self.fetch(
|
||||||
|
f'{self.hosts["bili"][0]}/room/v1/Area/getList?need_entrance=1&parent_id=0',
|
||||||
|
headers=self.gethr(0, 'bili')
|
||||||
|
).json()
|
||||||
|
return ('bili', [{'key': 'cate', 'name': '分类',
|
||||||
|
'value': [{'n': i['name'], 'v': str(i['id'])}
|
||||||
|
for i in self.blfdata['data']]}])
|
||||||
|
except Exception as e:
|
||||||
|
print(f"bili处理错误: {e}")
|
||||||
|
return 'bili', None
|
||||||
|
|
||||||
|
def process_douyin(self):
|
||||||
|
try:
|
||||||
|
data = self.getpq(self.hosts['douyin'], headers=self.dyheaders)('script')
|
||||||
|
for i in data.items():
|
||||||
|
if 'categoryData' in i.text():
|
||||||
|
content = i.text()
|
||||||
|
start = content.find('{')
|
||||||
|
end = content.rfind('}') + 1
|
||||||
|
if start != -1 and end != -1:
|
||||||
|
json_str = content[start:end]
|
||||||
|
json_str = json_str.replace('\\"', '"')
|
||||||
|
try:
|
||||||
|
self.dyifdata = json.loads(json_str)
|
||||||
|
return ('douyin', [{'key': 'cate', 'name': '分类',
|
||||||
|
'value': [{'n': i['partition']['title'],
|
||||||
|
'v': f"{i['partition']['id_str']}@@{i['partition']['title']}"}
|
||||||
|
for i in self.dyifdata['categoryData']]}])
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
print(f"douyin解析错误: {e}")
|
||||||
|
return 'douyin', None
|
||||||
|
except Exception as e:
|
||||||
|
print(f"douyin请求或处理错误: {e}")
|
||||||
|
return 'douyin', None
|
||||||
|
|
||||||
|
def process_douyu(self):
|
||||||
|
try:
|
||||||
|
self.dyufdata = self.fetch(
|
||||||
|
f'{self.referers["douyu"]}/api/cate/list',
|
||||||
|
headers=self.headers[1]
|
||||||
|
).json()
|
||||||
|
return ('douyu', [{'key': 'cate', 'name': '分类',
|
||||||
|
'value': [{'n': i['cate1Name'], 'v': str(i['cate1Id'])}
|
||||||
|
for i in self.dyufdata['data']['cate1Info']]}])
|
||||||
|
except Exception as e:
|
||||||
|
print(f"douyu错误: {e}")
|
||||||
|
return 'douyu', None
|
||||||
|
|
||||||
|
def homeContent(self, filter):
|
||||||
|
result = {}
|
||||||
|
cateManual = {
|
||||||
|
"虎牙": "huya",
|
||||||
|
"抖音": "douyin",
|
||||||
|
"斗鱼": "douyu",
|
||||||
|
"网易": "wangyi"
|
||||||
|
}
|
||||||
|
classes = []
|
||||||
|
filters = {
|
||||||
|
'huya': [{'key': 'cate', 'name': '分类',
|
||||||
|
'value': [{'n': '网游', 'v': '1'}, {'n': '单机', 'v': '2'},
|
||||||
|
{'n': '娱乐', 'v': '8'}, {'n': '手游', 'v': '3'}]}]
|
||||||
|
}
|
||||||
|
|
||||||
|
with ThreadPoolExecutor(max_workers=3) as executor:
|
||||||
|
futures = {
|
||||||
|
executor.submit(self.process_bili): 'bili',
|
||||||
|
executor.submit(self.process_douyin): 'douyin',
|
||||||
|
executor.submit(self.process_douyu): 'douyu'
|
||||||
|
}
|
||||||
|
|
||||||
|
for future in futures:
|
||||||
|
platform, filter_data = future.result()
|
||||||
|
if filter_data:
|
||||||
|
filters[platform] = filter_data
|
||||||
|
|
||||||
|
for k in cateManual:
|
||||||
|
classes.append({
|
||||||
|
'type_name': k,
|
||||||
|
'type_id': cateManual[k]
|
||||||
|
})
|
||||||
|
|
||||||
|
result['class'] = classes
|
||||||
|
result['filters'] = filters
|
||||||
|
return result
|
||||||
|
|
||||||
|
def homeVideoContent(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def categoryContent(self, tid, pg, filter, extend):
|
||||||
|
vdata = []
|
||||||
|
result = {}
|
||||||
|
pagecount = 9999
|
||||||
|
result['page'] = pg
|
||||||
|
result['limit'] = 90
|
||||||
|
result['total'] = 999999
|
||||||
|
if tid == 'wangyi':
|
||||||
|
vdata, pagecount = self.wyccContent(tid, pg, filter, extend, vdata)
|
||||||
|
elif 'bili' in tid:
|
||||||
|
vdata, pagecount = self.biliContent(tid, pg, filter, extend, vdata)
|
||||||
|
elif 'huya' in tid:
|
||||||
|
vdata, pagecount = self.huyaContent(tid, pg, filter, extend, vdata)
|
||||||
|
elif 'douyin' in tid:
|
||||||
|
vdata, pagecount = self.douyinContent(tid, pg, filter, extend, vdata)
|
||||||
|
elif 'douyu' in tid:
|
||||||
|
vdata, pagecount = self.douyuContent(tid, pg, filter, extend, vdata)
|
||||||
|
result['list'] = vdata
|
||||||
|
result['pagecount'] = pagecount
|
||||||
|
return result
|
||||||
|
|
||||||
|
def wyccContent(self, tid, pg, filter, extend, vdata):
|
||||||
|
params = {
|
||||||
|
'format': 'json',
|
||||||
|
'start': (int(pg) - 1) * 20,
|
||||||
|
'size': '20',
|
||||||
|
}
|
||||||
|
response = self.fetch(f'{self.hosts[tid]}/api/category/live/', params=params, headers=self.headers[0]).json()
|
||||||
|
for i in response['lives']:
|
||||||
|
if i.get('cuteid'):
|
||||||
|
bvdata = self.buildvod(
|
||||||
|
vod_id=f"{tid}@@{i['cuteid']}",
|
||||||
|
vod_name=i.get('title'),
|
||||||
|
vod_pic=i.get('cover'),
|
||||||
|
vod_remarks=i.get('nickname'),
|
||||||
|
style={"type": "rect", "ratio": 1.33}
|
||||||
|
)
|
||||||
|
vdata.append(bvdata)
|
||||||
|
return vdata, 9999
|
||||||
|
|
||||||
|
def biliContent(self, tid, pg, filter, extend, vdata):
|
||||||
|
if extend.get('cate') and pg == '1' and 'click' not in tid:
|
||||||
|
for i in self.blfdata['data']:
|
||||||
|
if str(i['id']) == extend['cate']:
|
||||||
|
for j in i['list']:
|
||||||
|
v = self.buildvod(
|
||||||
|
vod_id=f"click_{tid}@@{i['id']}@@{j['id']}",
|
||||||
|
vod_name=j.get('name'),
|
||||||
|
vod_pic=j.get('pic'),
|
||||||
|
vod_tag=1,
|
||||||
|
style={"type": "oval", "ratio": 1}
|
||||||
|
)
|
||||||
|
vdata.append(v)
|
||||||
|
return vdata, 1
|
||||||
|
else:
|
||||||
|
path = f'/xlive/web-interface/v1/second/getListByArea?platform=web&sort=online&page_size=30&page={pg}'
|
||||||
|
if 'click' in tid:
|
||||||
|
ids = tid.split('_')[1].split('@@')
|
||||||
|
tid = ids[0]
|
||||||
|
path = f'/xlive/web-interface/v1/second/getList?platform=web&parent_area_id={ids[1]}&area_id={ids[-1]}&sort_type=&page={pg}'
|
||||||
|
data = self.fetch(f'{self.hosts[tid][0]}{path}', headers=self.gethr(0, tid)).json()
|
||||||
|
for i in data['data']['list']:
|
||||||
|
if i.get('roomid'):
|
||||||
|
data = self.buildvod(
|
||||||
|
f"{tid}@@{i['roomid']}",
|
||||||
|
i.get('title'),
|
||||||
|
i.get('cover'),
|
||||||
|
i.get('watched_show', {}).get('text_large'),
|
||||||
|
0,
|
||||||
|
i.get('uname'),
|
||||||
|
style={"type": "rect", "ratio": 1.33}
|
||||||
|
)
|
||||||
|
vdata.append(data)
|
||||||
|
return vdata, 9999
|
||||||
|
|
||||||
|
def huyaContent(self, tid, pg, filter, extend, vdata):
|
||||||
|
if extend.get('cate') and pg == '1' and 'click' not in tid:
|
||||||
|
id = extend.get('cate')
|
||||||
|
data = self.fetch(f'{self.referers[tid]}/liveconfig/game/bussLive?bussType={id}',
|
||||||
|
headers=self.headers[1]).json()
|
||||||
|
for i in data['data']:
|
||||||
|
v = self.buildvod(
|
||||||
|
vod_id=f"click_{tid}@@{int(i['gid'])}",
|
||||||
|
vod_name=i.get('gameFullName'),
|
||||||
|
vod_pic=f'https://huyaimg.msstatic.com/cdnimage/game/{int(i["gid"])}-MS.jpg',
|
||||||
|
vod_tag=1,
|
||||||
|
style={"type": "oval", "ratio": 1}
|
||||||
|
)
|
||||||
|
vdata.append(v)
|
||||||
|
return vdata, 1
|
||||||
|
else:
|
||||||
|
gid = ''
|
||||||
|
if 'click' in tid:
|
||||||
|
ids = tid.split('_')[1].split('@@')
|
||||||
|
tid = ids[0]
|
||||||
|
gid = f'&gameId={ids[1]}'
|
||||||
|
data = self.fetch(f'{self.hosts[tid][0]}/cache.php?m=LiveList&do=getLiveListByPage&tagAll=0{gid}&page={pg}',
|
||||||
|
headers=self.headers[1]).json()
|
||||||
|
for i in data['data']['datas']:
|
||||||
|
if i.get('profileRoom'):
|
||||||
|
v = self.buildvod(
|
||||||
|
f"{tid}@@{i['profileRoom']}",
|
||||||
|
i.get('introduction'),
|
||||||
|
i.get('screenshot'),
|
||||||
|
str(int(i.get('totalCount', '1')) / 10000) + '万',
|
||||||
|
0,
|
||||||
|
i.get('nick'),
|
||||||
|
style={"type": "rect", "ratio": 1.33}
|
||||||
|
|
||||||
|
)
|
||||||
|
vdata.append(v)
|
||||||
|
return vdata, 9999
|
||||||
|
|
||||||
|
def douyinContent(self, tid, pg, filter, extend, vdata):
|
||||||
|
if extend.get('cate') and pg == '1' and 'click' not in tid:
|
||||||
|
ids = extend.get('cate').split('@@')
|
||||||
|
for i in self.dyifdata['categoryData']:
|
||||||
|
c = i['partition']
|
||||||
|
if c['id_str'] == ids[0] and c['title'] == ids[1]:
|
||||||
|
vlist = i['sub_partition'].copy()
|
||||||
|
vlist.insert(0, {'partition': c})
|
||||||
|
for j in vlist:
|
||||||
|
j = j['partition']
|
||||||
|
v = self.buildvod(
|
||||||
|
vod_id=f"click_{tid}@@{j['id_str']}@@{j['type']}",
|
||||||
|
vod_name=j.get('title'),
|
||||||
|
vod_pic='https://p3-pc-weboff.byteimg.com/tos-cn-i-9r5gewecjs/pwa_v3/512x512-1.png',
|
||||||
|
vod_tag=1,
|
||||||
|
style={"type": "oval", "ratio": 1}
|
||||||
|
)
|
||||||
|
vdata.append(v)
|
||||||
|
return vdata, 1
|
||||||
|
else:
|
||||||
|
path = f'/webcast/web/partition/detail/room/?aid=6383&app_name=douyin_web&live_id=1&device_platform=web&count=15&offset={(int(pg) - 1) * 15}&partition=720&partition_type=1'
|
||||||
|
if 'click' in tid:
|
||||||
|
ids = tid.split('_')[1].split('@@')
|
||||||
|
tid = ids[0]
|
||||||
|
path = f'/webcast/web/partition/detail/room/?aid=6383&app_name=douyin_web&live_id=1&device_platform=web&count=15&offset={(int(pg) - 1) * 15}&partition={ids[1]}&partition_type={ids[-1]}&req_from=2'
|
||||||
|
data = self.fetch(f'{self.hosts[tid]}{path}', headers=self.dyheaders).json()
|
||||||
|
for i in data['data']['data']:
|
||||||
|
v = self.buildvod(
|
||||||
|
vod_id=f"{tid}@@{i['web_rid']}",
|
||||||
|
vod_name=i['room'].get('title'),
|
||||||
|
vod_pic=i['room']['cover'].get('url_list')[0],
|
||||||
|
vod_year=i.get('user_count_str'),
|
||||||
|
vod_remarks=i['room']['owner'].get('nickname'),
|
||||||
|
style={"type": "rect", "ratio": 1.33}
|
||||||
|
)
|
||||||
|
vdata.append(v)
|
||||||
|
return vdata, 9999
|
||||||
|
|
||||||
|
def douyuContent(self, tid, pg, filter, extend, vdata):
|
||||||
|
if extend.get('cate') and pg == '1' and 'click' not in tid:
|
||||||
|
for i in self.dyufdata['data']['cate2Info']:
|
||||||
|
if str(i['cate1Id']) == extend['cate']:
|
||||||
|
v = self.buildvod(
|
||||||
|
vod_id=f"click_{tid}@@{i['cate2Id']}",
|
||||||
|
vod_name=i.get('cate2Name'),
|
||||||
|
vod_pic=i.get('icon'),
|
||||||
|
vod_remarks=i.get('count'),
|
||||||
|
vod_tag=1,
|
||||||
|
style={"type": "oval", "ratio": 1}
|
||||||
|
)
|
||||||
|
vdata.append(v)
|
||||||
|
return vdata, 1
|
||||||
|
else:
|
||||||
|
path = f'/japi/weblist/apinc/allpage/6/{pg}'
|
||||||
|
if 'click' in tid:
|
||||||
|
ids = tid.split('_')[1].split('@@')
|
||||||
|
tid = ids[0]
|
||||||
|
path = f'/gapi/rkc/directory/mixList/2_{ids[1]}/{pg}'
|
||||||
|
url = f'{self.hosts[tid]}{path}'
|
||||||
|
data = self.fetch(url, headers=self.headers[1]).json()
|
||||||
|
for i in data['data']['rl']:
|
||||||
|
v = self.buildvod(
|
||||||
|
vod_id=f"{tid}@@{i['rid']}",
|
||||||
|
vod_name=i.get('rn'),
|
||||||
|
vod_pic=i.get('rs16'),
|
||||||
|
vod_year=str(int(i.get('ol', 1)) / 10000) + '万',
|
||||||
|
vod_remarks=i.get('nn'),
|
||||||
|
style={"type": "rect", "ratio": 1.33}
|
||||||
|
)
|
||||||
|
vdata.append(v)
|
||||||
|
return vdata, 9999
|
||||||
|
|
||||||
|
def detailContent(self, ids):
|
||||||
|
ids = ids[0].split('@@')
|
||||||
|
if ids[0] == 'wangyi':
|
||||||
|
vod = self.wyccDetail(ids)
|
||||||
|
elif ids[0] == 'bili':
|
||||||
|
vod = self.biliDetail(ids)
|
||||||
|
elif ids[0] == 'huya':
|
||||||
|
vod = self.huyaDetail(ids)
|
||||||
|
elif ids[0] == 'douyin':
|
||||||
|
vod = self.douyinDetail(ids)
|
||||||
|
elif ids[0] == 'douyu':
|
||||||
|
vod = self.douyuDetail(ids)
|
||||||
|
return {'list': [vod]}
|
||||||
|
|
||||||
|
def wyccDetail(self, ids):
|
||||||
|
try:
|
||||||
|
vdata = self.getpq(f'{self.hosts[ids[0]]}/{ids[1]}', self.headers[0])('script').eq(-1).text()
|
||||||
|
|
||||||
|
def get_quality_name(vbr):
|
||||||
|
if vbr <= 600:
|
||||||
|
return "标清"
|
||||||
|
elif vbr <= 1000:
|
||||||
|
return "高清"
|
||||||
|
elif vbr <= 2000:
|
||||||
|
return "超清"
|
||||||
|
else:
|
||||||
|
return "蓝光"
|
||||||
|
|
||||||
|
data = json.loads(vdata)['props']['pageProps']['roomInfoInitData']
|
||||||
|
name = data['live'].get('title', ids[0])
|
||||||
|
vod = self.buildvod(vod_name=data.get('keywords_suffix'), vod_remarks=data['live'].get('title'),
|
||||||
|
vod_content=data.get('description_suffix'))
|
||||||
|
resolution_data = data['live']['quickplay']['resolution']
|
||||||
|
all_streams = {}
|
||||||
|
sorted_qualities = sorted(resolution_data.items(),
|
||||||
|
key=lambda x: x[1]['vbr'],
|
||||||
|
reverse=True)
|
||||||
|
for quality, data in sorted_qualities:
|
||||||
|
vbr = data['vbr']
|
||||||
|
quality_name = get_quality_name(vbr)
|
||||||
|
for cdn_name, url in data['cdn'].items():
|
||||||
|
if cdn_name not in all_streams and type(url) == str and url.startswith('http'):
|
||||||
|
all_streams[cdn_name] = []
|
||||||
|
if isinstance(url, str) and url.startswith('http'):
|
||||||
|
all_streams[cdn_name].extend([quality_name, url])
|
||||||
|
plists = []
|
||||||
|
names = []
|
||||||
|
for i, (cdn_name, stream_list) in enumerate(all_streams.items(), 1):
|
||||||
|
names.append(f'线路{i}')
|
||||||
|
pstr = f"{name}${ids[0]}@@{self.e64(json.dumps(stream_list))}"
|
||||||
|
plists.append(pstr)
|
||||||
|
vod['vod_play_from'] = "$$$".join(names)
|
||||||
|
vod['vod_play_url'] = "$$$".join(plists)
|
||||||
|
return vod
|
||||||
|
except Exception as e:
|
||||||
|
return self.handle_exception(e)
|
||||||
|
|
||||||
|
def biliDetail(self, ids):
|
||||||
|
try:
|
||||||
|
vdata = self.fetch(
|
||||||
|
f'{self.hosts[ids[0]][0]}/xlive/web-room/v1/index/getInfoByRoom?room_id={ids[1]}&wts={int(time.time())}',
|
||||||
|
headers=self.gethr(0, ids[0])).json()
|
||||||
|
v = vdata['data']['room_info']
|
||||||
|
vod = self.buildvod(
|
||||||
|
vod_name=v.get('title'),
|
||||||
|
type_name=v.get('parent_area_name') + '/' + v.get('area_name'),
|
||||||
|
vod_remarks=v.get('tags'),
|
||||||
|
vod_play_from=v.get('title'),
|
||||||
|
)
|
||||||
|
data = self.fetch(
|
||||||
|
f'{self.hosts[ids[0]][0]}/xlive/web-room/v2/index/getRoomPlayInfo?room_id={ids[1]}&protocol=0%2C1&format=0%2C1%2C2&codec=0%2C1&platform=web',
|
||||||
|
headers=self.gethr(0, ids[0])).json()
|
||||||
|
vdnams = data['data']['playurl_info']['playurl']['g_qn_desc']
|
||||||
|
all_accept_qns = []
|
||||||
|
streams = data['data']['playurl_info']['playurl']['stream']
|
||||||
|
for stream in streams:
|
||||||
|
for format_item in stream['format']:
|
||||||
|
for codec in format_item['codec']:
|
||||||
|
if 'accept_qn' in codec:
|
||||||
|
all_accept_qns.append(codec['accept_qn'])
|
||||||
|
max_accept_qn = max(all_accept_qns, key=len) if all_accept_qns else []
|
||||||
|
quality_map = {
|
||||||
|
item['qn']: item['desc']
|
||||||
|
for item in vdnams
|
||||||
|
}
|
||||||
|
quality_names = [f"{quality_map.get(qn)}${ids[0]}@@{ids[1]}@@{qn}" for qn in max_accept_qn]
|
||||||
|
vod['vod_play_url'] = "#".join(quality_names)
|
||||||
|
return vod
|
||||||
|
except Exception as e:
|
||||||
|
return self.handle_exception(e)
|
||||||
|
|
||||||
|
def huyaDetail(self, ids):
|
||||||
|
try:
|
||||||
|
vdata = self.fetch(f'{self.hosts[ids[0]][1]}/cache.php?m=Live&do=profileRoom&roomid={ids[1]}',
|
||||||
|
headers=self.headers[0]).json()
|
||||||
|
v = vdata['data']['liveData']
|
||||||
|
vod = self.buildvod(
|
||||||
|
vod_name=v.get('introduction'),
|
||||||
|
type_name=v.get('gameFullName'),
|
||||||
|
vod_director=v.get('nick'),
|
||||||
|
vod_remarks=v.get('contentIntro'),
|
||||||
|
)
|
||||||
|
data = dict(reversed(list(vdata['data']['stream'].items())))
|
||||||
|
names = []
|
||||||
|
plist = []
|
||||||
|
|
||||||
|
for stream_type, stream_data in data.items():
|
||||||
|
if isinstance(stream_data, dict) and 'multiLine' in stream_data and 'rateArray' in stream_data:
|
||||||
|
names.append(f"线路{len(names) + 1}")
|
||||||
|
qualities = sorted(
|
||||||
|
stream_data['rateArray'],
|
||||||
|
key=lambda x: (x['iBitRate'], x['sDisplayName']),
|
||||||
|
reverse=True
|
||||||
|
)
|
||||||
|
cdn_urls = []
|
||||||
|
for cdn in stream_data['multiLine']:
|
||||||
|
quality_urls = []
|
||||||
|
for quality in qualities:
|
||||||
|
quality_name = quality['sDisplayName']
|
||||||
|
bit_rate = quality['iBitRate']
|
||||||
|
base_url = cdn['url']
|
||||||
|
if bit_rate > 0:
|
||||||
|
if '.m3u8' in base_url:
|
||||||
|
new_url = base_url.replace(
|
||||||
|
'ratio=2000',
|
||||||
|
f'ratio={bit_rate}'
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
new_url = base_url.replace(
|
||||||
|
'imgplus.flv',
|
||||||
|
f'imgplus_{bit_rate}.flv'
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
new_url = base_url
|
||||||
|
quality_urls.extend([quality_name, new_url])
|
||||||
|
encoded_urls = self.e64(json.dumps(quality_urls))
|
||||||
|
cdn_urls.append(f"{cdn['cdnType']}${ids[0]}@@{encoded_urls}")
|
||||||
|
|
||||||
|
if cdn_urls:
|
||||||
|
plist.append('#'.join(cdn_urls))
|
||||||
|
vod['vod_play_from'] = "$$$".join(names)
|
||||||
|
vod['vod_play_url'] = "$$$".join(plist)
|
||||||
|
return vod
|
||||||
|
except Exception as e:
|
||||||
|
return self.handle_exception(e)
|
||||||
|
|
||||||
|
def douyinDetail(self, ids):
|
||||||
|
url = f'{self.hosts[ids[0]]}/webcast/room/web/enter/?aid=6383&app_name=douyin_web&live_id=1&device_platform=web&enter_from=web_live&web_rid={ids[1]}&room_id_str=&enter_source=&Room-Enter-User-Login-Ab=0&is_need_double_stream=false&cookie_enabled=true&screen_width=1980&screen_height=1080&browser_language=zh-CN&browser_platform=Win32&browser_name=Edge&browser_version=125.0.0.0'
|
||||||
|
data = self.fetch(url, headers=self.dyheaders).json()
|
||||||
|
try:
|
||||||
|
vdata = data['data']['data'][0]
|
||||||
|
vod = self.buildvod(
|
||||||
|
vod_name=vdata['title'],
|
||||||
|
vod_remarks=vdata['user_count_str'],
|
||||||
|
)
|
||||||
|
resolution_data = vdata['stream_url']['live_core_sdk_data']['pull_data']['options']['qualities']
|
||||||
|
stream_json = vdata['stream_url']['live_core_sdk_data']['pull_data']['stream_data']
|
||||||
|
stream_json = json.loads(stream_json)
|
||||||
|
available_types = []
|
||||||
|
if any(sdk_key in stream_json['data'] and 'main' in stream_json['data'][sdk_key] for sdk_key in
|
||||||
|
stream_json['data']):
|
||||||
|
available_types.append('main')
|
||||||
|
if any(sdk_key in stream_json['data'] and 'backup' in stream_json['data'][sdk_key] for sdk_key in
|
||||||
|
stream_json['data']):
|
||||||
|
available_types.append('backup')
|
||||||
|
plist = []
|
||||||
|
for line_type in available_types:
|
||||||
|
format_arrays = {'flv': [], 'hls': [], 'lls': []}
|
||||||
|
qualities = sorted(resolution_data, key=lambda x: x['level'], reverse=True)
|
||||||
|
for quality in qualities:
|
||||||
|
sdk_key = quality['sdk_key']
|
||||||
|
if sdk_key in stream_json['data'] and line_type in stream_json['data'][sdk_key]:
|
||||||
|
stream_info = stream_json['data'][sdk_key][line_type]
|
||||||
|
if stream_info.get('flv'):
|
||||||
|
format_arrays['flv'].extend([quality['name'], stream_info['flv']])
|
||||||
|
if stream_info.get('hls'):
|
||||||
|
format_arrays['hls'].extend([quality['name'], stream_info['hls']])
|
||||||
|
if stream_info.get('lls'):
|
||||||
|
format_arrays['lls'].extend([quality['name'], stream_info['lls']])
|
||||||
|
format_urls = []
|
||||||
|
for format_name, url_array in format_arrays.items():
|
||||||
|
if url_array:
|
||||||
|
encoded_urls = self.e64(json.dumps(url_array))
|
||||||
|
format_urls.append(f"{format_name}${ids[0]}@@{encoded_urls}")
|
||||||
|
|
||||||
|
if format_urls:
|
||||||
|
plist.append('#'.join(format_urls))
|
||||||
|
|
||||||
|
names = ['线路1', '线路2'][:len(plist)]
|
||||||
|
vod['vod_play_from'] = "$$$".join(names)
|
||||||
|
vod['vod_play_url'] = "$$$".join(plist)
|
||||||
|
return vod
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return self.handle_exception(e)
|
||||||
|
|
||||||
|
def douyuDetail(self, ids):
|
||||||
|
headers = self.gethr(0, zr=f'{self.hosts[ids[0]]}/{ids[1]}')
|
||||||
|
try:
|
||||||
|
data = self.fetch(f'{self.hosts[ids[0]]}/betard/{ids[1]}', headers=headers).json()
|
||||||
|
vname = data['room']['room_name']
|
||||||
|
vod = self.buildvod(
|
||||||
|
vod_name=vname,
|
||||||
|
vod_remarks=data['room'].get('second_lvl_name'),
|
||||||
|
vod_director=data['room'].get('nickname'),
|
||||||
|
)
|
||||||
|
vdata = self.fetch(f'{self.hosts[ids[0]]}/swf_api/homeH5Enc?rids={ids[1]}', headers=headers).json()
|
||||||
|
json_body = vdata['data']
|
||||||
|
json_body = {"html": self.douyu_text(json_body[f'room{ids[1]}']), "rid": ids[1]}
|
||||||
|
sign = self.post('http://alive.nsapps.cn/api/AllLive/DouyuSign', json=json_body, headers=self.headers[1]).json()['data']
|
||||||
|
body = f'{sign}&cdn=&rate=-1&ver=Douyu_223061205&iar=1&ive=1&hevc=0&fa=0'
|
||||||
|
body=self.params_to_json(body)
|
||||||
|
nubdata = self.post(f'{self.hosts[ids[0]]}/lapi/live/getH5Play/{ids[1]}', data=body, headers=headers).json()
|
||||||
|
plist = []
|
||||||
|
names = []
|
||||||
|
for i,x in enumerate(nubdata['data']['cdnsWithName']):
|
||||||
|
names.append(f'线路{i+1}')
|
||||||
|
d = {'sign': sign, 'cdn': x['cdn'], 'id': ids[1]}
|
||||||
|
plist.append(
|
||||||
|
f'{vname}${ids[0]}@@{self.e64(json.dumps(d))}@@{self.e64(json.dumps(nubdata["data"]["multirates"]))}')
|
||||||
|
vod['vod_play_from'] = "$$$".join(names)
|
||||||
|
vod['vod_play_url'] = "$$$".join(plist)
|
||||||
|
return vod
|
||||||
|
except Exception as e:
|
||||||
|
return self.handle_exception(e)
|
||||||
|
|
||||||
|
def douyu_text(self, text):
|
||||||
|
function_positions = [m.start() for m in re.finditer('function', text)]
|
||||||
|
total_functions = len(function_positions)
|
||||||
|
if total_functions % 2 == 0:
|
||||||
|
target_index = total_functions // 2 + 1
|
||||||
|
else:
|
||||||
|
target_index = (total_functions - 1) // 2 + 1
|
||||||
|
if total_functions >= target_index:
|
||||||
|
cut_position = function_positions[target_index - 1]
|
||||||
|
ctext = text[4:cut_position]
|
||||||
|
return re.sub(r'eval\(strc\)\([\w\d,]+\)', 'strc', ctext)
|
||||||
|
return text
|
||||||
|
|
||||||
|
def searchContent(self, key, quick, pg="1"):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def playerContent(self, flag, id, vipFlags):
|
||||||
|
try:
|
||||||
|
ids = id.split('@@')
|
||||||
|
p = 1
|
||||||
|
if ids[0] in ['wangyi', 'douyin','huya']:
|
||||||
|
p, url = 0, json.loads(self.d64(ids[1]))
|
||||||
|
elif ids[0] == 'bili':
|
||||||
|
p, url = self.biliplay(ids)
|
||||||
|
elif ids[0] == 'huya':
|
||||||
|
p, url = 0, json.loads(self.d64(ids[1]))
|
||||||
|
elif ids[0] == 'douyu':
|
||||||
|
p, url = self.douyuplay(ids)
|
||||||
|
return {'parse': p, 'url': url, 'header': self.playheaders[ids[0]]}
|
||||||
|
except Exception as e:
|
||||||
|
return {'parse': 1, 'url': self.excepturl, 'header': self.headers[0]}
|
||||||
|
|
||||||
|
def biliplay(self, ids):
|
||||||
|
try:
|
||||||
|
data = self.fetch(
|
||||||
|
f'{self.hosts[ids[0]][0]}/xlive/web-room/v2/index/getRoomPlayInfo?room_id={ids[1]}&protocol=0,1&format=0,2&codec=0&platform=web&qn={ids[2]}',
|
||||||
|
headers=self.gethr(0, ids[0])).json()
|
||||||
|
urls = []
|
||||||
|
line_index = 1
|
||||||
|
for stream in data['data']['playurl_info']['playurl']['stream']:
|
||||||
|
for format_item in stream['format']:
|
||||||
|
for codec in format_item['codec']:
|
||||||
|
for url_info in codec['url_info']:
|
||||||
|
full_url = f"{url_info['host']}/{codec['base_url'].lstrip('/')}{url_info['extra']}"
|
||||||
|
urls.extend([f"线路{line_index}", full_url])
|
||||||
|
line_index += 1
|
||||||
|
return 0, urls
|
||||||
|
except Exception as e:
|
||||||
|
return 1, self.excepturl
|
||||||
|
|
||||||
|
def douyuplay(self, ids):
|
||||||
|
try:
|
||||||
|
sdata = json.loads(self.d64(ids[1]))
|
||||||
|
headers = self.gethr(0, zr=f'{self.hosts[ids[0]]}/{sdata["id"]}')
|
||||||
|
ldata = json.loads(self.d64(ids[2]))
|
||||||
|
result_obj = {}
|
||||||
|
with ThreadPoolExecutor(max_workers=len(ldata)) as executor:
|
||||||
|
futures = [
|
||||||
|
executor.submit(
|
||||||
|
self.douyufp,
|
||||||
|
sdata,
|
||||||
|
quality,
|
||||||
|
headers,
|
||||||
|
self.hosts[ids[0]],
|
||||||
|
result_obj
|
||||||
|
) for quality in ldata
|
||||||
|
]
|
||||||
|
for future in futures:
|
||||||
|
future.result()
|
||||||
|
|
||||||
|
result = []
|
||||||
|
for bit in sorted(result_obj.keys(), reverse=True):
|
||||||
|
result.extend(result_obj[bit])
|
||||||
|
|
||||||
|
if result:
|
||||||
|
return 0, result
|
||||||
|
return 1, self.excepturl
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return 1, self.excepturl
|
||||||
|
|
||||||
|
def douyufp(self, sdata, quality, headers, host, result_obj):
|
||||||
|
try:
|
||||||
|
body = f'{sdata["sign"]}&cdn={sdata["cdn"]}&rate={quality["rate"]}'
|
||||||
|
body=self.params_to_json(body)
|
||||||
|
data = self.post(f'{host}/lapi/live/getH5Play/{sdata["id"]}',
|
||||||
|
data=body, headers=headers).json()
|
||||||
|
if data.get('data'):
|
||||||
|
play_url = data['data']['rtmp_url'] + '/' + data['data']['rtmp_live']
|
||||||
|
bit = quality.get('bit', 0)
|
||||||
|
if bit not in result_obj:
|
||||||
|
result_obj[bit] = []
|
||||||
|
result_obj[bit].extend([quality['name'], play_url])
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error fetching {quality['name']}: {str(e)}")
|
||||||
|
|
||||||
|
def localProxy(self, param):
|
||||||
|
pass
|
||||||
|
|
||||||
|
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 josn_to_params(self, params, skip_empty=False):
|
||||||
|
query = []
|
||||||
|
for k, v in params.items():
|
||||||
|
if skip_empty and not v:
|
||||||
|
continue
|
||||||
|
query.append(f"{k}={v}")
|
||||||
|
return "&".join(query)
|
||||||
|
|
||||||
|
def params_to_json(self, query_string):
|
||||||
|
parsed_data = parse_qs(query_string)
|
||||||
|
result = {key: value[0] for key, value in parsed_data.items()}
|
||||||
|
return result
|
||||||
|
|
||||||
|
def buildvod(self, vod_id='', vod_name='', vod_pic='', vod_year='', vod_tag='', vod_remarks='', style='',
|
||||||
|
type_name='', vod_area='', vod_actor='', vod_director='',
|
||||||
|
vod_content='', vod_play_from='', vod_play_url=''):
|
||||||
|
vod = {
|
||||||
|
'vod_id': vod_id,
|
||||||
|
'vod_name': vod_name,
|
||||||
|
'vod_pic': vod_pic,
|
||||||
|
'vod_year': vod_year,
|
||||||
|
'vod_tag': 'folder' if vod_tag else '',
|
||||||
|
'vod_remarks': vod_remarks,
|
||||||
|
'style': style,
|
||||||
|
'type_name': type_name,
|
||||||
|
'vod_area': vod_area,
|
||||||
|
'vod_actor': vod_actor,
|
||||||
|
'vod_director': vod_director,
|
||||||
|
'vod_content': vod_content,
|
||||||
|
'vod_play_from': vod_play_from,
|
||||||
|
'vod_play_url': vod_play_url
|
||||||
|
}
|
||||||
|
vod = {key: value for key, value in vod.items() if value}
|
||||||
|
return vod
|
||||||
|
|
||||||
|
def getpq(self, url, headers=None, cookies=None):
|
||||||
|
data = self.fetch(url, headers=headers, cookies=cookies).text
|
||||||
|
try:
|
||||||
|
return pq(data)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"解析页面错误: {str(e)}")
|
||||||
|
return pq(data.encode('utf-8'))
|
||||||
|
|
||||||
|
def gethr(self, index, rf='', zr=''):
|
||||||
|
headers = self.headers[index]
|
||||||
|
if zr:
|
||||||
|
headers['referer'] = zr
|
||||||
|
else:
|
||||||
|
headers['referer'] = f"{self.referers[rf]}/"
|
||||||
|
return headers
|
||||||
|
|
||||||
|
def handle_exception(self, e):
|
||||||
|
print(f"报错: {str(e)}")
|
||||||
|
return {'vod_play_from': '哎呀翻车啦', 'vod_play_url': f'翻车啦${self.excepturl}'}
|
||||||
|
|
||||||
@ -0,0 +1,107 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def load_json(path):
|
||||||
|
with open(path, "r", encoding="utf-8") as f:
|
||||||
|
return json.load(f)
|
||||||
|
|
||||||
|
|
||||||
|
def find_index_by_key(sites, key):
|
||||||
|
for i, item in enumerate(sites):
|
||||||
|
if isinstance(item, dict) and item.get("key") == key:
|
||||||
|
return i
|
||||||
|
return -1
|
||||||
|
|
||||||
|
|
||||||
|
def extract_local_paths(value):
|
||||||
|
"""提取字符串中的本地路径(支持 ./ 和 ../),忽略 URL"""
|
||||||
|
if not isinstance(value, str):
|
||||||
|
return []
|
||||||
|
paths = []
|
||||||
|
# 先按 ? 切分,保留路径及查询参数里的路径
|
||||||
|
parts = value.split("?")
|
||||||
|
for part in parts:
|
||||||
|
# 再按常见分隔符切
|
||||||
|
for token in part.replace("&", " ").replace("$", " ").split():
|
||||||
|
token = token.strip()
|
||||||
|
if token.startswith("./") or token.startswith("../"):
|
||||||
|
# 去掉可能的尾部参数
|
||||||
|
token = token.split("&", 1)[0]
|
||||||
|
token = token.split("$", 1)[0]
|
||||||
|
paths.append(token)
|
||||||
|
return paths
|
||||||
|
|
||||||
|
|
||||||
|
def iter_paths_between(sites, start_key, end_key):
|
||||||
|
start_idx = find_index_by_key(sites, start_key)
|
||||||
|
end_idx = find_index_by_key(sites, end_key)
|
||||||
|
if start_idx == -1 or end_idx == -1 or start_idx >= end_idx:
|
||||||
|
return []
|
||||||
|
subset = sites[start_idx + 1 : end_idx]
|
||||||
|
all_paths = []
|
||||||
|
for item in subset:
|
||||||
|
if not isinstance(item, dict):
|
||||||
|
continue
|
||||||
|
# api 仅拷贝 .py
|
||||||
|
api_val = item.get("api")
|
||||||
|
if isinstance(api_val, str):
|
||||||
|
for p in extract_local_paths(api_val):
|
||||||
|
if p.endswith(".py"):
|
||||||
|
all_paths.append(p)
|
||||||
|
# ext 拷贝所有本地路径
|
||||||
|
ext_val = item.get("ext")
|
||||||
|
if isinstance(ext_val, str):
|
||||||
|
all_paths.extend(extract_local_paths(ext_val))
|
||||||
|
# 去重,保留顺序
|
||||||
|
seen = set()
|
||||||
|
deduped = []
|
||||||
|
for p in all_paths:
|
||||||
|
if p not in seen:
|
||||||
|
seen.add(p)
|
||||||
|
deduped.append(p)
|
||||||
|
return deduped
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print("用法: python copy_xbpq.py <json路径>")
|
||||||
|
print("示例: python copy_xbpq.py jsm_with_app_sites.json")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
json_path = sys.argv[1]
|
||||||
|
data = load_json(json_path)
|
||||||
|
sites = data.get("sites", [])
|
||||||
|
|
||||||
|
paths = iter_paths_between(sites, "cbh", "push_agent")
|
||||||
|
if not paths:
|
||||||
|
print("⚠️ 未找到 cbh 到 push_agent 之间的 ./XBPQ/ 路径")
|
||||||
|
return
|
||||||
|
|
||||||
|
base_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
src_base = os.path.normpath(os.path.join(base_dir, "..", "xiaosa"))
|
||||||
|
dst_base = os.path.normpath(os.path.join(base_dir, ".."))
|
||||||
|
|
||||||
|
copied = 0
|
||||||
|
missing = 0
|
||||||
|
for path in paths:
|
||||||
|
rel_path = path.replace("./", "", 1)
|
||||||
|
rel_path = rel_path.replace("../", "", 1)
|
||||||
|
src = os.path.join(src_base, rel_path)
|
||||||
|
dst = os.path.join(dst_base, rel_path)
|
||||||
|
if not os.path.exists(src):
|
||||||
|
print(f"⚠️ 源文件不存在: {src}")
|
||||||
|
missing += 1
|
||||||
|
continue
|
||||||
|
os.makedirs(os.path.dirname(dst), exist_ok=True)
|
||||||
|
shutil.copy2(src, dst)
|
||||||
|
print(f"✅ 已覆盖: {dst}")
|
||||||
|
copied += 1
|
||||||
|
|
||||||
|
print(f"完成: 复制 {copied} 个,缺失 {missing} 个")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@ -1,6 +1,9 @@
|
|||||||
{
|
{
|
||||||
"简介": "",
|
"简介": "jianjie\">&&</p>",
|
||||||
"数组": "",
|
"副标题": "zhuangtai\">&&</div>",
|
||||||
"分类url": "http://www.yinghuadm.cn/show_{cateId}--{by}-{class}-----{catePg}---{year}.html",
|
"数组": "u-movie\">&&</article>",
|
||||||
"分类": "日本动漫$ribendongman#国产动漫$guochandongman#动漫电影$dongmandianying#欧美动漫$oumeidongman"
|
"线路标题": "ctitle\">&&在线播放",
|
||||||
|
"播放数组": "vlink&&</div>",
|
||||||
|
"分类url": "https://www.yinghuadongman.com.cn/sxvodshow/{cateId}-{year}/page/{catePg}/;;d0",
|
||||||
|
"分类": "国漫$2#日漫$1#美漫$3"
|
||||||
}
|
}
|
||||||
Binary file not shown.
Loading…
Reference in new issue