From ffd4f63bc115be4f6b9800e289faabb54ce71dfd Mon Sep 17 00:00:00 2001 From: fllesser Date: Fri, 20 Mar 2026 00:11:41 +0800 Subject: [PATCH 1/2] refactor: rename downloader instances for consistency across modules --- src/nonebot_plugin_parser/download/__init__.py | 6 +++--- src/nonebot_plugin_parser/matchers/__init__.py | 6 +++--- src/nonebot_plugin_parser/parsers/__init__.py | 4 ++-- src/nonebot_plugin_parser/parsers/base.py | 16 ++++++++-------- src/nonebot_plugin_parser/parsers/tiktok.py | 6 +++--- .../parsers/youtube/__init__.py | 6 +++--- tests/parsers/test_bilibili_need_ck.py | 4 ++-- tests/parsers/test_ytdlp.py | 12 ++++++------ 8 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/nonebot_plugin_parser/download/__init__.py b/src/nonebot_plugin_parser/download/__init__.py index 24c22a82..4b589c88 100644 --- a/src/nonebot_plugin_parser/download/__init__.py +++ b/src/nonebot_plugin_parser/download/__init__.py @@ -224,13 +224,13 @@ async def _get_m3u8_slices(self, m3u8_url: str): return slices -DOWNLOADER: StreamDownloader = StreamDownloader() +downloader: StreamDownloader = StreamDownloader() try: import yt_dlp as yt_dlp from .ytdlp import YtdlpDownloader - YTDLP_DOWNLOADER = YtdlpDownloader() + yt_dlp_downloader = YtdlpDownloader() except ImportError: - YTDLP_DOWNLOADER = None + yt_dlp_downloader = None diff --git a/src/nonebot_plugin_parser/matchers/__init__.py b/src/nonebot_plugin_parser/matchers/__init__.py index 966bc678..4b666fee 100644 --- a/src/nonebot_plugin_parser/matchers/__init__.py +++ b/src/nonebot_plugin_parser/matchers/__init__.py @@ -112,9 +112,9 @@ async def _(message: Message = CommandArg()): await UniMessage(UniHelper.file_seg(audio_path)).send() -from ..download import YTDLP_DOWNLOADER +from ..download import yt_dlp_downloader -if YTDLP_DOWNLOADER is not None: +if yt_dlp_downloader is not None: from ..parsers import YouTubeParser @on_command("ym", priority=3, block=True).handle() @@ -128,7 +128,7 @@ async def _(message: Message = CommandArg()): url = matched.group(0) - audio_path = await YTDLP_DOWNLOADER.download_audio(url) + audio_path = await yt_dlp_downloader.download_audio(url) await UniMessage(UniHelper.record_seg(audio_path)).send() if pconfig.need_upload: diff --git a/src/nonebot_plugin_parser/parsers/__init__.py b/src/nonebot_plugin_parser/parsers/__init__.py index 01b1a345..4f1f1e2e 100644 --- a/src/nonebot_plugin_parser/parsers/__init__.py +++ b/src/nonebot_plugin_parser/parsers/__init__.py @@ -7,10 +7,10 @@ from .twitter import TwitterParser as TwitterParser from .bilibili import BilibiliParser as BilibiliParser from .kuaishou import KuaiShouParser as KuaiShouParser -from ..download import YTDLP_DOWNLOADER +from ..download import yt_dlp_downloader as yt_dlp_downloader from .xiaohongshu import XiaoHongShuParser as XiaoHongShuParser -if YTDLP_DOWNLOADER is not None: +if yt_dlp_downloader is not None: from .tiktok import TikTokParser as TikTokParser from .youtube import YouTubeParser as YouTubeParser diff --git a/src/nonebot_plugin_parser/parsers/base.py b/src/nonebot_plugin_parser/parsers/base.py index a7c36a2f..dcb3b502 100644 --- a/src/nonebot_plugin_parser/parsers/base.py +++ b/src/nonebot_plugin_parser/parsers/base.py @@ -9,7 +9,7 @@ from .data import Platform, ParseResult, ImageContent, ParseResultKwargs from .task import PathTask from ..config import pconfig as pconfig -from ..download import DOWNLOADER +from ..download import downloader from ..constants import IOS_HEADER, COMMON_HEADER, ANDROID_HEADER, COMMON_TIMEOUT from ..constants import DOWNLOAD_TIMEOUT as DOWNLOAD_TIMEOUT from ..constants import PlatformEnum as PlatformEnum @@ -170,7 +170,7 @@ def create_author( author = Author(name=name, description=description) if avatar_url: - author.avatar = PathTask(DOWNLOADER.download_img(avatar_url, ext_headers=self.headers)) + author.avatar = PathTask(downloader.download_img(avatar_url, ext_headers=self.headers)) return author @@ -185,12 +185,12 @@ def create_video( from ..utils import extract_video_cover if isinstance(url_or_task, str): - path_task = DOWNLOADER.download_video(url_or_task, ext_headers=self.headers) + path_task = downloader.download_video(url_or_task, ext_headers=self.headers) elif isinstance(url_or_task, Task): path_task = url_or_task if cover_url: - cover_task = DOWNLOADER.download_img(cover_url, ext_headers=self.headers) + cover_task = downloader.download_img(cover_url, ext_headers=self.headers) else: # 如果没有封面 URL,尝试从视频中提取封面 async def extract_cover(): @@ -212,7 +212,7 @@ def create_images( """创建图片内容列表""" contents: list[ImageContent] = [] for url in image_urls: - task = DOWNLOADER.download_img(url, ext_headers=self.headers) + task = downloader.download_img(url, ext_headers=self.headers) contents.append(ImageContent(PathTask(task))) return contents @@ -223,7 +223,7 @@ def create_image( ): """创建单个图片内容""" if isinstance(url_or_task, str): - path_task = DOWNLOADER.download_img(url_or_task, ext_headers=self.headers) + path_task = downloader.download_img(url_or_task, ext_headers=self.headers) elif isinstance(url_or_task, Task): path_task = url_or_task @@ -238,7 +238,7 @@ def create_audio( from .data import AudioContent if isinstance(url_or_task, str): - path_task = DOWNLOADER.download_audio(url_or_task, ext_headers=self.headers) + path_task = downloader.download_audio(url_or_task, ext_headers=self.headers) elif isinstance(url_or_task, Task): path_task = url_or_task @@ -246,4 +246,4 @@ def create_audio( @property def downloader(self): - return DOWNLOADER + return downloader diff --git a/src/nonebot_plugin_parser/parsers/tiktok.py b/src/nonebot_plugin_parser/parsers/tiktok.py index 779c37db..f0e3a3eb 100644 --- a/src/nonebot_plugin_parser/parsers/tiktok.py +++ b/src/nonebot_plugin_parser/parsers/tiktok.py @@ -3,7 +3,7 @@ from .base import BaseParser, PlatformEnum, handle from .data import Author, Platform -from ..download import YTDLP_DOWNLOADER +from ..download import yt_dlp_downloader class TikTokParser(BaseParser): @@ -18,10 +18,10 @@ async def _parse(self, searched: re.Match[str]): url = await self.get_redirect_url(url) # 获取视频信息 - video_info = await YTDLP_DOWNLOADER.extract_video_info(url) + video_info = await yt_dlp_downloader.extract_video_info(url) # 下载封面和视频 - video = YTDLP_DOWNLOADER.download_video(url) + video = yt_dlp_downloader.download_video(url) video_content = self.create_video( video, video_info.thumbnail, diff --git a/src/nonebot_plugin_parser/parsers/youtube/__init__.py b/src/nonebot_plugin_parser/parsers/youtube/__init__.py index f42a5c61..20704234 100644 --- a/src/nonebot_plugin_parser/parsers/youtube/__init__.py +++ b/src/nonebot_plugin_parser/parsers/youtube/__init__.py @@ -5,7 +5,7 @@ from ..base import Platform, BaseParser, PlatformEnum, handle, pconfig from ..cookie import save_cookies_with_netscape -from ...download import YTDLP_DOWNLOADER +from ...download import yt_dlp_downloader class YouTubeParser(BaseParser): @@ -28,7 +28,7 @@ async def _parse_video(self, searched: re.Match[str]): return await self.parse_video(url) async def parse_video(self, url: str): - video_info = await YTDLP_DOWNLOADER.extract_video_info(url, self.cookies_file) + video_info = await yt_dlp_downloader.extract_video_info(url, self.cookies_file) author = await self._fetch_author_info(video_info.channel_id) result = self.result( @@ -38,7 +38,7 @@ async def parse_video(self, url: str): ) if video_info.duration <= pconfig.duration_maximum: - video = YTDLP_DOWNLOADER.download_video(url, self.cookies_file) + video = yt_dlp_downloader.download_video(url, self.cookies_file) result.video = self.create_video( video, video_info.thumbnail, diff --git a/tests/parsers/test_bilibili_need_ck.py b/tests/parsers/test_bilibili_need_ck.py index 004a990b..14d20a34 100644 --- a/tests/parsers/test_bilibili_need_ck.py +++ b/tests/parsers/test_bilibili_need_ck.py @@ -48,7 +48,7 @@ async def test_video(): @pytest.mark.asyncio async def test_max_size_video(): from nonebot_plugin_parser.parsers import BilibiliParser - from nonebot_plugin_parser.download import DOWNLOADER + from nonebot_plugin_parser.download import downloader from nonebot_plugin_parser.exception import IgnoreException parser = BilibiliParser() @@ -61,7 +61,7 @@ async def test_max_size_video(): assert audio_url is not None try: - await DOWNLOADER.download_audio(audio_url, ext_headers=parser.headers) + await downloader.download_audio(audio_url, ext_headers=parser.headers) except IgnoreException: pass diff --git a/tests/parsers/test_ytdlp.py b/tests/parsers/test_ytdlp.py index 8eab8a11..ace53d28 100644 --- a/tests/parsers/test_ytdlp.py +++ b/tests/parsers/test_ytdlp.py @@ -5,27 +5,27 @@ @pytest.mark.asyncio async def test_extract_video_info(): - from nonebot_plugin_parser.download import YTDLP_DOWNLOADER + from nonebot_plugin_parser.download import yt_dlp_downloader - await YTDLP_DOWNLOADER.extract_video_info(TIKTOK_URL) + await yt_dlp_downloader.extract_video_info(TIKTOK_URL) @pytest.mark.asyncio async def test_download_video(): - from nonebot_plugin_parser.download import YTDLP_DOWNLOADER + from nonebot_plugin_parser.download import yt_dlp_downloader - video_path = await YTDLP_DOWNLOADER.download_video(TIKTOK_URL) + video_path = await yt_dlp_downloader.download_video(TIKTOK_URL) assert video_path.exists() @pytest.mark.asyncio async def test_download_audio(): - from nonebot_plugin_parser.download import YTDLP_DOWNLOADER + from nonebot_plugin_parser.download import yt_dlp_downloader url = "https://www.tiktok.com/@fdznews/video/7575810064078884116?is_from_webapp=1&sender_device=pc" - audio_path = await YTDLP_DOWNLOADER.download_audio(url) + audio_path = await yt_dlp_downloader.download_audio(url) assert audio_path.exists() From 398cb65d9a5cd974422037801c796f76b9fe9ac9 Mon Sep 17 00:00:00 2001 From: fllesser Date: Fri, 20 Mar 2026 10:48:42 +0800 Subject: [PATCH 2/2] refactor: add module availability check and enhance downloader initialization --- src/nonebot_plugin_parser/download/__init__.py | 9 +++++---- src/nonebot_plugin_parser/renders/__init__.py | 11 ++--------- src/nonebot_plugin_parser/utils.py | 7 +++++++ 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/nonebot_plugin_parser/download/__init__.py b/src/nonebot_plugin_parser/download/__init__.py index 4b589c88..cadf70ed 100644 --- a/src/nonebot_plugin_parser/download/__init__.py +++ b/src/nonebot_plugin_parser/download/__init__.py @@ -225,12 +225,13 @@ async def _get_m3u8_slices(self, m3u8_url: str): downloader: StreamDownloader = StreamDownloader() +"""全局下载器实例,提供下载功能""" +yt_dlp_downloader = None +"""yt-dlp 下载器实例,提供下载视频功能,若 yt-dlp 未安装则为 None""" -try: - import yt_dlp as yt_dlp +from ..utils import is_module_available +if is_module_available("yt_dlp"): from .ytdlp import YtdlpDownloader yt_dlp_downloader = YtdlpDownloader() -except ImportError: - yt_dlp_downloader = None diff --git a/src/nonebot_plugin_parser/renders/__init__.py b/src/nonebot_plugin_parser/renders/__init__.py index 1dd404dd..74bd7733 100644 --- a/src/nonebot_plugin_parser/renders/__init__.py +++ b/src/nonebot_plugin_parser/renders/__init__.py @@ -6,19 +6,12 @@ from .common import CommonRenderer from .default import DefaultRenderer +RENDERER: type[BaseRenderer] | None = None -def is_module_available(module_name: str) -> bool: - """检查模块是否可用""" - import importlib.util - - return importlib.util.find_spec(module_name) is not None - - +from ..utils import is_module_available from ..config import pconfig from ..constants import RenderType -RENDERER: type[BaseRenderer] | None = None - match pconfig.render_type: case RenderType.common: RENDERER = CommonRenderer diff --git a/src/nonebot_plugin_parser/utils.py b/src/nonebot_plugin_parser/utils.py index 3cf7d1bb..e9e224da 100644 --- a/src/nonebot_plugin_parser/utils.py +++ b/src/nonebot_plugin_parser/utils.py @@ -195,3 +195,10 @@ def write_json_to_data(data: dict[str, Any] | str, file_name: str): with open(path, "w") as f: json.dump(data, f, ensure_ascii=False, indent=4) logger.success(f"数据写入 {path} 成功") + + +def is_module_available(module_name: str) -> bool: + """检查模块是否可用""" + import importlib.util + + return importlib.util.find_spec(module_name) is not None