Initial commit
This commit is contained in:
152
qr_login.py
Normal file
152
qr_login.py
Normal file
@@ -0,0 +1,152 @@
|
||||
"""网易云音乐二维码登录模块 (已修改为API)
|
||||
|
||||
提供网易云音乐二维码登录功能,被 main.py 调用。
|
||||
- 二维码生成 (key 和 base64)
|
||||
- 登录状态检查 (返回 code 和 cookie)
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import base64
|
||||
import io
|
||||
import re # [!! 已添加 !!]
|
||||
from random import randrange # [!! 关键修复 !!] 导入 randrange
|
||||
from typing import Optional, Dict, Any, Tuple
|
||||
|
||||
try:
|
||||
# qrcode 库在 music_api.py 中被导入,这里我们也需要它
|
||||
import qrcode
|
||||
except ImportError:
|
||||
print("错误:缺少 'qrcode' 库。请运行 `pip install qrcode`")
|
||||
qrcode = None
|
||||
|
||||
try:
|
||||
# 我们需要从 music_api 借用很多底层工具
|
||||
from music_api import (
|
||||
QRLoginManager, APIException, HTTPClient,
|
||||
CryptoUtils, APIConstants
|
||||
)
|
||||
from cookie_manager import CookieManager, CookieException
|
||||
except ImportError as e:
|
||||
print(f"导入模块失败: {e}")
|
||||
print("请确保 music_api.py 和 cookie_manager.py 文件存在且可用")
|
||||
|
||||
|
||||
def api_generate_qr_key() -> Dict[str, Any]:
|
||||
"""
|
||||
[新API] 生成二维码Key和Base64图像
|
||||
|
||||
Returns:
|
||||
{'success': bool, 'qr_key': str, 'qr_img_b64': str, 'message': str}
|
||||
"""
|
||||
if not qrcode:
|
||||
return {'success': False, 'message': "qrcode 库未安装"}
|
||||
|
||||
try:
|
||||
qr_manager = QRLoginManager()
|
||||
unikey = qr_manager.generate_qr_key()
|
||||
|
||||
if not unikey:
|
||||
raise APIException("生成二维码key失败")
|
||||
|
||||
# 生成二维码数据
|
||||
qr_data = f'https://music.163.com/login?codekey={unikey}'
|
||||
|
||||
# 在内存中生成二维码图片
|
||||
qr = qrcode.QRCode()
|
||||
qr.add_data(qr_data)
|
||||
qr.make(fit=True)
|
||||
img = qr.make_image(fill_color="black", back_color="white")
|
||||
|
||||
# 将图片保存到 BytesIO
|
||||
buffered = io.BytesIO()
|
||||
img.save(buffered, format="PNG")
|
||||
|
||||
# 转换为 Base64 字符串
|
||||
img_str = base64.b64encode(buffered.getvalue()).decode("utf-8")
|
||||
qr_img_b64 = f"data:image/png;base64,{img_str}"
|
||||
|
||||
return {
|
||||
'success': True,
|
||||
'qr_key': unikey,
|
||||
'qr_img_b64': qr_img_b64
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"生成二维码失败: {e}")
|
||||
return {'success': False, 'message': str(e)}
|
||||
|
||||
|
||||
def api_check_qr_status(unikey: str) -> Dict[str, Any]:
|
||||
"""
|
||||
[新API] 检查二维码登录状态
|
||||
|
||||
此函数复制并*修复*了 music_api.py 中 check_qr_login 的逻辑,
|
||||
以便返回完整的 Cookie 字符串,而不只是 MUSIC_U。
|
||||
|
||||
Args:
|
||||
unikey: 二维码key
|
||||
|
||||
Returns:
|
||||
{'code': int, 'cookie': Optional[str], 'message': str}
|
||||
code: 800=过期, 801=等待, 802=已扫码, 803=成功
|
||||
"""
|
||||
try:
|
||||
http_client = HTTPClient()
|
||||
crypto_utils = CryptoUtils()
|
||||
config = APIConstants.DEFAULT_CONFIG.copy()
|
||||
|
||||
# [!! 关键修复 !!] 现在 randrange 已经被导入,这行代码可以正常工作
|
||||
config["requestId"] = str(randrange(20000000, 30000000))
|
||||
|
||||
payload = {
|
||||
'key': unikey,
|
||||
'type': 1,
|
||||
'header': json.dumps(config)
|
||||
}
|
||||
|
||||
params = crypto_utils.encrypt_params(APIConstants.QR_LOGIN_API, payload)
|
||||
|
||||
# 我们需要完整的 response 对象来获取 headers
|
||||
response = http_client.post_request_full(
|
||||
APIConstants.QR_LOGIN_API, params, {}
|
||||
)
|
||||
|
||||
result = json.loads(response.text)
|
||||
code = result.get('code', -1)
|
||||
|
||||
cookie_string = None
|
||||
|
||||
if code == 803:
|
||||
# 登录成功,提取cookie
|
||||
raw_cookies = response.headers.get('Set-Cookie', '')
|
||||
|
||||
# 使用 re.sub 来处理 'path=/,' 这种导致错误分割的情况
|
||||
raw_cookies = re.sub(r"path=/,", "path=/", raw_cookies)
|
||||
cookie_list = [c.strip() for c in raw_cookies.split(',') if c.strip()]
|
||||
|
||||
final_cookie_parts = []
|
||||
for item in cookie_list:
|
||||
if not item:
|
||||
continue
|
||||
part = item.split(';')[0]
|
||||
if part.strip():
|
||||
final_cookie_parts.append(part.strip())
|
||||
|
||||
cookie_string = '; '.join(final_cookie_parts)
|
||||
|
||||
if "MUSIC_U" not in cookie_string:
|
||||
return {'code': -1, 'cookie': None, 'message': '登录成功但未获取到 MUSIC_U'}
|
||||
|
||||
return {
|
||||
'code': code,
|
||||
'cookie': cookie_string,
|
||||
'message': result.get('message', '')
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
# 在 `api_check_qr_status` 捕获异常时,记录详细的 traceback
|
||||
logging.error(f"检查登录状态响应失败: {e}", exc_info=True)
|
||||
return {'code': -1, 'cookie': None, 'message': f"检查失败: {e}"}
|
||||
|
||||
# --- 删除了所有旧的交互式代码 ---
|
||||
Reference in New Issue
Block a user