Unit3Dup 0.11.10__tar.gz → 0.12.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {unit3dup-0.11.10 → unit3dup-0.12.0}/PKG-INFO +2 -2
- {unit3dup-0.11.10 → unit3dup-0.12.0}/Unit3Dup.egg-info/PKG-INFO +2 -2
- {unit3dup-0.11.10 → unit3dup-0.12.0}/Unit3Dup.egg-info/requires.txt +1 -1
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/command.py +5 -2
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/settings.py +7 -3
- unit3dup-0.12.0/common/torrent_clients.py +281 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/trackers/signs_list.py +24 -8
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/trackers/tags_list.py +1 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/pyproject.toml +2 -2
- {unit3dup-0.11.10 → unit3dup-0.12.0}/requirements.txt +1 -1
- {unit3dup-0.11.10 → unit3dup-0.12.0}/unit3dup/__main__.py +1 -4
- {unit3dup-0.11.10 → unit3dup-0.12.0}/unit3dup/media_manager/TorrentManager.py +0 -3
- {unit3dup-0.11.10 → unit3dup-0.12.0}/unit3dup/media_manager/common.py +17 -23
- {unit3dup-0.11.10 → unit3dup-0.12.0}/unit3dup/pvtTracker.py +1 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/unit3dup/upload.py +4 -1
- unit3dup-0.11.10/common/torrent_clients.py +0 -275
- {unit3dup-0.11.10 → unit3dup-0.12.0}/LICENSE +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/README.rst +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/Unit3Dup.egg-info/SOURCES.txt +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/Unit3Dup.egg-info/dependency_links.txt +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/Unit3Dup.egg-info/entry_points.txt +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/Unit3Dup.egg-info/top_level.txt +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/__init__.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/bdinfo_string.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/bittorrent.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/constants.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/database.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/__init__.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/ftpx/__init__.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/ftpx/client.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/ftpx/core/__init__.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/ftpx/core/ftpx_service.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/ftpx/core/ftpx_session.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/ftpx/core/menu.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/ftpx/core/models/__init__.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/ftpx/core/models/list.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/igdb/__init__.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/igdb/client.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/igdb/core/__init__.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/igdb/core/api.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/igdb/core/models/__init__.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/igdb/core/models/search.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/igdb/core/platformid.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/igdb/core/tags.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/imageHost.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/mediaresult.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/sessions/__init__.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/sessions/agents.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/sessions/exceptions.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/sessions/session.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/theMovieDB/__init__.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/theMovieDB/core/__init__.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/theMovieDB/core/api.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/theMovieDB/core/keywords.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/theMovieDB/core/models/__init__.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/theMovieDB/core/models/movie/__init__.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/theMovieDB/core/models/movie/alternative_titles.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/theMovieDB/core/models/movie/details.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/theMovieDB/core/models/movie/movie.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/theMovieDB/core/models/movie/nowplaying.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/theMovieDB/core/models/movie/release_info.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/theMovieDB/core/models/tvshow/__init__.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/theMovieDB/core/models/tvshow/alternative.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/theMovieDB/core/models/tvshow/details.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/theMovieDB/core/models/tvshow/on_the_air.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/theMovieDB/core/models/tvshow/translations.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/theMovieDB/core/models/tvshow/tvshow.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/theMovieDB/core/videos.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/trailers/__init__.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/trailers/api.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/trailers/response.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/external_services/tvdb.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/extractor.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/frames.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/mediainfo.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/mediainfo_string.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/tags.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/title.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/trackers/__init__.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/trackers/ast.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/trackers/ban_list.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/trackers/data.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/trackers/itt.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/trackers/ptt.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/trackers/sis.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/trackers/trackers.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/common/utility.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/setup.cfg +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/unit3dup/__init__.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/unit3dup/automode.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/unit3dup/bot.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/unit3dup/duplicate.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/unit3dup/exceptions.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/unit3dup/media.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/unit3dup/media_manager/ContentManager.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/unit3dup/media_manager/DocuManager.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/unit3dup/media_manager/GameManager.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/unit3dup/media_manager/MediaInfoManager.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/unit3dup/media_manager/SeedManager.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/unit3dup/media_manager/VideoManager.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/unit3dup/media_manager/__init__.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/unit3dup/pvtDocu.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/unit3dup/pvtTorrent.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/unit3dup/pvtVideo.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/unit3dup/torrent.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/view/__init__.py +0 -0
- {unit3dup-0.11.10 → unit3dup-0.12.0}/view/custom_console.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Unit3Dup
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.12.0
|
|
4
4
|
Summary: An uploader for the Unit3D torrent tracker
|
|
5
5
|
Author: Parzival
|
|
6
6
|
License-Expression: MIT
|
|
@@ -10,7 +10,7 @@ Description-Content-Type: text/x-rst
|
|
|
10
10
|
License-File: LICENSE
|
|
11
11
|
Requires-Dist: guessit==3.8.0
|
|
12
12
|
Requires-Dist: pymediainfo==6.1.0
|
|
13
|
-
Requires-Dist:
|
|
13
|
+
Requires-Dist: qbittorrent-api==2026.6.0
|
|
14
14
|
Requires-Dist: pydantic==2.10.6
|
|
15
15
|
Requires-Dist: requests==2.32.3
|
|
16
16
|
Requires-Dist: rich==13.7.1
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Unit3Dup
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.12.0
|
|
4
4
|
Summary: An uploader for the Unit3D torrent tracker
|
|
5
5
|
Author: Parzival
|
|
6
6
|
License-Expression: MIT
|
|
@@ -10,7 +10,7 @@ Description-Content-Type: text/x-rst
|
|
|
10
10
|
License-File: LICENSE
|
|
11
11
|
Requires-Dist: guessit==3.8.0
|
|
12
12
|
Requires-Dist: pymediainfo==6.1.0
|
|
13
|
-
Requires-Dist:
|
|
13
|
+
Requires-Dist: qbittorrent-api==2026.6.0
|
|
14
14
|
Requires-Dist: pydantic==2.10.6
|
|
15
15
|
Requires-Dist: requests==2.32.3
|
|
16
16
|
Requires-Dist: rich==13.7.1
|
|
@@ -88,6 +88,9 @@ class CommandLine:
|
|
|
88
88
|
search_group.add_argument("-d", "--description", type=str, help="By description")
|
|
89
89
|
search_group.add_argument("-bd", "--bdinfo", type=str, help="Show BDInfo")
|
|
90
90
|
search_group.add_argument("-m", "--mediainfo", type=str, help="Show MediaInfo")
|
|
91
|
+
search_group.add_argument("-int", "--internal", action="store_true", help="Internal Release")
|
|
92
|
+
search_group.add_argument("-mod", "--moderation", action="store_true", help="In moderation")
|
|
93
|
+
|
|
91
94
|
|
|
92
95
|
# /////////////////////////
|
|
93
96
|
# Filter Options
|
|
@@ -123,7 +126,7 @@ class CommandLine:
|
|
|
123
126
|
special_group.add_argument("-str", "--stream", action="store_true", help="Stream")
|
|
124
127
|
special_group.add_argument("-sd", "--standard", action="store_true", help="SD")
|
|
125
128
|
special_group.add_argument("-hs", "--highspeed", action="store_true", help="Highspeed")
|
|
126
|
-
special_group.add_argument("-
|
|
129
|
+
special_group.add_argument("-inter", "--intern_r", action="store_true", help="Internal Release")
|
|
127
130
|
special_group.add_argument("-pr", "--prelease", action="store_true", help="Personal")
|
|
128
131
|
|
|
129
132
|
# /////////////////////////
|
|
@@ -159,4 +162,4 @@ class CommandLine:
|
|
|
159
162
|
if self.args.force.lower() not in valid_categories:
|
|
160
163
|
self.args.force = None
|
|
161
164
|
print("Invalid -force category")
|
|
162
|
-
exit()
|
|
165
|
+
exit()
|
|
@@ -24,7 +24,7 @@ user_tags_file = "tags_list.json"
|
|
|
24
24
|
user_sign_file = "sign_list.json"
|
|
25
25
|
bane_file = "ban_list.json"
|
|
26
26
|
|
|
27
|
-
version = "0.
|
|
27
|
+
version = "0.12.0"
|
|
28
28
|
|
|
29
29
|
if os.name == "nt":
|
|
30
30
|
WATCHER_DESTINATION_PATH: Path = Path(
|
|
@@ -112,6 +112,8 @@ class TorrentClientConfig(BaseModel):
|
|
|
112
112
|
SHARED_RTORR_PATH: str | None = None
|
|
113
113
|
TORRENT_CLIENT: str | None = None
|
|
114
114
|
TAG: str | None = None
|
|
115
|
+
CATEGORY_MOVIE: str | None = None
|
|
116
|
+
CATEGORY_TV: str | None = None
|
|
115
117
|
|
|
116
118
|
|
|
117
119
|
class UserPreferences(BaseModel):
|
|
@@ -452,7 +454,7 @@ class Config(BaseModel):
|
|
|
452
454
|
section[field] = Validate.integer(value=section[field], field_name=field)
|
|
453
455
|
|
|
454
456
|
if field in ['QBIT_PASS', 'TRASM_PASS', 'RTORR_PASS', 'QBIT_USER', 'TRASM_USER', 'RTORR_USER',
|
|
455
|
-
'TORRENT_CLIENT', 'TAG', 'RTORR_HOST']:
|
|
457
|
+
'TORRENT_CLIENT', 'TAG', 'CATEGORY_MOVIE','CATEGORY_TV', 'RTORR_HOST']:
|
|
456
458
|
section[field] = Validate.string(value=section[field], field_name=field)
|
|
457
459
|
|
|
458
460
|
if field in ['SHARED_TRASM_PATH', 'SHARED_QBIT_PATH', 'SHARED_RTORR_PATH']:
|
|
@@ -622,7 +624,9 @@ class Load:
|
|
|
622
624
|
"RTORR_PORT": "9091",
|
|
623
625
|
"SHARED_RTORR_PATH": "no_path",
|
|
624
626
|
"TORRENT_CLIENT": "qbittorrent",
|
|
625
|
-
"TAG": "ADDED TORRENTS"
|
|
627
|
+
"TAG": "ADDED TORRENTS",
|
|
628
|
+
"CATEGORY_MOVIE": "movie",
|
|
629
|
+
"CATEGORY_TV": "tv"
|
|
626
630
|
},
|
|
627
631
|
"user_preferences": {
|
|
628
632
|
"PTSCREENS_PRIORITY": 0,
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
import hashlib
|
|
3
|
+
import os
|
|
4
|
+
import stat
|
|
5
|
+
import time
|
|
6
|
+
import bencode2
|
|
7
|
+
import requests
|
|
8
|
+
from requests import Response
|
|
9
|
+
from rtorrent_rpc import RTorrent
|
|
10
|
+
|
|
11
|
+
from abc import ABC, abstractmethod
|
|
12
|
+
|
|
13
|
+
import qbittorrentapi
|
|
14
|
+
import transmission_rpc
|
|
15
|
+
|
|
16
|
+
from unit3dup.pvtTorrent import Mytorrent
|
|
17
|
+
from unit3dup import config_settings
|
|
18
|
+
from unit3dup.media import Media
|
|
19
|
+
|
|
20
|
+
from view import custom_console
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class TorrClient(ABC):
|
|
24
|
+
|
|
25
|
+
def __init__(self):
|
|
26
|
+
self.client = None
|
|
27
|
+
|
|
28
|
+
@abstractmethod
|
|
29
|
+
def connect(self):
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
@abstractmethod
|
|
33
|
+
def send_to_client(self, tracker_data_response: str, torrent: Mytorrent, content: Media, archive_path: str):
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
@staticmethod
|
|
37
|
+
def download(tracker_torrent_url: Response, full_path_archive: str):
|
|
38
|
+
# File archived
|
|
39
|
+
with open(full_path_archive, "wb") as file:
|
|
40
|
+
file.write(tracker_torrent_url.content)
|
|
41
|
+
|
|
42
|
+
# Ready for seeding
|
|
43
|
+
return open(full_path_archive, "rb")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class TransmissionClient(TorrClient):
|
|
47
|
+
def __init__(self) -> None:
|
|
48
|
+
super().__init__()
|
|
49
|
+
|
|
50
|
+
def connect(self) -> transmission_rpc.Client | None:
|
|
51
|
+
try:
|
|
52
|
+
self.client = transmission_rpc.Client(host=config_settings.torrent_client_config.TRASM_HOST,
|
|
53
|
+
port=config_settings.torrent_client_config.TRASM_PORT,
|
|
54
|
+
username=config_settings.torrent_client_config.TRASM_USER,
|
|
55
|
+
password=config_settings.torrent_client_config.TRASM_PASS,
|
|
56
|
+
timeout=10)
|
|
57
|
+
return self.client
|
|
58
|
+
except requests.exceptions.HTTPError:
|
|
59
|
+
custom_console.bot_error_log(
|
|
60
|
+
f"{self.__class__.__name__} HTTP Error. Check IP/port or run Transmission"
|
|
61
|
+
)
|
|
62
|
+
except requests.exceptions.ConnectionError:
|
|
63
|
+
custom_console.bot_error_log(
|
|
64
|
+
f"{self.__class__.__name__} Connection Error. Check IP/port or run Transmission"
|
|
65
|
+
)
|
|
66
|
+
except transmission_rpc.TransmissionError:
|
|
67
|
+
custom_console.bot_error_log(
|
|
68
|
+
f"{self.__class__.__name__} Login required. Check your username and password"
|
|
69
|
+
)
|
|
70
|
+
except Exception as e:
|
|
71
|
+
custom_console.bot_error_log(f"{self.__class__.__name__} Unexpected error: {e}")
|
|
72
|
+
custom_console.bot_error_log(f"{self.__class__.__name__} Please verify your configuration")
|
|
73
|
+
|
|
74
|
+
def send_to_client(self, tracker_data_response: str, torrent: Mytorrent, content: Media, archive_path: str):
|
|
75
|
+
# "Translate" files location to shared_path if necessary
|
|
76
|
+
if config_settings.torrent_client_config.SHARED_QBIT_PATH:
|
|
77
|
+
torr_location = config_settings.torrent_client_config.SHARED_QBIT_PATH
|
|
78
|
+
else:
|
|
79
|
+
# If no shared_path is specified set it to the path specified in the CLI commands (path)
|
|
80
|
+
torr_location = os.path.dirname(content.torrent_path)
|
|
81
|
+
|
|
82
|
+
# Send to the client
|
|
83
|
+
with open(archive_path, "rb") as file_buffer:
|
|
84
|
+
self.client.add_torrent(torrent=file_buffer, download_dir=str(torr_location))
|
|
85
|
+
|
|
86
|
+
def send_file_to_client(self, torrent_path: str):
|
|
87
|
+
self.client.add_torrent(torrent=open(torrent_path, "rb"), download_dir=str(os.path.dirname(torrent_path)))
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class QbittorrentClient(TorrClient):
|
|
91
|
+
|
|
92
|
+
def __init__(self):
|
|
93
|
+
super().__init__()
|
|
94
|
+
|
|
95
|
+
def connect(self) -> qbittorrentapi.Client | None:
|
|
96
|
+
try:
|
|
97
|
+
self.client = qbittorrentapi.Client(
|
|
98
|
+
host=config_settings.torrent_client_config.QBIT_HOST,
|
|
99
|
+
port=config_settings.torrent_client_config.QBIT_PORT,
|
|
100
|
+
username=config_settings.torrent_client_config.QBIT_USER,
|
|
101
|
+
password=config_settings.torrent_client_config.QBIT_PASS,
|
|
102
|
+
REQUESTS_ARGS={"timeout": 10},
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
login_count = 0
|
|
106
|
+
|
|
107
|
+
while True:
|
|
108
|
+
try:
|
|
109
|
+
self.client.auth_log_in()
|
|
110
|
+
break
|
|
111
|
+
|
|
112
|
+
except qbittorrentapi.LoginFailed:
|
|
113
|
+
|
|
114
|
+
if login_count > 5:
|
|
115
|
+
custom_console.bot_error_log("Failed to login.")
|
|
116
|
+
exit(1)
|
|
117
|
+
|
|
118
|
+
custom_console.bot_warning_log(
|
|
119
|
+
"Qbittorrent failed to login. Retry...Please wait"
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
time.sleep(2)
|
|
123
|
+
login_count += 1
|
|
124
|
+
|
|
125
|
+
return self.client
|
|
126
|
+
|
|
127
|
+
except requests.exceptions.HTTPError:
|
|
128
|
+
custom_console.bot_error_log(
|
|
129
|
+
f"{self.__class__.__name__} HTTP Error. "
|
|
130
|
+
f"Check IP/port or run qBittorrent"
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
except requests.exceptions.ConnectionError:
|
|
134
|
+
custom_console.bot_error_log(
|
|
135
|
+
f"{self.__class__.__name__} Connection Error. "
|
|
136
|
+
f"Check IP/port or run qBittorrent"
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
except qbittorrentapi.LoginFailed:
|
|
140
|
+
custom_console.bot_error_log(
|
|
141
|
+
f"{self.__class__.__name__} Login required. "
|
|
142
|
+
f"Check your username and password"
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
except Exception as e:
|
|
146
|
+
custom_console.bot_error_log(
|
|
147
|
+
f"{self.__class__.__name__} Unexpected error: {e}"
|
|
148
|
+
)
|
|
149
|
+
custom_console.bot_error_log(
|
|
150
|
+
f"{self.__class__.__name__} Please verify your configuration"
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
def _add_torrent_and_tag(self, torrent_bytes: bytes, save_path: str, info_hash: str,category: str) -> None:
|
|
154
|
+
|
|
155
|
+
self.client.torrents_add(torrent_files=torrent_bytes,save_path=save_path)
|
|
156
|
+
|
|
157
|
+
# wait for torrent to be registered
|
|
158
|
+
for _ in range(20):
|
|
159
|
+
try:
|
|
160
|
+
torrents = self.client.torrents_info(torrent_hashes=info_hash)
|
|
161
|
+
if torrents:
|
|
162
|
+
break
|
|
163
|
+
except Exception:
|
|
164
|
+
pass
|
|
165
|
+
time.sleep(0.25)
|
|
166
|
+
|
|
167
|
+
if category=='movie':
|
|
168
|
+
category = config_settings.torrent_client_config.CATEGORY_MOVIE
|
|
169
|
+
if category=='tv':
|
|
170
|
+
category = config_settings.torrent_client_config.CATEGORY_MOVIE
|
|
171
|
+
self.client.torrents_add_tags(tags=config_settings.torrent_client_config.TAG, torrent_hashes=info_hash)
|
|
172
|
+
self.client.torrents_set_category(category=category, torrent_hashes=info_hash)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def send_to_client(self, tracker_data_response: str, torrent: Mytorrent, content: Media,archive_path: str):
|
|
176
|
+
|
|
177
|
+
if config_settings.torrent_client_config.SHARED_QBIT_PATH:
|
|
178
|
+
torr_location = (
|
|
179
|
+
config_settings.torrent_client_config.SHARED_QBIT_PATH
|
|
180
|
+
)
|
|
181
|
+
else:
|
|
182
|
+
torr_location = os.path.dirname(content.torrent_path)
|
|
183
|
+
|
|
184
|
+
if not torrent:
|
|
185
|
+
with open(archive_path, "rb") as file_buffer:
|
|
186
|
+
torrent_data = file_buffer.read()
|
|
187
|
+
|
|
188
|
+
info = bencode2.bdecode(torrent_data)[b"info"]
|
|
189
|
+
info_hash = hashlib.sha1(bencode2.bencode(info)).hexdigest()
|
|
190
|
+
file_buffer.seek(0)
|
|
191
|
+
self._add_torrent_and_tag(torrent_bytes=file_buffer.read(),save_path=str(torr_location),
|
|
192
|
+
info_hash=info_hash, category = content.category)
|
|
193
|
+
else:
|
|
194
|
+
info = torrent.mytorr.metainfo["info"]
|
|
195
|
+
info_hash = hashlib.sha1(
|
|
196
|
+
bencode2.bencode(info)
|
|
197
|
+
).hexdigest()
|
|
198
|
+
with open(archive_path, "rb") as file_buffer:
|
|
199
|
+
self._add_torrent_and_tag(torrent_bytes=file_buffer.read(),save_path=str(torr_location),
|
|
200
|
+
info_hash=info_hash, category = content.category)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
class RTorrentClient(TorrClient):
|
|
204
|
+
def __init__(self):
|
|
205
|
+
super().__init__()
|
|
206
|
+
|
|
207
|
+
def connect(self) -> RTorrent | None:
|
|
208
|
+
|
|
209
|
+
# Build the socket string for rTorrent
|
|
210
|
+
# Tcp or File
|
|
211
|
+
if os.path.exists(config_settings.torrent_client_config.RTORR_HOST):
|
|
212
|
+
socket_type = os.stat(config_settings.torrent_client_config.RTORR_HOST).st_mode
|
|
213
|
+
if stat.S_ISSOCK(socket_type):
|
|
214
|
+
socket = f"scgi:///{config_settings.torrent_client_config.RTORR_HOST}"
|
|
215
|
+
else:
|
|
216
|
+
custom_console.bot_error_log("Invalid RTorrent host")
|
|
217
|
+
exit(1)
|
|
218
|
+
else:
|
|
219
|
+
socket = (f"scgi://{config_settings.torrent_client_config.RTORR_HOST}:"
|
|
220
|
+
f"{config_settings.torrent_client_config.RTORR_PORT}")
|
|
221
|
+
|
|
222
|
+
login_count = 0
|
|
223
|
+
while True:
|
|
224
|
+
try:
|
|
225
|
+
# open
|
|
226
|
+
self.client = RTorrent(address=socket, timeout=10)
|
|
227
|
+
# Test
|
|
228
|
+
self.client.system_list_methods()
|
|
229
|
+
return self.client
|
|
230
|
+
except requests.exceptions.HTTPError:
|
|
231
|
+
custom_console.bot_warning_log("Rtorrent failed to login. Retry...Please wait")
|
|
232
|
+
time.sleep(2)
|
|
233
|
+
login_count += 1
|
|
234
|
+
if login_count > 5:
|
|
235
|
+
custom_console.bot_error_log("Rtorrent failed to login.")
|
|
236
|
+
exit()
|
|
237
|
+
except requests.exceptions.ConnectionError:
|
|
238
|
+
custom_console.bot_error_log(
|
|
239
|
+
f"{self.__class__.__name__} Connection Error. Check IP/port or run rTorrent"
|
|
240
|
+
)
|
|
241
|
+
exit()
|
|
242
|
+
except TimeoutError:
|
|
243
|
+
custom_console.bot_error_log(
|
|
244
|
+
f"{self.__class__.__name__} Connection Error. Check IP/port or run rTorrent"
|
|
245
|
+
)
|
|
246
|
+
exit()
|
|
247
|
+
except AttributeError:
|
|
248
|
+
custom_console.bot_error_log(
|
|
249
|
+
f"{self.__class__.__name__} Socket connection error or wrong OS platform"
|
|
250
|
+
)
|
|
251
|
+
exit()
|
|
252
|
+
except ConnectionRefusedError:
|
|
253
|
+
custom_console.bot_error_log(
|
|
254
|
+
f"{self.__class__.__name__} Connection refused"
|
|
255
|
+
)
|
|
256
|
+
exit()
|
|
257
|
+
|
|
258
|
+
def send_to_client(self, tracker_data_response: str, torrent: Mytorrent, content: Media, archive_path: str):
|
|
259
|
+
# "Translate" files location to shared_path if necessary
|
|
260
|
+
if config_settings.torrent_client_config.SHARED_RTORR_PATH:
|
|
261
|
+
torr_location = config_settings.torrent_client_config.SHARED_RTORR_PATH
|
|
262
|
+
else:
|
|
263
|
+
# If no shared_path is specified set it to the path specified in the CLI commands (path)
|
|
264
|
+
torr_location = os.path.dirname(content.torrent_path)
|
|
265
|
+
|
|
266
|
+
# Add the torrent folder needed for rTorrent
|
|
267
|
+
if os.path.isdir(content.subfolder):
|
|
268
|
+
torr_location = os.path.join(torr_location, content.torrent_name)
|
|
269
|
+
# Save path for Windows or Linux.The root directory (/mnt or c:\) is the responsibility of the user
|
|
270
|
+
# in shared_folder
|
|
271
|
+
torr_location = torr_location.replace('\\', '/')
|
|
272
|
+
|
|
273
|
+
# Read and send
|
|
274
|
+
with open(archive_path, "rb") as file:
|
|
275
|
+
self.client.add_torrent_by_file(content=file.read(), directory_base=str(torr_location),
|
|
276
|
+
tags=[config_settings.torrent_client_config.TAG])
|
|
277
|
+
|
|
278
|
+
def send_file_to_client(self, torrent_path: str, media_location: str):
|
|
279
|
+
with open(torrent_path, "rb") as file:
|
|
280
|
+
self.client.add_torrent_by_file(content=file.read(), directory_base=str(media_location),
|
|
281
|
+
tags=[config_settings.torrent_client_config.TAG])
|
|
@@ -21,6 +21,7 @@ SIGNS_LIST = {
|
|
|
21
21
|
"BITSHIFT": "releaser",
|
|
22
22
|
"BLACKBIT": "releaser",
|
|
23
23
|
"BL4CK-B4RT-MIRCREW": "releaser",
|
|
24
|
+
"BLOOM": "releaser",
|
|
24
25
|
"BLUWORLD": "releaser",
|
|
25
26
|
"BOBDOBBS": "releaser",
|
|
26
27
|
"BONE": "releaser",
|
|
@@ -34,6 +35,9 @@ SIGNS_LIST = {
|
|
|
34
35
|
"CHD": "releaser",
|
|
35
36
|
"CHDWEB": "releaser",
|
|
36
37
|
"CIAME": "releaser",
|
|
38
|
+
"CINEPHILES": "releaser",
|
|
39
|
+
"CINEPTH": "releaser",
|
|
40
|
+
"CINEWEB": "releaser",
|
|
37
41
|
"CLOCLONE": "releaser",
|
|
38
42
|
"CODEX": "releaser",
|
|
39
43
|
"COMIX21": "releaser",
|
|
@@ -57,6 +61,7 @@ SIGNS_LIST = {
|
|
|
57
61
|
"DOCH74": "releaser",
|
|
58
62
|
"DR4GON": "releaser",
|
|
59
63
|
"DRIIP": "releaser",
|
|
64
|
+
"DRPLEB": "releaser",
|
|
60
65
|
"DST": "releaser",
|
|
61
66
|
"DYNUX": "releaser",
|
|
62
67
|
"EA": "releaser",
|
|
@@ -89,6 +94,7 @@ SIGNS_LIST = {
|
|
|
89
94
|
"GFM": "releaser",
|
|
90
95
|
"GIUSEPPETNT": "releaser",
|
|
91
96
|
"GOLDENTORRENT": "releaser",
|
|
97
|
+
"GRAPPA": "releaser",
|
|
92
98
|
"GRYM": "releaser",
|
|
93
99
|
"HAPPITEAM-YNK": "releaser",
|
|
94
100
|
"HASHDEV": "releaser",
|
|
@@ -107,6 +113,7 @@ SIGNS_LIST = {
|
|
|
107
113
|
"IDN-CREW": "releaser",
|
|
108
114
|
"IGS": "releaser",
|
|
109
115
|
"ILGANZO": "releaser",
|
|
116
|
+
"ILENIA": "releaser",
|
|
110
117
|
"ILPADRINO": "releaser",
|
|
111
118
|
"ILSAGGIO": "releaser",
|
|
112
119
|
"ILSOMMO": "releaser",
|
|
@@ -129,6 +136,7 @@ SIGNS_LIST = {
|
|
|
129
136
|
"KUCHU": "releaser",
|
|
130
137
|
"LAZY-SUBS": "releaser",
|
|
131
138
|
"LATENEVER": "releaser",
|
|
139
|
+
"LAXII": "releaser",
|
|
132
140
|
"LDI": "releaser",
|
|
133
141
|
"LEAGUENF": "releaser",
|
|
134
142
|
"LELE753": "releaser",
|
|
@@ -138,20 +146,20 @@ SIGNS_LIST = {
|
|
|
138
146
|
"LOZIO-MIRCREW": "releaser",
|
|
139
147
|
"LULLOZZO": "releaser",
|
|
140
148
|
"LZ": "releaser",
|
|
141
|
-
"LAXII": "releaser",
|
|
142
149
|
"LZ59": "releaser",
|
|
143
150
|
"MADHEX": "releaser",
|
|
144
151
|
"MADSKY": "releaser",
|
|
145
152
|
"MADTIA": "releaser",
|
|
146
153
|
"MAX": "releaser",
|
|
147
|
-
"MEM.GP" : "releaser",
|
|
148
|
-
"MEMGP": "releaser",
|
|
149
|
-
"MEM": "releaser",
|
|
150
154
|
"MAX2014": "releaser",
|
|
151
155
|
"ME7ALH": "releaser",
|
|
156
|
+
"MEM": "releaser",
|
|
157
|
+
"MEM.GP": "releaser",
|
|
158
|
+
"MEMGP": "releaser",
|
|
152
159
|
"MIK0YAN": "releaser",
|
|
153
160
|
"MIRCREW": "releaser",
|
|
154
161
|
"MISTERNED": "releaser",
|
|
162
|
+
"MOORKAT": "releaser",
|
|
155
163
|
"MORPHEUS": "releaser",
|
|
156
164
|
"MRBLONDE": "releaser",
|
|
157
165
|
"MRROBOT": "releaser",
|
|
@@ -166,9 +174,11 @@ SIGNS_LIST = {
|
|
|
166
174
|
"NOMADS": "releaser",
|
|
167
175
|
"NOVARIP": "releaser",
|
|
168
176
|
"NTB": "releaser",
|
|
177
|
+
"NTG": "releaser",
|
|
169
178
|
"NTROPIC": "releaser",
|
|
170
179
|
"ODS": "releaser",
|
|
171
180
|
"ODINO": "releaser",
|
|
181
|
+
"OFT": "releaser",
|
|
172
182
|
"ONGOIA": "releaser",
|
|
173
183
|
"PASO77": "releaser",
|
|
174
184
|
"PEPPE": "releaser",
|
|
@@ -178,6 +188,7 @@ SIGNS_LIST = {
|
|
|
178
188
|
"PING": "releaser",
|
|
179
189
|
"PINKER": "releaser",
|
|
180
190
|
"PIR8": "releaser",
|
|
191
|
+
"PLAYTV": "releaser",
|
|
181
192
|
"PLAYWEB": "releaser",
|
|
182
193
|
"PLUSAM": "releaser",
|
|
183
194
|
"POKE": "releaser",
|
|
@@ -187,10 +198,12 @@ SIGNS_LIST = {
|
|
|
187
198
|
"RADESE": "releaser",
|
|
188
199
|
"RAPTA": "releaser",
|
|
189
200
|
"RARBG": "releaser",
|
|
201
|
+
"RAMSEIS": "releaser",
|
|
190
202
|
"RAWR": "releaser",
|
|
191
203
|
"REALDMDJ": "releaser",
|
|
192
204
|
"RIDDICKK": "releaser",
|
|
193
205
|
"RMTEAM": "releaser",
|
|
206
|
+
"R00T": "releaser",
|
|
194
207
|
"ROMEO": "releaser",
|
|
195
208
|
"RUNE": "releaser",
|
|
196
209
|
"RYUKXX": "releaser",
|
|
@@ -207,6 +220,7 @@ SIGNS_LIST = {
|
|
|
207
220
|
"SK4TTO": "releaser",
|
|
208
221
|
"SMURF": "releaser",
|
|
209
222
|
"SMURFADMIN": "releaser",
|
|
223
|
+
"SM737": "releaser",
|
|
210
224
|
"SP33DY94-MIRCREW": "releaser",
|
|
211
225
|
"SPWEB": "releaser",
|
|
212
226
|
"SPYRO": "releaser",
|
|
@@ -215,7 +229,12 @@ SIGNS_LIST = {
|
|
|
215
229
|
"STEVEJOHNNEE": "releaser",
|
|
216
230
|
"STINGUAJT": "releaser",
|
|
217
231
|
"TAHTAKALECI": "releaser",
|
|
232
|
+
"TAREK": "releaser",
|
|
218
233
|
"TASKO": "releaser",
|
|
234
|
+
"TBR": "releaser",
|
|
235
|
+
"TBZ": "releaser",
|
|
236
|
+
"T4H": "releaser",
|
|
237
|
+
"T4P3": "releaser",
|
|
219
238
|
"TELESTO": "releaser",
|
|
220
239
|
"TEPES": "releaser",
|
|
221
240
|
"TERMINAL": "releaser",
|
|
@@ -224,11 +243,8 @@ SIGNS_LIST = {
|
|
|
224
243
|
"THUDARA": "releaser",
|
|
225
244
|
"TIGER": "releaser",
|
|
226
245
|
"TIGRE67": "releaser",
|
|
227
|
-
"TBR": "releaser",
|
|
228
|
-
"TBZ": "releaser",
|
|
229
|
-
"T4P3": "releaser",
|
|
230
|
-
"TORRENTER10": "releaser",
|
|
231
246
|
"TOTAL_SHARE88": "releaser",
|
|
247
|
+
"TORRENTER10": "releaser",
|
|
232
248
|
"TREE7458": "releaser",
|
|
233
249
|
"TRITON": "releaser",
|
|
234
250
|
"TRL": "releaser",
|
|
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
|
|
|
5
5
|
[project]
|
|
6
6
|
dynamic = ["dependencies"]
|
|
7
7
|
name = "Unit3Dup"
|
|
8
|
-
version = "0.
|
|
8
|
+
version = "0.12.0"
|
|
9
9
|
description = "An uploader for the Unit3D torrent tracker"
|
|
10
10
|
readme = "README.rst"
|
|
11
11
|
requires-python = ">=3.10"
|
|
@@ -13,8 +13,8 @@ license = "MIT"
|
|
|
13
13
|
|
|
14
14
|
authors = [
|
|
15
15
|
{ name = "Parzival" }
|
|
16
|
+
|
|
16
17
|
]
|
|
17
|
-
|
|
18
18
|
[project.urls]
|
|
19
19
|
Homepage = "https://github.com/31December99/Unit3Dup"
|
|
20
20
|
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
import json
|
|
3
3
|
|
|
4
|
-
import requests
|
|
5
|
-
|
|
6
4
|
from common.torrent_clients import TransmissionClient, QbittorrentClient, RTorrentClient
|
|
7
5
|
from common.command import CommandLine
|
|
8
6
|
from common.settings import Load, DEFAULT_JSON_PATH, USER_TAGS_PATH, USER_SIGN_PATH, BAN_TAGS_PATH, version
|
|
@@ -58,7 +56,6 @@ def main():
|
|
|
58
56
|
custom_console.bot_log(f"Tracker -> '{cli.args.tracker.upper()}' Online")
|
|
59
57
|
tracker_name_list = [cli.args.tracker.upper()]
|
|
60
58
|
|
|
61
|
-
|
|
62
59
|
# Send content to the multi_tracker list
|
|
63
60
|
if cli.args.mt:
|
|
64
61
|
tracker_name_list = config.tracker_config.MULTI_TRACKER
|
|
@@ -294,7 +291,7 @@ def main():
|
|
|
294
291
|
torrent_info.view_highspeed()
|
|
295
292
|
return
|
|
296
293
|
|
|
297
|
-
if cli.args.
|
|
294
|
+
if cli.args.intern_r:
|
|
298
295
|
torrent_info.view_internal()
|
|
299
296
|
return
|
|
300
297
|
|
|
@@ -124,9 +124,6 @@ class TorrentManager:
|
|
|
124
124
|
custom_console.bot_log(f"Tracker '{selected_tracker}' Done.")
|
|
125
125
|
custom_console.rule()
|
|
126
126
|
|
|
127
|
-
custom_console.bot_log(f"Done.")
|
|
128
|
-
custom_console.rule()
|
|
129
|
-
|
|
130
127
|
def reseed(self, trackers_name_list: list) -> None:
|
|
131
128
|
"""
|
|
132
129
|
|