495 lines
16 KiB
JavaScript
495 lines
16 KiB
JavaScript
var globalHeaders = [];
|
|
var classes = [];
|
|
var categories = {}; // 修复:正确使用categories存储分类数据
|
|
var picUrl = 'http://hufa.zabc.net:8080/lib/dream.jpg';
|
|
var webPaths = {};
|
|
var sourceHeaders = {};
|
|
var sourceConfigs = {}; // 修复:统一使用sourceConfigs变量名
|
|
var vodSourceConfigs = {}; // 修复:复数形式,与引用处保持一致
|
|
var globalHostHeaders = {};
|
|
var processedHosts = new Set();
|
|
|
|
function initGlobalHeaders() {
|
|
for (var i = 0; i < globalHeaders.length; i++) {
|
|
var config = globalHeaders[i];
|
|
if (config.host && config.header) globalHostHeaders[config.host] = config.header;
|
|
}
|
|
}
|
|
|
|
function extractHostFromUrl(url) {
|
|
try {
|
|
var urlObj = new URL(url);
|
|
return urlObj.host;
|
|
} catch (error) {
|
|
var match = url.match(/:\/\/([^\/]+)/);
|
|
return match ? match[1] : null;
|
|
}
|
|
}
|
|
|
|
function addToGlobalHeaders(host, headers) {
|
|
if (!host || !headers || Object.keys(headers).length === 0) return;
|
|
var exists = false;
|
|
for (var i = 0; i < globalHeaders.length; i++) {
|
|
if (globalHeaders[i].host === host) {
|
|
exists = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!exists) {
|
|
globalHeaders.push({ "host": host, "header": headers });
|
|
globalHostHeaders[host] = headers;
|
|
}
|
|
}
|
|
|
|
function getHeadersForUrl(url) {
|
|
var resultHeaders = {};
|
|
try {
|
|
var urlObj = new URL(url);
|
|
var host = urlObj.host;
|
|
if (globalHostHeaders[host]) {
|
|
resultHeaders = Object.assign({}, globalHostHeaders[host]);
|
|
} else {
|
|
for (var pattern in globalHostHeaders) {
|
|
if (pattern.includes('*') && url.includes(pattern.replace(':*', ''))) {
|
|
resultHeaders = Object.assign({}, globalHostHeaders[pattern]);
|
|
break;
|
|
} else if (url.includes(pattern)) {
|
|
resultHeaders = Object.assign({}, globalHostHeaders[pattern]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} catch (error) {
|
|
for (var pattern in globalHostHeaders) {
|
|
if (url.includes(pattern.replace(':*', ''))) {
|
|
resultHeaders = Object.assign({}, globalHostHeaders[pattern]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return resultHeaders;
|
|
}
|
|
|
|
function getPlayHeaders(playUrl, sourceConfig) {
|
|
var playHeaders = getHeadersForUrl(playUrl);
|
|
var hasGlobalHeaders = Object.keys(playHeaders).length > 0;
|
|
if (!hasGlobalHeaders && sourceConfig && sourceConfig.headers && Object.keys(sourceConfig.headers).length > 0) {
|
|
playHeaders = Object.assign({}, playHeaders, sourceConfig.headers);
|
|
}
|
|
return playHeaders;
|
|
}
|
|
|
|
function init(inputData) {
|
|
initGlobalHeaders();
|
|
try {
|
|
var parsedData = JSON.parse(inputData.trim());
|
|
var configs = extractLivesConfig(parsedData);
|
|
if (configs && configs.length > 0) {
|
|
for (var i = 0; i < configs.length; i++) {
|
|
var config = normalizeConfig(configs[i]);
|
|
if (config.host) continue;
|
|
var typeId = config.name + '$' + config.url;
|
|
var typeName = config.name;
|
|
classes.push({ 'type_id': typeId, 'type_name': typeName });
|
|
if (config.headers && Object.keys(config.headers).length > 0) {
|
|
sourceHeaders[typeId] = config.headers;
|
|
sourceConfigs[typeId] = { headers: config.headers, play_parse: config.play_parse || false };
|
|
if (config.url) {
|
|
var hostFromUrl = extractHostFromUrl(config.url);
|
|
if (hostFromUrl && !processedHosts.has(hostFromUrl)) {
|
|
addToGlobalHeaders(hostFromUrl, config.headers);
|
|
processedHosts.add(hostFromUrl);
|
|
}
|
|
}
|
|
} else {
|
|
sourceConfigs[typeId] = { headers: {}, play_parse: config.play_parse || false };
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
} catch (error) {
|
|
console.error('JSON解析错误:', error);
|
|
}
|
|
var baseUrl = '';
|
|
if (inputData.indexOf('$$$') > 0) {
|
|
baseUrl = inputData.split('$$$')[0].trim();
|
|
inputData = inputData.split('$$$')[1].trim();
|
|
}
|
|
if (inputData.indexOf('&&&') > 0) {
|
|
inputData = inputData.split('&&&')[0].trim();
|
|
}
|
|
var parts = inputData.split('#');
|
|
for (var j = 0; j < parts.length; j++) {
|
|
var part = parts[j];
|
|
if (part.indexOf('$') > 0) {
|
|
var typeId = part;
|
|
var typeName = part.split('$')[0];
|
|
if (typeId.indexOf('://') < 0) {
|
|
typeId = typeId.replace('$', '$' + baseUrl);
|
|
}
|
|
classes.push({ 'type_id': typeId, 'type_name': typeName.replace('!!', '') });
|
|
} else {
|
|
var url = part;
|
|
if (url.indexOf('://') < 0) {
|
|
url = baseUrl + url;
|
|
}
|
|
var requestHeaders = getHeadersForUrl(url);
|
|
try {
|
|
var response = req(url, { 'method': 'GET', 'headers': requestHeaders });
|
|
var data = JSON.parse(response.content);
|
|
var pathPrefix = url.substring(0, url.lastIndexOf('/') + 1);
|
|
for (var k = 0; k < data.length; k++) {
|
|
var item = data[k];
|
|
var itemName = item.name;
|
|
var itemUrl = item.url;
|
|
var itemTypeId = itemName + '$' + (itemUrl.indexOf('://') < 0 ? pathPrefix : '') + itemUrl;
|
|
classes.push({ 'type_id': itemTypeId, 'type_name': itemName.replace('!!', '') });
|
|
var normalizedItem = normalizeConfig(item);
|
|
if (normalizedItem.headers && Object.keys(normalizedItem.headers).length > 0) {
|
|
sourceHeaders[itemTypeId] = normalizedItem.headers;
|
|
sourceConfigs[itemTypeId] = normalizedItem;
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('加载二级数据错误:', error);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function extractLivesConfig(parsedData) {
|
|
if (Array.isArray(parsedData)) {
|
|
return parsedData;
|
|
}
|
|
if (typeof parsedData === 'object' && parsedData !== null) {
|
|
var livesKey = Object.keys(parsedData).find(key => key.toLowerCase() === 'lives');
|
|
if (livesKey && Array.isArray(parsedData[livesKey])) {
|
|
return parsedData[livesKey];
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function normalizeConfig(config) {
|
|
var normalized = Object.assign({}, config);
|
|
if (config.ua && typeof config.ua === 'string') {
|
|
if (!normalized.headers) {
|
|
normalized.headers = {};
|
|
}
|
|
normalized.headers['User-Agent'] = config.ua;
|
|
delete normalized.ua;
|
|
}
|
|
if (config.header && typeof config.header === 'object') {
|
|
if (!normalized.headers) {
|
|
normalized.headers = {};
|
|
}
|
|
Object.assign(normalized.headers, config.header);
|
|
delete normalized.header;
|
|
}
|
|
/* 保留 img 供后面取 */
|
|
if (config.img) normalized.img = config.img;
|
|
return normalized;
|
|
}
|
|
|
|
function home(param) {
|
|
return JSON.stringify({
|
|
'class': classes,
|
|
'filters': null
|
|
});
|
|
}
|
|
|
|
function homeVod(param) {
|
|
if (classes.length === 0) return JSON.stringify({ 'list': [] });
|
|
var data = getCategoryData(classes[0].type_id);
|
|
return JSON.stringify({
|
|
'list': data
|
|
});
|
|
}
|
|
|
|
function category(categoryId, page, filter, extend) {
|
|
var data = [];
|
|
if (page == 1) {
|
|
data = getCategoryData(categoryId);
|
|
}
|
|
return JSON.stringify({
|
|
'list': data
|
|
});
|
|
}
|
|
|
|
function getCategoryData(categoryId) {
|
|
var urlPart = categoryId.split('$')[1];
|
|
var namePart = categoryId.split('$')[0];
|
|
|
|
if (!categories[categoryId]) {
|
|
categories[categoryId] = [];
|
|
|
|
var requestHeaders = getHeadersForUrl(urlPart);
|
|
if (sourceHeaders[categoryId] && Object.keys(sourceHeaders[categoryId]).length > 0) {
|
|
requestHeaders = Object.assign({}, requestHeaders, sourceHeaders[categoryId]);
|
|
}
|
|
|
|
if (urlPart.indexOf('|') > 0) {
|
|
var paramsStr = decodeURIComponent(urlPart.split('|')[1]);
|
|
urlPart = urlPart.split('|')[0];
|
|
var params = paramsStr.split('&');
|
|
for (var l = 0; l < params.length; l++) {
|
|
var param = params[l];
|
|
if (param.indexOf('=') > 0) {
|
|
var key = param.split('=')[0];
|
|
var value = param.split('=')[1];
|
|
requestHeaders[key] = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
try {
|
|
var response = req(urlPart, {
|
|
'method': 'GET',
|
|
'headers': requestHeaders
|
|
});
|
|
var responseContent = response.content.trim();
|
|
var parsedContent = '';
|
|
|
|
if (responseContent.indexOf('#EXTM3U') >= 0) {
|
|
parsedContent = parseM3u(responseContent, namePart);
|
|
} else if (responseContent.indexOf('"channel"') > 0 && responseContent.indexOf('"urls"') > 0) {
|
|
parsedContent = parseFm(responseContent);
|
|
} else if (responseContent.indexOf('"datalist"') > 0 && responseContent.indexOf('"urls"') > 0) {
|
|
parsedContent = parseList(responseContent);
|
|
} else {
|
|
parsedContent = responseContent;
|
|
}
|
|
|
|
parseContent(parsedContent, categoryId, namePart, picUrl);
|
|
|
|
} catch (error) {
|
|
console.error('获取分类数据失败:', error, '分类ID:', categoryId);
|
|
}
|
|
}
|
|
|
|
return categories[categoryId];
|
|
}
|
|
|
|
function parseContent(content, categoryId, typeName, imageUrl) {
|
|
var lines = (typeName + '\n' + content.replace('\r', '')).split('\n');
|
|
var currentCategory = typeName;
|
|
var currentUrls = '';
|
|
|
|
for (var m = 0; m < lines.length; m++) {
|
|
var line = lines[m].replace(/\s+/g, '');
|
|
|
|
if (line != '' && line.indexOf('://') < 0 && (line.indexOf(',') < 0 || line.indexOf('#genre#') > 0)) {
|
|
if (currentUrls != '') {
|
|
saveCategoryItem(categoryId, currentCategory, currentUrls, typeName, imageUrl);
|
|
}
|
|
currentCategory = line.split(',')[0].trim();
|
|
currentUrls = '';
|
|
} else if (line.indexOf(',') > 0 && /http|rtmp|rtsp|rsp/.test(line)) {
|
|
var parts = line.split(',');
|
|
var channelName = parts[0].trim();
|
|
var channelUrl = parts[1].trim();
|
|
|
|
if (channelUrl && sourceHeaders[categoryId] && Object.keys(sourceHeaders[categoryId]).length > 0) {
|
|
var secondaryHost = extractHostFromUrl(channelUrl);
|
|
if (secondaryHost && !processedHosts.has(secondaryHost)) {
|
|
addToGlobalHeaders(secondaryHost, sourceHeaders[categoryId]);
|
|
processedHosts.add(secondaryHost);
|
|
}
|
|
}
|
|
|
|
if (currentUrls != '') {
|
|
currentUrls += '#';
|
|
}
|
|
currentUrls += channelName + '$' + channelUrl;
|
|
}
|
|
}
|
|
|
|
if (currentUrls != '') {
|
|
saveCategoryItem(categoryId, currentCategory, currentUrls, typeName, imageUrl);
|
|
}
|
|
|
|
return lines;
|
|
}
|
|
|
|
function saveCategoryItem(categoryId, categoryName, urls, typeName, imageUrl) {
|
|
/* ★★★ 唯一改动:用当前源 img,不存在再用默认 ★★★ */
|
|
var cfg = sourceConfigs[categoryId] || {};
|
|
var processedImageUrl = cfg.img || picUrl;
|
|
|
|
var vodId = categoryId + '$$$' + categories[categoryId].length;
|
|
vodSourceConfigs[vodId] = {
|
|
sourceHeaders: sourceHeaders[categoryId] || {},
|
|
sourceConfig: sourceConfigs[categoryId] || {},
|
|
categoryId: categoryId
|
|
};
|
|
|
|
var item = {
|
|
'vod_id': vodId,
|
|
'vod_name': categoryName,
|
|
'vod_pic': processedImageUrl, // ★ 这里已换成各自 img
|
|
'vod_remarks': '',
|
|
'type_name': '',
|
|
'vod_year': '',
|
|
'vod_area': '',
|
|
'vod_actor': '',
|
|
'vod_director': '',
|
|
'vod_content': '',
|
|
'vod_play_from': typeName,
|
|
'vod_play_url': urls
|
|
};
|
|
categories[categoryId].push(item);
|
|
}
|
|
|
|
function parseM3u(content, categoryName) {
|
|
var result = {};
|
|
var regex = /(#EXTINF:.+?),([^,]+?)\s*\n(.+?)\s*\n/g;
|
|
var match;
|
|
while ((match = regex.exec(content)) !== null) {
|
|
var infTag = match[1];
|
|
var name = match[2];
|
|
var url = match[3];
|
|
if (!name || !url || name == '' || url == '') continue;
|
|
name = name.trim();
|
|
url = url.trim();
|
|
var group = categoryName;
|
|
var groupRegex = /group-title="(.*?)"/;
|
|
if (groupRegex.test(infTag)) {
|
|
group = infTag.match(groupRegex)[1];
|
|
}
|
|
if (!result[group]) result[group] = [];
|
|
result[group].push(name + ',' + url);
|
|
}
|
|
var output = '';
|
|
for (var group in result) {
|
|
if (result.hasOwnProperty(group)) {
|
|
output += group + '\n';
|
|
result[group].forEach(item => output += item + '\n');
|
|
}
|
|
}
|
|
return output;
|
|
}
|
|
|
|
function parseFm(content) {
|
|
var output = '';
|
|
try {
|
|
var data = JSON.parse(content);
|
|
data.forEach(item => {
|
|
var channelName = item.name;
|
|
var urls = item.urls;
|
|
output += channelName + ',#genre#\n';
|
|
urls.forEach(urlItem => {
|
|
var itemName = urlItem.name;
|
|
urlItem.datalist.forEach(url => {
|
|
output += itemName + ',' + url + '\n';
|
|
});
|
|
});
|
|
});
|
|
} catch (error) {
|
|
console.error('解析Fm数据错误:', error);
|
|
}
|
|
return output;
|
|
}
|
|
|
|
function parseList(content) {
|
|
var output = '';
|
|
try {
|
|
var data = JSON.parse(content).data;
|
|
data.list.forEach(item => {
|
|
var channelName = item.prov;
|
|
var programs = item.list;
|
|
output += channelName + ',#genre#\n';
|
|
programs.forEach(program => {
|
|
var programName = program.name;
|
|
program.datalist.forEach(urlItem => {
|
|
output += programName + ' ===>>> ' + urlItem.vod_play_from + ',' + urlItem.vod_play_url + '\n';
|
|
});
|
|
});
|
|
});
|
|
} catch (error) {
|
|
console.error('解析List数据错误:', error);
|
|
}
|
|
return output;
|
|
}
|
|
|
|
function detail(detailId) {
|
|
var parts = detailId.split('$$$');
|
|
var categoryId = parts[0];
|
|
var index = parseInt(parts[1]);
|
|
|
|
if (!categories[categoryId] || !categories[categoryId][index]) {
|
|
return JSON.stringify({ 'list': [] });
|
|
}
|
|
|
|
var detailData = categories[categoryId][index];
|
|
var itemId = categoryId.split('$')[0];
|
|
|
|
if (itemId.indexOf('!!') >= 0) {
|
|
itemId = itemId.replace('!!', '');
|
|
var urls = detailData.vod_play_url.split('#');
|
|
var countMap = {};
|
|
var groupMap = {};
|
|
|
|
urls.forEach(url => {
|
|
var namePart = url.split('$')[0];
|
|
var displayName = itemId;
|
|
|
|
if (namePart.indexOf(' ===>>> ') > 0) {
|
|
displayName = namePart.split(' ===>>> ')[1];
|
|
namePart = namePart.split(' ===>>> ')[0];
|
|
}
|
|
|
|
countMap[namePart] = (countMap[namePart] || 0) + 1;
|
|
displayName = itemId + (countMap[namePart] > 1 ? ' ' + countMap[namePart] : '');
|
|
|
|
if (!groupMap[displayName]) groupMap[displayName] = [];
|
|
groupMap[displayName].push(namePart + '$' + url.split('$')[1]);
|
|
});
|
|
|
|
var groupNames = Object.keys(groupMap);
|
|
var groupUrls = groupNames.map(name => groupMap[name].join('#'));
|
|
|
|
detailData.vod_play_from = groupNames.join('$$$');
|
|
detailData.vod_play_url = groupUrls.join('$$$');
|
|
}
|
|
|
|
return JSON.stringify({
|
|
'list': [detailData]
|
|
});
|
|
}
|
|
|
|
function play(playId, playUrl, param) {
|
|
var parse = 0;
|
|
var sourceConfig = null;
|
|
|
|
if (vodSourceConfigs[playId]) {
|
|
var vodConfig = vodSourceConfigs[playId];
|
|
sourceConfig = vodConfig.sourceConfig;
|
|
if (vodConfig.sourceConfig && vodConfig.sourceConfig.play_parse !== undefined) {
|
|
parse = vodConfig.sourceConfig.play_parse ? 1 : 0;
|
|
}
|
|
}
|
|
|
|
var playHeaders = getPlayHeaders(playUrl, sourceConfig);
|
|
|
|
return JSON.stringify({
|
|
'parse': parse,
|
|
'url': playUrl,
|
|
'header': playHeaders,
|
|
'js': '',
|
|
'extra': {}
|
|
});
|
|
}
|
|
|
|
function search(keyword, param) {
|
|
return JSON.stringify({ 'list': [] });
|
|
}
|
|
|
|
__JS_SPIDER__ = {
|
|
'init': init,
|
|
'home': home,
|
|
'homeVod': homeVod,
|
|
'category': category,
|
|
'detail': detail,
|
|
'play': play,
|
|
'search': search
|
|
}; |