Files
Koneko_api_for_QZ-Music/Koneko_聆澜_酷我音乐_v0.0.3_QZv2.js

189 lines
6.5 KiB
JavaScript

var https = require('https');
var http = require('http');
var crypto = require('crypto');
var querystring = require('querystring');
// ===== 通用HTTP请求 =====
function doRequest(protocol, options, postData) {
return new Promise(function(resolve, reject) {
var client = protocol === 'https' ? https : http;
var req = client.request(options, function(res) {
var chunks = [];
res.on('data', function(chunk) { chunks.push(chunk); });
res.on('end', function() {
var buf = Buffer.concat(chunks);
var body;
try {
body = JSON.parse(buf.toString('utf8'));
} catch (e) {
body = buf.toString('utf8');
}
resolve({ body: body, statusCode: res.statusCode, headers: res.headers });
});
});
req.on('error', function(err) { reject(err); });
req.setTimeout(15000, function() { req.destroy(); reject(new Error('timeout')); });
if (postData) req.write(postData);
req.end();
});
}
function requestUrl(urlStr, method, headers, body) {
var parsed = new URL(urlStr);
var protocol = parsed.protocol.replace(':', '');
var options = {
hostname: parsed.hostname,
port: parsed.port || (protocol === 'https' ? 443 : 80),
path: parsed.pathname + parsed.search,
method: method || 'GET',
headers: headers || {}
};
var postData = null;
if (body) {
if (typeof body === 'string') {
postData = Buffer.from(body, 'utf8');
} else if (Buffer.isBuffer(body)) {
postData = body;
} else {
postData = Buffer.from(JSON.stringify(body), 'utf8');
if (!options.headers['Content-Type']) options.headers['Content-Type'] = 'application/json';
}
if (postData) options.headers['Content-Length'] = postData.length;
}
return doRequest(protocol, options, postData);
}
function formatPlayTime(seconds) {
if (!seconds) return '00:00';
var m = Math.floor(seconds / 60);
var s = Math.floor(seconds % 60);
return (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s);
}
// ===== 搜索实现 =====
function musicSearch(str, page, limit) {
var url = 'https://kuwo-api.vercel.app/api/search?key=' + encodeURIComponent(str) + '&page=' + page + '&limit=' + limit;
return requestUrl(url, 'GET', { 'User-Agent': 'Mozilla/5.0 (Linux; Android 10)' }).then(function(res) {
if (res.body && res.body.data && res.body.data.list) return res.body.data.list;
throw new Error('kw search failed');
});
}
function handleSearchResult(rawList) {
var list = [];
for (var i = 0; i < rawList.length; i++) {
var item = rawList[i];
var types = [];
var _types = {};
types.push({ type: '128k', size: '' }); _types['128k'] = { size: '' };
types.push({ type: '320k', size: '' }); _types['320k'] = { size: '' };
list.push({
singer: item.artist || '',
name: item.name || '',
albumName: item.album || '',
albumId: item.albumid || '',
source: 'kw',
interval: item.duration ? formatPlayTime(item.duration) : '',
songmid: item.id || item.rid || '',
songId: item.id || item.rid || '',
img: item.pic || '',
lrc: null,
types: types, _types: _types, typeUrl: {}
});
}
return list;
}
function search(str, page, limit, retryNum) {
if (retryNum === undefined) retryNum = 0;
if (++retryNum > 3) return Promise.reject(new Error('try max num'));
if (!limit) limit = 30;
return musicSearch(str, page, limit).then(function(rawList) {
var list = handleSearchResult(rawList);
return { list: list, allPage: page + 1, limit: limit, total: list.length * 10, source: 'kw' };
});
}
// ===== 播放链接 =====
function getUrl(songInfo, type) {
var q = type || '128k';
var songId = songInfo.songmid || songInfo.id || '';
var ceruKey = process.env.ceru_key || '';
var headers = { 'User-Agent': 'QZMusic/2.0' };
if (ceruKey) headers['X-API-Key'] = ceruKey;
return requestUrl('https://source.shiqianjiang.cn/api/music/url?source=kw&songId=' + encodeURIComponent(songId) + '&quality=' + encodeURIComponent(q), 'GET', headers)
.then(function(res) {
if (res.body && typeof res.body === 'object' && res.body.url) return res.body.url;
if (res.body && typeof res.body === 'object' && res.body.data && res.body.data.url) return res.body.data.url;
if (typeof res.body === 'string' && res.body.indexOf('http') === 0) return res.body;
return '';
}).catch(function() { return ''; });
}
// ===== 封面图 =====
function getPic(songInfo) {
if (songInfo.img) return Promise.resolve(songInfo.img);
return Promise.resolve('');
}
// ===== 歌词 =====
function getLyric(songInfo) {
var rid = songInfo.songmid || '';
if (!rid) return Promise.resolve('');
return requestUrl('https://kuwo-api.vercel.app/api/lyric?id=' + encodeURIComponent(rid), 'GET', {
'User-Agent': 'Mozilla/5.0 (Linux; Android 10)'
}).then(function(res) {
if (res.body && res.body.data && res.body.data.lrc) return res.body.data.lrc;
return '';
}).catch(function() { return ''; });
}
// ===== 音乐详情 =====
function musicDetail(songmid) {
return Promise.resolve(null);
}
function musicInfo(songmid) { return musicDetail(songmid); }
// ===== 热搜 =====
function hotSearch() { return Promise.resolve({ source: 'kw', list: [] }); }
// ===== 搜索建议 =====
function tipSearch(str) { return Promise.resolve({ order: [], songs: [], artists: [], albums: [], playlists: [] }); }
// ===== 榜单 =====
function leaderboard() { return Promise.resolve([]); }
// ===== 歌单 =====
function songList() { return Promise.resolve({ list: [], total: 0, page: 1, source: 'kw' }); }
// ===== 歌手 =====
function singer() { return Promise.resolve({ source: 'kw', list: [], total: 0 }); }
// ===== 专辑 =====
function album() { return Promise.resolve({ source: 'kw', list: [], total: 0 }); }
// ===== 插件信息 =====
var pluginInfo = {
id: 'koneko_ceru_kw',
name: '酷我音乐 - Koneko 聆澜',
version: '0.0.3',
source: 'kw',
description: '酷我音乐音源,基于 CeruMusic musicSdk 搜索 + Ceru 播放接口'
};
module.exports = {
pluginInfo: pluginInfo,
musicSearch: { search: search },
tipSearch: { search: tipSearch },
hotSearch: { getList: hotSearch },
leaderboard: { getBoards: leaderboard },
songList: { search: songList, getTags: function() { return Promise.resolve({ tags: [], hotTag: [], source: 'kw' }); }, getList: songList },
singer: { getInfo: singer, getSongList: singer, getAlbumList: singer },
album: { getInfo: album, getSongList: album },
getLyric: getLyric,
getPic: getPic,
getUrl: getUrl,
musicDetail: musicDetail,
musicInfo: musicInfo
};