2025-04-24 17:06:14 -04:00
|
|
|
import os
|
|
|
|
import sqlite3
|
2025-04-30 03:42:38 -04:00
|
|
|
from flask import Blueprint, jsonify, request, send_file, session
|
2025-04-24 17:06:14 -04:00
|
|
|
import json
|
|
|
|
from custom_functions.decrypt.api_decrypt import api_decrypt
|
2025-04-30 03:42:38 -04:00
|
|
|
from custom_functions.user_checks.device_allowed import user_allowed_to_use_device
|
2025-04-24 17:06:14 -04:00
|
|
|
import shutil
|
|
|
|
import math
|
|
|
|
import yaml
|
|
|
|
import mysql.connector
|
|
|
|
from io import StringIO
|
|
|
|
import tempfile
|
|
|
|
import time
|
2025-04-28 18:06:52 -04:00
|
|
|
from configs.icon_links import data as icon_data
|
2025-04-24 17:06:14 -04:00
|
|
|
|
2025-07-22 20:01:22 +07:00
|
|
|
api_bp = Blueprint("api", __name__)
|
|
|
|
with open(f"{os.getcwd()}/configs/config.yaml", "r") as file:
|
2025-04-24 17:06:14 -04:00
|
|
|
config = yaml.safe_load(file)
|
2025-07-22 20:01:22 +07:00
|
|
|
if config["database_type"].lower() != "mariadb":
|
|
|
|
from custom_functions.database.cache_to_db_sqlite import (
|
|
|
|
search_by_pssh_or_kid,
|
|
|
|
cache_to_db,
|
|
|
|
get_key_by_kid_and_service,
|
|
|
|
get_unique_services,
|
|
|
|
get_kid_key_dict,
|
|
|
|
key_count,
|
|
|
|
)
|
|
|
|
elif config["database_type"].lower() == "mariadb":
|
|
|
|
from custom_functions.database.cache_to_db_mariadb import (
|
|
|
|
search_by_pssh_or_kid,
|
|
|
|
cache_to_db,
|
|
|
|
get_key_by_kid_and_service,
|
|
|
|
get_unique_services,
|
|
|
|
get_kid_key_dict,
|
|
|
|
key_count,
|
|
|
|
)
|
|
|
|
|
2025-04-24 17:06:14 -04:00
|
|
|
|
|
|
|
def get_db_config():
|
|
|
|
# Configure your MariaDB connection
|
2025-07-22 20:01:22 +07:00
|
|
|
with open(f"{os.getcwd()}/configs/config.yaml", "r") as file:
|
2025-04-24 17:06:14 -04:00
|
|
|
config = yaml.safe_load(file)
|
|
|
|
db_config = {
|
2025-07-22 20:01:22 +07:00
|
|
|
"host": f'{config["mariadb"]["host"]}',
|
|
|
|
"user": f'{config["mariadb"]["user"]}',
|
|
|
|
"password": f'{config["mariadb"]["password"]}',
|
|
|
|
"database": f'{config["mariadb"]["database"]}',
|
2025-04-24 17:06:14 -04:00
|
|
|
}
|
|
|
|
return db_config
|
|
|
|
|
2025-07-22 20:01:22 +07:00
|
|
|
|
|
|
|
@api_bp.route("/api/cache/search", methods=["POST"])
|
2025-04-24 17:06:14 -04:00
|
|
|
def get_data():
|
2025-07-22 20:01:22 +07:00
|
|
|
search_argument = json.loads(request.data)["input"]
|
2025-04-24 17:06:14 -04:00
|
|
|
results = search_by_pssh_or_kid(search_filter=search_argument)
|
|
|
|
return jsonify(results)
|
|
|
|
|
2025-07-22 20:01:22 +07:00
|
|
|
|
|
|
|
@api_bp.route("/api/cache/<service>/<kid>", methods=["GET"])
|
2025-04-24 17:06:14 -04:00
|
|
|
def get_single_key_service(service, kid):
|
|
|
|
result = get_key_by_kid_and_service(kid=kid, service=service)
|
2025-07-22 20:01:22 +07:00
|
|
|
return jsonify(
|
|
|
|
{
|
|
|
|
"code": 0,
|
|
|
|
"content_key": result,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2025-04-24 17:06:14 -04:00
|
|
|
|
2025-07-22 20:01:22 +07:00
|
|
|
@api_bp.route("/api/cache/<service>", methods=["GET"])
|
2025-04-24 17:06:14 -04:00
|
|
|
def get_multiple_key_service(service):
|
|
|
|
result = get_kid_key_dict(service_name=service)
|
|
|
|
pages = math.ceil(len(result) / 10)
|
2025-07-22 20:01:22 +07:00
|
|
|
return jsonify({"code": 0, "content_keys": result, "pages": pages})
|
2025-04-24 17:06:14 -04:00
|
|
|
|
2025-07-22 20:01:22 +07:00
|
|
|
|
|
|
|
@api_bp.route("/api/cache/<service>/<kid>", methods=["POST"])
|
2025-04-24 17:06:14 -04:00
|
|
|
def add_single_key_service(service, kid):
|
|
|
|
body = request.get_json()
|
2025-07-22 20:01:22 +07:00
|
|
|
content_key = body["content_key"]
|
2025-04-24 17:06:14 -04:00
|
|
|
result = cache_to_db(service=service, kid=kid, key=content_key)
|
|
|
|
if result:
|
2025-07-22 20:01:22 +07:00
|
|
|
return jsonify(
|
|
|
|
{
|
|
|
|
"code": 0,
|
|
|
|
"updated": True,
|
|
|
|
}
|
|
|
|
)
|
2025-04-24 17:06:14 -04:00
|
|
|
elif result is False:
|
2025-07-22 20:01:22 +07:00
|
|
|
return jsonify(
|
|
|
|
{
|
|
|
|
"code": 0,
|
|
|
|
"updated": True,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2025-04-24 17:06:14 -04:00
|
|
|
|
2025-07-22 20:01:22 +07:00
|
|
|
@api_bp.route("/api/cache/<service>", methods=["POST"])
|
2025-04-24 17:06:14 -04:00
|
|
|
def add_multiple_key_service(service):
|
|
|
|
body = request.get_json()
|
|
|
|
keys_added = 0
|
|
|
|
keys_updated = 0
|
2025-07-22 20:01:22 +07:00
|
|
|
for kid, key in body["content_keys"].items():
|
2025-04-24 17:06:14 -04:00
|
|
|
result = cache_to_db(service=service, kid=kid, key=key)
|
|
|
|
if result is True:
|
|
|
|
keys_updated += 1
|
|
|
|
elif result is False:
|
|
|
|
keys_added += 1
|
2025-07-22 20:01:22 +07:00
|
|
|
return jsonify(
|
|
|
|
{
|
|
|
|
"code": 0,
|
|
|
|
"added": str(keys_added),
|
|
|
|
"updated": str(keys_updated),
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2025-04-24 17:06:14 -04:00
|
|
|
|
2025-07-22 20:01:22 +07:00
|
|
|
@api_bp.route("/api/cache", methods=["POST"])
|
2025-04-24 17:06:14 -04:00
|
|
|
def unique_service():
|
|
|
|
services = get_unique_services()
|
2025-07-22 20:01:22 +07:00
|
|
|
return jsonify(
|
|
|
|
{
|
|
|
|
"code": 0,
|
|
|
|
"service_list": services,
|
|
|
|
}
|
|
|
|
)
|
2025-04-24 17:06:14 -04:00
|
|
|
|
|
|
|
|
2025-07-22 20:01:22 +07:00
|
|
|
@api_bp.route("/api/cache/download", methods=["GET"])
|
2025-04-24 17:06:14 -04:00
|
|
|
def download_database():
|
2025-07-22 20:01:22 +07:00
|
|
|
if config["database_type"].lower() != "mariadb":
|
|
|
|
original_database_path = f"{os.getcwd()}/databases/sql/key_cache.db"
|
2025-04-24 17:06:14 -04:00
|
|
|
|
|
|
|
# Make a copy of the original database (without locking the original)
|
2025-07-22 20:01:22 +07:00
|
|
|
modified_database_path = f"{os.getcwd()}/databases/sql/key_cache_modified.db"
|
2025-04-24 17:06:14 -04:00
|
|
|
|
|
|
|
# Using shutil.copy2 to preserve metadata (timestamps, etc.)
|
|
|
|
shutil.copy2(original_database_path, modified_database_path)
|
|
|
|
|
|
|
|
# Open the copied database for modification using 'with' statement to avoid locks
|
|
|
|
with sqlite3.connect(modified_database_path) as conn:
|
|
|
|
cursor = conn.cursor()
|
|
|
|
|
|
|
|
# Update all rows to remove Headers and Cookies (set them to NULL or empty strings)
|
2025-07-22 20:01:22 +07:00
|
|
|
cursor.execute(
|
|
|
|
"""
|
2025-04-24 17:06:14 -04:00
|
|
|
UPDATE licenses
|
|
|
|
SET Headers = NULL,
|
|
|
|
Cookies = NULL
|
2025-07-22 20:01:22 +07:00
|
|
|
"""
|
|
|
|
)
|
2025-04-24 17:06:14 -04:00
|
|
|
|
|
|
|
# No need for explicit commit, it's done automatically with the 'with' block
|
|
|
|
# The connection will automatically be committed and closed when the block ends
|
|
|
|
|
|
|
|
# Send the modified database as an attachment
|
2025-07-22 20:01:22 +07:00
|
|
|
return send_file(
|
|
|
|
modified_database_path, as_attachment=True, download_name="key_cache.db"
|
|
|
|
)
|
|
|
|
if config["database_type"].lower() == "mariadb":
|
2025-04-24 17:06:14 -04:00
|
|
|
try:
|
|
|
|
# Connect to MariaDB
|
|
|
|
conn = mysql.connector.connect(**get_db_config())
|
|
|
|
cursor = conn.cursor()
|
|
|
|
|
|
|
|
# Update sensitive data (this updates the live DB, you may want to duplicate rows instead)
|
2025-07-22 20:01:22 +07:00
|
|
|
cursor.execute(
|
|
|
|
"""
|
2025-04-24 17:06:14 -04:00
|
|
|
UPDATE licenses
|
|
|
|
SET Headers = NULL,
|
|
|
|
Cookies = NULL
|
2025-07-22 20:01:22 +07:00
|
|
|
"""
|
|
|
|
)
|
2025-04-24 17:06:14 -04:00
|
|
|
|
|
|
|
conn.commit()
|
|
|
|
|
|
|
|
# Now export the table
|
2025-07-22 20:01:22 +07:00
|
|
|
cursor.execute("SELECT * FROM licenses")
|
2025-04-24 17:06:14 -04:00
|
|
|
rows = cursor.fetchall()
|
|
|
|
column_names = [desc[0] for desc in cursor.description]
|
|
|
|
|
|
|
|
# Dump to SQL-like format
|
|
|
|
output = StringIO()
|
|
|
|
output.write(f"-- Dump of `licenses` table\n")
|
|
|
|
for row in rows:
|
2025-07-22 20:01:22 +07:00
|
|
|
values = ", ".join(
|
|
|
|
f"'{str(v).replace('\'', '\\\'')}'" if v is not None else "NULL"
|
|
|
|
for v in row
|
|
|
|
)
|
|
|
|
output.write(
|
|
|
|
f"INSERT INTO licenses ({', '.join(column_names)}) VALUES ({values});\n"
|
|
|
|
)
|
2025-04-24 17:06:14 -04:00
|
|
|
|
|
|
|
# Write to a temp file for download
|
|
|
|
temp_dir = tempfile.gettempdir()
|
2025-07-22 20:01:22 +07:00
|
|
|
temp_path = os.path.join(temp_dir, "key_cache.sql")
|
|
|
|
with open(temp_path, "w", encoding="utf-8") as f:
|
2025-04-24 17:06:14 -04:00
|
|
|
f.write(output.getvalue())
|
|
|
|
|
2025-07-22 20:01:22 +07:00
|
|
|
return send_file(
|
|
|
|
temp_path, as_attachment=True, download_name="licenses_dump.sql"
|
|
|
|
)
|
2025-04-24 17:06:14 -04:00
|
|
|
except mysql.connector.Error as err:
|
|
|
|
return {"error": str(err)}, 500
|
|
|
|
|
|
|
|
|
2025-07-22 20:01:22 +07:00
|
|
|
_keycount_cache = {"count": None, "timestamp": 0}
|
|
|
|
|
|
|
|
|
|
|
|
@api_bp.route("/api/cache/keycount", methods=["GET"])
|
2025-04-24 17:06:14 -04:00
|
|
|
def get_count():
|
|
|
|
now = time.time()
|
2025-07-22 20:01:22 +07:00
|
|
|
if now - _keycount_cache["timestamp"] > 10 or _keycount_cache["count"] is None:
|
|
|
|
_keycount_cache["count"] = key_count()
|
|
|
|
_keycount_cache["timestamp"] = now
|
|
|
|
return jsonify({"count": _keycount_cache["count"]})
|
|
|
|
|
|
|
|
|
|
|
|
@api_bp.route("/api/decrypt", methods=["POST"])
|
2025-04-24 17:06:14 -04:00
|
|
|
def decrypt_data():
|
|
|
|
api_request_data = json.loads(request.data)
|
2025-07-22 20:01:22 +07:00
|
|
|
if "pssh" in api_request_data:
|
|
|
|
if api_request_data["pssh"] == "":
|
2025-04-24 17:06:14 -04:00
|
|
|
api_request_pssh = None
|
|
|
|
else:
|
2025-07-22 20:01:22 +07:00
|
|
|
api_request_pssh = api_request_data["pssh"]
|
2025-04-24 17:06:14 -04:00
|
|
|
else:
|
|
|
|
api_request_pssh = None
|
2025-07-22 20:01:22 +07:00
|
|
|
if "licurl" in api_request_data:
|
|
|
|
if api_request_data["licurl"] == "":
|
2025-04-24 17:06:14 -04:00
|
|
|
api_request_licurl = None
|
|
|
|
else:
|
2025-07-22 20:01:22 +07:00
|
|
|
api_request_licurl = api_request_data["licurl"]
|
2025-04-24 17:06:14 -04:00
|
|
|
else:
|
|
|
|
api_request_licurl = None
|
2025-07-22 20:01:22 +07:00
|
|
|
if "proxy" in api_request_data:
|
|
|
|
if api_request_data["proxy"] == "":
|
2025-04-26 19:21:13 -04:00
|
|
|
api_request_proxy = None
|
|
|
|
else:
|
2025-07-22 20:01:22 +07:00
|
|
|
api_request_proxy = api_request_data["proxy"]
|
2025-04-26 19:21:13 -04:00
|
|
|
else:
|
|
|
|
api_request_proxy = None
|
2025-07-22 20:01:22 +07:00
|
|
|
if "headers" in api_request_data:
|
|
|
|
if api_request_data["headers"] == "":
|
2025-04-24 17:06:14 -04:00
|
|
|
api_request_headers = None
|
|
|
|
else:
|
2025-07-22 20:01:22 +07:00
|
|
|
api_request_headers = api_request_data["headers"]
|
2025-04-24 17:06:14 -04:00
|
|
|
else:
|
|
|
|
api_request_headers = None
|
2025-07-22 20:01:22 +07:00
|
|
|
if "cookies" in api_request_data:
|
|
|
|
if api_request_data["cookies"] == "":
|
2025-04-24 17:06:14 -04:00
|
|
|
api_request_cookies = None
|
|
|
|
else:
|
2025-07-22 20:01:22 +07:00
|
|
|
api_request_cookies = api_request_data["cookies"]
|
2025-04-24 17:06:14 -04:00
|
|
|
else:
|
|
|
|
api_request_cookies = None
|
2025-07-22 20:01:22 +07:00
|
|
|
if "data" in api_request_data:
|
|
|
|
if api_request_data["data"] == "":
|
2025-04-30 03:42:38 -04:00
|
|
|
api_request_data_func = None
|
2025-04-24 17:06:14 -04:00
|
|
|
else:
|
2025-07-22 20:01:22 +07:00
|
|
|
api_request_data_func = api_request_data["data"]
|
|
|
|
else:
|
|
|
|
api_request_data_func = None
|
|
|
|
if "device" in api_request_data:
|
|
|
|
if (
|
|
|
|
api_request_data["device"] == "default"
|
|
|
|
or api_request_data["device"] == "CDRM-Project Public Widevine CDM"
|
|
|
|
or api_request_data["device"] == "CDRM-Project Public PlayReady CDM"
|
|
|
|
):
|
|
|
|
api_request_device = "public"
|
2025-04-30 03:42:38 -04:00
|
|
|
else:
|
2025-07-22 20:01:22 +07:00
|
|
|
api_request_device = api_request_data["device"]
|
2025-04-24 17:06:14 -04:00
|
|
|
else:
|
2025-07-22 20:01:22 +07:00
|
|
|
api_request_device = "public"
|
2025-04-30 03:42:38 -04:00
|
|
|
username = None
|
2025-07-22 20:01:22 +07:00
|
|
|
if api_request_device != "public":
|
|
|
|
username = session.get("username")
|
2025-04-30 03:42:38 -04:00
|
|
|
if not username:
|
2025-07-22 20:01:22 +07:00
|
|
|
return jsonify({"message": "Not logged in, not allowed"}), 400
|
2025-04-30 03:42:38 -04:00
|
|
|
if user_allowed_to_use_device(device=api_request_device, username=username):
|
|
|
|
api_request_device = api_request_device
|
|
|
|
else:
|
2025-07-22 20:01:22 +07:00
|
|
|
return jsonify({"message": f"Not authorized / Not found"}), 403
|
|
|
|
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_func,
|
|
|
|
device=api_request_device,
|
|
|
|
username=username,
|
|
|
|
)
|
|
|
|
if result["status"] == "success":
|
|
|
|
return jsonify({"status": "success", "message": result["message"]})
|
2025-04-24 17:06:14 -04:00
|
|
|
else:
|
2025-07-22 20:01:22 +07:00
|
|
|
return jsonify({"status": "fail", "message": result["message"]})
|
|
|
|
|
2025-04-28 18:06:52 -04:00
|
|
|
|
2025-07-22 20:01:22 +07:00
|
|
|
@api_bp.route("/api/links", methods=["GET"])
|
2025-04-28 18:06:52 -04:00
|
|
|
def get_links():
|
2025-07-22 20:01:22 +07:00
|
|
|
return jsonify(
|
|
|
|
{
|
|
|
|
"discord": icon_data["discord"],
|
|
|
|
"telegram": icon_data["telegram"],
|
|
|
|
"gitea": icon_data["gitea"],
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2025-06-01 12:19:19 -04:00
|
|
|
|
2025-07-22 20:01:22 +07:00
|
|
|
@api_bp.route("/api/extension", methods=["POST"])
|
2025-06-01 12:19:19 -04:00
|
|
|
def verify_extension():
|
2025-07-22 20:01:22 +07:00
|
|
|
return jsonify(
|
|
|
|
{
|
|
|
|
"status": True,
|
|
|
|
}
|
|
|
|
)
|