172 lines
4.8 KiB
TypeScript
172 lines
4.8 KiB
TypeScript
import {
|
|
getTxSongPlayableInfo,
|
|
TX_MUSICU_URL,
|
|
txRequest,
|
|
txSignedRequest
|
|
} from '~~/server/utils/native_tx'
|
|
import { SERVER_ERROR_CODES } from '~~/server/config/constants'
|
|
import { createApiError } from '~~/server/utils/apiError'
|
|
import {
|
|
buildQqCommentRequestParam,
|
|
normalizeQqCommentPage
|
|
} from '~~/server/utils/qqComment'
|
|
|
|
interface QqCommentListData {
|
|
Comments?: unknown
|
|
comments?: unknown
|
|
commentlist?: unknown
|
|
list?: unknown
|
|
Total?: unknown
|
|
total?: unknown
|
|
HasMore?: unknown
|
|
hasMore?: unknown
|
|
hasmore?: unknown
|
|
}
|
|
|
|
interface QqCommentModuleData extends QqCommentListData {
|
|
CommentList?: QqCommentListData
|
|
comment?: QqCommentListData
|
|
}
|
|
|
|
interface QqCommentResponse {
|
|
code?: unknown
|
|
request?: {
|
|
code?: unknown
|
|
data?: QqCommentModuleData
|
|
}
|
|
comment?: {
|
|
code?: unknown
|
|
data?: QqCommentModuleData
|
|
}
|
|
}
|
|
|
|
const getCommentListData = (response: QqCommentResponse) => {
|
|
const data = response.request?.data || response.comment?.data || {}
|
|
const list = data.CommentList || data.comment || data
|
|
return {
|
|
list,
|
|
comments: list.Comments || list.comments || list.commentlist || list.list || []
|
|
}
|
|
}
|
|
|
|
const isSuccessfulResponse = (response: QqCommentResponse | undefined) => {
|
|
if (!response || Number(response.code) !== 0) return false
|
|
const moduleCode = response.request?.code ?? response.comment?.code
|
|
return Number(moduleCode) === 0
|
|
}
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
const query = getQuery(event)
|
|
const musicId = String(query.musicId || '').trim()
|
|
const originalSongId = String(query.songId || '').trim()
|
|
const cursor = String(query.cursor || '').trim()
|
|
const rawPage = Number(query.page)
|
|
const rawPageSize = Number(query.pageSize)
|
|
const page = Number.isFinite(rawPage) ? Math.max(0, Math.floor(rawPage)) : 0
|
|
const pageSize = Number.isFinite(rawPageSize)
|
|
? Math.min(50, Math.max(1, Math.floor(rawPageSize)))
|
|
: 20
|
|
const type = query.type === 'latest' ? 'latest' : 'hot'
|
|
if (!musicId) {
|
|
throw createApiError(
|
|
400,
|
|
SERVER_ERROR_CODES.COMMON_INVALID_PARAMS,
|
|
'缺少 musicId 参数'
|
|
)
|
|
}
|
|
|
|
let topid = /^\d+$/.test(originalSongId) ? originalSongId : ''
|
|
if (!topid && /^\d+$/.test(musicId)) topid = musicId
|
|
if (!topid) {
|
|
try {
|
|
const info = await getTxSongPlayableInfo(musicId)
|
|
topid = String(info.songId || '').trim()
|
|
} catch (error) {
|
|
console.error('[QQ评论] 解析歌曲 ID 失败:', error instanceof Error ? error.message : error)
|
|
throw createApiError(
|
|
502,
|
|
SERVER_ERROR_CODES.QQ_COMMENT_FETCH_FAILED,
|
|
'QQ 音乐评论获取失败'
|
|
)
|
|
}
|
|
}
|
|
if (!topid) {
|
|
throw createApiError(
|
|
400,
|
|
SERVER_ERROR_CODES.COMMON_INVALID_PARAMS,
|
|
'QQ 音乐歌曲 ID 无效'
|
|
)
|
|
}
|
|
|
|
const requestBody = {
|
|
comm: {
|
|
ct: 11,
|
|
cv: '1003006',
|
|
v: '1003006',
|
|
os_ver: '15',
|
|
phonetype: '24122RKC7C',
|
|
tmeAppID: 'qqmusiclight',
|
|
nettype: 'NETWORK_WIFI',
|
|
udid: '0',
|
|
OpenUDID: '0',
|
|
QIMEI36: '0',
|
|
uin: '0'
|
|
},
|
|
request: {
|
|
module: 'music.globalComment.CommentRead',
|
|
method: type === 'hot' ? 'GetHotCommentList' : 'GetNewCommentList',
|
|
param: buildQqCommentRequestParam({ topid, cursor, page, pageSize, type })
|
|
}
|
|
}
|
|
|
|
let response: QqCommentResponse | undefined
|
|
try {
|
|
response = (await txRequest(TX_MUSICU_URL, requestBody, {
|
|
signal: AbortSignal.timeout(8000)
|
|
})) as QqCommentResponse
|
|
} catch (error) {
|
|
console.warn('[QQ评论] 直连接口请求失败,尝试签名接口:', error)
|
|
}
|
|
|
|
if (!isSuccessfulResponse(response)) {
|
|
try {
|
|
response = (await txSignedRequest(requestBody, {
|
|
signal: AbortSignal.timeout(8000)
|
|
})) as QqCommentResponse
|
|
} catch (error) {
|
|
console.error('[QQ评论] 签名接口请求失败:', error instanceof Error ? error.message : error)
|
|
throw createApiError(
|
|
502,
|
|
SERVER_ERROR_CODES.QQ_COMMENT_FETCH_FAILED,
|
|
'QQ 音乐评论获取失败'
|
|
)
|
|
}
|
|
}
|
|
|
|
if (!isSuccessfulResponse(response)) {
|
|
throw createApiError(
|
|
502,
|
|
SERVER_ERROR_CODES.QQ_COMMENT_FETCH_FAILED,
|
|
'QQ 音乐评论获取失败'
|
|
)
|
|
}
|
|
const { list, comments: responseComments } = getCommentListData(response)
|
|
const rawComments = Array.isArray(responseComments) ? responseComments : []
|
|
const normalizedPage = normalizeQqCommentPage(rawComments)
|
|
const commentItems = normalizedPage.comments
|
|
|
|
return {
|
|
code: 200,
|
|
data: {
|
|
comments: commentItems,
|
|
orphanReplies: normalizedPage.orphanReplies,
|
|
total: Number(list.Total ?? list.total) || commentItems.length,
|
|
more: Number(list.HasMore ?? list.hasMore ?? list.hasmore) === 1,
|
|
nextCursor:
|
|
Number(list.HasMore ?? list.hasMore ?? list.hasmore) === 1
|
|
? String(rawComments.at(-1)?.SeqNo || rawComments.at(-1)?.seqNo || '')
|
|
: ''
|
|
}
|
|
}
|
|
})
|