DDownloader 0.1.9__py3-none-any.whl → 0.2.0__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 +15 -15
- DDownloader/modules/banners.py +1 -1
- DDownloader/modules/dash_downloader.py +31 -30
- DDownloader/modules/hls_downloader.py +29 -27
- {DDownloader-0.1.9.dist-info → DDownloader-0.2.0.dist-info}/METADATA +7 -1
- DDownloader-0.2.0.dist-info/RECORD +12 -0
- DDownloader-0.1.9.dist-info/RECORD +0 -12
- {DDownloader-0.1.9.dist-info → DDownloader-0.2.0.dist-info}/WHEEL +0 -0
- {DDownloader-0.1.9.dist-info → DDownloader-0.2.0.dist-info}/entry_points.txt +0 -0
- {DDownloader-0.1.9.dist-info → DDownloader-0.2.0.dist-info}/top_level.txt +0 -0
DDownloader/main.py
CHANGED
@@ -4,14 +4,17 @@ import coloredlogs
|
|
4
4
|
from DDownloader.modules.banners import banners
|
5
5
|
from DDownloader.modules.dash_downloader import DASH
|
6
6
|
from DDownloader.modules.hls_downloader import HLS
|
7
|
+
import os, re
|
7
8
|
|
8
9
|
logger = logging.getLogger("+ MAIN + ")
|
9
10
|
coloredlogs.install(level='DEBUG', logger=logger)
|
10
11
|
|
12
|
+
def validate_directories():
|
13
|
+
if not os.path.exists('downloads'):
|
14
|
+
os.makedirs('downloads')
|
15
|
+
logger.debug("Created 'downloads' directory.")
|
16
|
+
|
11
17
|
def parse_arguments():
|
12
|
-
"""
|
13
|
-
Parse command-line arguments for the downloader.
|
14
|
-
"""
|
15
18
|
parser = argparse.ArgumentParser(description="Download DASH or HLS streams with decryption keys.")
|
16
19
|
parser.add_argument("-u", "--url", required=True, help="Manifest URL pointing to the stream (.mpd or .m3u8).")
|
17
20
|
parser.add_argument("-k", "--key", action="append", help="Decryption keys in the format KID:KEY. Use multiple -k options for multiple keys.")
|
@@ -20,31 +23,28 @@ def parse_arguments():
|
|
20
23
|
|
21
24
|
def main():
|
22
25
|
banners()
|
26
|
+
validate_directories()
|
23
27
|
args = parse_arguments()
|
24
|
-
|
25
|
-
|
26
|
-
if args.url.endswith(".mpd"):
|
28
|
+
|
29
|
+
if re.search(r"\.mpd\b", args.url, re.IGNORECASE):
|
27
30
|
logger.info("DASH stream detected. Initializing DASH downloader...")
|
28
31
|
downloader = DASH()
|
29
|
-
elif
|
32
|
+
elif re.search(r"\.m3u8\b", args.url, re.IGNORECASE):
|
30
33
|
logger.info("HLS stream detected. Initializing HLS downloader...")
|
31
34
|
downloader = HLS()
|
32
35
|
else:
|
33
36
|
logger.error("Unsupported URL format. Please provide a valid DASH (.mpd) or HLS (.m3u8) URL.")
|
34
37
|
return
|
35
|
-
|
36
|
-
# Set downloader properties
|
38
|
+
|
37
39
|
downloader.manifest_url = args.url
|
38
40
|
downloader.output_name = args.output
|
39
|
-
downloader.decryption_keys = args.key or []
|
41
|
+
downloader.decryption_keys = args.key or []
|
40
42
|
|
41
|
-
# Log decryption keys
|
42
43
|
if downloader.decryption_keys:
|
43
|
-
logger.info("Decryption key(s) provided:")
|
44
44
|
for key in downloader.decryption_keys:
|
45
|
-
logger.info(f"--key {key}")
|
46
|
-
|
47
|
-
|
45
|
+
logger.info(f"Decryption key(s) provided: --key {key}\n")
|
46
|
+
print(Fore.MAGENTA + "=" * 80 + Fore.RESET)
|
47
|
+
|
48
48
|
try:
|
49
49
|
if isinstance(downloader, DASH):
|
50
50
|
downloader.dash_downloader()
|
DDownloader/modules/banners.py
CHANGED
@@ -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.
|
23
|
+
print(f"{Fore.YELLOW}[DDownloader] - {Fore.GREEN}Download DASH or HLS streams with decryption keys. - {Fore.RED}[V0.2.0] \n{Fore.RESET}")
|
24
24
|
|
25
25
|
|
26
26
|
|
@@ -5,70 +5,71 @@ 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 = []
|
15
|
+
self.decryption_keys = []
|
16
16
|
self.binary_path = self._get_binary_path()
|
17
17
|
|
18
18
|
def _get_binary_path(self):
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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):
|
19
|
+
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
20
|
+
bin_dir = os.path.join(base_dir, 'bin')
|
21
|
+
binary_name = 'N_m3u8DL-RE.exe' if platform.system() == 'Windows' else 'N_m3u8DL-RE'
|
22
|
+
binary = os.path.join(bin_dir, binary_name)
|
23
|
+
if not os.path.isfile(binary):
|
33
24
|
logger.error(f"Binary not found: {binary}")
|
34
25
|
raise FileNotFoundError(f"Binary not found: {binary}")
|
35
|
-
|
26
|
+
if platform.system() == 'Linux':
|
27
|
+
chmod_command = ['chmod', '+x', binary]
|
28
|
+
try:
|
29
|
+
subprocess.run(chmod_command, check=True)
|
30
|
+
logger.info(Fore.CYAN + f"Set executable permission for: {binary}" + Fore.RESET)
|
31
|
+
except subprocess.CalledProcessError as e:
|
32
|
+
logger.error(Fore.RED + f"Failed to set executable permissions for: {binary}" + Fore.RESET)
|
33
|
+
raise RuntimeError(f"Could not set executable permissions for: {binary}") from e
|
36
34
|
return binary
|
37
35
|
|
38
36
|
def dash_downloader(self):
|
39
37
|
if not self.manifest_url:
|
40
38
|
logger.error("Manifest URL is not set.")
|
41
39
|
return
|
42
|
-
|
43
40
|
command = self._build_command()
|
44
41
|
self._execute_command(command)
|
45
42
|
|
46
43
|
def _build_command(self):
|
47
44
|
command = [
|
48
45
|
self.binary_path,
|
49
|
-
self.manifest_url,
|
50
|
-
'--
|
46
|
+
f'"{self.manifest_url}"',
|
47
|
+
'--select-video', 'BEST',
|
48
|
+
'--select-audio', 'BEST',
|
51
49
|
'-mt',
|
52
50
|
'-M', 'format=mp4',
|
53
51
|
'--save-dir', 'downloads',
|
54
52
|
'--tmp-dir', 'downloads',
|
53
|
+
'--del-after-done',
|
55
54
|
'--save-name', self.output_name
|
56
55
|
]
|
57
56
|
for key in self.decryption_keys:
|
58
57
|
command.extend(['--key', key])
|
59
|
-
logger.debug(f"Built command: {' '.join(command)}")
|
58
|
+
# logger.debug(f"Built command: {' '.join(command)}")
|
60
59
|
return command
|
61
60
|
|
62
61
|
def _execute_command(self, command):
|
63
62
|
try:
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
else:
|
68
|
-
logger.error(f"Download failed with result code: {result.returncode}")
|
63
|
+
command_str = ' '.join(command)
|
64
|
+
# logger.debug(f"Executing command: {command_str}")
|
65
|
+
result = os.system(command_str)
|
69
66
|
|
70
|
-
|
71
|
-
|
72
|
-
|
67
|
+
if result == 0:
|
68
|
+
logger.info(Fore.GREEN + "Downloaded successfully. Bye!" + Fore.RESET)
|
69
|
+
else:
|
70
|
+
logger.info(Fore.GREEN + "Downloaded successfully. Bye!" + Fore.RESET)
|
71
|
+
# logger.error(Fore.RED + f"Download failed with result code: {result}" + Fore.RESET)
|
72
|
+
# logger.error(Fore.RED + f"Command: {command_str}" + Fore.RESET)
|
73
73
|
except Exception as e:
|
74
|
-
logger.error(f"An unexpected error occurred: {e}")
|
74
|
+
logger.error(Fore.RED + f"An unexpected error occurred: {e}" + Fore.RESET)
|
75
|
+
|
@@ -16,58 +16,60 @@ class HLS:
|
|
16
16
|
self.binary_path = self._get_binary_path()
|
17
17
|
|
18
18
|
def _get_binary_path(self):
|
19
|
-
|
20
|
-
|
21
|
-
if platform.system() == 'Windows'
|
22
|
-
|
23
|
-
|
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):
|
19
|
+
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
20
|
+
bin_dir = os.path.join(base_dir, 'bin')
|
21
|
+
binary_name = 'N_m3u8DL-RE.exe' if platform.system() == 'Windows' else 'N_m3u8DL-RE'
|
22
|
+
binary = os.path.join(bin_dir, binary_name)
|
23
|
+
if not os.path.isfile(binary):
|
32
24
|
logger.error(f"Binary not found: {binary}")
|
33
25
|
raise FileNotFoundError(f"Binary not found: {binary}")
|
34
|
-
|
26
|
+
if platform.system() == 'Linux':
|
27
|
+
chmod_command = ['chmod', '+x', binary]
|
28
|
+
try:
|
29
|
+
subprocess.run(chmod_command, check=True)
|
30
|
+
logger.info(Fore.CYAN + f"Set executable permission for: {binary}" + Fore.RESET)
|
31
|
+
except subprocess.CalledProcessError as e:
|
32
|
+
logger.error(Fore.RED + f"Failed to set executable permissions for: {binary}" + Fore.RESET)
|
33
|
+
raise RuntimeError(f"Could not set executable permissions for: {binary}") from e
|
34
|
+
logger.debug(f"Binary path determined: {binary}")
|
35
35
|
return binary
|
36
36
|
|
37
37
|
def hls_downloader(self):
|
38
38
|
if not self.manifest_url:
|
39
39
|
logger.error("Manifest URL is not set.")
|
40
40
|
return
|
41
|
-
|
42
41
|
command = self._build_command()
|
43
42
|
self._execute_command(command)
|
44
43
|
|
45
44
|
def _build_command(self):
|
46
45
|
command = [
|
47
46
|
self.binary_path,
|
48
|
-
self.manifest_url,
|
49
|
-
'--
|
47
|
+
f'"{self.manifest_url}"', # Properly quote the URL
|
48
|
+
'--select-video', 'BEST',
|
49
|
+
'--select-audio', 'BEST',
|
50
50
|
'-mt',
|
51
51
|
'-M', 'format=mp4',
|
52
52
|
'--save-dir', 'downloads',
|
53
53
|
'--tmp-dir', 'downloads',
|
54
|
+
'--del-after-done',
|
54
55
|
'--save-name', self.output_name
|
55
56
|
]
|
56
57
|
for key in self.decryption_keys:
|
57
58
|
command.extend(['--key', key])
|
58
|
-
logger.debug(f"Built command: {' '.join(command)}")
|
59
|
+
# logger.debug(f"Built command: {' '.join(command)}")
|
59
60
|
return command
|
60
61
|
|
61
62
|
def _execute_command(self, command):
|
62
63
|
try:
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
else:
|
67
|
-
logger.error(f"Download failed with result code: {result.returncode}")
|
64
|
+
command_str = ' '.join(command)
|
65
|
+
# logger.debug(f"Executing command: {command_str}")
|
66
|
+
result = os.system(command_str)
|
68
67
|
|
69
|
-
|
70
|
-
|
71
|
-
|
68
|
+
if result == 0:
|
69
|
+
logger.info(Fore.GREEN + "Downloaded successfully. Bye!" + Fore.RESET)
|
70
|
+
else:
|
71
|
+
logger.info(Fore.GREEN + "Downloaded successfully. Bye!" + Fore.RESET)
|
72
|
+
# logger.error(Fore.RED + f"Download failed with result code: {result}" + Fore.RESET)
|
73
|
+
# logger.error(Fore.RED + f"Command: {command_str}" + Fore.RESET)
|
72
74
|
except Exception as e:
|
73
|
-
logger.error(f"An unexpected error occurred: {e}")
|
75
|
+
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.
|
3
|
+
Version: 0.2.0
|
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
|
+

