diff --git a/js/czzy_open.js b/js/czzy_open.js new file mode 100644 index 0000000..6477338 --- /dev/null +++ b/js/czzy_open.js @@ -0,0 +1,240 @@ +import { Crypto, load, _ } from './lib/cat.js'; + +let key = 'czzy'; +let url = 'https://cz4k.com'; +let siteKey = ''; +let siteType = 0; + +const 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 cookie = {}; + +async function request(reqUrl, referer, mth, data, hd) { + const headers = { + 'User-Agent': UA, + Cookie: _.map(cookie, (value, key) => { + return `${key}=${value}`; + }).join(';'), + }; + if (referer) headers.referer = encodeURIComponent(referer); + let res = await req(reqUrl, { + method: mth || 'get', + headers: headers, + data: data, + postType: mth === 'post' ? 'form' : '', + }); + if (res.headers['set-cookie']) { + const set_cookie = _.isArray(res.headers['set-cookie']) ? res.headers['set-cookie'].join(';') : res.headers['set-cookie']; + const cks = set_cookie.split(';'); + for (const c of cks) { + const tmp = c.trim(); + if (tmp.startsWith('result=')) { + cookie.result = tmp.substring(7); + return await request(reqUrl, reqUrl, 'post', { + result: cookie.result, + }); + } else if (tmp.startsWith('esc_search_captcha=1')) { + cookie.esc_search_captcha = 1; + delete cookie.result; + return await request(reqUrl); + } + } + // console.log(res.headers['set-cookie']); + } + return res.content; +} + +// cfg = {skey: siteKey, ext: extend} +async function init(cfg) { + siteKey = cfg.skey; + siteType = cfg.stype; +} + +async function home(filter) { + let filterObj = {}; + const html = await request(url + '/movie_bt'); + const $ = load(html); + const series = $('div#beautiful-taxonomy-filters-tax-movie_bt_series > a[cat-url*=movie_bt_series]'); + const tags = $('div#beautiful-taxonomy-filters-tax-movie_bt_tags > a'); + let tag = { + key: 'tag', + name: '类型', + value: _.map(tags, (n) => { + let v = n.attribs['cat-url'] || ''; + v = v.substring(v.lastIndexOf('/') + 1); + return { n: n.children[0].data, v: v }; + }), + }; + tag['init'] = tag.value[0].v; + let classes = _.map(series, (s) => { + let typeId = s.attribs['cat-url']; + typeId = typeId.substring(typeId.lastIndexOf('/') + 1); + filterObj[typeId] = [tag]; + return { + type_id: typeId, + type_name: s.children[0].data, + }; + }); + const sortName = ['电影', '电视剧', '国产剧', '美剧', '韩剧', '日剧', '海外剧(其他)', '华语电影', '印度电影', '日本电影', '欧美电影', '韩国电影', '动画', '俄罗斯电影', '加拿大电影']; + classes = _.sortBy(classes, (c) => { + const index = sortName.indexOf(c.type_name); + return index === -1 ? sortName.length : index; + }); + return JSON.stringify({ + class: classes, + filters: filterObj, + }); +} + +async function homeVod() { + return '{}'; +} + +async function category(tid, pg, filter, extend) { + if (pg <= 0) pg = 1; + const tag = extend.tag || ''; + const link = url + '/movie_bt' + (tag.length > 0 ? `/movie_bt_tags/${tag}` : '') + '/movie_bt_series/' + tid + (pg > 1 ? `/page/${pg}` : ''); + const html = await request(link); + const $ = load(html); + const items = $('div.mrb > ul > li'); + let videos = _.map(items, (item) => { + const img = $(item).find('img:first')[0]; + const a = $(item).find('a:first')[0]; + const hdinfo = $($(item).find('div.hdinfo')[0]).text().trim(); + const jidi = $($(item).find('div.jidi')[0]).text().trim(); + return { + vod_id: a.attribs.href.replace(/.*?\/movie\/(.*).html/g, '$1'), + vod_name: img.attribs.alt, + vod_pic: img.attribs['data-original'], + vod_remarks: jidi || hdinfo || '', + }; + }); + const hasMore = $('div.mrb > div.pagenavi_txt > a:contains(>)').length > 0; + const pgCount = hasMore ? parseInt(pg) + 1 : parseInt(pg); + return JSON.stringify({ + page: parseInt(pg), + pagecount: pgCount, + limit: 20, + total: 20 * pgCount, + list: videos, + }); +} + +function stripHtmlTag(src) { + return src + .replace(/<\/?[^>]+(>|$)/g, '') + .replace(/&.{1,5};/g, '') + .replace(/\s{2,}/g, ' '); +} + +async function detail(id) { + const html = await request(url + '/movie/' + id + '.html'); + const $ = load(html); + const detail = $('ul.moviedteail_list > li'); + let vod = { + vod_id: id, + vod_pic: $('div.dyimg img:first').attr('src'), + vod_remarks: '', + vod_content: stripHtmlTag($('div.yp_context').html()).trim(), + }; + for (const info of detail) { + const i = $(info).text().trim(); + if (i.startsWith('地区:')) { + vod.vod_area = i.substring(3); + } else if (i.startsWith('年份:')) { + vod.vod_year = i.substring(3); + } else if (i.startsWith('导演:')) { + vod.vod_director = _.map($(info).find('a'), (a) => { + return a.children[0].data; + }).join('/'); + } else if (i.startsWith('主演:')) { + vod.vod_actor = _.map($(info).find('a'), (a) => { + return a.children[0].data; + }).join('/'); + } else if (i.startsWith('语言:')) { + vod.vod_lang = i.substring(3); + } + } + const playlist = _.map($('div.paly_list_btn > a'), (a) => { + return a.children[0].data + '$' + a.attribs.href.replace(/.*?\/v_play\/(.*).html/g, '$1'); + }); + vod.vod_play_from = key; + vod.vod_play_url = playlist.join('#'); + return JSON.stringify({ + list: [vod], + }); +} + +async function play(flag, id, flags) { + const link = url + '/v_play/' + id + '.html'; + const html = await request(link); + const $ = load(html); + const iframe = $('body iframe[src*=Cloud]'); + if (iframe.length > 0) { + const iframeHtml = ( + await req(iframe[0].attribs.src, { + headers: { + Referer: link, + 'User-Agent': UA, + }, + }) + ).content; + let code = iframeHtml + .match(/var url = '(.*?)'/)[1] + .split('') + .reverse() + .join(''); + let temp = ''; + for (let i = 0x0; i < code.length; i = i + 0x2) { + temp += String.fromCharCode(parseInt(code[i] + code[i + 0x1], 0x10)); + } + const playUrl = temp.substring(0x0, (temp.length - 0x7) / 0x2) + temp.substring((temp.length - 0x7) / 0x2 + 0x7); + return JSON.stringify({ + parse: 0, + url: playUrl, + }); + } else { + const js = $('script:contains(window.wp_nonce)').html(); + const group = js.match(/(var.*)eval\((\w*\(\w*\))\)/); + const md5 = Crypto; + const result = eval(group[1] + group[2]); + const playUrl = result.match(/url:.*?['"](.*?)['"]/)[1]; + return JSON.stringify({ + parse: 0, + url: playUrl, + }); + } +} + +async function search(wd, quick) { + const html = await request(url + '/?s=' + wd); + const $ = load(html); + const items = $('div.search_list > ul > li'); + let videos = _.map(items, (item) => { + const img = $(item).find('img:first')[0]; + const a = $(item).find('a:first')[0]; + const hdinfo = $($(item).find('div.hdinfo')[0]).text().trim(); + const jidi = $($(item).find('div.jidi')[0]).text().trim(); + return { + vod_id: a.attribs.href.replace(/.*?\/movie\/(.*).html/g, '$1'), + vod_name: img.attribs.alt, + vod_pic: img.attribs['data-original'], + vod_remarks: jidi || hdinfo || '', + }; + }); + return JSON.stringify({ + list: videos, + }); +} + +export function __jsEvalReturn() { + return { + init: init, + home: home, + homeVod: homeVod, + category: category, + detail: detail, + play: play, + search: search, + }; +} diff --git a/js/ikanbot_open.js b/js/ikanbot_open.js new file mode 100644 index 0000000..2fc1f0a --- /dev/null +++ b/js/ikanbot_open.js @@ -0,0 +1,203 @@ +import { Crypto, load, _ } from './lib/cat.js'; + +let key = 'ikanbot'; +let url = 'https://www.ikanbot.com'; +let siteKey = ''; +let siteType = 0; + +const 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'; + +async function request(reqUrl, agentSp) { + let res = await req(reqUrl, { + method: 'get', + headers: { + 'User-Agent': agentSp || UA, + }, + }); + return res.content; +} + +// cfg = {skey: siteKey, ext: extend} +async function init(cfg) { + siteKey = cfg.skey; + siteType = cfg.stype; +} + +function getClass($) { + const nav = $('ul.nav-pills:eq(1) > li > a'); + let tags = { + key: 'tag', + name: '标签', + value: _.map(nav, (n) => { + return { n: n.children[0].data, v: n.attribs.href }; + }), + }; + tags['init'] = tags.value[0].v; + const title = $('title:first').text().split('-')[0].substring(2); + return { cls: { type_id: tags.value[0].v, type_name: title }, tags: [tags] }; +} + +async function home(filter) { + let classes = []; + let filterObj = {}; + for (const cate of ['/hot/index-movie-热门.html', '/hot/index-tv-热门.html']) { + const html = await request(url + cate); + const $ = load(html); + const { cls, tags } = getClass($); + classes.push(cls); + filterObj[cls.type_id] = tags; + } + return JSON.stringify({ + class: classes, + filters: filterObj, + }); +} + +async function homeVod() { + return '{}'; +} + +async function category(tid, pg, filter, extend) { + if (pg <= 0) pg = 1; + const link = url + (extend.tag || tid).replace('.html', pg > 1 ? `-p-${pg}.html` : '.html'); + const html = await request(link); + const $ = load(html); + const items = $('div.v-list a.item'); + var jsBase = await js2Proxy(true, siteType, siteKey, 'img/', {}); + let videos = _.map(items, (item) => { + const img = $(item).find('img:first')[0]; + return { + vod_id: item.attribs.href, + vod_name: img.attribs.alt, + vod_pic: jsBase + base64Encode(img.attribs['data-src']), + vod_remarks: '', + }; + }); + const hasMore = $('div.page-more > a:contains(下一页)').length > 0; + const pgCount = hasMore ? parseInt(pg) + 1 : parseInt(pg); + return JSON.stringify({ + page: parseInt(pg), + pagecount: pgCount, + limit: 24, + total: 24 * pgCount, + list: videos, + }); +} + +async function detail(id) { + const html = await request(url + id); + const $ = load(html); + var jsBase = await js2Proxy(true, siteType, siteKey, 'img/', {}); + const detail = $('div.detail > .meta'); + let vod = { + vod_id: id, + vod_pic: jsBase + base64Encode($('div.item-root > img')[0].attribs['data-src']), + vod_remarks: '', + }; + for (const info of detail) { + if ($(info).hasClass('title')) { + vod.vod_name = info.children[0].data; + } else if ($(info).hasClass('year')) { + vod.vod_area = info.children[0].data; + } else if ($(info).hasClass('country')) { + vod.vod_area = info.children[0].data; + } else if ($(info).hasClass('celebrity')) { + vod.vod_actor = info.children[0].data; + } + } + + const res = await req(url + '/api/getResN?videoId=' + id.substring(id.lastIndexOf('/') + 1) + '&mtype=2', { + headers: { + Referer: url, + 'User-Agent': UA, + }, + }); + const list = JSON.parse(res.content).data.list; + let playlist = {}; + for (const l of list) { + const flagData = JSON.parse(l.resData); + for (const f of flagData) { + const from = f.flag; + const urls = f.url; + if (!from || !urls) continue; + if (playlist[from]) continue; + playlist[from] = urls; + } + } + vod.vod_play_from = _.keys(playlist).join('$$$'); + vod.vod_play_url = _.values(playlist).join('$$$'); + return JSON.stringify({ + list: [vod], + }); +} + +function base64Encode(text) { + return Crypto.enc.Base64.stringify(Crypto.enc.Utf8.parse(text)); +} + +function base64Decode(text) { + return Crypto.enc.Utf8.stringify(Crypto.enc.Base64.parse(text)); +} + +async function proxy(segments, headers) { + let what = segments[0]; + let url = base64Decode(segments[1]); + if (what == 'img') { + var resp = await req(url, { + buffer: 2, + headers: { + Referer: url, + 'User-Agent': UA, + }, + }); + return JSON.stringify({ + code: resp.code, + buffer: 2, + content: resp.content, + headers: resp.headers, + }); + } + return JSON.stringify({ + code: 500, + content: '', + }); +} + +async function play(flag, id, flags) { + return JSON.stringify({ + parse: 0, + url: id, + }); +} + +async function search(wd, quick) { + const html = await request(url + '/search?q=' + wd); + const $ = load(html); + const items = $('div.media > div.media-left > a'); + var jsBase = await js2Proxy(true, siteType, siteKey, 'img/', {}); + let videos = _.map(items, (item) => { + const img = $(item).find('img:first')[0]; + return { + vod_id: item.attribs.href, + vod_name: img.attribs.alt, + vod_pic: jsBase + base64Encode(img.attribs['data-src']), + vod_remarks: '', + }; + }); + return JSON.stringify({ + list: videos, + }); +} + +export function __jsEvalReturn() { + return { + init: init, + home: home, + homeVod: homeVod, + category: category, + detail: detail, + play: play, + proxy: proxy, + search: search, + }; +} diff --git a/js/kunyu77_open.js b/js/kunyu77_open.js new file mode 100644 index 0000000..91d8936 --- /dev/null +++ b/js/kunyu77_open.js @@ -0,0 +1,341 @@ +import { Crypto, dayjs, jinja2, Uri, _ } from './lib/cat.js'; + +let key = 'kunyu77'; +let url = 'http://api.tyun77.cn'; +let device = {}; +let timeOffset = 0; +let siteKey = ''; +let siteType = 0; + +async function request(reqUrl, agentSp) { + let sj = dayjs().unix() - timeOffset; + let uri = new Uri(reqUrl); + uri.addQueryParam('pcode', '010110005'); + uri.addQueryParam('version', '2.1.6'); + uri.addQueryParam('devid', device.id); + uri.addQueryParam('package', 'com.sevenVideo.app.android'); + uri.addQueryParam('sys', 'android'); + uri.addQueryParam('sysver', device.release); + uri.addQueryParam('brand', device.brand); + uri.addQueryParam('model', device.model.replaceAll(' ', '_')); + uri.addQueryParam('sj', sj); + let keys = []; + for (var i = 0; i < uri.queryPairs.length; i++) { + keys.push(uri.queryPairs[i][0]); + } + keys = _.sortBy(keys, function (name) { + return name; + }); + let tkSrc = uri.path(); + for (let k of keys) { + let v = uri.getQueryParamValue(k); + v = encodeURIComponent(v); + tkSrc += v; + } + tkSrc += sj; + tkSrc += 'XSpeUFjJ'; + console.log(tkSrc); + let tk = Crypto.MD5(tkSrc).toString().toLowerCase(); + console.log(tk); + let header = { + 'user-agent': agentSp || 'okhttp/3.12.0', + t: sj, + TK: tk, + }; + let res = await req(uri.toString(), { + headers: header, + }); + + let serverTime = res.headers.date; // dart all response header key is lowercase + let content = res.content; + let serverTimeS = dayjs(serverTime).unix(); + timeOffset = dayjs().unix() - serverTimeS; + // console.log(content); + return content; +} + +async function init(cfg) { + siteKey = cfg.skey; + siteType = cfg.stype; + var deviceKey = 'device'; + var deviceInfo = await local.get(key, deviceKey); + if (deviceInfo.length > 0) { + try { + device = JSON.parse(deviceInfo); + } catch (error) {} + } + if (_.isEmpty(device)) { + device = randDevice(); + device.id = randStr(32).toLowerCase(); + device.ua = 'Dalvik/2.1.0 (Linux; U; Android ' + device.release + '; ' + device.model + ' Build/' + device.buildId + ')'; + await local.set(key, deviceKey, JSON.stringify(device)); + } + await request(url + '/api.php/provide/getDomain'); + await request(url + '/api.php/provide/config'); + await request(url + '/api.php/provide/checkUpgrade'); + await request(url + '/api.php/provide/channel'); +} + +async function home(filter) { + let data = JSON.parse(await request(url + '/api.php/provide/filter')).data; + let classes = []; + let filterObj = {}; + let filterAll = []; + for (const key in data) { + classes.push({ + type_id: key, + type_name: data[key][0].cat, + }); + if (!filter) continue; + try { + let typeId = key.toString(); + if (_.isEmpty(filterAll)) { + let filterData = JSON.parse(await request(url + '/api.php/provide/searchFilter?type_id=0&pagenum=1&pagesize=1')).data.conditions; + // console.log(filterData); + // 年份 + let year = { + key: 'year', + name: '年份', + init: '', + }; + let yearValues = []; + yearValues.push({ n: '全部', v: '' }); + filterData.y.forEach((e) => { + yearValues.push({ n: e.name, v: e.value }); + }); + year['value'] = yearValues; + // 地区 + let area = { + key: 'area', + name: '地区', + init: '', + }; + let areaValues = []; + areaValues.push({ n: '全部', v: '' }); + filterData.a.forEach((e) => { + areaValues.push({ n: e.name, v: e.value }); + }); + area['value'] = areaValues; + // 类型 + let type = { + key: 'category', + name: '类型', + init: '', + }; + let typeValues = []; + typeValues.push({ n: '全部', v: '' }); + filterData.scat.forEach((e) => { + typeValues.push({ n: e.name, v: e.value }); + }); + type['value'] = typeValues; + + filterAll.push(year, area, type); + } + if (!_.isEmpty(filterAll)) { + filterObj[typeId] = filterAll; + } + } catch (e) { + console.log(e); + } + } + // console.log(classes); + // console.log(filterObj); + return JSON.stringify({ + class: classes, + filters: filterObj, + }); +} + +async function homeVod() { + let data = JSON.parse(await request(url + '/api.php/provide/homeBlock?type_id=0')).data; + let blocks = data.blocks; + let videos = []; + for (const block of blocks) { + let name = block.block_name; + if (name.indexOf('热播') >= 0) continue; + let contents = block.contents; + for (const content of contents) { + videos.push({ + vod_id: content.id, + vod_name: content.title, + vod_pic: content.videoCover, + vod_remarks: content.msg, + }); + } + } + return JSON.stringify({ + list: videos, + }); +} + +async function category(tid, pg, filter, extend) { + let reqUrl = url + '/api.php/provide/searchFilter?type_id=' + tid + '&pagenum=' + pg + '&pagesize=24&'; + reqUrl += jinja2('year={{ext.year}}&category={{ext.category}}&area={{ext.area}}', { ext: extend }); + let data = JSON.parse(await request(reqUrl)).data; + let videos = []; + for (const vod of data.result) { + videos.push({ + vod_id: vod.id, + vod_name: vod.title, + vod_pic: vod.videoCover, + vod_remarks: vod.msg, + }); + } + return JSON.stringify({ + page: parseInt(data.page), + pagecount: data.pagesize, + limit: 24, + total: data.total, + list: videos, + }); +} + +async function detail(id) { + let data = JSON.parse(await request(url + '/api.php/provide/videoDetail?ids=' + id)).data; + console.log(data); + let vod = { + vod_id: data.id, + vod_name: data.videoName, + vod_pic: data.videoCover, + type_name: data.subCategory, + vod_year: data.year, + vod_area: data.area, + vod_remarks: data.msg, + vod_actor: data.actor, + vod_director: data.director, + vod_content: data.brief.trim(), + }; + let episodes = JSON.parse(await request(url + '/api.php/provide/videoPlaylist?ids=' + id)).data.episodes; + let playlist = {}; + for (const episode of episodes) { + let playurls = episode.playurls; + for (const playurl of playurls) { + let from = playurl.playfrom; + let t = formatPlayUrl(vod.vod_name, playurl.title); + if (t.length == 0) t = playurl.title.trim(); + if (!playlist.hasOwnProperty(from)) { + playlist[from] = []; + } + playlist[from].push(t + '$' + playurl.playurl); + } + } + vod.vod_play_from = _.keys(playlist).join('$$$'); + let urls = _.values(playlist); + let vod_play_url = []; + for (const urlist of urls) { + vod_play_url.push(urlist.join('#')); + } + vod.vod_play_url = vod_play_url.join('$$$'); + return JSON.stringify({ + list: [vod], + }); +} + +async function play(flag, id, flags) { + try { + let data = JSON.parse(await request(url + '/api.php/provide/parserUrl?url=' + id + '&retryNum=0')).data; + let playHeader = data.playHeader; + let jxUrl = data.url; + let res = await req(jxUrl, { + headers: { + 'user-agent': 'okhttp/3.12.0', + }, + }); + let result = jsonParse(id, JSON.parse(res.content)); + result['parse'] = 0; + if (playHeader) { + result.header = _.merge(result.header, playHeader); + } + return JSON.stringify(result); + } catch (e) { + return JSON.stringify({ + parse: 0, + url: id, + }); + } +} + +async function search(wd, quick) { + let data = JSON.parse(await request(url + '/api.php/provide/searchVideo?searchName=' + wd + '&pg=1', 'okhttp/3.12.0')).data; + let videos = []; + for (const vod of data) { + videos.push({ + vod_id: vod.id, + vod_name: vod.videoName, + vod_pic: vod.videoCover, + vod_remarks: vod.msg, + }); + } + return JSON.stringify({ + list: videos, + }); +} + +const charStr = 'abacdefghjklmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ0123456789'; +function randStr(len, withNum) { + var _str = ''; + let containsNum = withNum === undefined ? true : withNum; + for (var i = 0; i < len; i++) { + let idx = _.random(0, containsNum ? charStr.length - 1 : charStr.length - 11); + _str += charStr[idx]; + } + return _str; +} + +function randDevice() { + return { + brand: 'Huawei', + model: 'HUAWEI Mate 20', + release: '10', + buildId: randStr(3, false).toUpperCase() + _.random(11, 99) + randStr(1, false).toUpperCase(), + }; +} + +function formatPlayUrl(src, name) { + return name + .trim() + .replaceAll(src, '') + .replace(/<|>|《|》/g, '') + .replace(/\$|#/g, ' ') + .trim(); +} + +function jsonParse(input, json) { + try { + let url = json.url ?? ''; + if (url.startsWith('//')) { + url = 'https:' + url; + } + if (!url.startsWith('http')) { + return {}; + } + let headers = json['headers'] || {}; + let ua = (json['user-agent'] || '').trim(); + if (ua.length > 0) { + headers['User-Agent'] = ua; + } + let referer = (json['referer'] || '').trim(); + if (referer.length > 0) { + headers['Referer'] = referer; + } + return { + header: headers, + url: url, + }; + } catch (error) { + console.log(error); + } + return {}; +} + +export function __jsEvalReturn() { + return { + init: init, + home: home, + homeVod: homeVod, + category: category, + detail: detail, + play: play, + search: search, + }; +}