Skip to content

Commit 397a576

Browse files
authored
Merge pull request #372 from EbbLabs/feature/v0.8.7
Feature/v0.8.7
2 parents af92730 + 071fac2 commit 397a576

File tree

9 files changed

+853
-219
lines changed

9 files changed

+853
-219
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,7 @@ prof/
8686
# Json session files
8787
tidal*.json
8888
*.m3u8
89-
*.mpd
89+
*.mpd
90+
91+
# Local devtools
92+
devtools

HISTORY.rst

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,20 @@
33
History
44
=======
55

6-
Upcoming
6+
v0.8.7
77
--------
8+
* OAuth Client ID, secret updated - tehkillerbee_
9+
* Bugfix: `albums_paginated` using `get_artists_count` instead of `get_albums_count` - rafrombrc_
810
* TooManyRequests now includes the retry_after header in its data. - semohr_
911
* Added a central error class (TidalAPIError) to allow for unified error handling. - semohr_
1012

1113
v0.8.6
1214
------
13-
* Add support for get<track, album, artist, playlist>count(), Workers: Use get_*_count to get the actual number of items. - tehkillerbee_
15+
* Bugfix: Use get_*_count in workers to get the actual number of items. (Fixes #360) - tehkillerbee_
16+
* Feature: Add support for get<track, album, artist, playlist>count(), Workers: Use get_*_count to get the actual number of items. - tehkillerbee_
17+
* Feature: Get playlist tracks, items count. Get playlist tracks paginated. - tehkillerbee_
1418
* Only return warning if page itemtype (v2) is not implemented (Fixes: #362) - tehkillerbee_
1519
* Add legacy home endpoint for backwards compatibility - tehkillerbee_
16-
* Get playlist tracks, items count. Get playlist tracks paginated. - tehkillerbee_
1720

1821
v0.8.5
1922
------
@@ -248,7 +251,6 @@ v0.6.2
248251
* Add version tag for Track - Husky22_
249252
* Switch to netlify for documentation - morguldir_
250253

251-
.. _semohr: https://github.com/semohr
252254
.. _morguldir: https://github.com/morguldir
253255
.. _Husky22: https://github.com/Husky22
254256
.. _ktnrg45: https://github.com/ktnrg45
@@ -278,4 +280,6 @@ v0.6.2
278280
.. _C0rn3j: https://github.com/C0rn3j
279281
.. _Nokse22: https://github.com/Nokse22
280282
.. _nilathedragon: https://github.com/nilathedragon
283+
.. _semohr: https://github.com/semohr
284+
.. _rafrombrc: https://github.com/rafrombrc
281285

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
author = "The tidalapi Developers"
2424

2525
# The full version, including alpha/beta/rc tags
26-
release = "0.8.6"
26+
release = "0.8.7"
2727

2828

2929
# -- General configuration ---------------------------------------------------

examples/pkce_example.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,9 @@
3737
album_id = "77646169" # Beck / Sea Change (Max quality: HI_RES_LOSSLESS FLAC, 24bit/192000Hz)
3838
album = session.album(album_id)
3939
res = album.get_audio_resolution()
40-
tracks = album.tracks()
40+
4141
# list album tracks
42+
tracks = album.tracks()
4243
for track in tracks:
4344
print("{}: '{}' by '{}'".format(track.id, track.name, track.artist.name))
4445
stream = track.get_stream()

examples/simple.py

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,38 @@
3838
# album_id = "110827651" # The Black Keys / Let's Rock (Max quality: LOSSLESS FLAC, 24bit/48000Hz)
3939
album_id = "77646169" # Beck / Sea Change (Max quality: HI_RES_LOSSLESS FLAC, 24bit/192000Hz)
4040
album = session.album(album_id)
41-
tracks = album.tracks()
42-
# list album tracks
4341
print(album.name)
42+
4443
# list album tracks
44+
tracks = album.tracks()
4545
for track in tracks:
4646
print("{}: '{}' by '{}'".format(track.id, track.name, track.artist.name))
47-
print(track.get_url())
47+
stream = track.get_stream()
48+
print("MimeType:{}".format(stream.manifest_mime_type))
49+
50+
manifest = stream.get_stream_manifest()
51+
audio_resolution = stream.get_audio_resolution()
52+
53+
print(
54+
"track:{}, (quality:{}, codec:{}, {}bit/{}Hz)".format(
55+
track.id,
56+
stream.audio_quality,
57+
manifest.get_codecs(),
58+
audio_resolution[0],
59+
audio_resolution[1],
60+
)
61+
)
62+
if stream.is_mpd:
63+
# HI_RES_LOSSLESS quality supported when using MPEG-DASH stream (PKCE only!)
64+
# 1. Export as MPD manifest
65+
mpd = stream.get_manifest_data()
66+
# 2. Export as HLS m3u8 playlist
67+
hls = manifest.get_hls()
68+
# with open("{}_{}.mpd".format(album_id, track.id), "w") as my_file:
69+
# my_file.write(mpd)
70+
# with open("{}_{}.m3u8".format(album_id, track.id), "w") as my_file:
71+
# my_file.write(hls)
72+
elif stream.is_bts:
73+
# Direct URL (m4a or flac) is available for Quality < HI_RES_LOSSLESS
74+
url = manifest.get_urls()
75+
break

poetry.lock

Lines changed: 786 additions & 149 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "tidalapi"
3-
version = "0.8.6"
3+
version = "0.8.7"
44
description = "Unofficial API for TIDAL music streaming service."
55
authors = ["Thomas Amland <[email protected]>"]
66
maintainers = ["tehkillerbee <[email protected]>"]
@@ -30,6 +30,7 @@ typing-extensions = "^4.12.2"
3030
ratelimit = "^2.2.1"
3131
isodate = "^0.7.2"
3232
mpegdash = "^0.4.0"
33+
pyaes = "^1.6.1"
3334

3435
[tool.poetry.group.dev.dependencies]
3536
mypy = "^1.3.0"

tidalapi/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@
1717
User,
1818
)
1919

20-
__version__ = "0.8.6"
20+
__version__ = "0.8.7"

tidalapi/session.py

Lines changed: 18 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
)
4646
from urllib.parse import parse_qs, urlencode, urlsplit
4747

