Files
OqeeRewind/utils/downloader.py
√(noham)² 5dc55dbf62 Add CLI mode and refactor Oqee downloader workflow
Introduces a command-line interface to main.py for Oqee TV downloads, supporting argument parsing for channel, date, quality, and keys. Refactors stream selection, segment download, decryption, and merging logic for both CLI and interactive modes. Adds new utility modules for DRM key retrieval, segment merging, and decryption. Cleans up and simplifies Oqee client, input, stream, and time utilities for improved maintainability and usability.
2025-12-20 11:43:01 +01:00

72 lines
2.3 KiB
Python

import os
import base64
import requests
from uuid import UUID
from pywidevine.cdm import Cdm
from pywidevine.device import Device
from pywidevine.pssh import PSSH
def fetch_drm_keys(kid: str, api_url: str, api_key: str) -> str:
"""Fetch DRM keys for a given KID.
Args: kid: The key identifier string.
Returns: The DRM key as a string.
"""
headers = {
'Content-Type': 'application/json',
'Api-Key': api_key,
}
data = {"service": "oqee", "kid": kid}
response = requests.post(
api_url, headers=headers, json=data, timeout=10
)
return response.json()["key"]
def generate_pssh(kids: list[str]) -> PSSH:
"""Generate a PSSH box for given KIDs.
Args: kids: List of key identifier strings.
Returns: The PSSH object.
"""
default_pssh = (
"AAAAiHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAAGgIARIQrKzUjhLvvbqkebbW2/EQtBIQ"
"WxKIsxtqP3iaIFYUu9f6xxIQXn4atxoopds39jbUXbiFVBIQUUJpv9uuzWKv4ccKTtooMRIQ"
"ocf9FUFCoGm775zPIBr3HRoAKgAyADgASABQAA=="
)
pssh = PSSH(default_pssh)
pssh.set_key_ids([UUID(kid.replace("-", "").lower()) for kid in kids])
return pssh
def get_keys(kids: list[str], method: dict) -> list[str]:
"""Retrieve DRM keys using the specified method."""
if method["method"] == 'api':
print("Fetching DRM keys via API...")
keys = []
for kid in kids:
key = fetch_drm_keys(kid, method["api_url"], method["api_key"])
keys.append(f"{kid}:{key}")
return keys
else:
print("Fetching DRM keys via Widevine CDM...")
client = method["client_class"]
device = Device.load(method["device_file"])
cdm = Cdm.from_device(device)
session_id = cdm.open()
certificate = client.certificate()
cdm.set_service_certificate(session_id, certificate)
pssh_data = generate_pssh(kids)
challenge = cdm.get_license_challenge(session_id, pssh_data, privacy_mode=True)
license_data = client.license(challenge)
cdm.parse_license(session_id, license_data)
keys = []
for key in cdm.get_keys(session_id):
if key.type=='CONTENT':
keys.append(f"{key.kid.hex}:{key.key.hex()}")
cdm.close(session_id)
return keys