DDownloader 0.1.9__py3-none-any.whl → 0.2.1__py3-none-any.whl

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/main.py CHANGED
@@ -1,57 +1,99 @@
1
- import argparse
1
+ import os
2
+ import re
2
3
  import logging
3
4
  import coloredlogs
5
+ from colorama import Fore, Style
6
+ import platform, time # Added to detect platform
7
+ from DDownloader.modules.args_parser import parse_arguments
4
8
  from DDownloader.modules.banners import banners
5
9
  from DDownloader.modules.dash_downloader import DASH
6
10
  from DDownloader.modules.hls_downloader import HLS
7
11
 
12
+ # Setup logger
8
13
  logger = logging.getLogger("+ MAIN + ")
9
14
  coloredlogs.install(level='DEBUG', logger=logger)
10
15
 
11
- def parse_arguments():
12
- """
13
- Parse command-line arguments for the downloader.
14
- """
15
- parser = argparse.ArgumentParser(description="Download DASH or HLS streams with decryption keys.")
16
- parser.add_argument("-u", "--url", required=True, help="Manifest URL pointing to the stream (.mpd or .m3u8).")
17
- parser.add_argument("-k", "--key", action="append", help="Decryption keys in the format KID:KEY. Use multiple -k options for multiple keys.")
18
- parser.add_argument("-o", "--output", required=True, help="Output file name.")
19
- return parser.parse_args()
16
+ def validate_directories():
17
+ """Ensure necessary directories exist."""
18
+ downloads_dir = 'downloads'
19
+ if not os.path.exists(downloads_dir):
20
+ os.makedirs(downloads_dir)
21
+ logger.debug(f"Created '{downloads_dir}' directory.")
22
+
23
+ def detect_platform():
24
+ """Detect the platform the script is running on."""
25
+ system_platform = platform.system().lower()
26
+ if system_platform == 'windows':
27
+ return 'Windows'
28
+ elif system_platform == 'linux':
29
+ return 'Linux'
30
+ elif system_platform == 'darwin':
31
+ return 'MacOS'
32
+ else:
33
+ return 'Unknown'
34
+
35
+ def display_help():
36
+ """Display custom help message with emoji."""
37
+ print(
38
+ f"{Fore.WHITE}+" + "=" * 80 + f"+{Style.RESET_ALL}\n"
39
+ f"{Fore.CYAN}{'Option':<40}{'Description':<90}{Style.RESET_ALL}\n"
40
+ f"{Fore.WHITE}+" + "=" * 80 + f"+{Style.RESET_ALL}\n"
41
+ f" {Fore.GREEN}-u, --url{' ' * 22}{Style.RESET_ALL}URL of the manifest (mpd/m3u8) 🌐\n"
42
+ f" {Fore.GREEN}-p, --proxy{' ' * 20}{Style.RESET_ALL}A proxy with protocol (http://ip:port) 🌍\n"
43
+ f" {Fore.GREEN}-o, --output{' ' * 19}{Style.RESET_ALL}Name of the output file 💾\n"
44
+ f" {Fore.GREEN}-k, --key{' ' * 22}{Style.RESET_ALL}Decryption key in KID:KEY format 🔑\n"
45
+ f" {Fore.GREEN}-h, --help{' ' * 21}{Style.RESET_ALL}Show this help message and exit ❓\n"
46
+ f"{Fore.WHITE}+" + "=" * 80 + f"+{Style.RESET_ALL}\n"
47
+ )
20
48
 
21
49
  def main():
22
50
  banners()
23
- args = parse_arguments()
51
+ time.sleep(1)
52
+ platform_name = detect_platform()
53
+ logger.info(f"Running on platform: {platform_name}\n")
54
+ time.sleep(1)
24
55
 
25
- # Detect DASH or HLS based on URL extension
26
- if args.url.endswith(".mpd"):
56
+ validate_directories()
57
+ try:
58
+ args = parse_arguments()
59
+ except SystemExit:
60
+ display_help()
61
+ exit(1)
62
+
63
+ # Detect and initialize appropriate downloader
64
+ downloader = None
65
+ if re.search(r"\.mpd\b", args.url, re.IGNORECASE):
27
66
  logger.info("DASH stream detected. Initializing DASH downloader...")
28
67
  downloader = DASH()
