main #4
5
.gitignore
vendored
@ -3,4 +3,7 @@
|
|||||||
*.wvd
|
*.wvd
|
||||||
*.db
|
*.db
|
||||||
.idea/
|
.idea/
|
||||||
configs/config.yaml
|
configs/config.yaml
|
||||||
|
build
|
||||||
|
main.spec
|
||||||
|
pyinstallericon.ico
|
55
README.md
@ -1,29 +1,34 @@
|
|||||||
## CDRM-Project
|
|
||||||
   
|
|
||||||
## What is this?
|
|
||||||
|
|
||||||
An open source web application written in python to decrypt Widevine and PlayReady protected content.
|
|
||||||
|
|
||||||
## Prerequisites
|
## CDRM-Project
|
||||||
|
   
|
||||||
|
|
||||||
|
## Prerequisites (from source only)
|
||||||
|
|
||||||
|
- [Python](https://www.python.org/downloads/) version [3.12](https://www.python.org/downloads/release/python-3120/)+ with PIP and VENV installed
|
||||||
|
|
||||||
|
> Python 3.13 was used at the time of writing
|
||||||
|
|
||||||
- [Python](https://www.python.org/downloads/) with PIP installed
|
## Installation (Automatic) - Recommended
|
||||||
|
- Extract contents of CDRM-Project 2.0 git contents into a new folder
|
||||||
|
- Open a terminal and change directory into the new folder
|
||||||
|
- Run `python main.py`
|
||||||
|
- Follow the on-screen prompts
|
||||||
|
|
||||||
> Python 3.13 was used at the time of writing
|
## Installation (From binary)
|
||||||
|
- Download the latest release from the [releases](https://cdm-project.com/tpd94/CDRM-Project/releases) page and run the `.exe`
|
||||||
## Installation
|
|
||||||
|
|
||||||
- Open your terminal and navigate to where you'd like to store the application
|
|
||||||
- Create a new python virtual environment using `python -m venv CDRM-Project`
|
|
||||||
- Change directory into the new `CDRM-Project` folder
|
|
||||||
- Activate the virtual environment
|
|
||||||
|
|
||||||
> Windows - change directory into the `Scripts` directory then `activate.bat`
|
|
||||||
>
|
|
||||||
> Linux - `source bin/activate`
|
|
||||||
|
|
||||||
- Extract CDRM-Project 2.0 git contents into the newly created `CDRM-Project` folder
|
|
||||||
- Install python dependencies `pip install -r requirements.txt`
|
|
||||||
- (Optional) Place your .WVD file into `/configs/CDMs/WV`
|
|
||||||
- (Optional) Place your .PRD file into `/configs/CDMs/PR`
|
|
||||||
- Run the application `python main.py`
|
|
||||||
|
|
||||||
|
## Installation (Manual)
|
||||||
|
- Open your terminal and navigate to where you'd like to store the application
|
||||||
|
- Create a new python virtual environment using `python -m venv CDRM-Project`
|
||||||
|
- Change directory into the new `CDRM-Project` folder
|
||||||
|
- Activate the virtual environment
|
||||||
|
|
||||||
|
> Windows - change directory into the `Scripts` directory then `activate.bat`
|
||||||
|
>
|
||||||
|
> Linux - `source bin/activate`
|
||||||
|
|
||||||
|
- Extract CDRM-Project 2.0 git contents into the newly created `CDRM-Project` folder
|
||||||
|
- Install python dependencies `pip install -r requirements.txt`
|
||||||
|
- (Optional) Create the folder structure `/configs/CDMs/WV` and place your .WVD file into `/configs/CDMs/WV`
|
||||||
|
- (Optional) Create the folder structur `/config/CDMs/PR` and place your .PRD file into `/configs/CDMs/PR`
|
||||||
|
- Run the application with `python main.py`
|
||||||
|
18
cdrm-frontend/dist/index.html
vendored
@ -4,18 +4,18 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/favico.png" />
|
<link rel="icon" type="image/svg+xml" href="/favico.png" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta name="description" content="Decrypt Widevine and PlayReady protected content." />
|
<meta name="description" content="{{ data.description }}"/>
|
||||||
<meta name="keywords" content="CDRM, Widevine, PlayReady, DRM, Decrypt, CDM, CDM-Project, CDRM-Project, TPD94, Decryption" />
|
<meta name="keywords" content="{{ data.keywords }}"/>
|
||||||
<meta property='og:title' content='CDRM-Project' />
|
<meta property='og:title' content="{{ data.opengraph_title }}" />
|
||||||
<meta property='og:description' content='Decrypt Widevine & PlayReady Content' />
|
<meta property='og:description' content="{{ data.opengraph_description }}" />
|
||||||
<meta property='og:image' content='https://cdrm-project.com/lockforog.png' />
|
<meta property='og:image' content="{{ data.opengraph_image }}" />
|
||||||
<meta property='og:url' content='https://cdrm-project.com/' />
|
<meta property='og:url' content="{{ data.opengraph_url }}" />
|
||||||
<meta property='og:locale' content='en_US' />
|
<meta property='og:locale' content='en_US' />
|
||||||
<title>CDRM-Project</title>
|
<title>{{ data.tab_title }}</title>
|
||||||
<script type="module" crossorigin src="/assets/index-BCBsxJZZ.js"></script>
|
<script type="module" crossorigin src="/assets/index-D2On2KQO.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-0Rv9u7Qs.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-0Rv9u7Qs.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
BIN
cdrm-frontend/dist/lockforog.png
vendored
Before Width: | Height: | Size: 1.4 MiB |
BIN
cdrm-frontend/dist/og-api.jpg
vendored
Normal file
After Width: | Height: | Size: 189 KiB |
BIN
cdrm-frontend/dist/og-cache.jpg
vendored
Normal file
After Width: | Height: | Size: 207 KiB |
BIN
cdrm-frontend/dist/og-home.jpg
vendored
Normal file
After Width: | Height: | Size: 302 KiB |
BIN
cdrm-frontend/dist/og-testplayer.jpg
vendored
Normal file
After Width: | Height: | Size: 99 KiB |
@ -4,17 +4,17 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/favico.png" />
|
<link rel="icon" type="image/svg+xml" href="/favico.png" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta name="description" content="Decrypt Widevine and PlayReady protected content." />
|
<meta name="description" content="{{ data.description }}"/>
|
||||||
<meta name="keywords" content="CDRM, Widevine, PlayReady, DRM, Decrypt, CDM, CDM-Project, CDRM-Project, TPD94, Decryption" />
|
<meta name="keywords" content="{{ data.keywords }}"/>
|
||||||
<meta property='og:title' content='CDRM-Project' />
|
<meta property='og:title' content="{{ data.opengraph_title }}" />
|
||||||
<meta property='og:description' content='Decrypt Widevine & PlayReady Content' />
|
<meta property='og:description' content="{{ data.opengraph_description }}" />
|
||||||
<meta property='og:image' content='https://cdrm-project.com/lockforog.png' />
|
<meta property='og:image' content="{{ data.opengraph_image }}" />
|
||||||
<meta property='og:url' content='https://cdrm-project.com/' />
|
<meta property='og:url' content="{{ data.opengraph_url }}" />
|
||||||
<meta property='og:locale' content='en_US' />
|
<meta property='og:locale' content='en_US' />
|
||||||
<title>CDRM-Project</title>
|
<title>{{ data.tab_title }}</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<script type="module" src="/src/main.jsx"></script>
|
<script type="module" src="/src/main.jsx"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
Before Width: | Height: | Size: 1.4 MiB |
BIN
cdrm-frontend/public/og-api.jpg
Normal file
After Width: | Height: | Size: 189 KiB |
BIN
cdrm-frontend/public/og-cache.jpg
Normal file
After Width: | Height: | Size: 207 KiB |
BIN
cdrm-frontend/public/og-home.jpg
Normal file
After Width: | Height: | Size: 302 KiB |
BIN
cdrm-frontend/public/og-testplayer.jpg
Normal file
After Width: | Height: | Size: 99 KiB |
@ -1,6 +1,7 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { data } from 'react-router-dom';
|
import { data } from 'react-router-dom';
|
||||||
const { protocol, hostname, port } = window.location;
|
const { protocol, hostname, port } = window.location;
|
||||||
|
import { Helmet } from 'react-helmet';
|
||||||
|
|
||||||
let fullHost = `${protocol}//${hostname}`;
|
let fullHost = `${protocol}//${hostname}`;
|
||||||
if (
|
if (
|
||||||
@ -62,6 +63,9 @@ function API() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className='min-w-full w-full min-h-full overflow-x-auto bg-zinc-900 shadow-lg shadow-black flex flex-col flex-wrap p-10 justify-around'>
|
<div className='min-w-full w-full min-h-full overflow-x-auto bg-zinc-900 shadow-lg shadow-black flex flex-col flex-wrap p-10 justify-around'>
|
||||||
|
<Helmet>
|
||||||
|
<title>API</title>
|
||||||
|
</Helmet>
|
||||||
|
|
||||||
{/* Decryption Request Section */}
|
{/* Decryption Request Section */}
|
||||||
<details open className='p-5 mb-5 border shadow-lg shadow-black overflow-y-auto'>
|
<details open className='p-5 mb-5 border shadow-lg shadow-black overflow-y-auto'>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import React, { useState, useEffect, useRef } from 'react';
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
|
import { Helmet } from 'react-helmet';
|
||||||
|
|
||||||
function Cache() {
|
function Cache() {
|
||||||
const [searchQuery, setSearchQuery] = useState('');
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
@ -57,6 +58,9 @@ function Cache() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className='w-full h-full bg-zinc-900 flex flex-col p-0'>
|
<div className='w-full h-full bg-zinc-900 flex flex-col p-0'>
|
||||||
|
<Helmet>
|
||||||
|
<title>Cache</title>
|
||||||
|
</Helmet>
|
||||||
<div className='flex flex-row w-full'>
|
<div className='flex flex-row w-full'>
|
||||||
<form className='flex flex-row w-8/10 p-10 h-full rounded-xl self-start'>
|
<form className='flex flex-row w-8/10 p-10 h-full rounded-xl self-start'>
|
||||||
<input
|
<input
|
||||||
|
@ -30,6 +30,7 @@ function Home() {
|
|||||||
const handleSubmitButton = (event) => {
|
const handleSubmitButton = (event) => {
|
||||||
let pssh = document.getElementById('pssh').value;
|
let pssh = document.getElementById('pssh').value;
|
||||||
let licurl = document.getElementById('licurl').value;
|
let licurl = document.getElementById('licurl').value;
|
||||||
|
let proxy = document.getElementById('proxy').value;
|
||||||
let headers = document.getElementById('headers').value;
|
let headers = document.getElementById('headers').value;
|
||||||
let cookies = document.getElementById('cookies').value;
|
let cookies = document.getElementById('cookies').value;
|
||||||
let data = document.getElementById('data').value;
|
let data = document.getElementById('data').value;
|
||||||
@ -42,6 +43,7 @@ function Home() {
|
|||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
pssh: pssh,
|
pssh: pssh,
|
||||||
licurl: licurl,
|
licurl: licurl,
|
||||||
|
proxy: proxy,
|
||||||
headers: headers,
|
headers: headers,
|
||||||
cookies: cookies,
|
cookies: cookies,
|
||||||
data: data
|
data: data
|
||||||
@ -64,6 +66,7 @@ function Home() {
|
|||||||
const handleResetButton = (event) => {
|
const handleResetButton = (event) => {
|
||||||
let pssh = document.getElementById('pssh');
|
let pssh = document.getElementById('pssh');
|
||||||
let licurl = document.getElementById('licurl');
|
let licurl = document.getElementById('licurl');
|
||||||
|
let proxy = document.getElementById('proxy');
|
||||||
let headers = document.getElementById('headers');
|
let headers = document.getElementById('headers');
|
||||||
let cookies = document.getElementById('cookies');
|
let cookies = document.getElementById('cookies');
|
||||||
let data = document.getElementById('data');
|
let data = document.getElementById('data');
|
||||||
@ -86,11 +89,16 @@ function Home() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className='w-full min-h-full bg-zinc-900 flex flex-col items-center justify-center'>
|
<div className='w-full min-h-full bg-zinc-900 flex flex-col items-center justify-center'>
|
||||||
|
<Helmet>
|
||||||
|
<title>CDRM-Project</title>
|
||||||
|
</Helmet>
|
||||||
<form className='flex flex-col w-8/10 min-h-8/10 bg-[rgba(0,0,0,0.2)] p-10 border-black border-1 rounded-xl shadow-lg shadow-cyan-500/50 overflow-y-auto' onSubmit={handleSubmit}>
|
<form className='flex flex-col w-8/10 min-h-8/10 bg-[rgba(0,0,0,0.2)] p-10 border-black border-1 rounded-xl shadow-lg shadow-cyan-500/50 overflow-y-auto' onSubmit={handleSubmit}>
|
||||||
<label htmlFor='pssh' className='text-white mb-1'>PSSH:</label>
|
<label htmlFor='pssh' className='text-white mb-1'>PSSH:</label>
|
||||||
<input type='text' id='pssh' name='pssh' className='text-white bg-[rgba(0,0,0,0.2)] focus:outline-none rounded focus:shadow-sm focus:shadow-cyan-500/50 transition-shadow duration-300 ease-in-out p-2' />
|
<input type='text' id='pssh' name='pssh' className='text-white bg-[rgba(0,0,0,0.2)] focus:outline-none rounded focus:shadow-sm focus:shadow-cyan-500/50 transition-shadow duration-300 ease-in-out p-2' />
|
||||||
<label htmlFor='licurl' className='text-white mb-1 mt-1'>License URL:</label>
|
<label htmlFor='licurl' className='text-white mb-1 mt-1'>License URL:</label>
|
||||||
<input type='text' id='licurl' name='licurl' className='text-white bg-[rgba(0,0,0,0.2)] focus:outline-none rounded focus:shadow-sm focus:shadow-cyan-500/50 transition-shadow duration-300 ease-in-out p-2' />
|
<input type='text' id='licurl' name='licurl' className='text-white bg-[rgba(0,0,0,0.2)] focus:outline-none rounded focus:shadow-sm focus:shadow-cyan-500/50 transition-shadow duration-300 ease-in-out p-2' />
|
||||||
|
<label htmlFor='proxy' className='text-white mb-1 mt-1'>Proxy:</label>
|
||||||
|
<input type='text' id='proxy' name='proxy' className='text-white bg-[rgba(0,0,0,0.2)] focus:outline-none rounded focus:shadow-sm focus:shadow-cyan-500/50 transition-shadow duration-300 ease-in-out p-2' />
|
||||||
<label htmlFor='headers' className='text-white mb-1 mt-1'>Headers:</label>
|
<label htmlFor='headers' className='text-white mb-1 mt-1'>Headers:</label>
|
||||||
<textarea id='headers' name='headers' className='text-white bg-[rgba(0,0,0,0.2)] h-24 focus:h-92 focus:outline-none rounded focus:shadow-sm focus:shadow-cyan-500/50 transition-all duration-300 ease-in-out p-2 resize-none' />
|
<textarea id='headers' name='headers' className='text-white bg-[rgba(0,0,0,0.2)] h-24 focus:h-92 focus:outline-none rounded focus:shadow-sm focus:shadow-cyan-500/50 transition-all duration-300 ease-in-out p-2 resize-none' />
|
||||||
<label htmlFor='cookies' className='text-white mb-1 mt-1'>Cookies:</label>
|
<label htmlFor='cookies' className='text-white mb-1 mt-1'>Cookies:</label>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import React, { useState, useRef } from "react";
|
import React, { useState, useRef } from "react";
|
||||||
import shaka from "shaka-player"; // Import the Shaka Player library
|
import shaka from "shaka-player"; // Import the Shaka Player library
|
||||||
|
import { Helmet } from 'react-helmet';
|
||||||
|
|
||||||
function TestPlayer() {
|
function TestPlayer() {
|
||||||
const [mpdUrl, setMpdUrl] = useState("");
|
const [mpdUrl, setMpdUrl] = useState("");
|
||||||
@ -53,6 +54,9 @@ function TestPlayer() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-w-full w-full min-h-full h-full bg-zinc-900 shadow-lg shadow-black flex flex-row overflow-y-auto pt-5 pl-5 pr-5 items-center justify-around">
|
<div className="min-w-full w-full min-h-full h-full bg-zinc-900 shadow-lg shadow-black flex flex-row overflow-y-auto pt-5 pl-5 pr-5 items-center justify-around">
|
||||||
|
<Helmet>
|
||||||
|
<title>Test Player</title>
|
||||||
|
</Helmet>
|
||||||
<div className="min-8/10 w-8/10 min-h-8/10 h-8/10 flex flex-row overflow-y-auto items-center justify-around border shadow-lg shadow-red-700 rounded-2xl">
|
<div className="min-8/10 w-8/10 min-h-8/10 h-8/10 flex flex-row overflow-y-auto items-center justify-around border shadow-lg shadow-red-700 rounded-2xl">
|
||||||
<div className="w-7/10 h-7/10 border border-black rounded-2xl p-5 bg-[rgba(0,0,0,0.2)] shadow-lg shadow-black">
|
<div className="w-7/10 h-7/10 border border-black rounded-2xl p-5 bg-[rgba(0,0,0,0.2)] shadow-lg shadow-black">
|
||||||
<video
|
<video
|
||||||
|
38
configs/index_tags.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
tags = {
|
||||||
|
'index': {
|
||||||
|
'description': 'Decrypt Widevine and PlayReady protected content',
|
||||||
|
'keywords': 'CDRM, Widevine, PlayReady, DRM, Decrypt, CDM, CDM-Project, CDRM-Project, TPD94, Decryption',
|
||||||
|
'opengraph_title': 'CDRM-Project',
|
||||||
|
'opengraph_description': 'Self Hosted web application written in Python/JavaScript utilizing the Flask/Tailwind Framework and ReactJS library to decrypt Widevine & Playready content',
|
||||||
|
'opengraph_image': 'https://cdrm-project.com/og-home.jpg',
|
||||||
|
'opengraph_url': 'https://cdm-project.com/tpd94/cdrm-project',
|
||||||
|
'tab_title': 'CDRM-Project',
|
||||||
|
},
|
||||||
|
'cache': {
|
||||||
|
'description': 'Search the cache by KID or PSSH for decryption keys',
|
||||||
|
'keywords': 'Cache, Vault, Widevine, PlayReady, DRM, Decryption, CDM, CDRM-Project, CDRM-Project, TPD94, Decryption',
|
||||||
|
'opengraph_title': 'Search the Cache',
|
||||||
|
'opengraph_description': 'Search the cache by KID or PSSH for decryption keys',
|
||||||
|
'opengraph_image': 'https://cdrm-project.com/og-cache.jpg',
|
||||||
|
'opengraph_url': 'https://cdrm-project.com/cache',
|
||||||
|
'tab_title': 'Cache',
|
||||||
|
},
|
||||||
|
'testplayer': {
|
||||||
|
'description': 'Shaka Player for testing decryption keys',
|
||||||
|
'keywords': 'Shaka, Player, DRM, CDRM, CDM, CDRM-Project, TPD94, Decryption, CDM-Project, KID, KEY',
|
||||||
|
'opengraph_title': 'Test Player',
|
||||||
|
'opengraph_description': 'Shaka Player for testing decryption keys',
|
||||||
|
'opengraph_image': 'https://cdrm-project.com/og-testplayer.jpg',
|
||||||
|
'opengraph_url': 'https://cdrm-project.com/testplayer',
|
||||||
|
'tab_title': 'Test Player',
|
||||||
|
},
|
||||||
|
'api': {
|
||||||
|
'description': 'API documentation for the program "CDRM-Project"',
|
||||||
|
'keywords': 'API, python, requests, send, remotecdm, remote, cdm, CDM-Project, CDRM-Project, TPD94, Decryption, DRM, Web, Vault',
|
||||||
|
'opengraph_title': 'API',
|
||||||
|
'opengraph_description': 'Documentation for the program "CDRM-Project"',
|
||||||
|
'opengraph_image': 'https://cdrm-project.com/og-api.jpg',
|
||||||
|
'opengraph_url': 'https://cdrm-project.com/api',
|
||||||
|
'tab_title': 'API',
|
||||||
|
}
|
||||||
|
}
|
@ -10,7 +10,7 @@ import ast
|
|||||||
import glob
|
import glob
|
||||||
import os
|
import os
|
||||||
import yaml
|
import yaml
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -73,7 +73,18 @@ def is_base64(string):
|
|||||||
# If decoding or encoding fails, it's not Base64
|
# If decoding or encoding fails, it's not Base64
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def api_decrypt(pssh:str = None, license_url: str = None, headers: str = None, cookies: str = None, json_data: str = None):
|
def is_url_and_split(input_str):
|
||||||
|
parsed = urlparse(input_str)
|
||||||
|
|
||||||
|
# Check if it's a valid URL with scheme and netloc
|
||||||
|
if parsed.scheme and parsed.netloc:
|
||||||
|
protocol = parsed.scheme
|
||||||
|
fqdn = parsed.netloc
|
||||||
|
return True, protocol, fqdn
|
||||||
|
else:
|
||||||
|
return False, None, None
|
||||||
|
|
||||||
|
def api_decrypt(pssh:str = None, license_url: str = None, proxy: str = None, headers: str = None, cookies: str = None, json_data: str = None):
|
||||||
with open(f'{os.getcwd()}/configs/config.yaml', 'r') as file:
|
with open(f'{os.getcwd()}/configs/config.yaml', 'r') as file:
|
||||||
config = yaml.safe_load(file)
|
config = yaml.safe_load(file)
|
||||||
if config['database_type'].lower() == 'sqlite':
|
if config['database_type'].lower() == 'sqlite':
|
||||||
@ -165,14 +176,30 @@ def api_decrypt(pssh:str = None, license_url: str = None, headers: str = None, c
|
|||||||
'message': f'An error occurred getting json_data\n\n{error}'
|
'message': f'An error occurred getting json_data\n\n{error}'
|
||||||
}
|
}
|
||||||
licence = None
|
licence = None
|
||||||
|
proxies = None
|
||||||
|
if proxy is not None:
|
||||||
|
is_url, protocol, fqdn = is_url_and_split(proxy)
|
||||||
|
if is_url:
|
||||||
|
proxies = {'http': proxy, 'https': proxy}
|
||||||
|
else:
|
||||||
|
return {
|
||||||
|
'status': 'error',
|
||||||
|
'message': f'Your proxy is invalid, please put it in the format of http(s)://fqdn.tld:port'
|
||||||
|
}
|
||||||
try:
|
try:
|
||||||
licence = requests.post(
|
licence = requests.post(
|
||||||
url=license_url,
|
url=license_url,
|
||||||
headers=format_headers,
|
headers=format_headers,
|
||||||
|
proxies=proxies,
|
||||||
cookies=format_cookies,
|
cookies=format_cookies,
|
||||||
json=format_json_data if format_json_data is not None else None,
|
json=format_json_data if format_json_data is not None else None,
|
||||||
data=pr_challenge if format_json_data is None else None
|
data=pr_challenge if format_json_data is None else None
|
||||||
)
|
)
|
||||||
|
except requests.exceptions.ConnectionError as error:
|
||||||
|
return {
|
||||||
|
'status': 'error',
|
||||||
|
'message': f'An error occurred sending license challenge through your proxy\n\n{error}'
|
||||||
|
}
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
return {
|
return {
|
||||||
'status': 'error',
|
'status': 'error',
|
||||||
@ -310,14 +337,25 @@ def api_decrypt(pssh:str = None, license_url: str = None, headers: str = None, c
|
|||||||
'message': f'An error occurred getting json_data\n\n{error}'
|
'message': f'An error occurred getting json_data\n\n{error}'
|
||||||
}
|
}
|
||||||
licence = None
|
licence = None
|
||||||
|
proxies = None
|
||||||
|
if proxy is not None:
|
||||||
|
is_url, protocol, fqdn = is_url_and_split(proxy)
|
||||||
|
if is_url:
|
||||||
|
proxies = {'http': proxy, 'https': proxy}
|
||||||
try:
|
try:
|
||||||
licence = requests.post(
|
licence = requests.post(
|
||||||
url=license_url,
|
url=license_url,
|
||||||
headers=format_headers,
|
headers=format_headers,
|
||||||
|
proxies=proxies,
|
||||||
cookies=format_cookies,
|
cookies=format_cookies,
|
||||||
json=format_json_data if format_json_data is not None else None,
|
json=format_json_data if format_json_data is not None else None,
|
||||||
data=wv_challenge if format_json_data is None else None
|
data=wv_challenge if format_json_data is None else None
|
||||||
)
|
)
|
||||||
|
except requests.exceptions.ConnectionError as error:
|
||||||
|
return {
|
||||||
|
'status': 'error',
|
||||||
|
'message': f'An error occurred sending license challenge through your proxy\n\n{error}'
|
||||||
|
}
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
return {
|
return {
|
||||||
'status': 'error',
|
'status': 'error',
|
||||||
|
85
custom_functions/prechecks/python_checks.py
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import venv
|
||||||
|
|
||||||
|
def version_check():
|
||||||
|
major_version = sys.version_info.major
|
||||||
|
minor_version = sys.version_info.minor
|
||||||
|
|
||||||
|
if major_version >= 3:
|
||||||
|
if minor_version >= 12:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
exit("Python version 3.12 or higher is required")
|
||||||
|
else:
|
||||||
|
exit("Python 2 detected, Python version 3.12 or higher is required")
|
||||||
|
|
||||||
|
def pip_check():
|
||||||
|
try:
|
||||||
|
import pip
|
||||||
|
return
|
||||||
|
except ImportError:
|
||||||
|
exit("Pip is not installed")
|
||||||
|
|
||||||
|
def venv_check():
|
||||||
|
# Check if we're already inside a virtual environment
|
||||||
|
if hasattr(sys, 'real_prefix') or (hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix):
|
||||||
|
return
|
||||||
|
|
||||||
|
venv_path = os.path.join(os.getcwd(), 'cdrm-venv')
|
||||||
|
venv_python = os.path.join(venv_path, 'bin', 'python') if os.name != 'nt' else os.path.join(venv_path, 'Scripts', 'python.exe')
|
||||||
|
|
||||||
|
# If venv already exists, restart script using its Python
|
||||||
|
if os.path.exists(venv_path):
|
||||||
|
subprocess.call([venv_python] + sys.argv)
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
# Ask user for permission to create a virtual environment
|
||||||
|
answer = ''
|
||||||
|
while not answer or answer[0].upper() not in {'Y', 'N'}:
|
||||||
|
answer = input(
|
||||||
|
'Program is not running from a venv. To maintain compatibility and dependencies, this program must be run from one.\n'
|
||||||
|
'Would you like me to create one for you? (Y/N): '
|
||||||
|
)
|
||||||
|
|
||||||
|
if answer[0].upper() == 'Y':
|
||||||
|
print("Creating virtual environment...")
|
||||||
|
venv.create(venv_path, with_pip=True)
|
||||||
|
subprocess.call([venv_python] + sys.argv)
|
||||||
|
sys.exit()
|
||||||
|
else:
|
||||||
|
print("Exiting program. Please run it from a virtual environment next time.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def requirements_check():
|
||||||
|
try:
|
||||||
|
import pywidevine
|
||||||
|
import pyplayready
|
||||||
|
import flask
|
||||||
|
import flask_cors
|
||||||
|
import yaml
|
||||||
|
import mysql.connector
|
||||||
|
return
|
||||||
|
except ImportError:
|
||||||
|
while True:
|
||||||
|
user_input = input("Missing packages. Do you want to install them? (Y/N): ").strip().upper()
|
||||||
|
if user_input == 'Y':
|
||||||
|
print("Installing packages from requirements.txt...")
|
||||||
|
subprocess.check_call([sys.executable, "-m", "pip", "install", "-r", "requirements.txt"])
|
||||||
|
print("Installation complete.")
|
||||||
|
break
|
||||||
|
elif user_input == 'N':
|
||||||
|
print("Dependencies required, please install them and run again.")
|
||||||
|
sys.exit()
|
||||||
|
else:
|
||||||
|
print("Invalid input. Please enter 'Y' to install or 'N' to exit.")
|
||||||
|
|
||||||
|
def run_python_checks():
|
||||||
|
if getattr(sys, 'frozen', False): # Check if running from PyInstaller
|
||||||
|
return
|
||||||
|
version_check()
|
||||||
|
pip_check()
|
||||||
|
venv_check()
|
||||||
|
requirements_check()
|
2
main.py
@ -1,3 +1,5 @@
|
|||||||
|
from custom_functions.prechecks.python_checks import run_python_checks
|
||||||
|
run_python_checks()
|
||||||
from custom_functions.prechecks.precheck import run_precheck
|
from custom_functions.prechecks.precheck import run_precheck
|
||||||
run_precheck()
|
run_precheck()
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
|
6
package-lock.json
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "CDRM-Project",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {}
|
||||||
|
}
|
@ -195,6 +195,13 @@ def decrypt_data():
|
|||||||
api_request_licurl = api_request_data['licurl']
|
api_request_licurl = api_request_data['licurl']
|
||||||
else:
|
else:
|
||||||
api_request_licurl = None
|
api_request_licurl = None
|
||||||
|
if 'proxy' in api_request_data:
|
||||||
|
if api_request_data['proxy'] == '':
|
||||||
|
api_request_proxy = None
|
||||||
|
else:
|
||||||
|
api_request_proxy = api_request_data['proxy']
|
||||||
|
else:
|
||||||
|
api_request_proxy = None
|
||||||
if 'headers' in api_request_data:
|
if 'headers' in api_request_data:
|
||||||
if api_request_data['headers'] == '':
|
if api_request_data['headers'] == '':
|
||||||
api_request_headers = None
|
api_request_headers = None
|
||||||
@ -216,7 +223,7 @@ def decrypt_data():
|
|||||||
api_request_data = api_request_data['data']
|
api_request_data = api_request_data['data']
|
||||||
else:
|
else:
|
||||||
api_request_data = None
|
api_request_data = None
|
||||||
result = api_decrypt(pssh=api_request_pssh, license_url=api_request_licurl, headers=api_request_headers, cookies=api_request_cookies, json_data=api_request_data)
|
result = api_decrypt(pssh=api_request_pssh, proxy=api_request_proxy, license_url=api_request_licurl, headers=api_request_headers, cookies=api_request_cookies, json_data=api_request_data)
|
||||||
if result['status'] == 'success':
|
if result['status'] == 'success':
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'status': 'success',
|
'status': 'success',
|
||||||
|
@ -1,16 +1,33 @@
|
|||||||
from flask import Blueprint, send_from_directory, request
|
import sys
|
||||||
import os
|
import os
|
||||||
|
from flask import Blueprint, send_from_directory, request, render_template
|
||||||
|
from configs import index_tags
|
||||||
|
|
||||||
react_bp = Blueprint('react_bp', __name__, static_folder=f'{os.getcwd()}/cdrm-frontend/dist', static_url_path='/')
|
if getattr(sys, 'frozen', False): # Running as a bundled app
|
||||||
|
base_path = sys._MEIPASS
|
||||||
|
else: # Running in a normal Python environment
|
||||||
|
base_path = os.path.abspath(".")
|
||||||
|
|
||||||
|
static_folder = os.path.join(base_path, 'cdrm-frontend', 'dist')
|
||||||
|
|
||||||
|
react_bp = Blueprint(
|
||||||
|
'react_bp',
|
||||||
|
__name__,
|
||||||
|
static_folder=static_folder,
|
||||||
|
static_url_path='/',
|
||||||
|
template_folder=static_folder
|
||||||
|
)
|
||||||
|
|
||||||
@react_bp.route('/', methods=['GET'])
|
@react_bp.route('/', methods=['GET'])
|
||||||
@react_bp.route('/<path:path>', methods=["GET"])
|
@react_bp.route('/<path:path>', methods=["GET"])
|
||||||
@react_bp.route('/<path>', methods=["GET"])
|
@react_bp.route('/<path>', methods=["GET"])
|
||||||
def index(path=''):
|
def index(path=''):
|
||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
if path != "" and os.path.exists(react_bp.static_folder + '/' + path):
|
file_path = os.path.join(react_bp.static_folder, path)
|
||||||
|
if path != "" and os.path.exists(file_path):
|
||||||
return send_from_directory(react_bp.static_folder, path)
|
return send_from_directory(react_bp.static_folder, path)
|
||||||
|
elif path.lower() in ['', 'cache', 'api', 'testplayer']:
|
||||||
|
data = index_tags.tags.get(path.lower(), index_tags.tags['index'])
|
||||||
|
return render_template('index.html', data=data)
|
||||||
else:
|
else:
|
||||||
return send_from_directory(react_bp.static_folder, 'index.html')
|
return send_from_directory(react_bp.static_folder, 'index.html')
|
||||||
else:
|
|
||||||
return
|
|
||||||
|