Files
BYW-VoiceHub/server/api/native-api/lyric/tx.get.ts
T

124 lines
3.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { resolveQqNativeLyric, resolveQqSdkLyric } from '~~/server/utils/qq_music_sdk'
import { getTxSongPlayableInfo } from '~~/server/utils/native_tx'
import { getServerTimestamp } from '~~/server/utils/serverTime'
export default defineEventHandler(async (event) => {
const query = getQuery(event)
const songmid = (query.songmid as string) || ''
const songid = (query.songid as string) || ''
const cookie = (query.cookie as string) || ''
const name = (query.name as string) || ''
const artist = (query.artist as string) || ''
const album = (query.album as string) || ''
const duration = Number(query.duration) || 0
if (!songmid && !songid) {
throw createError({ statusCode: 400, message: '缺少 songmid 或 songid 参数' })
}
let resolvedSongId: string = songid
let resolvedSongmid: string = songmid
// mid → 数字 songId(原生 QRC 接口必须用数字 ID)
if (!resolvedSongId && resolvedSongmid) {
try {
const info = await getTxSongPlayableInfo(resolvedSongmid)
resolvedSongId = info.songId || ''
resolvedSongmid = info.songmid || resolvedSongmid
} catch (e) {
console.warn('[tx.lyric] 无法解析 songId,将回退到 SDK 接口:', e)
}
}
// 优先:原生 GetPlayLyricInfo 接口,支持 QRC 逐字歌词
if (resolvedSongId) {
try {
const data = await resolveQqNativeLyric({
songId: resolvedSongId,
name,
artist,
album,
duration,
cookie
})
if (data.qrc || data.lrc) {
return {
success: true,
data: {
lrc: data.lrc || '',
qrc: data.qrc || '',
trans: data.trans || '',
roma: data.roma || ''
}
}
}
} catch (e) {
console.warn('[tx.lyric] 原生 QRC 接口失败,回退到 SDK 接口:', e)
}
}
// 回退:qq-music-api SDK(只返回 LRC)
try {
const data = await resolveQqSdkLyric({ songmid: resolvedSongmid, songid: resolvedSongId, cookie })
return {
success: true,
data: {
lrc: data.lrc || '',
trans: data.trans || ''
}
}
} catch (sdkErr) {
console.warn('[tx.lyric] SDK 接口失败,回退到旧接口:', sdkErr)
}
// 最终回退:旧版 fcg_query_lyric_new 接口
const params = new URLSearchParams({
format: 'json',
outCharset: 'utf-8',
pcachetime: String(getServerTimestamp()),
loginUin: '0',
...(resolvedSongmid ? { songmid: resolvedSongmid } : {}),
...(resolvedSongId ? { songid: resolvedSongId } : {})
})
const response = await fetch(
`https://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric_new.fcg?${params.toString()}`,
{
headers: {
Referer: 'https://y.qq.com/portal/player.html',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'
},
signal: AbortSignal.timeout(8000)
}
)
if (!response.ok) {
throw createError({ statusCode: 502, message: `QQ 歌词接口返回 ${response.status}` })
}
const text = await response.text()
let legacyData: any
try {
legacyData = JSON.parse(text)
} catch {
legacyData = JSON.parse(text.replace(/^\w+\(/, '').replace(/\)\s*$/, ''))
}
if (!legacyData || legacyData.code !== 0) {
throw createError({ statusCode: 502, message: `QQ 歌词接口异常: ${legacyData?.code ?? '未知'}` })
}
const decodeBase64 = (value: unknown): string => {
if (typeof value !== 'string' || !value) return ''
try { return Buffer.from(value, 'base64').toString() || value } catch { return value }
}
return {
success: true,
data: {
lrc: decodeBase64(legacyData.lyric),
trans: decodeBase64(legacyData.trans)
}
}
})