refactor(async): music_api 异步化 + 全项目 lint 收敛

music_api.py:
- 22 个方法改 async(HTTPClient.post_request*/NeteaseAPI.*/QRLoginManager.*),
  33 处 requests.* → await core.aiohttp_compat.*
- 16 个模块级同步包装改走 run_sync 桥(main.py 等 99 个同步调用点零改动)
- async 内 time.sleep → asyncio.sleep(修 7 处阻塞事件循环)

musica_downloader/qr_login:
- 单曲三请求 ThreadPoolExecutor → asyncio.gather(语义等价,失败处理保留)
- interactive_login 改 async,CLI 入口用 asyncio.run

真实缺陷修复(lint 发现):
- podcast_api._to_int 未定义(2 处调用会 NameError)→ 补安全转换 helper
- main.py VALID_QUALITY_LEVELS 重复定义(覆盖 core.config 导入值)→ 移除
- 移除死代码变量(salt×2/device_id×3/file_path/zipped_files)

lint 收敛:524→0(ruff All checks passed);pyright 0 errors。
pyproject 记录「有意保留」项(B904/RUF012/S305 上游协议等)及理由。

验证:冒烟 10/10;pytest 16/16;真实上游 get_toplist 异步直调与桥接均得 63 条。
This commit is contained in:
snowy-yang committed 2026-09-26 00:32:45 +08:00
1 parent 4962542e78
commit ce7ea66d6a
8 files changed
+520 -490

No files matched your search

+13 -13
View File
@@ -7,17 +7,17 @@
- 用户友好的交互界面
"""
import asyncio
import datetime
import logging
import os
import shutil
import sys
import time
import shutil
import logging
import datetime
from pathlib import Path
from typing import Optional, Tuple
try:
from music_api import QRLoginManager, APIException, load_cookies
from music_api import APIException, QRLoginManager, load_cookies
except ImportError as e:
print(f"导入模块失败: {e}")
print("请确保 music_api.py 文件存在且可用")
@@ -87,7 +87,7 @@ class QRLoginClient:
shutil.copy2(backup, self.cookie_file)
return False
def _backup_cookie(self, suffix: str = "backup") -> Optional[Path]:
def _backup_cookie(self, suffix: str = "backup") -> Path | None:
if not self.cookie_file.exists():
return None
ts = datetime.datetime.now().strftime("%Y%m%d_%H%M%S_%f") # 第 91 条(P3):微秒防同秒覆盖
@@ -172,7 +172,7 @@ class QRLoginClient:
self.logger.error(f"检查登录状态失败: {e}")
return False
def interactive_login(self) -> Tuple[bool, Optional[str]]:
async def interactive_login(self) -> tuple[bool, str | None]:
"""交互式二维码登录"""
try:
print("\n=== 网易云音乐二维码登录 ===")
@@ -186,7 +186,7 @@ class QRLoginClient:
print("\n开始二维码登录流程...")
print("正在生成二维码...")
# create_qr_login 返回 unikey 字符串(失败为 None),并已在控制台打印二维码
qr_key = self.qr_manager.create_qr_login()
qr_key = await self.qr_manager.create_qr_login()
if not qr_key:
error_msg = "生成二维码失败"
@@ -203,7 +203,7 @@ class QRLoginClient:
while attempt < max_attempts:
try:
# check_qr_login 返回 (状态码, cookie字典):801等待/802已扫/803成功/800过期
code, cookie_dict = self.qr_manager.check_qr_login(qr_key)
code, cookie_dict = await self.qr_manager.check_qr_login(qr_key)
if code == 803:
musu_u = cookie_dict.get('MUSIC_U', '')
@@ -233,7 +233,7 @@ class QRLoginClient:
if code == 801 and attempt % 10 == 0:
print(f"等待扫码中... ({attempt + 1}/{max_attempts})")
time.sleep(5)
await asyncio.sleep(5)
attempt += 1
except KeyboardInterrupt:
@@ -241,7 +241,7 @@ class QRLoginClient:
return False, "用户取消登录"
except Exception as e:
self.logger.error(f"检查登录状态时发生错误: {e}")
time.sleep(5)
await asyncio.sleep(5)
attempt += 1
print("\n[超时] 登录超时,请重新尝试")
@@ -325,7 +325,7 @@ def main():
command = sys.argv[1].lower()
if command == 'login':
success, error = client.interactive_login()
success, error = asyncio.run(client.interactive_login())
if success:
print("\n登录完成!")
client.show_login_info()
@@ -368,7 +368,7 @@ def main():
choice = input("\n请选择操作 (1-4): ").strip()
if choice == '1':
success, error = client.interactive_login()
success, error = asyncio.run(client.interactive_login())
if success:
print("\n登录成功!")
client.show_login_info()