From 2e2f8f5099555dd4f44e8919f53ed095af65e287 Mon Sep 17 00:00:00 2001 From: TPD94 Date: Mon, 29 Sep 2025 20:48:59 -0400 Subject: [PATCH 01/10] Fix remoteCDM, add curl_cffi to instance check --- .gitignore | 6 ++++++ .idea/.gitignore | 8 ++++++++ unshackle/commands/dl.py | 12 ++++++++---- unshackle/core/manifests/dash.py | 2 +- 4 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 .idea/.gitignore diff --git a/.gitignore b/.gitignore index 26a73b6..b84292e 100644 --- a/.gitignore +++ b/.gitignore @@ -235,3 +235,9 @@ cython_debug/ marimo/_static/ marimo/_lsp/ __marimo__/ +.idea/vcs.xml +.idea/unshackle.iml +.idea/modules.xml +.idea/misc.xml +.idea/inspectionProfiles/Project_Default.xml +.idea/inspectionProfiles/profiles_settings.xml diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/unshackle/commands/dl.py b/unshackle/commands/dl.py index 9a99bfc..06d1b71 100644 --- a/unshackle/commands/dl.py +++ b/unshackle/commands/dl.py @@ -1701,10 +1701,14 @@ class dl: # All DecryptLabs CDMs use DecryptLabsRemoteCDM return DecryptLabsRemoteCDM(service_name=service, vaults=self.vaults, **cdm_api) else: - del cdm_api["name"] - if "type" in cdm_api: - del cdm_api["type"] - return RemoteCdm(**cdm_api) + return RemoteCdm( + device_type=cdm_api['Device Type'], + system_id=cdm_api['System ID'], + security_level=cdm_api['Security Level'], + host=cdm_api['Host'], + secret=cdm_api['Secret'], + device_name=cdm_api['Device Name'], + ) prd_path = config.directories.prds / f"{cdm_name}.prd" if not prd_path.is_file(): diff --git a/unshackle/core/manifests/dash.py b/unshackle/core/manifests/dash.py index ec19e25..67ef362 100644 --- a/unshackle/core/manifests/dash.py +++ b/unshackle/core/manifests/dash.py @@ -253,7 +253,7 @@ class DASH: ): if not session: session = Session() - elif not isinstance(session, Session): + elif not isinstance(session, (Session, CurlSession)): raise TypeError(f"Expected session to be a {Session}, not {session!r}") if proxy: From 55f116f1e8d7b510b4bd9861ebd8cb3f6c23a4e5 Mon Sep 17 00:00:00 2001 From: TPD94 <39639333+TPD94@users.noreply.github.com> Date: Mon, 29 Sep 2025 20:53:16 -0400 Subject: [PATCH 02/10] Delete .idea directory --- .idea/.gitignore | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 .idea/.gitignore diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 13566b8..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml From bade3f8c09bb00862788b6995adcc214627b49d5 Mon Sep 17 00:00:00 2001 From: TPD94 <39639333+TPD94@users.noreply.github.com> Date: Mon, 29 Sep 2025 20:53:38 -0400 Subject: [PATCH 03/10] Update .gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index b84292e..b7c4e73 100644 --- a/.gitignore +++ b/.gitignore @@ -202,7 +202,7 @@ cython_debug/ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ +.idea/ # Abstra # Abstra is an AI-powered process automation framework. From 4f3d0f1f7ab1c14e7f4fd5744447392bae14e970 Mon Sep 17 00:00:00 2001 From: TPD94 <39639333+TPD94@users.noreply.github.com> Date: Mon, 29 Sep 2025 20:54:42 -0400 Subject: [PATCH 04/10] Update .gitignore --- .gitignore | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.gitignore b/.gitignore index b7c4e73..317b448 100644 --- a/.gitignore +++ b/.gitignore @@ -235,9 +235,3 @@ cython_debug/ marimo/_static/ marimo/_lsp/ __marimo__/ -.idea/vcs.xml -.idea/unshackle.iml -.idea/modules.xml -.idea/misc.xml -.idea/inspectionProfiles/Project_Default.xml -.idea/inspectionProfiles/profiles_settings.xml From 724703d14b4a8a11cb0915ee2902630b5744eba6 Mon Sep 17 00:00:00 2001 From: TPD94 <39639333+TPD94@users.noreply.github.com> Date: Mon, 29 Sep 2025 20:56:25 -0400 Subject: [PATCH 05/10] Update .gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 317b448..26a73b6 100644 --- a/.gitignore +++ b/.gitignore @@ -202,7 +202,7 @@ cython_debug/ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. -.idea/ +#.idea/ # Abstra # Abstra is an AI-powered process automation framework. From 03f08159b45f4f9cf44fa7a039309d18ee9f7bfc Mon Sep 17 00:00:00 2001 From: TPD94 <39639333+TPD94@users.noreply.github.com> Date: Mon, 29 Sep 2025 21:01:55 -0400 Subject: [PATCH 06/10] Update dash.py --- unshackle/core/manifests/dash.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unshackle/core/manifests/dash.py b/unshackle/core/manifests/dash.py index 67ef362..56fec08 100644 --- a/unshackle/core/manifests/dash.py +++ b/unshackle/core/manifests/dash.py @@ -254,7 +254,7 @@ class DASH: if not session: session = Session() elif not isinstance(session, (Session, CurlSession)): - raise TypeError(f"Expected session to be a {Session}, not {session!r}") + raise TypeError(f"Expected session to be a {Session} or {CurlSession}, not {session!r}") if proxy: session.proxies.update({"all": proxy}) From e1e2e35ff43656ec8fbacb5db56478e2e66c6208 Mon Sep 17 00:00:00 2001 From: TPD94 Date: Tue, 30 Sep 2025 00:14:44 -0400 Subject: [PATCH 07/10] Update binaries.py to check subdirs in binaries folders named after the binary --- .gitignore | 2 ++ unshackle/core/binaries.py | 15 +++++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 26a73b6..e38a05e 100644 --- a/.gitignore +++ b/.gitignore @@ -235,3 +235,5 @@ cython_debug/ marimo/_static/ marimo/_lsp/ __marimo__/ +/unshackle/binaries +/.idea diff --git a/unshackle/core/binaries.py b/unshackle/core/binaries.py index da31fb5..878a36f 100644 --- a/unshackle/core/binaries.py +++ b/unshackle/core/binaries.py @@ -3,6 +3,8 @@ import sys from pathlib import Path from typing import Optional +from mypy.types import names + __shaka_platform = {"win32": "win", "darwin": "osx"}.get(sys.platform, sys.platform) @@ -15,16 +17,17 @@ def find(*names: str) -> Optional[Path]: for name in names: # First check local binaries folder if local_binaries_dir.exists(): - local_path = local_binaries_dir / name - if local_path.is_file() and local_path.stat().st_mode & 0o111: # Check if executable - return local_path - - # Also check with .exe extension on Windows + # On Windows, check for .exe extension first if sys.platform == "win32": - local_path_exe = local_binaries_dir / f"{name}.exe" + local_path_exe = local_binaries_dir / f"{name}" / f"{name}.exe" if local_path_exe.is_file(): return local_path_exe + # Check for exact name match with executable bit on Unix-like systems + local_path = local_binaries_dir / f"{name}" / f"{name}" + if local_path.is_file() and local_path.stat().st_mode & 0o111: # Check if executable + return local_path + # Fall back to system PATH path = shutil.which(name) if path: From 087df59fb6a20f8514e806e1311daa250bdd6ca3 Mon Sep 17 00:00:00 2001 From: TPD94 Date: Tue, 21 Oct 2025 21:07:24 -0400 Subject: [PATCH 08/10] Update hls.py --- unshackle/core/manifests/hls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unshackle/core/manifests/hls.py b/unshackle/core/manifests/hls.py index d48d96e..0bb1c9b 100644 --- a/unshackle/core/manifests/hls.py +++ b/unshackle/core/manifests/hls.py @@ -439,7 +439,7 @@ class HLS: elif len(files) != range_len: raise ValueError(f"Missing {range_len - len(files)} segment files for {segment_range}...") - if isinstance(drm, Widevine): + if isinstance(drm, (Widevine, PlayReady)): # with widevine we can merge all segments and decrypt once merge(to=merged_path, via=files, delete=True, include_map_data=True) drm.decrypt(merged_path) From e04399fbce9f14421983d5866e4b38b288ec2edd Mon Sep 17 00:00:00 2001 From: TPD94 Date: Tue, 21 Oct 2025 21:18:36 -0400 Subject: [PATCH 09/10] Update binaries.py Refactor code to search for binaries either in root of binary folder or in a subfolder named after the binary. --- unshackle/core/binaries.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/unshackle/core/binaries.py b/unshackle/core/binaries.py index 878a36f..e3b04aa 100644 --- a/unshackle/core/binaries.py +++ b/unshackle/core/binaries.py @@ -10,23 +10,23 @@ __shaka_platform = {"win32": "win", "darwin": "osx"}.get(sys.platform, sys.platf def find(*names: str) -> Optional[Path]: """Find the path of the first found binary name.""" - # Get the directory containing this file to find the local binaries folder - current_dir = Path(__file__).parent.parent + current_dir = Path(__file__).resolve().parent.parent local_binaries_dir = current_dir / "binaries" - for name in names: - # First check local binaries folder - if local_binaries_dir.exists(): - # On Windows, check for .exe extension first - if sys.platform == "win32": - local_path_exe = local_binaries_dir / f"{name}" / f"{name}.exe" - if local_path_exe.is_file(): - return local_path_exe + ext = ".exe" if sys.platform == "win32" else "" - # Check for exact name match with executable bit on Unix-like systems - local_path = local_binaries_dir / f"{name}" / f"{name}" - if local_path.is_file() and local_path.stat().st_mode & 0o111: # Check if executable - return local_path + for name in names: + if local_binaries_dir.exists(): + candidate_paths = [ + local_binaries_dir / f"{name}{ext}", + local_binaries_dir / name / f"{name}{ext}" + ] + + for path in candidate_paths: + if path.is_file(): + # On Unix-like systems, check if file is executable + if sys.platform == "win32" or (path.stat().st_mode & 0o111): + return path # Fall back to system PATH path = shutil.which(name) From df09998a47ef9b5bebdf3c181b5f696853aa982e Mon Sep 17 00:00:00 2001 From: TPD94 <39639333+TPD94@users.noreply.github.com> Date: Tue, 21 Oct 2025 21:19:55 -0400 Subject: [PATCH 10/10] Update .gitignore --- .gitignore | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitignore b/.gitignore index e38a05e..26a73b6 100644 --- a/.gitignore +++ b/.gitignore @@ -235,5 +235,3 @@ cython_debug/ marimo/_static/ marimo/_lsp/ __marimo__/ -/unshackle/binaries -/.idea