Unit3Dup 0.9.11__tar.gz → 0.9.13__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 (102) hide show
  1. {unit3dup-0.9.11 → unit3dup-0.9.13}/PKG-INFO +3 -3
  2. {unit3dup-0.9.11 → unit3dup-0.9.13}/README.rst +2 -2
  3. {unit3dup-0.9.11 → unit3dup-0.9.13}/Unit3Dup.egg-info/PKG-INFO +3 -3
  4. {unit3dup-0.9.11 → unit3dup-0.9.13}/Unit3Dup.egg-info/SOURCES.txt +1 -1
  5. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/command.py +14 -16
  6. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/settings.py +99 -5
  7. unit3dup-0.9.13/common/tags.py +291 -0
  8. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/utility.py +49 -29
  9. {unit3dup-0.9.11 → unit3dup-0.9.13}/pyproject.toml +1 -1
  10. {unit3dup-0.9.11 → unit3dup-0.9.13}/unit3dup/__main__.py +14 -3
  11. {unit3dup-0.9.11 → unit3dup-0.9.13}/unit3dup/bot.py +3 -2
  12. {unit3dup-0.9.11 → unit3dup-0.9.13}/unit3dup/media.py +6 -47
  13. {unit3dup-0.9.11 → unit3dup-0.9.13}/unit3dup/media_manager/ContentManager.py +5 -6
  14. {unit3dup-0.9.11 → unit3dup-0.9.13}/unit3dup/media_manager/DocuManager.py +3 -3
  15. {unit3dup-0.9.11 → unit3dup-0.9.13}/unit3dup/media_manager/GameManager.py +3 -3
  16. {unit3dup-0.9.11 → unit3dup-0.9.13}/unit3dup/media_manager/TorrentManager.py +3 -5
  17. {unit3dup-0.9.11 → unit3dup-0.9.13}/unit3dup/media_manager/VideoManager.py +30 -25
  18. {unit3dup-0.9.11 → unit3dup-0.9.13}/unit3dup/torrent.py +2 -2
  19. {unit3dup-0.9.11 → unit3dup-0.9.13}/unit3dup/upload.py +3 -3
  20. unit3dup-0.9.11/common/p2p_tags.py +0 -332
  21. {unit3dup-0.9.11 → unit3dup-0.9.13}/LICENSE +0 -0
  22. {unit3dup-0.9.11 → unit3dup-0.9.13}/Unit3Dup.egg-info/dependency_links.txt +0 -0
  23. {unit3dup-0.9.11 → unit3dup-0.9.13}/Unit3Dup.egg-info/entry_points.txt +0 -0
  24. {unit3dup-0.9.11 → unit3dup-0.9.13}/Unit3Dup.egg-info/requires.txt +0 -0
  25. {unit3dup-0.9.11 → unit3dup-0.9.13}/Unit3Dup.egg-info/top_level.txt +0 -0
  26. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/__init__.py +0 -0
  27. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/bdinfo_string.py +0 -0
  28. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/bittorrent.py +0 -0
  29. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/constants.py +0 -0
  30. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/database.py +0 -0
  31. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/__init__.py +0 -0
  32. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/ftpx/__init__.py +0 -0
  33. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/ftpx/client.py +0 -0
  34. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/ftpx/core/__init__.py +0 -0
  35. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/ftpx/core/ftpx_service.py +0 -0
  36. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/ftpx/core/ftpx_session.py +0 -0
  37. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/ftpx/core/menu.py +0 -0
  38. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/ftpx/core/models/__init__.py +0 -0
  39. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/ftpx/core/models/list.py +0 -0
  40. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/igdb/__init__.py +0 -0
  41. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/igdb/client.py +0 -0
  42. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/igdb/core/__init__.py +0 -0
  43. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/igdb/core/api.py +0 -0
  44. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/igdb/core/models/__init__.py +0 -0
  45. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/igdb/core/models/search.py +0 -0
  46. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/igdb/core/platformid.py +0 -0
  47. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/igdb/core/tags.py +0 -0
  48. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/imageHost.py +0 -0
  49. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/mediaresult.py +0 -0
  50. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/sessions/__init__.py +0 -0
  51. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/sessions/agents.py +0 -0
  52. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/sessions/exceptions.py +0 -0
  53. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/sessions/session.py +0 -0
  54. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/theMovieDB/__init__.py +0 -0
  55. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/theMovieDB/core/__init__.py +0 -0
  56. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/theMovieDB/core/api.py +0 -0
  57. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/theMovieDB/core/keywords.py +0 -0
  58. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/theMovieDB/core/models/__init__.py +0 -0
  59. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/theMovieDB/core/models/movie/__init__.py +0 -0
  60. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/theMovieDB/core/models/movie/alternative_titles.py +0 -0
  61. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/theMovieDB/core/models/movie/details.py +0 -0
  62. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/theMovieDB/core/models/movie/movie.py +0 -0
  63. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/theMovieDB/core/models/movie/nowplaying.py +0 -0
  64. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/theMovieDB/core/models/movie/release_info.py +0 -0
  65. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/theMovieDB/core/models/tvshow/__init__.py +0 -0
  66. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/theMovieDB/core/models/tvshow/alternative.py +0 -0
  67. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/theMovieDB/core/models/tvshow/details.py +0 -0
  68. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/theMovieDB/core/models/tvshow/on_the_air.py +0 -0
  69. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/theMovieDB/core/models/tvshow/translations.py +0 -0
  70. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/theMovieDB/core/models/tvshow/tvshow.py +0 -0
  71. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/theMovieDB/core/videos.py +0 -0
  72. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/trailers/__init__.py +0 -0
  73. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/trailers/api.py +0 -0
  74. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/trailers/response.py +0 -0
  75. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/external_services/tvdb.py +0 -0
  76. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/extractor.py +0 -0
  77. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/frames.py +0 -0
  78. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/mediainfo.py +0 -0
  79. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/mediainfo_string.py +0 -0
  80. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/title.py +0 -0
  81. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/torrent_clients.py +0 -0
  82. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/trackers/__init__.py +0 -0
  83. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/trackers/data.py +0 -0
  84. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/trackers/itt.py +0 -0
  85. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/trackers/sis.py +0 -0
  86. {unit3dup-0.9.11 → unit3dup-0.9.13}/common/trackers/trackers.py +0 -0
  87. {unit3dup-0.9.11 → unit3dup-0.9.13}/requirements.txt +0 -0
  88. {unit3dup-0.9.11 → unit3dup-0.9.13}/setup.cfg +0 -0
  89. {unit3dup-0.9.11 → unit3dup-0.9.13}/unit3dup/__init__.py +0 -0
  90. {unit3dup-0.9.11 → unit3dup-0.9.13}/unit3dup/automode.py +0 -0
  91. {unit3dup-0.9.11 → unit3dup-0.9.13}/unit3dup/duplicate.py +0 -0
  92. {unit3dup-0.9.11 → unit3dup-0.9.13}/unit3dup/exceptions.py +0 -0
  93. {unit3dup-0.9.11 → unit3dup-0.9.13}/unit3dup/media_manager/MediaInfoManager.py +0 -0
  94. {unit3dup-0.9.11 → unit3dup-0.9.13}/unit3dup/media_manager/SeedManager.py +0 -0
  95. {unit3dup-0.9.11 → unit3dup-0.9.13}/unit3dup/media_manager/__init__.py +0 -0
  96. {unit3dup-0.9.11 → unit3dup-0.9.13}/unit3dup/media_manager/common.py +0 -0
  97. {unit3dup-0.9.11 → unit3dup-0.9.13}/unit3dup/pvtDocu.py +0 -0
  98. {unit3dup-0.9.11 → unit3dup-0.9.13}/unit3dup/pvtTorrent.py +0 -0
  99. {unit3dup-0.9.11 → unit3dup-0.9.13}/unit3dup/pvtTracker.py +0 -0
  100. {unit3dup-0.9.11 → unit3dup-0.9.13}/unit3dup/pvtVideo.py +0 -0
  101. {unit3dup-0.9.11 → unit3dup-0.9.13}/view/__init__.py +0 -0
  102. {unit3dup-0.9.11 → unit3dup-0.9.13}/view/custom_console.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Unit3Dup
