[GH-ISSUE #272] [REQUEST] Fetch audio key from seektables & MP4 Format #46

Open
opened 2026-02-27 08:11:36 +03:00 by kerem · 6 comments
Owner

Originally created by @KagChi on GitHub (May 21, 2024).
Original GitHub issue: https://github.com/kokarare1212/librespot-python/issues/272

You can fetch audio key from seektables then try to decrypt the pssh_widevine to get the audio key
the url are https://seektables.scdn.co/seektable/fileId.json file id are hex from audio formats

Seems you can only fetch MP4_* audio key

Originally created by @KagChi on GitHub (May 21, 2024). Original GitHub issue: https://github.com/kokarare1212/librespot-python/issues/272 You can fetch audio key from seektables then try to decrypt the `pssh_widevine` to get the audio key the url are `https://seektables.scdn.co/seektable/fileId.json` file id are hex from audio formats Seems you can only fetch MP4_* audio key
Author
Owner

@kokarare1212 commented on GitHub (May 21, 2024):

This is the first time I have heard of the MP4 format.
Does that mean they will be decrypted by Widevine?

<!-- gh-comment-id:2123578287 --> @kokarare1212 commented on GitHub (May 21, 2024): This is the first time I have heard of the MP4 format. Does that mean they will be decrypted by Widevine?
Author
Owner

@KagChi commented on GitHub (May 22, 2024):

Does that mean they will be decrypted by Widevine?

No, you can decrypt the track with current strategy. just fetching the audio key from the seektables without requesting it from mercury client.

response = self.__session.client() \
    .get("https://seektables.scdn.co/seektable/{}.json".format(util.bytes_to_hex(file_id)))
if response.status_code != 200:
    raise IOError("{}".format(response.status_code))

json = response.json()
if not json:
    raise IOError("Seektable seems empty")
                
wideresponse = self.__session.client() \
    .post("https://integration.widevine.com/_/pssh_decode", json.get("pssh_widevine"))

widejson = response.text()
if not widejson:
    raise IOError("Widevine decoders seems empty")

json_start = widejson.find('{"algorithm":')

# Parse the JSON object
json_data = json.loads(widejson[json_start:])

return base64.b64decode(base64.urlsafe_b64encode(json_data.get("key_ids").pop(0).bytes))

here is the snippet. why not use PSSH module? the project doesnt suppport latest protobuf. so we are safe to fetch higher MP4 quality without premiums. since the seektables were available for all MP4 formats.

Example:
image

https://seektables.scdn.co/seektable/12464744a06d5c7e4a8848266682d78228e529a1.json (MP4_256) HIGHEST QUALITY 
https://seektables.scdn.co/seektable/a6281f4b53c19695ae51216133e14f2eceeb2582.json (MP4_128) LOWEST QUALITY

image
image

keyIds are the key to decrypt the track. they are in base64 so convert it to hex first.

<!-- gh-comment-id:2123731265 --> @KagChi commented on GitHub (May 22, 2024): > Does that mean they will be decrypted by Widevine? No, you can decrypt the track with current strategy. just fetching the audio key from the seektables without requesting it from mercury client. ```py response = self.__session.client() \ .get("https://seektables.scdn.co/seektable/{}.json".format(util.bytes_to_hex(file_id))) if response.status_code != 200: raise IOError("{}".format(response.status_code)) json = response.json() if not json: raise IOError("Seektable seems empty") wideresponse = self.__session.client() \ .post("https://integration.widevine.com/_/pssh_decode", json.get("pssh_widevine")) widejson = response.text() if not widejson: raise IOError("Widevine decoders seems empty") json_start = widejson.find('{"algorithm":') # Parse the JSON object json_data = json.loads(widejson[json_start:]) return base64.b64decode(base64.urlsafe_b64encode(json_data.get("key_ids").pop(0).bytes)) ``` here is the snippet. why not use PSSH module? the project doesnt suppport latest protobuf. so we are safe to fetch higher MP4 quality without premiums. since the seektables were available for all MP4 formats. Example: ![image](https://github.com/kokarare1212/librespot-python/assets/59391215/1263cdda-c711-4f67-b30f-8d96a31d1be9) ``` https://seektables.scdn.co/seektable/12464744a06d5c7e4a8848266682d78228e529a1.json (MP4_256) HIGHEST QUALITY https://seektables.scdn.co/seektable/a6281f4b53c19695ae51216133e14f2eceeb2582.json (MP4_128) LOWEST QUALITY ``` ![image](https://github.com/kokarare1212/librespot-python/assets/59391215/0164c052-9c53-4d07-83ab-605b34505a17) ![image](https://github.com/kokarare1212/librespot-python/assets/59391215/1f7e6c01-c1eb-4729-b460-8e3e01453544) keyIds are the key to decrypt the track. they are in base64 so convert it to hex first.
Author
Owner

@KagChi commented on GitHub (Jun 8, 2024):

Any updates on this?

<!-- gh-comment-id:2156042628 --> @KagChi commented on GitHub (Jun 8, 2024): Any updates on this?
Author
Owner

@kokarare1212 commented on GitHub (Jun 8, 2024):

I do not currently have the time to add features, if you can write the code, I will merge it after reviewing it.

<!-- gh-comment-id:2156043230 --> @kokarare1212 commented on GitHub (Jun 8, 2024): I do not currently have the time to add features, if you can write the code, I will merge it after reviewing it.
Author
Owner

@KagChi commented on GitHub (Jun 8, 2024):

i have the implementation, but i cant test it. the proto seems need to build? do u mind to test it out?

and also, i dont really know MP4_128 proto code. only guessing it... 😂

<!-- gh-comment-id:2156045230 --> @KagChi commented on GitHub (Jun 8, 2024): i have the implementation, but i cant test it. the proto seems need to build? do u mind to test it out? and also, i dont really know MP4_128 proto code. only guessing it... 😂
Author
Owner

@kokarare1212 commented on GitHub (Jun 8, 2024):

There are a lot of unknowns about MP4_128.
For now, we will wait until we see movement in other related libraries...

<!-- gh-comment-id:2156050074 --> @kokarare1212 commented on GitHub (Jun 8, 2024): There are a lot of unknowns about MP4_128. For now, we will wait until we see movement in other related libraries...
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/librespot-python-kokarare1212#46
No description provided.