mirror of
https://github.com/MeoProject/lx-music-api-server.git
synced 2025-05-23 19:17:41 +08:00
feat: 刷新失活 qqmusic_key
This commit is contained in:
parent
e13b7e3831
commit
77737d18e6
@ -133,6 +133,7 @@ module:
|
|||||||
user: # 用户数据,可以通过浏览器获取,需要vip账号来获取会员歌曲,如果没有请留为空值,qqmusic_key可以从Cookie中/客户端的请求体中(comm.authst)获取
|
user: # 用户数据,可以通过浏览器获取,需要vip账号来获取会员歌曲,如果没有请留为空值,qqmusic_key可以从Cookie中/客户端的请求体中(comm.authst)获取
|
||||||
qqmusic_key: ""
|
qqmusic_key: ""
|
||||||
uin: "" # key对应的QQ号
|
uin: "" # key对应的QQ号
|
||||||
|
refresh_key: "" # 刷新失活 qqmusic_key
|
||||||
refresh_login: # 刷新登录相关配置,enable是否启动,interval刷新间隔
|
refresh_login: # 刷新登录相关配置,enable是否启动,interval刷新间隔
|
||||||
enable: false
|
enable: false
|
||||||
interval: 86000
|
interval: 86000
|
||||||
@ -209,6 +210,7 @@ module:
|
|||||||
tx:
|
tx:
|
||||||
- qqmusic_key: ""
|
- qqmusic_key: ""
|
||||||
uin: ""
|
uin: ""
|
||||||
|
refresh_key: ""
|
||||||
refresh_login: # cookie池中对于此账号刷新登录的配置,账号间互不干扰
|
refresh_login: # cookie池中对于此账号刷新登录的配置,账号间互不干扰
|
||||||
enable: false
|
enable: false
|
||||||
interval: 86000
|
interval: 86000
|
||||||
|
@ -7,206 +7,152 @@
|
|||||||
# ----------------------------------------
|
# ----------------------------------------
|
||||||
# This file is part of the "lx-music-api-server" project.
|
# This file is part of the "lx-music-api-server" project.
|
||||||
|
|
||||||
from common import Httpx, variable
|
from common import Httpx, variable, scheduler, config, log
|
||||||
from common import scheduler
|
|
||||||
from common import config
|
|
||||||
from common import log
|
|
||||||
from .utils import sign
|
from .utils import sign
|
||||||
import ujson as json
|
import ujson as json
|
||||||
|
from typing import Dict, Any, Optional
|
||||||
|
|
||||||
logger = log.log('qqmusic_refresh_login')
|
logger = log.log("qqmusic_refresh_login")
|
||||||
|
|
||||||
|
|
||||||
async def refresh():
|
def _build_request_body(user_info: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
if (not config.read_config('module.tx.user.qqmusic_key')):
|
"""构建统一请求体结构"""
|
||||||
return
|
return {
|
||||||
if (not config.read_config('module.tx.user.refresh_login.enable')):
|
"comm": {
|
||||||
return
|
"fPersonality": "0",
|
||||||
print(config.read_config('module.tx.user.qqmusic_key'))
|
"tmeLoginType": "2"
|
||||||
if (config.read_config('module.tx.user.qqmusic_key').startswith('W_X')):
|
if user_info["qqmusic_key"].startswith("Q_H_L")
|
||||||
options = {
|
else "1",
|
||||||
'method': 'POST',
|
"qq": str(user_info["uin"]),
|
||||||
'body': json.dumps({
|
"authst": user_info["qqmusic_key"],
|
||||||
"comm": {
|
"ct": "11",
|
||||||
"fPersonality": "0",
|
"cv": "12080008",
|
||||||
"tmeLoginType": "1",
|
"v": "12080008",
|
||||||
"tmeLoginMethod": "1",
|
"tmeAppID": "qqmusic",
|
||||||
"qq": "",
|
},
|
||||||
"authst": "",
|
"req1": {
|
||||||
"ct": "11",
|
"module": "music.login.LoginServer",
|
||||||
"cv": "12080008",
|
"method": "Login",
|
||||||
"v": "12080008",
|
"param": {
|
||||||
"tmeAppID": "qqmusic"
|
"str_musicid": str(user_info["uin"]),
|
||||||
},
|
"musickey": user_info["qqmusic_key"],
|
||||||
"req1": {
|
"refresh_key": user_info.get("refresh_key", ""),
|
||||||
"module": "music.login.LoginServer",
|
},
|
||||||
"method": "Login",
|
},
|
||||||
"param": {
|
}
|
||||||
"code": "",
|
|
||||||
"openid": "",
|
|
||||||
"refresh_token": "",
|
async def _update_user_config(
|
||||||
"str_musicid": str(config.read_config('module.tx.user.uin')),
|
user_info: Dict[str, Any], new_data: Dict[str, Any]
|
||||||
"musickey": config.read_config('module.tx.user.qqmusic_key'),
|
) -> None:
|
||||||
"unionid": "",
|
"""统一更新用户配置"""
|
||||||
"refresh_key": "",
|
updates = {
|
||||||
"loginMode": 2
|
"uin": str(new_data.get("musicid", user_info["uin"])),
|
||||||
}
|
"qqmusic_key": new_data.get("musickey", user_info["qqmusic_key"]),
|
||||||
}
|
"refresh_key": new_data.get("refresh_key", user_info.get("refresh_key", "")),
|
||||||
})
|
}
|
||||||
}
|
|
||||||
signature = sign(options['body'])
|
if variable.use_cookie_pool:
|
||||||
req = await Httpx.AsyncRequest(f'https://u.y.qq.com/cgi-bin/musics.fcg?sign={signature}', options)
|
user_list = config.read_config("module.cookiepool.tx")
|
||||||
body = req.json()
|
target_user = next((u for u in user_list if u["uin"] == user_info["uin"]), None)
|
||||||
if (body['req1']['code'] != 0):
|
if target_user:
|
||||||
logger.warning('刷新登录失败, code: ' +
|
target_user.update(updates)
|
||||||
str(body['req1']['code']) + f'\n响应体: {body}')
|
config.write_config("module.cookiepool.tx", user_list)
|
||||||
return
|
|
||||||
else:
|
|
||||||
logger.info('刷新登录成功')
|
|
||||||
config.write_config('module.tx.user.uin',
|
|
||||||
str(body['req1']['data']['musicid']))
|
|
||||||
logger.info('已通过相应数据更新uin')
|
|
||||||
config.write_config('module.tx.user.qqmusic_key',
|
|
||||||
body['req1']['data']['musickey'])
|
|
||||||
logger.info('已通过相应数据更新qqmusic_key')
|
|
||||||
elif (config.read_config('module.tx.user.qqmusic_key').startswith('Q_H_L')):
|
|
||||||
options = {
|
|
||||||
'method': 'POST',
|
|
||||||
'body': json.dumps({
|
|
||||||
'req1': {
|
|
||||||
'module': 'QQConnectLogin.LoginServer',
|
|
||||||
'method': 'QQLogin',
|
|
||||||
'param': {
|
|
||||||
'expired_in': 7776000,
|
|
||||||
'musicid': int(config.read_config('module.tx.user.uin')),
|
|
||||||
'musickey': config.read_config('module.tx.user.qqmusic_key')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
signature = sign(options['body'])
|
|
||||||
req = await Httpx.AsyncRequest(f'https://u6.y.qq.com/cgi-bin/musics.fcg?sign={signature}', options)
|
|
||||||
body = req.json()
|
|
||||||
if (body['req1']['code'] != 0):
|
|
||||||
logger.warning('刷新登录失败, code: ' +
|
|
||||||
str(body['req1']['code']) + f'\n响应体: {body}')
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
logger.info('刷新登录成功')
|
|
||||||
config.write_config('module.tx.user.uin',
|
|
||||||
str(body['req1']['data']['musicid']))
|
|
||||||
logger.info('已通过相应数据更新uin')
|
|
||||||
config.write_config('module.tx.user.qqmusic_key',
|
|
||||||
body['req1']['data']['musickey'])
|
|
||||||
logger.info('已通过相应数据更新qqmusic_key')
|
|
||||||
else:
|
else:
|
||||||
logger.error('未知的qqmusic_key格式')
|
for key, value in updates.items():
|
||||||
|
config.write_config(f"module.tx.user.{key}", value)
|
||||||
|
|
||||||
if (not variable.use_cookie_pool):
|
|
||||||
# changed refresh login config path
|
|
||||||
txconfig = config.read_config('module.tx')
|
|
||||||
refresh_login_info = txconfig.get('refresh_login')
|
|
||||||
if (refresh_login_info):
|
|
||||||
txconfig['user']['refresh_login'] = refresh_login_info
|
|
||||||
txconfig.pop('refresh_login')
|
|
||||||
config.write_config('module.tx', txconfig)
|
|
||||||
|
|
||||||
if (config.read_config('module.tx.user.refresh_login.enable') and not variable.use_cookie_pool):
|
async def _process_refresh(user_info: Dict[str, Any]) -> Optional[bool]:
|
||||||
scheduler.append('qqmusic_refresh_login', refresh,
|
"""统一处理刷新逻辑"""
|
||||||
config.read_config('module.tx.user.refresh_login.interval'))
|
try:
|
||||||
|
# 构建请求参数
|
||||||
|
request_body = _build_request_body(user_info)
|
||||||
|
signature = sign(json.dumps(request_body))
|
||||||
|
|
||||||
async def refresh_login_for_pool(user_info):
|
# 发送请求
|
||||||
if (user_info['qqmusic_key'].startswith('W_X')):
|
response = await Httpx.AsyncRequest(
|
||||||
options = {
|
f"https://u.y.qq.com/cgi-bin/musics.fcg?sign={signature}",
|
||||||
'method': 'POST',
|
{
|
||||||
'body': json.dumps({
|
"method": "POST",
|
||||||
"comm": {
|
"body": json.dumps(request_body),
|
||||||
"fPersonality": "0",
|
"headers": {
|
||||||
"tmeLoginType": "1",
|
"Content-Type": "application/json",
|
||||||
"tmeLoginMethod": "1",
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
|
||||||
"qq": "",
|
|
||||||
"authst": "",
|
|
||||||
"ct": "11",
|
|
||||||
"cv": "12080008",
|
|
||||||
"v": "12080008",
|
|
||||||
"tmeAppID": "qqmusic"
|
|
||||||
},
|
},
|
||||||
"req1": {
|
},
|
||||||
"module": "music.login.LoginServer",
|
)
|
||||||
"method": "Login",
|
|
||||||
"param": {
|
response_data = response.json()
|
||||||
"code": "",
|
if response_data.get("req1", {}).get("code") != 0:
|
||||||
"openid": "",
|
|
||||||
"refresh_token": "",
|
|
||||||
"str_musicid": str(user_info['uin']),
|
|
||||||
"musickey": user_info['qqmusic_key'],
|
|
||||||
"unionid": "",
|
|
||||||
"refresh_key": "",
|
|
||||||
"loginMode": 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
signature = sign(options['body'])
|
|
||||||
req = await Httpx.AsyncRequest(f'https://u.y.qq.com/cgi-bin/musics.fcg?sign={signature}', options)
|
|
||||||
body = req.json()
|
|
||||||
if (body['req1']['code'] != 0):
|
|
||||||
logger.warning(f'为QQ音乐账号({user_info["uin"]})刷新登录失败, code: ' +
|
|
||||||
str(body['req1']['code']) + f'\n响应体: {body}')
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
logger.info(f'为QQ音乐账号(WeChat_{user_info["uin"]})刷新登录成功')
|
|
||||||
user_list = config.read_config('module.cookiepool.tx')
|
|
||||||
user_list[user_list.index(
|
|
||||||
user_info)]['qqmusic_key'] = body['req1']['data']['musickey']
|
|
||||||
user_list[user_list.index(
|
|
||||||
user_info)]['uin'] = str(body['req1']['data']['musicid'])
|
|
||||||
config.write_config('module.cookiepool.tx', user_list)
|
|
||||||
logger.info(f'为QQ音乐账号(WeChat_{user_info["uin"]})数据更新完毕')
|
|
||||||
return
|
|
||||||
elif (user_info['qqmusic_key'].startswith('Q_H_L')):
|
|
||||||
options = {
|
|
||||||
'method': 'POST',
|
|
||||||
'body': json.dumps({
|
|
||||||
'req1': {
|
|
||||||
'module': 'QQConnectLogin.LoginServer',
|
|
||||||
'method': 'QQLogin',
|
|
||||||
'param': {
|
|
||||||
'expired_in': 7776000,
|
|
||||||
'musicid': int(user_info['uin']),
|
|
||||||
'musickey': user_info['qqmusic_key']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
signature = sign(options['body'])
|
|
||||||
req = await Httpx.AsyncRequest(f'https://u6.y.qq.com/cgi-bin/musics.fcg?sign={signature}', options)
|
|
||||||
body = req.json()
|
|
||||||
if (body['req1']['code'] != 0):
|
|
||||||
logger.warning(
|
logger.warning(
|
||||||
f'为QQ音乐账号({user_info["uin"]})刷新登录失败, code: ' + str(body['req1']['code']) + f'\n响应体: {body}')
|
f"刷新失败 [账号: {user_info['uin']} 代码: {response_data['req1']['code']}]"
|
||||||
return
|
)
|
||||||
else:
|
return False
|
||||||
logger.info(f'为QQ音乐账号(QQ_{user_info["uin"]})刷新登录成功')
|
|
||||||
user_list = config.read_config('module.cookiepool.tx')
|
# 更新配置
|
||||||
user_list[user_list.index(
|
await _update_user_config(user_info, response_data["req1"]["data"])
|
||||||
user_info)]['qqmusic_key'] = body['req1']['data']['musickey']
|
logger.info(f"刷新成功 [账号: {user_info['uin']}]")
|
||||||
user_list[user_list.index(
|
return True
|
||||||
user_info)]['uin'] = str(body['req1']['data']['musicid'])
|
|
||||||
config.write_config('module.cookiepool.tx', user_list)
|
except json.JSONDecodeError:
|
||||||
logger.info(f'为QQ音乐账号(QQ_{user_info["uin"]})数据更新完毕')
|
logger.error(
|
||||||
return
|
"响应解析失败 [账号: %s] 原始响应: %s",
|
||||||
else:
|
user_info["uin"],
|
||||||
logger.warning(f'为QQ音乐账号({user_info["uin"]})刷新登录失败: 未知或不支持的key类型')
|
response.text[:100],
|
||||||
|
)
|
||||||
|
except KeyError as e:
|
||||||
|
logger.error(
|
||||||
|
"响应数据格式异常 [账号: %s] 缺失字段: %s", user_info["uin"], str(e)
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(
|
||||||
|
"刷新过程异常 [账号: %s] 错误信息: %s",
|
||||||
|
user_info["uin"],
|
||||||
|
str(e),
|
||||||
|
exc_info=True,
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
async def refresh() -> None:
|
||||||
|
"""主刷新入口(非Cookie池模式)"""
|
||||||
|
if not config.read_config("module.tx.user.refresh_login.enable"):
|
||||||
return
|
return
|
||||||
|
await _process_refresh(
|
||||||
def reg_refresh_login_pool_task():
|
{
|
||||||
user_info_pool = config.read_config('module.cookiepool.tx')
|
"uin": config.read_config("module.tx.user.uin"),
|
||||||
for user_info in user_info_pool:
|
"qqmusic_key": config.read_config("module.tx.user.qqmusic_key"),
|
||||||
if (user_info['refresh_login'].get('enable')):
|
"refresh_key": config.read_config("module.tx.user.refresh_key"),
|
||||||
scheduler.append(
|
}
|
||||||
f'qqmusic_refresh_login_pooled_{user_info["uin"]}', refresh_login_for_pool, user_info['refresh_login']['interval'], args = {'user_info': user_info})
|
)
|
||||||
|
|
||||||
|
|
||||||
if (variable.use_cookie_pool):
|
async def refresh_login_for_pool(user_info: Dict[str, Any]) -> None:
|
||||||
reg_refresh_login_pool_task()
|
"""Cookie池刷新入口"""
|
||||||
|
if user_info.get("refresh_login", {}).get("enable", False):
|
||||||
|
await _process_refresh(user_info)
|
||||||
|
|
||||||
|
|
||||||
|
def _setup_scheduler() -> None:
|
||||||
|
"""初始化定时任务"""
|
||||||
|
if variable.use_cookie_pool:
|
||||||
|
user_list = config.read_config("module.cookiepool.tx")
|
||||||
|
for user in user_list:
|
||||||
|
if user.get("refresh_login", {}).get("enable", False):
|
||||||
|
scheduler.append(
|
||||||
|
f"qq_refresh_{user['uin']}",
|
||||||
|
refresh_login_for_pool,
|
||||||
|
user["refresh_login"].get("interval", 3600),
|
||||||
|
args={"user_info": user},
|
||||||
|
)
|
||||||
|
elif config.read_config("module.tx.user.refresh_login.enable"):
|
||||||
|
scheduler.append(
|
||||||
|
"qqmusic_main_refresh",
|
||||||
|
refresh,
|
||||||
|
config.read_config("module.tx.user.refresh_login.interval"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# 初始化定时任务
|
||||||
|
_setup_scheduler()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user