From 3ec10f3aefa4d54e93b5e35bb9d2baacc315972d Mon Sep 17 00:00:00 2001 From: n3rddd <119649988+n3rddd@users.noreply.github.com> Date: Thu, 27 Jun 2024 22:23:27 +0800 Subject: [PATCH] Add files via upload --- JN/dr_py/js/直播转点播[合].js | 419 ++++++++++++++++++++++++++++++++++ 1 file changed, 419 insertions(+) create mode 100644 JN/dr_py/js/直播转点播[合].js diff --git a/JN/dr_py/js/直播转点播[合].js b/JN/dr_py/js/直播转点播[合].js new file mode 100644 index 00000000..607c0a90 --- /dev/null +++ b/JN/dr_py/js/直播转点播[合].js @@ -0,0 +1,419 @@ +/** + * 支持本地包直播链接 + * 传参 ?type=url¶ms=../json/live2cms.json + live2cms.json + 支持m3u类直播,支持线路归并。支持筛选切换显示模式 + [ + { + "name": "GitHub", + "url": "https://ghproxy.net/https://raw.githubusercontent.com/ssili126/tv/main/itvlist.txt" + }, + { + "name": "CNTV", + "url": "./live_cntv.txt" + } + ] + */ + +/** + * m3u直播格式转一般直播格式 + * @param m3u + * @returns {string} + */ +function convertM3uToNormal(m3u) { + try { + const lines = m3u.split('\n'); + let result = ''; + let TV = ''; + // let flag='#genre#'; + let flag = '#m3u#'; + let currentGroupTitle = ''; + lines.forEach((line) => { + if (line.startsWith('#EXTINF:')) { + const groupTitle = line.split('"')[1].trim(); + TV = line.split('"')[2].substring(1); + if (currentGroupTitle !== groupTitle) { + currentGroupTitle = groupTitle; + result += `\n${currentGroupTitle},${flag}\n`; + } + } else if (line.startsWith('http')) { + const splitLine = line.split(','); + result += `${TV}\,${splitLine[0]}\n`; + } + }); + return result.trim() + } catch (e) { + log(`m3u直播转普通直播发生错误:${e.message}`); + return m3u + } +} + +/** + * 线路归类/小棉袄算法 + * @param arr 数组 + * @param parse 解析式 + * @returns {[[*]]} + */ +function splitArray(arr, parse) { + parse = parse && typeof (parse) == 'function' ? parse : ''; + let result = [[arr[0]]]; + for (let i = 1; i < arr.length; i++) { + let index = -1; + for (let j = 0; j < result.length; j++) { + if (parse && result[j].map(parse).includes(parse(arr[i]))) { + index = j; + } else if ((!parse) && result[j].includes(arr[i])) { + index = j; + } + } + if (index >= result.length - 1) { + result.push([]); + result[result.length - 1].push(arr[i]); + } else { + result[index + 1].push(arr[i]); + } + } + return result; +} + +/** + * 搜索结果生成分组字典 + * @param arr + * @param parse x=>x.split(',')[0] + * @returns {{}} + */ +function gen_group_dict(arr, parse) { + let dict = {}; + arr.forEach((it) => { + let k = it.split(',')[0]; + if (parse && typeof (parse) === 'function') { + k = parse(k); + } + if (!dict[k]) { + dict[k] = [it]; + } else { + dict[k].push(it); + } + }); + return dict +} + +globalThis.convertM3uToNormal = convertM3uToNormal; +globalThis.splitArray = splitArray; +globalThis.gen_group_dict = gen_group_dict; +globalThis.getRandomItem = function (items) {//从列表随机取出一个元素 + return items[Math.random() * items.length | 0]; +} +globalThis.__ext = {data_dict: {}}; +var rule = { + title: '直播转点播[合]', + author: '道长', + version: '20240627 beta3', + update_info: ` +20240627 beta1: +1.将原drpy项目的live2cms.js转换成hipy传参源。 +【特别说明】支持m3u和txt的直播 +`, + host: '', + homeUrl: '', + searchUrl: '#wd=**&pg=#TruePage##page=fypage', + url: 'fyclass#pg=fypage&t=fyfilter', + filter_url: '{{fl.show}}', + headers: {'User-Agent': 'MOBILE_UA'}, + timeout: 5000, // class_name: '电影&电视剧&综艺&动漫', + limit: 20, + search_limit: 5, // 搜索限制取前5个,可以注释掉,就不限制搜索 + searchable: 1,//是否启用全局搜索, + quickSearch: 0,//是否启用快速搜索, + filterable: 1,//是否启用分类筛选, + play_parse: true, + // params: 'http://127.0.0.1:5707/files/json/live2cms.json', + // 下面自定义一些源的配置 + def_pic: 'https://avatars.githubusercontent.com/u/97389433?s=120&v=4', //默认列表图片 + showMode: 'groups',// groups按组分类显示 all全部一条线路展示 + groupDict: {},// 搜索分组字典 + tips: '', //二级提示信息 + 预处理: $js.toString(() => { + // 初始化保存的数据 + rule.showMode = getItem('showMode', 'groups'); + rule.groupDict = JSON.parse(getItem('groupDict', '{}')); + rule.tips = `道长直播转点播js-当前版本${rule.version}`; + + if (typeof (batchFetch) === 'function') { + // 支持批量请求直接放飞自我。搜索限制最大线程数量16 + rule.search_limit = 16; + log('当前程序支持批量请求[batchFetch],搜索限制已设置为16'); + } + let _url = rule.params; + if (_url && typeof (_url) === 'string' && /^(http|file)/.test(_url)) { + let html = request(_url); + let json = JSON.parse(html); + + let _classes = []; + rule.filter = {}; + rule.filter_def = {}; + json.forEach(it => { + if (it.url && !/^(http|file)/.test(it.url)) { + it.url = urljoin(_url, it.url); + } + let _obj = { + type_name: it.name, + type_id: it.url, + img: it.img, + }; + _classes.push(_obj); + let json1 = [{'n': '多线路分组', 'v': 'groups'}, {'n': '单线路', 'v': 'all'}]; + try { + rule.filter[_obj.type_id] = [ + {'key': 'show', 'name': '播放展示', 'value': json1} + ]; + if (json1.length > 0) { + rule.filter_def[it.url] = {"show": json1[0].v}; + } + } catch (e) { + rule.filter[it.url] = json1 + } + }); + __ext.data = json; + rule.classes = _classes; + } + }), + class_parse: $js.toString(() => { + input = rule.classes; + }), + 推荐: $js.toString(() => { + let update_info = [{ + vod_name: '更新日志', + vod_id: 'update_info', + vod_remarks: `版本:${rule.version}`, + vod_pic: 'https://ghproxy.net/https://raw.githubusercontent.com/hjdhnx/hipy-server/master/app/static/img/logo.png' + }]; + VODS = []; + if (rule.classes) { + let randomClass = getRandomItem(rule.classes); + let _get_url = randomClass.type_id; + // let current_vod = rule.classes.find(item => item.type_id === _get_url); + // let _pic = current_vod ? current_vod.img : ''; + let _pic = randomClass.img; + let html; + if (__ext.data_dict[_get_url]) { + html = __ext.data_dict[_get_url]; + } else { + html = request(_get_url); + if (/#EXTM3U/.test(html)) { + html = convertM3uToNormal(html); + } + __ext.data_dict[_get_url] = html; + } + let arr = html.match(/.*?[,,]#[\s\S].*?#/g); // 可能存在中文逗号 + try { + arr.forEach(it => { + let vname = it.split(/[,,]/)[0]; + let vtab = it.match(/#(.*?)#/)[0]; + VODS.push({ + vod_name: vname, + vod_id: _get_url + '$' + vname, + vod_pic: _pic || rule.def_pic, + vod_remarks: vtab, + }); + }); + } catch (e) { + log(`直播转点播获取首页推荐发送错误:${e.message}`); + } + } + VODS = update_info.concat(VODS); + }), + 一级: $js.toString(() => { + VODS = []; + // 一级限制页数不允许翻页 + if (rule.classes && MY_PAGE <= 1) { + if (MY_FL.show) { + rule.showMode = MY_FL.show; + setItem('showMode', rule.showMode); + } + let _get_url = input.split('#')[0]; + let current_vod = rule.classes.find(item => item.type_id === MY_CATE); + let _pic = current_vod ? current_vod.img : ''; + let html; + if (__ext.data_dict[_get_url]) { + html = __ext.data_dict[_get_url]; + } else { + html = request(_get_url); + if (/#EXTM3U/.test(html)) { + html = convertM3uToNormal(html); + } + __ext.data_dict[_get_url] = html; + } + let arr = html.match(/.*?[,,]#[\s\S].*?#/g); // 可能存在中文逗号 + try { + arr.forEach(it => { + let vname = it.split(/[,,]/)[0]; + let vtab = it.match(/#(.*?)#/)[0]; + VODS.push({ + // vod_name:it.split(',')[0], + vod_name: vname, + vod_id: _get_url + '$' + vname, + vod_pic: _pic || rule.def_pic, + vod_remarks: vtab, + }); + }); + } catch (e) { + log(`直播转点播获取一级分类页发生错误:${e.message}`); + } + } + }), + 二级: $js.toString(() => { + VOD = {}; + if (orId === 'update_info') { + VOD = { + vod_content: rule.update_info.trim(), + vod_name: '更新日志', + type_name: '更新日志', + vod_pic: 'https://resource-cdn.tuxiaobei.com/video/FtWhs2mewX_7nEuE51_k6zvg6awl.png', + vod_remarks: `版本:${rule.version}`, + vod_play_from: '道长在线', + // vod_play_url: '嗅探播放$https://resource-cdn.tuxiaobei.com/video/10/8f/108fc9d1ac3f69d29a738cdc097c9018.mp4', + vod_play_url: '随机小视频$http://api.yujn.cn/api/zzxjj.php', + }; + } else { + if (rule.classes) { + let _get_url = orId.split('$')[0]; + let _tab = orId.split('$')[1]; + if (orId.includes('#search#')) { + let vod_name = _tab.replace('#search#', ''); + let vod_play_from = '来自搜索'; + vod_play_from += `:${_get_url}`; + let vod_play_url = rule.groupDict[_get_url].map(x => x.replace(',', '$')).join('#'); + log(orId); + VOD = { + vod_name: '搜索:' + vod_name, + type_name: "直播列表", + vod_pic: rule.def_pic, + // vod_content: orId, + vod_content: orId.replace(getHome(orId), 'http://***'), + vod_play_from: vod_play_from, + vod_play_url: vod_play_url, + vod_director: rule.tips, + vod_remarks: rule.tips, + } + } else { + let current_vod = rule.classes.find(item => item.type_id === _get_url); + let _pic = current_vod ? current_vod.img : ''; + let html; + if (__ext.data_dict[_get_url]) { + html = __ext.data_dict[_get_url]; + } else { + html = request(_get_url); + if (/#EXTM3U/.test(html)) { + html = convertM3uToNormal(html); + } + __ext.data_dict[_get_url] = html; + } + let a = new RegExp(`.*?${_tab.replace('(','\\(').replace(')','\\)')}[,,]#[\\s\\S].*?#`); + let b = html.match(a)[0]; + let c = html.split(b)[1]; + if (c.match(/.*?[,,]#[\s\S].*?#/)) { + let d = c.match(/.*?[,,]#[\s\S].*?#/)[0]; + c = c.split(d)[0]; + } + let arr = c.trim().split('\n'); + let _list = []; + arr.forEach((it) => { + if (it.trim()) { + let t = it.trim().split(',')[0]; + let u = it.trim().split(',')[1]; + _list.push(t + '$' + u); + } + }); + + let vod_name = __ext.data.find(x => x.url === _get_url).name; + let vod_play_url; + let vod_play_from; + + if (rule.showMode === 'groups') { + let groups = splitArray(_list, x => x.split('$')[0]); + let tabs = []; + for (let i = 0; i < groups.length; i++) { + if (i === 0) { + tabs.push(vod_name + '1'); + } else { + tabs.push(` ${i + 1} `); + } + } + vod_play_url = groups.map(it => it.join('#')).join('$$$'); + vod_play_from = tabs.join('$$$'); + } else { + vod_play_url = _list.join('#'); + vod_play_from = vod_name; + } + log(orId); + VOD = { + vod_id: orId, + vod_name: vod_name + '|' + _tab, + type_name: "直播列表", + vod_pic: _pic || rule.def_pic, + // vod_content: orId, + vod_content: orId.replace(getHome(orId), 'http://***'), + vod_play_from: vod_play_from, + vod_play_url: vod_play_url, + vod_director: rule.tips, + vod_remarks: rule.tips, + }; + + } + } + } + }), + 搜索: $js.toString(() => { + VODS = []; + if (rule.classes && MY_PAGE <= 1) { + let _get_url = __ext.data[0].url; + let current_vod = rule.classes.find(item => item.type_id === _get_url); + let _pic = current_vod ? current_vod.img : ''; + let html; + if (__ext.data_dict[_get_url]) { + html = __ext.data_dict[_get_url]; + } else { + html = request(_get_url); + if (/#EXTM3U/.test(html)) { + html = convertM3uToNormal(html); + } + __ext.data_dict[_get_url] = html; + } + let str = ''; + Object.keys(__ext.data_dict).forEach(() => { + str += __ext.data_dict[_get_url]; + }); + let links = str.split('\n').filter(it => it.trim() && it.includes(',') && it.split(',')[1].trim().startsWith('http')); + links = links.map(it => it.trim()); + let plays = Array.from(new Set(links)); + log('搜索关键词:' + KEY); + log('过滤前:' + plays.length); + // plays = plays.filter(it => it.includes(KEY)); + plays = plays.filter(it => new RegExp(KEY, 'i').test(it)); + log('过滤后:' + plays.length); + log(plays); + let new_group = gen_group_dict(plays); + rule.groupDict = Object.assign(rule.groupDict, new_group); + // 搜索分组结果存至本地方便二级调用 + setItem('groupDict', JSON.stringify(rule.groupDict)); + // 返回的还是搜索的new_group + Object.keys(new_group).forEach((it) => { + VODS.push({ + 'vod_name': it, + 'vod_id': it + '$' + KEY + '#search#', + 'vod_pic': _pic || rule.def_pic, + }); + }); + } + }), + lazy: $js.toString(() => { + if (/\.(m3u8|mp4)/.test(input)) { + input = {parse: 0, url: input} + } else if (/yangshipin/.test(input)) { + input = {parse: 1, url: input, js: '', header: {'User-Agent': PC_UA}, parse_extra: '&is_pc=1'}; + } else { + input + } + }), +} \ No newline at end of file