fix #516 支持磁力链下载

This commit is contained in:
jxxghp 2023-09-13 17:56:57 +08:00
parent 5eb37b5d28
commit 4e515ec442
5 changed files with 91 additions and 50 deletions

View File

@ -234,27 +234,27 @@ class ChainBase(metaclass=ABCMeta):
return self.run_module("filter_torrents", rule_string=rule_string, return self.run_module("filter_torrents", rule_string=rule_string,
torrent_list=torrent_list, season_episodes=season_episodes) 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 episodes: Set[int] = None, category: str = None
) -> Optional[Tuple[Optional[str], str]]: ) -> Optional[Tuple[Optional[str], str]]:
""" """
根据种子文件选择并添加下载任务 根据种子文件选择并添加下载任务
:param torrent_path: 种子文件地址 :param content: 种子文件地址或者磁力链接
:param download_dir: 下载目录 :param download_dir: 下载目录
:param cookie: cookie :param cookie: cookie
:param episodes: 需要下载的集数 :param episodes: 需要下载的集数
:param category: 种子分类 :param category: 种子分类
:return: 种子Hash错误信息 :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) 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 context: 上下文包括识别信息媒体信息种子信息
:param torrent_path: 种子文件地址
:param download_dir: 下载目录 :param download_dir: 下载目录
:param torrent_path: 种子文件地址
:return: None该方法可被多个模块同时处理 :return: None该方法可被多个模块同时处理
""" """
return self.run_module("download_added", context=context, torrent_path=torrent_path, return self.run_module("download_added", context=context, torrent_path=torrent_path,

View File

@ -70,16 +70,22 @@ class DownloadChain(ChainBase):
def download_torrent(self, torrent: TorrentInfo, def download_torrent(self, torrent: TorrentInfo,
channel: MessageChannel = None, 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: 种子路径种子目录名种子文件清单 :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, url=torrent.enclosure,
cookie=torrent.site_cookie, cookie=torrent.site_cookie,
ua=torrent.site_ua, ua=torrent.site_ua,
proxy=torrent.site_proxy) proxy=torrent.site_proxy)
if isinstance(content, str):
# 磁力链
return content, "", []
if not torrent_file: if not torrent_file:
logger.error(f"下载种子文件失败:{torrent.title} - {torrent.enclosure}") logger.error(f"下载种子文件失败:{torrent.title} - {torrent.enclosure}")
self.post_message(Notification( self.post_message(Notification(
@ -89,6 +95,8 @@ class DownloadChain(ChainBase):
text=f"错误信息:{error_msg}\n站点:{torrent.site_name}", text=f"错误信息:{error_msg}\n站点:{torrent.site_name}",
userid=userid)) userid=userid))
return None, "", [] return None, "", []
# 返回 种子文件路径,种子目录名,种子文件清单
return torrent_file, download_folder, files return torrent_file, download_folder, files
def download_single(self, context: Context, torrent_file: Path = None, def download_single(self, context: Context, torrent_file: Path = None,
@ -98,19 +106,27 @@ class DownloadChain(ChainBase):
userid: Union[str, int] = None) -> Optional[str]: 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 _torrent = context.torrent_info
_media = context.media_info _media = context.media_info
_meta = context.meta_info _meta = context.meta_info
_folder_name = "" _folder_name = ""
if not torrent_file: if not torrent_file:
# 下载种子文件 # 下载种子文件,得到的可能是文件也可能是磁力链
torrent_file, _folder_name, _file_list = self.download_torrent(_torrent, userid=userid) content, _folder_name, _file_list = self.download_torrent(_torrent, userid=userid)
if not torrent_file: if not content:
return return
else: else:
content = torrent_file
# 获取种子文件的文件夹名和文件清单 # 获取种子文件的文件夹名和文件清单
_folder_name, _file_list = self.torrent.get_torrent_info(torrent_file) _folder_name, _file_list = self.torrent.get_torrent_info(torrent_file)
# 下载目录 # 下载目录
if not save_path: if not save_path:
if settings.DOWNLOAD_CATEGORY and _media and _media.category: if settings.DOWNLOAD_CATEGORY and _media and _media.category:
@ -149,7 +165,7 @@ class DownloadChain(ChainBase):
download_dir = Path(save_path) 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, cookie=_torrent.site_cookie,
episodes=episodes, episodes=episodes,
download_dir=download_dir, download_dir=download_dir,
@ -208,11 +224,10 @@ class DownloadChain(ChainBase):
# 发送消息 # 发送消息
self.post_download_message(meta=_meta, mediainfo=_media, torrent=_torrent, channel=channel) 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, { self.eventmanager.send_event(EventType.DownloadAdded, {
"hash": _hash, "hash": _hash,
"torrent_file": torrent_file,
"context": context "context": context
}) })
else: else:
@ -346,8 +361,11 @@ class DownloadChain(ChainBase):
if set(torrent_season).issubset(set(need_season)): if set(torrent_season).issubset(set(need_season)):
if len(torrent_season) == 1: if len(torrent_season) == 1:
# 只有一季的可能是命名错误,需要打开种子鉴别,只有实际集数大于等于总集数才下载 # 只有一季的可能是命名错误,需要打开种子鉴别,只有实际集数大于等于总集数才下载
torrent_path, _, torrent_files = self.download_torrent(torrent) content, _, torrent_files = self.download_torrent(torrent)
if not torrent_path: if not content:
continue
if isinstance(content, str):
logger.warn(f"{meta.org_string} 下载地址是磁力链,无法确定种子文件集数")
continue continue
torrent_episodes = self.torrent.get_torrent_episodes(torrent_files) torrent_episodes = self.torrent.get_torrent_episodes(torrent_files)
logger.info(f"{meta.org_string} 解析文件集数为 {torrent_episodes}") logger.info(f"{meta.org_string} 解析文件集数为 {torrent_episodes}")
@ -365,10 +383,12 @@ class DownloadChain(ChainBase):
continue continue
else: else:
# 下载 # 下载
download_id = self.download_single(context=context, download_id = self.download_single(
torrent_file=torrent_path, context=context,
save_path=save_path, torrent_file=content if isinstance(content, Path) else None,
userid=userid) save_path=save_path,
userid=userid
)
else: else:
# 下载 # 下载
download_id = self.download_single(context, save_path=save_path, userid=userid) 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 len(meta.season_list) == 1 \
and meta.season_list[0] == need_season: and meta.season_list[0] == need_season:
# 检查种子看是否有需要的集 # 检查种子看是否有需要的集
torrent_path, _, torrent_files = self.download_torrent(torrent, userid=userid) content, _, torrent_files = self.download_torrent(torrent, userid=userid)
if not torrent_path: if not content:
continue
if isinstance(content, str):
logger.warn(f"{meta.org_string} 下载地址是磁力链,无法解析种子文件集数")
continue continue
# 种子全部集 # 种子全部集
torrent_episodes = self.torrent.get_torrent_episodes(torrent_files) torrent_episodes = self.torrent.get_torrent_episodes(torrent_files)
@ -498,11 +521,13 @@ class DownloadChain(ChainBase):
continue continue
logger.info(f"{torrent.site_name} - {torrent.title} 选中集数:{selected_episodes}") logger.info(f"{torrent.site_name} - {torrent.title} 选中集数:{selected_episodes}")
# 添加下载 # 添加下载
download_id = self.download_single(context=context, download_id = self.download_single(
torrent_file=torrent_path, context=context,
episodes=selected_episodes, torrent_file=content if isinstance(content, Path) else None,
save_path=save_path, episodes=selected_episodes,
userid=userid) save_path=save_path,
userid=userid
)
if not download_id: if not download_id:
continue continue
# 把识别的集更新到上下文 # 把识别的集更新到上下文

View File

@ -36,19 +36,22 @@ class QbittorrentModule(_ModuleBase):
if self.qbittorrent.is_inactive(): if self.qbittorrent.is_inactive():
self.qbittorrent = Qbittorrent() 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]]: episodes: Set[int] = None, category: str = None) -> Optional[Tuple[Optional[str], str]]:
""" """
根据种子文件选择并添加下载任务 根据种子文件选择并添加下载任务
:param torrent_path: 种子文件地址 :param content: 种子文件地址或者磁力链接
:param download_dir: 下载目录 :param download_dir: 下载目录
:param cookie: cookie :param cookie: cookie
:param episodes: 需要下载的集数 :param episodes: 需要下载的集数
:param category: 分类 :param category: 分类
:return: 种子Hash错误信息 :return: 种子Hash错误信息
""" """
if not torrent_path or not torrent_path.exists(): if not content:
return None, f"种子文件不存在:{torrent_path}" return
if isinstance(content, Path) and not content.exists():
return None, f"种子文件不存在:{content}"
# 生成随机Tag # 生成随机Tag
tag = StringUtils.generate_random_str(10) tag = StringUtils.generate_random_str(10)
if settings.TORRENT_TAG: if settings.TORRENT_TAG:
@ -58,19 +61,21 @@ class QbittorrentModule(_ModuleBase):
# 如果要选择文件则先暂停 # 如果要选择文件则先暂停
is_paused = True if episodes else False is_paused = True if episodes else False
# 添加任务 # 添加任务
state = self.qbittorrent.add_torrent(content=torrent_path.read_bytes(), state = self.qbittorrent.add_torrent(
download_dir=str(download_dir), content=content.read_bytes() if isinstance(content, Path) else content,
is_paused=is_paused, download_dir=str(download_dir),
tag=tags, is_paused=is_paused,
cookie=cookie, tag=tags,
category=category) cookie=cookie,
category=category
)
if not state: if not state:
return None, f"添加种子任务失败:{torrent_path}" return None, f"添加种子任务失败:{content}"
else: else:
# 获取种子Hash # 获取种子Hash
torrent_hash = self.qbittorrent.get_torrent_id_by_tag(tags=tag) torrent_hash = self.qbittorrent.get_torrent_id_by_tag(tags=tag)
if not torrent_hash: if not torrent_hash:
return None, f"获取种子Hash失败{torrent_path}" return None, f"获取种子Hash失败{content}"
else: else:
if is_paused: if is_paused:
# 种子文件 # 种子文件

