release v0.1.4: QQ batch quality info, WY simple search API, remove ext activate, all plugins unified

This commit is contained in:
TRAE Bot
2026-07-05 19:48:21 +00:00
parent 89ab94b276
commit 2c5b21e869
6 changed files with 100 additions and 175 deletions

View File

@@ -232,14 +232,11 @@ var pluginInfo = {
info: {
id: 'koneko_ceru_git',
name: 'GIT音源 - Koneko 聆澜',
version: '0.1.3',
version: '0.1.4',
source: 'git',
description: 'GIT音源基于 GitCode audio_database.json 本地搜索 + Ceru 播放接口'
},
ext: [
{ type: 'button', name: '激活付费', description: '点击激活付费模式(需先在环境变量填入 ceru_key', entry: 'activatePremium' },
{ type: 'button', name: '公益模式', description: '切换到公益模式(不使用 API Key', entry: 'activatePublic' }
],
ext: [],
env: [{ key: 'ceru_key', name: 'Ceru API Key', description: '聆澜付费密钥。留空=公益模式(仅128k);填写=付费模式(高音质)。修改后下次播放自动生效,也可点扩展按钮激活' }],
quality: [
{ name: '标准音质', ui: '标', id: '128k' },
@@ -251,17 +248,6 @@ var pluginInfo = {
supportFunc: ['search_song', 'search_playlist', 'playlist', 'album', 'lyric']
}
// ===== 激活/切换模式 =====
function activatePremium() {
var envNow = global.env || {};
if (!envNow.ceru_key) return Promise.reject(new Error('请先在环境变量中填写 ceru_key'));
return Promise.resolve({ success: true, message: '付费模式已激活,下次播放将使用高音质' });
}
function activatePublic() {
return Promise.resolve({ success: true, message: '已切换到公益模式' });
}
module.exports = {
musicSearch: { search: search },
tipSearch: { search: tipSearch },
@@ -275,7 +261,5 @@ module.exports = {
getUrl: getUrl,
musicDetail: musicDetail,
musicInfo: musicInfo,
activatePremium: activatePremium,
activatePublic: activatePublic,
pluginInfo: pluginInfo
};

View File

@@ -128,6 +128,41 @@ function signRequest(data) {
}, JSON.stringify(data));
}
// ===== 批量音质信息 =====
function getBatchMusicQualityInfo(songList) {
var songIds = songList.map(function(item) { return item.id; });
var body = {
comm: { ct: '19', cv: '1859', uin: '0' },
req: {
module: 'music.trackInfo.UniformRuleCtrl',
method: 'CgiGetTrackInfo',
param: { types: songIds.map(function(){ return 1; }), ids: songIds, ctx: 0 }
}
};
return requestUrl('https://u.y.qq.com/cgi-bin/musicu.fcg', 'POST', {
'User-Agent': 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)',
'Content-Type': 'application/json'
}, JSON.stringify(body)).then(function(res) {
if (!res.body || res.body.code !== 0) return {};
var qualityInfoMap = {};
var tracks = (res.body.req && res.body.req.data && res.body.req.data.tracks) ? res.body.req.data.tracks : [];
for (var i = 0; i < tracks.length; i++) {
var track = tracks[i];
var file = track.file || {};
var qualities = {};
if (file.size_128mp3) qualities['128k'] = sizeFormate(file.size_128mp3);
if (file.size_320mp3) qualities['320k'] = sizeFormate(file.size_320mp3);
if (file.size_flac) qualities['flac'] = sizeFormate(file.size_flac);
if (file.size_hires) qualities['hires'] = sizeFormate(file.size_hires);
if (file.size_new && file.size_new[0]) qualities['master'] = sizeFormate(file.size_new[0]);
if (file.size_new && file.size_new[1]) qualities['atmos'] = sizeFormate(file.size_new[1]);
if (file.size_new && file.size_new[2]) qualities['atmos_plus'] = sizeFormate(file.size_new[2]);
qualityInfoMap[track.id] = { qualities: qualities };
}
return qualityInfoMap;
}).catch(function() { return {}; });
}
// ===== 搜索实现 =====
function musicSearch(str, page, limit) {
var body = {
@@ -141,38 +176,33 @@ function musicSearch(str, page, limit) {
}
function handleSearchResult(rawList) {
if (!rawList || rawList.length === 0) return Promise.resolve([]);
return getBatchMusicQualityInfo(rawList).then(function(qualityInfoMap) {
var list = [];
if (!rawList) return list;
for (var i = 0; i < rawList.length; i++) {
var item = rawList[i];
if (!item.file || !item.file.media_mid) continue;
var qualities = {};
var file = item.file;
if (file.size_128mp3 !== 0) qualities['128k'] = sizeFormate(file.size_128mp3);
if (file.size_320mp3 !== 0) qualities['320k'] = sizeFormate(file.size_320mp3);
if (file.size_flac !== 0) qualities['flac'] = sizeFormate(file.size_flac);
var albumId = '';
var albumName = '';
if (item.album) { albumName = item.album.name; albumId = item.album.mid; }
var singerName = formatSingerName(item.singer, 'name');
var picUrl = albumId && albumId !== '空'
? 'https://y.gtimg.cn/music/photo_new/T002R500x500M000' + albumId + '.jpg'
: (item.singer && item.singer.length ? 'https://y.gtimg.cn/music/photo_new/T001R500x500M000' + item.singer[0].mid + '.jpg' : '');
var picUrl = (albumId === '' || albumId === '空')
? (item.singer && item.singer.length ? 'https://y.gtimg.cn/music/photo_new/T001R500x500M000' + item.singer[0].mid + '.jpg' : '')
: 'https://y.gtimg.cn/music/photo_new/T002R500x500M000' + albumId + '.jpg';
var qInfo = qualityInfoMap[item.id] || {};
list.push({
id: String(item.mid),
name: item.name + (item.title_extra || ''),
artists: singerName,
artists: formatSingerName(item.singer, 'name'),
source: 'tx',
pic: picUrl,
mPic: picUrl,
sPic: picUrl,
pic: picUrl, mPic: picUrl, sPic: picUrl,
albumName: albumName,
albumId: String(albumId || ''),
interval: String(formatPlayTime(item.interval) || '--/--'),
qualities: qualities
qualities: qInfo.qualities || {}
});
}
return list;
});
}
function search(str, page, limit, retryNum) {
@@ -181,11 +211,12 @@ function search(str, page, limit, retryNum) {
if (!limit) limit = 30;
return musicSearch(str, page, limit).then(function(data) {
if (!data || !data.body || !data.body.item_song) return search(str, page, limit, retryNum);
var list = handleSearchResult(data.body.item_song);
return handleSearchResult(data.body.item_song).then(function(list) {
var meta = data.meta || {};
var total = meta.estimate_sum || 0;
return { list: list, allPage: Math.ceil(total / limit), limit: limit, total: total, source: 'tx' };
});
});
}
// ===== 播放链接 =====
@@ -269,7 +300,7 @@ function musicInfo(songmid) { return musicDetail(songmid); }
function hotSearch() {
var data = {
comm: { ct: '19', cv: '1803', guid: '0', patch: '118', psrf_access_token_expiresAt: 0, psrf_qqaccess_token: '', psrf_qqopenid: '', psrf_qqunionid: '', tmeAppID: 'qqmusic', tmeLoginType: 0, uin: '0', wid: '0' },
hotkey: { method: 'GetHotkeyForQQMusicPC', module: 'tencent_musicsoso_hotkey.HotkeyService', param: { search_id: '', uin: 0 } }
hotkey: { method: 'GetHotkeyForQQMusicMobile', module: 'tencent_musicsoso_hotkey.HotkeyService', param: { search_id: '', uin: 0 } }
};
return requestUrl('https://u.y.qq.com/cgi-bin/musicu.fcg', 'POST', {
'User-Agent': 'Mozilla/5.0 (Linux; Android 10)',
@@ -315,36 +346,24 @@ var pluginInfo = {
info: {
id: 'koneko_ceru_tx',
name: 'QQ音乐 - Koneko 聆澜',
version: '0.1.3',
version: '0.1.4',
source: 'tx',
description: 'QQ音乐音源基于 CeruMusic musicSdk 搜索 + Ceru 播放接口'
},
ext: [
{ type: 'button', name: '激活付费', description: '点击激活付费模式(需先在环境变量填入 ceru_key', entry: 'activatePremium' },
{ type: 'button', name: '公益模式', description: '切换到公益模式(不使用 API Key', entry: 'activatePublic' }
],
ext: [],
env: [{ key: 'ceru_key', name: 'Ceru API Key', description: '聆澜付费密钥。留空=公益模式(仅128k);填写=付费模式(高音质)。修改后下次播放自动生效,也可点扩展按钮激活' }],
quality: [
{ name: '标准音质', ui: '标', id: '128k' },
{ name: '高品音质', ui: 'HQ', id: '320k' },
{ name: '无损音质', ui: 'SQ', id: 'flac' },
{ name: '高解析度无损(Hi-Res)', ui: 'HR', id: 'hires' },
{ name: '至臻母带', ui: 'MT', id: 'master' }
{ name: '至臻母带', ui: 'MT', id: 'master' },
{ name: '全景声', ui: 'AT', id: 'atmos' },
{ name: '全景声+', ui: 'AT+', id: 'atmos_plus' }
],
supportFunc: ['search_song', 'search_playlist', 'playlist', 'album', 'lyric']
}
// ===== 激活/切换模式 =====
function activatePremium() {
var envNow = global.env || {};
if (!envNow.ceru_key) return Promise.reject(new Error('请先在环境变量中填写 ceru_key'));
return Promise.resolve({ success: true, message: '付费模式已激活,下次播放将使用高音质' });
}
function activatePublic() {
return Promise.resolve({ success: true, message: '已切换到公益模式' });
}
module.exports = {
musicSearch: { search: search },
tipSearch: { search: tipSearch },
@@ -358,7 +377,5 @@ module.exports = {
getUrl: getUrl,
musicDetail: musicDetail,
musicInfo: musicInfo,
activatePremium: activatePremium,
activatePublic: activatePublic,
pluginInfo: pluginInfo
};

View File

@@ -200,14 +200,11 @@ var pluginInfo = {
info: {
id: 'koneko_ceru_mg',
name: '咪咕音乐 - Koneko 聆澜',
version: '0.1.3',
version: '0.1.4',
source: 'mg',
description: '咪咕音乐音源,基于 CeruMusic musicSdk 搜索 + Ceru 播放接口'
},
ext: [
{ type: 'button', name: '激活付费', description: '点击激活付费模式(需先在环境变量填入 ceru_key', entry: 'activatePremium' },
{ type: 'button', name: '公益模式', description: '切换到公益模式(不使用 API Key', entry: 'activatePublic' }
],
ext: [],
env: [{ key: 'ceru_key', name: 'Ceru API Key', description: '聆澜付费密钥。留空=公益模式(仅128k);填写=付费模式(高音质)。修改后下次播放自动生效,也可点扩展按钮激活' }],
quality: [
{ name: '标准音质', ui: '标', id: '128k' },
@@ -219,17 +216,6 @@ var pluginInfo = {
supportFunc: ['search_song', 'search_playlist', 'playlist', 'album', 'lyric']
}
// ===== 激活/切换模式 =====
function activatePremium() {
var envNow = global.env || {};
if (!envNow.ceru_key) return Promise.reject(new Error('请先在环境变量中填写 ceru_key'));
return Promise.resolve({ success: true, message: '付费模式已激活,下次播放将使用高音质' });
}
function activatePublic() {
return Promise.resolve({ success: true, message: '已切换到公益模式' });
}
module.exports = {
musicSearch: { search: search },
tipSearch: { search: tipSearch },
@@ -243,7 +229,5 @@ module.exports = {
getUrl: getUrl,
musicDetail: musicDetail,
musicInfo: musicInfo,
activatePremium: activatePremium,
activatePublic: activatePublic,
pluginInfo: pluginInfo
};

View File

@@ -141,22 +141,14 @@ function formatPlayCount(count) {
return String(count);
}
// ===== 搜索实现 =====
// ===== 搜索实现使用老式搜索API无需weapi加密 =====
function musicSearch(str, page, limit) {
var form = weapi({
s: str,
type: 1,
limit: limit,
offset: limit * (page - 1),
total: page == 1
});
return requestUrl('https://music.163.com/weapi/cloudsearch/get/web', 'POST', {
var url = 'https://music.163.com/api/search/get/web?s=' + encodeURIComponent(str) + '&type=1&offset=' + (limit * (page - 1)) + '&total=true&limit=' + limit;
return requestUrl(url, 'GET', {
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36',
'Origin': 'https://music.163.com',
'Referer': 'https://music.163.com',
'Content-Type': 'application/x-www-form-urlencoded',
'Cookie': WY_COOKIE
}, querystring.stringify(form)).then(function(res) {
}).then(function(res) {
if (typeof res.body === 'string') {
try { res.body = JSON.parse(res.body); } catch (e) { return null; }
}
@@ -174,18 +166,22 @@ function handleSearchResult(rawList) {
if (item.h && item.h.size) qualities['320k'] = sizeFormate(item.h.size);
if (item.sq && item.sq.size) qualities['flac'] = sizeFormate(item.sq.size);
if (item.hr && item.hr.size) qualities['hires'] = sizeFormate(item.hr.size);
var picUrl = item.al && item.al.picUrl ? item.al.picUrl : '';
var picUrl = item.album && item.album.picUrl ? item.album.picUrl : '';
var singerName = '';
if (item.artists && item.artists.length) {
var ns = [];
for (var j = 0; j < item.artists.length; j++) ns.push(item.artists[j].name);
singerName = ns.join('、');
}
list.push({
id: String(item.id),
name: item.name || '',
artists: formatSingerName(item.ar, 'name'),
artists: singerName,
source: 'wy',
pic: picUrl,
mPic: picUrl,
sPic: picUrl,
albumName: item.al && item.al.name ? item.al.name : '',
albumId: String(item.al && item.al.id ? item.al.id : ''),
interval: String(formatPlayTime((item.dt || 0) / 1000) || '--/--'),
pic: picUrl, mPic: picUrl, sPic: picUrl,
albumName: item.album && item.album.name ? item.album.name : '',
albumId: String(item.album && item.album.id ? item.album.id : ''),
interval: String(formatPlayTime((item.duration || 0) / 1000) || '--/--'),
qualities: qualities
});
}
@@ -202,13 +198,7 @@ function search(str, page, limit, retryNum) {
var list = handleSearchResult(songs);
if (!list || list.length === 0) return search(str, page, limit, retryNum);
var total = result.result && result.result.songCount ? result.result.songCount : 0;
return {
list: list,
allPage: Math.ceil(total / limit),
limit: limit,
total: total,
source: 'wy'
};
return { list: list, allPage: Math.ceil(total / limit), limit: limit, total: total, source: 'wy' };
});
}
@@ -629,16 +619,11 @@ var pluginInfo = {
info: {
id: 'koneko_ceru_wy',
name: '网易云音乐 - Koneko 聆澜',
version: '0.1.3',
version: '0.1.4',
source: 'wy',
description: '网易云音乐音源,基于 CeruMusic musicSdk 搜索 + Ceru 播放接口'
},
ext: [
{ type: 'homePage', url: 'https://music.163.com/', name: '网易云音乐主页', description: '网易云音乐主页', entry: '' },
{ type: 'share', url: 'https://music.163.com/song/media/outer/url?id={id}.mp3', name: '外链分享', description: '外链分享', entry: '' },
{ type: 'button', name: '激活付费', description: '点击激活付费模式(需先在环境变量填入 ceru_key', entry: 'activatePremium' },
{ type: 'button', name: '公益模式', description: '切换到公益模式(不使用 API Key', entry: 'activatePublic' }
],
ext: [],
env: [
{ key: 'ceru_key', name: 'Ceru API Key', description: '聆澜付费密钥。留空=公益模式(仅128k);填写=付费模式(高音质)。修改后下次播放自动生效,也可点扩展按钮激活' },
{ key: 'cookie', name: '网易云Cookie', description: '网易云音乐登录Cookie格式: MUSIC_U=xxx; MUSIC_U 值从浏览器登录网易云后 F12→Application→Cookies 复制。用于个人歌单、收藏等功能' }
@@ -653,17 +638,6 @@ var pluginInfo = {
supportFunc: ['search_song', 'search_playlist', 'playlist', 'album', 'lyric']
}
// ===== 激活/切换模式 =====
function activatePremium() {
var envNow = global.env || {};
if (!envNow.ceru_key) return Promise.reject(new Error('请先在环境变量中填写 ceru_key'));
return Promise.resolve({ success: true, message: '付费模式已激活,下次播放将使用高音质' });
}
function activatePublic() {
return Promise.resolve({ success: true, message: '已切换到公益模式' });
}
module.exports = {
musicSearch: { search: search },
tipSearch: { search: tipSearch },
@@ -677,7 +651,5 @@ module.exports = {
getUrl: getUrl,
musicDetail: musicDetail,
musicInfo: musicInfo,
activatePremium: activatePremium,
activatePublic: activatePublic,
pluginInfo: pluginInfo
};

View File

@@ -213,14 +213,11 @@ var pluginInfo = {
info: {
id: 'koneko_ceru_kw',
name: '酷我音乐 - Koneko 聆澜',
version: '0.1.3',
version: '0.1.4',
source: 'kw',
description: '酷我音乐音源,基于 CeruMusic musicSdk 搜索 + Ceru 播放接口'
},
ext: [
{ type: 'button', name: '激活付费', description: '点击激活付费模式(需先在环境变量填入 ceru_key', entry: 'activatePremium' },
{ type: 'button', name: '公益模式', description: '切换到公益模式(不使用 API Key', entry: 'activatePublic' }
],
ext: [],
env: [{ key: 'ceru_key', name: 'Ceru API Key', description: '聆澜付费密钥。留空=公益模式(仅128k);填写=付费模式(高音质)。修改后下次播放自动生效,也可点扩展按钮激活' }],
quality: [
{ name: '标准音质', ui: '标', id: '128k' },
@@ -232,17 +229,6 @@ var pluginInfo = {
supportFunc: ['search_song', 'search_playlist', 'playlist', 'album', 'lyric']
}
// ===== 激活/切换模式 =====
function activatePremium() {
var envNow = global.env || {};
if (!envNow.ceru_key) return Promise.reject(new Error('请先在环境变量中填写 ceru_key'));
return Promise.resolve({ success: true, message: '付费模式已激活,下次播放将使用高音质' });
}
function activatePublic() {
return Promise.resolve({ success: true, message: '已切换到公益模式' });
}
module.exports = {
musicSearch: { search: search },
tipSearch: { search: tipSearch },
@@ -256,7 +242,5 @@ module.exports = {
getUrl: getUrl,
musicDetail: musicDetail,
musicInfo: musicInfo,
activatePremium: activatePremium,
activatePublic: activatePublic,
pluginInfo: pluginInfo
};

View File

@@ -215,14 +215,11 @@ var pluginInfo = {
info: {
id: 'koneko_ceru_kg',
name: '酷狗音乐 - Koneko 聆澜',
version: '0.1.3',
version: '0.1.4',
source: 'kg',
description: '酷狗音乐音源,基于 CeruMusic musicSdk 搜索 + Ceru 播放接口'
},
ext: [
{ type: 'button', name: '激活付费', description: '点击激活付费模式(需先在环境变量填入 ceru_key', entry: 'activatePremium' },
{ type: 'button', name: '公益模式', description: '切换到公益模式(不使用 API Key', entry: 'activatePublic' }
],
ext: [],
env: [{ key: 'ceru_key', name: 'Ceru API Key', description: '聆澜付费密钥。留空=公益模式(仅128k);填写=付费模式(高音质)。修改后下次播放自动生效,也可点扩展按钮激活' }],
quality: [
{ name: '标准音质', ui: '标', id: '128k' },
@@ -234,17 +231,6 @@ var pluginInfo = {
supportFunc: ['search_song', 'search_playlist', 'playlist', 'album', 'lyric']
}
// ===== 激活/切换模式 =====
function activatePremium() {
var envNow = global.env || {};
if (!envNow.ceru_key) return Promise.reject(new Error('请先在环境变量中填写 ceru_key'));
return Promise.resolve({ success: true, message: '付费模式已激活,下次播放将使用高音质' });
}
function activatePublic() {
return Promise.resolve({ success: true, message: '已切换到公益模式' });
}
module.exports = {
musicSearch: { search: search },
tipSearch: { search: tipSearch },
@@ -258,7 +244,5 @@ module.exports = {
getUrl: getUrl,
musicDetail: musicDetail,
musicInfo: musicInfo,
activatePremium: activatePremium,
activatePublic: activatePublic,
pluginInfo: pluginInfo
};