feat: migrate ChKSz API and support API key authentication
This commit is contained in:
+5
-5
@@ -1,11 +1,11 @@
|
||||
const { API } = require('./config');
|
||||
const { API, buildApiUrl } = require('./config');
|
||||
const { fetchJSON } = require('./network');
|
||||
const { formatPersonNames } = require('./utils');
|
||||
|
||||
async function searchSongs(keyword, limit = 10) {
|
||||
const res = await fetchJSON(`${API.search}?keyword=${encodeURIComponent(keyword)}&limit=${limit}`);
|
||||
const res = await fetchJSON(buildApiUrl(API.search, { keyword, limit }));
|
||||
if (res.code !== 200) {
|
||||
console.log('❌ 搜索无结果');
|
||||
console.log(`❌ 搜索失败: ${res.msg || '无结果'}`);
|
||||
return [];
|
||||
}
|
||||
const songs = Array.isArray(res.data) ? res.data : (res.data?.songs || []);
|
||||
@@ -40,9 +40,9 @@ async function interactiveSearch(rl, ask) {
|
||||
}
|
||||
|
||||
async function fetchPlaylist(playlistId) {
|
||||
const res = await fetchJSON(`${API.playlist}?id=${playlistId}`);
|
||||
const res = await fetchJSON(buildApiUrl(API.playlist, { id: playlistId }));
|
||||
if (!res.data?.tracks?.length) {
|
||||
console.log('❌ 歌单获取失败或为空');
|
||||
console.log(`❌ 歌单获取失败或为空${res.msg ? `: ${res.msg}` : ''}`);
|
||||
return null;
|
||||
}
|
||||
return res.data;
|
||||
|
||||
+20
-4
@@ -5,10 +5,11 @@ const ROOT_DIR = path.join(__dirname, '..');
|
||||
const APP_CONFIG_FILE = path.join(ROOT_DIR, 'config.json');
|
||||
const DEFAULT_APP_CONFIG = {
|
||||
api: {
|
||||
music: 'https://api.chksz.top/api/163_music',
|
||||
lyric: 'https://api.chksz.top/api/163_lyric',
|
||||
search: 'https://api.chksz.top/api/163_search',
|
||||
playlist: 'https://api.chksz.top/api/163_playlist',
|
||||
apikey: process.env.CHKSZ_API_KEY || '',
|
||||
music: 'https://api.chksz.com/api/163_music',
|
||||
lyric: 'https://api.chksz.com/api/163_lyric',
|
||||
search: 'https://api.chksz.com/api/163_search',
|
||||
playlist: 'https://api.chksz.com/api/163_playlist',
|
||||
neteaseBaseUrl: 'https://music.163.com',
|
||||
},
|
||||
paths: {
|
||||
@@ -51,12 +52,26 @@ function getConfigString(value, fallback) {
|
||||
|
||||
const APP_CONFIG = loadAppConfig();
|
||||
const API = {
|
||||
apikey: getConfigString(APP_CONFIG.api.apikey, DEFAULT_APP_CONFIG.api.apikey),
|
||||
music: getConfigString(APP_CONFIG.api.music, DEFAULT_APP_CONFIG.api.music),
|
||||
lyric: getConfigString(APP_CONFIG.api.lyric, DEFAULT_APP_CONFIG.api.lyric),
|
||||
search: getConfigString(APP_CONFIG.api.search, DEFAULT_APP_CONFIG.api.search),
|
||||
playlist: getConfigString(APP_CONFIG.api.playlist, DEFAULT_APP_CONFIG.api.playlist),
|
||||
neteaseBaseUrl: getConfigString(APP_CONFIG.api.neteaseBaseUrl, DEFAULT_APP_CONFIG.api.neteaseBaseUrl),
|
||||
};
|
||||
|
||||
function buildApiUrl(endpoint, params = {}) {
|
||||
const url = new URL(endpoint);
|
||||
for (const [key, value] of Object.entries(params)) {
|
||||
if (value !== undefined && value !== null) {
|
||||
url.searchParams.set(key, String(value));
|
||||
}
|
||||
}
|
||||
if (API.apikey && !url.searchParams.has('apikey')) {
|
||||
url.searchParams.set('apikey', API.apikey);
|
||||
}
|
||||
return url.toString();
|
||||
}
|
||||
const NETEASE_BASE_URL = API.neteaseBaseUrl.replace(/\/$/, '');
|
||||
const DOWNLOAD_DIR = resolveConfigPath(APP_CONFIG.paths.downloadDir, DEFAULT_APP_CONFIG.paths.downloadDir);
|
||||
const HISTORY_FILE = resolveConfigPath(APP_CONFIG.paths.historyFile, DEFAULT_APP_CONFIG.paths.historyFile);
|
||||
@@ -123,4 +138,5 @@ module.exports = {
|
||||
HISTORY_LIMIT,
|
||||
getConfig,
|
||||
setConfig,
|
||||
buildApiUrl,
|
||||
};
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { API, DEFAULT_RETRIES, DOWNLOAD_DIR } = require('./config');
|
||||
const { API, DEFAULT_RETRIES, DOWNLOAD_DIR, buildApiUrl } = require('./config');
|
||||
const { addHistory } = require('./history');
|
||||
const { fetchLyric, mergeLrc, parseCredits } = require('./lyrics');
|
||||
const { fetchNeteaseAlbumDetail, fetchNeteaseSongDetail } = require('./metadata');
|
||||
@@ -39,7 +39,7 @@ async function downloadSongOnce(songId, level, {
|
||||
outputBaseName = null,
|
||||
} = {}) {
|
||||
console.log(`\n🎵 解析歌曲 ID: ${songId} ...`);
|
||||
const result = await fetchJSON(`${API.music}?id=${songId}&level=${level}`);
|
||||
const result = await fetchJSON(buildApiUrl(API.music, { id: songId, level }));
|
||||
if (result.code !== 200 || !result.data?.url) throw new Error(result.msg || 'API 返回错误');
|
||||
|
||||
const { name, artist: rawArtist, album, url: audioUrl, br, size, level: actualLevel, picUrl } = result.data;
|
||||
|
||||
+2
-2
@@ -1,12 +1,12 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { API, DOWNLOAD_DIR } = require('./config');
|
||||
const { API, DOWNLOAD_DIR, buildApiUrl } = require('./config');
|
||||
const { fetchJSON } = require('./network');
|
||||
const { formatPersonNames } = require('./utils');
|
||||
|
||||
async function fetchLyric(songId) {
|
||||
try {
|
||||
const res = await fetchJSON(`${API.lyric}?id=${songId}`);
|
||||
const res = await fetchJSON(buildApiUrl(API.lyric, { id: songId }));
|
||||
if (res.code !== 200) return null;
|
||||
return res.data;
|
||||
} catch {
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@ const fs = require('fs');
|
||||
const http = require('http');
|
||||
const https = require('https');
|
||||
const path = require('path');
|
||||
const { API, DOWNLOAD_DIR, NETEASE_BASE_URL } = require('./config');
|
||||
const { API, DOWNLOAD_DIR, NETEASE_BASE_URL, buildApiUrl } = require('./config');
|
||||
const { downloadBuffer, fetchJSON } = require('./network');
|
||||
const { sanitizePathSegment } = require('./utils');
|
||||
|
||||
@@ -127,7 +127,7 @@ async function saveAlbumCover(coverUrl, outputDir) {
|
||||
async function fetchFullMetadata(songId) {
|
||||
const [songDetail, chkszData] = await Promise.all([
|
||||
fetchNeteaseSongDetail(songId),
|
||||
fetchJSON(`${API.music}?id=${songId}&level=standard`).catch(() => null),
|
||||
fetchJSON(buildApiUrl(API.music, { id: songId, level: 'standard' })).catch(() => null),
|
||||
]);
|
||||
if (!songDetail) return null;
|
||||
|
||||
|
||||
+13
-1
@@ -7,8 +7,20 @@ function getProtocol(url) {
|
||||
}
|
||||
|
||||
function fetchJSON(url) {
|
||||
let targetUrl = url;
|
||||
try {
|
||||
const { API } = require('./config');
|
||||
const parsed = new URL(url);
|
||||
if (!parsed.searchParams.has('apikey') && API?.apikey && parsed.hostname.includes('chksz.')) {
|
||||
parsed.searchParams.set('apikey', API.apikey);
|
||||
targetUrl = parsed.toString();
|
||||
}
|
||||
} catch {}
|
||||
return new Promise((resolve, reject) => {
|
||||
getProtocol(url).get(url, (res) => {
|
||||
getProtocol(targetUrl).get(targetUrl, (res) => {
|
||||
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
||||
return fetchJSON(res.headers.location).then(resolve).catch(reject);
|
||||
}
|
||||
let data = '';
|
||||
res.on('data', chunk => data += chunk);
|
||||
res.on('end', () => {
|
||||
|
||||
Reference in New Issue
Block a user