增加歌词分类
This commit is contained in:
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@ -11,7 +11,7 @@
|
|||||||
"skipFiles": [
|
"skipFiles": [
|
||||||
"<node_internals>/**"
|
"<node_internals>/**"
|
||||||
],
|
],
|
||||||
"program": "${workspaceFolder}/dist/test/index.js",
|
"program": "${workspaceFolder}/test.js",
|
||||||
"outFiles": [
|
"outFiles": [
|
||||||
"${workspaceFolder}/**/*.js"
|
"${workspaceFolder}/**/*.js"
|
||||||
]
|
]
|
||||||
|
@ -19,7 +19,7 @@ import {utils, qrc, trc, krc, lrcx, lrc} from 'smart-lyric'
|
|||||||
async function example(){
|
async function example(){
|
||||||
|
|
||||||
// 从QQ音乐下载歌词
|
// 从QQ音乐下载歌词
|
||||||
const qrcTextFromNetwork = await utils.downloadQQMusicLyric({songID: 102878776})
|
const qrcTextFromNetwork = await utils.downloadQQMusicLyric({songID: 102878776}).then(result=>result.karaok)
|
||||||
console.log(qrcTextFromNetwork) //输出qrc歌词的XML文本
|
console.log(qrcTextFromNetwork) //输出qrc歌词的XML文本
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "smart-lyric",
|
"name": "smart-lyric",
|
||||||
"version": "1.0.2",
|
"version": "1.0.3",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "typing/index.d.ts",
|
"types": "typing/index.d.ts",
|
||||||
@ -25,7 +25,9 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^18.11.18"
|
"@types/node": "^18.11.18",
|
||||||
|
"ts-node": "^10.9.1",
|
||||||
|
"typescript": "^4.9.5"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"iconv-lite": "^0.6.3",
|
"iconv-lite": "^0.6.3",
|
||||||
|
@ -56,6 +56,9 @@ export function request<T = any>(option: IRequestOption) {
|
|||||||
case 'deflate':
|
case 'deflate':
|
||||||
data = zlib.inflateSync(data)
|
data = zlib.inflateSync(data)
|
||||||
break
|
break
|
||||||
|
case 'br':
|
||||||
|
data = zlib.brotliDecompressSync(data)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
switch (option.type) {
|
switch (option.type) {
|
||||||
case 'text':
|
case 'text':
|
||||||
|
@ -17,7 +17,7 @@ function toInt(v: string | number) {
|
|||||||
* @param line 歌词行
|
* @param line 歌词行
|
||||||
*/
|
*/
|
||||||
export function parseLyricTag(lyric: ILyric, line: string) {
|
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
|
if (!match) return false
|
||||||
switch (match[1]) {
|
switch (match[1]) {
|
||||||
case 'ti':
|
case 'ti':
|
||||||
|
@ -2,7 +2,6 @@ import zlib from 'zlib'
|
|||||||
import iconv from 'iconv-lite'
|
import iconv from 'iconv-lite'
|
||||||
import { IKaraokeWord, ILyric, ILyricLine, LyricType } from './declare'
|
import { IKaraokeWord, ILyric, ILyricLine, LyricType } from './declare'
|
||||||
import { genLyricTag, parseLyricTag } from './common'
|
import { genLyricTag, parseLyricTag } from './common'
|
||||||
import { parse as parseLrc } from './lrc'
|
|
||||||
|
|
||||||
|
|
||||||
const KUWO_LYRIC_KEY = Buffer.from('yeelion')
|
const KUWO_LYRIC_KEY = Buffer.from('yeelion')
|
||||||
@ -23,19 +22,13 @@ function cryptLRCX(buffer: Buffer) {
|
|||||||
export function decrypt(buffer: Buffer) {
|
export function decrypt(buffer: Buffer) {
|
||||||
try {
|
try {
|
||||||
//解压
|
//解压
|
||||||
const b64Buffer = zlib.inflateSync(buffer)
|
const b64Str = zlib.inflateSync(buffer).toString()
|
||||||
const b64Str = b64Buffer.toString()
|
|
||||||
//不需要解码
|
|
||||||
if (/\s*\[[a-z0-9A-z]+:/.test(b64Str)) return iconv.decode(b64Buffer, 'gb18030')
|
|
||||||
//进行解码
|
|
||||||
else {
|
|
||||||
//取得内容
|
//取得内容
|
||||||
const content = Buffer.from(b64Str, 'base64')
|
const content = Buffer.from(b64Str, 'base64')
|
||||||
//解密
|
//解密
|
||||||
cryptLRCX(content)
|
cryptLRCX(content)
|
||||||
//return
|
//return
|
||||||
return iconv.decode(content, 'gb18030')
|
return iconv.decode(content, 'gb18030')
|
||||||
}
|
|
||||||
//完成
|
//完成
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return null
|
return null
|
||||||
@ -62,9 +55,6 @@ export function encrypt(content: string | Buffer) {
|
|||||||
* @param lrcxText LRCX歌词文本内容
|
* @param lrcxText LRCX歌词文本内容
|
||||||
*/
|
*/
|
||||||
export function parse(lrcxText: string) {
|
export function parse(lrcxText: string) {
|
||||||
//如果不是lrcx则按照lrc解析
|
|
||||||
if (!/^\[kuwo:\d+\]$/im.test(lrcxText)) return parseLrc(lrcxText)
|
|
||||||
|
|
||||||
//结果
|
//结果
|
||||||
const result: ILyric = { type: LyricType.KARA, content: [] }
|
const result: ILyric = { type: LyricType.KARA, content: [] }
|
||||||
|
|
||||||
|
@ -1,9 +1,17 @@
|
|||||||
import crypto from 'crypto'
|
import crypto from 'crypto'
|
||||||
|
import iconv from 'iconv-lite'
|
||||||
|
import zlib from 'zlib'
|
||||||
import * as qrcUtil from '../lyric/qrc'
|
import * as qrcUtil from '../lyric/qrc'
|
||||||
import * as krcUtil from '../lyric/krc'
|
import * as krcUtil from '../lyric/krc'
|
||||||
import * as lrcxUtil from '../lyric/lrcx'
|
import * as lrcxUtil from '../lyric/lrcx'
|
||||||
import { request } from '../lib/request'
|
import { request } from '../lib/request'
|
||||||
|
|
||||||
|
interface IDownloadResult {
|
||||||
|
/** 卡拉OK歌词 */
|
||||||
|
karaok: string | null
|
||||||
|
/** 常规歌词 */
|
||||||
|
regular: string | null
|
||||||
|
}
|
||||||
|
|
||||||
/** QQ音乐歌词下载选项 */
|
/** QQ音乐歌词下载选项 */
|
||||||
interface IQQMusicLyricDownloadOption {
|
interface IQQMusicLyricDownloadOption {
|
||||||
@ -15,13 +23,15 @@ interface IQQMusicLyricDownloadOption {
|
|||||||
songName?: string
|
songName?: string
|
||||||
/** 指定歌手名称 */
|
/** 指定歌手名称 */
|
||||||
singerName?: string | string[]
|
singerName?: string | string[]
|
||||||
|
/** 是否下载QRC歌词,默认`true` */
|
||||||
|
qrc?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从QQ音乐API下载歌词
|
* 从QQ音乐API下载歌词
|
||||||
* @param option 下载选项
|
* @param option 下载选项
|
||||||
*/
|
*/
|
||||||
export async function downloadQQMusicLyric(option: IQQMusicLyricDownloadOption) {
|
export async function downloadQQMusicLyric(option: IQQMusicLyricDownloadOption): Promise<IDownloadResult> {
|
||||||
//创建一个参数
|
//创建一个参数
|
||||||
const createParam = (paranName: string, value: string | undefined | string[]) => {
|
const createParam = (paranName: string, value: string | undefined | string[]) => {
|
||||||
if (value && typeof value === 'string') return { [paranName]: Buffer.from(value).toString('base64') }
|
if (value && typeof value === 'string') return { [paranName]: Buffer.from(value).toString('base64') }
|
||||||
@ -53,8 +63,7 @@ export async function downloadQQMusicLyric(option: IQQMusicLyricDownloadOption)
|
|||||||
// "cv": 1906,
|
// "cv": 1906,
|
||||||
// "interval": 304,
|
// "interval": 304,
|
||||||
// "lrc_t": 0,
|
// "lrc_t": 0,
|
||||||
qrc: 1,
|
qrc: (option.qrc ?? true) ? 1 : 0,
|
||||||
// "qrc": (option.qrc ?? true) ? 1 : 0,
|
|
||||||
// "qrc_t": 0,
|
// "qrc_t": 0,
|
||||||
// "roma": option.roma ? 1 : 0,
|
// "roma": option.roma ? 1 : 0,
|
||||||
// "roma_t": 0,
|
// "roma_t": 0,
|
||||||
@ -80,8 +89,11 @@ export async function downloadQQMusicLyric(option: IQQMusicLyricDownloadOption)
|
|||||||
else return Buffer.from(str, 'base64').toString()
|
else return Buffer.from(str, 'base64').toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lyricInfo?.qrc) return parseLyric(lyricInfo.lyric, 'hex')
|
if (lyricInfo.lyric) {
|
||||||
return null
|
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
|
// let result: IQQMusicDownloadResult | null = null
|
||||||
// //内容为qrc
|
// //内容为qrc
|
||||||
@ -123,7 +135,7 @@ interface IKugouLyricDownloadOption {
|
|||||||
* @param option 下载选项
|
* @param option 下载选项
|
||||||
* @returns krc歌词文本内容
|
* @returns krc歌词文本内容
|
||||||
*/
|
*/
|
||||||
export async function downloadKugouLyric(option: IKugouLyricDownloadOption) {
|
export async function downloadKugouLyric(option: IKugouLyricDownloadOption): Promise<IDownloadResult> {
|
||||||
//请求数据
|
//请求数据
|
||||||
const resp = await request<any>({
|
const resp = await request<any>({
|
||||||
url: `http://lyrics.kugou.com/download?ver=1&client=pc&id=${option.id}&accesskey=${option.accesskey}&fmt=${option.fmt ?? 'krc'}&charset=utf8`,
|
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',
|
type: 'json',
|
||||||
})
|
})
|
||||||
|
|
||||||
if (resp.status !== 200 || !resp.content) return null
|
if (resp.status === 200 && resp.content) {
|
||||||
|
if (resp.fmt === 'krc') return { karaok: krcUtil.decrypt(Buffer.from(resp.content, 'base64')), regular: null }
|
||||||
return krcUtil.decrypt(Buffer.from(resp.content, 'base64'))
|
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 下载选项
|
* @param option 下载选项
|
||||||
*/
|
*/
|
||||||
export async function downloadKuwoLyric(option: IKuwoLyricDownloadOption) {
|
export async function downloadKuwoLyric(option: IKuwoLyricDownloadOption): Promise<IDownloadResult> {
|
||||||
|
|
||||||
const KUWO_KEY = Buffer.from('yeelion')
|
const KUWO_KEY = Buffer.from('yeelion')
|
||||||
//加密歌词参数
|
//加密歌词参数
|
||||||
@ -224,8 +238,11 @@ export async function downloadKuwoLyric(option: IKuwoLyricDownloadOption) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//解码歌词
|
if (buffer.byteLength && baseInfo.lrc_length) {
|
||||||
return lrcxUtil.decrypt(buffer)
|
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 下载选项
|
* @param option 下载选项
|
||||||
*/
|
*/
|
||||||
export async function downloadNeteasyLyric(option: IDownloadNeteasyLyric) {
|
export async function downloadNeteasyLyric(option: IDownloadNeteasyLyric): Promise<IDownloadResult> {
|
||||||
|
|
||||||
const encryptData = (paramData: any) => {
|
const encryptData = (paramData: any) => {
|
||||||
const text = JSON.stringify(paramData);
|
const text = JSON.stringify(paramData);
|
||||||
@ -270,15 +287,13 @@ export async function downloadNeteasyLyric(option: IDownloadNeteasyLyric) {
|
|||||||
}),
|
}),
|
||||||
headers: {
|
headers: {
|
||||||
'Accept': '*/*',
|
'Accept': '*/*',
|
||||||
'Accept-Encoding': 'gzip, deflate',
|
'Accept-Encoding': 'gzip, deflate,br',
|
||||||
// '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,
|
|
||||||
},
|
},
|
||||||
type: 'json',
|
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,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,34 +13,39 @@ async function test() {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
console.log('==============================测试qrc==========================================')
|
console.log('==============================测试qrc==========================================')
|
||||||
await utils.downloadQQMusicLyric({ songID: 102878776 }).then(data => {
|
await utils.downloadQQMusicLyric({ songID: 102878776, qrc: true }).then(res => {
|
||||||
const lyric = qrc.parse(data!)
|
if (res.karaok) {
|
||||||
|
const lyric = qrc.parse(res.karaok)
|
||||||
console.log(lyric)
|
console.log(lyric)
|
||||||
console.log(qrc.stringify(lyric))
|
console.log(qrc.stringify(lyric))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log('==============================测试krc==========================================')
|
console.log('==============================测试krc==========================================')
|
||||||
await utils.downloadKugouLyric({ id: 10515303, accesskey: '3A20F6A1933DE370EBA0187297F5477D' }).then(data => {
|
await utils.downloadKugouLyric({ id: 10515303, accesskey: '3A20F6A1933DE370EBA0187297F5477D', fmt: 'lrc' }).then(res => {
|
||||||
const lyric = krc.parse(data!)
|
if (res.karaok) {
|
||||||
|
const lyric = krc.parse(res.karaok)
|
||||||
console.log(lyric)
|
console.log(lyric)
|
||||||
console.log(lrcx.stringify(lyric))
|
console.log(lrcx.stringify(lyric))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log('==============================测试lrcx==========================================')
|
console.log('==============================测试lrcx==========================================')
|
||||||
await utils.downloadKuwoLyric({
|
await utils.downloadKuwoLyric({ musicID: 'MUSIC_67340506', lrcx: true }).then(res => {
|
||||||
musicID: 'MUSIC_67340506',
|
if (res.karaok) {
|
||||||
lrcx: true,
|
const lyric = lrcx.parse(res.karaok)
|
||||||
}).then(txt => {
|
|
||||||
const lyric = lrcx.parse(txt!)
|
|
||||||
console.log(lyric)
|
console.log(lyric)
|
||||||
console.log(lrcx.stringify(lyric))
|
console.log(lrcx.stringify(lyric))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log('==============================测试nrc==========================================')
|
console.log('==============================测试nrc==========================================')
|
||||||
await utils.downloadNeteasyLyric({ musicID: '210049' }).then(data => {
|
await utils.downloadNeteasyLyric({ musicID: '210049' }).then(res => {
|
||||||
const lyric = nrc.parse(data!)
|
if (res.karaok) {
|
||||||
|
const lyric = nrc.parse(res.karaok)
|
||||||
console.log(lyric)
|
console.log(lyric)
|
||||||
console.log(nrc.stringify(lyric))
|
console.log(nrc.stringify(lyric))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user