From 4eb227493e37b4401ffa2bb0dfa93fac3608ce4d Mon Sep 17 00:00:00 2001 From: chu23465 <130033130+chu23465@users.noreply.github.com> Date: Tue, 10 Dec 2024 08:14:32 +0530 Subject: [PATCH] Playready --- README.md | 4 +++- gamdl/apple_music_api.py | 10 ++++++++-- gamdl/cli.py | 3 ++- gamdl/downloader.py | 14 ++++++++------ 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index df602aa..6bdc1b9 100644 --- a/README.md +++ b/README.md @@ -121,7 +121,7 @@ Config file values can be overridden using command line arguments. | `--codec-music-video` / `codec_music_video` | Music video codec. | `h264` | | `--quality-post` / `quality_post` | Post video quality. | `best` | | `--no-config-file`, `-n` / - | Do not use a config file. | `false` | - +| `--playready`, `playready` / - | Use Playready DRM | `false` | ### Tags variables The following variables can be used in the template folders/files and/or in the `exclude_tags` list: @@ -178,6 +178,7 @@ The following codecs are available: * `aac-legacy` * `aac-he-legacy` + The following codecs are also available, **but are not guaranteed to work**, as currently most (or all) of the songs fails to be downloaded when using them: * `aac` * `aac-he` @@ -190,6 +191,7 @@ The following codecs are also available, **but are not guaranteed to work**, as * `alac` * `ask` * When using this option, Gamdl will ask you which codec from this list to use that is available for the song. +With PlayReady and the right CDM, binaural, atmos and aac should download. ### Music videos codecs The following codecs are available: diff --git a/gamdl/apple_music_api.py b/gamdl/apple_music_api.py index 22ed0c2..c717575 100644 --- a/gamdl/apple_music_api.py +++ b/gamdl/apple_music_api.py @@ -1,6 +1,7 @@ from __future__ import annotations import functools +import json import re import time import typing @@ -41,7 +42,7 @@ class AppleMusicApi: self.storefront = self.session.cookies.get_dict()["itua"] self.session.headers.update( { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.2903.86", #"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:95.0) Gecko/20100101 Firefox/95.0", + "User-Agent": "iTunes/12.11.3 (Windows; Microsoft Windows 10 x64 Professional Edition (Build 19041); x64) AppleWebKit/7611.1022.4001.1 (dt:2)", #"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.2903.86", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:95.0) Gecko/20100101 Firefox/95.0", "Accept": "application/json", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip, deflate, br", @@ -273,7 +274,7 @@ class AppleMusicApi: "uri": track_uri, "adamId": track_id, "isLibrary": False, - "user-initiated": True, + "user-initiated": False, }, ) try: @@ -286,7 +287,12 @@ class AppleMusicApi: requests.exceptions.JSONDecodeError, AssertionError, ): + if response_dict.get("status") == -1002: + raise Exception("-1002: You do not own the title in the requested quality.") + elif response_dict.get("status") == -1021: + raise Exception("-1021: Device has insufficient security level or is blacklisted/revoked.") self._raise_response_exception(response) + return license diff --git a/gamdl/cli.py b/gamdl/cli.py index 9df7468..2ba1a54 100644 --- a/gamdl/cli.py +++ b/gamdl/cli.py @@ -561,9 +561,10 @@ def main( logger.warning( f"({queue_progress}) Song is not downloadable or is not" " available in the chosen codec, skipping" + f"\n{stream_info.pssh}, {stream_info.stream_url}" + ) continue - logger.debug(f"{stream_info.pssh}, {stream_info.stream_url}") logger.debug("Getting decryption key") decryption_key = downloader.get_decryption_key( stream_info.pssh, track_metadata["id"] diff --git a/gamdl/downloader.py b/gamdl/downloader.py index 89a81c8..f0fd57d 100644 --- a/gamdl/downloader.py +++ b/gamdl/downloader.py @@ -343,7 +343,7 @@ class Downloader: elif self.drm == DRM.Playready: from pyplayready import PSSH try: - pssh_obj = PSSH(pssh.split(",")[-1]).get_wrm_headers(downgrade_to_v4=False)[0] + pssh_obj = PSSH(pssh.split(",")[-1]).get_wrm_headers(downgrade_to_v4=True)[0] # Downgrade to v4 has to be set to True cdm_session = self.cdm.open() challenge = base64.b64encode( self.cdm.get_license_challenge(cdm_session, pssh_obj).encode("utf-8") @@ -354,13 +354,15 @@ class Downloader: challenge, self.drm, ) - self.cdm.parse_license(cdm_session, license) - decryption_key = next( - i for i in self.cdm.get_keys(cdm_session) if i.key_type == "AES_128_CBC" - ).key.hex() + self.cdm.parse_license(cdm_session, base64.b64decode(license.encode("utf-8")).decode("utf-8")) + decryption_keys = [i.key.hex() for i in self.cdm.get_keys(cdm_session)] + if len(decryption_keys) != 1: + raise ValueError(f"Expecting only one key to be returned, but {len(decryption_keys)} keys were returned") + elif len(decryption_keys) == 1 and "32b8ade1769e26b1ffb8986352793fc6" in decryption_keys: + raise ValueError("Only default key returned for track.") finally: self.cdm.close(cdm_session) - return decryption_key + return decryption_keys[0] def download(self, path: Path, stream_url: str): if self.download_mode == DownloadMode.YTDLP: