netease-music-downloader
Version:
Download music from NetEase Cloud Music
300 lines (299 loc) • 13.5 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getAutoProxy = getAutoProxy;
const axios_1 = __importDefault(require("axios"));
const netease_1 = require("./netease");
const netease_2 = require("./netease");
const cheerio_1 = __importDefault(require("cheerio"));
class ProxyManager {
constructor() {
this.proxyList = [];
this.lastUpdate = null;
this.UPDATE_INTERVAL = 10 * 60 * 1000; // 10 minutes
this.TIMEOUT = 3000; // 3 seconds timeout
this.MAX_PARALLEL_TESTS = 5; // test 5 proxies at once
this.TEST_SONG_ID = '1956534932'; // 用于测试的歌曲ID(热门歌曲)
this.PROXY_SOURCES = [
{
url: 'https://www.kuaidaili.com/free/inha/',
type: 'kuaidaili'
},
// {
// url: 'http://www.89ip.cn/index_1.html',
// type: '89ip'
// },
{
url: 'https://proxy.ip3366.net/free/',
type: 'ip3366'
},
{
url: 'https://proxylist.geonode.com/api/proxy-list?filterUpTime=90&country=CN&protocols=http%2Chttps&limit=100',
type: 'geonode'
},
{
url: 'https://api.proxyscrape.com/v2/?request=getproxies&protocol=http&country=CN&ssl=all&anonymity=all',
type: 'proxyscrape'
},
{
url: 'https://raw.githubusercontent.com/fate0/proxylist/master/proxy.list',
type: 'fate0'
},
{
url: 'https://raw.githubusercontent.com/sunny9577/proxy-scraper/master/proxies.json',
type: 'sunny9577'
}
];
}
// 强制更新代理列表
async forceUpdateProxyList() {
this.lastUpdate = null;
await this.updateProxyList();
}
async getWorkingProxy(forceUpdate = false) {
if (forceUpdate || !this.lastUpdate || Date.now() - this.lastUpdate.getTime() > this.UPDATE_INTERVAL) {
await this.updateProxyList();
}
const sortedProxies = [...this.proxyList].sort((a, b) => {
if (a.lastChecked && b.lastChecked) {
return b.lastChecked.getTime() - a.lastChecked.getTime();
}
if (a.lastChecked)
return -1;
if (b.lastChecked)
return 1;
if (!a.speed)
return 1;
if (!b.speed)
return -1;
return a.speed - b.speed;
});
const chunks = [];
for (let i = 0; i < sortedProxies.length; i += this.MAX_PARALLEL_TESTS) {
chunks.push(sortedProxies.slice(i, i + this.MAX_PARALLEL_TESTS));
}
for (let i = 0; i < chunks.length; i++) {
const chunk = chunks[i];
console.log(`\n开始测试第 ${i + 1}/${chunks.length} 组代理 Testing group ${i + 1}/${chunks.length} (${chunk.length} proxies)...`);
const results = await Promise.all(chunk.map((proxy) => this.testProxy(proxy)));
const workingIndex = results.findIndex((speed) => speed !== null);
if (workingIndex !== -1) {
const workingProxy = chunk[workingIndex];
workingProxy.speed = results[workingIndex];
workingProxy.lastChecked = new Date();
const proxyUrl = `${workingProxy.protocol}://${workingProxy.host}:${workingProxy.port}`;
console.log(`\n✨ 找到可用代理 Found working proxy: ${proxyUrl}`);
return workingProxy;
}
console.log(`❌ 第 ${i + 1} 组代理全部测试失败 All proxies in group ${i + 1} failed`);
}
if (this.proxyList.length === 0) {
console.log('\n💡 代理列表为空,尝试重新获取 Proxy list empty, trying to update...');
await this.updateProxyList();
return this.getWorkingProxy();
}
console.log('\n❌ 所有代理均不可用 All proxies failed');
return null;
}
async updateProxyList() {
this.proxyList = [];
console.log('\n🔄 正在从代理源获取代理列表 Fetching proxy list from sources...');
const results = await Promise.all(this.PROXY_SOURCES.map(async (source) => {
try {
console.log(`- 正在获取 Fetching from: ${source.url}`);
const response = await axios_1.default.get(source.url, { timeout: this.TIMEOUT });
const proxies = this.parseProxyList(response.data, source.type);
console.log(`✅ 成功获取 ${proxies.length} 个代理 Successfully got ${proxies.length} proxies from ${source.url}`);
return proxies;
}
catch (error) {
console.log(`❌ 获取失败 Failed to fetch from ${source.url}:`, error instanceof Error ? error.message : 'Unknown error');
return [];
}
}));
this.proxyList = results.flat();
this.lastUpdate = new Date();
console.log(`\n📊 代理列表更新完成,共找到 ${this.proxyList.length} 个中国代理 Proxy list updated, found ${this.proxyList.length} Chinese proxies`);
}
parseProxyList(data, sourceType) {
try {
switch (sourceType) {
case 'proxy-list.download':
return data
.split('\n')
.filter(Boolean)
.map((line) => {
const [host, port] = line.split(':');
return { host, port: parseInt(port), protocol: 'http', country: 'CN' };
});
case 'geonode':
return data.data.map((item) => ({
host: item.ip,
port: parseInt(item.port),
protocol: item.protocols[0],
country: 'CN',
}));
case 'proxyscrape':
return data
.split('\n')
.filter(Boolean)
.map((line) => {
const [host, port] = line.split(':');
return { host, port: parseInt(port), protocol: 'http', country: 'CN' };
});
case 'openproxylist':
return data
.split('\n')
.filter(Boolean)
.map((line) => {
const [host, port] = line.split(':');
return { host, port: parseInt(port), protocol: 'http', country: 'CN' };
});
case 'fate0':
return data
.split('\n')
.filter(Boolean)
.map((line) => {
try {
const item = JSON.parse(line);
if (item.country === 'CN') {
return {
host: item.host,
port: item.port,
protocol: item.type || 'http',
country: 'CN',
};
}
}
catch (e) { }
return null;
})
.filter((item) => item !== null);
case 'sunny9577':
return JSON.parse(data)
.filter((item) => item.country === 'CN')
.map((item) => ({
host: item.ip,
port: parseInt(item.port),
protocol: 'http',
country: 'CN',
}));
case 'kuaidaili':
const $ = cheerio_1.default.load(data);
return $('table tbody tr')
.map((_, tr) => {
const host = $(tr).find('td[data-title="IP"]').text().trim();
const port = parseInt($(tr).find('td[data-title="PORT"]').text().trim());
if (host && port) {
return { host, port, protocol: 'http', country: 'CN' };
}
return null;
})
.get()
.filter(Boolean);
case '89ip':
const $89 = cheerio_1.default.load(data);
return $89('table tbody tr')
.map((_, tr) => {
const host = $89(tr).find('td:nth-child(1)').text().trim();
const port = parseInt($89(tr).find('td:nth-child(2)').text().trim());
if (host && port) {
return { host, port, protocol: 'http', country: 'CN' };
}
return null;
})
.get()
.filter(Boolean);
case '7yip':
const $7yip = cheerio_1.default.load(data);
return $7yip('table tbody tr')
.map((_, tr) => {
const host = $7yip(tr).find('td:nth-child(1)').text().trim();
const port = parseInt($7yip(tr).find('td:nth-child(2)').text().trim());
if (host && port) {
return { host, port, protocol: 'http', country: 'CN' };
}
return null;
})
.get()
.filter(Boolean);
case 'ip3366':
const $ip3366 = cheerio_1.default.load(data);
return $ip3366('table tbody tr').map((_, tr) => {
const host = $ip3366(tr).find('td:nth-child(1)').text().trim();
const port = parseInt($ip3366(tr).find('td:nth-child(2)').text().trim());
if (host && port) {
return { host, port, protocol: 'http', country: 'CN' };
}
return null;
}).get().filter(Boolean);
default:
return [];
}
}
catch (error) {
console.log(`解析代理列表失败 Failed to parse proxy list from ${sourceType}:`, error instanceof Error ? error.message : 'Unknown error');
return [];
}
}
async testProxy(proxy) {
const startTime = Date.now();
const proxyUrl = `${proxy.protocol}://${proxy.host}:${proxy.port}`;
console.log(`正在测试代理 Testing proxy: ${proxyUrl}`);
try {
// 第一步:测试基本连接
await axios_1.default.get('https://music.163.com', {
timeout: this.TIMEOUT,
proxy: {
host: proxy.host,
port: proxy.port,
protocol: proxy.protocol,
},
});
// 第二步:测试API连接
await axios_1.default.get('https://music.163.com/api/v3/playlist/detail', {
timeout: this.TIMEOUT,
proxy: {
host: proxy.host,
port: proxy.port,
protocol: proxy.protocol,
},
});
// 第三步:测试实际歌曲信息获取
(0, netease_1.setProxy)(proxyUrl); // 设置代理
try {
await (0, netease_2.getSongInfo)(this.TEST_SONG_ID);
const speed = Date.now() - startTime;
console.log(`✅ 代理完全可用 Proxy fully working: ${proxyUrl}, 响应时间 Response time: ${speed}ms`);
return speed; // 如果测试成功,保持代理设置
}
catch (error) {
console.log(`❌ 代理可连接但无法获取歌曲信息 Proxy connected but failed to get song info: ${proxyUrl}`);
(0, netease_1.setProxy)(undefined); // 只在测试失败时清除代理
return null;
}
}
catch (error) {
console.log(`❌ 代理连接失败 Proxy connection failed: ${proxyUrl}`);
(0, netease_1.setProxy)(undefined); // 只在测试失败时清除代理
return null;
}
}
}
const proxyManager = new ProxyManager();
async function getAutoProxy(forceUpdate = false) {
try {
const proxy = await proxyManager.getWorkingProxy(forceUpdate);
if (proxy) {
const proxyUrl = `${proxy.protocol}://${proxy.host}:${proxy.port}`;
(0, netease_1.setProxy)(proxyUrl);
return proxyUrl;
}
}
catch (error) {
console.error('Failed to get auto proxy:', error instanceof Error ? error.message : 'Unknown error');
}
return null;
}