WYY/lib/直转点.js
2024-10-02 08:30:42 -05:00

467 lines
18 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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:')) {
line = line.replace(/'/g, '"');
let groupTitle = '未知频道';
let tvg_name = '';
let tvg_logo = '';
try {
groupTitle = line.match(/group-title="(.*?)"/)[1].trim();
} catch (e) {
}
try {
tvg_name = line.match(/tvg-name="(.*?)"/)[1].trim();
} catch (e) {
}
try {
tvg_logo = line.match(/tvg-logo="(.*?)"/)[1].trim();
} catch (e) {
}
TV = line.split(',').slice(-1)[0].trim();
if (currentGroupTitle !== groupTitle) {
currentGroupTitle = groupTitle;
let ret_list = [currentGroupTitle, flag];
// if(tvg_name){
// ret_list.push(tvg_name);
// }
// if(tvg_logo){
// ret_list.push(tvg_logo);
// }
result += `\n${ret_list.join(",")}\n`;
}
} else if (line.startsWith('http')) {
const splitLine = line.split(',');
result += `${TV}\,${splitLine[0]}\n`;
}
});
// result = result.trim();
result = mergeChannels(result);
// log(result);
return result
} 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
}
/**
* txt格式直播自动合并频道链接
* @param text
* @returns {string}
*/
function mergeChannels(text) {
const lines = text.split('\n');
const channelMap = new Map();
let currentChannel = ''; // 当前处理的频道
lines.forEach(line => {
// 使用正则表达式匹配频道行,假设频道行包含",#"即可识别为频道行
if (/,#/.test(line)) {
// 如果是频道名称,作为键值存储,初始化为空数组
currentChannel = line;
if (!channelMap.has(line)) {
channelMap.set(line, []);
}
} else if (line) { // 忽略空行
// 将当前行(链接)添加到当前频道数组中
if (currentChannel) {
channelMap.get(currentChannel).push(line);
}
}
});
// 构建结果字符串
let result = '';
channelMap.forEach((value, key) => {
result += key + '\n' + value.join('\n') + '\n\n';
});
return result.trim(); // 移除尾部的多余换行符
}
globalThis.mergeChannels = mergeChannels;
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: '20240628 beta6',
update_info: `
`,
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', //默认列表图片
def_pic: 'https://gitee.com/wyygj/gjbox/raw/master/ox/1.png', //默认列表图片
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://gitee.com/wyygj/gjbox/raw/master/ox/2.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);
} else {
html = mergeChannels(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);
} else {
html = mergeChannels(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://gitee.com/wyygj/gjbox/raw/master/ox/1.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);
} else {
html = mergeChannels(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);
} else {
html = mergeChannels(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)) {
if (input.includes('?') && typeof (playObj) == 'object' && playObj.url) {
input = playObj.url;
}
input = {parse: 0, url: input}
} else if (/yangshipin|1905\.com/.test(input)) {
input = {parse: 1, jx: 0, url: input, js: '', header: {'User-Agent': PC_UA}, parse_extra: '&is_pc=1'};
} else {
input
}
}),
}