Skip to content

Commit 9c85aab

Browse files
authored
refactor: introduce TorrentInfo dataclass and enhance testing (#20)
- Add TorrentInfo dataclass to encapsulate torrent metadata and standardize response formatting - Update torrent upload functions to handle response status consistently - Add comprehensive test cases for ru_torrent and jackett modules - Test URL parsing and error handling - Test label formatting with various inputs - Test torrent name encoding - Test response handling for different scenarios - Improve response formatting with consistent layout and units - Add type hints for better code clarity and safety - Clean up imports and string handling
1 parent 4a8fdbe commit 9c85aab

5 files changed

Lines changed: 245 additions & 69 deletions

File tree

feral_services/jackett.py

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,35 @@
22

33
import os
44
import random
5+
from dataclasses import dataclass
56

67
import requests
78
from requests.exceptions import Timeout
89

9-
1010
_TOTAL_RESULTS_TO_RETURN = 20
1111

1212

13+
@dataclass
14+
class TorrentInfo:
15+
name: str
16+
size: str
17+
seeds: int
18+
peers: int
19+
source: str
20+
magnet: str
21+
link: str
22+
23+
def format_response(self, req_id: str | None = None) -> str:
24+
prefix = f"/get{req_id} - " if req_id else "Success - "
25+
return (
26+
f"{prefix}{self.name}\n"
27+
f"└─ {self.source} | "
28+
f"Seeds: {self.seeds:,} | "
29+
f"Peers: {self.peers:,} | "
30+
f"Size: {self.size}"
31+
)
32+
33+
1334
def search(query) -> (str, list):
1435
params = (
1536
('apikey', os.getenv('JACKETT_API_KEY')),
@@ -67,28 +88,19 @@ def format_and_filter_results(results: list, user_id: int, user_id_to_results: d
6788
if count > 4:
6889
return 'id collision happened?...'
6990

70-
user_id_to_results[user_id][req_id] = {
71-
'magnet': result['MagnetUri'],
72-
'link': result['Link'],
73-
'label': result['Tracker'],
74-
'title': result['Title'],
75-
'size': round((result['Size'] / 1024 / 1024 / 1024), 2),
76-
}
77-
78-
details = (
79-
f"└─ {result['Tracker']} | "
80-
f"Seeds: {format(result['Seeders'], ',')} | "
81-
f"Peers: {format(result['Peers'], ',')} | "
82-
f"Size: {format(round(result['Size'] / 1024 / 1024 / 1024, 2), '.2f')} GB"
83-
)
84-
85-
formatted_result = (
86-
f"/get{req_id} - {result['Title']}\n"
87-
f"{details}"
91+
torrent_info = TorrentInfo(
92+
name=result['Title'],
93+
size=f"{round((result['Size'] / 1024 / 1024 / 1024), 2)} GB",
94+
seeds=result['Seeders'],
95+
peers=result['Peers'],
96+
source=result['Tracker'],
97+
magnet=result['MagnetUri'],
98+
link=result['Link']
8899
)
89100

90-
returned_results.append(formatted_result)
101+
user_id_to_results[user_id][req_id] = torrent_info
102+
returned_results.append(torrent_info.format_response(req_id))
91103

92-
result_count = f"Results ({len(returned_results)}/{len(results)})"
104+
result_count_str = f'Results ({len(returned_results)}/{len(results)})'
93105
returned_results_str = '\n\n'.join(returned_results)
94-
return f"{result_count}\n\n{returned_results_str}"
106+
return f'{result_count_str}\n\n{returned_results_str}'

feral_services/ru_torrent.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import bencodepy
88
import requests
99

10+
from feral_services.jackett import TorrentInfo
11+
1012
load_dotenv()
1113

1214

@@ -17,10 +19,10 @@
1719
def _format_return_url(url):
1820
parsed_url = urllib.parse.urlparse(url)
1921
query_params = urllib.parse.parse_qs(parsed_url.query)
20-
return query_params['result[]'][0]
22+
return query_params['result[]'][0].strip().casefold()
2123

2224

23-
def upload_torrent(torrent_file, label, username):
25+
def upload_torrent(torrent_file, label: str, username: str, torrent_info: TorrentInfo):
2426
metadata = bencodepy.decode(torrent_file)
2527
file_name = urllib.parse.quote(
2628
metadata[b'info'][b'name'].decode(), # noqa
@@ -37,11 +39,13 @@ def upload_torrent(torrent_file, label, username):
3739
)
3840

3941
if response.ok:
40-
return _format_return_url(response.url)
42+
status = _format_return_url(response.url)
43+
if status == "success":
44+
return torrent_info.format_response()
4145
return f'Error: {response.status_code}'
4246

4347

44-
def upload_magnet(magnet_link, label, username):
48+
def upload_magnet(magnet_link: str, label: str, username: str, torrent_info: TorrentInfo | None = None):
4549
if username:
4650
label = f'{username}, {label}'
4751

@@ -53,5 +57,10 @@ def upload_magnet(magnet_link, label, username):
5357
)
5458

5559
if response.ok:
56-
return _format_return_url(response.url)
57-
return f'Error: {response.status_code}'
60+
status = _format_return_url(response.url)
61+
if torrent_info:
62+
if status == "success":
63+
return torrent_info.format_response()
64+
else:
65+
return status.capitalize()
66+
return f'Error: {response.status_code}'

main.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
from feral_services import jackett
1717
from feral_services import ru_torrent
1818
from feral_services.instance import execute_command
19+
from feral_services.jackett import TorrentInfo
20+
1921
load_dotenv()
2022

2123
_RESULTS = {}
@@ -147,22 +149,23 @@ async def get(update: Update, _context):
147149
return
148150

149151
_, get_id = update.message.text.split('/get', 1)
150-
result = users_data.get(get_id)
152+
result: TorrentInfo = users_data.get(get_id)
151153
if not result:
152154
await update.message.reply_text('Not a valid item')
153155
return
154156

155157
username = update.effective_user.username or update.effective_user.first_name
156-
if magnet := result.get('magnet'):
158+
if magnet := result.magnet:
157159
magnet_upload_result = ru_torrent.upload_magnet(
158160
magnet,
159-
result['label'],
161+
result.source,
160162
username,
163+
result
161164
)
162165
await update.message.reply_text(magnet_upload_result)
163166
return
164167

165-
elif link := result.get('link'):
168+
elif link := result.link:
166169
url_response = requests.get(link, allow_redirects=False)
167170
if not url_response.ok:
168171
await update.message.reply_text(
@@ -171,23 +174,28 @@ async def get(update: Update, _context):
171174
)
172175
return
173176

174-
with contextlib.suppress(Exception):
177+
try:
175178
if url_response.status_code == 302:
176179
magnet_upload_result = ru_torrent.upload_magnet(
177180
url_response.headers['Location'],
178-
result['label'],
181+
result.source,
179182
username,
183+
result
180184
)
181185
await update.message.reply_text(magnet_upload_result)
182186
return
183187

184188
torrent_upload_result = ru_torrent.upload_torrent(
185189
url_response.content,
186-
result['label'],
190+
result.source,
187191
username,
192+
result
188193
)
189194
await update.message.reply_text(torrent_upload_result)
190195
return
196+
except Exception as e:
197+
print(e)
198+
191199

192200
await update.message.reply_text('Something went wrong')
193201

tests/jackett_test.py

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,28 @@ def test_format_and_filter_results(mocker):
8787

8888
user_id = 12345
8989
memory_database = {}
90-
results = _SHAWSHANK_RESULTS
90+
91+
results = [
92+
{
93+
'Title': 'The Shawshank Redemption 1994 REMASTERED 1080p BluRay H264 AAC-LAMA',
94+
'Size': 2910237066, # ~2.71GB
95+
'Seeders': 42,
96+
'Peers': 0,
97+
'Tracker': 'IPTorrents',
98+
'MagnetUri': 'magnet:test1',
99+
'Link': 'http://test1.com'
100+
},
101+
{
102+
'Title': 'The Shawshank Redemption 1994 REMASTERED 1080p BluRay H264 AAC R4RBG TGx',
103+
'Size': 2984725094, # ~2.78GB
104+
'Seeders': 84,
105+
'Peers': 15,
106+
'Tracker': '1337x',
107+
'MagnetUri': 'magnet:test2',
108+
'Link': 'http://test2.com'
109+
}
110+
]
111+
91112
formatted_results = jackett.format_and_filter_results(
92113
results, user_id, memory_database,
93114
)
@@ -96,20 +117,23 @@ def test_format_and_filter_results(mocker):
96117

97118
expected_result_count_str = 'Results (2/2)'
98119
expected_returned_results_str = (
99-
'/get11111 - IPTorrents, Seeds: 42, Peers: 0, Size: 2.71GB\n'
100-
'The Shawshank Redemption 1994 REMASTERED 1080p BluRay H264 AAC-LAMA\n\n' # noqa
101-
'/get22222 - 1337x, Seeds: 84, Peers: 15, Size: 2.78GB\n'
102-
'The Shawshank Redemption 1994 REMASTERED 1080p BluRay H264 AAC R4RBG TGx' # noqa
120+
'/get11111 - The Shawshank Redemption 1994 REMASTERED 1080p BluRay H264 AAC-LAMA\n'
121+
'└─ IPTorrents | Seeds: 42 | Peers: 0 | Size: 2.71 GB\n\n'
122+
'/get22222 - The Shawshank Redemption 1994 REMASTERED 1080p BluRay H264 AAC R4RBG TGx\n'
123+
'└─ 1337x | Seeds: 84 | Peers: 15 | Size: 2.78 GB'
103124
)
125+
104126
assert formatted_results.startswith(expected_result_count_str)
105127
assert expected_returned_results_str in formatted_results
106128

107129
assert len(memory_database) == 1
108130
assert isinstance(memory_database[user_id], dict)
109131
assert len(memory_database[user_id]) == 2
110-
for req_id, values in memory_database[user_id].items():
132+
133+
for req_id, torrent_info in memory_database[user_id].items():
111134
assert isinstance(req_id, str)
112-
assert isinstance(values, dict)
113-
assert set(values.keys()) == {
114-
'magnet', 'link', 'label', 'title', 'size',
115-
}
135+
assert isinstance(torrent_info, jackett.TorrentInfo)
136+
assert all(
137+
hasattr(torrent_info, attr)
138+
for attr in ['name', 'size', 'seeds', 'peers', 'source', 'magnet', 'link']
139+
)

0 commit comments

Comments
 (0)