Unit3Dup 0.8.20__py3-none-any.whl → 0.8.24__py3-none-any.whl
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.
- common/external_services/imdb.py +3 -7
- common/external_services/mediaresult.py +2 -1
- common/external_services/theMovieDB/core/api.py +63 -65
- common/external_services/tvdb.py +27 -0
- common/settings.py +9 -5
- common/utility.py +3 -1
- unit3dup/media.py +11 -1
- unit3dup/media_manager/ContentManager.py +9 -1
- unit3dup/media_manager/VideoManager.py +4 -3
- unit3dup/pvtTracker.py +4 -5
- unit3dup/pvtVideo.py +4 -2
- unit3dup/upload.py +3 -1
- {unit3dup-0.8.20.dist-info → unit3dup-0.8.24.dist-info}/METADATA +176 -176
- {unit3dup-0.8.20.dist-info → unit3dup-0.8.24.dist-info}/RECORD +18 -17
- {unit3dup-0.8.20.dist-info → unit3dup-0.8.24.dist-info}/WHEEL +1 -1
- {unit3dup-0.8.20.dist-info → unit3dup-0.8.24.dist-info}/licenses/LICENSE +0 -0
- {unit3dup-0.8.20.dist-info → unit3dup-0.8.24.dist-info}/entry_points.txt +0 -0
- {unit3dup-0.8.20.dist-info → unit3dup-0.8.24.dist-info}/top_level.txt +0 -0
common/external_services/imdb.py
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
|
|
3
|
-
from imdb import Cinemagoer
|
|
4
|
-
from view import custom_console
|
|
5
|
-
from rich.table import Table
|
|
6
|
-
from rich.align import Align
|
|
7
|
-
|
|
3
|
+
from imdb import Cinemagoer # Installato da github , su pipy non ancora disponibile la versione 2025
|
|
8
4
|
from common.utility import ManageTitles
|
|
9
|
-
from
|
|
5
|
+
from unit3dup import config_settings
|
|
10
6
|
|
|
11
7
|
|
|
12
8
|
class IMDB:
|
|
@@ -20,4 +16,4 @@ class IMDB:
|
|
|
20
16
|
for movie in movies:
|
|
21
17
|
if ManageTitles.fuzzyit(str1=query, str2=movie.data['title']) > 95:
|
|
22
18
|
return movie.movieID
|
|
23
|
-
|
|
19
|
+
return None
|
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
from datetime import datetime
|
|
4
4
|
|
|
5
5
|
class MediaResult:
|
|
6
|
-
def __init__(self, result=None, video_id: int = 0, imdb_id = None, trailer_key: str = None,
|
|
6
|
+
def __init__(self, result=None, video_id: int = 0, imdb_id = None, tvdb_id = None, trailer_key: str = None,
|
|
7
7
|
keywords_list: str = None, season_title = None):
|
|
8
8
|
self.result = result
|
|
9
9
|
self.trailer_key = trailer_key
|
|
10
10
|
self.keywords_list = keywords_list
|
|
11
11
|
self.video_id = video_id
|
|
12
12
|
self.imdb_id = imdb_id
|
|
13
|
+
self.tvdb_id = tvdb_id
|
|
13
14
|
self.season_title = season_title
|
|
14
15
|
self.year = None
|
|
15
16
|
|
|
@@ -20,7 +20,7 @@ from common.external_services.sessions.session import MyHttp
|
|
|
20
20
|
from common.external_services.trailers.api import YtTrailer
|
|
21
21
|
from common.external_services.sessions.agents import Agent
|
|
22
22
|
from common.external_services.theMovieDB import config
|
|
23
|
-
from common.external_services.
|
|
23
|
+
from common.external_services.tvdb import TVDB
|
|
24
24
|
from common.utility import ManageTitles
|
|
25
25
|
|
|
26
26
|
from unit3dup.media import Media
|
|
@@ -35,33 +35,29 @@ T = TypeVar('T')
|
|
|
35
35
|
|
|
36
36
|
class MovieEndpoint:
|
|
37
37
|
@staticmethod
|
|
38
|
-
def search(query: str)-> dict:
|
|
39
|
-
return {'url': f'{base_url}/search/movie', 'datatype': Movie, 'query': query, 'results': 'results'
|
|
40
|
-
|
|
38
|
+
def search(query: str) -> dict:
|
|
39
|
+
return {'url': f'{base_url}/search/movie', 'datatype': Movie, 'query': query, 'results': 'results'}
|
|
41
40
|
|
|
42
41
|
@staticmethod
|
|
43
|
-
def playing()-> dict:
|
|
42
|
+
def playing() -> dict:
|
|
44
43
|
return {'url': f'{base_url}/movie/now_playing', 'datatype': NowPlaying, 'query': '', 'results': 'results'}
|
|
45
44
|
|
|
46
|
-
|
|
47
45
|
@staticmethod
|
|
48
|
-
def alternative(movie_id: int)-> dict:
|
|
46
|
+
def alternative(movie_id: int) -> dict:
|
|
49
47
|
return {'url': f'{base_url}/movie/{movie_id}/alternative_titles', 'datatype': Alternative, 'query': '',
|
|
50
48
|
'results': 'titles'}
|
|
51
49
|
|
|
52
|
-
|
|
53
50
|
@staticmethod
|
|
54
|
-
def videos(movie_id: int)-> dict:
|
|
51
|
+
def videos(movie_id: int) -> dict:
|
|
55
52
|
return {'url': f'{base_url}/movie/{movie_id}/videos', 'datatype': Videos, 'query': '',
|
|
56
53
|
'results': 'results'}
|
|
57
54
|
|
|
58
55
|
@staticmethod
|
|
59
|
-
def details(movie_id: int)-> dict:
|
|
56
|
+
def details(movie_id: int) -> dict:
|
|
60
57
|
return {'url': f'{base_url}/movie/{movie_id}', 'datatype': MovieDetails, 'query': ''}
|
|
61
58
|
|
|
62
|
-
|
|
63
59
|
@staticmethod
|
|
64
|
-
def keywords(movie_id: int)-> dict:
|
|
60
|
+
def keywords(movie_id: int) -> dict:
|
|
65
61
|
return {'url': f'{base_url}/movie/{movie_id}/keywords', 'datatype': Keyword, 'query': '',
|
|
66
62
|
'results': 'keywords'}
|
|
67
63
|
|
|
@@ -71,38 +67,31 @@ class TvEndpoint:
|
|
|
71
67
|
def search(query: str):
|
|
72
68
|
return {'url': f'{base_url}/search/tv', 'datatype': TvShow, 'query': query, 'results': 'results'}
|
|
73
69
|
|
|
74
|
-
|
|
75
70
|
@staticmethod
|
|
76
71
|
def playing():
|
|
77
72
|
return {'url': f'{base_url}/tv/on_the_air', 'datatype': OnTheAir, 'query': '', 'results': 'results'}
|
|
78
73
|
|
|
79
|
-
|
|
80
74
|
@staticmethod
|
|
81
75
|
def alternative(serie_id: int) -> dict:
|
|
82
76
|
return {'url': f'{base_url}/tv/{serie_id}/alternative_titles', 'datatype': Alternative, 'query': '',
|
|
83
77
|
'results': 'results'}
|
|
84
78
|
|
|
85
|
-
|
|
86
79
|
@staticmethod
|
|
87
|
-
def videos(serie_id: int)-> dict:
|
|
80
|
+
def videos(serie_id: int) -> dict:
|
|
88
81
|
return {'url': f'{base_url}/tv/{serie_id}/videos', 'datatype': Videos, 'query': '',
|
|
89
82
|
'results': 'results'}
|
|
90
83
|
|
|
91
84
|
@staticmethod
|
|
92
|
-
def details(serie_id: int)-> dict:
|
|
85
|
+
def details(serie_id: int) -> dict:
|
|
93
86
|
return {'url': f'{base_url}/tv/{serie_id}', 'datatype': TVShowDetails, 'query': ''}
|
|
94
87
|
|
|
95
|
-
|
|
96
|
-
|
|
97
88
|
@staticmethod
|
|
98
|
-
def keywords(serie_id: int)-> dict:
|
|
89
|
+
def keywords(serie_id: int) -> dict:
|
|
99
90
|
return {'url': f'{base_url}/tv/{serie_id}/keywords', 'datatype': Keyword, 'query': '',
|
|
100
91
|
'results': 'results'}
|
|
101
92
|
|
|
102
93
|
|
|
103
|
-
|
|
104
94
|
class TmdbAPI(MyHttp):
|
|
105
|
-
|
|
106
95
|
params = {
|
|
107
96
|
"api_key": config.TMDB_APIKEY,
|
|
108
97
|
"language": "it-IT",
|
|
@@ -147,7 +136,7 @@ class TmdbAPI(MyHttp):
|
|
|
147
136
|
:return: list of T or None
|
|
148
137
|
"""
|
|
149
138
|
|
|
150
|
-
if endpoint_class:=self.ENDPOINTS.get(category):
|
|
139
|
+
if endpoint_class := self.ENDPOINTS.get(category):
|
|
151
140
|
request = endpoint_class.playing()
|
|
152
141
|
return self.request(endpoint=request)
|
|
153
142
|
else:
|
|
@@ -161,7 +150,7 @@ class TmdbAPI(MyHttp):
|
|
|
161
150
|
:param category: category of the search query, e.g., 'movie' or 'tv'
|
|
162
151
|
:return: list of T or None
|
|
163
152
|
"""
|
|
164
|
-
if endpoint_class:=self.ENDPOINTS.get(category):
|
|
153
|
+
if endpoint_class := self.ENDPOINTS.get(category):
|
|
165
154
|
request = endpoint_class.alternative(media_id)
|
|
166
155
|
return self.request(endpoint=request)
|
|
167
156
|
else:
|
|
@@ -169,7 +158,7 @@ class TmdbAPI(MyHttp):
|
|
|
169
158
|
return []
|
|
170
159
|
|
|
171
160
|
def _videos(self, video_id: int, category: str) -> list[T] | None:
|
|
172
|
-
if endpoint_class:=self.ENDPOINTS.get(category):
|
|
161
|
+
if endpoint_class := self.ENDPOINTS.get(category):
|
|
173
162
|
request = endpoint_class.videos(video_id)
|
|
174
163
|
return self.request(endpoint=request)
|
|
175
164
|
else:
|
|
@@ -177,23 +166,21 @@ class TmdbAPI(MyHttp):
|
|
|
177
166
|
return []
|
|
178
167
|
|
|
179
168
|
def details(self, video_id: int, category: str) -> list[T] | None:
|
|
180
|
-
if endpoint_class:=self.ENDPOINTS.get(category):
|
|
169
|
+
if endpoint_class := self.ENDPOINTS.get(category):
|
|
181
170
|
request = endpoint_class.details(video_id)
|
|
182
171
|
return self.request(endpoint=request)
|
|
183
172
|
else:
|
|
184
173
|
print(f"Endpoint for category '{category}' not found.")
|
|
185
174
|
return []
|
|
186
175
|
|
|
187
|
-
|
|
188
176
|
def _keywords(self, video_id: int, category: str) -> list[T] | None:
|
|
189
|
-
if endpoint_class:=self.ENDPOINTS.get(category):
|
|
177
|
+
if endpoint_class := self.ENDPOINTS.get(category):
|
|
190
178
|
request = endpoint_class.keywords(video_id)
|
|
191
179
|
return self.request(endpoint=request)
|
|
192
180
|
else:
|
|
193
181
|
print(f"Endpoint for category '{category}' not found.")
|
|
194
182
|
return []
|
|
195
183
|
|
|
196
|
-
|
|
197
184
|
def request(self, endpoint: dict) -> list[T] | None:
|
|
198
185
|
"""
|
|
199
186
|
Sends a request to the API and returns a list of instances of the specified 'datatype'.
|
|
@@ -215,20 +202,24 @@ class TmdbAPI(MyHttp):
|
|
|
215
202
|
return []
|
|
216
203
|
return None
|
|
217
204
|
|
|
205
|
+
|
|
218
206
|
class DbOnline(TmdbAPI):
|
|
219
207
|
def __init__(self, media: Media, category: str, no_title: str) -> None:
|
|
220
208
|
super().__init__()
|
|
221
209
|
self.media = media
|
|
222
210
|
self.query = media.guess_title
|
|
223
211
|
self.category = category
|
|
212
|
+
self.imdb_id = None
|
|
213
|
+
self.tvdb_id = None
|
|
224
214
|
|
|
225
215
|
# Load the cache file
|
|
226
216
|
if config_settings.user_preferences.CACHE_DBONLINE:
|
|
227
217
|
self.cache = diskcache.Cache(str(os.path.join(config_settings.user_preferences.CACHE_PATH, "tmdb.cache")))
|
|
228
218
|
|
|
229
|
-
if media.tmdb_id or media.imdb_id:
|
|
219
|
+
if media.tmdb_id or media.imdb_id or media.tmdb_id:
|
|
230
220
|
# Skip cache if there is a tmdb id or imdb in the title string
|
|
231
|
-
self.media_result = self.
|
|
221
|
+
self.media_result = self.results_in_string(tmdb_id=int(media.tmdb_id), imdb_id=int(media.imdb_id),
|
|
222
|
+
tvdb_id=int(media.tvdb_id))
|
|
232
223
|
else:
|
|
233
224
|
# Load from the cache or search online for a tmdb id or imdb
|
|
234
225
|
# Search for a video based on the filename or the title from the -notitle flag in the CLI
|
|
@@ -247,8 +238,9 @@ class DbOnline(TmdbAPI):
|
|
|
247
238
|
for result in results:
|
|
248
239
|
# check date
|
|
249
240
|
if result.get_date() and self.media.guess_filename.guessit_year:
|
|
250
|
-
if not datetime.strptime(result.get_date(),
|
|
251
|
-
|
|
241
|
+
if not datetime.strptime(result.get_date(),
|
|
242
|
+
'%Y-%m-%d').year == self.media.guess_filename.guessit_year:
|
|
243
|
+
continue
|
|
252
244
|
|
|
253
245
|
# Search for title
|
|
254
246
|
if ManageTitles.fuzzyit(str1=self.query, str2=ManageTitles.clean_text(result.get_title())) > 95:
|
|
@@ -265,7 +257,7 @@ class DbOnline(TmdbAPI):
|
|
|
265
257
|
return result
|
|
266
258
|
return False
|
|
267
259
|
|
|
268
|
-
def results_in_string(self, tmdb_id:int, imdb_id:int)-> MediaResult:
|
|
260
|
+
def results_in_string(self, tmdb_id: int, imdb_id: int, tvdb_id: int) -> MediaResult:
|
|
269
261
|
"""
|
|
270
262
|
Use id from the string filename or name folder
|
|
271
263
|
Cache disabled
|
|
@@ -281,12 +273,11 @@ class DbOnline(TmdbAPI):
|
|
|
281
273
|
else:
|
|
282
274
|
tmdb_id = 0
|
|
283
275
|
|
|
284
|
-
search_results = MediaResult(video_id=tmdb_id, imdb_id=imdb_id, trailer_key=trailer_key,
|
|
276
|
+
search_results = MediaResult(video_id=tmdb_id, imdb_id=self.imdb_id, tvdb_id=tvdb_id, trailer_key=trailer_key,
|
|
285
277
|
keywords_list=keywords_list)
|
|
286
278
|
self.print_results(results=search_results)
|
|
287
279
|
return search_results
|
|
288
280
|
|
|
289
|
-
|
|
290
281
|
def search(self) -> MediaResult | None:
|
|
291
282
|
"""
|
|
292
283
|
Search for results based on a tmdb query
|
|
@@ -303,13 +294,19 @@ class DbOnline(TmdbAPI):
|
|
|
303
294
|
# or start an on-line search
|
|
304
295
|
results = self._search(self.query, self.category)
|
|
305
296
|
# Use imdb_id when tmdb_id is not available
|
|
306
|
-
|
|
297
|
+
|
|
298
|
+
if 'tv' in self.category:
|
|
299
|
+
result = self.tvdb_search()
|
|
300
|
+
if result:
|
|
301
|
+
self.tvdb_id = result.get('tvdb_id', None)
|
|
302
|
+
self.imdb_id = result.get('imdb_id', None)
|
|
303
|
+
|
|
307
304
|
if results:
|
|
308
|
-
if result:=self.is_like(results):
|
|
305
|
+
if result := self.is_like(results):
|
|
309
306
|
# Get the trailer
|
|
310
307
|
trailer_key = self.trailer(result.id)
|
|
311
308
|
keywords_list = self.keywords(result.id)
|
|
312
|
-
search_results = MediaResult(result, video_id=result.id, imdb_id=imdb_id,
|
|
309
|
+
search_results = MediaResult(result, video_id=result.id, imdb_id=self.imdb_id, tvdb_id=self.tvdb_id,
|
|
313
310
|
trailer_key=trailer_key, keywords_list=keywords_list)
|
|
314
311
|
|
|
315
312
|
self.print_results(results=search_results)
|
|
@@ -340,6 +337,9 @@ class DbOnline(TmdbAPI):
|
|
|
340
337
|
self.cache[self.hash_key(self.query)] = search_results
|
|
341
338
|
return search_results
|
|
342
339
|
|
|
340
|
+
def tvdb_search(self) -> dict | None:
|
|
341
|
+
tvdb = TVDB(category=self.category)
|
|
342
|
+
return tvdb.search(query=self.query)
|
|
343
343
|
|
|
344
344
|
def manual_search(self) -> MediaResult | None:
|
|
345
345
|
"""
|
|
@@ -348,8 +348,6 @@ class DbOnline(TmdbAPI):
|
|
|
348
348
|
Returns:
|
|
349
349
|
MediaResult
|
|
350
350
|
"""
|
|
351
|
-
imdb = IMDB()
|
|
352
|
-
imdb_id = 0
|
|
353
351
|
user_id = 0
|
|
354
352
|
while True:
|
|
355
353
|
# Check if user wants to skip
|
|
@@ -363,11 +361,10 @@ class DbOnline(TmdbAPI):
|
|
|
363
361
|
custom_console.bot_warning_log(f"\n ** Auto skip TMDB ID **\n")
|
|
364
362
|
|
|
365
363
|
# Try to add IMDB ID if tmdb is not available
|
|
366
|
-
if user_id==0:
|
|
367
|
-
imdb_id = imdb.search(query=self.query)
|
|
364
|
+
if user_id == 0:
|
|
368
365
|
# try searching for a YouTube video anyway
|
|
369
366
|
trailer_key = self.youtube_trailer()
|
|
370
|
-
search_results = MediaResult(video_id=user_id, imdb_id=imdb_id, trailer_key=trailer_key,
|
|
367
|
+
search_results = MediaResult(video_id=user_id, imdb_id=self.imdb_id, trailer_key=trailer_key,
|
|
371
368
|
keywords_list='not available')
|
|
372
369
|
self.print_results(results=search_results)
|
|
373
370
|
return search_results
|
|
@@ -377,15 +374,15 @@ class DbOnline(TmdbAPI):
|
|
|
377
374
|
# Request trailer and keywords
|
|
378
375
|
trailer_key = self.trailer(user_id)
|
|
379
376
|
keywords_list = self.keywords(user_id) if trailer_key else ''
|
|
380
|
-
search_results = MediaResult(result=result, video_id=user_id, imdb_id=imdb_id,
|
|
377
|
+
search_results = MediaResult(result=result, video_id=user_id, imdb_id=self.imdb_id,
|
|
378
|
+
trailer_key=trailer_key,
|
|
381
379
|
keywords_list=keywords_list)
|
|
382
380
|
self.print_results(results=search_results)
|
|
383
381
|
return search_results
|
|
384
382
|
|
|
385
|
-
|
|
386
383
|
def youtube_trailer(self) -> str | None:
|
|
387
384
|
# Search trailer on YouTube
|
|
388
|
-
if 'no_key'
|
|
385
|
+
if 'no_key' in config_settings.tracker_config.YOUTUBE_KEY:
|
|
389
386
|
return "not available"
|
|
390
387
|
|
|
391
388
|
yt_trailer = YtTrailer(self.query)
|
|
@@ -395,11 +392,13 @@ class DbOnline(TmdbAPI):
|
|
|
395
392
|
# todo compare against the media title especially for the favorite channel
|
|
396
393
|
return result[0].items[0].id.videoId
|
|
397
394
|
else:
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
395
|
+
if not config_settings.user_preferences.SKIP_YOUTUBE:
|
|
396
|
+
user_youtube_id = custom_console.user_input_str(message="Title not found."
|
|
397
|
+
" Please digit a valid Youtube ID (0=skip)->")
|
|
398
|
+
if user_youtube_id == 0:
|
|
399
|
+
return "not available"
|
|
400
|
+
return user_youtube_id
|
|
401
|
+
return None
|
|
403
402
|
|
|
404
403
|
def trailer(self, video_id: int) -> str | None:
|
|
405
404
|
# Search for tmdb trailer
|
|
@@ -422,17 +421,17 @@ class DbOnline(TmdbAPI):
|
|
|
422
421
|
else:
|
|
423
422
|
return "not available"
|
|
424
423
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
custom_console.
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
def load_cache(self, query: str)-> MediaResult | None:
|
|
424
|
+
def print_results(self, results: MediaResult) -> None:
|
|
425
|
+
custom_console.bot_log(f"'TMDB TITLE'..... {self.query}")
|
|
426
|
+
custom_console.bot_log(f"'TMDB ID'........ {results.video_id}")
|
|
427
|
+
if results.imdb_id:
|
|
428
|
+
custom_console.bot_warning_log(f"'IMDB ID'........ '{results.imdb_id}'")
|
|
429
|
+
if results.tvdb_id:
|
|
430
|
+
custom_console.bot_warning_log(f"'TVDB ID'........ '{results.tvdb_id}'")
|
|
431
|
+
custom_console.bot_log(f"'TMDB KEYWORDS'.. {results.keywords_list}")
|
|
432
|
+
custom_console.bot_log(f"'TRAILER CODE' .. {results.trailer_key}")
|
|
433
|
+
|
|
434
|
+
def load_cache(self, query: str) -> MediaResult | None:
|
|
436
435
|
# Check if the item is in the cache
|
|
437
436
|
if query not in self.cache:
|
|
438
437
|
return None
|
|
@@ -447,7 +446,6 @@ class DbOnline(TmdbAPI):
|
|
|
447
446
|
custom_console.bot_error_log("Proceed to extract the screenshot again. Please wait..")
|
|
448
447
|
return None
|
|
449
448
|
|
|
450
|
-
|
|
451
449
|
def search_id(self, video_id: int) -> list[T] | None:
|
|
452
450
|
"""
|
|
453
451
|
Search Movie or Tvshow based on ID
|
|
@@ -458,4 +456,4 @@ class DbOnline(TmdbAPI):
|
|
|
458
456
|
result = self.details(video_id=video_id, category=self.category)
|
|
459
457
|
if result:
|
|
460
458
|
return result[0]
|
|
461
|
-
return None
|
|
459
|
+
return None
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import tvdb_v4_official
|
|
2
|
+
from common.utility import ManageTitles
|
|
3
|
+
from unit3dup import config_settings
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TVDB:
|
|
7
|
+
|
|
8
|
+
def __init__(self, category: str):
|
|
9
|
+
self.api = tvdb_v4_official.TVDB(config_settings.tracker_config.TVDB_APIKEY)
|
|
10
|
+
self.category = category
|
|
11
|
+
self.filtered_results = []
|
|
12
|
+
|
|
13
|
+
def search(self, query: str) -> dict | None:
|
|
14
|
+
results = self.api.search(query=query, type='series')
|
|
15
|
+
self.filtered_results = [item for item in results]
|
|
16
|
+
|
|
17
|
+
for item in self.filtered_results:
|
|
18
|
+
title = item.get('name', '') or item.get('extended_title', '')
|
|
19
|
+
remote_ids = item.get('remote_ids', [])
|
|
20
|
+
imdb_id = None
|
|
21
|
+
for remote_id in remote_ids:
|
|
22
|
+
if 'IMDB' in remote_id.get('sourceName').upper():
|
|
23
|
+
imdb_id = remote_id.get('id').lower().replace('tt', '')
|
|
24
|
+
score = ManageTitles.fuzzyit(str1=query, str2=title)
|
|
25
|
+
if score > 95:
|
|
26
|
+
return {'tvdb_id' : item.get('tvdb_id'), 'imdb_id': imdb_id}
|
|
27
|
+
return None
|
common/settings.py
CHANGED
|
@@ -13,7 +13,7 @@ from common.utility import ManageTitles
|
|
|
13
13
|
from common import trackers
|
|
14
14
|
|
|
15
15
|
config_file = "Unit3Dbot.json"
|
|
16
|
-
version = "0.8.
|
|
16
|
+
version = "0.8.24"
|
|
17
17
|
|
|
18
18
|
if os.name == "nt":
|
|
19
19
|
PW_TORRENT_ARCHIVE_PATH: Path = Path(os.getenv("LOCALAPPDATA", ".")) / "Unit3Dup_config" / "pw_torrent_archive"
|
|
@@ -63,6 +63,7 @@ class TrackerConfig(BaseModel):
|
|
|
63
63
|
SIS_PID: str | None = None
|
|
64
64
|
MULTI_TRACKER: list[str] | None = None
|
|
65
65
|
TMDB_APIKEY: str | None = None
|
|
66
|
+
TVDB_APIKEY: str | None = None
|
|
66
67
|
IMGBB_KEY: str | None = None
|
|
67
68
|
FREE_IMAGE_KEY: str | None = None
|
|
68
69
|
LENSDUMP_KEY: str | None = None
|
|
@@ -111,6 +112,7 @@ class UserPreferences(BaseModel):
|
|
|
111
112
|
DUPLICATE_ON: bool = False
|
|
112
113
|
SKIP_DUPLICATE: bool = False
|
|
113
114
|
SKIP_TMDB: bool = False
|
|
115
|
+
SKIP_YOUTUBE: bool = False
|
|
114
116
|
SIZE_TH: int = 50
|
|
115
117
|
WATCHER_INTERVAL: int = 60
|
|
116
118
|
WATCHER_PATH: str | None = None
|
|
@@ -424,8 +426,8 @@ class Config(BaseModel):
|
|
|
424
426
|
else:
|
|
425
427
|
field = field.upper()
|
|
426
428
|
|
|
427
|
-
if field in ['DUPLICATE_ON','SKIP_DUPLICATE','SKIP_TMDB','
|
|
428
|
-
'CACHE_SCR','CACHE_DBONLINE', 'PERSONAL_RELEASE']:
|
|
429
|
+
if field in ['DUPLICATE_ON','SKIP_DUPLICATE','SKIP_TMDB','SKIP_YOUTUBE','RESIZE_SCSHOT','ANON',
|
|
430
|
+
'WEBP_ENABLED', 'CACHE_SCR','CACHE_DBONLINE', 'PERSONAL_RELEASE']:
|
|
429
431
|
section[field] = Validate.boolean(value=section[field], field_name=field)
|
|
430
432
|
|
|
431
433
|
if field in ['TORRENT_COMMENT','WATCHER_PATH','DEFAULT_TRACKER']:
|
|
@@ -529,6 +531,7 @@ class Load:
|
|
|
529
531
|
"SIS_PID": "no_key",
|
|
530
532
|
"MULTI_TRACKER" : ["itt"],
|
|
531
533
|
"TMDB_APIKEY": "no_key",
|
|
534
|
+
"TVDB_APIKEY": "no_key",
|
|
532
535
|
"IMGBB_KEY": "no_key",
|
|
533
536
|
"FREE_IMAGE_KEY": "no_key",
|
|
534
537
|
"LENSDUMP_KEY": "no_key",
|
|
@@ -557,8 +560,7 @@ class Load:
|
|
|
557
560
|
"RTORR_PORT": "9091",
|
|
558
561
|
"SHARED_RTORR_PATH": "no_path",
|
|
559
562
|
"TORRENT_CLIENT": "qbittorrent",
|
|
560
|
-
"TAG": "ADDED TORRENTS"
|
|
561
|
-
"FAST_LOAD": "0",
|
|
563
|
+
"TAG": "ADDED TORRENTS"
|
|
562
564
|
},
|
|
563
565
|
"user_preferences": {
|
|
564
566
|
"PTSCREENS_PRIORITY": 0,
|
|
@@ -574,6 +576,7 @@ class Load:
|
|
|
574
576
|
"DUPLICATE_ON": "true",
|
|
575
577
|
"SKIP_DUPLICATE": "false",
|
|
576
578
|
"SKIP_TMDB": "false",
|
|
579
|
+
"SKIP_YOUTUBE" :'true',
|
|
577
580
|
"SIZE_TH": 10,
|
|
578
581
|
"WATCHER_INTERVAL": 60,
|
|
579
582
|
"WATCHER_PATH": "no_path",
|
|
@@ -589,6 +592,7 @@ class Load:
|
|
|
589
592
|
"CACHE_SCR": "False",
|
|
590
593
|
"CACHE_DBONLINE": "False",
|
|
591
594
|
"PERSONAL_RELEASE": "False",
|
|
595
|
+
"FAST_LOAD": "0"
|
|
592
596
|
},
|
|
593
597
|
"options": {
|
|
594
598
|
"PW_API_KEY": "no_key",
|
common/utility.py
CHANGED
|
@@ -107,7 +107,9 @@ class ManageTitles:
|
|
|
107
107
|
"""
|
|
108
108
|
Replaces hyphens with dots and removes video resolutions
|
|
109
109
|
"""
|
|
110
|
-
resolutions = ["4320", "2160", "1080", "720", "576", "480"]
|
|
110
|
+
# resolutions = ["4320", "2160", "1080", "720", "576", "480"]
|
|
111
|
+
resolutions = ["4320p", "2160p", "1080p", "720p", "576p", "480p"]
|
|
112
|
+
|
|
111
113
|
subdir = subdir.replace("-", ".")
|
|
112
114
|
for res in resolutions:
|
|
113
115
|
subdir = subdir.replace(res, " ")
|
unit3dup/media.py
CHANGED
|
@@ -3,6 +3,7 @@ import os
|
|
|
3
3
|
import re
|
|
4
4
|
|
|
5
5
|
from common.external_services.igdb.core.tags import crew_patterns, platform_patterns
|
|
6
|
+
from common.title import Guessit
|
|
6
7
|
from common.utility import ManageTitles, System
|
|
7
8
|
from common.mediainfo import MediaFile
|
|
8
9
|
from common import title
|
|
@@ -21,7 +22,7 @@ class Media:
|
|
|
21
22
|
self._platform_list: list[str] | None = None
|
|
22
23
|
self._title_sanitized: str | None = None
|
|
23
24
|
self._guess_title: str | None = None
|
|
24
|
-
self._guess_filename:
|
|
25
|
+
self._guess_filename: Guessit | None = None
|
|
25
26
|
self._guess_season: int | None = None
|
|
26
27
|
self._episode: str | None = None
|
|
27
28
|
self._source: str | None = None
|
|
@@ -49,6 +50,7 @@ class Media:
|
|
|
49
50
|
self._game_nfo: str | None = None
|
|
50
51
|
self._tmdb_id: int | None = None
|
|
51
52
|
self._imdb_id: int | None = None
|
|
53
|
+
self._tvdb_id: int | None = None
|
|
52
54
|
self._igdb_id: int | None = None
|
|
53
55
|
self._generate_title: str | None = None
|
|
54
56
|
|
|
@@ -158,6 +160,14 @@ class Media:
|
|
|
158
160
|
def imdb_id(self, value):
|
|
159
161
|
self._imdb_id = value
|
|
160
162
|
|
|
163
|
+
@property
|
|
164
|
+
def tvdb_id(self) -> int:
|
|
165
|
+
return self._tvdb_id
|
|
166
|
+
|
|
167
|
+
@tvdb_id.setter
|
|
168
|
+
def tvdb_id(self, value):
|
|
169
|
+
self._tvdb_id = value
|
|
170
|
+
|
|
161
171
|
@property
|
|
162
172
|
def igdb_id(self) -> int:
|
|
163
173
|
return self._igdb_id
|
|
@@ -34,6 +34,7 @@ class ContentManager:
|
|
|
34
34
|
self.doc_description: str | None = None
|
|
35
35
|
self.tmdb_id: int = 0
|
|
36
36
|
self.imdb_id: int = 0
|
|
37
|
+
self.tvdb_id: int = 0
|
|
37
38
|
self.igdb_id: int = 0
|
|
38
39
|
self.generate_title: str | None = None
|
|
39
40
|
|
|
@@ -85,6 +86,7 @@ class ContentManager:
|
|
|
85
86
|
|
|
86
87
|
# Add ID if there are one in the title
|
|
87
88
|
media.imdb_id = self.imdb_id
|
|
89
|
+
media.tvdb_id = self.tvdb_id
|
|
88
90
|
media.tmdb_id = self.tmdb_id
|
|
89
91
|
media.igdb_id = self.igdb_id
|
|
90
92
|
media.display_name = self.display_name
|
|
@@ -112,7 +114,10 @@ class ContentManager:
|
|
|
112
114
|
self.tmdb_id = id.replace('tmdb-', '')
|
|
113
115
|
self.tmdb_id = self.tmdb_id if self.tmdb_id.isdigit() else None
|
|
114
116
|
self.display_name = self.display_name.replace(f"tmdb-{self.tmdb_id}", '')
|
|
115
|
-
|
|
117
|
+
elif 'tvdb-' in id:
|
|
118
|
+
self.tvdb_id = id.replace('tvdb-', '')
|
|
119
|
+
self.tvdb_id = self.tvdb_id if self.tvdb_id.isdigit() else None
|
|
120
|
+
self.display_name = self.display_name.replace(f"tvdb-{self.tvdb_id}", '')
|
|
116
121
|
elif 'igdb-' in id:
|
|
117
122
|
self.igdb_id = id.replace('igdb-', '')
|
|
118
123
|
self.igdb_id = self.igdb_id if self.igdb_id.isdigit() else None
|
|
@@ -121,6 +126,7 @@ class ContentManager:
|
|
|
121
126
|
else:
|
|
122
127
|
self.imdb_id = 0
|
|
123
128
|
self.tmdb_id = 0
|
|
129
|
+
self.tvdb_id = 0
|
|
124
130
|
self.igdb_id = 0
|
|
125
131
|
|
|
126
132
|
def process_file(self) -> bool:
|
|
@@ -129,6 +135,7 @@ class ContentManager:
|
|
|
129
135
|
# Display name on webpage
|
|
130
136
|
self.display_name, _ = os.path.splitext(os.path.basename(self.file_name))
|
|
131
137
|
self.display_name = ManageTitles.clean_text(self.display_name)
|
|
138
|
+
self.display_name = re.sub(r'[\[\]()]', '', self.display_name)
|
|
132
139
|
# current media path
|
|
133
140
|
self.torrent_path = self.path
|
|
134
141
|
# Try to get video ID from the string title
|
|
@@ -153,6 +160,7 @@ class ContentManager:
|
|
|
153
160
|
self.file_name = os.path.join(self.path, files_list[0])
|
|
154
161
|
# Display name on webpage
|
|
155
162
|
self.display_name = ManageTitles.clean_text(os.path.basename(self.path))
|
|
163
|
+
self.display_name = re.sub(r'[\[\]()]', '', self.display_name)
|
|
156
164
|
# current media path
|
|
157
165
|
self.torrent_path = self.path
|
|
158
166
|
# Torrent name
|
|
@@ -26,7 +26,7 @@ class VideoManager:
|
|
|
26
26
|
|
|
27
27
|
self.torrent_found:bool = False
|
|
28
28
|
self.contents: list[Media] = contents
|
|
29
|
-
self.cli: argparse = cli
|
|
29
|
+
self.cli: argparse.Namespace = cli
|
|
30
30
|
|
|
31
31
|
def process(self, selected_tracker: str, tracker_name_list: list, tracker_archive: str) -> list[BittorrentData]:
|
|
32
32
|
"""
|
|
@@ -90,6 +90,7 @@ class VideoManager:
|
|
|
90
90
|
# Get meta from the media video
|
|
91
91
|
video_info = Video(media=content, tmdb_id=db.video_id, trailer_key=db.trailer_key)
|
|
92
92
|
video_info.build_info()
|
|
93
|
+
|
|
93
94
|
# print the title will be shown on the torrent page
|
|
94
95
|
custom_console.bot_log(f"'DISPLAYNAME'...{{{content.display_name}}}\n")
|
|
95
96
|
|
|
@@ -97,8 +98,8 @@ class VideoManager:
|
|
|
97
98
|
unit3d_up = UploadBot(content=content, tracker_name=selected_tracker, cli = self.cli)
|
|
98
99
|
|
|
99
100
|
# Get the data
|
|
100
|
-
unit3d_up.data(show_id=db.video_id, imdb_id=db.imdb_id,
|
|
101
|
-
video_info=video_info)
|
|
101
|
+
unit3d_up.data(show_id=db.video_id, imdb_id=db.imdb_id, tvdb_id = db.tvdb_id,
|
|
102
|
+
show_keywords_list=db.keywords_list, video_info=video_info)
|
|
102
103
|
|
|
103
104
|
# Don't upload if -noup is set to True
|
|
104
105
|
if self.cli.noup:
|
unit3dup/pvtTracker.py
CHANGED
|
@@ -29,10 +29,10 @@ class Myhttp:
|
|
|
29
29
|
self.headers = {
|
|
30
30
|
"User-Agent": "Unit3D-up/0.0 (Linux 5.10.0-23-amd64)",
|
|
31
31
|
"Accept": "application/json",
|
|
32
|
+
"Authorization": f"Bearer {self.api_token}"
|
|
32
33
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
34
|
+
|
|
35
|
+
self.params = {}
|
|
36
36
|
|
|
37
37
|
self.data = {
|
|
38
38
|
"name": "TEST.torrent",
|
|
@@ -43,7 +43,7 @@ class Myhttp:
|
|
|
43
43
|
"resolution_id": 10, # mandatory
|
|
44
44
|
"tmdb": "", # mandatory
|
|
45
45
|
"imdb": "0",
|
|
46
|
-
"tvdb": "0",
|
|
46
|
+
"tvdb": "0",
|
|
47
47
|
"mal": "0", # no ancora implementato
|
|
48
48
|
"igdb": "0",
|
|
49
49
|
"anonymous": 0,
|
|
@@ -56,7 +56,6 @@ class Myhttp:
|
|
|
56
56
|
"free": 0,
|
|
57
57
|
"doubleup": 0,
|
|
58
58
|
"sticky": 0,
|
|
59
|
-
"torrent-cover": "", # no ancora implementato
|
|
60
59
|
}
|
|
61
60
|
|
|
62
61
|
|
unit3dup/pvtVideo.py
CHANGED
|
@@ -79,8 +79,10 @@ class Video:
|
|
|
79
79
|
# Build the description
|
|
80
80
|
build_description = Build(extracted_frames=extracted_frames_webp+extracted_frames, filename= self.display_name)
|
|
81
81
|
self.description = build_description.description()
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
|
|
83
|
+
if self.trailer_key:
|
|
84
|
+
self.description += (f"[b][spoiler=Spoiler: PLAY TRAILER][center][youtube]{self.trailer_key}[/youtube]"
|
|
85
|
+
f"[/center][/spoiler][/b]")
|
|
84
86
|
self.is_hd = is_hd
|
|
85
87
|
|
|
86
88
|
# Caching
|
unit3dup/upload.py
CHANGED
|
@@ -62,11 +62,13 @@ class UploadBot:
|
|
|
62
62
|
custom_console.rule()
|
|
63
63
|
return {}, error_message
|
|
64
64
|
|
|
65
|
-
def data(self,show_id: int , imdb_id: int, show_keywords_list: str, video_info: Video) -> Unit3d | None:
|
|
65
|
+
def data(self,show_id: int , imdb_id: int, tvdb_id: int, show_keywords_list: str, video_info: Video) -> Unit3d | None:
|
|
66
66
|
|
|
67
67
|
self.tracker.data["name"] = self.content.display_name
|
|
68
68
|
self.tracker.data["tmdb"] = show_id
|
|
69
69
|
self.tracker.data["imdb"] = imdb_id if imdb_id else 0
|
|
70
|
+
self.tracker.data["tvdb"] = tvdb_id if tvdb_id else None
|
|
71
|
+
|
|
70
72
|
self.tracker.data["keywords"] = show_keywords_list
|
|
71
73
|
self.tracker.data["category_id"] = self.tracker_data.category.get(self.content.category)
|
|
72
74
|
self.tracker.data["anonymous"] = int(config_settings.user_preferences.ANON)
|
|
@@ -1,176 +1,176 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: Unit3Dup
|
|
3
|
-
Version: 0.8.
|
|
4
|
-
Summary: An uploader for the Unit3D torrent tracker
|
|
5
|
-
Author: Parzival
|
|
6
|
-
License-Expression: MIT
|
|
7
|
-
Project-URL: Homepage, https://github.com/31December99/Unit3Dup
|
|
8
|
-
Requires-Python: >=3.10
|
|
9
|
-
Description-Content-Type: text/x-rst
|
|
10
|
-
License-File: LICENSE
|
|
11
|
-
Requires-Dist: guessit==3.8.0
|
|
12
|
-
Requires-Dist: pymediainfo==6.1.0
|
|
13
|
-
Requires-Dist: python-qbittorrent==0.4.3
|
|
14
|
-
Requires-Dist: pydantic==2.10.6
|
|
15
|
-
Requires-Dist: requests==2.32.3
|
|
16
|
-
Requires-Dist: rich==13.7.1
|
|
17
|
-
Requires-Dist: torf==4.2.7
|
|
18
|
-
Requires-Dist: tqdm==4.66.5
|
|
19
|
-
Requires-Dist: thefuzz==0.22.1
|
|
20
|
-
Requires-Dist: unidecode==1.3.8
|
|
21
|
-
Requires-Dist: pillow==10.4.0
|
|
22
|
-
Requires-Dist: patool==2.4.0
|
|
23
|
-
Requires-Dist: diskcache==5.6.3
|
|
24
|
-
Requires-Dist: httpx==0.27.2
|
|
25
|
-
Requires-Dist: transmission-rpc==7.0.11
|
|
26
|
-
Requires-Dist:
|
|
27
|
-
Requires-Dist:
|
|
28
|
-
Requires-Dist:
|
|
29
|
-
Requires-Dist:
|
|
30
|
-
Dynamic: license-file
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
**Hi !**
|
|
34
|
-
===============================================
|
|
35
|
-
|version| |online| |status| |python| |ubuntu| |debian| |windows|
|
|
36
|
-
|
|
37
|
-
.. |version| image:: https://img.shields.io/pypi/v/unit3dup.svg
|
|
38
|
-
.. |online| image:: https://img.shields.io/badge/Online-green
|
|
39
|
-
.. |status| image:: https://img.shields.io/badge/Status-Active-brightgreen
|
|
40
|
-
.. |python| image:: https://img.shields.io/badge/Python-3.10+-blue
|
|
41
|
-
.. |ubuntu| image:: https://img.shields.io/badge/Ubuntu-22-blue
|
|
42
|
-
.. |debian| image:: https://img.shields.io/badge/Debian-12-blue
|
|
43
|
-
.. |windows| image:: https://img.shields.io/badge/Windows-10-blue
|
|
44
|
-
|
|
45
|
-
Auto Torrent Generator and Uploader
|
|
46
|
-
===================================
|
|
47
|
-
|
|
48
|
-
This Python script generates and uploads torrents based on input provided for movies or TV series and Games.
|
|
49
|
-
|
|
50
|
-
It performs the following tasks:
|
|
51
|
-
|
|
52
|
-
- Scan folder and subfolders
|
|
53
|
-
- Compiles various metadata information to create a torrent
|
|
54
|
-
- Extracts a series of screenshots directly from the video
|
|
55
|
-
- Add webp to your torrent description page
|
|
56
|
-
- Extracts cover from the PDF documents
|
|
57
|
-
- Generates meta-info derived from the video or game
|
|
58
|
-
- Searches for the corresponding ID on TMDB, IGDB
|
|
59
|
-
- Add trailer from TMDB or YouTube
|
|
60
|
-
- Seeding in qBittorrent, Transmission or rTorrent
|
|
61
|
-
- Reseeding one or more torrents at a time
|
|
62
|
-
- Seed your torrents across different OS
|
|
63
|
-
- Add a custom title to your seasons
|
|
64
|
-
- Generate info for a title using MediaInfo
|
|
65
|
-
|
|
66
|
-
unit3dup can grab the first page, convert it to an image (using xpdf),
|
|
67
|
-
and then the bot can upload it to an image host, then add the link to the torrent page description.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
Install and Upgrade
|
|
71
|
-
===================
|
|
72
|
-
|
|
73
|
-
- pip install unit3dup --upgrade
|
|
74
|
-
|
|
75
|
-
Windows Dependencies
|
|
76
|
-
--------------------
|
|
77
|
-
1. Download and unzip https://www.ffmpeg.org/download.html and add its folder to
|
|
78
|
-
PATH environment user variable
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
Only for pdf
|
|
82
|
-
~~~~~~~~~~~~
|
|
83
|
-
1. Download and unzip poppler for Windows from https://github.com/oschwartz10612/poppler-windows/releases
|
|
84
|
-
2. Put the folder 'bin' in the system path (e.g. ``C:\poppler-24.08.0\Library\bin``)
|
|
85
|
-
3. *Close and reopen a new console window*
|
|
86
|
-
4. Test it: Run ``pdftocairo`` in the terminal
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
Ubuntu/Debian Dependencies
|
|
90
|
-
--------------------------
|
|
91
|
-
- sudo apt install ffmpeg
|
|
92
|
-
|
|
93
|
-
Only for pdf
|
|
94
|
-
~~~~~~~~~~~~
|
|
95
|
-
- sudo apt install poppler-utils
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
RUN
|
|
99
|
-
======
|
|
100
|
-
|
|
101
|
-
.. code-block:: python
|
|
102
|
-
|
|
103
|
-
unit3dup -u <filepath>
|
|
104
|
-
unit3dup -f <folderpath>
|
|
105
|
-
unit3dup -scan <folderpath>
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
DOC
|
|
110
|
-
===
|
|
111
|
-
|
|
112
|
-
Link `Unit3DUP <https://unit3dup.readthedocs.io/en/latest/index.html#>`_
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
ImageHost
|
|
116
|
-
=========
|
|
117
|
-
|
|
118
|
-
The bot can upload images to the following image hosting platforms:
|
|
119
|
-
|
|
120
|
-
+------------------+----------------------------+
|
|
121
|
-
| **Image Host** | **URL** |
|
|
122
|
-
+==================+============================+
|
|
123
|
-
| ``ImgBB`` | https://imgbb.com |
|
|
124
|
-
+------------------+----------------------------+
|
|
125
|
-
| ``FreeImage`` | https://freeimage.host |
|
|
126
|
-
+------------------+----------------------------+
|
|
127
|
-
| ``PtScreens`` | https://ptscreens.com |
|
|
128
|
-
+------------------+----------------------------+
|
|
129
|
-
| ``LensDump`` | https://lensdump.com |
|
|
130
|
-
+------------------+----------------------------+
|
|
131
|
-
| ``ImgFI`` | https://imgfi.com |
|
|
132
|
-
+------------------+----------------------------+
|
|
133
|
-
| ``PassIMA`` | https://passtheima.ge |
|
|
134
|
-
+------------------+----------------------------+
|
|
135
|
-
| ``ImaRide`` | https://www.imageride.net |
|
|
136
|
-
+------------------+----------------------------+
|
|
137
|
-
|
|
138
|
-
Trackers
|
|
139
|
-
========
|
|
140
|
-
|
|
141
|
-
The Italian tracker: a multitude of people from diverse technical and social backgrounds,
|
|
142
|
-
united by a shared passion for torrents and more
|
|
143
|
-
|
|
144
|
-
+------------------+----------------------------+
|
|
145
|
-
| **Trackers** | **Description** |
|
|
146
|
-
+==================+============================+
|
|
147
|
-
| ``ITT`` | https://itatorrents.xyz/ |
|
|
148
|
-
+------------------+----------------------------+
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
.. image:: https://img.shields.io/badge/Telegram-Join-blue?logo=telegram
|
|
152
|
-
:target: https://t.me/+hj294GabGWJlMDI8
|
|
153
|
-
:alt: Unisciti su Telegram
|
|
154
|
-
|
|
155
|
-
.. image:: https://img.shields.io/discord/1214696147600408698?label=Discord&logo=discord&style=flat
|
|
156
|
-
:target: https://discord.gg/Skvune9P
|
|
157
|
-
:alt: Discord Server
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
🎯 Streaming Community
|
|
162
|
-
======================
|
|
163
|
-
|
|
164
|
-
`goto GitHub Project <https://github.com/Arrowar/StreamingCommunity>`_
|
|
165
|
-
|
|
166
|
-
An open-source script for downloading movies, TV shows, and anime from various websites,
|
|
167
|
-
built by a community of people with a shared interest in programming.
|
|
168
|
-
|
|
169
|
-
.. image:: https://img.shields.io/badge/Telegram-Join-blue?logo=telegram
|
|
170
|
-
:target: https://t.me/+hj294GabGWJlMDI8
|
|
171
|
-
:alt: Unisciti su Telegram
|
|
172
|
-
|
|
173
|
-
.. image:: https://img.shields.io/badge/StreamingCommunity-blue.svg
|
|
174
|
-
:target: https://github.com/Arrowar/StreamingCommunity
|
|
175
|
-
:alt: StreamingCommunity Badge
|
|
176
|
-
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: Unit3Dup
|
|
3
|
+
Version: 0.8.24
|
|
4
|
+
Summary: An uploader for the Unit3D torrent tracker
|
|
5
|
+
Author: Parzival
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/31December99/Unit3Dup
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Description-Content-Type: text/x-rst
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Requires-Dist: guessit==3.8.0
|
|
12
|
+
Requires-Dist: pymediainfo==6.1.0
|
|
13
|
+
Requires-Dist: python-qbittorrent==0.4.3
|
|
14
|
+
Requires-Dist: pydantic==2.10.6
|
|
15
|
+
Requires-Dist: requests==2.32.3
|
|
16
|
+
Requires-Dist: rich==13.7.1
|
|
17
|
+
Requires-Dist: torf==4.2.7
|
|
18
|
+
Requires-Dist: tqdm==4.66.5
|
|
19
|
+
Requires-Dist: thefuzz==0.22.1
|
|
20
|
+
Requires-Dist: unidecode==1.3.8
|
|
21
|
+
Requires-Dist: pillow==10.4.0
|
|
22
|
+
Requires-Dist: patool==2.4.0
|
|
23
|
+
Requires-Dist: diskcache==5.6.3
|
|
24
|
+
Requires-Dist: httpx==0.27.2
|
|
25
|
+
Requires-Dist: transmission-rpc==7.0.11
|
|
26
|
+
Requires-Dist: pathvalidate==3.2.3
|
|
27
|
+
Requires-Dist: bencode2==0.3.24
|
|
28
|
+
Requires-Dist: rtorrent-rpc==0.9.4
|
|
29
|
+
Requires-Dist: tvdb_v4_official==1.1.0
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
**Hi !**
|
|
34
|
+
===============================================
|
|
35
|
+
|version| |online| |status| |python| |ubuntu| |debian| |windows|
|
|
36
|
+
|
|
37
|
+
.. |version| image:: https://img.shields.io/pypi/v/unit3dup.svg
|
|
38
|
+
.. |online| image:: https://img.shields.io/badge/Online-green
|
|
39
|
+
.. |status| image:: https://img.shields.io/badge/Status-Active-brightgreen
|
|
40
|
+
.. |python| image:: https://img.shields.io/badge/Python-3.10+-blue
|
|
41
|
+
.. |ubuntu| image:: https://img.shields.io/badge/Ubuntu-22-blue
|
|
42
|
+
.. |debian| image:: https://img.shields.io/badge/Debian-12-blue
|
|
43
|
+
.. |windows| image:: https://img.shields.io/badge/Windows-10-blue
|
|
44
|
+
|
|
45
|
+
Auto Torrent Generator and Uploader
|
|
46
|
+
===================================
|
|
47
|
+
|
|
48
|
+
This Python script generates and uploads torrents based on input provided for movies or TV series and Games.
|
|
49
|
+
|
|
50
|
+
It performs the following tasks:
|
|
51
|
+
|
|
52
|
+
- Scan folder and subfolders
|
|
53
|
+
- Compiles various metadata information to create a torrent
|
|
54
|
+
- Extracts a series of screenshots directly from the video
|
|
55
|
+
- Add webp to your torrent description page
|
|
56
|
+
- Extracts cover from the PDF documents
|
|
57
|
+
- Generates meta-info derived from the video or game
|
|
58
|
+
- Searches for the corresponding ID on TMDB, IGDB, IMDB,TVDB
|
|
59
|
+
- Add trailer from TMDB or YouTube
|
|
60
|
+
- Seeding in qBittorrent, Transmission or rTorrent
|
|
61
|
+
- Reseeding one or more torrents at a time
|
|
62
|
+
- Seed your torrents across different OS
|
|
63
|
+
- Add a custom title to your seasons
|
|
64
|
+
- Generate info for a title using MediaInfo
|
|
65
|
+
|
|
66
|
+
unit3dup can grab the first page, convert it to an image (using xpdf),
|
|
67
|
+
and then the bot can upload it to an image host, then add the link to the torrent page description.
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
Install and Upgrade
|
|
71
|
+
===================
|
|
72
|
+
|
|
73
|
+
- pip install unit3dup --upgrade
|
|
74
|
+
|
|
75
|
+
Windows Dependencies
|
|
76
|
+
--------------------
|
|
77
|
+
1. Download and unzip https://www.ffmpeg.org/download.html and add its folder to
|
|
78
|
+
PATH environment user variable
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
Only for pdf
|
|
82
|
+
~~~~~~~~~~~~
|
|
83
|
+
1. Download and unzip poppler for Windows from https://github.com/oschwartz10612/poppler-windows/releases
|
|
84
|
+
2. Put the folder 'bin' in the system path (e.g. ``C:\poppler-24.08.0\Library\bin``)
|
|
85
|
+
3. *Close and reopen a new console window*
|
|
86
|
+
4. Test it: Run ``pdftocairo`` in the terminal
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
Ubuntu/Debian Dependencies
|
|
90
|
+
--------------------------
|
|
91
|
+
- sudo apt install ffmpeg
|
|
92
|
+
|
|
93
|
+
Only for pdf
|
|
94
|
+
~~~~~~~~~~~~
|
|
95
|
+
- sudo apt install poppler-utils
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
RUN
|
|
99
|
+
======
|
|
100
|
+
|
|
101
|
+
.. code-block:: python
|
|
102
|
+
|
|
103
|
+
unit3dup -u <filepath>
|
|
104
|
+
unit3dup -f <folderpath>
|
|
105
|
+
unit3dup -scan <folderpath>
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
DOC
|
|
110
|
+
===
|
|
111
|
+
|
|
112
|
+
Link `Unit3DUP <https://unit3dup.readthedocs.io/en/latest/index.html#>`_
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
ImageHost
|
|
116
|
+
=========
|
|
117
|
+
|
|
118
|
+
The bot can upload images to the following image hosting platforms:
|
|
119
|
+
|
|
120
|
+
+------------------+----------------------------+
|
|
121
|
+
| **Image Host** | **URL** |
|
|
122
|
+
+==================+============================+
|
|
123
|
+
| ``ImgBB`` | https://imgbb.com |
|
|
124
|
+
+------------------+----------------------------+
|
|
125
|
+
| ``FreeImage`` | https://freeimage.host |
|
|
126
|
+
+------------------+----------------------------+
|
|
127
|
+
| ``PtScreens`` | https://ptscreens.com |
|
|
128
|
+
+------------------+----------------------------+
|
|
129
|
+
| ``LensDump`` | https://lensdump.com |
|
|
130
|
+
+------------------+----------------------------+
|
|
131
|
+
| ``ImgFI`` | https://imgfi.com |
|
|
132
|
+
+------------------+----------------------------+
|
|
133
|
+
| ``PassIMA`` | https://passtheima.ge |
|
|
134
|
+
+------------------+----------------------------+
|
|
135
|
+
| ``ImaRide`` | https://www.imageride.net |
|
|
136
|
+
+------------------+----------------------------+
|
|
137
|
+
|
|
138
|
+
Trackers
|
|
139
|
+
========
|
|
140
|
+
|
|
141
|
+
The Italian tracker: a multitude of people from diverse technical and social backgrounds,
|
|
142
|
+
united by a shared passion for torrents and more
|
|
143
|
+
|
|
144
|
+
+------------------+----------------------------+
|
|
145
|
+
| **Trackers** | **Description** |
|
|
146
|
+
+==================+============================+
|
|
147
|
+
| ``ITT`` | https://itatorrents.xyz/ |
|
|
148
|
+
+------------------+----------------------------+
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
.. image:: https://img.shields.io/badge/Telegram-Join-blue?logo=telegram
|
|
152
|
+
:target: https://t.me/+hj294GabGWJlMDI8
|
|
153
|
+
:alt: Unisciti su Telegram
|
|
154
|
+
|
|
155
|
+
.. image:: https://img.shields.io/discord/1214696147600408698?label=Discord&logo=discord&style=flat
|
|
156
|
+
:target: https://discord.gg/Skvune9P
|
|
157
|
+
:alt: Discord Server
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
🎯 Streaming Community
|
|
162
|
+
======================
|
|
163
|
+
|
|
164
|
+
`goto GitHub Project <https://github.com/Arrowar/StreamingCommunity>`_
|
|
165
|
+
|
|
166
|
+
An open-source script for downloading movies, TV shows, and anime from various websites,
|
|
167
|
+
built by a community of people with a shared interest in programming.
|
|
168
|
+
|
|
169
|
+
.. image:: https://img.shields.io/badge/Telegram-Join-blue?logo=telegram
|
|
170
|
+
:target: https://t.me/+hj294GabGWJlMDI8
|
|
171
|
+
:alt: Unisciti su Telegram
|
|
172
|
+
|
|
173
|
+
.. image:: https://img.shields.io/badge/StreamingCommunity-blue.svg
|
|
174
|
+
:target: https://github.com/Arrowar/StreamingCommunity
|
|
175
|
+
:alt: StreamingCommunity Badge
|
|
176
|
+
|
|
@@ -8,14 +8,15 @@ common/extractor.py,sha256=WKZwt2kQfKO2VJ1rtwE_j6Zl84zICnowZq_Ql16wmRc,4564
|
|
|
8
8
|
common/frames.py,sha256=p_jsaXII5tZTVt5ymu-w1hk2c_UMeOn3PZeuVR-wXWY,7973
|
|
9
9
|
common/mediainfo.py,sha256=U2r1jJejBsV8GP3iPk4O8_NkHO5RQ9Kkh2bKwVNUBmg,6229
|
|
10
10
|
common/mediainfo_string.py,sha256=8vuWlF2bqWRKpDbn81bV2fPA7hbl7RwOnxN2i4E3zNE,3958
|
|
11
|
-
common/settings.py,sha256=
|
|
11
|
+
common/settings.py,sha256=IJ1Mh2zTZDdcF4bs5H8dnGlTAwuIDTJdnNVE25xZXvY,33148
|
|
12
12
|
common/title.py,sha256=nFainfUBTYW9ML4Y-CB9ZFD_Es-OZXcAMPUo6D09u3k,3793
|
|
13
13
|
common/torrent_clients.py,sha256=NOIpYtLG_bA19HwcH9ahGFmGNtRkoMO6nAjma-JzDfs,12040
|
|
14
|
-
common/utility.py,sha256=
|
|
14
|
+
common/utility.py,sha256=QV_kVjUJ1GQ67fH3UB-zwqh6_5aHhinqLVMR0kaPG1w,9050
|
|
15
15
|
common/external_services/__init__.py,sha256=rU7HPEcZ7WQFD8eqDzuye2xHPBjxXPwPqpt7IT08mkM,178
|
|
16
16
|
common/external_services/imageHost.py,sha256=00sVVmSFQD0AE1I8batCJQUxNn734NpNz8p1qDWyzHc,10215
|
|
17
|
-
common/external_services/imdb.py,sha256=
|
|
18
|
-
common/external_services/mediaresult.py,sha256=
|
|
17
|
+
common/external_services/imdb.py,sha256=oqD2qKJVUjUDkhL2gT8kie_8St1_UMe5YpDixpzpXTM,588
|
|
18
|
+
common/external_services/mediaresult.py,sha256=x5Kb6dCZKACV-xkQ7un8cuTU7-3EyKreS32MGhFnj5s,725
|
|
19
|
+
common/external_services/tvdb.py,sha256=qnlAG6Snbln80fXlaRl3dDuvGA5fS7x_luJksQFHvZA,1076
|
|
19
20
|
common/external_services/ftpx/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
21
|
common/external_services/ftpx/client.py,sha256=D_23Cw9zeQQIiVhUIUE13ENXNVyP4-MMx_Rgqg67U7Y,10713
|
|
21
22
|
common/external_services/ftpx/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -38,7 +39,7 @@ common/external_services/sessions/exceptions.py,sha256=s3jpm-LiIJvGFa-Vr9cU09qJ9
|
|
|
38
39
|
common/external_services/sessions/session.py,sha256=DFqcIKQAEJrJ5bOEBxbnxbDrxEx31nmS_NjCYMmJ7EQ,4877
|
|
39
40
|
common/external_services/theMovieDB/__init__.py,sha256=uBRzseXxb-L4XS6Np548FlWrSwVTQcTwLkOca9cuDyc,82
|
|
40
41
|
common/external_services/theMovieDB/core/__init__.py,sha256=37I0EaaHJEfnVUHmswZwJtEOvI929Cel9p15VJjhF_k,16
|
|
41
|
-
common/external_services/theMovieDB/core/api.py,sha256=
|
|
42
|
+
common/external_services/theMovieDB/core/api.py,sha256=UN6kZ4WJKDmoGGqJ9_P1EElzRYJf-wi05HLt-a7MT8Y,19412
|
|
42
43
|
common/external_services/theMovieDB/core/keywords.py,sha256=hGm4-iUU32LlrTlNO4DnUl3a0lknaMYVymcuk8k38G0,118
|
|
43
44
|
common/external_services/theMovieDB/core/videos.py,sha256=K3unERRMTKpSJTwhvboRHUQHhxTdreTWIxJOrfzDnTs,329
|
|
44
45
|
common/external_services/theMovieDB/core/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -68,30 +69,30 @@ unit3dup/automode.py,sha256=JbHIvK5mHbNxpq_m5o26FEpqEUlL9XbTyUUPvKD6EqQ,4412
|
|
|
68
69
|
unit3dup/bot.py,sha256=2P24z_8UM58binKqrM0eIXxPTiWEduyK2bdfVkmxPEQ,8759
|
|
69
70
|
unit3dup/duplicate.py,sha256=0soPxYGTB6P5tb-iIUbZTdcI0-8CJntzoG5PnKNJ1vg,10437
|
|
70
71
|
unit3dup/exceptions.py,sha256=DhlcbbZEuvEYk5VSZTnle8oRg5I8Pq6Y6fxXTdOOOfo,4078
|
|
71
|
-
unit3dup/media.py,sha256=
|
|
72
|
+
unit3dup/media.py,sha256=nd2Wf78EN-eyoK6-OugvCcdDw6KtSkTU7MEpM4QR8qw,13920
|
|
72
73
|
unit3dup/pvtDocu.py,sha256=ZLMaal2fn6ZcFPLtET0LTmYEPjhJmLitEjkICHrm9CQ,4594
|
|
73
74
|
unit3dup/pvtTorrent.py,sha256=cItlsCpcUJL23iXQHy0YzrrvV3JSl54UlBgm8_UROAs,2559
|
|
74
|
-
unit3dup/pvtTracker.py,sha256=
|
|
75
|
-
unit3dup/pvtVideo.py,sha256=
|
|
75
|
+
unit3dup/pvtTracker.py,sha256=p5a1GoYcWqDQ1iiaFwVwAchRSXGbGaycm4QGHhw35fI,18611
|
|
76
|
+
unit3dup/pvtVideo.py,sha256=HZbkk1GiCFe6g7e0UPoOc5VGzRaTsViNnU1SCuijkIs,3944
|
|
76
77
|
unit3dup/torrent.py,sha256=surV0royo_GCEvdlPIP1S9gvFvJb1u26XM11rZOGVEg,20064
|
|
77
|
-
unit3dup/upload.py,sha256=
|
|
78
|
-
unit3dup/media_manager/ContentManager.py,sha256=
|
|
78
|
+
unit3dup/upload.py,sha256=cXK1yXOH73wz62ghy7OEeDsp4dtxf_bYR69QmQn9NpM,6694
|
|
79
|
+
unit3dup/media_manager/ContentManager.py,sha256=fj0ftb64LkI7yZQ4L9S4AW76Vf28vk8RoEV3eI4h_qc,8012
|
|
79
80
|
unit3dup/media_manager/DocuManager.py,sha256=oFt7jlxj-gIUty9PADBQV5a24bsv3yhjKhwI6niOhf4,3116
|
|
80
81
|
unit3dup/media_manager/GameManager.py,sha256=9EmPeNrirOwaVOj-vkLr29Xo7daIA4ssqrrt0WyMl30,4090
|
|
81
82
|
unit3dup/media_manager/MediaInfoManager.py,sha256=0NO9dgD7seJM67B3DRnwvRIdoy7bfquBUox-PnHInK8,1081
|
|
82
83
|
unit3dup/media_manager/SeedManager.py,sha256=Vqpf_xpGbxsJHg0C3-kL0_tdF4f2FRPlZH7UpnmAE3g,1912
|
|
83
84
|
unit3dup/media_manager/TorrentManager.py,sha256=qqM1d1TyfBuruXtKLRbQ8gFk3_2JNH9dOa6Yg-QnDCw,6507
|
|
84
|
-
unit3dup/media_manager/VideoManager.py,sha256=
|
|
85
|
+
unit3dup/media_manager/VideoManager.py,sha256=SHhchcVoID2JnCP7oXp8TcDFJDFDUk_CQylW2YLy31c,5471
|
|
85
86
|
unit3dup/media_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
86
87
|
unit3dup/media_manager/common.py,sha256=EDsD1FVNiWPS9teHs5vyGkYkC92gzFdSanMyMAR5vpU,10147
|
|
87
88
|
unit3dup/web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
88
89
|
unit3dup/web/main.py,sha256=BzjKDgAjKZMnoQwx7nDDbs_64kCrFO1VYpbHmsGiFVc,1267
|
|
89
|
-
unit3dup-0.8.
|
|
90
|
+
unit3dup-0.8.24.dist-info/licenses/LICENSE,sha256=GNAZDLhU0xz8QPbIyHAOYlVnQYDvKWk2N9fZJMhqaG8,1090
|
|
90
91
|
view/__init__.py,sha256=XIzb6rl58HmYPnksD73cYMFF88dn6FHa3u7bOHFbChk,81
|
|
91
92
|
view/custom_console.py,sha256=OITmkEoQH9N_uE5ElPaSdc8XvaLzE9wcwTbOHtcMvrI,5629
|
|
92
93
|
view/web_console.py,sha256=YkxutJK5GqswMKEF77EllphPYQW0eb8OIBlplucHhvM,7697
|
|
93
|
-
unit3dup-0.8.
|
|
94
|
-
unit3dup-0.8.
|
|
95
|
-
unit3dup-0.8.
|
|
96
|
-
unit3dup-0.8.
|
|
97
|
-
unit3dup-0.8.
|
|
94
|
+
unit3dup-0.8.24.dist-info/METADATA,sha256=nWDki-S6GSthm4FV3867IQ__e-HyLbLafr-Nu15-6EE,5656
|
|
95
|
+
unit3dup-0.8.24.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
96
|
+
unit3dup-0.8.24.dist-info/entry_points.txt,sha256=fxXSyI6-r6jy9_v-C5ZHm03q1aC3tE9EvCQZxC1NQnI,52
|
|
97
|
+
unit3dup-0.8.24.dist-info/top_level.txt,sha256=19NVMnQNkJxBUKebRNaYCRs56A5CO4U1L67GMQCPiLU,21
|
|
98
|
+
unit3dup-0.8.24.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|