mirror of
https://cdm-project.com/Decryption-Tools/TPD-Keys.git
synced 2025-04-29 19:24:26 +02:00
v1.31
Added Udemy support, added PSSH parse for CLI
This commit is contained in:
parent
92f8f24429
commit
6b52e6ecf0
@ -1,6 +1,5 @@
|
||||
# Import dependencies
|
||||
import os
|
||||
from sys import exit
|
||||
|
||||
|
||||
# Define api key check
|
||||
|
@ -29,11 +29,15 @@ def web_dl_generic(mpd: str = None, device: str = None, api_key: str = None, rem
|
||||
mp4decrypt_keys, _ = Sites.Generic.decrypt_generic(mpd_url=mpd, wvd=device, license_curl_headers=license_curl.headers)
|
||||
if site == 'rte':
|
||||
mp4decrypt_keys, _ = Sites.RTE.decrypt_rte(mpd_url=mpd, wvd=device, license_curl_headers=license_curl.headers)
|
||||
if site == 'udemy':
|
||||
mp4decrypt_keys, _ = Sites.Udemy.decrypt_udemy(mpd_url=mpd, wvd=device, license_curl_headers=license_curl.headers, license_curl_cookies=license_curl.cookies)
|
||||
if remote:
|
||||
if not site:
|
||||
mp4decrypt_keys, _ = Sites.Generic.decrypt_generic_remotely(api_key=api_key, license_curl_headers=license_curl.headers, mpd_url=mpd)
|
||||
if site == 'rte':
|
||||
mp4decrypt_keys, _ = Sites.RTE.decrypt_rte_remotely(mpd_url=mpd, api_key=api_key, license_curl_headers=license_curl.headers)
|
||||
if site == 'udemy':
|
||||
mp4decrypt_keys, _ = Sites.Udemy.decrypt_udemy_remotely(mpd_url=mpd, api_key=api_key, license_curl_headers=license_curl.headers, license_curl_cookies=license_curl.cookies)
|
||||
|
||||
# Define n_m3u8dl-re download parameters
|
||||
n_m3u8dl_re_download = [
|
||||
|
@ -41,15 +41,15 @@ def start_gui(wvd: str = None, api_key: str = None):
|
||||
[sg.Multiline(key='-COOKIES-', visible=False, expand_x=True, expand_y=True, tooltip=f"Paste cookies dictionary starting with the first curly brace '{{' and ending with the last curly brace '}}'")],
|
||||
[sg.Text('releasePid:', key='-PID_TEXT-', visible=False)],
|
||||
[sg.Input(key='-PID-', visible=False, expand_x=True, expand_y=False)],
|
||||
[sg.Combo(values=['Generic', 'Crunchyroll', 'YouTube', 'RTE'], default_value='Generic', key='-OPTIONS-',
|
||||
[sg.Combo(values=['Generic', 'Crunchyroll', 'YouTube', 'RTE', 'Udemy'], default_value='Generic', key='-OPTIONS-',
|
||||
enable_events=True), sg.Push(), sg.Checkbox(text="Use CDM-Project API", key='-USE_API-')]
|
||||
]
|
||||
|
||||
if wvd is None and api_key is not None:
|
||||
right_frame[8] = [sg.Combo(values=['Generic', 'Crunchyroll', 'YouTube', 'RTE'], default_value='Generic', key='-OPTIONS-',
|
||||
right_frame[8] = [sg.Combo(values=['Generic', 'Crunchyroll', 'YouTube', 'RTE', 'Udemy'], default_value='Generic', key='-OPTIONS-',
|
||||
enable_events=True), sg.Push(), sg.Checkbox(text="Use CDM-Project API", key='-USE_API-', default=True, disabled=True)]
|
||||
if api_key is None and wvd is not None:
|
||||
right_frame[8] = [sg.Combo(values=['Generic', 'Crunchyroll', 'YouTube', 'RTE'], default_value='Generic', key='-OPTIONS-',
|
||||
right_frame[8] = [sg.Combo(values=['Generic', 'Crunchyroll', 'YouTube', 'RTE', 'Udemy'], default_value='Generic', key='-OPTIONS-',
|
||||
enable_events=True), sg.Push(), sg.Checkbox(text="Use CDM-Project API", key='-USE_API-', default=False, disabled=True)]
|
||||
|
||||
right_frame_normal = sg.Col(right_frame, expand_x=True, expand_y=True)
|
||||
@ -65,7 +65,7 @@ def start_gui(wvd: str = None, api_key: str = None):
|
||||
# the event loop
|
||||
while True:
|
||||
if wvd is None and api_key is None:
|
||||
sg.popup(title="TPD-Keys", custom_text="No CDM or API key found!")
|
||||
sg.popup('Error!', 'No CDM or API Key found', custom_text="Close", icon=assets.images.taskbar)
|
||||
break
|
||||
|
||||
event, values = window.read()
|
||||
@ -236,6 +236,46 @@ def start_gui(wvd: str = None, api_key: str = None):
|
||||
if values['-PSSH-'] != '' and values['-LIC_URL-'] != '' and values['-PID-'] == '' and values['-OPTIONS-'] == 'RTE':
|
||||
window['-OUTPUT-'].update(f"No PID provided")
|
||||
|
||||
# Action for Decrypt for Udemy decrypt if fields are filled out
|
||||
if values['-PSSH-'] != '' and values['-OPTIONS-'] == 'Udemy' and values['-LIC_URL-'] != '' and values['-HEADERS-'] != '' and values['-COOKIES-'] != '':
|
||||
if not values['-USE_API-']:
|
||||
try:
|
||||
_, key_out = Sites.Udemy.decrypt_udemy(wvd=wvd, in_pssh=values['-PSSH-'],
|
||||
license_curl_headers=ast.literal_eval(clean_dict(dict=values['-HEADERS-'])),
|
||||
license_curl_cookies=ast.literal_eval(clean_dict(dict=values['-COOKIES-'])))
|
||||
window['-OUTPUT-'].update(f"{key_out}")
|
||||
except Exception as error:
|
||||
window['-OUTPUT-'].update(f"{error}")
|
||||
if values['-USE_API-']:
|
||||
if api_key is None:
|
||||
window['-OUTPUT-'].update(f"No API key")
|
||||
if api_key is not None:
|
||||
try:
|
||||
_, key_out = Sites.Udemy.decrypt_udemy_remotely(api_key=api_key, in_pssh=values['-PSSH-'],
|
||||
license_curl_headers=ast.literal_eval(clean_dict(dict=values['-HEADERS-'])),
|
||||
license_curl_cookies=ast.literal_eval(clean_dict(dict=values['-COOKIES-'])))
|
||||
window['-OUTPUT-'].update(f"{key_out}")
|
||||
except Exception as error:
|
||||
window['-OUTPUT-'].update(f"{error}")
|
||||
|
||||
# Error for no PSSH - Udemy
|
||||
if values['-PSSH-'] == '' and values['-OPTIONS-'] == 'Udemy':
|
||||
window['-OUTPUT-'].update(f"No PSSH provided")
|
||||
|
||||
# Error for no License URL - Udemy
|
||||
if values['-LIC_URL-'] == '' and values['-OPTIONS-'] == 'Udemy':
|
||||
window['-OUTPUT-'].update(f"No License URL provided")
|
||||
|
||||
# Error for no Headers - Udemy
|
||||
if values['-PSSH-'] != '' and values['-LIC_URL-'] != '' and values['-OPTIONS-'] == 'Udemy' and values['-HEADERS-'] == '':
|
||||
window['-OUTPUT-'].update(f"No Headers provided")
|
||||
|
||||
# Error for no Cookies - Udemy
|
||||
if values['-PSSH-'] != '' and values['-LIC_URL-'] != '' and values['-OPTIONS-'] == 'Udemy' and values['-COOKIES-'] == '':
|
||||
window['-OUTPUT-'].update(f"No Cookies provided")
|
||||
|
||||
|
||||
|
||||
|
||||
# Actions for reset button
|
||||
if event == 'Reset':
|
||||
@ -291,6 +331,19 @@ def start_gui(wvd: str = None, api_key: str = None):
|
||||
window['-PID_TEXT-'].update(visible=True)
|
||||
window['-PID-'].update(visible=True, value="")
|
||||
|
||||
# Actions for Udemy selector
|
||||
if event == '-OPTIONS-' and values['-OPTIONS-'] == 'Udemy':
|
||||
window['-PSSH-'].update(value="", disabled=False)
|
||||
window['-LIC_URL-'].update(value="", disabled=False)
|
||||
window['-HEADERS_TEXT-'].update(visible=True)
|
||||
window['-HEADERS-'].update(value="", visible=True)
|
||||
window['-JSON-'].update(value="", visible=False)
|
||||
window['-JSON_TEXT-'].update(visible=False)
|
||||
window['-COOKIES-'].update(value="", visible=True)
|
||||
window['-COOKIES_TEXT-'].update(visible=True)
|
||||
window['-PID_TEXT-'].update(visible=False)
|
||||
window['-PID-'].update(visible=False, value="")
|
||||
|
||||
# Actions for YouTube selector
|
||||
if event == '-OPTIONS-' and values['-OPTIONS-'] == 'YouTube':
|
||||
window['-PSSH-'].update(value="", disabled=True)
|
||||
@ -314,7 +367,7 @@ def start_gui(wvd: str = None, api_key: str = None):
|
||||
if event == 'Source Code':
|
||||
webbrowser.open(url='https://cdm-project.com/Decryption-Tools/TPD-Keys')
|
||||
if event == 'Version':
|
||||
sg.popup('Version 1.3', custom_text='Close', grab_anywhere=True)
|
||||
sg.popup('Version 1.31', custom_text='Close', grab_anywhere=True)
|
||||
|
||||
# 4 - the close
|
||||
window.close()
|
||||
|
@ -4,10 +4,10 @@ from sys import exit
|
||||
|
||||
|
||||
# Define MPD / m3u8 PSSH parser
|
||||
def parse_pssh(manifest_url):
|
||||
def parse_pssh(manifest_url, license_headers: dict = None):
|
||||
manifest = manifest_url
|
||||
try:
|
||||
response = requests.get(manifest)
|
||||
response = requests.get(manifest, headers=license_headers)
|
||||
except:
|
||||
pssh = input("Couldn't retrieve manifest, please input PSSH: ")
|
||||
return pssh
|
||||
|
@ -1,5 +1,4 @@
|
||||
import os
|
||||
from sys import exit
|
||||
|
||||
|
||||
def get_os_specific():
|
||||
|
@ -1,7 +1,6 @@
|
||||
# Import dependencies
|
||||
import os
|
||||
import glob
|
||||
from sys import exit
|
||||
|
||||
|
||||
# Define WVD device check
|
||||
|
@ -20,7 +20,7 @@ def decrypt_crunchyroll(wvd: str = None, license_curl_headers: dict = None, mpd_
|
||||
|
||||
# Try getting pssh via MPD URL if web-dl
|
||||
if mpd_url is not None:
|
||||
input_pssh = Helpers.mpd_parse.parse_pssh(mpd_url)
|
||||
input_pssh = Helpers.mpd_parse.parse_pssh(mpd_url, license_headers=license_curl_headers)
|
||||
if input_pssh is not None:
|
||||
print(f'\nPSSH found: {input_pssh}')
|
||||
else:
|
||||
@ -135,7 +135,7 @@ def decrypt_crunchyroll_remotely(api_key: str = None, license_curl_headers: dict
|
||||
|
||||
# Try getting pssh via MPD URL if web-dl
|
||||
if mpd_url is not None and in_pssh is None:
|
||||
input_pssh = Helpers.mpd_parse.parse_pssh(mpd_url)
|
||||
input_pssh = Helpers.mpd_parse.parse_pssh(mpd_url, license_headers=license_curl_headers)
|
||||
if input_pssh is not None:
|
||||
print(f'\nPSSH found: {input_pssh}')
|
||||
else:
|
||||
@ -191,6 +191,7 @@ def decrypt_crunchyroll_remotely(api_key: str = None, license_curl_headers: dict
|
||||
|
||||
if license.status_code != 200:
|
||||
print(f"An error occurred!\n{license.content}")
|
||||
return None, license.content
|
||||
|
||||
# Retrieve the license message
|
||||
license = license.json()["license"]
|
||||
|
249
Sites/Udemy.py
Normal file
249
Sites/Udemy.py
Normal file
@ -0,0 +1,249 @@
|
||||
# Import dependencies
|
||||
|
||||
from pywidevine import PSSH
|
||||
from pywidevine import Cdm
|
||||
from pywidevine import Device
|
||||
import requests
|
||||
import base64
|
||||
import os
|
||||
import Helpers
|
||||
from sys import exit
|
||||
|
||||
|
||||
# Defining decrypt function for generic services
|
||||
def decrypt_udemy(wvd: str = None, license_curl_headers: dict = None, license_curl_cookies: str = None, mpd_url: str = None,
|
||||
in_pssh: str = None, in_license_url: str = None):
|
||||
|
||||
# Exit if no device
|
||||
if wvd is None:
|
||||
exit(f"No CDM! to use local decryption place a .wvd in {os.getcwd()}/WVDs")
|
||||
|
||||
# Try getting pssh via MPD URL if web-dl
|
||||
if mpd_url is not None:
|
||||
input_pssh = Helpers.mpd_parse.parse_pssh(mpd_url)
|
||||
if input_pssh is not None:
|
||||
print(f'\nPSSH found: {input_pssh}')
|
||||
else:
|
||||
input_pssh = input(f"\nPSSH not found! Input PSSH: ")
|
||||
|
||||
# Ask for PSSH if just keys function
|
||||
if mpd_url is None and in_pssh is None:
|
||||
# Ask for PSSH if web-dl not selected:
|
||||
input_pssh = input(f"\nPSSH: ")
|
||||
|
||||
# prepare pssh
|
||||
if in_pssh is None:
|
||||
try:
|
||||
pssh = PSSH(input_pssh)
|
||||
except Exception as error:
|
||||
print(f'an error occurred!\n{error}')
|
||||
return None, error
|
||||
if in_pssh is not None:
|
||||
try:
|
||||
pssh = PSSH(in_pssh)
|
||||
except Exception as error:
|
||||
print(f'an error occurred!\n{error}')
|
||||
return None, error
|
||||
|
||||
# Ask for license URL
|
||||
if in_license_url is None:
|
||||
license_url = input(f"\nLicense URL: ")
|
||||
if in_license_url is not None:
|
||||
license_url = in_license_url
|
||||
|
||||
# load device
|
||||
device = Device.load(wvd)
|
||||
|
||||
# load CDM from device
|
||||
cdm = Cdm.from_device(device)
|
||||
|
||||
# open CDM session
|
||||
session_id = cdm.open()
|
||||
|
||||
# generate license challenge
|
||||
challenge = cdm.get_license_challenge(session_id, pssh)
|
||||
|
||||
# send license challenge
|
||||
license = requests.post(
|
||||
url=license_url,
|
||||
data=challenge,
|
||||
headers=license_curl_headers,
|
||||
cookies=license_curl_cookies
|
||||
)
|
||||
|
||||
if license.status_code != 200:
|
||||
print(f'An error occurred!\n{license.content}')
|
||||
return license.content
|
||||
|
||||
# Extract license from content
|
||||
license = license.content
|
||||
|
||||
# parse license challenge
|
||||
try:
|
||||
cdm.parse_license(session_id, license)
|
||||
except Exception as error:
|
||||
print(f'an error occurred!\n{error}')
|
||||
return None, error
|
||||
|
||||
# assign variable for returned keys
|
||||
returned_keys = ""
|
||||
for key in cdm.get_keys(session_id):
|
||||
if key.type != "SIGNING":
|
||||
returned_keys += f"{key.kid.hex}:{key.key.hex()}\n"
|
||||
|
||||
# assign variable for mp4decrypt keys
|
||||
mp4decrypt_keys = []
|
||||
for key in cdm.get_keys(session_id):
|
||||
if key.type != "SIGNING":
|
||||
mp4decrypt_keys.append('--key')
|
||||
mp4decrypt_keys.append(f'{key.kid.hex}:{key.key.hex()}')
|
||||
|
||||
# close session, disposes of session data
|
||||
cdm.close(session_id)
|
||||
|
||||
# Cache the keys
|
||||
if in_pssh is None:
|
||||
Helpers.cache_key.cache_keys(pssh=input_pssh, keys=returned_keys)
|
||||
if in_pssh is not None:
|
||||
Helpers.cache_key.cache_keys(pssh=in_pssh, keys=returned_keys)
|
||||
|
||||
# Print out the keys
|
||||
print(f'\nKeys:\n{returned_keys}')
|
||||
|
||||
# Return the keys for future ripper use.
|
||||
return mp4decrypt_keys, returned_keys
|
||||
|
||||
|
||||
# Defining remote decrypt function for generic services
|
||||
def decrypt_udemy_remotely(api_key: str = None, license_curl_headers: dict = None, license_curl_cookies: dict = None, mpd_url: str = None,
|
||||
in_pssh: str = None, in_license_url: str = None):
|
||||
|
||||
# Exit if no API key
|
||||
if api_key is None:
|
||||
exit(f"No API Key! to use remote decryption place an API key in {os.getcwd()}/Config/api-key.txt")
|
||||
|
||||
# Set CDM Project API URL
|
||||
api_url = "https://api.cdm-project.com"
|
||||
|
||||
# Set API device
|
||||
api_device = "CDM"
|
||||
|
||||
# Try getting pssh via MPD URL if web-dl
|
||||
if mpd_url is not None and in_pssh is None:
|
||||
input_pssh = Helpers.mpd_parse.parse_pssh(mpd_url)
|
||||
if input_pssh is not None:
|
||||
print(f'\nPSSH found: {input_pssh}')
|
||||
else:
|
||||
input_pssh = input(f"\nPSSH not found! Input PSSH: ")
|
||||
|
||||
# Ask for PSSH if just keys function
|
||||
if mpd_url is None and in_pssh is None:
|
||||
# Ask for PSSH if web-dl not selected:
|
||||
input_pssh = input(f"\nPSSH: ")
|
||||
|
||||
if in_pssh is not None:
|
||||
input_pssh = in_pssh
|
||||
|
||||
# Ask for license URL
|
||||
if in_license_url is None:
|
||||
input_license_url = input(f"\nLicense URL: ")
|
||||
if in_license_url is not None:
|
||||
input_license_url = in_license_url
|
||||
|
||||
# Set headers for API key
|
||||
api_key_headers = {
|
||||
"X-Secret-Key": api_key
|
||||
}
|
||||
|
||||
# Open CDM session
|
||||
open_session = requests.get(url=f'{api_url}/{api_device}/open', headers=api_key_headers)
|
||||
|
||||
# Error handling
|
||||
if open_session.status_code != 200:
|
||||
print(f"An error occurred!\n{open_session.content}")
|
||||
return None, open_session.content
|
||||
|
||||
# Get the session ID from the open CDM session
|
||||
session_id = open_session.json()["data"]["session_id"]
|
||||
|
||||
# Set JSON required to generate a license challenge
|
||||
generate_challenge_json = {
|
||||
"session_id": session_id,
|
||||
"init_data": input_pssh
|
||||
}
|
||||
|
||||
# Generate the license challenge
|
||||
generate_challenge = requests.post(url=f'{api_url}/{api_device}/get_license_challenge/AUTOMATIC', headers=api_key_headers, json=generate_challenge_json)
|
||||
|
||||
# Error handling
|
||||
if generate_challenge.status_code != 200:
|
||||
print(f"An error occurred!\n{generate_challenge.content}")
|
||||
return None, generate_challenge.content
|
||||
|
||||
# Retrieve the challenge and base64 decode it
|
||||
challenge = base64.b64decode(generate_challenge.json()["data"]["challenge_b64"])
|
||||
|
||||
# Send the challenge to the widevine license server
|
||||
license = requests.post(
|
||||
url=input_license_url,
|
||||
headers=license_curl_headers,
|
||||
cookies=license_curl_cookies,
|
||||
data=challenge
|
||||
)
|
||||
|
||||
if license.status_code != 200:
|
||||
print(f'An error occurred!\n{license.content}')
|
||||
return None, license.content
|
||||
|
||||
# Retrieve the license message
|
||||
license = base64.b64encode(license.content).decode()
|
||||
|
||||
# Set JSON required to parse license message
|
||||
license_message_json = {
|
||||
"session_id": session_id,
|
||||
"license_message": license
|
||||
}
|
||||
|
||||
# Parse the license
|
||||
parse = requests.post(url=f'{api_url}/{api_device}/parse_license', headers=api_key_headers, json=license_message_json)
|
||||
|
||||
# Error handling
|
||||
if parse.status_code != 200:
|
||||
print(f"An error occurred!\n{parse.content}")
|
||||
return None, parse.content
|
||||
|
||||
# Retrieve the keys
|
||||
get_keys = requests.post(url=f'{api_url}/{api_device}/get_keys/ALL',
|
||||
json={"session_id": session_id},
|
||||
headers=api_key_headers)
|
||||
|
||||
# Error handling
|
||||
if get_keys.status_code != 200:
|
||||
print(f"An error occurred!\n{get_keys.content}")
|
||||
return None, get_keys.content
|
||||
|
||||
# Iterate through the keys, ignoring signing key
|
||||
returned_keys = ''
|
||||
for key in get_keys.json()["data"]["keys"]:
|
||||
if not key["type"] == "SIGNING":
|
||||
returned_keys += f"{key['key_id']}:{key['key']}\n"
|
||||
|
||||
# assign variable for mp4decrypt keys
|
||||
mp4decrypt_keys = []
|
||||
for key in get_keys.json()["data"]["keys"]:
|
||||
if not key["type"] == "SIGNING":
|
||||
mp4decrypt_keys.append('--key')
|
||||
mp4decrypt_keys.append(f"{key['key_id']}:{key['key']}")
|
||||
|
||||
# Cache the keys
|
||||
Helpers.cache_key.cache_keys(pssh=input_pssh, keys=returned_keys)
|
||||
|
||||
|
||||
# Print out keys
|
||||
print(f'\nKeys:\n{returned_keys}')
|
||||
|
||||
# Close session
|
||||
requests.get(url=f'{api_url}/{api_device}/close/{session_id}', headers=api_key_headers)
|
||||
|
||||
# Return mp4decrypt keys
|
||||
return mp4decrypt_keys, returned_keys
|
@ -5,3 +5,4 @@ from . import VDOCipher
|
||||
from . import Canal
|
||||
from . import RTE
|
||||
from . import AstroGo
|
||||
from . import Udemy
|
@ -1 +0,0 @@
|
||||
headers = {}
|
29
tpd-keys.py
29
tpd-keys.py
@ -21,8 +21,11 @@ services.add_argument('--crunchyroll', action='store_true', help="Decrypt Crunch
|
||||
services.add_argument('--crunchyroll-remote', action='store_true', help="Decrypt Crunchyroll remotely")
|
||||
services.add_argument('--generic', action='store_true', help="Decrypt generic services")
|
||||
services.add_argument('--generic-remote', action='store_true', help="Decrypt generic services remotely")
|
||||
services.add_argument('--pssh', action='store_true', help="Parse a PSSH from an MPD link.")
|
||||
services.add_argument('--rte', action='store_true', help="Decrypt RTE")
|
||||
services.add_argument('--rte-remote', action='store_true', help="Decrypt RTE remotely")
|
||||
services.add_argument('--udemy', action='store_true', help="Decrypt Udemy")
|
||||
services.add_argument('--udemy-remote', action='store_true', help="Decrypt Udemy remotely")
|
||||
services.add_argument('--youtube', action='store_true', help="Decrypt YouTube")
|
||||
services.add_argument('--youtube-remote', action='store_true', help="Decrypt YouTube remotely")
|
||||
|
||||
@ -72,9 +75,14 @@ elif switches.generic_remote:
|
||||
else:
|
||||
Sites.Generic.decrypt_generic_remotely(api_key=api_key, license_curl_headers=license_curl.headers)
|
||||
|
||||
elif switches.pssh:
|
||||
# Perform action for --pssh
|
||||
mpd = input("MPD URL: ")
|
||||
print(f'PSSH: {Helpers.mpd_parse.parse_pssh(manifest_url=mpd)}')
|
||||
|
||||
|
||||
elif switches.rte:
|
||||
# Perform action for --rte , perform a default action
|
||||
# Perform action for --rte
|
||||
if switches.web_dl:
|
||||
mpd = input("MPD URL: ")
|
||||
file = Helpers.download.web_dl_generic(mpd=mpd, device=device, site='rte')
|
||||
@ -92,6 +100,25 @@ elif switches.rte_remote:
|
||||
else:
|
||||
Sites.RTE.decrypt_rte_remotely(api_key=api_key)
|
||||
|
||||
elif switches.udemy:
|
||||
# Perform action for --udemy
|
||||
if switches.web_dl:
|
||||
mpd = input("MPD URL: ")
|
||||
file = Helpers.download.web_dl_generic(mpd=mpd, device=device, site='udemy')
|
||||
print(f'Saved at {file[0]}')
|
||||
else:
|
||||
Sites.Udemy.decrypt_udemy(wvd=device, license_curl_headers=license_curl.headers, license_curl_cookies=license_curl.cookies)
|
||||
|
||||
|
||||
elif switches.udemy_remote:
|
||||
# Perform action for --udemy-remote
|
||||
if switches.web_dl:
|
||||
mpd = input("MPD URL: ")
|
||||
file = Helpers.download.web_dl_generic(mpd=mpd, api_key=api_key, remote=True, site='udemy')
|
||||
print(f'Saved at {file[0]}')
|
||||
else:
|
||||
Sites.Udemy.decrypt_udemy_remotely(api_key=api_key, license_curl_headers=license_curl.headers, license_curl_cookies=license_curl.cookies)
|
||||
|
||||
|
||||
elif switches.youtube:
|
||||
# Perform action for --YouTube
|
||||
|
Loading…
x
Reference in New Issue
Block a user