Files
Koneko_api_for_QZ-Music/Koneko_聆澜_GIT音源_v0.0.3_QZv2.js

213 lines
7.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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);
}
// ===== Git音源配置 =====
var GITCODE_CONFIG = { owner: 'ikun_0014', repo: 'music', token: 'WzsER9knWNgC_4tjeJCtHKcN' };
function getGitcodeUrl() {
return 'https://api.gitcode.com/api/v5/repos/' + GITCODE_CONFIG.owner + '/' + GITCODE_CONFIG.repo + '/raw/audio_database.json?access_token=' + GITCODE_CONFIG.token;
}
var cachedList = null;
var cacheTime = 0;
var CACHE_TTL = 5 * 60 * 1000;
function fetchAllGitMusic() {
var now = Date.now();
if (cachedList && now - cacheTime < CACHE_TTL) return Promise.resolve(cachedList);
return requestUrl(getGitcodeUrl(), 'GET', { 'User-Agent': 'Mozilla/5.0' }).then(function(res) {
var body = Array.isArray(res.body) ? res.body : [];
var list = [];
for (var i = 0; i < body.length; i++) list.push(formatGitItem(body[i]));
cachedList = list;
cacheTime = now;
return list;
});
}
function formatGitItem(item) {
var types = [];
var _types = {};
var fmt = (item.format || '').toLowerCase();
if (fmt === 'flac') { types.push({ type: 'flac', size: item.filesize || '' }); _types.flac = { size: item.filesize || '' }; }
else if (fmt === 'mp3') { types.push({ type: '320k', size: item.filesize || '' }); _types['320k'] = { size: item.filesize || '' }; }
else { types.push({ type: '128k', size: item.filesize || '' }); _types['128k'] = { size: item.filesize || '' }; }
return {
singer: item.artist || '未知歌手',
name: item.title || item.filename || '',
albumName: item.album || '未知专辑',
albumId: '',
songmid: item.relative_path || '',
source: 'git',
interval: '',
img: item.img || '',
lrc: item.lyrics || null,
types: types, _types: _types, typeUrl: {},
_gitcodeData: item
};
}
// ===== 搜索实现 =====
function musicSearch(str, page, limit) {
return fetchAllGitMusic().then(function(list) {
var regexStr = str.split('').map(function(c) { return c.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); }).join('.*');
var regex = new RegExp(regexStr, 'i');
var filtered = [];
for (var i = 0; i < list.length; i++) {
var item = list[i];
if (regex.test(item.singer) || regex.test(item.name) || regex.test(item.albumName)) filtered.push(item);
}
var start = (page - 1) * limit;
return { data: filtered.slice(start, start + limit), total: filtered.length };
});
}
function handleSearchResult(result) {
return result.data || [];
}
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(result) {
var list = handleSearchResult(result);
return { list: list, allPage: Math.ceil(result.total / limit), limit: limit, total: result.total, source: 'git' };
});
}
// ===== 播放链接 =====
function getUrl(songInfo, type) {
var q = type || '128k';
var songId = songInfo.songmid || '';
if (!songId) return Promise.resolve('');
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=git&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) {
if (songInfo.lrc) return Promise.resolve(songInfo.lrc);
return Promise.resolve('');
}
// ===== 音乐详情 =====
function musicDetail(songmid) {
return Promise.resolve(null);
}
function musicInfo(songmid) { return musicDetail(songmid); }
// ===== 热搜 =====
function hotSearch() { return Promise.resolve({ source: 'git', 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: 'git' }); }
// ===== 歌手 =====
function singer() { return Promise.resolve({ source: 'git', list: [], total: 0 }); }
// ===== 专辑 =====
function album() { return Promise.resolve({ source: 'git', list: [], total: 0 }); }
// ===== 插件信息 =====
var pluginInfo = {
id: 'koneko_ceru_git',
name: 'GIT音源 - Koneko 聆澜',
version: '0.0.3',
source: 'git',
description: 'GIT音源基于 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: 'git' }); }, getList: songList },
singer: { getInfo: singer, getSongList: singer, getAlbumList: singer },
album: { getInfo: album, getSongList: album },
getLyric: getLyric,
getPic: getPic,
getUrl: getUrl,
musicDetail: musicDetail,
musicInfo: musicInfo
};