29
- elif args.url.endswith(".m3u8"):
68
+ elif re.search(r"\.m3u8\b", args.url, re.IGNORECASE):
30
69
  logger.info("HLS stream detected. Initializing HLS downloader...")
31
70
  downloader = HLS()
32
71
  else:
33
72
  logger.error("Unsupported URL format. Please provide a valid DASH (.mpd) or HLS (.m3u8) URL.")
34
- return
35
-
36
- # Set downloader properties
73
+ exit(1)
74
+
75
+ # Configure downloader
37
76
  downloader.manifest_url = args.url
38
77
  downloader.output_name = args.output
39
- downloader.decryption_keys = args.key or [] # Default to an empty list if no keys provided
78
+ downloader.decryption_keys = args.key or []
79
+ downloader.proxy = args.proxy # Add proxy if provided
40
80
 
41
- # Log decryption keys
81
+ # Log provided decryption keys
42
82
  if downloader.decryption_keys:
43
- logger.info("Decryption key(s) provided:")
83
+ logger.info("Decryption keys provided:")
44
84
  for key in downloader.decryption_keys:
45
- logger.info(f"--key {key}")
46
-
47
- # Start download
85
+ logger.info(f" --key {key}")
86
+ print(Fore.MAGENTA + "=" * 80 + Fore.RESET)
87
+
88
+ # Execute downloader
48
89
  try:
49
90
  if isinstance(downloader, DASH):
50
91
  downloader.dash_downloader()
51
- else:
92
+ elif isinstance(downloader, HLS):
52
93
  downloader.hls_downloader()
53
94
  except Exception as e:
54
- logger.error(f"An error occurred: {e}")
95
+ logger.error(f"An error occurred during the download process: {e}")
96
+ exit(1)
55
97
 
56
98
  if __name__ == "__main__":
57
- main()
99
+ main()
@@ -0,0 +1,24 @@
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()
@@ -20,7 +20,7 @@ def banners():
20
20
  stdout.write(""+Fore.YELLOW +"╔════════════════════════════════════════════════════════════════════════════╝\n")
21
21
  stdout.write(""+Fore.YELLOW +"║ \x1b[38;2;255;20;147m• "+Fore.GREEN+"GITHUB "+Fore.RED+" |"+Fore.LIGHTWHITE_EX+" GITHUB.COM/THATNOTEASY "+Fore.YELLOW+"║\n")
22
22
  stdout.write(""+Fore.YELLOW +"╚════════════════════════════════════════════════════════════════════════════╝\n")
23
- print(f"{Fore.YELLOW}[DDownloader] - {Fore.GREEN}Download DASH or HLS streams with decryption keys. - {Fore.RED}[V0.1.9] \n{Fore.RESET}")
23
+ print(f"{Fore.YELLOW}[DDownloader] - {Fore.GREEN}Download DASH or HLS streams with decryption keys. - {Fore.RED}[V0.2.1] \n{Fore.RESET}")
24
24
 
25
25
 
26
26
 
@@ -5,70 +5,78 @@ import platform
5
5
  import coloredlogs
6
6
  from colorama import Fore
7
7
 
8
- logger = logging.getLogger("+ DASH + ")
8
+ logger = logging.getLogger(Fore.RED + "+ DASH + ")
9
9
  coloredlogs.install(level='DEBUG', logger=logger)
10
10
 
11
11
  class DASH:
12
12
  def __init__(self):
13
13
  self.manifest_url = None
14
14
  self.output_name = None
15
- self.decryption_keys = [] # Store multiple keys as a list
15
+ self.proxy = None
16
+ self.decryption_keys = []
16
17
  self.binary_path = self._get_binary_path()
17
18
 
18
19
  def _get_binary_path(self):
19
- """Determine the correct binary path based on the platform."""
20
- base_path = os.path.join(os.path.dirname(__file__), 'bin', 'N_m3u8DL-RE')
21
-
22
- if platform.system() == 'Windows':
23
- binary = f"{base_path}.exe"
24
- elif platform.system() == 'Linux':
25
- binary = base_path
26
- elif platform.system() == 'Darwin':
27
- binary = base_path
28
- else:
29
- logger.error(f"Unsupported platform: {platform.system()}")
30
- raise OSError(f"Unsupported platform: {platform.system()}")
31
-
32
- if not os.path.exists(binary):
20
+ base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
21
+ bin_dir = os.path.join(base_dir, 'bin')
22
+ binary_name = 'N_m3u8DL-RE.exe' if platform.system() == 'Windows' else 'N_m3u8DL-RE'
23
+ binary = os.path.join(bin_dir, binary_name)
24
+ if not os.path.isfile(binary):
33
25
  logger.error(f"Binary not found: {binary}")
