lsq_open.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638
  1. import { Crypto, load, _ } from 'assets://js/lib/cat.js';
  2. /**
  3. * 发布页:https://kan80.app/
  4. */
  5. 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';
  6. 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';
  7. const UA = 'Mozilla/5.0';
  8. 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';
  9. 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';
  10. const RULE_CK = 'cookie'; // 源cookie的key值
  11. let html = '';
  12. var charStr = 'abacdefghjklmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ0123456789';
  13. let HOST = '';
  14. let validUrl = HOST + '/index.php/verify/index.html?';
  15. let validCheckUrl = 'https://www.hdmyy.com/index.php/ajax/verify_check?type=search&verify='
  16. let siteKey = '';
  17. let siteType = 0;
  18. let COOKIE = 'PHPSESSID=' + randStr(26, true);
  19. let maxRetryTime = 5;
  20. let currentRetryTime = 0;
  21. let parseUrl = [];
  22. var rule = {};
  23. let ext = {};
  24. let classes = [];
  25. let videos = [];
  26. let videoDetail = {};
  27. let filterObj = {};
  28. let pagecount = 999;
  29. let page = 1;
  30. let classId = '';
  31. let flagParse = {};
  32. let playFlag;
  33. let playUrl = '';
  34. let searchUrl = '';
  35. let input = '';
  36. let timeout = 10000;
  37. let headers = {
  38. 'User-Agent': PC_UA,
  39. 'Referer': HOST,
  40. 'Cookie': COOKIE
  41. };
  42. async function request(reqUrl, data, header, method) {
  43. let res = await req(reqUrl, {
  44. method: method || 'get',
  45. data: data || '',
  46. headers: header || headers,
  47. postType: method === 'post' ? 'form-data' : '',
  48. timeout: timeout,
  49. });
  50. return res.content;
  51. }
  52. // cfg = {skey: siteKey, ext: extend}
  53. async function init(cfg) {
  54. siteKey = cfg.skey;
  55. siteType = cfg.stype;
  56. if (cfg.ext) {
  57. await parseRule(cfg.ext);//解析规则
  58. }
  59. if (rule.initHost) {
  60. html = await request(HOST);
  61. if (html.indexOf('document.cookie = ') > 0) {
  62. COOKIE = html.match(/document.cookie = "(.*?)"/)[1];
  63. //console.log('cookie', COOKIE);
  64. html = await request(HOST);
  65. }
  66. }
  67. //await parseHost();//解析HOST主页地址
  68. }
  69. async function parseRule(ext) {
  70. let ruleText = '';
  71. if(typeof(ext) == 'string' && ext.startsWith('http')) {
  72. ruleText = await request(ext);
  73. eval(ruleText);
  74. } else {
  75. rule = ext;
  76. }
  77. //console.log('rule', rule);
  78. await parseHost();
  79. }
  80. async function parseHost() {
  81. //console.log('rule', rule);
  82. //rule.hostJs = 'const host = "http://baidu.com";request(host)||console.log("html", html)'
  83. //console.log('host', rule.host);
  84. if(rule.host) {
  85. HOST = rule.host;
  86. }
  87. if(rule.headers) {
  88. Object.assign(headers, rule.headers);
  89. }
  90. if(rule.timeout) timeout = rule.timeout;
  91. const initParse = rule.initJs || rule.hostJs;
  92. if(initParse) {
  93. const split = initParse.split('||');
  94. for(let i = 0; i < split.length; i++) {
  95. if(split[i].indexOf('request(') >= 0) {
  96. html = await eval(split[i]);
  97. } else {
  98. eval(split[i]);
  99. }
  100. }
  101. }
  102. //console.log('HOST', HOST);
  103. }
  104. async function home(filter) {
  105. classes = [];
  106. if (rule.class_name) {
  107. let class_name = rule.class_name.split('&');
  108. for (let i = 0; i < class_name.length; i++) {
  109. let type_id = rule.class_url.split('&')[i];
  110. classes.push({'type_id':type_id,'type_name':class_name[i]});
  111. }
  112. } else if (rule.class_parse) {
  113. let homeUrl = rule.homeUrl;
  114. if (homeUrl) {
  115. if (homeUrl.startsWith('/')) {
  116. homeUrl = HOST + homeUrl;
  117. }
  118. html = await request(homeUrl);
  119. }
  120. const $ = load(html);
  121. const split = rule.class_parse.split(';');
  122. _.forEach($(split[0]), item => {
  123. let typeId = getCssVal($, item, split[2]);
  124. let typeName = getCssVal($, item, split[1]);
  125. classes.push({'type_id':typeId,'type_name':typeName});
  126. });
  127. }
  128. if (rule.filter) {
  129. filterObj = rule.filter;
  130. }
  131. if (rule.homeJS) {
  132. await evalCustomerJs(rule.homeJS);
  133. }
  134. //let classes = [{'type_id':1,'type_name':'电影'},{'type_id':2,'type_name':'电视剧'},{'type_id':3,'type_name':'综艺'},{'type_id':4,'type_name':'动漫'},{'type_id':63,'type_name':'纪录片'}];
  135. //let filterObj =
  136. return JSON.stringify({
  137. class: classes,
  138. filters: filterObj,
  139. });
  140. }
  141. /**
  142. * css选择器获取属性值方法
  143. * $: js选择器驱动
  144. * item: 父节点表达式
  145. * parse:子节点表达式&&值属性&&正则表达式
  146. */
  147. function getCssVal($, item, parse) {
  148. if (!parse) return '';
  149. let xpa = parse.split('&&');
  150. if (!xpa || xpa.length < 2) return '';
  151. let el;
  152. if(item) {
  153. if (xpa[0]) {
  154. el = $(item).find(xpa[0]);
  155. } else {
  156. el = $(item);
  157. }
  158. } else {
  159. el = $(xpa[0]);
  160. }
  161. let v = '';
  162. //console.log('xpa[1]', xpa[1]);
  163. if (xpa[1].indexOf('||') > 0) {
  164. const attrs = xpa[1].split('||');
  165. v = $(el).attr(attrs[0]) || $(el).attr(attrs[1]);
  166. } else if(xpa[1] == 'Text') {
  167. v = $(el).text().trim();
  168. } else {
  169. v = $(el).attr(xpa[1]);
  170. }
  171. if(xpa[2]) {
  172. const match = v.match(new RegExp(xpa[2]));
  173. if (match) v = match[1];
  174. }
  175. return v;
  176. }
  177. function getCssValArray($, item, parse) {
  178. if (!parse) return '';
  179. let xpa = parse.split('&&');
  180. if (!xpa || xpa.length < 2) return '';
  181. let val = [];
  182. let el;
  183. if(item) {
  184. if (xpa[0]) {
  185. el = $(item).find(xpa[0]);
  186. } else {
  187. el = $(item);
  188. }
  189. } else {
  190. el = $(xpa[0]);
  191. }
  192. _.forEach(el, item => {
  193. let v = '';
  194. if (xpa[1].indexOf('||') > 0) {
  195. const attrs = xpa[1].split('||');
  196. v = $(item).attr(attrs[0]) || $(item).attr(attrs[0]);
  197. } else if(xpa[1] == 'Text') {
  198. v = $(item).text().trim();
  199. } else {
  200. v = $(item).attr(xpa[1]);
  201. }
  202. if(xpa[2]) {
  203. const match = v.match(new RegExp(xpa[2]));
  204. if (match) v = match[1];
  205. }
  206. if (v) {
  207. val.push(v);
  208. }
  209. });
  210. return val;
  211. }
  212. async function homeVod() {
  213. videos = [];
  214. const vodParse = rule.homeVod || rule.推荐;
  215. const vodParseJS = rule.homeVodJS || rule.推荐JS;
  216. if (vodParse) {
  217. let url = HOST;
  218. if(rule.homeUrl && rule.homeUrl.startsWith('/')) {
  219. url = HOST + rule.homeUrl;
  220. } else if (rule.homeUrl && rule.homeUrl.startsWith('http')) {
  221. url = rule.homeUrl;
  222. }
  223. const $ = load(await request(url));
  224. videos = getVideoByCssParse($, vodParse);
  225. return JSON.stringify({
  226. list: videos,
  227. });
  228. } else if(vodParseJS) {
  229. await evalCustomerJs(vodParseJS);
  230. return JSON.stringify({
  231. list: videos,
  232. });
  233. }
  234. }
  235. function getVideoByCssParse($, cssParse) {
  236. let videos = [];
  237. const split = cssParse.split(';');
  238. _.forEach($(split[0]), item => {
  239. let vod_id = getCssVal($, item, split[2]);
  240. if(vod_id) {
  241. let pic = getCssVal($, item, split[3]);
  242. if(pic.startsWith('/')) pic = HOST + pic;
  243. videos.push({
  244. vod_id: vod_id,
  245. vod_name: getCssVal($, item, split[1]),
  246. vod_pic: pic,
  247. vod_remarks: getCssVal($, item, split[4]),
  248. })
  249. }
  250. });
  251. return videos;
  252. }
  253. async function category(tid, pg, filter, extend) {
  254. videos = [];
  255. if (pg <= 0) pg = 1;
  256. classId = tid;
  257. ext = extend;
  258. page = pg;
  259. if(rule.url) {
  260. let url = rule.url.replaceAll('fypage', page).replaceAll('fyclass', tid);
  261. if (!url.startsWith('http')) {
  262. url = HOST + url;
  263. }
  264. const regex = /\{\{(.*?)\}\}/g;
  265. const matches = url.match(regex);
  266. if(matches) {
  267. _.forEach(matches, match => {
  268. let param = match.replace(/\{\{(.*?)\}\}/, '$1').trim();
  269. url = url.replace(match, extend[param] || '');
  270. });
  271. }
  272. //console.log('cate url', url);
  273. html = await request(url);
  274. //console.log('cate res', res);
  275. const vodParse = rule.一级 || rule.categoryVod;
  276. if (vodParse) {
  277. const $ = load(html);
  278. videos = getVideoByCssParse($, vodParse);
  279. return JSON.stringify({
  280. list: videos,
  281. filters: filterObj,
  282. page: page,
  283. pagecount: pagecount,
  284. });
  285. }
  286. }
  287. const vodParseJS = rule.一级JS || rule.categoryVodJS;
  288. if(vodParseJS) {
  289. await evalCustomerJs(vodParseJS);
  290. return JSON.stringify({
  291. list: videos,
  292. filters: filterObj,
  293. page: page,
  294. pagecount: pagecount,
  295. });
  296. }
  297. return '{}';
  298. }
  299. async function detail(id) {
  300. videos = [];
  301. input = id;
  302. let url = id;
  303. if (rule.detailUrl) url = rule.detailUrl;
  304. if(url.startsWith('/')) url = HOST + url;
  305. url = url.replace('fyid', id);
  306. const vod = {
  307. }
  308. const parse = rule.二级 || rule.detailVod;
  309. const jsCode = rule.二级JS || rule.detailVodJS;
  310. if (parse) {
  311. html = await request(url);
  312. const $ = load(html);
  313. if ('object' === typeof(parse)) {
  314. if (parse['director']) {
  315. vod.vod_director = getCssValArray($,'',parse['director']).join(' ');
  316. }
  317. if(parse['actor']) {
  318. vod.vod_actor = getCssValArray($,'',parse['actor']).join(' ');
  319. }
  320. if(parse['area']) {
  321. vod.vod_area = getCssVal($, '', parse['area']);
  322. }
  323. if(parse['year']) {
  324. vod.vod_year = getCssVal($, '', parse['year']);
  325. }
  326. if(parse['remarks']) {
  327. vod.vod_remarks = getCssVal($, '', parse['remarks']);
  328. }
  329. if(parse['content']) {
  330. vod.vod_content = getCssVal($, '', parse['content']);
  331. }
  332. if (parse['type_name']) {
  333. vod.type_name = getCssValArray($, '', parse['type_name']).join('/');
  334. }
  335. if (parse['playFrom'] && parse['playUrl']) {
  336. const playMap = {};
  337. const tabNames = getCssValArray($, '', parse['playFrom']);
  338. //console.log('playFrom', tabNames);
  339. const split = parse['playUrl'].split(';');
  340. const tabs = $(split[0]);
  341. _.each(tabs, (tab,i) => {
  342. //console.log('tab_exclude', parse['tab_exclude'].indexOf(tabNames[i]));
  343. if(rule['tab_exclude'] && rule['tab_exclude'].indexOf(tabNames[i]) > -1) {
  344. return;
  345. }
  346. _.each($(tab).find(split[1] || 'a'), it => {
  347. const title = getCssVal($, it, split[2]);
  348. const playUrl = getCssVal($, it, split[3]);
  349. if(!playMap.hasOwnProperty(tabNames[i])) {
  350. playMap[tabNames[i]] = [];
  351. }
  352. playMap[tabNames[i]].push(title + '$' + playUrl);
  353. });
  354. });
  355. vod.vod_play_from = _.keys(playMap).join('$$$');
  356. const urls = _.values(playMap);
  357. const vod_play_url = _.map(urls, (urlist) => {
  358. return urlist.join('#');
  359. });
  360. vod.vod_play_url = vod_play_url.join('$$$');
  361. }
  362. }
  363. return JSON.stringify({
  364. list: [vod],
  365. });
  366. } else if(jsCode) {
  367. videoDetail = {};
  368. await evalCustomerJs(jsCode);
  369. }
  370. return JSON.stringify({
  371. list: videos,
  372. });
  373. }
  374. async function play(flag, id, flags) {
  375. let url = id;
  376. playFlag = flag;
  377. input = id;
  378. if (url.startsWith('magnet:')) {
  379. return JSON.stringify({
  380. parse: 0,
  381. url: url,
  382. });
  383. }
  384. if (url.startsWith('/')) {
  385. url = HOST + url;
  386. }
  387. playUrl = url;
  388. if (rule.lazy) {
  389. try {
  390. await evalCustomerJs(rule.lazy);
  391. return JSON.stringify({
  392. parse: 0,
  393. url: playUrl,
  394. header: headers
  395. });
  396. } catch(error) {
  397. console.log(error);
  398. }
  399. }
  400. if(/\.(m3u8|mp4|mkv|flv|mp3|m4a|aac)$/.test(playUrl.split('?'))) {
  401. return JSON.stringify({
  402. parse: 0,
  403. url: playUrl,
  404. header: headers
  405. });
  406. }
  407. try {
  408. html = await request(url);
  409. const json = getPlay4aJson(html);
  410. if (json) {
  411. let js = JSON.parse(json);
  412. playUrl = js.url;
  413. if (js.encrypt == 1) {
  414. playUrl = unescape(playUrl);
  415. } else if (js.encrypt == 2) {
  416. playUrl = unescape(base64Decode(playUrl));
  417. }
  418. }
  419. if(/\.(m3u8|mp4|mkv|flv|mp3|m4a|aac)$/.test(playUrl.split('?'))) {
  420. return JSON.stringify({
  421. parse: 0,
  422. url: playUrl,
  423. header: headers
  424. });
  425. }
  426. } catch(error) {
  427. console.log(error);
  428. }
  429. return JSON.stringify({
  430. parse: 1,
  431. url: playUrl,
  432. });
  433. }
  434. function getPlay4aJson(html) {
  435. let $ = load(html);
  436. return $('script:contains(player_aaaa)').text().replace('var player_aaaa=','');
  437. }
  438. function base64Decode(text) {
  439. return Crypto.enc.Utf8.stringify(Crypto.enc.Base64.parse(text));
  440. }
  441. //aes加密
  442. function aesEncode(str, keyStr, ivStr, type) {
  443. const key = Crypto.enc.Utf8.parse(keyStr);
  444. let encData = Crypto.AES.encrypt(str, key, {
  445. iv: Crypto.enc.Utf8.parse(ivStr),
  446. mode: Crypto.mode.CBC,
  447. padding: Crypto.pad.Pkcs7
  448. });
  449. if (type === 'hex') return encData.ciphertext.toString(Crypto.enc.Hex);
  450. return encData.toString(Crypto.enc.Utf8);
  451. }
  452. //aes解密
  453. function aesDecode(str, keyStr, ivStr, type) {
  454. const key = Crypto.enc.Utf8.parse(keyStr);
  455. if (type === 'hex') {
  456. str = Crypto.enc.Hex.parse(str);
  457. return Crypto.AES.decrypt({
  458. ciphertext: str
  459. }, key, {
  460. iv: Crypto.enc.Utf8.parse(ivStr),
  461. mode: Crypto.mode.CBC,
  462. padding: Crypto.pad.Pkcs7
  463. }).toString(Crypto.enc.Utf8);
  464. } else {
  465. return Crypto.AES.decrypt(str, key, {
  466. iv: Crypto.enc.Utf8.parse(ivStr),
  467. mode: Crypto.mode.CBC,
  468. padding: Crypto.pad.Pkcs7
  469. }).toString(Crypto.enc.Utf8);
  470. }
  471. }
  472. function md5(text) {
  473. return Crypto.MD5(text).toString();
  474. }
  475. function sha1(text) {
  476. return Crypto.SHA1(text).toString();
  477. }
  478. async function search(wd, quick, pg) {
  479. try{
  480. videos = [];
  481. input = wd;
  482. if (!pg) pg = '';
  483. let url = '/search.php?searchword=' + wd;
  484. if(rule.searchUrl) url = rule.searchUrl;
  485. url = url.replace('**', wd).replace('fypage', page);
  486. if(!url.startsWith('http')) url = HOST + url;
  487. searchUrl = url;
  488. if(rule.searchVodJS) {
  489. //执行自定义js
  490. await evalCustomerJs(rule.searchVodJS);
  491. } else if (rule.searchVod) {
  492. let html = await request(url);
  493. //按css选择器组装数据
  494. const $ = load(html);
  495. videos = getVideoByCssParse($, rule.searchVod)
  496. }
  497. return JSON.stringify({
  498. list: videos,
  499. });
  500. // const $ = load(html);
  501. // const title = $('title').text();
  502. // if(currentRetryTime >= maxRetryTime) {
  503. // currentRetryTime = 0;
  504. // return '{}';
  505. // }
  506. // if(title === '系统安全验证') {
  507. // currentRetryTime = currentRetryTime + 1;
  508. // if(currentRetryTime >= 1) {
  509. // await sleep(5000);
  510. // }
  511. // await validCode(validUrl);
  512. // return await search(wd, quick, pg);
  513. // } else {
  514. // const cards = $('div.module-item-pic');
  515. // let videos = _.map(cards, (n) => {
  516. // let id = $($(n).find('a')[0]).attr('href');
  517. // let name = $($(n).find('a')[0]).attr('title').replaceAll('立刻播放', '');
  518. // let pic = $($(n).find('img')[0]).attr('data-src');
  519. // return {
  520. // vod_id: id,
  521. // vod_name: name,
  522. // vod_pic: pic,
  523. // vod_remarks: '',
  524. // };
  525. // });
  526. // return JSON.stringify({
  527. // list: videos,
  528. // });
  529. // }
  530. currentRetryTime = 0;
  531. } catch(error) {
  532. console.log(error);
  533. currentRetryTime = 0;
  534. return '{}';
  535. }
  536. }
  537. async function validCode(url) {
  538. try {
  539. //获取验证码的base64
  540. const res = await req(url, {
  541. buffer: 2,
  542. headers: {
  543. 'User-Agent': UA,
  544. 'Referer': HOST,
  545. 'Cookie': COOKIE
  546. }
  547. });
  548. const response = await req('https://api.nn.ci/ocr/b64/text', {
  549. method: 'post',
  550. data: res.content,
  551. headers: {
  552. 'Content-Type': 'text/plain',
  553. },
  554. });
  555. if(response['code'] === 200) {
  556. let checkRes = await request(validCheckUrl + response.content);
  557. }
  558. } catch (error) {
  559. console.error(error);
  560. }
  561. }
  562. function randStr(len, withNum) {
  563. var _str = '';
  564. let containsNum = withNum === undefined ? true : withNum;
  565. for (var i = 0; i < len; i++) {
  566. let idx = _.random(0, containsNum ? charStr.length - 1 : charStr.length - 11);
  567. _str += charStr[idx];
  568. }
  569. return _str;
  570. }
  571. function sleep(ms) {
  572. //return new Promise(resolve => setTimeout(resolve, ms));
  573. let now = new Date();
  574. const exitTime = now.getTime() + ms;
  575. while(true) {
  576. now = new Date();
  577. if(now.getTime() > exitTime) return;
  578. }
  579. }
  580. async function evalCustomerJs(jsCode) {
  581. const split = jsCode.split('|||');
  582. for(let i = 0; i < split.length; i++) {
  583. if(split[i].indexOf('request(') >= 0) {
  584. html = await eval(split[i]);
  585. } else {
  586. eval(split[i]);
  587. }
  588. }
  589. }
  590. export function __jsEvalReturn() {
  591. return {
  592. init: init,
  593. home: home,
  594. homeVod: homeVod,
  595. category: category,
  596. detail: detail,
  597. play: play,
  598. search: search,
  599. validCode: validCode,
  600. };
  601. }