Skip to content

Commit

Permalink
#135 Add ClientToken support
Browse files Browse the repository at this point in the history
  • Loading branch information
kokarare1212 committed Jun 28, 2022
1 parent 1a91446 commit b28e684
Show file tree
Hide file tree
Showing 7 changed files with 323 additions and 3 deletions.
2 changes: 1 addition & 1 deletion librespot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@


class Version:
version_name = "0.0.1"
version_name = "0.0.4"

@staticmethod
def platform() -> Platform:
Expand Down
47 changes: 46 additions & 1 deletion librespot/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from librespot.crypto import CipherPair, DiffieHellman, Packet
from librespot.mercury import MercuryClient, MercuryRequests, RawMercuryRequest
from librespot.metadata import AlbumId, ArtistId, EpisodeId, ShowId, TrackId
from librespot.proto import Authentication_pb2 as Authentication, Connect_pb2 as Connect, Keyexchange_pb2 as Keyexchange, Metadata_pb2 as Metadata
from librespot.proto import Authentication_pb2 as Authentication, ClientToken_pb2 as ClientToken, Connect_pb2 as Connect, Connectivity_pb2 as Connectivity, Keyexchange_pb2 as Keyexchange, Metadata_pb2 as Metadata
from librespot.proto.ExplicitContentPubsub_pb2 import UserAttributesUpdate
from librespot.structure import Closeable, MessageListener, RequestListener, SubListener
import base64
Expand All @@ -39,6 +39,7 @@
class ApiClient(Closeable):
logger = logging.getLogger("Librespot:ApiClient")
__base_url: str
__client_token_str: str = None
__session: Session

def __init__(self, session: Session):
Expand All @@ -49,6 +50,11 @@ def build_request(
self, method: str, suffix: str,
headers: typing.Union[None, typing.Dict[str, str]],
body: typing.Union[None, bytes]) -> requests.PreparedRequest:
if self.__client_token_str is None:
resp = self.__client_token()
self.__client_token_str = resp.granted_token.token
self.logger.debug("Updated client token: {}".format(self.__client_token_str))

request = requests.PreparedRequest()
request.method = method
request.data = body
Expand Down Expand Up @@ -147,6 +153,45 @@ def get_metadata_4_show(self, show: ShowId) -> Metadata.Show:
proto.ParseFromString(body)
return proto

def set_client_token(self, client_token):
self.__client_token_str = client_token

def __client_token(self):
proto_req = ClientToken.ClientTokenRequest(
request_type=ClientToken.ClientTokenRequestType.REQUEST_CLIENT_DATA_REQUEST,
client_data=ClientToken.ClientDataRequest(
client_id=MercuryRequests.keymaster_client_id,
client_version=Version.version_name,
connectivity_sdk_data=Connectivity.ConnectivitySdkData(
device_id=self.__session.device_id(),
platform_specific_data=Connectivity.PlatformSpecificData(
windows=Connectivity.NativeWindowsData(
something1=10,
something3=21370,
something4=2,
something6=9,
something7=332,
something8=33404,
something10=True,
),
),
),
),
)

resp = requests.post("https://clienttoken.spotify.com/v1/clienttoken",
proto_req.SerializeToString(),
headers={
"Accept": "application/x-protobuf",
"Content-Encoding": "",
})

ApiClient.StatusCodeException.check_status(resp)

proto_resp = ClientToken.ClientTokenResponse()
proto_resp.ParseFromString(resp.content)
return proto_resp

class StatusCodeException(IOError):
code: int

Expand Down
63 changes: 63 additions & 0 deletions librespot/proto/ClientToken_pb2.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 36 additions & 0 deletions librespot/proto/Connectivity_pb2.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

125 changes: 125 additions & 0 deletions proto/client_token.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
syntax = "proto3";

package spotify.clienttoken.http.v0;

import "connectivity.proto";

option optimize_for = CODE_SIZE;
option java_package = "com.spotify.clienttoken.http.v0";

message ClientTokenRequest {
ClientTokenRequestType request_type = 1;

oneof request {
ClientDataRequest client_data = 2;
ChallengeAnswersRequest challenge_answers = 3;
}
}

message ClientDataRequest {
string client_version = 1;
string client_id = 2;

oneof data {
data.v0.ConnectivitySdkData connectivity_sdk_data = 3;
}
}

message ChallengeAnswersRequest {
string state = 1;
repeated ChallengeAnswer answers = 2;
}

message ClientTokenResponse {
ClientTokenResponseType response_type = 1;

oneof response {
GrantedTokenResponse granted_token = 2;
ChallengesResponse challenges = 3;
}
}

message TokenDomain {
string domain = 1;
}

message GrantedTokenResponse {
string token = 1;
int32 expires_after_seconds = 2;
int32 refresh_after_seconds = 3;
repeated TokenDomain domains = 4;
}

message ChallengesResponse {
string state = 1;
repeated Challenge challenges = 2;
}

message ClientSecretParameters {
string salt = 1;
}

message EvaluateJSParameters {
string code = 1;
repeated string libraries = 2;
}

message HashCashParameters {
int32 length = 1;
string prefix = 2;
}

message Challenge {
ChallengeType type = 1;

oneof parameters {
ClientSecretParameters client_secret_parameters = 2;
EvaluateJSParameters evaluate_js_parameters = 3;
HashCashParameters evaluate_hashcash_parameters = 4;
}
}

message ClientSecretHMACAnswer {
string hmac = 1;
}

message EvaluateJSAnswer {
string result = 1;
}

message HashCashAnswer {
string suffix = 1;
}

message ChallengeAnswer {
ChallengeType ChallengeType = 1;

oneof answer {
ClientSecretHMACAnswer client_secret = 2;
EvaluateJSAnswer evaluate_js = 3;
HashCashAnswer hash_cash = 4;
}
}

message ClientTokenBadRequest {
string message = 1;
}

enum ClientTokenRequestType {
REQUEST_UNKNOWN = 0;
REQUEST_CLIENT_DATA_REQUEST = 1;
REQUEST_CHALLENGE_ANSWERS_REQUEST = 2;
}

enum ClientTokenResponseType {
RESPONSE_UNKNOWN = 0;
RESPONSE_GRANTED_TOKEN_RESPONSE = 1;
RESPONSE_CHALLENGES_RESPONSE = 2;
}

enum ChallengeType {
CHALLENGE_UNKNOWN = 0;
CHALLENGE_CLIENT_SECRET_HMAC = 1;
CHALLENGE_EVALUATE_JS = 2;
CHALLENGE_HASH_CASH = 3;
}
51 changes: 51 additions & 0 deletions proto/connectivity.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
syntax = "proto3";

package spotify.clienttoken.data.v0;

option optimize_for = CODE_SIZE;
option java_package = "com.spotify.clienttoken.data.v0";

message ConnectivitySdkData {
PlatformSpecificData platform_specific_data = 1;
string device_id = 2;
}

message PlatformSpecificData {
oneof data {
NativeAndroidData android = 1;
NativeIOSData ios = 2;
NativeWindowsData windows = 4;
}
}

message NativeAndroidData {
int32 major_sdk_version = 1;
int32 minor_sdk_version = 2;
int32 patch_sdk_version = 3;
uint32 api_version = 4;
Screen screen_dimensions = 5;
}

message NativeIOSData {
int32 user_interface_idiom = 1;
bool target_iphone_simulator = 2;
string hw_machine = 3;
string system_version = 4;
string simulator_model_identifier = 5;
}

message NativeWindowsData {
int32 something1 = 1;
int32 something3 = 3;
int32 something4 = 4;
int32 something6 = 6;
int32 something7 = 7;
int32 something8 = 8;
bool something10 = 10;
}

message Screen {
int32 width = 1;
int32 height = 2;
int32 density = 3;
}
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import setuptools

setuptools.setup(name="librespot",
version="0.0.3",
version="0.0.4",
description="Open Source Spotify Client",
long_description=open("README.md").read(),
long_description_content_type="text/markdown",
Expand Down

0 comments on commit b28e684

Please sign in to comment.