#135 Add ClientToken support
This commit is contained in:
parent
1a91446084
commit
b28e684e97
|
@ -6,7 +6,7 @@ import platform
|
|||
|
||||
|
||||
class Version:
|
||||
version_name = "0.0.1"
|
||||
version_name = "0.0.4"
|
||||
|
||||
@staticmethod
|
||||
def platform() -> Platform:
|
||||
|
|
|
@ -12,7 +12,7 @@ from librespot.cache import CacheManager
|
|||
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
|
||||
|
@ -39,6 +39,7 @@ import websocket
|
|||
class ApiClient(Closeable):
|
||||
logger = logging.getLogger("Librespot:ApiClient")
|
||||
__base_url: str
|
||||
__client_token_str: str = None
|
||||
__session: Session
|
||||
|
||||
def __init__(self, session: Session):
|
||||
|
@ -49,6 +50,11 @@ class ApiClient(Closeable):
|
|||
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
|
||||
|
@ -147,6 +153,45 @@ class ApiClient(Closeable):
|
|||
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
|
||||
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: client_token.proto
|
||||
"""Generated protocol buffer code."""
|
||||
from google.protobuf.internal import builder as _builder
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
from librespot.proto import Connectivity_pb2 as connectivity__pb2
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x12\x63lient_token.proto\x12\x1bspotify.clienttoken.http.v0\x1a\x12\x63onnectivity.proto\"\x84\x02\n\x12\x43lientTokenRequest\x12I\n\x0crequest_type\x18\x01 \x01(\x0e\x32\x33.spotify.clienttoken.http.v0.ClientTokenRequestType\x12\x45\n\x0b\x63lient_data\x18\x02 \x01(\x0b\x32..spotify.clienttoken.http.v0.ClientDataRequestH\x00\x12Q\n\x11\x63hallenge_answers\x18\x03 \x01(\x0b\x32\x34.spotify.clienttoken.http.v0.ChallengeAnswersRequestH\x00\x42\t\n\x07request\"\x99\x01\n\x11\x43lientDataRequest\x12\x16\n\x0e\x63lient_version\x18\x01 \x01(\t\x12\x11\n\tclient_id\x18\x02 \x01(\t\x12Q\n\x15\x63onnectivity_sdk_data\x18\x03 \x01(\x0b\x32\x30.spotify.clienttoken.data.v0.ConnectivitySdkDataH\x00\x42\x06\n\x04\x64\x61ta\"g\n\x17\x43hallengeAnswersRequest\x12\r\n\x05state\x18\x01 \x01(\t\x12=\n\x07\x61nswers\x18\x02 \x03(\x0b\x32,.spotify.clienttoken.http.v0.ChallengeAnswer\"\x81\x02\n\x13\x43lientTokenResponse\x12K\n\rresponse_type\x18\x01 \x01(\x0e\x32\x34.spotify.clienttoken.http.v0.ClientTokenResponseType\x12J\n\rgranted_token\x18\x02 \x01(\x0b\x32\x31.spotify.clienttoken.http.v0.GrantedTokenResponseH\x00\x12\x45\n\nchallenges\x18\x03 \x01(\x0b\x32/.spotify.clienttoken.http.v0.ChallengesResponseH\x00\x42\n\n\x08response\"\x1d\n\x0bTokenDomain\x12\x0e\n\x06\x64omain\x18\x01 \x01(\t\"\x9e\x01\n\x14GrantedTokenResponse\x12\r\n\x05token\x18\x01 \x01(\t\x12\x1d\n\x15\x65xpires_after_seconds\x18\x02 \x01(\x05\x12\x1d\n\x15refresh_after_seconds\x18\x03 \x01(\x05\x12\x39\n\x07\x64omains\x18\x04 \x03(\x0b\x32(.spotify.clienttoken.http.v0.TokenDomain\"_\n\x12\x43hallengesResponse\x12\r\n\x05state\x18\x01 \x01(\t\x12:\n\nchallenges\x18\x02 \x03(\x0b\x32&.spotify.clienttoken.http.v0.Challenge\"&\n\x16\x43lientSecretParameters\x12\x0c\n\x04salt\x18\x01 \x01(\t\"7\n\x14\x45valuateJSParameters\x12\x0c\n\x04\x63ode\x18\x01 \x01(\t\x12\x11\n\tlibraries\x18\x02 \x03(\t\"4\n\x12HashCashParameters\x12\x0e\n\x06length\x18\x01 \x01(\x05\x12\x0e\n\x06prefix\x18\x02 \x01(\t\"\xda\x02\n\tChallenge\x12\x38\n\x04type\x18\x01 \x01(\x0e\x32*.spotify.clienttoken.http.v0.ChallengeType\x12W\n\x18\x63lient_secret_parameters\x18\x02 \x01(\x0b\x32\x33.spotify.clienttoken.http.v0.ClientSecretParametersH\x00\x12S\n\x16\x65valuate_js_parameters\x18\x03 \x01(\x0b\x32\x31.spotify.clienttoken.http.v0.EvaluateJSParametersH\x00\x12W\n\x1c\x65valuate_hashcash_parameters\x18\x04 \x01(\x0b\x32/.spotify.clienttoken.http.v0.HashCashParametersH\x00\x42\x0c\n\nparameters\"&\n\x16\x43lientSecretHMACAnswer\x12\x0c\n\x04hmac\x18\x01 \x01(\t\"\"\n\x10\x45valuateJSAnswer\x12\x0e\n\x06result\x18\x01 \x01(\t\" \n\x0eHashCashAnswer\x12\x0e\n\x06suffix\x18\x01 \x01(\t\"\xb4\x02\n\x0f\x43hallengeAnswer\x12\x41\n\rChallengeType\x18\x01 \x01(\x0e\x32*.spotify.clienttoken.http.v0.ChallengeType\x12L\n\rclient_secret\x18\x02 \x01(\x0b\x32\x33.spotify.clienttoken.http.v0.ClientSecretHMACAnswerH\x00\x12\x44\n\x0b\x65valuate_js\x18\x03 \x01(\x0b\x32-.spotify.clienttoken.http.v0.EvaluateJSAnswerH\x00\x12@\n\thash_cash\x18\x04 \x01(\x0b\x32+.spotify.clienttoken.http.v0.HashCashAnswerH\x00\x42\x08\n\x06\x61nswer\"(\n\x15\x43lientTokenBadRequest\x12\x0f\n\x07message\x18\x01 \x01(\t*u\n\x16\x43lientTokenRequestType\x12\x13\n\x0fREQUEST_UNKNOWN\x10\x00\x12\x1f\n\x1bREQUEST_CLIENT_DATA_REQUEST\x10\x01\x12%\n!REQUEST_CHALLENGE_ANSWERS_REQUEST\x10\x02*v\n\x17\x43lientTokenResponseType\x12\x14\n\x10RESPONSE_UNKNOWN\x10\x00\x12#\n\x1fRESPONSE_GRANTED_TOKEN_RESPONSE\x10\x01\x12 \n\x1cRESPONSE_CHALLENGES_RESPONSE\x10\x02*|\n\rChallengeType\x12\x15\n\x11\x43HALLENGE_UNKNOWN\x10\x00\x12 \n\x1c\x43HALLENGE_CLIENT_SECRET_HMAC\x10\x01\x12\x19\n\x15\x43HALLENGE_EVALUATE_JS\x10\x02\x12\x17\n\x13\x43HALLENGE_HASH_CASH\x10\x03\x42#\n\x1f\x63om.spotify.clienttoken.http.v0H\x02\x62\x06proto3')
|
||||
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
|
||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'client_token_pb2', globals())
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\037com.spotify.clienttoken.http.v0H\002'
|
||||
_CLIENTTOKENREQUESTTYPE._serialized_start=2107
|
||||
_CLIENTTOKENREQUESTTYPE._serialized_end=2224
|
||||
_CLIENTTOKENRESPONSETYPE._serialized_start=2226
|
||||
_CLIENTTOKENRESPONSETYPE._serialized_end=2344
|
||||
_CHALLENGETYPE._serialized_start=2346
|
||||
_CHALLENGETYPE._serialized_end=2470
|
||||
_CLIENTTOKENREQUEST._serialized_start=72
|
||||
_CLIENTTOKENREQUEST._serialized_end=332
|
||||
_CLIENTDATAREQUEST._serialized_start=335
|
||||
_CLIENTDATAREQUEST._serialized_end=488
|
||||
_CHALLENGEANSWERSREQUEST._serialized_start=490
|
||||
_CHALLENGEANSWERSREQUEST._serialized_end=593
|
||||
_CLIENTTOKENRESPONSE._serialized_start=596
|
||||
_CLIENTTOKENRESPONSE._serialized_end=853
|
||||
_TOKENDOMAIN._serialized_start=855
|
||||
_TOKENDOMAIN._serialized_end=884
|
||||
_GRANTEDTOKENRESPONSE._serialized_start=887
|
||||
_GRANTEDTOKENRESPONSE._serialized_end=1045
|
||||
_CHALLENGESRESPONSE._serialized_start=1047
|
||||
_CHALLENGESRESPONSE._serialized_end=1142
|
||||
_CLIENTSECRETPARAMETERS._serialized_start=1144
|
||||
_CLIENTSECRETPARAMETERS._serialized_end=1182
|
||||
_EVALUATEJSPARAMETERS._serialized_start=1184
|
||||
_EVALUATEJSPARAMETERS._serialized_end=1239
|
||||
_HASHCASHPARAMETERS._serialized_start=1241
|
||||
_HASHCASHPARAMETERS._serialized_end=1293
|
||||
_CHALLENGE._serialized_start=1296
|
||||
_CHALLENGE._serialized_end=1642
|
||||
_CLIENTSECRETHMACANSWER._serialized_start=1644
|
||||
_CLIENTSECRETHMACANSWER._serialized_end=1682
|
||||
_EVALUATEJSANSWER._serialized_start=1684
|
||||
_EVALUATEJSANSWER._serialized_end=1718
|
||||
_HASHCASHANSWER._serialized_start=1720
|
||||
_HASHCASHANSWER._serialized_end=1752
|
||||
_CHALLENGEANSWER._serialized_start=1755
|
||||
_CHALLENGEANSWER._serialized_end=2063
|
||||
_CLIENTTOKENBADREQUEST._serialized_start=2065
|
||||
_CLIENTTOKENBADREQUEST._serialized_end=2105
|
||||
# @@protoc_insertion_point(module_scope)
|
|
@ -0,0 +1,36 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: connectivity.proto
|
||||
"""Generated protocol buffer code."""
|
||||
from google.protobuf.internal import builder as _builder
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x12\x63onnectivity.proto\x12\x1bspotify.clienttoken.data.v0\"{\n\x13\x43onnectivitySdkData\x12Q\n\x16platform_specific_data\x18\x01 \x01(\x0b\x32\x31.spotify.clienttoken.data.v0.PlatformSpecificData\x12\x11\n\tdevice_id\x18\x02 \x01(\t\"\xdf\x01\n\x14PlatformSpecificData\x12\x41\n\x07\x61ndroid\x18\x01 \x01(\x0b\x32..spotify.clienttoken.data.v0.NativeAndroidDataH\x00\x12\x39\n\x03ios\x18\x02 \x01(\x0b\x32*.spotify.clienttoken.data.v0.NativeIOSDataH\x00\x12\x41\n\x07windows\x18\x04 \x01(\x0b\x32..spotify.clienttoken.data.v0.NativeWindowsDataH\x00\x42\x06\n\x04\x64\x61ta\"\xb9\x01\n\x11NativeAndroidData\x12\x19\n\x11major_sdk_version\x18\x01 \x01(\x05\x12\x19\n\x11minor_sdk_version\x18\x02 \x01(\x05\x12\x19\n\x11patch_sdk_version\x18\x03 \x01(\x05\x12\x13\n\x0b\x61pi_version\x18\x04 \x01(\r\x12>\n\x11screen_dimensions\x18\x05 \x01(\x0b\x32#.spotify.clienttoken.data.v0.Screen\"\x9e\x01\n\rNativeIOSData\x12\x1c\n\x14user_interface_idiom\x18\x01 \x01(\x05\x12\x1f\n\x17target_iphone_simulator\x18\x02 \x01(\x08\x12\x12\n\nhw_machine\x18\x03 \x01(\t\x12\x16\n\x0esystem_version\x18\x04 \x01(\t\x12\"\n\x1asimulator_model_identifier\x18\x05 \x01(\t\"\xa0\x01\n\x11NativeWindowsData\x12\x12\n\nsomething1\x18\x01 \x01(\x05\x12\x12\n\nsomething3\x18\x03 \x01(\x05\x12\x12\n\nsomething4\x18\x04 \x01(\x05\x12\x12\n\nsomething6\x18\x06 \x01(\x05\x12\x12\n\nsomething7\x18\x07 \x01(\x05\x12\x12\n\nsomething8\x18\x08 \x01(\x05\x12\x13\n\x0bsomething10\x18\n \x01(\x08\"8\n\x06Screen\x12\r\n\x05width\x18\x01 \x01(\x05\x12\x0e\n\x06height\x18\x02 \x01(\x05\x12\x0f\n\x07\x64\x65nsity\x18\x03 \x01(\x05\x42#\n\x1f\x63om.spotify.clienttoken.data.v0H\x02\x62\x06proto3')
|
||||
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
|
||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'connectivity_pb2', globals())
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
DESCRIPTOR._serialized_options = b'\n\037com.spotify.clienttoken.data.v0H\002'
|
||||
_CONNECTIVITYSDKDATA._serialized_start=51
|
||||
_CONNECTIVITYSDKDATA._serialized_end=174
|
||||
_PLATFORMSPECIFICDATA._serialized_start=177
|
||||
_PLATFORMSPECIFICDATA._serialized_end=400
|
||||
_NATIVEANDROIDDATA._serialized_start=403
|
||||
_NATIVEANDROIDDATA._serialized_end=588
|
||||
_NATIVEIOSDATA._serialized_start=591
|
||||
_NATIVEIOSDATA._serialized_end=749
|
||||
_NATIVEWINDOWSDATA._serialized_start=752
|
||||
_NATIVEWINDOWSDATA._serialized_end=912
|
||||
_SCREEN._serialized_start=914
|
||||
_SCREEN._serialized_end=970
|
||||
# @@protoc_insertion_point(module_scope)
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue