From 3eac4d7706cea159fcb9edff60ede6676a45a6ae Mon Sep 17 00:00:00 2001 From: CDM-Project Date: Mon, 16 Sep 2024 13:28:01 -0400 Subject: [PATCH] v3.00-alpha --- .gitignore | 1 + CDRM-Keys.py | 8 + CDRM_Modules/CDM_Selector.py | 27 +++ CDRM_Modules/CDRM_Keys_API.py | 321 +++++++++++++++++++++++++++++++++ CDRM_Modules/Decrypt.py | 150 +++++++++++++++ CDRM_Modules/Main_Menu.py | 23 +++ CDRM_Modules/Startup_Checks.py | 218 ++++++++++++++++++++++ CDRM_Modules/__init__.py | 5 + requirements.txt | 8 + 9 files changed, 761 insertions(+) create mode 100644 .gitignore create mode 100644 CDRM-Keys.py create mode 100644 CDRM_Modules/CDM_Selector.py create mode 100644 CDRM_Modules/CDRM_Keys_API.py create mode 100644 CDRM_Modules/Decrypt.py create mode 100644 CDRM_Modules/Main_Menu.py create mode 100644 CDRM_Modules/Startup_Checks.py create mode 100644 CDRM_Modules/__init__.py create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7e99e36 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.pyc \ No newline at end of file diff --git a/CDRM-Keys.py b/CDRM-Keys.py new file mode 100644 index 0000000..d64268b --- /dev/null +++ b/CDRM-Keys.py @@ -0,0 +1,8 @@ +# Import dependencies +import CDRM_Modules + +# Run startup checks +CDRM_Modules.Startup_Checks.run_startup_checks(startup=True) + +# Run main menu +CDRM_Modules.Main_Menu.main_menu() diff --git a/CDRM_Modules/CDM_Selector.py b/CDRM_Modules/CDM_Selector.py new file mode 100644 index 0000000..12510d2 --- /dev/null +++ b/CDRM_Modules/CDM_Selector.py @@ -0,0 +1,27 @@ +#Import dependencies +from distutils.command.config import config + +import yaml +from InquirerPy import inquirer +import os + +# Define function to choose local CDM +def select_local_cdm(): + files_in_wvds_folder = os.listdir(f'{os.getcwd()}/WVDs') + wvds = [file for file in files_in_wvds_folder if file.endswith('.wvd')] + choice = inquirer.select( + message=f'\nChoose the WVD you wish to use:', + choices=wvds, + ).execute() + return choice + +# Define function to choose remote CDM +def select_remote_cdm(): + with open(f'{os.getcwd()}/Config.yaml', 'r') as ymlfile: + config = yaml.safe_load(ymlfile) + remote_cdms = [remotecdm for remotecdm in config['Remote_CDMs']] + choice = inquirer.select( + message=f'\nChoose the Remote CDM you wish to use:', + choices=remote_cdms, + ).execute() + return choice \ No newline at end of file diff --git a/CDRM_Modules/CDRM_Keys_API.py b/CDRM_Modules/CDRM_Keys_API.py new file mode 100644 index 0000000..07c8c20 --- /dev/null +++ b/CDRM_Modules/CDRM_Keys_API.py @@ -0,0 +1,321 @@ +# Import dependencies + +import os + +import yaml +from colorama import Fore +from InquirerPy import inquirer +from flask import Flask, request, jsonify, current_app, Response +import base64 +import sys +from pathlib import Path +from typing import Any, Optional, Union +from google.protobuf.message import DecodeError +from pywidevine.pssh import PSSH +from pywidevine import __version__, Device +from pywidevine.cdm import Cdm +from pywidevine.exceptions import (InvalidContext, InvalidInitData, InvalidLicenseMessage, InvalidLicenseType, InvalidSession, SignatureMismatch, TooManySessions) + + +# Define a function to choose a WVD +def choose_wvd(): + try: + files_in_wvds_folder = os.listdir(f'{os.getcwd()}/WVDs') + wvds = [file for file in files_in_wvds_folder if file.endswith('.wvd')] + choice = inquirer.select( + message=f'\nChoose the WVD you wish to serve:', + choices=wvds, + ).execute() + return choice + except Exception as error: + print(f'{Fore.RED}An error occurred!\n\n{error}') + exit(1) + + +# Define function to start flask app +def start_cdrm_keys_api(): + choice = choose_wvd() + + app = Flask("CDRM-Keys API") + + @app.route('/remote_cdm', methods=['HEAD', 'GET']) + def remote_cdm(): + if request.method == 'HEAD': + response = Response(status=200) + response.headers.update({ + 'Server': f'https://github.com/devine-dl/pywidevine serve v{__version__}' + }) + return response + elif request.method == 'GET': + with open(f'{os.getcwd()}/Config.yaml', 'r') as file: + config = yaml.safe_load(file) + device = Device.load(f'{os.getcwd()}/WVDs/{choice}') + remote_cdm_details = { + 'name': 'TPD-Keys-API', + 'device_type': device.type.name, + 'system_id': device.system_id, + 'security_level': device.security_level, + 'host': f'{config["Remote_CDM_API_FQDN"]}/remote_cdm', + 'secret': 'CDRM-Keys', + } + return jsonify(remote_cdm_details) + else: + return jsonify({"message": "Method not allowed"}) + + @app.route('/remote_cdm//open', methods=['GET']) + def device_open(device): + if request.method == 'GET': + device = Device.load(f'{os.getcwd()}/WVDs/{choice}') + cdm = current_app.config['cdm'] = Cdm.from_device(device) + try: + session_id = cdm.open() + except TooManySessions as error: + return jsonify({ + 'status': 400, + 'message': str(error) + }) + return jsonify({ + 'status': 200, + 'message': 'Success', + 'data': { + 'session_id': session_id.hex(), + 'device': { + 'system_id': cdm.system_id, + 'security_level': cdm.security_level + } + } + }) + + else: + return jsonify({"message": "Method not allowed"}) + + @app.route('/remote_cdm//close/', methods=['GET']) + def device_close(device, session_id): + if request.method == 'GET': + cdm = current_app.config['cdm'] + session_id = bytes.fromhex(session_id) + try: + cdm.close(session_id) + except InvalidSession: + return jsonify({ + 'status': 400, + 'message': f'Invalid Session ID "{session_id.hex()}", it may have expired.' + }) + return jsonify({ + 'status': 200, + 'message': f'Successfully closed Session "{session_id.hex()}"', + }) + else: + return jsonify({"message": "Method not allowed"}) + + @app.route('/remote_cdm//set_service_certificate', methods=['POST']) + def set_service_certificate(device): + if request.method == 'POST': + body = request.get_json() + for required_field in ("session_id", "certificate"): + if required_field == "certificate": + has_field = required_field in body + else: + has_field = body.get(required_field) + if not has_field: + return jsonify({ + 'status': 400, + 'message': f'Missing required field "{required_field}" in JSON body' + }) + session_id = bytes.fromhex(body['session_id']) + cdm = current_app.config['cdm'] + certificate = body.get('certificate') + try: + provider_id = cdm.set_service_certificate(session_id, certificate) + except InvalidSession: + return jsonify({ + 'status': 400, + 'message': f'Invalid Session ID "{session_id.hex()}", it may have expired.' + }) + except DecodeError as error: + return jsonify({ + 'status': 400, + 'message': f'Invalid Service Certificate, {error}' + }) + except SignatureMismatch: + return jsonify({ + 'status': 400, + 'message': f'Signature Validation failed on the Service Certificate, rejecting.' + }) + return jsonify({ + 'status': 200, + 'message': f'Successfully {["set", "unset"][not certificate]} the Service Certificate.', + 'data': { + 'provider_id': provider_id, + } + }) + else: + return jsonify({"message": "Method not allowed"}) + + @app.route('/remote_cdm//get_service_certificate', methods=['POST']) + def get_service_certificate(device): + if request.method == 'POST': + body = request.get_json() + for required_field in ("session_id",): + if not body.get(required_field): + return jsonify({ + 'status': 400, + 'message': f'Missing required field "{required_field}" in JSON body' + }) + session_id = bytes.fromhex(body['session_id']) + cdm = current_app.config['cdm'] + try: + service_certificate = cdm.get_service_certificate(session_id) + except InvalidSession: + return jsonify({ + 'status': 400, + 'message': f'Invalid Session ID "{session_id.hex()}", it may have expired.' + }) + if service_certificate: + service_certificate_b64 = base64.b64encode(service_certificate.SerializeToString()).decode() + else: + service_certificate_b64 = None + return jsonify({ + 'status': 200, + 'message': 'Successfully got the Service Certificate.', + 'data': { + 'service_certificate': service_certificate_b64, + } + }) + else: + return jsonify({"message": "Method not allowed"}) + + @app.route('/remote_cdm//get_license_challenge/', methods=['POST']) + def get_license_challenge(device, license_type): + if request.method == 'POST': + body = request.get_json() + for required_field in ("session_id", "init_data"): + if not body.get(required_field): + return jsonify({ + 'status': 400, + 'message': f'Missing required field "{required_field}" in JSON body' + }) + session_id = bytes.fromhex(body['session_id']) + privacy_mode = body.get('privacy_mode', True) + cdm = current_app.config['cdm'] + + if not cdm.get_service_certificate(session_id): + privacy_mode = False + + init_data = PSSH(body['init_data']) + try: + license_request = cdm.get_license_challenge( + session_id=session_id, + pssh=init_data, + license_type=license_type, + privacy_mode=privacy_mode + ) + except InvalidSession: + return jsonify({ + 'status': 400, + 'message': f'Invalid Session ID "{session_id.hex()}", it may have expired.' + }) + except InvalidInitData as error: + return jsonify({ + 'status': 400, + 'message': f'Invalid Init Data, {error}' + }) + except InvalidLicenseType: + return jsonify({ + 'status': 400, + 'message': f'Invalid License Type "{license_type}"' + }) + return jsonify({ + 'status': 200, + 'message': 'Success', + 'data': { + 'challenge_b64': base64.b64encode(license_request).decode() + } + }) + else: + return jsonify({"message": "Method not allowed"}) + + @app.route('/remote_cdm//parse_license', methods=['POST']) + def parse_license(device): + if request.method == 'POST': + body = request.get_json() + for required_field in ("session_id", "license_message"): + if not body.get(required_field): + return jsonify({ + 'status': 400, + 'message': f'Missing required field "{required_field}" in JSON body' + }) + session_id = bytes.fromhex(body['session_id']) + cdm = current_app.config['cdm'] + try: + cdm.parse_license(session_id, body['license_message']) + except InvalidSession: + return jsonify({ + 'status': 400, + 'message': f'Invalid Session ID "{session_id.hex()}", it may have expired.' + }) + except InvalidLicenseMessage as error: + return jsonify({ + 'status': 400, + 'message': f'Invalid License Message, {error}' + }) + except InvalidContext as error: + return jsonify({ + 'status': 400, + 'message': f'Invalid Context "{error}"' + }) + except SignatureMismatch: + return jsonify({ + 'status': 400, + 'message': f'Signature Validation failed on the License Message, rejecting.' + }) + return jsonify({ + 'status': 200, + 'message': 'Successfully parsed and loaded the Keys from the License message.', + }) + + @app.route('/remote_cdm//get_keys/', methods=['POST']) + def get_keys(device, key_type): + if request.method == 'POST': + body = request.get_json() + for required_field in ("session_id",): + if not body.get(required_field): + return jsonify({ + 'status': 400, + 'message': f'Missing required field "{required_field}" in JSON body' + }) + session_id = bytes.fromhex(body['session_id']) + key_type = str(key_type) + if key_type == 'ALL': + key_type = None + cdm = current_app.config['cdm'] + try: + keys = cdm.get_keys(session_id, key_type) + except InvalidSession: + return jsonify({ + 'status': 400, + 'message': f'Invalid Session ID "{session_id.hex()}", it may have expired.' + }) + except ValueError as error: + return jsonify({ + 'status': 400, + 'message': f'They Key Type value "{key_type}" is invalid, {error}' + }) + keys_json = [ + { + 'key_id': key.kid.hex, + 'key': key.key.hex(), + 'type': key.type, + 'permissions': key.permissions, + } + for key in keys + ] + return jsonify({ + 'status': 200, + 'message': 'Success', + 'data': { + 'keys': keys_json + } + }) + + app.run() \ No newline at end of file diff --git a/CDRM_Modules/Decrypt.py b/CDRM_Modules/Decrypt.py new file mode 100644 index 0000000..c06c9c8 --- /dev/null +++ b/CDRM_Modules/Decrypt.py @@ -0,0 +1,150 @@ +import httpx +from pywidevine.cdm import Cdm +from pywidevine.device import Device +from pywidevine.pssh import PSSH +from pywidevine.remotecdm import RemoteCdm +import yaml +import os +from colorama import Fore +from . import CDM_Selector + +def load_license_curl(): + class LicenseCURL: + def __init__(self): + self.headers = headers + self.cookies = cookies + + try: + with open(f'{os.getcwd()}/License_cURL.py', 'r') as file: + local_namespace = {} + exec(file.read(), globals(), local_namespace) + except Exception as error: + print(f'{Fore.RED}An error occurred.\n\n{error}') + + if 'headers' in local_namespace: + headers = local_namespace['headers'] + else: + headers = None + if 'cookies' in local_namespace: + cookies = local_namespace['cookies'] + else: + cookies = None + return LicenseCURL() + +def decrypt_local_cdm(): + cdm_choice = CDM_Selector.select_local_cdm() + try: + device = Device.load(f'{os.getcwd()}/WVDs/{cdm_choice}') + except Exception as error: + print(f'{Fore.RED}An error occurred.\n\n{error}') + exit(1) + try: + cdm = Cdm.from_device(device) + except Exception as error: + print(f'{Fore.RED}An error occurred.\n\n{error}') + exit(1) + try: + session_id = cdm.open() + except Exception as error: + print(f'{Fore.RED}An error occurred.\n\n{error}') + exit(1) + try: + pssh = PSSH(input("\nPSSH: ")) + except Exception as error: + print(f'{Fore.RED}An error occurred.\n\n{error}') + exit(1) + user_license_url = input("License URL: ") + print() + license_curl = load_license_curl() + try: + service_certificate = httpx.post( + url=user_license_url, + headers=license_curl.headers, + cookies=license_curl.cookies, + data=cdm.service_certificate_challenge + ).content + cdm.set_service_certificate(session_id=session_id, certificate=service_certificate) + except Exception as error: + print(f'{Fore.RED}An error occurred.\n\n{error}\n\nTrying without service certificate') + challenge = cdm.get_license_challenge(session_id=session_id, pssh=pssh) + try: + license_response = httpx.post( + url=user_license_url, + headers=license_curl.headers, + cookies=license_curl.cookies, + data=challenge + ).content + except Exception as error: + print(f'{Fore.RED}An error occurred.\n\n{error}') + exit(1) + try: + cdm.parse_license(session_id=session_id, license_message=license_response) + except Exception as error: + print(f'{Fore.RED}An error occurred.\n\n{error}') + exit(1) + for key in cdm.get_keys(session_id): + if key.type == 'CONTENT': + print(f"{key.kid.hex}:{key.key.hex()}") + + return + +def decrypt_remote_cdm(): + remote_cdm_choice = CDM_Selector.select_remote_cdm() + try: + with open(f'{os.getcwd()}/Config.yaml', 'r') as ymlfile: + config = yaml.safe_load(ymlfile) + cdm = RemoteCdm( + device_type=config['Remote_CDMs'][remote_cdm_choice]['device_type'], + system_id=config['Remote_CDMs'][remote_cdm_choice]['system_id'], + security_level=config['Remote_CDMs'][remote_cdm_choice]['security_level'], + host=config['Remote_CDMs'][remote_cdm_choice]['host'], + secret=config['Remote_CDMs'][remote_cdm_choice]['secret'], + device_name=config['Remote_CDMs'][remote_cdm_choice]['name'], + ) + except Exception as error: + print(f'{Fore.RED}An error occurred.\n\n{error}') + exit(1) + try: + session_id = cdm.open() + except Exception as error: + print(f'{Fore.RED}An error occurred.\n\n{error}') + exit(1) + try: + pssh = PSSH(input("\nPSSH: ")) + except Exception as error: + print(f'{Fore.RED}An error occurred.\n\n{error}') + exit(1) + user_license_url = input("License URL: ") + print() + license_curl = load_license_curl() + try: + service_certificate = httpx.post( + url=user_license_url, + headers=license_curl.headers, + cookies=license_curl.cookies, + data=cdm.service_certificate_challenge + ).content + cdm.set_service_certificate(session_id=session_id, certificate=service_certificate) + except Exception as error: + print(f'{Fore.RED}An error occurred.\n\n{error}\n\nTrying without service certificate') + challenge = cdm.get_license_challenge(session_id=session_id, pssh=pssh) + try: + license_response = httpx.post( + url=user_license_url, + headers=license_curl.headers, + cookies=license_curl.cookies, + data=challenge + ).content + except Exception as error: + print(f'{Fore.RED}An error occurred.\n\n{error}') + exit(1) + try: + cdm.parse_license(session_id=session_id, license_message=license_response) + except Exception as error: + print(f'{Fore.RED}An error occurred.\n\n{error}') + exit(1) + for key in cdm.get_keys(session_id): + if key.type == 'CONTENT': + print(f"{key.kid.hex}:{key.key.hex()}") + + return \ No newline at end of file diff --git a/CDRM_Modules/Main_Menu.py b/CDRM_Modules/Main_Menu.py new file mode 100644 index 0000000..535ec76 --- /dev/null +++ b/CDRM_Modules/Main_Menu.py @@ -0,0 +1,23 @@ +from InquirerPy import inquirer +from . import Startup_Checks +from . import Decrypt +from . import CDRM_Keys_API + +def main_menu(): + local_cdms, remote_cdms = Startup_Checks.check_for_wvds() + choices = [] + if local_cdms: + choices.append('Local CDM Decryption') + choices.append('CDRM-Keys API Mode') + if remote_cdms: + choices.append('Remote CDM Decryption') + choice = inquirer.select( + message='CDRM-Keys:', + choices=choices, + ).execute() + if choice == 'Local CDM Decryption': + Decrypt.decrypt_local_cdm() + if choice == 'Remote CDM Decryption': + Decrypt.decrypt_remote_cdm() + if choice == 'CDRM-Keys API Mode': + CDRM_Keys_API.start_cdrm_keys_api() \ No newline at end of file diff --git a/CDRM_Modules/Startup_Checks.py b/CDRM_Modules/Startup_Checks.py new file mode 100644 index 0000000..6002e80 --- /dev/null +++ b/CDRM_Modules/Startup_Checks.py @@ -0,0 +1,218 @@ +# Import dependencies +import os +import time +import requests +import yaml +import sqlite3 +from colorama import Fore +import sys +import json + + +# Define a function to check if script is running from a virtual environment +def check_is_venv(): + if hasattr(sys, 'real_prefix') or (hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix): + print(f'{Fore.GREEN}Virtual Environment [✓]') + return + else: + print(f'{Fore.RED}Not running Virtual Environment!\n\nPlease run CDRM-Keys in a Python Virtual Environment') + exit(1) + + +# Define a check to see if config.yaml exists +def check_if_config_exists(): + if os.path.isfile(f'{os.getcwd()}/Config.yaml'): + print(f'{Fore.GREEN}Config.yaml [✓]') + return + else: + initial_config_data = { + 'First_Run': 'True', + 'Remote_CDM_API_FQDN': 'http://127.0.0.1:5000' + } + create_input = input(f'\n{Fore.YELLOW}Config.yaml not found, would you like to create a Config.yaml? [(Y)es/(N)o]: ') + if create_input: + if create_input[0].lower() == 'y': + try: + with open(f'{os.getcwd()}/Config.yaml', 'w') as file: + yaml.dump(initial_config_data, file) + print(f'{Fore.GREEN}Config.yaml created!') + except Exception as error: + print(f'{Fore.RED}An error occurred!\n\n{error}') + exit(1) + return + else: + print(f'{Fore.YELLOW}Exiting.') + exit(1) + + +# Define a function to see if keys.db exists +def check_if_keys_db_exists(): + if os.path.isfile(f'{os.getcwd()}/Keys.db'): + print(f'{Fore.GREEN}Keys.db [✓]') + return + else: + try: + create_input = input(f'\n{Fore.YELLOW}Keys.db not found, would you like to create a Keys.db? [(Y)es/(N)o]: ') + if create_input: + if create_input[0].lower() == 'y': + keys_db_connection = sqlite3.connect(f'{os.getcwd()}/Keys.db') + keys_db_cursor = keys_db_connection.cursor() + keys_db_cursor.execute(''' + CREATE TABLE IF NOT EXISTS keys ( + MPD TEXT, + PSSH TEXT, + KID TEXT, + KEY TEXT, + License_URL TEXT, + Headers TEXT, + Cookies TEXT, + Data TEXT + ) + ''') + keys_db_connection.commit() + keys_db_connection.close() + print(f'{Fore.GREEN}Keys.db created!') + return + else: + print(f'{Fore.YELLOW}Exiting.') + exit(1) + except Exception as error: + print(f'{Fore.RED}An error occurred!\n\n{error}') + exit(1) + + +# Define a function to check if License_cURL.py exists +def check_if_license_curl_exists(): + if os.path.isfile(f'{os.getcwd()}/License_cURL.py'): + print(f'{Fore.GREEN}License_cURL.py [✓]') + return + else: + try: + create_input = input(f'\n{Fore.YELLOW}License_cURL.py not found, would you like to create a License_cURL.py? [(Y)es/(N)o]: ') + if create_input: + if create_input[0].lower() == 'y': + headers = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:130.0) Gecko/20100101 Firefox/130.0', + 'Accept': '*/*', + 'Accept-Language': 'en-US,en;q=0.5', + # 'Accept-Encoding': 'gzip, deflate, br, zstd', + 'Origin': 'https://bitmovin.com', + 'DNT': '1', + 'Sec-GPC': '1', + 'Connection': 'keep-alive', + 'Referer': 'https://bitmovin.com/', + 'Sec-Fetch-Dest': 'empty', + 'Sec-Fetch-Mode': 'cors', + 'Sec-Fetch-Site': 'cross-site', + 'Content-Type': 'application/x-www-form-urlencoded', + } + with open(f'{os.getcwd()}/License_cURL.py', 'w') as file: + file.write("# This file contains a sample license URL curl from BitMovin (https://bitmovin.com/demos/drm)\n") + file.write("headers = ") + json.dump(headers, file, indent=4) + file.write("\n") + print(f'{Fore.GREEN}License_cURL.py created!') + return + else: + print(f'{Fore.YELLOW}Exiting.') + exit(1) + except Exception as error: + print(f'{Fore.RED}An error occurred!\n\n{error}') + exit(1) + + +# Define a function to check if WVDs folder exists +def check_if_wvd_folder_exists(): + if os.path.isdir(f'{os.getcwd()}/WVDs'): + print(f'{Fore.GREEN}WVDs Folder [✓]') + return + else: + create_input = input(f'\n{Fore.YELLOW}WVDs folder not found, would you like to create a WVDs folder? [(Y)es/(N)o]: ') + try: + if create_input: + if create_input[0].lower() == 'y': + os.mkdir(f'{os.getcwd()}/WVDs') + print(f'{Fore.GREEN}WVDs folder created!') + return + except Exception as error: + print(f'{Fore.RED}An error occurred!\n\n{error}') + exit(1) + + +# Define a function to check for CDMs +def check_for_wvds(startup: bool = False): + try: + cmds_exist = False + remote_cdms_exist = False + files_in_wvds_folder = os.listdir(f'{os.getcwd()}/WVDs') + wvds = [file for file in files_in_wvds_folder if file.endswith('.wvd')] + with open('Config.yaml', 'r') as ymlfile: + config = yaml.safe_load(ymlfile) + if wvds: + if startup: + print(f'{Fore.GREEN}WVDs [✓]') + cmds_exist = True + if 'Remote_CDMs' in config: + if startup: + print(f'{Fore.GREEN}Remote CDMs [✓]') + remote_cdms_exist = True + elif 'Remote_CDMs' not in config and startup: + if config['First_Run'] == 'True': + create_input = input( + f'\n{Fore.YELLOW}No remote CDM found, would you like to use CDRM-Project\'s? [(Y)es/(N)o]: ') + if create_input: + if create_input[0].lower() == 'y': + try: + cdrm_project_remote_cdm_info = requests.get( + url='https://remote-cdm.cdrm-project.com/remote_cdm' + ).json() + device_details = { + 'Remote_CDMs': { + 'CDRM_Project_CDM_API': { + 'name': cdrm_project_remote_cdm_info['name'], + 'host': cdrm_project_remote_cdm_info['host'], + 'device_type': cdrm_project_remote_cdm_info['device_type'], + 'security_level': cdrm_project_remote_cdm_info['security_level'], + 'system_id': cdrm_project_remote_cdm_info['system_id'], + 'secret': cdrm_project_remote_cdm_info['secret'] + } + } + } + with open(f'{os.getcwd()}/Config.yaml', 'r') as ymlfile: + config = yaml.safe_load(ymlfile) + config.update(device_details) + with open(f'{os.getcwd()}/Config.yaml', 'w') as ymlfile: + yaml.safe_dump(config, ymlfile) + print(f'{Fore.GREEN}CDRM-Project RemoteCDM added') + remote_cdms_exist = True + except Exception as error: + print(f'{Fore.RED}An error occurred!\n\n{error}') + + if not cmds_exist and not remote_cdms_exist: + print(f'{Fore.YELLOW}No WVDs available! Exiting.') + exit(1) + return cmds_exist, remote_cdms_exist + except Exception as error: + print(f'{Fore.RED}An error occurred!\n\n{error}') + exit(1) + + +# Define a function to run all startup checks +def run_startup_checks(startup: bool = False): + check_is_venv() + check_if_license_curl_exists() + check_if_config_exists() + check_if_keys_db_exists() + check_if_wvd_folder_exists() + check_for_wvds(startup=startup) + time.sleep(1) + with open(f'{os.getcwd()}/Config.yaml', 'r') as ymlfile: + config = yaml.safe_load(ymlfile) + config['First_Run'] = 'False' + with open(f'{os.getcwd()}/Config.yaml', 'w') as ymlfile: + yaml.safe_dump(config, ymlfile) + if os.name == 'nt': # For Windows + os.system('cls') + else: # For Unix-based systems (Linux, macOS) + os.system('clear') + return \ No newline at end of file diff --git a/CDRM_Modules/__init__.py b/CDRM_Modules/__init__.py new file mode 100644 index 0000000..c71062e --- /dev/null +++ b/CDRM_Modules/__init__.py @@ -0,0 +1,5 @@ +from . import Startup_Checks +from . import CDRM_Keys_API +from . import Decrypt +from . import CDM_Selector +from . import Main_Menu \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..66c5afc --- /dev/null +++ b/requirements.txt @@ -0,0 +1,8 @@ +pyyaml +colorama +pywidevine +flask +google +inquirerpy +requests +httpx \ No newline at end of file