diff --git a/publish/changeLog.md b/publish/changeLog.md index a98a5103..957c4c26 100644 --- a/publish/changeLog.md +++ b/publish/changeLog.md @@ -4,6 +4,7 @@ - 新增播放详情页通过歌词调整播放进度,默认关闭,需要到设置-播放详情页设置开启,开启后在播放详情页拖动歌词时将会出现跳转当前行歌词播放的按钮 - 新增全屏状态,按F11可以进入、退出全屏状态,由于全屏时会隐藏控制栏按钮,所以需要使用鼠标右键双击(详情页的任意地方都可以)来关闭播放详情页 - 新增动态主题“道法自然”,你可以预先设置一个亮色主题及暗色主题,此后将根据系统的亮、暗主题色自动切换为你预先设置的相应主题。注:鼠标 右击 此主题项即可打开亮、暗色主题设置窗口。 +- 新增对kw源卡拉OK歌词的支持 ### 优化 diff --git a/src/renderer/store/modules/player.js b/src/renderer/store/modules/player.js index 7e54afc3..7ca330e3 100644 --- a/src/renderer/store/modules/player.js +++ b/src/renderer/store/modules/player.js @@ -188,6 +188,7 @@ const actions = { }, async getLrc({ commit, state }, musicInfo) { const lrcInfo = await getStoreLyric(musicInfo) + // lrcInfo = {} // if (lrcRequest && lrcRequest.cancelHttp) lrcRequest.cancelHttp() if (lrcInfo.lyric && lrcInfo.tlyric != null) { // if (musicInfo.lrc.startsWith('\ufeff[id:$00000000]')) { @@ -198,7 +199,15 @@ const actions = { // commit('setLrc', { musicInfo, lyric: str, tlyric: musicInfo.tlrc, lxlyric: musicInfo.tlrc }) // } - if ((lrcInfo.lxlyric == null && musicInfo.source != 'kg') || lrcInfo.lxlyric != null) return lrcInfo + if (lrcInfo.lxlyric == null) { + switch (musicInfo.source) { + case 'kg': + case 'kw': + break + default: + return lrcInfo + } + } else return lrcInfo } // lrcRequest = music[musicInfo.source].getLyric(musicInfo) diff --git a/src/renderer/utils/music/kw/lyric.js b/src/renderer/utils/music/kw/lyric.js index 81d4c7ae..f2e67f00 100644 --- a/src/renderer/utils/music/kw/lyric.js +++ b/src/renderer/utils/music/kw/lyric.js @@ -1,5 +1,5 @@ import { httpFetch } from '../../request' -import { decodeLyric } from './util' +import { decodeLyric, lrcTools } from './util' import { decodeName } from '../../index' /* @@ -99,6 +99,7 @@ export default { for (const item of arr) { if (lrcSet.has(item.time)) { + if (lrc.length < 2) continue const tItem = lrc.pop() tItem.time = lrc[lrc.length - 1].time lrcT.push(tItem) @@ -120,11 +121,12 @@ export default { lrcT, } }, - transformLrc(songinfo, lrclist) { - return `[ti:${songinfo.name ?? ''}]\n[ar:${songinfo.singer ?? ''}]\n[al:${songinfo.albumName ?? ''}]\n[by:]\n[offset:0]\n${lrclist ? lrclist.map(l => `[${l.time}]${l.text}\n`).join('') : '暂无歌词'}` + transformLrc(tags, lrclist) { + return `${tags.join('\n')}\n${lrclist ? lrclist.map(l => `[${l.time}]${l.text}\n`).join('') : '暂无歌词'}` }, - parseLrc(musicInfo, lrc) { + parseLrc(lrc) { const lines = lrc.split(/\r\n|\r|\n/) + let tags = [] let lrcArr = [] for (let i = 0; i < lines.length; i++) { const line = lines[i].trim() @@ -135,26 +137,66 @@ export default { time: RegExp.$1, text, }) + } else if (lrcTools.rxps.tagLine.test(line)) { + tags.push(line) } } const lrcInfo = this.sortLrcArr(lrcArr) return { - lyric: decodeName(this.transformLrc(musicInfo, lrcInfo.lrc)), - tlyric: lrcInfo.lrcT.length ? decodeName(this.transformLrc(musicInfo, lrcInfo.lrcT)) : '', + lyric: decodeName(this.transformLrc(tags, lrcInfo.lrc)), + tlyric: lrcInfo.lrcT.length ? decodeName(this.transformLrc(tags, lrcInfo.lrcT)) : '', } }, - getLyric(musicInfo, isGetLyricx = false) { + // getLyric2(musicInfo, isGetLyricx = true) { + // const requestObj = httpFetch(`http://newlyric.kuwo.cn/newlyric.lrc?${buildParams(musicInfo.songmid, isGetLyricx)}`) + // requestObj.promise = requestObj.promise.then(({ statusCode, body, raw }) => { + // if (statusCode != 200) return Promise.reject(new Error(JSON.stringify(body))) + // return decodeLyric({ lrcBase64: raw.toString('base64'), isGetLyricx }).then(base64Data => { + // let lrcInfo + // console.log(Buffer.from(base64Data, 'base64').toString()) + // try { + // lrcInfo = this.parseLrc(Buffer.from(base64Data, 'base64').toString()) + // } catch { + // return Promise.reject(new Error('Get lyric failed')) + // } + // if (lrcInfo.tlyric) lrcInfo.tlyric = lrcInfo.tlyric.replace(lrcTools.rxps.wordTimeAll, '') + // lrcInfo.lxlyric = lrcTools.parse(lrcInfo.lyric) + // // console.log(lrcInfo.lyric) + // // console.log(lrcInfo.tlyric) + // // console.log(lrcInfo.lxlyric) + // // console.log(JSON.stringify(lrcInfo)) + // }) + // }) + // return requestObj + // }, + getLyric(musicInfo, isGetLyricx = true) { + // this.getLyric2(musicInfo) const requestObj = httpFetch(`http://newlyric.kuwo.cn/newlyric.lrc?${buildParams(musicInfo.songmid, isGetLyricx)}`) requestObj.promise = requestObj.promise.then(({ statusCode, body, raw }) => { if (statusCode != 200) return Promise.reject(new Error(JSON.stringify(body))) return decodeLyric({ lrcBase64: raw.toString('base64'), isGetLyricx }).then(base64Data => { + // let lrcInfo + // try { + // lrcInfo = this.parseLrc(Buffer.from(base64Data, 'base64').toString()) + // } catch { + // return Promise.reject(new Error('Get lyric failed')) + // } let lrcInfo + // console.log(Buffer.from(base64Data, 'base64').toString()) try { - lrcInfo = this.parseLrc(musicInfo, Buffer.from(base64Data, 'base64').toString()) - } catch { + lrcInfo = this.parseLrc(Buffer.from(base64Data, 'base64').toString()) + } catch (err) { return Promise.reject(new Error('Get lyric failed')) } // console.log(lrcInfo) + if (lrcInfo.tlyric) lrcInfo.tlyric = lrcInfo.tlyric.replace(lrcTools.rxps.wordTimeAll, '') + try { + lrcInfo.lxlyric = lrcTools.parse(lrcInfo.lyric) + } catch { + lrcInfo.lxlyric = '' + } + if (lrcInfo.lxlyric) lrcInfo.lyric = lrcInfo.lyric.replace(lrcTools.rxps.wordTimeAll, '') + // console.log(lrcInfo.lxlyric) return lrcInfo }) }) diff --git a/src/renderer/utils/music/kw/util.js b/src/renderer/utils/music/kw/util.js index a8b86c70..5dbf98a2 100644 --- a/src/renderer/utils/music/kw/util.js +++ b/src/renderer/utils/music/kw/util.js @@ -76,3 +76,81 @@ export const tokenRequest = async(url, options = {}) => { }) return requestObj } + +export const lrcTools = { + rxps: { + wordLine: /^(\[\d{1,2}:.*\d{1,4}\])\s*(\S+(?:\s+\S+)*)?\s*/, + tagLine: /\[(ver|ti|ar|al|by|kuwo):\s*(\S+(?:\s+\S+)*)\s*\]/, + wordTimeAll: /<(-?\d+),(-?\d+)(?:,-?\d+)?>/g, + wordTime: /<(-?\d+),(-?\d+)(?:,-?\d+)?>/, + }, + offset: 1, + offset2: 1, + isOK: false, + lines: [], + getWordInfo(str, str2) { + const offset = parseInt(str) + const offset2 = parseInt(str2) + const startTime = Math.floor((offset + offset2) / (this.offset * 2)) + const timeLength = Math.floor((offset - offset2) / (this.offset2 * 2)) + return { + startTime, + timeLength, + } + }, + parseLine(line) { + if (line.length < 6) return + let result = this.rxps.wordLine.exec(line) + if (result) { + const time = result[1] + let words = result[2] + if (words == null) { + words = '' + } + const wordTimes = words.match(this.rxps.wordTimeAll) + if (!wordTimes) { + this.isOK = false + return + } + // console.log(wordTimes) + for (const timeStr of wordTimes) { + const result = this.rxps.wordTime.exec(timeStr) + const wordInfo = this.getWordInfo(result[1], result[2]) + words = words.replace(timeStr, `<${wordInfo.startTime},${wordInfo.timeLength}>`) + } + this.lines.push(time + words) + return + } + result = this.rxps.tagLine.exec(line) + if (!result) return + if (result[1] == 'kuwo') { + let content = result[2] + if (content != null && content.includes('][')) { + content = content.substring(0, content.indexOf('][')) + } + const valueOf = parseInt(content, 8) + this.offset = Math.floor(valueOf / 10) + this.offset2 = Math.floor(valueOf % 10) + if (this.offset == 0 || Number.isNaN(this.offset) || this.offset2 == 0 || Number.isNaN(this.offset2)) { + this.isOK = false + } + } else { + this.lines.push(line) + } + }, + parse(lrc) { + const lines = lrc.split(/\r\n|\r|\n/) + const tools = Object.create(this) + tools.isOK = true + tools.offset = 1 + tools.offset2 = 1 + tools.lines = [] + + for (const line of lines) { + if (!tools.isOK) return '' + tools.parseLine(line) + } + + return tools.lines.length ? tools.lines.join('\n') : '' + }, +}