3
- Version: 0.9.11
3
+ Version: 0.9.13
4
4
  Summary: An uploader for the Unit3D torrent tracker
5
5
  Author: Parzival
6
6
  License-Expression: MIT
@@ -60,8 +60,8 @@ It performs the following tasks:
60
60
  - Seeding in qBittorrent, Transmission or rTorrent
61
61
  - Reseeding one or more torrents at a time
62
62
  - Seed your torrents across different OS
63
- - Add a custom title to your seasons
64
- - Generate info for a title using MediaInfo
63
+ - Generate various tags for titles: ``version``, ``resolution``, ``uhd``, ``platform``, ``source``, ``remux``, ``multi``, ``acodec``, ``channels``, ``flag``, ``subtitle``, ``vcodec``, ``hdr``, ``video_encoder``
64
+
65
65
 
66
66
  unit3dup can grab the first page, convert it to an image (using xpdf),
67
67
  and then the bot can upload it to an image host, then add the link to the torrent page description.
@@ -29,8 +29,8 @@ It performs the following tasks:
29
29
  - Seeding in qBittorrent, Transmission or rTorrent
30
30
  - Reseeding one or more torrents at a time
31
31
  - Seed your torrents across different OS
32
- - Add a custom title to your seasons
33
- - Generate info for a title using MediaInfo
32
+ - Generate various tags for titles: ``version``, ``resolution``, ``uhd``, ``platform``, ``source``, ``remux``, ``multi``, ``acodec``, ``channels``, ``flag``, ``subtitle``, ``vcodec``, ``hdr``, ``video_encoder``
33
+
34
34
 
