Cache tokens and improve Oqee login flow

Add local token caching and validation for Oqee client, including _is_token_valid, load_cache and save_cache using .oqee_token.json to reuse valid JWTs and avoid unnecessary re-authentication. Update login() to try loading cache first, keep existing credential/IP login flows with clearer logging and improved fallback/error handling, and save tokens after successful auth. Also add required imports (json, os, time) and minor logging tweak in utils/downloader.py to include the Widevine CDM device filename in the fetch message.
This commit is contained in:
√(noham)²
2026-05-26 17:21:54 +02:00
parent c49254a6e4
commit 8f115881e2
2 changed files with 86 additions and 31 deletions

View File

@@ -51,7 +51,8 @@ def get_keys(kids: list[str], method: dict) -> list[str]:
keys.append(f"{kid}:{key}")
return keys
logger.info("Fetching DRM keys via Widevine CDM...")
cdm_name = (method["device_file"]).split("/")[-1]
logger.info(f"Fetching DRM keys via Widevine CDM {cdm_name}...")
client = method["client_class"]
device = Device.load(method["device_file"])

View File

@@ -1,6 +1,9 @@
"""OQEE streaming service client for authentication and content access."""
import base64
import json
import os
import time
from urllib.parse import urlparse, parse_qs
from bs4 import BeautifulSoup
import requests
@@ -92,6 +95,50 @@ class OqeeClient: # pylint: disable=too-many-instance-attributes
logger.info("Logging in")
self.login(username, password)
def _is_token_valid(self, token):
"""Check if JWT token is still valid (checking expiry)."""
if not token:
return False
try:
payload_b64 = token.split(".")[1]
payload_b64 += "=" * (-len(payload_b64) % 4)
payload = json.loads(base64.b64decode(payload_b64))
return payload.get("exp", 0) > time.time()
except Exception:
return False
def load_cache(self):
"""Load tokens from cache file."""
if os.path.exists(".oqee_token.json"):
try:
with open(".oqee_token.json", "r", encoding="utf-8") as f:
cache = json.load(f)
if self._is_token_valid(
cache.get("access_token")
) and self._is_token_valid(cache.get("right_token")):
self.access_token = cache.get("access_token")
self.right_token = cache.get("right_token")
self.profil_id = cache.get("profil_id")
return True
except Exception as e:
logger.debug("Failed to load token cache: %s", e)
return False
def save_cache(self):
"""Save tokens to cache file."""
try:
with open(".oqee_token.json", "w", encoding="utf-8") as f:
json.dump(
{
"access_token": self.access_token,
"right_token": self.right_token,
"profil_id": self.profil_id,
},
f,
)
except Exception as e:
logger.debug("Failed to save token cache: %s", e)
def _build_headers(self, overrides=None, remove=None):
"""Clone default headers and apply optional overrides/removals."""
headers = self._headers_template.copy()
@@ -338,6 +385,9 @@ class OqeeClient: # pylint: disable=too-many-instance-attributes
"""
Log in to the Oqee service and set up necessary tokens and headers.
"""
if self.load_cache():
logger.info("Loaded valid tokens from cache.")
else:
if not username or not password:
logger.info("No credentials provided, using IP login.")
self.access_token = self.login_ip()
@@ -353,7 +403,9 @@ class OqeeClient: # pylint: disable=too-many-instance-attributes
logger.info(
"Logging in with provided credentials (abo account detected, non-FBX)."
)
self.access_token = self.login_cred_mobile(username, password)
self.access_token = self.login_cred_mobile(
username, password
)
else:
logger.info(
"Logging in with provided credentials (free account detected)."
@@ -375,6 +427,8 @@ class OqeeClient: # pylint: disable=too-many-instance-attributes
self.profil_id = self.profil()
logger.debug("Profile ID obtained: %s", self.profil_id)
self.save_cache()
self.headers = self._build_headers(
overrides={
"x-fbx-rights-token": self.right_token,