This commit is contained in:
jxxghp 2023-06-07 20:03:53 +08:00
parent a1d65b7c87
commit 061c16c659
14 changed files with 91 additions and 53 deletions

View File

@ -2,7 +2,7 @@ from pathlib import Path
from typing import List, Optional from typing import List, Optional
from app.chain import _ChainBase from app.chain import _ChainBase
from app.core import MetaInfo, MediaInfo from app.core import MetaInfo, MediaInfo, settings
from app.log import logger from app.log import logger
from app.utils.types import TorrentStatus from app.utils.types import TorrentStatus
@ -18,7 +18,7 @@ class TransferChain(_ChainBase):
""" """
logger.info("开始执行下载器文件转移 ...") logger.info("开始执行下载器文件转移 ...")
# 从下载器获取种子列表 # 从下载器获取种子列表
torrents: Optional[List[dict]] = self.run_module("list_torrents", status=TorrentStatus.COMPLETE) torrents: Optional[List[dict]] = self.run_module("list_torrents", status=TorrentStatus.TRANSFER)
if not torrents: if not torrents:
logger.info("没有获取到已完成的下载任务") logger.info("没有获取到已完成的下载任务")
return False return False
@ -43,8 +43,15 @@ class TransferChain(_ChainBase):
if not dest_path: if not dest_path:
logger.warn(f"{torrent.get('title')} 转移失败") logger.warn(f"{torrent.get('title')} 转移失败")
continue continue
# 转移完成
self.run_module("transfer_completed", hashs=torrent.get("hash"))
# 刮剥 # 刮剥
self.run_module("scrape_metadata", path=dest_path, mediainfo=mediainfo) self.run_module("scrape_metadata", path=dest_path, mediainfo=mediainfo)
# 移动模式删除种子
if settings.TRANSFER_TYPE == "move":
result = self.run_module("remove_torrents", hashs=torrent.get("hash"))
if result:
logger.info(f"移动模式删除种子成功:{torrent.get('title')} ")
logger.info("下载器文件转移执行完成") logger.info("下载器文件转移执行完成")
return True return True

View File

@ -91,6 +91,8 @@ class Settings(BaseSettings):
TR_USER: str = None TR_USER: str = None
# Transmission密码 # Transmission密码
TR_PASSWORD: str = None TR_PASSWORD: str = None
# 种子标签
TORRENT_TAG: str = "MP"
# 下载保存目录,容器内映射路径需要一致 # 下载保存目录,容器内映射路径需要一致
DOWNLOAD_PATH: str = "/downloads" DOWNLOAD_PATH: str = "/downloads"
# 媒体服务器 emby/jellyfin/plex # 媒体服务器 emby/jellyfin/plex

Binary file not shown.

View File

@ -157,6 +157,14 @@ class _ModuleBase(metaclass=ABCMeta):
""" """
pass pass
def transfer_completed(self, hashs: Union[str, list]) -> bool:
"""
转移完成后的处理
:param hashs: 种子Hash
:return: 处理状态
"""
pass
def media_exists(self, mediainfo: MediaInfo) -> Optional[dict]: def media_exists(self, mediainfo: MediaInfo) -> Optional[dict]:
""" """
判断媒体文件是否存在 判断媒体文件是否存在

View File

@ -1,8 +1,6 @@
import re import re
from typing import List, Tuple, Union, Dict, Optional from typing import List, Tuple, Union, Dict, Optional
from pyparsing import ParseResults
from app.core import TorrentInfo, settings from app.core import TorrentInfo, settings
from app.modules import _ModuleBase from app.modules import _ModuleBase
from app.modules.filter.RuleParser import RuleParser from app.modules.filter.RuleParser import RuleParser

View File

