上传文件至「py」
This commit is contained in:
Vendored
+1
@@ -0,0 +1 @@
|
||||
import cheerio from"assets://js/lib/cheerio.min.js";import"assets://js/lib/crypto-js.js";import 模板 from"./模板.js";import{gbkTool}from"./gbk.js";function init_test(){console.log("init_test_start");console.log("当前版本号:"+VERSION);console.log(RKEY);console.log(JSON.stringify(rule));console.log("init_test_end")}function pre(){if(typeof rule.预处理==="string"&&rule.预处理&&rule.预处理.trim()){let code=rule.预处理.trim();console.log("执行预处理代码:"+code);if(code.startsWith("js:")){code=code.replace("js:","")}try{eval(code)}catch(e){console.log("预处理执行失败:"+e.message)}}}let rule={};let vercode=typeof pdfl==="function"?"drpy2.1":"drpy2";const VERSION=vercode+" 3.9.49beta2 20231122";const MOBILE_UA="Mozilla/5.0 (Linux; Android 11; M2007J3SC Build/RKQ1.200826.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/77.0.3865.120 MQQBrowser/6.2 TBS/045714 Mobile Safari/537.36";const PC_UA="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36";const UA="Mozilla/5.0";const UC_UA="Mozilla/5.0 (Linux; U; Android 9; zh-CN; MI 9 Build/PKQ1.181121.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/12.5.5.1035 Mobile Safari/537.36";const IOS_UA="Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1";const RULE_CK="cookie";const CATE_EXCLUDE="首页|留言|APP|下载|资讯|新闻|动态";const TAB_EXCLUDE="猜你|喜欢|下载|剧情|热播";const OCR_RETRY=3;const OCR_API="http://drpy.nokia.press:8028/ocr/drpy/text";if(typeof MY_URL==="undefined"){var MY_URL}var HOST;var RKEY;var fetch;var print;var log;var rule_fetch_params;var fetch_params;var oheaders;var _pdfh;var _pdfa;var _pd;const DOM_CHECK_ATTR=/(url|src|href|-original|-src|-play|-url|style)$/;const SPECIAL_URL=/^(ftp|magnet|thunder|ws):/;const NOADD_INDEX=/:eq|:lt|:gt|:first|:last|^body$|^#/;const URLJOIN_ATTR=/(url|src|href|-original|-src|-play|-url|style)$/;const SELECT_REGEX=/:eq|:lt|:gt|#/g;const SELECT_REGEX_A=/:eq|:lt|:gt/g;if(typeof Object.assign!="function"){Object.assign=function(){var target=arguments[0];for(var i=1;i<arguments.length;i++){var source=arguments[i];for(var key in source){if(Object.prototype.hasOwnProperty.call(source,key)){target[key]=source[key]}}}return target}}if(!String.prototype.includes){String.prototype.includes=function(search,start){if(typeof start!=="number"){start=0}if(start+search.length>this.length){return false}else{return this.indexOf(search,start)!==-1}}}if(!Array.prototype.includes){Object.defineProperty(Array.prototype,"includes",{value:function(searchElement,fromIndex){if(this==null){throw new TypeError('"this" is null or not defined')}var o=Object(this);var len=o.length>>>0;if(len===0){return false}var n=fromIndex|0;var k=Math.max(n>=0?n:len-Math.abs(n),0);while(k<len){if(o[k]===searchElement){return true}k++}return false}})}if(typeof String.prototype.startsWith!="function"){String.prototype.startsWith=function(prefix){return this.slice(0,prefix.length)===prefix}}if(typeof String.prototype.endsWith!="function"){String.prototype.endsWith=function(suffix){return this.indexOf(suffix,this.length-suffix.length)!==-1}}Object.prototype.myValues=function(obj){if(obj==null){throw new TypeError("Cannot convert undefined or null to object")}var res=[];for(var k in obj){if(obj.hasOwnProperty(k)){res.push(obj[k])}}return res};if(typeof Object.prototype.values!="function"){Object.prototype.values=function(obj){if(obj==null){throw new TypeError("Cannot convert undefined or null to object")}var res=[];for(var k in obj){if(obj.hasOwnProperty(k)){res.push(obj[k])}}return res}}if(typeof Array.prototype.join!="function"){Array.prototype.join=function(emoji){emoji=emoji||"";let self=this;let str="";let i=0;if(!Array.isArray(self)){throw String(self)+"is not Array"}if(self.length===0){return""}if(self.length===1){return String(self[0])}i=1;str=this[0];for(;i<self.length;i++){str+=String(emoji)+String(self[i])}return str}}String.prototype.rstrip=function(chars){let regex=new RegExp(chars+"$");return this.replace(regex,"")};Array.prototype.append=Array.prototype.push;String.prototype.strip=String.prototype.trim;function 是否正版(vipUrl){let flag=new RegExp("qq.com|iqiyi.com|youku.com|mgtv.com|bilibili.com|sohu.com|ixigua.com|pptv.com|miguvideo.com|le.com|1905.com|fun.tv");return flag.test(vipUrl)}function urlDeal(vipUrl){if(!vipUrl){return""}if(!是否正版(vipUrl)){return vipUrl}if(!/miguvideo/.test(vipUrl)){vipUrl=vipUrl.split("#")[0].split("?")[0]}return vipUrl}function setResult(d){if(!Array.isArray(d)){return[]}VODS=[];d.forEach(function(it){let obj={vod_id:it.url||"",vod_name:it.title||"",vod_remarks:it.desc||"",vod_content:it.content||"",vod_pic:it.pic_url||it.img||""};let keys=Object.keys(it);if(keys.includes("tname")){obj.type_name=it.tname||""}if(keys.includes("tid")){obj.type_id=it.tid||""}if(keys.includes("year")){obj.vod_year=it.year||""}if(keys.includes("actor")){obj.vod_actor=it.actor||""}if(keys.includes("director")){obj.vod_director=it.director||""}if(keys.includes("area")){obj.vod_area=it.area||""}VODS.push(obj)});return VODS}function setResult2(res){VODS=res.list||[];return VODS}function setHomeResult(res){if(!res||typeof res!=="object"){return[]}return setResult(res.list)}function rc(js){if(js==="maomi_aes.js"){var a=CryptoJS.enc.Utf8.parse("625222f9149e961d");var t=CryptoJS.enc.Utf8.parse("5efdtf6060e2o330");return{De:function(word){word=CryptoJS.enc.Hex.parse(word);return CryptoJS.AES.decrypt(CryptoJS.enc.Base64.stringify(word),a,{iv:t,mode:CryptoJS.mode.CBC,padding:CryptoJS.pad.Pkcs7}).toString(CryptoJS.enc.Utf8)},En:function(word){var Encrypted=CryptoJS.AES.encrypt(word,a,{iv:t,mode:CryptoJS.mode.CBC,padding:CryptoJS.pad.Pkcs7});return Encrypted.ciphertext.toString()}}}return{}}function maoss(jxurl,ref,key){fetch_params=JSON.parse(JSON.stringify(rule_fetch_params));eval(getCryptoJS());try{var getVideoInfo=function(text){return CryptoJS.AES.decrypt(text,key,{iv:iv,padding:CryptoJS.pad.Pkcs7}).toString(CryptoJS.enc.Utf8)};var token_key=key==undefined?"dvyYRQlnPRCMdQSe":key;if(ref){var html=request(jxurl,{headers:{Referer:ref}})}else{var html=request(jxurl)}if(html.indexOf("&btwaf=")!=-1){html=request(jxurl+"&btwaf"+html.match(/&btwaf(.*?)"/)[1],{headers:{Referer:ref}})}var token_iv=html.split('_token = "')[1].split('"')[0];var key=CryptoJS.enc.Utf8.parse(token_key);var iv=CryptoJS.enc.Utf8.parse(token_iv);eval(html.match(/var config = {[\s\S]*?}/)[0]+"");if(!config.url.startsWith("http")){config.url=CryptoJS.AES.decrypt(config.url,key,{iv:iv,padding:CryptoJS.pad.Pkcs7}).toString(CryptoJS.enc.Utf8)}return config.url}catch(e){return""}}function urlencode(str){str=(str+"").toString();return encodeURIComponent(str).replace(/!/g,"%21").replace(/'/g,"%27").replace(/\(/g,"%28").replace(/\)/g,"%29").replace(/\*/g,"%2A").replace(/%20/g,"+")}function base64Encode(text){return CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(text))}function base64Decode(text){return CryptoJS.enc.Utf8.stringify(CryptoJS.enc.Base64.parse(text))}function md5(text){return CryptoJS.MD5(text).toString()}function encodeStr(input,encoding){encoding=encoding||"gbk";if(encoding.startsWith("gb")){const strTool=gbkTool();input=strTool.encode(input)}return input}function decodeStr(input,encoding){encoding=encoding||"gbk";if(encoding.startsWith("gb")){const strTool=gbkTool();input=strTool.decode(input)}return input}function getCryptoJS(){return'console.log("CryptoJS已装载");'}const RSA={encode:function(data,key,option){if(typeof rsaEncrypt==="function"){if(!option||typeof option!=="object"){return rsaEncrypt(data,key)}else{return rsaEncrypt(data,key,option)}}else{return false}},decode:function(data,key,option){if(typeof rsaDecrypt==="function"){if(!option||typeof option!=="object"){return rsaDecrypt(data,key)}else{return rsaDecrypt(data,key,option)}}else{return false}}};function getProxyUrl(){if(typeof getProxy==="function"){return getProxy(true)}else{return"http://127.0.0.1:9978/proxy?do=js"}}function forceOrder(lists,key,option){let start=Math.floor(lists.length/2);let end=Math.min(lists.length-1,start+1);if(start>=end){return lists}let first=lists[start];let second=lists[end];if(key){try{first=first[key];second=second[key]}catch(e){}}if(option&&typeof option==="function"){try{first=option(first);second=option(second)}catch(e){}}first+="";second+="";if(first.match(/(\d+)/)&&second.match(/(\d+)/)){let num1=Number(first.match(/(\d+)/)[1]);let num2=Number(second.match(/(\d+)/)[1]);if(num1>num2){lists.reverse()}}return lists}let VODS=[];let VOD={};let TABS=[];let LISTS=[];globalThis.encodeUrl=urlencode;globalThis.urlencode=urlencode;function urljoin(fromPath,nowPath){fromPath=fromPath||"";nowPath=nowPath||"";return joinUrl(fromPath,nowPath)}var urljoin2=urljoin;const defaultParser={pdfh:pdfh,pdfa:pdfa,pd:pd};function pdfh2(html,parse){let html2=html;try{if(typeof html!=="string"){html2=html.rr(html.ele).toString()}}catch(e){print("html对象转文本发生了错误:"+e.message)}let result=defaultParser.pdfh(html2,parse);let option=parse.includes("&&")?parse.split("&&").slice(-1)[0]:parse.split(" ").slice(-1)[0];if(/style/.test(option.toLowerCase())&&/url\(/.test(result)){try{result=result.match(/url\((.*?)\)/)[1];result=result.replace(/^['|"](.*)['|"]$/,"$1")}catch(e){}}return result}function pdfa2(html,parse){let html2=html;try{if(typeof html!=="string"){html2=html.rr(html.ele).toString()}}catch(e){print("html对象转文本发生了错误:"+e.message)}return defaultParser.pdfa(html2,parse)}function pd2(html,parse,uri){let ret=pdfh2(html,parse);if(typeof uri==="undefined"||!uri){uri=""}if(DOM_CHECK_ATTR.test(parse)&&!SPECIAL_URL.test(ret)){if(/http/.test(ret)){ret=ret.substr(ret.indexOf("http"))}else{ret=urljoin(MY_URL,ret)}}return ret}const parseTags={jsp:{pdfh:pdfh2,pdfa:pdfa2,pd:pd2},json:{pdfh(html,parse){if(!parse||!parse.trim()){return""}if(typeof html==="string"){html=JSON.parse(html)}parse=parse.trim();if(!parse.startsWith("$.")){parse="$."+parse}parse=parse.split("||");for(let ps of parse){let ret=cheerio.jp(ps,html);if(Array.isArray(ret)){ret=ret[0]||""}else{ret=ret||""}if(ret&&typeof ret!=="string"){ret=ret.toString()}if(ret){return ret}}return""},pdfa(html,parse){if(!parse||!parse.trim()){return""}if(typeof html==="string"){html=JSON.parse(html)}parse=parse.trim();if(!parse.startsWith("$.")){parse="$."+parse}let ret=cheerio.jp(parse,html);if(Array.isArray(ret)&&Array.isArray(ret[0])&&ret.length===1){return ret[0]||[]}return ret||[]},pd(html,parse){let ret=parseTags.json.pdfh(html,parse);if(ret){return urljoin(MY_URL,ret)}return ret}},jq:{pdfh(html,parse){if(!html||!parse||!parse.trim()){return""}parse=parse.trim();let result=defaultParser.pdfh(html,parse);return result},pdfa(html,parse){if(!html||!parse||!parse.trim()){return[]}parse=parse.trim();let result=defaultParser.pdfa(html,parse);print(`pdfa解析${parse}=>${result.length}`);return result},pd(html,parse,base_url){if(!html||!parse||!parse.trim()){return""}parse=parse.trim();base_url=base_url||MY_URL;return defaultParser.pd(html,parse,base_url)}},getParse(p0){if(p0.startsWith("jsp:")){return this.jsp}else if(p0.startsWith("json:")){return this.json}else if(p0.startsWith("jq:")){return this.jq}else{return this.jq}}};const stringify=JSON.stringify;const jsp=parseTags.jsp;const jq=parseTags.jq;function readFile(filePath){filePath=filePath||"./uri.min.js";var fd=os.open(filePath);var buffer=new ArrayBuffer(1024);var len=os.read(fd,buffer,0,1024);console.log(len);let text=String.fromCharCode.apply(null,new Uint8Array(buffer));console.log(text);return text}function dealJson(html){try{html=html.trim();if(!(html.startsWith("{")&&html.endsWith("}")||html.startsWith("[")&&html.endsWith("]"))){html="{"+html.match(/.*?\{(.*)\}/m)[1]+"}"}}catch(e){}try{html=JSON.parse(html)}catch(e){}return html}var OcrApi={api:OCR_API,classification:function(img){let code="";try{log("通过drpy_ocr验证码接口过验证...");let html=request(OCR_API,{data:{img:img},headers:{"User-Agent":PC_UA},method:"POST"},true);code=html||""}catch(e){log(`OCR识别验证码发生错误:${e.message}`)}return code}};function verifyCode(url){let cnt=0;let host=getHome(url);let cookie="";while(cnt<OCR_RETRY){try{let yzm_url=`${host}/index.php/verify/index.html`;console.log(`验证码链接:${yzm_url}`);let hhtml=request(yzm_url,{withHeaders:true,toBase64:true},true);let json=JSON.parse(hhtml);if(!cookie){let setCk=Object.keys(json).find(it=>it.toLowerCase()==="set-cookie");cookie=setCk?json[setCk].split(";")[0]:""}console.log("cookie:"+cookie);let img=json.body;let code=OcrApi.classification(img);console.log(`第${cnt+1}次验证码识别结果:${code}`);let submit_url=`${host}/index.php/ajax/verify_check?type=search&verify=${code}`;console.log(submit_url);let html=request(submit_url,{headers:{Cookie:cookie,"User-Agent":MOBILE_UA},method:"POST"});html=JSON.parse(html);if(html.msg==="ok"){console.log(`第${cnt+1}次验证码提交成功`);return cookie}else if(html.msg!=="ok"&&cnt+1>=OCR_RETRY){cookie=""}}catch(e){console.log(`第${cnt+1}次验证码提交失败:${e.message}`);if(cnt+1>=OCR_RETRY){cookie=""}}cnt+=1}return cookie}function setItem(k,v){local.set(RKEY,k,v);console.log(`规则${RKEY}设置${k} => ${v}`)}function getItem(k,v){return local.get(RKEY,k)||v}function clearItem(k){local.delete(RKEY,k)}function getHome(url){if(!url){return""}let tmp=url.split("//");url=tmp[0]+"//"+tmp[1].split("/")[0];try{url=decodeURIComponent(url)}catch(e){}return url}function buildUrl(url,obj){obj=obj||{};if(url.indexOf("?")<0){url+="?"}let param_list=[];let keys=Object.keys(obj);keys.forEach(it=>{param_list.push(it+"="+obj[it])});let prs=param_list.join("&");if(keys.length>0&&!url.endsWith("?")){url+="&"}url+=prs;return url}function require(url){eval(request(url))}function request(url,obj,ocr_flag){ocr_flag=ocr_flag||false;if(typeof obj==="undefined"||!obj||obj==={}){if(!fetch_params||!fetch_params.headers){let headers={"User-Agent":MOBILE_UA};if(rule.headers){Object.assign(headers,rule.headers)}if(!fetch_params){fetch_params={}}fetch_params.headers=headers}if(!fetch_params.headers.Referer){fetch_params.headers.Referer=getHome(url)}obj=fetch_params}else{let headers=obj.headers||{};let keys=Object.keys(headers).map(it=>it.toLowerCase());if(!keys.includes("user-agent")){headers["User-Agent"]=MOBILE_UA}if(!keys.includes("referer")){headers["Referer"]=getHome(url)}obj.headers=headers}if(rule.encoding&&rule.encoding!=="utf-8"&&!ocr_flag){if(!obj.headers.hasOwnProperty("Content-Type")&&!obj.headers.hasOwnProperty("content-type")){obj.headers["Content-Type"]="text/html; charset="+rule.encoding}}if(typeof obj.body!="undefined"&&obj.body&&typeof obj.body==="string"){if(!obj.headers.hasOwnProperty("Content-Type")&&!obj.headers.hasOwnProperty("content-type")){obj.headers["Content-Type"]="application/x-www-form-urlencoded; charset="+rule.encoding}}else if(typeof obj.body!="undefined"&&obj.body&&typeof obj.body==="object"){obj.data=obj.body;delete obj.body}if(!url){return obj.withHeaders?"{}":""}if(obj.toBase64){obj.buffer=2;delete obj.toBase64}console.log(JSON.stringify(obj.headers));console.log("request:"+url+`|method:${obj.method||"GET"}|body:${obj.body||""}`);let res=req(url,obj);let html=res.content||"";if(obj.withHeaders){let htmlWithHeaders=res.headers;htmlWithHeaders.body=html;return JSON.stringify(htmlWithHeaders)}else{return html}}function post(url,obj){obj.method="POST";return request(url,obj)}fetch=request;print=function(data){data=data||"";if(typeof data=="object"&&Object.keys(data).length>0){try{data=JSON.stringify(data);console.log(data)}catch(e){console.log(typeof data+":"+data.length);return}}else if(typeof data=="object"&&Object.keys(data).length<1){console.log("null object")}else{console.log(data)}};log=print;function checkHtml(html,url,obj){if(/\?btwaf=/.test(html)){let btwaf=html.match(/btwaf(.*?)"/)[1];url=url.split("#")[0]+"?btwaf"+btwaf;print("宝塔验证访问链接:"+url);html=request(url,obj)}return html}function getCode(url,obj){let html=request(url,obj);html=checkHtml(html,url,obj);return html}function getHtml(url){let obj={};if(rule.headers){obj.headers=rule.headers}let cookie=getItem(RULE_CK,"");if(cookie){if(obj.headers&&!Object.keys(obj.headers).map(it=>it.toLowerCase()).includes("cookie")){log("历史无cookie,新增过验证后的cookie");obj.headers["Cookie"]=cookie}else if(obj.headers&&obj.headers.cookie&&obj.headers.cookie!==cookie){obj.headers["Cookie"]=cookie;log("历史有小写过期的cookie,更新过验证后的cookie")}else if(obj.headers&&obj.headers.Cookie&&obj.headers.Cookie!==cookie){obj.headers["Cookie"]=cookie;log("历史有大写过期的cookie,更新过验证后的cookie")}else if(!obj.headers){obj.headers={Cookie:cookie};log("历史无headers,更新过验证后的含cookie的headers")}}let html=getCode(url,obj);return html}function homeParse(homeObj){fetch_params=JSON.parse(JSON.stringify(rule_fetch_params));let classes=[];if(homeObj.class_name&&homeObj.class_url){let names=homeObj.class_name.split("&");let urls=homeObj.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]})}}if(homeObj.class_parse){let p=homeObj.class_parse.split(";");let _ps=parseTags.getParse(p[0]);_pdfa=_ps.pdfa;_pdfh=_ps.pdfh;_pd=_ps.pd;MY_URL=rule.url;if(p.length>=3){try{let html=getHtml(homeObj.MY_URL);if(html){homeHtmlCache=html;let list=_pdfa(html,p[0]);if(list&&list.length>0){list.forEach((it,idex)=>{try{let name=_pdfh(it,p[1]);if(homeObj.cate_exclude&&new RegExp(homeObj.cate_exclude).test(name)){return}let url=_pd(it,p[2]);if(p.length>3&&p[3]){let exp=new RegExp(p[3]);url=url.match(exp)[1]}classes.push({type_id:url.trim(),type_name:name.trim()})}catch(e){console.log(`分类列表定位第${idex}个元素正常报错:${e.message}`)}})}}}catch(e){console.log(e.message)}}}classes=classes.filter(it=>!homeObj.cate_exclude||!new RegExp(homeObj.cate_exclude).test(it.type_name));let resp={class:classes};if(homeObj.filter){resp.filters=homeObj.filter}console.log(JSON.stringify(resp));return JSON.stringify(resp)}function getPP(p,pn,pp,ppn){try{let ps=p[pn]==="*"&&pp.length>ppn?pp[ppn]:p[pn];return ps}catch(e){return""}}function homeVodParse(homeVodObj){fetch_params=JSON.parse(JSON.stringify(rule_fetch_params));let d=[];MY_URL=homeVodObj.homeUrl;console.log(MY_URL);let t1=(new Date).getTime();let p=homeVodObj.推荐;print("p:"+p);if(p==="*"&&rule.一级){p=rule.一级;homeVodObj.double=false}if(!p||typeof p!=="string"){return"{}"}p=p.trim();let pp=rule.一级.split(";");if(p.startsWith("js:")){const TYPE="home";var input=MY_URL;HOST=rule.host;eval(p.replace("js:",""));d=VODS}else{p=p.split(";");if(!homeVodObj.double&&p.length<5){return"{}"}else if(homeVodObj.double&&p.length<6){return"{}"}let p0=getPP(p,0,pp,0);let _ps=parseTags.getParse(p0);_pdfa=_ps.pdfa;_pdfh=_ps.pdfh;_pd=_ps.pd;let is_json=p0.startsWith("json:");p0=p0.replace(/^(jsp:|json:|jq:)/,"");let html=homeHtmlCache||getHtml(MY_URL);homeHtmlCache=undefined;if(is_json){html=dealJson(html)}try{console.log("double:"+homeVodObj.double);if(homeVodObj.double){let items=_pdfa(html,p0);let p1=getPP(p,1,pp,0);let p2=getPP(p,2,pp,1);let p3=getPP(p,3,pp,2);let p4=getPP(p,4,pp,3);let p5=getPP(p,5,pp,4);let p6=getPP(p,6,pp,5);for(let item of items){let items2=_pdfa(item,p1);for(let item2 of items2){try{let title=_pdfh(item2,p2);let img="";try{img=_pd(item2,p3)}catch(e){}let desc="";try{desc=_pdfh(item2,p4)}catch(e){}let links=[];for(let _p5 of p5.split("+")){let link=!homeVodObj.detailUrl?_pd(item2,_p5,MY_URL):_pdfh(item2,_p5);links.push(link)}let content;if(p.length>6&&p[6]){content=_pdfh(item2,p6)}else{content=""}let vid=links.join("$");if(rule.二级==="*"){vid=vid+"@@"+title+"@@"+img}let vod={vod_name:title,vod_pic:img,vod_remarks:desc,vod_content:content,vod_id:vid};d.push(vod)}catch(e){console.log("首页列表双层定位处理发生错误:"+e.message)}}}}else{let items=_pdfa(html,p0);let p1=getPP(p,1,pp,1);let p2=getPP(p,2,pp,2);let p3=getPP(p,3,pp,3);let p4=getPP(p,4,pp,4);let p5=getPP(p,5,pp,5);for(let item of items){try{let title=_pdfh(item,p1);let img="";try{img=_pd(item,p2,MY_URL)}catch(e){}let desc="";try{desc=_pdfh(item,p3)}catch(e){}let links=[];for(let _p5 of p4.split("+")){let link=!homeVodObj.detailUrl?_pd(item,_p5,MY_URL):_pdfh(item,_p5);links.push(link)}let content;if(p.length>5&&p[5]){content=_pdfh(item,p5)}else{content=""}let vid=links.join("$");if(rule.二级==="*"){vid=vid+"@@"+title+"@@"+img}let vod={vod_name:title,vod_pic:img,vod_remarks:desc,vod_content:content,vod_id:vid};d.push(vod)}catch(e){console.log("首页列表单层定位处理发生错误:"+e.message)}}}}catch(e){}}let t2=(new Date).getTime();console.log("加载首页推荐耗时:"+(t2-t1)+"毫秒");if(rule.图片来源){d.forEach(it=>{if(it.vod_pic&&it.vod_pic.startsWith("http")){it.vod_pic=it.vod_pic+rule.图片来源}})}if(d.length>0){print(d.slice(0,2))}return JSON.stringify({list:d})}function categoryParse(cateObj){fetch_params=JSON.parse(JSON.stringify(rule_fetch_params));let p=cateObj.一级;if(!p||typeof p!=="string"){return"{}"}let d=[];let url=cateObj.url.replaceAll("fyclass",cateObj.tid);if(cateObj.pg===1&&url.includes("[")&&url.includes("]")){url=url.split("[")[1].split("]")[0]}else if(cateObj.pg>1&&url.includes("[")&&url.includes("]")){url=url.split("[")[0]}if(rule.filter_url){if(!/fyfilter/.test(url)){if(!url.endsWith("&")&&!rule.filter_url.startsWith("&")){url+="&"}url+=rule.filter_url}else{url=url.replace("fyfilter",rule.filter_url)}let fl=cateObj.filter?cateObj.extend:{};if(rule.filter_def&&typeof rule.filter_def==="object"){try{if(Object.keys(rule.filter_def).length>0&&rule.filter_def.hasOwnProperty(cateObj.tid)){let self_fl_def=rule.filter_def[cateObj.tid];if(self_fl_def&&typeof self_fl_def==="object"){let fl_def=JSON.parse(JSON.stringify(self_fl_def));fl=Object.assign(fl_def,fl)}}}catch(e){print("合并不同分类对应的默认筛选出错:"+e.message)}}let new_url;new_url=cheerio.jinja2(url,{fl:fl});url=new_url}if(/fypage/.test(url)){if(url.includes("(")&&url.includes(")")){let url_rep=url.match(/.*?\((.*)\)/)[1];let cnt_page=url_rep.replaceAll("fypage",cateObj.pg);let cnt_pg=eval(cnt_page);url=url.replaceAll(url_rep,cnt_pg).replaceAll("(","").replaceAll(")","")}else{url=url.replaceAll("fypage",cateObj.pg)}}MY_URL=url;console.log(MY_URL);p=p.trim();const MY_CATE=cateObj.tid;if(p.startsWith("js:")){var MY_FL=cateObj.extend;const TYPE="cate";var input=MY_URL;const MY_PAGE=cateObj.pg;var desc="";eval(p.trim().replace("js:",""));d=VODS}else{p=p.split(";");if(p.length<5){return"{}"}let _ps=parseTags.getParse(p[0]);_pdfa=_ps.pdfa;_pdfh=_ps.pdfh;_pd=_ps.pd;let is_json=p[0].startsWith("json:");p[0]=p[0].replace(/^(jsp:|json:|jq:)/,"");try{let html=getHtml(MY_URL);if(html){if(is_json){html=dealJson(html)}let list=_pdfa(html,p[0]);list.forEach(it=>{let links=p[4].split("+").map(p4=>{return!rule.detailUrl?_pd(it,p4,MY_URL):_pdfh(it,p4)});let link=links.join("$");let vod_id=rule.detailUrl?MY_CATE+"$"+link:link;let vod_name=_pdfh(it,p[1]).replace(/\n|\t/g,"").trim();let vod_pic=_pd(it,p[2],MY_URL);if(rule.二级==="*"){vod_id=vod_id+"@@"+vod_name+"@@"+vod_pic}d.push({vod_id:vod_id,vod_name:vod_name,vod_pic:vod_pic,vod_remarks:_pdfh(it,p[3]).replace(/\n|\t/g,"").trim()})})}}catch(e){console.log(e.message)}}if(rule.图片来源){d.forEach(it=>{if(it.vod_pic&&it.vod_pic.startsWith("http")){it.vod_pic=it.vod_pic+rule.图片来源}})}if(d.length>0){print(d.slice(0,2))}let pagecount=0;if(rule.pagecount&&typeof rule.pagecount==="object"&&rule.pagecount.hasOwnProperty(MY_CATE)){print(`MY_CATE:${MY_CATE},pagecount:${JSON.stringify(rule.pagecount)}`);pagecount=parseInt(rule.pagecount[MY_CATE])}let nodata={list:[{vod_name:"无数据,防无限请求",vod_id:"no_data",vod_remarks:"不要点,会崩的",vod_pic:"https://ghproxy.net/https://raw.githubusercontent.com/hjdhnx/dr_py/main/404.jpg"}],total:1,pagecount:1,page:1,limit:1};let vod=d.length<1?JSON.stringify(nodata):JSON.stringify({page:parseInt(cateObj.pg),pagecount:pagecount||999,limit:20,total:999,list:d});return vod}function searchParse(searchObj){fetch_params=JSON.parse(JSON.stringify(rule_fetch_params));let d=[];if(!searchObj.searchUrl){return"{}"}let p=searchObj.搜索==="*"&&rule.一级?rule.一级:searchObj.搜索;if(!p||typeof p!=="string"){return"{}"}p=p.trim();let pp=rule.一级.split(";");let url=searchObj.searchUrl.replaceAll("**",searchObj.wd);if(searchObj.pg===1&&url.includes("[")&&url.includes("]")&&!url.includes("#")){url=url.split("[")[1].split("]")[0]}else if(searchObj.pg>1&&url.includes("[")&&url.includes("]")&&!url.includes("#")){url=url.split("[")[0]}if(/fypage/.test(url)){if(url.includes("(")&&url.includes(")")){let url_rep=url.match(/.*?\((.*)\)/)[1];let cnt_page=url_rep.replaceAll("fypage",searchObj.pg);let cnt_pg=eval(cnt_page);url=url.replaceAll(url_rep,cnt_pg).replaceAll("(","").replaceAll(")","")}else{url=url.replaceAll("fypage",searchObj.pg)}}MY_URL=url;console.log(MY_URL);if(p.startsWith("js:")){const TYPE="search";const MY_PAGE=searchObj.pg;const KEY=searchObj.wd;var input=MY_URL;var detailUrl=rule.detailUrl||"";eval(p.trim().replace("js:",""));d=VODS}else{p=p.split(";");if(p.length<5){return"{}"}let p0=getPP(p,0,pp,0);let _ps=parseTags.getParse(p0);_pdfa=_ps.pdfa;_pdfh=_ps.pdfh;_pd=_ps.pd;let is_json=p0.startsWith("json:");p0=p0.replace(/^(jsp:|json:|jq:)/,"");try{let req_method=MY_URL.split(";").length>1?MY_URL.split(";")[1].toLowerCase():"get";let html;if(req_method==="post"){let rurls=MY_URL.split(";")[0].split("#");let rurl=rurls[0];let params=rurls.length>1?rurls[1]:"";print(`post=》rurl:${rurl},params:${params}`);let _fetch_params=JSON.parse(JSON.stringify(rule_fetch_params));let postData={body:params};Object.assign(_fetch_params,postData);html=post(rurl,_fetch_params)}else if(req_method==="postjson"){let rurls=MY_URL.split(";")[0].split("#");let rurl=rurls[0];let params=rurls.length>1?rurls[1]:"";print(`postjson-》rurl:${rurl},params:${params}`);try{params=JSON.parse(params)}catch(e){params="{}"}let _fetch_params=JSON.parse(JSON.stringify(rule_fetch_params));let postData={body:params};Object.assign(_fetch_params,postData);html=post(rurl,_fetch_params)}else{html=getHtml(MY_URL)}if(html){if(/系统安全验证|输入验证码/.test(html)){let cookie=verifyCode(MY_URL);if(cookie){console.log(`本次成功过验证,cookie:${cookie}`);setItem(RULE_CK,cookie)}else{console.log(`本次自动过搜索验证失败,cookie:${cookie}`)}html=getHtml(MY_URL)}if(!html.includes(searchObj.wd)){console.log("搜索结果源码未包含关键字,疑似搜索失败,正为您打印结果源码");console.log(html)}if(is_json){html=dealJson(html)}let list=_pdfa(html,p0);let p1=getPP(p,1,pp,1);let p2=getPP(p,2,pp,2);let p3=getPP(p,3,pp,3);let p4=getPP(p,4,pp,4);let p5=getPP(p,5,pp,5);list.forEach(it=>{let links=p4.split("+").map(_p4=>{return!rule.detailUrl?_pd(it,_p4,MY_URL):_pdfh(it,_p4)});let link=links.join("$");let content;if(p.length>5&&p[5]){content=_pdfh(it,p5)}else{content=""}let vod_id=link;let vod_name=_pdfh(it,p1).replace(/\n|\t/g,"").trim();let vod_pic=_pd(it,p2,MY_URL);if(rule.二级==="*"){vod_id=vod_id+"@@"+vod_name+"@@"+vod_pic}let ob={vod_id:vod_id,vod_name:vod_name,vod_pic:vod_pic,vod_remarks:_pdfh(it,p3).replace(/\n|\t/g,"").trim(),vod_content:content.replace(/\n|\t/g,"").trim()};d.push(ob)})}}catch(e){print("搜索发生错误:"+e.message);return"{}"}}if(rule.图片来源){d.forEach(it=>{if(it.vod_pic&&it.vod_pic.startsWith("http")){it.vod_pic=it.vod_pic+rule.图片来源}})}return JSON.stringify({page:parseInt(searchObj.pg),pagecount:10,limit:20,total:100,list:d})}function detailParse(detailObj){let t1=(new Date).getTime();fetch_params=JSON.parse(JSON.stringify(rule_fetch_params));let orId=detailObj.orId;let vod_name="片名";let vod_pic="";let vod_id=orId;if(rule.二级==="*"){let extra=orId.split("@@");vod_name=extra.length>1?extra[1]:vod_name;vod_pic=extra.length>2?extra[2]:vod_pic}let vod={vod_id:vod_id,vod_name:vod_name,vod_pic:vod_pic,type_name:"类型",vod_year:"年份",vod_area:"地区",vod_remarks:"更新信息",vod_actor:"主演",vod_director:"导演",vod_content:"简介"};let p=detailObj.二级;let url=detailObj.url;let detailUrl=detailObj.detailUrl;let fyclass=detailObj.fyclass;let tab_exclude=detailObj.tab_exclude;let html=detailObj.html||"";MY_URL=url;if(detailObj.二级访问前){try{print(`尝试在二级访问前执行代码:${detailObj.二级访问前}`);eval(detailObj.二级访问前.trim().replace("js:",""))}catch(e){print(`二级访问前执行代码出现错误:${e.message}`)}}if(p==="*"){vod.vod_play_from="道长在线";vod.vod_remarks=detailUrl;vod.vod_actor="没有二级,只有一级链接直接嗅探播放";vod.vod_content=MY_URL;vod.vod_play_url="嗅探播放$"+MY_URL.split("@@")[0]}else if(typeof p==="string"&&p.trim().startsWith("js:")){const TYPE="detail";var input=MY_URL;var play_url="";eval(p.trim().replace("js:",""));vod=VOD;console.log(JSON.stringify(vod))}else if(p&&typeof p==="object"){let tt1=(new Date).getTime();if(!html){html=getHtml(MY_URL)}print(`二级${MY_URL}仅获取源码耗时:${(new Date).getTime()-tt1}毫秒`);let _ps;if(p.is_json){print("二级是json");_ps=parseTags.json;html=dealJson(html)}else if(p.is_jsp){print("二级是jsp");_ps=parseTags.jsp}else if(p.is_jq){print("二级是jq");_ps=parseTags.jq}else{print("二级默认jq");_ps=parseTags.jq}let tt2=(new Date).getTime();print(`二级${MY_URL}获取并装载源码耗时:${tt2-tt1}毫秒`);_pdfa=_ps.pdfa;_pdfh=_ps.pdfh;_pd=_ps.pd;if(p.title){let p1=p.title.split(";");vod.vod_name=_pdfh(html,p1[0]).replace(/\n|\t/g,"").trim();let type_name=p1.length>1?_pdfh(html,p1[1]).replace(/\n|\t/g,"").replace(/ /g,"").trim():"";vod.type_name=type_name||vod.type_name}if(p.desc){try{let p1=p.desc.split(";");vod.vod_remarks=_pdfh(html,p1[0]).replace(/\n|\t/g,"").trim();vod.vod_year=p1.length>1?_pdfh(html,p1[1]).replace(/\n|\t/g,"").trim():"";vod.vod_area=p1.length>2?_pdfh(html,p1[2]).replace(/\n|\t/g,"").trim():"";vod.vod_actor=p1.length>3?_pdfh(html,p1[3]).replace(/\n|\t/g,"").trim():"";vod.vod_director=p1.length>4?_pdfh(html,p1[4]).replace(/\n|\t/g,"").trim():""}catch(e){}}if(p.content){try{let p1=p.content.split(";");vod.vod_content=_pdfh(html,p1[0]).replace(/\n|\t/g,"").trim()}catch(e){}}if(p.img){try{let p1=p.img.split(";");vod.vod_pic=_pd(html,p1[0],MY_URL)}catch(e){}}let vod_play_from="$$$";let playFrom=[];if(p.重定向&&p.重定向.startsWith("js:")){print("开始执行重定向代码:"+p.重定向);html=eval(p.重定向.replace("js:",""))}if(p.tabs){if(p.tabs.startsWith("js:")){print("开始执行tabs代码:"+p.tabs);var input=MY_URL;eval(p.tabs.replace("js:",""));playFrom=TABS}else{let p_tab=p.tabs.split(";")[0];let vHeader=_pdfa(html,p_tab);console.log(vHeader.length);let tab_text=p.tab_text||"body&&Text";let new_map={};for(let v of vHeader){let v_title=_pdfh(v,tab_text).trim();console.log(v_title);if(tab_exclude&&new RegExp(tab_exclude).test(v_title)){continue}if(!new_map.hasOwnProperty(v_title)){new_map[v_title]=1}else{new_map[v_title]+=1}if(new_map[v_title]>1){v_title+=Number(new_map[v_title]-1)}playFrom.push(v_title)}}console.log(JSON.stringify(playFrom))}else{playFrom=["道长在线"]}vod.vod_play_from=playFrom.join(vod_play_from);let vod_play_url="$$$";let vod_tab_list=[];if(p.lists){if(p.lists.startsWith("js:")){print("开始执行lists代码:"+p.lists);try{var input=MY_URL;var play_url="";eval(p.lists.replace("js:",""));for(let i in LISTS){if(LISTS.hasOwnProperty(i)){try{LISTS[i]=LISTS[i].map(it=>it.split("$").slice(0,2).join("$"))}catch(e){print("格式化LISTS发生错误:"+e.message)}}}vod_play_url=LISTS.map(it=>it.join("#")).join(vod_play_url)}catch(e){print("js执行lists: 发生错误:"+e.message)}}else{let list_text=p.list_text||"body&&Text";let list_url=p.list_url||"a&&href";let is_tab_js=p.tabs.trim().startsWith("js:");for(let i=0;i<playFrom.length;i++){let tab_name=playFrom[i];let tab_ext=p.tabs.split(";").length>1&&!is_tab_js?p.tabs.split(";")[1]:"";let p1=p.lists.replaceAll("#idv",tab_name).replaceAll("#id",i);tab_ext=tab_ext.replaceAll("#idv",tab_name).replaceAll("#id",i);let tabName=tab_ext?_pdfh(html,tab_ext):tab_name;console.log(tabName);let new_vod_list=[];let tt1=(new Date).getTime();if(typeof pdfl==="function"){new_vod_list=pdfl(html,p1,list_text,list_url,MY_URL)}else{let vodList=[];try{vodList=_pdfa(html,p1);console.log("len(vodList):"+vodList.length)}catch(e){}for(let i=0;i<vodList.length;i++){let it=vodList[i];new_vod_list.push(_pdfh(it,list_text).trim()+"$"+_pd(it,list_url,MY_URL))}}if(new_vod_list.length>0){new_vod_list=forceOrder(new_vod_list,"",x=>x.split("$")[0]);console.log(`drpy影响性能代码共计列表数循环次数:${new_vod_list.length},耗时:${(new Date).getTime()-tt1}毫秒`)}let vlist=new_vod_list.join("#");vod_tab_list.push(vlist)}vod_play_url=vod_tab_list.join(vod_play_url)}}vod.vod_play_url=vod_play_url}if(rule.图片来源&&vod.vod_pic&&vod.vod_pic.startsWith("http")){vod.vod_pic=vod.vod_pic+rule.图片来源}if(!vod.vod_id||vod_id.includes("$")&&vod.vod_id!==vod_id){vod.vod_id=vod_id}let t2=(new Date).getTime();console.log(`加载二级界面${MY_URL}耗时:${t2-t1}毫秒`);vod=vodDeal(vod);return JSON.stringify({list:[vod]})}function get_tab_index(vod){let obj={};vod.vod_play_from.split("$$$").forEach((it,index)=>{obj[it]=index});return obj}function vodDeal(vod){let vod_play_from=vod.vod_play_from.split("$$$");let vod_play_url=vod.vod_play_url.split("$$$");let tab_removed_list=vod_play_from;let tab_ordered_list=vod_play_from;let tab_renamed_list=vod_play_from;let tab_list=vod_play_from;let play_ordered_list=vod_play_url;if(rule.tab_remove&&rule.tab_remove.length>0||rule.tab_order&&rule.tab_order.length>0){let tab_index_dict=get_tab_index(vod);if(rule.tab_remove&&rule.tab_remove.length>0){tab_removed_list=vod_play_from.filter(it=>!rule.tab_remove.includes(it));tab_list=tab_removed_list}if(rule.tab_order&&rule.tab_order.length>0){let tab_order=rule.tab_order;tab_ordered_list=tab_removed_list.sort((a,b)=>{return(tab_order.indexOf(a)===-1?9999:tab_order.indexOf(a))-(tab_order.indexOf(b)===-1?9999:tab_order.indexOf(b))});tab_list=tab_ordered_list}play_ordered_list=tab_list.map(it=>vod_play_url[tab_index_dict[it]])}if(rule.tab_rename&&typeof rule.tab_rename==="object"&Object.keys(rule.tab_rename).length>0){tab_renamed_list=tab_list.map(it=>rule.tab_rename[it]||it);tab_list=tab_renamed_list}vod.vod_play_from=tab_list.join("$$$");vod.vod_play_url=play_ordered_list.join("$$$");return vod}function tellIsJx(url){try{let is_vip=!/\.(m3u8|mp4|m4a)$/.test(url.split("?")[0])&&是否正版(url);return is_vip?1:0}catch(e){return 1}}function playParse(playObj){fetch_params=JSON.parse(JSON.stringify(rule_fetch_params));MY_URL=playObj.url;if(!/http/.test(MY_URL)){try{MY_URL=base64Decode(MY_URL)}catch(e){}}MY_URL=decodeURIComponent(MY_URL);var input=MY_URL;let common_play={parse:1,url:input,jx:tellIsJx(input)};let lazy_play;if(!rule.play_parse||!rule.lazy){lazy_play=common_play}else if(rule.play_parse&&rule.lazy&&typeof rule.lazy==="string"){try{let lazy_code=rule.lazy.replace("js:","").trim();print("开始执行js免嗅=>"+lazy_code);eval(lazy_code);lazy_play=typeof input==="object"?input:{parse:1,jx:tellIsJx(input),url:input}}catch(e){print("js免嗅错误:"+e.message);lazy_play=common_play}}else{lazy_play=common_play}if(Array.isArray(rule.play_json)&&rule.play_json.length>0){let web_url=lazy_play.url;for(let pjson of rule.play_json){if(pjson.re&&(pjson.re==="*"||web_url.match(new RegExp(pjson.re)))){if(pjson.json&&typeof pjson.json==="object"){let base_json=pjson.json;lazy_play=Object.assign(lazy_play,base_json);break}}}}else if(rule.play_json&&!Array.isArray(rule.play_json)){let base_json={jx:1,parse:1};lazy_play=Object.assign(lazy_play,base_json)}else if(!rule.play_json){let base_json={jx:0,parse:1};lazy_play=Object.assign(lazy_play,base_json)}console.log(JSON.stringify(lazy_play));return JSON.stringify(lazy_play)}function proxyParse(proxyObj){var input=proxyObj.params;if(proxyObj.proxy_rule){log("准备执行本地代理规则:\n"+proxyObj.proxy_rule);try{eval(proxyObj.proxy_rule);if(input&&input!==proxyObj.params&&Array.isArray(input)&&input.length===3){return input}else{return[404,"text/plain","Not Found"]}}catch(e){return[500,"text/plain","代理规则错误:"+e.message]}}else{return[404,"text/plain","Not Found"]}}function isVideoParse(isVideoObj){var input=isVideoObj.url;if(!isVideoObj.t){let re_matcher=new RegExp(isVideoObj.isVideo,"i");return re_matcher.test(input)}else{try{eval(isVideoObj.isVideo);if(typeof input==="boolean"){return input}else{return false}}catch(e){log("执行嗅探规则发生错误:"+e.message);return false}}}function init(ext){console.log("init");try{let muban=模板.getMubans();if(typeof ext=="object"){rule=ext}else if(typeof ext=="string"){if(ext.startsWith("http")){let js=request(ext,{method:"GET"});if(js){eval(js.replace("var rule","rule"))}}else{eval(ext.replace("var rule","rule"))}}if(rule.模板&&muban.hasOwnProperty(rule.模板)){print("继承模板:"+rule.模板);rule=Object.assign(muban[rule.模板],rule)}let rule_cate_excludes=(rule.cate_exclude||"").split("|").filter(it=>it.trim());let rule_tab_excludes=(rule.tab_exclude||"").split("|").filter(it=>it.trim());rule_cate_excludes=rule_cate_excludes.concat(CATE_EXCLUDE.split("|").filter(it=>it.trim()));rule_tab_excludes=rule_tab_excludes.concat(TAB_EXCLUDE.split("|").filter(it=>it.trim()));rule.cate_exclude=rule_cate_excludes.join("|");rule.tab_exclude=rule_tab_excludes.join("|");rule.host=(rule.host||"").rstrip("/");HOST=rule.host;if(rule.hostJs){console.log(`检测到hostJs,准备执行...`);try{eval(rule.hostJs);rule.host=HOST.rstrip("/")}catch(e){console.log(`执行${rule.hostJs}获取host发生错误:`+e.message)}}rule.url=rule.url||"";rule.double=rule.double||false;rule.homeUrl=rule.homeUrl||"";rule.detailUrl=rule.detailUrl||"";rule.searchUrl=rule.searchUrl||"";rule.homeUrl=rule.host&&rule.homeUrl?urljoin(rule.host,rule.homeUrl):rule.homeUrl||rule.host;rule.detailUrl=rule.host&&rule.detailUrl?urljoin(rule.host,rule.detailUrl):rule.detailUrl;rule.二级访问前=rule.二级访问前||"";if(rule.url.includes("[")&&rule.url.includes("]")){let u1=rule.url.split("[")[0];let u2=rule.url.split("[")[1].split("]")[0];rule.url=rule.host&&rule.url?urljoin(rule.host,u1)+"["+urljoin(rule.host,u2)+"]":rule.url}else{rule.url=rule.host&&rule.url?urljoin(rule.host,rule.url):rule.url}if(rule.searchUrl.includes("[")&&rule.searchUrl.includes("]")&&!rule.searchUrl.includes("#")){let u1=rule.searchUrl.split("[")[0];let u2=rule.searchUrl.split("[")[1].split("]")[0];rule.searchUrl=rule.host&&rule.searchUrl?urljoin(rule.host,u1)+"["+urljoin(rule.host,u2)+"]":rule.searchUrl}else{rule.searchUrl=rule.host&&rule.searchUrl?urljoin(rule.host,rule.searchUrl):rule.searchUrl}rule.timeout=rule.timeout||5e3;rule.encoding=rule.编码||rule.encoding||"utf-8";rule.search_encoding=rule.搜索编码||rule.search_encoding||"";rule.图片来源=rule.图片来源||"";rule.play_json=rule.hasOwnProperty("play_json")?rule.play_json:[];rule.pagecount=rule.hasOwnProperty("pagecount")?rule.pagecount:{};rule.proxy_rule=rule.hasOwnProperty("proxy_rule")?rule.proxy_rule:"";rule.sniffer=rule.hasOwnProperty("sniffer")?rule.sniffer:"";rule.sniffer=!!(rule.sniffer&&rule.sniffer!=="0"&&rule.sniffer!=="false");rule.isVideo=rule.hasOwnProperty("isVideo")?rule.isVideo:"";rule.tab_remove=rule.hasOwnProperty("tab_remove")?rule.tab_remove:[];rule.tab_order=rule.hasOwnProperty("tab_order")?rule.tab_order:[];rule.tab_rename=rule.hasOwnProperty("tab_rename")?rule.tab_rename:{};if(rule.headers&&typeof rule.headers==="object"){try{let header_keys=Object.keys(rule.headers);for(let k of header_keys){if(k.toLowerCase()==="user-agent"){let v=rule.headers[k];console.log(v);if(["MOBILE_UA","PC_UA","UC_UA","IOS_UA","UA"].includes(v)){rule.headers[k]=eval(v)}}else if(k.toLowerCase()==="cookie"){let v=rule.headers[k];if(v&&v.startsWith("http")){console.log(v);try{v=fetch(v);console.log(v);rule.headers[k]=v}catch(e){console.log(`从${v}获取cookie发生错误:`+e.message)}}}}}catch(e){console.log("处理headers发生错误:"+e.message)}}rule_fetch_params={headers:rule.headers||false,timeout:rule.timeout,encoding:rule.encoding};oheaders=rule.headers||{};RKEY=typeof key!=="undefined"&&key?key:"drpy_"+(rule.title||rule.host);pre();init_test()}catch(e){console.log("init_test发生错误:"+e.message)}}let homeHtmlCache=undefined;function home(filter){console.log("home");let homeObj={filter:rule.filter||false,MY_URL:rule.homeUrl,class_name:rule.class_name||"",class_url:rule.class_url||"",class_parse:rule.class_parse||"",cate_exclude:rule.cate_exclude};return homeParse(homeObj)}function homeVod(params){console.log("homeVod");let homeVodObj={"推荐":rule.推荐,double:rule.double,homeUrl:rule.homeUrl,detailUrl:rule.detailUrl};return homeVodParse(homeVodObj)}function category(tid,pg,filter,extend){let cateObj={url:rule.url,"一级":rule.一级,tid:tid,pg:parseInt(pg),filter:filter,extend:extend};return categoryParse(cateObj)}function detail(vod_url){let orId=vod_url;let fyclass="";log("orId:"+orId);if(vod_url.indexOf("$")>-1){let tmp=vod_url.split("$");fyclass=tmp[0];vod_url=tmp[1]}let detailUrl=vod_url.split("@@")[0];let url;if(!detailUrl.startsWith("http")&&!detailUrl.includes("/")){url=rule.detailUrl.replaceAll("fyid",detailUrl).replaceAll("fyclass",fyclass)}else if(detailUrl.includes("/")){url=urljoin(rule.homeUrl,detailUrl)}else{url=detailUrl}let detailObj={orId:orId,url:url,"二级":rule.二级,"二级访问前":rule.二级访问前,detailUrl:detailUrl,fyclass:fyclass,tab_exclude:rule.tab_exclude};return detailParse(detailObj)}function play(flag,id,flags){let playObj={url:id,flag:flag,flags:flags};return playParse(playObj)}function search(wd,quick,pg){if(rule.search_encoding){if(rule.search_encoding.toLowerCase()!=="utf-8"){wd=encodeStr(wd,rule.search_encoding)}}else if(rule.encoding&&rule.encoding.toLowerCase()!=="utf-8"){wd=encodeStr(wd,rule.encoding)}let searchObj={searchUrl:rule.searchUrl,"搜索":rule.搜索,wd:wd,pg:pg||1,quick:quick};return searchParse(searchObj)}function proxy(params){if(rule.proxy_rule&&rule.proxy_rule.trim()){rule.proxy_rule=rule.proxy_rule.trim()}if(rule.proxy_rule.startsWith("js:")){rule.proxy_rule=rule.proxy_rule.replace("js:","")}let proxyObj={params:params,proxy_rule:rule.proxy_rule};return proxyParse(proxyObj)}function sniffer(){let enable_sniffer=rule.sniffer||false;if(enable_sniffer){log("开始执行辅助嗅探代理规则...")}return enable_sniffer}function isVideo(url){let t=0;let is_video;if(rule.isVideo&&rule.isVideo.trim()){is_video=rule.isVideo.trim()}if(is_video.startsWith("js:")){is_video=is_video.replace("js:","");t=1}let isVideoObj={url:url,isVideo:is_video,t:t};let result=isVideoParse(isVideoObj);if(result){log("成功执行辅助嗅探规则并检测到视频地址:\n"+rule.isVideo)}return result}function DRPY(){return{init:init,home:home,homeVod:homeVod,category:category,detail:detail,play:play,search:search,proxy:proxy,sniffer:sniffer,isVideo:isVideo}}export default{init:init,home:home,homeVod:homeVod,category:category,detail:detail,play:play,search:search,proxy:proxy,sniffer:sniffer,isVideo:isVideo,DRPY:DRPY};
|
||||
@@ -0,0 +1,180 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# @Author : Doubebly
|
||||
# @Time : 2025/1/21 23:07
|
||||
|
||||
import hashlib
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
import requests
|
||||
sys.path.append('..')
|
||||
from base.spider import Spider
|
||||
|
||||
|
||||
class Spider(Spider):
|
||||
def getName(self):
|
||||
return "JieYingShi"
|
||||
|
||||
def init(self, extend):
|
||||
self.home_url = 'https://www.hkybqufgh.com'
|
||||
self.error_url = 'https://json.doube.eu.org/error/4gtv/index.m3u8'
|
||||
self.headers = {
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
|
||||
}
|
||||
|
||||
def getDependence(self):
|
||||
return []
|
||||
|
||||
def isVideoFormat(self, url):
|
||||
pass
|
||||
|
||||
def manualVideoCheck(self):
|
||||
pass
|
||||
|
||||
def homeContent(self, filter):
|
||||
|
||||
return {'class': [
|
||||
{
|
||||
'type_id': '1',
|
||||
'type_name': '电影'
|
||||
},
|
||||
{
|
||||
'type_id': '2',
|
||||
'type_name': '电视剧'
|
||||
},
|
||||
{
|
||||
'type_id': '4',
|
||||
'type_name': '动漫'
|
||||
},
|
||||
{
|
||||
'type_id': '3',
|
||||
'type_name': '综艺'
|
||||
}
|
||||
]}
|
||||
|
||||
def homeVideoContent(self):
|
||||
a = self.get_data(self.home_url)
|
||||
return {'list': a, 'parse': 0, 'jx': 0}
|
||||
|
||||
def categoryContent(self, cid, page, filter, ext):
|
||||
url = self.home_url + f'/vod/show/id/{cid}/page/{page}'
|
||||
data = self.get_data(url)
|
||||
return {'list': data, 'parse': 0, 'jx': 0}
|
||||
|
||||
|
||||
def detailContent(self, did):
|
||||
ids = did[0]
|
||||
data = self.get_detail_data(ids)
|
||||
return {"list": data, 'parse': 0, 'jx': 0}
|
||||
|
||||
def searchContent(self, key, quick, page='1'):
|
||||
if int(page) > 1:
|
||||
return {'list': [], 'parse': 0, 'jx': 0}
|
||||
url = self.home_url + f'/vod/search/{key}'
|
||||
data = self.get_data(url)
|
||||
return {'list': data, 'parse': 0, 'jx': 0}
|
||||
|
||||
def playerContent(self, flag, pid, vipFlags):
|
||||
url = self.get_play_data(pid)
|
||||
return {"url": url, "header": self.headers, "parse": 1, "jx": 0}
|
||||
|
||||
def localProxy(self, params):
|
||||
pass
|
||||
|
||||
def destroy(self):
|
||||
return '正在Destroy'
|
||||
|
||||
|
||||
def get_data(self, url):
|
||||
data = []
|
||||
try:
|
||||
res = requests.get(url, headers=self.headers)
|
||||
if res.status_code != 200:
|
||||
return data
|
||||
vod_id_s = re.findall(r'\\"vodId\\":(.*?),', res.text)
|
||||
vod_name_s = re.findall(r'\\"vodName\\":\\"(.*?)\\"', res.text)
|
||||
vod_pic_s = re.findall(r'\\"vodPic\\":\\"(.*?)\\"', res.text)
|
||||
vod_remarks_s = re.findall(r'\\"vodRemarks\\":\\"(.*?)\\"', res.text)
|
||||
|
||||
for i in range(len(vod_id_s)):
|
||||
data.append(
|
||||
{
|
||||
'vod_id': vod_id_s[i],
|
||||
'vod_name': vod_name_s[i],
|
||||
'vod_pic': vod_pic_s[i],
|
||||
'vod_remarks': vod_remarks_s[i],
|
||||
}
|
||||
)
|
||||
except requests.RequestException as e:
|
||||
print(e)
|
||||
return data
|
||||
|
||||
def get_detail_data(self, ids):
|
||||
url = self.home_url + f'/api/mw-movie/anonymous/video/detail?id={ids}'
|
||||
t = str(int(time.time() * 1000))
|
||||
headers = self.get_headers(t, f'id={ids}&key=cb808529bae6b6be45ecfab29a4889bc&t={t}')
|
||||
try:
|
||||
res = requests.get(url, headers=headers)
|
||||
if res.status_code != 200:
|
||||
return []
|
||||
i = res.json()['data']
|
||||
urls = []
|
||||
for ii in res.json()['data']['episodeList']:
|
||||
name = ii['name']
|
||||
url = ii['nid']
|
||||
urls.append(f'{name}${ids}-{url}')
|
||||
data = {
|
||||
'type_name': i['vodClass'],
|
||||
'vod_id': i['vodId'],
|
||||
'vod_name': i['vodName'],
|
||||
'vod_remarks': i['vodRemarks'],
|
||||
'vod_year': i['vodYear'],
|
||||
'vod_area': i['vodArea'],
|
||||
'vod_actor': i['vodActor'],
|
||||
'vod_director': i['vodDirector'],
|
||||
'vod_content': i['vodContent'],
|
||||
'vod_play_from': '🌈七星专享',
|
||||
'vod_play_url': '#'.join(urls),
|
||||
|
||||
}
|
||||
return [data]
|
||||
|
||||
except requests.RequestException as e:
|
||||
print(e)
|
||||
return []
|
||||
|
||||
def get_play_data(self, play):
|
||||
info = play.split('-')
|
||||
_id = info[0]
|
||||
_pid = info[1]
|
||||
url = self.home_url + f'/api/mw-movie/anonymous/v2/video/episode/url?id={_id}&nid={_pid}'
|
||||
t = str(int(time.time() * 1000))
|
||||
headers = self.get_headers(t, f'id={_id}&nid={_pid}&key=cb808529bae6b6be45ecfab29a4889bc&t={t}')
|
||||
try:
|
||||
res = requests.get(url, headers=headers)
|
||||
if res.status_code != 200:
|
||||
return self.error_url
|
||||
return res.json()['data']['list'][0]['url']
|
||||
except requests.RequestException as e:
|
||||
print(e)
|
||||
return self.error_url
|
||||
|
||||
@staticmethod
|
||||
def get_headers(t, e):
|
||||
sign = hashlib.sha1(hashlib.md5(e.encode()).hexdigest().encode()).hexdigest()
|
||||
headers = {
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
|
||||
'Accept': 'application/json, text/plain, */*',
|
||||
'sign': sign,
|
||||
'sec-ch-ua': '"Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
|
||||
't': t,
|
||||
'referer': 'https://www.hkybqufgh.com/',
|
||||
}
|
||||
return headers
|
||||
|
||||
if __name__ == '__main__':
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
||||
+392
@@ -0,0 +1,392 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import re, urllib.parse
|
||||
import json
|
||||
from bs4 import BeautifulSoup
|
||||
import requests
|
||||
from base.spider import Spider as BaseSpider
|
||||
|
||||
|
||||
class Spider(BaseSpider):
|
||||
def init(self, extend=""):
|
||||
self.host = "https://maihaolian.com"
|
||||
self.headers = {
|
||||
"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
||||
"Accept-Language": "zh-CN,zh;q=0.9",
|
||||
}
|
||||
|
||||
def getName(self):
|
||||
return '枫叶影院'
|
||||
|
||||
def homeContent(self, filter):
|
||||
return {"class": [
|
||||
{'type_id': "/label/qq", 'type_name': "腾讯VIP精选"},
|
||||
{'type_id': "/label/bli", 'type_name': "B站VIP精选"},
|
||||
{'type_id': "/label/youku", 'type_name': "优酷VIP精选"},
|
||||
{"type_id": "5", "type_name": "红果短剧"},
|
||||
{"type_id": "2", "type_name": "电视剧"},
|
||||
{"type_id": "1", "type_name": "电影"},
|
||||
{"type_id": "4", "type_name": "动漫"},
|
||||
{"type_id": "3", "type_name": "综艺"},
|
||||
], "filters": self._build_filters()}
|
||||
|
||||
def _build_filters(self):
|
||||
area = [{"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": "其它"}]
|
||||
year = [{"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"}]
|
||||
lang = [{"n": "全部", "v": ""}, {"n": "国语", "v": "国语"}, {"n": "英语", "v": "英语"},
|
||||
{"n": "粤语", "v": "粤语"}, {"n": "闽南语", "v": "闽南语"}, {"n": "韩语", "v": "韩语"},
|
||||
{"n": "日语", "v": "日语"}, {"n": "法语", "v": "法语"}, {"n": "德语", "v": "德语"},
|
||||
{"n": "其它", "v": "其它"}]
|
||||
sort = [{"n": "时间", "v": "time"}, {"n": "人气", "v": "hits"}, {"n": "评分", "v": "score"}]
|
||||
letter = [{"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"}]
|
||||
return {
|
||||
"2": [
|
||||
{"key": "class", "name": "类型",
|
||||
"value": [{"n": "全部", "v": "2"}, {"n": "国产剧", "v": "13"}, {"n": "日韩剧", "v": "15"},
|
||||
{"n": "海外剧", "v": "16"}]},
|
||||
{"key": "area", "name": "地区", "value": area},
|
||||
{"key": "genre", "name": "剧情", "value": [{"n": v[0], "v": v[1]} for v in
|
||||
[("全部", ""), ("古装", "古装"), ("战争", "战争"),
|
||||
("青春偶像", "青春偶像"), ("喜剧", "喜剧"),
|
||||
("家庭", "家庭"), ("犯罪", "犯罪"), ("动作", "动作"),
|
||||
("奇幻", "奇幻"), ("剧情", "剧情"), ("历史", "历史"),
|
||||
("经典", "经典"), ("乡村", "乡村"), ("情景", "情景"),
|
||||
("商战", "商战"), ("网剧", "网剧"), ("其他", "其他")]]},
|
||||
{"key": "year", "name": "年份", "value": year},
|
||||
{"key": "lang", "name": "语言", "value": lang},
|
||||
{"key": "letter", "name": "字母", "value": letter},
|
||||
{"key": "sort", "name": "排序", "value": sort},
|
||||
],
|
||||
"1": [
|
||||
{"key": "class", "name": "类型",
|
||||
"value": [{"n": "全部", "v": "1"}, {"n": "动作片", "v": "6"}, {"n": "喜剧片", "v": "7"},
|
||||
{"n": "恐怖片", "v": "8"}, {"n": "科幻片", "v": "9"}, {"n": "爱情片", "v": "10"},
|
||||
{"n": "剧情片", "v": "11"}, {"n": "战争片", "v": "12"}, {"n": "纪录片", "v": "20"}]},
|
||||
{"key": "area", "name": "地区", "value": area},
|
||||
{"key": "genre", "name": "剧情", "value": [{"n": v[0], "v": v[1]} for v in
|
||||
[("全部", ""), ("喜剧", "喜剧"), ("爱情", "爱情"),
|
||||
("恐怖", "恐怖"), ("动作", "动作"), ("科幻", "科幻"),
|
||||
("剧情", "剧情"), ("战争", "战争"), ("警匪", "警匪"),
|
||||
("犯罪", "犯罪"), ("动画", "动画"), ("奇幻", "奇幻"),
|
||||
("武侠", "武侠"), ("冒险", "冒险"), ("枪战", "枪战"),
|
||||
("悬疑", "悬疑"), ("惊悚", "惊悚"), ("经典", "经典"),
|
||||
("青春", "青春"), ("文艺", "文艺"), ("微电影", "微电影"),
|
||||
("古装", "古装"), ("历史", "历史"), ("运动", "运动"),
|
||||
("农村", "农村"), ("儿童", "儿童"),
|
||||
("网络电影", "网络电影")]]},
|
||||
{"key": "year", "name": "年份", "value": year},
|
||||
{"key": "lang", "name": "语言", "value": lang},
|
||||
{"key": "letter", "name": "字母", "value": letter},
|
||||
{"key": "sort", "name": "排序", "value": sort},
|
||||
],
|
||||
"4": [
|
||||
{"key": "class", "name": "类型",
|
||||
"value": [{"n": "全部", "v": "4"}, {"n": "国产动漫", "v": "25"}, {"n": "日韩动漫", "v": "26"}]},
|
||||
{"key": "genre", "name": "剧情", "value": [{"n": v[0], "v": v[1]} for v in
|
||||
[("全部", ""), ("情感", "情感"), ("科幻", "科幻"),
|
||||
("热血", "热血"), ("推理", "推理"), ("搞笑", "搞笑"),
|
||||
("冒险", "冒险"), ("奇幻", "奇幻"), ("战斗", "战斗"),
|
||||
("校园", "校园"), ("萝莉", "萝莉"), ("治愈", "治愈"),
|
||||
("原创", "原创"), ("亲子", "亲子"), ("益智", "益智"),
|
||||
("励志", "励志"), ("其他", "其他")]]},
|
||||
{"key": "area", "name": "地区",
|
||||
"value": [{"n": "全部", "v": ""}, {"n": "大陆", "v": "大陆"}, {"n": "香港", "v": "香港"},
|
||||
{"n": "台湾", "v": "台湾"}, {"n": "美国", "v": "美国"}, {"n": "韩国", "v": "韩国"},
|
||||
{"n": "日本", "v": "日本"}, {"n": "法国", "v": "法国"}, {"n": "英国", "v": "英国"},
|
||||
{"n": "其它", "v": "其它"}]},
|
||||
{"key": "year", "name": "年份", "value": year},
|
||||
{"key": "lang", "name": "语言", "value": lang},
|
||||
{"key": "letter", "name": "字母", "value": letter},
|
||||
{"key": "sort", "name": "排序", "value": sort},
|
||||
],
|
||||
"3": [
|
||||
{"key": "class", "name": "类型",
|
||||
"value": [{"n": "全部", "v": "3"}, {"n": "大陆综艺", "v": "21"}, {"n": "日韩综艺", "v": "22"}]},
|
||||
{"key": "genre", "name": "剧情", "value": [{"n": v[0], "v": v[1]} for v in
|
||||
[("全部", ""), ("选秀", "选秀"), ("情感", "情感"),
|
||||
("访谈", "访谈"), ("播报", "播报"), ("音乐", "音乐"),
|
||||
("美食", "美食"), ("旅游", "旅游"), ("搞笑", "搞笑"),
|
||||
("游戏", "游戏"), ("亲子", "亲子"), ("其它", "其它")]]},
|
||||
{"key": "area", "name": "地区",
|
||||
"value": [{"n": "全部", "v": ""}, {"n": "大陆", "v": "大陆"}, {"n": "香港", "v": "香港"},
|
||||
{"n": "台湾", "v": "台湾"}, {"n": "美国", "v": "美国"}, {"n": "韩国", "v": "韩国"},
|
||||
{"n": "日本", "v": "日本"}, {"n": "英国", "v": "英国"}, {"n": "其它", "v": "其它"}]},
|
||||
{"key": "year", "name": "年份", "value": year},
|
||||
{"key": "lang", "name": "语言", "value": lang},
|
||||
{"key": "letter", "name": "字母", "value": letter},
|
||||
{"key": "sort", "name": "排序", "value": sort},
|
||||
],
|
||||
}
|
||||
|
||||
def homeVideoContent(self):
|
||||
html = self._fetch('/')
|
||||
return {"list": self._parse_video_list(html)}
|
||||
|
||||
def categoryContent(self, tid, pg, filter, extend):
|
||||
# 构建筛选参数:参照歪比巴卜,直接取extend里的值,fallback到filter
|
||||
if tid.startswith('/label'):
|
||||
url = f'{tid}/page/{pg}.html'
|
||||
html = self._fetch(url)
|
||||
items = self._parse_video_list(html)
|
||||
page = int(pg)
|
||||
page_count = page if len(items) < 24 else page + 2
|
||||
return {"list": items, "page": page, "pagecount": page_count, "limit": 24, "total": page_count * 24}
|
||||
|
||||
args = {}
|
||||
if extend and isinstance(extend, dict):
|
||||
for k, v in extend.items():
|
||||
if v:
|
||||
args[k] = str(v)
|
||||
if isinstance(filter, dict):
|
||||
for k, v in filter.items():
|
||||
if v and k not in args:
|
||||
args[k] = str(v)
|
||||
route_tid = args.get('class', args.get('tid', str(tid)))
|
||||
area = args.get('area', '')
|
||||
genre = args.get('genre', '')
|
||||
year = args.get('year', '')
|
||||
lang = args.get('lang', '')
|
||||
letter = args.get('letter', '')
|
||||
sort = args.get('sort', '')
|
||||
# 无筛选走正常分页
|
||||
if not area and not genre and not year and not lang and not letter and not sort:
|
||||
url = f'/cupfox-list/{route_tid}--------{pg}---.html'
|
||||
html = self._fetch(url)
|
||||
items = self._parse_video_list(html)
|
||||
page = int(pg)
|
||||
soup = BeautifulSoup(html, 'html.parser')
|
||||
pagecount = page
|
||||
for a in soup.select('a.page-link'):
|
||||
if a.text == '尾页':
|
||||
m = re.search(r'---(\d+)---', a.get('href', ''))
|
||||
if m:
|
||||
pagecount = int(m.group(1))
|
||||
break
|
||||
if not items:
|
||||
pagecount = 0
|
||||
return {"list": items, "page": page, "pagecount": pagecount, "limit": 36, "total": 9999}
|
||||
# 有筛选:{tid}-{area}-{sort}-{genre}-{lang}-{letter}------{year}.html
|
||||
segs = [route_tid, area, sort, genre, lang, letter, '', '', year]
|
||||
url = '/cupfox-list/' + '-'.join(segs) + '.html'
|
||||
html = self._fetch(url)
|
||||
items = self._parse_video_list(html)
|
||||
return {"list": items, "page": 1, "pagecount": 1, "limit": 36, "total": 9999}
|
||||
|
||||
def detailContent(self, ids):
|
||||
result = {"list": []}
|
||||
vid = ids[0].split(',')[0].strip()
|
||||
try:
|
||||
html = self._fetch(f'/detail/{vid}.html')
|
||||
if not html: return result
|
||||
soup = BeautifulSoup(html, 'html.parser')
|
||||
vod_name = soup.select_one('h3.slide-info-title')
|
||||
vod_name = vod_name.text.strip() if vod_name else ''
|
||||
vod_pic = soup.select_one('img.lazy')
|
||||
vod_pic = self._fix_pic(vod_pic.get('data-src', '')) if vod_pic else ''
|
||||
vod_director = ''
|
||||
vod_actor = ''
|
||||
for el in soup.select('.slide-info'):
|
||||
text = el.get_text(' ').strip()
|
||||
if text.startswith('导演:'):
|
||||
vod_director = text.replace('导演:', '').strip()
|
||||
elif text.startswith('演员:'):
|
||||
vod_actor = text.replace('演员:', '').strip()
|
||||
vod_content = soup.select_one('#height_limit')
|
||||
vod_content = vod_content.get_text(' ', strip=True) if vod_content else ''
|
||||
play_from, play_url = [], []
|
||||
for tab in soup.select('.anthology-tab a.swiper-slide'):
|
||||
src_name = re.sub(r'<[^>]+>', '', str(tab)).strip() or tab.get_text(' ', strip=True).strip()
|
||||
if src_name:
|
||||
play_from.append(src_name)
|
||||
tab_blocks = soup.select('.anthology-list-box')
|
||||
for i, block in enumerate(tab_blocks):
|
||||
ep_list = []
|
||||
for a in block.select('li a'):
|
||||
href = a.get('href', '')
|
||||
m = re.search(r'/play/(.*?)\.html', href)
|
||||
if m:
|
||||
ep_list.append(f'{a.text.strip()}${vid}-{m.group(1)}')
|
||||
ep_list.reverse()
|
||||
if ep_list and i < len(play_from):
|
||||
play_url.append('#'.join(ep_list))
|
||||
valid_from = [pf for i, pf in enumerate(play_from) if i < len(play_url)]
|
||||
result["list"].append({
|
||||
"vod_id": vid, "vod_name": vod_name, "vod_pic": vod_pic,
|
||||
"vod_director": vod_director, "vod_actor": vod_actor,
|
||||
"vod_content": vod_content,
|
||||
"vod_play_from": "$$$".join(valid_from),
|
||||
"vod_play_url": "$$$".join(play_url),
|
||||
})
|
||||
except:
|
||||
pass
|
||||
return result
|
||||
|
||||
def searchContent(self, key, quick, pg="1"):
|
||||
try:
|
||||
decoded = urllib.parse.unquote(key)
|
||||
except:
|
||||
decoded = key
|
||||
html = self._fetch(f'/cupfox-search/{urllib.parse.quote(decoded)}----------{pg}---.html')
|
||||
items = self._parse_search_list(html)
|
||||
return {"list": items, "page": int(pg), "pagecount": 1, "limit": 36, "total": len(items)}
|
||||
|
||||
def playerContent(self, flag, id, vipFlags):
|
||||
url = ''
|
||||
try:
|
||||
url = id if id.startswith('http') else f'{self.host}/play/{id}.html'
|
||||
html = self._fetch(url)
|
||||
if html:
|
||||
m = re.search(r'player_aaaa=(.*?)</script>', html, re.S)
|
||||
if m:
|
||||
|
||||
try:
|
||||
pd = json.loads(m.group(1))
|
||||
except Exception as e:
|
||||
print(e)
|
||||
pd = {}
|
||||
# print('pd:', pd)
|
||||
play_url = pd.get('url')
|
||||
play_id = pd.get('from')
|
||||
|
||||
api_map = {
|
||||
'YYNB': 'https://zzrs.mfdyvip.com/player/mplayer.php',
|
||||
'JD4K': 'https://fgsrg.hzqingshan.com/player/mplayer.php',
|
||||
}
|
||||
if not play_url:
|
||||
return {"parse": 0, "url": 'https://php.doube.eu.org/error.m3u8',
|
||||
"header": {'User-Agent': 'Mozilla/5.0'}}
|
||||
if play_url.startswith('http') and (play_url.endswith('.m3u8') or play_url.endswith('.mp4')):
|
||||
return {"parse": 0, "url": play_url, "header": {'User-Agent': 'Mozilla/5.0'}}
|
||||
|
||||
else:
|
||||
headers = {
|
||||
'User-Agent': "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36",
|
||||
'Accept': "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
|
||||
'accept-language': "zh-CN,zh;q=0.9",
|
||||
'cache-control': "no-cache",
|
||||
'pragma': "no-cache",
|
||||
'priority': "u=0, i",
|
||||
'referer': "https://www.ht10010.com/",
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
}
|
||||
response = requests.get(f"https://fgsrg.hzqingshan.com/player/?url={play_url}", headers=headers)
|
||||
token = re.search(r'data-te="(.*?)"', response.text)
|
||||
if token:
|
||||
token = token.group(1)
|
||||
payload = {
|
||||
'url': play_url,
|
||||
'token': token
|
||||
}
|
||||
# print('payload', payload)
|
||||
try:
|
||||
response = self.post(api_map[play_id], data=payload, headers=headers)
|
||||
|
||||
response.raise_for_status()
|
||||
result = response.json()
|
||||
# print('result:', result)
|
||||
if result['code'] == 200 and 'url' in result:
|
||||
play_url = result['url']
|
||||
return {"parse": 0, "url": play_url, "header": {
|
||||
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1'}}
|
||||
except Exception as e:
|
||||
print(e)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return {"parse": 1, "url": url}
|
||||
|
||||
def localProxy(self, param=''):
|
||||
return {}
|
||||
|
||||
def isVideoFormat(self, url):
|
||||
return False
|
||||
|
||||
def manualVideoCheck(self):
|
||||
return False
|
||||
|
||||
def _fetch(self, url):
|
||||
try:
|
||||
if not url.startswith('http'):
|
||||
url = self.host + url
|
||||
rsp = self.fetch(url, headers=self.headers)
|
||||
return rsp.text if rsp else ''
|
||||
except:
|
||||
return ''
|
||||
|
||||
def _fix_pic(self, u):
|
||||
if not u: return ''
|
||||
if u.startswith('//'): return 'https:' + u
|
||||
return u.replace('&', '&')
|
||||
|
||||
def _parse_video_list(self, html):
|
||||
videos, seen = [], set()
|
||||
soup = BeautifulSoup(html, 'html.parser')
|
||||
cards = soup.select('a.public-list-exp')
|
||||
for a in cards:
|
||||
href = a.get('href', '')
|
||||
m = re.search(r'/detail/(\d+)\.html', href)
|
||||
if not m: continue
|
||||
vod_id = m.group(1)
|
||||
if vod_id in seen: continue
|
||||
seen.add(vod_id)
|
||||
span = ','.join([span.text for span in a.select('span.public-prt')])
|
||||
# print('span', span)
|
||||
vod_name = a.get('title', '') or (a.select_one('img') and a.select_one('img').get('alt', '')) or ''
|
||||
pic_el = a.select_one('img')
|
||||
vod_pic = self._fix_pic(pic_el.get('data-src', '')) if pic_el else ''
|
||||
remark_el = a.select_one('.ft2') or a.select_one('.public-list-prb')
|
||||
vod_remarks = remark_el.text.strip() if remark_el else ''
|
||||
videos.append(
|
||||
{"vod_id": vod_id, "vod_name": vod_name.strip(), "vod_pic": vod_pic, "vod_remarks": vod_remarks, "vod_year": span})
|
||||
return videos
|
||||
|
||||
def _parse_search_list(self, html):
|
||||
videos, seen = [], set()
|
||||
soup = BeautifulSoup(html, 'html.parser')
|
||||
cards = soup.select('a.public-list-exp')
|
||||
for a in cards:
|
||||
href = a.get('href', '')
|
||||
m = re.search(r'/detail/(\d+)\.html', href)
|
||||
if not m: continue
|
||||
vod_id = m.group(1)
|
||||
if vod_id in seen: continue
|
||||
seen.add(vod_id)
|
||||
pic_el = a.select_one('img')
|
||||
vod_pic = self._fix_pic(pic_el.get('data-src', '')) if pic_el else ''
|
||||
title_el = soup.select_one(f'a.thumb-txt[href="/detail/{vod_id}.html"]')
|
||||
if title_el:
|
||||
vod_name = title_el.text.strip()
|
||||
else:
|
||||
vod_name = a.select_one('img') and a.select_one('img').get('alt', '') or ''
|
||||
remark_el = a.select_one('.public-list-prb') or a.select_one('.ft2')
|
||||
vod_remarks = remark_el.text.strip() if remark_el else ''
|
||||
videos.append(
|
||||
{"vod_id": vod_id, "vod_name": vod_name.strip(), "vod_pic": vod_pic, "vod_remarks": vod_remarks})
|
||||
return videos
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sp = Spider()
|
||||
sp.init()
|
||||
# 20067-5-189
|
||||
print(sp.categoryContent('/label/qq','1',True, {}))
|
||||
# print(sp.playerContent('', '20067-6-189', []))
|
||||
# print(sp.playerContent('', '20067-5-189', []))
|
||||
pass
|
||||
@@ -0,0 +1,270 @@
|
||||
"""
|
||||
@header({
|
||||
searchable: 1,
|
||||
filterable: 1,
|
||||
quickSearch: 1,
|
||||
title: '独播库',
|
||||
lang: 'hipy'
|
||||
})
|
||||
"""
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
# 本资源来源于互联网公开渠道,仅可用于个人学习爬虫技术。
|
||||
# 严禁将其用于任何商业用途,下载后请于 24 小时内删除,搜索结果均来自源站,本人不承担任何责任。
|
||||
|
||||
from base.spider import Spider
|
||||
import sys,time,json,base64,random,urllib3
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
sys.path.append('..')
|
||||
|
||||
class Spider(Spider):
|
||||
headers,host = {
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
|
||||
'Connection': 'Keep-Alive',
|
||||
'Accept-Encoding': 'gzip',
|
||||
'Referer': 'https://www.dbku.tv/'
|
||||
}, 'https://api.dbokutv.com'
|
||||
|
||||
def init(self, extend=""):
|
||||
pass
|
||||
|
||||
filters = {
|
||||
1: [
|
||||
{'key': 'area', 'name': '地区', 'value': [
|
||||
{'n': '全部', 'v': ''}, {'n': '大陆', 'v': '大陆'}, {'n': '香港', 'v': '香港'},
|
||||
{'n': '台湾', 'v': '台湾'}, {'n': '韩国', 'v': '韩国'}, {'n': '日本', 'v': '日本'},
|
||||
{'n': '欧美', 'v': '欧美'}, {'n': '泰国', 'v': '泰国'}]},
|
||||
{'key': 'order', 'name': '排序', 'value': [
|
||||
{'n': '时间', 'v': ''}, {'n': '人气', 'v': '人气'}, {'n': '评分', 'v': '评分'}]},
|
||||
{'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': '短片'}]},
|
||||
{'key': 'lang', 'name': '语言', 'value': [
|
||||
{'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': '更早', 'v': '更早'}]},
|
||||
],
|
||||
2: [
|
||||
{'key': 'area', 'name': '地区', 'value': [
|
||||
{'n': '全部', 'v': ''}, {'n': '大陆', 'v': '大陆'}, {'n': '香港', 'v': '香港'},
|
||||
{'n': '台湾', 'v': '台湾'}, {'n': '韩国', 'v': '韩国'}, {'n': '日本', 'v': '日本'},
|
||||
{'n': '新加坡', 'v': '新加坡'}, {'n': '泰国', 'v': '泰国'}]},
|
||||
{'key': 'order', 'name': '排序', 'value': [
|
||||
{'n': '时间', 'v': ''}, {'n': '人气', 'v': '人气'}, {'n': '评分', 'v': '评分'}]},
|
||||
{'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': '同性'}]},
|
||||
{'key': 'lang', 'name': '语言', 'value': [
|
||||
{'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': '更早', 'v': '更早'}]},
|
||||
],
|
||||
3: [
|
||||
{'key': 'area', 'name': '地区', 'value': [
|
||||
{'n': '全部', 'v': ''}, {'n': '大陆', 'v': '大陆'}, {'n': '香港', 'v': '香港'},
|
||||
{'n': '台湾', 'v': '台湾'}, {'n': '韩国', 'v': '韩国'}, {'n': '日本', 'v': '日本'},
|
||||
{'n': '欧美', 'v': '欧美'}]},
|
||||
{'key': 'order', 'name': '排序', 'value': [
|
||||
{'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': '更早', 'v': '更早'}]},
|
||||
],
|
||||
4: [
|
||||
{'key': 'area', 'name': '地区', 'value': [
|
||||
{'n': '全部', 'v': ''}, {'n': '大陆', 'v': '大陆'}, {'n': '日本', 'v': '日本'},
|
||||
{'n': '欧美', 'v': '欧美'}, {'n': '韩国', 'v': '韩国'}]},
|
||||
{'key': 'order', 'name': '排序', 'value': [
|
||||
{'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': '更早', 'v': '更早'}]},
|
||||
],
|
||||
}
|
||||
|
||||
def homeContent(self, filter):
|
||||
if not self.host: return None
|
||||
result = {'class': [
|
||||
{'type_id': 1, 'type_name': '电影'},
|
||||
{'type_id': 2, 'type_name': '连续剧'},
|
||||
{'type_id': 3, 'type_name': '综艺'},
|
||||
{'type_id': 4, 'type_name': '动漫'}]}
|
||||
if filter:
|
||||
result['filters'] = self.filters
|
||||
return result
|
||||
|
||||
def homeVideoContent(self):
|
||||
response = self.fetch(self.sign(f'{self.host}/home'), headers=self.headers, verify=False).json()
|
||||
videos = []
|
||||
for i in response:
|
||||
for j in i.get('VodList',[]):
|
||||
videos.append({
|
||||
'vod_id': self.decode(j.get('DId', j.get('DuId'))),
|
||||
'vod_name': j['Name'],
|
||||
'vod_pic': self.decode(j['TnId']),
|
||||
'vod_remarks': j['Tag']
|
||||
})
|
||||
return {'list': videos}
|
||||
|
||||
def categoryContent(self, tid, pg, filter, extend):
|
||||
area = extend.get('area', '')
|
||||
order = extend.get('order', '')
|
||||
cls = extend.get('class', '')
|
||||
lang = extend.get('lang', '')
|
||||
year = extend.get('year', '')
|
||||
pg_str = '' if str(pg) == '1' else str(pg)
|
||||
# URL格式: /vodshow/{tid}-{area}-{order}-{class}-{lang}-{letter}-{?}-{?}-{pg}-{?}-{year}
|
||||
path = f"{self.host}/vodshow/{tid}-{area}-{order}-{cls}-{lang}----{pg_str}---{year}"
|
||||
response = self.fetch(self.sign(path), headers=self.headers, verify=False).json()
|
||||
videos,pagecount = [], 0
|
||||
for i in response['VodList']:
|
||||
videos.append({
|
||||
'vod_id': self.decode(i.get('DId',i.get('DuId'))),
|
||||
'vod_name': i['Name'],
|
||||
'vod_pic': self.decode(i['TnId']),
|
||||
'vod_remarks': i['Tag']
|
||||
})
|
||||
try:
|
||||
for j in response['PaginationList']:
|
||||
if j['Type'] == 'StartEnd':
|
||||
pgcount = int(self.decode(j.get('PId',j.get('PuId'))).split('-')[8])
|
||||
if pgcount: pagecount = pgcount
|
||||
except Exception:
|
||||
pass
|
||||
if pagecount == 0:
|
||||
try:
|
||||
for j in response['PaginationList']:
|
||||
if j['Type'] == 'ShortPage':
|
||||
pgcount = int(j['Name'].split('/')[1])
|
||||
if pgcount: pagecount = pgcount
|
||||
except Exception:
|
||||
pagecount = 1
|
||||
return {'list': videos,'pagecount': pagecount, 'page': pg}
|
||||
|
||||
def searchContent(self, key, quick, pg='1'):
|
||||
response = self.fetch(f"{self.sign(f'{self.host}/vodsearch')}&wd={key}", headers=self.headers, verify=False).json()
|
||||
videos = []
|
||||
for i in response:
|
||||
videos.append({
|
||||
'vod_id': self.decode(i.get('DId', i.get('DuId'))),
|
||||
'vod_name': i['Name'],
|
||||
'vod_pic': self.decode(i['TnId']),
|
||||
'vod_remarks': i['Tag'],
|
||||
'vod_actor': i['Actor'],
|
||||
'vod_score': i['Rating']
|
||||
})
|
||||
return {'list': videos, 'page': pg}
|
||||
|
||||
def detailContent(self, ids):
|
||||
data = self.fetch(self.sign(f"{self.host}{ids[0]}"), headers=self.headers, verify=False).json()
|
||||
play_urls = [f"{i['EpisodeName']}${self.decode(i['VId'])}" for i in data['Playlist']]
|
||||
video = {
|
||||
'vod_id': ids[0],
|
||||
'vod_name': data['Name'],
|
||||
'vod_pic': self.decode(data['TnId']),
|
||||
'vod_remarks': f"评分:{data['Rating']}",
|
||||
'vod_year': data['ReleaseYear'],
|
||||
'vod_area': data['Region'],
|
||||
'vod_actor': ','.join(data['Actor']),
|
||||
'vod_director': data['Director'],
|
||||
'vod_content': data['Description'],
|
||||
'vod_play_from': '独播库',
|
||||
'vod_play_url': '#'.join(play_urls),
|
||||
'type_name': f"{data['Genre']},{data['Scenario']},{data['Language']}"
|
||||
}
|
||||
return {'list': [video]}
|
||||
|
||||
def playerContent(self, flag, video_id, vip_flags):
|
||||
res = self.fetch(self.sign(f"{self.host}{video_id}"), headers=self.headers, verify=False).json()
|
||||
headers = {
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
|
||||
'Accept-Encoding': 'gzip, deflate',
|
||||
'origin': 'https://w.duboku.io',
|
||||
'referer': 'https://w.duboku.io/',
|
||||
'priority': 'u=1, i'
|
||||
}
|
||||
return { 'jx': 0, 'parse': 0, 'url': self.decode(res['HId']), 'header': headers}
|
||||
|
||||
def decode(self, data):
|
||||
if not data or not isinstance(data, str): return ''
|
||||
stripped_str = data.strip('\'"')
|
||||
if not stripped_str: return ''
|
||||
segment_length = 10
|
||||
try:
|
||||
reversed_segments = (stripped_str[i:i + segment_length][::-1] for i in range(0, len(stripped_str), segment_length))
|
||||
processed_base64 = ''.join(reversed_segments).replace('.', '=')
|
||||
except Exception:
|
||||
return ''
|
||||
try:
|
||||
padding_needed = 4 - len(processed_base64) % 4
|
||||
if padding_needed != 4:
|
||||
processed_base64 += '=' * padding_needed
|
||||
decoded_bytes = base64.b64decode(processed_base64, validate=True)
|
||||
return decoded_bytes.decode('utf-8')
|
||||
except Exception:
|
||||
return ''
|
||||
|
||||
def sign(self,raw):
|
||||
random.seed(int(time.time()))
|
||||
random_number = random.randint(0, 800000000)
|
||||
value_a = random_number + 100000000
|
||||
value_b = 900000000 - random_number
|
||||
current_unix_timestamp = int(time.time())
|
||||
unix_timestamp_str = str(current_unix_timestamp)
|
||||
interleaved_str = self.interleave_strings(f'{value_a}{value_b}', unix_timestamp_str)
|
||||
ssid_base64_encoded = base64.b64encode(interleaved_str.encode()).decode().replace('=', '.')
|
||||
random_sign = self.random_string(60)
|
||||
random_token = self.random_string(38)
|
||||
return f"{raw}?sign={random_sign}&token={random_token}&ssid={ssid_base64_encoded}"
|
||||
|
||||
def interleave_strings(self,first_str, second_str):
|
||||
interleaved_chars = []
|
||||
min_length = min(len(first_str), len(second_str))
|
||||
for i in range(min_length):
|
||||
interleaved_chars.append(first_str[i])
|
||||
interleaved_chars.append(second_str[i])
|
||||
interleaved_chars.append(first_str[min_length:])
|
||||
interleaved_chars.append(second_str[min_length:])
|
||||
return ''.join(interleaved_chars)
|
||||
|
||||
|
||||
def random_string(self, length):
|
||||
character = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
|
||||
random.seed(int(time.time()) + len(character))
|
||||
return ''.join(random.choice(character) for _ in range(length))
|
||||
|
||||
def getName(self):
|
||||
pass
|
||||
|
||||
def isVideoFormat(self, url):
|
||||
pass
|
||||
|
||||
def manualVideoCheck(self):
|
||||
pass
|
||||
|
||||
def destroy(self):
|
||||
pass
|
||||
|
||||
def localProxy(self, param):
|
||||
pass
|
||||
@@ -0,0 +1,510 @@
|
||||
# coding = utf-8
|
||||
#!/usr/bin/python
|
||||
import re
|
||||
import sys
|
||||
import json
|
||||
import time
|
||||
import base64
|
||||
import requests
|
||||
import hashlib
|
||||
import random
|
||||
import string
|
||||
import urllib.parse
|
||||
from Crypto.Cipher import AES
|
||||
from Crypto.Util.Padding import pad, unpad
|
||||
from Crypto.PublicKey import RSA
|
||||
from Crypto.Cipher import PKCS1_v1_5
|
||||
from base.spider import Spider
|
||||
|
||||
sys.path.append('..')
|
||||
|
||||
class Spider(Spider):
|
||||
def __init__(self):
|
||||
self.name = "瓜子"
|
||||
self.hosts = [
|
||||
'https://apinew.uozvr.com',
|
||||
'https://api.w32z7vtd.com',
|
||||
'https://api.6a7nnf7.com',
|
||||
'https://api.umygrx3.com',
|
||||
'https://api.rmedphk.com'
|
||||
]
|
||||
self.host_index = 0
|
||||
self.host = self.hosts[self.host_index]
|
||||
|
||||
# AES 固定密钥(与Java版一致)
|
||||
self.AES_KEY = 'OITxa5OqAYjhswxx'
|
||||
self.AES_IV = 'rCMNwZASNBKZ8mXV'
|
||||
|
||||
# RSA 公钥/私钥
|
||||
self.RSA_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDUM5+/y8sPsWkd1/RQS64X259EUwxFXFE5HlA65MqrxnPs0JqoSRojSDy5QhwvROlaD6TwRQHKMY2OAZ6SnQeUJsChTEFIR9qUkwrs3/MVUMxjsv6JS6Oe/juclyJGTgVmDhB55EafXsD0SQYVj/QXXsxR6ewR5E2kL52yAAD4yQIDAQAB"
|
||||
self.RSA_PRIVATE_KEY = """-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGAe6hKrWLi1zQmjTT1
|
||||
ozbE4QdFeJGNxubxld6GrFGximxfMsMB6BpJhpcTouAqywAFppiKetUBBbXwYsYU
|
||||
1wNr648XVmPmCMCy4rY8vdliFnbMUj086DU6Z+/oXBdWU3/b1G0DN3E9wULRSwcK
|
||||
ZT3wj/cCI1vsCm3gj2R5SqkA9Y0CAwEAAQKBgAJH+4CxV0/zBVcLiBCHvSANm0l7
|
||||
HetybTh/j2p0Y1sTXro4ALwAaCTUeqdBjWiLSo9lNwDHFyq8zX90+gNxa7c5EqcW
|
||||
V9FmlVXr8VhfBzcZo1nXeNdXFT7tQ2yah/odtdcx+vRMSGJd1t/5k5bDd9wAvYdI
|
||||
DblMAg+wiKKZ5KcdAkEA1cCakEN4NexkF5tHPRrR6XOY/XHfkqXxEhMqmNbB9U34
|
||||
saTJnLWIHC8IXys6Qmzz30TtzCjuOqKRRy+FMM4TdwJBAJQZFPjsGC+RqcG5UvVM
|
||||
iMPhnwe/bXEehShK86yJK/g/UiKrO87h3aEu5gcJqBygTq3BBBoH2md3pr/W+hUM
|
||||
WBsCQQChfhTIrdDinKi6lRxrdBnn0Ohjg2cwuqK5zzU9p/N+S9x7Ck8wUI53DKm8
|
||||
jUJE8WAG7WLj/oCOWEh+ic6NIwTdAkEAj0X8nhx6AXsgCYRql1klbqtVmL8+95KZ
|
||||
K7PnLWG/IfjQUy3pPGoSaZ7fdquG8bq8oyf5+dzjE/oTXcByS+6XRQJAP/5ciy1b
|
||||
L3NhUhsaOVy55MHXnPjdcTX0FaLi+ybXZIfIQ2P4rb19mVq1feMbCXhz+L1rG8oa
|
||||
t5lYKfpe8k83ZA==
|
||||
-----END RSA PRIVATE KEY-----"""
|
||||
|
||||
self.DEVICE_OLD_KEY = "aLFBMWpxBrIDAD1Si/KVvm41"
|
||||
|
||||
# 设备信息(随机生成)
|
||||
self.deviceId = str(864150060000000 + random.randint(0, 9999))
|
||||
self.deviceKey = ''.join(random.choices('0123456789ABCDEF', k=40)) # 20字节hex大写
|
||||
self.token = ""
|
||||
self.token_id = ""
|
||||
self.registered = False
|
||||
|
||||
self.header = {
|
||||
'User-Agent': 'Lavf/57.83.100',
|
||||
'code': 'GZ0369',
|
||||
'deviceId': self.deviceId,
|
||||
'lang': 'zh_cn',
|
||||
'Cache-Control': 'no-cache',
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Version': '2604028',
|
||||
'PackageName': 'com.ae06aebdbb.y286327f5a.ofe849883320260517',
|
||||
'Ver': '3.0.3.2',
|
||||
'api-ver': '3.0.3.2',
|
||||
'Referer': self.host
|
||||
}
|
||||
|
||||
self.cache = {}
|
||||
self.cache_timeout = 300
|
||||
|
||||
# 初始化token
|
||||
self.init_token()
|
||||
|
||||
def getName(self):
|
||||
return self.name
|
||||
|
||||
def init(self, extend=''):
|
||||
pass
|
||||
|
||||
# ---------- 设备注册与认证 ----------
|
||||
def init_token(self):
|
||||
"""初始化token:注册设备 -> 刷新"""
|
||||
print("===== 初始化设备认证 =====")
|
||||
try:
|
||||
if not self.registered:
|
||||
self.sign_up()
|
||||
# 刷新获取最终token
|
||||
self.refresh_token()
|
||||
except Exception as e:
|
||||
print(f"初始化token失败: {e}")
|
||||
# 兜底使用原有硬编码(几乎没用)
|
||||
self.token = '024212ef0975c5306a1434e113a46463.bc77313e11a248558a6ca244ca980944ec3421fa480c50e0229ad91f1cb15aea582603202cd71796885c9e5163e500f1b72f737059aff1ddb8beea47c5a331d6760540345b7f88b2302a0e6e09589f9dcf3ff9175d8c905f990203f5fc04748008ea7a366571cbf5b09509a873dcfba3cf1d5590385f5f7ef6e01d1850974aa220eb5178c89e61c24411af9b9a19435e.06fde789ece48d9b33c5dc857e04e9b5838f08264d928b87237d3476c4484b46'
|
||||
|
||||
def sign_up(self):
|
||||
"""注册设备"""
|
||||
print("注册新设备...")
|
||||
params = {
|
||||
"new_key": self.deviceKey,
|
||||
"old_key": self.DEVICE_OLD_KEY,
|
||||
"phone_type": 1,
|
||||
"code": ""
|
||||
}
|
||||
result = self._auth_request('/App/Authentication/Device/signUp', params)
|
||||
self._apply_auth(result)
|
||||
self.registered = True
|
||||
|
||||
def sign_in(self):
|
||||
"""登录设备"""
|
||||
print("设备登录...")
|
||||
params = {
|
||||
"new_key": self.deviceKey,
|
||||
"old_key": self.DEVICE_OLD_KEY
|
||||
}
|
||||
result = self._auth_request('/App/Authentication/Device/signIn', params)
|
||||
self._apply_auth(result)
|
||||
|
||||
def _apply_auth(self, result):
|
||||
"""从认证响应中提取token"""
|
||||
new_token = result.get('token', '')
|
||||
if not new_token:
|
||||
raise Exception("认证失败,无token返回: {}".format(result))
|
||||
self.token = new_token
|
||||
new_token_id = result.get('app_user_id', '')
|
||||
if new_token_id:
|
||||
self.token_id = new_token_id
|
||||
print(f"获取token成功, token前缀: {self.token[:30]}...")
|
||||
|
||||
def refresh_token(self):
|
||||
"""刷新token"""
|
||||
print("刷新token...")
|
||||
result = self._auth_request('/App/Authentication/Authenticator/refresh', {})
|
||||
self._apply_auth(result)
|
||||
|
||||
def _auth_request(self, path, params):
|
||||
"""认证类请求(不需要ensure_token)"""
|
||||
return self._send_encrypted_request(params, path, is_auth=True)
|
||||
|
||||
# ---------- 业务请求核心(修复加密与签名) ----------
|
||||
def ensure_token(self):
|
||||
"""确保token有效,如未就绪则重新获取"""
|
||||
if not self.token or not self.token_id:
|
||||
if self.registered:
|
||||
self.sign_in()
|
||||
else:
|
||||
self.sign_up()
|
||||
self.refresh_token()
|
||||
|
||||
def _send_encrypted_request(self, data, path, is_auth=False):
|
||||
"""
|
||||
发送加密请求,返回解密后的字典
|
||||
:param data: 业务参数字典
|
||||
:param path: 请求路径
|
||||
:param is_auth: 是否为认证类请求(signUp/signIn/refresh),此时不使用ensure_token
|
||||
"""
|
||||
try:
|
||||
if not is_auth:
|
||||
self.ensure_token()
|
||||
|
||||
# 1. 将参数转为JSON并AES加密
|
||||
json_params = json.dumps(data)
|
||||
encrypted = self.aes_encrypt(json_params, self.AES_KEY, self.AES_IV)
|
||||
request_key = encrypted.upper() # Java中是bytesToHex(encrypted).toUpperCase()
|
||||
|
||||
# 2. 生成keys (RSA加密 iv/key JSON)
|
||||
key_json = json.dumps({"iv": self.AES_IV, "key": self.AES_KEY})
|
||||
keys = self.rsa_encrypt(key_json, self.RSA_PUBLIC_KEY)
|
||||
|
||||
# 3. 生成签名
|
||||
t = str(int(time.time()))
|
||||
sign_str = f"token_id=,token={self.token},phone_type=1,request_key={request_key},app_id=1,time={t},keys={keys}*&zvdvdvddbfikkkumtmdwqppp?|4Y!s!2br"
|
||||
signature = self.get_md5(sign_str) # 已改为大写
|
||||
|
||||
# 4. 构建请求体
|
||||
body = {
|
||||
'token': self.token,
|
||||
'token_id': '',
|
||||
'phone_type': '1',
|
||||
'time': t,
|
||||
'phone_model': 'xiaomi-25031', # 与Java版保持一致
|
||||
'keys': keys,
|
||||
'request_key': request_key,
|
||||
'signature': signature,
|
||||
'app_id': '1',
|
||||
'ad_version': '1'
|
||||
}
|
||||
|
||||
# 5. 发送请求
|
||||
url = f"{self.host}{path}"
|
||||
response = self.post(url, headers=self.header, data=body, timeout=10)
|
||||
|
||||
if response.status_code != 200:
|
||||
raise Exception(f"HTTP {response.status_code}")
|
||||
|
||||
resp_json = response.json()
|
||||
# 检查业务code(若不为200可能token过期)
|
||||
if 'code' in resp_json and resp_json['code'] != 200:
|
||||
print(f"业务错误码: {resp_json['code']}, 信息: {resp_json}")
|
||||
# 如果不是认证请求,尝试重新获取token后重试一次(这里简单处理,外层get_data已有重试)
|
||||
raise Exception("业务错误")
|
||||
|
||||
data_section = resp_json.get('data')
|
||||
if not data_section:
|
||||
raise Exception("响应缺少data字段")
|
||||
|
||||
encrypted_response = data_section.get('response_key', '')
|
||||
encrypted_keys = data_section.get('keys', '')
|
||||
|
||||
# 6. 解密响应
|
||||
decrypted_keys_json = self.rsa_decrypt(encrypted_keys, self.RSA_PRIVATE_KEY)
|
||||
key_info = json.loads(decrypted_keys_json)
|
||||
resp_key = key_info['key']
|
||||
resp_iv = key_info['iv']
|
||||
decrypted_data = self.aes_decrypt(encrypted_response, resp_key, resp_iv)
|
||||
return json.loads(decrypted_data)
|
||||
|
||||
except Exception as e:
|
||||
print(f"请求失败 [{path}]: {e}")
|
||||
return None
|
||||
|
||||
def get_data(self, data, path, use_cache=True):
|
||||
"""带重试和域名轮询的数据获取(保持原框架)"""
|
||||
try:
|
||||
cache_key = f"{path}_{hash(str(data))}" if use_cache else None
|
||||
if use_cache and cache_key in self.cache:
|
||||
cached_data, timestamp = self.cache[cache_key]
|
||||
if time.time() - timestamp < self.cache_timeout:
|
||||
return cached_data
|
||||
|
||||
for attempt in range(3):
|
||||
tried = 0
|
||||
while tried < len(self.hosts):
|
||||
self.host = self.hosts[self.host_index]
|
||||
self.header['Referer'] = self.host
|
||||
result = self._send_encrypted_request(data, path)
|
||||
if result is not None:
|
||||
print(f"请求成功: {path}, 域名: {self.host}")
|
||||
if use_cache and cache_key:
|
||||
self.cache[cache_key] = (result, time.time())
|
||||
return result
|
||||
|
||||
# 切换到下一个域名
|
||||
self.host_index = (self.host_index + 1) % len(self.hosts)
|
||||
tried += 1
|
||||
|
||||
# 所有域名失败,尝试重新认证并重试
|
||||
if attempt < 2:
|
||||
print("所有域名失败,尝试重新认证...")
|
||||
try:
|
||||
self.ensure_token()
|
||||
except:
|
||||
pass
|
||||
self.host_index = 0
|
||||
else:
|
||||
break
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"get_data异常: {e}")
|
||||
return None
|
||||
|
||||
# ---------- 加解密工具 ----------
|
||||
def aes_encrypt(self, text, key, iv):
|
||||
try:
|
||||
key_bytes = key.encode('utf-8')
|
||||
iv_bytes = iv.encode('utf-8')
|
||||
cipher = AES.new(key_bytes, AES.MODE_CBC, iv_bytes)
|
||||
encrypted = cipher.encrypt(pad(text.encode('utf-8'), AES.block_size))
|
||||
return encrypted.hex().upper()
|
||||
except Exception as e:
|
||||
print(f"AES加密失败: {e}")
|
||||
return ""
|
||||
|
||||
def aes_decrypt(self, text, key, iv):
|
||||
try:
|
||||
key_bytes = key.encode('utf-8')
|
||||
iv_bytes = iv.encode('utf-8')
|
||||
cipher = AES.new(key_bytes, AES.MODE_CBC, iv_bytes)
|
||||
encrypted_bytes = bytes.fromhex(text)
|
||||
decrypted = unpad(cipher.decrypt(encrypted_bytes), AES.block_size)
|
||||
return decrypted.decode('utf-8')
|
||||
except Exception as e:
|
||||
print(f"AES解密失败: {e}")
|
||||
return ""
|
||||
|
||||
def rsa_encrypt(self, text, public_key_str):
|
||||
"""RSA公钥加密(PKCS1v1.5)"""
|
||||
try:
|
||||
key = RSA.import_key("-----BEGIN PUBLIC KEY-----\n" + public_key_str + "\n-----END PUBLIC KEY-----")
|
||||
cipher = PKCS1_v1_5.new(key)
|
||||
encrypted = cipher.encrypt(text.encode('utf-8'))
|
||||
return base64.b64encode(encrypted).decode('utf-8')
|
||||
except Exception as e:
|
||||
print(f"RSA加密失败: {e}")
|
||||
return ""
|
||||
|
||||
def rsa_decrypt(self, encrypted_data, private_key_str):
|
||||
"""RSA私钥解密"""
|
||||
try:
|
||||
encrypted_bytes = base64.b64decode(encrypted_data)
|
||||
rsa_key = RSA.import_key(private_key_str)
|
||||
cipher = PKCS1_v1_5.new(rsa_key)
|
||||
decrypted = cipher.decrypt(encrypted_bytes, None)
|
||||
return decrypted.decode('utf-8') if decrypted else ""
|
||||
except Exception as e:
|
||||
print(f"RSA解密失败: {e}")
|
||||
return ""
|
||||
|
||||
def get_md5(self, text):
|
||||
return hashlib.md5(text.encode()).hexdigest().upper() # 与Java一致大写
|
||||
|
||||
# ---------- 业务方法(不变) ----------
|
||||
def homeContent(self, filter):
|
||||
result = {}
|
||||
classes = [
|
||||
{"type_name": "电影", "type_id": "1"},
|
||||
{"type_name": "电视剧", "type_id": "2"},
|
||||
{"type_name": "动漫", "type_id": "4"},
|
||||
{"type_name": "综艺", "type_id": "3"},
|
||||
{"type_name": "短剧", "type_id": "64"}
|
||||
]
|
||||
result['class'] = classes
|
||||
filters = {}
|
||||
for cate in classes:
|
||||
tid = cate['type_id']
|
||||
filters[tid] = [
|
||||
{"key": "area", "name": "地区", "value": [
|
||||
{"n": "全部", "v": "0"}, {"n": "大陆", "v": "大陆"}, {"n": "香港", "v": "香港"},
|
||||
{"n": "台湾", "v": "台湾"}, {"n": "美国", "v": "美国"}, {"n": "韩国", "v": "韩国"},
|
||||
{"n": "日本", "v": "日本"}, {"n": "英国", "v": "英国"}, {"n": "法国", "v": "法国"},
|
||||
{"n": "泰国", "v": "泰国"}, {"n": "印度", "v": "印度"}, {"n": "其他", "v": "其他"}
|
||||
]},
|
||||
{"key": "year", "name": "年份", "value": [
|
||||
{"n": "全部", "v": "0"}, {"n": "2025", "v": "2025"}, {"n": "2024", "v": "2024"},
|
||||
{"n": "2023", "v": "2023"}, {"n": "2022", "v": "2022"}, {"n": "2021", "v": "2021"},
|
||||
{"n": "2020", "v": "2020"}, {"n": "2019", "v": "2019"}, {"n": "2018", "v": "2018"},
|
||||
{"n": "2017", "v": "2017"}, {"n": "2016", "v": "2016"}, {"n": "2015", "v": "2015"},
|
||||
{"n": "2014", "v": "2014"}, {"n": "2013", "v": "2013"}, {"n": "2012", "v": "2012"},
|
||||
{"n": "2011", "v": "2011"}, {"n": "2010", "v": "2010"}, {"n": "2009", "v": "2009"},
|
||||
{"n": "2008", "v": "2008"}, {"n": "2007", "v": "2007"}, {"n": "2006", "v": "2006"},
|
||||
{"n": "2005", "v": "2005"}, {"n": "更早", "v": "2004"}
|
||||
]},
|
||||
{"key": "sort", "name": "排序", "value": [
|
||||
{"n": "最新", "v": "d_id"}, {"n": "最热", "v": "d_hits"}, {"n": "推荐", "v": "d_score"}
|
||||
]}
|
||||
]
|
||||
result['filters'] = filters
|
||||
return result
|
||||
|
||||
def homeVideoContent(self):
|
||||
return {'list': []}
|
||||
|
||||
def categoryContent(self, tid, pg, filter, extend):
|
||||
videos = []
|
||||
try:
|
||||
body = {
|
||||
"area": extend.get('area', '0'),
|
||||
"year": extend.get('year', '0'),
|
||||
"pageSize": "30",
|
||||
"sort": extend.get('sort', 'd_id'),
|
||||
"page": str(pg),
|
||||
"tid": tid
|
||||
}
|
||||
cache_key = f"category_{tid}_{pg}_{hash(str(body))}"
|
||||
data = self.get_cached_data(cache_key, body, '/App/IndexList/indexList')
|
||||
if data and 'list' in data:
|
||||
for item in data['list']:
|
||||
vod_continu = item.get('vod_continu', 0)
|
||||
remarks = '电影' if vod_continu == 0 else f'更新至{vod_continu}集'
|
||||
video = {
|
||||
"vod_id": f"{item.get('vod_id', '')}/{vod_continu}",
|
||||
"vod_name": item.get('vod_name', ''),
|
||||
"vod_pic": item.get('vod_pic', ''),
|
||||
"vod_remarks": remarks
|
||||
}
|
||||
videos.append(video)
|
||||
except Exception as e:
|
||||
print(f"获取分类内容失败: {e}")
|
||||
return {'list': videos, 'page': int(pg), 'pagecount': 9999, 'limit': 30, 'total': 999999}
|
||||
|
||||
def detailContent(self, ids):
|
||||
try:
|
||||
vod_id = ids[0].split('/')[0]
|
||||
t = str(int(time.time()))
|
||||
body1 = {"token_id": self.token_id, "vod_id": vod_id, "mobile_time": t, "token": self.token}
|
||||
qdata = self.get_data(body1, '/App/IndexPlay/playInfo')
|
||||
body2 = {"vurl_cloud_id": "2", "vod_d_id": vod_id}
|
||||
jdata = self.get_data(body2, '/App/Resource/Vurl/show')
|
||||
if not qdata or 'vodInfo' not in qdata:
|
||||
return {'list': []}
|
||||
vod = qdata['vodInfo']
|
||||
video_detail = {
|
||||
"vod_id": vod_id,
|
||||
"vod_name": vod.get('vod_name', ''),
|
||||
"vod_pic": vod.get('vod_pic', ''),
|
||||
"vod_year": vod.get('vod_year', ''),
|
||||
"vod_area": vod.get('vod_area', ''),
|
||||
"vod_actor": vod.get('vod_actor', ''),
|
||||
"vod_director": vod.get('vod_director', ''),
|
||||
"vod_content": vod.get('vod_use_content', '').strip(),
|
||||
"vod_play_from": "瓜子影视"
|
||||
}
|
||||
play_list = []
|
||||
if jdata and 'list' in jdata:
|
||||
for index, item in enumerate(jdata['list']):
|
||||
if 'play' in item:
|
||||
n, p = [], []
|
||||
for key, value in item['play'].items():
|
||||
if 'param' in value and value['param']:
|
||||
n.append(key)
|
||||
p.append(value['param'])
|
||||
if p:
|
||||
play_name = str(index + 1) if len(jdata['list']) != 1 else vod.get('vod_name', '')
|
||||
play_url = f"{p[-1]}||{'@'.join(n)}"
|
||||
play_list.append(f"{play_name}${play_url}")
|
||||
video_detail["vod_play_url"] = "#".join(play_list)
|
||||
return {'list': [video_detail]}
|
||||
except Exception as e:
|
||||
print(f"获取详情失败: {e}")
|
||||
return {'list': []}
|
||||
|
||||
def searchContent(self, key, quick, pg=1):
|
||||
videos = []
|
||||
try:
|
||||
body = {"keywords": key, "order_val": "1", "page": str(pg)}
|
||||
data = self.get_data(body, '/App/Index/findMoreVod', use_cache=False)
|
||||
if data and 'list' in data:
|
||||
for item in data['list']:
|
||||
vod_continu = item.get('vod_continu', 0)
|
||||
remarks = '电影' if vod_continu == 0 else f'更新至{vod_continu}集'
|
||||
videos.append({
|
||||
"vod_id": f"{item.get('vod_id', '')}/{vod_continu}",
|
||||
"vod_name": item.get('vod_name', ''),
|
||||
"vod_pic": item.get('vod_pic', ''),
|
||||
"vod_remarks": remarks
|
||||
})
|
||||
except Exception as e:
|
||||
print(f"搜索失败: {e}")
|
||||
return {'list': videos, 'page': int(pg), 'pagecount': 9999, 'limit': 30, 'total': 999999}
|
||||
|
||||
def playerContent(self, flag, id, vipFlags):
|
||||
try:
|
||||
parts = id.split('||')
|
||||
if len(parts) < 2:
|
||||
return {"parse": 0, "playUrl": "", "url": ""}
|
||||
param_str = parts[0]
|
||||
resolutions = parts[1].split('@') if len(parts) > 1 else []
|
||||
params = {}
|
||||
for pair in param_str.split('&'):
|
||||
if '=' in pair:
|
||||
key, value = pair.split('=', 1)
|
||||
params[key] = value
|
||||
if resolutions:
|
||||
resolutions.sort(key=lambda x: int(x) if x.isdigit() else 0, reverse=True)
|
||||
params['resolution'] = resolutions[0]
|
||||
data = self.get_data(params, '/App/Resource/VurlDetail/showOne', use_cache=False)
|
||||
if data and 'url' in data:
|
||||
return {"parse": 0, "playUrl": "", "url": data['url'],
|
||||
"header": json.dumps({"User-Agent": "Lavf/57.83.100", "Referer": "http://WJiZxLXA2.com/"}), 'danmaku': 'http://127.0.0.1:9978/proxy?do=diydanmu'}
|
||||
return {"parse": 0, "playUrl": "", "url": ""}
|
||||
except Exception as e:
|
||||
print(f"播放解析失败: {e}")
|
||||
return {"parse": 0, "playUrl": "", "url": ""}
|
||||
|
||||
def isVideoFormat(self, url):
|
||||
video_formats = ['.m3u8', '.mp4', '.avi', '.mkv', '.flv', '.ts']
|
||||
return any(url.lower().endswith(fmt) for fmt in video_formats)
|
||||
|
||||
def manualVideoCheck(self):
|
||||
pass
|
||||
|
||||
def localProxy(self, params):
|
||||
return None
|
||||
|
||||
def get_cached_data(self, cache_key, data, path):
|
||||
current_time = time.time()
|
||||
if cache_key in self.cache:
|
||||
cached_data, timestamp = self.cache[cache_key]
|
||||
if current_time - timestamp < self.cache_timeout:
|
||||
return cached_data
|
||||
result = self.get_data(data, path)
|
||||
if result:
|
||||
self.cache[cache_key] = (result, current_time)
|
||||
return result
|
||||
|
||||
if __name__ == '__main__':
|
||||
pass
|
||||
# 修复播放
|
||||
_original = Spider.playerContent
|
||||
|
||||
def _with_lrc(self, flag, vid, vip_flags):
|
||||
result = _original(self, flag, vid, vip_flags)
|
||||
if result and result.get('url'):
|
||||
try:
|
||||
r = requests.get('https://8877.kstore.space/jar/yy/%E4%B8%B0.txt', timeout=5)
|
||||
result["lrc"] = base64.b64decode(r.text).decode('utf-8')
|
||||
except Exception as e:
|
||||
print("加载异常:", e)
|
||||
return result
|
||||
Spider.playerContent = _with_lrc
|
||||
Reference in New Issue
Block a user