mirror of
https://github.com/unshackle-dl/unshackle.git
synced 2026-05-16 21:59:26 +00:00
feat(dl): cache content keys in-memory to skip duplicate license requests
A title with many tracks sharing the same KID issued one license request per track even when keys were identical. Add an in-memory KID -> key cache shared across all tracks of a single invocation, populated on vault hit and on license success. Subsequent tracks with cached KIDs short-circuit before the vault and license calls, reducing traffic to one request per unique KID.
This commit is contained in:
@@ -558,6 +558,7 @@ class dl:
|
|||||||
|
|
||||||
DRM_TABLE_LOCK = Lock()
|
DRM_TABLE_LOCK = Lock()
|
||||||
EXPORT_LOCK = Lock()
|
EXPORT_LOCK = Lock()
|
||||||
|
LICENSE_KEY_CACHE: dict[UUID, str] = {}
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@@ -2824,6 +2825,26 @@ class dl:
|
|||||||
|
|
||||||
is_track_kid = ["", "*"][kid == track_kid]
|
is_track_kid = ["", "*"][kid == track_kid]
|
||||||
|
|
||||||
|
cached_key = self.LICENSE_KEY_CACHE.get(kid)
|
||||||
|
if cached_key:
|
||||||
|
drm.content_keys[kid] = cached_key
|
||||||
|
label = f"[text2]{kid.hex}:{cached_key}{is_track_kid} from cache"
|
||||||
|
if not any(f"{kid.hex}:{cached_key}" in x.label for x in cek_tree.children):
|
||||||
|
cek_tree.add(label)
|
||||||
|
if self.debug_logger:
|
||||||
|
self.debug_logger.log(
|
||||||
|
level="INFO",
|
||||||
|
operation="license_cache_hit",
|
||||||
|
service=self.service,
|
||||||
|
context={
|
||||||
|
"kid": kid.hex,
|
||||||
|
"content_key": cached_key,
|
||||||
|
"track": str(track),
|
||||||
|
"drm_type": "Widevine",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
if not cdm_only:
|
if not cdm_only:
|
||||||
content_key, vault_used = self.vaults.get_key(kid)
|
content_key, vault_used = self.vaults.get_key(kid)
|
||||||
if content_key:
|
if content_key:
|
||||||
@@ -2832,6 +2853,7 @@ class dl:
|
|||||||
if not any(f"{kid.hex}:{content_key}" in x.label for x in cek_tree.children):
|
if not any(f"{kid.hex}:{content_key}" in x.label for x in cek_tree.children):
|
||||||
cek_tree.add(label)
|
cek_tree.add(label)
|
||||||
self.vaults.add_key(kid, content_key, excluding=vault_used)
|
self.vaults.add_key(kid, content_key, excluding=vault_used)
|
||||||
|
self.LICENSE_KEY_CACHE[kid] = content_key
|
||||||
|
|
||||||
if self.debug_logger:
|
if self.debug_logger:
|
||||||
self.debug_logger.log_vault_query(
|
self.debug_logger.log_vault_query(
|
||||||
@@ -2936,6 +2958,8 @@ class dl:
|
|||||||
# So we re-add the keys from vaults earlier overwriting blanks or removed KIDs data.
|
# So we re-add the keys from vaults earlier overwriting blanks or removed KIDs data.
|
||||||
drm.content_keys.update(from_vaults)
|
drm.content_keys.update(from_vaults)
|
||||||
|
|
||||||
|
self.LICENSE_KEY_CACHE.update(drm.content_keys)
|
||||||
|
|
||||||
successful_caches = self.vaults.add_keys(drm.content_keys)
|
successful_caches = self.vaults.add_keys(drm.content_keys)
|
||||||
self.log.info(
|
self.log.info(
|
||||||
f"Cached {len(drm.content_keys)} Key{'' if len(drm.content_keys) == 1 else 's'} to "
|
f"Cached {len(drm.content_keys)} Key{'' if len(drm.content_keys) == 1 else 's'} to "
|
||||||
@@ -3002,6 +3026,26 @@ class dl:
|
|||||||
|
|
||||||
is_track_kid = ["", "*"][kid == track_kid]
|
is_track_kid = ["", "*"][kid == track_kid]
|
||||||
|
|
||||||
|
cached_key = self.LICENSE_KEY_CACHE.get(kid)
|
||||||
|
if cached_key:
|
||||||
|
drm.content_keys[kid] = cached_key
|
||||||
|
label = f"[text2]{kid.hex}:{cached_key}{is_track_kid} from cache"
|
||||||
|
if not any(f"{kid.hex}:{cached_key}" in x.label for x in cek_tree.children):
|
||||||
|
cek_tree.add(label)
|
||||||
|
if self.debug_logger:
|
||||||
|
self.debug_logger.log(
|
||||||
|
level="INFO",
|
||||||
|
operation="license_cache_hit",
|
||||||
|
service=self.service,
|
||||||
|
context={
|
||||||
|
"kid": kid.hex,
|
||||||
|
"content_key": cached_key,
|
||||||
|
"track": str(track),
|
||||||
|
"drm_type": "PlayReady",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
if not cdm_only:
|
if not cdm_only:
|
||||||
content_key, vault_used = self.vaults.get_key(kid)
|
content_key, vault_used = self.vaults.get_key(kid)
|
||||||
if content_key:
|
if content_key:
|
||||||
@@ -3010,6 +3054,7 @@ class dl:
|
|||||||
if not any(f"{kid.hex}:{content_key}" in x.label for x in cek_tree.children):
|
if not any(f"{kid.hex}:{content_key}" in x.label for x in cek_tree.children):
|
||||||
cek_tree.add(label)
|
cek_tree.add(label)
|
||||||
self.vaults.add_key(kid, content_key, excluding=vault_used)
|
self.vaults.add_key(kid, content_key, excluding=vault_used)
|
||||||
|
self.LICENSE_KEY_CACHE[kid] = content_key
|
||||||
|
|
||||||
if self.debug_logger:
|
if self.debug_logger:
|
||||||
self.debug_logger.log_vault_query(
|
self.debug_logger.log_vault_query(
|
||||||
@@ -3084,6 +3129,8 @@ class dl:
|
|||||||
|
|
||||||
drm.content_keys.update(from_vaults)
|
drm.content_keys.update(from_vaults)
|
||||||
|
|
||||||
|
self.LICENSE_KEY_CACHE.update(drm.content_keys)
|
||||||
|
|
||||||
successful_caches = self.vaults.add_keys(drm.content_keys)
|
successful_caches = self.vaults.add_keys(drm.content_keys)
|
||||||
self.log.info(
|
self.log.info(
|
||||||
f"Cached {len(drm.content_keys)} Key{'' if len(drm.content_keys) == 1 else 's'} to "
|
f"Cached {len(drm.content_keys)} Key{'' if len(drm.content_keys) == 1 else 's'} to "
|
||||||
|
|||||||
Reference in New Issue
Block a user