Skip to content

Commit 4fb3f27

Browse files
committed
Add Kinozal (tracker) support and bump libs
1 parent 5989638 commit 4fb3f27

10 files changed

+86
-18
lines changed

.github/img/TorrUpd.jpg

14.3 KB
Loading

.github/img/TorrUpd.xcf

56 KB
Binary file not shown.

LICENSE

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2024 Konkere
3+
Copyright (c) 2023-2024 Konkere
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
![Torrent updater](/.github/img/TorrUpd.jpg)
22
Tool for automatically checking the relevance of torrents and updating them in the torrent client.
33

4-
Supports trackers: RuTracker and NNM-Club (hash comparison in topics) and TeamHD (torrent size comparison in RSS, login problem due to reCaptcha).
4+
Supports trackers: RuTracker and NNM-Club (hash comparison in topics), Kinozal (torrent size comparison in topics) and TeamHD (torrent size comparison in RSS, login problem due to reCaptcha).
55

66
Supports clients: qBittorrent, Transmission.
77

@@ -44,7 +44,7 @@ Fill data in files in ``$HOME/.config/TorrUpd/`` (or ``/PATH/TO/HOST/DIR`` for D
4444

4545
1.5. ``host``, ``username`` and ``password`` in client section (fill out separately for Transmission: ``protocol`` and ``port``).
4646

47-
1.6. in section ``[Settings]`` set ``client`` name and ``source`` for IDs (``file`` is default, ``client`` for check all torrents)
47+
1.6. in section ``[Settings]`` set ``client`` name and ``source`` for IDs (``client`` for check all torrents, ``file`` for limited checking list from file)
4848

4949
2. ``update.list``:
5050

config.py

+12-2
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ def __init__(self):
6161
'url': self.read_config('TeamHD', 'url'),
6262
'passkey': self.read_config('TeamHD', 'passkey'),
6363
},
64+
'kinozal': {
65+
'url': self.read_config('Kinozal', 'url'),
66+
'username': self.read_config('Kinozal', 'username'),
67+
'password': self.read_config('Kinozal', 'password'),
68+
},
6469
'qbittorrent': {
6570
'host': self.read_config('qBittorrent', 'host'),
6671
'username': self.read_config('qBittorrent', 'username'),
@@ -113,6 +118,10 @@ def create_config(self):
113118
self.config.add_section('TeamHD')
114119
self.config.set('TeamHD', 'url', 'https://teamhd.org')
115120
self.config.set('TeamHD', 'passkey', '1a2b3c4d5e6f7g8h9i0j10k11l12m13n')
121+
self.config.add_section('Kinozal')
122+
self.config.set('Kinozal', 'url', 'https://kinozal.tv')
123+
self.config.set('Kinozal', 'username', 'KTVUsername')
124+
self.config.set('Kinozal', 'password', 'KTVPassword')
116125
self.config.add_section('qBittorrent')
117126
self.config.set('qBittorrent', 'host', 'qBtHostURL:port')
118127
self.config.set('qBittorrent', 'username', 'qBtUsername')
@@ -125,13 +134,13 @@ def create_config(self):
125134
self.config.set('Transmission', 'password', 'TMPassword')
126135
self.config.add_section('Settings')
127136
self.config.set('Settings', 'client', 'qBittorrent')
128-
self.config.set('Settings', 'source', 'file')
137+
self.config.set('Settings', 'source', 'client')
129138
with open(self.config_file, 'w') as file:
130139
self.config.write(file)
131140
raise FileNotFoundError(f'Required to fill data in config: {self.config_file}')
132141

133142
def create_update_file(self):
134-
update_info = '[RuTracker]\n\n[NNMClub]\n\n[TeamHD]\n'
143+
update_info = '[RuTracker]\n\n[NNMClub]\n\n[TeamHD]\n\n[Kinozal]\n'
135144
with open(self.update_file, 'w') as file:
136145
file.write(update_info)
137146
raise FileNotFoundError(f'Required to fill list of topics id in: {self.update_file}')
@@ -145,6 +154,7 @@ def get_ids(self):
145154
'rutracker': [],
146155
'nnmclub': [],
147156
'teamhd': [],
157+
'kinozal': [],
148158
}
149159
if self.source == 'file':
150160
tracker_ids = get_ids_from_file(self.update_file, tracker_ids)

examples/settings.conf-dist

+6-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ password = NNMPassword
1313
url = https://teamhd.org
1414
passkey = 1a2b3c4d5e6f7g8h9i0j10k11l12m13n
1515

16+
[Kinozal]
17+
url = https://kinozal.tv
18+
username = KTVUsername
19+
password = KTVPassword
20+
1621
[qBittorrent]
1722
host = https://qBtHostURL:port
1823
username = qBtUsername
@@ -27,4 +32,4 @@ password = TMPassword
2732

2833
[Settings]
2934
client = qBittorrent
30-
source = file
35+
source = client

examples/update.list-dist

+3
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,6 @@
1313
91234 you know
1414
92345 mmm, cool
1515
#93456 Ignore again?
16+
17+
[Kinozal]
18+
8123456 Yeah!

