ittup 1.2.1__tar.gz → 1.2.3__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.
- {ittup-1.2.1 → ittup-1.2.3}/PKG-INFO +2 -2
- {ittup-1.2.1 → ittup-1.2.3}/README.rst +1 -1
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/settings.py +3 -5
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/tags.py +123 -20
- {ittup-1.2.1 → ittup-1.2.3}/ittup/core/bot.py +10 -4
- {ittup-1.2.1 → ittup-1.2.3}/ittup/core/media_manager/DocuManager.py +4 -4
- {ittup-1.2.1 → ittup-1.2.3}/ittup/core/media_manager/GameManager.py +4 -5
- {ittup-1.2.1 → ittup-1.2.3}/ittup/core/media_manager/VideoManager.py +4 -3
- {ittup-1.2.1 → ittup-1.2.3}/ittup.egg-info/PKG-INFO +2 -2
- {ittup-1.2.1 → ittup-1.2.3}/ittup.egg-info/SOURCES.txt +2 -1
- {ittup-1.2.1 → ittup-1.2.3}/pyproject.toml +1 -1
- ittup-1.2.3/tests/test_vcodec.py +129 -0
- {ittup-1.2.1 → ittup-1.2.3}/LICENSE +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/__init__.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/__main__.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/__init__.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/about.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/bdinfo_string.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/bittorrent.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/command.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/constants.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/database.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/__init__.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/ftpx/__init__.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/ftpx/client.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/ftpx/core/__init__.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/ftpx/core/ftpx_service.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/ftpx/core/ftpx_session.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/ftpx/core/menu.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/ftpx/core/models/__init__.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/ftpx/core/models/list.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/igdb/__init__.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/igdb/client.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/igdb/core/__init__.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/igdb/core/api.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/igdb/core/models/__init__.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/igdb/core/models/search.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/igdb/core/platformid.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/igdb/core/tags.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/imageHost.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/mediaresult.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/sessions/__init__.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/sessions/agents.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/sessions/exceptions.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/sessions/session.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/theMovieDB/__init__.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/theMovieDB/core/__init__.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/theMovieDB/core/api.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/theMovieDB/core/keywords.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/theMovieDB/core/models/__init__.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/theMovieDB/core/models/movie/__init__.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/theMovieDB/core/models/movie/alternative.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/theMovieDB/core/models/movie/details.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/theMovieDB/core/models/movie/movie.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/theMovieDB/core/models/movie/nowplaying.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/theMovieDB/core/models/movie/release_info.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/theMovieDB/core/models/movie/translations.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/theMovieDB/core/models/tvshow/__init__.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/theMovieDB/core/models/tvshow/alternative.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/theMovieDB/core/models/tvshow/details.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/theMovieDB/core/models/tvshow/on_the_air.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/theMovieDB/core/models/tvshow/translations.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/theMovieDB/core/models/tvshow/tvshow.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/theMovieDB/core/videos.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/trailers/__init__.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/trailers/api.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/trailers/response.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/tvdb.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/extractor.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/frames.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/mediainfo.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/mediainfo_string.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/title.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/torrent_clients.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/trackers/__init__.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/trackers/ban_list.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/trackers/data.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/trackers/itt.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/trackers/signs_list.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/trackers/tags_list.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/trackers/trackers.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/common/utility.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/core/__init__.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/core/__main__.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/core/automode.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/core/duplicate.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/core/exceptions.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/core/media.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/core/media_manager/ContentManager.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/core/media_manager/MediaInfoManager.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/core/media_manager/SeedManager.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/core/media_manager/TorrentManager.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/core/media_manager/__init__.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/core/media_manager/common.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/core/pvtDocu.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/core/pvtTorrent.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/core/pvtTracker.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/core/pvtVideo.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/core/torrent.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/core/upload.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/view/__init__.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup/view/custom_console.py +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup.egg-info/dependency_links.txt +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup.egg-info/entry_points.txt +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup.egg-info/requires.txt +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/ittup.egg-info/top_level.txt +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/requirements.txt +0 -0
- {ittup-1.2.1 → ittup-1.2.3}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ittup
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.3
|
|
4
4
|
Summary: ItaTorrents uploader
|
|
5
5
|
Author: Parzival, TehMaat
|
|
6
6
|
License-Expression: MIT
|
|
@@ -68,7 +68,7 @@ It performs the following tasks:
|
|
|
68
68
|
- Seeding in qBittorrent, Transmission or rTorrent
|
|
69
69
|
- Reseeding one or more torrents at a time
|
|
70
70
|
- Seed your torrents across different OS
|
|
71
|
-
- Generate various tags for titles: ``version``, ``resolution``, ``uhd``, ``platform``, ``source``, ``remux``, ``multi``, ``acodec``, ``channels``, ``flag``, ``subtitle``, ``vcodec``, ``hdr
|
|
71
|
+
- Generate various tags for titles: ``version``, ``resolution``, ``uhd``, ``platform``, ``source``, ``remux``, ``multi``, ``acodec``, ``channels``, ``flag``, ``subtitle``, ``vcodec``, ``hdr``
|
|
72
72
|
|
|
73
73
|
|
|
74
74
|
ITTup can grab the first page, convert it to an image (using xpdf),
|
|
@@ -35,7 +35,7 @@ It performs the following tasks:
|
|
|
35
35
|
- Seeding in qBittorrent, Transmission or rTorrent
|
|
36
36
|
- Reseeding one or more torrents at a time
|
|
37
37
|
- Seed your torrents across different OS
|
|
38
|
-
- Generate various tags for titles: ``version``, ``resolution``, ``uhd``, ``platform``, ``source``, ``remux``, ``multi``, ``acodec``, ``channels``, ``flag``, ``subtitle``, ``vcodec``, ``hdr
|
|
38
|
+
- Generate various tags for titles: ``version``, ``resolution``, ``uhd``, ``platform``, ``source``, ``remux``, ``multi``, ``acodec``, ``channels``, ``flag``, ``subtitle``, ``vcodec``, ``hdr``
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
ITTup can grab the first page, convert it to an image (using xpdf),
|
|
@@ -625,13 +625,11 @@ class Load:
|
|
|
625
625
|
"NUMBER_OF_SCREENSHOTS": 4,
|
|
626
626
|
"TAGS_POSITION_MOVIE": ["title", "year", "part", "version", "resolution", "uhd", "platform", "source",
|
|
627
627
|
"remux",
|
|
628
|
-
"multi", "acodec", "channels", "flag", "subtitle", "hdr", "vcodec",
|
|
629
|
-
"video_encoder"],
|
|
628
|
+
"multi", "acodec", "channels", "flag", "subtitle", "hdr", "vcodec"],
|
|
630
629
|
|
|
631
630
|
"TAGS_POSITION_SERIE": ["title", "season", "version", "resolution", "uhd", "platform", "source",
|
|
632
631
|
"remux",
|
|
633
|
-
"multi", "acodec", "channels", "flag", "subtitle", "hdr", "vcodec",
|
|
634
|
-
"video_encoder"],
|
|
632
|
+
"multi", "acodec", "channels", "flag", "subtitle", "hdr", "vcodec"],
|
|
635
633
|
|
|
636
634
|
"YOUTUBE_PREF_LANG": "IT",
|
|
637
635
|
"DUPLICATE_ON": "true",
|
|
@@ -662,7 +660,7 @@ class Load:
|
|
|
662
660
|
"MY_TEXT_1": "",
|
|
663
661
|
"MY_TEXT_2": "",
|
|
664
662
|
"MY_TEXT_3": "",
|
|
665
|
-
"MY_TEXT_H": "
|
|
663
|
+
"MY_TEXT_H": ""
|
|
666
664
|
},
|
|
667
665
|
"options": {
|
|
668
666
|
"FTPX_USER": "user",
|
|
@@ -46,18 +46,28 @@ audio_translate = {
|
|
|
46
46
|
"MPEG Audio": "MPEG",
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"H264": "H.264",
|
|
49
|
+
# Codec family from the MediaInfo 'format' of the video track
|
|
50
|
+
video_family = {
|
|
51
|
+
"AVC": "264",
|
|
52
|
+
"HEVC": "265",
|
|
54
53
|
}
|
|
55
54
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
# Release type from the source/remux tags of the file name. Each tag is mapped on its own:
|
|
56
|
+
# DLMUX is in the "remux" category but it is a WEB-DL
|
|
57
|
+
UNTOUCHED_TAGS = {"REMUX", "BDREMUX", "BDMUX", "VU", "UNTOUCHED", "DVDMUX", "FULL DISC"}
|
|
58
|
+
WEB_TAGS = {"WEB-DL", "WEB", "WEBDL", "WEB-DLMUX", "WEBDLMUX", "WEB-MUX", "WEBMUX", "DLMUX", "HDTV"}
|
|
59
|
+
ENCODE_TAGS = {"ENCODE", "WEBRIP", "BDRIP", "BRRIP", "DVDRIP", "UHDRIP", "FHDRIP", "WEB-DLRIP", "WEBDLRIP"}
|
|
60
|
+
|
|
61
|
+
# How each release type writes the 264/265 codec
|
|
62
|
+
video_codec_labels = {
|
|
63
|
+
"untouched": {"264": "AVC", "265": "HEVC"},
|
|
64
|
+
"web": {"264": "H.264", "265": "H.265"},
|
|
65
|
+
"encode": {"264": "x264", "265": "x265"},
|
|
59
66
|
}
|
|
60
67
|
|
|
68
|
+
# x264, x.264, H264, H.265, AVC, HEVC... in the file name
|
|
69
|
+
NAME_CODEC_PATTERN = re.compile(r'(?<![a-z0-9])(?:[xh][._ -]?26([45])|(avc)|(hevc))(?![a-z0-9])', re.IGNORECASE)
|
|
70
|
+
|
|
61
71
|
TAG_NORMALIZE = {
|
|
62
72
|
"BLURAY": "BluRay",
|
|
63
73
|
"BLU-RAY": "BluRay",
|
|
@@ -85,6 +95,8 @@ class SearchTags(object):
|
|
|
85
95
|
self.title = title
|
|
86
96
|
self.year = year
|
|
87
97
|
self.tags_dict = {}
|
|
98
|
+
self.release_type: str | None = None
|
|
99
|
+
self.name_family: str | None = None
|
|
88
100
|
|
|
89
101
|
self.TAG_TYPES: dict = tags_list
|
|
90
102
|
self.SIGNS_LIST: dict = sign_list
|
|
@@ -187,6 +199,14 @@ class SearchTags(object):
|
|
|
187
199
|
if self.tags_dict.get("source", None):
|
|
188
200
|
self.tags_dict["source"] = list(dict.fromkeys(self.tags_dict["source"]))
|
|
189
201
|
|
|
202
|
+
# x264/x265 from the file name only tell the codec family: the codec is written by vcodec
|
|
203
|
+
name_encoders = self.tags_dict.pop('video_encoder', [])
|
|
204
|
+
self.name_family = self.codec_family_from_name(self.filename, name_encoders)
|
|
205
|
+
# Read the release type before 'remux' gets the REMUX tag appended below
|
|
206
|
+
self.release_type = self.detect_release_type(
|
|
207
|
+
self.tags_dict.get('source', []) + self.tags_dict.get('remux', [])
|
|
208
|
+
)
|
|
209
|
+
|
|
190
210
|
if norm:
|
|
191
211
|
# Skip if it is part of title es: "Wicked.Parte.2.2025.iTA" Title = Wicked Parte 2
|
|
192
212
|
if not any(t in self.title.lower() for t in ['part', 'parte']):
|
|
@@ -208,10 +228,6 @@ class SearchTags(object):
|
|
|
208
228
|
elif category == "vcodec":
|
|
209
229
|
updated_category = self.mediainfo_video(category=category)
|
|
210
230
|
|
|
211
|
-
elif category == "video_encoder":
|
|
212
|
-
if self.tags_dict.get('video_encoder', None):
|
|
213
|
-
self.tags_dict['video_encoder'][0] = self.tags_dict['video_encoder'][0].lower()
|
|
214
|
-
|
|
215
231
|
elif category == "hdr":
|
|
216
232
|
updated_category = self.mediainfo_hdr(category=category)
|
|
217
233
|
if not updated_category:
|
|
@@ -297,16 +313,103 @@ class SearchTags(object):
|
|
|
297
313
|
audio_codecs.extend(languages)
|
|
298
314
|
return {category: audio_codecs}
|
|
299
315
|
|
|
316
|
+
@staticmethod
|
|
317
|
+
def detect_release_type(tags: list) -> str | None:
|
|
318
|
+
"""
|
|
319
|
+
Release type from the source/remux tags: 'encode', 'untouched', 'web' or None when uncertain
|
|
320
|
+
(es. BLURAY alone or no tags)
|
|
321
|
+
"""
|
|
322
|
+
tags = {str(tag).upper() for tag in tags}
|
|
323
|
+
if tags & ENCODE_TAGS:
|
|
324
|
+
return "encode"
|
|
325
|
+
if tags & UNTOUCHED_TAGS:
|
|
326
|
+
return "untouched"
|
|
327
|
+
if tags & WEB_TAGS:
|
|
328
|
+
return "web"
|
|
329
|
+
return None
|
|
330
|
+
|
|
331
|
+
@staticmethod
|
|
332
|
+
def codec_family_from_name(filename: str, name_encoders: list) -> str | None:
|
|
333
|
+
"""
|
|
334
|
+
Codec family ('264' or '265') written in the file name, None if missing or ambiguous
|
|
335
|
+
"""
|
|
336
|
+
families = {str(tag)[-3:] for tag in name_encoders}
|
|
337
|
+
for match in NAME_CODEC_PATTERN.finditer(filename):
|
|
338
|
+
if match.group(1):
|
|
339
|
+
families.add(f"26{match.group(1)}")
|
|
340
|
+
elif match.group(2):
|
|
341
|
+
families.add("264")
|
|
342
|
+
else:
|
|
343
|
+
families.add("265")
|
|
344
|
+
families &= {"264", "265"}
|
|
345
|
+
return families.pop() if len(families) == 1 else None
|
|
346
|
+
|
|
347
|
+
@staticmethod
|
|
348
|
+
def encoder_signature(video: dict) -> str | None:
|
|
349
|
+
"""
|
|
350
|
+
x264/x265 signature left by the encoder in the video track ('264', '265' or None)
|
|
351
|
+
"""
|
|
352
|
+
library = str(video.get('writing_library') or '').lower()
|
|
353
|
+
for family in ("264", "265"):
|
|
354
|
+
if f"x{family}" in library:
|
|
355
|
+
return family
|
|
356
|
+
settings = str(video.get('encoding_settings') or '').lower()
|
|
357
|
+
if "cabac=" in settings:
|
|
358
|
+
return "264"
|
|
359
|
+
if "cpuid=" in settings or "ctu=" in settings:
|
|
360
|
+
return "265"
|
|
361
|
+
return None
|
|
362
|
+
|
|
363
|
+
@staticmethod
|
|
364
|
+
def normalize_other_codec(video: dict, filename: str) -> str:
|
|
365
|
+
"""
|
|
366
|
+
Codecs other than 264/265 (AV1, VP9, VC-1, MPEG-2, XviD...)
|
|
367
|
+
"""
|
|
368
|
+
video_format = str(video.get('format') or '')
|
|
369
|
+
if video_format == "MPEG Video":
|
|
370
|
+
version = re.search(r'\d', str(video.get('format_version') or ''))
|
|
371
|
+
return f"MPEG-{version.group(0)}" if version else "MPEG"
|
|
372
|
+
if video_format == "MPEG-4 Visual":
|
|
373
|
+
hint = f"{video.get('codec_id') or ''} {video.get('writing_library') or ''} {filename}".upper()
|
|
374
|
+
if "XVID" in hint:
|
|
375
|
+
return "XviD"
|
|
376
|
+
if "DIVX" in hint or "DX50" in hint:
|
|
377
|
+
return "DivX"
|
|
378
|
+
return "MPEG-4"
|
|
379
|
+
return video_format
|
|
380
|
+
|
|
300
381
|
def mediainfo_video(self, category: str) -> dict:
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
382
|
+
video = self.mediafile.video_track[0] if self.mediafile.video_track else {}
|
|
383
|
+
video_format = str(video.get('format') or '')
|
|
384
|
+
mediainfo_family = video_family.get(video_format)
|
|
385
|
+
|
|
386
|
+
# The file name tells the family, MediaInfo wins when they don't agree
|
|
387
|
+
family = self.name_family
|
|
388
|
+
if video_format:
|
|
389
|
+
if self.name_family and self.name_family != mediainfo_family:
|
|
390
|
+
custom_console.bot_warning_log(
|
|
391
|
+
f"<> Video codec warning: the file name says {self.name_family} but MediaInfo says"
|
|
392
|
+
f" '{video_format}'. Using MediaInfo")
|
|
393
|
+
family = mediainfo_family
|
|
394
|
+
|
|
395
|
+
if not family:
|
|
396
|
+
if video_format:
|
|
397
|
+
return {category: self.normalize_other_codec(video, self.filename)}
|
|
398
|
+
return {}
|
|
399
|
+
|
|
400
|
+
# The encoder in MediaInfo is only a check: it is never read on a WEB-DL
|
|
401
|
+
if self.release_type == "untouched":
|
|
402
|
+
if self.encoder_signature(video):
|
|
403
|
+
custom_console.bot_warning_log(
|
|
404
|
+
f"<> Warning: possible fake REMUX, the video track was encoded with"
|
|
405
|
+
f" '{video.get('writing_library') or 'x' + family}'")
|
|
406
|
+
label = "untouched"
|
|
407
|
+
elif self.release_type in ("encode", "web"):
|
|
408
|
+
label = self.release_type
|
|
409
|
+
else:
|
|
410
|
+
label = "encode" if self.encoder_signature(video) else "web"
|
|
309
411
|
|
|
412
|
+
return {category: video_codec_labels[label][family]}
|
|
310
413
|
|
|
311
414
|
def mediainfo_hdr(self, category: str) -> dict:
|
|
312
415
|
if self.mediafile.video_track:
|
|
@@ -198,11 +198,10 @@ class Bot:
|
|
|
198
198
|
|
|
199
199
|
# Start uploading only the new files, one at a time
|
|
200
200
|
print()
|
|
201
|
-
self.run(include_paths=moved_paths)
|
|
202
|
-
print()
|
|
203
|
-
announce_idle = True
|
|
204
201
|
for moved_path in moved_paths:
|
|
205
202
|
self._run_isolated(include_path=moved_path, timeout=timeout)
|
|
203
|
+
print()
|
|
204
|
+
announce_idle = True
|
|
206
205
|
|
|
207
206
|
except KeyboardInterrupt:
|
|
208
207
|
custom_console.bot_log("Exiting...")
|
|
@@ -216,8 +215,15 @@ class Bot:
|
|
|
216
215
|
Returns:
|
|
217
216
|
True if the child process ended without errors
|
|
218
217
|
"""
|
|
218
|
+
# The child must be a fork of the watcher: it shares the loaded config and the console (the questions
|
|
219
|
+
# are answered on stdin), and the Bot can't be pickled for 'spawn' or 'forkserver' (the default
|
|
220
|
+
# since Python 3.14). Where fork isn't available or safe (Windows, macOS) the entry runs in the watcher
|
|
221
|
+
if "fork" not in multiprocessing.get_all_start_methods() or sys.platform == "darwin":
|
|
222
|
+
return self.run(include_paths=[include_path])
|
|
223
|
+
|
|
219
224
|
name = os.path.basename(include_path)
|
|
220
|
-
process = multiprocessing.Process(target=_run_worker, args=(self, include_path),
|
|
225
|
+
process = multiprocessing.get_context("fork").Process(target=_run_worker, args=(self, include_path),
|
|
226
|
+
daemon=True)
|
|
221
227
|
process.start()
|
|
222
228
|
try:
|
|
223
229
|
process.join(timeout if timeout > 0 else None)
|
|
@@ -38,14 +38,14 @@ class DocuManager:
|
|
|
38
38
|
custom_console.bot_log(f"Watcher Active.. skip the old upload '{content.file_name}'")
|
|
39
39
|
continue
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
selected_tracker=selected_tracker, this_path=torrent_filepath)
|
|
43
|
-
|
|
44
|
-
# Skip if it is a duplicate
|
|
41
|
+
# Skip if it is a duplicate, before hashing the files
|
|
45
42
|
if ((self.cli.duplicate or config_settings.user_preferences.DUPLICATE_ON)
|
|
46
43
|
and UserContent.is_duplicate(content=content, tracker_name=selected_tracker, cli=self.cli)):
|
|
47
44
|
continue
|
|
48
45
|
|
|
46
|
+
torrent_response = UserContent.torrent(content=content, tracker_name_list=tracker_name_list,
|
|
47
|
+
selected_tracker=selected_tracker, this_path=torrent_filepath)
|
|
48
|
+
|
|
49
49
|
# print the title will be shown on the torrent page
|
|
50
50
|
custom_console.bot_log(f"'DISPLAYNAME'...{{{content.display_name}}}\n")
|
|
51
51
|
|
|
@@ -61,15 +61,14 @@ class GameManager:
|
|
|
61
61
|
custom_console.bot_log(f"Watcher Active.. skip the old upload '{content.file_name}'")
|
|
62
62
|
continue
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
selected_tracker=selected_tracker, this_path=torrent_filepath)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
# Skip if it is a duplicate
|
|
64
|
+
# Skip if it is a duplicate, before hashing the files
|
|
69
65
|
if ((self.cli.duplicate or config_settings.user_preferences.DUPLICATE_ON)
|
|
70
66
|
and UserContent.is_duplicate(content=content, tracker_name=selected_tracker, cli=self.cli)):
|
|
71
67
|
continue
|
|
72
68
|
|
|
69
|
+
torrent_response = UserContent.torrent(content=content, tracker_name_list=tracker_name_list,
|
|
70
|
+
selected_tracker=selected_tracker, this_path=torrent_filepath)
|
|
71
|
+
|
|
73
72
|
# Login to IGDB only when the first game needs it
|
|
74
73
|
if not self.igdb_logged:
|
|
75
74
|
if not self.igdb.connect():
|
|
@@ -83,15 +83,16 @@ class VideoManager:
|
|
|
83
83
|
# Filter contents based on the preferred language or duplicates
|
|
84
84
|
if UserContent.is_preferred_language(content=content):
|
|
85
85
|
|
|
86
|
-
torrent_response = UserContent.torrent(content=content, tracker_name_list=tracker_name_list,
|
|
87
|
-
selected_tracker=selected_tracker, this_path=torrent_filepath)
|
|
88
|
-
|
|
89
86
|
# Skip(S) if it is a duplicate or let the user choose to continue (C)
|
|
87
|
+
# Checked before hashing so a skipped duplicate doesn't cost a torrent file
|
|
90
88
|
if ((self.cli.duplicate or config_settings.user_preferences.DUPLICATE_ON)
|
|
91
89
|
and UserContent.is_duplicate(content=content, tracker_name=selected_tracker,
|
|
92
90
|
cli=self.cli)):
|
|
93
91
|
continue
|
|
94
92
|
|
|
93
|
+
torrent_response = UserContent.torrent(content=content, tracker_name_list=tracker_name_list,
|
|
94
|
+
selected_tracker=selected_tracker, this_path=torrent_filepath)
|
|
95
|
+
|
|
95
96
|
# Search for VIDEO ID
|
|
96
97
|
db_online = DbOnline(media=content, category=content.category, no_title=self.cli.notitle)
|
|
97
98
|
db = db_online.media_result
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ittup
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.3
|
|
4
4
|
Summary: ItaTorrents uploader
|
|
5
5
|
Author: Parzival, TehMaat
|
|
6
6
|
License-Expression: MIT
|
|
@@ -68,7 +68,7 @@ It performs the following tasks:
|
|
|
68
68
|
- Seeding in qBittorrent, Transmission or rTorrent
|
|
69
69
|
- Reseeding one or more torrents at a time
|
|
70
70
|
- Seed your torrents across different OS
|
|
71
|
-
- Generate various tags for titles: ``version``, ``resolution``, ``uhd``, ``platform``, ``source``, ``remux``, ``multi``, ``acodec``, ``channels``, ``flag``, ``subtitle``, ``vcodec``, ``hdr
|
|
71
|
+
- Generate various tags for titles: ``version``, ``resolution``, ``uhd``, ``platform``, ``source``, ``remux``, ``multi``, ``acodec``, ``channels``, ``flag``, ``subtitle``, ``vcodec``, ``hdr``
|
|
72
72
|
|
|
73
73
|
|
|
74
74
|
ITTup can grab the first page, convert it to an image (using xpdf),
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
from types import SimpleNamespace
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from ittup.common import tags
|
|
7
|
+
from ittup.common.settings import Validate
|
|
8
|
+
from ittup.common.tags import SearchTags
|
|
9
|
+
from ittup.common.trackers.tags_list import TAGS_LIST
|
|
10
|
+
|
|
11
|
+
TAGS_POSITION = ["title", "year", "part", "version", "resolution", "uhd", "platform", "source", "remux",
|
|
12
|
+
"multi", "acodec", "channels", "flag", "subtitle", "hdr", "vcodec"]
|
|
13
|
+
|
|
14
|
+
X264_LIBRARY = "x264 core 164 r3095 baee400"
|
|
15
|
+
X265_LIBRARY = "x265 3.5+1-f0c1022b6:[Linux][GCC 9.3.0][64 bit] 10bit"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@pytest.fixture
|
|
19
|
+
def warnings(monkeypatch):
|
|
20
|
+
logged = []
|
|
21
|
+
monkeypatch.setattr(tags.custom_console, "bot_warning_log", logged.append)
|
|
22
|
+
return logged
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def build_title(filename: str, video_format: str | None, writing_library: str = "",
|
|
26
|
+
encoding_settings: str = "", tags_position: list | None = None,
|
|
27
|
+
**video_fields) -> tuple[str, dict]:
|
|
28
|
+
video_track = []
|
|
29
|
+
if video_format:
|
|
30
|
+
video_track.append({"format": video_format, "writing_library": writing_library,
|
|
31
|
+
"encoding_settings": encoding_settings, **video_fields})
|
|
32
|
+
mediafile = SimpleNamespace(video_track=video_track, audio_track=[], subtitle_track=[])
|
|
33
|
+
media = SimpleNamespace(mediafile=mediafile, torrent_pack=False, resolution="1080p", pack=None)
|
|
34
|
+
search = SearchTags(filename=filename, title="Movie", year="2020", season=None, episode=None, media=media,
|
|
35
|
+
tags_position=list(tags_position or TAGS_POSITION), tags_list=TAGS_LIST, sign_list={},
|
|
36
|
+
ban_list={"dummy": "banned"}, releaser_sign="")
|
|
37
|
+
return search.process()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def vcodec(filename: str, video_format: str | None, **kwargs) -> str | None:
|
|
41
|
+
_, tags_dict = build_title(filename, video_format, **kwargs)
|
|
42
|
+
return tags_dict.get("vcodec")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_remux_hevc(warnings):
|
|
46
|
+
assert vcodec("Movie.2020.2160p.BluRay.REMUX.HEVC.TrueHD.7.1-GRP.mkv", "HEVC") == "HEVC"
|
|
47
|
+
assert not warnings
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_remux_with_x265_signature_warns(warnings):
|
|
51
|
+
codec = vcodec("Movie.2020.2160p.BluRay.REMUX.HEVC-GRP.mkv", "HEVC", writing_library=X265_LIBRARY)
|
|
52
|
+
assert codec == "HEVC"
|
|
53
|
+
assert any("fake REMUX" in message for message in warnings)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_web_dl_avc_with_x264_signature(warnings):
|
|
57
|
+
codec = vcodec("Movie.2020.1080p.AMZN.WEB-DL.DDP5.1.H.264-GRP.mkv", "AVC", writing_library=X264_LIBRARY)
|
|
58
|
+
assert codec == "H.264"
|
|
59
|
+
assert not warnings
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_dlmux_is_web_dl():
|
|
63
|
+
assert vcodec("Movie.2020.1080p.DLMux.ITA.ENG.AC3-GRP.mkv", "AVC", writing_library=X264_LIBRARY) == "H.264"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_webrip_x265():
|
|
67
|
+
assert vcodec("Movie.2020.1080p.WEBRip.x265-GRP.mkv", "HEVC") == "x265"
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_encode_x265_is_not_doubled(warnings):
|
|
71
|
+
title, tags_dict = build_title("Movie.2020.1080p.BDRip.x265.10bit-GRP.mkv", "HEVC",
|
|
72
|
+
writing_library=X265_LIBRARY)
|
|
73
|
+
assert tags_dict["vcodec"] == "x265"
|
|
74
|
+
assert "video_encoder" not in tags_dict
|
|
75
|
+
assert "H.265" not in title
|
|
76
|
+
assert title.count("x265") == 1
|
|
77
|
+
assert not warnings
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def test_bluray_alone_with_x264_signature():
|
|
81
|
+
assert vcodec("Movie.2020.1080p.BluRay.DTS-GRP.mkv", "AVC", writing_library=X264_LIBRARY) == "x264"
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_bluray_alone_without_signature():
|
|
85
|
+
assert vcodec("Movie.2020.1080p.BluRay.DTS-GRP.mkv", "AVC") == "H.264"
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def test_no_release_type_no_signature():
|
|
89
|
+
assert vcodec("Movie.2020.1080p.ITA.ENG-GRP.mkv", "AVC") == "H.264"
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def test_signature_from_encoding_settings():
|
|
93
|
+
settings = "cabac=1 / ref=4 / deblock=1:-3:-3 / analyse=0x3:0x133"
|
|
94
|
+
assert vcodec("Movie.2020.1080p.ITA-GRP.mkv", "AVC", encoding_settings=settings) == "x264"
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def test_name_and_mediainfo_disagree(warnings):
|
|
98
|
+
codec = vcodec("Movie.2020.1080p.BDRip.x264-GRP.mkv", "HEVC", writing_library=X265_LIBRARY)
|
|
99
|
+
assert codec == "x265"
|
|
100
|
+
assert any("MediaInfo" in message for message in warnings)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def test_family_from_name_without_mediainfo():
|
|
104
|
+
assert vcodec("Movie.2020.1080p.WEBRip.x264-GRP.mkv", None) == "x264"
|
|
105
|
+
assert vcodec("Movie.2020.1080p.BluRay.REMUX.AVC-GRP.mkv", None) == "AVC"
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@pytest.mark.parametrize("video_format, video_fields, expected", [
|
|
109
|
+
("AV1", {}, "AV1"),
|
|
110
|
+
("VP9", {}, "VP9"),
|
|
111
|
+
("VC-1", {}, "VC-1"),
|
|
112
|
+
("MPEG Video", {"format_version": "Version 2"}, "MPEG-2"),
|
|
113
|
+
("MPEG-4 Visual", {"codec_id": "XVID"}, "XviD"),
|
|
114
|
+
])
|
|
115
|
+
def test_other_codecs(warnings, video_format, video_fields, expected):
|
|
116
|
+
# The release type rules don't apply to codecs other than 264/265
|
|
117
|
+
assert vcodec("Movie.2020.1080p.BluRay.REMUX-GRP.mkv", video_format, **video_fields) == expected
|
|
118
|
+
assert vcodec("Movie.2020.1080p.WEBRip-GRP.mkv", video_format, writing_library=X264_LIBRARY,
|
|
119
|
+
**video_fields) == expected
|
|
120
|
+
assert not warnings
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def test_video_encoder_in_tags_position():
|
|
124
|
+
position = TAGS_POSITION + ["video_encoder"]
|
|
125
|
+
assert Validate.validate_tags_position(position) == position
|
|
126
|
+
|
|
127
|
+
title, tags_dict = build_title("Movie.2020.1080p.WEBRip.x265-GRP.mkv", "HEVC", tags_position=position)
|
|
128
|
+
assert "video_encoder" not in tags_dict
|
|
129
|
+
assert title.lower().count("265") == 1
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/theMovieDB/core/models/__init__.py
RENAMED
|
File without changes
|
{ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/theMovieDB/core/models/movie/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/theMovieDB/core/models/movie/details.py
RENAMED
|
File without changes
|
{ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/theMovieDB/core/models/movie/movie.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/theMovieDB/core/models/tvshow/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/theMovieDB/core/models/tvshow/details.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ittup-1.2.1 → ittup-1.2.3}/ittup/common/external_services/theMovieDB/core/models/tvshow/tvshow.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|