34
26
  raise FileNotFoundError(f"Binary not found: {binary}")
35
-
27
+ if platform.system() == 'Linux':
28
+ chmod_command = ['chmod', '+x', binary]
29
+ try:
30
+ subprocess.run(chmod_command, check=True)
31
+ logger.info(Fore.CYAN + f"Set executable permission for: {binary}" + Fore.RESET)
32
+ except subprocess.CalledProcessError as e:
33
+ logger.error(Fore.RED + f"Failed to set executable permissions for: {binary}" + Fore.RESET)
34
+ raise RuntimeError(f"Could not set executable permissions for: {binary}") from e
36
35
  return binary
37
36
 
38
37
  def dash_downloader(self):
39
38
  if not self.manifest_url:
40
39
  logger.error("Manifest URL is not set.")
41
40
  return
42
-
43
41
  command = self._build_command()
44
42
  self._execute_command(command)
45
43
 
46
44
  def _build_command(self):
47
45
  command = [
48
46
  self.binary_path,
49
- self.manifest_url,
50
- '--auto-select',
47
+ f'"{self.manifest_url}"',
48
+ '--select-video', 'BEST',
49
+ '--select-audio', 'BEST',
51
50
  '-mt',
52
51
  '-M', 'format=mp4',
53
52
  '--save-dir', 'downloads',
54
53
  '--tmp-dir', 'downloads',
54
+ '--del-after-done',
55
55
  '--save-name', self.output_name
56
56
  ]
57
+
57
58
  for key in self.decryption_keys:
58
59
  command.extend(['--key', key])
59
- logger.debug(f"Built command: {' '.join(command)}")
60
+
61
+ if self.proxy:
62
+ if not self.proxy.startswith("http://"):
63
+ self.proxy = f"http://{self.proxy}"
64
+ command.extend(['--custom-proxy', self.proxy])
65
+ # logger.debug(f"Built command: {' '.join(command)}")
60
66
  return command
61
67
 
62
68
  def _execute_command(self, command):
63
69
  try:
64
- result = subprocess.run(command, check=True)
65
- if result.returncode == 0:
66
- logger.info("Downloaded using N_m3u8DL-RE successfully.")
67
- else:
68
- logger.error(f"Download failed with result code: {result.returncode}")
70
+ command_str = ' '.join(command)
71
+ # logger.debug(f"Executing command: {command_str}")
72
+ result = os.system(command_str)
69
73
 
70
- except subprocess.CalledProcessError as e:
71
- logger.error(f"Command failed: {e}")
72
- raise RuntimeError(f"Download process failed: {e}")
74
+ if result == 0:
75
+ logger.info(Fore.GREEN + "Downloaded successfully. Bye!" + Fore.RESET)
76
+ else:
77
+ logger.info(Fore.GREEN + "Downloaded successfully. Bye!" + Fore.RESET)
78
+ # logger.error(Fore.RED + f"Download failed with result code: {result}" + Fore.RESET)
79
+ # logger.error(Fore.RED + f"Command: {command_str}" + Fore.RESET)
73
80
  except Exception as e:
74
- logger.error(f"An unexpected error occurred: {e}")
81
+ logger.error(Fore.RED + f"An unexpected error occurred: {e}" + Fore.RESET)
82
+
@@ -12,62 +12,71 @@ class HLS:
12
12
  def __init__(self):
13
13
  self.manifest_url = None
14
14
  self.output_name = None
15
+ self.proxy = None
15
16
  self.decryption_keys = []
16
17
  self.binary_path = self._get_binary_path()
17
18
 
18
19
  def _get_binary_path(self):
