feat: align with upstream 1.5.9.9

This commit is contained in:
2026-09-12 18:21:11 +08:00
commit 34f0679117
805 changed files with 376275 additions and 0 deletions
+150
View File
@@ -0,0 +1,150 @@
import { upgradeTxAudioUrl } from '~~/server/utils/native_tx'
import {
getQqCookieDiagnostic,
normalizeQqCookie,
resolveQqOfficialPlayUrl,
resolveQqSdkPlayUrl
} from '~~/server/utils/qq_music_sdk'
const HYW_TX_URL = 'http://103.79.184.97/api/music/url'
const HYW_CARD_KEY = 'PYPW-QFRL-3DBF-95O6'
const resolveTxWithHyw = async (songmid: string, quality: unknown) => {
const qualityMap: Record<string, string> = {
'4': '128k',
'8': '320k',
'10': 'flac',
'11': 'master',
'14': 'master'
}
const query = new URLSearchParams({
source: 'tx',
songId: songmid,
songmid,
platform: 'tx',
quality: qualityMap[String(quality)] || String(quality || '128k'),
key: HYW_CARD_KEY
})
const response = await fetch(`${HYW_TX_URL}?${query.toString()}`, {
signal: AbortSignal.timeout(8000),
headers: { Accept: 'application/json', 'X-Card-Key': HYW_CARD_KEY }
})
if (!response.ok) throw new Error(`HYW 返回 ${response.status}`)
const data: any = await response.json()
const url = typeof data?.url === 'string' ? data.url.trim() : ''
if (data?.code !== 200 || !/^https?:\/\//i.test(url)) {
throw new Error(data?.message || 'HYW 未返回播放链接')
}
return upgradeTxAudioUrl(url)
}
export default defineEventHandler(async (event) => {
const body = await readBody(event)
const platform = String(body?.platform || '').trim()
const musicId = body?.musicId
const playUrl = String(body?.playUrl || '').trim()
const cookie = normalizeQqCookie(String(body?.cookie || '').trim())
// official-first:QQ 官方 Cookie 链路;fallback:第三方兜底链路
const strategy = body?.strategy === 'official-first' ? 'official-first' : 'fallback'
const excludedSources = new Set(
Array.isArray(body?.excludeSources)
? body.excludeSources.map((item: unknown) => String(item || '').trim()).filter(Boolean)
: []
)
if (playUrl) {
return {
success: true,
url: platform === 'tencent' ? upgradeTxAudioUrl(playUrl) : playUrl,
source: 'play-url',
normalizedMusicId: musicId ? String(musicId).trim() : '',
idType: 'provided-url',
authUsed: Boolean(cookie),
authDiagnostic: getQqCookieDiagnostic(cookie)
}
}
if (platform !== 'tencent') {
throw createError({ statusCode: 400, message: '暂不支持的平台' })
}
const normalizedMusicId = String(musicId || '').trim()
// 官方 Cookie 链路:带用户登录态走 CgiGetVkey,可获取 VIP/高音质直链
if (strategy === 'official-first') {
if (excludedSources.has('qq-official')) {
throw createError({ statusCode: 502, message: 'QQ 音乐播放链接解析失败' })
}
try {
const url = await resolveQqSdkPlayUrl(
normalizedMusicId,
body?.quality,
cookie || undefined,
String(body?.mediaId || '').trim() || undefined
)
return {
success: true,
url,
source: 'qq-official',
normalizedMusicId,
idType: 'songmid',
authUsed: Boolean(cookie),
authDiagnostic: getQqCookieDiagnostic(cookie)
}
} catch (error: any) {
const sdkError = error?.message || String(error)
console.warn('[music/resolve-url] QQ 官方 SDK 解析失败,尝试原生直连:', sdkError)
// 原生直连:单文件名组合 + zzcSign 签名请求,与 SDK 的文件名策略互为校验
try {
const url = await resolveQqOfficialPlayUrl({
songmid: normalizedMusicId,
quality: body?.quality,
cookie: cookie || undefined,
mediaId: String(body?.mediaId || '').trim() || undefined
})
return {
success: true,
url,
source: 'qq-official',
normalizedMusicId,
idType: 'songmid',
authUsed: Boolean(cookie),
authDiagnostic: getQqCookieDiagnostic(cookie)
}
} catch (nativeError: any) {
const nativeErrorText = nativeError?.message || String(nativeError)
console.warn('[music/resolve-url] QQ 原生直连解析失败:', nativeErrorText)
throw createError({
statusCode: 502,
message: `QQ 官方链路解析失败:${sdkError};原生直连:${nativeErrorText}`
})
}
}
}
if (excludedSources.has('hyw-tx')) {
throw createError({ statusCode: 502, message: 'QQ 音乐播放链接解析失败' })
}
try {
const url = await resolveTxWithHyw(normalizedMusicId, body?.quality)
return {
success: true,
url,
source: 'hyw-tx',
normalizedMusicId,
idType: 'songmid',
authUsed: Boolean(cookie),
authDiagnostic: getQqCookieDiagnostic(cookie)
}
} catch (error: any) {
console.warn('[music/resolve-url] HYW QQ 回退失败:', error?.message || error)
}
throw createError({
statusCode: 502,
message: 'QQ 音乐播放链接解析失败'
})
})