mirror of https://github.com/devine-dl/devine.git
Log DRM license info under track downloads
This commit is contained in:
parent
178bd01069
commit
70106d32ce
|
@ -16,7 +16,7 @@ from copy import deepcopy
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from http.cookiejar import MozillaCookieJar
|
from http.cookiejar import MozillaCookieJar
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from threading import Event
|
from threading import Event, Lock
|
||||||
from typing import Any, Callable, Optional
|
from typing import Any, Callable, Optional
|
||||||
|
|
||||||
import click
|
import click
|
||||||
|
@ -34,6 +34,7 @@ from rich.progress import BarColumn, Progress, SpinnerColumn, TextColumn, TimeRe
|
||||||
from rich.rule import Rule
|
from rich.rule import Rule
|
||||||
from rich.table import Table
|
from rich.table import Table
|
||||||
from rich.text import Text
|
from rich.text import Text
|
||||||
|
from rich.tree import Tree
|
||||||
|
|
||||||
from devine.core.config import config
|
from devine.core.config import config
|
||||||
from devine.core.console import console
|
from devine.core.console import console
|
||||||
|
@ -130,6 +131,7 @@ class dl:
|
||||||
return dl(ctx, **kwargs)
|
return dl(ctx, **kwargs)
|
||||||
|
|
||||||
DL_POOL_STOP = Event()
|
DL_POOL_STOP = Event()
|
||||||
|
DRM_TABLE_LOCK = Lock()
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
@ -421,13 +423,16 @@ class dl:
|
||||||
|
|
||||||
selected_tracks, tracks_progress_callables = title.tracks.tree(add_progress=True)
|
selected_tracks, tracks_progress_callables = title.tracks.tree(add_progress=True)
|
||||||
|
|
||||||
|
download_table = Table.grid()
|
||||||
|
download_table.add_row(selected_tracks)
|
||||||
|
|
||||||
if skip_dl:
|
if skip_dl:
|
||||||
console.log("Skipping Download...")
|
console.log("Skipping Download...")
|
||||||
else:
|
else:
|
||||||
with Live(
|
with Live(
|
||||||
Padding(
|
Padding(
|
||||||
selected_tracks,
|
download_table,
|
||||||
(0, 5, 1, 5)
|
(1, 5)
|
||||||
),
|
),
|
||||||
console=console,
|
console=console,
|
||||||
refresh_per_second=5
|
refresh_per_second=5
|
||||||
|
@ -441,7 +446,10 @@ class dl:
|
||||||
track=track,
|
track=track,
|
||||||
title=title,
|
title=title,
|
||||||
prepare_drm=partial(
|
prepare_drm=partial(
|
||||||
self.prepare_drm,
|
partial(
|
||||||
|
self.prepare_drm,
|
||||||
|
table=download_table
|
||||||
|
),
|
||||||
track=track,
|
track=track,
|
||||||
title=title,
|
title=title,
|
||||||
certificate=partial(
|
certificate=partial(
|
||||||
|
@ -509,6 +517,7 @@ class dl:
|
||||||
title: Title_T,
|
title: Title_T,
|
||||||
certificate: Callable,
|
certificate: Callable,
|
||||||
licence: Callable,
|
licence: Callable,
|
||||||
|
table: Table = None,
|
||||||
cdm_only: bool = False,
|
cdm_only: bool = False,
|
||||||
vaults_only: bool = False,
|
vaults_only: bool = False,
|
||||||
export: Optional[Path] = None
|
export: Optional[Path] = None
|
||||||
|
@ -521,71 +530,95 @@ class dl:
|
||||||
return
|
return
|
||||||
|
|
||||||
if isinstance(drm, Widevine):
|
if isinstance(drm, Widevine):
|
||||||
console.log(f"Licensing Content Keys using Widevine for {drm.pssh.dumps()}")
|
with self.DRM_TABLE_LOCK:
|
||||||
|
cek_tree = Tree(Text.assemble(
|
||||||
|
("Widevine", "cyan"),
|
||||||
|
(f"({drm.pssh.dumps()})", "text"),
|
||||||
|
overflow="fold"
|
||||||
|
))
|
||||||
|
pre_existing_tree = next((
|
||||||
|
x
|
||||||
|
for x in table.columns[0].cells
|
||||||
|
if isinstance(x, Tree) and x.label == cek_tree.label
|
||||||
|
), None)
|
||||||
|
if pre_existing_tree:
|
||||||
|
cek_tree = pre_existing_tree
|
||||||
|
|
||||||
for kid in drm.kids:
|
for kid in drm.kids:
|
||||||
if kid in drm.content_keys:
|
if kid in drm.content_keys:
|
||||||
continue
|
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:
|
||||||
drm.content_keys[kid] = content_key
|
drm.content_keys[kid] = content_key
|
||||||
console.log(f" + {kid.hex}:{content_key} ({vault_used})")
|
label = f"[text2]{kid.hex}:{content_key} from {vault_used}"
|
||||||
add_count = self.vaults.add_key(kid, content_key, excluding=vault_used)
|
if not any(x.label == label for x in cek_tree.children):
|
||||||
console.log(f" + Cached to {add_count}/{len(self.vaults) - 1} Vaults")
|
cek_tree.add(label)
|
||||||
elif vaults_only:
|
self.vaults.add_key(kid, content_key, excluding=vault_used)
|
||||||
self.log.error(f" - No Content Key found in any Vault for {kid.hex}")
|
elif vaults_only:
|
||||||
sys.exit(1)
|
cek_tree.add(f"[logging.level.error]No Vault has a Key for {kid.hex}, cannot decrypt...")
|
||||||
|
if not pre_existing_tree:
|
||||||
|
table.add_row(cek_tree)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
if kid not in drm.content_keys and not vaults_only:
|
if kid not in drm.content_keys and not vaults_only:
|
||||||
from_vaults = drm.content_keys.copy()
|
from_vaults = drm.content_keys.copy()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
drm.get_content_keys(
|
drm.get_content_keys(
|
||||||
cdm=self.cdm,
|
cdm=self.cdm,
|
||||||
licence=licence,
|
licence=licence,
|
||||||
certificate=certificate
|
certificate=certificate
|
||||||
)
|
)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
self.log.error(str(e))
|
cek_tree.add(f"[logging.level.error]{str(e)}")
|
||||||
sys.exit(1)
|
if not pre_existing_tree:
|
||||||
|
table.add_row(cek_tree)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
for kid_, key in drm.content_keys.items():
|
for kid_, key in drm.content_keys.items():
|
||||||
msg = f" + {kid_.hex}:{key}"
|
if key == "0" * 32:
|
||||||
if kid_ == kid:
|
key = f"[red]{key}[/]"
|
||||||
msg += " *"
|
if kid_ == kid:
|
||||||
if key == "0" * 32:
|
key += " *"
|
||||||
msg += " (Unusable!)"
|
label = f"[text2]{kid_.hex}:{key}"
|
||||||
console.log(msg)
|
if not any(x.label == label for x in cek_tree.children):
|
||||||
|
cek_tree.add(label)
|
||||||
|
|
||||||
drm.content_keys = {
|
drm.content_keys = {
|
||||||
kid_: key
|
kid_: key
|
||||||
for kid_, key in drm.content_keys.items()
|
for kid_, key in drm.content_keys.items()
|
||||||
if key and key.count("0") != len(key)
|
if key and key.count("0") != len(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
# The CDM keys may have returned blank content keys for KIDs we got from vaults.
|
# The CDM keys may have returned blank content keys for KIDs we got from vaults.
|
||||||
# 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)
|
||||||
|
|
||||||
cached_keys = self.vaults.add_keys(drm.content_keys)
|
cached_keys = self.vaults.add_keys(drm.content_keys)
|
||||||
console.log(f" + Newly added to {cached_keys}/{len(drm.content_keys)} Vaults")
|
console.log(f" + Newly added to {cached_keys}/{len(drm.content_keys)} Vaults")
|
||||||
|
|
||||||
if kid not in drm.content_keys:
|
if kid not in drm.content_keys:
|
||||||
self.log.error(f" - No usable key was returned for {kid.hex}, cannot continue")
|
cek_tree.add(f"[logging.level.error]No key was returned for {kid.hex}, cannot decrypt...")
|
||||||
sys.exit(1)
|
if not pre_existing_tree:
|
||||||
|
table.add_row(cek_tree)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
if export:
|
if cek_tree.children and not pre_existing_tree:
|
||||||
keys = {}
|
table.add_row()
|
||||||
if export.is_file():
|
table.add_row(cek_tree)
|
||||||
keys = jsonpickle.loads(export.read_text(encoding="utf8"))
|
|
||||||
if str(title) not in keys:
|
if export:
|
||||||
keys[str(title)] = {}
|
keys = {}
|
||||||
if str(track) not in keys[str(title)]:
|
if export.is_file():
|
||||||
keys[str(title)][str(track)] = {}
|
keys = jsonpickle.loads(export.read_text(encoding="utf8"))
|
||||||
keys[str(title)][str(track)].update(drm.content_keys)
|
if str(title) not in keys:
|
||||||
export.write_text(jsonpickle.dumps(keys, indent=4), encoding="utf8")
|
keys[str(title)] = {}
|
||||||
|
if str(track) not in keys[str(title)]:
|
||||||
|
keys[str(title)][str(track)] = {}
|
||||||
|
keys[str(title)][str(track)].update(drm.content_keys)
|
||||||
|
export.write_text(jsonpickle.dumps(keys, indent=4), encoding="utf8")
|
||||||
|
|
||||||
def download_track(
|
def download_track(
|
||||||
self,
|
self,
|
||||||
|
|
Loading…
Reference in New Issue