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.
Files changed (21) hide show
  1. {ddownloader-0.2.8 → ddownloader-0.3.0}/DDownloader/main.py +24 -8
  2. ddownloader-0.3.0/DDownloader/modules/args_parser.py +58 -0
  3. {ddownloader-0.2.8 → ddownloader-0.3.0}/DDownloader/modules/banners.py +1 -1
  4. {ddownloader-0.2.8 → ddownloader-0.3.0}/DDownloader/modules/dash_downloader.py +11 -5
  5. {ddownloader-0.2.8 → ddownloader-0.3.0}/DDownloader/modules/hls_downloader.py +11 -5
  6. {ddownloader-0.2.8 → ddownloader-0.3.0}/DDownloader.egg-info/PKG-INFO +2 -2
  7. {ddownloader-0.2.8 → ddownloader-0.3.0}/PKG-INFO +2 -2
  8. {ddownloader-0.2.8 → ddownloader-0.3.0}/pyproject.toml +1 -1
  9. ddownloader-0.2.8/DDownloader/modules/args_parser.py +0 -24
  10. {ddownloader-0.2.8 → ddownloader-0.3.0}/DDownloader/__init__.py +0 -0
  11. {ddownloader-0.2.8 → ddownloader-0.3.0}/DDownloader/modules/__init__.py +0 -0
  12. {ddownloader-0.2.8 → ddownloader-0.3.0}/DDownloader/modules/helper.py +0 -0
  13. {ddownloader-0.2.8 → ddownloader-0.3.0}/DDownloader/modules/streamlink.py +0 -0
  14. {ddownloader-0.2.8 → ddownloader-0.3.0}/DDownloader.egg-info/SOURCES.txt +0 -0
  15. {ddownloader-0.2.8 → ddownloader-0.3.0}/DDownloader.egg-info/dependency_links.txt +0 -0
  16. {ddownloader-0.2.8 → ddownloader-0.3.0}/DDownloader.egg-info/entry_points.txt +0 -0
  17. {ddownloader-0.2.8 → ddownloader-0.3.0}/DDownloader.egg-info/requires.txt +0 -0
  18. {ddownloader-0.2.8 → ddownloader-0.3.0}/DDownloader.egg-info/top_level.txt +0 -0
  19. {ddownloader-0.2.8 → ddownloader-0.3.0}/LICENSE +0 -0
  20. {ddownloader-0.2.8 → ddownloader-0.3.0}/README.md +0 -0
  21. {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
- if not os.path.exists(downloads_dir):
21
- os.makedirs(downloads_dir)
22
- # logger.debug(f"Created '{downloads_dir}' directory.")
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}+" + "=" * 80 + f"+{Style.RESET_ALL}\n"
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}+" + "=" * 80 + f"+{Style.RESET_ALL}\n"
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, --help{' ' * 21}{Style.RESET_ALL}Show this help message and exit ❓\n"
35
- f"{Fore.WHITE}+" + "=" * 80 + f"+{Style.RESET_ALL}\n"
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 + "=" * 80 + Fore.RESET)
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.2.8] \n{Fore.RESET}")
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,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: DDownloader
3
- Version: 0.2.8
3
+ Version: 0.3.0
4
4
  Summary: A downloader for DRM-protected content.
5
5
  Author-email: ThatNotEasy <apidotmy@proton.me>
6
6
  License: MIT License
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: DDownloader
3
- Version: 0.2.8
3
+ Version: 0.3.0
4
4
  Summary: A downloader for DRM-protected content.
5
5
  Author-email: ThatNotEasy <apidotmy@proton.me>
6
6
  License: MIT License
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "DDownloader"
7
- version = "0.2.8"
7
+ version = "0.3.0"
8
8
  description = "A downloader for DRM-protected content."
9
9
  readme = { file = "README.md", content-type = "text/markdown" }
10
10
  authors = [
@@ -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