背景:看到 青旨佬的音乐解析 API,想着用到博客上,但是大多数博客系统和插件使用的是 Meting 框架,API 互不兼容,所以用 Cloudflare Workers 部署一个适配器。
感谢:青旨大佬的 API,还有详尽的 API 文档,介绍在下面,还有稳定成熟的 Meting 框架
https://linux.do/t/topic/1212285
难点:Gemini 2.5 和 3.0-pro 实现代码然后人工调试,比较难处理的是 QQ 音乐和酷我使用了新的链接,但是 MetingJS 没有适配,导致使用 MetingJS 的插件等认不出链接,无法发送正确的参数,这我没有办法解决了,适配器是以 MetingJS 支持的旧版 QQ 音乐和酷我的链接的,下面是旧版格式
对于 QQ 音乐:
单曲:https://y.qq.com/n/yqq/song/{歌曲ID}.html
歌单:https://y.qq.com/n/yqq/playlist/{歌单ID}.html
专辑:https://y.qq.com/n/yqq/album/{专辑ID}.html
对于酷我音乐:
单曲:https://www.kuwo.cn/yinyue/{ID}
歌单:https://www.kuwo.cn/playlist/index?pid={ID}
源码:
// TuneHub API 的基础地址 (固定)
const TUNEHUB_BASE = "https://music-dl.sayqz.com/api";
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
// 策略配置 (优先读取环境变量,否则使用默认值)
const config = {
INFO_CACHE_TTL: env.INFO_CACHE_TTL !== undefined ? parseInt(env.INFO_CACHE_TTL) : 600,
AUDIO_CACHE_TTL: env.AUDIO_CACHE_TTL !== undefined ? parseInt(env.AUDIO_CACHE_TTL) : 3600,
MAX_RETRIES: env.MAX_RETRIES !== undefined ? parseInt(env.MAX_RETRIES) : 3
};
// 路由分发
if (url.pathname.startsWith('/proxy')) {
// 只有网易云的音频会走到这里
return handleProxy(request, config);
}
return handleInfoWithCache(request, ctx, config);
},
};
/**
* 模块 A: 带缓存的信息获取处理
*/
async function handleInfoWithCache(request, ctx, config) {
if (config.INFO_CACHE_TTL <= 0) {
return handleInfo(request, config);
}
const cache = caches.default;
let response = await cache.match(request);
if (response) {
const newHeaders = new Headers(response.headers);
newHeaders.set('X-Worker-Cache', 'HIT');
return new Response(response.body, { status: response.status, statusText: response.statusText, headers: newHeaders });
}
response = await handleInfo(request, config);
if (response.status === 200) {
const responseToCache = response.clone();
responseToCache.headers.set('Cache-Control', `public, max-age=${config.INFO_CACHE_TTL}`);
ctx.waitUntil(cache.put(request, responseToCache));
}
return response;
}
/**
* 模块 B: 核心逻辑 - 获取歌单/歌曲元数据 (智能分流版)
*/
async function handleInfo(request, config) {
try {
const url = new URL(request.url);
const params = url.searchParams;
const server = params.get("server") || "netease";
const id = params.get("id");
const type = params.get("type");
const bitrate = params.get("bitrate");
let tuneHubBr = null;
if (bitrate) {
if (bitrate === '320000') tuneHubBr = '320k';
else if (bitrate === '999000') tuneHubBr = 'flac';
else if (bitrate === '1400000') tuneHubBr = 'flac24bit';
}
const tuneHubParams = new URLSearchParams();
let targetSource = server;
if (server === 'tencent') {
targetSource = 'qq';
}
tuneHubParams.set("source", targetSource);
tuneHubParams.set("id", id);
let tuneHubType = type === "playlist" ? "playlist" : "info";
tuneHubParams.set("type", tuneHubType);
if (tuneHubType !== 'playlist' && tuneHubBr) {
tuneHubParams.set('br', tuneHubBr);
}
const targetUrl = `${TUNEHUB_BASE}?${tuneHubParams.toString()}`;
const response = await fetchWithRetry(targetUrl, {}, config.MAX_RETRIES);
if (!response.ok) {
throw new Error(`Upstream API Error: ${response.status}`);
}
const data = await response.json();
let finalResult = [];
if (data.code === 200 && data.data) {
let songList = type === "playlist" ? (data.data.list || []) : [data.data];
finalResult = songList.map(song => {
// [关键修改] 智能分流
if (song.url) {
const realUrl = new URL(song.url);
if (tuneHubBr) {
realUrl.searchParams.set('br', tuneHubBr);
}
// 只对网易云(netease)的链接进行反向代理
if (server === 'netease') {
const proxyUrl = new URL(url.origin);
proxyUrl.pathname = '/proxy';
proxyUrl.searchParams.set('real_url', realUrl.toString());
song.url = proxyUrl.toString();
} else {
// 对于 QQ、酷我等,直接返回 TuneHub 的 API 链接
// (虽然很可能因为 Referer 等原因播放失败,但这是不代理的唯一选择)
song.url = realUrl.toString();
}
}
if (song.pic) song.pic = song.pic.replace('http://', 'https://');
if (song.lrc) song.lrc = song.lrc.replace('http://', 'https://');
return song;
});
}
return new Response(JSON.stringify(finalResult), {
headers: { "Content-Type": "application/json; charset=utf-8", "Access-Control-Allow-Origin": "*" },
});
} catch (error) {
console.error("handleInfo Error:", error);
return new Response("[]", {
headers: { "Content-Type": "application/json", "Access-Control-Allow-Origin": "*" }
});
}
}
/**
* 模块 C: 核心逻辑 - 音频流反向代理 (只为网易云服务)
*/
async function handleProxy(request, config) {
try {
const url = new URL(request.url);
const realUrl = url.searchParams.get('real_url');
if (!realUrl) {
return new Response('Missing real_url parameter', { status: 400 });
}
const realUrlObj = new URL(realUrl);
const source = realUrlObj.searchParams.get('source');
const initialRequest = new Request(realUrl, {
method: 'GET',
redirect: 'manual'
});
let finalUrl = realUrl;
let initialResponse = await fetch(initialRequest);
if (initialResponse.status === 301 || initialResponse.status === 302) {
finalUrl = initialResponse.headers.get('Location');
if (!finalUrl) finalUrl = realUrl;
}
const newHeaders = new Headers();
if (request.headers.has('range')) {
newHeaders.set('Range', request.headers.get('range'));
}
if (request.headers.has('user-agent')) {
newHeaders.set('User-Agent', request.headers.get('user-agent'));
}
// Referer 伪装只对网易云生效
if (source === 'netease') {
newHeaders.set('Referer', 'https://music.163.com/');
newHeaders.set('Cookie', 'os=pc');
}
const finalRequest = new Request(finalUrl, {
headers: newHeaders,
redirect: 'follow',
cf: {
cacheTtl: config.AUDIO_CACHE_TTL > 0 ? config.AUDIO_CACHE_TTL : undefined,
cacheEverything: config.AUDIO_CACHE_TTL > 0
}
});
const realResponse = await fetchWithRetry(finalRequest, {}, config.MAX_RETRIES);
const isRangeSupported = realResponse.status === 206;
const responseHeaders = new Headers(realResponse.headers);
responseHeaders.set('Access-Control-Allow-Origin', '*');
responseHeaders.set('Access-Control-Allow-Headers', 'Range, User-Agent');
if (config.AUDIO_CACHE_TTL > 0) {
responseHeaders.set('Cache-Control', `public, max-age=${config.AUDIO_CACHE_TTL}`);
} else {
responseHeaders.set('Cache-Control', 'no-store');
}
if (isRangeSupported) {
responseHeaders.set('Accept-Ranges', 'bytes');
}
return new Response(realResponse.body, {
status: realResponse.status,
statusText: realResponse.statusText,
headers: responseHeaders
});
} catch (error) {
return new Response(null, { status: 500, statusText: "Proxy Failed" });
}
}
/**
* 工具函数:带重试机制的 fetch
*/
async function fetchWithRetry(input, init, maxRetries = 1) {
let attempt = 0;
while (attempt <= maxRetries) {
try {
const response = await fetch(input, init);
if (response.status >= 500) {
throw new Error(`Server Error: ${response.status}`);
}
return response;
} catch (error) {
attempt++;
if (attempt > maxRetries) {
throw error;
}
await new Promise(resolve => setTimeout(resolve, 200));
}
}
}
复制粘贴到 Workers 代码部署就可用,国内屏蔽 workers.dev 域名,需要自定义域名,下面介绍代码功能吧 (懒得自己写了
为什么始终使用最高音质呢?因为 TuneHub 解析如果没有对应音质会自动降级,出处:

评论区(暂无评论)