From e899fd656353fe2f491a26c22578d76615d1edbd Mon Sep 17 00:00:00 2001 From: yizhi <946185759@qq.com> Date: Fri, 10 Feb 2023 15:49:33 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=AD=8C=E8=AF=8D=E5=88=86?= =?UTF-8?q?=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/launch.json | 2 +- README.md | 2 +- package.json | 6 +++-- src/lib/request.ts | 3 +++ src/lyric/common.ts | 2 +- src/lyric/lrcx.ts | 24 ++++++-------------- src/net/download.ts | 55 ++++++++++++++++++++++++++++----------------- src/test/index.ts | 43 +++++++++++++++++++---------------- test.js | 6 +++++ 9 files changed, 82 insertions(+), 61 deletions(-) create mode 100644 test.js diff --git a/.vscode/launch.json b/.vscode/launch.json index bf9ed01..9614dc7 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -11,7 +11,7 @@ "skipFiles": [ "/**" ], - "program": "${workspaceFolder}/dist/test/index.js", + "program": "${workspaceFolder}/test.js", "outFiles": [ "${workspaceFolder}/**/*.js" ] diff --git a/README.md b/README.md index 8a9f9d6..3fa4d17 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ import {utils, qrc, trc, krc, lrcx, lrc} from 'smart-lyric' async function example(){ // 从QQ音乐下载歌词 - const qrcTextFromNetwork = await utils.downloadQQMusicLyric({songID: 102878776}) + const qrcTextFromNetwork = await utils.downloadQQMusicLyric({songID: 102878776}).then(result=>result.karaok) console.log(qrcTextFromNetwork) //输出qrc歌词的XML文本 diff --git a/package.json b/package.json index f98ffaf..a908375 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "smart-lyric", - "version": "1.0.2", + "version": "1.0.3", "description": "", "main": "dist/index.js", "types": "typing/index.d.ts", @@ -25,7 +25,9 @@ "author": "", "license": "ISC", "devDependencies": { - "@types/node": "^18.11.18" + "@types/node": "^18.11.18", + "ts-node": "^10.9.1", + "typescript": "^4.9.5" }, "dependencies": { "iconv-lite": "^0.6.3", diff --git a/src/lib/request.ts b/src/lib/request.ts index aa0743a..64fe68e 100644 --- a/src/lib/request.ts +++ b/src/lib/request.ts @@ -56,6 +56,9 @@ export function request(option: IRequestOption) { case 'deflate': data = zlib.inflateSync(data) break + case 'br': + data = zlib.brotliDecompressSync(data) + break } switch (option.type) { case 'text': diff --git a/src/lyric/common.ts b/src/lyric/common.ts index e11c40e..6442578 100644 --- a/src/lyric/common.ts +++ b/src/lyric/common.ts @@ -17,7 +17,7 @@ function toInt(v: string | number) { * @param line 歌词行 */ export function parseLyricTag(lyric: ILyric, line: string) { - const match = line.match(/^\[([a-zA-Z][a-zA-Z0-9_]*):(.*)\]$/) + const match = line.match(/^\[([a-zA-Z#][a-zA-Z0-9_]*):(.*)\]$/) if (!match) return false switch (match[1]) { case 'ti': diff --git a/src/lyric/lrcx.ts b/src/lyric/lrcx.ts index 1d6af53..826beba 100644 --- a/src/lyric/lrcx.ts +++ b/src/lyric/lrcx.ts @@ -2,7 +2,6 @@ import zlib from 'zlib' import iconv from 'iconv-lite' import { IKaraokeWord, ILyric, ILyricLine, LyricType } from './declare' import { genLyricTag, parseLyricTag } from './common' -import { parse as parseLrc } from './lrc' const KUWO_LYRIC_KEY = Buffer.from('yeelion') @@ -23,19 +22,13 @@ function cryptLRCX(buffer: Buffer) { export function decrypt(buffer: Buffer) { try { //解压 - const b64Buffer = zlib.inflateSync(buffer) - const b64Str = b64Buffer.toString() - //不需要解码 - if (/\s*\[[a-z0-9A-z]+:/.test(b64Str)) return iconv.decode(b64Buffer, 'gb18030') - //进行解码 - else { - //取得内容 - const content = Buffer.from(b64Str, 'base64') - //解密 - cryptLRCX(content) - //return - return iconv.decode(content, 'gb18030') - } + const b64Str = zlib.inflateSync(buffer).toString() + //取得内容 + const content = Buffer.from(b64Str, 'base64') + //解密 + cryptLRCX(content) + //return + return iconv.decode(content, 'gb18030') //完成 } catch (err) { return null @@ -62,9 +55,6 @@ export function encrypt(content: string | Buffer) { * @param lrcxText LRCX歌词文本内容 */ export function parse(lrcxText: string) { - //如果不是lrcx则按照lrc解析 - if (!/^\[kuwo:\d+\]$/im.test(lrcxText)) return parseLrc(lrcxText) - //结果 const result: ILyric = { type: LyricType.KARA, content: [] } diff --git a/src/net/download.ts b/src/net/download.ts index 1021365..76be332 100644 --- a/src/net/download.ts +++ b/src/net/download.ts @@ -1,9 +1,17 @@ import crypto from 'crypto' +import iconv from 'iconv-lite' +import zlib from 'zlib' import * as qrcUtil from '../lyric/qrc' import * as krcUtil from '../lyric/krc' import * as lrcxUtil from '../lyric/lrcx' import { request } from '../lib/request' +interface IDownloadResult { + /** 卡拉OK歌词 */ + karaok: string | null + /** 常规歌词 */ + regular: string | null +} /** QQ音乐歌词下载选项 */ interface IQQMusicLyricDownloadOption { @@ -15,13 +23,15 @@ interface IQQMusicLyricDownloadOption { songName?: string /** 指定歌手名称 */ singerName?: string | string[] + /** 是否下载QRC歌词,默认`true` */ + qrc?: boolean } /** * 从QQ音乐API下载歌词 * @param option 下载选项 */ -export async function downloadQQMusicLyric(option: IQQMusicLyricDownloadOption) { +export async function downloadQQMusicLyric(option: IQQMusicLyricDownloadOption): Promise { //创建一个参数 const createParam = (paranName: string, value: string | undefined | string[]) => { if (value && typeof value === 'string') return { [paranName]: Buffer.from(value).toString('base64') } @@ -53,8 +63,7 @@ export async function downloadQQMusicLyric(option: IQQMusicLyricDownloadOption) // "cv": 1906, // "interval": 304, // "lrc_t": 0, - qrc: 1, - // "qrc": (option.qrc ?? true) ? 1 : 0, + qrc: (option.qrc ?? true) ? 1 : 0, // "qrc_t": 0, // "roma": option.roma ? 1 : 0, // "roma_t": 0, @@ -80,8 +89,11 @@ export async function downloadQQMusicLyric(option: IQQMusicLyricDownloadOption) else return Buffer.from(str, 'base64').toString() } - if (lyricInfo?.qrc) return parseLyric(lyricInfo.lyric, 'hex') - return null + if (lyricInfo.lyric) { + if (lyricInfo?.qrc) return { karaok: parseLyric(lyricInfo.lyric, 'hex'), regular: null } + else return { karaok: null, regular: Buffer.from(lyricInfo.lyric, 'base64').toString() } + } + return { karaok: null, regular: null } // let result: IQQMusicDownloadResult | null = null // //内容为qrc @@ -123,7 +135,7 @@ interface IKugouLyricDownloadOption { * @param option 下载选项 * @returns krc歌词文本内容 */ -export async function downloadKugouLyric(option: IKugouLyricDownloadOption) { +export async function downloadKugouLyric(option: IKugouLyricDownloadOption): Promise { //请求数据 const resp = await request({ url: `http://lyrics.kugou.com/download?ver=1&client=pc&id=${option.id}&accesskey=${option.accesskey}&fmt=${option.fmt ?? 'krc'}&charset=utf8`, @@ -131,9 +143,11 @@ export async function downloadKugouLyric(option: IKugouLyricDownloadOption) { type: 'json', }) - if (resp.status !== 200 || !resp.content) return null - - return krcUtil.decrypt(Buffer.from(resp.content, 'base64')) + if (resp.status === 200 && resp.content) { + if (resp.fmt === 'krc') return { karaok: krcUtil.decrypt(Buffer.from(resp.content, 'base64')), regular: null } + else if (resp.fmt === 'lrc') return { karaok: null, regular: Buffer.from(resp.content, 'base64').toString() } + } + return { karaok: null, regular: null } } /** 酷我音乐歌词下载选项 */ @@ -156,7 +170,7 @@ interface IKuwoLyricDownloadOption { * 下载酷我音乐歌词 * @param option 下载选项 */ -export async function downloadKuwoLyric(option: IKuwoLyricDownloadOption) { +export async function downloadKuwoLyric(option: IKuwoLyricDownloadOption): Promise { const KUWO_KEY = Buffer.from('yeelion') //加密歌词参数 @@ -224,8 +238,11 @@ export async function downloadKuwoLyric(option: IKuwoLyricDownloadOption) { } } - //解码歌词 - return lrcxUtil.decrypt(buffer) + if (buffer.byteLength && baseInfo.lrc_length) { + if (baseInfo.lrcx) return { karaok: lrcxUtil.decrypt(buffer), regular: null } + else return { karaok: null, regular: iconv.decode(zlib.inflateSync(buffer), 'gb18030') } + } + return { karaok: null, regular: null } } @@ -241,7 +258,7 @@ interface IDownloadNeteasyLyric { * 下载网易云音乐歌词 * @param option 下载选项 */ -export async function downloadNeteasyLyric(option: IDownloadNeteasyLyric) { +export async function downloadNeteasyLyric(option: IDownloadNeteasyLyric): Promise { const encryptData = (paramData: any) => { const text = JSON.stringify(paramData); @@ -270,15 +287,13 @@ export async function downloadNeteasyLyric(option: IDownloadNeteasyLyric) { }), headers: { 'Accept': '*/*', - 'Accept-Encoding': 'gzip, deflate', - // 'Origin': 'orpheus://orpheus', - // 'Cookie': 'os=pc; deviceId=9E6E85D4C25B23CD18B4781301BF5AA8C83A260E525400485AC4; osver=Microsoft-Windows-10-Professional-build-22000-64bit; appver=2.10.6.200601; NMTID=00OxyKBYOYjYuWko0YsqwDxN_KA3-kAAAGGFcYW_g; channel=netease; WEVNSM=1.0.0; mode=Standard%20PC%20(Q35%20%2B%20ICH9%2C%202009); __csrf=98ba68b228a2c0065e9820245d7c3605; MUSIC_A=e177342ffaa63031618420730fe7c83bd0d668d4476a15687eb986f8c79eeb1c062890f94ac493a999e7ad1570fb37952038bc2b7b0dc759fd95a411e59159fe033fad747150c42ddd2c085dbd859f914156aea54e160355b25a13460616b0f319145b139d7e79695bdffc390ee188a1f246a3151047f27c4212382188fe1965; ntes_kaola_ad=1; WNMCID=qbecdc.1675402548248.01.0', - // 'MConfig-Info': '{"IuRPVVmc3WWul9fT":{"version":288768,"appver":"2.10.6.200601"}}', - // 'MG-Product-Name': 'music', - // 'Nm-GCore-Status': 1, + 'Accept-Encoding': 'gzip, deflate,br', }, type: 'json', }) - return (res.yrc.lyric as string | null) || null + return { + karaok: ((res?.klyric?.lyric ?? '') + (res?.yrc?.lyric ?? '')) || null, + regular: ((res?.klyric?.lyric ?? '') + (res?.lrc?.lyric ?? '')) || null, + } } diff --git a/src/test/index.ts b/src/test/index.ts index d3689be..f776a6c 100644 --- a/src/test/index.ts +++ b/src/test/index.ts @@ -13,34 +13,39 @@ async function test() { // } console.log('==============================测试qrc==========================================') - await utils.downloadQQMusicLyric({ songID: 102878776 }).then(data => { - const lyric = qrc.parse(data!) - console.log(lyric) - console.log(qrc.stringify(lyric)) + await utils.downloadQQMusicLyric({ songID: 102878776, qrc: true }).then(res => { + if (res.karaok) { + const lyric = qrc.parse(res.karaok) + console.log(lyric) + console.log(qrc.stringify(lyric)) + } }) console.log('==============================测试krc==========================================') - await utils.downloadKugouLyric({ id: 10515303, accesskey: '3A20F6A1933DE370EBA0187297F5477D' }).then(data => { - const lyric = krc.parse(data!) - console.log(lyric) - console.log(lrcx.stringify(lyric)) + await utils.downloadKugouLyric({ id: 10515303, accesskey: '3A20F6A1933DE370EBA0187297F5477D', fmt: 'lrc' }).then(res => { + if (res.karaok) { + const lyric = krc.parse(res.karaok) + console.log(lyric) + console.log(lrcx.stringify(lyric)) + } }) console.log('==============================测试lrcx==========================================') - await utils.downloadKuwoLyric({ - musicID: 'MUSIC_67340506', - lrcx: true, - }).then(txt => { - const lyric = lrcx.parse(txt!) - console.log(lyric) - console.log(lrcx.stringify(lyric)) + await utils.downloadKuwoLyric({ musicID: 'MUSIC_67340506', lrcx: true }).then(res => { + if (res.karaok) { + const lyric = lrcx.parse(res.karaok) + console.log(lyric) + console.log(lrcx.stringify(lyric)) + } }) console.log('==============================测试nrc==========================================') - await utils.downloadNeteasyLyric({ musicID: '210049' }).then(data => { - const lyric = nrc.parse(data!) - console.log(lyric) - console.log(nrc.stringify(lyric)) + await utils.downloadNeteasyLyric({ musicID: '210049' }).then(res => { + if (res.karaok) { + const lyric = nrc.parse(res.karaok) + console.log(lyric) + console.log(nrc.stringify(lyric)) + } }) } diff --git a/test.js b/test.js new file mode 100644 index 0000000..c320bd3 --- /dev/null +++ b/test.js @@ -0,0 +1,6 @@ +require('ts-node').register({ + project: __dirname + '/tsconfig.json', + files: true, +}) + +require('./src/test/index.ts') \ No newline at end of file