View File

@ -34,18 +34,22 @@ class SubtitleModule(_ModuleBase):
def stop(self) -> None: def stop(self) -> None:
pass 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 context: 上下文包括识别信息媒体信息种子信息
:param torrent_path: 种子文件地址
:param download_dir: 下载目录 :param download_dir: 下载目录
:param torrent_path: 种子文件地址
:return: None该方法可被多个模块同时处理 :return: None该方法可被多个模块同时处理
""" """
if not settings.DOWNLOAD_SUBTITLE: if not settings.DOWNLOAD_SUBTITLE:
return None return None
# 种子信息 # 没有种子文件不处理
if not torrent_path:
return
# 没有详情页不处理
torrent = context.torrent_info torrent = context.torrent_info
if not torrent.page_url: if not torrent.page_url:
return return

View File

@ -36,17 +36,22 @@ class TransmissionModule(_ModuleBase):
if not self.transmission.is_inactive(): if not self.transmission.is_inactive():
self.transmission = Transmission() 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]]: episodes: Set[int] = None, category: str = None) -> Optional[Tuple[Optional[str], str]]:
""" """
根据种子文件选择并添加下载任务 根据种子文件选择并添加下载任务
:param torrent_path: 种子文件地址 :param content: 种子文件地址或者磁力链接
:param download_dir: 下载目录 :param download_dir: 下载目录
:param cookie: cookie :param cookie: cookie
:param episodes: 需要下载的集数 :param episodes: 需要下载的集数
:param category: 分类TR中未使用 :param category: 分类TR中未使用
:return: 种子Hash :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 is_paused = True if episodes else False
# 标签 # 标签
@ -55,13 +60,15 @@ class TransmissionModule(_ModuleBase):
else: else:
labels = None labels = None
# 添加任务 # 添加任务
torrent = self.transmission.add_torrent(content=torrent_path.read_bytes(), torrent = self.transmission.add_torrent(
download_dir=str(download_dir), content=content.read_bytes() if isinstance(content, Path) else content,
is_paused=is_paused, download_dir=str(download_dir),
labels=labels, is_paused=is_paused,
cookie=cookie) labels=labels,
cookie=cookie
)
if not torrent: if not torrent:
return None, f"添加种子任务失败:{torrent_path}" return None, f"添加种子任务失败:{content}"
else: else:
torrent_hash = torrent.hashString torrent_hash = torrent.hashString
if is_paused: if is_paused: