Unit3Dup 0.9.10__tar.gz → 0.9.12__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {unit3dup-0.9.10 → unit3dup-0.9.12}/PKG-INFO +1 -1
- {unit3dup-0.9.10 → unit3dup-0.9.12}/Unit3Dup.egg-info/PKG-INFO +1 -1
- {unit3dup-0.9.10 → unit3dup-0.9.12}/Unit3Dup.egg-info/SOURCES.txt +1 -1
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/command.py +8 -7
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/settings.py +6 -4
- unit3dup-0.9.12/common/tags.py +357 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/utility.py +43 -23
- {unit3dup-0.9.10 → unit3dup-0.9.12}/pyproject.toml +1 -1
- {unit3dup-0.9.10 → unit3dup-0.9.12}/unit3dup/media.py +6 -20
- {unit3dup-0.9.10 → unit3dup-0.9.12}/unit3dup/media_manager/ContentManager.py +5 -6
- {unit3dup-0.9.10 → unit3dup-0.9.12}/unit3dup/media_manager/VideoManager.py +29 -13
- {unit3dup-0.9.10 → unit3dup-0.9.12}/unit3dup/torrent.py +2 -2
- unit3dup-0.9.10/common/p2p_tags.py +0 -328
- {unit3dup-0.9.10 → unit3dup-0.9.12}/LICENSE +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/README.rst +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/Unit3Dup.egg-info/dependency_links.txt +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/Unit3Dup.egg-info/entry_points.txt +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/Unit3Dup.egg-info/requires.txt +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/Unit3Dup.egg-info/top_level.txt +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/__init__.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/bdinfo_string.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/bittorrent.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/constants.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/database.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/__init__.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/ftpx/__init__.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/ftpx/client.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/ftpx/core/__init__.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/ftpx/core/ftpx_service.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/ftpx/core/ftpx_session.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/ftpx/core/menu.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/ftpx/core/models/__init__.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/ftpx/core/models/list.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/igdb/__init__.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/igdb/client.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/igdb/core/__init__.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/igdb/core/api.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/igdb/core/models/__init__.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/igdb/core/models/search.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/igdb/core/platformid.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/igdb/core/tags.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/imageHost.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/mediaresult.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/sessions/__init__.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/sessions/agents.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/sessions/exceptions.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/sessions/session.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/theMovieDB/__init__.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/theMovieDB/core/__init__.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/theMovieDB/core/api.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/theMovieDB/core/keywords.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/theMovieDB/core/models/__init__.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/theMovieDB/core/models/movie/__init__.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/theMovieDB/core/models/movie/alternative_titles.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/theMovieDB/core/models/movie/details.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/theMovieDB/core/models/movie/movie.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/theMovieDB/core/models/movie/nowplaying.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/theMovieDB/core/models/movie/release_info.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/theMovieDB/core/models/tvshow/__init__.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/theMovieDB/core/models/tvshow/alternative.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/theMovieDB/core/models/tvshow/details.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/theMovieDB/core/models/tvshow/on_the_air.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/theMovieDB/core/models/tvshow/translations.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/theMovieDB/core/models/tvshow/tvshow.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/theMovieDB/core/videos.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/trailers/__init__.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/trailers/api.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/trailers/response.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/external_services/tvdb.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/extractor.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/frames.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/mediainfo.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/mediainfo_string.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/title.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/torrent_clients.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/trackers/__init__.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/trackers/data.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/trackers/itt.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/trackers/sis.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/common/trackers/trackers.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/requirements.txt +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/setup.cfg +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/unit3dup/__init__.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/unit3dup/__main__.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/unit3dup/automode.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/unit3dup/bot.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/unit3dup/duplicate.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/unit3dup/exceptions.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/unit3dup/media_manager/DocuManager.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/unit3dup/media_manager/GameManager.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/unit3dup/media_manager/MediaInfoManager.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/unit3dup/media_manager/SeedManager.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/unit3dup/media_manager/TorrentManager.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/unit3dup/media_manager/__init__.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/unit3dup/media_manager/common.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/unit3dup/pvtDocu.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/unit3dup/pvtTorrent.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/unit3dup/pvtTracker.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/unit3dup/pvtVideo.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/unit3dup/upload.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/view/__init__.py +0 -0
- {unit3dup-0.9.10 → unit3dup-0.9.12}/view/custom_console.py +0 -0
|
@@ -27,6 +27,7 @@ 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
33
|
parser.add_argument("-gentitle", "--gentitle", action="store_true", help="")
|
|
@@ -40,19 +41,19 @@ class CommandLine:
|
|
|
40
41
|
parser.add_argument('-force', nargs='?', const="movie", type=str, default=None)
|
|
41
42
|
parser.add_argument("-noseed", "--noseed", action="store_true", help="No seeding after upload")
|
|
42
43
|
parser.add_argument("-noup", "--noup", action="store_true", help="Torrent only. No upload")
|
|
43
|
-
parser.add_argument("-
|
|
44
|
+
parser.add_argument("-dup", "--duplicate", action="store_true", help="Find duplicates")
|
|
44
45
|
parser.add_argument("-personal", "--personal", action="store_true", help="Set to personal release")
|
|
45
46
|
|
|
46
47
|
parser.add_argument("-ftp", "--ftp", action="store_true", help="Connect to FTP")
|
|
47
48
|
|
|
48
49
|
# optional
|
|
49
|
-
parser.add_argument("-
|
|
50
|
-
parser.add_argument("-
|
|
50
|
+
parser.add_argument("-dmp", "--dump", action="store_true", help="Download all torrent titles")
|
|
51
|
+
parser.add_argument("-sch", "--search", type=str, help="Search for torrent")
|
|
51
52
|
parser.add_argument("-db", "--dbsave", action="store_true", help="Save the search results")
|
|
52
53
|
parser.add_argument("-i", "--info", type=str, help="Get info on torrent")
|
|
53
54
|
parser.add_argument("-up", "--uploader", type=str, help="Search by uploader")
|
|
54
|
-
parser.add_argument("-
|
|
55
|
-
parser.add_argument("-
|
|
55
|
+
parser.add_argument("-d", "--description", type=str, help="Search by description")
|
|
56
|
+
parser.add_argument("-bd", "--bdinfo", type=str, help="Show BDInfo")
|
|
56
57
|
parser.add_argument("-m", "--mediainfo", type=str, help="Show MediaInfo")
|
|
57
58
|
parser.add_argument("-st", "--startyear", type=str, help="Start year")
|
|
58
59
|
parser.add_argument("-en", "--endyear", type=str, help="End year")
|
|
@@ -68,8 +69,8 @@ class CommandLine:
|
|
|
68
69
|
parser.add_argument("-playid", "--playlist_id", type=str, help="Playlist ID")
|
|
69
70
|
parser.add_argument("-coll", "--collection_id", type=str, help="Collection ID")
|
|
70
71
|
parser.add_argument("-free", "--freelech", type=str, help="Freelech discount")
|
|
71
|
-
parser.add_argument("-
|
|
72
|
-
parser.add_argument("-
|
|
72
|
+
parser.add_argument("-al", "--alive", action="store_true", help="Alive torrent")
|
|
73
|
+
parser.add_argument("-dd", "--dead", action="store_true", help="Dead torrent")
|
|
73
74
|
parser.add_argument("-dy", "--dying", action="store_true", help="Dying torrent")
|
|
74
75
|
|
|
75
76
|
parser.add_argument(
|
|
@@ -15,7 +15,7 @@ from common.utility import ManageTitles
|
|
|
15
15
|
from common import trackers
|
|
16
16
|
|
|
17
17
|
config_file = "Unit3Dbot.json"
|
|
18
|
-
version = "0.9.
|
|
18
|
+
version = "0.9.12"
|
|
19
19
|
|
|
20
20
|
if os.name == "nt":
|
|
21
21
|
WATCHER_DESTINATION_PATH: Path = Path(os.getenv("LOCALAPPDATA", ".")) / "Unit3Dup_config" / "watcher_destination_path"
|
|
@@ -250,12 +250,13 @@ class Validate:
|
|
|
250
250
|
print(f"-> Invalid TAG position. The list is empty !")
|
|
251
251
|
exit(1)
|
|
252
252
|
|
|
253
|
-
if len(position_list)
|
|
253
|
+
if len(position_list) > 17 or len(position_list) < 5:
|
|
254
254
|
print(f"-> Invalid TAG position list. Wrong number of elements !")
|
|
255
255
|
exit(1)
|
|
256
256
|
|
|
257
257
|
for tag in position_list:
|
|
258
|
-
if tag.lower() not in ["
|
|
258
|
+
if tag.lower() not in ["title", "year", "season", "version", "resolution", "uhd", "platform", "source", "remux",
|
|
259
|
+
"multi", "acodec", "channels", "flag", "subtitle", "vcodec", "hdr", "video_encoder"]:
|
|
259
260
|
print(f"-> Invalid TAG position '{tag}'. Please fix your configuration file")
|
|
260
261
|
exit(1)
|
|
261
262
|
|
|
@@ -578,7 +579,8 @@ class Load:
|
|
|
578
579
|
"PASSIMA_PRIORITY": 5,
|
|
579
580
|
"IMARIDE_PRIORITY": 6,
|
|
580
581
|
"NUMBER_OF_SCREENSHOTS": 4,
|
|
581
|
-
"TAGS_POSITION": ["
|
|
582
|
+
"TAGS_POSITION": ["title", "year", "season", "version", "resolution", "uhd", "platform", "source", "remux",
|
|
583
|
+
"multi", "acodec", "channels", "flag", "subtitle", "vcodec", "hdr", "video_encoder"],
|
|
582
584
|
"YOUTUBE_FAV_CHANNEL_ID": "UCGCbxpnt25hWPFLSbvwfg_w",
|
|
583
585
|
"YOUTUBE_CHANNEL_ENABLE": "False",
|
|
584
586
|
"DUPLICATE_ON": "true",
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
import os
|
|
3
|
+
import re
|
|
4
|
+
from common.mediainfo import MediaFile
|
|
5
|
+
from common.utility import ManageTitles
|
|
6
|
+
|
|
7
|
+
TAG_TYPES = {
|
|
8
|
+
"REMUX": "remux",
|
|
9
|
+
"WEB-DL": "source",
|
|
10
|
+
"WEB-DLMUX": "source",
|
|
11
|
+
"WEBMUX": "source",
|
|
12
|
+
"WEBRIP": "source",
|
|
13
|
+
"BD-UNTOUCHED": "source",
|
|
14
|
+
"TS": "source",
|
|
15
|
+
"CAM": "source",
|
|
16
|
+
"HDTS": "source",
|
|
17
|
+
"MD": "source",
|
|
18
|
+
"UHDRIP": "source",
|
|
19
|
+
"BLURAY": "source",
|
|
20
|
+
"BRRIP": "source",
|
|
21
|
+
"BDRIP": "source",
|
|
22
|
+
"FHDRIP": "source",
|
|
23
|
+
|
|
24
|
+
"ATVP": "platform",
|
|
25
|
+
"AMZN": "platform",
|
|
26
|
+
"AMC": "platform",
|
|
27
|
+
"CN": "platform",
|
|
28
|
+
"CR": "platform",
|
|
29
|
+
"DCU": "platform",
|
|
30
|
+
"DISC": "platform",
|
|
31
|
+
"DSCP": "platform",
|
|
32
|
+
"DSNY": "platform",
|
|
33
|
+
"DSNP": "platform",
|
|
34
|
+
"DPLY": "platform",
|
|
35
|
+
"ESPN": "platform",
|
|
36
|
+
"FOOD": "platform",
|
|
37
|
+
"FOX": "platform",
|
|
38
|
+
"PLAY": "platform",
|
|
39
|
+
"HBO": "platform",
|
|
40
|
+
"HMAX": "platform",
|
|
41
|
+
"HGTV": "platform",
|
|
42
|
+
"HIST": "platform",
|
|
43
|
+
"HULU": "platform",
|
|
44
|
+
"MTOD": "platform",
|
|
45
|
+
"NATG": "platform",
|
|
46
|
+
"NF": "platform",
|
|
47
|
+
"NICK": "platform",
|
|
48
|
+
"NOW": "platform",
|
|
49
|
+
"PMNT": "platform",
|
|
50
|
+
"PMTP": "platform",
|
|
51
|
+
"PCOK": "platform",
|
|
52
|
+
"RKTN": "platform",
|
|
53
|
+
"SHO": "platform",
|
|
54
|
+
"SKST": "platform",
|
|
55
|
+
"STAN": "platform",
|
|
56
|
+
"STRP": "platform",
|
|
57
|
+
"STZ": "platform",
|
|
58
|
+
"TIMV": "platform",
|
|
59
|
+
|
|
60
|
+
"REPACK": "version",
|
|
61
|
+
"EXTENDED": "version",
|
|
62
|
+
"SUBBED": "version",
|
|
63
|
+
"MUX": "version",
|
|
64
|
+
"REMASTERED": "version",
|
|
65
|
+
"READNFO": "version",
|
|
66
|
+
"UNRATED": "version",
|
|
67
|
+
"LIMITED": "version",
|
|
68
|
+
"VU": "version",
|
|
69
|
+
"STV": "version",
|
|
70
|
+
"RECODE": "version",
|
|
71
|
+
"INTERNAL": "version",
|
|
72
|
+
"PROPER": "version",
|
|
73
|
+
"DUAL": "version",
|
|
74
|
+
"UNTOUCHED": "version",
|
|
75
|
+
"COMPLETE": "version",
|
|
76
|
+
"COMPLETA": "version",
|
|
77
|
+
|
|
78
|
+
"X264": "video_encoder",
|
|
79
|
+
"X265": "video_encoder",
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
# From hdr format
|
|
83
|
+
hdr_map = {
|
|
84
|
+
"DOLBY VISION": "DV",
|
|
85
|
+
"DOLBY VISION HDR": "DV HDR",
|
|
86
|
+
"DOLBY VISION HDR10": "DV HDR10",
|
|
87
|
+
"HDR10PLUS": "HDR10+",
|
|
88
|
+
"HDRPLUS+": "HDR10+",
|
|
89
|
+
"HDR10+": "HDR10+",
|
|
90
|
+
"HDR10": "HDR10",
|
|
91
|
+
"HDR10 / HDR10": "HDR10",
|
|
92
|
+
"DOVI": "DV",
|
|
93
|
+
"HDR": "HDR",
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
audio_translate = {
|
|
97
|
+
"AC3": "DD",
|
|
98
|
+
"AAC LC": "AAC",
|
|
99
|
+
"AC-3": "DD",
|
|
100
|
+
"EAC3": "DD+",
|
|
101
|
+
"E-AC3": "DD+",
|
|
102
|
+
"E-AC-3": "DD+",
|
|
103
|
+
"E-AC-3 JOC": "DD+",
|
|
104
|
+
"DTS": "DTS",
|
|
105
|
+
"DTS ES": "DTS-ES",
|
|
106
|
+
"DTS ES XLL": "DTS-HD MA",
|
|
107
|
+
"DTS XLL": "DTS-HD MA",
|
|
108
|
+
"MLP FBA 16-ch": "TrueHD",
|
|
109
|
+
"MPEG Audio": "MPEG",
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
video_translate = {
|
|
113
|
+
"AVC": "H.264",
|
|
114
|
+
"HEVC": "H.265",
|
|
115
|
+
"H265": "H.265",
|
|
116
|
+
"H264": "H.264",
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
video_encoder_translate = {
|
|
120
|
+
"X265": "x.265",
|
|
121
|
+
"X264": "x.264",
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class SearchTags(object):
|
|
126
|
+
def __init__(self, filename, title: str, year: str, season: int, episode: int,
|
|
127
|
+
mediafile: MediaFile, tags_position: list, releaser_sign: str):
|
|
128
|
+
|
|
129
|
+
self.tags_position = tags_position
|
|
130
|
+
self.releaser_sign = releaser_sign
|
|
131
|
+
self.mediafile = mediafile
|
|
132
|
+
self.filename = filename
|
|
133
|
+
self.episode = episode
|
|
134
|
+
self.season = season
|
|
135
|
+
self.title = title
|
|
136
|
+
self.year = year
|
|
137
|
+
self.tags_dict = {}
|
|
138
|
+
self.tags_position = tags_position
|
|
139
|
+
|
|
140
|
+
def normalize_version_tag(self, tag: str) -> str:
|
|
141
|
+
tag_esc = re.escape(tag)
|
|
142
|
+
# Filter hyphenated,space compounds
|
|
143
|
+
tag_esc = tag_esc.replace(r'\ ', r'[.\s_-]*')
|
|
144
|
+
return tag_esc
|
|
145
|
+
|
|
146
|
+
def normalize_platform_tag(self, tag: str) -> str:
|
|
147
|
+
# escape
|
|
148
|
+
tag_esc = re.escape(tag)
|
|
149
|
+
return tag_esc
|
|
150
|
+
|
|
151
|
+
def normalize_sources(self, tag: str) -> str:
|
|
152
|
+
tag_esc = re.escape(tag)
|
|
153
|
+
# Filter hyphenated,space compounds
|
|
154
|
+
tag_esc = tag_esc.replace(r'\ ', r'[.\s_-]*')
|
|
155
|
+
return tag_esc
|
|
156
|
+
|
|
157
|
+
def normalize_video_encoder(self, tag: str) -> str:
|
|
158
|
+
tag_esc = re.escape(tag)
|
|
159
|
+
tag_esc = re.sub(r'([A-Z])(\d+)', r'\1[._-]?\2', tag_esc)
|
|
160
|
+
return tag_esc
|
|
161
|
+
|
|
162
|
+
def process(self) -> str:
|
|
163
|
+
patterns = []
|
|
164
|
+
|
|
165
|
+
# loop sorted TAG_TYPES dictionary
|
|
166
|
+
for i, (tag, category) in enumerate(
|
|
167
|
+
sorted(TAG_TYPES.items(), key=lambda x: len(x[0]), reverse=True)
|
|
168
|
+
):
|
|
169
|
+
if category == "version":
|
|
170
|
+
norm = self.normalize_version_tag(tag)
|
|
171
|
+
elif category == "platform":
|
|
172
|
+
norm = self.normalize_platform_tag(tag)
|
|
173
|
+
elif category == "source":
|
|
174
|
+
norm = self.normalize_sources(tag)
|
|
175
|
+
elif category == "video_encoder":
|
|
176
|
+
norm = self.normalize_video_encoder(tag)
|
|
177
|
+
else:
|
|
178
|
+
norm = re.escape(tag)
|
|
179
|
+
# Save a regex pattern for each category
|
|
180
|
+
patterns.append([norm, category])
|
|
181
|
+
|
|
182
|
+
# Run regex
|
|
183
|
+
for p, category in patterns:
|
|
184
|
+
regex = re.compile(r'(?<!\w)' + p + r'(?!\w)', re.IGNORECASE)
|
|
185
|
+
matches = regex.findall(self.filename)
|
|
186
|
+
if matches:
|
|
187
|
+
self.tags_dict.setdefault(category, []).append(matches[0])
|
|
188
|
+
|
|
189
|
+
# /// Read from mediainfo
|
|
190
|
+
updated_category = {}
|
|
191
|
+
for category in self.tags_position:
|
|
192
|
+
if category == "acodec":
|
|
193
|
+
updated_category = self.mediainfo_audio(category=category)
|
|
194
|
+
|
|
195
|
+
elif category == "vcodec":
|
|
196
|
+
updated_category = self.mediainfo_video(category=category)
|
|
197
|
+
|
|
198
|
+
elif category == "hdr":
|
|
199
|
+
updated_category = self.mediainfo_hdr(category=category)
|
|
200
|
+
|
|
201
|
+
elif category == "uhd":
|
|
202
|
+
updated_category = self.mediainfo_uhd(category=category)
|
|
203
|
+
|
|
204
|
+
elif category == "subtitle":
|
|
205
|
+
updated_category = {'subtitle': "SUBS" if len(self.mediafile.subtitle_track) > 1 else "SUB"}
|
|
206
|
+
|
|
207
|
+
if updated_category:
|
|
208
|
+
self.tags_dict.update(updated_category)
|
|
209
|
+
|
|
210
|
+
# /// Add S#E#, title, Year
|
|
211
|
+
se_str = ''
|
|
212
|
+
if self.season is not None and self.episode is not None:
|
|
213
|
+
se_str = f"S{self.season:02d}E{self.episode:02d}"
|
|
214
|
+
elif self.season is not None:
|
|
215
|
+
se_str = f"S{self.season:02d}"
|
|
216
|
+
elif self.episode is not None:
|
|
217
|
+
se_str = f"E{self.episode:02d}"
|
|
218
|
+
|
|
219
|
+
self.tags_dict.update({'title': self.title})
|
|
220
|
+
if self.year:
|
|
221
|
+
self.tags_dict.update({'year': self.year})
|
|
222
|
+
if se_str:
|
|
223
|
+
self.tags_dict.update({'season': se_str})
|
|
224
|
+
|
|
225
|
+
# /// Add Sign
|
|
226
|
+
if not self.releaser_sign:
|
|
227
|
+
filename, _ = os.path.splitext(os.path.basename(self.filename))
|
|
228
|
+
m = re.search(r'-([A-Za-z0-9]+)$', filename)
|
|
229
|
+
self.releaser_sign = f"-{m.group(1)}" if m and m.group(1) not in TAG_TYPES else ""
|
|
230
|
+
else:
|
|
231
|
+
self.releaser_sign = f"-{self.releaser_sign}"
|
|
232
|
+
|
|
233
|
+
# /// Order according to tag position
|
|
234
|
+
tags_dict = {
|
|
235
|
+
k: self.tags_dict[k]
|
|
236
|
+
for k in self.tags_position
|
|
237
|
+
if k in self.tags_dict
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
# /// Build the title
|
|
241
|
+
build = []
|
|
242
|
+
for k, v in tags_dict.items():
|
|
243
|
+
if isinstance(v, list):
|
|
244
|
+
build.append(' '.join(v))
|
|
245
|
+
else:
|
|
246
|
+
build.append(str(v))
|
|
247
|
+
|
|
248
|
+
refactored = ' '.join(build) + self.releaser_sign
|
|
249
|
+
return refactored
|
|
250
|
+
|
|
251
|
+
def mediainfo_audio(self, category: str) -> dict:
|
|
252
|
+
langs = set()
|
|
253
|
+
audio_codecs = []
|
|
254
|
+
if self.mediafile.audio_track:
|
|
255
|
+
for audio in self.mediafile.audio_track:
|
|
256
|
+
other_format = audio.get('other_format', [])
|
|
257
|
+
if other_format:
|
|
258
|
+
codec_translated = audio_translate.get(other_format[0], '')
|
|
259
|
+
if not codec_translated:
|
|
260
|
+
codec_translated = other_format[0]
|
|
261
|
+
# Check Atmos
|
|
262
|
+
dolby = audio.get('commercial_name', "").lower()
|
|
263
|
+
atmos = 'Atmos' if 'atmos' in dolby else ''
|
|
264
|
+
# Add audio codec
|
|
265
|
+
channel_s = audio.get('channel_s', 0)
|
|
266
|
+
# Add channels
|
|
267
|
+
ch = {2: "2.0", 6: "5.1", 8: "7.1"}.get(channel_s, "")
|
|
268
|
+
if f"{codec_translated} {ch} {atmos}".strip() not in audio_codecs:
|
|
269
|
+
audio_codecs.append(f"{codec_translated} {ch} {atmos}".strip())
|
|
270
|
+
print(f"Mediainfo {other_format} -> {codec_translated} {ch} {atmos}")
|
|
271
|
+
|
|
272
|
+
# Add flags
|
|
273
|
+
for l in audio.get('other_language', []):
|
|
274
|
+
c = ManageTitles.convert_iso(l)
|
|
275
|
+
if c:
|
|
276
|
+
if isinstance(c, list):
|
|
277
|
+
langs.update(c)
|
|
278
|
+
else:
|
|
279
|
+
langs.add(c)
|
|
280
|
+
break
|
|
281
|
+
# Add multilanguage tag when languages > 2
|
|
282
|
+
if len(langs) > 2:
|
|
283
|
+
self.tags_dict.update({'multi': 'MULTI'})
|
|
284
|
+
|
|
285
|
+
audio_codecs.extend(list(langs))
|
|
286
|
+
return {category: audio_codecs}
|
|
287
|
+
|
|
288
|
+
def mediainfo_video(self, category: str) -> dict:
|
|
289
|
+
codec_translated = {}
|
|
290
|
+
if self.mediafile.video_track:
|
|
291
|
+
for video in self.mediafile.video_track:
|
|
292
|
+
video_format = video.get('format', "")
|
|
293
|
+
codec_translated = video_translate.get(video_format, video_format)
|
|
294
|
+
if codec_translated:
|
|
295
|
+
return {category: codec_translated}
|
|
296
|
+
return codec_translated
|
|
297
|
+
|
|
298
|
+
def mediainfo_hdr(self, category: str) -> dict:
|
|
299
|
+
if self.mediafile.video_track:
|
|
300
|
+
for video in self.mediafile.video_track:
|
|
301
|
+
hdr_format_commercial = video.get('hdr_format_commercial', "")
|
|
302
|
+
hdr_format = video.get('hdr_format', "")
|
|
303
|
+
# Check hdr
|
|
304
|
+
if hdr_format_commercial:
|
|
305
|
+
print(f"hdr_format_commercial: {hdr_format_commercial}")
|
|
306
|
+
print(f"hdr_format: {hdr_format}")
|
|
307
|
+
hdr = ''
|
|
308
|
+
if hdr_format_commercial in hdr_map:
|
|
309
|
+
print(f"hdr_format_commercial: {hdr_format_commercial} -> Tag: {hdr_map[hdr_format_commercial]}")
|
|
310
|
+
hdr = hdr_map[hdr_format_commercial]
|
|
311
|
+
# Check dolby vision
|
|
312
|
+
if 'DOLBY VISION' in hdr_format_commercial.upper() or 'DOLBY VISION' in hdr_format.upper():
|
|
313
|
+
hdr = f"DOLBY VISION {hdr}"
|
|
314
|
+
print(hdr)
|
|
315
|
+
return {category: hdr_map[hdr]}
|
|
316
|
+
return {}
|
|
317
|
+
|
|
318
|
+
def mediainfo_uhd(self, category: str) -> dict:
|
|
319
|
+
"""
|
|
320
|
+
identify resolution based on Height and Width tolerance 5%
|
|
321
|
+
"""
|
|
322
|
+
result = {}
|
|
323
|
+
if self.mediafile.video_track:
|
|
324
|
+
video_height = int(self.mediafile.video_track[0].get('height', 0))
|
|
325
|
+
video_width = int(self.mediafile.video_track[0].get('width', 0))
|
|
326
|
+
print(f"VideoTrack : W{video_width} x H{video_height}")
|
|
327
|
+
|
|
328
|
+
# Calculate range 5%
|
|
329
|
+
def in_range(value, standard):
|
|
330
|
+
tol = standard * 0.05
|
|
331
|
+
return standard - tol <= value <= standard + tol
|
|
332
|
+
|
|
333
|
+
# /// UHD
|
|
334
|
+
if video_height >= 2000 or video_width >= 3840:
|
|
335
|
+
result[category] = 'UHD'
|
|
336
|
+
result['resolution'] = '2160p'
|
|
337
|
+
# /// Full HD
|
|
338
|
+
elif in_range(video_height, 1080) or in_range(video_width, 1920):
|
|
339
|
+
result[category] = 'FullHD'
|
|
340
|
+
result['resolution'] = '1080p'
|
|
341
|
+
# /// HD
|
|
342
|
+
elif in_range(video_height, 720) or in_range(video_width, 1280):
|
|
343
|
+
result[category] = 'HD'
|
|
344
|
+
result['resolution'] = '720p'
|
|
345
|
+
# /// SD 576p
|
|
346
|
+
elif in_range(video_height, 576) or in_range(video_width, 768):
|
|
347
|
+
result[category] = 'SD'
|
|
348
|
+
result['resolution'] = '576p'
|
|
349
|
+
# /// SD 480p
|
|
350
|
+
elif in_range(video_height, 480) or in_range(video_width, 640):
|
|
351
|
+
result[category] = 'SD'
|
|
352
|
+
result['resolution'] = '480p'
|
|
353
|
+
else:
|
|
354
|
+
result[category] = 'unknown'
|
|
355
|
+
result['resolution'] = f'{video_width}x{video_height}'
|
|
356
|
+
|
|
357
|
+
return result
|
|
@@ -171,8 +171,39 @@ class ManageTitles:
|
|
|
171
171
|
|
|
172
172
|
return filename
|
|
173
173
|
|
|
174
|
+
@staticmethod
|
|
175
|
+
def recover_tag(filename_sanitized: str) -> str:
|
|
176
|
+
|
|
177
|
+
# Add the tag
|
|
178
|
+
replacements = [
|
|
179
|
+
(r'\b7 \b1\b', '7.1'),
|
|
180
|
+
(r'\b5 \b1\b', '5.1'),
|
|
181
|
+
(r'\bDDP5 \b1\b', 'DDP5.1'),
|
|
182
|
+
(r'\bDDP2 \b0\b', 'DDP2.0'),
|
|
183
|
+
(r'\bDD5 \b1\b', 'DD5.1'),
|
|
184
|
+
(r'\bDD2 \b0\b', 'DD2.0'),
|
|
185
|
+
(r'\b2 \b0\b', '2.0'),
|
|
186
|
+
(r'\bWEB \bDL\b', 'WEB-DL'),
|
|
187
|
+
(r'\bWEB \bDLMUX\b', 'WEB-DLMUX'),
|
|
188
|
+
(r'\bBD \bUNTOUCHED\b', 'BD-UNTOUCHED'),
|
|
189
|
+
(r'\bCINEMA \bMD\b', 'CINEMA-MD'),
|
|
190
|
+
(r'\bHEVC \bFHC\b', 'HEVC-FHC'),
|
|
191
|
+
(r'\bCBR \bCBZ\b', 'CBR-CBZ'),
|
|
192
|
+
(r'\bH \b264\b', 'H.264'),
|
|
193
|
+
(r'\bH \b265\b', 'H.265'),
|
|
194
|
+
(r'\bAAC2 \b0\b', 'AAC2.0'),
|
|
195
|
+
(r'\bAAC5 \b1\b', 'AAC5.1'),
|
|
196
|
+
]
|
|
197
|
+
|
|
198
|
+
for tag, replacement in replacements:
|
|
199
|
+
filename_sanitized = re.sub(tag, replacement, filename_sanitized, flags=re.IGNORECASE)
|
|
200
|
+
return filename_sanitized
|
|
201
|
+
|
|
174
202
|
@staticmethod
|
|
175
203
|
def clean_text(filename: str) -> str:
|
|
204
|
+
"""
|
|
205
|
+
Clean filename
|
|
206
|
+
"""
|
|
176
207
|
|
|
177
208
|
# Remove each addition from the string
|
|
178
209
|
filename_sanitized = filename
|
|
@@ -193,34 +224,23 @@ class ManageTitles:
|
|
|
193
224
|
|
|
194
225
|
# remove double space
|
|
195
226
|
filename_sanitized = re.sub(r"\s+", " ", filename_sanitized).strip()
|
|
196
|
-
|
|
197
227
|
return filename_sanitized
|
|
198
228
|
|
|
199
229
|
@staticmethod
|
|
200
|
-
def
|
|
230
|
+
def clean_tags(filename: str) -> str:
|
|
231
|
+
"""
|
|
232
|
+
Clean filename for title generation
|
|
233
|
+
"""
|
|
201
234
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
(r'\b5 \b1\b', '5.1'),
|
|
206
|
-
(r'\bDDP5 \b1\b', 'DDP5.1'),
|
|
207
|
-
(r'\bDDP2 \b0\b', 'DDP2.0'),
|
|
208
|
-
(r'\bDD5 \b1\b', 'DD5.1'),
|
|
209
|
-
(r'\bDD2 \b0\b', 'DD2.0'),
|
|
210
|
-
(r'\b2 \b0\b', '2.0'),
|
|
211
|
-
(r'\bWEB \bDL\b', 'WEB-DL'),
|
|
212
|
-
(r'\bWEB \bDLMUX\b', 'WEB-DLMUX'),
|
|
213
|
-
(r'\bBD \bUNTOUCHED\b', 'BD-UNTOUCHED'),
|
|
214
|
-
(r'\bCINEMA \bMD\b', 'CINEMA-MD'),
|
|
215
|
-
(r'\bHEVC \bFHC\b', 'HEVC-FHC'),
|
|
216
|
-
(r'\bCBR \bCBZ\b', 'CBR-CBZ'),
|
|
217
|
-
(r'\bH \b264\b', 'H.264'),
|
|
218
|
-
(r'\bH \b265\b', 'H.265'),
|
|
219
|
-
(r'\bAAC2 \b0\b', 'AAC2.0'),
|
|
220
|
-
]
|
|
235
|
+
filename_sanitized = filename
|
|
236
|
+
# Remove v version
|
|
237
|
+
filename_sanitized = re.sub(r"v\d+(?:[ .]\d+)*", "", filename_sanitized).strip()
|
|
221
238
|
|
|
222
|
-
|
|
223
|
-
|
|
239
|
+
# remove spaces, tab, newline
|
|
240
|
+
filename_sanitized = re.sub(r"\s+", " ", filename_sanitized)
|
|
241
|
+
|
|
242
|
+
# remove double space
|
|
243
|
+
filename_sanitized = re.sub(r"\s+", " ", filename_sanitized).strip()
|
|
224
244
|
return filename_sanitized
|
|
225
245
|
|
|
226
246
|
|
|
@@ -6,9 +6,7 @@ from common.external_services.igdb.core.tags import crew_patterns, platform_patt
|
|
|
6
6
|
from common.title import Guessit
|
|
7
7
|
from common.utility import ManageTitles, System
|
|
8
8
|
from common.mediainfo import MediaFile
|
|
9
|
-
from common.p2p_tags import P2pTags
|
|
10
9
|
from common import title
|
|
11
|
-
from unit3dup import config_settings
|
|
12
10
|
from view import custom_console
|
|
13
11
|
|
|
14
12
|
|
|
@@ -62,9 +60,12 @@ class Media:
|
|
|
62
60
|
self._title_sanitized = ManageTitles.clean_text(self.title)
|
|
63
61
|
return self._title_sanitized
|
|
64
62
|
|
|
65
|
-
@
|
|
66
|
-
def
|
|
67
|
-
self.
|
|
63
|
+
@property
|
|
64
|
+
def title_sanitize_tags(self) -> str:
|
|
65
|
+
if not self._title_sanitized:
|
|
66
|
+
self._title_sanitized = ManageTitles.clean_tags(self.title)
|
|
67
|
+
return self._title_sanitized
|
|
68
|
+
|
|
68
69
|
|
|
69
70
|
@property
|
|
70
71
|
def crew_list(self) -> list['str']:
|
|
@@ -219,21 +220,6 @@ class Media:
|
|
|
219
220
|
|
|
220
221
|
@property
|
|
221
222
|
def display_name(self):
|
|
222
|
-
if not self._display_name:
|
|
223
|
-
self._guess_filename = title.Guessit(self.title_sanitized)
|
|
224
|
-
guess = self._guess_filename.guessit
|
|
225
|
-
p2p_tags = P2pTags(filename=self.title_sanitized,
|
|
226
|
-
title=guess.get("title", None),
|
|
227
|
-
year=guess.get("year", ""),
|
|
228
|
-
episode_title=guess.get("episode_title", None),
|
|
229
|
-
mediafile_resolution=self.resolution,
|
|
230
|
-
season=self.guess_season,
|
|
231
|
-
episode=self.guess_episode,
|
|
232
|
-
releaser_sign=config_settings.user_preferences.RELEASER_SIGN,
|
|
233
|
-
tags_position=config_settings.user_preferences.TAGS_POSITION,
|
|
234
|
-
mediafile=self.mediafile
|
|
235
|
-
)
|
|
236
|
-
self._display_name = p2p_tags.process()
|
|
237
223
|
return self._display_name
|
|
238
224
|
|
|
239
225
|
@display_name.setter
|
|
@@ -135,10 +135,9 @@ class ContentManager:
|
|
|
135
135
|
self.file_name = self.path
|
|
136
136
|
|
|
137
137
|
# # Display name on webpage
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
# self.display_name = re.sub(r'[\[\]()]', '', self.display_name)
|
|
138
|
+
self.display_name, _ = os.path.splitext(os.path.basename(self.file_name))
|
|
139
|
+
self.display_name = ManageTitles.clean_text(self.display_name)
|
|
140
|
+
self.display_name = re.sub(r'[\[\]()]', '', self.display_name)
|
|
142
141
|
|
|
143
142
|
# current media path
|
|
144
143
|
self.torrent_path = self.path
|
|
@@ -164,8 +163,8 @@ class ContentManager:
|
|
|
164
163
|
self.file_name = os.path.join(self.path, files_list[0])
|
|
165
164
|
|
|
166
165
|
# # Display name on webpage
|
|
167
|
-
|
|
168
|
-
|
|
166
|
+
self.display_name = ManageTitles.clean_text(os.path.basename(self.path))
|
|
167
|
+
self.display_name = re.sub(r'[\[\]()]', '', self.display_name)
|
|
169
168
|
|
|
170
169
|
# current media path
|
|
171
170
|
self.torrent_path = self.path
|