ファイル
sg/js/布谷音乐.js
T
2026-08-14 11:22:09 +02:00

242 行
8.8 KiB
JavaScript

/*
@header({
searchable: 1,
filterable: 1,
quickSearch: 1,
title: '布谷音乐[音]',
author: 'cupid',
lang: 'cat'
})
*/
const API_SEARCH = 'http://buguyy.top/api/search';
const API_URL = 'http://buguyy.top/api/geturl';
const API_HOTLIST = 'http://buguyy.top/api/hotlist';
const API_NEWLIST = 'http://buguyy.top/api/newlist';
const API_RANDOM = 'http://buguyy.top/api/random';
const PIC = 'https://copyright.bdstatic.com/vcg/creative/fcbafd433c4960b5039ef96217838ecf.jpg@h_1280';
const headers = {
'User-Agent': 'Mozilla/5.0',
'Referer': 'https://buguyy.top/'
};
const DEFAULT_SEARCH_KEYWORD = '周杰伦';
const CLASSES = [
{ type_id: 'random', type_name: '精选歌曲' },
{ type_id: 'hot', type_name: '热门歌曲' },
{ type_id: 'new', type_name: '最新歌曲' },
{ type_id: 'search', type_name: '歌手筛选' }
];
const SEARCH_FILTERS = [{
key: 'keyword',
name: '关键词',
value: ['周杰伦','陈奕迅','林俊杰','邓紫棋','薛之谦','张学友','刘德华','王菲','孙燕姿','李荣浩','毛不易','张杰','许嵩','汪苏泷','周深','五月天','Beyond','蔡依林','梁静茹','张惠妹','周华健','任贤齐','赵雷','朴树','华晨宇','杨宗纬','张碧晨','王力宏','陶喆','伍佰','许巍'].map(x => ({ n: x, v: x }))
}];
function text(v) { return String(v == null ? '' : v).trim(); }
function safeJson(v, def) { try { return JSON.parse(String(v || '')); } catch (e) { return def; } }
function cleanAbout(s) { return text(s).replace(/<br\s*\/?\s*>/gi, ' ').replace(/\[[^\]]+\]/g, '').replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim(); }
function enc(v) { return encodeURIComponent(String(v == null ? '' : v)); }
function dec(v) { try { return decodeURIComponent(String(v || '')); } catch (e) { return String(v || ''); } }
function parseBase64Json(v) {
v = text(v);
if (!v) return {};
try {
v = v.replace(/-/g, '+').replace(/_/g, '/');
while (v.length % 4) v += '=';
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
let str = '', i = 0;
v = v.replace(/[^A-Za-z0-9+/=]/g, '');
while (i < v.length) {
const e1 = chars.indexOf(v.charAt(i++));
const e2 = chars.indexOf(v.charAt(i++));
const e3 = chars.indexOf(v.charAt(i++));
const e4 = chars.indexOf(v.charAt(i++));
const c1 = (e1 << 2) | (e2 >> 4);
const c2 = ((e2 & 15) << 4) | (e3 >> 2);
const c3 = ((e3 & 3) << 6) | e4;
str += String.fromCharCode(c1);
if (e3 !== 64) str += String.fromCharCode(c2);
if (e4 !== 64) str += String.fromCharCode(c3);
}
return JSON.parse(decodeURIComponent(escape(str)));
} catch (e) { return {}; }
}
function parseExtend(x) {
if (!x) return {};
if (typeof x === 'object') return x;
x = text(x);
const b = parseBase64Json(x);
if (Object.keys(b).length) return b;
const j = safeJson(x, null);
if (j && typeof j === 'object') return j;
const out = {};
x.replace(/^\?/, '').split('&').forEach(seg => {
const p = seg.split('=');
if (p[0]) out[p[0]] = dec(p.slice(1).join('='));
});
return out;
}
async function reqText(url, opt) {
opt = opt || {};
const o = { headers: Object.assign({}, headers, opt.headers || {}), timeout: opt.timeout || 15000 };
let r = '';
if (typeof Java !== 'undefined' && Java && Java.req) {
r = await Java.req(url, o);
} else if (typeof req !== 'undefined') {
r = await req(url, o);
} else if (typeof request !== 'undefined') {
r = await request(url, o);
}
if (typeof r === 'string') return r;
return String((r && (r.content || r.body || r.data)) || r || '');
}
async function requestJson(url) {
const html = await reqText(url);
const js = safeJson(html, null);
return js && typeof js === 'object' ? js : {};
}
function safeJsonObj(v, def) {
if (v && typeof v === 'object') return v;
return safeJson(v, def || {});
}
function normalizeSong(song) {
song = song || {};
const id = text(song.id);
const name = text(song.title || song.name || '未知歌曲');
const pic = text(song.picurl || song.pic || '') || PIC;
const singer = text(song.singer || song.artist || '');
const about = cleanAbout(song.about || song.artist || '');
return {
vod_id: 'song@@' + enc(id) + '@@' + enc(name),
vod_name: name,
vod_pic: pic,
vod_remarks: singer || (about ? about.slice(0, 24) : '布谷音乐')
};
}
async function fetchList(url, limit) {
const data = await requestJson(url);
const raw = Array.isArray(data.data) ? data.data : (Array.isArray(data.list) ? data.list : (Array.isArray(data) ? data : []));
return raw.slice(0, Math.max(1, Number(limit) || 20));
}
async function fetchSongs(keyword, limit) {
keyword = text(keyword);
if (!keyword) return [];
const data = await requestJson(API_SEARCH + '?keyword=' + enc(keyword) + '&limit=' + (Number(limit) || 20));
return Array.isArray(data.data) ? data.data : [];
}
async function fetchPlayUrl(id) {
id = text(id);
if (!id) return '';
const data = await requestJson(API_URL + '?id=' + enc(id));
return text(data.url || (data.data && data.data.url) || '');
}
function resolveKeyword(tid, extend) {
extend = parseExtend(extend);
return text(extend.keyword || extend.wd || extend.q || (tid === 'search' ? DEFAULT_SEARCH_KEYWORD : ''));
}
async function home(filter) {
const list = (await fetchList(API_RANDOM, 20).catch(() => [])).map(normalizeSong);
return JSON.stringify({ class: CLASSES, filters: { search: SEARCH_FILTERS }, list });
}
async function homeT4(reqObj) {
return home(reqObj && reqObj.filter);
}
async function homeVod() {
const list = (await fetchList(API_RANDOM, 20).catch(() => [])).map(normalizeSong);
return JSON.stringify({ list });
}
async function category(tid, pg, filter, extend) {
if (tid && typeof tid === 'object') {
const reqObj = tid;
return category(reqObj.tid || reqObj.t || reqObj.id || 'hot', reqObj.pg || reqObj.page || 1, reqObj.filter || false, reqObj.extend || reqObj.ext || reqObj.f || {});
}
pg = Number(pg) || 1;
const limit = 20;
let rows = [];
if (tid === 'hot' || tid === 'new' || tid === 'random') {
const url = tid === 'hot' ? API_HOTLIST : (tid === 'new' ? API_NEWLIST : API_RANDOM);
rows = await fetchList(url, limit).catch(() => []);
} else {
rows = await fetchSongs(resolveKeyword(tid, extend) || DEFAULT_SEARCH_KEYWORD, limit).catch(() => []);
}
const list = rows.map(normalizeSong);
return JSON.stringify({ code: 1, msg: '数据列表', page: pg, pagecount: 1, limit, total: list.length, list });
}
async function search(wd, quick, pg) {
pg = Number(pg) || 1;
const list = (await fetchSongs(wd, 20).catch(() => [])).map(normalizeSong);
return JSON.stringify({ code: 1, msg: '数据列表', page: pg, pagecount: 1, limit: 20, total: list.length, list });
}
async function detail(ids) {
if (ids && typeof ids === 'object' && !Array.isArray(ids)) ids = ids.ids || ids.id || ids.vod_id || '';
const idStr = Array.isArray(ids) ? text(ids[0]) : text(ids);
if (!idStr) return { list: [] };
const parts = idStr.split('@@');
const songId = dec(parts[1] || idStr);
const songName = dec(parts[2] || '布谷音乐');
const displayName = text(songName || ('歌曲-' + songId));
return JSON.stringify({ code: 1, msg: '数据列表', list: [{
vod_id: idStr,
vod_name: displayName,
vod_pic: '',
vod_remarks: '布谷音乐',
type_name: '音乐',
vod_content: '布谷音乐',
vod_play_from: '布谷音乐',
vod_play_url: displayName + '$' + songId
}] });
}
async function play(flag, id, flags) {
if (flag && typeof flag === 'object') {
const reqObj = flag;
return play(reqObj.flag || '', reqObj.id || reqObj.play || reqObj.url || '', reqObj.flags || []);
}
id = text(id);
let playUrl = /^https?:\/\//i.test(id) ? id : await fetchPlayUrl(id).catch(() => '');
return JSON.stringify({ parse: 0, jx: 0, playUrl: '', url: playUrl, header: headers, msg: playUrl ? '' : '播放地址解析失败' });
}
async function homeContent(filter) { return safeJsonObj(await home(filter), { class: [], filters: {}, list: [] }); }
async function homeVideoContent() { return safeJsonObj(await homeVod(), { list: [] }); }
async function categoryContent(tid, pg, filter, extend) { return safeJsonObj(await category(tid, pg, filter, extend || {}), { list: [] }); }
async function detailContent(ids) { return safeJsonObj(await detail(ids), { list: [] }); }
async function searchContent(wd, quick, pg) { return safeJsonObj(await search(wd, quick, pg || 1), { list: [] }); }
async function playerContent(flag, id, flags) { return safeJsonObj(await play(flag, id, flags), { parse: 1, url: id }); }
export function __jsEvalReturn() {
return {
init: function () {},
home,
homeVod,
category,
detail,
search,
play,
homeContent,
homeVideoContent,
categoryContent,
detailContent,
searchContent,
playerContent
};
}