上传文件至「open」
This commit is contained in:
@@ -0,0 +1,340 @@
|
||||
/*
|
||||
@header({
|
||||
searchable: 1,
|
||||
filterable: 1,
|
||||
quickSearch: 1,
|
||||
title: '围观短剧',
|
||||
lang: 'cat'
|
||||
})
|
||||
*/
|
||||
|
||||
let siteName = '围观短剧';
|
||||
let siteKey = '';
|
||||
let siteType = 0;
|
||||
|
||||
let UA = "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36";
|
||||
|
||||
let rule = {
|
||||
host: 'https://api.drama.9ddm.com',
|
||||
tagsUrl: '/drama/home/shortVideoTags?version_code=1500&os_type=1',
|
||||
searchUrl: '/drama/home/search?version_code=1500&os_type=1',
|
||||
detailUrl: '/drama/home/shortVideoDetail?version_code=1500&os_type=1'
|
||||
};
|
||||
|
||||
const DEFAULT_HEADERS = {
|
||||
'User-Agent': UA,
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json'
|
||||
};
|
||||
|
||||
function init(cfg) {
|
||||
try {
|
||||
siteName = (cfg.skey?.split('_')[1] || cfg.skey) || (cfg.key?.split('_')[1] || cfg.key) || '围观短剧';
|
||||
siteKey = cfg.skey;
|
||||
siteType = cfg.stype;
|
||||
console.log(`【${siteName}】初始化完成`);
|
||||
} catch (e) {
|
||||
console.log(`【${siteName}】初始化失败: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
function safeJSONParse(str, defaultValue = {}) {
|
||||
if (!str) return defaultValue;
|
||||
if (typeof str === 'object') return str;
|
||||
try {
|
||||
return JSON.parse(str);
|
||||
} catch (e) {
|
||||
console.log(`【${siteName}】JSON解析失败: ${e.message}`);
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
// 通用请求函数
|
||||
async function request(url, options = {}) {
|
||||
console.log(`【${siteName}】${options.method || 'GET'} ${url.split('?')[0]}`);
|
||||
|
||||
const baseOptions = {
|
||||
method: options.method || 'GET',
|
||||
headers: { ...DEFAULT_HEADERS, ...options.headers },
|
||||
timeout: options.timeout || 15000
|
||||
};
|
||||
|
||||
let dataStr = '';
|
||||
const requestData = options.body || options.data;
|
||||
if (requestData) {
|
||||
dataStr = typeof requestData === 'string' ? requestData : JSON.stringify(requestData);
|
||||
baseOptions.headers['Content-Type'] = baseOptions.headers['Content-Type'] || 'application/json';
|
||||
}
|
||||
|
||||
for (let key of ['data', 'body']) {
|
||||
try {
|
||||
const reqOptions = { ...baseOptions, [key]: dataStr };
|
||||
const response = await req(url, reqOptions);
|
||||
const content = response?.content || response?.data || response;
|
||||
|
||||
if (!content) continue;
|
||||
|
||||
const data = typeof content === 'object' ? content : safeJSONParse(content);
|
||||
|
||||
if (data?.code && data.code !== 200) continue;
|
||||
|
||||
console.log(`【${siteName}】${key}成功: ${JSON.stringify(data).substring(0, 300)}`);
|
||||
return data;
|
||||
} catch (e) {
|
||||
console.log(`【${siteName}】${key}异常: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`【${siteName}】全部失败`);
|
||||
return null;
|
||||
}
|
||||
|
||||
async function home() {
|
||||
let classes = [{ type_name: '全部', type_id: '全部' }];
|
||||
let filters = { '全部': [] };
|
||||
|
||||
const data = await request(`${rule.host}${rule.tagsUrl}`);
|
||||
|
||||
if (data && data.code === 200) {
|
||||
// 分类
|
||||
if (data.audiences && Array.isArray(data.audiences)) {
|
||||
const audienceClasses = data.audiences.map(audience => ({
|
||||
type_name: audience,
|
||||
type_id: audience
|
||||
}));
|
||||
classes.push(...audienceClasses);
|
||||
|
||||
// 为每个分类创建筛选
|
||||
for (const audience of data.audiences) {
|
||||
filters[audience] = [];
|
||||
|
||||
if (data.tags && Array.isArray(data.tags) && data.tags.length > 0) {
|
||||
const tagValues = [{ n: '全部', v: '' }];
|
||||
data.tags.forEach(tag => {
|
||||
if (tag) tagValues.push({ n: tag, v: tag });
|
||||
});
|
||||
if (tagValues.length > 1) {
|
||||
filters[audience].push({ key: 'tag', name: '标签', value: tagValues });
|
||||
}
|
||||
}
|
||||
|
||||
if (data.orders && Array.isArray(data.orders) && data.orders.length > 0) {
|
||||
const orderValues = data.orders.map(order => ({ n: order, v: order }));
|
||||
filters[audience].push({ key: 'order', name: '排序', value: orderValues });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return JSON.stringify({ class: classes, filters: filters });
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
try {
|
||||
const result = await category('全部', 1, {}, {});
|
||||
const data = typeof result === 'string' ? safeJSONParse(result) : result;
|
||||
const list = (data && data.list) ? data.list.slice(0, 12) : [];
|
||||
return JSON.stringify({ list: list });
|
||||
} catch (e) {
|
||||
console.log(`【${siteName}】homeVod失败: ${e.message}`);
|
||||
return JSON.stringify({ list: [] });
|
||||
}
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
const videos = [];
|
||||
const page = pg || 1;
|
||||
|
||||
const tag = (extend && extend.tag) ? extend.tag : "";
|
||||
const order = (extend && extend.order) ? extend.order : "";
|
||||
|
||||
const orderMap = { "最热": "hot", "最新": "new", "热度": "hot", "时间": "new" };
|
||||
let orderValue = orderMap[order] || "";
|
||||
|
||||
const postData = {
|
||||
audience: tid === '全部' ? "" : tid,
|
||||
page: page,
|
||||
pageSize: 30,
|
||||
searchWord: "",
|
||||
subject: tag,
|
||||
order: orderValue
|
||||
};
|
||||
|
||||
console.log(`【${siteName}】分类请求: tid=${tid}, tag=${tag}, order=${order}`);
|
||||
|
||||
const data = await request(`${rule.host}${rule.searchUrl}`, {
|
||||
method: 'POST',
|
||||
body: postData
|
||||
});
|
||||
|
||||
if (data && data.code === 200 && data.data && Array.isArray(data.data)) {
|
||||
for (const it of data.data) {
|
||||
if (it && it.oneId) {
|
||||
videos.push({
|
||||
vod_id: String(it.oneId),
|
||||
vod_name: it.title || '未知标题',
|
||||
vod_pic: it.vertPoster || it.horizonPoster || '',
|
||||
vod_remarks: `集数:${it.episodeCount || 0} 播放:${it.viewCount || 0}`,
|
||||
vod_content: it.description || '',
|
||||
vod_year: it.publishDate || ''
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log(`【${siteName}】分类数据异常:`, data?.code);
|
||||
}
|
||||
|
||||
console.log(`【${siteName}】分类获取到 ${videos.length} 条数据`);
|
||||
|
||||
return JSON.stringify({
|
||||
list: videos,
|
||||
page: page,
|
||||
pagecount: page + 1,
|
||||
limit: videos.length,
|
||||
total: videos.length * (page + 1)
|
||||
});
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
if (!id) return JSON.stringify({ list: [] });
|
||||
|
||||
const url = `${rule.host}${rule.detailUrl}&oneId=${encodeURIComponent(id)}&page=1&pageSize=1000`;
|
||||
const data = await request(url);
|
||||
|
||||
if (data && data.code === 200 && data.data && Array.isArray(data.data)) {
|
||||
const episodes = data.data.filter(ep => ep);
|
||||
const firstEpisode = episodes[0] || {};
|
||||
|
||||
const playItems = [];
|
||||
for (let i = 0; i < episodes.length; i++) {
|
||||
const episode = episodes[i];
|
||||
let playSetting = episode.playSetting || episode.videoClarityList || [];
|
||||
|
||||
if (typeof playSetting === 'string') {
|
||||
playSetting = safeJSONParse(playSetting, []);
|
||||
}
|
||||
if (!Array.isArray(playSetting)) playSetting = [];
|
||||
|
||||
const clarityInfo = {};
|
||||
for (const item of playSetting) {
|
||||
if (item && item.url && item.name) {
|
||||
clarityInfo[item.name] = item.url;
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(clarityInfo).length > 0) {
|
||||
const episodeNum = episode.playOrder || episode.episodeNumber || (i + 1);
|
||||
playItems.push(`第${episodeNum}集$${JSON.stringify(clarityInfo)}`);
|
||||
}
|
||||
}
|
||||
|
||||
const playUrl = playItems.join('#');
|
||||
if (!playUrl) return JSON.stringify({ list: [] });
|
||||
|
||||
const vod = {
|
||||
vod_id: String(id),
|
||||
vod_name: firstEpisode.title || '未知剧名',
|
||||
vod_pic: firstEpisode.vertPoster || firstEpisode.horizonPoster || '',
|
||||
vod_remarks: `共${episodes.length}集`,
|
||||
vod_content: firstEpisode.description || '',
|
||||
vod_play_from: '围观短剧',
|
||||
vod_play_url: playUrl
|
||||
};
|
||||
|
||||
return JSON.stringify({ list: [vod] });
|
||||
}
|
||||
|
||||
return JSON.stringify({ list: [] });
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
let clarityInfo = {};
|
||||
|
||||
try {
|
||||
if (typeof id === 'object') {
|
||||
clarityInfo = id;
|
||||
} else if (typeof id === 'string') {
|
||||
clarityInfo = safeJSONParse(id, {});
|
||||
}
|
||||
} catch (e) {
|
||||
return JSON.stringify({ parse: 0, url: id || '', msg: '播放参数错误' });
|
||||
}
|
||||
|
||||
const clarityOrder = ['4K', '超清', '1080P', '高清', '720P', '流畅', '480P'];
|
||||
const urls = [];
|
||||
const added = new Set();
|
||||
|
||||
for (const clarity of clarityOrder) {
|
||||
const url = clarityInfo[clarity];
|
||||
if (url && typeof url === 'string' && url.startsWith('http') && !added.has(clarity)) {
|
||||
urls.push(clarity, url);
|
||||
added.add(clarity);
|
||||
}
|
||||
}
|
||||
|
||||
if (urls.length > 0) {
|
||||
return JSON.stringify({
|
||||
parse: 0,
|
||||
url: urls,
|
||||
header: { 'User-Agent': UA }
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof id === 'string' && id.startsWith('http')) {
|
||||
return JSON.stringify({ parse: 0, url: id, header: { 'User-Agent': UA } });
|
||||
}
|
||||
|
||||
return JSON.stringify({ parse: 0, url: '', msg: '暂无可用播放地址' });
|
||||
}
|
||||
|
||||
async function search(wd, quick, pg) {
|
||||
if (!wd || wd.trim() === '') {
|
||||
return JSON.stringify({ list: [], page: 1 });
|
||||
}
|
||||
|
||||
const page = pg || 1;
|
||||
const postData = {
|
||||
audience: "",
|
||||
page: page,
|
||||
pageSize: 30,
|
||||
searchWord: wd,
|
||||
subject: ""
|
||||
};
|
||||
|
||||
const data = await request(`${rule.host}${rule.searchUrl}`, {
|
||||
method: 'POST',
|
||||
body: postData,
|
||||
timeout: 8000
|
||||
});
|
||||
|
||||
let videos = [];
|
||||
if (data && data.code === 200 && data.data && Array.isArray(data.data)) {
|
||||
videos = data.data.filter(it => it && it.oneId).map(it => ({
|
||||
vod_id: String(it.oneId),
|
||||
vod_name: it.title || '未知标题',
|
||||
vod_pic: it.vertPoster || it.horizonPoster || '',
|
||||
vod_remarks: `集数:${it.episodeCount || 0} 播放:${it.viewCount || 0}`,
|
||||
vod_content: it.description || ''
|
||||
}));
|
||||
}
|
||||
|
||||
return JSON.stringify({
|
||||
list: videos,
|
||||
page: page,
|
||||
pagecount: videos.length === 30 ? page + 1 : page,
|
||||
limit: videos.length,
|
||||
total: videos.length * page
|
||||
});
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return {
|
||||
init: init,
|
||||
home: home,
|
||||
homeVod: homeVod,
|
||||
category: category,
|
||||
detail: detail,
|
||||
play: play,
|
||||
search: search
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,436 @@
|
||||
/*
|
||||
@header({
|
||||
searchable: 1,
|
||||
filterable: 0,
|
||||
quickSearch: 1,
|
||||
title: '网盘资源[搜]',
|
||||
lang: 'ds'
|
||||
})
|
||||
*/
|
||||
import '../lib/htmlParser.js';
|
||||
import { Quark, Baidu, UC } from "../lib/pans.js";
|
||||
|
||||
|
||||
// ==================== 全局变量 ====================
|
||||
let siteName = '网盘资源搜索', siteKey = '', siteType = 0;
|
||||
|
||||
|
||||
// ==================== 开关配置 (1=开启,0=关闭)====================
|
||||
let quarkInfinite = 0;
|
||||
let quarkOrig = 1;
|
||||
let quarkTransfer = 1;
|
||||
let enableProxy = 0;
|
||||
// ================================================
|
||||
|
||||
let UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36";
|
||||
|
||||
let headers = {
|
||||
"User-Agent": UA,
|
||||
"Accept": "application/json, text/plain, */*",
|
||||
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8"
|
||||
};
|
||||
|
||||
let proxyurl = 'http://127.0.0.1:2525/proxy?url=';
|
||||
let downThreads = '20';
|
||||
|
||||
let defaultImg = `https://cnb.cool/zhyadc/YsBox/-/git/raw/main/images/icon_cookie/网盘搜索.png`;
|
||||
|
||||
let host = 'https://so.yinpai.xyz';
|
||||
let maxPages = 5;
|
||||
|
||||
function safeJSONParse(str, defaultValue = {}) {
|
||||
if (!str || typeof str === 'object') return str || defaultValue;
|
||||
try {
|
||||
return JSON.parse(str);
|
||||
} catch {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
async function request(url, options = {}) {
|
||||
const reqHeaders = { ...headers, ...options.headers };
|
||||
let postType = reqHeaders['Content-Type']?.includes('json') ? 'json' :
|
||||
reqHeaders['Content-Type']?.includes('form') ? 'form' : '';
|
||||
|
||||
try {
|
||||
const response = await req(url, {
|
||||
method: options.method || 'GET',
|
||||
headers: reqHeaders,
|
||||
data: options.data,
|
||||
postType: postType,
|
||||
timeout: options.timeout || 15000
|
||||
});
|
||||
return response?.content || response?.data || response;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function mapResolution(res) {
|
||||
const map = {
|
||||
'low': '流畅', 'normal': '标清', 'high': '高清', 'super': '超清',
|
||||
'4k': '4K', '2k': '2K', 'hdr': 'HDR', 'dolby_vision': 'HDR',
|
||||
'M3U8_AUTO_480': '480P', 'M3U8_AUTO_720': '720P', 'M3U8_AUTO_1080': '1080P',
|
||||
'M3U8_AUTO_2K': '2K', 'M3U8_AUTO_4K': '4K'
|
||||
};
|
||||
return map[res] || res;
|
||||
}
|
||||
|
||||
async function loadRemoteConfig(cfgUrl) {
|
||||
try {
|
||||
const res = await request(cfgUrl, { timeout: 10000 });
|
||||
if (res) {
|
||||
const remoteCfg = safeJSONParse(res);
|
||||
if (remoteCfg.quark_cookie?.length > 10) Quark.cookie = remoteCfg.quark_cookie;
|
||||
if (remoteCfg.baidu_cookie?.length > 10) Baidu.cookie = remoteCfg.baidu_cookie;
|
||||
if (remoteCfg.uc_cookie?.length > 10) UC.cookie = remoteCfg.uc_cookie;
|
||||
if (remoteCfg.uc_token?.length > 10) UC.token = remoteCfg.uc_token;
|
||||
if (remoteCfg.threads) downThreads = remoteCfg.threads;
|
||||
if (remoteCfg.enableProxy !== undefined) enableProxy = remoteCfg.enableProxy;
|
||||
if (remoteCfg.infinite !== undefined) quarkInfinite = remoteCfg.infinite;
|
||||
if (remoteCfg.quark_original !== undefined) quarkOrig = remoteCfg.quark_original;
|
||||
if (remoteCfg.quark_transfer !== undefined) quarkTransfer = remoteCfg.quark_transfer;
|
||||
return true;
|
||||
}
|
||||
} catch (e) {}
|
||||
return false;
|
||||
}
|
||||
|
||||
async function init(cfg) {
|
||||
siteName = cfg.skey?.split('_')[1] || cfg.skey || siteName;
|
||||
siteKey = cfg.skey;
|
||||
siteType = cfg.stype;
|
||||
|
||||
let ext = cfg.ext || cfg;
|
||||
if (typeof ext === 'string') ext = decodeURIComponent(ext);
|
||||
|
||||
if (typeof ext === 'string') {
|
||||
await loadRemoteConfig(ext);
|
||||
}
|
||||
}
|
||||
|
||||
function home() {
|
||||
return JSON.stringify({
|
||||
class: [
|
||||
{ type_id: 'search', type_name: '这是一个搜索源' }
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await category('search', 1, null, null);
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, ext) {
|
||||
if (tid === 'search') {
|
||||
return JSON.stringify({
|
||||
list: [{
|
||||
vod_id: 'only_search',
|
||||
vod_name: '🔍 网盘资源搜索',
|
||||
vod_pic: defaultImg,
|
||||
}]
|
||||
});
|
||||
}
|
||||
return JSON.stringify({ list: [] });
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
let detailObj;
|
||||
try {
|
||||
let decoded = decodeURIComponent(id);
|
||||
detailObj = safeJSONParse(decoded);
|
||||
} catch (e) {
|
||||
let parts = id.split('|');
|
||||
detailObj = {
|
||||
rid: parts[0],
|
||||
name: parts[1] || '网盘资源',
|
||||
panName: parts[2] || '夸克网盘'
|
||||
};
|
||||
}
|
||||
|
||||
let resourceId = detailObj.rid;
|
||||
let vod_name = detailObj.name;
|
||||
|
||||
let play_from = [];
|
||||
let play_url = [];
|
||||
let play_pic = [];
|
||||
|
||||
let apiUrl = `${host}/api.php?ids=${resourceId}`;
|
||||
let response = await request(apiUrl, { headers: headers, timeout: 15000 });
|
||||
|
||||
let shareUrl = null;
|
||||
if (response) {
|
||||
try {
|
||||
let data = safeJSONParse(response);
|
||||
if (data.list && data.list[0]) {
|
||||
let item = data.list[0];
|
||||
shareUrl = item.vod_play_url?.split('$')?.[1] || '';
|
||||
vod_name = item.vod_name || vod_name;
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
if (shareUrl && shareUrl.startsWith('http')) {
|
||||
let counters = { '夸克': 1, '百度': 1, '优汐': 1 };
|
||||
|
||||
if (/\.quark|pan\.quark/.test(shareUrl)) {
|
||||
let shareData = Quark.getShareData(shareUrl);
|
||||
if (shareData) {
|
||||
let files = await Quark.getFilesByShareUrl(shareData);
|
||||
if (files?.length) {
|
||||
let url = files.map(v => {
|
||||
let size = v.size ? ` 【${formatSize(v.size)}】` : '';
|
||||
return `${v.file_name}${size}$${[shareData.shareId, v.stoken, v.fid, v.share_fid_token, v.pdir_fid || '', v.subtitle?.fid || '', v.subtitle?.share_fid_token || ''].join('*')}`;
|
||||
}).join('#');
|
||||
let imgs = files.map(v => v.thumbnail || v.thumb || v.pic || '').join('#');
|
||||
play_from.push(`夸克#${counters['夸克']++}`);
|
||||
play_url.push(url);
|
||||
play_pic.push(imgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (/\.baidu|pan\.baidu/.test(shareUrl)) {
|
||||
let shareData = Baidu.getShareData(shareUrl);
|
||||
if (shareData) {
|
||||
let files = await Baidu.getFilesByShareUrl(shareData);
|
||||
if (files?.length) {
|
||||
let url = files.map(v => {
|
||||
let size = v.size ? ` 【${formatSize(v.size)}】` : '';
|
||||
let info = { surl: shareData.surl, pwd: shareData.pwd || '' };
|
||||
return `${v.name}${size}$${[v.path, v.uk, v.shareid, v.fsid, JSON.stringify(info)].join('*')}`;
|
||||
}).join('#');
|
||||
let imgs = files.map(v => v.thumbnail || v.thumb || v.pic || '').join('#');
|
||||
play_from.push(`百度#${counters['百度']++}`);
|
||||
play_url.push(url);
|
||||
play_pic.push(imgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (/\.uc|drive\.uc/.test(shareUrl)) {
|
||||
let shareData = UC.getShareData(shareUrl);
|
||||
if (shareData) {
|
||||
let files = await UC.getFilesByShareUrl(shareData);
|
||||
if (files?.length) {
|
||||
let url = files.map(v => {
|
||||
let size = v.size ? ` 【${formatSize(v.size)}】` : '';
|
||||
return `${v.file_name}${size}$${[shareData.shareId, v.stoken, v.fid, v.share_fid_token, v.subtitle?.fid || '', v.subtitle?.share_fid_token || ''].join('*')}`;
|
||||
}).join('#');
|
||||
let imgs = files.map(v => v.thumbnail || v.thumb || v.pic || '').join('#');
|
||||
play_from.push(`优汐#${counters['优汐']++}`);
|
||||
play_url.push(url);
|
||||
play_pic.push(imgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (play_from.length === 0) {
|
||||
play_from.push('提示');
|
||||
play_url.push(shareUrl ? `分享链接: ${shareUrl}` : `获取分享链接失败,资源ID: ${resourceId}`);
|
||||
play_pic.push('');
|
||||
}
|
||||
|
||||
let contentParts = [`资源ID: ${resourceId}`];
|
||||
if (shareUrl) {
|
||||
contentParts.push(`网盘链接: ${shareUrl}`);
|
||||
}
|
||||
let vod_content = contentParts.join(' | ');
|
||||
|
||||
let vod = {
|
||||
vod_id: id,
|
||||
vod_name: vod_name,
|
||||
vod_pic: '',
|
||||
vod_content: vod_content,
|
||||
vod_remarks: play_from.length > 0 ? (play_from[0].split('#')[0] + '可播放') : '解析失败',
|
||||
vod_play_from: play_from.join('$$$'),
|
||||
vod_play_url: play_url.join('$$$'),
|
||||
vod_play_pic: play_pic.join('$$$'),
|
||||
vod_play_pic_ratio: 1.0
|
||||
};
|
||||
|
||||
return JSON.stringify({ list: [vod] });
|
||||
}
|
||||
|
||||
async function search(wd, quick, pg) {
|
||||
let page = pg || 1;
|
||||
|
||||
const typeMap = [
|
||||
{ type: 'baidu', name: '百度网盘', icon: 'https://cnb.cool/zhyadc/YsBox/-/git/raw/main/images/icon_cookie/百度.png' },
|
||||
{ type: 'uc', name: '优汐网盘', icon: 'https://cnb.cool/zhyadc/YsBox/-/git/raw/main/images/icon_cookie/优汐.png' },
|
||||
{ type: 'quark', name: '夸克网盘', icon: 'https://cnb.cool/zhyadc/YsBox/-/git/raw/main/images/icon_cookie/夸克.png' }
|
||||
];
|
||||
|
||||
let urls = [];
|
||||
for (let t of typeMap) {
|
||||
urls.push({
|
||||
url: `${host}/api.php?type=${t.type}&wd=${encodeURIComponent(wd)}`,
|
||||
options: { headers: headers, timeout: 15000 }
|
||||
});
|
||||
}
|
||||
|
||||
let results = await batchFetch(urls);
|
||||
let allList = [];
|
||||
let seenIds = new Set();
|
||||
|
||||
for (let idx = 0; idx < results.length; idx++) {
|
||||
let content = results[idx];
|
||||
if (!content) continue;
|
||||
|
||||
let typeInfo = typeMap[idx];
|
||||
|
||||
try {
|
||||
let data = safeJSONParse(content);
|
||||
if (data.list && Array.isArray(data.list)) {
|
||||
for (let item of data.list) {
|
||||
let title = item.vod_name;
|
||||
if (!title || title.length < 2) continue;
|
||||
|
||||
if (seenIds.has(item.vod_id)) continue;
|
||||
seenIds.add(item.vod_id);
|
||||
|
||||
let detailObj = {
|
||||
rid: item.vod_id,
|
||||
name: title,
|
||||
panName: typeInfo.name
|
||||
};
|
||||
let detailId = encodeURIComponent(JSON.stringify(detailObj));
|
||||
|
||||
allList.push({
|
||||
vod_id: detailId,
|
||||
vod_name: title,
|
||||
vod_pic: typeInfo.icon,
|
||||
vod_content: item.vod_remarks || '',
|
||||
vod_remarks: typeInfo.name
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
let filteredList = allList.filter(item => new RegExp(wd, "i").test(item.vod_name));
|
||||
|
||||
return JSON.stringify({
|
||||
page: page,
|
||||
pagecount: maxPages,
|
||||
limit: filteredList.length,
|
||||
total: filteredList.length,
|
||||
list: filteredList
|
||||
});
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
let ids = id.split('*');
|
||||
let urls = [];
|
||||
|
||||
const addUrl = (name, u) => {
|
||||
if (u) urls.push(name, proxyUrl(u) + `&thread=${downThreads}`);
|
||||
};
|
||||
|
||||
if (flag.startsWith('夸克') && ids.length >= 4) {
|
||||
let [shareId, stoken, fid, share_fid_token] = ids;
|
||||
let header = {
|
||||
'User-Agent': headers['User-Agent'],
|
||||
'origin': 'https://pan.quark.cn',
|
||||
'referer': 'https://pan.quark.cn/',
|
||||
'Cookie': Quark.cookie
|
||||
};
|
||||
|
||||
if (quarkInfinite == 1) {
|
||||
const tokenUrls = await Quark.getUrl(shareId, stoken, fid, share_fid_token);
|
||||
tokenUrls?.forEach(item => { if (item?.url) addUrl("无限" + (item.name || ''), item.url); });
|
||||
}
|
||||
|
||||
let shouldTransfer = quarkTransfer;
|
||||
if (quarkInfinite != 1 && quarkOrig != 1 && quarkTransfer != 1) {
|
||||
shouldTransfer = 1;
|
||||
}
|
||||
|
||||
if (shouldTransfer == 1) {
|
||||
if (quarkOrig == 1) {
|
||||
let down = await Quark.getDownload(shareId, stoken, fid, share_fid_token, true);
|
||||
if (down && down.error) {
|
||||
const errorMsg = down.message || '未知错误';
|
||||
return JSON.stringify({ parse: 0, msg: `夸克: ${errorMsg}`, header: header });
|
||||
}
|
||||
if (down?.download_url) addUrl("原画", down.download_url);
|
||||
}
|
||||
|
||||
let transcoding = await Quark.getLiveTranscoding(shareId, stoken, fid, share_fid_token);
|
||||
if (transcoding?.length) {
|
||||
transcoding.filter(t => t.accessable).forEach(t => {
|
||||
if (t?.video_info?.url) {
|
||||
addUrl(mapResolution(t.resolution), t.video_info.url);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (urls.length) {
|
||||
return JSON.stringify({ parse: 0, url: urls, header: header });
|
||||
}
|
||||
}
|
||||
|
||||
if (flag.startsWith('百度') && ids.length >= 5) {
|
||||
let [path, uk, shareid, fsid, shareDataStr] = ids;
|
||||
let shareData = safeJSONParse(shareDataStr);
|
||||
|
||||
let original = await Baidu.getAppShareUrl(path, uk, shareid, fsid, shareData);
|
||||
if (original && !original.error) {
|
||||
addUrl("原画", original);
|
||||
} else {
|
||||
const errorMsg = original?.message || '获取链接失败,请检查Cookie是否有效';
|
||||
return JSON.stringify({ parse: 0, msg: `百度: ${errorMsg}`, header: headers });
|
||||
}
|
||||
|
||||
if (urls.length) {
|
||||
return JSON.stringify({
|
||||
parse: 0,
|
||||
url: urls,
|
||||
header: { "User-Agent": 'netdisk;P2SP;2.2.91.136;android-android;' }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (flag.startsWith('优汐') && ids.length >= 4) {
|
||||
let [shareId, stoken, fid, share_fid_token] = ids;
|
||||
|
||||
let down = await UC.getDownload(shareId, stoken, fid, share_fid_token, true);
|
||||
if (down && down.error) {
|
||||
const errorMsg = down.message || '未知错误';
|
||||
return JSON.stringify({ parse: 0, msg: `优汐: ${errorMsg}`, header: headers });
|
||||
}
|
||||
if (down?.length) {
|
||||
down.forEach(item => { if (item?.url) addUrl(mapResolution(item.name), item.url); });
|
||||
}
|
||||
|
||||
if (urls.length) {
|
||||
return JSON.stringify({ parse: 0, url: urls });
|
||||
}
|
||||
}
|
||||
|
||||
return JSON.stringify({ parse: 0, msg: `解析分享链接失败, 分享链接可能已失效`, header: headers });
|
||||
}
|
||||
|
||||
function formatSize(bytes) {
|
||||
let size = typeof bytes === 'string' ? parseInt(bytes, 10) : bytes;
|
||||
if (!size || isNaN(size) || size <= 0) return '0 B';
|
||||
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
let i = 0;
|
||||
while (size >= 1024 && i < units.length - 1) { size /= 1024; i++; }
|
||||
return size.toFixed(2) + ' ' + units[i];
|
||||
}
|
||||
|
||||
function proxyUrl(url) {
|
||||
if (!url) return '';
|
||||
if (enableProxy && proxyurl) return proxyurl + encodeURIComponent(url);
|
||||
return url;
|
||||
}
|
||||
|
||||
async function proxy(params) {
|
||||
let url = params.url;
|
||||
return [302, '', '', { 'Location': url }];
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return { init, home, homeVod, category, detail, play, search, proxy };
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
@header({
|
||||
searchable: 1,
|
||||
filterable: 1,
|
||||
quickSearch: 1,
|
||||
title: '喜马拉雅[听]',
|
||||
lang: 'cat',
|
||||
})
|
||||
*/
|
||||
let host = 'https://m.ximalaya.com';
|
||||
let searchHost = 'https://api.cenguigui.cn';
|
||||
|
||||
let siteName = '喜马拉雅', siteKey = '', siteType = 0;
|
||||
|
||||
let UA = "Mozilla/5.0 (Linux; Android 10; Mobile) AppleWebKit/537.36 (Chrome/91.0.4472.120 Mobile Safari/537.36)";
|
||||
|
||||
const headers = {
|
||||
'User-Agent': UA,
|
||||
'Accept': 'application/json, text/plain, */*'
|
||||
};
|
||||
|
||||
function init(cfg) {
|
||||
siteName = cfg.skey?.split('_')[1] || cfg.skey || '喜马拉雅';
|
||||
siteKey = cfg.skey;
|
||||
siteType = cfg.stype;
|
||||
|
||||
if (cfg && typeof cfg === 'string') {
|
||||
host = cfg;
|
||||
} else if (cfg && typeof cfg === 'object') {
|
||||
const ext = cfg.ext;
|
||||
if (ext) {
|
||||
host = ext.host || ext.hosturl || ext.url || ext.site || host;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function safeJSONParse(str, defaultValue = {}) {
|
||||
if (!str || typeof str === 'object') return str || defaultValue;
|
||||
try {
|
||||
return JSON.parse(str);
|
||||
} catch {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
async function request(url, options = {}) {
|
||||
const reqHeaders = { ...headers, ...options.headers };
|
||||
let postType = reqHeaders['Content-Type']?.includes('json') ? 'json' :
|
||||
reqHeaders['Content-Type']?.includes('form') ? 'form' : '';
|
||||
|
||||
try {
|
||||
const response = await req(url, {
|
||||
method: options.method || 'GET',
|
||||
headers: reqHeaders,
|
||||
data: options.data,
|
||||
postType: postType,
|
||||
timeout: options.timeout || 15000
|
||||
});
|
||||
return response?.content || response?.data || response;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
const classNames = '有声书&儿童&音乐&相声&娱乐&广播剧&历史&外语';
|
||||
const classUrls = 'youshengshu&ertong&yinyue&xiangsheng&yule&guangbojv&lishi&waiyu';
|
||||
|
||||
const names = classNames.split('&');
|
||||
const urls = classUrls.split('&');
|
||||
|
||||
const classes = [];
|
||||
for (let i = 0; i < names.length && i < urls.length; i++) {
|
||||
classes.push({ type_id: urls[i], type_name: names[i] });
|
||||
}
|
||||
|
||||
const filters = {};
|
||||
classes.forEach(cls => { filters[cls.type_id] = []; });
|
||||
|
||||
return JSON.stringify({ class: classes, filters: filters });
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
return await category('youshengshu', 1, null, {});
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
const page = pg || 1;
|
||||
|
||||
try {
|
||||
const url = `${host}/m-revision/page/category/queryCategoryAlbumsByPage?sort=0&pageSize=50&page=${page}&categoryCode=${tid}`;
|
||||
const html = await request(url);
|
||||
|
||||
if (!html) {
|
||||
return JSON.stringify({ list: [], page: page, pagecount: 1, limit: 50, total: 0 });
|
||||
}
|
||||
|
||||
const data = safeJSONParse(html).data;
|
||||
const albumList = data.albumBriefDetailInfos || [];
|
||||
const videos = [];
|
||||
|
||||
albumList.forEach(it => {
|
||||
const vip = it.albumInfo?.albumVipPayType;
|
||||
if (vip === 0) {
|
||||
const id = `http://mobile.ximalaya.com/mobile/others/ca/album/track/${it.id}/true/0/200?albumId=${it.id}`;
|
||||
videos.push({
|
||||
vod_id: id,
|
||||
vod_name: it.albumInfo?.title || '未知标题',
|
||||
vod_pic: `http://imagev2.xmcdn.com/${it.albumInfo?.cover}`,
|
||||
vod_remarks: '免费'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return JSON.stringify({ list: videos, page: page, pagecount: Math.ceil(videos.length / 50) + 1, limit: 50, total: videos.length });
|
||||
} catch (e) {
|
||||
return JSON.stringify({ list: [], page: page, pagecount: 1, limit: 50, total: 0 });
|
||||
}
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
try {
|
||||
const urls = [];
|
||||
const albumIdMatch = id.match(/albumId=(\d+)/);
|
||||
const albumId = albumIdMatch ? albumIdMatch[1] : '';
|
||||
|
||||
const html = await request(id);
|
||||
const json = safeJSONParse(html);
|
||||
const album = json.album || {};
|
||||
let data = json.tracks?.list || [];
|
||||
const maxPageId = json.tracks?.maxPageId || 1;
|
||||
|
||||
data.forEach(it => {
|
||||
if (it.playPathAacv164) {
|
||||
urls.push(`${it.title}$${it.playPathAacv164}`);
|
||||
}
|
||||
});
|
||||
|
||||
if (maxPageId > 1) {
|
||||
for (let j = 2; j <= maxPageId; j++) {
|
||||
const pageUrl = id.replace('/0/', `/${j}/`);
|
||||
try {
|
||||
const pageHtml = await request(pageUrl);
|
||||
const pageJson = safeJSONParse(pageHtml);
|
||||
const pageData = pageJson.tracks?.list || [];
|
||||
pageData.forEach(it => {
|
||||
if (it.playPathAacv164) {
|
||||
urls.push(`${it.title}$${it.playPathAacv164}`);
|
||||
}
|
||||
});
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
if (urls.length === 0) return JSON.stringify({ list: [] });
|
||||
|
||||
const vod = {
|
||||
vod_id: id,
|
||||
vod_name: album.title || '暂无名称',
|
||||
vod_pic: album.coverLarge || '暂无图片',
|
||||
vod_content: album.intro || '暂无简介',
|
||||
vod_remarks: `共${urls.length}集`,
|
||||
vod_play_from: '喜马拉雅',
|
||||
vod_play_url: urls.join('#')
|
||||
};
|
||||
|
||||
return JSON.stringify({ list: [vod] });
|
||||
} catch (e) {
|
||||
return JSON.stringify({ list: [] });
|
||||
}
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
if (!id) {
|
||||
return JSON.stringify({ parse: 0, jx: 0, url: '', msg: '播放地址为空', header: {} });
|
||||
}
|
||||
return JSON.stringify({ parse: 0, jx: 0, url: id, header: {} });
|
||||
}
|
||||
|
||||
async function search(wd, quick, pg = "1") {
|
||||
const page = parseInt(pg) || 1;
|
||||
|
||||
try {
|
||||
const url = `${searchHost}/api/music/ximalaya.php?name=${encodeURIComponent(wd)}`;
|
||||
const html = await request(url);
|
||||
|
||||
if (!html) {
|
||||
return JSON.stringify({ list: [], page: page, pagecount: 0, limit: 20, total: 0 });
|
||||
}
|
||||
|
||||
const data = safeJSONParse(html).data;
|
||||
|
||||
if (!Array.isArray(data)) {
|
||||
return JSON.stringify({ list: [], page: page, pagecount: 0, limit: 20, total: 0 });
|
||||
}
|
||||
|
||||
const videos = data.map(it => {
|
||||
const id = `http://mobile.ximalaya.com/mobile/others/ca/album/track/${it.albumId}/true/0/200?albumId=${it.albumId}`;
|
||||
return {
|
||||
vod_id: id,
|
||||
vod_name: it.title || '未知标题',
|
||||
vod_pic: it.cover || '',
|
||||
vod_remarks: '喜马拉雅'
|
||||
};
|
||||
}).filter(v => v.vod_id);
|
||||
|
||||
return JSON.stringify({ list: videos, page: page, pagecount: 1, limit: videos.length, total: videos.length });
|
||||
} catch (e) {
|
||||
return JSON.stringify({ list: [], page: page, pagecount: 0, limit: 20, total: 0 });
|
||||
}
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return { init, home, homeVod, category, detail, play, search };
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
@header({
|
||||
searchable: 1,
|
||||
filterable: 1,
|
||||
quickSearch: 1,
|
||||
title: '聚合央视[视]',
|
||||
lang: 'cat'
|
||||
})
|
||||
*/
|
||||
|
||||
let siteName = '聚合央视', siteKey = '', siteType = 0;
|
||||
|
||||
const platformList = [
|
||||
{ name: '央视新闻', id: 'xinwen' },
|
||||
{ name: '央视聚场', id: 'juchang' },
|
||||
{ name: '央视大全', id: 'quan' }
|
||||
];
|
||||
|
||||
const headers = {
|
||||
'User-Agent': 'Mozilla/5.0 (Linux; Android 11; M2007J3SC Build/RKQ1.200826.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/77.0.3865.120 MQQBrowser/6.2 TBS/045714 Mobile Safari/537.36'
|
||||
};
|
||||
|
||||
const rule = {
|
||||
xinwen: { host: 'http://api.cntv.cn', videoList: '/NewVideo/getVideoListByColumn', playUrl: 'https://cntv.playdreamer.cn/proxy/asp/hls/850/0303000a/3/default/' },
|
||||
juchang: { host: 'http://api.cntv.cn', videoList: '/NewVideo/getVideoListByColumn', playUrl: 'https://cntv.playdreamer.cn/proxy/asp/hls/850/0303000a/3/default/' },
|
||||
quan: { host: 'https://api.cntv.cn', columnSearch: '/lanmu/columnSearch', videoAlbum: '/list/getVideoAlbumList', albumDetail: '/NewVideo/getVideoListByAlbumIdNew', videoInfo: '/video/videoinfoByGuid', playUrl: 'https://cntv.playdreamer.cn/proxy/asp/hls/850/0303000a/3/default/' }
|
||||
};
|
||||
|
||||
const filterOptions = {
|
||||
xinwen: [{ key: "area", name: "分类", value: [{ "n": "新闻直播间", "v": "TOPC1451559129520755" }, { "n": "中国新闻", "v": "TOPC1451539894330405" }, { "n": "朝闻天下", "v": "TOPC1451558496100826" }, { "n": "新闻联播", "v": "TOPC1451528971114112" }, { "n": "晚间新闻", "v": "TOPC1451528792881669" }, { "n": "午夜新闻", "v": "TOPC1451558779639282" }, { "n": "新闻30分", "v": "TOPC1451559097947700" }, { "n": "24小时", "v": "TOPC1451558428005729" }, { "n": "新闻1+1", "v": "TOPC1451559066181661" }, { "n": "海峡两岸", "v": "TOPC1451540328102649" }, { "n": "今日关注", "v": "TOPC1451540389082713" }, { "n": "今日亚洲", "v": "TOPC1451540448405749" }, { "n": "今日环球", "v": "TOPC1571034705435323" }, { "n": "新闻调查", "v": "TOPC1451558819463311" }, { "n": "军事报道", "v": "TOPC1451527941788652" }, { "n": "经济信息联播", "v": "TOPC1451533782742171" }, { "n": "体坛快讯", "v": "TOPC1451550970356385" }, { "n": "焦点访谈", "v": "TOPC1451558976694518" }, { "n": "东方时空", "v": "TOPC1451558532019883" }, { "n": "新闻周刊", "v": "TOPC1451559180488841" }, { "n": "一线", "v": "TOPC1451543462858283" }] }],
|
||||
juchang: [{ key: "area", name: "分类", value: [{ "n": "动画大放映", "v": "TOPC1451559025546574" }, { "n": "第一动画乐园", "v": "TOPC1451378857272262" }, { "n": "探索·发现", "v": "TOPC1451557893544236" }, { "n": "动物世界", "v": "TOPC1451378967257534" }, { "n": "人与自然", "v": "TOPC1451525103989666" }, { "n": "自然传奇", "v": "TOPC1451558150787467" }, { "n": "地理·中国", "v": "TOPC1451557421544786" }, { "n": "健康之路", "v": "TOPC1451557646802924" }, { "n": "百家讲坛", "v": "TOPC1451557052519584" }, { "n": "走进科学", "v": "TOPC1451558190239536" }, { "n": "是真的吗", "v": "TOPC1451534366388377" }, { "n": "故事里的中国", "v": "TOPC1451464884159276" }, { "n": "远方的家", "v": "TOPC1451541349400938" }, { "n": "跟着书本去旅行", "v": "TOPC1575253587571324" }, { "n": "今日说法", "v": "TOPC1451464665008914" }, { "n": "开讲啦", "v": "TOPC1451464884159276" }, { "n": "天网", "v": "TOPC1451530382483536" }, { "n": "高端访谈", "v": "TOPC1665739007799851" }, { "n": "对话", "v": "TOPC1514182710380601" }, { "n": "面对面", "v": "TOPC1451559038345600" }, { "n": "等着我", "v": "TOPC1451378757637200" }, { "n": "空中剧院", "v": "TOPC1451558856402351" }, { "n": "精彩音乐汇", "v": "TOPC1451541414450906" }, { "n": "音乐厅", "v": "TOPC1451534421925242" }, { "n": "民歌·中国", "v": "TOPC1451541994820527" }, { "n": "中国电影报道", "v": "TOPC1451354597100320" }, { "n": "星光大道", "v": "TOPC1451467630488780" }, { "n": "星推荐", "v": "TOPC1451469943519994" }, { "n": "方圆剧阵", "v": "TOPC1571217727564820" }, { "n": "正大综艺", "v": "TOPC1650782829200997" }, { "n": "第一时间", "v": "TOPC1451530259915198" }, { "n": "共同关注", "v": "TOPC1451558858788377" }, { "n": "经济半小时", "v": "TOPC1601362002656197" }, { "n": "经济大讲堂", "v": "TOPC1451533652476962" }, { "n": "正点财经", "v": "TOPC1453100395512779" }, { "n": "开门大吉", "v": "TOPC1451465894294259" }, { "n": "生活圈", "v": "TOPC1451546588784893" }, { "n": "生活提示", "v": "TOPC1451526037568184" }] }],
|
||||
quan: [{ key: "area", name: "分类", value: [{ "n": "栏目大全", "v": "栏目大全" }, { "n": "特别节目", "v": "特别节目" }, { "n": "纪录片", "v": "纪录片" }, { "n": "电视剧", "v": "电视剧" }, { "n": "动画片", "v": "动画片" }] }]
|
||||
};
|
||||
|
||||
const ruleFilterDef = {
|
||||
xinwen: { area: 'TOPC1451559129520755' },
|
||||
juchang: { area: 'TOPC1451559025546574' },
|
||||
quan: { area: '栏目大全' }
|
||||
};
|
||||
|
||||
function init(cfg) {
|
||||
siteName = cfg.skey?.split('_')[1] || cfg.skey || '聚合央视';
|
||||
siteKey = cfg.skey;
|
||||
siteType = cfg.stype;
|
||||
}
|
||||
|
||||
function safeJSONParse(str, defaultValue = {}) {
|
||||
if (!str || typeof str === 'object') return str || defaultValue;
|
||||
try {
|
||||
return JSON.parse(str);
|
||||
} catch {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
async function request(url, options = {}) {
|
||||
const reqHeaders = { ...headers, ...options.headers };
|
||||
let postType = reqHeaders['Content-Type']?.includes('json') ? 'json' :
|
||||
reqHeaders['Content-Type']?.includes('form') ? 'form' : '';
|
||||
|
||||
try {
|
||||
const response = await req(url, {
|
||||
method: options.method || 'GET',
|
||||
headers: reqHeaders,
|
||||
data: options.data,
|
||||
postType: postType,
|
||||
timeout: options.timeout || 15000
|
||||
});
|
||||
return response?.content || response?.data || response;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function getPlatList() { return platformList; }
|
||||
|
||||
async function getXinwenList(typeId, page) {
|
||||
let videos = [];
|
||||
try {
|
||||
const url = `${rule.xinwen.host}${rule.xinwen.videoList}?id=${typeId}&n=10&sort=desc&p=${page}&mode=0&serviceId=tvcctv`;
|
||||
const html = await request(url);
|
||||
const data = safeJSONParse(html);
|
||||
const list = data.data?.list || [];
|
||||
videos = list.map(item => ({ vod_id: `xinwen@${item.guid}`, vod_name: item.title || '未知视频', vod_pic: item.image || '', vod_remarks: `央视新闻 | ${item.time || ''}`, vod_content: '' }));
|
||||
} catch (e) {}
|
||||
return videos;
|
||||
}
|
||||
|
||||
async function getJuchangList(typeId, page) {
|
||||
let videos = [];
|
||||
try {
|
||||
const url = `${rule.juchang.host}${rule.juchang.videoList}?id=${typeId}&n=10&sort=desc&p=${page}&mode=0&serviceId=tvcctv`;
|
||||
const html = await request(url);
|
||||
const data = safeJSONParse(html);
|
||||
const list = data.data?.list || [];
|
||||
videos = list.map(item => ({ vod_id: `juchang@${item.guid}`, vod_name: item.title || '未知视频', vod_pic: item.image || '', vod_remarks: `央视聚场 | ${item.time || ''}`, vod_content: '' }));
|
||||
} catch (e) {}
|
||||
return videos;
|
||||
}
|
||||
|
||||
async function getQuanList(typeId, page) {
|
||||
let videos = [];
|
||||
try {
|
||||
const channelMap = { "特别节目": "CHAL1460955953877151", "纪录片": "CHAL1460955924871139", "电视剧": "CHAL1460955853485115", "动画片": "CHAL1460955899450127" };
|
||||
|
||||
if (typeId === '栏目大全') {
|
||||
const url = `${rule.quan.host}${rule.quan.columnSearch}?p=${page}&n=20&serviceId=tvcctv&t=json`;
|
||||
const html = await request(url);
|
||||
const data = safeJSONParse(html);
|
||||
const docs = data.response?.docs || [];
|
||||
videos = docs.map(item => ({ vod_id: `quan@${item.lastVIDE?.videoSharedCode}|${item.column_firstclass}|${item.column_name}|${item.channel_name}|${item.column_brief}|${item.column_logo}|${item.lastVIDE?.videoTitle}|栏目大全`, vod_name: item.column_name || '未知栏目', vod_pic: item.column_logo || '', vod_remarks: `央视大全 | ${item.channel_name || ''}`, vod_content: item.column_brief || '' }));
|
||||
} else {
|
||||
const params = { p: page, n: 24, serviceId: 'tvcctv', t: 'json', channelid: channelMap[typeId] || '', fc: encodeURIComponent(typeId) };
|
||||
const queryString = Object.keys(params).map(key => `${key}=${params[key]}`).join('&');
|
||||
const url = `${rule.quan.host}${rule.quan.videoAlbum}?${queryString}`;
|
||||
const html = await request(url);
|
||||
const data = safeJSONParse(html);
|
||||
const list = data.data?.list || [];
|
||||
videos = list.map(item => ({ vod_id: `quan@${item.id}|${item.sc}|${item.title}|${item.channel}|${item.brief}|${item.image}|${item.count}|${typeId}`, vod_name: item.title || '未知视频', vod_pic: item.image || '', vod_remarks: `央视大全 | ${item.sc || ''}${item.year ? '·' + item.year : ''}${item.area ? '·' + item.area : ''}`, vod_content: item.brief || '' }));
|
||||
}
|
||||
} catch (e) {}
|
||||
return videos;
|
||||
}
|
||||
|
||||
async function getXinwenDetail(id) {
|
||||
return { vod_id: id, vod_name: '', vod_remarks: '', vod_play_from: '央视新闻', vod_play_url: `点击播放$${id}` };
|
||||
}
|
||||
|
||||
async function getJuchangDetail(id) {
|
||||
return { vod_id: id, vod_name: '', vod_remarks: '', vod_play_from: '央视聚场', vod_play_url: `点击播放$${id}` };
|
||||
}
|
||||
|
||||
async function getQuanDetail(did) {
|
||||
let vod = {};
|
||||
try {
|
||||
const info = did.split("|");
|
||||
const cate = info[7];
|
||||
const ctid = info[0];
|
||||
const modeMap = { "特别节目": "0", "纪录片": "0", "电视剧": "0", "动画片": "1" };
|
||||
const mode = modeMap[cate] || '0';
|
||||
const albumUrl = `${rule.quan.host}${rule.quan.albumDetail}?id=${ctid}&serviceId=tvcctv&p=1&n=100&mode=${mode}&pub=1`;
|
||||
const html = await request(albumUrl);
|
||||
const data = safeJSONParse(html);
|
||||
let playUrls = [];
|
||||
if (data.errcode === '1001') {
|
||||
const videoInfoUrl = `${rule.quan.host}${rule.quan.videoInfo}?guid=${ctid}&serviceId=tvcctv`;
|
||||
const vInfoRes = await request(videoInfoUrl);
|
||||
const vInfoData = safeJSONParse(vInfoRes);
|
||||
const realCtid = vInfoData.ctid;
|
||||
const columnUrl = `${rule.quan.host}/NewVideo/getVideoListByColumn?id=${realCtid}&d=&p=1&n=100&sort=desc&mode=0&serviceId=tvcctv&t=json`;
|
||||
const colRes = await request(columnUrl);
|
||||
const colData = safeJSONParse(colRes);
|
||||
playUrls = colData.data?.list || [];
|
||||
} else {
|
||||
playUrls = data.data?.list || [];
|
||||
}
|
||||
const playList = playUrls.map(item => { const title = item.title || `第${item.index || '?'}集`; const cleanTitle = title.replace(/\$/g, ''); const guid = item.guid || ''; return `${cleanTitle}$${guid}`; });
|
||||
vod = { vod_id: did, vod_name: info[2] || '', vod_pic: info[5] || '', vod_content: info[4] || '', vod_remarks: info[6] ? `共${info[6]}集` : '', vod_play_from: playList.length > 0 ? '央视大全' : '', vod_play_url: playList.length > 0 ? playList.join('#') : '' };
|
||||
} catch (e) {}
|
||||
return vod;
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
const platForms = getPlatList();
|
||||
const classes = platForms.map(item => ({ type_name: item.name, type_id: item.id, type_flag: '[CFS][SUBSITE2][FILTERBAR]' }));
|
||||
const filters = {};
|
||||
platForms.forEach(item => { if (filterOptions[item.id]) filters[item.id] = filterOptions[item.id]; });
|
||||
return JSON.stringify({ class: classes, filters: filters });
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
const platForms = getPlatList();
|
||||
const randomPlat = platForms[Math.floor(Math.random() * platForms.length)];
|
||||
const randomArea = ruleFilterDef[randomPlat.id]?.area || '';
|
||||
const categoryResult = await category(randomPlat.id, 1, { area: randomArea }, {});
|
||||
const categoryList = safeJSONParse(categoryResult).list || [];
|
||||
return JSON.stringify({ list: categoryList });
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
const page = pg || 1;
|
||||
extend = extend || {};
|
||||
const platformItem = platformList.find(p => p.id === tid);
|
||||
if (!platformItem) return JSON.stringify({ list: [], page, pagecount: 1, limit: 0, total: 0 });
|
||||
const searchKeyword = extend?.custom;
|
||||
if (searchKeyword) return await cfs(tid, searchKeyword, pg);
|
||||
const area = filter?.area || extend?.area || ruleFilterDef[tid]?.area || '';
|
||||
const videos = [];
|
||||
switch (tid) {
|
||||
case 'xinwen': videos.push(...await getXinwenList(area, page)); break;
|
||||
case 'juchang': videos.push(...await getJuchangList(area, page)); break;
|
||||
case 'quan': videos.push(...await getQuanList(area, page)); break;
|
||||
}
|
||||
return JSON.stringify({ list: videos, page: page, pagecount: page + 1, limit: videos.length, total: videos.length * (page + 1) });
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
const parts = id.split('@');
|
||||
const platform = parts[0];
|
||||
const did = parts.slice(1).join('@');
|
||||
let vod = {};
|
||||
if (platform === 'xinwen') vod = await getXinwenDetail(did);
|
||||
else if (platform === 'juchang') vod = await getJuchangDetail(did);
|
||||
else if (platform === 'quan') vod = await getQuanDetail(did);
|
||||
return JSON.stringify({ list: [vod] });
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
const playUrl = `${rule.xinwen.playUrl}${id}/850.m3u8`;
|
||||
return JSON.stringify({ parse: 0, url: playUrl, header: headers });
|
||||
}
|
||||
|
||||
async function cfs(siteId, wd, pg) {
|
||||
return JSON.stringify({ list: [], page: pg || 1, pagecount: 1, limit: 0, total: 0 });
|
||||
}
|
||||
|
||||
async function search(wd, quick, pg) {
|
||||
return JSON.stringify({ list: [], page: pg || 1, pagecount: 1, limit: 0, total: 0 });
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return { init, home, homeVod, category, detail, play, search };
|
||||
}
|
||||
@@ -0,0 +1,708 @@
|
||||
/*
|
||||
@header({
|
||||
searchable: 1,
|
||||
filterable: 1,
|
||||
quickSearch: 1,
|
||||
title: '聚合音乐[听]',
|
||||
lang: 'cat'
|
||||
})
|
||||
*/
|
||||
|
||||
let siteName = '聚合音乐', siteKey = '', siteType = 0;
|
||||
|
||||
const platformList = [
|
||||
{ name: '网易云音乐', id: 'wangyi' },
|
||||
{ name: '听海音乐', id: 'tinghai' },
|
||||
{ name: '米兔音乐', id: 'mitu' }
|
||||
];
|
||||
|
||||
const QUALITY_MAP = [
|
||||
["超高", "hrMusic"], ["无损", "sqMusic"], ["极高", "hMusic"],
|
||||
["较高", "mMusic"], ["标准", "lMusic"]
|
||||
];
|
||||
|
||||
const rule = {
|
||||
wangyi: {
|
||||
host: 'https://music.163.com',
|
||||
playApi: 'http://oiapi.net/api/Music_163',
|
||||
searchApi: 'http://mc.alger.fun/api/cloudsearch',
|
||||
toplist: '/api/toplist',
|
||||
hotPlaylist: '/api/playlist/list',
|
||||
topArtists: '/api/artist/top',
|
||||
personalized: '/api/personalized/playlist',
|
||||
artistDetail: '/api/artist/',
|
||||
playlistDetail: '/api/playlist/detail',
|
||||
songDetail: '/api/song/detail',
|
||||
songLyric: '/api/song/lyric',
|
||||
referer: 'https://music.163.com/'
|
||||
},
|
||||
tinghai: {
|
||||
host: 'http://wapi.kuwo.cn',
|
||||
tagPlaylist: '/api/pc/classify/playlist/getTagPlayList',
|
||||
playlistInfo: '/api/www/playlist/playListInfo',
|
||||
songUrl: 'https://nmobi.kuwo.cn/mobi.s',
|
||||
lyricApi: 'https://kuwo.cn/openapi/v1/www/lyric/getlyric',
|
||||
searchApi: 'https://search.kuwo.cn/r.s',
|
||||
picApi: 'http://artistpicserver.kuwo.cn/pic.web',
|
||||
referer: 'https://kuwo.cn/'
|
||||
},
|
||||
mitu: {
|
||||
host: 'https://www.qqmp3.vip',
|
||||
songsApi: '/api/songs.php',
|
||||
kwApi: '/api/kw.php',
|
||||
referer: 'https://www.qqmp3.vip/'
|
||||
}
|
||||
};
|
||||
|
||||
const baseHeaders = {
|
||||
'User-Agent': '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 filterOptions = {
|
||||
wangyi: [{ key: "area", name: "分类", value: [{ "n": "推荐歌单", "v": "recommend" }, { "n": "排行榜", "v": "toplist" }, { "n": "热门歌单", "v": "hot" }, { "n": "热门歌手", "v": "artist" }] }],
|
||||
tinghai: [{ key: "area", name: "分类", value: [{ "n": "专区", "v": "12" }, { "n": "主题", "v": "2189" }, { "n": "心情", "v": "146" }, { "n": "场景", "v": "376" }, { "n": "年代", "v": "637" }, { "n": "曲风流派", "v": "393" }, { "n": "语言", "v": "37" }] }],
|
||||
mitu: [{ key: "area", name: "分类", value: [{ "n": "热门", "v": "hot" }, { "n": "新歌", "v": "new" }, { "n": "随机", "v": "rand" }] }]
|
||||
};
|
||||
|
||||
const ruleFilterDef = {
|
||||
wangyi: { area: 'recommend' },
|
||||
tinghai: { area: '12' },
|
||||
mitu: { area: 'hot' }
|
||||
};
|
||||
|
||||
function init(cfg) {
|
||||
siteName = cfg.skey?.split('_')[1] || cfg.skey || '聚合音乐';
|
||||
siteKey = cfg.skey;
|
||||
siteType = cfg.stype;
|
||||
}
|
||||
|
||||
function safeJSONParse(str, defaultValue = {}) {
|
||||
if (!str || typeof str === 'object') return str || defaultValue;
|
||||
try {
|
||||
return JSON.parse(str);
|
||||
} catch {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
async function request(url, options = {}, retries = 2, platform = null) {
|
||||
let headers = { ...baseHeaders };
|
||||
if (platform && rule[platform] && rule[platform].referer) {
|
||||
headers['Referer'] = rule[platform].referer;
|
||||
}
|
||||
if (options.headers) {
|
||||
headers = { ...headers, ...options.headers };
|
||||
}
|
||||
|
||||
let reqHeaders = { ...headers };
|
||||
let postType = reqHeaders['Content-Type']?.includes('json') ? 'json' :
|
||||
reqHeaders['Content-Type']?.includes('form') ? 'form' : '';
|
||||
|
||||
for (let i = 0; i < retries; i++) {
|
||||
try {
|
||||
const response = await req(url, {
|
||||
method: options.method || 'GET',
|
||||
headers: reqHeaders,
|
||||
data: options.data,
|
||||
postType: postType,
|
||||
timeout: options.timeout || 10000
|
||||
});
|
||||
const content = response?.content || response?.data || response;
|
||||
if (content && content.length > 50) return content;
|
||||
if (i < retries - 1) await sleep(0.5);
|
||||
return content;
|
||||
} catch (e) {
|
||||
if (i === retries - 1) return '';
|
||||
await sleep(0.5);
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function sleep(seconds) {
|
||||
return new Promise(resolve => setTimeout(resolve, seconds * 1000));
|
||||
}
|
||||
|
||||
function hd(img) {
|
||||
if (!img) return '';
|
||||
return img.replace('/120/', '/4000/').replace('/500/', '/2160/').replace('/150/', '/1000/').replace('/300/', '/1500/');
|
||||
}
|
||||
|
||||
function getPicUrl(pic, size = '500y500') {
|
||||
if (!pic) return '';
|
||||
return pic + '?param=' + size;
|
||||
}
|
||||
|
||||
function getSongPic(song, defaultPic, size = '300y300') {
|
||||
let pic = song.al?.picUrl || song.album?.picUrl || defaultPic;
|
||||
if (pic) {
|
||||
pic = pic.replace('?param=500y500', '?param=' + size);
|
||||
pic = pic.replace('?param=300y300', '?param=' + size);
|
||||
}
|
||||
return pic || '';
|
||||
}
|
||||
|
||||
function buildWangyiPlayData(tracks, defaultPic) {
|
||||
let songPicArr = tracks.map(s => getSongPic(s, defaultPic, '300y300'));
|
||||
const playPic = songPicArr.join('#');
|
||||
|
||||
const playUrl = QUALITY_MAP.map(q =>
|
||||
tracks.map(s => {
|
||||
let songPic = getSongPic(s, defaultPic, '300y300');
|
||||
let artistName = s.ar?.map(a => a.name).join('/') || s.artists?.map(a => a.name).join('/') || '';
|
||||
let displayName = artistName ? `${s.name} - ${artistName}` : s.name;
|
||||
return `${displayName}$${s.id}|${q[1]}&&${songPic}`;
|
||||
}).join('#')
|
||||
).join('$$$');
|
||||
|
||||
const playFrom = QUALITY_MAP.map(q => q[0]).join('$$$');
|
||||
|
||||
return { playFrom, playUrl, playPic };
|
||||
}
|
||||
|
||||
function formatNumber(num) {
|
||||
if (!num) return '0';
|
||||
if (num >= 10000) return (num / 10000).toFixed(1) + '万';
|
||||
return num.toString();
|
||||
}
|
||||
|
||||
function getPlatList() {
|
||||
return platformList;
|
||||
}
|
||||
|
||||
async function getWangyiList(type, page, extend) {
|
||||
const limit = 20;
|
||||
const offset = (page - 1) * limit;
|
||||
let videos = [];
|
||||
|
||||
try {
|
||||
let url = '';
|
||||
let rawData = [];
|
||||
|
||||
switch (type) {
|
||||
case 'recommend':
|
||||
url = `${rule.wangyi.host}${rule.wangyi.personalized}?limit=${page * limit}`;
|
||||
break;
|
||||
case 'toplist':
|
||||
url = `${rule.wangyi.host}${rule.wangyi.toplist}`;
|
||||
break;
|
||||
case 'hot':
|
||||
const cat = extend?.cat || '全部';
|
||||
url = `${rule.wangyi.host}${rule.wangyi.hotPlaylist}?cat=${encodeURIComponent(cat)}&limit=${limit}&offset=${offset}&order=hot`;
|
||||
break;
|
||||
case 'artist':
|
||||
url = `${rule.wangyi.host}${rule.wangyi.topArtists}?limit=${limit}&offset=${offset}`;
|
||||
break;
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
|
||||
const html = await request(url, {}, 2, 'wangyi');
|
||||
const json = safeJSONParse(html);
|
||||
|
||||
if (type === 'recommend') {
|
||||
rawData = json.result || [];
|
||||
if (page > 1) rawData = rawData.slice(offset);
|
||||
videos = rawData.map(it => ({
|
||||
vod_id: `wangyi@playlist@${it.id}`,
|
||||
vod_name: it.name,
|
||||
vod_pic: getPicUrl(it.picUrl, '300y300'),
|
||||
vod_remarks: `网易云 | 🎧${formatNumber(it.playCount || 0)}`,
|
||||
vod_content: ''
|
||||
}));
|
||||
} else if (type === 'toplist') {
|
||||
rawData = json.list || [];
|
||||
videos = rawData.slice(offset, offset + limit).map(it => ({
|
||||
vod_id: `wangyi@toplist@${it.id}`,
|
||||
vod_name: it.name,
|
||||
vod_pic: getPicUrl(it.coverImgUrl || it.picUrl, '300y300'),
|
||||
vod_remarks: `网易云 | ${it.updateFrequency || `${it.trackCount || 0}首歌曲`}`,
|
||||
vod_content: ''
|
||||
}));
|
||||
} else if (type === 'artist') {
|
||||
rawData = json.artists || [];
|
||||
videos = rawData.map(it => ({
|
||||
vod_id: `wangyi@artist@${it.id}`,
|
||||
vod_name: it.name,
|
||||
vod_pic: getPicUrl(it.img1v1Url || it.picUrl, '300y300'),
|
||||
vod_remarks: `网易云 | ${it.albumSize || 0}张专辑`,
|
||||
vod_content: ''
|
||||
}));
|
||||
} else {
|
||||
rawData = json.playlists || [];
|
||||
videos = rawData.map(it => ({
|
||||
vod_id: `wangyi@playlist@${it.id}`,
|
||||
vod_name: it.name,
|
||||
vod_pic: getPicUrl(it.coverImgUrl, '300y300'),
|
||||
vod_remarks: `网易云 | ${it.playCount ? formatNumber(it.playCount) : ''}`,
|
||||
vod_content: ''
|
||||
}));
|
||||
}
|
||||
} catch (e) {}
|
||||
return videos;
|
||||
}
|
||||
|
||||
async function getTinghaiList(categoryId, page) {
|
||||
let videos = [];
|
||||
try {
|
||||
const url = `${rule.tinghai.host}${rule.tinghai.tagPlaylist}?pn=${page}&rn=30&id=${categoryId}`;
|
||||
const html = await request(url, {}, 2, 'tinghai');
|
||||
const json = safeJSONParse(html);
|
||||
const data = json.data?.data || [];
|
||||
videos = data.map(item => ({
|
||||
vod_id: `tinghai@${categoryId}@${item.id || item.pid}`,
|
||||
vod_name: item.name || item.title || '未命名歌单',
|
||||
vod_pic: hd(item.img || item.pic || item.cover || ''),
|
||||
vod_remarks: `听海音乐 | ${item.listencnt ? formatNumber(item.listencnt) : ''}`,
|
||||
vod_content: item.info || item.userName || ''
|
||||
}));
|
||||
} catch (e) {}
|
||||
return videos;
|
||||
}
|
||||
|
||||
async function getMituList(type, page) {
|
||||
let apiPath = '';
|
||||
if (type === 'hot') apiPath = 'api/songs.php';
|
||||
else if (type === 'new') apiPath = 'api/songs.php?type=new';
|
||||
else apiPath = 'api/songs.php?type=rand';
|
||||
|
||||
const url = `${rule.mitu.host}/${apiPath}`;
|
||||
let videos = [];
|
||||
|
||||
try {
|
||||
const html = await request(url, {}, 2, 'mitu');
|
||||
const json = safeJSONParse(html);
|
||||
if (json.code === 200 && Array.isArray(json.data)) {
|
||||
videos = json.data.map(item => {
|
||||
const vodData = { id: item.rid, name: item.name, pic: item.pic, artist: item.artist, downurl: item.downurl || [] };
|
||||
const vodId = `mitu@${type}@${encodeURIComponent(JSON.stringify(vodData))}`;
|
||||
return {
|
||||
vod_id: vodId,
|
||||
vod_name: `${item.name} - ${item.artist}`,
|
||||
vod_pic: item.pic || '',
|
||||
vod_remarks: `米兔音乐 | ${type === 'hot' ? '热门' : (type === 'new' ? '新歌' : '随机')}`,
|
||||
vod_content: `歌手:${item.artist}`
|
||||
};
|
||||
});
|
||||
}
|
||||
} catch (e) {}
|
||||
return videos;
|
||||
}
|
||||
|
||||
async function getWangyiDetail(type, id) {
|
||||
let vod = {};
|
||||
try {
|
||||
let url = '';
|
||||
let data = {};
|
||||
let tracks = [];
|
||||
|
||||
if (type === 'artist') {
|
||||
url = `${rule.wangyi.host}${rule.wangyi.artistDetail}${id}`;
|
||||
const html = await request(url, {}, 2, 'wangyi');
|
||||
const json = safeJSONParse(html);
|
||||
data = json.artist || {};
|
||||
tracks = json.hotSongs || [];
|
||||
const defaultPic = getPicUrl(data.picUrl || data.img1v1Url, '500y500');
|
||||
const { playFrom, playUrl, playPic } = buildWangyiPlayData(tracks, defaultPic);
|
||||
vod = {
|
||||
vod_id: `wangyi@artist@${id}`,
|
||||
vod_name: data.name || '未知歌手',
|
||||
vod_pic: defaultPic,
|
||||
vod_content: data.briefDesc || data.name,
|
||||
vod_remarks: `共${tracks.length}首`,
|
||||
vod_play_from: playFrom,
|
||||
vod_play_url: playUrl,
|
||||
vod_play_pic: playPic,
|
||||
vod_play_pic_ratio: 1.0
|
||||
};
|
||||
} else {
|
||||
url = `${rule.wangyi.host}${rule.wangyi.playlistDetail}?id=${id}`;
|
||||
const html = await request(url, {}, 2, 'wangyi');
|
||||
const json = safeJSONParse(html);
|
||||
const playlist = json.result || json.playlist || {};
|
||||
data = playlist;
|
||||
tracks = playlist.tracks || [];
|
||||
const defaultPic = getPicUrl(data.coverImgUrl || data.picUrl, '500y500');
|
||||
const { playFrom, playUrl, playPic } = buildWangyiPlayData(tracks, defaultPic);
|
||||
vod = {
|
||||
vod_id: `wangyi@playlist@${id}`,
|
||||
vod_name: data.name || '未知歌单',
|
||||
vod_pic: defaultPic,
|
||||
vod_content: data.description || data.name,
|
||||
vod_remarks: `${formatNumber(data.playCount || 0)} | 共${tracks.length}首`,
|
||||
vod_play_from: playFrom,
|
||||
vod_play_url: playUrl,
|
||||
vod_play_pic: playPic,
|
||||
vod_play_pic_ratio: 1.0
|
||||
};
|
||||
}
|
||||
} catch (e) {}
|
||||
return vod;
|
||||
}
|
||||
|
||||
async function getTinghaiDetail(id) {
|
||||
let vod = {};
|
||||
try {
|
||||
const limit = 100;
|
||||
let baseUrl = `${rule.tinghai.host}${rule.tinghai.playlistInfo}?pid=${id}&rn=${limit}&httpsStatus=1&pn=`;
|
||||
let html = await request(baseUrl + '1', {}, 2, 'tinghai');
|
||||
let json = safeJSONParse(html);
|
||||
let data = json.data || {};
|
||||
let songs = data.musicList || data.musiclist || [];
|
||||
let total = parseInt(data.total || 0);
|
||||
|
||||
if (total > limit) {
|
||||
let tasks = [];
|
||||
for (let p = 2; p <= Math.min(Math.ceil(total / limit), 5); p++) {
|
||||
tasks.push(request(baseUrl + p, {}, 2, 'tinghai'));
|
||||
}
|
||||
let results = await Promise.all(tasks);
|
||||
results.forEach(r => {
|
||||
let d = safeJSONParse(r).data || {};
|
||||
songs = songs.concat(d.musicList || d.musiclist || []);
|
||||
});
|
||||
}
|
||||
|
||||
let playArr = [];
|
||||
let songPicArr = [];
|
||||
songs.forEach(it => {
|
||||
let rid = (it.rid || it.musicrid || '').toString().replace('MUSIC_', '');
|
||||
let song = (it.name || '').replace(/ /g, ' ');
|
||||
let artist = (it.artist || '').replace(/ /g, ' ');
|
||||
let albumpic = hd(it.albumpic || it.pic);
|
||||
let displayName = artist ? `${song} [${artist}]` : song;
|
||||
if (rid) {
|
||||
playArr.push(`${displayName}$${rid}&&${albumpic}&&${albumpic}`);
|
||||
songPicArr.push(albumpic);
|
||||
}
|
||||
});
|
||||
|
||||
vod = {
|
||||
vod_id: id,
|
||||
vod_name: data.name || '听海歌单',
|
||||
vod_pic: hd(data.img || data.img500),
|
||||
vod_content: data.info || '',
|
||||
vod_remarks: `共${songs.length}首`,
|
||||
vod_play_from: "听海音乐",
|
||||
vod_play_url: playArr.join('#'),
|
||||
vod_play_pic: songPicArr.join('#'),
|
||||
vod_play_pic_ratio: 1.0
|
||||
};
|
||||
} catch (e) {}
|
||||
return vod;
|
||||
}
|
||||
|
||||
async function getMituDetail(encodedData) {
|
||||
let vod = {};
|
||||
try {
|
||||
const songData = safeJSONParse(encodedData);
|
||||
const { id: rid, name, pic, artist, downurl } = songData;
|
||||
let playUrl = '';
|
||||
let rawLrc = '暂无歌词';
|
||||
const res = await request(`${rule.mitu.host}${rule.mitu.kwApi}?rid=${rid}&type=json&level=exhigh&lrc=true`, {}, 2, 'mitu');
|
||||
const data = safeJSONParse(res);
|
||||
if (data.code === 200 && data.data) {
|
||||
playUrl = data.data.url || '';
|
||||
rawLrc = data.data.lrc || '暂无歌词';
|
||||
}
|
||||
const displayLrc = rawLrc === '暂无歌词' ? rawLrc : rawLrc.replace(/\[\d{2}:\d{2}\.\d{2}\]/g, '\n');
|
||||
let playFrom = [];
|
||||
let playUrls = [];
|
||||
if (playUrl) {
|
||||
playFrom.push('在线播放');
|
||||
playUrls.push(`第1集$${JSON.stringify({ url: playUrl, lrc: rawLrc, cover: pic })}`);
|
||||
}
|
||||
if (downurl && downurl.length) {
|
||||
playFrom.push('网盘下载');
|
||||
const downUrls = downurl.map(item => { const [n, u] = item.split('$$'); return `${n}$push://${u}`; }).join('#');
|
||||
playUrls.push(downUrls);
|
||||
}
|
||||
vod = {
|
||||
vod_id: rid,
|
||||
vod_name: name,
|
||||
vod_pic: pic,
|
||||
vod_content: displayLrc,
|
||||
vod_actor: artist,
|
||||
vod_play_from: playFrom.join('$$$'),
|
||||
vod_play_url: playUrls.join('$$$')
|
||||
};
|
||||
} catch (e) {}
|
||||
return vod;
|
||||
}
|
||||
|
||||
async function playWangyi(id) {
|
||||
try {
|
||||
const [musicId, qualityType] = id.split('|');
|
||||
const playApi = `${rule.wangyi.playApi}&id=${musicId}`;
|
||||
const playJson = safeJSONParse(await request(playApi, {}, 2, 'wangyi'));
|
||||
let songUrl = '';
|
||||
if (playJson && playJson.code === 0 && playJson.data && playJson.data.length > 0) {
|
||||
songUrl = playJson.data[0].url || '';
|
||||
}
|
||||
const lyricApi = `${rule.wangyi.host}${rule.wangyi.songLyric}?id=${musicId}&lv=1&kv=1&tv=-1`;
|
||||
const lyricJson = safeJSONParse(await request(lyricApi, {}, 2, 'wangyi'));
|
||||
let lyric = lyricJson.lrc?.lyric || '';
|
||||
if (lyricJson.tlyric?.lyric) lyric = lyric + '\n\n【翻译】\n' + lyricJson.tlyric.lyric;
|
||||
const infoApi = `${rule.wangyi.host}${rule.wangyi.songDetail}?ids=[${musicId}]`;
|
||||
const infoJson = safeJSONParse(await request(infoApi, {}, 2, 'wangyi'));
|
||||
let cover = '';
|
||||
if (infoJson.songs && infoJson.songs[0]) {
|
||||
const song = infoJson.songs[0];
|
||||
if (song.album && song.album.picUrl) cover = song.album.picUrl + '?param=500y500';
|
||||
else if (song.al && song.al.picUrl) cover = song.al.picUrl + '?param=500y500';
|
||||
}
|
||||
return JSON.stringify({ parse: 0, url: songUrl, header: baseHeaders, lrc: lyric, cover: cover, pic: cover, height: 720 });
|
||||
} catch (e) {
|
||||
return JSON.stringify({ parse: 0, url: id });
|
||||
}
|
||||
}
|
||||
|
||||
async function getTinghaiSongUrl(rid, br) {
|
||||
const url = `${rule.tinghai.songUrl}?f=web&user=0&source=kwplayer_ar_4.4.2.7_B_nuoweida_vh.apk&type=convert_url_with_sign&rid=${rid}&format=flac&br=${br}`;
|
||||
const html = await request(url, {}, 2, 'tinghai');
|
||||
const json = safeJSONParse(html);
|
||||
return json?.data?.url?.trim() || '';
|
||||
}
|
||||
|
||||
async function getTinghaiLyric(rid) {
|
||||
for (let i = 0; i < 20; i++) {
|
||||
try {
|
||||
const url = `${rule.tinghai.lyricApi}?musicId=${rid}`;
|
||||
const html = await request(url, {}, 2, 'tinghai');
|
||||
if (html) {
|
||||
const json = safeJSONParse(html);
|
||||
if (json.code === 200 && json.data && json.data.lrclist && json.data.lrclist.length > 0) {
|
||||
const lrclist = json.data.lrclist;
|
||||
const lyric = lrclist.map(item => {
|
||||
const time = parseFloat(item.time) || 0;
|
||||
const min = Math.floor(time / 60).toString().padStart(2, '0');
|
||||
const sec = Math.floor(time % 60).toString().padStart(2, '0');
|
||||
const ms = Math.floor((time % 1) * 100).toString().padStart(2, '0');
|
||||
return `[${min}:${sec}.${ms}]${item.lineLyric || ''}`;
|
||||
}).join('\n');
|
||||
return lyric;
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
if (i < 19) await sleep(0.01);
|
||||
}
|
||||
return '暂无歌词';
|
||||
}
|
||||
|
||||
async function playTinghai(id) {
|
||||
try {
|
||||
const parts = id.split('&&');
|
||||
const firstPart = parts[0] || '';
|
||||
const firstParts = firstPart.split('$');
|
||||
const songId = firstParts.length > 1 ? firstParts[1] : firstParts[0];
|
||||
const albumPic = hd(parts[1]);
|
||||
let url = await getTinghaiSongUrl(songId, '320kmp3');
|
||||
if (!url) url = await getTinghaiSongUrl(songId, '128kmp3');
|
||||
let lrc = await getTinghaiLyric(songId);
|
||||
let picUrl = albumPic;
|
||||
if (!picUrl) {
|
||||
try {
|
||||
let picRes = await request(`${rule.tinghai.picApi}?type=rid_pic&pictype=url&size=500&rid=${songId}`, {}, 2, 'tinghai');
|
||||
picUrl = picRes.trim().replace('/500/', '/2160/');
|
||||
} catch (e) {}
|
||||
}
|
||||
const result = { parse: 0, url: url || '', header: baseHeaders, height: 720 };
|
||||
if (picUrl) { result.pic = picUrl; result.cover = picUrl; }
|
||||
if (lrc && lrc !== '暂无歌词') result.lrc = lrc;
|
||||
return JSON.stringify(result);
|
||||
} catch (e) {
|
||||
return JSON.stringify({ parse: 0, url: id });
|
||||
}
|
||||
}
|
||||
|
||||
async function playMitu(id) {
|
||||
try {
|
||||
const playData = safeJSONParse(id);
|
||||
let subt;
|
||||
if (playData.lrc && playData.lrc !== '暂无歌词') {
|
||||
subt = 'data:text/plain;charset=utf-8,' + encodeURIComponent(playData.lrc);
|
||||
}
|
||||
return JSON.stringify({
|
||||
parse: 0,
|
||||
url: playData.url,
|
||||
header: { ...baseHeaders, 'Referer': rule.mitu.referer },
|
||||
lrc: playData.lrc,
|
||||
subt,
|
||||
cover: playData.cover,
|
||||
pic: playData.cover,
|
||||
height: 720
|
||||
});
|
||||
} catch (e) {
|
||||
return JSON.stringify({ parse: 0, url: id });
|
||||
}
|
||||
}
|
||||
|
||||
async function searchWangyi(wd) {
|
||||
const results = [];
|
||||
const searchTypes = [
|
||||
{ type: 1, prefix: 'wangyi@song@', remark: '歌曲', key: 'songs' },
|
||||
{ type: 10, prefix: 'wangyi@album@', remark: '专辑', key: 'albums' },
|
||||
{ type: 1000, prefix: 'wangyi@playlist@', remark: '歌单', key: 'playlists' },
|
||||
{ type: 100, prefix: 'wangyi@artist@', remark: '歌手', key: 'artists' }
|
||||
];
|
||||
try {
|
||||
for (const st of searchTypes) {
|
||||
const url = `${rule.wangyi.searchApi}?keywords=${encodeURIComponent(wd)}&type=${st.type}`;
|
||||
const res = await request(url, {}, 2, 'wangyi');
|
||||
const json = safeJSONParse(res);
|
||||
if (json.result?.[st.key]) {
|
||||
for (const item of json.result[st.key]) {
|
||||
const result = { vod_id: `${st.prefix}${item.id}`, vod_name: item.name, vod_remarks: st.remark, vod_pic: '' };
|
||||
if (st.type === 1) {
|
||||
if (item.ar) result.vod_name += ' - ' + item.ar.map(a => a.name).join('/');
|
||||
if (item.al?.picUrl) result.vod_pic = getPicUrl(item.al.picUrl, '300y300');
|
||||
} else if (st.type === 10) {
|
||||
if (item.artist) result.vod_name += ' - ' + item.artist.name;
|
||||
if (item.picUrl) result.vod_pic = getPicUrl(item.picUrl, '300y300');
|
||||
} else if (st.type === 1000) {
|
||||
if (item.coverImgUrl) result.vod_pic = getPicUrl(item.coverImgUrl, '300y300');
|
||||
result.vod_remarks += ` | ${formatNumber(item.playCount || 0)}`;
|
||||
} else if (st.type === 100) {
|
||||
const picUrl = item.picUrl || item.img1v1Url;
|
||||
if (picUrl) result.vod_pic = getPicUrl(picUrl, '300y300');
|
||||
}
|
||||
results.push(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
return results;
|
||||
}
|
||||
|
||||
async function searchTinghai(wd, page) {
|
||||
const results = [];
|
||||
const offset = (page - 1) * 30;
|
||||
const url = `${rule.tinghai.searchApi}?client=kt&all=${encodeURIComponent(wd)}&pn=${offset}&rn=30&vipver=1&ft=music&encoding=utf8&rformat=json&mobi=1`;
|
||||
try {
|
||||
let html = '';
|
||||
let retry = 0;
|
||||
while (!html && retry < 3) {
|
||||
html = await request(url, {}, 2, 'tinghai');
|
||||
if (!html) await sleep(0.1);
|
||||
retry++;
|
||||
}
|
||||
if (html) {
|
||||
const json = safeJSONParse(html.replace(/'/g, '"'));
|
||||
if (json.abslist) {
|
||||
json.abslist.forEach(it => {
|
||||
const rid = it.DC_TARGETID || it.MUSICRID?.replace('MUSIC_', '') || '';
|
||||
const pic = it.web_albumpic_short ? `http://img1.kuwo.cn/star/albumcover/${it.web_albumpic_short}` : (it.hts_MVPIC || '');
|
||||
results.push({
|
||||
vod_id: `tinghai@song@${rid}`,
|
||||
vod_name: `${it.SONGNAME || it.NAME || '未知歌曲'} - ${it.ARTIST || '未知歌手'}`,
|
||||
vod_pic: hd(pic),
|
||||
vod_remarks: it.ALBUM || '听海音乐',
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
return results;
|
||||
}
|
||||
|
||||
async function searchMitu(wd) {
|
||||
const results = [];
|
||||
const url = `${rule.mitu.host}/api/songs.php?type=search&keyword=${encodeURIComponent(wd)}`;
|
||||
try {
|
||||
const html = await request(url, {}, 2, 'mitu');
|
||||
const json = safeJSONParse(html);
|
||||
if (json.code === 200 && Array.isArray(json.data)) {
|
||||
json.data.forEach(item => {
|
||||
const vodData = { id: item.rid, name: item.name, pic: item.pic, artist: item.artist, downurl: item.downurl || [] };
|
||||
results.push({
|
||||
vod_id: `mitu@song@${encodeURIComponent(JSON.stringify(vodData))}`,
|
||||
vod_name: `${item.name} - ${item.artist}`,
|
||||
vod_pic: item.pic || '',
|
||||
vod_remarks: '米兔音乐'
|
||||
});
|
||||
});
|
||||
}
|
||||
} catch (e) {}
|
||||
return results;
|
||||
}
|
||||
|
||||
async function cfs(siteId, wd, pg) {
|
||||
const page = pg || 1;
|
||||
let results = [];
|
||||
if (siteId === 'wangyi') results = await searchWangyi(wd);
|
||||
else if (siteId === 'tinghai') results = await searchTinghai(wd, page);
|
||||
else if (siteId === 'mitu') results = await searchMitu(wd);
|
||||
return JSON.stringify({ list: results, page: page, pagecount: page + 1, limit: results.length, total: results.length * (page + 1) });
|
||||
}
|
||||
|
||||
async function home(filter) {
|
||||
const platForms = getPlatList();
|
||||
const classes = platForms.map(item => ({ type_name: item.name, type_id: item.id }));
|
||||
const filters = {};
|
||||
platForms.forEach(item => { if (filterOptions[item.id]) filters[item.id] = filterOptions[item.id]; });
|
||||
return JSON.stringify({ class: classes, filters: filters });
|
||||
}
|
||||
|
||||
async function homeVod() {
|
||||
const platForms = getPlatList();
|
||||
const randomPlat = platForms[Math.floor(Math.random() * platForms.length)];
|
||||
const randomArea = ruleFilterDef[randomPlat.id]?.area || '';
|
||||
const categoryResult = await category(randomPlat.id, 1, { area: randomArea }, {});
|
||||
const categoryList = safeJSONParse(categoryResult).list || [];
|
||||
return JSON.stringify({ list: categoryList.slice(0, 20) });
|
||||
}
|
||||
|
||||
async function category(tid, pg, filter, extend) {
|
||||
const page = pg || 1;
|
||||
extend = extend || {};
|
||||
const platformItem = platformList.find(p => p.id === tid);
|
||||
if (!platformItem) return JSON.stringify({ list: [], page, pagecount: 1, limit: 0, total: 0 });
|
||||
const searchKeyword = extend?.custom;
|
||||
if (searchKeyword) return await cfs(tid, searchKeyword, pg);
|
||||
const area = filter?.area || extend?.area || ruleFilterDef[tid]?.area || '';
|
||||
const videos = [];
|
||||
switch (tid) {
|
||||
case 'wangyi': videos.push(...await getWangyiList(area, page, extend)); break;
|
||||
case 'tinghai': videos.push(...await getTinghaiList(area, page)); break;
|
||||
case 'mitu': videos.push(...await getMituList(area, page)); break;
|
||||
}
|
||||
return JSON.stringify({ list: videos, page: page, pagecount: page + 1, limit: videos.length, total: videos.length * (page + 1) });
|
||||
}
|
||||
|
||||
async function detail(id) {
|
||||
const parts = id.split('@');
|
||||
const platform = parts[0];
|
||||
const type = parts[1];
|
||||
const did = decodeURIComponent(parts.slice(2).join('@'));
|
||||
let vod = {};
|
||||
switch (platform) {
|
||||
case 'wangyi': vod = await getWangyiDetail(type, did); break;
|
||||
case 'tinghai': vod = await getTinghaiDetail(did); break;
|
||||
case 'mitu': vod = await getMituDetail(did); break;
|
||||
}
|
||||
return JSON.stringify({ list: [vod] });
|
||||
}
|
||||
|
||||
async function play(flag, id, flags) {
|
||||
if (flag.includes('网易云') || flag.includes('超高') || flag.includes('无损') || flag.includes('极高') || flag.includes('较高') || flag.includes('标准')) {
|
||||
return await playWangyi(id);
|
||||
}
|
||||
if (flag.includes('听海')) return await playTinghai(id);
|
||||
if (flag.includes('网盘下载')) return JSON.stringify({ parse: 0, url: id });
|
||||
if (flag.includes('在线播放')) return await playMitu(id);
|
||||
return JSON.stringify({ parse: 0, url: id });
|
||||
}
|
||||
|
||||
async function search(wd, quick, pg) {
|
||||
const videos = [];
|
||||
const page = pg || 1;
|
||||
const searchPromises = [cfs('wangyi', wd, page), cfs('tinghai', wd, page), cfs('mitu', wd, page)];
|
||||
const searchResults = await Promise.all(searchPromises);
|
||||
searchResults.forEach(result => { videos.push(...safeJSONParse(result).list || []); });
|
||||
const filteredResults = videos.filter(item => (item.vod_name || '').toLowerCase().includes(wd.toLowerCase()));
|
||||
return JSON.stringify({ list: filteredResults, page: page, pagecount: page + 1, limit: filteredResults.length, total: filteredResults.length * (page + 1) });
|
||||
}
|
||||
|
||||
export function __jsEvalReturn() {
|
||||
return { init, home, homeVod, category, detail, play, search, cfs };
|
||||
}
|
||||
Reference in New Issue
Block a user