Files
tvbox_collect/cpu_iy3/lib/wyy.js
T
2026-06-29 03:41:14 +00:00

323 lines
10 KiB
JavaScript

/*
@header({
searchable: 1,
filterable: 1,
quickSearch: 1,
title: '网易云音乐',
more: {
sourceTag: "音乐,歌单,专辑,歌手",
errorPlayNext: true
},
'类型': '音乐',
logo: 'https://s1.music.126.net/style/favicon.ico?v20180823',
lang: 'cat'
})
*/
// ==================== URL配置集中管理 ====================
let rule = {
host: 'https://music.163.com',
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',
playUrl: 'http://oiapi.net/api/Music_163',
search: 'http://mc.alger.fun/api/cloudsearch'
};
let siteName = '网易云音乐';
let host = rule.host;
let siteKey = '';
let siteType = 0;
let headers = {
'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'
};
// 带重试的请求函数,直接返回content
async function request(url, options, retries = 2) {
for (let i = 0; i < retries; i++) {
try {
const res = await req(url, options);
const content = res.content || res;
// 检查是否有有效数据(内容不为空且长度大于100)
if (content && content.length > 100) {
return content;
}
// 数据太少,重试
if (i < retries - 1) {
await new Promise(r => setTimeout(r, 500));
continue;
}
return content;
} catch (e) {
if (i === retries - 1) throw e;
await new Promise(r => setTimeout(r, 500));
}
}
}
async function init(cfg) {
console.log('网易云音乐初始化完成');
}
function home(filter) {
const classes = [
{ type_id: "recommend_playlist", type_name: "推荐歌单" },
{ type_id: "toplist", type_name: "排行榜" },
{ type_id: "hot_playlist", type_name: "热门歌单" },
{ type_id: "top_artists", type_name: "热门歌手" }
];
return JSON.stringify({ class: classes, filters: {} });
}
async function homeVod(params) {
return await getPersonalized(1);
}
async function category(tid, pg, filter, extend) {
let limit = 20;
let offset = (pg - 1) * limit;
let url, dataKey;
if (tid === 'recommend_playlist') {
return await getPersonalized(pg);
}
if (tid === 'toplist') {
url = `${host}${rule.toplist}`;
dataKey = 'list';
} else if (tid === 'hot_playlist') {
const cat = extend.cat || '全部';
url = `${host}${rule.hotPlaylist}?cat=${encodeURIComponent(cat)}&limit=${limit}&offset=${offset}&order=hot`;
dataKey = 'playlists';
} else if (tid === 'top_artists') {
url = `${host}${rule.topArtists}?limit=${limit}&offset=${offset}`;
dataKey = 'artists';
}
const json = JSON.parse(await request(url, { headers }));
const rawList = json[dataKey] || [];
let list = [];
if (tid === 'toplist') {
list = rawList.slice(offset, offset + limit).map(it => ({
vod_name: it.name,
vod_pic: (it.coverImgUrl || it.picUrl || '') + '?param=300y300',
vod_remarks: it.updateFrequency || `${it.trackCount || 0}首歌曲`,
vod_id: `toplist_${it.id}`
}));
} else if (tid === 'hot_playlist') {
list = rawList.map(it => ({
vod_name: it.name,
vod_pic: (it.coverImgUrl || '') + '?param=300y300',
vod_remarks: it.playCount ? (it.playCount > 10000 ? (it.playCount / 10000).toFixed(1) + '万' : it.playCount) : '',
vod_id: `playlist_${it.id}`
}));
} else if (tid === 'top_artists') {
list = rawList.map(it => ({
vod_name: it.name,
vod_pic: (it.img1v1Url || it.picUrl || '') + '?param=300y300',
vod_remarks: `${it.albumSize || 0}张专辑`,
vod_id: `artist_${it.id}`
}));
}
return JSON.stringify({ list: list, page: +pg, limit: limit });
}
async function getPersonalized(pg) {
let limit = 20;
let offset = (pg - 1) * limit;
const url = `${host}${rule.personalized}?limit=${pg * limit}`;
const json = JSON.parse(await request(url, { headers }));
let rawList = json.result || [];
if (pg > 1) {
rawList = rawList.slice(offset);
}
let list = rawList.map(it => ({
vod_name: it.name,
vod_pic: (it.picUrl || '') + '?param=300y300',
vod_remarks: it.playCount ? '🎧' + (it.playCount > 10000 ? (it.playCount / 10000).toFixed(1) + '万' : it.playCount) : '',
vod_id: `playlist_${it.id}`
}));
return JSON.stringify({ list: list, page: +pg, limit: limit });
}
async function detail(id) {
const did = id.toString();
const realId = did.split('_')[1];
let vod_name, vod_pic, vod_content, tracks;
if (did.startsWith('artist_')) {
const url = `${host}${rule.artistDetail}${realId}`;
const json = JSON.parse(await request(url, { headers }));
vod_name = json.artist.name;
vod_pic = (json.artist.picUrl || json.artist.cover || '') + '?param=500y500';
vod_content = json.artist.briefDesc || json.artist.name;
tracks = json.hotSongs || [];
} else {
const url = `${host}${rule.playlistDetail}?id=${realId}`;
const json = JSON.parse(await request(url, { headers }));
const playlist = json.result || json.playlist;
vod_name = playlist.name;
vod_pic = (playlist.coverImgUrl || playlist.picUrl || '') + '?param=500y500';
vod_content = playlist.description || playlist.name;
tracks = playlist.tracks || [];
}
// 构建播放数据
let playArr = [];
let songPicArr = [];
tracks.forEach(s => {
let songName = s.name;
let artist = (s.artists || s.ar || []).map(a => a.name).join('/');
let displayName = artist ? `${songName} - ${artist}` : songName;
let songPic = (s.al?.picUrl || s.album?.picUrl || vod_pic).replace('?param=500y500', '?param=300y300');
playArr.push(`${displayName}$${s.id}|lMusic&&${songPic}`);
songPicArr.push(songPic);
});
// 音质映射(从高到低排序)
const qualityMap = [
["超高", "hrMusic"],
["无损", "sqMusic"],
["极高", "hMusic"],
["较高", "mMusic"],
["标准", "lMusic"]
];
const playFrom = qualityMap.map(q => q[0]).join('$$$');
const playUrl = qualityMap.map(q =>
tracks.map(s => `${s.name}$${s.id}|${q[1]}&&${vod_pic}`).join('#')
).join('$$$');
const playPic = songPicArr.join('#');
return JSON.stringify({
list: [{
vod_id: did,
vod_name: vod_name,
vod_pic: vod_pic,
vod_content: vod_content,
vod_play_from: playFrom,
vod_play_url: playUrl,
vod_play_pic: playPic,
vod_play_pic_ratio: 1.0
}]
});
}
async function play(flag, id, flags) {
const [musicId, qualityType] = id.split('|');
const playApi = `${rule.playUrl}&id=${musicId}`;
const playJson = JSON.parse(await request(playApi, { headers }));
let songUrl = '';
if (playJson && playJson.code === 0 && playJson.data && playJson.data.length > 0) {
songUrl = playJson.data[0].url || '';
}
const lyricApi = `${host}${rule.songLyric}?id=${musicId}&lv=1&kv=1&tv=-1`;
const lyricJson = JSON.parse(await request(lyricApi, { headers }));
let lyric = lyricJson.lrc?.lyric || '';
if (lyricJson.tlyric?.lyric) {
lyric = lyric + '\n\n【翻译】\n' + lyricJson.tlyric.lyric;
}
const infoApi = `${host}${rule.songDetail}?ids=[${musicId}]`;
const infoJson = JSON.parse(await request(infoApi, { headers }));
// 获取封面
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: headers,
lrc: lyric,
cover: cover,
pic: cover,
height: 720
});
}
async function search(wd, quick) {
const searchTypes = [
{ type: 1, prefix: 'song_', remark: '歌曲', key: 'songs' },
{ type: 10, prefix: 'album_', remark: '专辑', key: 'albums' },
{ type: 1000, prefix: 'playlist_', remark: '歌单', key: 'playlists' },
{ type: 100, prefix: 'artist_', remark: '歌手', key: 'artists' }
];
let allResults = [];
for (const st of searchTypes) {
const url = `${rule.search}?keywords=${encodeURIComponent(wd)}&type=${st.type}`;
const json = JSON.parse(await request(url, { headers }));
if (json.result?.[st.key]) {
for (const item of json.result[st.key]) {
const result = {
vod_name: item.name,
vod_remarks: st.remark,
vod_id: st.prefix + item.id,
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 = item.al.picUrl + '?param=300y300';
} else if (st.type === 10) {
if (item.artist) result.vod_name += ' - ' + item.artist.name;
if (item.picUrl) result.vod_pic = item.picUrl + '?param=300y300';
} else if (st.type === 1000) {
if (item.coverImgUrl) result.vod_pic = item.coverImgUrl + '?param=300y300';
} else if (st.type === 100) {
const picUrl = item.picUrl || item.img1v1Url;
if (picUrl) result.vod_pic = picUrl + '?param=300y300';
}
allResults.push(result);
}
}
}
return JSON.stringify({ list: allResults });
}
export function __jsEvalReturn() {
return {
init: init,
home: home,
homeVod: homeVod,
category: category,
detail: detail,
play: play,
search: search
}
}