Skip to content

Commit 3c6aceb

Browse files
authored
feat: auto make torrent and add it to qb (#13)
1 parent dfb76d1 commit 3c6aceb

File tree

10 files changed

+474
-283
lines changed

10 files changed

+474
-283
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -169,3 +169,5 @@ cython_debug/
169169
/store.json
170170
/conf/store.json
171171
/tests/store.json
172+
173+
*.torrent

animepipeline/bt/qb.py

+55-13
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33

44
import qbittorrentapi
55
from loguru import logger
6+
from torrentool.torrent import Torrent
67

78
from animepipeline.config import QBitTorrentConfig
8-
from animepipeline.util import gen_magnet_link
9+
from animepipeline.util import ANNOUNCE_URLS, gen_magnet_link
910

1011

1112
class QBittorrentManager:
@@ -27,25 +28,44 @@ def __init__(self, config: QBitTorrentConfig) -> None:
2728

2829
self.COMPLETE_STATES = ["uploading", "stalledUP", "pausedUP", "queuedUP"]
2930

30-
def add_torrent(self, torrent_hash: str, torrent_url: Optional[Union[str, Path]] = None) -> None:
31+
def add_torrent(
32+
self, torrent_hash: str, torrent_url: Optional[str] = None, torrent_file_path: Optional[Union[str, Path]] = None
33+
) -> None:
3134
"""
32-
Add a torrent to download
35+
Add a torrent to download, either from a magnet link or a torrent file
3336
3437
:param torrent_hash: Torrent hash
35-
:param torrent_url: Torrent URL, defaults to None, in which case a magnet link will be
38+
:param torrent_url: Torrent URL, defaults to None, in which case a magnet link will be generated
39+
:param torrent_file_path: Torrent file path, defaults to None
3640
"""
37-
if torrent_url is None:
38-
torrent_url = gen_magnet_link(torrent_hash)
39-
4041
if self.check_torrent_exist(torrent_hash):
4142
logger.warning(f"Torrent {torrent_hash} already exists.")
4243
return
4344

44-
try:
45-
self.client.torrents.add(urls=torrent_url)
46-
logger.info(f"Torrent {torrent_url} added for download.")
47-
except Exception as e:
48-
logger.error(f"Failed to add torrent: {e}")
45+
if torrent_file_path is None:
46+
# add fron torrent url
47+
if torrent_url is None:
48+
torrent_url = gen_magnet_link(torrent_hash)
49+
50+
try:
51+
self.client.torrents.add(urls=torrent_url)
52+
logger.info(f"Torrent {torrent_url} added for download.")
53+
except Exception as e:
54+
logger.error(f"Failed to add torrent: {e}")
55+
else:
56+
# add from torrent file path
57+
if not Path(torrent_file_path).exists():
58+
logger.error(f"Torrent file {torrent_file_path} does not exist.")
59+
return
60+
61+
with open(torrent_file_path, "rb") as f:
62+
torrent_file = f.read()
63+
64+
try:
65+
self.client.torrents.add(torrent_files=torrent_file)
66+
logger.info(f"Torrent {torrent_file_path} added for download.")
67+
except Exception as e:
68+
logger.error(f"Failed to add torrent: {e}")
4969

5070
def check_download_complete(self, torrent_hash: str) -> bool:
5171
"""
@@ -56,7 +76,7 @@ def check_download_complete(self, torrent_hash: str) -> bool:
5676

5777
try:
5878
torrent = self.client.torrents_info(torrent_hashes=torrent_hash)
59-
79+
# logger.debug(f"Torrent state: {torrent[0].state}")
6080
if torrent[0].state in self.COMPLETE_STATES:
6181
return True
6282
else:
@@ -100,3 +120,25 @@ def check_torrent_exist(self, torrent_hash: str) -> bool:
100120
except Exception as e:
101121
logger.error(f"Error checking torrent existence: {e}")
102122
return False
123+
124+
@staticmethod
125+
def make_torrent_file(file_path: Union[str, Path], torrent_file_save_path: Union[str, Path]) -> str:
126+
"""
127+
Make a torrent file from a file, return the hash of the torrent
128+
129+
:param file_path: File path
130+
:param torrent_file_save_path: Torrent file save path
131+
"""
132+
if not Path(file_path).exists():
133+
logger.error(f"File {file_path} does not exist.")
134+
raise FileNotFoundError(f"File {file_path} does not exist.")
135+
136+
new_torrent = Torrent.create_from(file_path)
137+
logger.info(f"Editing torrent file: {file_path} ...")
138+
new_torrent.private = False
139+
new_torrent.announce_urls = ANNOUNCE_URLS
140+
new_torrent.comment = "Created by TensoRaws/AnimePipeline"
141+
new_torrent.created_by = "TensoRaws"
142+
new_torrent.to_file(torrent_file_save_path)
143+
144+
return new_torrent.info_hash

animepipeline/loop.py

+25-11
Original file line numberDiff line numberDiff line change
@@ -243,25 +243,39 @@ async def pipeline_finalrip(self, task_info: TaskInfo) -> None:
243243
async def pipeline_post(self, task_info: TaskInfo) -> None:
244244
task_status = await self.json_store.get_task(task_info.hash)
245245

246-
if self.tg_channel_sender is None:
247-
logger.info("Telegram Channel Sender is not enabled. Skip upload.")
248-
return
249-
250-
# check tg
251-
if task_status.tg_posted:
246+
# check posted
247+
if task_status.posted:
252248
return
253249

254250
if task_status.finalrip_downloaded_path is None:
255251
logger.error("FinalRip download path is None! finalrip download task not finished?")
256252
raise ValueError("FinalRip download path is None! finalrip download task not finished?")
257253

258-
logger.info(f'Post to Telegram Channel for "{task_info.name}" EP {task_info.episode}')
254+
finalrip_downloaded_path = Path(task_info.download_path) / task_status.finalrip_downloaded_path
255+
torrent_file_save_path = Path(task_info.download_path) / (str(finalrip_downloaded_path.name) + ".torrent")
256+
257+
try:
258+
torrent_file_hash = QBittorrentManager.make_torrent_file(
259+
file_path=finalrip_downloaded_path,
260+
torrent_file_save_path=torrent_file_save_path,
261+
)
262+
logger.info(f"Torrent file created: {torrent_file_save_path}, hash: {torrent_file_hash}")
263+
except Exception as e:
264+
logger.error(f"Failed to create torrent file: {e}")
265+
raise e
266+
267+
self.qbittorrent_manager.add_torrent(torrent_hash=torrent_file_hash, torrent_file_path=torrent_file_save_path)
268+
269+
logger.info(f"Post to Telegram Channel for {task_info.name} EP {task_info.episode}")
259270

260271
finalrip_downloaded_path = Path(task_info.download_path) / task_status.finalrip_downloaded_path
261272

262-
await self.tg_channel_sender.send_text(
263-
text=f"{task_info.translation} | EP {task_info.episode} | {finalrip_downloaded_path.name}",
264-
)
273+
if self.tg_channel_sender is None:
274+
logger.info("Telegram Channel Sender is not enabled. Skip upload.")
275+
else:
276+
await self.tg_channel_sender.send_text(
277+
text=f"{task_info.translation} | EP {task_info.episode} | {finalrip_downloaded_path.name} | hash: {torrent_file_hash}"
278+
)
265279

266-
task_status.tg_posted = True
280+
task_status.posted = True
267281
await self.json_store.update_task(task_info.hash, task_status)

animepipeline/post/tg.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def __init__(self, config: TelegramConfig) -> None:
1717
self.bot = Bot(token=config.bot_token)
1818
self.channel_id = config.channel_id
1919

20-
@retry(wait=wait_random(min=3, max=5), stop=stop_after_attempt(5))
20+
@retry(wait=wait_random(min=3, max=15), stop=stop_after_attempt(5))
2121
async def send_text(self, text: str) -> None:
2222
"""
2323
Send text to the channel.

animepipeline/store/task.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class TaskStatus(BaseModel):
1212
done: bool = False
1313
bt_downloaded_path: Optional[str] = None
1414
finalrip_downloaded_path: Optional[str] = None
15-
tg_posted: bool = False
15+
posted: bool = False
1616
ex_status_dict: Optional[Dict[str, Any]] = None
1717

1818

animepipeline/util/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
from animepipeline.util.bt import gen_magnet_link # noqa
1+
from animepipeline.util.bt import gen_magnet_link, ANNOUNCE_URLS # noqa

animepipeline/util/bt.py

+58
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,61 @@ def gen_magnet_link(torrent_hash: str) -> str:
55
:param torrent_hash: The torrent hash.
66
"""
77
return f"magnet:?xt=urn:btih:{torrent_hash}"
8+
9+
10+
# bt tracker urls
11+
ANNOUNCE_URLS = [
12+
"http://nyaa.tracker.wf:7777/announce",
13+
"http://open.acgtracker.com:1096/announce",
14+
"http://t.nyaatracker.com:80/announce",
15+
"http://tracker4.itzmx.com:2710/announce",
16+
"https://tracker.nanoha.org/announce",
17+
"http://t.acg.rip:6699/announce",
18+
"https://tr.bangumi.moe:9696/announce",
19+
"http://tr.bangumi.moe:6969/announce",
20+
"udp://tr.bangumi.moe:6969/announce",
21+
"http://open.acgnxtracker.com/announce",
22+
"https://open.acgnxtracker.com/announce",
23+
"udp://open.stealth.si:80/announce",
24+
"udp://tracker.opentrackr.org:1337/announce",
25+
"udp://exodus.desync.com:6969/announce",
26+
"udp://tracker.torrent.eu.org:451/announce",
27+
"udp://tracker.openbittorrent.com:80/announce",
28+
"udp://tracker.publicbt.com:80/announce",
29+
"udp://tracker.prq.to:80/announce",
30+
"udp://104.238.198.186:8000/announce",
31+
"http://104.238.198.186:8000/announce",
32+
"http://94.228.192.98/announce",
33+
"http://share.dmhy.org/annonuce",
34+
"http://tracker.btcake.com/announce",
35+
"http://tracker.ktxp.com:6868/announce",
36+
"http://tracker.ktxp.com:7070/announce",
37+
"http://bt.sc-ol.com:2710/announce",
38+
"http://btfile.sdo.com:6961/announce",
39+
"https://t-115.rhcloud.com/only_for_ylbud",
40+
"http://exodus.desync.com:6969/announce",
41+
"udp://coppersurfer.tk:6969/announce",
42+
"http://tracker3.torrentino.com/announce",
43+
"http://tracker2.torrentino.com/announce",
44+
"udp://open.demonii.com:1337/announce",
45+
"udp://tracker.ex.ua:80/announce",
46+
"http://pubt.net:2710/announce",
47+
"http://tracker.tfile.me/announce",
48+
"http://bigfoot1942.sektori.org:6969/announce",
49+
"udp://bt.sc-ol.com:2710/announce",
50+
"http://1337.abcvg.info:80/announce",
51+
"http://bt.okmp3.ru:2710/announce",
52+
"http://ipv6.rer.lol:6969/announce",
53+
"https://tr.burnabyhighstar.com:443/announce",
54+
"https://tracker.gbitt.info:443/announce",
55+
"https://tracker.gcrenwp.top:443/announce",
56+
"https://tracker.kuroy.me:443/announce",
57+
"https://tracker.lilithraws.org:443/announce",
58+
"https://tracker.loligirl.cn:443/announce",
59+
"https://tracker1.520.jp:443/announce",
60+
"udp://amigacity.xyz:6969/announce",
61+
"udp://bt1.archive.org:6969/announce",
62+
"udp://bt2.archive.org:6969/announce",
63+
"udp://epider.me:6969/announce",
64+
"wss://tracker.openwebtorrent.com:443/announce",
65+
]

0 commit comments

Comments
 (0)