From 01cf29e115bebe457e8fb25e29c31bd2bafa7273 Mon Sep 17 00:00:00 2001 From: cluntop <85211716+cluntop@users.noreply.github.com> Date: Sat, 27 Dec 2025 08:47:11 +0800 Subject: [PATCH] Update Up --- box.json | 30 ++++ js/apple.js | 149 ++++++++++++++++ js/永乐视频.js | 107 +++++++++++ js/海龟.js | 150 ++++++++++++++++ js/荐片.js | 152 ++++++++++++++++ js/鬼片之家.js | 115 ++++++++++++ py/瓜子.py | 471 +++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 1174 insertions(+) create mode 100755 js/apple.js create mode 100755 js/永乐视频.js create mode 100755 js/海龟.js create mode 100755 js/荐片.js create mode 100755 js/鬼片之家.js create mode 100755 py/瓜子.py diff --git a/box.json b/box.json index 417939d1..7b536655 100755 --- a/box.json +++ b/box.json @@ -17,6 +17,36 @@ "type": 3, "api": "./js/金牌影院.js" }, + { + "key": "荐片", + "name": "荐片", + "type": 3, + "api": "./js/荐片.js" + }, + { + "key": "海龟", + "name": "海龟", + "type": 3, + "api": "./js/海龟.js" + }, + { + "key": "鬼片之家", + "name": "鬼片之家", + "type": 3, + "api": "./js/鬼片之家.js" + }, + { + "key": "永乐视频", + "name": "永乐视频", + "type": 3, + "api": "./js/永乐视频.js" + }, + { + "key": "apple", + "name": "apple", + "type": 3, + "api": "./js/apple.js" + }, { "key": "tt", "name": "老三", diff --git a/js/apple.js b/js/apple.js new file mode 100755 index 00000000..fc8e75aa --- /dev/null +++ b/js/apple.js @@ -0,0 +1,149 @@ +let host = 'http://asp.xpgtv.com'; +let headers = { + "User-Agent": "okhttp/3.12.11" +}; + +async function init(cfg) {} + +function getList(data) { + let videos = []; + data.forEach(vod => { + let r = vod.updateInfo ? "更新至" + vod.updateInfo : ""; + videos.push({ + "vod_id": vod.id.toString(), + "vod_name": vod.name, + "vod_pic": vod.pic, + "vod_remarks": r || (vod.score ? vod.score.toString() : "") + }); + }); + return videos; +} + +// ------------------- 兼容 JSON ------------------- +function parseResp(resp) { + return typeof resp.content === "string" ? JSON.parse(resp.content) : resp.content; +} + +async function home(filter) { + let url = host + "/api.php/v2.vod/androidtypes"; + let resp = await req(url, { headers: headers }); + let data = parseResp(resp); + + let dy = { "classes": "类型", "areas": "地区", "years": "年份", "sortby": "排序" }; + let demos = ['时间', '人气', '评分']; + let classes = []; + let filters = {}; + + data.data.forEach(item => { + let typeId = item.type_id.toString(); + classes.push({ "type_name": item.type_name, "type_id": typeId }); + item['sortby'] = ['updatetime', 'hits', 'score']; + let filterArray = []; + for (let key in dy) { + if (item[key] && item[key].length > 1) { + let values = []; + item[key].forEach((val, idx) => { + let vStr = val.toString().trim(); + if (vStr !== "") { + values.push({ "n": key === "sortby" ? demos[idx] : vStr, "v": vStr }); + } + }); + let fKey = key === "areas" ? "areaes" : (key === "years" ? "yeares" : key); + filterArray.push({ "key": fKey, "name": dy[key], "value": values }); + } + } + filters[typeId] = filterArray; + }); + return JSON.stringify({ class: classes, filters: filters }); +} + +async function homeVod() { + let url = host + "/api.php/v2.main/androidhome"; + let resp = await req(url, { headers: headers }); + let data = parseResp(resp); + + let videos = []; + data.data.list.forEach(i => { videos = videos.concat(getList(i.list)); }); + return JSON.stringify({ list: videos }); +} + +async function category(tid, pg, filter, extend) { + let params = { + "page": pg, + "type": tid, + "area": extend.areaes || '', + "year": extend.yeares || '', + "sortby": extend.sortby || '', + "class": extend.classes || '' + }; + let query = Object.keys(params).filter(k => params[k] !== '').map(k => k + '=' + encodeURIComponent(params[k])).join('&'); + let url = host + '/api.php/v2.vod/androidfilter10086?' + query; + let resp = await req(url, { headers: headers }); + let data = parseResp(resp); + + return JSON.stringify({ list: getList(data.data), page: parseInt(pg), pagecount: 9999, limit: 90, total: 999999 }); +} + +async function detail(id) { + let url = host + '/api.php/v3.vod/androiddetail2?vod_id=' + id; + let resp = await req(url, { headers: headers }); + let data = parseResp(resp).data; + + // 过滤掉包含“及时雨”的选集 + let filteredUrls = data.urls.filter(i => !i.key.includes("及时雨")); + let playlist = filteredUrls.map(i => i.key + '$' + i.url).join('#'); + + let vod = { + 'vod_id': id, + 'vod_name': data.name, + 'vod_year': data.year, + 'vod_area': data.area, + 'vod_lang': data.lang, + 'type_name': data.className, + 'vod_actor': data.actor, + 'vod_director': data.director, + 'vod_content': data.content, + 'vod_play_from': '书生精选线路', + 'vod_play_url': playlist + }; + + return JSON.stringify({ list: [vod] }); +} + +async function search(wd, quick, pg) { + let page = pg || '1'; + let url = host + '/api.php/v2.vod/androidsearch10086?page=' + page + '&wd=' + encodeURIComponent(wd); + let resp = await req(url, { headers: headers }); + let data = parseResp(resp); + + return JSON.stringify({ list: getList(data.data), page: page }); +} + +async function play(flag, id, flags) { + let playUrl = id; + if (!id.startsWith('http')) { + playUrl = "http://c.xpgtv.net/m3u8/" + id + ".m3u8"; + } + + const playHeader = { + 'user_id': 'XPGBOX', + 'token2': 'SnAXiSW8vScXE0Z9aDOnK5xffbO75w1+uPom3WjnYfVEA1oWtUdi2Ihy1N8=', + 'version': 'XPGBOX com.phoenix.tv1.5.7', + 'hash': 'd78a', + 'screenx': '2345', + 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36', + 'token': 'ElEDlwCVgXcFHFhddiq2JKteHofExRBUrfNlmHrWetU3VVkxnzJAodl52N9EUFS+Dig2A/fBa/V9RuoOZRBjYvI+GW8kx3+xMlRecaZuECdb/3AdGkYpkjW3wCnpMQxf8vVeCz5zQLDr8l8bUChJiLLJLGsI+yiNskiJTZz9HiGBZhZuWh1mV1QgYah5CLTbSz8=', + 'timestamp': '1743060300', + 'screeny': '1065', + 'Accept': '*/*', + 'Connection': 'keep-alive' + }; + + return JSON.stringify({ + parse: 0, + url: playUrl, + header: playHeader + }); +} + +export default { init, home, homeVod, category, detail, search, play }; \ No newline at end of file diff --git a/js/永乐视频.js b/js/永乐视频.js new file mode 100755 index 00000000..5068314f --- /dev/null +++ b/js/永乐视频.js @@ -0,0 +1,107 @@ +let host = 'https://www.ylys.tv'; +let headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", + "Referer": host + "/" +}; + +async function init(cfg) {} + +/** + * 通用解析:不做过滤,不做干扰 + */ +function getList(html) { + let videos = []; + let items = pdfa(html, ".module-item,.module-card-item"); + items.forEach(it => { + let idMatch = it.match(/detail\/(\d+)/); + let nameMatch = it.match(/title="(.*?)"/) || it.match(/(.*?)<\/strong>/); + let picMatch = it.match(/data-original="(.*?)"/) || it.match(/src="(.*?)"/); + + if (idMatch && nameMatch) { + let pic = picMatch ? (picMatch[1] || picMatch[2]) : ""; + videos.push({ + "vod_id": idMatch[1], + "vod_name": nameMatch[1].replace(/<.*?>/g, ""), + "vod_pic": pic.startsWith('/') ? host + pic : pic, + "vod_remarks": (it.match(/module-item-note">(.*?)<\/div>/) || ["",""])[1].replace(/<.*?>/g, "") + }); + } + }); + return videos; +} + +async function home(filter) { + return JSON.stringify({ + "class": [ + {"type_id":"1","type_name":"电影"}, + {"type_id":"2","type_name":"剧集"}, + {"type_id":"3","type_name":"综艺"}, + {"type_id":"4","type_name":"动漫"} + ], + "filters": { + "1":[{"key":"class","name":"类型","value":[{"n":"全部","v":""},{"n":"动作片","v":"6"},{"n":"喜剧片","v":"7"},{"n":"爱情片","v":"8"},{"n":"科幻片","v":"9"},{"n":"恐怖片","v":"11"}]}], + "2":[{"key":"class","name":"类型","value":[{"n":"全部","v":""},{"n":"国产剧","v":"13"},{"n":"港台剧","v":"14"},{"n":"日剧","v":"15"},{"n":"韩剧","v":"33"},{"n":"欧美剧","v":"16"}]}], + "3":[{"key":"class","name":"类型","value":[{"n":"全部","v":""},{"n":"内地综艺","v":"27"},{"n":"港台综艺","v":"28"},{"n":"日本综艺","v":"29"},{"n":"韩国综艺","v":"36"}]}], + "4":[{"key":"class","name":"类型","value":[{"n":"全部","v":""},{"n":"国产动漫","v":"31"},{"n":"日本动漫","v":"32"},{"n":"欧美动漫","v":"42"},{"n":"其他动漫","v":"43"}]}] + } + }); +} + +async function homeVod() { + let resp = await req(host, { headers: headers }); + return JSON.stringify({ list: getList(resp.content) }); +} + +async function category(tid, pg, filter, extend) { + let p = pg || 1; + let targetId = (extend && extend.class) ? extend.class : tid; + let url = host + "/vodtype/" + targetId + "/" + (parseInt(p) > 1 ? "page/" + p + "/" : ""); + let resp = await req(url, { headers: headers }); + return JSON.stringify({ + "list": getList(resp.content), + "page": parseInt(p) + }); +} + +async function detail(id) { + let url = host + '/voddetail/' + id + '/'; + let resp = await req(url, { headers: headers }); + let html = resp.content; + + let playFrom = pdfa(html, ".module-tab-item").map(it => (it.match(/(.*?)<\/span>/) || ["","线路"])[1]).join('$$$'); + let playUrl = pdfa(html, ".module-play-list-content").map(list => + pdfa(list, "a").map(a => { + let n = (a.match(/(.*?)<\/span>/) || ["","播放"])[1]; + let v = a.match(/href="\/play\/(.*?)\/"/); + return n + '$' + (v ? v[1] : ""); + }).join('#') + ).join('$$$'); + + return JSON.stringify({ + list: [{ + 'vod_id': id, + 'vod_name': (html.match(/

(.*?)<\/h1>/) || ["", ""])[1], + 'vod_pic': (html.match(/data-original="(.*?)"/) || ["", ""])[1], + 'vod_content': (html.match(/introduction-content">.*?

(.*?)<\/p>/s) || ["", ""])[1].replace(/<.*?>/g, ""), + 'vod_play_from': playFrom, + 'vod_play_url': playUrl + }] + }); +} + +async function search(wd, quick, pg) { + let p = pg || 1; + let url = host + "/vodsearch/" + encodeURIComponent(wd) + "-------------/" + (parseInt(p) > 1 ? "page/" + p + "/" : ""); + let resp = await req(url, { headers: headers }); + return JSON.stringify({ list: getList(resp.content) }); +} + +async function play(flag, id, flags) { + let url = host + "/play/" + id + "/"; + let resp = await req(url, { headers: headers }); + let m3u8 = resp.content.match(/"url":"([^"]+\.m3u8)"/); + if (m3u8) return JSON.stringify({ parse: 0, url: m3u8[1].replace(/\\/g, ""), header: headers }); + return JSON.stringify({ parse: 1, url: url, header: headers }); +} + +export default { init, home, homeVod, category, detail, search, play }; diff --git a/js/海龟.js b/js/海龟.js new file mode 100755 index 00000000..2d4ba69b --- /dev/null +++ b/js/海龟.js @@ -0,0 +1,150 @@ +const host = 'https://www.haigui.tv'; +const headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", + "Referer": host + "/" +}; + +async function init(cfg) {} + +const m = (s, r, i = 1) => (s.match(r) || [])[i] || ""; +const fixPic = p => p && p.startsWith('/') ? host + p : p; + +function getList(html) { + return pdfa(html, ".module-item").map(it => { + let id = m(it, /href="\/video\/(.*?)\/"/); + let name = m(it, /title="(.*?)"/); + if (!id || !name) return null; + return { + vod_id: id, + vod_name: name, + vod_pic: fixPic(m(it, /data-original="(.*?)"/) || m(it, /src="(.*?)"/)), + vod_remarks: m(it, /module-item-note">(.*?)<\/div>/).replace(/<.*?>/g, "") + }; + }).filter(Boolean); +} + +async function home(filter) { + const classes = [ + ["dianying","电影"],["dianshiju","电视剧"],["zongyi","综艺"], + ["dongman","动漫"],["jilupian","纪录片"],["duanju","短剧"] + ].map(([id,name]) => ({type_id:id,type_name:name})); + + const dict = { + area: ["中国大陆","中国香港","中国台湾","美国","韩国","日本","泰国"], + year: ["2025","2024","2023","2022","2021","2020"], + lang: ["国语","英语","粤语","韩语","日语"] + }; + + const filters = {}; + classes.forEach(c => { + filters[c.type_id] = [ + { key:"class", name:"类型", value:getSubClasses(c.type_id) }, + ...Object.keys(dict).map(k => ({ + key:k, + name:{area:"地区",year:"年份",lang:"语言"}[k], + value:[{n:"全部",v:""}].concat(dict[k].map(v=>({n:v,v}))) + })) + ]; + }); + + return JSON.stringify({ class: classes, filters }); +} + +function getSubClasses(tid) { + const map = { + dianying: [ + ["dongzuopian","动作片"],["xijupian","喜剧片"], + ["aiqingpian","爱情片"],["kehuanpian","科幻片"],["kongbupian","恐怖片"] + ], + dianshiju: [ + ["guochanju","国产剧"],["gangtaiju","港台剧"], + ["rihanju","日韩剧"],["oumeiju","欧美剧"] + ] + }; + return [{n:"全部",v:""}].concat((map[tid]||[]).map(([v,n])=>({n,v}))); +} + +async function homeVod() { + const r = await req(host,{headers}); + return JSON.stringify({ list:getList(r.content) }); +} + +async function category(tid, pg, filter, extend={}) { + let p = pg || 1; + let id = extend.class || tid; + let url = `${host}/filter/${id}`; + ["area","lang","year"].forEach(k=>{ + if (extend[k]) url += `/${k}/${encodeURIComponent(extend[k])}`; + }); + url += `/page/${p}/`; + const r = await req(url,{headers}); + return JSON.stringify({ page:p, list:getList(r.content) }); +} + +async function detail(id) { + const r = await req(`${host}/video/${id}/`,{headers}); + const h = r.content; + + const playFrom = pdfa(h,".module-tab-item") + .map(it=>m(it,/(.*?)<\/span>/)||"播放源") + .join("$$$"); + + const playUrl = pdfa(h,".module-player-list").map(l => + pdfa(l,".module-blocklist a").map(it=>{ + let pid = m(it,/href=\"\/play\/(.*?)\/\"/); + if (!pid) return null; + return `${m(it,/(.*?)<\/span>/)||"正片"}$${pid}`; + }).filter(Boolean).join("#") + ).join("$$$"); + + return JSON.stringify({ + list:[{ + vod_id:id, + vod_name:m(h,/page-title\">(.*?)<\/h1>/), + vod_pic:fixPic(m(h,/video-cover.*?data-src=\"(.*?)\"/)||m(h,/video-cover.*?src=\"(.*?)\"/)), + type_name:( + m(h,/tag-link[^>]*>[\s\S]*?icon-cate-[^<]*<\/i>\s*([^<\n]+)/) || + m(h,/icon-cate-[^<]*<\/i>\s*([^<\n]+)/) + ).trim(), + vod_year:m(h,/year\/(\d+)\//), + vod_area:m(h,/area\/.*?\/ \">(.*?)\t/s).trim(), + vod_director:m(h,/导演:<\/span>.*?(.*?)<\/div>/s).replace(/<.*?>|\//g,"").trim(), + vod_actor:m(h,/主演:<\/span>.*?(.*?)<\/div>/s).replace(/<.*?>|\//g,"").trim(), + vod_remarks:m(h,/备注:<\/span>.*?(.*?)<\/div>/), + vod_content:m(h,/vod_content.*?(.*?)<\/span>/s)||"暂无简介", + vod_play_from:playFrom, + vod_play_url:playUrl + }] + }); +} + +async function search(wd, quick, pg=1) { + let url = `${host}/search/${encodeURIComponent(wd)}/${pg>1?`page/${pg}/`:""}`; + const r = await req(url,{headers}); + const h = r.content; + + const items = pdfa(h,".module-search-item") || pdfa(h,".module-item"); + + const list = items.map(it=>{ + let id = m(it,/\/video\/(.*?)\//); + let name = m(it,/title=\"(.*?)\"/) || m(it,/alt=\"(.*?)\"/); + if (!id || !name) return null; + return { + vod_id:id, + vod_name:name, + vod_pic:fixPic(m(it,/data-src=\"(.*?)\"/)||m(it,/data-original=\"(.*?)\"/)||m(it,/src=\"(.*?)\"/)), + vod_remarks:m(it,/module-item-note\">(.*?)<\/div>/)||m(it,/video-serial\">(.*?)<\/span>/) + }; + }).filter(Boolean); + + return JSON.stringify({ page:pg, list }); +} + +async function play(flag, id, flags) { + const r = await req(`${host}/play/${id}/`,{headers}); + let u = m(r.content,/"url":"([^"]+\.m3u8[^"]*)"/); + if (u) return JSON.stringify({ parse:0, url:u.replace(/\\/g,""), header:headers }); + return JSON.stringify({ parse:1, url:`${host}/play/${id}/`, header:headers }); +} + +export default { init, home, homeVod, category, detail, search, play }; \ No newline at end of file diff --git a/js/荐片.js b/js/荐片.js new file mode 100755 index 00000000..e29984c6 --- /dev/null +++ b/js/荐片.js @@ -0,0 +1,152 @@ +/** + * 荐片 新式 JS0 接口源 + * 适配 FongMi TVBox 最新规范 + */ + +let host = 'https://api.ztcgi.com'; +let UA = 'Mozilla/5.0 (Linux; Android 9; V2196A Build/PQ3A.190705.08211809; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/91.0.4472.114 Mobile Safari/537.36;webank/h5face;webank/1.0;netType:NETWORK_WIFI;appVersion:416;packageName:com.jp3.xg3'; +let imghost = ''; + +/** + * 初始化配置 + */ +async function init(cfg) { + try { + let res = await req(`${host}/api/appAuthConfig`, { headers: { 'User-Agent': UA } }); + let config = JSON.parse(res.content); + imghost = `https://${config.data.imgDomain}`; + } catch (e) { + imghost = 'https://img.jianpian.com'; + } +} + +/** + * 首页分类与筛选 + */ +async function home(filter) { + let classes = [ + {type_id: '1', type_name: '电影'}, + {type_id: '2', type_name: '电视剧'}, + {type_id: '3', type_name: '动漫'}, + {type_id: '4', type_name: '综艺'} + ]; + + // 筛选数据模板 + const filterItem = [ + {"key": "cateId", "name": "分类", "value": [{"v": "1", "n": "剧情"}, {"v": "2", "n": "爱情"}, {"v": "3", "n": "动画"}, {"v": "4", "n": "喜剧"}, {"v": "5", "n": "战争"}, {"v": "6", "n": "歌舞"}, {"v": "7", "n": "古装"}, {"v": "8", "n": "奇幻"}, {"v": "9", "n": "冒险"}, {"v": "10", "n": "动作"}, {"v": "11", "n": "科幻"}, {"v": "12", "n": "悬疑"}, {"v": "13", "n": "犯罪"}, {"v": "14", "n": "家庭"}, {"v": "15", "n": "传记"}, {"v": "16", "n": "运动"}, {"v": "18", "n": "惊悚"}, {"v": "20", "n": "短片"}, {"v": "21", "n": "历史"}, {"v": "22", "n": "音乐"}, {"v": "23", "n": "西部"}, {"v": "24", "n": "武侠"}, {"v": "25", "n": "恐怖"}]}, + {"key": "area", "name": "地區", "value": [{"v": "1", "n": "国产"}, {"v": "3", "n": "中国香港"}, {"v": "6", "n": "中国台湾"}, {"v": "5", "n": "美国"}, {"v": "18", "n": "韩国"}, {"v": "2", "n": "日本"}]}, + {"key": "year", "name": "年代", "value": [{"v": "107", "n": "2025"}, {"v": "119", "n": "2024"}, {"v": "153", "n": "2023"}, {"v": "101", "n": "2022"}, {"v": "118", "n": "2021"}, {"v": "16", "n": "2020"}, {"v": "7", "n": "2019"}, {"v": "22", "n": "2016"}, {"v": "2015", "n": "2015以前"}]}, + {"key": "sort", "name": "排序", "value": [{"v": "update", "n": "最新"}, {"v": "hot", "n": "最热"}, {"v": "rating", "n": "评分"}]} + ]; + + let filterObj = {"1": filterItem, "2": filterItem, "3": filterItem, "4": filterItem}; + + return JSON.stringify({ + class: classes, + filters: filterObj + }); +} + +/** + * 首页推荐 + */ +async function homeVod() { + let html = await req(`${host}/api/slide/list?pos_id=88`, { headers: { 'User-Agent': UA, 'Referer': host } }); + let res = JSON.parse(html.content); + let videos = res.data.map(item => ({ + vod_id: item.jump_id, + vod_name: item.title, + vod_pic: item.thumbnail.includes('http') ? item.thumbnail : `${imghost}${item.thumbnail}`, + vod_remarks: "" + })); + return JSON.stringify({ list: videos }); +} + +/** + * 分类列表 + */ +async function category(tid, pg, filter, extend) { + let url = `${host}/api/crumb/list?fcate_pid=${tid}&category_id=&area=${extend.area || ''}&year=${extend.year || ''}&type=${extend.cateId || ''}&sort=${extend.sort || ''}&page=${pg}`; + let html = await req(url, { headers: { 'User-Agent': UA, 'Referer': host } }); + let res = JSON.parse(html.content); + let videos = res.data.map(item => ({ + vod_id: item.id, + vod_name: item.title, + vod_pic: item.path.includes('http') ? item.path : `${imghost}${item.path}`, + vod_remarks: item.mask + })); + return JSON.stringify({ + page: pg, + list: videos + }); +} + +/** + * 详情页 + */ +async function detail(id) { + let html = await req(`${host}/api/video/detailv2?id=${id}`, { headers: { 'User-Agent': UA, 'Referer': host } }); + let data = JSON.parse(html.content).data; + + // 线路处理:将“常规线路”显示为“边下边播” + let play_from = data.source_list_source.map(item => item.name).join('$$$').replace(/常规线路/g, '边下边播'); + let play_url = data.source_list_source.map(play => + play.source_list.map(({source_name, url}) => `${source_name}$${url}`).join('#') + ).join('$$$'); + + let vod = { + vod_id: data.id, + vod_name: data.title, + vod_year: data.year, + vod_area: data.area, + vod_remarks: data.mask, + vod_content: data.description, + vod_play_from: play_from, + vod_play_url: play_url, + vod_pic: data.thumbnail.includes('http') ? data.thumbnail : `${imghost}${data.thumbnail}` + }; + + return JSON.stringify({ list: [vod] }); +} + +/** + * 搜索功能 + */ +async function search(wd, quick) { + let url = `${host}/api/v2/search/videoV2?key=${encodeURIComponent(wd)}&category_id=88&page=1&pageSize=20`; + let html = await req(url, { headers: { 'User-Agent': UA, 'Referer': host } }); + let res = JSON.parse(html.content); + let videos = res.data.map(item => ({ + vod_id: item.id, + vod_name: item.title, + vod_pic: item.thumbnail.includes('http') ? item.thumbnail : `${imghost}${item.thumbnail}`, + vod_remarks: item.mask + })); + return JSON.stringify({ list: videos }); +} + +/** + * 播放解析 + */ +async function play(flag, id, flags) { + let playUrl = id; + // 判断是否需要添加专用协议前缀 + if (!id.includes(".m3u8") && !id.includes(".mp4")) { + playUrl = `tvbox-xg:${id}`; + } + return JSON.stringify({ + parse: 0, + url: playUrl + }); +} + +// 导出标准接口对象 +export default { + init, + home, + homeVod, + category, + detail, + search, + play +}; diff --git a/js/鬼片之家.js b/js/鬼片之家.js new file mode 100755 index 00000000..47188833 --- /dev/null +++ b/js/鬼片之家.js @@ -0,0 +1,115 @@ +let host = 'https://www.guipian360.com'; +let headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", + "Referer": host + "/" +}; + +const extractVideos = html => { + if (!html) return []; + return pdfa(html, '.m-movies .u-movie').map(it => { + const url = it.match(/href="([^"]+)"/)?.[1] || ""; + const id = url.match(/\/(\d+)\.html/)?.[1]; + let name = it.match(/title="([^"]+)"/)?.[1] || ""; + name = name.split('/')[0].trim().replace(/.*《([^》]+)》.*/, "$1"); + const pic = it.match(/src="([^"]+)"/)?.[1] || ""; + const remarks = it.match(/class="zhuangtai"[^>]*>([^<]+)/)?.[1] || ""; + return id && name ? { + vod_id: id, + vod_name: name, + vod_pic: pic.startsWith('/') ? host + pic : pic, + vod_remarks: remarks.trim() + } : null; + }).filter(Boolean); +}; + +const home = async () => { + const list = extractVideos((await req(host, { headers }))?.content || ''); + return JSON.stringify({ + class: [ + {type_id:"1",type_name:"鬼片大全"},{type_id:"6",type_name:"大陆鬼片"},{type_id:"9",type_name:"港台鬼片"}, + {type_id:"8",type_name:"林正英鬼片"},{type_id:"7",type_name:"日韩鬼片"},{type_id:"11",type_name:"欧美鬼片"}, + {type_id:"10",type_name:"泰国鬼片"},{type_id:"3",type_name:"恐怖片"},{type_id:"2",type_name:"电视剧"}, + {type_id:"12",type_name:"国产剧"},{type_id:"20",type_name:"港台剧"},{type_id:"13",type_name:"美剧"}, + {type_id:"14",type_name:"韩剧"},{type_id:"15",type_name:"日剧"},{type_id:"16",type_name:"泰剧"}, + {type_id:"22",type_name:"其它剧"},{type_id:"4",type_name:"动漫"} + ], + filters: {}, + list + }); +}; + +const homeVod = async () => { + const list = extractVideos((await req(host, { headers }))?.content || ''); + return JSON.stringify({ list }); +}; + +const category = async (tid, pg, _, extend) => { + const url = pg > 1 ? `${host}/list/${tid}_${pg}.html` : `${host}/list/${tid}.html`; + const html = (await req(url, { headers }))?.content || ''; + const list = extractVideos(html); + const m = html.match(/href="\/list\/\d+_(\d+)\.html"[^>]*>\.\.(\d+)<\/a>/); + return JSON.stringify({ list, page: +pg, pagecount: m ? +m[1] : 999, limit: 20 }); +}; + +const detail = async id => { + const html = (await req(`${host}/nv/${id}.html`, { headers }))?.content || ''; + if (!html) return JSON.stringify({ list: [] }); + + const tabs = pdfa(html, '#tv_tab li a').map(a => (a.match(/>([^<]+)/)?.[1] || "线路").trim()); + const urls = pdfa(html, '#tv_tab .list').map(list => + pdfa(list, 'ul.abc li a').map(a => { + const name = (a.match(/>([^<]+)/)?.[1] || "播放").trim(); + const vid = (a.match(/href="\/play\/(.*?)\.html"/)?.[1] || ""); + return `${name}$${vid}`; + }).join('#') + ); + + if (!tabs.length || !urls.length) return JSON.stringify({ list: [] }); + + let vod_name = (html.match(/

(.*?)<\/h1>/)?.[1] || "").split('/')[0].trim().replace(/.*《([^》]+)】?.*/, "$1"); + const m = s => (html.match(s) || ["", ""])[1].trim(); + const vod_pic = m(/]+src="([^"]+)"[^>]*class="lazy"/); + const vod_content = (html.match(/

]*>([\s\S]*?)<\/p>/)?.[1] || "").replace(/<.*?>/g, "").trim() || "暂无简介"; + + return JSON.stringify({ + list: [{ + vod_id: id, + vod_name, + vod_pic: vod_pic.startsWith('/') ? host + vod_pic : vod_pic, + vod_content, + vod_director: m(/