48+
import pyaes
4849
import requests
4950
from requests.exceptions import HTTPError
5051

@@ -111,7 +112,6 @@ class Config:
111112
api_v1_location: str = "https://api.tidal.com/v1/"
112113
api_v2_location: str = "https://api.tidal.com/v2/"
113114
openapi_v2_location: str = "https://openapi.tidal.com/v2/"
114-
api_token: str
115115
client_id: str
116116
client_secret: str
117117
image_url: str = "https://resources.tidal.com/images/%s/%ix%i.jpg"
@@ -151,65 +151,25 @@ def __init__(
151151
else:
152152
self.item_limit = item_limit
153153

154-
self.api_token = eval("\x67\x6c\x6f\x62\x61\x6c\x73".encode("437"))()[
155-
"\x5f\x5f\x6e\x61\x6d\x65\x5f\x5f".encode(
156-
"".join(map(chr, [105, 105, 99, 115, 97][::-1]))
157-
).decode("".join(map(chr, [117, 116, 70, 95, 56])))
158-
]
159-
self.api_token += "." + eval(
160-
"\x74\x79\x70\x65\x28\x73\x65\x6c\x66\x29\x2e\x5f\x5f\x6e\x61\x6d\x65\x5f\x5f".encode(
161-
"".join(map(chr, [105, 105, 99, 115, 97][::-1]))
162-
).decode(
163-
"".join(map(chr, [117, 116, 70, 95, 56]))
154+
# OAuth Client Authorization
155+
self.client_id = base64.b64decode(
156+
base64.b64decode(b"V214bmVWTnVhR3RpVnpVdw==")
157+
+ base64.b64decode(b"VjJ4a1RFMUhiRFJXUVQwOQ==")
158+
).decode("utf-8")
159+
self.client_secret = base64.b64decode(
160+
base64.b64decode(
161+
b"VFZVMWRVOVZSbTFTUlVaeFpVaEtibE5yV2t0WmEzUlBWakI0YkZGWQ=="
164162
)
165-
)
166-
token = self.api_token
167-
token = token[:8] + token[16:]
168-
self.api_token = list(
169-
(base64.b64decode("d3RjaThkamFfbHlhQnBKaWQuMkMwb3puT2ZtaXhnMA==").decode())
170-
)
171-
tok = "".join(([chr(ord(x) - 2) for x in token[-6:]]))
172-
token2 = token
173-
token = token[:9]
174-
token += tok
175-
tok2 = "".join(([chr(ord(x) - 2) for x in token[:-7]]))
176-
token = token[8:]
177-
token = tok2 + token
178-
self.api_token = list(
179-
(base64.b64decode("enJVZzRiWF9IalZfVm5rZ2MuMkF0bURsUGRvZzRldA==").decode())
180-
)
181-
for word in token:
182-
self.api_token.remove(word)
183-
self.api_token = "".join(self.api_token)
184-
string = ""
185-
save = False
186-
if not isinstance(token2, str):
187-
save = True
188-
string = "".encode("ISO-8859-1")
189-
token2 = token2.encode("ISO-8859-1")
190-
tok = string.join(([chr(ord(x) + 24) for x in token2[:-7]]))
191-
token2 = token2[8:]
192-
token2 = tok + token2
193-
tok2 = string.join(([chr(ord(x) + 23) for x in token2[-6:]]))
194-
token2 = token2[:9]
195-
token2 += tok2
196-
self.client_id = list(
197-
(
198-
base64.b64decode(
199-
"VoxKgUt8aHlEhEZ5cYhKgVAucVp2hnOFUH1WgE5+QlY2"
200-
"dWtYVEptd2x2YnR0UDd3bE1scmM3MnNlND0="
201-
).decode("ISO-8859-1")
163+
+ base64.b64decode(
164+
b"YkV4U01WcElZbFZzVDJSV2FGRlZSWGhKVm14b1FtUnVhRUphZWpBOQ=="
202165
)
203-
)
204-
if save:
205-
token2.decode("ISO-8859-1").encode("utf-16")
206-
self.client_id = [x.encode("ISO-8859-1") for x in self.client_id]
207-
for word in token2:
208-
self.client_id.remove(word)
209-
self.client_id = "".join(self.client_id)
210-
self.client_secret = self.client_id
211-
self.client_id = self.api_token
212-
# PKCE Authorization. We will keep the former `client_id` as a fallback / will only be used for non PCKE
166+
).decode("utf-8")
167+
168+
# If client_secret not supplied, fall back to client_id (matching original behavior)
169+
if not self.client_secret and self.client_id:
170+
self.client_secret = self.client_id
171+
172+
# PKCE Client Authorization. We will keep the former `client_id` as a fallback / will only be used for non PCKE
213173
# authorizations.
214174
self.client_unique_key = format(random.getrandbits(64), "02x")
215175
self.code_verifier = base64.urlsafe_b64encode(os.urandom(32))[:-1].decode(

0 commit comments

Comments
 (0)