/*! * @name 墨澜聚合音源 * @description 全平台支持flac,酷狗、QQ、网易支持母带(全用的是别人的接口,类似于全豆要) * @version 2.2.0 * @author 白姬9527(2449067834) * */ const { EVENT_NAMES, request, on, send, utils, env, version, currentScriptInfo } = globalThis.lx // ==================== 解析头部注解 ==================== const currentScript = currentScriptInfo ? currentScriptInfo.rawScript : (typeof document !== 'undefined' ? document.currentScript?.textContent || '' : '') const parseHeader = (str) => { const comment = /^\/\*!(?:.|\n)+?\*\//.exec(str)?.[0] if (!comment) return {} const result = {} const pairs = [ { key: 'tx_cookie', regex: /\*\s*@tx_cookie\s+(.+)/ }, { key: 'wy_cookie', regex: /\*\s*@wy_cookie\s+(.+)/ }, ] for (const { key, regex } of pairs) { const match = regex.exec(comment) const val = match?.[1]?.trim() result[key] = (!val || val === 'null') ? '' : val } return result } const config = parseHeader(currentScript) const TX_COOKIE = config.tx_cookie const WY_COOKIE = config.wy_cookie const HAS_TX_COOKIE = !!TX_COOKIE const HAS_WY_COOKIE = !!WY_COOKIE // ==================== 音质列表(参照ikun音源格式) ==================== const MUSIC_QUALITY = JSON.parse(HAS_TX_COOKIE && HAS_WY_COOKIE ? '{"tx":["128k","320k","flac","flac24bit","hires","atmos","atmos_plus","master"],"wy":["128k","320k","flac","flac24bit","hires","atmos","master"],"kw":["128k","192k","320k","flac","flac24bit"],"kg":["128k","320k","flac","hires","atmos","master"],"mg":["128k","320k","flac"]}' : HAS_TX_COOKIE ? '{"tx":["128k","320k","flac","flac24bit","hires","atmos","atmos_plus","master"],"wy":["128k","320k","flac"],"kw":["128k","192k","320k","flac","flac24bit"],"kg":["128k","320k","flac","hires","atmos","master"],"mg":["128k","320k","flac"]}' : HAS_WY_COOKIE ? '{"tx":["128k","320k","flac"],"wy":["128k","320k","flac","flac24bit","hires","atmos","master"],"kw":["128k","192k","320k","flac","flac24bit"],"kg":["128k","320k","flac","hires","atmos","master"],"mg":["128k","320k","flac"]}' : '{"tx":["128k","320k","flac"],"wy":["128k","320k","flac"],"kw":["128k","192k","320k","flac","flac24bit"],"kg":["128k","320k","flac","hires","atmos","master"],"mg":["128k","320k","flac"]}' ) const MUSIC_SOURCE = Object.keys(MUSIC_QUALITY) // ==================== 工具函数 ==================== const httpFetch = (url, options = { method: 'GET' }) => new Promise((resolve, reject) => { request(url, options, (err, resp) => { if (err) return reject(err) let body = resp.body if (typeof body === 'string') { const trimmed = body.trim() if (trimmed.startsWith('{') || trimmed.startsWith('[') || trimmed.startsWith('"')) { try { body = JSON.parse(trimmed) } catch (e) {} } } resolve({ body, statusCode: resp.statusCode, headers: resp.headers || {} }) }) }) const md5 = (str) => utils.crypto.md5(str) const randomGuid = () => { const hex = '0123456789abcdef' let guid = '' for (let i = 0; i < 32; i++) guid += hex[Math.floor(Math.random() * 16)] return guid } const aesEncrypt = (data, key, iv, mode) => { if (!version) mode = mode.split('-').pop() return utils.crypto.aesEncrypt(data, mode, key, iv) } const buf2hex = (buffer) => { return version ? utils.buffer.bufToString(buffer, 'hex') : [...new Uint8Array(buffer)].map(x => x.toString(16).padStart(2, '0')).join('') } const wyEapi = (url, object) => { const eapiKey = 'e82ckenh8dichen8' const text = typeof object === 'object' ? JSON.stringify(object) : object const digest = md5('nobody' + url + 'use' + text + 'md5forencrypt') const data = url + '-36cd479b6b5-' + text + '-36cd479b6b5-' + digest return { params: buf2hex(aesEncrypt(data, eapiKey, '', 'aes-128-ecb')).toUpperCase() } } const objToForm = (obj) => Object.keys(obj).map(k => encodeURIComponent(k) + '=' + encodeURIComponent(obj[k])).join('&') const extractUrl = (obj, paths) => { for (const path of paths) { let val = obj for (const key of path) { if (val == null) { val = undefined; break } val = val[key] } if (Array.isArray(val)) val = val[0] if (typeof val === 'string' && (val.startsWith('http://') || val.startsWith('https://'))) return val if (typeof val === 'string' && val.startsWith('//')) return 'https:' + val } return '' } const cleanUrl = (url) => { if (!url) return '' const s = String(url).replace(/\\?u0026/gi, '&').replace(/\\&/g, '&').replace(/\$/g, '&') const idx = s.indexOf('?') return idx > 0 ? s.substring(0, idx) : s } // ==================== 通用音质转Level工具 ==================== const qualityToLevel = (quality) => { const map = { '128k': 'standard', '192k': 'standard', '320k': 'exhigh', 'flac': 'lossless', 'flac24bit': 'lossless', 'hires': 'lossless', 'atmos': 'lossless', 'atmos_plus': 'lossless', 'master': 'lossless', } return map[quality] || 'standard' } // ==================== SHA256 工具(用于 Hello World API 签名) ==================== const sha256 = (function() { var HEX_CHARS = '0123456789abcdef'.split(''); function Sha256() { this.blocks = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; this.h0 = 0x6a09e667; this.h1 = 0xbb67ae85; this.h2 = 0x3c6ef372; this.h3 = 0xa54ff53a; this.h4 = 0x510e527f; this.h5 = 0x9b05688c; this.h6 = 0x1f83d9ab; this.h7 = 0x5be0cd19; this.block = this.start = this.bytes = this.hBytes = 0; this.finalized = this.hashed = false; this.first = true; } Sha256.prototype.update = function(message) { if (this.finalized) return; var notString = typeof message !== 'string'; var blocks = this.blocks; for (var i = 0; i < message.length; i++) { if (this.hashed) { this.hashed = false; blocks[0] = this.block; blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; } var code = notString ? message[i] : message.charCodeAt(i); blocks[this.start >> 2] |= code << (24 - (this.start % 4) * 8); this.start++; if (this.start === 64) { this.block = blocks[16]; this.start = 0; this.hash(); this.hashed = true; } } this.bytes += message.length; if (this.bytes > 4294967295) { this.hBytes += this.bytes / 4294967296 << 0; this.bytes = this.bytes % 4294967296; } return this; }; Sha256.prototype.finalize = function() { if (this.finalized) return; this.finalized = true; var blocks = this.blocks; var i = this.start; blocks[16] = this.block; blocks[i >> 2] |= 0x80 << (24 - (i % 4) * 8); this.block = blocks[16]; if (i >= 56) { if (!this.hashed) this.hash(); blocks[0] = this.block; blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; } blocks[14] = this.hBytes << 3 | this.bytes >>> 29; blocks[15] = this.bytes << 3; this.hash(); }; Sha256.prototype.hash = function() { var K = [0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2]; var a = this.h0, b = this.h1, c = this.h2, d = this.h3, e = this.h4, f = this.h5, g = this.h6, h = this.h7, blocks = this.blocks; for (var j = 0; j < 64; j++) { if (j >= 16) { var w0 = blocks[j - 15]; var w1 = blocks[j - 2]; var s0 = ((w0 >>> 7) | (w0 << 25)) ^ ((w0 >>> 18) | (w0 << 14)) ^ (w0 >>> 3); var s1 = ((w1 >>> 17) | (w1 << 15)) ^ ((w1 >>> 19) | (w1 << 13)) ^ (w1 >>> 10); blocks[j] = blocks[j - 16] + s0 + blocks[j - 7] + s1; } var S1 = ((e >>> 6) | (e << 26)) ^ ((e >>> 11) | (e << 21)) ^ ((e >>> 25) | (e << 7)); var ch = (e & f) ^ ((~e) & g); var temp1 = h + S1 + ch + K[j] + (blocks[j] >>> 0); var S0 = ((a >>> 2) | (a << 30)) ^ ((a >>> 13) | (a << 19)) ^ ((a >>> 22) | (a << 10)); var maj = (a & b) ^ (a & c) ^ (b & c); var temp2 = S0 + maj; h = g; g = f; f = e; e = (d + temp1) >>> 0; d = c; c = b; b = a; a = (temp1 + temp2) >>> 0; } this.h0 = (this.h0 + a) >>> 0; this.h1 = (this.h1 + b) >>> 0; this.h2 = (this.h2 + c) >>> 0; this.h3 = (this.h3 + d) >>> 0; this.h4 = (this.h4 + e) >>> 0; this.h5 = (this.h5 + f) >>> 0; this.h6 = (this.h6 + g) >>> 0; this.h7 = (this.h7 + h) >>> 0; }; Sha256.prototype.hex = function() { this.finalize(); var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3, h4 = this.h4, h5 = this.h5, h6 = this.h6, h7 = this.h7; return HEX_CHARS[(h0 >> 28) & 0x0F] + HEX_CHARS[(h0 >> 24) & 0x0F] + HEX_CHARS[(h0 >> 20) & 0x0F] + HEX_CHARS[(h0 >> 16) & 0x0F] + HEX_CHARS[(h0 >> 12) & 0x0F] + HEX_CHARS[(h0 >> 8) & 0x0F] + HEX_CHARS[(h0 >> 4) & 0x0F] + HEX_CHARS[h0 & 0x0F] + HEX_CHARS[(h1 >> 28) & 0x0F] + HEX_CHARS[(h1 >> 24) & 0x0F] + HEX_CHARS[(h1 >> 20) & 0x0F] + HEX_CHARS[(h1 >> 16) & 0x0F] + HEX_CHARS[(h1 >> 12) & 0x0F] + HEX_CHARS[(h1 >> 8) & 0x0F] + HEX_CHARS[(h1 >> 4) & 0x0F] + HEX_CHARS[h1 & 0x0F] + HEX_CHARS[(h2 >> 28) & 0x0F] + HEX_CHARS[(h2 >> 24) & 0x0F] + HEX_CHARS[(h2 >> 20) & 0x0F] + HEX_CHARS[(h2 >> 16) & 0x0F] + HEX_CHARS[(h2 >> 12) & 0x0F] + HEX_CHARS[(h2 >> 8) & 0x0F] + HEX_CHARS[(h2 >> 4) & 0x0F] + HEX_CHARS[h2 & 0x0F] + HEX_CHARS[(h3 >> 28) & 0x0F] + HEX_CHARS[(h3 >> 24) & 0x0F] + HEX_CHARS[(h3 >> 20) & 0x0F] + HEX_CHARS[(h3 >> 16) & 0x0F] + HEX_CHARS[(h3 >> 12) & 0x0F] + HEX_CHARS[(h3 >> 8) & 0x0F] + HEX_CHARS[(h3 >> 4) & 0x0F] + HEX_CHARS[h3 & 0x0F] + HEX_CHARS[(h4 >> 28) & 0x0F] + HEX_CHARS[(h4 >> 24) & 0x0F] + HEX_CHARS[(h4 >> 20) & 0x0F] + HEX_CHARS[(h4 >> 16) & 0x0F] + HEX_CHARS[(h4 >> 12) & 0x0F] + HEX_CHARS[(h4 >> 8) & 0x0F] + HEX_CHARS[(h4 >> 4) & 0x0F] + HEX_CHARS[h4 & 0x0F] + HEX_CHARS[(h5 >> 28) & 0x0F] + HEX_CHARS[(h5 >> 24) & 0x0F] + HEX_CHARS[(h5 >> 20) & 0x0F] + HEX_CHARS[(h5 >> 16) & 0x0F] + HEX_CHARS[(h5 >> 12) & 0x0F] + HEX_CHARS[(h5 >> 8) & 0x0F] + HEX_CHARS[(h5 >> 4) & 0x0F] + HEX_CHARS[h5 & 0x0F] + HEX_CHARS[(h6 >> 28) & 0x0F] + HEX_CHARS[(h6 >> 24) & 0x0F] + HEX_CHARS[(h6 >> 20) & 0x0F] + HEX_CHARS[(h6 >> 16) & 0x0F] + HEX_CHARS[(h6 >> 12) & 0x0F] + HEX_CHARS[(h6 >> 8) & 0x0F] + HEX_CHARS[(h6 >> 4) & 0x0F] + HEX_CHARS[h6 & 0x0F] + HEX_CHARS[(h7 >> 28) & 0x0F] + HEX_CHARS[(h7 >> 24) & 0x0F] + HEX_CHARS[(h7 >> 20) & 0x0F] + HEX_CHARS[(h7 >> 16) & 0x0F] + HEX_CHARS[(h7 >> 12) & 0x0F] + HEX_CHARS[(h7 >> 8) & 0x0F] + HEX_CHARS[(h7 >> 4) & 0x0F] + HEX_CHARS[h7 & 0x0F]; }; return function(message) { return new Sha256().update(message).hex(); }; })(); const HELLO_WORLD_API_KEY = 'lxmusic'; const HELLO_WORLD_SECRET_KEY = 'JaJ?a7Nwk_Fgj?2o:znAkst'; const HELLO_WORLD_SCRIPT_MD5 = '1888f9865338afe6d5534b35171c61a4'; const HELLO_WORLD_API_URL = 'https://88.lxmusic.xn--fiqs8s'; const helloWorldSign = (requestPath) => sha256(requestPath + HELLO_WORLD_SCRIPT_MD5 + HELLO_WORLD_SECRET_KEY); const HYW_API_BASE = 'http://103.79.184.97'; const HYW_CARD_KEY = 'MOLAN-BAIJI'; // ==================== QQ 音乐音质文件映射 ==================== const TX_FILE_CONFIG = { '128k': { s: 'M500', e: '.mp3', br: '128k' }, '320k': { s: 'M800', e: '.mp3', br: '320k' }, flac: { s: 'F000', e: '.flac', br: 'flac' }, flac24bit: { s: 'AI00', e: '.flac', br: 'flac24bit' }, hires: { s: 'AI00', e: '.flac', br: 'hires' }, atmos: { s: 'AI00', e: '.flac', br: 'atmos' }, atmos_plus: { s: 'AI00', e: '.flac', br: 'atmos' }, master: { s: 'AI00', e: '.flac', br: 'master' }, } // ==================== 网易云音质映射 ==================== const WY_LEVEL_MAP = { '128k': 'standard', '320k': 'exhigh', flac: 'lossless', flac24bit: 'hires', hires: 'hires', atmos: 'sky', master: 'jymaster', } const WY_BR_MAP = { '128k': 128000, '320k': 320000, flac: 999000, flac24bit: 999000, hires: 999001, atmos: 999002, master: 999003, } // ==================== 酷我音质Level映射(笒鬼鬼等专用) ==================== const KW_LEVEL_MAP = { '128k': '128k', '192k': '128k', '320k': '320k', flac: 'lossless', flac24bit: 'lossless', } // ==================== 酷狗音质Level映射(长青SVIP音源二改版专用) ==================== const KG_LEVEL_MAP = { '128k': 'standard', '192k': 'standard', '320k': 'exhigh', flac: 'lossless', flac24bit: 'hires', hires: 'hires', atmos: 'atmos', atmos_plus: 'atmos', master: 'clear', } // ==================== 酷我流媒体音质Level映射(175.27.166.236:8928 专用) ==================== const KW_STREAM_LEVEL_MAP = { '128k': '128k', '192k': '128k', '320k': '320k', flac: 'flac', flac24bit: 'flac', hires: 'hires', atmos: 'atmos', atmos_plus: 'atmos_plus', master: 'master', } // ==================== Fish API 签名工具 ==================== const FISH_DOMAIN = 'music.gdstudio.xyz' const FISH_VERSION = '20260510' const fishSign = async (secret) => { const timeRes = await httpFetch('https://' + FISH_DOMAIN + '/time', { method: 'GET', timeout: 10000 }) const timeStr = String(Number(timeRes.body) || Date.now()).slice(0, 9) const signInput = FISH_DOMAIN + '|' + FISH_VERSION + '|' + timeStr + '|' + secret return md5(signInput).slice(-8).toUpperCase() } const fishPost = async (params, secret) => { const sign = await fishSign(secret) params.s = sign const body = objToForm(params) const res = await httpFetch('https://' + FISH_DOMAIN + '/api.php', { method: 'POST', timeout: 15000, headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', Origin: 'https://' + FISH_DOMAIN, Referer: 'https://' + FISH_DOMAIN + '/', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', 'X-Requested-With': 'XMLHttpRequest', }, body: body, }) return res.body } // ==================== QQ音乐 后端接口列表(按优先级排列) ==================== const TX_BACKENDS = [ // === 后端1: QQ官方接口(带Cookie可解锁VIP) === { name: 'QQ官方', fetch: async (songmid, quality) => { const fileInfo = TX_FILE_CONFIG[quality] if (!fileInfo) throw new Error('不支持的音质') const guid = randomGuid() const file = fileInfo.s + songmid + fileInfo.e const reqData = { req_0: { module: 'vkey.GetVkeyServer', method: 'CgiGetVkey', param: { filename: [file], guid, songmid: [songmid], songtype: [0], uin: '0', loginflag: HAS_TX_COOKIE ? 1 : 0, platform: '20' }, }, loginUin: '0', comm: { uin: '0', format: 'json', ct: 24, cv: 0 }, } const headers = { 'Content-Type': 'application/json', 'User-Agent': 'Mozilla/5.0', Referer: 'https://y.qq.com/' } if (HAS_TX_COOKIE) headers.Cookie = TX_COOKIE const res = await httpFetch('https://u.y.qq.com/cgi-bin/musicu.fcg', { method: 'POST', headers, body: JSON.stringify(reqData) }) const d = res.body if (d && d.req_0 && d.req_0.data && d.req_0.data.midurlinfo && d.req_0.data.midurlinfo[0] && d.req_0.data.midurlinfo[0].purl) { const sip = d.req_0.data.sip || ['https://isure.stream.qqmusic.qq.com/'] return sip[Math.floor(Math.random() * sip.length)] + d.req_0.data.midurlinfo[0].purl } throw new Error('QQ官方: 无数据') }, }, // === 后端2: 星海音乐源主后端(yy.zddyr.top) === { name: '星海主后端', fetch: async (songmid, quality) => { const res = await httpFetch('https://yy.zddyr.top/lx/api/?source=qq&songmid=' + songmid + '&quality=' + quality, { method: 'GET', timeout: 8000, headers: { 'User-Agent': 'Mozilla/5.0' }, }) const d = res.body if (d && d.code === 200 && d.url) return d.url throw new Error('星海主后端: ' + (d?.msg || '无数据')) }, }, // === 后端3: 星海音乐源备用后端(zrcdy) === { name: '星海备后端', fetch: async (songmid, quality) => { const res = await httpFetch('https://zrcdy.dpdns.org/lx/api/api.php?source=qq&songmid=' + songmid + '&quality=' + quality, { method: 'GET', timeout: 8000, headers: { 'User-Agent': 'Mozilla/5.0' }, }) const d = res.body if (d && d.code === 200 && d.url) return d.url throw new Error('星海备后端: ' + (d?.msg || '无数据')) }, }, // === 后端4: 溯音API(oiapi.net) === { name: '溯音QQ', fetch: async (songmid, quality) => { const brMap = { '128k': '7', '320k': '5', flac: '4', flac24bit: '1', hires: '1', atmos: '1', master: '1' } const br = brMap[quality] || '7' const res = await httpFetch('https://oiapi.net/api/QQ_Music?key=oiapi-ef6133b7-ac2f-dc7d-878c-d3e207a82575&type=json&br=' + br + '&n=1&mid=' + songmid, { method: 'GET', timeout: 8000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json' }, }) const d = res.body const url = extractUrl(d, [['data', 'music'], ['data', 'url'], ['url']]) if (url) return url throw new Error('溯音QQ: 无数据') }, }, // === 后端5: xcvts API(fish源主用) === { name: 'xcvts', fetch: async (songmid, quality) => { const apiKeys = ['78993344b9bf1105655599009cdba3d2', 'ce778eb0d1858edfb4b2071a115f1edf'] const qualityMap = { '128k': 'standard', '320k': 'exhigh', flac: 'lossless', flac24bit: 'hires' } const q = qualityMap[quality] || 'standard' const errors = [] for (const key of apiKeys) { try { const res = await httpFetch('https://api.xcvts.cn/api/music/qq?apiKey=' + key + '&mid=' + songmid + '&type=' + q, { method: 'GET', timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json' }, }) const d = res.body const url = extractUrl(d, [['data', 'music'], ['data', 'url'], ['url']]) if (url) return url } catch (e) { errors.push(e.message) } } throw new Error('xcvts: ' + errors.join(' | ')) }, }, // === 后端6: vkeys API === { name: 'vkeys', fetch: async (songmid, quality) => { const qualityMap = { '128k': '8', '320k': '9', flac: '10', flac24bit: '16', hires: '14', atmos: '13', atmos_plus: '12', master: '11' } const q = qualityMap[quality] if (!q) throw new Error('vkeys 不支持的音质') const res = await httpFetch('https://api.vkeys.cn/v2/music/tencent/geturl?mid=' + songmid + '&quality=' + q, { method: 'GET', timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json' }, }) const d = res.body if (d && d.data && d.data.url) return d.data.url if (d && d.url) return d.url throw new Error('vkeys: 无数据') }, }, // === 后端7: vkeys 旧版API === { name: 'vkeys旧版', fetch: async (songmid, quality) => { const qualityMap = { '128k': '8', '320k': '9', flac: '10', flac24bit: '16', hires: '14', atmos: '13', atmos_plus: '12', master: '11' } const q = qualityMap[quality] if (!q) throw new Error('vkeys旧版 不支持的音质') const res = await httpFetch('https://api.vkeys.cn/music/tencent/song/link?mid=' + songmid + '&quality=' + q, { method: 'GET', timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json' }, }) const d = res.body const url = extractUrl(d, [['data', 'url'], ['url']]) if (url) return url throw new Error('vkeys旧版: 无数据') }, }, // === 后端8: 柳云API(liuyunidc) === { name: '柳云API', fetch: async (songmid, quality) => { const qualityMap = { '128k': '128k', '320k': '320k', flac: 'flac', flac24bit: 'master', hires: 'atmos', atmos: 'atmos', atmos_plus: 'atmos', master: 'master' } const q = qualityMap[quality] || '128k' // 先获取card密钥 let card = '' try { const cardRes = await httpFetch('https://github.com/CharlesPikachu/musicdl/releases/download/keys/baimusic.txt', { method: 'GET', timeout: 5000 }) card = String(cardRes.body || '').trim() } catch (e) {} const res = await httpFetch('https://api.liuyunidc.cn/baimusic/musicurl.php?source=tx&musicId=' + songmid + '&quality=' + q + (card ? '&card=' + encodeURIComponent(card) : ''), { method: 'GET', timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json', Referer: 'http://api.liuyunidc.cn/baimusic/' }, }) const d = res.body const url = extractUrl(d, [['url'], ['data', 'url']]) if (url) return url throw new Error('柳云API: 无数据') }, }, // === 后端9: 317ak API === { name: '317ak', fetch: async (songmid, quality) => { const brMap = { '128k': '5', '320k': '6', flac: '8', flac24bit: '7', hires: '9', atmos: '10', atmos_plus: '10', master: '10' } const br = brMap[quality] || '5' const res = await httpFetch('https://api.317ak.cn/api/yinyue/qqyinyue?ckey=ZK76QJCIH5PPICJOOXUH&i=' + songmid + '&br=' + br + '&type=json&lrc=1', { method: 'GET', timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json' }, }) const d = res.body const url = extractUrl(d, [['url'], ['data', 'url']]) if (url) return url throw new Error('317ak: 无数据') }, }, // === 后端10: nki.pw API(flac用) === { name: 'nki', fetch: async (songmid, quality) => { if (quality !== 'flac') throw new Error('nki仅支持flac') const apiKeys = ['28fece925439b052792a97989c870ced3803a71c6b534f71e5a5338b2d31ef8', 'c4c4f5fc36bad4cacb98839e14fea40277b35ea2eb1babdad7bbde128400f3b1'] const errors = [] for (const key of apiKeys) { try { const res = await httpFetch('https://api.nki.pw/API/music_open_api.php?mid=' + songmid + '&apikey=' + key, { method: 'GET', timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json' }, }) const d = res.body const url = extractUrl(d, [['song_play_url_sq'], ['song_play_url_pq'], ['song_play_url_hq'], ['song_play_url'], ['song_play_url_standard']]) if (url) return url } catch (e) { errors.push(e.message) } } throw new Error('nki: ' + errors.join(' | ')) }, }, // === 后端11: tang.api.s01s.cn(flac用) === { name: 'tang', fetch: async (songmid, quality) => { if (quality !== 'flac') throw new Error('tang仅支持flac') const res = await httpFetch('https://tang.api.s01s.cn/music_open_api.php?mid=' + songmid, { method: 'GET', timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json' }, }) const d = res.body const url = extractUrl(d, [['song_play_url_sq'], ['song_play_url_pq'], ['song_play_url_hq'], ['song_play_url'], ['song_play_url_standard']]) if (url) return url throw new Error('tang: 无数据') }, }, // === 后端12: 玉宁熙API === { name: '玉宁熙', fetch: async (songmid, quality) => { const qualityMap = { '128k': '标准', '320k': 'HQ', flac: 'SQ', flac24bit: '母带', hires: '母带', atmos: '母带', master: '母带' } const q = qualityMap[quality] || '标准' const res = await httpFetch('https://api-v2.yuafeng.cn/API/qqmusic.php?type=' + encodeURIComponent(q) + '&mid=' + songmid + '&apikey=3ff23523e47465224a3f48579acf41f241540ce04b6cc0b94164f37a5b6299d5', { method: 'GET', timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json' }, }) const d = res.body if (d && d.data && d.data.music) return d.data.music throw new Error('玉宁熙: 无数据') }, }, // === 后端13: 收集の聚合接口(cyapi) === { name: '收集聚合', fetch: async (songmid, quality) => { const res = await httpFetch('https://cyapi.top/API/qq_music.php?apikey=1ffdf5733f5d538760e63d7e46ba17438d9f7b9dfc18c51be1109386fd74c3a1&type=json&mid=' + songmid, { method: 'GET', timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json' }, }) const d = res.body if (d && d.url) return d.url throw new Error('收集聚合: 无数据') }, }, // === 后端14: 88.lxmusic(独家音源v3/v4) === { name: 'lxmusic88', fetch: async (songmid, quality) => { try { const res = await httpFetch('https://88.lxmusic.xn--fiqs8s/lxmusicv4/url/tx/' + songmid + '/' + quality, { method: 'GET', timeout: 8000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json', 'x-request-key': 'lxmusic' }, }) const d = res.body if (d && (d.code === 0 || d.code === 200) && d.data) return d.data if (d && d.url) return d.url } catch (e) {} // 降级到v3 const res = await httpFetch('https://88.lxmusic.xn--fiqs8s/lxmusicv3/url/tx/' + songmid + '/' + quality, { method: 'GET', timeout: 8000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json' }, }) const d = res.body if (d && d.data) return d.data throw new Error('lxmusic88: 无数据') }, }, // === 后端15: 长青SVIP 海棠直链 === { name: '长青直链', fetch: async (songmid, quality) => { const res = await httpFetch('http://175.27.166.236/kgqq1/qq.php?type=mp3&id=' + songmid + '&level=' + quality, { method: 'GET', timeout: 8000, headers: { 'User-Agent': 'Mozilla/5.0' }, }) const d = res.body if (typeof d === 'string' && (d.startsWith('http://') || d.startsWith('https://'))) return d if (d && d.url) return d.url throw new Error('长青直链: 无数据') }, }, // === 后端16: nxinxz 念心直链 === { name: '念心直链', fetch: async (songmid, quality) => { const res = await httpFetch('https://music.nxinxz.com/kgqq/tx.php?id=' + songmid + '&level=' + quality + '&type=mp3', { method: 'GET', timeout: 8000, headers: { 'User-Agent': 'Mozilla/5.0' }, }) const d = res.body if (typeof d === 'string' && (d.startsWith('http://') || d.startsWith('https://'))) return d if (d && d.url) return d.url throw new Error('念心直链: 无数据') }, }, // === 后端17: 妖狐API === { name: '妖狐', fetch: async (songmid, quality) => { const res = await httpFetch('https://api.yaohud.cn/api/music/qq_plus?id=' + songmid + '&level=' + quality, { method: 'GET', timeout: 8000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json' }, }) const d = res.body const url = extractUrl(d, [['url'], ['data', 'url']]) if (url) return url throw new Error('妖狐: 无数据') }, }, // === 后端18: GD Studio API === { name: 'GDStudio', fetch: async (songmid, quality) => { const brMap = { '128k': '128', '320k': '320', flac: '740', flac24bit: '999', hires: '999' } const br = brMap[quality] || '128' const res = await httpFetch('https://music-api.gdstudio.xyz/api.php?types=url&source=qq&id=' + songmid + '&br=' + br, { method: 'GET', timeout: 8000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json' }, }) const d = res.body if (d && d.url) return d.url throw new Error('GDStudio: 无数据') }, }, // === 后端19: ChKsZ 聚合API === { name: 'ChKsZ', fetch: async (songmid, quality) => { const res = await httpFetch('https://api.chksz.top/api', { method: 'POST', timeout: 8000, headers: { 'Content-Type': 'application/json', 'User-Agent': 'Mozilla/5.0' }, body: JSON.stringify({ source: 'qq', songmid, quality }), }) const d = res.body if (d && d.code === 200 && d.data && d.data.url) return d.data.url throw new Error('ChKsZ: ' + (d?.msg || '无数据')) }, }, // === 后端20: Huibq API === { name: 'Huibq', fetch: async (songmid, quality) => { const res = await httpFetch('https://lxmusicapi.onrender.com/url/tx/' + songmid + '/' + quality, { method: 'GET', timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json', 'X-Request-Key': 'share-v3' }, }) const d = res.body if (d && d.code === 0) { if (d.url) return d.url if (d.data && d.data.url) return d.data.url } throw new Error('Huibq: 无数据') }, }, // === 后端21: 聚合API(lerd.dpdns.org) === { name: '聚合API', fetch: async (songmid, quality) => { const res = await httpFetch('https://api.music.lerd.dpdns.org/tx', { method: 'POST', timeout: 10000, headers: { 'Content-Type': 'application/json', 'User-Agent': 'Mozilla/5.0' }, body: JSON.stringify({ musicInfo: { songmid }, type: quality }), }) const d = res.body if (d && d.code === 200 && d.data && d.data.url) return d.data.url throw new Error('聚合API: 无数据') }, }, // === 后端22: Fish API(gdstudio POST) === { name: 'FishAPI', fetch: async (songmid, quality) => { const brMap = { '128k': 128, '320k': 320, flac: 740, flac24bit: 999 } const br = brMap[quality] if (!br) throw new Error('FishAPI 不支持的音质') const result = await fishPost({ types: 'url', id: songmid, source: 'qq', br: br }, encodeURIComponent(songmid)) const url = result && result.url ? cleanUrl(String(result.url)) : '' if (url.startsWith('http')) return url throw new Error('FishAPI: 无数据') }, }, // === 后端23: 汽水VIP API === { name: '汽水VIP', fetch: async (songmid, quality) => { const levelMap = { '128k': 'standard', '320k': 'exhigh', flac: 'lossless', flac24bit: 'hires' } const level = levelMap[quality] || 'standard' const res = await httpFetch('https://api.vsaa.cn/api/music.qishui.vip?act=song&id=' + songmid + '&quality=' + level, { method: 'GET', timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json' }, }) const d = res.body const url = extractUrl(d, [['data', 'data', 0, 'url'], ['data', 'data', 'url'], ['data', 'url'], ['url']]) if (url) return url throw new Error('汽水VIP: 无数据') }, }, // === 后端24: HYWmusic API(白姬专用,103.79.184.97) === { name: 'HYWmusic', fetch: async (songmid, quality) => { const res = await httpFetch(HYW_API_BASE + '/api/music/url?source=tx&songId=' + songmid + '&quality=' + quality + '&key=' + HYW_CARD_KEY, { method: 'GET', timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json', 'X-Card-Key': HYW_CARD_KEY }, }) const d = res.body if (d && d.code === 200) { if (d.url) return d.url if (d.data && d.data.url) return d.data.url } throw new Error('HYWmusic: 无数据') }, }, ] // ==================== 网易云音乐 后端接口列表(按优先级排列) ==================== const WY_BACKENDS = [ // === 后端1: 网易云eapi官方接口(带Cookie可解锁VIP) === { name: '网易云官方', fetch: async (songmid, quality) => { const level = WY_LEVEL_MAP[quality] || 'standard' const targetUrl = 'https://interface3.music.163.com/eapi/song/enhance/player/url/v1' const eapiUrl = '/api/song/enhance/player/url/v1' const payload = { ids: [Number(songmid)], level, encodeType: 'flac', immerseType: 'c51' } const encrypted = wyEapi(eapiUrl, payload) let cookieValue = 'os=pc; appver=; osver=; deviceId=pyncm!' if (HAS_WY_COOKIE) cookieValue = WY_COOKIE + '; ' + cookieValue const res = await httpFetch(targetUrl, { method: 'POST', timeout: 10000, headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Safari/537.36 Chrome/91.0.4472.164 NeteaseMusicDesktop/2.10.2.200154', Referer: 'https://music.163.com/', Cookie: cookieValue, }, form: encrypted, }) const d = res.body if (d && d.data && d.data[0] && d.data[0].url && !d.data[0].freeTrialInfo) return d.data[0].url if (d && d.data && d.data[0] && d.data[0].freeTrialInfo) throw new Error('VIP歌曲仅试听(配置Cookie后可用完整版)') throw new Error('网易云官方: 无数据') }, }, // === 后端2: 星海音乐源VIP接口(ChKsZ) === { name: 'ChKsZ-VIP', fetch: async (songmid, quality) => { const level = WY_LEVEL_MAP[quality] || 'standard' const res = await httpFetch('https://api.chksz.top/api/163_music?id=' + songmid + '&level=' + level, { method: 'GET', timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json', Referer: 'https://cp.chksz.top/' }, }) const d = res.body if (d && d.code === 200 && d.data && d.data.url) return d.data.url throw new Error('ChKsZ-VIP: ' + (d?.msg || '无数据')) }, }, // === 后端3: 笒鬼鬼API(cenguigui) === { name: '笒鬼鬼', fetch: async (songmid, quality) => { const level = WY_LEVEL_MAP[quality] || 'standard' const res = await httpFetch('https://api.cenguigui.cn/api/netease/music_v1.php?id=' + songmid + '&type=json&level=' + level, { method: 'GET', timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json' }, }) const d = res.body if (d && d.data && d.data.url) return d.data.url if (d && d.url) return d.url throw new Error('笒鬼鬼: 无数据') }, }, // === 后端4: 溯音API(oiapi) === { name: '溯音163', fetch: async (songmid, quality) => { const res = await httpFetch('https://oiapi.net/api/Music_163?id=' + songmid + '&type=json', { method: 'GET', timeout: 8000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json' }, }) const d = res.body if (d && d.url) return d.url if (d && d.data && d.data[0] && d.data[0].url) return d.data[0].url if (d && d.data && d.data.url) return d.data.url throw new Error('溯音163: 无数据') }, }, // === 后端5: wyapi.toubiec.cn(洛雪音乐源用) === { name: 'toubiec', fetch: async (songmid, quality) => { const level = WY_LEVEL_MAP[quality] || 'standard' const res = await httpFetch('https://wyapi.toubiec.cn/api/music/url', { method: 'POST', timeout: 10000, headers: { 'Content-Type': 'application/json', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36', Origin: 'https://wyapi.toubiec.cn', Referer: 'https://wyapi.toubiec.cn/', }, body: JSON.stringify({ id: songmid, level }), }) const d = res.body if (d && d.data && d.data[0] && d.data[0].url) return d.data[0].url if (d && d.url) return d.url throw new Error('toubiec: 无数据') }, }, // === 后端6: GD Studio API === { name: 'GDStudio', fetch: async (songmid, quality) => { const brMap = { '128k': '128', '320k': '320', flac: '740', flac24bit: '999', hires: '999' } const br = brMap[quality] || '128' const res = await httpFetch('https://music-api.gdstudio.xyz/api.php?use_xbridge3=true&loader_name=forest&need_sec_link=1&sec_link_scene=im&theme=light&types=url&source=netease&id=' + songmid + '&br=' + br, { method: 'GET', timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json' }, }) const d = res.body if (d && d.url) return d.url throw new Error('GDStudio: 无数据') }, }, // === 后端7: 星海音乐源主后端(yy.zddyr.top) === { name: '星海主后端', fetch: async (songmid, quality) => { const res = await httpFetch('https://yy.zddyr.top/lx/api/?source=netease&songmid=' + songmid + '&quality=' + quality, { method: 'GET', timeout: 8000, headers: { 'User-Agent': 'Mozilla/5.0' }, }) const d = res.body if (d && d.code === 200 && d.url) return d.url throw new Error('星海主后端: ' + (d?.msg || '无数据')) }, }, // === 后端8: 星海音乐源备用后端(zrcdy) === { name: '星海备后端', fetch: async (songmid, quality) => { const res = await httpFetch('https://zrcdy.dpdns.org/lx/api/api.php?source=netease&songmid=' + songmid + '&quality=' + quality, { method: 'GET', timeout: 8000, headers: { 'User-Agent': 'Mozilla/5.0' }, }) const d = res.body if (d && d.code === 200 && d.url) return d.url throw new Error('星海备后端: ' + (d?.msg || '无数据')) }, }, // === 后端9: api.bugpk.com(多平台聚合音源) === { name: 'bugpk', fetch: async (songmid, quality) => { const level = WY_LEVEL_MAP[quality] || 'standard' const res = await httpFetch('https://api.bugpk.com/api/163_music?type=json&ids=' + songmid + '&level=' + level, { method: 'GET', timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json' }, }) const d = res.body const url = extractUrl(d, [['url'], ['data', 'url'], ['data', 0, 'url']]) if (url) return url throw new Error('bugpk: 无数据') }, }, // === 后端10: nxinxz 念心直链 === { name: '念心直链', fetch: async (songmid, quality) => { const res = await httpFetch('http://music.nxinxz.com/wy.php?id=' + songmid + '&level=' + quality + '&type=mp3', { method: 'GET', timeout: 8000, headers: { 'User-Agent': 'Mozilla/5.0' }, }) const d = res.body if (typeof d === 'string' && (d.startsWith('http://') || d.startsWith('https://'))) return d if (d && d.url) return d.url throw new Error('念心直链: 无数据') }, }, // === 后端11: 长青SVIP 直链 === { name: '长青直链', fetch: async (songmid, quality) => { const res = await httpFetch('http://175.27.166.236/wy1/wy.php?type=mp3&id=' + songmid + '&level=' + quality, { method: 'GET', timeout: 8000, headers: { 'User-Agent': 'Mozilla/5.0' }, }) const d = res.body if (typeof d === 'string' && (d.startsWith('http://') || d.startsWith('https://'))) return d if (d && d.url) return d.url throw new Error('长青直链: 无数据') }, }, // === 后端12: 妖狐API === { name: '妖狐', fetch: async (songmid, quality) => { const res = await httpFetch('https://api.yaohud.cn/api/music/wyvip?id=' + songmid + '&level=' + quality, { method: 'GET', timeout: 8000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json' }, }) const d = res.body const url = extractUrl(d, [['url'], ['data', 'url']]) if (url) return url throw new Error('妖狐: 无数据') }, }, // === 后端13: 88.lxmusic(独家音源v4) === { name: 'lxmusic88', fetch: async (songmid, quality) => { const level = WY_LEVEL_MAP[quality] || 'standard' const res = await httpFetch('https://88.lxmusic.xn--fiqs8s/lxmusicv4/url/wy/' + songmid + '/' + level, { method: 'GET', timeout: 8000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json', 'x-request-key': 'lxmusic' }, }) const d = res.body if (d && (d.code === 0 || d.code === 200)) { if (d.data) return d.data if (d.url) return d.url } throw new Error('lxmusic88: 无数据') }, }, // === 后端14: Fish API(gdstudio POST) === { name: 'FishAPI', fetch: async (songmid, quality) => { const brMap = { '128k': 128, '320k': 320, flac: 740, flac24bit: 999 } const br = brMap[quality] if (!br) throw new Error('FishAPI 不支持的音质') const result = await fishPost({ types: 'url', id: songmid, source: 'netease', br: br }, encodeURIComponent(songmid)) const url = result && result.url ? cleanUrl(String(result.url)) : '' if (url.startsWith('http')) return url throw new Error('FishAPI: 无数据') }, }, // === 后端15: Huibq API === { name: 'Huibq', fetch: async (songmid, quality) => { const res = await httpFetch('https://lxmusicapi.onrender.com/url/wy/' + songmid + '/' + quality, { method: 'GET', timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json', 'X-Request-Key': 'share-v3' }, }) const d = res.body if (d && d.code === 0) { if (d.url) return d.url if (d.data && d.data.url) return d.data.url } throw new Error('Huibq: 无数据') }, }, // === 后端16: 聚合API(lerd.dpdns.org) === { name: '聚合API', fetch: async (songmid, quality) => { const res = await httpFetch('https://api.music.lerd.dpdns.org/wy', { method: 'POST', timeout: 10000, headers: { 'Content-Type': 'application/json', 'User-Agent': 'Mozilla/5.0' }, body: JSON.stringify({ musicInfo: { songmid }, type: quality }), }) const d = res.body if (d && d.code === 200 && d.data && d.data.url) return d.data.url throw new Error('聚合API: 无数据') }, }, // === 后端17: 汽水VIP API === { name: '汽水VIP', fetch: async (songmid, quality) => { const levelMap = { '128k': 'standard', '320k': 'exhigh', flac: 'lossless', flac24bit: 'hires' } const level = levelMap[quality] || 'standard' const res = await httpFetch('https://api.vsaa.cn/api/music.qishui.vip?act=song&id=' + songmid + '&quality=' + level, { method: 'GET', timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json' }, }) const d = res.body const url = extractUrl(d, [['data', 'data', 0, 'url'], ['data', 'data', 'url'], ['data', 'url'], ['url']]) if (url) return url throw new Error('汽水VIP: 无数据') }, }, // === 后端18: HYWmusic API(白姬专用,103.79.184.97) === { name: 'HYWmusic', fetch: async (songmid, quality) => { const res = await httpFetch(HYW_API_BASE + '/api/music/url?source=wy&songId=' + songmid + '&quality=' + quality + '&key=' + HYW_CARD_KEY, { method: 'GET', timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json', 'X-Card-Key': HYW_CARD_KEY }, }) const d = res.body if (d && d.code === 200) { if (d.url) return d.url if (d.data && d.data.url) return d.data.url } throw new Error('HYWmusic: 无数据') }, }, ] // ==================== 酷我音乐(kw) 后端接口列表(按优先级排列) ==================== const KW_BACKENDS = [ // === 后端1: 酷我流媒体直链(175.27.166.236:8928,返回二进制音频流,取自酷我流媒体音源) === // 注:该服务器atmos/atmos_plus/master返回200,其他音质返回400,但URL本身即为可播放地址,无需验证 { name: '酷我流媒体', fetch: async (songmid, quality, musicInfo) => { const level = KW_STREAM_LEVEL_MAP[quality] || 'master' const songIdTmp = musicInfo?.songmid || musicInfo?.id || musicInfo?.hash || musicInfo?.songId || musicInfo?.musicId || songmid if (!songIdTmp) throw new Error('酷我流媒体: 找不到歌曲ID') const songId = String(songIdTmp).trim() // 该URL返回二进制音频流,URL本身即为可播放地址,直接返回无需验证 return 'http://175.27.166.236:8928/kwstream?id=' + encodeURIComponent(songId) + '&level=' + level + '&stream=1' }, }, // === 后端2: 星海音乐源主后端(yy.zddyr.top,带完整歌曲信息) === { name: '星海主后端', fetch: async (songmid, quality, musicInfo) => { const name = musicInfo?.name || '' const singer = musicInfo?.singer || '' const interval = musicInfo?.interval || '' const albumName = musicInfo?.albumName || musicInfo?.album || '' const res = await httpFetch('https://yy.zddyr.top/lx/api/?source=kw&name=' + encodeURIComponent(name) + '&singer=' + encodeURIComponent(singer) + '&songmid=' + encodeURIComponent(songmid) + '&interval=' + encodeURIComponent(interval) + '&albumName=' + encodeURIComponent(albumName) + '&quality=' + quality, { method: 'GET', timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0' }, }) const d = res.body if (d && d.code === 200 && d.url) return d.url throw new Error('星海主后端: ' + (d?.msg || '无数据')) }, }, // === 后端2: 星海音乐源备用后端(zrcdy,带完整歌曲信息) === { name: '星海备后端', fetch: async (songmid, quality, musicInfo) => { const name = musicInfo?.name || '' const singer = musicInfo?.singer || '' const interval = musicInfo?.interval || '' const albumName = musicInfo?.albumName || musicInfo?.album || '' const res = await httpFetch('https://zrcdy.dpdns.org/lx/api/api.php?source=kw&name=' + encodeURIComponent(name) + '&singer=' + encodeURIComponent(singer) + '&songmid=' + encodeURIComponent(songmid) + '&interval=' + encodeURIComponent(interval) + '&albumName=' + encodeURIComponent(albumName) + '&quality=' + quality, { method: 'GET', timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0' }, }) const d = res.body if (d && d.code === 200 && d.url) return d.url throw new Error('星海备后端: ' + (d?.msg || '无数据')) }, }, // === 后端3: 笒鬼鬼API(cenguigui) === { name: '笒鬼鬼', fetch: async (songmid, quality) => { const level = KW_LEVEL_MAP[quality] || '128k' const res = await httpFetch('https://api.cenguigui.cn/api/kuwo/music_v1.php?id=' + songmid + '&type=song&format=json&level=' + level, { method: 'GET', timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json' }, }) const d = res.body const url = extractUrl(d, [['data', 'url'], ['url']]) if (url) return url throw new Error('笒鬼鬼: 无数据') }, }, // === 后端4: 聚合API(lerd.dpdns.org) === { name: '聚合API', fetch: async (songmid, quality) => { const res = await httpFetch('https://api.music.lerd.dpdns.org/kw', { method: 'POST', timeout: 10000, headers: { 'Content-Type': 'application/json', 'User-Agent': 'Mozilla/5.0' }, body: JSON.stringify({ musicInfo: { songmid }, type: quality }), }) const d = res.body if (d && d.code === 200 && d.data && d.data.url) return d.data.url throw new Error('聚合API: 无数据') }, }, // === 后端5: 妖狐API === { name: '妖狐', fetch: async (songmid, quality) => { const res = await httpFetch('https://api.yaohud.cn/api/music/kwvip?id=' + songmid + '&level=' + quality, { method: 'GET', timeout: 8000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json' }, }) const d = res.body const url = extractUrl(d, [['url'], ['data', 'url']]) if (url) return url throw new Error('妖狐: 无数据') }, }, // === 后端6: 长青直链 === { name: '长青直链', fetch: async (songmid, quality) => { const level = qualityToLevel(quality) const res = await httpFetch('http://175.27.166.236/kgqq1/kw.php?type=mp3&id=' + songmid + '&level=' + level, { method: 'GET', timeout: 8000, headers: { 'User-Agent': 'Mozilla/5.0' }, }) const d = res.body if (typeof d === 'string' && (d.startsWith('http://') || d.startsWith('https://'))) return d if (d && d.url) return d.url throw new Error('长青直链: 无数据') }, }, // === 后端7: 念心直链 === { name: '念心直链', fetch: async (songmid, quality) => { const level = qualityToLevel(quality) const res = await httpFetch('https://music.nxinxz.com/kgqq/kw.php?id=' + songmid + '&level=' + level + '&type=mp3', { method: 'GET', timeout: 8000, headers: { 'User-Agent': 'Mozilla/5.0' }, }) const d = res.body if (typeof d === 'string' && (d.startsWith('http://') || d.startsWith('https://'))) return d if (d && d.url) return d.url throw new Error('念心直链: 无数据') }, }, // === 后端8: 酷我官方接口(KuwoDES格式,surl=1) === { name: '酷我官方', fetch: async (songmid, quality, musicInfo) => { const brMap = { '128k': '128kmp3', '192k': '128kmp3', '320k': '320kmp3', flac: '2000kflac', flac24bit: '4000kflac' } const br = brMap[quality] if (!br) throw new Error('酷我官方 不支持的音质') let rid = musicInfo?.rid || '' if (!rid && musicInfo?.musicrid) rid = String(musicInfo.musicrid).replace(/^MUSIC_/, '') if (!rid) rid = songmid // 使用KuwoDES格式,surl=1让服务器返回surl字段 const res = await httpFetch('https://mobi.kuwo.cn/mobi.s?f=web&rid=' + rid + '&br=' + br + '&source=jiakong&type=convert_url_with_sign&surl=1', { method: 'GET', timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Mobile Safari/537.36' }, }) const d = res.body if (d && d.code === 200 && d.data && d.data.surl) return d.data.surl if (d && d.code === 200 && d.data && d.data.url) return d.data.url throw new Error('酷我官方: 无数据') }, }, // === 后端9: 酷我手机版(不同source标识) === { name: '酷我手机版', fetch: async (songmid, quality) => { const brMap = { '128k': '128kmp3', '192k': '128kmp3', '320k': '320kmp3', flac: '2000kflac', flac24bit: '4000kflac' } const br = brMap[quality] if (!br) throw new Error('酷我手机版 不支持的音质') const res = await httpFetch('https://nmobi.kuwo.cn/mobi.s?f=web&user=0&source=kwplayerhd_ar_4.3.0.8_tianbao_T1A_qirui.apk&type=convert_url_with_sign&rid=' + songmid + '&br=' + br, { method: 'GET', timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36' }, }) const d = res.body if (d && d.code === 200 && d.data && d.data.url) return d.data.url if (d && d.code === 200 && d.data && d.data.surl) return d.data.surl throw new Error('酷我手机版: 无数据') }, }, // === 后端10: 酷我车机版(不同source标识) === { name: '酷我车机版', fetch: async (songmid, quality) => { const brMap = { '128k': '128kmp3', '192k': '128kmp3', '320k': '320kmp3', flac: '2000kflac', flac24bit: '4000kflac' } const br = brMap[quality] if (!br) throw new Error('酷我车机版 不支持的音质') const res = await httpFetch('https://mobi.kuwo.cn/mobi.s?f=web&user=0&source=kwplayercar_ar_6.0.0.9_B_jiakong_vh.apk&type=convert_url_with_sign&br=' + br + '&sig=0&rid=' + songmid, { method: 'GET', timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36' }, }) const d = res.body if (d && d.code === 200 && d.data && d.data.url) return d.data.url if (d && d.code === 200 && d.data && d.data.surl) return d.data.surl throw new Error('酷我车机版: 无数据') }, }, // === 后端11: 聆澜API === { name: '聆澜', fetch: async (songmid, quality) => { const res = await httpFetch('https://source.shiqianjiang.cn/api/music/url?source=kw&songId=' + songmid + '&quality=' + quality, { method: 'GET', timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json' }, }) const d = res.body if (d && d.code === 200 && d.url) return d.url if (d && d.data && d.data.url) return d.data.url throw new Error('聆澜: 无数据') }, }, // === 后端12: HYWmusic API(白姬专用,103.79.184.97) === { name: 'HYWmusic', fetch: async (songmid, quality) => { const res = await httpFetch(HYW_API_BASE + '/api/music/url?source=kw&songId=' + songmid + '&quality=' + quality + '&key=' + HYW_CARD_KEY, { method: 'GET', timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json', 'X-Card-Key': HYW_CARD_KEY }, }) const d = res.body if (d && d.code === 200) { if (d.url) return d.url if (d.data && d.data.url) return d.data.url } throw new Error('HYWmusic: 无数据') }, }, // === 后端13: 溯音酷我(oiapi.net,搜索式API,不依赖songmid) === { name: '溯音酷我', fetch: async (songmid, quality, musicInfo) => { const brMap = { '128k': '7', '192k': '5', '320k': '5', flac: '1', flac24bit: '1' } const br = brMap[quality] || '7' const name = musicInfo?.name || '' const singer = musicInfo?.singer || '' const keyword = name + (singer ? ' ' + singer : '') if (!keyword) throw new Error('溯音酷我: 缺少歌曲名') const res = await httpFetch('https://oiapi.net/api/Kuwo?msg=' + encodeURIComponent(keyword) + '&n=1&br=' + br, { method: 'GET', timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json' }, }) const d = res.body if (d && d.data && d.data.url) return d.data.url if (d && d.url) return d.url throw new Error('溯音酷我: 无数据') }, }, // === 后端14: Hello World KW API(lxmusic.xn--fiqs8s,带SHA256签名) === { name: 'HelloWorld', fetch: async (songmid, quality, musicInfo) => { const songId = musicInfo?.rid || musicInfo?.hash || musicInfo?.songmid || musicInfo?.id || songmid if (!songId) throw new Error('HelloWorld: 找不到歌曲ID') const requestPath = '/lxmusicv4/url/kw/' + songId + '/' + quality const sign = helloWorldSign(requestPath) const url = HELLO_WORLD_API_URL + requestPath + '?sign=' + sign const res = await httpFetch(url, { method: 'GET', timeout: 10000, headers: { 'accept': 'application/json', 'x-request-key': HELLO_WORLD_API_KEY, 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', }, }) const d = res.body if (d && (d.code === 0 || d.code === 200)) { const musicUrl = d.data || d.url if (musicUrl) return musicUrl } throw new Error('HelloWorld: ' + (d?.msg || '无数据')) }, }, ] // ==================== 酷狗音乐(kg) 后端接口列表(按优先级排列) ==================== const KG_BACKENDS = [ // === 后端1: 长青海棠主后端(musicserver.haitangw.cc,取自长青SVIP音源二改版主API) === { name: '长青海棠', fetch: async (songmid, quality, musicInfo) => { const level = KG_LEVEL_MAP[quality] || 'standard' const hash = musicInfo?.hash || musicInfo?.songmid || songmid const res = await httpFetch('https://musicserver.haitangw.cc/v1/music/resolve-url', { method: 'POST', timeout: 10000, headers: { 'Content-Type': 'application/json', 'User-Agent': 'Mozilla/5.0' }, body: JSON.stringify({ source: 'kg', rid: hash, level: level }), }) const d = res.body // 响应格式: {code: 0, data: {url: "..."}} if (d && d.code === 0 && d.data && d.data.url) return d.data.url throw new Error('长青海棠: ' + (d?.msg || '无数据')) }, }, // === 后端2: 长青SVIP直链(取自Hei Music,直接构造URL,不发起HTTP请求) === { name: '长青SVIP直链', fetch: async (songmid, quality, musicInfo) => { const level = KG_LEVEL_MAP[quality] || 'standard' const hash = musicInfo?.hash || musicInfo?.songmid || songmid // 直接构造URL,该URL本身即为有效直链 const url = 'https://music.haitangw.cc/kgqq1/kg.php?type=mp3&id=' + hash + '&level=' + level const res = await httpFetch(url, { method: 'GET', timeout: 8000, headers: { 'User-Agent': 'Mozilla/5.0' }, }) const d = res.body if (typeof d === 'string' && (d.startsWith('http://') || d.startsWith('https://'))) return d if (d && d.url) return d.url throw new Error('长青SVIP直链: 无数据') }, }, // === 后端3: 长青直链(175.27.166.236,备用) === { name: '长青直链', fetch: async (songmid, quality, musicInfo) => { const level = KG_LEVEL_MAP[quality] || 'standard' const hash = musicInfo?.hash || songmid const res = await httpFetch('http://175.27.166.236/kgqq1/kg.php?type=mp3&id=' + hash + '&level=' + level, { method: 'GET', timeout: 8000, headers: { 'User-Agent': 'Mozilla/5.0' }, }) const d = res.body if (typeof d === 'string' && (d.startsWith('http://') || d.startsWith('https://'))) return d if (d && d.url) return d.url throw new Error('长青直链: 无数据') }, }, // === 后端4: 长青POST(175.27.166.236 POST接口) === { name: '长青POST', fetch: async (songmid, quality, musicInfo) => { const level = KG_LEVEL_MAP[quality] || 'standard' const hash = musicInfo?.hash || songmid const res = await httpFetch('http://175.27.166.236/kgqq1/kg.php', { method: 'POST', timeout: 10000, headers: { 'Content-Type': 'application/json', 'User-Agent': 'Mozilla/5.0' }, body: JSON.stringify({ source: 'kg', id: hash, level: level }), }) const d = res.body if (typeof d === 'string' && (d.startsWith('http://') || d.startsWith('https://'))) return d if (d && d.url) return d.url if (d && d.code === 200 && d.data && d.data.url) return d.data.url throw new Error('长青POST: 无数据') }, }, // === 后端5: 星海音乐源主后端(yy.zddyr.top) === { name: '星海主后端', fetch: async (songmid, quality, musicInfo) => { const hash = musicInfo?.hash || (musicInfo?._types?.[quality]?.hash) || songmid const albumId = musicInfo?.albumId || '' const mainHash = hash const res = await httpFetch('https://yy.zddyr.top/lx/api/?source=kg&quality=' + quality + '&songmid=' + (musicInfo?.songmid || songmid) + '&albumId=' + albumId + '&mainHash=' + mainHash + '&hash=' + hash, { method: 'GET', timeout: 8000, headers: { 'User-Agent': 'Mozilla/5.0' }, }) const d = res.body if (d && d.code === 200 && d.url) return d.url throw new Error('星海主后端: ' + (d?.msg || '无数据')) }, }, // === 后端6: 星海音乐源备用后端(zrcdy) === { name: '星海备后端', fetch: async (songmid, quality) => { const res = await httpFetch('https://zrcdy.dpdns.org/lx/api/api.php?source=kg&songmid=' + songmid + '&quality=' + quality, { method: 'GET', timeout: 8000, headers: { 'User-Agent': 'Mozilla/5.0' }, }) const d = res.body if (d && d.code === 200 && d.url) return d.url throw new Error('星海备后端: ' + (d?.msg || '无数据')) }, }, // === 后端7: 聚合API(lerd.dpdns.org) === { name: '聚合API', fetch: async (songmid, quality) => { const res = await httpFetch('https://api.music.lerd.dpdns.org/kg', { method: 'POST', timeout: 10000, headers: { 'Content-Type': 'application/json', 'User-Agent': 'Mozilla/5.0' }, body: JSON.stringify({ musicInfo: { songmid }, type: quality }), }) const d = res.body if (d && d.code === 200 && d.data && d.data.url) return d.data.url throw new Error('聚合API: 无数据') }, }, // === 后端8: Hello World KG API(lxmusic.xn--fiqs8s,带SHA256签名) === { name: 'HelloWorld', fetch: async (songmid, quality, musicInfo) => { const songId = musicInfo?.hash || musicInfo?.songmid || musicInfo?.id || songmid if (!songId) throw new Error('HelloWorld: 找不到歌曲ID') const requestPath = '/lxmusicv4/url/kg/' + songId + '/' + quality const sign = helloWorldSign(requestPath) const url = HELLO_WORLD_API_URL + requestPath + '?sign=' + sign const res = await httpFetch(url, { method: 'GET', timeout: 10000, headers: { 'accept': 'application/json', 'x-request-key': HELLO_WORLD_API_KEY, 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', }, }) const d = res.body if (d && (d.code === 0 || d.code === 200)) { const musicUrl = d.data || d.url if (musicUrl) return musicUrl } throw new Error('HelloWorld: ' + (d?.msg || '无数据')) }, }, // === 后端9: 妖狐API === { name: '妖狐', fetch: async (songmid, quality) => { const res = await httpFetch('https://api.yaohud.cn/api/music/kgvip?id=' + songmid + '&level=' + quality, { method: 'GET', timeout: 8000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json' }, }) const d = res.body const url = extractUrl(d, [['url'], ['data', 'url']]) if (url) return url throw new Error('妖狐: 无数据') }, }, // === 后端10: 念心KG === { name: '念心KG', fetch: async (songmid, quality) => { const level = KG_LEVEL_MAP[quality] || 'standard' const res = await httpFetch('https://music.nxinxz.com/kgqq/kg.php?id=' + songmid + '&level=' + level + '&type=mp3', { method: 'GET', timeout: 8000, headers: { 'User-Agent': 'Mozilla/5.0' }, }) const d = res.body if (d && d.code === 200 && d.url) return d.url if (typeof d === 'string' && (d.startsWith('http://') || d.startsWith('https://'))) return d throw new Error('念心KG: 无数据') }, }, // === 后端11: ChKsZ 聚合API === { name: 'ChKsZ', fetch: async (songmid, quality) => { const res = await httpFetch('https://api.chksz.top/api', { method: 'POST', timeout: 8000, headers: { 'Content-Type': 'application/json', 'User-Agent': 'Mozilla/5.0' }, body: JSON.stringify({ source: 'kg', songmid, quality }), }) const d = res.body if (d && d.code === 200 && d.data && d.data.url) return d.data.url throw new Error('ChKsZ: ' + (d?.msg || '无数据')) }, }, // === 后端12: 海棠API(使用KG_LEVEL_MAP修正master音质) === { name: '海棠API', fetch: async (songmid, quality, musicInfo) => { const level = KG_LEVEL_MAP[quality] || 'standard' const hash = musicInfo?.hash || (musicInfo?._types?.[quality]?.hash) || songmid const res = await httpFetch('https://musicapi.haitangw.net/kgqq/kg.php?type=json&id=' + hash + '&level=' + level, { method: 'GET', timeout: 8000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json' }, }) const d = res.body const url = extractUrl(d, [['url'], ['data', 'url']]) if (url) return url throw new Error('海棠API: 无数据') }, }, // === 后端13: 酷狗官方API(直接调用酷狗官方接口) === { name: '酷狗官方', fetch: async (songmid, quality, musicInfo) => { const hash = musicInfo?.hash || songmid const albumId = musicInfo?.albumId || '' const res = await httpFetch('https://wwwapi.kugou.com/yy/index.php?r=play/getdata&hash=' + hash + '&platid=4&album_id=' + albumId + '&mid=00000000000000000000000000000000', { method: 'GET', timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0', Referer: 'https://www.kugou.com/' }, }) const d = res.body if (d && d.status === 1 && d.data && d.data.play_backup_url) return d.data.play_backup_url if (d && d.status === 1 && d.data && d.data.play_url) return d.data.play_url throw new Error('酷狗官方: 无数据') }, }, // === 后端14: 聆澜API === { name: '聆澜', fetch: async (songmid, quality) => { const res = await httpFetch('https://source.shiqianjiang.cn/api/music/url?source=kg&songId=' + songmid + '&quality=' + quality, { method: 'GET', timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json' }, }) const d = res.body if (d && d.code === 200 && d.url) return d.url if (d && d.data && d.data.url) return d.data.url throw new Error('聆澜: 无数据') }, }, // === 后端15: HYWmusic API(白姬专用,103.79.184.97) === { name: 'HYWmusic', fetch: async (songmid, quality) => { const res = await httpFetch(HYW_API_BASE + '/api/music/url?source=kg&songId=' + songmid + '&quality=' + quality + '&key=' + HYW_CARD_KEY, { method: 'GET', timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json', 'X-Card-Key': HYW_CARD_KEY }, }) const d = res.body if (d && d.code === 200) { if (d.url) return d.url if (d.data && d.data.url) return d.data.url } throw new Error('HYWmusic: 无数据') }, }, // === 后端16: GD Studio API === { name: 'GDStudio', fetch: async (songmid, quality) => { const brMap = { '128k': '128', '320k': '320', flac: '740', flac24bit: '999', hires: '999' } const br = brMap[quality] || '128' const res = await httpFetch('https://music-api.gdstudio.xyz/api.php?types=url&source=kg&id=' + songmid + '&br=' + br, { method: 'GET', timeout: 8000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json' }, }) const d = res.body if (d && d.url) return d.url throw new Error('GDStudio: 无数据') }, }, ] // ==================== 咪咕音乐(mg) 后端接口列表(按优先级排列) ==================== const MG_BACKENDS = [ // === 后端1: 星海音乐源主后端(yy.zddyr.top) === { name: '星海主后端', fetch: async (songmid, quality) => { const res = await httpFetch('https://yy.zddyr.top/lx/api/?source=migu&songmid=' + songmid + '&quality=' + quality, { method: 'GET', timeout: 8000, headers: { 'User-Agent': 'Mozilla/5.0' }, }) const d = res.body if (d && d.code === 200 && d.url) return d.url throw new Error('星海主后端: ' + (d?.msg || '无数据')) }, }, // === 后端2: 星海音乐源备用后端(zrcdy) === { name: '星海备后端', fetch: async (songmid, quality) => { const res = await httpFetch('https://zrcdy.dpdns.org/lx/api/api.php?source=migu&songmid=' + songmid + '&quality=' + quality, { method: 'GET', timeout: 8000, headers: { 'User-Agent': 'Mozilla/5.0' }, }) const d = res.body if (d && d.code === 200 && d.url) return d.url throw new Error('星海备后端: ' + (d?.msg || '无数据')) }, }, // === 后端3: 聚合API(lerd.dpdns.org) === { name: '聚合API', fetch: async (songmid, quality) => { const res = await httpFetch('https://api.music.lerd.dpdns.org/mg', { method: 'POST', timeout: 10000, headers: { 'Content-Type': 'application/json', 'User-Agent': 'Mozilla/5.0' }, body: JSON.stringify({ musicInfo: { songmid }, type: quality }), }) const d = res.body if (d && d.code === 200 && d.data && d.data.url) return d.data.url throw new Error('聚合API: 无数据') }, }, // === 后端4: GD Studio API === { name: 'GDStudio', fetch: async (songmid, quality) => { const brMap = { '128k': '128', '320k': '320', flac: '1000' } const br = brMap[quality] || '128' const res = await httpFetch('https://music-api.gdstudio.xyz/api.php?types=url&source=migu&id=' + songmid + '&br=' + br, { method: 'GET', timeout: 8000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json' }, }) const d = res.body if (d && d.url) return d.url throw new Error('GDStudio: 无数据') }, }, // === 后端5: Migu直接源(Hei Music) === { name: 'Migu直接源', fetch: async (songmid, quality) => { const level = qualityToLevel(quality) const res = await httpFetch('https://music.migu.cn/v3/api/music/audioPlayer/getPlayInfo?copyrightId=' + encodeURIComponent(String(songmid)) + '&level=' + level, { method: 'GET', timeout: 8000, headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', Referer: 'https://music.migu.cn/' }, }) const d = res.body if (d && d.data && d.data.playUrl) return d.data.playUrl if (d && d.url) return d.url if (d && d.playUrl) return d.playUrl throw new Error('Migu直接源: 无数据') }, }, // === 后端6: Migu API(Hei Music) === { name: 'Migu API', fetch: async (songmid, quality) => { const levelMap = { '128k': 'PQ', '320k': 'HQ', flac: 'SQ', flac24bit: 'ZQ' } const level = levelMap[quality] || 'HQ' const res = await httpFetch('https://app.c.nf.migu.cn/MIGUM2.0/strategy/listen-url/v2.2?copyrightId=' + encodeURIComponent(String(songmid)) + '&quality=' + level, { method: 'GET', timeout: 8000, headers: { 'User-Agent': 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36', Referer: 'https://app.c.nf.migu.cn/' }, }) const d = res.body if (d && d.data && d.data.url) return d.data.url if (d && d.url) return d.url if (d && d.data && d.data.playUrl) return d.data.playUrl throw new Error('Migu API: 无数据') }, }, // === 后端7: 星海后端(Hei Music xhbackend) === { name: '星海后端', fetch: async (songmid, quality) => { const level = qualityToLevel(quality) const res = await httpFetch('https://api.xinghai-backend.cn/migu?id=' + encodeURIComponent(String(songmid)) + '&quality=' + level, { method: 'GET', timeout: 8000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json' }, }) const d = res.body if (d && d.code === 200 && d.url) return d.url if (d && d.data && d.data.url) return d.data.url throw new Error('星海后端: 无数据') }, }, // === 后端8: 长青直链(haitangw) === { name: '长青直链', fetch: async (songmid, quality) => { const level = qualityToLevel(quality) const res = await httpFetch('https://music.haitangw.cc/musicapi/mg.php?type=mp3&id=' + encodeURIComponent(String(songmid)) + '&level=' + level, { method: 'GET', timeout: 8000, headers: { 'User-Agent': 'Mozilla/5.0' }, }) const d = res.body if (typeof d === 'string' && (d.startsWith('http://') || d.startsWith('https://'))) return d if (d && d.url) return d.url throw new Error('长青直链: 无数据') }, }, // === 后端9: 念心直链 === { name: '念心直链', fetch: async (songmid, quality) => { const level = qualityToLevel(quality) const res = await httpFetch('http://music.nxinxz.com/mg.php?id=' + encodeURIComponent(String(songmid)) + '&level=' + level + '&type=mp3', { method: 'GET', timeout: 8000, headers: { 'User-Agent': 'Mozilla/5.0' }, }) const d = res.body if (typeof d === 'string' && (d.startsWith('http://') || d.startsWith('https://'))) return d if (d && d.url) return d.url throw new Error('念心直链: 无数据') }, }, // === 后端10: 聆澜API === { name: '聆澜', fetch: async (songmid, quality) => { const res = await httpFetch('https://source.shiqianjiang.cn/api/music/url?source=mg&songId=' + songmid + '&quality=' + quality, { method: 'GET', timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json' }, }) const d = res.body if (d && d.code === 200 && d.url) return d.url if (d && d.data && d.data.url) return d.data.url throw new Error('聆澜: 无数据') }, }, // === 后端11: HYWmusic API(白姬专用,103.79.184.97) === { name: 'HYWmusic', fetch: async (songmid, quality) => { const res = await httpFetch(HYW_API_BASE + '/api/music/url?source=mg&songId=' + songmid + '&quality=' + quality + '&key=' + HYW_CARD_KEY, { method: 'GET', timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0', Accept: 'application/json', 'X-Card-Key': HYW_CARD_KEY }, }) const d = res.body if (d && d.code === 200) { if (d.url) return d.url if (d.data && d.data.url) return d.data.url } throw new Error('HYWmusic: 无数据') }, }, ] // ==================== 获取音乐URL(带多后端轮询) ==================== const handleGetMusicUrl = async (source, musicInfo, quality) => { const songId = musicInfo.hash ?? musicInfo.songmid ?? musicInfo.id if (!songId) throw new Error('无法获取歌曲ID') let backends = { tx: TX_BACKENDS, wy: WY_BACKENDS, kw: KW_BACKENDS, kg: KG_BACKENDS, mg: MG_BACKENDS, }[source] if (!backends) throw new Error('未知音源: ' + source) // 酷我音乐:高音质(atmos/atmos_plus/master)走流媒体直链,普通音质走星海等其他后端 if (source === 'kw') { const highQuality = ['atmos', 'atmos_plus', 'master'] if (highQuality.includes(quality)) { // 高音质只走酷我流媒体(后端1),不做降级 backends = [backends[0]] } else { // 普通音质跳过酷我流媒体(后端1),从星海主后端(后端2)开始 backends = backends.slice(1) } } const errors = [] for (const backend of backends) { try { console.log('[' + source + '] 尝试后端: ' + backend.name + ' ID: ' + songId + ' 音质: ' + quality) const url = await backend.fetch(songId, quality, musicInfo) if (url) { console.log('[' + source + '] ' + backend.name + ' 成功') return url } } catch (e) { errors.push(backend.name + ': ' + e.message) console.log('[' + source + '] ' + backend.name + ' 失败: ' + e.message) } } throw new Error('所有后端均失败(共' + backends.length + '个)\n' + errors.join('\n')) } // ==================== 注册请求事件 ==================== on(EVENT_NAMES.request, ({ action, source, info }) => { switch (action) { case 'musicUrl': return handleGetMusicUrl(source, info.musicInfo, info.type) .then((data) => Promise.resolve(data)) .catch((err) => Promise.reject(err)) default: return Promise.reject('action not support: ' + action) } }) // ==================== 初始化音源 ==================== const musicSources = {} MUSIC_SOURCE.forEach((item) => { const nameMap = { tx: 'QQ音乐', wy: '网易云音乐', kw: '酷我音乐', kg: '酷狗音乐', mg: '咪咕音乐', } musicSources[item] = { name: nameMap[item] || item, type: 'music', actions: ['musicUrl'], qualitys: MUSIC_QUALITY[item], } }) send(EVENT_NAMES.inited, { status: true, openDevTools: false, sources: musicSources, }) console.log('[QQ音乐+网易云音乐+酷我+酷狗+咪咕聚合音源 v4.3.0] 已加载完成') console.log('[QQ音乐] 后端数: ' + TX_BACKENDS.length + ' Cookie: ' + (HAS_TX_COOKIE ? '已配置' : '未配置')) console.log('[网易云音乐] 后端数: ' + WY_BACKENDS.length + ' Cookie: ' + (HAS_WY_COOKIE ? '已配置' : '未配置')) console.log('[酷我音乐] 后端数: ' + KW_BACKENDS.length) console.log('[酷狗音乐] 后端数: ' + KG_BACKENDS.length + ' 主API: 长青海棠') console.log('[咪咕音乐] 后端数: ' + MG_BACKENDS.length)