requirements.txt

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
beautifulsoup4==4.12.2
1+
beautifulsoup4==4.12.3
22
bencoder.pyx==3.0.1
3-
certifi==2023.11.17
3+
certifi==2024.8.30
44
charset-normalizer==3.1.0
55
feedparser==6.0.11
6-
idna==3.4
6+
idna==3.10
77
packaging==23.2
8-
qbittorrent-api==2023.11.57
9-
requests==2.31.0
8+
qbittorrent-api==2024.9.66
9+
requests==2.32.3
1010
sgmllib3k==1.0.0
1111
six==1.16.0
1212
soupsieve==2.4.1
13-
transmission-rpc==7.0.3
13+
transmission-rpc==7.0.11
1414
typing_extensions==4.9.0
15-
urllib3==2.0.2
15+
urllib3==2.2.3

torrent_updater.py

+12-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from config import Conf
77
from bencoder import bdecode
88
from urllib.parse import urljoin
9-
from tracker import RuTracker, NNMClub, TeamHD, rss_parser
9+
from tracker import RuTracker, NNMClub, TeamHD, Kinozal, rss_parser
1010

1111

1212
def main():
@@ -16,15 +16,23 @@ def main():
1616
'rutracker': {
1717
'incarnation': RuTracker,
1818
'fingerprint': 'hash',
19+
'dl_from': 'topic',
1920
},
2021
'nnmclub': {
2122
'incarnation': NNMClub,
2223
'fingerprint': 'hash',
24+
'dl_from': 'topic',
2325
},
2426
'teamhd': {
2527
'incarnation': TeamHD,
2628
'fingerprint': 'size',
27-
'rssjoin': 'rss.php?feed=dl&passkey='
29+
'rssjoin': 'rss.php?feed=dl&passkey=',
30+
'dl_from': 'feed',
31+
},
32+
'kinozal': {
33+
'incarnation': Kinozal,
34+
'fingerprint': 'size',
35+
'dl_from': 'topic',
2836
},
2937
}
3038
logging.basicConfig(
@@ -35,8 +43,8 @@ def main():
3543
)
3644
for tracker in config.tracker_ids.keys():
3745
sessions[tracker] = None
38-
if trackers[tracker]['fingerprint'] == 'size' and config.tracker_ids[tracker]:
39-
rss_url = urljoin(config.auth[tracker]["url"], trackers[tracker]["rssjoin"])
46+
if trackers[tracker]['dl_from'] == 'feed' and config.tracker_ids[tracker]:
47+
rss_url = urljoin(config.auth[tracker]['url'], trackers[tracker]['rssjoin'])
4048
rss_url = f'{rss_url}{config.auth[tracker]["passkey"]}'
4149
config.tracker_ids[tracker] = rss_parser(rss_url, config.tracker_ids[tracker])
4250
for tracker in config.tracker_ids.keys():

tracker.py

+43-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from bs4 import BeautifulSoup
77
from bencoder import bencode, bdecode
88
from feedparser import parse as feed_parse
9-
from urllib.parse import urljoin, urlsplit, urlunsplit
9+
from urllib.parse import urljoin, urlsplit, urlunsplit, urlparse
1010

1111

1212
def rss_parser(rss_url, ids):
@@ -41,6 +41,13 @@ def extract_base_url(url):
4141
return str(base_url)
4242

4343

44+
def add_subdomain(url, subdomain):
45+
scheme = urlparse(url).scheme
46+
netloc = urlparse(url).netloc
47+
url_sub = urlunsplit((scheme, f'{subdomain}.{netloc}', '', '', ''))
48+
return url_sub
49+
50+
4451
class Tracker:
4552

4653
def __init__(self, auth, topic_id, session=None):
@@ -153,3 +160,38 @@ def download_torrent(self):
153160
response = requests.get(self.download_url)
154161
torrent = response.content
155162
return torrent
163+
164+
165+
class Kinozal(Tracker):
166+
167+
def __init__(self, *args, **kwargs):
168+
super().__init__(*args, **kwargs)
169+
self.post_params['username'] = self.auth['username'].encode('Windows-1251')
170+
self.post_params['password'] = self.auth['password']
171+
del self.post_params['login']
172+
self.base_url = self.auth['url']
173+
self.login_url = urljoin(self.base_url, 'takelogin.php')
174+
self.topic_url = urljoin(self.base_url, f'details.php?id={self.topic_id}')
175+
self.download_url = urljoin(
176+
add_subdomain(self.base_url, 'dl'), f'download.php?id={self.topic_id}'
177+
)
178+
self.fingerprint = self.get_actual_weight()
179+
180+
def download_torrent(self):
181+
if not self.session:
182+
self.create_session()
183+
torrent = self.session.get(self.download_url).content
184+
return torrent
185+
186+
def get_actual_weight(self):
187+
response = requests.get(self.topic_url)
188+
topic = BeautifulSoup(response.text, features='html.parser')
189+
try:
190+
weight_field = topic.find('span', {'class': 'floatright green n'}).get_text()
191+
except AttributeError:
192+
weight = ''
193+
else:
194+
pattern = r'^[\s\./d\w]*\(([\d\,]*)\)$'
195+
weight = re.match(pattern, weight_field).group(1)
196+
weight = weight.replace(',', '')
197+
return weight

0 commit comments

Comments
 (0)