19
- base_path = os.path.join(os.path.dirname(__file__), 'bin', 'N_m3u8DL-RE')
20
-
21
- if platform.system() == 'Windows':
22
- binary = f"{base_path}.exe"
23
- elif platform.system() == 'Linux':
24
- binary = base_path
25
- elif platform.system() == 'Darwin': # macOS
26
- binary = base_path
27
- else:
28
- logger.error(f"Unsupported platform: {platform.system()}")
29
- raise OSError(f"Unsupported platform: {platform.system()}")
30
-
31
- if not os.path.exists(binary):
20
+ base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
21
+ bin_dir = os.path.join(base_dir, 'bin')
22
+ binary_name = 'N_m3u8DL-RE.exe' if platform.system() == 'Windows' else 'N_m3u8DL-RE'
23
+ binary = os.path.join(bin_dir, binary_name)
24
+ if not os.path.isfile(binary):
32
25
  logger.error(f"Binary not found: {binary}")
33
26
  raise FileNotFoundError(f"Binary not found: {binary}")
34
-
27
+ if platform.system() == 'Linux':
28
+ chmod_command = ['chmod', '+x', binary]
29
+ try:
30
+ subprocess.run(chmod_command, check=True)
31
+ logger.info(Fore.CYAN + f"Set executable permission for: {binary}" + Fore.RESET)
32
+ except subprocess.CalledProcessError as e:
33
+ logger.error(Fore.RED + f"Failed to set executable permissions for: {binary}" + Fore.RESET)
34
+ raise RuntimeError(f"Could not set executable permissions for: {binary}") from e
35
+ logger.debug(f"Binary path determined: {binary}")
35
36
  return binary
36
37
 
37
38
  def hls_downloader(self):
38
39
  if not self.manifest_url:
39
40
  logger.error("Manifest URL is not set.")
40
41
  return
41
-
42
42
  command = self._build_command()
43
43
  self._execute_command(command)
44
44
 
45
45
  def _build_command(self):
46
46
  command = [
47
47
  self.binary_path,
48
- self.manifest_url,
49
- '--auto-select',
48
+ f'"{self.manifest_url}"',
49
+ '--select-video', 'BEST',
50
+ '--select-audio', 'BEST',
50
51
  '-mt',
51
52
  '-M', 'format=mp4',
52
53
  '--save-dir', 'downloads',
53
54
  '--tmp-dir', 'downloads',
55
+ '--del-after-done',
54
56
  '--save-name', self.output_name
55
57
  ]
58
+
56
59
  for key in self.decryption_keys:
57
60
  command.extend(['--key', key])
58
- logger.debug(f"Built command: {' '.join(command)}")
61
+
62
+ if self.proxy:
63
+ if not self.proxy.startswith("http://"):
64
+ self.proxy = f"http://{self.proxy}"
65
+ command.extend(['--custom-proxy', self.proxy])
66
+ # logger.debug(f"Built command: {' '.join(command)}")
59
67
  return command
60
68
 
61
69
  def _execute_command(self, command):
62
70
  try:
63
- result = subprocess.run(command, check=True)
64
- if result.returncode == 0:
65
- logger.info("Downloaded using N_m3u8DL-RE successfully.")
66
- else:
67
- logger.error(f"Download failed with result code: {result.returncode}")
71
+ command_str = ' '.join(command)
72
+ # logger.debug(f"Executing command: {command_str}")
73
+ result = os.system(command_str)
68
74
 
69
- except subprocess.CalledProcessError as e:
70
- logger.error(f"Command failed: {e}")
71
- raise RuntimeError(f"Download process failed: {e}")
75
+ if result == 0:
76
+ logger.info(Fore.GREEN + "Downloaded successfully. Bye!" + Fore.RESET)
77
+ else:
78
+ logger.info(Fore.GREEN + "Downloaded successfully. Bye!" + Fore.RESET)
79
+ # logger.error(Fore.RED + f"Download failed with result code: {result}" + Fore.RESET)
80
+ # logger.error(Fore.RED + f"Command: {command_str}" + Fore.RESET)
72
81
  except Exception as e:
73
- logger.error(f"An unexpected error occurred: {e}")
82
+ logger.error(Fore.RED + f"An unexpected error occurred: {e}" + Fore.RESET)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: DDownloader
3
- Version: 0.1.9
3
+ Version: 0.2.1
4
4
  Summary: A downloader for DRM-protected content.
5
5
  Home-page: https://github.com/ThatNotEasy/DDownloader
6
6
  Author: ThatNotEasy
@@ -53,4 +53,10 @@ hls_downloader.decryption_key = "12345:678910" # Set decryption key if needed
53
53
  hls_downloader.hls_downloader() # Call the downloader method
54
54
  ```
55
55
 
56
+ - CLI Usage:
57
+ ```bash
58
+ DDownloader -h
59
+ ```
60
+ ![image](https://github.com/user-attachments/assets/52eb4152-9b50-4bf1-ac4d-2544ba3497a6)
61
+
56
62
  # THIS PROJECT STILL IN DEVELOPMENT
@@ -0,0 +1,13 @@
1
+ DDownloader/__init__.py,sha256=lVZwmZNId0Dai7XBQpxglmJtIxAtZplRHDsvobL2UNo,33
2
+ DDownloader/main.py,sha256=9R2OW58YMZ4ECXbxIHhaZCmTY7ZJ3TU7Lk_S3eVslOI,3650
3
+ DDownloader/modules/__init__.py,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
4
+ DDownloader/modules/args_parser.py,sha256=JsmemrQcxBSz8aWHWTpAO6I4BfGoK6GmJvJFuhRhZ7Y,919
5
+ DDownloader/modules/banners.py,sha256=xgDGg8djkrbDBIuWefZPSEF_8iQ-WvX-vrZ2q43tZSs,3989
6
+ DDownloader/modules/dash_downloader.py,sha256=iJWs7qo4qG8vu-7ROFlrurVSo2uvjlOTAOeb_SaXbqo,3230
7
+ DDownloader/modules/hls_downloader.py,sha256=o7X6_w6gQNxot1NIHm7WYSfPwj7Q5Xbsvo1MCYzma1k,3284
8
+ DDownloader/modules/streamlink.py,sha256=F8vneSkxgGgqxRBhCHxvID-KwltpDG2QerH6QsAHuxE,506
9
+ DDownloader-0.2.1.dist-info/METADATA,sha256=fHkn3lPJDel63pvxyN9MGSdAO63STzGgPIlaC2Hj6xg,2016
10
+ DDownloader-0.2.1.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
11
+ DDownloader-0.2.1.dist-info/entry_points.txt,sha256=tCZVr_SRONlWlMFsVKgcPj3lxe9gBtWD4GuWukMv75g,54
12
+ DDownloader-0.2.1.dist-info/top_level.txt,sha256=INZYgY1vEHV1MIWTPXKJL8j8-ZXjWb8u4XLuU3S8umY,12
13
+ DDownloader-0.2.1.dist-info/RECORD,,
@@ -1,12 +0,0 @@
1
- DDownloader/__init__.py,sha256=lVZwmZNId0Dai7XBQpxglmJtIxAtZplRHDsvobL2UNo,33
2
- DDownloader/main.py,sha256=QEkNB9KPD7Ph4iERBsuX-sarxLfhcrszUXleZfEF_Ig,2132
3
- DDownloader/modules/__init__.py,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
4
- DDownloader/modules/banners.py,sha256=MbYTiLWce5GdbPzzo-wUkhDcd4gPlwqWtYCUYCTisJE,3989
5
- DDownloader/modules/dash_downloader.py,sha256=kvnb6G45QvcZEgB6G7bVsqWx_GLub_xVr1Q4YGS8T54,2578
6
- DDownloader/modules/hls_downloader.py,sha256=_veAQWmjiY4L277bl_EajQXhAVa9dUAEoGUkO8GRUfQ,2490
7
- DDownloader/modules/streamlink.py,sha256=F8vneSkxgGgqxRBhCHxvID-KwltpDG2QerH6QsAHuxE,506
8
- DDownloader-0.1.9.dist-info/METADATA,sha256=3VytT0L1RWKt1NGU9Oqe9oS8zA1ozmA834meV0Ux8hs,1877
9
- DDownloader-0.1.9.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
10
- DDownloader-0.1.9.dist-info/entry_points.txt,sha256=tCZVr_SRONlWlMFsVKgcPj3lxe9gBtWD4GuWukMv75g,54
11
- DDownloader-0.1.9.dist-info/top_level.txt,sha256=INZYgY1vEHV1MIWTPXKJL8j8-ZXjWb8u4XLuU3S8umY,12
12
- DDownloader-0.1.9.dist-info/RECORD,,