release: v0.1.0 - bump version, all 6 Ceru plugins stable
This commit is contained in:
210
Koneko_聆澜_酷狗音乐_v0.1.0_QZv2.js
Normal file
210
Koneko_聆澜_酷狗音乐_v0.1.0_QZv2.js
Normal file
@@ -0,0 +1,210 @@
|
||||
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 = 'http://mobilecdn.kugou.com/api/v3/search/song?showtype=14&highlight=em&pagesize=' + limit + '&page=' + page + '&keyword=' + encodeURIComponent(str);
|
||||
return requestUrl(url, 'GET', { 'User-Agent': 'Mozilla/5.0 (Linux; Android 10)' }).then(function(res) {
|
||||
if (res.body && res.body.data && res.body.data.info) return res.body.data.info;
|
||||
throw new Error('kg search failed');
|
||||
});
|
||||
}
|
||||
|
||||
function handleSearchResult(rawList) {
|
||||
var list = [];
|
||||
for (var i = 0; i < rawList.length; i++) {
|
||||
var item = rawList[i];
|
||||
var qualities = {};
|
||||
if (item.filesize) qualities.standard = String(item.filesize);
|
||||
if (item.sqfilesize) qualities.exhigh = String(item.sqfilesize);
|
||||
if (item.flacfilesize) qualities.lossless = String(item.flacfilesize);
|
||||
var picUrl = item.img || '';
|
||||
list.push({
|
||||
id: item.hash || item.audio_id || '',
|
||||
name: item.songname || '',
|
||||
artists: item.singername || '',
|
||||
source: 'kg',
|
||||
pic: picUrl,
|
||||
mPic: picUrl,
|
||||
sPic: picUrl,
|
||||
albumName: item.album_name || '',
|
||||
albumId: item.album_id || '',
|
||||
interval: item.duration ? formatPlayTime(item.duration) : '',
|
||||
qualities: qualities
|
||||
});
|
||||
}
|
||||
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: 'kg' };
|
||||
});
|
||||
}
|
||||
|
||||
// ===== 播放链接 =====
|
||||
function getUrl(songInfo, type) {
|
||||
var q = type || '128k';
|
||||
var songId = 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=kg&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.pic) return Promise.resolve(songInfo.pic);
|
||||
return Promise.resolve('');
|
||||
}
|
||||
|
||||
// ===== 歌词 =====
|
||||
function getLyric(songInfo) {
|
||||
var hash = songInfo.id || '';
|
||||
if (!hash) return Promise.resolve('');
|
||||
return requestUrl('https://krcs.kugou.com/search?ver=1&man=yes&client=mobi&keyword=&duration=&hash=' + encodeURIComponent(hash) + '&album_audio_id=', 'GET', {
|
||||
'User-Agent': 'Mozilla/5.0 (Linux; Android 10)'
|
||||
}).then(function(res) {
|
||||
if (res.body && res.body.candidates && res.body.candidates.length) {
|
||||
var id = res.body.candidates[0].id;
|
||||
var accesskey = res.body.candidates[0].accesskey;
|
||||
return requestUrl('https://krcs.kugou.com/download?ver=1&client=pc&id=' + encodeURIComponent(id) + '&accesskey=' + encodeURIComponent(accesskey) + '&fmt=lrc&charset=utf8', 'GET', {
|
||||
'User-Agent': 'Mozilla/5.0 (Linux; Android 10)'
|
||||
}).then(function(res2) {
|
||||
if (res2.body && res2.body.content) {
|
||||
try { return Buffer.from(res2.body.content, 'base64').toString('utf8'); } catch(e) { return ''; }
|
||||
}
|
||||
return '';
|
||||
});
|
||||
}
|
||||
return '';
|
||||
}).catch(function() { return ''; });
|
||||
}
|
||||
|
||||
// ===== 音乐详情 =====
|
||||
function musicDetail(songmid) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
function musicInfo(songmid) { return musicDetail(songmid); }
|
||||
|
||||
// ===== 热搜 =====
|
||||
function hotSearch() { return Promise.resolve({ source: 'kg', 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: 'kg' }); }
|
||||
|
||||
// ===== 歌手 =====
|
||||
function singer() { return Promise.resolve({ source: 'kg', list: [], total: 0 }); }
|
||||
|
||||
// ===== 专辑 =====
|
||||
function album() { return Promise.resolve({ source: 'kg', list: [], total: 0 }); }
|
||||
|
||||
// ===== 插件信息 =====
|
||||
var pluginInfo = {
|
||||
info: {
|
||||
id: 'koneko_ceru_kg',
|
||||
name: '酷狗音乐 - Koneko 聆澜',
|
||||
version: '0.1.0',
|
||||
source: 'kg',
|
||||
description: '酷狗音乐音源,基于 CeruMusic musicSdk 搜索 + Ceru 播放接口'
|
||||
},
|
||||
ext: [],
|
||||
env: [{ key: 'ceru_key', name: 'Ceru API Key', description: '聆澜播放接口密钥' }],
|
||||
quality: [
|
||||
{ name: '标准', ui: '标', id: '128k' },
|
||||
{ name: '高品', ui: 'HQ', id: '320k' },
|
||||
{ name: '无损', ui: 'SQ', id: 'flac' }
|
||||
],
|
||||
supportFunc: []
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
musicSearch: { search: search },
|
||||
tipSearch: { search: tipSearch },
|
||||
hotSearch: { getList: hotSearch },
|
||||
leaderboard: { getBoards: leaderboard },
|
||||
songList: songList,
|
||||
artists: singer,
|
||||
album: album,
|
||||
getLyric: getLyric,
|
||||
getPic: getPic,
|
||||
getUrl: getUrl,
|
||||
musicDetail: musicDetail,
|
||||
musicInfo: musicInfo,
|
||||
pluginInfo: pluginInfo
|
||||
};
|
||||
Reference in New Issue
Block a user