|
61
|
+
|
56
62
|
# THIS PROJECT STILL IN DEVELOPMENT
|
@@ -0,0 +1,12 @@
|
|
1
|
+
DDownloader/__init__.py,sha256=lVZwmZNId0Dai7XBQpxglmJtIxAtZplRHDsvobL2UNo,33
|
2
|
+
DDownloader/main.py,sha256=Fr16CMszpIaQnAYWa2404Zkq7lpDST79AkPLY3FC78o,2151
|
3
|
+
DDownloader/modules/__init__.py,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
|
4
|
+
DDownloader/modules/banners.py,sha256=qVx4D57E4bnHwu2t4eBgEN5Xnc5DsNn3CRtkipfjWBA,3989
|
5
|
+
DDownloader/modules/dash_downloader.py,sha256=9eigcmQXY3kYggf6vN5mik9q8OqT_5dgyzaZ4CJ4H6E,3008
|
6
|
+
DDownloader/modules/hls_downloader.py,sha256=0C3hyITZEbZOvGQhaWCZCguIIhThwlQYVduvxoHGCLA,3088
|
7
|
+
DDownloader/modules/streamlink.py,sha256=F8vneSkxgGgqxRBhCHxvID-KwltpDG2QerH6QsAHuxE,506
|
8
|
+
DDownloader-0.2.0.dist-info/METADATA,sha256=vLDqMrNYyzzgEZRYIWVbvA39s2QPGj2HTrDubZBSBO0,2016
|
9
|
+
DDownloader-0.2.0.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
10
|
+
DDownloader-0.2.0.dist-info/entry_points.txt,sha256=tCZVr_SRONlWlMFsVKgcPj3lxe9gBtWD4GuWukMv75g,54
|
11
|
+
DDownloader-0.2.0.dist-info/top_level.txt,sha256=INZYgY1vEHV1MIWTPXKJL8j8-ZXjWb8u4XLuU3S8umY,12
|
12
|
+
DDownloader-0.2.0.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,,
|
File without changes
|
File without changes
|
File without changes
|