添加x源逐字歌词的支持

This commit is contained in:
lyswhut
2023-02-04 18:38:06 +08:00
parent 62330571a3
commit ab1d18debe
19 changed files with 377 additions and 30 deletions
+1
View File
@@ -73,6 +73,7 @@ const modules = {
// lang_s2t: 'lang_s2t',
handle_kw_decode_lyric: 'handle_kw_decode_lyric',
handle_tx_decode_lyric: 'handle_tx_decode_lyric',
get_lyric_info: 'get_lyric_info',
set_lyric_info: 'set_lyric_info',
set_config: 'set_config',
@@ -3,6 +3,7 @@ import { registerRendererEvents as list } from '@main/modules/commonRenderers/li
import app, { sendConfigChange } from './app'
import hotKey from './hotKey'
import kw_decodeLyric from './kw_decodeLyric'
import tx_decodeLyric from './tx_decodeLyric'
import userApi from './userApi'
import sync from './sync'
import data from './data'
@@ -26,6 +27,7 @@ export default () => {
app()
hotKey()
kw_decodeLyric()
tx_decodeLyric()
userApi()
sync()
data()
@@ -0,0 +1,46 @@
import { inflate } from 'zlib'
// import path from 'path'
import { mainHandle } from '@common/mainIpc'
import { WIN_MAIN_RENDERER_EVENT_NAME } from '@common/ipcNames'
// eslint-disable-next-line @typescript-eslint/dot-notation, @typescript-eslint/quotes
// const require = module[`require`].bind(module)
let qrc_decode: (buf: Buffer, len: number) => Buffer
const decode = async(str: string): Promise<string> => {
if (!str) return ''
const buf = Buffer.from(str, 'hex')
return new Promise((resolve, reject) => {
inflate(qrc_decode(buf, buf.length), (err, lrc) => {
if (err) reject(err)
else resolve(lrc.toString())
})
})
}
const handleDecode = async(lrc: string, tlrc: string, rlrc: string) => {
if (!qrc_decode) {
// const nativeBindingPath = path.join(__dirname, '../build/Release/qrc_decode.node')
// const nativeBindingPath = isDev ? path.join(__dirname, '../build/Release/qrc_decode.node')
// eslint-disable-next-line @typescript-eslint/no-var-requires
const addon = require('qrc_decode.node')
console.log(addon)
qrc_decode = addon.qrc_decode
}
const [lyric, tlyric, rlyric] = await Promise.all([decode(lrc), decode(tlrc), decode(rlrc)])
return {
lyric,
tlyric,
rlyric,
}
}
export default () => {
mainHandle<{ lrc: string, tlrc: string, rlrc: string }, { lyric: string, tlyric: string, rlyric: string }>(WIN_MAIN_RENDERER_EVENT_NAME.handle_tx_decode_lyric, async({ params: { lrc, tlrc, rlrc } }) => {
return handleDecode(lrc, tlrc, rlrc)
})
}
+1 -1
View File
@@ -18,7 +18,7 @@ const tx = {
},
getLyric(songInfo) {
// let singer = songInfo.singer.indexOf('、') > -1 ? songInfo.singer.split('、')[0] : songInfo.singer
return lyric.getLyric(songInfo.songmid)
return lyric.getLyric(songInfo)
},
getPic(songInfo) {
return apis('tx').getPic(songInfo)
+276 -16
View File
@@ -1,23 +1,283 @@
import { httpFetch } from '../../request'
import { b64DecodeUnicode, decodeName } from '../../index'
import getMusicInfo from './musicInfo'
import { rendererInvoke } from '@common/rendererIpc'
import { WIN_MAIN_RENDERER_EVENT_NAME } from '@common/ipcNames'
export default {
regexps: {
matchLrc: /.+"lyric":"([\w=+/]*)".+/,
const songIdMap = new Map()
const promises = new Map()
export const decodeLyric = (lrc, tlrc, rlrc) => rendererInvoke(WIN_MAIN_RENDERER_EVENT_NAME.handle_tx_decode_lyric, { lrc, tlrc, rlrc })
const parseTools = {
rxps: {
info: /^{"/,
lineTime: /^\[(\d+),\d+\]/,
wordTime: /\(\d+,\d+\)/,
wordTimeAll: /(\(\d+,\d+\))/g,
timeLabelFixRxp: /(?:\.0+|0+)$/,
},
getLyric(songmid) {
const requestObj = httpFetch(`https://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric_new.fcg?songmid=${songmid}&g_tk=5381&loginUin=0&hostUin=0&format=json&inCharset=utf8&outCharset=utf-8&platform=yqq`, {
headers: {
Referer: 'https://y.qq.com/portal/player.html',
},
})
requestObj.promise = requestObj.promise.then(({ body }) => {
if (body.code != 0 || !body.lyric) return Promise.reject(new Error('Get lyric failed'))
return {
lyric: decodeName(b64DecodeUnicode(body.lyric)),
tlyric: decodeName(b64DecodeUnicode(body.trans)),
msFormat(timeMs) {
if (Number.isNaN(timeMs)) return ''
let ms = timeMs % 1000
timeMs /= 1000
let m = parseInt(timeMs / 60).toString().padStart(2, '0')
timeMs %= 60
let s = parseInt(timeMs).toString().padStart(2, '0')
return `[${m}:${s}.${String(ms).padStart(3, '0')}]`
},
parseLyric(lrc) {
lrc = lrc.trim()
lrc = lrc.replace(/\r/g, '')
if (!lrc) return { lyric: '', lxlyric: '' }
const lines = lrc.split('\n')
const lxlrcLines = []
const lrcLines = []
for (let line of lines) {
line = line.trim()
let result = this.rxps.lineTime.exec(line)
if (!result) {
if (line.startsWith('[offset')) {
lxlrcLines.push(line)
lrcLines.push(line)
}
continue
}
const startMsTime = parseInt(result[1])
const startTimeStr = this.msFormat(startMsTime)
if (!startTimeStr) continue
let words = line.replace(this.rxps.lineTime, '')
lrcLines.push(`${startTimeStr}${words.replace(this.rxps.wordTimeAll, '')}`)
let times = words.match(this.rxps.wordTimeAll)
if (!times) continue
times = times.map(time => {
const result = /\((\d+),(\d+)\)/.exec(time)
return `<${Math.max(parseInt(result[1]) - startMsTime, 0)},${result[2]}>`
})
const wordArr = words.split(this.rxps.wordTime)
const newWords = times.map((time, index) => `${time}${wordArr[index]}`).join('')
lxlrcLines.push(`${startTimeStr}${newWords}`)
}
return {
lyric: lrcLines.join('\n'),
lxlyric: lxlrcLines.join('\n'),
}
},
parseRlyric(lrc) {
lrc = lrc.trim()
lrc = lrc.replace(/\r/g, '')
if (!lrc) return { lyric: '', lxlyric: '' }
const lines = lrc.split('\n')
const lrcLines = []
for (let line of lines) {
line = line.trim()
let result = this.rxps.lineTime.exec(line)
if (!result) continue
const startMsTime = parseInt(result[1])
const startTimeStr = this.msFormat(startMsTime)
if (!startTimeStr) continue
let words = line.replace(this.rxps.lineTime, '')
lrcLines.push(`${startTimeStr}${words.replace(this.rxps.wordTimeAll, '')}`)
}
return lrcLines.join('\n')
},
removeTag(str) {
return str.replace(/^[\S\s]*?LyricContent="/, '').replace(/"\/>[\S\s]*?$/, '')
},
getIntv(interval) {
let [m, s, ms] = interval.split(/:|\./)
return parseInt(m) * 3600000 + parseInt(s) * 1000 + parseInt(ms)
},
fixRlrcTimeTag(rlrc, lrc) {
// console.log(lrc)
// console.log(rlrc)
const rlrcLines = rlrc.split('\n')
let lrcLines = lrc.split('\n')
// let temp = []
const timeTagRxp = /^\[([\d:.]+)\]/
let newLrc = []
rlrcLines.forEach((line) => {
const result = timeTagRxp.exec(line)
if (!result) return
const words = line.replace(timeTagRxp, '')
if (!words.trim()) return
const t1 = this.getIntv(result[1])
while (lrcLines.length) {
const lrcLine = lrcLines.shift()
const lrcLineResult = timeTagRxp.exec(lrcLine)
if (!lrcLineResult) continue
const t2 = this.getIntv(lrcLineResult[1])
if (Math.abs(t1 - t2) < 10) {
newLrc.push(line.replace(timeTagRxp, lrcLineResult[0]))
break
}
// temp.push(line)
}
// lrcLines = [...temp, ...lrcLines]
// temp = []
})
return requestObj
return newLrc.join('\n')
},
fixTlrcTimeTag(tlrc, lrc) {
// console.log(lrc)
// console.log(tlrc)
const tlrcLines = tlrc.split('\n')
let lrcLines = lrc.split('\n')
// let temp = []
const timeTagRxp = /^\[[\d:.]+\]/
let newLrc = []
tlrcLines.forEach((line) => {
const result = timeTagRxp.exec(line)
if (!result) return
const words = line.replace(timeTagRxp, '')
if (!words.trim()) return
const tag = result[0].replace(/\d]/, '').replace(this.rxps.timeLabelFixRxp, '')
while (lrcLines.length) {
const lrcLine = lrcLines.shift()
const lrcLineResult = timeTagRxp.exec(lrcLine)
if (!lrcLineResult) continue
if (lrcLineResult[0].includes(tag)) {
newLrc.push(line.replace(timeTagRxp, lrcLineResult[0]))
break
}
// temp.push(line)
}
// lrcLines = [...temp, ...lrcLines]
// temp = []
})
return newLrc.join('\n')
},
parse(lrc, tlrc, rlrc) {
const info = {
lyric: '',
tlyric: '',
rlyric: '',
lxlyric: '',
}
if (lrc) {
let { lyric, lxlyric } = this.parseLyric(this.removeTag(lrc))
info.lyric = lyric
info.lxlyric = lxlyric
// console.log(lyric)
// console.log(lxlyric)
}
if (rlrc) info.rlyric = this.fixRlrcTimeTag(this.parseRlyric(this.removeTag(rlrc)), info.lyric)
if (tlrc) info.tlyric = this.fixTlrcTimeTag(tlrc, info.lyric)
// console.log(info.lyric)
// console.log(info.tlyric)
// console.log(info.rlyric)
return info
},
}
export default {
successCode: 0,
async getSongId({ songId, songmid }) {
if (songId) return songId
if (songIdMap.has(songmid)) return songIdMap.get(songmid)
if (promises.has(songmid)) return (await promises.get(songmid)).songId
const promise = getMusicInfo(songmid)
promises.set(promise)
const info = await promise
songIdMap.set(songmid, info.songId)
promises.delete(songmid)
return info.songId
},
async parseLyric(lrc, tlrc, rlrc) {
const { lyric, tlyric, rlyric } = await decodeLyric(lrc, tlrc, rlrc)
// return {
// }
// console.log(lyric)
// console.log(tlyric)
// console.log(rlyric)
return parseTools.parse(lyric, tlyric, rlyric)
},
getLyric(mInfo, retryNum = 0) {
if (retryNum > 3) return Promise.reject(new Error('Get lyric failed'))
return {
cancelHttp() {
},
promise: this.getSongId(mInfo).then(songId => {
const requestObj = httpFetch('https://u.y.qq.com/cgi-bin/musicu.fcg', {
method: 'post',
headers: {
referer: 'https://y.qq.com',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36',
},
body: {
comm: {
ct: '19',
cv: '1859',
uin: '0',
},
req: {
method: 'GetPlayLyricInfo',
module: 'music.musichallSong.PlayLyricInfo',
param: {
format: 'json',
crypt: 1,
ct: 19,
cv: 1873,
interval: 0,
lrc_t: 0,
qrc: 1,
qrc_t: 0,
roma: 1,
roma_t: 0,
songID: songId,
trans: 1,
trans_t: 0,
type: -1,
},
},
},
})
return requestObj.promise.then(({ body }) => {
// console.log(body)
if (body.code != this.successCode || body.req.code != this.successCode) return this.getLyric(songId, ++retryNum)
const data = body.req.data
return this.parseLyric(data.lyric, data.trans, data.roma)
})
}),
}
},
}
// export default {
// regexps: {
// matchLrc: /.+"lyric":"([\w=+/]*)".+/,
// },
// getLyric(songmid) {
// const requestObj = httpFetch(`https://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric_new.fcg?songmid=${songmid}&g_tk=5381&loginUin=0&hostUin=0&format=json&inCharset=utf8&outCharset=utf-8&platform=yqq`, {
// headers: {
// Referer: 'https://y.qq.com/portal/player.html',
// },
// })
// requestObj.promise = requestObj.promise.then(({ body }) => {
// if (body.code != 0 || !body.lyric) return Promise.reject(new Error('Get lyric failed'))
// return {
// lyric: decodeName(b64DecodeUnicode(body.lyric)),
// tlyric: decodeName(b64DecodeUnicode(body.trans)),
// }
// })
// return requestObj
// },
// }
+7 -1
View File
@@ -77,7 +77,13 @@ const parseTools = {
for (let line of lines) {
line = line.trim()
let result = this.rxps.lineTime.exec(line)
if (!result) continue
if (!result) {
if (line.startsWith('[offset')) {
lxlrcLines.push(line)
lrcLines.push(line)
}
continue
}
const startMsTime = parseInt(result[1])
const startTimeStr = this.msFormat(startMsTime)