diff --git a/app/chain/__init__.py b/app/chain/__init__.py index 45eeaf2a..092b5cc0 100644 --- a/app/chain/__init__.py +++ b/app/chain/__init__.py @@ -234,27 +234,27 @@ class ChainBase(metaclass=ABCMeta): return self.run_module("filter_torrents", rule_string=rule_string, torrent_list=torrent_list, season_episodes=season_episodes) - def download(self, torrent_path: Path, download_dir: Path, cookie: str, + def download(self, content: Union[Path, str], download_dir: Path, cookie: str, episodes: Set[int] = None, category: str = None ) -> Optional[Tuple[Optional[str], str]]: """ 根据种子文件,选择并添加下载任务 - :param torrent_path: 种子文件地址 + :param content: 种子文件地址或者磁力链接 :param download_dir: 下载目录 :param cookie: cookie :param episodes: 需要下载的集数 :param category: 种子分类 :return: 种子Hash,错误信息 """ - return self.run_module("download", torrent_path=torrent_path, download_dir=download_dir, + return self.run_module("download", content=content, download_dir=download_dir, cookie=cookie, episodes=episodes, category=category) - def download_added(self, context: Context, torrent_path: Path, download_dir: Path) -> None: + def download_added(self, context: Context, download_dir: Path, torrent_path: Path = None) -> None: """ 添加下载任务成功后,从站点下载字幕,保存到下载目录 :param context: 上下文,包括识别信息、媒体信息、种子信息 - :param torrent_path: 种子文件地址 :param download_dir: 下载目录 + :param torrent_path: 种子文件地址 :return: None,该方法可被多个模块同时处理 """ return self.run_module("download_added", context=context, torrent_path=torrent_path, diff --git a/app/chain/download.py b/app/chain/download.py index d1162768..a6009eee 100644 --- a/app/chain/download.py +++ b/app/chain/download.py @@ -70,16 +70,22 @@ class DownloadChain(ChainBase): def download_torrent(self, torrent: TorrentInfo, channel: MessageChannel = None, - userid: Union[str, int] = None) -> Tuple[Optional[Path], str, list]: + userid: Union[str, int] = None + ) -> Tuple[Optional[Union[Path, str]], str, list]: """ - 下载种子文件 + 下载种子文件,如果是磁力链,会返回磁力链接本身 :return: 种子路径,种子目录名,种子文件清单 """ - torrent_file, _, download_folder, files, error_msg = self.torrent.download_torrent( + torrent_file, content, download_folder, files, error_msg = self.torrent.download_torrent( url=torrent.enclosure, cookie=torrent.site_cookie, ua=torrent.site_ua, proxy=torrent.site_proxy) + + if isinstance(content, str): + # 磁力链 + return content, "", [] + if not torrent_file: logger.error(f"下载种子文件失败:{torrent.title} - {torrent.enclosure}") self.post_message(Notification( @@ -89,6 +95,8 @@ class DownloadChain(ChainBase): text=f"错误信息:{error_msg}\n站点:{torrent.site_name}", userid=userid)) return None, "", [] + + # 返回 种子文件路径,种子目录名,种子文件清单 return torrent_file, download_folder, files def download_single(self, context: Context, torrent_file: Path = None, @@ -98,19 +106,27 @@ class DownloadChain(ChainBase): userid: Union[str, int] = None) -> Optional[str]: """ 下载及发送通知 + :param context: 资源上下文 + :param torrent_file: 种子文件路径 + :param episodes: 需要下载的集数 + :param channel: 通知渠道 + :param save_path: 保存路径 + :param userid: 用户ID """ _torrent = context.torrent_info _media = context.media_info _meta = context.meta_info _folder_name = "" if not torrent_file: - # 下载种子文件 - torrent_file, _folder_name, _file_list = self.download_torrent(_torrent, userid=userid) - if not torrent_file: + # 下载种子文件,得到的可能是文件也可能是磁力链 + content, _folder_name, _file_list = self.download_torrent(_torrent, userid=userid) + if not content: return else: + content = torrent_file # 获取种子文件的文件夹名和文件清单 _folder_name, _file_list = self.torrent.get_torrent_info(torrent_file) + # 下载目录 if not save_path: if settings.DOWNLOAD_CATEGORY and _media and _media.category: @@ -149,7 +165,7 @@ class DownloadChain(ChainBase): download_dir = Path(save_path) # 添加下载 - result: Optional[tuple] = self.download(torrent_path=torrent_file, + result: Optional[tuple] = self.download(content=content, cookie=_torrent.site_cookie, episodes=episodes, download_dir=download_dir, @@ -208,11 +224,10 @@ class DownloadChain(ChainBase): # 发送消息 self.post_download_message(meta=_meta, mediainfo=_media, torrent=_torrent, channel=channel) # 下载成功后处理 - self.download_added(context=context, torrent_path=torrent_file, download_dir=download_dir) + self.download_added(context=context, download_dir=download_dir, torrent_path=torrent_file) # 广播事件 self.eventmanager.send_event(EventType.DownloadAdded, { "hash": _hash, - "torrent_file": torrent_file, "context": context }) else: @@ -346,8 +361,11 @@ class DownloadChain(ChainBase): if set(torrent_season).issubset(set(need_season)): if len(torrent_season) == 1: # 只有一季的可能是命名错误,需要打开种子鉴别,只有实际集数大于等于总集数才下载 - torrent_path, _, torrent_files = self.download_torrent(torrent) - if not torrent_path: + content, _, torrent_files = self.download_torrent(torrent) + if not content: + continue + if isinstance(content, str): + logger.warn(f"{meta.org_string} 下载地址是磁力链,无法确定种子文件集数") continue torrent_episodes = self.torrent.get_torrent_episodes(torrent_files) logger.info(f"{meta.org_string} 解析文件集数为 {torrent_episodes}") @@ -365,10 +383,12 @@ class DownloadChain(ChainBase): continue else: # 下载 - download_id = self.download_single(context=context, - torrent_file=torrent_path, - save_path=save_path, - userid=userid) + download_id = self.download_single( + context=context, + torrent_file=content if isinstance(content, Path) else None, + save_path=save_path, + userid=userid + ) else: # 下载 download_id = self.download_single(context, save_path=save_path, userid=userid) @@ -485,8 +505,11 @@ class DownloadChain(ChainBase): and len(meta.season_list) == 1 \ and meta.season_list[0] == need_season: # 检查种子看是否有需要的集 - torrent_path, _, torrent_files = self.download_torrent(torrent, userid=userid) - if not torrent_path: + content, _, torrent_files = self.download_torrent(torrent, userid=userid) + if not content: + continue + if isinstance(content, str): + logger.warn(f"{meta.org_string} 下载地址是磁力链,无法解析种子文件集数") continue # 种子全部集 torrent_episodes = self.torrent.get_torrent_episodes(torrent_files) @@ -498,11 +521,13 @@ class DownloadChain(ChainBase): continue logger.info(f"{torrent.site_name} - {torrent.title} 选中集数:{selected_episodes}") # 添加下载 - download_id = self.download_single(context=context, - torrent_file=torrent_path, - episodes=selected_episodes, - save_path=save_path, - userid=userid) + download_id = self.download_single( + context=context, + torrent_file=content if isinstance(content, Path) else None, + episodes=selected_episodes, + save_path=save_path, + userid=userid + ) if not download_id: continue # 把识别的集更新到上下文 diff --git a/app/modules/qbittorrent/__init__.py b/app/modules/qbittorrent/__init__.py index d6845f31..698b85e5 100644 --- a/app/modules/qbittorrent/__init__.py +++ b/app/modules/qbittorrent/__init__.py @@ -36,19 +36,22 @@ class QbittorrentModule(_ModuleBase): if self.qbittorrent.is_inactive(): self.qbittorrent = Qbittorrent() - def download(self, torrent_path: Path, download_dir: Path, cookie: str, + def download(self, content: Union[Path, str], download_dir: Path, cookie: str, episodes: Set[int] = None, category: str = None) -> Optional[Tuple[Optional[str], str]]: """ 根据种子文件,选择并添加下载任务 - :param torrent_path: 种子文件地址 + :param content: 种子文件地址或者磁力链接 :param download_dir: 下载目录 :param cookie: cookie :param episodes: 需要下载的集数 :param category: 分类 :return: 种子Hash,错误信息 """ - if not torrent_path or not torrent_path.exists(): - return None, f"种子文件不存在:{torrent_path}" + if not content: + return + if isinstance(content, Path) and not content.exists(): + return None, f"种子文件不存在:{content}" + # 生成随机Tag tag = StringUtils.generate_random_str(10) if settings.TORRENT_TAG: @@ -58,19 +61,21 @@ class QbittorrentModule(_ModuleBase): # 如果要选择文件则先暂停 is_paused = True if episodes else False # 添加任务 - state = self.qbittorrent.add_torrent(content=torrent_path.read_bytes(), - download_dir=str(download_dir), - is_paused=is_paused, - tag=tags, - cookie=cookie, - category=category) + state = self.qbittorrent.add_torrent( + content=content.read_bytes() if isinstance(content, Path) else content, + download_dir=str(download_dir), + is_paused=is_paused, + tag=tags, + cookie=cookie, + category=category + ) if not state: - return None, f"添加种子任务失败:{torrent_path}" + return None, f"添加种子任务失败:{content}" else: # 获取种子Hash torrent_hash = self.qbittorrent.get_torrent_id_by_tag(tags=tag) if not torrent_hash: - return None, f"获取种子Hash失败:{torrent_path}" + return None, f"获取种子Hash失败:{content}" else: if is_paused: # 种子文件 diff --git a/app/modules/subtitle/__init__.py b/app/modules/subtitle/__init__.py index da8b3c8d..bc2cf1c0 100644 --- a/app/modules/subtitle/__init__.py +++ b/app/modules/subtitle/__init__.py @@ -34,18 +34,22 @@ class SubtitleModule(_ModuleBase): def stop(self) -> None: pass - def download_added(self, context: Context, torrent_path: Path, download_dir: Path) -> None: + def download_added(self, context: Context, download_dir: Path, torrent_path: Path = None) -> None: """ 添加下载任务成功后,从站点下载字幕,保存到下载目录 :param context: 上下文,包括识别信息、媒体信息、种子信息 - :param torrent_path: 种子文件地址 :param download_dir: 下载目录 + :param torrent_path: 种子文件地址 :return: None,该方法可被多个模块同时处理 """ if not settings.DOWNLOAD_SUBTITLE: return None - # 种子信息 + # 没有种子文件不处理 + if not torrent_path: + return + + # 没有详情页不处理 torrent = context.torrent_info if not torrent.page_url: return diff --git a/app/modules/transmission/__init__.py b/app/modules/transmission/__init__.py index 57e22c22..b81e06c9 100644 --- a/app/modules/transmission/__init__.py +++ b/app/modules/transmission/__init__.py @@ -36,17 +36,22 @@ class TransmissionModule(_ModuleBase): if not self.transmission.is_inactive(): self.transmission = Transmission() - def download(self, torrent_path: Path, download_dir: Path, cookie: str, + def download(self, content: Union[Path, str], download_dir: Path, cookie: str, episodes: Set[int] = None, category: str = None) -> Optional[Tuple[Optional[str], str]]: """ 根据种子文件,选择并添加下载任务 - :param torrent_path: 种子文件地址 + :param content: 种子文件地址或者磁力链接 :param download_dir: 下载目录 :param cookie: cookie :param episodes: 需要下载的集数 :param category: 分类,TR中未使用 :return: 种子Hash """ + if not content: + return + if isinstance(content, Path) and not content.exists(): + return None, f"种子文件不存在:{content}" + # 如果要选择文件则先暂停 is_paused = True if episodes else False # 标签 @@ -55,13 +60,15 @@ class TransmissionModule(_ModuleBase): else: labels = None # 添加任务 - torrent = self.transmission.add_torrent(content=torrent_path.read_bytes(), - download_dir=str(download_dir), - is_paused=is_paused, - labels=labels, - cookie=cookie) + torrent = self.transmission.add_torrent( + content=content.read_bytes() if isinstance(content, Path) else content, + download_dir=str(download_dir), + is_paused=is_paused, + labels=labels, + cookie=cookie + ) if not torrent: - return None, f"添加种子任务失败:{torrent_path}" + return None, f"添加种子任务失败:{content}" else: torrent_hash = torrent.hashString if is_paused: