# -*- coding: utf-8 -*- """服务类与进程级服务实例。 """ import logging from pathlib import Path from core.config import ( Settings, ) from music_api import ( NeteaseAPI, load_cookies, ) from music_downloader import ( MusicDownloader, ) # 向后兼容别名:既有代码/文档沿用 APIConfig 名称 APIConfig = Settings class MusicAPIService: """音乐API服务类""" def __init__(self, config: APIConfig): self.config = config self.logger = self._setup_logger() self.netease_api = NeteaseAPI() # 下载目录传给下载器——原实现下载器固定 downloads, # 服务配置指向自定义目录时音乐落盘位置与配置分离 self.downloader = MusicDownloader(download_dir=config.downloads_dir) # 创建下载目录 self.downloads_path = Path(config.downloads_dir) self.downloads_path.mkdir(exist_ok=True) self.logger.info(f"音乐API服务初始化完成,下载目录: {self.downloads_path.absolute()}") def _setup_logger(self) -> logging.Logger: """设置日志记录器""" logger = logging.getLogger('music_api') logger.setLevel(getattr(logging, self.config.log_level.upper())) if not logger.handlers: # 控制台处理器 console_handler = logging.StreamHandler() console_formatter = logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) console_handler.setFormatter(console_formatter) logger.addHandler(console_handler) # 文件处理器(轮转:单文件 5MB,保留 3 份,防止长期运行无限膨胀) try: from logging.handlers import RotatingFileHandler file_handler = RotatingFileHandler('music_api.log', maxBytes=5 * 1024 * 1024, backupCount=3, encoding='utf-8') file_formatter = logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(funcName)s:%(lineno)d - %(message)s' ) file_handler.setFormatter(file_formatter) logger.addHandler(file_handler) except Exception as e: logger.warning(f"无法创建日志文件: {e}") return logger def _get_cookies(self) -> dict[str, str]: """直接从 cookie.txt 读取并解析为 dict""" try: cookies = load_cookies() if not cookies: self.logger.warning("cookie.txt 为空或不存在,部分接口(VIP 音质)将不可用") return cookies except Exception as e: self.logger.error(f"读取 cookie.txt 异常: {e}") return {} def _format_file_size(self, size_bytes) -> str: """格式化文件大小(上游 size 可能为 null/垃圾值:容错返回"大小未知"而非 500)""" try: size = float(size_bytes or 0) except (TypeError, ValueError): return "大小未知" if size <= 0: return "0B" units = ["B", "KB", "MB", "GB", "TB"] unit_index = 0 while size >= 1024.0 and unit_index < len(units) - 1: size /= 1024.0 unit_index += 1 return f"{size:.2f}{units[unit_index]}" def _get_quality_display_name(self, quality: str) -> str: """获取音质显示名称""" quality_names = { 'standard': "标准音质", 'exhigh': "极高音质", 'lossless': "无损音质", 'hires': "Hi-Res音质", 'sky': "沉浸环绕声", 'jyeffect': "高清环绕声", 'jymaster': "超清母带", 'dolby': "杜比全景声" } return quality_names.get(quality, f"未知音质({quality})") # 服务实例(框架无关) config = APIConfig() api_service = MusicAPIService(config)