@ -30,6 +30,8 @@ class QbittorrentModule(_ModuleBase):
return None, f"种子文件不存在:{torrent_path}" return None, f"种子文件不存在:{torrent_path}"
# 生成随机Tag # 生成随机Tag
tag = StringUtils.generate_random_str(10) tag = StringUtils.generate_random_str(10)
if settings.TORRENT_TAG:
tag = [tag, settings.TORRENT_TAG]
# 如果要选择文件则先暂停 # 如果要选择文件则先暂停
is_paused = True if episodes else False is_paused = True if episodes else False
# 添加任务 # 添加任务
@ -75,16 +77,22 @@ class QbittorrentModule(_ModuleBase):
else: else:
return torrent_hash, "添加下载成功" return torrent_hash, "添加下载成功"
def transfer_completed(self, hashs: Union[str, list]) -> bool:
"""
转移完成后的处理
:param hashs: 种子Hash
:return: 处理状态
"""
return self.qbittorrent.set_torrents_tag(ids=hashs, tag=['已整理'])
def list_torrents(self, status: TorrentStatus) -> Optional[List[dict]]: def list_torrents(self, status: TorrentStatus) -> Optional[List[dict]]:
""" """
获取下载器种子列表 获取下载器种子列表
:param status: 种子状态 :param status: 种子状态
:return: 下载器中符合状态的种子列表 :return: 下载器中符合状态的种子列表
""" """
if status == TorrentStatus.COMPLETE: if status == TorrentStatus.TRANSFER:
torrents = self.qbittorrent.get_completed_torrents() torrents = self.qbittorrent.get_transfer_torrents(tag=settings.TORRENT_TAG)
elif status == TorrentStatus.DOWNLOADING:
torrents = self.qbittorrent.get_downloading_torrents()
else: else:
return None return None
return torrents return torrents

View File

@ -1,9 +1,9 @@
import time import time
from pathlib import Path from pathlib import Path
from typing import Optional, Union, Tuple from typing import Optional, Union, Tuple, List
import qbittorrentapi import qbittorrentapi
from qbittorrentapi import TorrentFilesList from qbittorrentapi import TorrentFilesList, TorrentDictionary
from qbittorrentapi.client import Client from qbittorrentapi.client import Client
from app.core import settings from app.core import settings
@ -51,7 +51,8 @@ class Qbittorrent(metaclass=Singleton):
return None return None
def get_torrents(self, ids: Union[str, list] = None, def get_torrents(self, ids: Union[str, list] = None,
status: Union[str, list] = None, tag: Union[str, list] = None) -> Tuple[list, bool]: status: Union[str, list] = None,
tag: Union[str, list] = None) -> Tuple[List[TorrentDictionary], bool]:
""" """
获取种子列表 获取种子列表
return: 种子列表, 是否发生异常 return: 种子列表, 是否发生异常
@ -73,14 +74,14 @@ class Qbittorrent(metaclass=Singleton):
break break
if include_flag: if include_flag:
results.append(torrent) results.append(torrent)
return results or [], False return results, False
return torrents or [], False return torrents or [], False
except Exception as err: except Exception as err:
logger.error(f"获取种子列表出错:{err}") logger.error(f"获取种子列表出错:{err}")
return [], True return [], True
def get_completed_torrents(self, ids: Union[str, list] = None, def get_completed_torrents(self, ids: Union[str, list] = None,
tag: Union[str, list] = None) -> Optional[list]: tag: Union[str, list] = None) -> Optional[List[TorrentDictionary]]:
""" """
获取已完成的种子 获取已完成的种子
return: 种子列表, 如发生异常则返回None return: 种子列表, 如发生异常则返回None
@ -91,7 +92,7 @@ class Qbittorrent(metaclass=Singleton):
return None if error else torrents or [] return None if error else torrents or []
def get_downloading_torrents(self, ids: Union[str, list] = None, def get_downloading_torrents(self, ids: Union[str, list] = None,
tag: Union[str, list] = None) -> Optional[list]: tag: Union[str, list] = None) -> Optional[List[TorrentDictionary]]:
""" """
获取正在下载的种子 获取正在下载的种子
return: 种子列表, 如发生异常则返回None return: 种子列表, 如发生异常则返回None
@ -116,7 +117,7 @@ class Qbittorrent(metaclass=Singleton):
logger.error(f"移除种子Tag出错{err}") logger.error(f"移除种子Tag出错{err}")
return False return False
def set_torrents_status(self, ids: Union[str, list]): def set_torrents_tag(self, ids: Union[str, list], tag: list):
""" """
设置种子状态为已整理以及是否强制做种 设置种子状态为已整理以及是否强制做种
""" """
@ -124,7 +125,7 @@ class Qbittorrent(metaclass=Singleton):
return return
try: try:
# 打标签 # 打标签
self.qbc.torrents_add_tags(tags="已整理", torrent_hashes=ids) self.qbc.torrents_add_tags(tags=tag, torrent_hashes=ids)
except Exception as err: except Exception as err:
logger.error(f"设置种子Tag出错{err}") logger.error(f"设置种子Tag出错{err}")
@ -137,7 +138,7 @@ class Qbittorrent(metaclass=Singleton):
except Exception as err: except Exception as err:
logger.error(f"设置强制作种出错:{err}") logger.error(f"设置强制作种出错:{err}")
def get_transfer_task(self, tag: Union[str, list] = None) -> Optional[list]: def get_transfer_torrents(self, tag: Union[str, list] = None) -> Optional[List[dict]]:
""" """
获取下载文件转移任务种子 获取下载文件转移任务种子
""" """
@ -166,8 +167,10 @@ class Qbittorrent(metaclass=Singleton):
else: else:
trans_name = torrent.get('name') trans_name = torrent.get('name')
trans_tasks.append({ trans_tasks.append({
'title': torrent.get('name'),
'path': Path(settings.DOWNLOAD_PATH) / trans_name, 'path': Path(settings.DOWNLOAD_PATH) / trans_name,
'id': torrent.get('hash') 'id': torrent.get('hash'),
'tags': torrent.get('tags')
}) })
return trans_tasks return trans_tasks

