mirror of
https://github.com/MeoProject/lx-music-api-server.git
synced 2025-05-23 19:17:41 +08:00
123 lines
4.5 KiB
Python
123 lines
4.5 KiB
Python
# ----------------------------------------
|
|
# - 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 common.exceptions import FailedException
|
|
from common.utils import encodeURI, createBase64Decode
|
|
from .musicInfo import getMusicInfo
|
|
from common import Httpx
|
|
import ujson as json
|
|
import zlib
|
|
import re
|
|
|
|
class ParseTools:
|
|
def __init__(self):
|
|
self.head_exp = r'^.*\[id:\$\w+\]\n'
|
|
|
|
def parse(self, string):
|
|
string = string.replace('\r', '')
|
|
if re.match(self.head_exp, string):
|
|
string = re.sub(self.head_exp, '', string)
|
|
trans = re.search(r'\[language:([\w=\\/+]+)\]', string)
|
|
lyric = None
|
|
rlyric = None
|
|
tlyric = None
|
|
if trans:
|
|
string = re.sub(r'\[language:[\w=\\/+]+\]\n', '', string)
|
|
decoded_trans = createBase64Decode(trans.group(1)).decode('utf-8')
|
|
trans_json = json.loads(decoded_trans)
|
|
for item in trans_json['content']:
|
|
if item['type'] == 0:
|
|
rlyric = item['lyricContent']
|
|
elif item['type'] == 1:
|
|
tlyric = item['lyricContent']
|
|
self.i = 0
|
|
lxlyric = re.sub(r'\[((\d+),\d+)\].*', lambda x: self.process_lyric_match(x, rlyric, tlyric, self.i), string)
|
|
rlyric = '\n'.join(rlyric) if rlyric else ''
|
|
tlyric = '\n'.join(tlyric) if tlyric else ''
|
|
lxlyric = re.sub(r'<(\d+,\d+),\d+>', r'<\1>', lxlyric)
|
|
lyric = re.sub(r'<\d+,\d+>', '', lxlyric)
|
|
return {
|
|
'lyric': lyric,
|
|
'tlyric': tlyric,
|
|
'rlyric': rlyric,
|
|
'lxlyric': lxlyric
|
|
}
|
|
|
|
def process_lyric_match(self, match, rlyric, tlyric, i):
|
|
result = re.match(r'\[((\d+),\d+)\].*', match.group(0))
|
|
time = int(result.group(2))
|
|
ms = time % 1000
|
|
time /= 1000
|
|
m = str(int(time / 60)).zfill(2)
|
|
time %= 60
|
|
s = str(int(time)).zfill(2)
|
|
time_string = f'{m}:{s}.{ms}'
|
|
transformed_t = ''
|
|
if (tlyric):
|
|
for t in tlyric[i]:
|
|
transformed_t += t
|
|
tlyric[i] = transformed_t
|
|
if (rlyric):
|
|
nr = []
|
|
for r in rlyric[i]:
|
|
nr.append(r)
|
|
_tnr = ''.join(nr)
|
|
if (' ' in _tnr):
|
|
rlyric[i] = _tnr
|
|
else:
|
|
nr = []
|
|
for r in rlyric[i]:
|
|
nr.append(r.strip())
|
|
rlyric[i] = ' '.join(nr)
|
|
if rlyric:
|
|
rlyric[i] = f'[{time_string}]{rlyric[i] if rlyric[i] else ""}'.replace(' ', ' ')
|
|
if tlyric:
|
|
tlyric[i] = f'[{time_string}]{tlyric[i] if tlyric[i] else ""}'
|
|
self.i += 1
|
|
return re.sub(result.group(1), time_string, match.group(0))
|
|
|
|
global_parser = ParseTools()
|
|
|
|
def krcDecode(a:bytes):
|
|
encrypt_key = (64, 71, 97, 119, 94, 50, 116, 71, 81, 54, 49, 45, 206, 210, 110, 105)
|
|
content = a[4:] # krc1
|
|
compress_content = bytes(content[i] ^ encrypt_key[i % len(encrypt_key)] for i in range(len(content)))
|
|
text_bytes = zlib.decompress(bytes(compress_content))
|
|
text = text_bytes.decode("utf-8")
|
|
return text
|
|
|
|
async def lyricSearchByHash(hash_):
|
|
musicInfo = await getMusicInfo(hash_)
|
|
if (not musicInfo):
|
|
raise FailedException('歌曲信息获取失败')
|
|
hash_new = musicInfo['audio_info']['hash']
|
|
name = musicInfo['songname']
|
|
timelength = int(musicInfo['audio_info']['timelength']) // 1000
|
|
req = await Httpx.AsyncRequest(encodeURI(f'https://lyrics.kugou.com/search?ver=1&man=yes&client=pc&keyword=' +
|
|
name + '&hash=' + hash_new + '&timelength=' + str(timelength)), {
|
|
'method': 'GET',
|
|
})
|
|
body = req.json()
|
|
if (body['status'] != 200):
|
|
raise FailedException('歌词获取失败')
|
|
if (not body['candidates']):
|
|
raise FailedException('歌词获取失败: 当前歌曲无歌词')
|
|
return body['candidates']
|
|
|
|
async def getLyric(lyric_id, accesskey):
|
|
req = await Httpx.AsyncRequest(f'https://lyrics.kugou.com/download?ver=1&client=pc&id={lyric_id}&accesskey={accesskey}', {
|
|
'method': 'GET',
|
|
})
|
|
body = req.json()
|
|
if (body['status'] != 200 or body['error_code'] != 0 or (not body['content'])):
|
|
raise FailedException('歌词获取失败')
|
|
content = createBase64Decode(body['content'])
|
|
content = krcDecode(content)
|
|
|
|
return global_parser.parse(content) |