From a599e0716f789ee93d9a45bc2e1f302c1f8097ba Mon Sep 17 00:00:00 2001 From: lyswhut Date: Wed, 25 Sep 2024 18:04:42 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=9C=AC=E5=9C=B0=E5=90=8C?= =?UTF-8?q?=E5=90=8D=20`krc`=20=E6=A0=BC=E5=BC=8F=E6=AD=8C=E8=AF=8D?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E7=9A=84=E8=AF=BB=E5=8F=96=EF=BC=88#2053?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- publish/changeLog.md | 1 + src/common/utils/lyricUtils/kg.js | 74 +++++++++++++++++++++++++ src/common/utils/lyricUtils/util.ts | 13 +++++ src/renderer/utils/music.ts | 37 +++++++++++-- src/renderer/utils/musicSdk/kg/lyric.js | 58 +------------------ src/renderer/utils/musicSdk/kg/util.js | 15 ----- src/renderer/worker/main/music.ts | 4 +- 7 files changed, 122 insertions(+), 80 deletions(-) create mode 100644 src/common/utils/lyricUtils/kg.js create mode 100644 src/common/utils/lyricUtils/util.ts diff --git a/publish/changeLog.md b/publish/changeLog.md index da5e6e97..9e6ce163 100644 --- a/publish/changeLog.md +++ b/publish/changeLog.md @@ -1,6 +1,7 @@ ### 新增 - 新增托盘图标颜色 跟随系统亮暗模式 设置,可以在 设置-其他 启用 (#2016) +- 支持本地同名 `krc` 格式歌词文件的读取(#2053) ### 优化 diff --git a/src/common/utils/lyricUtils/kg.js b/src/common/utils/lyricUtils/kg.js new file mode 100644 index 00000000..8cf64ac7 --- /dev/null +++ b/src/common/utils/lyricUtils/kg.js @@ -0,0 +1,74 @@ +import { inflate } from 'zlib' +import { decodeName } from './util' + +// https://github.com/lyswhut/lx-music-desktop/issues/296#issuecomment-683285784 +const enc_key = Buffer.from([0x40, 0x47, 0x61, 0x77, 0x5e, 0x32, 0x74, 0x47, 0x51, 0x36, 0x31, 0x2d, 0xce, 0xd2, 0x6e, 0x69], 'binary') +const decodeLyric = str => new Promise((resolve, reject) => { + if (!str.length) return + const buf_str = Buffer.from(str, 'base64').subarray(4) + for (let i = 0, len = buf_str.length; i < len; i++) { + buf_str[i] = buf_str[i] ^ enc_key[i % 16] + } + inflate(buf_str, (err, result) => { + if (err) return reject(err) + resolve(result.toString()) + }) +}) + +const headExp = /^.*\[id:\$\w+\]\n/ + +const parseLyric = str => { + str = str.replace(/\r/g, '') + if (headExp.test(str)) str = str.replace(headExp, '') + let trans = str.match(/\[language:([\w=\\/+]+)\]/) + let lyric + let rlyric + let tlyric + if (trans) { + str = str.replace(/\[language:[\w=\\/+]+\]\n/, '') + let json = JSON.parse(Buffer.from(trans[1], 'base64').toString()) + for (const item of json.content) { + switch (item.type) { + case 0: + rlyric = item.lyricContent + break + case 1: + tlyric = item.lyricContent + break + } + } + } + let i = 0 + let lxlyric = str.replace(/\[((\d+),\d+)\].*/g, str => { + let result = str.match(/\[((\d+),\d+)\].*/) + let time = parseInt(result[2]) + let ms = time % 1000 + time /= 1000 + let m = parseInt(time / 60).toString().padStart(2, '0') + time %= 60 + let s = parseInt(time).toString().padStart(2, '0') + time = `${m}:${s}.${ms}` + if (rlyric) rlyric[i] = `[${time}]${rlyric[i]?.join('') ?? ''}` + if (tlyric) tlyric[i] = `[${time}]${tlyric[i]?.join('') ?? ''}` + i++ + return str.replace(result[1], time) + }) + rlyric = rlyric ? rlyric.join('\n') : '' + tlyric = tlyric ? tlyric.join('\n') : '' + lxlyric = lxlyric.replace(/<(\d+,\d+),\d+>/g, '<$1>') + lxlyric = decodeName(lxlyric) + lyric = lxlyric.replace(/<\d+,\d+>/g, '') + rlyric = decodeName(rlyric) + tlyric = decodeName(tlyric) + return { + lyric, + tlyric, + rlyric, + lxlyric, + } +} + + +export const decodeKrc = async(data) => { + return decodeLyric(data).then(parseLyric) +} diff --git a/src/common/utils/lyricUtils/util.ts b/src/common/utils/lyricUtils/util.ts new file mode 100644 index 00000000..ab08d208 --- /dev/null +++ b/src/common/utils/lyricUtils/util.ts @@ -0,0 +1,13 @@ +const encodeNames = { + ' ': ' ', + '&': '&', + '<': '<', + '>': '>', + '"': '"', + ''': "'", + ''': "'", +} as const + +export const decodeName = (str: string | null = '') => { + return str?.replace(/(?:&|<|>|"|'|'| )/gm, (s: string) => encodeNames[s as keyof typeof encodeNames]) ?? '' +} diff --git a/src/renderer/utils/music.ts b/src/renderer/utils/music.ts index 6d021358..db899b30 100644 --- a/src/renderer/utils/music.ts +++ b/src/renderer/utils/music.ts @@ -1,6 +1,7 @@ import { checkPath, joinPath, extname, basename, readFile, getFileStats } from '@common/utils/nodejs' import { formatPlayTime } from '@common/utils/common' import type { IComment } from 'music-metadata/lib/type' +import { decodeKrc } from '@common/utils/lyricUtils/kg' export const checkDownloadFileAvailable = async(musicInfo: LX.Download.ListItem, savePath: string): Promise => { return musicInfo.isComplate && !/\.ape$/.test(musicInfo.metadata.fileName) && @@ -161,10 +162,11 @@ export const getLocalMusicFilePic = async(path: string) => { * 获取歌曲文件歌词 * @param path 路径 */ -export const getLocalMusicFileLyric = async(path: string): Promise => { +export const getLocalMusicFileLyric = async(path: string): Promise => { // 尝试读取同目录下的同名lrc文件 - const lrcPath = path.replace(new RegExp('\\' + extname(path) + '$'), '.lrc') - const stats = await getFileStats(lrcPath) + const filePath = new RegExp('\\' + extname(path) + '$') + let lrcPath = path.replace(filePath, '.lrc') + let stats = await getFileStats(lrcPath) // console.log(lrcPath, stats) if (stats && stats.size < 1024 * 1024 * 10) { const lrcBuf = await readFile(lrcPath) @@ -175,23 +177,46 @@ export const getLocalMusicFileLyric = async(path: string): Promise 10) { - return metadata.common.lyrics[0].text + return { + lyric: metadata.common.lyrics[0].text, + } } // console.log(metadata) for (const info of Object.values(metadata.native)) { const ust = info.find(i => i.id == 'USLT') if (ust) { const value = ust.value as IComment - if (value.text && value.text.length > 10) return value.text + if (value.text && value.text.length > 10) { + return { + lyric: value.text, + } + } } } return null diff --git a/src/renderer/utils/musicSdk/kg/lyric.js b/src/renderer/utils/musicSdk/kg/lyric.js index 33480957..2d6f7a08 100644 --- a/src/renderer/utils/musicSdk/kg/lyric.js +++ b/src/renderer/utils/musicSdk/kg/lyric.js @@ -1,59 +1,5 @@ import { httpFetch } from '../../request' -import { decodeLyric } from './util' -import { decodeName } from '../../index' - -const headExp = /^.*\[id:\$\w+\]\n/ - -const parseLyric = str => { - str = str.replace(/\r/g, '') - if (headExp.test(str)) str = str.replace(headExp, '') - let trans = str.match(/\[language:([\w=\\/+]+)\]/) - let lyric - let rlyric - let tlyric - if (trans) { - str = str.replace(/\[language:[\w=\\/+]+\]\n/, '') - let json = JSON.parse(Buffer.from(trans[1], 'base64').toString()) - for (const item of json.content) { - switch (item.type) { - case 0: - rlyric = item.lyricContent - break - case 1: - tlyric = item.lyricContent - break - } - } - } - let i = 0 - let lxlyric = str.replace(/\[((\d+),\d+)\].*/g, str => { - let result = str.match(/\[((\d+),\d+)\].*/) - let time = parseInt(result[2]) - let ms = time % 1000 - time /= 1000 - let m = parseInt(time / 60).toString().padStart(2, '0') - time %= 60 - let s = parseInt(time).toString().padStart(2, '0') - time = `${m}:${s}.${ms}` - if (rlyric) rlyric[i] = `[${time}]${rlyric[i]?.join('') ?? ''}` - if (tlyric) tlyric[i] = `[${time}]${tlyric[i]?.join('') ?? ''}` - i++ - return str.replace(result[1], time) - }) - rlyric = rlyric ? rlyric.join('\n') : '' - tlyric = tlyric ? tlyric.join('\n') : '' - lxlyric = lxlyric.replace(/<(\d+,\d+),\d+>/g, '<$1>') - lxlyric = decodeName(lxlyric) - lyric = lxlyric.replace(/<\d+,\d+>/g, '') - rlyric = decodeName(rlyric) - tlyric = decodeName(tlyric) - return { - lyric, - tlyric, - rlyric, - lxlyric, - } -} +import { decodeKrc } from '@common/utils/lyricUtils/kg' export default { getIntv(interval) { @@ -130,7 +76,7 @@ export default { switch (body.fmt) { case 'krc': - return decodeLyric(body.content).then(result => parseLyric(result)) + return decodeKrc(body.content) case 'lrc': return { lyric: Buffer.from(body.content, 'base64').toString('utf-8'), diff --git a/src/renderer/utils/musicSdk/kg/util.js b/src/renderer/utils/musicSdk/kg/util.js index a9be1877..9c15bdeb 100644 --- a/src/renderer/utils/musicSdk/kg/util.js +++ b/src/renderer/utils/musicSdk/kg/util.js @@ -1,21 +1,6 @@ -import { inflate } from 'zlib' import { toMD5 } from '../utils' import { httpFetch } from '../../request' -// https://github.com/lyswhut/lx-music-desktop/issues/296#issuecomment-683285784 -const enc_key = Buffer.from([0x40, 0x47, 0x61, 0x77, 0x5e, 0x32, 0x74, 0x47, 0x51, 0x36, 0x31, 0x2d, 0xce, 0xd2, 0x6e, 0x69], 'binary') -export const decodeLyric = str => new Promise((resolve, reject) => { - if (!str.length) return - const buf_str = Buffer.from(str, 'base64').slice(4) - for (let i = 0, len = buf_str.length; i < len; i++) { - buf_str[i] = buf_str[i] ^ enc_key[i % 16] - } - inflate(buf_str, (err, result) => { - if (err) return reject(err) - resolve(result.toString()) - }) -}) - // s.content[0].lyricContent.forEach(([str]) => { // console.log(str) // }) diff --git a/src/renderer/worker/main/music.ts b/src/renderer/worker/main/music.ts index 4f2951cf..d2ebc5c8 100644 --- a/src/renderer/worker/main/music.ts +++ b/src/renderer/worker/main/music.ts @@ -31,7 +31,5 @@ export const getMusicFilePic = async(filePath: string) => { export const getMusicFileLyric = async(filePath: string): Promise => { const lyric = await getLocalMusicFileLyric(filePath) if (!lyric) return null - return { - lyric, - } + return lyric }