View File

@ -28,10 +28,16 @@ class TransmissionModule(_ModuleBase):
""" """
# 如果要选择文件则先暂停 # 如果要选择文件则先暂停
is_paused = True if episodes else False is_paused = True if episodes else False
# 标签
if settings.TORRENT_TAG:
labels = [settings.TORRENT_TAG]
else:
labels = None
# 添加任务 # 添加任务
torrent = self.transmission.add_torrent(content=torrent_path.read_bytes(), torrent = self.transmission.add_torrent(content=torrent_path.read_bytes(),
download_dir=settings.DOWNLOAD_PATH, download_dir=settings.DOWNLOAD_PATH,
is_paused=is_paused, is_paused=is_paused,
labels=labels,
cookie=cookie) cookie=cookie)
if not torrent: if not torrent:
return None, f"添加种子任务失败:{torrent_path}" return None, f"添加种子任务失败:{torrent_path}"
@ -70,16 +76,22 @@ class TransmissionModule(_ModuleBase):
else: else:
return torrent_hash, "添加下载任务成功" return torrent_hash, "添加下载任务成功"
def transfer_completed(self, hashs: Union[str, list]) -> bool:
"""
转移完成后的处理
:param hashs: 种子Hash
:return: 处理状态
"""
return self.transmission.set_torrent_tag(ids=hashs, tag=['已整理'])
def list_torrents(self, status: TorrentStatus) -> Optional[List[dict]]: def list_torrents(self, status: TorrentStatus) -> Optional[List[dict]]:
""" """
获取下载器种子列表 获取下载器种子列表
:param status: 种子状态 :param status: 种子状态
:return: 下载器中符合状态的种子列表 :return: 下载器中符合状态的种子列表
""" """
if status == TorrentStatus.COMPLETE: if status == TorrentStatus.TRANSFER:
torrents = self.transmission.get_completed_torrents() torrents = self.transmission.get_transfer_torrents(tag=settings.TORRENT_TAG)
elif status == TorrentStatus.DOWNLOADING:
torrents = self.transmission.get_completed_torrents()
else: else:
return None return None
return torrents return torrents

View File

