mirror of
https://github.com/MeoProject/lx-music-api-server.git
synced 2025-05-23 19:17:41 +08:00
feat: QQ音乐歌词功能
This commit is contained in:
parent
40943f354e
commit
d4056fcb7b
@ -60,7 +60,7 @@ default = {
|
||||
"proxy": {
|
||||
"enable": False,
|
||||
"http_addr": "http://127.0.0.1:7890",
|
||||
"https_addr": "https://127.0.0.1:7890",
|
||||
"https_addr": "http://127.0.0.1:7890",
|
||||
},
|
||||
"_proxy-desc": "代理配置,HTTP与HTTPS协议需分开配置",
|
||||
"log_file": True,
|
||||
|
10
common/natives/__init__.py
Normal file
10
common/natives/__init__.py
Normal file
@ -0,0 +1,10 @@
|
||||
# ----------------------------------------
|
||||
# - mode: python -
|
||||
# - author: helloplhm-qwq -
|
||||
# - name: __init__.py -
|
||||
# - project: lx-music-api-server -
|
||||
# - license: MIT -
|
||||
# ----------------------------------------
|
||||
# This file is part of the "lx-music-api-server" project.
|
||||
|
||||
from . import qdes
|
32
common/qdes.py
Normal file
32
common/qdes.py
Normal file
@ -0,0 +1,32 @@
|
||||
# ----------------------------------------
|
||||
# - mode: python -
|
||||
# - author: helloplhm-qwq -
|
||||
# - name: qdes.py -
|
||||
# - project: lx-music-api-server -
|
||||
# - license: MIT -
|
||||
# ----------------------------------------
|
||||
# This file is part of the "lx-music-api-server" project.
|
||||
|
||||
from .log import log
|
||||
from . import variable
|
||||
import binascii
|
||||
import zlib
|
||||
|
||||
logger = log('qdes')
|
||||
|
||||
try:
|
||||
from .natives import qdes
|
||||
variable.qdes_lib_loaded = True
|
||||
except:
|
||||
try:
|
||||
import qdes
|
||||
variable.qdes_lib_loaded = True
|
||||
except:
|
||||
logger.warning('QRC解密库qdes加载失败, 可能为不支持当前系统, QRC相关的逐字歌词获取将无法使用')
|
||||
|
||||
def qdes_decrypt(qrc):
|
||||
if variable.qdes_lib_loaded:
|
||||
decoded = zlib.decompress(qdes.LyricDecode(binascii.unhexlify(qrc.encode('utf-8')))).decode('utf-8')
|
||||
return decoded
|
||||
else:
|
||||
raise ModuleNotFoundError('qdes解密库未被加载')
|
@ -7,13 +7,13 @@
|
||||
# ----------------------------------------
|
||||
# This file is part of the "lx-music-api-server" project.
|
||||
|
||||
import os
|
||||
import ujson as json
|
||||
import os as _os
|
||||
import ujson as _json
|
||||
|
||||
def _read_config_file():
|
||||
try:
|
||||
with open("./config.json", "r", encoding = "utf-8") as f:
|
||||
return json.load(f)
|
||||
return _json.load(f)
|
||||
except:
|
||||
pass
|
||||
|
||||
@ -42,8 +42,9 @@ log_length_limit = log_length_limit if (log_length_limit := _read_config("common
|
||||
log_file = log_file if (not isinstance(log_file := _read_config("common.log_file"), type(None))) else True
|
||||
running = True
|
||||
config = {}
|
||||
workdir = os.getcwd()
|
||||
workdir = _os.getcwd()
|
||||
banList_suggest = 0
|
||||
iscn = True
|
||||
fake_ip = None
|
||||
aioSession = None
|
||||
aioSession = None
|
||||
qdes_lib_loaded = False
|
5
main.py
5
main.py
@ -97,7 +97,10 @@ async def handle(request):
|
||||
return handleResult({"code": 1, "msg": "lxm请求头验证失败", "data": None}, 403)
|
||||
|
||||
try:
|
||||
return handleResult(await getattr(modules, method)(source, songId, quality))
|
||||
if (method in dir(modules)):
|
||||
return handleResult(await getattr(modules, method)(source, songId, quality))
|
||||
else:
|
||||
return handleResult(await modules.other(method, source, songId, quality))
|
||||
except:
|
||||
logger.error(traceback.format_exc())
|
||||
return handleResult({'code': 4, 'msg': '内部服务器错误', 'data': None}, 500)
|
||||
|
@ -123,9 +123,9 @@ async def url(source, songId, quality):
|
||||
'data': None,
|
||||
}
|
||||
|
||||
async def info(source, songid, _):
|
||||
async def other(method, source, songid, _):
|
||||
try:
|
||||
func = require('modules.' + source + '.info')
|
||||
func = require('modules.' + source + '.' + method)
|
||||
except:
|
||||
return {
|
||||
'code': 1,
|
||||
@ -145,25 +145,3 @@ async def info(source, songid, _):
|
||||
'msg': e.args[0],
|
||||
'data': None,
|
||||
}
|
||||
|
||||
async def mv(source, mvId, _):
|
||||
try:
|
||||
func = require('modules.' + source + '.mv')
|
||||
except:
|
||||
return {
|
||||
'code': 1,
|
||||
'msg': '未知的源或不支持的方法',
|
||||
'data': None,
|
||||
}
|
||||
try:
|
||||
result = await func(mvId)
|
||||
return {
|
||||
'code': 0,
|
||||
'msg': 'success',
|
||||
'data': result
|
||||
}
|
||||
except FailedException as e:
|
||||
return {
|
||||
'code': 2,
|
||||
'msg': e.args[0],
|
||||
}
|
@ -10,6 +10,7 @@
|
||||
from .player import url
|
||||
from .musicInfo import getMusicInfo as _getInfo
|
||||
from .utils import formatSinger
|
||||
from .lyric import getLyric as _getLyric
|
||||
from common import utils
|
||||
|
||||
|
||||
@ -75,5 +76,6 @@ async def info(songid):
|
||||
'bpm': req['track_info']['bpm'],
|
||||
}
|
||||
|
||||
|
||||
async def lyric(songId):
|
||||
return await _getLyric(songId)
|
||||
|
||||
|
@ -1,3 +1,18 @@
|
||||
# ----------------------------------------
|
||||
# - mode: python -
|
||||
# - author: helloplhm-qwq -
|
||||
# - name: lyric.py -
|
||||
# - project: lx-music-api-server -
|
||||
# - license: MIT -
|
||||
# ----------------------------------------
|
||||
# This file is part of the "lx-music-api-server" project.
|
||||
|
||||
from .utils import signRequest
|
||||
from .musicInfo import getMusicInfo
|
||||
from common.exceptions import FailedException
|
||||
from common.utils import createBase64Decode
|
||||
from common import variable
|
||||
from common import qdes
|
||||
import re
|
||||
|
||||
class ParseTools:
|
||||
@ -130,26 +145,26 @@ class ParseTools:
|
||||
tlrc_lines = tlrc.split('\n')
|
||||
lrc_lines = lrc.split('\n')
|
||||
new_lrc = []
|
||||
time_tag_rxp = r'^\[[\d:.]+\]'
|
||||
|
||||
for line in tlrc_lines:
|
||||
result = self.rxps['lineTime2'].search(line)
|
||||
result = re.match(time_tag_rxp, line)
|
||||
if not result:
|
||||
continue
|
||||
words = re.sub(self.rxps['lineTime2'], '', line)
|
||||
words = re.sub(time_tag_rxp, '', line)
|
||||
if not words.strip():
|
||||
continue
|
||||
time = result.group(1)
|
||||
if '.' in time:
|
||||
time += ''.ljust(3 - len(time.split('.')[1]), '0')
|
||||
t1 = self.get_intv(time)
|
||||
tag = re.sub(r'\[\d+:\d+\.\d+\]', '', result.group(0))
|
||||
|
||||
while lrc_lines:
|
||||
lrc_line = lrc_lines.pop(0)
|
||||
lrc_line_result = self.rxps['lineTime2'].search(lrc_line)
|
||||
lrc_line_result = re.match(time_tag_rxp, lrc_line)
|
||||
if not lrc_line_result:
|
||||
continue
|
||||
t2 = self.get_intv(lrc_line_result.group(1))
|
||||
if abs(t1 - t2) < 100:
|
||||
new_lrc.append(re.sub(self.rxps['lineTime2'], lrc_line_result.group(0), line))
|
||||
if tag in lrc_line_result.group(0):
|
||||
new_lrc.append(re.sub(time_tag_rxp, lrc_line_result.group(0), line))
|
||||
break
|
||||
|
||||
return '\n'.join(new_lrc)
|
||||
|
||||
def parse(self, lrc, tlrc, rlrc):
|
||||
@ -169,3 +184,74 @@ class ParseTools:
|
||||
info['tlyric'] = self.fix_tlrc_time_tag(tlrc, info['lyric'])
|
||||
|
||||
return info
|
||||
|
||||
global_parser = ParseTools()
|
||||
|
||||
def parseLyric(l, t = '', r = ''):
|
||||
return global_parser.parse(l, t, r)
|
||||
|
||||
async def getLyric(songId):
|
||||
# mid and Numberid
|
||||
if (re.match("^[0-9]+$", str(songId))):
|
||||
songId = int(songId)
|
||||
else:
|
||||
try:
|
||||
getNumberIDRequest = await getMusicInfo(songId)
|
||||
except:
|
||||
raise FailedException('歌曲信息获取失败')
|
||||
songId = getNumberIDRequest['track_info']['id']
|
||||
req = await signRequest({
|
||||
"comm": {
|
||||
"ct": '19',
|
||||
"cv": '1859',
|
||||
"uin": '0',
|
||||
},
|
||||
"req": {
|
||||
"method": 'GetPlayLyricInfo',
|
||||
"module": 'music.musichallSong.PlayLyricInfo',
|
||||
"param": {
|
||||
"format": 'json',
|
||||
"crypt": 1 if variable.qdes_lib_loaded else 0,
|
||||
"ct": 19,
|
||||
"cv": 1873,
|
||||
"interval": 0,
|
||||
"lrc_t": 0,
|
||||
"qrc": 1 if variable.qdes_lib_loaded else 0,
|
||||
"qrc_t": 0,
|
||||
"roma": 1 if variable.qdes_lib_loaded else 0,
|
||||
"roma_t": 0,
|
||||
"songID": songId,
|
||||
"trans": 1,
|
||||
"trans_t": 0,
|
||||
"type": -1,
|
||||
}
|
||||
}
|
||||
})
|
||||
body = req.json()
|
||||
if ((body['code'] != 0) or (body['req']['code'] != 0)):
|
||||
raise FailedException('歌词获取失败')
|
||||
if (variable.qdes_lib_loaded):
|
||||
l = body['req']['data']['lyric']
|
||||
t = body['req']['data']['trans']
|
||||
r = body['req']['data']['roma']
|
||||
if (l.startswith('789C') and len(l) < 200): # unsupported format
|
||||
raise FailedException('纯音乐短歌词不受支持')
|
||||
dl = qdes.qdes_decrypt(l)
|
||||
if (t):
|
||||
dt = qdes.qdes_decrypt(t)
|
||||
else:
|
||||
dt = ''
|
||||
if (r):
|
||||
dr = qdes.qdes_decrypt(r)
|
||||
else:
|
||||
dr = ''
|
||||
return global_parser.parse(dl, dt, dr)
|
||||
else: # 不获取QRC时的歌词不被加密,解码base64,不进行parse,不支持逐字和罗马音,歌词数据没有毫秒
|
||||
l = body['req']['data']['lyric']
|
||||
t = body['req']['data']['trans']
|
||||
return {
|
||||
'lyric': createBase64Decode(l).decode('utf-8'),
|
||||
'tlyric': createBase64Decode(t).decode('utf-8'),
|
||||
'rlyric': '',
|
||||
'lxlyric': '',
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user