ittup 1.2.1__tar.gz → 1.2.2__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.
Files changed (108) hide show
  1. {ittup-1.2.1 → ittup-1.2.2}/PKG-INFO +2 -2
  2. {ittup-1.2.1 → ittup-1.2.2}/README.rst +1 -1
  3. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/settings.py +3 -5
  4. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/tags.py +123 -20
  5. {ittup-1.2.1 → ittup-1.2.2}/ittup.egg-info/PKG-INFO +2 -2
  6. {ittup-1.2.1 → ittup-1.2.2}/ittup.egg-info/SOURCES.txt +2 -1
  7. {ittup-1.2.1 → ittup-1.2.2}/pyproject.toml +1 -1
  8. ittup-1.2.2/tests/test_vcodec.py +129 -0
  9. {ittup-1.2.1 → ittup-1.2.2}/LICENSE +0 -0
  10. {ittup-1.2.1 → ittup-1.2.2}/ittup/__init__.py +0 -0
  11. {ittup-1.2.1 → ittup-1.2.2}/ittup/__main__.py +0 -0
  12. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/__init__.py +0 -0
  13. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/about.py +0 -0
  14. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/bdinfo_string.py +0 -0
  15. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/bittorrent.py +0 -0
  16. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/command.py +0 -0
  17. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/constants.py +0 -0
  18. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/database.py +0 -0
  19. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/__init__.py +0 -0
  20. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/ftpx/__init__.py +0 -0
  21. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/ftpx/client.py +0 -0
  22. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/ftpx/core/__init__.py +0 -0
  23. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/ftpx/core/ftpx_service.py +0 -0
  24. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/ftpx/core/ftpx_session.py +0 -0
  25. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/ftpx/core/menu.py +0 -0
  26. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/ftpx/core/models/__init__.py +0 -0
  27. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/ftpx/core/models/list.py +0 -0
  28. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/igdb/__init__.py +0 -0
  29. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/igdb/client.py +0 -0
  30. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/igdb/core/__init__.py +0 -0
  31. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/igdb/core/api.py +0 -0
  32. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/igdb/core/models/__init__.py +0 -0
  33. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/igdb/core/models/search.py +0 -0
  34. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/igdb/core/platformid.py +0 -0
  35. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/igdb/core/tags.py +0 -0
  36. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/imageHost.py +0 -0
  37. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/mediaresult.py +0 -0
  38. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/sessions/__init__.py +0 -0
  39. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/sessions/agents.py +0 -0
  40. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/sessions/exceptions.py +0 -0
  41. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/sessions/session.py +0 -0
  42. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/theMovieDB/__init__.py +0 -0
  43. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/theMovieDB/core/__init__.py +0 -0
  44. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/theMovieDB/core/api.py +0 -0
  45. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/theMovieDB/core/keywords.py +0 -0
  46. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/theMovieDB/core/models/__init__.py +0 -0
  47. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/theMovieDB/core/models/movie/__init__.py +0 -0
  48. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/theMovieDB/core/models/movie/alternative.py +0 -0
  49. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/theMovieDB/core/models/movie/details.py +0 -0
  50. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/theMovieDB/core/models/movie/movie.py +0 -0
  51. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/theMovieDB/core/models/movie/nowplaying.py +0 -0
  52. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/theMovieDB/core/models/movie/release_info.py +0 -0
  53. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/theMovieDB/core/models/movie/translations.py +0 -0
  54. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/theMovieDB/core/models/tvshow/__init__.py +0 -0
  55. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/theMovieDB/core/models/tvshow/alternative.py +0 -0
  56. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/theMovieDB/core/models/tvshow/details.py +0 -0
  57. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/theMovieDB/core/models/tvshow/on_the_air.py +0 -0
  58. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/theMovieDB/core/models/tvshow/translations.py +0 -0
  59. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/theMovieDB/core/models/tvshow/tvshow.py +0 -0
  60. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/theMovieDB/core/videos.py +0 -0
  61. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/trailers/__init__.py +0 -0
  62. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/trailers/api.py +0 -0
  63. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/trailers/response.py +0 -0
  64. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/external_services/tvdb.py +0 -0
  65. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/extractor.py +0 -0
  66. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/frames.py +0 -0
  67. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/mediainfo.py +0 -0
  68. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/mediainfo_string.py +0 -0
  69. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/title.py +0 -0
  70. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/torrent_clients.py +0 -0
  71. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/trackers/__init__.py +0 -0
  72. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/trackers/ban_list.py +0 -0
  73. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/trackers/data.py +0 -0
  74. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/trackers/itt.py +0 -0
  75. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/trackers/signs_list.py +0 -0
  76. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/trackers/tags_list.py +0 -0
  77. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/trackers/trackers.py +0 -0
  78. {ittup-1.2.1 → ittup-1.2.2}/ittup/common/utility.py +0 -0
  79. {ittup-1.2.1 → ittup-1.2.2}/ittup/core/__init__.py +0 -0
  80. {ittup-1.2.1 → ittup-1.2.2}/ittup/core/__main__.py +0 -0
  81. {ittup-1.2.1 → ittup-1.2.2}/ittup/core/automode.py +0 -0
  82. {ittup-1.2.1 → ittup-1.2.2}/ittup/core/bot.py +0 -0
  83. {ittup-1.2.1 → ittup-1.2.2}/ittup/core/duplicate.py +0 -0
  84. {ittup-1.2.1 → ittup-1.2.2}/ittup/core/exceptions.py +0 -0
  85. {ittup-1.2.1 → ittup-1.2.2}/ittup/core/media.py +0 -0
  86. {ittup-1.2.1 → ittup-1.2.2}/ittup/core/media_manager/ContentManager.py +0 -0
  87. {ittup-1.2.1 → ittup-1.2.2}/ittup/core/media_manager/DocuManager.py +0 -0
  88. {ittup-1.2.1 → ittup-1.2.2}/ittup/core/media_manager/GameManager.py +0 -0
  89. {ittup-1.2.1 → ittup-1.2.2}/ittup/core/media_manager/MediaInfoManager.py +0 -0
  90. {ittup-1.2.1 → ittup-1.2.2}/ittup/core/media_manager/SeedManager.py +0 -0
  91. {ittup-1.2.1 → ittup-1.2.2}/ittup/core/media_manager/TorrentManager.py +0 -0
  92. {ittup-1.2.1 → ittup-1.2.2}/ittup/core/media_manager/VideoManager.py +0 -0
  93. {ittup-1.2.1 → ittup-1.2.2}/ittup/core/media_manager/__init__.py +0 -0
  94. {ittup-1.2.1 → ittup-1.2.2}/ittup/core/media_manager/common.py +0 -0
  95. {ittup-1.2.1 → ittup-1.2.2}/ittup/core/pvtDocu.py +0 -0
  96. {ittup-1.2.1 → ittup-1.2.2}/ittup/core/pvtTorrent.py +0 -0
  97. {ittup-1.2.1 → ittup-1.2.2}/ittup/core/pvtTracker.py +0 -0
  98. {ittup-1.2.1 → ittup-1.2.2}/ittup/core/pvtVideo.py +0 -0
  99. {ittup-1.2.1 → ittup-1.2.2}/ittup/core/torrent.py +0 -0
  100. {ittup-1.2.1 → ittup-1.2.2}/ittup/core/upload.py +0 -0
  101. {ittup-1.2.1 → ittup-1.2.2}/ittup/view/__init__.py +0 -0
  102. {ittup-1.2.1 → ittup-1.2.2}/ittup/view/custom_console.py +0 -0
  103. {ittup-1.2.1 → ittup-1.2.2}/ittup.egg-info/dependency_links.txt +0 -0
  104. {ittup-1.2.1 → ittup-1.2.2}/ittup.egg-info/entry_points.txt +0 -0
  105. {ittup-1.2.1 → ittup-1.2.2}/ittup.egg-info/requires.txt +0 -0
  106. {ittup-1.2.1 → ittup-1.2.2}/ittup.egg-info/top_level.txt +0 -0
  107. {ittup-1.2.1 → ittup-1.2.2}/requirements.txt +0 -0
  108. {ittup-1.2.1 → ittup-1.2.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ittup
3
- Version: 1.2.1
3
+ Version: 1.2.2
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``, ``video_encoder``
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``, ``video_encoder``
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": "[center]━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━[/center]"
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
- video_translate = {
50
- "AVC": "H.264",
51
- "HEVC": "H.265",
52
- "H265": "H.265",
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
- video_encoder_translate = {
57
- "X265": "x.265",
58
- "X264": "x.264",
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
- codec_translated = {}
302
- if self.mediafile.video_track:
303
- for video in self.mediafile.video_track:
304
- video_format = video.get('format', "")
305
- codec_translated = video_translate.get(video_format, video_format)
306
- if codec_translated:
307
- return {category: codec_translated}
308
- return codec_translated
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:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ittup
3
- Version: 1.2.1
3
+ Version: 1.2.2
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``, ``video_encoder``
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),
@@ -102,4 +102,5 @@ ittup/core/media_manager/VideoManager.py
102
102
  ittup/core/media_manager/__init__.py
103
103
  ittup/core/media_manager/common.py
104
104
  ittup/view/__init__.py
105
- ittup/view/custom_console.py
105
+ ittup/view/custom_console.py
106
+ tests/test_vcodec.py
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
  [project]
6
6
  dynamic = ["dependencies"]
7
7
  name = "ittup"
8
- version = "1.2.1"
8
+ version = "1.2.2"
9
9
  description = "ItaTorrents uploader"
10
10
  readme = "README.rst"
11
11
  requires-python = ">=3.10"
@@ -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