@ -50,7 +50,7 @@ class Transmission(metaclass=Singleton):
return None return None
def get_torrents(self, ids: Union[str, list] = None, status: Union[str, list] = None, def get_torrents(self, ids: Union[str, list] = None, status: Union[str, list] = None,
tag: Union[str, list] = None) -> Tuple[list, bool]: tag: Union[str, list] = None) -> Tuple[List[Torrent], bool]:
""" """
获取种子列表 获取种子列表
返回结果 种子列表, 是否有错误 返回结果 种子列表, 是否有错误
@ -81,7 +81,8 @@ class Transmission(metaclass=Singleton):
ret_torrents.append(torrent) ret_torrents.append(torrent)
return ret_torrents, False return ret_torrents, False
def get_completed_torrents(self, ids: Union[str, list] = None, tag: Union[str, list] = None) -> Optional[list]: def get_completed_torrents(self, ids: Union[str, list] = None,
tag: Union[str, list] = None) -> Optional[List[Torrent]]:
""" """
获取已完成的种子列表 获取已完成的种子列表
return 种子列表, 发生错误时返回None return 种子列表, 发生错误时返回None
@ -96,7 +97,7 @@ class Transmission(metaclass=Singleton):
return None return None
def get_downloading_torrents(self, ids: Union[str, list] = None, def get_downloading_torrents(self, ids: Union[str, list] = None,
tag: Union[str, list] = None) -> Optional[list]: tag: Union[str, list] = None) -> Optional[List[Torrent]]:
""" """
获取正在下载的种子列表 获取正在下载的种子列表
return 种子列表, 发生错误时返回None return 种子列表, 发生错误时返回None
@ -112,30 +113,7 @@ class Transmission(metaclass=Singleton):
logger.error(f"获取正在下载的种子列表出错:{err}") logger.error(f"获取正在下载的种子列表出错:{err}")
return None return None
def set_torrents_status(self, ids: Union[str, list], def set_torrent_tag(self, ids: str, tag: list) -> bool:
tags: Union[str, list] = None) -> bool:
"""
设置种子为已整理状态
"""
if not self.trc:
return False
# 合成标签
if tags:
if not isinstance(tags, list):
tags = [tags, "已整理"]
else:
tags.append("已整理")
else:
tags = ["已整理"]
# 打标签
try:
self.trc.change_torrent(labels=tags, ids=ids)
return True
except Exception as err:
logger.error(f"设置种子为已整理状态出错:{err}")
return False
def set_torrent_tag(self, ids: str, tag: Union[str, list]) -> bool:
""" """
设置种子标签 设置种子标签
""" """
@ -148,7 +126,7 @@ class Transmission(metaclass=Singleton):
logger.error(f"设置种子标签出错:{err}") logger.error(f"设置种子标签出错:{err}")
return False return False
def get_transfer_task(self, tag: Union[str, list] = None) -> List[dict]: def get_transfer_torrents(self, tag: Union[str, list] = None) -> List[dict]:
""" """
获取下载文件转移任务种子 获取下载文件转移任务种子
""" """
@ -174,6 +152,7 @@ class Transmission(metaclass=Singleton):
logger.debug(f"未获取到 {torrent.name} 下载保存路径") logger.debug(f"未获取到 {torrent.name} 下载保存路径")
continue continue
trans_tasks.append({ trans_tasks.append({
'title': torrent.name,
'path': Path(settings.DOWNLOAD_PATH) / torrent.name, 'path': Path(settings.DOWNLOAD_PATH) / torrent.name,
'id': torrent.hashString, 'id': torrent.hashString,
'tags': torrent.labels 'tags': torrent.labels
@ -183,12 +162,14 @@ class Transmission(metaclass=Singleton):
def add_torrent(self, content: Union[str, bytes], def add_torrent(self, content: Union[str, bytes],
is_paused: bool = False, is_paused: bool = False,
download_dir: str = None, download_dir: str = None,
labels=None,
cookie=None) -> Optional[Torrent]: cookie=None) -> Optional[Torrent]:
""" """
添加下载任务 添加下载任务
:param content: 种子urls或文件内容 :param content: 种子urls或文件内容
:param is_paused: 添加后暂停 :param is_paused: 添加后暂停
:param download_dir: 下载路径 :param download_dir: 下载路径
:param labels: 标签
:param cookie: 站点Cookie用于辅助下载种子 :param cookie: 站点Cookie用于辅助下载种子
:return: Torrent :return: Torrent
""" """
@ -196,6 +177,7 @@ class Transmission(metaclass=Singleton):
return self.trc.add_torrent(torrent=content, return self.trc.add_torrent(torrent=content,
download_dir=download_dir, download_dir=download_dir,
paused=is_paused, paused=is_paused,
labels=labels,
cookies=cookie) cookies=cookie)
except Exception as err: except Exception as err:
logger.error(f"添加种子出错:{err}") logger.error(f"添加种子出错:{err}")

View File

@ -8,8 +8,7 @@ class MediaType(Enum):
class TorrentStatus(Enum): class TorrentStatus(Enum):
COMPLETE = "已完成" TRANSFER = "可转移"
DOWNLOADING = "下载中"
# 可监听事件 # 可监听事件

View File

@ -1,12 +1,14 @@
import unittest import unittest
from tests.test_metainfo import MetaInfoTest from tests.test_metainfo import MetaInfoTest
from tests.test_recognize import RecognizeTest
if __name__ == '__main__': if __name__ == '__main__':
suite = unittest.TestSuite() suite = unittest.TestSuite()
# 测试名称识别 # 测试名称识别
suite.addTest(MetaInfoTest('test_metainfo')) suite.addTest(MetaInfoTest('test_metainfo'))
# 测试媒体识别
suite.addTest(RecognizeTest('test_recognize'))
# 运行测试 # 运行测试
runner = unittest.TextTestRunner() runner = unittest.TextTestRunner()
runner.run(suite) runner.run(suite)

17
tests/test_recognize.py Normal file
View File

@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
from unittest import TestCase
from app.chain.identify import IdentifyChain
class RecognizeTest(TestCase):
def setUp(self) -> None:
pass
def tearDown(self) -> None:
pass
def test_recognize(self):
result = IdentifyChain().process(title="我和我的祖国 2019")
self.assertEqual(str(result.media_info.tmdb_id), '612845')