v1
Initial upload
This commit is contained in:
@@ -1,3 +0,0 @@
|
||||
# CDRM-Extension-API
|
||||
|
||||
Open source API back end for the popular Google Chrome extension written by Zane.
|
||||
12
cache_keys.py
Normal file
12
cache_keys.py
Normal file
@@ -0,0 +1,12 @@
|
||||
# Import dependencies
|
||||
import sqlite3
|
||||
import os
|
||||
|
||||
|
||||
# Define cache function
|
||||
def cache_keys(pssh: str, keys: str, mpd: str):
|
||||
dbconnection = sqlite3.connect(f"{os.getcwd()}/keys/database.db")
|
||||
dbcursor = dbconnection.cursor()
|
||||
dbcursor.execute("INSERT or REPLACE INTO database VALUES (?, ?, ?)", (pssh, keys, mpd))
|
||||
dbconnection.commit()
|
||||
dbconnection.close()
|
||||
17
database_check.py
Normal file
17
database_check.py
Normal file
@@ -0,0 +1,17 @@
|
||||
# Import dependencies
|
||||
import os
|
||||
import sqlite3
|
||||
|
||||
|
||||
# Check to see if the database already exists, if not create a keys folder, and create the database.
|
||||
def database_check():
|
||||
# Check to see if the "keys" directory exists, if not creates it
|
||||
if "keys" not in os.listdir(os.getcwd()):
|
||||
os.makedirs('keys')
|
||||
|
||||
# Check to see if a database exists in keys directory, if not create it
|
||||
if not os.path.isfile(f"{os.getcwd()}/keys/database.db"):
|
||||
dbconnection = sqlite3.connect(f"{os.getcwd()}/keys/database.db")
|
||||
dbcursor = dbconnection.cursor()
|
||||
dbcursor.execute('CREATE TABLE IF NOT EXISTS "DATABASE" ( "pssh" TEXT, "keys" TEXT, "mpd" TEXT, PRIMARY KEY("pssh") )')
|
||||
dbconnection.close()
|
||||
79
decrypt.py
Normal file
79
decrypt.py
Normal file
@@ -0,0 +1,79 @@
|
||||
# import dependencies
|
||||
import os
|
||||
from pywidevine import PSSH
|
||||
from pywidevine import Cdm
|
||||
from pywidevine import Device
|
||||
import requests
|
||||
import glob
|
||||
from cache_keys import cache_keys
|
||||
|
||||
# Get the current working directory
|
||||
main_directory = os.getcwd()
|
||||
|
||||
# Making sure a .wvd file exists and using that as the extracted device
|
||||
try:
|
||||
extracted_device = glob.glob(f'{main_directory}/*.wvd')[0]
|
||||
except:
|
||||
extracted_cdm = None
|
||||
print(f"Please place a WVD in {main_directory}/")
|
||||
|
||||
|
||||
# Defining decrypt function
|
||||
def decrypt_content(in_pssh: str = None, license_url: str = None, headers: dict = None, mpd: str = None) -> list:
|
||||
|
||||
# prepare pssh
|
||||
pssh = PSSH(in_pssh)
|
||||
|
||||
# load device
|
||||
device = Device.load(extracted_device)
|
||||
|
||||
# load CDM from device
|
||||
cdm = Cdm.from_device(device)
|
||||
|
||||
# open CDM session
|
||||
session_id = cdm.open()
|
||||
|
||||
# Generate the challenge
|
||||
challenge = cdm.get_license_challenge(session_id, pssh)
|
||||
|
||||
# send license challenge
|
||||
license = requests.post(
|
||||
url=license_url,
|
||||
headers=headers,
|
||||
data=challenge
|
||||
)
|
||||
|
||||
# Parse the license if it comes back in plain bytes
|
||||
try:
|
||||
cdm.parse_license(session_id, license.content)
|
||||
except:
|
||||
# Exception, try to find by regex via json
|
||||
try:
|
||||
cdm.parse_license(session_id, license.json()['license'])
|
||||
except:
|
||||
try:
|
||||
cdm.parse_license(session_id, license.json()['licenseData'])
|
||||
except Exception as error:
|
||||
return [error]
|
||||
|
||||
|
||||
# Assign variable for caching keys
|
||||
cached_keys = ""
|
||||
|
||||
# assign variable for returned keys
|
||||
returned_keys = []
|
||||
|
||||
|
||||
for key in cdm.get_keys(session_id):
|
||||
if key.type != "SIGNING":
|
||||
returned_keys.append(f"--key {key.kid.hex}:{key.key.hex()}")
|
||||
cached_keys += f"{key.kid.hex}:{key.key.hex()}\n"
|
||||
|
||||
# Cache the keys
|
||||
cache_keys(pssh=in_pssh, keys=cached_keys, mpd=mpd)
|
||||
|
||||
# close session, disposes of session data
|
||||
cdm.close(session_id)
|
||||
|
||||
# Return the keys
|
||||
return returned_keys
|
||||
53
main.py
Normal file
53
main.py
Normal file
@@ -0,0 +1,53 @@
|
||||
from decrypt import decrypt_content
|
||||
import json
|
||||
from flask import Flask, request
|
||||
from database_check import database_check
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
database_check()
|
||||
|
||||
|
||||
@app.route('/', methods=['POST'])
|
||||
def post_endpoint():
|
||||
|
||||
# Get the JSON data from the request
|
||||
data = json.loads(request.data.decode())
|
||||
print(json.dumps(data, indent=4))
|
||||
|
||||
# Get the PSSH
|
||||
pssh = data['pssh']
|
||||
|
||||
# Get the license URL
|
||||
lic_url = data['license_url']
|
||||
|
||||
# Get the headers
|
||||
headers = data['headers']
|
||||
|
||||
# Get the MPD url
|
||||
mpd = data['manifest_url']
|
||||
|
||||
# Format the headers
|
||||
|
||||
# Split the string into lines
|
||||
lines = headers.strip().split('\n')
|
||||
|
||||
# Create an empty dictionary to store the key-value pairs
|
||||
headers_dict = {}
|
||||
|
||||
# Iterate through each line and split it into key-value pairs
|
||||
for line in lines:
|
||||
key, value = line.split(': ', 1) # Split only at the first occurrence of ': ' to handle values containing ': '
|
||||
headers_dict[key] = value
|
||||
|
||||
print(json.dumps(headers_dict, indent=4))
|
||||
|
||||
try:
|
||||
keys = decrypt_content(in_pssh=pssh, license_url=lic_url, headers=headers_dict, mpd=mpd)
|
||||
return {"keys": keys}
|
||||
except Exception as error:
|
||||
return {"keys": [f'{error}']}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(debug=True)
|
||||
3
requirements.txt
Normal file
3
requirements.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
flask>=3.0.3
|
||||
requests>=2.31.0
|
||||
pywidevine>=1.8.0
|
||||
Reference in New Issue
Block a user