35
35
  unit3dup can grab the first page, convert it to an image (using xpdf),
36
36
  and then the bot can upload it to an image host, then add the link to the torrent page description.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Unit3Dup
3
- Version: 0.9.11
3
+ Version: 0.9.13
4
4
  Summary: An uploader for the Unit3D torrent tracker
5
5
  Author: Parzival
6
6
  License-Expression: MIT
@@ -60,8 +60,8 @@ It performs the following tasks:
60
60
  - Seeding in qBittorrent, Transmission or rTorrent
61
61
  - Reseeding one or more torrents at a time
62
62
  - Seed your torrents across different OS
63
- - Add a custom title to your seasons
64
- - Generate info for a title using MediaInfo
63
+ - Generate various tags for titles: ``version``, ``resolution``, ``uhd``, ``platform``, ``source``, ``remux``, ``multi``, ``acodec``, ``channels``, ``flag``, ``subtitle``, ``vcodec``, ``hdr``, ``video_encoder``
64
+
65
65
 
66
66
  unit3dup can grab the first page, convert it to an image (using xpdf),
67
67
  and then the bot can upload it to an image host, then add the link to the torrent page description.
@@ -18,8 +18,8 @@ common/extractor.py
18
18
  common/frames.py
19
19
  common/mediainfo.py
20
20
  common/mediainfo_string.py
21
- common/p2p_tags.py
22
21
  common/settings.py
22
+ common/tags.py
23
23
  common/title.py
24
24
  common/torrent_clients.py
25
25
  common/utility.py
@@ -1,10 +1,10 @@
1
1
  # -*- coding: utf-8 -*-
2
-
3
2
  import os
4
3
  import argparse
5
4
  from common.utility import System
6
5
  from common.settings import Load
7
6
 
7
+
8
8
  class CommandLine:
