DDownloader 0.2.8__tar.gz → 0.3.0__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.
- {ddownloader-0.2.8 → ddownloader-0.3.0}/DDownloader/main.py +24 -8
- ddownloader-0.3.0/DDownloader/modules/args_parser.py +58 -0
- {ddownloader-0.2.8 → ddownloader-0.3.0}/DDownloader/modules/banners.py +1 -1
- {ddownloader-0.2.8 → ddownloader-0.3.0}/DDownloader/modules/dash_downloader.py +11 -5
- {ddownloader-0.2.8 → ddownloader-0.3.0}/DDownloader/modules/hls_downloader.py +11 -5
- {ddownloader-0.2.8 → ddownloader-0.3.0}/DDownloader.egg-info/PKG-INFO +2 -2
- {ddownloader-0.2.8 → ddownloader-0.3.0}/PKG-INFO +2 -2
- {ddownloader-0.2.8 → ddownloader-0.3.0}/pyproject.toml +1 -1
- ddownloader-0.2.8/DDownloader/modules/args_parser.py +0 -24
- {ddownloader-0.2.8 → ddownloader-0.3.0}/DDownloader/__init__.py +0 -0
- {ddownloader-0.2.8 → ddownloader-0.3.0}/DDownloader/modules/__init__.py +0 -0
- {ddownloader-0.2.8 → ddownloader-0.3.0}/DDownloader/modules/helper.py +0 -0
- {ddownloader-0.2.8 → ddownloader-0.3.0}/DDownloader/modules/streamlink.py +0 -0
- {ddownloader-0.2.8 → ddownloader-0.3.0}/DDownloader.egg-info/SOURCES.txt +0 -0
- {ddownloader-0.2.8 → ddownloader-0.3.0}/DDownloader.egg-info/dependency_links.txt +0 -0
- {ddownloader-0.2.8 → ddownloader-0.3.0}/DDownloader.egg-info/entry_points.txt +0 -0
- {ddownloader-0.2.8 → ddownloader-0.3.0}/DDownloader.egg-info/requires.txt +0 -0
- {ddownloader-0.2.8 → ddownloader-0.3.0}/DDownloader.egg-info/top_level.txt +0 -0
- {ddownloader-0.2.8 → ddownloader-0.3.0}/LICENSE +0 -0
- {ddownloader-0.2.8 → ddownloader-0.3.0}/README.md +0 -0
- {ddownloader-0.2.8 → ddownloader-0.3.0}/setup.cfg +0 -0
@@ -16,26 +16,32 @@ logger = logging.getLogger("+ DDOWNLOADER + ")
|
|
16
16
|
coloredlogs.install(level='DEBUG', logger=logger)
|
17
17
|
|
18
18
|
def validate_directories():
|
19
|
+
"""Ensure necessary directories exist."""
|
19
20
|
downloads_dir = 'downloads'
|
20
|
-
|
21
|
-
os.makedirs(downloads_dir)
|
22
|
-
# logger.debug(f"
|
21
|
+
try:
|
22
|
+
os.makedirs(downloads_dir, exist_ok=True)
|
23
|
+
# logger.debug(f"Validated existence of directory: '{downloads_dir}'.")
|
24
|
+
except Exception as e:
|
25
|
+
logger.error(f"Failed to create or validate downloads directory: {e}")
|
26
|
+
exit(1)
|
23
27
|
|
24
28
|
def display_help():
|
25
29
|
"""Display custom help message with emoji."""
|
26
30
|
print(
|
27
|
-
f"{Fore.WHITE}+" + "=" *
|
31
|
+
f"{Fore.WHITE}+" + "=" * 100 + f"+{Style.RESET_ALL}\n"
|
28
32
|
f"{Fore.CYAN}{'Option':<40}{'Description':<90}{Style.RESET_ALL}\n"
|
29
|
-
f"{Fore.WHITE}+" + "=" *
|
33
|
+
f"{Fore.WHITE}+" + "=" * 100 + f"+{Style.RESET_ALL}\n"
|
30
34
|
f" {Fore.GREEN}-u, --url{' ' * 22}{Style.RESET_ALL}URL of the manifest (mpd/m3u8) 🌐\n"
|
31
35
|
f" {Fore.GREEN}-p, --proxy{' ' * 20}{Style.RESET_ALL}A proxy with protocol (http://ip:port) 🌍\n"
|
32
36
|
f" {Fore.GREEN}-o, --output{' ' * 19}{Style.RESET_ALL}Name of the output file 💾\n"
|
33
37
|
f" {Fore.GREEN}-k, --key{' ' * 22}{Style.RESET_ALL}Decryption key in KID:KEY format 🔑\n"
|
34
|
-
f" {Fore.GREEN}-h, --
|
35
|
-
f"{Fore.
|
38
|
+
f" {Fore.GREEN}-h, --header{' ' * 19}{Style.RESET_ALL}Custom HTTP headers (e.g., User-Agent: value) 📋\n"
|
39
|
+
f" {Fore.GREEN}-?, --help{' ' * 21}{Style.RESET_ALL}Show this help message and exit ❓\n"
|
40
|
+
f"{Fore.WHITE}+" + "=" * 100 + f"+{Style.RESET_ALL}\n"
|
36
41
|
)
|
37
42
|
|
38
43
|
def main():
|
44
|
+
"""Main entry point for the downloader."""
|
39
45
|
clear_and_print()
|
40
46
|
platform_name = detect_platform()
|
41
47
|
if platform_name == 'Unknown':
|
@@ -54,6 +60,8 @@ def main():
|
|
54
60
|
clear_and_print()
|
55
61
|
|
56
62
|
validate_directories()
|
63
|
+
|
64
|
+
# Parse arguments
|
57
65
|
try:
|
58
66
|
args = parse_arguments()
|
59
67
|
except SystemExit:
|
@@ -78,12 +86,20 @@ def main():
|
|
78
86
|
downloader.decryption_keys = args.key or []
|
79
87
|
downloader.proxy = args.proxy # Add proxy if provided
|
80
88
|
|
89
|
+
# Add headers if specified
|
90
|
+
if hasattr(args, 'header') and args.header:
|
91
|
+
downloader.headers = args.header # Pass headers to downloader
|
92
|
+
logger.info("Custom HTTP headers provided:")
|
93
|
+
for header in args.header:
|
94
|
+
logger.info(f" --header {header}")
|
95
|
+
print(Fore.MAGENTA + "=" * 100 + Fore.RESET)
|
96
|
+
|
81
97
|
# Log provided decryption keys
|
82
98
|
if downloader.decryption_keys:
|
83
99
|
logger.info("Decryption keys provided:")
|
84
100
|
for key in downloader.decryption_keys:
|
85
101
|
logger.info(f" --key {key}")
|
86
|
-
print(Fore.MAGENTA + "=" *
|
102
|
+
print(Fore.MAGENTA + "=" * 100 + Fore.RESET)
|
87
103
|
|
88
104
|
# Execute downloader
|
89
105
|
try:
|
@@ -0,0 +1,58 @@
|
|
1
|
+
import argparse
|
2
|
+
from colorama import Fore, Style
|
3
|
+
|
4
|
+
def parse_arguments():
|
5
|
+
"""Parse and return command-line arguments."""
|
6
|
+
# Create the ArgumentParser with a custom usage and help message
|
7
|
+
parser = argparse.ArgumentParser(
|
8
|
+
add_help=False, # Disable default help
|
9
|
+
usage=f"""{Fore.CYAN}Usage:{Style.RESET_ALL}
|
10
|
+
script.py {Fore.YELLOW}-u{Style.RESET_ALL} <manifest_url> {Fore.YELLOW}-o{Style.RESET_ALL} <output_name> [{Fore.YELLOW}-p{Style.RESET_ALL} <proxy>] [{Fore.YELLOW}-k{Style.RESET_ALL} <key>] [{Fore.YELLOW}-h{Style.RESET_ALL} <header>]"""
|
11
|
+
)
|
12
|
+
|
13
|
+
# Add arguments (no default descriptions, suppress help visibility)
|
14
|
+
parser.add_argument(
|
15
|
+
"-u", "--url",
|
16
|
+
required=True,
|
17
|
+
help=argparse.SUPPRESS
|
18
|
+
)
|
19
|
+
parser.add_argument(
|
20
|
+
"-p", "--proxy",
|
21
|
+
help=argparse.SUPPRESS
|
22
|
+
)
|
23
|
+
parser.add_argument(
|
24
|
+
"-o", "--output",
|
25
|
+
required=True,
|
26
|
+
help=argparse.SUPPRESS
|
27
|
+
)
|
28
|
+
parser.add_argument(
|
29
|
+
"-k", "--key",
|
30
|
+
action="append", # Allow multiple keys
|
31
|
+
help=argparse.SUPPRESS
|
32
|
+
)
|
33
|
+
parser.add_argument(
|
34
|
+
"-h", "--header",
|
35
|
+
action="append", # Allow multiple headers
|
36
|
+
help=argparse.SUPPRESS
|
37
|
+
)
|
38
|
+
parser.add_argument(
|
39
|
+
"-?", "--help",
|
40
|
+
action="help",
|
41
|
+
default=argparse.SUPPRESS,
|
42
|
+
help=argparse.SUPPRESS
|
43
|
+
)
|
44
|
+
|
45
|
+
# Parse arguments
|
46
|
+
args = parser.parse_args()
|
47
|
+
|
48
|
+
# Validate mandatory arguments
|
49
|
+
if not args.url:
|
50
|
+
print(f"{Fore.RED}Error: The URL (-u) is required.{Style.RESET_ALL}")
|
51
|
+
parser.print_usage()
|
52
|
+
exit(1)
|
53
|
+
if not args.output:
|
54
|
+
print(f"{Fore.RED}Error: The output (-o) is required.{Style.RESET_ALL}")
|
55
|
+
parser.print_usage()
|
56
|
+
exit(1)
|
57
|
+
|
58
|
+
return args
|
@@ -19,7 +19,7 @@ def banners():
|
|
19
19
|
stdout.write(""+Fore.YELLOW +"╔════════════════════════════════════════════════════════════════════════════╝\n")
|
20
20
|
stdout.write(""+Fore.YELLOW +"║ \x1b[38;2;255;20;147m• "+Fore.GREEN+"GITHUB "+Fore.RED+" |"+Fore.LIGHTWHITE_EX+" GITHUB.COM/THATNOTEASY "+Fore.YELLOW+"║\n")
|
21
21
|
stdout.write(""+Fore.YELLOW +"╚════════════════════════════════════════════════════════════════════════════╝\n")
|
22
|
-
print(f"{Fore.YELLOW}[DDownloader] - {Fore.GREEN}Download DASH or HLS streams with decryption keys. - {Fore.RED}[V0.
|
22
|
+
print(f"{Fore.YELLOW}[DDownloader] - {Fore.GREEN}Download DASH or HLS streams with decryption keys. - {Fore.RED}[V0.3.0] \n{Fore.RESET}")
|
23
23
|
|
24
24
|
def clear_and_print():
|
25
25
|
time.sleep(1)
|
@@ -14,6 +14,7 @@ class DASH:
|
|
14
14
|
self.output_name = None
|
15
15
|
self.proxy = None
|
16
16
|
self.decryption_keys = []
|
17
|
+
self.headers = []
|
17
18
|
self.binary_path = self._get_binary_path()
|
18
19
|
|
19
20
|
def _get_binary_path(self):
|
@@ -61,19 +62,24 @@ class DASH:
|
|
61
62
|
'--select-audio', 'BEST',
|
62
63
|
'-mt',
|
63
64
|
'-M', 'format=mp4',
|
64
|
-
'--save-dir', 'downloads',
|
65
|
-
'--tmp-dir', 'downloads',
|
65
|
+
'--save-dir', '"downloads"',
|
66
|
+
'--tmp-dir', '"downloads"',
|
66
67
|
'--del-after-done',
|
67
|
-
'--save-name', self.output_name
|
68
|
+
'--save-name', f'"{self.output_name}"'
|
68
69
|
]
|
69
70
|
|
70
71
|
for key in self.decryption_keys:
|
71
|
-
command.extend(['--key', key])
|
72
|
+
command.extend(['--key', f'"{key}"'])
|
72
73
|
|
73
74
|
if self.proxy:
|
74
75
|
if not self.proxy.startswith("http://"):
|
75
76
|
self.proxy = f"http://{self.proxy}"
|
76
|
-
command.extend(['--custom-proxy', self.proxy])
|
77
|
+
command.extend(['--custom-proxy', f'"{self.proxy}"'])
|
78
|
+
|
79
|
+
# Add headers if any are provided
|
80
|
+
for header in self.headers:
|
81
|
+
command.extend(['-H', f'"{header}"'])
|
82
|
+
|
77
83
|
# logger.debug(f"Built command: {' '.join(command)}")
|
78
84
|
return command
|
79
85
|
|
@@ -14,6 +14,7 @@ class HLS:
|
|
14
14
|
self.output_name = None
|
15
15
|
self.proxy = None
|
16
16
|
self.decryption_keys = []
|
17
|
+
self.headers = []
|
17
18
|
self.binary_path = self._get_binary_path()
|
18
19
|
|
19
20
|
def _get_binary_path(self):
|
@@ -61,19 +62,24 @@ class HLS:
|
|
61
62
|
'--select-audio', 'BEST',
|
62
63
|
'-mt',
|
63
64
|
'-M', 'format=mp4',
|
64
|
-
'--save-dir', 'downloads',
|
65
|
-
'--tmp-dir', 'downloads',
|
65
|
+
'--save-dir', '"downloads"',
|
66
|
+
'--tmp-dir', '"downloads"',
|
66
67
|
'--del-after-done',
|
67
|
-
'--save-name', self.output_name
|
68
|
+
'--save-name', f'"{self.output_name}"'
|
68
69
|
]
|
69
70
|
|
70
71
|
for key in self.decryption_keys:
|
71
|
-
command.extend(['--key', key])
|
72
|
+
command.extend(['--key', f'"{key}"'])
|
72
73
|
|
73
74
|
if self.proxy:
|
74
75
|
if not self.proxy.startswith("http://"):
|
75
76
|
self.proxy = f"http://{self.proxy}"
|
76
|
-
command.extend(['--custom-proxy', self.proxy])
|
77
|
+
command.extend(['--custom-proxy', f'"{self.proxy}"'])
|
78
|
+
|
79
|
+
# Add headers if any are provided
|
80
|
+
for header in self.headers:
|
81
|
+
command.extend(['-H', f'"{header}"'])
|
82
|
+
|
77
83
|
# logger.debug(f"Built command: {' '.join(command)}")
|
78
84
|
return command
|
79
85
|
|
@@ -1,24 +0,0 @@
|
|
1
|
-
import argparse
|
2
|
-
from colorama import Fore, Style
|
3
|
-
|
4
|
-
def parse_arguments():
|
5
|
-
"""Parse and return command-line arguments."""
|
6
|
-
# Create the ArgumentParser with no default help and no description
|
7
|
-
parser = argparse.ArgumentParser(
|
8
|
-
add_help=False, # Disable default help
|
9
|
-
usage="", # Suppress the default usage message
|
10
|
-
)
|
11
|
-
|
12
|
-
# Add arguments (these will not include the default descriptions)
|
13
|
-
parser.add_argument("-u", "--url", required=True, help=argparse.SUPPRESS)
|
14
|
-
parser.add_argument("-p", "--proxy", help=argparse.SUPPRESS)
|
15
|
-
parser.add_argument("-o", "--output", required=True, help=argparse.SUPPRESS)
|
16
|
-
parser.add_argument("-k", "--key", action="append", help=argparse.SUPPRESS)
|
17
|
-
parser.add_argument(
|
18
|
-
"-h", "--help",
|
19
|
-
action="help",
|
20
|
-
default=argparse.SUPPRESS,
|
21
|
-
help=argparse.SUPPRESS
|
22
|
-
)
|
23
|
-
|
24
|
-
return parser.parse_args()
|
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
|