Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
55f1bd9d9b | |||
6b78ac120f | |||
3f7538838d | |||
c25a891ea9 | |||
29b61668a4 |
1
cdrm-frontend/.gitignore
vendored
1
cdrm-frontend/.gitignore
vendored
@ -8,7 +8,6 @@ pnpm-debug.log*
|
|||||||
lerna-debug.log*
|
lerna-debug.log*
|
||||||
|
|
||||||
node_modules
|
node_modules
|
||||||
dist
|
|
||||||
dist-ssr
|
dist-ssr
|
||||||
*.local
|
*.local
|
||||||
|
|
||||||
|
1
cdrm-frontend/dist/assets/index-DQNyIeaF.css
vendored
Normal file
1
cdrm-frontend/dist/assets/index-DQNyIeaF.css
vendored
Normal file
File diff suppressed because one or more lines are too long
160
cdrm-frontend/dist/assets/index-DWCLK6jB.js
vendored
Normal file
160
cdrm-frontend/dist/assets/index-DWCLK6jB.js
vendored
Normal file
File diff suppressed because one or more lines are too long
4
cdrm-frontend/dist/index.html
vendored
4
cdrm-frontend/dist/index.html
vendored
@ -12,8 +12,8 @@
|
|||||||
<meta property='og:url' content="{{ data.opengraph_url }}" />
|
<meta property='og:url' content="{{ data.opengraph_url }}" />
|
||||||
<meta property='og:locale' content='en_US' />
|
<meta property='og:locale' content='en_US' />
|
||||||
<title>{{ data.tab_title }}</title>
|
<title>{{ data.tab_title }}</title>
|
||||||
<script type="module" crossorigin src="/assets/index-C2DUB5KK.js"></script>
|
<script type="module" crossorigin src="/assets/index-DWCLK6jB.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-BXlb7x7c.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-DQNyIeaF.css">
|
||||||
</head>
|
</head>
|
||||||
<body class="w-full h-full">
|
<body class="w-full h-full">
|
||||||
<div id="root" class="w-full h-full"></div>
|
<div id="root" class="w-full h-full"></div>
|
||||||
|
@ -6,6 +6,11 @@ function MyAccount() {
|
|||||||
const [prList, setPrList] = useState([]);
|
const [prList, setPrList] = useState([]);
|
||||||
const [uploading, setUploading] = useState(false);
|
const [uploading, setUploading] = useState(false);
|
||||||
const [username, setUsername] = useState('');
|
const [username, setUsername] = useState('');
|
||||||
|
const [apiKey, setApiKey] = useState('');
|
||||||
|
const [password, setPassword] = useState('');
|
||||||
|
const [passwordError, setPasswordError] = useState('');
|
||||||
|
const [newApiKey, setNewApiKey] = useState('');
|
||||||
|
const [apiKeyError, setApiKeyError] = useState('');
|
||||||
|
|
||||||
// Fetch user info
|
// Fetch user info
|
||||||
const fetchUserInfo = async () => {
|
const fetchUserInfo = async () => {
|
||||||
@ -13,7 +18,8 @@ function MyAccount() {
|
|||||||
const response = await axios.post('/userinfo');
|
const response = await axios.post('/userinfo');
|
||||||
setWvList(response.data.Widevine_Devices || []);
|
setWvList(response.data.Widevine_Devices || []);
|
||||||
setPrList(response.data.Playready_Devices || []);
|
setPrList(response.data.Playready_Devices || []);
|
||||||
setUsername(response.data.Username || '');
|
setUsername(response.data.Styled_Username || '');
|
||||||
|
setApiKey(response.data.API_Key || '');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to fetch user info', err);
|
console.error('Failed to fetch user info', err);
|
||||||
}
|
}
|
||||||
@ -60,83 +66,193 @@ function MyAccount() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Handle change password
|
||||||
|
const handleChangePassword = async () => {
|
||||||
|
if (passwordError || password === '') {
|
||||||
|
alert('Please enter a valid password.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await axios.post('/user/change_password', {
|
||||||
|
new_password: password
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.data.message === 'True') {
|
||||||
|
alert('Password changed successfully.');
|
||||||
|
setPassword('');
|
||||||
|
} else {
|
||||||
|
alert('Failed to change password.');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error.response && error.response.data?.message === 'Invalid password format') {
|
||||||
|
alert('Password format is invalid. Please try again.');
|
||||||
|
} else {
|
||||||
|
alert('Error occurred while changing password.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle change API key
|
||||||
|
const handleChangeApiKey = async () => {
|
||||||
|
if (apiKeyError || newApiKey === '') {
|
||||||
|
alert('Please enter a valid API key.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await axios.post('/user/change_api_key', {
|
||||||
|
new_api_key: newApiKey,
|
||||||
|
});
|
||||||
|
if (response.data.message === 'True') {
|
||||||
|
alert('API key changed successfully.');
|
||||||
|
setApiKey(newApiKey);
|
||||||
|
setNewApiKey('');
|
||||||
|
} else {
|
||||||
|
alert('Failed to change API key.');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
alert('Error occurred while changing API key.');
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id="myaccount" className="flex flex-row w-full min-h-full overflow-y-auto p-4">
|
<div id="myaccount" className="flex flex-col lg:flex-row gap-4 w-full min-h-full overflow-y-auto p-4">
|
||||||
<div className="flex flex-col w-full min-h-full lg:flex-row">
|
<div className="flex-col w-full min-h-164 lg:h-full lg:w-96 border-2 border-yellow-500/50 rounded-2xl p-4 flex items-center overflow-y-auto">
|
||||||
{/* Left Panel */}
|
<h1 className="text-2xl font-bold text-white border-b-2 border-white p-2 w-full text-center mb-2">
|
||||||
<div className="border-2 border-yellow-500/50 lg:h-full lg:w-96 w-full rounded-2xl p-4 flex flex-col items-center overflow-y-auto">
|
{username ? `${username}` : 'My Account'}
|
||||||
<h1 className="text-2xl font-bold text-white border-b-2 border-white p-2 w-full text-center mb-2">
|
</h1>
|
||||||
{username ? `${username}` : 'My Account'}
|
|
||||||
</h1>
|
{/* API Key Section */}
|
||||||
|
<div className="w-full flex flex-col items-center">
|
||||||
|
<label htmlFor="apiKey" className="text-white font-semibold mb-1">API Key</label>
|
||||||
|
<input
|
||||||
|
id="apiKey"
|
||||||
|
type="text"
|
||||||
|
value={apiKey}
|
||||||
|
readOnly
|
||||||
|
className="w-full p-2 mb-4 rounded bg-gray-800 text-white border border-gray-600 text-center"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* New API Key Section */}
|
||||||
|
<label htmlFor="newApiKey" className="text-white font-semibold mt-4 mb-1">New API Key</label>
|
||||||
|
<input
|
||||||
|
id="newApiKey"
|
||||||
|
type="text"
|
||||||
|
value={newApiKey}
|
||||||
|
onChange={(e) => {
|
||||||
|
const value = e.target.value;
|
||||||
|
const isValid = /^[^\s]+$/.test(value); // No spaces
|
||||||
|
if (!isValid) {
|
||||||
|
setApiKeyError('API key must not contain spaces.');
|
||||||
|
} else {
|
||||||
|
setApiKeyError('');
|
||||||
|
}
|
||||||
|
setNewApiKey(value);
|
||||||
|
}}
|
||||||
|
placeholder="Enter new API key"
|
||||||
|
className="w-full p-2 mb-1 rounded bg-gray-800 text-white border border-gray-600 text-center"
|
||||||
|
/>
|
||||||
|
{apiKeyError && <p className="text-red-500 text-sm mb-3">{apiKeyError}</p>}
|
||||||
<button
|
<button
|
||||||
onClick={handleLogout}
|
className="w-full h-12 bg-yellow-500/50 rounded-2xl text-2xl text-white"
|
||||||
className="mt-auto w-full h-12 bg-yellow-500/50 rounded-2xl text-2xl text-white"
|
onClick={handleChangeApiKey}
|
||||||
>
|
>
|
||||||
Log out
|
Change API Key
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Change Password Section */}
|
||||||
|
<label htmlFor="password" className="text-white font-semibold mt-4 mb-1">Change Password</label>
|
||||||
|
<input
|
||||||
|
id="password"
|
||||||
|
type="password"
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => {
|
||||||
|
const value = e.target.value;
|
||||||
|
const isValid = /^[A-Za-z0-9!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?`~]*$/.test(value);
|
||||||
|
if (!isValid) {
|
||||||
|
setPasswordError('Password must not contain spaces or invalid characters.');
|
||||||
|
} else {
|
||||||
|
setPasswordError('');
|
||||||
|
}
|
||||||
|
setPassword(value);
|
||||||
|
}}
|
||||||
|
placeholder="New Password"
|
||||||
|
className="w-full p-2 mb-1 rounded bg-gray-800 text-white border border-gray-600 text-center"
|
||||||
|
/>
|
||||||
|
{passwordError && <p className="text-red-500 text-sm mb-3">{passwordError}</p>}
|
||||||
|
<button
|
||||||
|
className="w-full h-12 bg-yellow-500/50 rounded-2xl text-2xl text-white"
|
||||||
|
onClick={handleChangePassword}
|
||||||
|
>
|
||||||
|
Change Password
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right Panel */}
|
<button
|
||||||
<div className="flex flex-col grow lg:ml-2 mt-2 lg:mt-0">
|
onClick={handleLogout}
|
||||||
{/* Widevine Section */}
|
className="mt-auto w-full h-12 bg-yellow-500/50 rounded-2xl text-2xl text-white"
|
||||||
<div className="border-2 border-yellow-500/50 flex flex-col w-full min-h-1/2 text-center rounded-2xl lg:p-4 p-2 overflow-y-auto">
|
>
|
||||||
<h1 className="text-2xl font-bold text-white border-b-2 border-white p-2">Widevine CDMs</h1>
|
Log out
|
||||||
<div className="flex flex-col w-full grow p-2 bg-white/5 rounded-2xl mt-2 text-white text-left">
|
</button>
|
||||||
{wvList.length === 0 ? (
|
</div>
|
||||||
<div className="text-white text-center font-bold">No Widevine CDMs uploaded.</div>
|
|
||||||
) : (
|
|
||||||
wvList.map((filename, i) => (
|
|
||||||
<div
|
|
||||||
key={i}
|
|
||||||
className={`text-center font-bold text-white p-2 rounded ${
|
|
||||||
i % 2 === 0 ? 'bg-black/30' : 'bg-black/60'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{filename}
|
|
||||||
</div>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<label className="bg-yellow-500 text-white w-full h-16 mt-4 rounded-2xl flex items-center justify-center cursor-pointer">
|
|
||||||
{uploading ? 'Uploading...' : 'Upload CDM'}
|
|
||||||
<input
|
|
||||||
type="file"
|
|
||||||
accept=".wvd"
|
|
||||||
hidden
|
|
||||||
onChange={(e) => handleUpload(e, 'WV')}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Playready Section */}
|
<div className="flex flex-col w-full lg:ml-2 mt-2 lg:mt-0">
|
||||||
<div className="border-2 border-yellow-500/50 flex flex-col w-full min-h-1/2 text-center rounded-2xl p-2 mt-2 lg:mt-2 overflow-y-auto">
|
{/* Widevine Section */}
|
||||||
<h1 className="text-2xl font-bold text-white border-b-2 border-white p-2">Playready CDMs</h1>
|
<div className="border-2 border-yellow-500/50 flex flex-col w-full min-h-1/2 text-center rounded-2xl lg:p-4 p-2 overflow-y-auto">
|
||||||
<div className="flex flex-col w-full bg-white/5 grow rounded-2xl mt-2 text-white text-left p-2">
|
<h1 className="bg-black text-2xl font-bold text-white border-b-2 border-white p-2">Widevine CDMs</h1>
|
||||||
{prList.length === 0 ? (
|
<div className="flex flex-col w-full grow p-2 bg-white/5 rounded-2xl mt-2 text-white text-left">
|
||||||
<div className="text-white text-center font-bold">No Playready CDMs uploaded.</div>
|
{wvList.length === 0 ? (
|
||||||
) : (
|
<div className="text-white text-center font-bold">No Widevine CDMs uploaded.</div>
|
||||||
prList.map((filename, i) => (
|
) : (
|
||||||
<div
|
wvList.map((filename, i) => (
|
||||||
key={i}
|
<div
|
||||||
className={`text-center font-bold text-white p-2 rounded ${
|
key={i}
|
||||||
i % 2 === 0 ? 'bg-black/30' : 'bg-black/60'
|
className={`text-center font-bold text-white p-2 rounded ${i % 2 === 0 ? 'bg-black/30' : 'bg-black/60'}`}
|
||||||
}`}
|
>
|
||||||
>
|
{filename}
|
||||||
{filename}
|
</div>
|
||||||
</div>
|
))
|
||||||
))
|
)}
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<label className="bg-yellow-500 text-white w-full h-16 mt-4 rounded-2xl flex items-center justify-center cursor-pointer">
|
|
||||||
{uploading ? 'Uploading...' : 'Upload CDM'}
|
|
||||||
<input
|
|
||||||
type="file"
|
|
||||||
accept=".prd"
|
|
||||||
hidden
|
|
||||||
onChange={(e) => handleUpload(e, 'PR')}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
|
<label className="bg-yellow-500 text-white w-full min-h-16 lg:min-h-16 mt-4 rounded-2xl flex items-center justify-center cursor-pointer">
|
||||||
|
{uploading ? 'Uploading...' : 'Upload CDM'}
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
accept=".wvd"
|
||||||
|
hidden
|
||||||
|
onChange={(e) => handleUpload(e, 'WV')}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Playready Section */}
|
||||||
|
<div className="border-2 border-yellow-500/50 flex flex-col w-full min-h-1/2 text-center rounded-2xl p-2 mt-2 lg:mt-2 overflow-y-auto">
|
||||||
|
<h1 className="text-2xl font-bold text-white border-b-2 border-white p-2 bg-black">Playready CDMs</h1>
|
||||||
|
<div className="flex flex-col w-full bg-white/5 grow rounded-2xl mt-2 text-white text-left p-2">
|
||||||
|
{prList.length === 0 ? (
|
||||||
|
<div className="text-white text-center font-bold">No Playready CDMs uploaded.</div>
|
||||||
|
) : (
|
||||||
|
prList.map((filename, i) => (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
className={`text-center font-bold text-white p-2 rounded ${i % 2 === 0 ? 'bg-black/30' : 'bg-black/60'}`}
|
||||||
|
>
|
||||||
|
{filename}
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<label className="bg-yellow-500 text-white w-full min-h-16 lg:min-h-16 mt-4 rounded-2xl flex items-center justify-center cursor-pointer">
|
||||||
|
{uploading ? 'Uploading...' : 'Upload CDM'}
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
accept=".prd"
|
||||||
|
hidden
|
||||||
|
onChange={(e) => handleUpload(e, 'PR')}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -5,7 +5,20 @@ function Register() {
|
|||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
const [status, setStatus] = useState('');
|
const [status, setStatus] = useState('');
|
||||||
|
|
||||||
|
// Validation functions
|
||||||
|
const validateUsername = (name) => /^[A-Za-z0-9_-]+$/.test(name);
|
||||||
|
const validatePassword = (pass) => /^\S+$/.test(pass); // No spaces
|
||||||
|
|
||||||
const handleRegister = async () => {
|
const handleRegister = async () => {
|
||||||
|
if (!validateUsername(username)) {
|
||||||
|
setStatus("Invalid username. Use only letters, numbers, hyphens, or underscores.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!validatePassword(password)) {
|
||||||
|
setStatus("Invalid password. Spaces are not allowed.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/register', {
|
const response = await fetch('/register', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@ -26,6 +39,15 @@ function Register() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleLogin = async () => {
|
const handleLogin = async () => {
|
||||||
|
if (!validateUsername(username)) {
|
||||||
|
setStatus("Invalid username. Use only letters, numbers, hyphens, or underscores.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!validatePassword(password)) {
|
||||||
|
setStatus("Invalid password. Spaces are not allowed.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/login', {
|
const response = await fetch('/login', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
data = {
|
data = {
|
||||||
'discord': 'https://discord.cdrm-project.com/',
|
'discord': 'https://discord.cdrm-project.com/',
|
||||||
'telegram': 'https://telegram.cdrm-project.com/',
|
'telegram': 'https://telegram.cdrm-project.com/',
|
||||||
'gitea': 'https://cdm-project.com/tpd94/cdm-project'
|
'gitea': 'https://cdm-project.com/tpd94/cdrm-project'
|
||||||
}
|
}
|
@ -11,18 +11,20 @@ def create_user_database():
|
|||||||
cursor.execute('''
|
cursor.execute('''
|
||||||
CREATE TABLE IF NOT EXISTS user_info (
|
CREATE TABLE IF NOT EXISTS user_info (
|
||||||
Username TEXT PRIMARY KEY,
|
Username TEXT PRIMARY KEY,
|
||||||
Password TEXT
|
Password TEXT,
|
||||||
|
Styled_Username TEXT,
|
||||||
|
API_Key TEXT
|
||||||
)
|
)
|
||||||
''')
|
''')
|
||||||
|
|
||||||
|
|
||||||
def add_user(username, password):
|
def add_user(username, password, api_key):
|
||||||
hashed_pw = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())
|
hashed_pw = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())
|
||||||
|
|
||||||
with sqlite3.connect(f'{os.getcwd()}/databases/sql/users.db') as conn:
|
with sqlite3.connect(f'{os.getcwd()}/databases/sql/users.db') as conn:
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
try:
|
try:
|
||||||
cursor.execute('INSERT INTO user_info (Username, Password) VALUES (?, ?)', (username, hashed_pw))
|
cursor.execute('INSERT INTO user_info (Username, Password, Styled_Username, API_Key) VALUES (?, ?, ?, ?)', (username.lower(), hashed_pw, username, api_key))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
return True
|
return True
|
||||||
except sqlite3.IntegrityError:
|
except sqlite3.IntegrityError:
|
||||||
@ -32,7 +34,7 @@ def add_user(username, password):
|
|||||||
def verify_user(username, password):
|
def verify_user(username, password):
|
||||||
with sqlite3.connect(f'{os.getcwd()}/databases/sql/users.db') as conn:
|
with sqlite3.connect(f'{os.getcwd()}/databases/sql/users.db') as conn:
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute('SELECT Password FROM user_info WHERE Username = ?', (username,))
|
cursor.execute('SELECT Password FROM user_info WHERE Username = ?', (username.lower(),))
|
||||||
result = cursor.fetchone()
|
result = cursor.fetchone()
|
||||||
|
|
||||||
if result:
|
if result:
|
||||||
@ -43,3 +45,56 @@ def verify_user(username, password):
|
|||||||
return bcrypt.checkpw(password.encode('utf-8'), stored_hash)
|
return bcrypt.checkpw(password.encode('utf-8'), stored_hash)
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def fetch_api_key(username):
|
||||||
|
with sqlite3.connect(f'{os.getcwd()}/databases/sql/users.db') as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute('SELECT API_Key FROM user_info WHERE Username = ?', (username.lower(),))
|
||||||
|
result = cursor.fetchone()
|
||||||
|
|
||||||
|
if result:
|
||||||
|
return result[0]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def change_password(username, new_password):
|
||||||
|
|
||||||
|
# Hash the new password
|
||||||
|
new_hashed_pw = bcrypt.hashpw(new_password.encode('utf-8'), bcrypt.gensalt())
|
||||||
|
|
||||||
|
# Update the password in the database
|
||||||
|
with sqlite3.connect(f'{os.getcwd()}/databases/sql/users.db') as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute('UPDATE user_info SET Password = ? WHERE Username = ?', (new_hashed_pw, username.lower()))
|
||||||
|
conn.commit()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def change_api_key(username, new_api_key):
|
||||||
|
# Update the API key in the database
|
||||||
|
with sqlite3.connect(f'{os.getcwd()}/databases/sql/users.db') as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute('UPDATE user_info SET API_Key = ? WHERE Username = ?', (new_api_key, username.lower()))
|
||||||
|
conn.commit()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def fetch_styled_username(username):
|
||||||
|
with sqlite3.connect(f'{os.getcwd()}/databases/sql/users.db') as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute('SELECT Styled_Username FROM user_info WHERE Username = ?', (username.lower(),))
|
||||||
|
result = cursor.fetchone()
|
||||||
|
|
||||||
|
if result:
|
||||||
|
return result[0]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def fetch_username_by_api_key(api_key):
|
||||||
|
with sqlite3.connect(f'{os.getcwd()}/databases/sql/users.db') as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute('SELECT Username FROM user_info WHERE API_Key = ?', (api_key,))
|
||||||
|
result = cursor.fetchone()
|
||||||
|
|
||||||
|
if result:
|
||||||
|
return result[0] # Return the username
|
||||||
|
else:
|
||||||
|
return None # If no user is found for the API key
|
2
main.py
2
main.py
@ -12,6 +12,7 @@ from routes.upload import upload_bp
|
|||||||
from routes.user_info import user_info_bp
|
from routes.user_info import user_info_bp
|
||||||
from routes.register import register_bp
|
from routes.register import register_bp
|
||||||
from routes.login import login_bp
|
from routes.login import login_bp
|
||||||
|
from routes.user_changes import user_change_bp
|
||||||
import os
|
import os
|
||||||
import yaml
|
import yaml
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
@ -30,6 +31,7 @@ app.register_blueprint(user_info_bp)
|
|||||||
app.register_blueprint(upload_bp)
|
app.register_blueprint(upload_bp)
|
||||||
app.register_blueprint(remotecdm_wv_bp)
|
app.register_blueprint(remotecdm_wv_bp)
|
||||||
app.register_blueprint(remotecdm_pr_bp)
|
app.register_blueprint(remotecdm_pr_bp)
|
||||||
|
app.register_blueprint(user_change_bp)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
app.run(debug=True, host='0.0.0.0')
|
app.run(debug=True, host='0.0.0.0')
|
@ -15,7 +15,7 @@ def login():
|
|||||||
return jsonify({'error': f'Missing required field: {required_field}'}), 400
|
return jsonify({'error': f'Missing required field: {required_field}'}), 400
|
||||||
|
|
||||||
if verify_user(data['username'], data['password']):
|
if verify_user(data['username'], data['password']):
|
||||||
session['username'] = data['username'] # Stored securely in a signed cookie
|
session['username'] = data['username'].lower() # Stored securely in a signed cookie
|
||||||
return jsonify({'message': 'Successfully logged in!'})
|
return jsonify({'message': 'Successfully logged in!'})
|
||||||
else:
|
else:
|
||||||
return jsonify({'error': 'Invalid username or password!'}), 401
|
return jsonify({'error': 'Invalid username or password!'}), 401
|
||||||
|
@ -1,29 +1,42 @@
|
|||||||
|
import re
|
||||||
from flask import Blueprint, request, jsonify
|
from flask import Blueprint, request, jsonify
|
||||||
from custom_functions.database.user_db import add_user
|
from custom_functions.database.user_db import add_user
|
||||||
|
import uuid
|
||||||
|
|
||||||
register_bp = Blueprint(
|
register_bp = Blueprint('register_bp', __name__)
|
||||||
'register_bp',
|
|
||||||
__name__,
|
USERNAME_REGEX = re.compile(r'^[A-Za-z0-9_-]+$')
|
||||||
)
|
PASSWORD_REGEX = re.compile(r'^\S+$')
|
||||||
|
|
||||||
@register_bp.route('/register', methods=['POST'])
|
@register_bp.route('/register', methods=['POST'])
|
||||||
def register():
|
def register():
|
||||||
if request.method == 'POST':
|
if request.method != 'POST':
|
||||||
data = request.get_json()
|
return jsonify({'error': 'Method not supported'}), 405
|
||||||
for required_field in ['username', 'password']:
|
|
||||||
if required_field not in data:
|
data = request.get_json()
|
||||||
return jsonify({
|
|
||||||
'error': f'Missing required field: {required_field}'
|
# Check required fields
|
||||||
})
|
for required_field in ['username', 'password']:
|
||||||
if add_user(data['username'], data['password']):
|
if required_field not in data:
|
||||||
return jsonify({
|
return jsonify({'error': f'Missing required field: {required_field}'}), 400
|
||||||
'message': 'User successfully registered!'
|
|
||||||
})
|
username = data['username']
|
||||||
else:
|
password = data['password']
|
||||||
return jsonify({
|
api_key = str(uuid.uuid4())
|
||||||
'error': 'User already exists!'
|
|
||||||
})
|
# Validate username and password
|
||||||
else:
|
if not USERNAME_REGEX.fullmatch(username):
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'error': 'Method not supported'
|
'error': 'Invalid username. Only letters, numbers, hyphens, and underscores are allowed.'
|
||||||
})
|
}), 400
|
||||||
|
|
||||||
|
if not PASSWORD_REGEX.fullmatch(password):
|
||||||
|
return jsonify({
|
||||||
|
'error': 'Invalid password. Spaces are not allowed.'
|
||||||
|
}), 400
|
||||||
|
|
||||||
|
# Attempt to add user
|
||||||
|
if add_user(username, password, api_key):
|
||||||
|
return jsonify({'message': 'User successfully registered!'}), 201
|
||||||
|
else:
|
||||||
|
return jsonify({'error': 'User already exists!'}), 409
|
||||||
|
@ -5,6 +5,8 @@ from pyplayready.device import Device as PlayReadyDevice
|
|||||||
from pyplayready.cdm import Cdm as PlayReadyCDM
|
from pyplayready.cdm import Cdm as PlayReadyCDM
|
||||||
from pyplayready import PSSH as PlayReadyPSSH
|
from pyplayready import PSSH as PlayReadyPSSH
|
||||||
from pyplayready.exceptions import (InvalidSession, TooManySessions, InvalidLicense, InvalidPssh)
|
from pyplayready.exceptions import (InvalidSession, TooManySessions, InvalidLicense, InvalidPssh)
|
||||||
|
from custom_functions.database.user_db import fetch_username_by_api_key
|
||||||
|
from custom_functions.user_checks.device_allowed import user_allowed_to_use_device
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -53,148 +55,169 @@ def remote_cdm_playready_open(device):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
if request.headers['X-Secret-Key'] and str(device).lower() != config['default_pr_cdm'].lower():
|
||||||
|
api_key = request.headers['X-Secret-Key']
|
||||||
|
user = fetch_username_by_api_key(api_key=api_key)
|
||||||
|
if user:
|
||||||
|
if user_allowed_to_use_device(device=device, username=user):
|
||||||
|
pr_device = PlayReadyDevice.load(f'{os.getcwd()}/configs/CDMs/{user}/PR/{device}.prd')
|
||||||
|
cdm = current_app.config['CDM'] = PlayReadyCDM.from_device(pr_device)
|
||||||
|
session_id = cdm.open()
|
||||||
|
return jsonify({
|
||||||
|
'message': 'Success',
|
||||||
|
'data': {
|
||||||
|
'session_id': session_id.hex(),
|
||||||
|
'device': {
|
||||||
|
'security_level': cdm.security_level
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
return jsonify({
|
||||||
|
'message': f"Device '{device}' is not found or you are not authorized to use it.",
|
||||||
|
}), 403
|
||||||
|
else:
|
||||||
|
return jsonify({
|
||||||
|
'message': f"Device '{device}' is not found or you are not authorized to use it.",
|
||||||
|
}), 403
|
||||||
|
else:
|
||||||
|
return jsonify({
|
||||||
|
'message': f"Device '{device}' is not found or you are not authorized to use it.",
|
||||||
|
}), 403
|
||||||
|
|
||||||
@remotecdm_pr_bp.route('/remotecdm/playready/<device>/close/<session_id>', methods=['GET'])
|
@remotecdm_pr_bp.route('/remotecdm/playready/<device>/close/<session_id>', methods=['GET'])
|
||||||
def remote_cdm_playready_close(device, session_id):
|
def remote_cdm_playready_close(device, session_id):
|
||||||
if str(device).lower() == config['default_pr_cdm'].lower():
|
try:
|
||||||
session_id = bytes.fromhex(session_id)
|
session_id = bytes.fromhex(session_id)
|
||||||
cdm = current_app.config["CDM"]
|
cdm = current_app.config["CDM"]
|
||||||
if not cdm:
|
if not cdm:
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'status': 400,
|
|
||||||
'message': f'No CDM for "{device}" has been opened yet. No session to close'
|
'message': f'No CDM for "{device}" has been opened yet. No session to close'
|
||||||
})
|
}), 400
|
||||||
try:
|
try:
|
||||||
cdm.close(session_id)
|
cdm.close(session_id)
|
||||||
except InvalidSession:
|
except InvalidSession:
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'status': 400,
|
|
||||||
'message': f'Invalid session ID "{session_id.hex()}", it may have expired'
|
'message': f'Invalid session ID "{session_id.hex()}", it may have expired'
|
||||||
})
|
}), 400
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'status': 200,
|
|
||||||
'message': f'Successfully closed Session "{session_id.hex()}".',
|
'message': f'Successfully closed Session "{session_id.hex()}".',
|
||||||
})
|
}), 200
|
||||||
else:
|
except Exception as e:
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'status': 400,
|
'message': f'Failed to close Session "{session_id.hex()}".'
|
||||||
'message': f'Unauthorized'
|
}), 400
|
||||||
})
|
|
||||||
|
|
||||||
@remotecdm_pr_bp.route('/remotecdm/playready/<device>/get_license_challenge', methods=['POST'])
|
@remotecdm_pr_bp.route('/remotecdm/playready/<device>/get_license_challenge', methods=['POST'])
|
||||||
def remote_cdm_playready_get_license_challenge(device):
|
def remote_cdm_playready_get_license_challenge(device):
|
||||||
if str(device).lower() == config['default_pr_cdm'].lower():
|
body = request.get_json()
|
||||||
body = request.get_json()
|
for required_field in ("session_id", "init_data"):
|
||||||
for required_field in ("session_id", "init_data"):
|
if not body.get(required_field):
|
||||||
if not body.get(required_field):
|
return jsonify({
|
||||||
return jsonify({
|
'message': f'Missing required field "{required_field}" in JSON body'
|
||||||
'status': 400,
|
}), 400
|
||||||
'message': f'Missing required field "{required_field}" in JSON body'
|
cdm = current_app.config["CDM"]
|
||||||
})
|
session_id = bytes.fromhex(body["session_id"])
|
||||||
cdm = current_app.config["CDM"]
|
init_data = body["init_data"]
|
||||||
session_id = bytes.fromhex(body["session_id"])
|
if not init_data.startswith("<WRMHEADER"):
|
||||||
init_data = body["init_data"]
|
|
||||||
if not init_data.startswith("<WRMHEADER"):
|
|
||||||
try:
|
|
||||||
pssh = PlayReadyPSSH(init_data)
|
|
||||||
if pssh.wrm_headers:
|
|
||||||
init_data = pssh.wrm_headers[0]
|
|
||||||
except InvalidPssh as e:
|
|
||||||
return jsonify({
|
|
||||||
'message': f'Unable to parse base64 PSSH, {e}'
|
|
||||||
})
|
|
||||||
try:
|
try:
|
||||||
license_request = cdm.get_license_challenge(
|
pssh = PlayReadyPSSH(init_data)
|
||||||
session_id=session_id,
|
if pssh.wrm_headers:
|
||||||
wrm_header=init_data
|
init_data = pssh.wrm_headers[0]
|
||||||
)
|
except InvalidPssh as e:
|
||||||
except InvalidSession:
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'message': f"Invalid Session ID '{session_id.hex()}', it may have expired."
|
'message': f'Unable to parse base64 PSSH, {e}'
|
||||||
})
|
|
||||||
except Exception as e:
|
|
||||||
return jsonify({
|
|
||||||
'message': f'Error, {e}'
|
|
||||||
})
|
})
|
||||||
|
try:
|
||||||
|
license_request = cdm.get_license_challenge(
|
||||||
|
session_id=session_id,
|
||||||
|
wrm_header=init_data
|
||||||
|
)
|
||||||
|
except InvalidSession:
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'message': 'success',
|
'message': f"Invalid Session ID '{session_id.hex()}', it may have expired."
|
||||||
'data': {
|
|
||||||
'challenge': license_request
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({
|
||||||
|
'message': f'Error, {e}'
|
||||||
|
})
|
||||||
|
return jsonify({
|
||||||
|
'message': 'success',
|
||||||
|
'data': {
|
||||||
|
'challenge': license_request
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
@remotecdm_pr_bp.route('/remotecdm/playready/<device>/parse_license', methods=['POST'])
|
@remotecdm_pr_bp.route('/remotecdm/playready/<device>/parse_license', methods=['POST'])
|
||||||
def remote_cdm_playready_parse_license(device):
|
def remote_cdm_playready_parse_license(device):
|
||||||
if str(device).lower() == config['default_pr_cdm'].lower():
|
body = request.get_json()
|
||||||
body = request.get_json()
|
for required_field in ("license_message", "session_id"):
|
||||||
for required_field in ("license_message", "session_id"):
|
if not body.get(required_field):
|
||||||
if not body.get(required_field):
|
|
||||||
return jsonify({
|
|
||||||
'message': f'Missing required field "{required_field}" in JSON body'
|
|
||||||
})
|
|
||||||
cdm = current_app.config["CDM"]
|
|
||||||
if not cdm:
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'message': f"No Cdm session for {device} has been opened yet. No session to use."
|
'message': f'Missing required field "{required_field}" in JSON body'
|
||||||
})
|
|
||||||
session_id = bytes.fromhex(body["session_id"])
|
|
||||||
license_message = body["license_message"]
|
|
||||||
try:
|
|
||||||
cdm.parse_license(session_id, license_message)
|
|
||||||
except InvalidSession:
|
|
||||||
return jsonify({
|
|
||||||
'message': f"Invalid Session ID '{session_id.hex()}', it may have expired."
|
|
||||||
})
|
|
||||||
except InvalidLicense as e:
|
|
||||||
return jsonify({
|
|
||||||
'message': f"Invalid License, {e}"
|
|
||||||
})
|
|
||||||
except Exception as e:
|
|
||||||
return jsonify({
|
|
||||||
'message': f"Error, {e}"
|
|
||||||
})
|
})
|
||||||
|
cdm = current_app.config["CDM"]
|
||||||
|
if not cdm:
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'message': 'Successfully parsed and loaded the Keys from the License message'
|
'message': f"No Cdm session for {device} has been opened yet. No session to use."
|
||||||
})
|
})
|
||||||
|
session_id = bytes.fromhex(body["session_id"])
|
||||||
|
license_message = body["license_message"]
|
||||||
|
try:
|
||||||
|
cdm.parse_license(session_id, license_message)
|
||||||
|
except InvalidSession:
|
||||||
|
return jsonify({
|
||||||
|
'message': f"Invalid Session ID '{session_id.hex()}', it may have expired."
|
||||||
|
})
|
||||||
|
except InvalidLicense as e:
|
||||||
|
return jsonify({
|
||||||
|
'message': f"Invalid License, {e}"
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({
|
||||||
|
'message': f"Error, {e}"
|
||||||
|
})
|
||||||
|
return jsonify({
|
||||||
|
'message': 'Successfully parsed and loaded the Keys from the License message'
|
||||||
|
})
|
||||||
|
|
||||||
@remotecdm_pr_bp.route('/remotecdm/playready/<device>/get_keys', methods=['POST'])
|
@remotecdm_pr_bp.route('/remotecdm/playready/<device>/get_keys', methods=['POST'])
|
||||||
def remote_cdm_playready_get_keys(device):
|
def remote_cdm_playready_get_keys(device):
|
||||||
if str(device).lower() == config['default_pr_cdm'].lower():
|
body = request.get_json()
|
||||||
body = request.get_json()
|
for required_field in ("session_id",):
|
||||||
for required_field in ("session_id",):
|
if not body.get(required_field):
|
||||||
if not body.get(required_field):
|
|
||||||
return jsonify({
|
|
||||||
'message': f'Missing required field "{required_field}" in JSON body'
|
|
||||||
})
|
|
||||||
session_id = bytes.fromhex(body["session_id"])
|
|
||||||
cdm = current_app.config["CDM"]
|
|
||||||
if not cdm:
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'message': f"Missing required field '{required_field}' in JSON body."
|
'message': f'Missing required field "{required_field}" in JSON body'
|
||||||
})
|
})
|
||||||
try:
|
session_id = bytes.fromhex(body["session_id"])
|
||||||
keys = cdm.get_keys(session_id)
|
cdm = current_app.config["CDM"]
|
||||||
except InvalidSession:
|
if not cdm:
|
||||||
return jsonify({
|
|
||||||
'message': f"Invalid Session ID '{session_id.hex()}', it may have expired."
|
|
||||||
})
|
|
||||||
except Exception as e:
|
|
||||||
return jsonify({
|
|
||||||
'message': f"Error, {e}"
|
|
||||||
})
|
|
||||||
keys_json = [
|
|
||||||
{
|
|
||||||
"key_id": key.key_id.hex,
|
|
||||||
"key": key.key.hex(),
|
|
||||||
"type": key.key_type.value,
|
|
||||||
"cipher_type": key.cipher_type.value,
|
|
||||||
"key_length": key.key_length,
|
|
||||||
}
|
|
||||||
for key in keys
|
|
||||||
]
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'message': 'success',
|
'message': f"Missing required field '{required_field}' in JSON body."
|
||||||
'data': {
|
})
|
||||||
'keys': keys_json
|
try:
|
||||||
}
|
keys = cdm.get_keys(session_id)
|
||||||
})
|
except InvalidSession:
|
||||||
|
return jsonify({
|
||||||
|
'message': f"Invalid Session ID '{session_id.hex()}', it may have expired."
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({
|
||||||
|
'message': f"Error, {e}"
|
||||||
|
})
|
||||||
|
keys_json = [
|
||||||
|
{
|
||||||
|
"key_id": key.key_id.hex,
|
||||||
|
"key": key.key.hex(),
|
||||||
|
"type": key.key_type.value,
|
||||||
|
"cipher_type": key.cipher_type.value,
|
||||||
|
"key_length": key.key_length,
|
||||||
|
}
|
||||||
|
for key in keys
|
||||||
|
]
|
||||||
|
return jsonify({
|
||||||
|
'message': 'success',
|
||||||
|
'data': {
|
||||||
|
'keys': keys_json
|
||||||
|
}
|
||||||
|
})
|
@ -11,6 +11,8 @@ from pywidevine.exceptions import (InvalidContext, InvalidInitData, InvalidLicen
|
|||||||
InvalidSession, SignatureMismatch, TooManySessions)
|
InvalidSession, SignatureMismatch, TooManySessions)
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
|
from custom_functions.database.user_db import fetch_api_key, fetch_username_by_api_key
|
||||||
|
from custom_functions.user_checks.device_allowed import user_allowed_to_use_device
|
||||||
|
|
||||||
remotecdm_wv_bp = Blueprint('remotecdm_wv', __name__)
|
remotecdm_wv_bp = Blueprint('remotecdm_wv', __name__)
|
||||||
with open(f'{os.getcwd()}/configs/config.yaml', 'r') as file:
|
with open(f'{os.getcwd()}/configs/config.yaml', 'r') as file:
|
||||||
@ -61,309 +63,307 @@ def remote_cdm_widevine_open(device):
|
|||||||
'security_level': cdm.security_level,
|
'security_level': cdm.security_level,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}), 200
|
||||||
|
if request.headers['X-Secret-Key'] and str(device).lower() != config['default_wv_cdm'].lower():
|
||||||
|
api_key = request.headers['X-Secret-Key']
|
||||||
|
user = fetch_username_by_api_key(api_key=api_key)
|
||||||
|
if user:
|
||||||
|
if user_allowed_to_use_device(device=device, username=user):
|
||||||
|
wv_device = widevineDevice.load(f'{os.getcwd()}/configs/CDMs/{user}/WV/{device}.wvd')
|
||||||
|
cdm = current_app.config["CDM"] = widevineCDM.from_device(wv_device)
|
||||||
|
session_id = cdm.open()
|
||||||
|
return jsonify({
|
||||||
|
'status': 200,
|
||||||
|
'message': 'Success',
|
||||||
|
'data': {
|
||||||
|
'session_id': session_id.hex(),
|
||||||
|
'device': {
|
||||||
|
'system_id': cdm.system_id,
|
||||||
|
'security_level': cdm.security_level,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}), 200
|
||||||
|
else:
|
||||||
|
return jsonify({
|
||||||
|
'message': f"Device '{device}' is not found or you are not authorized to use it.",
|
||||||
|
'status': 403
|
||||||
|
}), 403
|
||||||
|
else:
|
||||||
|
return jsonify({
|
||||||
|
'message': f"Device '{device}' is not found or you are not authorized to use it.",
|
||||||
|
'status': 403
|
||||||
|
}), 403
|
||||||
else:
|
else:
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'status': 400,
|
'message': f"Device '{device}' is not found or you are not authorized to use it.",
|
||||||
'message': 'Unauthorized'
|
'status': 403
|
||||||
})
|
}), 403
|
||||||
|
|
||||||
|
|
||||||
@remotecdm_wv_bp.route('/remotecdm/widevine/<device>/close/<session_id>', methods=['GET'])
|
@remotecdm_wv_bp.route('/remotecdm/widevine/<device>/close/<session_id>', methods=['GET'])
|
||||||
def remote_cdm_widevine_close(device, session_id):
|
def remote_cdm_widevine_close(device, session_id):
|
||||||
if str(device).lower() == config['default_wv_cdm'].lower():
|
|
||||||
session_id = bytes.fromhex(session_id)
|
session_id = bytes.fromhex(session_id)
|
||||||
cdm = current_app.config["CDM"]
|
cdm = current_app.config["CDM"]
|
||||||
if not cdm:
|
if not cdm:
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'status': 400,
|
'status': 400,
|
||||||
'message': f'No CDM for "{device}" has been opened yet. No session to close'
|
'message': f'No CDM for "{device}" has been opened yet. No session to close'
|
||||||
})
|
}), 400
|
||||||
try:
|
try:
|
||||||
cdm.close(session_id)
|
cdm.close(session_id)
|
||||||
except InvalidSession:
|
except InvalidSession:
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'status': 400,
|
'status': 400,
|
||||||
'message': f'Invalid session ID "{session_id.hex()}", it may have expired'
|
'message': f'Invalid session ID "{session_id.hex()}", it may have expired'
|
||||||
})
|
}), 400
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'status': 200,
|
'status': 200,
|
||||||
'message': f'Successfully closed Session "{session_id.hex()}".',
|
'message': f'Successfully closed Session "{session_id.hex()}".',
|
||||||
})
|
}), 200
|
||||||
else:
|
|
||||||
return jsonify({
|
|
||||||
'status': 400,
|
|
||||||
'message': f'Unauthorized'
|
|
||||||
})
|
|
||||||
|
|
||||||
@remotecdm_wv_bp.route('/remotecdm/widevine/<device>/set_service_certificate', methods=['POST'])
|
@remotecdm_wv_bp.route('/remotecdm/widevine/<device>/set_service_certificate', methods=['POST'])
|
||||||
def remote_cdm_widevine_set_service_certificate(device):
|
def remote_cdm_widevine_set_service_certificate(device):
|
||||||
if str(device).lower() == config['default_wv_cdm'].lower():
|
body = request.get_json()
|
||||||
body = request.get_json()
|
for required_field in ("session_id", "certificate"):
|
||||||
for required_field in ("session_id", "certificate"):
|
if required_field == "certificate":
|
||||||
if required_field == "certificate":
|
has_field = required_field in body # it needs the key, but can be empty/null
|
||||||
has_field = required_field in body # it needs the key, but can be empty/null
|
else:
|
||||||
else:
|
has_field = body.get(required_field)
|
||||||
has_field = body.get(required_field)
|
if not has_field:
|
||||||
if not has_field:
|
return jsonify({
|
||||||
return jsonify({
|
'status': 400,
|
||||||
'status': 400,
|
'message': f'Missing required field "{required_field}" in JSON body'
|
||||||
'message': f'Missing required field "{required_field}" in JSON body'
|
}), 400
|
||||||
})
|
|
||||||
|
|
||||||
session_id = bytes.fromhex(body["session_id"])
|
session_id = bytes.fromhex(body["session_id"])
|
||||||
|
|
||||||
cdm = current_app.config["CDM"]
|
cdm = current_app.config["CDM"]
|
||||||
if not cdm:
|
if not cdm:
|
||||||
return jsonify({
|
|
||||||
'status': 400,
|
|
||||||
'message': f'No CDM session for "{device}" has been opened yet. No session to use'
|
|
||||||
})
|
|
||||||
|
|
||||||
certificate = body["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': '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({
|
return jsonify({
|
||||||
'status': 400,
|
'status': 400,
|
||||||
'message': f'Unauthorized'
|
'message': f'No CDM session for "{device}" has been opened yet. No session to use'
|
||||||
})
|
}), 400
|
||||||
|
|
||||||
|
certificate = body["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'
|
||||||
|
}), 400
|
||||||
|
except DecodeError as error:
|
||||||
|
return jsonify({
|
||||||
|
'status': 400,
|
||||||
|
'message': f'Invalid Service Certificate, {error}'
|
||||||
|
}), 400
|
||||||
|
except SignatureMismatch:
|
||||||
|
return jsonify({
|
||||||
|
'status': 400,
|
||||||
|
'message': 'Signature Validation failed on the Service Certificate, rejecting'
|
||||||
|
}), 400
|
||||||
|
return jsonify({
|
||||||
|
'status': 200,
|
||||||
|
'message': f"Successfully {['set', 'unset'][not certificate]} the Service Certificate.",
|
||||||
|
'data': {
|
||||||
|
'provider_id': provider_id,
|
||||||
|
}
|
||||||
|
}), 200
|
||||||
|
|
||||||
@remotecdm_wv_bp.route('/remotecdm/widevine/<device>/get_service_certificate', methods=['POST'])
|
@remotecdm_wv_bp.route('/remotecdm/widevine/<device>/get_service_certificate', methods=['POST'])
|
||||||
def remote_cdm_widevine_get_service_certificate(device):
|
def remote_cdm_widevine_get_service_certificate(device):
|
||||||
if str(device).lower() == config['default_wv_cdm'].lower():
|
body = request.get_json()
|
||||||
body = request.get_json()
|
for required_field in ("session_id",):
|
||||||
for required_field in ("session_id",):
|
if not body.get(required_field):
|
||||||
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"]
|
|
||||||
|
|
||||||
if not cdm:
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'status': 400,
|
'status': 400,
|
||||||
'message': f'No CDM session for "{device}" has been opened yet. No session to use'
|
'message': f'Missing required field "{required_field}" in JSON body'
|
||||||
})
|
}), 400
|
||||||
|
|
||||||
try:
|
session_id = bytes.fromhex(body["session_id"])
|
||||||
service_certificate = cdm.get_service_certificate(session_id)
|
|
||||||
except InvalidSession:
|
cdm = current_app.config["CDM"]
|
||||||
return jsonify({
|
|
||||||
'status': 400,
|
if not cdm:
|
||||||
'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({
|
return jsonify({
|
||||||
'status': 400,
|
'status': 400,
|
||||||
'message': f'Unauthorized'
|
'message': f'No CDM session for "{device}" has been opened yet. No session to use'
|
||||||
})
|
}), 400
|
||||||
|
|
||||||
|
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'
|
||||||
|
}), 400
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}), 200
|
||||||
|
|
||||||
@remotecdm_wv_bp.route('/remotecdm/widevine/<device>/get_license_challenge/<license_type>', methods=['POST'])
|
@remotecdm_wv_bp.route('/remotecdm/widevine/<device>/get_license_challenge/<license_type>', methods=['POST'])
|
||||||
def remote_cdm_widevine_get_license_challenge(device, license_type):
|
def remote_cdm_widevine_get_license_challenge(device, license_type):
|
||||||
if str(device).lower() == config['default_wv_cdm'].lower():
|
body = request.get_json()
|
||||||
body = request.get_json()
|
for required_field in ("session_id", "init_data"):
|
||||||
for required_field in ("session_id", "init_data"):
|
if not body.get(required_field):
|
||||||
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:
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'status': 400,
|
'status': 400,
|
||||||
'message': f'No CDM session for "{device}" has been opened yet. No session to use'
|
'message': f'Missing required field "{required_field}" in JSON body'
|
||||||
})
|
}), 400
|
||||||
if current_app.config.get("force_privacy_mode"):
|
session_id = bytes.fromhex(body["session_id"])
|
||||||
privacy_mode = True
|
privacy_mode = body.get("privacy_mode", True)
|
||||||
if not cdm.get_service_certificate(session_id):
|
cdm = current_app.config["CDM"]
|
||||||
return jsonify({
|
if not cdm:
|
||||||
'status': 403,
|
|
||||||
'message': 'No Service Certificate set but Privacy Mode is Enforced.'
|
|
||||||
})
|
|
||||||
|
|
||||||
current_app.config['pssh'] = body['init_data']
|
|
||||||
init_data = widevinePSSH(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({
|
return jsonify({
|
||||||
'status': 400,
|
'status': 400,
|
||||||
'message': f'Unauthorized'
|
'message': f'No CDM session for "{device}" has been opened yet. No session to use'
|
||||||
})
|
}), 400
|
||||||
|
if current_app.config.get("force_privacy_mode"):
|
||||||
|
privacy_mode = True
|
||||||
|
if not cdm.get_service_certificate(session_id):
|
||||||
|
return jsonify({
|
||||||
|
'status': 403,
|
||||||
|
'message': 'No Service Certificate set but Privacy Mode is Enforced.'
|
||||||
|
}), 403
|
||||||
|
|
||||||
|
current_app.config['pssh'] = body['init_data']
|
||||||
|
init_data = widevinePSSH(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'
|
||||||
|
}), 400
|
||||||
|
except InvalidInitData as error:
|
||||||
|
return jsonify({
|
||||||
|
'status': 400,
|
||||||
|
'message': f'Invalid Init Data, {error}'
|
||||||
|
}), 400
|
||||||
|
except InvalidLicenseType:
|
||||||
|
return jsonify({
|
||||||
|
'status': 400,
|
||||||
|
'message': f'Invalid License Type {license_type}'
|
||||||
|
}), 400
|
||||||
|
return jsonify({
|
||||||
|
'status': 200,
|
||||||
|
'message': 'Success',
|
||||||
|
'data': {
|
||||||
|
'challenge_b64': base64.b64encode(license_request).decode()
|
||||||
|
}
|
||||||
|
}), 200
|
||||||
|
|
||||||
|
|
||||||
@remotecdm_wv_bp.route('/remotecdm/widevine/<device>/parse_license', methods=['POST'])
|
@remotecdm_wv_bp.route('/remotecdm/widevine/<device>/parse_license', methods=['POST'])
|
||||||
def remote_cdm_widevine_parse_license(device):
|
def remote_cdm_widevine_parse_license(device):
|
||||||
if str(device).lower() == config['default_wv_cdm'].lower():
|
body = request.get_json()
|
||||||
body = request.get_json()
|
for required_field in ("session_id", "license_message"):
|
||||||
for required_field in ("session_id", "license_message"):
|
if not body.get(required_field):
|
||||||
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"]
|
|
||||||
if not cdm:
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'status': 400,
|
'status': 400,
|
||||||
'message': f'No CDM session for "{device}" has been opened yet. No session to use'
|
'message': f'Missing required field "{required_field}" in JSON body'
|
||||||
})
|
}), 400
|
||||||
try:
|
session_id = bytes.fromhex(body["session_id"])
|
||||||
cdm.parse_license(session_id, body['license_message'])
|
cdm = current_app.config["CDM"]
|
||||||
except InvalidLicenseMessage as error:
|
if not cdm:
|
||||||
return jsonify({
|
|
||||||
'status': 400,
|
|
||||||
'message': f'Invalid License Message, {error}'
|
|
||||||
})
|
|
||||||
except InvalidContext as error:
|
|
||||||
return jsonify({
|
|
||||||
'status': 400,
|
|
||||||
'message': f'Invalid Context, {error}'
|
|
||||||
})
|
|
||||||
except InvalidSession:
|
|
||||||
return jsonify({
|
|
||||||
'status': 400,
|
|
||||||
'message': f'Invalid Session ID "{session_id.hex()}", it may have expired'
|
|
||||||
})
|
|
||||||
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.',
|
|
||||||
})
|
|
||||||
else:
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'status': 400,
|
'status': 400,
|
||||||
'message': 'Unauthorized'
|
'message': f'No CDM session for "{device}" has been opened yet. No session to use'
|
||||||
})
|
}), 400
|
||||||
|
try:
|
||||||
|
cdm.parse_license(session_id, body['license_message'])
|
||||||
|
except InvalidLicenseMessage as error:
|
||||||
|
return jsonify({
|
||||||
|
'status': 400,
|
||||||
|
'message': f'Invalid License Message, {error}'
|
||||||
|
}), 400
|
||||||
|
except InvalidContext as error:
|
||||||
|
return jsonify({
|
||||||
|
'status': 400,
|
||||||
|
'message': f'Invalid Context, {error}'
|
||||||
|
}), 400
|
||||||
|
except InvalidSession:
|
||||||
|
return jsonify({
|
||||||
|
'status': 400,
|
||||||
|
'message': f'Invalid Session ID "{session_id.hex()}", it may have expired'
|
||||||
|
}), 400
|
||||||
|
except SignatureMismatch:
|
||||||
|
return jsonify({
|
||||||
|
'status': 400,
|
||||||
|
'message': f'Signature Validation failed on the License Message, rejecting.'
|
||||||
|
}), 400
|
||||||
|
return jsonify({
|
||||||
|
'status': 200,
|
||||||
|
'message': 'Successfully parsed and loaded the Keys from the License message.',
|
||||||
|
}), 200
|
||||||
|
|
||||||
@remotecdm_wv_bp.route('/remotecdm/widevine/<device>/get_keys/<key_type>', methods=['POST'])
|
@remotecdm_wv_bp.route('/remotecdm/widevine/<device>/get_keys/<key_type>', methods=['POST'])
|
||||||
def remote_cdm_widevine_get_keys(device, key_type):
|
def remote_cdm_widevine_get_keys(device, key_type):
|
||||||
if str(device).lower() == config['default_wv_cdm'].lower():
|
body = request.get_json()
|
||||||
body = request.get_json()
|
for required_field in ("session_id",):
|
||||||
for required_field in ("session_id",):
|
if not body.get(required_field):
|
||||||
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: Optional[str] = key_type
|
|
||||||
if key_type == 'ALL':
|
|
||||||
key_type = None
|
|
||||||
cdm = current_app.config["CDM"]
|
|
||||||
if not cdm:
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'status': 400,
|
'status': 400,
|
||||||
'message': f'No CDM session for "{device}" has been opened yet. No session to use'
|
'message': f'Missing required field "{required_field}" in JSON body'
|
||||||
})
|
}), 400
|
||||||
try:
|
session_id = bytes.fromhex(body["session_id"])
|
||||||
keys = cdm.get_keys(session_id, key_type)
|
key_type: Optional[str] = key_type
|
||||||
except InvalidSession:
|
if key_type == 'ALL':
|
||||||
return jsonify({
|
key_type = None
|
||||||
'status': 400,
|
cdm = current_app.config["CDM"]
|
||||||
'message': f'Invalid Session ID "{session_id.hex()}", it may have expired'
|
if not cdm:
|
||||||
})
|
|
||||||
except ValueError as error:
|
|
||||||
return jsonify({
|
|
||||||
'status': 400,
|
|
||||||
'message': f'The 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
|
|
||||||
if not key_type or key.type == key_type
|
|
||||||
]
|
|
||||||
for entry in keys_json:
|
|
||||||
if config['database_type'].lower() != 'mariadb':
|
|
||||||
from custom_functions.database.cache_to_db_sqlite import cache_to_db
|
|
||||||
elif config['database_type'].lower() == 'mariadb':
|
|
||||||
from custom_functions.database.cache_to_db_mariadb import cache_to_db
|
|
||||||
if entry['type'] != 'SIGNING':
|
|
||||||
cache_to_db(pssh=str(current_app.config['pssh']), kid=entry['key_id'], key=entry['key'])
|
|
||||||
|
|
||||||
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'status': 200,
|
'status': 400,
|
||||||
'message': 'Success',
|
'message': f'No CDM session for "{device}" has been opened yet. No session to use'
|
||||||
'data': {
|
}), 400
|
||||||
'keys': keys_json
|
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'
|
||||||
|
}), 400
|
||||||
|
except ValueError as error:
|
||||||
|
return jsonify({
|
||||||
|
'status': 400,
|
||||||
|
'message': f'The Key Type value "{key_type}" is invalid, {error}'
|
||||||
|
}), 400
|
||||||
|
keys_json = [
|
||||||
|
{
|
||||||
|
"key_id": key.kid.hex,
|
||||||
|
"key": key.key.hex(),
|
||||||
|
"type": key.type,
|
||||||
|
"permissions": key.permissions
|
||||||
|
}
|
||||||
|
for key in keys
|
||||||
|
if not key_type or key.type == key_type
|
||||||
|
]
|
||||||
|
for entry in keys_json:
|
||||||
|
if config['database_type'].lower() != 'mariadb':
|
||||||
|
from custom_functions.database.cache_to_db_sqlite import cache_to_db
|
||||||
|
elif config['database_type'].lower() == 'mariadb':
|
||||||
|
from custom_functions.database.cache_to_db_mariadb import cache_to_db
|
||||||
|
if entry['type'] != 'SIGNING':
|
||||||
|
cache_to_db(pssh=str(current_app.config['pssh']), kid=entry['key_id'], key=entry['key'])
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
'status': 200,
|
||||||
|
'message': 'Success',
|
||||||
|
'data': {
|
||||||
|
'keys': keys_json
|
||||||
|
}
|
||||||
|
}), 200
|
54
routes/user_changes.py
Normal file
54
routes/user_changes.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import re
|
||||||
|
from flask import Blueprint, request, jsonify, session
|
||||||
|
from custom_functions.database.user_db import change_password, change_api_key
|
||||||
|
|
||||||
|
user_change_bp = Blueprint('user_change_bp', __name__)
|
||||||
|
|
||||||
|
# Define allowed characters regex (no spaces allowed)
|
||||||
|
PASSWORD_REGEX = re.compile(r'^[A-Za-z0-9!@#$%^&*()_+\-=\[\]{};\'":\\|,.<>\/?`~]+$')
|
||||||
|
|
||||||
|
@user_change_bp.route('/user/change_password', methods=['POST'])
|
||||||
|
def change_password_route():
|
||||||
|
username = session.get('username')
|
||||||
|
if not username:
|
||||||
|
return jsonify({'message': 'False'}), 400
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = request.get_json()
|
||||||
|
new_password = data.get('new_password', '')
|
||||||
|
|
||||||
|
if not PASSWORD_REGEX.match(new_password):
|
||||||
|
return jsonify({'message': 'Invalid password format'}), 400
|
||||||
|
|
||||||
|
change_password(username=username, new_password=new_password)
|
||||||
|
return jsonify({'message': 'True'}), 200
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({'message': 'False'}), 400
|
||||||
|
|
||||||
|
|
||||||
|
@user_change_bp.route('/user/change_api_key', methods=['POST'])
|
||||||
|
def change_api_key_route():
|
||||||
|
# Ensure the user is logged in by checking session for 'username'
|
||||||
|
username = session.get('username')
|
||||||
|
if not username:
|
||||||
|
return jsonify({'message': 'False', 'error': 'User not logged in'}), 400
|
||||||
|
|
||||||
|
# Get the new API key from the request body
|
||||||
|
new_api_key = request.json.get('new_api_key')
|
||||||
|
|
||||||
|
if not new_api_key:
|
||||||
|
return jsonify({'message': 'False', 'error': 'New API key not provided'}), 400
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Call the function to update the API key in the database
|
||||||
|
success = change_api_key(username=username, new_api_key=new_api_key)
|
||||||
|
|
||||||
|
if success:
|
||||||
|
return jsonify({'message': 'True', 'success': 'API key changed successfully'}), 200
|
||||||
|
else:
|
||||||
|
return jsonify({'message': 'False', 'error': 'Failed to change API key'}), 500
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
# Catch any unexpected errors and return a response
|
||||||
|
return jsonify({'message': 'False', 'error': str(e)}), 500
|
@ -2,6 +2,7 @@ from flask import Blueprint, request, jsonify, session
|
|||||||
import os
|
import os
|
||||||
import glob
|
import glob
|
||||||
import logging
|
import logging
|
||||||
|
from custom_functions.database.user_db import fetch_api_key, fetch_styled_username
|
||||||
|
|
||||||
user_info_bp = Blueprint('user_info_bp', __name__)
|
user_info_bp = Blueprint('user_info_bp', __name__)
|
||||||
|
|
||||||
@ -12,14 +13,16 @@ def user_info():
|
|||||||
return jsonify({'message': 'False'}), 400
|
return jsonify({'message': 'False'}), 400
|
||||||
|
|
||||||
try:
|
try:
|
||||||
base_path = os.path.join(os.getcwd(), 'configs', 'CDMs', username)
|
base_path = os.path.join(os.getcwd(), 'configs', 'CDMs', username.lower())
|
||||||
pr_files = [os.path.basename(f) for f in glob.glob(os.path.join(base_path, 'PR', '*.prd'))]
|
pr_files = [os.path.basename(f) for f in glob.glob(os.path.join(base_path, 'PR', '*.prd'))]
|
||||||
wv_files = [os.path.basename(f) for f in glob.glob(os.path.join(base_path, 'WV', '*.wvd'))]
|
wv_files = [os.path.basename(f) for f in glob.glob(os.path.join(base_path, 'WV', '*.wvd'))]
|
||||||
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'Username': username,
|
'Username': username,
|
||||||
'Widevine_Devices': wv_files,
|
'Widevine_Devices': wv_files,
|
||||||
'Playready_Devices': pr_files
|
'Playready_Devices': pr_files,
|
||||||
|
'API_Key': fetch_api_key(username),
|
||||||
|
'Styled_Username': fetch_styled_username(username)
|
||||||
})
|
})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception("Error retrieving device files")
|
logging.exception("Error retrieving device files")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user