9
9
  """
10
10
  Class to handle user input from the command line
@@ -27,9 +27,9 @@ class CommandLine:
27
27
  parser.add_argument("-u", "--upload", type=str, help="Upload path")
28
28
  parser.add_argument("-f", "--folder", type=str, help="Upload folder")
29
29
  parser.add_argument("-scan", "--scan", type=str, help="Scan folder")
30
+ parser.add_argument("-b", "--buildtags", action="store_true", help="Auto build torrent name")
30
31
 
31
32
  parser.add_argument("-reseed", "--reseed", action="store_true", help="reseed folder")
32
- parser.add_argument("-gentitle", "--gentitle", action="store_true", help="")
33
33
  parser.add_argument("-watcher", "--watcher", action="store_true", help="Start watcher")
34
34
 
35
35
  parser.add_argument("-notitle", "--notitle", type=str, help="")
@@ -40,19 +40,19 @@ class CommandLine:
40
40
  parser.add_argument('-force', nargs='?', const="movie", type=str, default=None)
41
41
  parser.add_argument("-noseed", "--noseed", action="store_true", help="No seeding after upload")
42
42
  parser.add_argument("-noup", "--noup", action="store_true", help="Torrent only. No upload")
43
- parser.add_argument("-duplicate", "--duplicate", action="store_true", help="Find duplicates")
43
+ parser.add_argument("-dup", "--duplicate", action="store_true", help="Find duplicates")
44
44
  parser.add_argument("-personal", "--personal", action="store_true", help="Set to personal release")
45
45
 
46
46
  parser.add_argument("-ftp", "--ftp", action="store_true", help="Connect to FTP")
47
47
 
48
48
  # optional
49
- parser.add_argument("-dump", "--dump", action="store_true", help="Download all torrent titles")
50
- parser.add_argument("-s", "--search", type=str, help="Search for torrent")
49
+ parser.add_argument("-dmp", "--dump", action="store_true", help="Download all torrent titles")
50
+ parser.add_argument("-sch", "--search", type=str, help="Search for torrent")
51
51
  parser.add_argument("-db", "--dbsave", action="store_true", help="Save the search results")
52
52
  parser.add_argument("-i", "--info", type=str, help="Get info on torrent")
53
53
  parser.add_argument("-up", "--uploader", type=str, help="Search by uploader")
54
- parser.add_argument("-desc", "--description", type=str, help="Search by description")
55
- parser.add_argument("-bdinfo", "--bdinfo", type=str, help="Show BDInfo")
54
+ parser.add_argument("-d", "--description", type=str, help="Search by description")
55
+ parser.add_argument("-bd", "--bdinfo", type=str, help="Show BDInfo")
56
56
  parser.add_argument("-m", "--mediainfo", type=str, help="Show MediaInfo")
57
57
  parser.add_argument("-st", "--startyear", type=str, help="Start year")
58
58
  parser.add_argument("-en", "--endyear", type=str, help="End year")
@@ -68,8 +68,8 @@ class CommandLine:
68
68
  parser.add_argument("-playid", "--playlist_id", type=str, help="Playlist ID")
69
69
  parser.add_argument("-coll", "--collection_id", type=str, help="Collection ID")
70
70
  parser.add_argument("-free", "--freelech", type=str, help="Freelech discount")
71
- parser.add_argument("-a", "--alive", action="store_true", help="Alive torrent")
72
- parser.add_argument("-d", "--dead", action="store_true", help="Dead torrent")
71
+ parser.add_argument("-al", "--alive", action="store_true", help="Alive torrent")
72
+ parser.add_argument("-dd", "--dead", action="store_true", help="Dead torrent")
73
73
  parser.add_argument("-dy", "--dying", action="store_true", help="Dying torrent")
74
74
 
75
75
  parser.add_argument(
@@ -98,8 +98,6 @@ class CommandLine:
98
98
  "-pr", "--prelease", action="store_true", help="Personal release torrent"
99
99
  )
100
100
 
101
-
102
-
103
101
  # Parsing the User cli
104
102
  self.args: parser = parser.parse_args()
105
103
 
@@ -113,10 +111,10 @@ class CommandLine:
113
111
  # Test -force flag
114
112
  if self.args.force:
115
113
  self.args.force = self.args.force[:10]
116
- if self.args.force.lower() not in [ System.category_list[System.MOVIE],
117
- System.category_list[System.GAME],
118
- System.category_list[System.TV_SHOW],
119
- System.category_list[System.DOCUMENTARY]]:
114
+ if self.args.force.lower() not in [System.category_list[System.MOVIE],
115
+ System.category_list[System.GAME],
116
+ System.category_list[System.TV_SHOW],
117
+ System.category_list[System.DOCUMENTARY]]:
120
118
  self.args.force = None
121
119
  print("Invalid -force category")
122
- exit()
120
+ exit()
@@ -15,7 +15,8 @@ from common.utility import ManageTitles
15
15
  from common import trackers
16
16
 
17
17
  config_file = "Unit3Dbot.json"
18
- version = "0.9.11"
18
+ user_tags_file = "tags_list.json"
19
+ version = "0.9.13"
19
20
 
20
21
  if os.name == "nt":
21
22
  WATCHER_DESTINATION_PATH: Path = Path(os.getenv("LOCALAPPDATA", ".")) / "Unit3Dup_config" / "watcher_destination_path"
@@ -23,6 +24,7 @@ if os.name == "nt":
23
24
  CACHE_PATH: Path = Path(os.getenv("LOCALAPPDATA", ".")) / "Unit3Dup_config" / "cache_path"
24
25
  TORRENT_ARCHIVE_PATH: Path = Path(os.getenv("LOCALAPPDATA", ".")) / "Unit3Dup_config" / "torrent_archive_path"
25
26
  DEFAULT_JSON_PATH: Path = Path(os.getenv("LOCALAPPDATA", ".")) / "Unit3Dup_config" / f"{config_file}"
27
+ USER_TAGS_PATH: Path = Path(os.getenv("LOCALAPPDATA", ".")) / "Unit3Dup_config" / f"{user_tags_file}"
26
28
 
27
29
  else:
28
30
  WATCHER_DESTINATION_PATH: Path = Path.home() / "Unit3Dup_config" / "watcher_destination_path"
@@ -30,7 +32,7 @@ else:
30
32
  CACHE_PATH: Path = Path.home() / "Unit3Dup_config" / "cache_path"
31
33
  TORRENT_ARCHIVE_PATH: Path = Path.home() / "Unit3Dup_config" / "torrent_archive_path"
32
34
  DEFAULT_JSON_PATH: Path = Path.home() / "Unit3Dup_config" / f"{config_file}"
33
-
35
+ USER_TAGS_PATH: Path = Path.home() / "Unit3Dup_config" / f"{user_tags_file}"
34
36
 
35
37
  def get_default_path(field: str)-> str:
36
38
  default_paths = {
@@ -250,12 +252,13 @@ class Validate:
250
252
  print(f"-> Invalid TAG position. The list is empty !")
251
253
  exit(1)
252
254
 
253
- if len(position_list) != 9:
255
+ if len(position_list) > 17 or len(position_list) < 5:
254
256
  print(f"-> Invalid TAG position list. Wrong number of elements !")
255
257
  exit(1)
256
258
 
257
259
  for tag in position_list:
258
- if tag.lower() not in ["version","resolution", "platform","source", "audio", "channels", "flag", "subtitle", "video"]:
260
+ if tag.lower() not in ["title", "year", "season", "version", "resolution", "uhd", "platform", "source", "remux",
261
+ "multi", "acodec", "channels", "flag", "subtitle", "vcodec", "hdr", "video_encoder"]:
259
262
  print(f"-> Invalid TAG position '{tag}'. Please fix your configuration file")
260
263
  exit(1)
261
264
 
@@ -521,6 +524,92 @@ class Load:
521
524
  def _load_config(self):
522
525
  self.config = self.load_config()
523
526
 
527
+ @staticmethod
528
+ def create_tags_list_file(path: Path):
529
+ """
530
+ Creates a tags list file
531
+ """
532
+ TAG_TYPES = {
533
+ "REMUX": "remux",
534
+ "WEB-DL": "source",
535
+ "WEB-DLMUX": "source",
536
+ "WEBMUX": "source",
537
+ "WEBRIP": "source",
538
+ "BD-UNTOUCHED": "source",
539
+ "TS": "source",
540
+ "CAM": "source",
541
+ "HDTS": "source",
542
+ "MD": "source",
543
+ "UHDRIP": "source",
544
+ "BLURAY": "source",
545
+ "BRRIP": "source",
546
+ "BDRIP": "source",
547
+ "FHDRIP": "source",
548
+
549
+ "ATVP": "platform",
550
+ "AMZN": "platform",
551
+ "AMC": "platform",
552
+ "CN": "platform",
553
+ "CR": "platform",
554
+ "DCU": "platform",
555
+ "DISC": "platform",
556
+ "DSCP": "platform",
557
+ "DSNY": "platform",
558
+ "DSNP": "platform",
559
+ "DPLY": "platform",
560
+ "ESPN": "platform",
561
+ "FOOD": "platform",
562
+ "FOX": "platform",
563
+ "PLAY": "platform",
564
+ "HBO": "platform",
565
+ "HMAX": "platform",
566
+ "HGTV": "platform",
567
+ "HIST": "platform",
568
+ "HULU": "platform",
569
+ "MTOD": "platform",
570
+ "NATG": "platform",
571
+ "NF": "platform",
572
+ "NICK": "platform",
573
+ "NOW": "platform",
574
+ "PMNT": "platform",
575
+ "PMTP": "platform",
576
+ "PCOK": "platform",
577
+ "RKTN": "platform",
578
+ "SHO": "platform",
579
+ "SKST": "platform",
580
+ "STAN": "platform",
581
+ "STRP": "platform",
582
+ "STZ": "platform",
583
+ "TIMV": "platform",
584
+
585
+ "REPACK": "version",
586
+ "EXTENDED": "version",
587
+ "SUBBED": "version",
588
+ "MUX": "version",
589
+ "REMASTERED": "version",
590
+ "READNFO": "version",
591
+ "UNRATED": "version",
592
+ "LIMITED": "version",
593
+ "VU": "version",
594
+ "STV": "version",
595
+ "RECODE": "version",
596
+ "INTERNAL": "version",
597
+ "PROPER": "version",
598
+ "DUAL": "version",
599
+ "UNTOUCHED": "version",
600
+ "COMPLETE": "version",
601
+ "COMPLETA": "version",
602
+
603
+ "X264": "video_encoder",
604
+ "X265": "video_encoder",
605
+ }
606
+
607
+ path.parent.mkdir(parents=True, exist_ok=True)
608
+ with open(path, "w", encoding="utf-8") as tags_list_file:
609
+ json.dump(TAG_TYPES, tags_list_file, ensure_ascii=False, indent=4)
610
+
611
+
612
+
524
613
 
525
614
  @staticmethod
526
615
  def create_default_json_file(path: Path):
@@ -578,7 +667,8 @@ class Load:
578
667
  "PASSIMA_PRIORITY": 5,
579
668
  "IMARIDE_PRIORITY": 6,
580
669
  "NUMBER_OF_SCREENSHOTS": 4,
581
- "TAGS_POSITION": ["version","resolution", "platform", "source", "audio", "channels", "flag", "subtitle", "video"],
670
+ "TAGS_POSITION": ["title", "year", "season", "version", "resolution", "uhd", "platform", "source", "remux",
671
+ "multi", "acodec", "channels", "flag", "subtitle", "hdr", "vcodec", "video_encoder"],
582
672
  "YOUTUBE_FAV_CHANNEL_ID": "UCGCbxpnt25hWPFLSbvwfg_w",
583
673
  "YOUTUBE_CHANNEL_ENABLE": "False",
584
674
  "DUPLICATE_ON": "true",
@@ -653,6 +743,10 @@ class Load:
653
743
  print(f"Create default configuration file: {DEFAULT_JSON_PATH}")
654
744
  Load.create_default_json_file(DEFAULT_JSON_PATH)
655
745
 
746
+ if not USER_TAGS_PATH.exists():
747
+ print(f"Create default Tags_list file: {USER_TAGS_PATH}")
748
+ Load.create_tags_list_file(USER_TAGS_PATH)
749
+
656
750
  # Since the last bot version there might are new attributes
657
751
  # Load the json file, find the difference between json file and the code. Update the user's json file
658
752
  update_config = JsonConfig(default_json_path=DEFAULT_JSON_PATH)
@@ -0,0 +1,291 @@
1
+ # -*- coding: utf-8 -*-
2
+ import os
3
+ import re
4
+ from common.mediainfo import MediaFile
5
+ from common.utility import ManageTitles
6
+
7
+ # From hdr format
8
+ hdr_map = {
9
+ "DOLBY VISION": "DV",
10
+ "DOLBY VISION HDR": "DV HDR",
11
+ "DOLBY VISION HDR10": "DV HDR10",
12
+ "HDR10PLUS": "HDR10+",
13
+ "HDRPLUS+": "HDR10+",
14
+ "HDR10+": "HDR10+",
15
+ "HDR10": "HDR10",
16
+ "HDR10 / HDR10": "HDR10",
17
+ "DOVI": "DV",
18
+ "HDR": "HDR",
19
+ }
20
+
21
+ audio_translate = {
22
+ "AC3": "DD",
23
+ "AAC LC": "AAC",
24
+ "AC-3": "DD",
25
+ "EAC3": "DD+",
26
+ "E-AC3": "DD+",
27
+ "E-AC-3": "DD+",
28
+ "E-AC-3 JOC": "DD+",
29
+ "DTS": "DTS",
30
+ "DTS ES": "DTS-ES",
31
+ "DTS ES XLL": "DTS-HD MA",
32
+ "DTS XLL": "DTS-HD MA",
33
+ "MLP FBA 16-ch": "TrueHD",
34
+ "MPEG Audio": "MPEG",
35
+ }
36
+
37
+ video_translate = {
38
+ "AVC": "H.264",
39
+ "HEVC": "H.265",
40
+ "H265": "H.265",
41
+ "H264": "H.264",
42
+ }
43
+
44
+ video_encoder_translate = {
45
+ "X265": "x.265",
46
+ "X264": "x.264",
47
+ }
48
+
49
+
50
+ class SearchTags(object):
51
+ def __init__(self, filename, title: str, year: str, season: int, episode: int,
52
+ mediafile: MediaFile, tags_position: list, tags_list: dict, releaser_sign: str):
53
+
54
+ self.tags_position = tags_position
55
+ self.releaser_sign = releaser_sign
56
+ self.mediafile = mediafile
57
+ self.filename = filename
58
+ self.episode = episode
59
+ self.season = season
60
+ self.title = title
61
+ self.year = year
62
+ self.tags_dict = {}
63
+ self.tags_position = tags_position
64
+ self.TAG_TYPES = tags_list
65
+
66
+ @staticmethod
67
+ def normalize_version_tag(tag: str) -> str:
68
+ tag_esc = re.escape(tag)
69
+ # Filter hyphenated,space compounds
70
+ tag_esc = tag_esc.replace(r'\ ', r'[.\s_-]*')
71
+ return tag_esc
72
+
73
+ @staticmethod
74
+ def normalize_platform_tag(tag: str) -> str:
75
+ # escape
76
+ tag_esc = re.escape(tag)
77
+ return tag_esc
78
+
79
+ @staticmethod
80
+ def normalize_sources(tag: str) -> str:
81
+ tag_esc = re.escape(tag)
82
+ # Filter hyphenated,space compounds
83
+ tag_esc = tag_esc.replace(r'\ ', r'[.\s_-]*')
84
+ return tag_esc
85
+
86
+ @staticmethod
87
+ def normalize_video_encoder(tag: str) -> str:
88
+ tag_esc = re.escape(tag)
89
+ tag_esc = re.sub(r'([A-Z])(\d+)', r'\1[._-]?\2', tag_esc)
90
+ return tag_esc
91
+
92
+ def process(self) -> str:
93
+ patterns = []
94
+
95
+ # loop sorted TAG_TYPES dictionary
96
+ for i, (tag, category) in enumerate(
97
+ sorted(self.TAG_TYPES.items(), key=lambda x: len(x[0]), reverse=True)
98
+ ):
99
+ if category == "version":
100
+ norm = self.normalize_version_tag(tag)
101
+ elif category == "platform":
102
+ norm = self.normalize_platform_tag(tag)
103
+ elif category == "source":
104
+ norm = self.normalize_sources(tag)
105
+ elif category == "video_encoder":
106
+ norm = self.normalize_video_encoder(tag)
107
+ else:
108
+ norm = re.escape(tag)
109
+ # Save a regex pattern for each category
110
+ patterns.append([norm, category])
111
+
112
+ # Run regex
113
+ for p, category in patterns:
114
+ regex = re.compile(r'(?<!\w)' + p + r'(?!\w)', re.IGNORECASE)
115
+ matches = regex.findall(self.filename)
116
+ if matches:
117
+ self.tags_dict.setdefault(category, []).append(matches[0])
118
+
119
+ # /// Read from mediainfo
120
+ updated_category = {}
121
+ for category in self.tags_position:
122
+ if category == "acodec":
123
+ updated_category = self.mediainfo_audio(category=category)
124
+
125
+ elif category == "vcodec":
126
+ updated_category = self.mediainfo_video(category=category)
127
+
128
+ elif category == "hdr":
129
+ updated_category = self.mediainfo_hdr(category=category)
130
+
131
+ elif category == "uhd":
132
+ updated_category = self.mediainfo_uhd(category=category)
133
+
134
+ elif category == "subtitle":
135
+ if self.mediafile.subtitle_track:
136
+ updated_category = {'subtitle': "SUBS" if len(self.mediafile.subtitle_track) > 1 else "SUB"}
137
+
138
+ if updated_category:
139
+ self.tags_dict.update(updated_category)
140
+
141
+ # /// Add S#E#, title, Year
142
+ se_str = ''
143
+ if self.season is not None and self.episode is not None:
144
+ se_str = f"S{self.season:02d}E{self.episode:02d}"
145
+ elif self.season is not None:
146
+ se_str = f"S{self.season:02d}"
147
+ elif self.episode is not None:
148
+ se_str = f"E{self.episode:02d}"
149
+
150
+ self.tags_dict.update({'title': self.title})
151
+ if self.year:
152
+ self.tags_dict.update({'year': self.year})
153
+ if se_str:
154
+ self.tags_dict.update({'season': se_str})
155
+
156
+ # /// Add Sign
157
+ if not self.releaser_sign:
158
+ filename, _ = os.path.splitext(os.path.basename(self.filename))
159
+ m = re.search(r'-([A-Za-z0-9]+)$', filename)
160
+ self.releaser_sign = f"-{m.group(1)}" if m and m.group(1) not in self.TAG_TYPES else ""
161
+ else:
162
+ self.releaser_sign = f"-{self.releaser_sign}"
163
+
164
+ # /// Order according to tag position
165
+ tags_dict = {
166
+ k: self.tags_dict[k]
167
+ for k in self.tags_position
168
+ if k in self.tags_dict
169
+ }
170
+
171
+ # /// Build the title
172
+ build = []
173
+ for k, v in tags_dict.items():
174
+ if isinstance(v, list):
175
+ build.append(' '.join(v))
176
+ else:
177
+ build.append(str(v))
178
+
179
+ refactored = ' '.join(build) + self.releaser_sign
180
+ return refactored
181
+
182
+ def mediainfo_audio(self, category: str) -> dict:
183
+ languages = []
184
+ audio_codecs = []
185
+ if self.mediafile.audio_track:
186
+ for audio in self.mediafile.audio_track:
187
+ other_format = audio.get('other_format', [])
188
+ if other_format:
189
+ codec_translated = audio_translate.get(other_format[0], '')
190
+ if not codec_translated:
191
+ codec_translated = other_format[0]
192
+ # Check Atmos
193
+ dolby = audio.get('commercial_name', "").lower()
194
+ atmos = 'Atmos' if 'atmos' in dolby else ''
195
+ # Add audio codec
196
+ channel_s = audio.get('channel_s', 0)
197
+ # Add channels
198
+ ch = {2: "2.0", 6: "5.1", 8: "7.1"}.get(channel_s, "")
199
+ if f"{codec_translated} {ch} {atmos}".strip() not in audio_codecs:
200
+ audio_codecs.append(f"{codec_translated} {ch} {atmos}".strip())
201
+ # print(f"Mediainfo {other_format} -> {codec_translated} {ch} {atmos}")
202
+
203
+ # Add flags
204
+ for l in audio.get('other_language', []):
205
+ c = ManageTitles.convert_iso(l)
206
+ if c:
207
+ if isinstance(c, list):
208
+ languages.append(c[0])
209
+ else:
210
+ languages.append(c)
211
+ break
212
+ languages = list(dict.fromkeys(languages))
213
+ # Add multilanguage tag when languages > 2
214
+ if len(languages) > 2:
215
+ self.tags_dict.update({'multi': 'MULTI'})
216
+
217
+ audio_codecs.extend(languages)
218
+ return {category: audio_codecs}
219
+
220
+ def mediainfo_video(self, category: str) -> dict:
221
+ codec_translated = {}
222
+ if self.mediafile.video_track:
223
+ for video in self.mediafile.video_track:
224
+ video_format = video.get('format', "")
225
+ codec_translated = video_translate.get(video_format, video_format)
226
+ if codec_translated:
227
+ return {category: codec_translated}
228
+ return codec_translated
229
+
230
+ def mediainfo_hdr(self, category: str) -> dict:
231
+ if self.mediafile.video_track:
232
+ for video in self.mediafile.video_track:
233
+ hdr_format_commercial = video.get('hdr_format_commercial', "")
234
+ hdr_format = video.get('hdr_format', "")
235
+ # Check hdr
236
+ if hdr_format_commercial:
237
+ # print(f"hdr_format_commercial: {hdr_format_commercial}")
238
+ # print(f"hdr_format: {hdr_format}")
239
+ hdr = ''
240
+ if hdr_format_commercial in hdr_map:
241
+ # print(
242
+ # f"hdr_format_commercial: {hdr_format_commercial} -> Tag: {hdr_map[hdr_format_commercial]}")
243
+ hdr = hdr_map[hdr_format_commercial]
244
+ # Check dolby vision
245
+ if 'DOLBY VISION' in hdr_format_commercial.upper() or 'DOLBY VISION' in hdr_format.upper():
246
+ hdr = f"DOLBY VISION {hdr}"
247
+ # print(hdr)
248
+ return {category: hdr_map[hdr]}
249
+ return {}
250
+
251
+ def mediainfo_uhd(self, category: str) -> dict:
252
+ """
253
+ identify resolution based on Height and Width tolerance 5%
254
+ """
255
+ result = {}
256
+ if self.mediafile.video_track:
257
+ video_height = int(self.mediafile.video_track[0].get('height', 0))
258
+ video_width = int(self.mediafile.video_track[0].get('width', 0))
259
+
260
+ # print(f"VideoTrack : W{video_width} x H{video_height}")
261
+
262
+ # Calculate range 5%
263
+ def in_range(value, standard):
264
+ tol = standard * 0.05
265
+ return standard - tol <= value <= standard + tol
266
+
267
+ # /// UHD
268
+ if video_height >= 2000 or video_width >= 3840:
269
+ result[category] = 'UHD'
270
+ result['resolution'] = '2160p'
271
+ # /// Full HD
272
+ elif in_range(video_height, 1080) or in_range(video_width, 1920):
273
+ result[category] = 'FullHD'
274
+ result['resolution'] = '1080p'
275
+ # /// HD
276
+ elif in_range(video_height, 720) or in_range(video_width, 1280):
277
+ result[category] = 'HD'
278
+ result['resolution'] = '720p'
279
+ # /// SD 576p
280
+ elif in_range(video_height, 576) or in_range(video_width, 768):
281
+ result[category] = 'SD'
282
+ result['resolution'] = '576p'
283
+ # /// SD 480p
284
+ elif in_range(video_height, 480) or in_range(video_width, 640):
285
+ result[category] = 'SD'
286
+ result['resolution'] = '480p'
287
+ else:
288
+ result[category] = 'unknown'
289
+ result['resolution'] = f'{video_width}x{video_height}'
290
+
291
+ return result